java基础
java基础概念
-
java概述和语言背景
- java语言是没有sun公司(Stanford University Network:斯坦福大学网络)在1995年推出的计算机语言
- java之父:詹姆斯·高斯林(James Gosling)
- 2009年,sun公司被甲骨文公司收购,所以现在访问Oracle官网就行:https://www.oracle.com
-
java版本/技术平台
- javaSE(java standard edition,java平台标准版):java技术的核心和基础,是其他两类的基础
- javaEE(java enterprise edition,java平台企业版):企业级应用开发的一套解决方案,包括Servlet、JSP、JDBC等技术。
- javaME(java micro edition,java平台微型版):针对移动设备应用的解决方案,目前被ios和安卓取代
-
特性/优点
- 面向对象、平台无关性、多线程。。。
-
jdk,jre,jvm
- JDK(Java Development Kit)称为Java开发工具,包含了JRE和开发工具(java、javac…)
- JRE(Java Runtime Environment),Java运行环境,包含了JVM和Java的核心类库(java API)
- JVM(Java Virtual Machine),Java虚拟机,真正运行java的地方
- 总结:我们只需安装JDK即可,它包含了java的运行环境和虚拟机。
-
java环境搭建
- 下载安装jdk,注意下载 LTS(long-term support,长期支持版)
- 配置环境变量,JAVA_HOME,Path
- 检测是否配置成功
java -version
- 环境搭建详情:https://coderwcb.blog.csdn.net/article/details/127143366
-
开发运行流程
-
java快速入门
- 入门常用命令
javac
(java compiler):负责编译java
:负责运行
- 编写
.java
文件 - 编译代码
javac xxx.java
- 运行代码
java xxx
,不要带class
- 入门常用命令
-
java跨平台原理
Java程序并非是直接运行的,Java编译器将Java源程序编译成与平台无关的字节码文件(class文件),然后由Java虚拟机(JVM)对字节码文件解释执行,即“先编译在解释”。所以在不同的操作系统下,只需安装不同的Java虚拟机即可实现java程序的跨平台。即一次编译,到处运行
-
安装idea,并进行相关配置
- 01_IDEA配置_2019.md
- 02_IDEA配置_2020.md
- 03_IDEA快捷键.md
- 04_IDEA插件.md
语言基础
-
注释:注释不影响程序的运行,编译后都会消失。但是文档注释可以通过 Javadoc 生成 API 帮助文档
- 注释分类
- 单行注释
- 多行注释
- 文档注释
- 注释分类
-
标识符:标识符是用户编程时使用的名字,用于给类、方法、变量、常量等命名。
- Java中标识符的组成规则:
- 由字母、数字、下划线“_”、美元符号“$”组成,第一个字符不能是数字。
- 不能使用java中的关键字作为标识符。
- 标识符对大小写敏感(区分大小写)。
- Java中标识符的组成规则:
-
关键字:是被赋予了特殊含义的单词,也叫有特别意义的标识符,有时又叫保留字,不需要记忆,因为在开发工具中会报错
- 关键字特点:字母全部小写,常用的代码编辑器会高亮显示
-
方法体
- 即
{}
- 即
-
数据类型
-
计算机存储单元
我们知道计算机是可以用来存储数据的,但是无论是内存还是硬盘,计算机存储设备的最小信息单元叫“位(bit)”,我们又称之为“比特位”,通常用小写的字母”b”表示,一个二进制位只能表示0和1两种状态。而计算机中最基本的存储单元叫“字节(byte)”,通常用大写字母”B”表示,字节是由连续的8个位组成。
除了字节外还有一些常用的存储单位,其换算单位如下:-
1B(字节) = 8bit
-
1KB = 1024B
-
1MB = 1024KB
-
1GB = 1024MB
-
1TB = 1024GB
-
-
java中的数据类型
-
基本数据类型
数据类型 关键字 字节 默认值 包装类 整数类型 byte 1 0 Byte short 2 0 Short int(默认) 4 0 Integer long 8 0L Long 浮点类型 float 4 0.0f Float double(默认) 8 0.0d Double 字符类型 char 2 \u0000 (空格)->0 Character 布尔类型 boolean 1 false Boolean 定义long类型的变量时,需要在整数的后面加L(大小写均可,建议大写)。因为整数默认是int类型,整数太大可能超出int范围。
定义float类型的变量时,需要在小数的后面加F(大小写均可,建议大写)。因为浮点数的默认类型是double, double的取值范围是大于float的,类型不兼容。 -
引用数据类型
数组、对象
-
-
类型转换
- 隐式转换
- 把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量。这种转换方式是自动的,直接书写即可,如下
- [外链图片转存中…(img-hXeezMP8-1682045512612)]
- 强制转换
- 把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量。
- 常量优化机制
- 在给一个变量赋值的时候,**如果“=”的右边全部是常量(包括final关键字定义的常量在内)**那么在编译阶段会把右边的结果赋值给左边的变量, Java中的常量优化机制针对数据类型byte,short,char,String
- 隐式转换
-
-
常量:在程序运行过程中,其值不可以发生改变的量。
- Java中的常量分类:
- 整数常量 整数,例如:-10、0、88等
- 小数常量 小数,例如:-5.5、1.0、88.88等
- 字符常量 用单引号括起来的一个字符,例如:‘a’、‘5’、‘B’、'中’等
- 布尔常量 布尔值,表示真假,只有两个值true和false
- 空常量 一个特殊的值,空值,值为null
- 字符串常量 用双引号括起来的多个字符(可以包含0个、一个或多个),例如"a"、“abc”、"中国"等
- 除空常量外,其他常量均可使用输出语句直接输出。
- Java中的常量分类:
-
变量:在程序运行过程中,其值可以发生改变的量。
- 注意:在同一对花括号中,变量名不能重复。变量在使用之前,必须初始化(赋值)。
-
运算符
- 运算符和表达式的概念
- 运算符:对常量或者变量进行操作的符号
- 表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式。不同运算符连接的表达式体现的是不同类型的表达式。比如
int c = a + b
:由于+是算术运算符,所以这个表达式叫算术表达式。
- 算数运算符
- 加
+
- 字符的”+“操作
- char类型参与算术运算,使用的是计算机底层对应的十进制数值。
System.out.println((char) 'a' + 1); // 输出98,97 + 1 = 98
- char类型参与算术运算,使用的是计算机底层对应的十进制数值。
- 字符串的”+“操作
- 当“+”操作中出现字符串时,这个”+”是字符串连接符,而不是算术运算。当连续进行“+”操作时,从左到右逐个执行。
System.out.println(1 + 2 + "测试" + 3 + 4); // 3测试34 =》 从左往右执行
- 当“+”操作中出现字符串时,这个”+”是字符串连接符,而不是算术运算。当连续进行“+”操作时,从左到右逐个执行。
- 字符的”+“操作
- 减
-
- 乘
*
- 除
/
System.out.println(10 / 3); // 3
- 取余
%
System.out.println(10 % 3); // 1
- 注意
- /和%的区别:两个数据做除法,/取结果的商,%取结果的余数。
- **整数操作只能得到整数,要想得到小数,必须有浮点数参与运算。**即
System.out.println((double) 10 / 3); // 3.33...
或者System.out.println(10.0 / 3); // 3.33...
;为什么呢?先计算10/3=3.33…然后别忘了,默认类型时int ,相当于(int)10/3自然等于3
- 加
- 关系运算符
- 大于
>
-> GT(greater than) - 大于等于
>=
-> GE(greater equal) - 小于
<
-> LT(less than) - 小于等于
<=
->LE (less equal) - 等于
==
-> EQ(equal) - 不等于
!=
-> NE(not equal) - 注意:关系运算符的结果都是boolean类型,要么是true,要么是false。千万不要把“”误写成“=”,"“是判断是否相等的关系,”="是赋值。
- 大于
- 逻辑运算符
- && =》 短路与
- || =》 短路或
- ! =》 逻辑非
- | =》 逻辑或
- & =》 逻辑非
- 注意:我们常用的,也是我们常叫的就是与或非(&&,||,!)。区别在于,短路可能只会执行一边,而逻辑都会执行
- 自增自减运算符
- 自增
++
- 自减
--
- 注意:++和-- 既可以放在变量的后边,也可以放在变量的前边。区别在于参与操作的时候,如果放在变量的前边,先计算出最新的变量,然后使用最新的变量操作
- 自增
- 赋值运算符
=,+=,-=,*=,/=,%=
- 三元运算符
关系表达式 ? 表达式1 : 表达式2;
- 关系表达式结果为boolean型,为true时调用表达式1,为false时调用表达式2。
- 位运算符
左移(<<),右移(>>)
实际上基本用不到
- 运算符和表达式的概念
-
流程控制语句
-
顺序结构
-
条件(分支)结构
- if
- switch
-
switch语句case穿透问题
-
在switch的不同case中定义同一个变量会报错(如下),这是什么原因,如何解决呢?
- 原因:在括号"{}"中定义的对象,作用域在这对括号里面。变量的作用域在整个switch语句中,不只是在具体的某个case语句中,所以重复定义相同变量,会导致报错。
- 解决:在case中增加括号"{}",改变对象的作用域。
switch ("A") {case "A": {int aa = 10;}break; case "B": {int aa = 10;}break; }
-
- if和switch的区别?
- switch参数应该是byte、short、int、char 这类整数。long,string 都不能作用于swtich。switch 可以是枚举类型(JDK1.5 之后),**string在JDK1.7之后可以,**但是long还是不行,呢么为什么其他整数类型就行呢?因为switch的参数可以是int 基本类型或Integer 包装类型,byte,short,char 都可以隐含转换为int
-
循环结构
- for,while,do-while
- 三种循环的区别
- for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
- do…while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)
- 死循环(无限循环)的三种格式 =》原因,没有终止条件
- for( ; ; ){}
- while(true){}
- do {} while(true);
- 跳出循环语句
- break:直接强行跳出当前循环,不再执行剩余代码。但在多重循环的情况下,若break在内层循环中,则仅仅终止了内层循环,外循环照常执行。
- continue:仅仅终止此次循环。
- return:使程序返回到调用某方法的地方。
- break+标签:可用于终止多重循环。在多重循环前设置一个标签,配合break即可终止整个循环。
- System.exit(0) ;终止当前运行的 Java 虚拟机 参数传入一个数字即可。通常传入0记为正常状态,其它为异常状态。
-
数组
- 数组概述
- 数组就是存储数据的长度固定的容器,存储多个数据的数据类型要一致。数组一旦定义,程序执行的过程中,长度、类型以已经固定
- 数组定义
- 动态初始化
- 数组动态初始化就是只给定数组的长度,由系统给出默认初始化值。
int[] arr = new int[3];
- 数组动态初始化就是只给定数组的长度,由系统给出默认初始化值。
- 静态初始化
- 在创建数组时,直接将元素确定。
int[] num = {1,2,3};
- 在创建数组时,直接将元素确定。
- 动态初始化
- 数组访问
- 数组名[索引];即
num[0]
。 - 什么叫索引?
- 每一个存储到数组的元素,都会自动的拥有一个编号,从0开始。这个自动编号称为数组索引(index)
- 数组名[索引];即
- 常用操作
- 遍历 -》 for、for增强、Interator(迭代器)、lambda,stream、Arrays
- 过滤/查找 =》 常见过滤、二分查找
- 排序 =》 冒泡 / 选择 / 插入 排序
- 求和/字符串拼接
- 获取最大值
- Arrays=>toString、sort、binarySearch
- 二维数组-》同理
面向对象
-
基本概念
-
面向过程(POP:Procedure-Oriented Programming):就是一步一步的按照逻辑走
-
面向对象(OOP:Object Oriented Programming):面对对象是一种思想,是基于面向过程而言的,是把数据以及对数据的操作放在一起,作为一个相互依存的整体
-
类:类是现实世界在计算机中的反应,他将数据和对数据的操作封装在一起(模板)
-
对象:是看得到摸的着的实体
-
小结:类是对事物的描述,是一种抽象的概念,而对象是一个具体的事物。即类是对象的模板,而对象是类型的实例
-
-
java类中包含那几个元素?啥作用
- 变量:用来定义对象的数据
- **方法:**用来定义对象的行为
- **构造方法:**用来创建对象
- **代码块:**用来在类加载时执行操作或者在每次实例化前执行通用操作
- **内部类:**作为类的一个成员存在、能够访问外部类的属性和方法
-
-
成员变量和局部变量
- **类中位置不同:**成员变量(类中方法外)局部变量(方法内部或方法声明上)
- **内存中位置不同:**成员变量(堆内存)局部变量(栈内存)
- **生命周期不同:**成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
- **初始化值不同:**成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
-
构造方法
- 格式:1、方法名和类名完全相同;2、没有返回值,连void也没有;
public【权限修饰符】 Demo【类名】(){}
- 作用:给对象进行初始化
- 注意事项:
- 没有定义构造方法,呢吗系统默认给出一个默认无参构造方法
- 如果定义了构造方法,呢吗系统不在提供默认构造方法
- 在使用中通常定义无参构造方法和全参构造方法
- 格式:1、方法名和类名完全相同;2、没有返回值,连void也没有;
-
this/super
-
this:就是一个引用,指向的就是当前对象。一般用于区分局部变量和成员变量
-
super:父类对象引用
-
关键字 访问成员变量 访问成员方法 访问构造方法 this this.成员变量 this.成员方法(参数…) this(参数…) super super.成员变量 super.成员方法(参数…) super(参数…) -
注意: this(参数…), super(参数…) 必须放在构造方法的第一行有效语句,并且二者不能共存
-
-
权限修饰符
-
权限 类内 同包 不同包子类 不同包非子类 private √ × × × default √ √ × × protected √ √ √ × public √ √ √ √
-
-
static关键字
- Static 可以修饰变量、方法、代码块、内部类;
- Static 修饰变量是静态变量或者叫类变量,静态变量被所有实例所共享,不属于于对象,属于类。 随着类的加载而加载,只为静态分配一次内存。
- Static 修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。
- Static 修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。
- Static 修饰的类是静态内部类;
- 在 static 方法中不能使用 this 或者 super 关键字。
- 静态方法只能访问静态的成员。非静态方法可以访问静态的成员,也可以访问非静态的成员
- Static 可以修饰变量、方法、代码块、内部类;
-
重写 / 重载
-
**重载(Overload):**发生在一个类中,方法名相同,参数列表不同(类型,个数,顺序) ,与访问修饰符,返回值无关
-
**重写(Override):**发生在继承类中,方法名相同,参数列表和返回值 相同
- 构造方法不能被重写;声明为 final 的方法不能被重写;声明为 static 的方法不存在重写(重写和多态联合才有意义);访问权限(访问修饰符)不能比父类更低;重写之后的方法不能抛出更宽泛的异常
-
-
面向对象三大特征
- 总结:
- 封装:将资源私有化,使用private关键字,隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。提高了数据的安全性,代码复用性,降低了程序之间的耦合性
- 继承:发生在两个类中,一个类继承另一个具有访问权限的属性和方法,使用extends关键字,只能单继承。提高了代码复用性,并且可以实现子类可以对父类进行扩展和修改(重写)
- 多态:多态建立在继承的基础上,先有继承才能有多态,多态是指不同的子类继承父类后分别都重写覆盖了父类的方法,这样可以根据不同的对象执行方法,且**父类引用指向子类对象(向上转型对象)**的时候叫多态。(继承、重写、父类引用指向子类对象)
- 补充:
- 封装原则:成员变量private,提供对应的getXxx()/setXxx()方法
- 继承访问特点:子类中所有的构造方法默认都会访问父类中无参的构造方法,因为每一个子类构造方法的第一条语句默认都是:super()
- 如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢
- 通过使用super关键字去显示的调用父类的带参构造方法
- 在父类中自己提供一个无参构造方法
- 引用类型之间的类型转换
- 向上转型(upcasting):父类引用指向子类对象为向上转型;
fatherClass obj = new sonClass();
- 向下转型(upcasting):与向上转型相反,子类引用指向父类对象;子类型 对象名 = (子类型)父类引用;
sonClass obj = (sonClass) fatherClass;
- 向上转型(upcasting):父类引用指向子类对象为向上转型;
- 多态中转型存在的风险 => ClassCastException ; 解决方案 => instanceof
- 总结:
-
抽象类(abstract)
- 对现实世界中某一种类型的多种事物的抽象描述。说白了,分析事物时, 发现了共性的内容,就向上抽取,但是如果父类 的方法功能与子类不同,那么这时就不抽取方法主体了,比如 【狼、猫 】都属于【动物】,都有一个
eat()
方法,但是狼吃肉,猫吃素,这个时候【动物】就说不清我到底是吃肉还是吃素,这个时候就需要子类自己定义 - 抽象类和抽象方法必须使用 abstract 关键字修饰
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类不能实例化 ;抽象类为什么不能实例化
- 因为这是规定。抽象:就是不具体的意思。类是对对象的具体描述,而抽象类不具体,没有方法体,(提供的成员不足以生成一个具体对象),那么就无法生成一个不具体的对象。就好比,你可以实例化一个苹果,但你不能实例化一个水果(这个现实中存在的实物)。
- 抽象类可以有构造方法
- 抽象类的子类
- 要么重写抽象类中的所有抽象方法
- 要么是抽象类
- 对现实世界中某一种类型的多种事物的抽象描述。说白了,分析事物时, 发现了共性的内容,就向上抽取,但是如果父类 的方法功能与子类不同,那么这时就不抽取方法主体了,比如 【狼、猫 】都属于【动物】,都有一个
-
接口(interface)
- 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
- Java中接口存在的两个意义
- 用来定义规范
- 用来做功能的拓展
- 接口的组成
- 静态常量:public static final
- 抽象方法:public abstract
- 默认方法(Java8):default
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
- 静态方法(Java8):static
- 私有方法(Java9):private或者private static
- 类和接口的关系
- 类与类的关系:继承关系,只能单继承,但是可以多层继承
- 类与接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口与接口的关系:继承关系,可以单继承,也可以多继承
- 接口不能实例化 => 参考抽象类
- 接口的子类
- 要么重写接口中的所有抽象方法
- 要么子类也是抽象类
- 抽象类和接口的区别
- 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
- 类可以实现很多个接口,但是只能继承一个抽象类
- 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现 抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
- 抽象类可以在不提供接口方法实现的情况下实现接口。
- Java 接口中声明的变量默认都是 final 的。抽象类可以包含非 final 的变量。
- Java 接口中的成员函数默认是 public 的。抽象类的成员函数可以是 private,protecte 或者是 public 。
- 接口和抽象类都不可以实例化。但是接口中不能有构造方法,抽象类可以有构造方法
-
final关键字
- final代表最终的意思,可以修饰成员方法,成员变量,类
- fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
- final修饰方法:该方法不能被重写
- final修饰变量:表明该变量是一个常量,不能再次赋值
- 变量是基本类型,不能改变的是值
- 变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
- final代表最终的意思,可以修饰成员方法,成员变量,类
-
代码块:在Java中,使用 { } 括起来的代码被称为代码块
- 局部代码块:缩短变量的生命周期,及早释放,提高内存利用率;比如解决switch变量重名问题
- 静态内部块:随着类的加载而加载,并且只执行一次;初始化类的属性,比如创建一个数据库连接对象这种需要一直使用的
- 构造代码块:位于类中成员位置,用"{}"包起来,每次调用构造函数前,都会先执行一次构造代码块,可以把多个构造函数中的共同代码放一起,给对象进行初始化
- 同步内部块:多线程环境下互斥执行
- 优先级:静态代码块 > main > 构造代码块 > 构造方法;代码块可以有多个,如果有多个,按照先后顺序依次执行。
-
内部类
- 成员内部类
- 静态成员内部类
- 局部内部类
- 匿名内部类
- 前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类
- 本质:是一个继承了该类或者实现了该接口的 **子类匿名对象,**不是创建了接口的对象
- 格式:
new 接口名(){ 重写接口方法 }
- 高级用法:Lambda表达式=》jdk8新语法,简化匿名内部类的写法,注意:只能简化函数式接口的匿名内部类的写法形式
- 什么时函数式接口?必须时接口,并且只有一个抽象方法的形式,同时会在接口上加上一个@FunctionalInterface注解,标记该接口必须满足函数式接口
- 格式:
接口名 aa = (参数列表) -> { }
常用api类
-
object
- toString:获取对象的地址值
- equals:对比对象的地址值,但是实际中的使用不希望比较对象的地址值,而是对比对象的属性值,这个时候就需要重写equals方法,但是同时必须还要重写hashCode方法,为什么呢?
- hashCode是用于查找位置使用的,而equals是用于比较两个对象的是否相等的
- hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象(比如hashSet)在哈希表中的索引位置。
如果两个对象相等,则 hashcode 一定也 是相同的。但是hashcode 相同,类却不一定相同,所以重写 equals 时必须重写 hashCode
就好比说,我在衣柜里找两件相同的衣服,第一步我肯定是通过hash值确定在衣柜的那个盒子里,第二部才是使用equal比较衣服一样吗
-
Objects
- isNull:判断对象是否为空
- nonNull:判断对象是否不为空
- equals:对比对象的地址值,推荐使用这个对比对象,因为有非空判断,避免空指针异常。
-
String/StringBuiler
- String:Java 程序中所有的双引号字符串,都是 String 类的对象。创建后内容不可变
- equals,equalsIsIgnoreCase,toLowerCase,toUpperCase,subString,replace,split,charAt,indexOf,toCharArray
- StringBuilder:内容可变,一般拼接字符串使用
- append:添加数据,并返回对象本身,可以直接链式编程
- reverse:返回相反的字符串序列
- String:Java 程序中所有的双引号字符串,都是 String 类的对象。创建后内容不可变
-
Math
- abs-绝对值;round-四舍五入;max-最大值;min-最小值,random-随机数(0.0-1.0)
-
System
- currentTimeMillis-获取当前毫秒值;arraycopy-数组复制
-
BigDecimal
- 用来精确计算,比如金额相关
- 创建BigDecimal时最好使用字符串,否则有精确问题;
- BigDecimal bigDecimal1 = new BigDecimal(“0.1”);
- BigDecimal bigDecimal2 = BigDecimal.valueOf(0.1); // 内部进行了Double的toString
- 常用方法:add-加;subtract-减;multiply-乘;divide-除法;
- 除法注意:BigDecimal divide = bd1.divide(参与运算的对象,小数点后精确到多少位,舍入模式); 第三个参数如下
- BigDecimal.ROUND_UP 进一法
- BigDecimal.ROUND_FLOOR 去尾法
- BigDecimal.ROUND_HALF_UP 四舍五入
-
时间日期
-
使用场景:获取/设置时间;字符串转时间,时间转字符串 =》 还有就是指定格式转换;然后就是获取几天获取几月之后的时间
-
Date
-
getTime-获取的是日期对象从1970年1月1日 00 : 00 : 00到现在的毫秒值,不过一般使用
System.currentTimeMillis()
-
setTime:设置时间,单位毫秒 ;不过一般直接使用
new Date(System.currentTimeMillis())
-
实例:获取一分钟之后的日期
-
new Date(System.currentTimeMillis() + 1 * 1000 * 60)
-
-
-
SimpleDateFormat=>时间格式化,线程不安全
- 时间转字符串:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a"); String format = sdf.format(new Date()); // 2023年02月26日 22:43:38 星期日 下午
- 字符串转时间:
String str = "2020年11月11日 0:0:0"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); // 格式必须和str一样 Date parse = sdf.parse(str); // Wed Nov 11 00:00:00 CST 2020
-
Calendar:日历,calendar是可变日期,一旦修改后其本身表示的时间的时间将发生变化 ,可以获取几天,或者几月之后的日期
-
JDK8时间日期类:方法很多,线程安全,上面线程不安全
- 常用方法:Instant、LocalDateTime、LocalDate、LocalTime、DateTimeFormatter、Period、Duration
-
-
包装类
-
基本数据类型 包装类 byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean
-
-
Arrays
- toString、sort=》数字顺序、binarySearch=》二分查找、asList
-
正则表达式(英语:Regular Expression,在代码中常简写为regex)。
-
boolean b = telNumber.matches("1[34857][0-9]{9}"); str = str.replaceAll("[\\d]+", "#"); //#代替数字
-
异常
- 概述:异常就是和正常情况不一样,有错误出现。在java中,阻断当前方法或者作用域的情况称为异常
- 异常体系
- 异常主要分为:错误、编译时异常(受控异常)、运行时异常(非受控异常)
- Error:系统级别问题,是程序中无法处理的错误
- Virtual MachineError(虚拟机运行错误)
- NoClassDefFoundError(类定义错误)
- 比如说当jvm耗完可用内存时,将出现OutOfMemoryError
- Exception:程序本身可以捕获并且处理的异常
- 编译时异常:编译期间出现的异常,必须处理异常,否则编译不通过
- IOException (操作输入流和输出流时可能出现的异常)
- ClassNotFoundException 找不到类异常
- SQLException 提供有关数据库访问错误或其他错误的信息的异常。
- 运行时异常:运行期间可能出现的异常,不要求处理异常
- NullPointerException 空指针异常
- ArithmeticException 数学操作异常
- NumberFormatException 数字转换异常
- IndexOutOfBoundsException 数组索引越界异常
- ClassCastException(类型转换异常类)
- IllegalArgumentException(非法参数异常)
- 编译时异常:编译期间出现的异常,必须处理异常,否则编译不通过
- Error:系统级别问题,是程序中无法处理的错误
- 异常处理
- 常用方法:getMessage、printStackTract
- 格式:try-catch-finally、throw、throws、try-with-resources
- 自定义异常
- 继承RuntinmeException或者其他异常类
- 实现无参构造,有参构造
- 直接使用就行
- 日志:有异常则就需要日志记录,这也是一种规范
- 规范:日志规范就是一些接口,常见的规范,apache的JCL(Jakarta Commons Logging),slf4j (Simple Logging Facade for Java)
- 具体实现技术:Log4j、JUI、LogBack、log4j2
- 发展历程:
- Log4j:Apache的一个开源项目,解决传统日志
System.out.println()
无法定制化,且日志粒度不够细。 - JUI(java util logging): Java原生日志框架,亲儿子,仿照Log4J
- JCL(Jakarta Commons Logging):制定日志标准(就像jdbc一样),使其可以选择合适的日志实现类
- Slf4j(simple Logging Facade for java):原Log4J的作者,它觉得Apache Commons Logging不够优秀,所以他想搞一套更优雅的方案,于是slf4j日志体系诞生了,slf4j实际上就是一个日志门面接口,它的作用类似于Commons Loggins。 并且他还为slf4j提供了一个日志的实现-logback。
- LogBack: 由Log4j之父做的另一个开源项目
- log4j2: 因为slf4j以及它的实现logback出来以后,很快就赶超了原本apache的log4j体系,所以apache在2012年重写了log4j, 成立了新的项目Log4j2 ,几乎涵盖了logback的全部新特性
- Log4j:Apache的一个开源项目,解决传统日志
- 日志级别:用来控制系统中哪些日志级可以输出、输出不低于设定级别的日志信息。优先级从低到高依次为:ALL < TRACE < DEBUG(logback默认) < INFO < WARN < ERROR(log4j默认) < FATAL < OFF,加粗的标识常见
- all【所有】:所有日志级别,包括定制级别。
- trace【跟踪/追踪】: 一般用来追踪详细的程序运行流,比如程序的运行过程中,运行到了哪一个方法,进入了哪一条分支。通过trace程序的运行流程,可以判断程序是否按照期望的逻辑在运行。
- debug【调试】: 这类日志往往用在判断是否有出现bug的场景,且往往记录了代码运行的详细信息,比如方法调用传入的参数信息。
- info【信息】:用来记录程序运行的一些关键信息,它不像trace那样记录程序运行的整个流程,也不像debug那样为了解决问题而记录详细的信息。info记录的是整个系统的运行信息,比如系统运行到了哪一个阶段,到达了哪一个状态。
- warn【警告】:用来记录一些警告信息。警告信息表示,程序进入了一个特殊的状态,在该状态下程序可以继续运行,但是不建议让程序进入该状态,因为该状态可能导致结果出现问题。
- error【错误】:用来记录运行时的错误信息,表示程序运行过程中出现了需要被解决的问题,往往是一些异常。使用error日志的时候,一般会将详细的异常出现的原因记录
- fatal【致命】:指明非常严重的可能会导致应用终止执行错误事件。
- off【关闭】:最高级别,不打印日志。
- 打印格式注意
- 日志要打印出方法的入参、出参
- 建议使用参数占位{},而不是用+拼接。
- 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。
正例:logger.error(各类参数或者对象 toString + “_” + e.getMessage(), e); - 禁止在线上环境开启 debug,因为一般系统的debug日志会很多,并且各种框架中也大量使用 debug的日志,线上开启debug不久可能会打满磁盘,影响业务系统的正常运行。
集合
-
数据结构:数据结构是计算机底层存储、组织数据的方式。是指数据以什么方式排列在一起的
-
树的演变:二叉树 -》 二叉查找树 -》 平衡二叉树 -》 红黑树
-
java集合体系图
-
java集合体系特点总结
-
Collection 是单列集合的顶层接口:常见成员方法 add、clear、remove、contains、isEmpty、size
-
List系列集合:添加的元素 有序、可重复、有索引。可以用 Iterator 实现单向遍历,也可用ListIterator 实现双向遍历。
-
ArrayList:底层的数据结构是数组结构。特点是:查询快,增删慢。线程不同步 。
-
LinkedList:底层使用的是双向链表数据结构。特点是:查询慢,增删快。线程不同步 。本身提供了很多直接操作首尾元素的特有api,比如addFirst、addLast、getFirst、getLast、removeFirst、removeLast
-
Vector:底层是数组数据结构,线程安全,效率低,JDK1.2已经被遗弃
-
-
Set系列集合:添加的元素 无序、不重复、无索引。只能用 Iterator 实现单项遍历。线程不同步 。Set 中最多包含一个 null 元素
- HashSet:底层是哈希表数据结构,是一种对于增删改查数据性能都较好的结构。特点是无序、不重复、无索引。HashSet 的实现是依赖于 HashMap 的,HashSet 的值都是存储在 HashMap 中的。在 HashSet 的构造法中会初始化一个 HashMap 对象,HashSet 不允许值重复。因此,HashSet 的值是作为 HashMap 的 key 存储在 HashMap 中的,当存储的值已经存在时返回 false。根据 hashCode 和 equals 方法来确定元素的唯一性(判断hashCode值是否相同,如果相同继续判断equal)
- LinkedHashSet:底层数据结构由双向链表和哈希表组成,增删改查性能较好。特点是有序、不重复、无索引。就是哈希表的基础上又加了一个双向链表。由链表保证元素有序。由哈希表保证元素唯一。
- TreeSet:底层的数据结构是红黑树。特点是可排序、不重复、无索引。可以对 Set 集合中的元素进行排序(默认排序-自然循序),也可以自己写个类实现 Comparable接口或者创建集合时自定义Comparator比较器对象将其作为参数传递给 TreeSet 的构造函数,这也是TreeSet底层保证唯一的原理。
-
-
Map:是双列集合的顶层接口 。这个集合是存储键值对的, 而且要确保键的唯一性。常用成员方法有put、remove、clear、containsKey、containsValue,isEmpty、size。Map 中可以将 Key 和 Value 单独抽取出来,其中 KeySet()方法可以将所有的 keys 抽取成一个 Set,而 Values()方法可以将 map 中所有的 values 抽取成一个集合。
-
HashTable:底层是哈希表(也叫散列值)数据结构,不可以存入 null 键和 null 值,该集合线程是同步的,效率比较低。出现于 JDK1.0 。
-
HashMap:底层是哈希表数据结构,可以存入 null 键和 null 值,线程不同步,效率较高,代替了 HashTable,出现于 JDK 1.2 。
-
TreeMap:底层是二叉树数据结构,不可以存入 null 键但是可以存入null 值,线程不同步,具体顺序可以由指定的Comparator来决定,或者根据键的自然顺序来判断。
-
-
-
补充
- ArrayList集合底层原理:
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建一个新的长度为10的数组
- 存满时,会扩容1.5倍,也就是10*1.5=15
- 如果一次添加多个元素,1.5倍还放不行,则新创建的长度以实际为准;比如添加100个元素,新数组的长度是110
- ArrayList集合底层原理:
-
java集合遍历方式
-
遍历方式 Array List Set Map for √ √ × × 增强for √ √ √ √ Iteration √ √ √ √ Stream √ √ √ √ Arrays √ × × × 直接打印 × √ √ √
-
-
java增删
- 倒叙fori、Iteration
-
泛型
- 是JDK5中引入的特性,用于统一数据类型,可以在编译阶段约束操作的数据类型,并进行检查。
- 注意java中的泛型是伪泛型,因为在class文件中是没有这个的
- 泛型只支持使用引用数据类型。为什么?因为不写泛型默认是Object,而基础数据类型不能转化成Object
- 分类
- 泛型类 =》 类名< 类型 > =>
class Test<K,V>{}
,其中“K”,“V”可以理解为变量,但是不代表值,而是表示类型 - 泛型方法 =>
public <T> testAdd(T key){}
- 泛型接口
interface Test<K,V>{}
- 泛型类 =》 类名< 类型 > =>
- 常用类型:T (Type表示类型) 、 K V (分辨表示键值对中的key value)、E 代表Element、?表示不确定的类型
- 泛型的继承和通配符
- 泛型不具备继承性,但是数据具备继承性
- 泛型的通配符:
?
? extend E
=》E或者是E的子类? super E
=》E或者是E的父类
- 使用场景
- 类型不确定的时候可以定义泛型
- 类型不确定但是指定是哪个继承体系中的,可以使用泛型的通配符
-
java可变参数:可变参数本质上就是一个数据,用于在形参中接受多个数据,比如
int ...a
- 形参列表中的可变参数只能有一个
- 可变参数必须放在形参列表的最后面
-
stream流
- 获取Stream流:创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法:处理数据,操作完毕之后,还可以继续进行其他操作
- filter,limit,skip,concat,distinct,map
- 终结方法:一个Stream流只能有一个终结方法
- forEach、count、collect
IO流
-
File定位文件:可以进行删除文件内容,读取文件信息等,但是不能读写文件内容
-
操作File File常用方法 增删 createNewFile、mkdir、mkdirs、delete 判断和获取 isDirectory、isFile、exists、getAbsolutePath、getPath、getName、listFiles
-
-
字符集:字节、字符
-
我们是怎么把文字存储到计算机的?
- 我们把文字转换成十进制的表现方式(比如“a”相当于97),然后转换成二进制,然后就可以存储到计算机。
-
什么是字符集?什么叫字符编码
- 字符集(Character set)是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等
- 字符集:规定了字符和字符码之间的对应关系。 可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。
- 字符编码:规定了一个字符码在计算机中如何存储。
-
常用字符集
- ASCII字符集(American Standard Code for Information Interchange,美国信息交换标准代码)
- 是最早产生的编码规范,包括数字、英文、符号
- 一个字节存储一个字符,也就是8位,总共可以表示128个字符信息(2^8=256,不包括负数)
- GBK(即“国标”“扩展”汉语拼音的第一个字母) 字符集:全称《汉字内码扩展规范》
- window系统默认的码表。兼容ASCII码表,包括中日韩
- 一个中文以两个字节存储,英文1个字节
- Unicode字符集(又叫万国码、统一码):是计算机科学领域里的一项业界字符编码规范
- Unicode包含了全世界所有的字符,兼容ASCII 。Unicode最多可以保存4个字节容量的字符。也就是说,要区分每个字符,每个字符的地址需要4个字节。这是十分浪费存储空间的,于是,程序员就设计了几种字符编码方式,比如:UTF-8,UTF-16,UTF-32。最广为程序员使用的就是UTF-8,UTF-8是一种变长字符编码,注意:UTF-8不是编码规范,而是编码方式。
- utf-8:一个中文以3个字节存储,英文1个字节
- Unicode包含了全世界所有的字符,兼容ASCII 。Unicode最多可以保存4个字节容量的字符。也就是说,要区分每个字符,每个字符的地址需要4个字节。这是十分浪费存储空间的,于是,程序员就设计了几种字符编码方式,比如:UTF-8,UTF-16,UTF-32。最广为程序员使用的就是UTF-8,UTF-8是一种变长字符编码,注意:UTF-8不是编码规范,而是编码方式。
- 小结
- 英文和数字在任何国家的字符集中都占一个字节(在任何国家的编码中都不会乱码)
- GBK中一个中文字符2个字节
- UTF-8中一个中文字符3个字节
- 编码前的字符集和编码后的字符集必须一致,否则乱码
- 中文的字节存储方式
- 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
- 字符集的编码、解码操作
- 编码:str.getBytes(“GBK”)
- 解码:new String(gbks2,“GBK”)
- ASCII字符集(American Standard Code for Information Interchange,美国信息交换标准代码)
-
-
读写文件内容:IO流可以对硬盘中的文件进行读写
-
什么叫io流
-
I/O是Input/Output的缩写, 用于处理设备之间的数据传输。如读/写文件,网络通讯等。
流是一种抽象概念,它代表了数据的无结构化传递。
Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行,所以叫io流。java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。常见的应用: 文件复制; 文件上传; 文件下载
-
-
分类
- 按照数据的流向(方向)
- 输入流:读数据,是数据从硬盘文件读入到内存的过程 =》 硬盘->内容
- 输出流:写数据,是内存程序的数据从内存写出到硬盘文件的过程=》内存->硬盘
- 按照数据类型(单位)
- 字节流:以字节为单位,可以读写所有数据
- 字符流:以字符为单位
- 按照数据的流向(方向)
-
使用场景
-
如果操作的是纯文本,比如json文件,txt文件,优先使用字符流
- 怎么判断是纯文本呢? 能用记事本打开并且能看懂的就是纯文本
-
如果是图片、视频、音频等二进制文件,优先使用字节流
-
如果不确认文件类型,优先使用字节流,字节流是万能的流
-
-
IO流快速入门
- 创建输入/输出流
- 读/写 操作
- 关闭输入/输出流
-
IO流体系结构图
-
IO流体系结构图
字节流(byte) 字符流(char) 分类 字节输入流 字节输出流 字符输入流 字符输出流 抽象基类 InputStream OutputStream Reader Writer 操作文件 FileInputStream FileOutputStream FileReader FileWriter 操作数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 转换流 InputStreamReader OutputStreamWriter 对象操作流 ObjectInputStream ObjectOutputStream 打印流 PrintStream PrintWriter 其中,转换流都是字节流转字符流。
- OutputStreamWriter : 字节输出流 -> 字符输出流;
- InputStreamReader : 字节输入流 -> 字符输入流
对象操作流也叫序列化流
-
读写常用方法:read、write、flush、close
-
另外,字符缓冲流还有两个独特的方法:
- BufferedWriter类newLine() :写入一个行分隔符。这个方法会自动适配所在系统的行分隔符。
- BufferedReader类readLine() :读取一个文本行。
注意:输出流必须刷新才能写入,当然也可以直接调用
close()
,他里面包含了flush
,但是使用close()
关闭流后就不能在使用了 -
对象操作流
- 序列化:将对象的状态信息转换为可以存储或传输的形式的过程。程序通过序列化把Java对象转换成二进制字节流,然后就可以把二进制字节流写入网络或磁盘。
- 反序列化:读取磁盘或网络中的二进制字节流数据,转化为Java对象
- 序列化流注意事项
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口,Serializable是一个标记接口,实现该接口,不需要重写任何方法
- 定义serialVersionUID:验证版本一致性
private static final long serialVersionUID = 42L;
- 如果一个对象中的某个成员变量的值不想被序列化,给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程;
private transient int age;
-
Properties作为Map集合的使用
- Properties介绍
- 不属于io流体系,
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 属性列表中的每个键及其对应的值都是一个字符串
- 常用方法:load、store
- Properties介绍
-
-
多线程
-
多线程相关概念
- 并发和并行
- 并行:在同一时刻,有多个指令在多个CPU上同时执行。
- 并发:在同一时刻,有多个指令在单个CPU上交替执行。
- 进程和线程
- 进程:是正在运行的程序
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
- 动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
- 并发性:任何进程都可以同其他进程一起并发执行
- 线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则称为多线程程序
- 进程:是正在运行的程序
- 并发和并行
-
线程状态/线程生命周期
-
多线程常用方法
-
方法名 说明 Thread(Runnable target) 分配一个新的Thread对象 Thread(Runnable target, String name) 分配一个新的Thread对象,并设置线程名 void run() 在线程开启后,此方法将被调用执行 void start() 使此线程开始执行,Java虚拟机会调用run方法() String getName() 获取当前线程名称,默认线程名称时Thread-索引 void setName() 设置线程名称 public static Thread currentThread() 获取当前正在执行的线程对象
-
-
创建多线程
-
继承Thread类并重写run方法
-
实现Runnable接口并重写run方法配合Thread构造方法
-
实现Callable接口并重写call方法 配合FutureTask(是Runnable和Futrue的实现类)
-
-
多线程创建线程三种实现方式的对比
-
方式 优点 缺点 继承Thread接口 编程比较简单,可以直接使用Thread类中的方法 可以扩展性较差,不能再继承其他的类 实现Runnable接口 扩展性强,可以继续继承和实现其他的类 不能返回线程执行的结果 实现Callable接口配合FutureTask 扩展性强,可以继续继承和实现其他的类。可以得到线程执行的结果 编程相对复杂
-
-
线程安全=》线程同步
-
线程安全:多个线程同时操作一个共享资源的时候可能会出现业务安全问题,称为线程安全问题
-
线程同步:为了解决线程安全的问题
-
线程同步的核心思想:加锁,把共享资源上锁,每次只能让一个线程进入访问,访问完毕之后解锁,然后其他线程才能进来
-
实现线程同步的方式
-
同步代码块
synchronized(任意对象) { 多条语句操作共享数据的代码 }
-
同步方法
- 普通同步方法:
修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体;}
=> 锁对象是this
- 静态同步方法:
修饰符 static synchronized 返回值类型 方法名(方法参数) { 方法体;}
=> 锁对象是类名.class
- 普通同步方法:
-
同步锁(lock锁)
-
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
-
常用方法:
-
方法名 说明 ReentrantLock() 创建一个ReentrantLock的实例 void lock() 获得锁 void unlock() 释放锁
-
-
-
死锁
-
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行
-
什么情况下会产生死锁
-
资源有限
-
同步嵌套
-
-
-
-
Synchronized与Lock的区别
-
Lock是一个接口,而Synchronized是关键字。
-
Synchronized会自动释放锁,而Lock必须手动释放锁。
-
Lock可以让等待锁的线程响应中断,而Synchronized不会,线程会一直等待下去。
-
Synchronized能锁住类、方法和代码块,而Lock是块范围内的
-
通过Lock可以知道线程有没有拿到锁,而Synchronized不能。
-
-
-
常见场景
-
场景 方法名 说明 线程休眠 public static void sleep() 使当前正在执行的线程停留(暂停执行)指定的毫秒数 线程优先级 final int getPriority() 返回此线程的优先级 final void setPriority(int newPriority) 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10 守护线程 void setDaemon(boolean on) 守护线程是指为其他线程服务的线程。
与守护线程相对应就是用户线程,用户线程可以理解为我们平常创建的普通线程,而守护线程守护的就是用户线程。当用户线程全部执行完毕,守护线程会跟着结束。
也就是说守护线程必须伴随着用户线程,如果一个应用内只存在一个守护线程,没有用户线程,那么守护线程自然会退出。礼让线程/出让线程 yield() 线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。 插队线程/插入线程 join() 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程的所有任务。 生产者消费者-等待唤醒机制 void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 void notify() 唤醒正在等待对象监视器的单个线程 void notifyAll() 唤醒正在等待对象监视器的所有线程
-
-
阻塞队列实现等待唤醒机制
-
put(anObject): 将参数放入队列,如果放不进去会阻塞;take(): 取出第一个数据,取不到会阻塞
-
阻塞队列继承结构图参考集合体系
-
常见BlockingQueue:
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
-
-
-
线程池
- 创建线程池
- 自带线程池
- ExecutorService threadPool= Executors.newCachedThreadPool(); 创建一个默认的线程池
- ExecutorService threadPool= Executors.newFixedThreadPool(10); 创建一个指定最多线程数量的线程池
- 自定义线程池=》一般使用自带的线程池
- ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
- 自带线程池
- 提交线程池:
threadPool.submit(new MyRunnable());
- 销毁线程池:
threadPool.shutdown();
=》 服务器24小时运行,一般不销毁
- 创建线程池