![](https://img-blog.csdnimg.cn/img_convert/ed8a6503611be3be73788c64607c0ae8.png)
-
JVM(Java Virtual Machine):Java虚拟机, 真正运行Java程序的地方。
-
核心类库:Java自己写好的程序,给程序员自己的程序调用的。
-
JRE(Java Runtime Environment): Java的运行环境。
-
JDK(Java Development Kit): Java开发工具包(包括上面所有)。
项目顺序
![](https://img-blog.csdnimg.cn/img_convert/f6ff64f08def965f441bfb258881dae5.png)
New project/module/package/class
快捷键 | 功能效果 |
main/psvm、sout、… | 快速键入相关代码 |
Ctrl + D | 复制当前行数据到下一行 |
Ctrl + Y | 删除所在行,建议用Ctrl + X |
Ctrl + ALT + L | 格式化代码 |
ALT + SHIFT + ↑ , ALT + SHIFT + ↓ | 上下移动当前代码 |
Ctrl + / , Ctrl + Shift + / | 对代码进行注释(讲注释的时候再说) |
常用数据 | 程序中的写法 | 说明 |
整数 | 666,-88 | 写法一致 |
小数 | 13.14,-5.21 | 写法一致 |
字符 | ‘A’,‘0’, ‘我’ | 程序中必须使用单引号,有且仅能一个字符 |
字符串 | “HelloWorld” | 程序中必须使用双引号,内容可有可无 |
布尔值 | true 、false | 只有两个值:true:代表真,false:代表假 |
空值 | 值是:null | 一个特殊的值,空值(后面会讲解作用,暂时不管) |
变量的格式
数据类型 变量名称 = 初始值;
3、变量的基本特点
变量中只能存一个值
变量中存的值是可以替换的
变量要先声明再使用
变量声明后,不能存储其他类型的数据。
变量的有效范围是从定义开始到“}”截止,且在同一个范围内部不能定义2个同名的变量。
变量定义的时候可以没有初始值,但是使用的时候必须给初始值。
标识符的要求
基本要求:由数字、字母、下划线(_)和美元符($)等组成
强制要求:不能以数字开头、不能是关键字、区分大小写
命名指导规范
变量名称:满足标识符规则,建议全英文、有意义、首字母小写,满足“驼峰模式”,例如:int studyNumber = 59。
类名称: 满足标识符规则,建议全英文、有意义、首字母大写,满足“驼峰模式”,例如:HelloWorld.java。
![](https://img-blog.csdnimg.cn/img_convert/9b264cd547e5a6b23895fc9241e5bd71.png)
计算机中数据最小的组成单元:使用8个二进制位为一组,称之为一个字节(byte,简称B)
字节中的每个二进制位就称为 位(bit,简称b), 1B = 8b
1B = 8b
1KB = 1024B1MB = 1024KB
1GB = 1024MB1TB = 1024GB
数据类型就是约束变量存储数据的形式。
数据类型 变量名称 = 初始值;
数据类型 | 关键字 | 取值范围 | 内存占用(字节数) |
整数 | byte | -128~127 | 1 |
short | -32768~32767 | 2 | |
int(默认) | -2147483648~2147483647 (10位数) | 4 | |
long | -9223372036854775808 ~ 9223372036854775807 (19位数) | 8 | |
浮点数 | float | 1.401298e-45到3.402823e+38 | 4 |
double(默认) | 4.9000000e-324 到1.797693e+308 | 8 | |
字符 | char | 0-65535 | 2 |
布尔 | boolean | true,false | 1 |
23 ,默认是int类型 , 加上L/l就是long类型的数据了。
23.8,默认是double类型,加上F/f就是float类型了。
类型范围小的变量,可以直接赋值给类型范围大的变量
![](https://img-blog.csdnimg.cn/img_convert/722bd14e50822e12a974ac80d50b9b91.png)
注意事项:
表达式的最终结果类型由表达式中的最高类型决定。
在表达式中,byte、short、char 是直接转换成int类型参与运算的。
类型范围大的数据或者变量,不能直接赋值给类型范围小的变量,会报错。
可以强行将类型范围大的变量、数据赋值给类型范围小的变量。
数据类型 变量2 = (数据类型)变量1、数据
注意事项
强制类型转换可能造成数据(丢失)溢出;
浮点型强转成整型,直接丢掉小数部分,保留整数部分返回
![](https://img-blog.csdnimg.cn/img_convert/6a1b018db07e10c5ece9b8ba8cba0889.png)
运算符
符号 | 作用 | 说明 |
+ | 加 | 参考小学一年级 |
- | 减 | 参考小学一年级 |
* | 乘 | 参考小学二年级,与“×”相同 |
/ | 除 | 与“÷”相同,注意:在Java中两个整数相除结果还是整数。 |
% | 取余 | 获取的是两个数据做除法的余数 |
如果两个整数做除法,其结果一定是整数,因为最高类型是整数。
“+”符号与字符串运算的时候是用作连接符的,其结果依然是一个字符串
自增自减运算符
注意:
++ 和 -- 既可以放在变量的后边,也可以放在变量的前边。
++ 、-- 只能操作变量,不能操作字面量的。
符号 | 作用 | 说明 |
++ | 自增 | 变量自身的值加1 |
-- | 自减 | 变量自身的值减1 |
自增自减的使用注意事项
++、--如果不是单独使用(如在表达式中、或者同时有其它操作),放在变量前后会存在明显区别
放在变量的前面,先对变量进行+1、-1,再拿变量的值进行运算。
放在变量的后面,先拿变量的值进行运算,再对变量的值进行+1、-1
只能操作变量,不能操作字面量
如果单独使用放前后是没有区别的。
非单独使用:在变量前 ,先进行变量自增/自减,再使用变量。
非单独使用:在变量后 ,先使用变量,再进行变量自增/自减
扩展赋值运算符
符号 | 作用 | 说明 |
+= | 加后赋值 | a+=b 等价于 a = (a的数据类型)(a+b); 将a + b的值给a |
-= | 减后赋值 | a-=b 等价于 a = (a的数据类型)(a-b); 将a - b的值给a |
*= | 乘后赋值 | a*=b 等价于 a = (a的数据类型)(a*b); 将a * b的值给a |
/= | 除后赋值 | a/=b 等价于 a = (a的数据类型)(a/b); 将a / b的商给a |
%= | 取余后赋值 | a%=b 等价于 a = (a的数据类型)(a%b); 将a % b的商给a |
注意:扩展的赋值运算符隐含了强制类型转换。
关系运算符
符号 | 说明 |
== | a==b,判断a和b的值是否相等,成立为true,不成立为false |
!= | a!=b,判断a和b的值是否不相等,成立为true,不成立为false |
> | a>b, 判断a是否大于b,成立为true,不成立为false |
>= | a>=b,判断a是否大于等于b,成立为true,不成立为false |
< | a<b, 判断a是否小于b,成立为true,不成立为false |
<= | a<=b,判断a是否小于等于b,成立为true,不成立为false |
逻辑运算符概述
符号 | 介绍 | 说明 |
& | 逻辑与 | 必须都是true,结果才是true; 只要有一个是false,结果一定是false。 |
| | 逻辑或 | 只要有一个为true、结果就是true |
! | 逻辑非 | 你真我假、你假我真。 !true=false 、 !false= true |
^ | 逻辑异或 | 如果两个条件都是false或者都是true则结果是false。两个条件不同结果是true。 |
短路逻辑运算符
符号 | 介绍 | 说明 |
&& | 短路与 | 判断结果与“&”一样。过程是左边为 false,右边则不执行。 |
|| | 短路或 | 判断结果与“|”一样。过程是左边为 true, 右边则不执行。 |
格式:条件表达式 ? 值1 : 值2;
运算符优先级
![](https://img-blog.csdnimg.cn/img_convert/8c7939b774027f71ff4a3977b49c06fc.png)
If分支
根据判定的结果(真或假)决定执行某个分支的代码
格式1:
if (条件表达式) {
语句体;
}
格式2:
if (条件表达式) {
语句体1;
} else {
语句体2;
}
格式3:
if (条件表达式1) {
语句体1;
} else if (条件表达式2) {
语句体2;
} else if (条件表达式3) {
语句体3;
}
. . .
else {
语句体n+1;
}
首先判断条件表达式的结果,如果为true执行语句体,为 false 就不执行语句体。
![](https://img-blog.csdnimg.cn/img_convert/e8e2ac5959b64679e7067080de0744cf.png)
首先判断条件表达式的结果,如果为true执行语句体1,为 false 就执行语句体2。
![](https://img-blog.csdnimg.cn/img_convert/8d7dced9ba0d0f41e503b448ff2426e4.png)
执行流程:
先判断条件1的值,如果为true则执行语句体1,分支结束;如果为false则判断条件2的值
如果值为true就执行语句体2,分支结束;如果为false则判断条件3的值
...
如果没有任何条件为true,就执行else分支的语句体n+1。
![](https://img-blog.csdnimg.cn/img_convert/785d08a8415bb08e3cfefbb6257134db.png)
switch分支
也是匹配条件去执行分支, 适合做值匹配的分支选择,结构清晰,格式良好
switch(表达式){
case 值1: 执行代码...;
break;
case 值2: 执行代码...;
break;
…
case 值n-1: 执行代码...;
break;
default:
执行代码n;}
执行流程:
先执行表达式的值,拿着这个值去与case后的值进行匹配。
匹配哪个case的值为true就执行哪个case,遇到break就跳出switch分支。
如果case后的值都不匹配则执行default代码。
if其实在功能上远远强大于switch。
if适合做区间匹配。
switch适合做:值匹配的分支选择、代码优雅。
switch分支注意事项
表达式类型只能是byte、short、int、char,JDK5开始支持枚举,JDK7开始支持String、不支持double、float、long。
case给出的值不允许重复,且只能是字面量,不能是变量。
不要忘记写break,否则会出现穿透现象。(程序继续执行判断)
如果代码执行到没有写break的case块,执行完后将直接进入下一个case块执行代码(而且不会进行任何匹配),直到遇到break才跳出分支,这就是switch的穿透性。
for 循环
控制一段代码反复执行很多次
格式:
for (初始化语句; 循环条件; 迭代语句) {循环体语句(重复执行的代码);
}
while 循环格式与执行流程
初始化语句;
while (循环条件) { 循环体语句(被重复执行的代码);
迭代语句;
}
![](https://img-blog.csdnimg.cn/img_convert/5dd8a926b3ae4bf0376c810c2e152efe.png)
功能上是完全一样的,for能解决的while也能解决,反之亦然。
使用规范是:知道循环几次:使用for;不知道循环几次建议使用:while
do-while循环
先执行再判断循环条件。
初始化语句;
do {
循环体语句;
迭代语句;
} while (循环条件)
![](https://img-blog.csdnimg.cn/img_convert/2d85562848c920f28d43e703eaf0a549.png)
跳转控制语句介绍
break : 跳出并结束当前所在循环的执行。
continue: 用于跳出当前循环的当次执行,进入下一次循环。
break : 只能用于结束所在循环, 或者结束所在switch分支的执行。
continue : 只能在循环中进行使用。
数组
数组就是用来存储一批同种类型数据的容器。
静态初始化数组
// 完整格式数据类型[] 数组名 = new 数据类型[]{元素1,元素2 ,元素3… };
// 简化格式
数据类型[] 数组名 = { 元素1,元素2 ,元素3,… };
![](https://img-blog.csdnimg.cn/img_convert/3fc6f9587114928cb7b518d79fdf70b5.png)
注意:数组变量名中存储的是数组在内存中的地址,数组是引用类型。数组中存储的元素并不是对象本身,而是对象的地址。
数组的访问
数组名称[索引]
数组的长度属性:数组名称.length
数组的最大索引 数组名. length – 1 // 前提:元素个数大于0
数组的几个注意事项:
数据类型[] 数组名”也可以写成数据类型 数组名[]数据类型[] 数组名也可以写成数据类型 数组名[]
什么类型的数组存放什么类型的数据,否则报错。
数组一旦定义出来,程序执行的过程中,长度、类型就固定了。
数组的动态初始化
定义数组的时候只确定元素的类型和数组的长度,之后再存入具体数据。
数据类型[] 数组名 = new 数据类型[长度];
(堆内存,后赋值)
数据类型 | 明细 | 默认值 |
基本类型 | byte、short、char、int、long | 0 |
float、double | 0.0 | |
boolean | false | |
引用类型 | 类、接口、数组、String | null |
两种初始化的的使用场景总结、注意事项说明:
动态初始化:只指定数组长度,后期赋值,适合开始知道数据的数量,但是不确定具体元素值的业务场景。
静态初始化:开始就存入元素值,适合一开始就能确定元素值的业务场景。
两种格式的写法是独立的,不可以混用。
数组遍历
一个一个数据的访问
int[] ages = {20, 30, 40, 50};
for (int i = 0(定义变量); i < ages.length(制定条件); i++(判断操作)) {
System.out.println(ages[i]);
}
数组使用常见问题
问题1:如果访问的元素位置超过最大索引,执行时会出现ArrayIndexOutOfBoundsException(数组索引越界异常)
问题2:如果数组变量中没有存储数组的地址,而是null, 在访问数组信息时会出现NullPointerException(空指针异常)
Java 内存分配介绍
栈
堆
方法区
本地方法栈
寄存器
![](https://img-blog.csdnimg.cn/img_convert/34756fed34101868202816e7e44ee658.png)
方法
方法是一种语法结构,它可以把一段代码封装成一个功能,以方便重复调用。
修饰符 返回值类型
方法名( 形参列表 ){
方法体代码(需要执行的功能代码)
return 返回值;
}
方法的修饰符:暂时都使用public static 修饰。
方法申明了具体的返回值类型,内部必须使用return返回对应类型的数据。
形参列表可以有多个,甚至可以没有; 如果有多个形参,多个形参必须用“,”隔开,且不能给初始化值。
如果方法不需要返回结果,返回值类型必须申明成void(无返回值), 此时方法内部不可以使用return返回数据。
方法如果没有参数,或者返回值类型申明为void可以称为无参数、无返回值的方法,依次类推。
有返回值的方法调用时可以选择定义变量接收结果,或者直接输出调用,甚至直接调用;无返回值方法的调用只能直接调用。
方法的调用流程 – 内存图解
栈内存
![](https://img-blog.csdnimg.cn/img_convert/5f5b4d4d5f68f1af6fe66e1ed7594a2f.png)
Java的参数传递机制:值传递
在传输实参给方法的形参的时候,并不是传输实参变量本身, 而是传输实参变量中存储的值,这就是值传递。
基本类型的参数传输存储的数据值。
引用类型的参数传输存储的地址值(数组)
方法重载
同一个类中,出现多个方法名称相同,但是形参列表是不同的,那么这些方法就是重载方法。
调用方法的时候,
会通过参数的不同来区分调用的是哪个方法
形参列表不同指的是:形参的个数、类型、顺序不同,不关心形参的名称。
结束当前方法的执行
return; 跳出并立即结束所在方法的执行。
break; 跳出并结束当前所在循环的执行。
continue; 结束当前所在循环的当次继续,进入下一次执行。
面向对象
![](https://img-blog.csdnimg.cn/img_convert/895621fa55f121538e0bbac9aead7501.png)
类(设计图) :是对象共同特征的描述;
对象:是真实存在的具体实例。
结论:在Java中,必须先设计类,才能创建对象并使用。
类名 对象名 = new 类名();
访问属性: 对象名.成员变量
访问行为: 对象名.方法名
public class 类名 {
1、成员变量(代表属性,一般是名词)
2、成员方法(代表行为,一般是动词)
3、构造器 (后几节再说)
4、代码块 (后面再学)
5、内部类 (后面再学)
}
定义类的补充注意事项
类名首字母建议大写,且有意义,满足“驼峰模式”。
一个Java文件中可以定义多个class类,但只能一个类是public修饰,而且public修饰的类名必须成为代码文件名。
实际开发中建议还是一个文件定义一个class类。
成员变量的完整定义格式是:修饰符 数据类型 变量名称 = 初始化值; 一般无需指定初始化值,存在默认值。
成员变量的默认值规则
数据类型 | 明细 | 默认值 |
基本类型 | byte、short、char、int、long | 0 |
float、double | 0.0 | |
boolean | false | |
引用类型 | 类、接口、数组、String | null |
1. 对象到底是放在堆内存中
2. Car c = new Car(); 变量名中存储的是对象在堆内存中的地址。
3. 成员变量数据放在对象中,存在于堆内存中。
![](https://img-blog.csdnimg.cn/img_convert/edb228b4029808c9ef800d036635330a.png)
两个变量指向同一个对象内存图
![](https://img-blog.csdnimg.cn/img_convert/ba27d8190757f07c504a9a71744b01a4.png)
两个变量指定同一个对象时,内存地址相同,一个改了跟着改。
垃圾回收
注意:当堆内存中的对象,没有被任何变量引用(指向)时,就会被判定为内存中的“垃圾”。
public class修饰符 类名(形参列表) {
... // 无参数构造器
public Car(){... }
// 有参数构造器
public Car(String n, double p){
... }
构造器的作用
定义在类中的,可以用于初始化一个类的对象,并返回对象的地址。
构造器的分类和作用
无参数构造器(默认存在的):初始化对象时,成员变量的数据均采用默认值。
有参数构造器:在初始化对象的时候,同时可以接收参数为对象进行赋值。
构造器的注意事项
任何类定义出来,默认就自带了无参数构造器,写不写都有。
一旦定义了有参数构造器,那么无参数构造器就没有了,如果还想用无参数构造器,此时就需要自己手写一个无参数构造器了
this关键字
可以出现在构造器、方法中
代表当前对象的地址。
可以用于指定访问当前对象的成员变量、成员方法。
public class Car { public Car() { System.out.println("this在构造器中:" + this); } public void run() { System.out.println("this在方法中:" + this); }}
面向对象的三大特征:封装,继承,多态。
原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
(物体提供相对应的行为)
一般建议对成员变量使用private(私有、隐藏)关键字修饰进(private修饰的成员只能在当前类中访问)。
为每个成员变量提供配套public修饰的的getter、setter方法暴露其取值和赋值。
JavaBean
成员变量使用 private 修饰。
提供成员变量对应的 setXxx() / getXxx()方法。
必须提供一个无参构造器;有参数构造器是可写可不写的。
成员变量和局部变量的区别
区别 | 成员变量 | 局部变量 |
类中位置不同 | 类中,方法外 | 常见于方法中 |
初始化值不同 | 有默认值,无需初始化 | 没有默认值,使用之前需要完成赋值 |
内存位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的创建而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的运行结束而消失 |
作用域 | 在所归属的大括号中 |
API (Application Programming Interface,应用程序编程接口)
Java写好的程序
String类
定义的变量可以用于存储字符串,同时String类提供了很多操作字符串的功能
java.lang.String 类代表字符串,String类定义的变量可以用于指向字符串对象,然后操作该字符串。
Java 程序中的所有字符串文字(例如“abc”)都为此类的对象。
String其实常被称为不可变字符串类型,它的对象在创建后不能被更改。
以“”方式给出的字符串对象,在字符串常量池中存储。
![](https://img-blog.csdnimg.cn/img_convert/ccde266ddbacad79b8ca2c22e0a7d939.png)
String变量每次的修改其实都是产生并指向了新的字符串对象。
原来的字符串对象都是没有改变的,所以称不可变字符串。
字符串的内容比较
结论:字符串的内容比较不适合用“==”比较,基本数据类型比较时使用,等号是用来比较变量地址是否一致
方法名 | 说明 |
public boolean equals (Object anObject) | 将此字符串与指定对象进行比较。只关心字符内容是否一致! |
public boolean equalsIgnoreCase (String anotherString) | 将此字符串与指定对象进行比较,忽略大小写比较字符串。只关心字符内容是否一致! |
String常用API
方法名 | 说明 |
public int length() | 返回此字符串的长度 |
public char charAt(int index) | 获取某个索引位置处的字符 |
public char[] toCharArray(): | 将当前字符串转换成字符数组返回 |
public String substring(int beginIndex, int endIndex) | 根据开始和结束索引进行截取,得到新的字符串(包前不包后) |
public String substring(int beginIndex) | 从传入的索引处截取,截取到末尾,得到新的字符串 |
public String replace(CharSequence target, CharSequence replacement) | 使用新值,将字符串中的旧值替换,得到新的字符串 |
public String[] split(String regex) | 根据传入的规则切割字符串,得到字符串数组返回 |
创建字符串对象的2种方式
方式一:直接使用“”定义。(推荐方式)
String name = "传智教育";
方式二:通过String类的构造器创建对象
构造器 | 说明 |
public String() | 创建一个空白字符串对象,不含有任何内容 |
public String(String original) | 根据传入的字符串内容,来创建字符串对象 |
public String(char[] chs) | 根据字符数组的内容,来创建字符串对象 |
public String(byte[] chs) | 根据字节数组的内容,来创建字符串对象 |
注意:
以“”方式给出的字符串对象,在字符串常量池中存储,而且相同内容只会在其中存储一份。
通过构造器new对象,每new一次都会产生一个新对象,放在堆内存中。
![](https://img-blog.csdnimg.cn/img_convert/1810cc49148969d523183bb081a9fd00.png)
![](https://img-blog.csdnimg.cn/img_convert/c6000011f6cc9e2bf55ea5796f66e837.png)
Java存在编译优化机制,程序在编译时: “a” + “b” + “c” 会直接转成 "abc"
所以除非后加不然都是有一样的
ArrayList代表的是集合类,集合是一种容器,与数组类似,不同的是集合的大小是不固定的。
通过创建ArrayList的对象表示得到一个集合容器,同时ArrayList提供了比数组更好用,更丰富的API (功能)给程序员
集合
ArrayList是集合中的一种,它支持索引。 (暂时先学习这个,后期课程会学习整个集合体系)
与数组类似,也是一种容器,用于装数据的
集合还提供了许多丰富、好用的功能,而数组的功能很单一。
集合非常适合做元素个数不确定,且要进行增删操作的业务场景。数组定义完成并启动后,类型确定、长度固定。
集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。
构造器 | 说明 |
public ArrayList() | 创建一个空的集合对象 |
ArrayList集合的添加元素的方法
方法名 | 说明 |
public boolean add(E e) | 将指定的元素追加到此集合的末尾 |
public void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
泛型
ArrayList<E>:其实就是一个泛型类,可以在编译阶段约束集合对象只能操作某种数据类型
注意:泛型只能支持引用数据类型,不支持基本数据类型。
ArrayList集合常用方法
方法名称 | 说明 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public boolean remove(Object o) | 删除指定的元素,返回删除是否成功 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
static
静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在 静态存储区内分配存储单元),该区域中的数据在整个程序的运行期间一直占用这些存储空间(在程序整个运行期间都不释放),也可以认为是其内存地址不变,直 到整个程序运行结束(相反,而auto自动变量,即动态局部变量,属于动态存储类别,占动态存储空间,函数调用结束后即释放)。静态变量虽在程序的整个执 行过程中始终存在,但是在它作用域之外不能使用。
static是静态的意思,可以用来修饰成员变量、成员方法。
static修饰成员变量之后称为静态成员变量(类变量),修饰方法之后称为静态方法(类方法)。
static修饰后的成员变量,可以被类的所有对象共享(访问、修改)。
静态成员变量(有static修饰,属于类、加载一次,内存中只有一份),建议用类名访问,也可以用对象访问
访问格式:
类名.静态成员变量(推荐)
对象.静态成员变量(不推荐)。
实例成员变量(无static修饰,属于对象),只能用对象触发访问
访问格式:
对象.实例成员变量
静态成员变量:表示在线人数等需要被类的所有对象共享的信息时。
实例成员变量:属于每个对象,且每个对象的该信息不同时(如:name,age,money…)
![](https://img-blog.csdnimg.cn/img_convert/04b76e8858f309a4f574bcc64d86e37f.png)
成员方法的使用场景
表示对象自己的行为的,且方法中需要直接访问实例成员,则该方法必须申明成实例方法。
如果该方法是以执行一个通用功能为目的,或者需要方便访问,则可以申明成静态方法
static访问注意事项:
静态方法只能访问静态的成员,不可以直接访问实例成员。
实例方法可以访问静态的成员,也可以访问实例成员。
静态方法中是不可以出现this关键字的。 super同理
工具类定义时的其他要求:
由于工具里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有。
代码块概述
代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。
在Java类下,使用 { } 括起来的代码被称为代码块 。
代码块分为
静态代码块:
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
构造代码块(了解,见的少):
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
使用场景:初始化实例资源。
静态代码块的作用
如果要在启动系统时对静态资源进行初始化,则建议使用静态代码块完成数据的初始化操作。
设计模式(Design pattern)
单例模式
可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。
在用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
定义一个类,把构造器私有。
定义一个静态变量存储一个对象。
![](https://img-blog.csdnimg.cn/img_convert/8ae40441f1cca206fb028394ec2330ab.png)
懒汉单例设计模式
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
定义一个类,把构造器私有。
定义一个静态变量存储一个对象。
提供一个返回单例对象的方法
![](https://img-blog.csdnimg.cn/img_convert/ce9c49764d3375b1096a6db5aabbceca.png)
继承
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
public class Student extends People {}
Student称为子类(派生类),People称为父类(基类 或超类)。
作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了
子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的的属性和行为应该定义在子类自己里面
![](https://img-blog.csdnimg.cn/img_convert/0d35a8b613f9f6d729d3cc2ea2c72791.png)
继承的特点
子类可以继承父类的属性和行为,但是子类不能继承父类的构造器,父类的构造器只能够被子类调用,
不能直接访问父类的私有属性,子类不能继承父类的静态成员。(共享并非继承)。
Java是单继承模式:一个类只能继承一个直接父类。
Java不支持多继承、但是支持多层继承,Java中所有类,要么直接继承了Object , 要么默认继承了Object , 要么间接继承了Object, Object是祖宗类。
![](https://img-blog.csdnimg.cn/img_convert/9cdbae4f3a43a42912ca588054b3a7bf.png)
Java中所有的类都是Object类的子类。
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
先子类局部范围找
然后子类成员范围找
然后父类成员范围找,如果父类范围还没有找到则报错。
如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
可以通过super关键字,指定访问父类的成员。
格式:super.父类成员变量/父类成员方法
方法重写的应用场景
当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
子类可以重写父类中的方法。
@Override重写注解
@Override是放在重写后的方法上,作为重写是否正确的校验注解。
加上该注解后如果重写错误,编译阶段会出现错误提示。
建议重写方法都加@Override注解,代码安全,优雅!
方法重写注意事项和要求
重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
私有方法不能被重写。
子类重写父类方法时,访问权限必须大于或者等于父类 (暂时了解 :缺省 < protected < public)
子类不能重写父类的静态方法,如果重写会报错的。
子类继承父类后构造器的特点:
子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。
原因:子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
注意:子类构造器的第一行语句默认都是:super(),不写也存在。
如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?
会报错。因为子类默认是调用父类无参构造器的。
super调用父类有参数构造器的作用:
初始化继承自父类的数据
this和super详情
this:代表本类对象的引用;super:代表父类存储空间的标识。
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构器 |
super | super.成员变量 访问父类成员变量 | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造器 |
this(...)和super(…)使用注意点:
子类通过 this (...)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。
注意:this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。
包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护
语法格式:package 公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
package com.itheima.javabean;
public class Student {
}
导包
相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:import 包名.类名;
假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
权限修饰符
权限修饰符:是用来控制一个成员能够被访问的范围。
可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
权限修饰符的分类和具体作用范围:
权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected - > public )
修饰符 | 同一 个类中 | 同一个包中 其他类 | 不同包下的 子类 | 不同包下的 无关类 |
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
要求:
成员变量一般私有。
方法一般公开。
如果该成员只希望本类访问,使用private修饰。
如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。
final的作用
final 关键字是最终的意思,可以修饰(类、方法、变量)
修饰类:表明该类是最终类,不能被继承。
修饰方法:表明该方法是最终方法,不能被重写。
修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
final修饰变量的注意
final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。
常量
public static final修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。
常量名的命名规范:英文单词全部大写,多个单词下划线连接起来。
常量的作用:通常用来记录系统的配置数据。
public class Constant {
public static final String SCHOOL_NAME = “传智教育";
public static final String LOGIN_NAME = “admin";
public static final String PASS_WORD = “123456";
}
在编译阶段会进行“宏替换”:把使用常量的地方全部替换成真实的字面量。
维护系统容易,可读性更好。
枚举的概述
枚举是Java中的一种特殊类型
枚举的作用:"是为了做信息的标志和信息的分类"
定义枚举类的格式:
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称。
}
enum Season{ SPRING , SUMMER , AUTUMN , WINTER;}
Compiled from "Season.java"public final class Season extends java.lang.Enum<Season> { public static final Season SPRING = new Season(); public static final Season SUMMER = new Season(); public static final Season AUTUMN = new Season(); public static final Season WINTER = new Season(); public static Season[] values(); public static Season valueOf(java.lang.String);}
枚举类都是继承了枚举类型:java.lang.Enum
枚举都是最终类,不可以被继承。
构造器都是私有的,枚举对外不能创建对象。
枚举类的第一行默认都是罗列枚举对象的名称的。
枚举类相当于是多例模式。
抽象类
可以修饰类、成员方法
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表);
注意事项
抽象方法只有方法签名,不能声明方法体。
一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错。
一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
使用场景
抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。
当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
特征和注意事项
类有的成员(成员变量、方法、构造器)抽象类都具备
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
不能用abstract修饰变量、代码块、构造器。
最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)
final和abstract是什么关系?
互斥关系
abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
多态
常见形式
父类类型 对象名称 = new 子类构造器;
多态中成员访问特点
方法调用:编译看左边,运行看右边。
变量调用:编译看左边,运行也看左边。(注意)
.多态中成员的特点
多态成员变量:编译运行看左边
Fu f=new Zi();
System.out.println(f.num);//f是Fu中的值,只能取到父中的值
2.多态成员方法:编译看左边,运行看右边
Fu f1=new Zi();
System.out.println(f1.show());//f1的门面类型是Fu,但实际类型是Zi,所以调用的是重写后的方法。
多态的前提
有继承/实现关系;有父类引用指向子类对象;有方法重写(多态侧重行为多态)。
优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
![](https://img-blog.csdnimg.cn/img_convert/b08382b2936722fe48059e026af6755f.png)
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
多态下会产生的一个问题:
多态下不能使用子类的独有功能
创建的是子类对象,但实际上我们想要的是父类
自动类型转换(从子到父):
强制类型转换(从父到子)
从父到子( 必须进行强制类型转换,否则报错): 子类 对象变量 = (子类)父类类型的变量
作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
注意: 有继承/实现关系的类就可以在编译阶段进行强制类型转换;但是,如果转型后的类型和对象真实对象的类型不是同一种类型,那么在运行代码时,就会出现ClassCastException
![](https://img-blog.csdnimg.cn/img_convert/5f5f2ff9970767741ab2298d46621a82.png)
Java建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换
接口
接口用关键字interface来定义
public interface 接口名 {
// 常量
// 抽象方法
}
接口中的成员都是public修饰的,写不写都是,因为规范的目的是为了公开化。
接口的用法:
接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {
}
实现的关键字:implements
从上面可以看出,接口可以被类单实现,也可以被类多实现。
接口实现的注意事项:
一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
基本小结
类和类的关系:单继承。
类和接口的关系:多实现。
接口和接口的关系:多继承,一个接口可以同时继承多个接口
默认方法:default修饰,实现类对象调用。
静态方法:static修饰,必须用当前接口名调用
私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
他们都会默认被public修饰。
接口的注意事项
1、接口不能创建对象
2、一个类实现多个接口,多个接口的规范不能冲突
2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。
3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
内部类
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)
public class People{ // 内部类 public class Heart{ }}
内部类的使用场景
场景:当一个事物的内部,还有一个部分需要一个完整的结构进行描述时。
基本作用
内部类通常可以方便访问外部类的成员,包括私有的成员。
内部类提供了更好的封装性,内部类本身就可以用private ,protectecd等修饰,封装性可以做更多控制
静态内部类?
有static修饰,属于外部类本身。
它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已。
public class Outer{
// 静态成员内部类 public static class Inner{ }}
静态内部类创建对象的格式:
格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器;范例:Outer.Inner in = new Outer.Inner();
静态内部类中是否可以直接访问外部类的静态成员?
可以,外部类的静态成员只有一份可以被共享访问。
静态内部类中是否可以直接访问外部类的实例成员?
不可以的,外部类的实例成员必须用外部类对象访问。
使用场景、特点、访问总结。
如果一个类中包含了一个完整的成分,如汽车类中的发动机类。
特点、使用与普通类是一样的,类有的成分它都有,只是位置在别人里面而已。
访问总结:可以直接访问外部类的静态成员,不能直接访问外部类的实例成员。
注意:开发中实际上用的还是比较少。
成员内部类?
无static修饰,属于外部类的对象。
JDK16之前,成员内部类中不能定义静态成员,JDK 16开始也可以定义静态成员了。
public class Outer { // 成员内部类 public class Inner {
}}
成员内部类创建对象的格式:
格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();范例:Outer.Inner in = new Outer().new Inner();
1、成员内部类中是否可以直接访问外部类的静态成员?
可以,外部类的静态成员只有一份可以被共享访问。
2、成员内部类的实例方法中是否可以直接访问外部类的实例成员?
可以的,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员
成员内部类是什么样的、有什么特点?
无static修饰,属于外部类的对象。
可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员。
成员内部类如何创建对象?
外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
匿名内部类:
本质上是一个没有名字的局部内部类。
作用:方便创建子类对象,最终目的是为了简化代码编写。
格式:
new 类|抽象类名|或者接口名() { 重写方法;};
Employee a = new Employee() { public void work() { }};a. work();
特点总结:
匿名内部类是一个没有名字的内部类,同时也代表一个对象。
匿名内部类产生的对象类型,相当于是当前new的那个的类型的子类类型。
匿名内部类的特点?
匿名内部类是一个没有名字的内部类,同时也代表一个对象。
匿名内部类的对象类型,相当于是当前new的那个类型的子类类型。
匿名内部类可以作为一个对象,直接传输给方法。
Object类的作用:
一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类。
Object作为所有类的父类,提供了很多常用的方法给每个子类对象拿来使用。
Object类的常用方法:
方法名 | 说明 |
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址 |
public boolean equals(Object o) | 默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false |
Object的toString方法:
方法名 | 说明 |
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址 |
toString存在的意义
父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息!!
Object的equals方法
方法名 | 说明 |
public boolean equals(Object o) | 默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false |
equals存在的意义
为了被子类重写,以便子类自己来定制比较规则(比如比较对象内容)。
Objects的常见方法:
方法名 | 说明 |
public static boolean equals(Object a, Object b) | 比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常。再进行equals比较 |
public static boolean isNull(Object obj) | 判断变量是否为null ,为null返回true ,反之 |
StringBuilder
StringBuilder是一个可变的字符串的操作类,我们可以把它看成是一个对象容器。
使用StringBuilder的核心作用:操作字符串的性能比String要更高(如拼接、修改等)。
![](https://img-blog.csdnimg.cn/img_convert/296c10d60868f4490297e26ecafadc2f.png)
![](https://img-blog.csdnimg.cn/img_convert/f255f3cda6ce885ae9717000351759c9.png)
StringBuilder 构造器
名称 | 说明 |
public StringBuilder() | 创建一个空白的可变的字符串对象,不包含任何内容 |
public StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
StringBuilder常用方法
方法名称 | 说明 |
public StringBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
public int length() | 返回对象内容长度 |
public String toString() | 通过toString()就可以实现把StringBuilder转换为String |
拼接、反转字符串建议使用StringBuilder?
StringBuilder:内容是可变的、拼接字符串性能好、代码优雅。
String :内容是不可变的、拼接字符串性能差。
定义字符串使用String
拼接、修改等操作字符串使用StringBuilder
Date 类概述
Date类代表当前所在系统的日期时间信息。
名称 | 说明 |
public Date() | 创建一个Date对象,代表的是系统当前此刻日期时间。 |
名称 | 说明 |
public long getTime() | 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数 |
时间毫秒值 -> 日期对象
构造器 | 说明 |
public Date(long time) | 把时间毫秒值转换成Date日期对象。 |
Date方法 | 说明 |
public void setTime(long time) | 设置日期对象的时间为当前时间毫秒值对应的时间 |
SimpleDateFormat
构造
构造器 | 说明 |
public SimpleDateFormat(String pattern) | 创建简单日期格式化对象,并封装格式化的形式信息 |
格式化方法
格式化方法 | 说明 |
public final String format(Date date) | 将日期格式化成日期/时间字符串 |
public final String format(Object time) | 将时间毫秒值式化成日期/时间字符串 |
SimpleDateFormat解析字符串时间成为日期对象
解析方法 | 说明 |
public Date parse(String source) | 从给定字符串的开始解析文本以生成日期 |
Calendar概述
方法名 | 说明 |
public static Calendar getInstance() | 获取当前日历对象 |
Calendar常用方法
方法名 | 说明 |
public int get(int field) | 取日期中的某个字段信息。 |
public void set(int field,int value) | 修改日历的某个字段信息。 |
public void add(int field,int amount) | 为某个字段增加/减少指定的值 |
public final Date getTime() | 拿到此刻日期对象。 |
public long getTimeInMillis() | 拿到此刻时间毫秒值 |
注意:calendar是可变日期对象,一旦修改后其对象本身表示的时间将产生变化。
LocalDate、LocalTime、LocalDateTime
他们 分别表示日期,时间,日期时间对象,他们的类的实例是不可变的对象。
他们三者构建对象和API都是通用的
方法名 | 说明 | |
public static Xxxx now(); | 静态方法,根据当前时间创建对象 | LocaDate localDate = LocalDate.now();LocalTime llocalTime = LocalTime.now();LocalDateTime localDateTime = LocalDateTime.now(); |
public static Xxxx of(…); | 静态方法,指定日期/时间创建对象 | LocalDate localDate1 = LocalDate.of(2099 , 11,11);LocalTime localTime1 = LocalTime.of(11, 11, 11);LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43); |
LocalDate、LocalTime、LocalDateTime获取信息的API.
方法名 | 说明 |
public int geYear() | 获取年 |
public int getMonthValue() | 获取月份(1-12) |
Public int getDayOfMonth() | 获取月中第几天乘法 |
Public int getDayOfYear() | 获取年中第几天 |
Public DayOfWeek getDayOfWeek() | 获取星期 |
转换相关的API
![](https://img-blog.csdnimg.cn/img_convert/be717e45e248318892c122f02df8d474.png)
LocalDateTime的转换API
方法名 | 说明 |
public LocalDate toLocalDate() | 转换成一个LocalDate对象 |
public LocalTime toLocalTime() | 转换成一个LocalTime对象 |
LocalDateTime 综合了 LocalDate 和 LocalTime 里面的方法,所以下面只用 LocalDate 和 LocalTime 来举例。
这些方法返回的是一个新的实例引用,因为LocalDateTime 、LocalDate 、LocalTime 都是不可变的。
方法名 | 说明 |
plusDays, plusWeeks, plusMonths, plusYears | 向当前 LocalDate 对象添加几天、 几周、几个月、几年 |
minusDays, minusWeeks, minusMonths, minusYears | 从当前 LocalDate 对象减去几天、 几周、几个月、几年 |
withDayOfMonth, withDayOfYear, withMonth, withYear | 将月份天数、年份天数、月份、年 份 修 改 为 指 定 的 值 并 返 回 新 的 LocalDate 对象 |
isBefore, isAfter | 比较两个 LocalDate |
Instant类由一个静态的工厂方法now()可以返回当前时间戳。
Instant instant = Instant.now();System.out.println("当前时间戳是:" + instant);Date date = Date.from(instant);System.out.println("当前时间戳是:" + date);instant = date.toInstant();System.out.println(instant);
Instant和Date这两个类可以进行转换。
DateTimeFormatter
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String ldtStr = ldt.format(dtf);System.out.println(ldtStr);//2021-03-01 15:09:17
Period
计算日期间隔差异
getYears(),getMonths() 和 getDays() 来计算,只能精确到年月日
用于 LocalDate 之间的比较。
LocalDate today = LocalDate.now();System.out.println(today); // 2021-03-01LocalDate birthDate = LocalDate.of(1995, 1, 11);System.out.println(birthDate); // 1995-01-11Period period = Period.between(birthDate, today);System.out.printf("年龄 : %d 年 %d 月 %d 日", period.getYears(), period.getMonths(), period.getDays());
计算时间间隔差异
使用基于时间的值测量时间量的方法。
用于 LocalDateTime 之间的比较。也可用于 Instant 之间的比较。
LocalDateTime today = LocalDateTime.now();System.out.println(today);LocalDateTime birthDate = LocalDateTime.of(1990,10,1,10,50,30);System.out.println(birthDate);Duration duration = Duration.between(birthDate, today);//第二个参数减第一个参数System.out.println(duration.toDays());//两个时间差的天数System.out.println(duration.toHours());//两个时间差的小时数System.out.println(duration.toMinutes());//两个时间差的分钟数System.out.println(duration.toMillis());//两个时间差的毫秒数System.out.println(duration.toNanos());//两个时间差的纳秒数
java.time.temporal.ChronoUnit
ChronoUnit类可用于在单个时间单位内测量一段时间,这个工具类是最全的了,可以用于比较所有的时间单位
LocalDateTime today = LocalDateTime.now();System.out.println(today);LocalDateTime birthDate = LocalDateTime.of(1990,10,1,10,50,30);System.out.println(birthDate);System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today));System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today));System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today));System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today));System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today));System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today));
包装类
基本数据类型 | 引用数据类型 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
Java为了实现一切皆对象,为8种基本类型提供了对应的引用类型。
后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型。
自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量。
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量。
特有功能
包装类的变量的默认值可以是null,容错率更高。
可以把基本类型的数据转换成字符串类型(用处不大)
调用toString()方法得到字符串结果。
调用Integer.toString(基本类型的数据)
可以把字符串类型的数值转换成真实的数据类型(真的很有用)
Integer.parseInt(“字符串类型的整数”)
Double.parseDouble(“字符串类型的小数”)。
匹配正则表达
public boolean matches(String regex): 判断是否匹配正则表达式,匹配返回true,不匹配返回false。
字符类(默认匹配一个字符)
[abc] 只能是a, b, 或c
[^abc] 除了a, b, c之外的任何字符
[a-zA-Z] a到z A到Z,包括(范围)
[a-d[m-p]] a到d,或m通过p:([a-dm-p]联合)
[a-z&&[def]] d, e, 或f(交集)
[a-z&&[^bc]] a到z,除了b和c:([ad-z]减法)
[a-z&&[^m-p]] a到z,除了m到p:([a-lq-z]减法)
预定义的字符类(默认匹配一个字符)
.任何字符
\d一个数字: [0-9]
\D非数字: [^0-9]
\s一个空白字符: [ \t\n\x0B\f\r]
\S非空白字符: [^\s]
\w[a-zA-Z_0-9] 英文、数字、下划线
\W [^\w] 一个非单词字符
贪婪的量词(配合匹配多个字符)
X?X , 一次或根本不
X*X,零次或多次
X+X , 一次或多次
X {n}X,正好n次
X {n, }X,至少n次
X {n,m}X,至少n但不超过m次
正则表达式在字符串方法中的使用
方法名 | 说明 |
public String replaceAll(String regex,String newStr) | 按照正则表达式匹配的内容进行替换 |
public String[] split(String regex): | 按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。 |
Arrays类概述
常用API
方法名 | 说明 |
public static String toString(类型[] a) | 返回数组的内容(字符串形式) |
public static void sort(类型[] a) | 对数组进行默认升序排序 |
public static <T> void sort(类型[] a, Comparator<? super T> c) | 使用比较器对象自定义排序 |
public static int binarySearch(int[] a, int key) | 二分搜索数组中的数据,存在返回索引,不存在返回-1(排序) |
自定义排序规则
设置Comparator接口对应的比较器对象,来定制比较规则。
如果认为左边数据 大于 右边数据 返回正整数
如果认为左边数据 小于 右边数据 返回负整数
如果认为左边数据 等于 右边数据 返回0
Lambda概述
作用:简化匿名内部类的代码写法。
简化格式
(匿名内部类被重写方法的形参列表) -> { 被重写方法的方法体代码。}
注:-> 是语法形式,无实际含义
注意:Lambda表达式只能简化函数式接口的匿名内部类的写法形式
函数式接口
首先必须是接口、其次接口中有且仅有一个抽象方法的形式
必须是接口的匿名内部类,接口中只能有一个抽象方法
简化Comparator接口的匿名形式
public static void main(String[] args) { Integer[] ages = {66, 33, 22, 55, 88}; Arrays.sort(ages, new Comparator<Integer>(){ @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); System.out.println("内容:" + Arrays.toString(ages));}
![](https://img-blog.csdnimg.cn/img_convert/ab7895d86a830258dcdefa2160dadeb3.png)
注意:通常我们见到的函数式接口上都有一个@FunctionalInterface注解,标记该接口必须是满足函数式接口。
Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
参数类型可以省略不写。
如果只有一个参数,参数类型可以省略,同时()也可以省略。
如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号!
如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"不写
数组的特点
数组定义完成并启动后,类型确定、长度固定。
不适合元素的个数和类型不确定的业务场景,更不适合做需要增删数据操作。
数组的功能也比较的单一,处理数据的能力并不是很强大。
集合
特点
集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球。
集合非常适合元素个数不能确定,且需要做元素的增删操作的场景。
同时,集合提供的种类特别的丰富,功能也是非常强大的,开发中集合用的更多。
1、数组和集合的元素存储的个数问题。
数组定义后类型确定,长度固定
集合类型可以不固定,大小是可变的。
2、数组和集合存储元素的类型问题。
数组可以存储基本类型和引用类型的数据。
集合只能存储引用数据类型的数据。
3、数组和集合适合的场景
数组适合做数据个数和类型确定的场景。
集合适合做数据个数不确定,且要做增删元素的场景,集合种类更多,功能更强大。
集合类体系结构
![](https://img-blog.csdnimg.cn/img_convert/c83bf43b3f1534bc36c2dbc58fcbb798.png)
Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。
注意:前期先掌握Collection集合体系的使用。
Collection集合特点
![](https://img-blog.csdnimg.cn/img_convert/51f23226f6cc667eef75605e59707f32.png)
List系列集合:添加的元素是有序、可重复、有索引。
ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
HashSet: 无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。
泛型
集合都是泛型的形式,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>();Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写
注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。
Collection集合
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
方法名称 | 说明 |
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
迭代器遍历概述
遍历就是一个一个的把容器中的元素访问一遍。
迭代器在Java中的代表是Iterator,迭代器是集合的专用的遍历方式。
Collection集合获取迭代器
方法名称 | 说明 |
Iterator<E> iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引 |
Iterator中的常用方法
方法名称 | 说明 |
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界。 |
Iterator<E> iterator():得到迭代器对象,默认指向当前集合的索引0
2、迭代器如果取元素越界会出现什么问题。
会出现NoSuchElementException异常。
增强for循环
for(元素数据类型 变量名 : 数组或者Collection集合) {
//在此处使用变量即可,该变量就是元素
}
增强for循环:既可以遍历集合也可以遍历数组。
修改第三方变量的值不会影响到集合中的元素
(取决于是否是值传递还是引用传递)
Lambda表达式遍历集合
Collection结合Lambda遍历的API
方法名称 | 说明 |
default void forEach(Consumer<? super T> action): | 结合lambda遍历集合 |
Collection<String> lists = new ArrayList<>();...lists.forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); }});
lists.forEach(s -> { System.out.println(s); });// lists.forEach(s -> System.out.println(s));
集合中存储的是元素对象的地址。
栈数据结构的执行特点
后进先出,先进后出
数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈
![](https://img-blog.csdnimg.cn/img_convert/644a5189f9dcbadc5f2d515547678cc7.png)
队列
先进先出,后进后出
数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列
![](https://img-blog.csdnimg.cn/img_convert/0eceebdca1887158a20fa87ee3fc7043.png)
数组
查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
删除效率低:要将原始数据删除,同时后面每个数据前移。
添加效率极低:添加位置后的每个数据后移,再添加元素
链表的特点
链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址。
![](https://img-blog.csdnimg.cn/img_convert/f0c1b11ae891e3e9b59df197a699b742.png)
链表中的元素是游离存储的,每个元素节点包含数据值和下一个元素的地址。
链表查询慢。无论查询哪个数据都要从头开始找
链表是一种增删快的模型(对比数组)
链表是一种查询慢的模型(对比数组)
链表的种类
![](https://img-blog.csdnimg.cn/img_convert/57b02b0b08d4fa1684a5d57886806735.png)
![](https://img-blog.csdnimg.cn/img_convert/36600fe2b31505d54566671729ba2f1e.png)
二叉树概述
![](https://img-blog.csdnimg.cn/img_convert/4074ca4f145da94ba8070da2badc0aaf.png)
只能有一个根节点,每个节点最多支持2个直接子节点。
节点的度: 节点拥有的子树的个数,二叉树的度不大于2 叶子节点 度为0的节点,也称之为终端结点。
高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。
层:根节点在第一层,以此类推
兄弟节点 :拥有共同父节点的节点互称为兄弟节点
![](https://img-blog.csdnimg.cn/img_convert/c8bd89b3311907d47e13e176766d1134.png)
二叉查找树又称二叉排序树或者二叉搜索树。
特点:
1,每一个节点上最多有两个子节点
2,左子树上所有节点的值都小于根节点的值
3,右子树上所有节点的值都大于根节点的值
![](https://img-blog.csdnimg.cn/img_convert/90386e89e7cf56a64a1ff9bcf0e68a20.png)
数据结构-平衡二叉树
平衡二叉树是在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树
![](https://img-blog.csdnimg.cn/img_convert/9bc1347e926119250eec368886d568d0.png)
红黑树概述
每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。
每一个节点或是红色的,或者是黑色的,根节点必须是黑色。
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)。
对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
![](https://img-blog.csdnimg.cn/img_convert/ee37e7f37e1bf3e5371526504f743344.png)
![](https://img-blog.csdnimg.cn/img_convert/5c762593b01a1e3aacce39e3ff8b65d9.png)
Collection集合体系
![](https://img-blog.csdnimg.cn/img_convert/643d9e8f2c8176d772e33893c4cf43f3.png)
List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
底层数据结构是ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢。
方法名称 | 说明 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
LinkedList的特点
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
方法名称 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
使用for循环遍历并删除元素不会存在这个问题。
哪些遍历存在问题?
迭代器遍历集合且直接用集合删除元素的时候可能出现。
增强for循环遍历集合且直接用集合删除元素的时候可能出现。
泛型概述
泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>; 注意:泛型只能支持引用数据类型。
集合体系的全部接口和实现类都是支持泛型的使用的。
泛型的好处:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。
泛型类的概述
定义类时同时定义了泛型的类就是泛型类。
泛型类的格式:修饰符 class 类名<泛型变量>{ }
作用:编译阶段可以指定数据类型,类似于集合的作用。
T:任意类型 type
E:集合中元素的类型 element
K:key-value形式 key
V:key-value形式 value
泛型类的原理:
把出现泛型变量的地方全部替换成传输的真实数据类型
泛型方法的概述
定义方法时同时定义了泛型的方法就是泛型方法。
泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
泛型接口的概述
使用了泛型定义的接口就是泛型接口。
泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
通配符:?
? 可以在“使用泛型”的时候代表一切类型。
E T K V 是在定义泛型的时候使用的。
泛型的上下限:
? extends Car: ?必须是Car或者其子类 泛型上限
? super Car : ?必须是Car或者其父类 泛型下限
Collection集合体系
![](https://img-blog.csdnimg.cn/img_convert/b1dcb8e19208111584de7ae1d8e8595d.png)
Set系列集合特点
无序:存取顺序不一致
不重复:可以去除重复
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
Set集合实现类特点
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:排序、不重复、无索引。
HashSet底层原理
HashSet集合底层采取哈希表存储的数据。
哈希表是一种对于增删改查数据性能都较好的结构。
哈希值
是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
Object类的API
public int hashCode():返回对象的哈希值
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。(数组、链表、红黑树的结合体)
Set<String> sets = new HashSet<>();
创建一个默认长度16的数组,数组名table
根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
判断当前位置是否为null,如果是null直接存入
如果位置不为null,表示有元素,则调用equals方法比较
如果一样,则不存,如果不一样,则存入数组
JDK 7新元素占老元素位置,指向老元素
JDK 8中新元素挂在老元素下面
当链表长度超过8的时候,自动转换为红黑树
结论:如果希望Set集合认为2个内容一样的对象是重复的,
必须重写对象的hashCode()和equals()方法
LinkedHashSet集合概述和特点
有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
TreeSet集合概述和特点
不重复、无索引、可排序
可排序:按照元素的大小默认升序(有小到大)排序。
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet集合默认的规则
对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet无法直接排序。
结论:想要使用TreeSet存储自定义类型,需要制定排序规则
方式一
让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
方式二
TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
返回值的规则:
如果认为第一个元素大于第二个元素返回正整数即可。
如果认为第一个元素小于第二个元素返回负整数即可。
如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
![](https://img-blog.csdnimg.cn/img_convert/eef6d0a97cfe632b6af2ff46b4095655.png)
可变参数
void foo(String... args);
void foo(String[] args);
可变参数用在形参中可以接收多个数据。
可变参数的格式:数据类型...参数名称
可变参数的作用
可变参数在方法内部本质上就是一个数组。
可变参数的注意事项:
1.一个形参列表中可变参数只能有一个
2.可变参数必须放在形参列表的最后面
Collections集合工具类
java.utils.Collections:是集合工具类
作用:Collections并不属于集合,是用来操作集合的工具类
Collections常用的API
方法名称 | 说明 |
public static <T> boolean addAll(Collection<? super T> c, T... elements) | 给集合对象批量添加元素 |
public static void shuffle(List<?> list) | 打乱List集合元素的顺序 |
Collections排序相关API
使用范围:只能对于List集合的排序
排序方式1:
方法名称 | 说明 |
public static <T> void sort(List<T> list) | 将集合中元素按照默认规则排序 |
注意:本方式不可以直接对自定义类型的List集合排序,除非自定义类型实现了比较规则Comparable接口。
排序方式2:
方法名称 | 说明 |
public static <T> void sort(List<T> list,Comparator<? super T> c) | 将集合中元素按照指定规则排序 |
Map集合概述和使用
Map集合是一种双列集合,每个元素包含两个数据。
Map集合的每个元素的格式:key=value(键值对元素)。
Map集合也被称为“键值对集合”。
Map集合整体格式:
Collection集合的格式: [元素1,元素2,元素3..]
Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , ...}
Map集合体系
![](https://img-blog.csdnimg.cn/img_convert/7c49240e2bc27f94fb5fa8ffeb98e3fa.png)
Map集合体系特点
Map集合的特点都是由键决定的。
Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
Map集合后面重复的键对应的值会覆盖前面重复键的值。
Map集合的键值对都可以为null。
Map集合实现类特点
HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
TreeMap:元素按照建是排序,不重复,无索引的,值不做要求。
Map集合
Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的
方法名称 | 说明 |
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
Map集合的遍历方式有:3种。
方式一:键找值的方式遍历:先获取Map集合全部的键,再根据遍历键找值。
先获取Map集合的全部键的Set集合。
遍历键的Set集合,然后通过键提取对应值。
方法名称 | 说明 |
Set<K> keySet() | 获取所有键的集合 |
V get(Object key) | 根据键获取值 |
方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大。
先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。
遍历Set集合,然后提取键以及提取值。
方法名称 | 说明 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
K getKey() | 获得键 |
V getValue() | 获取值 |
方式三:JDK 1.8开始之后的新技术:Lambda表达式。
方法名称 | 说明 |
default void forEach(BiConsumer<? super K, ? super V> action) | 结合lambda遍历Map集合 |
HashMap的特点
HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引
没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
public HashSet() { map = new HashMap<>();}
实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
HashMap的特点和底层原理
由键决定:无序、不重复、无索引。HashMap底层是哈希表结构的。
依赖hashCode方法和equals方法保证键的唯一。
如果键要存储的是自定义对象,需要重写hashCode和equals方法。
基于哈希表。增删改查的性能都较好。
LinkedHashMap集合概述和特点
由键决定:有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
TreeMap集合概述和特点
由键决定特性:不重复、无索引、可排序
可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
TreeMap跟TreeSet一样底层原理是一样的。
TreeMap集合自定义排序规则有2种
类实现Comparable接口,重写比较规则。
集合自定义Comparator比较器对象,重写比较规则。
在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
方法名称 | 说明 |
static <E> List<E> of(E…elements) | 创建一个具有指定元素的List集合对象 |
static <E> Set<E> of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K , V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
这个集合不能添加,不能删除,不能修改。
Stream流
目的:用于简化集合和数组操作的API。(结合了Lambda表达式)
Stream流的思想和使用步骤。
获取Stream流
创建一条流水线,并把数据放到流水线上准备进行操作
中间方法(非终结方法)
流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。
终结方法
一个Stream流只能有一个终结方法,是流水线上的最后一个操作
集合获取Stream流的方式
名称 | 说明 |
default Stream<E> stream() | 获取当前集合对象的Stream流 |
数组获取Stream流的方式
名称 | 说明 |
public static <T> Stream<T> stream(T[] array) | 获取当前数组的Stream流 |
public static<T> Stream<T> of(T... values) | 获取当前数组/可变数据的Stream流 |
Stream流的常用API(中间操作方法)
名称 | 说明 |
Stream<T> filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤。 |
Stream<T> limit(long maxSize) | 获取前几个元素 |
Stream<T> skip(long n) | 跳过前几个元素 |
Stream<T> distinct() | 去除流中重复的元素。依赖(hashCode和equals方法) |
static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
注意:
中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
在Stream流中无法直接修改集合、数组中的数据。
Stream流的常见终结操作方法
名称 | 说明 |
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
long count() | 返回此流中的元素数 |
注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。
收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去。
Stream流:方便操作集合/数组的手段。
集合/数组:才是开发中的目的。
Stream流的收集方法
名称 | 说明 |
R collect(Collector collector) | 开始收集Stream流,指定收集器 |
Collectors工具类提供了具体的收集方式
名称 | 说明 |
public static <T> Collector toList() | 把元素收集到List集合中 |
public static <T> Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
异常体系
![](https://img-blog.csdnimg.cn/img_convert/77dac39cb842ee60fdabc7ec2511f49e.png)
Error:
系统级别问题、JVM退出等,代码无法控制。
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
RuntimeException及其子类:运行时异常,编译阶段不会报错。 (空指针异常,数组索引越界异常)
除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。 (日期格式化异常)
编译时异常和运行时异常
![](https://img-blog.csdnimg.cn/img_convert/568b3433375b054382b1c14a172e366f.png)
运行时异常
直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
![](https://img-blog.csdnimg.cn/img_convert/49b309356468dbcd298c03443ad14414.png)
编译时异常
不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
作用
是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒不要出错!
编译时异常是可遇不可求。遇到了就遇到了呗。
异常处理方式1 —— throws
出现异常直接抛出去给调用者,调用者也继续抛出去
throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。
抛出异常格式:
方法 throws 异常1 ,异常2 ,异常3 ..{}
异常处理方式2 —— try…catch…( 出现异常自己捕获处理,不麻烦别人。)
监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。
格式:
try{ // 监视可能出现异常的代码! }catch(异常类型1 变量){ // 处理异常 }catch(异常类型2 变量){ // 处理异常 }...
建议格式:
try{ // 可能出现异常的代码!}catch (Exception e){ e.printStackTrace(); // 直接打印异常栈信息}
Exception可以捕获处理一切异常类型!
异常处理方式3 —— 前两者结合
方法直接将异通过throws抛出去给调用者
调用者收到异常后直接捕获处理。
前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。
自定义异常
1、自定义编译时异常
定义一个异常类继承Exception.
重写构造器。
在出现异常的地方用throw new 自定义对象抛出,
作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
2、自定义运行时异常
定义一个异常类继承RuntimeException.
重写构造器。
在出现异常的地方用throw new 自定义对象抛出!
作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!
日志技术
输出语句 | 日志技术 | |
输出位置 | 只能是控制台 | 可以将日志信息写入到文件或者数据库中 |
取消日志 | 需要修改代码,灵活性比较差 | 不需要修改代码,灵活性比较好 |
多线程 | 性能较差 | 性能较好 |
体系结构
![](https://img-blog.csdnimg.cn/img_convert/c6a4095763036a12e81a95e98668215e.png)
日志规范:一些接口,提供给日志的实现框架设计的标准。
日志框架:牛人或者第三方公司已经做好的日志记录实现代码,后来者直接可以拿去使用。
因为对Commons Logging的接口不满意,有人就搞了SLF4J。因为对Log4j的性能不满意,有人就搞了Logback。
Logback需要使用哪几个模块,各自的作用是什么
slf4j-api:日志规范
logback-core:基础模块。
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API
Logback主要分为三个技术模块:
logback-core: logback-core 模块为其他两个模块奠定了基础,必须有。
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API。
logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能
Logback日志系统的特性都是通过核心配置文件logback.xml控制的。
Logback日志输出位置、格式设置:
通过logback.xml 中的<append>标签可以设置输出位置和日志信息的详细格式。
通常可以设置2个日志输出位置:一个是控制台、一个是系统文件中
输出到控制台的配置标志
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
输出到系统文件的配置标志
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
日志级别
级别程度依次是:TRACE< DEBUG< INFO<WARN<ERROR ; 默认级别是debug(忽略大小写),对应其方法。
作用:用于控制系统中哪些日志级别是可以输出的,只输出级别不低于设定级别的日志信息。
ALL 和 OFF分别是打开全部日志信息,及关闭全部日志信息。
具体在<root level=“INFO”>标签的level属性中设置日志级别。
<root level=“INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE" /></root>
File类创建对象
方法名称 | 说明 |
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 根据父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 |
注意
File对象可以定位文件和文件夹
File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。
绝对路径和相对路径
绝对路径:从盘符开始
File file1 = new File(“D:\\itheima\\a.txt”);
相对路径:不带盘符,默认直接到当前工程下的目录寻找文件
File file3 = new File(“模块名\\a.txt”);
File类的判断文件类型、获取文件信息功能
方法名称 | 说明 |
public boolean isDirectory() | 判断此路径名表示的File是否为文件夹 |
public boolean isFile() | 判断此路径名表示的File是否为文件 |
public boolean exists() | 判断此路径名表示的File是否存在 |
public long length() | 返回文件的大小(字节数量) |
public String getAbsolutePath() | 返回文件的绝对路径 |
public String getPath() | 返回定义文件时使用的路径 |
public String getName() | 返回文件的名称,带后缀 |
public long lastModified() | 返回文件的最后修改时间(时间毫秒值) |
File类创建文件的功能
方法名称 | 说明 |
public boolean createNewFile() | 创建一个新的空的文件 |
public boolean mkdir() | 只能创建一级文件夹 |
public boolean mkdirs() | 可以创建多级文件夹 |
File类删除文件的功能
方法名称 | 说明 |
public boolean delete() | 删除由此抽象路径名表示的文件或空文件夹 |
注意
delete方法默认只能删除文件和空文件夹,delete方法直接删除不走回收站
File类的遍历功能
方法名称 | 说明 |
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 |
public File[] listFiles()(常用) | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) |
listFiles方法注意事项:
当文件不存在时或者代表文件时,返回null
当文件对象代表一个空文件夹时,返回一个长度为0的数组
当文件对象是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
当文件对象是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
当没有权限访问该文件夹时,返回null
什么是方法递归?
方法调用自身的形式称为方法递归( recursion)。
递归的形式
直接递归:方法自己调用自己。
间接递归:方法调用其他方法,其他方法又回调方法自己。
方法递归存在的问题?
递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出现象。
![](https://img-blog.csdnimg.cn/img_convert/00437c65f366b537965a860f2946f18f.png)
字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:
ASCII字符集
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。
ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于表示英文、数字来说是够用
GBK字符集
GBK是中国的码表,包含了几万个汉字等字符,同时也要兼容ASCII编码,
GBK编码中一个中文字符一般以两个字节的形式存储。
Unicode(UTF-8)字符集等。
统一码,也叫万国码。是计算机科学领域里的一项业界标准。
UTF-8是Unicode的一种常见编码方式。
注意
UTF-8编码后一个中文一般以三个字节的形式存储,同时也要兼容ASCII编码表。
技术人员都应该使用UTF-8的字符集编码。
注意:英文和数字在任何国家的编码中都不会乱码。
String编码
方法名称 | 说明 |
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
String解码
构造器 | 说明 |
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的 String |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来构造新的 String |
IO流概述
I表示intput,把硬盘文件中的数据读入到内存的过程,称之输入,负责读。
O表示output,把内存中的数据写出到硬盘文件的过程,称之输出,负责写。
IO流的分类
按流的方向分
![](https://img-blog.csdnimg.cn/img_convert/393d869997251ac5ef31be989ba57f73.png)
按流中的数据最小单位分为
![](https://img-blog.csdnimg.cn/img_convert/cff62b5229af5abb35dab20ef66140ad.png)
总结流的四大类:
字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。
![](https://img-blog.csdnimg.cn/img_convert/495b71c0fb5f8a920357fd09cec00c78.png)
文件字节输入流:InputStream
作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。
方法名称 | 说明 |
public int read() | 每次读取一个字节返回,如果字节已经没有可读的返回-1 |
public int read(byte[] buffer) | 每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
读取文件的全部字节
方式一
自己定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成
方法名称 | 说明 |
public int read(byte[] buffer) | 每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中
方法名称 | 说明 |
public byte[] readAllBytes() throws IOException | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
文件字节输出流:FileOutputStream
作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流。
构造器 | 说明 |
public FileOutputStream(File file) | 创建字节输出流管道与源文件对象接通 |
public FileOutputStream(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileOutputStream(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileOutputStream(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
文件字节输出流(FileOutputStream)写数据出去的API
方法名称 | 说明 |
public void write(int a) | 写一个字节出去 |
public void write(byte[] buffer) | 写一个字节数组出去 |
public void write(byte[] buffer , int pos , int len) | 写一个字节数组的一部分出去。 |
流的关闭与刷新
方法 | 说明 |
flush() | 刷新流,还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
字符输出流如何实现数据追加
public FileWriter(String filepath,boolean append) | 创建字符输出流管道与源文件路径接通,可追加数据 |
字节输出流如何实现写出去的数据能换行
os.write(“\r\n”.getBytes())
字节流适合做一切文件数据的拷贝吗?
任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式、编码一致没有任何问题。
try-catch-finally
finally:放在try-catch后面的,无论是正常执行还是异常执行代码,最后一定要执行,除非JVM退出。
作用:一般用于进行最后的资源释放操作(专业级做法)
try-with-resource
资源都是实现了Closeable/AutoCloseable接口的类对象
public abstract class InputStream implements Closeable {}
public abstract class OutputStream implements Closeable, Flushable{}
定义输入流对象;
定义输出流对象;
try(输入流对象;输出流对象){ 可能出现异常的代码;}catch(异常类名 变量名){ 异常的处理代码;}
资源用完最终自动释放
字节流读取中文输出会存在什么问题?
会乱码。或者内存溢出。
读取中文输出,哪个流更合适,为什么?
字符流更合适,最小单位是按照单个字符读取的。
文件字符输入流:Reader
作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。
方法名称 | 说明 |
public int read() | 每次读取一个字符返回,如果字符已经没有可读的返回-1 |
public int read(char[] buffer) | 每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1 |
文件字符输入流:FileReader
作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。
构造器 | 说明 |
public FileReader(File file) | 创建字符输入流管道与源文件对象接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件路径接通 |
文件字符输出流:FileWriter
作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流。
构造器 | 说明 |
public FileWriter(File file) | 创建字符输出流管道与源文件对象接通 |
public FileWriter(File file,boolean append) | 创建字符输出流管道与源文件对象接通,可追加数据 |
public FileWriter(String filepath) | 创建字符输出流管道与源文件路径接通 |
public FileWriter(String filepath,boolean append) | 创建字符输出流管道与源文件路径接通,可追加数据 |
文件字符输出流(FileWriter)写数据出去的API
方法名称 | 说明 |
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
流的关闭与刷新
方法 | 说明 |
flush() | 刷新流,还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
字符输出流如何实现数据追加
public FileWriter(String filepath,boolean append) | 创建字符输出流管道与源文件路径接通,可追加数据 |
![](https://img-blog.csdnimg.cn/img_convert/9582e7f4565455d02a6d431552108b02.png)
缓冲流概述
缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。
作用:缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能
![](https://img-blog.csdnimg.cn/img_convert/1388183d2d9754701426dc8ed6c5ec40.png)
字节缓冲流性能优化原理:
字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能较好。
字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能极高了。
字节缓冲流
字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能。
字节缓冲输出流:BufferedOutputStream,提高字节输出流读取数据的性能。
构造器 | 说明 |
public BufferedInputStream(InputStream is) | 可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能 |
public BufferedOutputStream(OutputStream os) | 可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
推荐使用哪种方式提高字节流读写数据的性能?
建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前来看是性能最优的组合。
字符缓冲输入流
字符缓冲输入流:BufferedReader。
作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能。
构造器 | 说明 |
public BufferedReader(Reader r) | 可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能 |
字符缓冲输入流新增功能
方法 | 说明 |
public String readLine() | 读取一行数据返回,如果读取没有完毕,无行可读返回null |
字符缓冲输出流
字符缓冲输出流:BufferedWriter。
作用:提高字符输出流写取数据的性能,除此之外多了换行功能
构造器 | 说明 |
public BufferedWriter(Writer w) | 可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
字符缓冲输出流新增功能
方法 | 说明 |
public void newLine() | 换行操作 |
字符缓冲流为什么提高了操作数据的性能?
字符缓冲流自带8K缓冲区
可以提高原始字符流读写数据的性能
字符缓冲流的功能如何使用?
public BufferedReader(Reader r)
性能提升了,多了readLine()按照行读取的功能
![](https://img-blog.csdnimg.cn/img_convert/938df46f53256dd73601ed4e393229a1.png)
构造器 | 说明 |
public ObjectOutputStream(OutputStream out) | 把低级字节输出流包装成高级的对象字节输出流 |
字符输入转换流
字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流。
构造器 | 说明 |
public InputStreamReader(InputStream is) | 可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样。 |
public InputStreamReader(InputStream is ,String charset) | 可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不乱码了(重点) |
字符输出转换流
字符输入转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流。
构造器 | 说明 |
public OutputStreamWriter(OutputStream os) | 可以把原始的字节输出流按照代码默认编码转换成字符输出流。几乎不用。 |
public OutputStreamWriter(OutputStream os,String charset) | 可以把原始的字节输出流按照指定编码转换成字符输出流(重点) |
对象序列化:
作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化。
使用到的流是对象字节输出流:ObjectOutputStream
构造器 | 说明 |
public ObjectOutputStream(OutputStream out) | 把低级字节输出流包装成高级的对象字节输出流 |
ObjectOutputStream序列化方法
方法名称 | 说明 |
public final void writeObject(Object obj) | 把对象写出去到对象序列化流的文件中去 |
对象反序列化:
使用到的流是对象字节输入流:ObjectInputStream
作用:以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化。
构造器 | 说明 |
public ObjectInputStream(InputStream out) | 把低级字节输如流包装成高级的对象字节输入流 |
ObjectInputStream序列化方法
方法名称 | 说明 |
public Object readObject() | 把存储到磁盘文件中去的对象数据恢复成内存中的对象返回 |
打印流
作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。
可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。
PrintStream
构造器 | 说明 |
public PrintStream(OutputStream os) | 打印流直接通向字节输出流管道 |
public PrintStream(File f) | 打印流直接通向文件对象 |
public PrintStream(String filepath) | 打印流直接通向文件路径 |
方法 | 说明 |
public void print(Xxx xx) | 打印任意类型的数据出去 |
PrintWriter
构造器 | 说明 |
public PrintWriter(OutputStream os) | 打印流直接通向字节输出流管道 |
public PrintWriter (Writer w) | 打印流直接通向字符输出流管道 |
public PrintWriter (File f) | 打印流直接通向文件对象 |
public PrintWriter (String filepath) | 打印流直接通向文件路径 |
方法 | 说明 |
public void print(Xxx xx) | 打印任意类型的数据出去 |
PrintStream和PrintWriter的区别
打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)
PrintStream继承自字节输出流OutputStream,支持写字节数据的方法。
PrintWriter继承自字符输出流Writer,支持写字符数据出去。
输出语句重定向
属于打印流的一种应用,可以把输出语句的打印位置改到文件
PrintStream ps = new PrintStream("文件地址")System.setOut(ps);
.properties结尾的属性文件
Map集合体系
![](https://img-blog.csdnimg.cn/img_convert/18e30627fe172f486a8a630b89ba94e8.png)
Map集合
Properties核心作用:
Properties代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去。
属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value,后续做系统配置信息的
Properties的API:
Properties和IO流结合的方法:
构造器 | 说明 |
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
public Object setProperty(String key, String value) | 保存键值对(put) |
public String getProperty(String key) | 使用此属性列表中指定的键搜索属性值 (get) |
public Set<String> stringPropertyNames() | 所有键的名称的集合 (keySet()) |
commons-io概述
commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils
String readFileToString(File file, String encoding) | 读取文件中的数据, 返回字符串 |
void copyFile(File srcFile, File destFile) | 复制文件。 |
void copyDirectoryToDirectory(File srcDir, File destDir) | 复制文件夹。 |
Thread类
Java是通过java.lang.Thread 类来代表线程的。
按照面向对象的思想,Thread类应该提供了实现多线程的方式。
多线程的实现方案一:继承Thread类
定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
创建MyThread类的对象
调用线程对象的start()方法启动线程(启动后还是执行run方法的)
方式一优缺点:
多线程的实现方案二:实现Runnable接口
定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
创建MyRunnable任务对象
把MyRunnable任务对象交给Thread处理。
调用线程对象的start()方法启动线程
Thread的构造器
构造器 | 说明 |
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
public Thread(Runnable target ,String name ) | 封装Runnable对象成为线程对象,并指定线程名称 |
方式二优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
多线程的实现方案二:实现Runnable接口(匿名内部类形式)
可以创建Runnable的匿名内部类对象。
交给Thread处理。
调用线程对象的start()启动线程。
多线程的实现方案三:利用Callable、FutureTask接口实现。
、得到任务对象
定义类实现Callable接口,重写call方法,封装要做的事情。
用FutureTask把Callable对象封装成线程任务对象。
、把线程任务对象交给Thread处理。
、调用Thread的start方法启动线程,执行任务
、线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果。
FutureTask的API
方法名称 | 说明 |
public FutureTask<>(Callable call) | 把Callable对象封装成FutureTask对象。 |
public V get() throws Exception | 获取线程执行call方法返回的结果。 |
方式三优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂一点。
方式 | 优点 | 缺点 |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 扩展性较差,不能再继承其他的类,不能返回线程执行的结果 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。 | 编程相对复杂,不能返回线程执行的结果 |
实现Callable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果 | 编程相对复杂 |
Thread获取和设置线程名称
方法名称 | 说明 |
String getName() | 获取当前线程的名称,默认线程名称是Thread-索引 |
void setName(String name) | 将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称 |
Thread类获得当前线程的对象
方法名称 | 说明 |
public static Thread currentThread(): | 返回对当前正在执行的线程对象的引用 |
注意
1、此方法是Thread类的静态方法,可以直接使用Thread类调用。
2、这个方法是在哪个线程执行中调用的,就会得到哪个线程对象
Thread的构造器
方法名称 | 说明 |
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
public Thread(Runnable target ,String name ) | 封装Runnable对象成为线程对象,并指定线程名称 |
Thread类的线程休眠方法
方法名称 | 说明 |
public static void sleep(long time) | 让当前线程休眠指定的时间后再继续执行,单位为毫秒。 |
线程同步
加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。
同步代码块
作用:把出现线程安全问题的核心代码给上锁。
原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。
synchronized(同步锁对象) {操作共享资源的代码(核心代码)}
对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码(类名.class)对象作为锁对象。
同步方法
作用:把出现线程安全问题的核心方法给上锁。
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
格式
修饰符 synchronized 返回值类型 方法名称(形参列表) {操作共享资源的代码}
同步方法底层原理
同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
如果方法是实例方法:同步方法默认用this作为的锁对象。但是代码要高度面向对象!
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
Lock锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象。
方法名称 | 说明 |
public ReentrantLock() | 获得Lock锁的实现类对象 |
Lock的API
方法名称 | 说明 |
void lock() | 获得锁 |
void unlock() | 释放锁 |
Object类的等待和唤醒方法:
方法名称 | 说明 |
void wait() | 让当前线程等待并释放所占锁,直到另一个线程调用notify()方法或 notifyAll()方法 |
void notify() | 唤醒正在等待的单个线程 |
void notifyAll() | 唤醒正在等待的所有线程 |
注意
上述方法应该使用当前同步锁对象进行调用。
线程池
方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
![](https://img-blog.csdnimg.cn/img_convert/065e80b819da43e3f75619eeef57945d.png)
ThreadPoolExecutor构造器的参数说明
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory, RejectedExecutionHandler handler)
![](https://img-blog.csdnimg.cn/img_convert/e49ba52ba01a0c705cb259ea2fe1467f.png)
ThreadPoolExecutor创建线程池对象示例
ExecutorService pools = new ThreadPoolExecutor(3, 5 , 8 , TimeUnit.SECONDS, new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy());
ExecutorService的常用方法
方法名称 | 说明 |
void execute(Runnable command) | 执行任务/命令,没有返回值,一般用来执行 Runnable 任务 |
Future<T> submit(Callable<T> task) | 执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务 |
void shutdown() | 等任务执行完毕后关闭线程池 |
List<Runnable> shutdownNow() | 立刻关闭,停止正在执行的任务,并返回队列中未执行的任务 |
新任务拒绝策略
策略 | 详解 |
ThreadPoolExecutor.AbortPolicy | 丢弃任务并抛出RejectedExecutionException异常。是默认的策略 |
ThreadPoolExecutor.DiscardPolicy: | 丢弃任务,但是不抛出异常 这是不推荐的做法 |
ThreadPoolExecutor.DiscardOldestPolicy | 抛弃队列中等待最久的任务 然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy | 由主线程负责调用任务的run()方法从而绕过线程池直接执行 |
Executors得到线程池对象的常用方法
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。
方法名称 | 说明 |
public static ExecutorService newCachedThreadPool() | 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。 |
public static ExecutorService newFixedThreadPool(int nThreads) | 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
public static ExecutorService newSingleThreadExecutor () | 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) | 创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 |
注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。
Executors使用可能存在的陷阱
大型并发系统环境中使用Executors如果不注意可能会出现系统风险。
方法名称 | 存在问题 |
public static ExecutorService newFixedThreadPool(int nThreads) | 允许请求的任务队列长度是Integer.MAX_VALUE,可能出现OOM错误( java.lang.OutOfMemoryError ) |
public static ExecutorService newSingleThreadExecutor() | |
public static ExecutorService newCachedThreadPool() | 创建的线程数量最大上限是Integer.MAX_VALUE, 线程数可能会随着任务1:1增长,也可能出现OOM错误( java.lang.OutOfMemoryError ) |
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) |
定时器
定时器是一种控制任务延时调用,或者周期调用的技术。
作用:闹钟、定时邮件发送。
定时器的实现方式
方式一:Timer
构造器 | 说明 |
public Timer() | 创建Timer定时器对象 |
方式二: ScheduledExecutorService
方法 | 说明 |
public void schedule(TimerTask task, long delay, long period) | 开启一个定时器,按照计划处理TimerTask任务 |
Timer定时器的特点和存在的问题
1、Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。
2、可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。
并发与并行
正在运行的程序(软件)就是一个独立的进程, 线程是属于进程的,多个线程其实是并发与并行同时进行的。
并发的理解:
CPU同时处理线程的数量有限。
CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发
![](https://img-blog.csdnimg.cn/img_convert/45471a8c474d05945279f066272ea8b5.png)
并行的理解:
在同一个时刻上,同时有多个线程在被CPU处理并执行。
![](https://img-blog.csdnimg.cn/img_convert/77fad0cb41a91342478ea2d3cdde1920.png)
线程的状态
线程的状态:也就是线程从生到死的过程,以及中间经历的各种状态及状态转换。
理解线程的状态有利于提升并发编程的理解能力。
![](https://img-blog.csdnimg.cn/img_convert/736a215da12982c1cb32b8ee009a64d8.png)
线程状态 | 描述 |
NEW(新建) | 线程刚被创建,但是并未启动。 |
Runnable(可运行) | 线程已经调用了start()等待CPU调度 |
Blocked(锁阻塞) | 线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态;。 |
Waiting(无限等待) | 一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。带有超时参数的常用方法有Thread.sleep 、Object.wait。 |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
网络通信基本模式
常见的通信模式有如下2种形式:Client-Server(CS) 、 Browser/Server(BS)
关键的三要素
IP地址:设备在网络中的地址,是唯一的标识。
端口:应用程序在设备中唯一的标识。
协议: 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
IP地址
IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志。
常见的IP分类为:IPv4和IPv6
![](https://img-blog.csdnimg.cn/img_convert/57ece15aeaa8e50632ba80a21644809d.png)
IPv6:128位(16个字节),号称可以为地球每一粒沙子编号。
IPv6分成8个整数,每个整数用四个十六进制位表示, 数之间用冒号(:)分开。
![](https://img-blog.csdnimg.cn/img_convert/9efd67d5f64e8c92ab65883b406055e8.png)
IP地址形式:
公网地址、和私有地址(局域网使用)。
192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用。
IP常用命令:
ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
特殊IP地址:
本机IP: 127.0.0.1或者localhost:称为回送地址也可称本地回环地址,只会寻找当前所在本机。
InetAddress 的使用
此类表示Internet协议(IP)地址。
InetAddress API如下
名称 | 说明 |
public static InetAddress getLocalHost() | 返回本主机的地址对象 |
public static InetAddress getByName(String host) | 得到指定主机的IP地址对象,参数是域名或者IP地址 |
public String getHostName() | 获取此IP地址的主机名 |
public String getHostAddress() | 返回IP地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连通该IP地址对应的主机,连通返回true |
端口号
端口号:标识正在计算机设备上运行的进程(程序),被规定为一个 16 位的二进制,范围是 0~65535。
端口类型
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占 用8080,MySQL占用3306)
动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
通信协议
连接和通信数据的规则被称为网络通信协议
OSI参考模型 | TCP/IP参考模型 | 各层对应 | 面向操作 |
应用层 | 应用层 | HTTP、FTP、DNS、SMTP… | 应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发 |
表示层 | |||
会话层 | |||
传输层 | 传输层 | TCP、UDP… | 选择使用的TCP , UDP协议 |
网络层 | 网络层 | IP、ICMP… | 封装源和目标IP,进行路径选择 |
数据链路层 | 数据链路层+物理 | 物理寻址、比特流… | 物理设备中传输 |
物理层 |
传输层的2个常见协议
TCP(Transmission Control Protocol) :传输控制协议
UDP(User Datagram Protocol):用户数据报协议
TCP协议特点
使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。
传输前,采用“三次握手”方式建立连接,所以是可靠的 。
在连接中可进行大数据量的传输 。
连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低。
TCP协议通信场景
对信息安全要求较高的场景,例如:文件下载、金融等数据通信。
TCP四次挥手断开连接
UDP协议:
UDP是一种无连接、不可靠传输的协议。
将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
每个数据包的大小限制在64KB内
发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快。
UDP协议通信场景
语音通话,视频会话等。
DatagramPacket:数据包对象(韭菜盘子)
构造器 | 说明 |
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 创建发送端数据包对象 buf:要发送的内容,字节数组 length:要发送内容的字节长度 address:接收端的IP地址对象 port:接收端的端口号 |
public DatagramPacket(byte[] buf, int length) | 创建接收端的数据包对象 buf:用来存储接收的内容 length:能够接收内容的长度 |
DatagramPacket常用方法
方法 | 说明 |
public int getLength() | 获得实际接收到的字节个数 |
DatagramSocket:发送端和接收端对象(人)
构造器 | 说明 |
public DatagramSocket() | 创建发送端的Socket对象,系统会随机分配一个端口号。 |
public DatagramSocket(int port) | 创建接收端的Socket对象并指定端口号 |
DatagramSocket类成员方法
方法 | 说明 |
public void send(DatagramPacket dp) | 发送数据包 |
public void receive(DatagramPacket p) | 接收数据包 |
需求:客户端实现步骤
创建DatagramSocket对象(发送端对象) 扔韭菜的人
创建DatagramPacket对象封装需要发送的数据(数据包对象) 韭菜盘子
使用DatagramSocket对象的send方法传入DatagramPacket对象 开始抛出韭菜
释放资源
需求:接收端实现步骤
创建DatagramSocket对象并指定端口(接收端对象) 接韭菜的人
创建DatagramPacket对象接收数据(数据包对象) 韭菜盘子
使用DatagramSocket对象的receive方法传入DatagramPacket对象 开始接收韭菜
释放资源
UDP的三种通信方式
单播:单台主机与单台主机之间的通信。
广播:当前主机与所在网络中的所有主机通信。
组播:当前主机与选定的一组主机的通信。
![](https://img-blog.csdnimg.cn/img_convert/b8a214dd8f9afa81d4dedd151f494c0c.png)
UDP如何实现组播
使用组播地址:224.0.0.0 ~ 239.255.255.255
具体操作:
发送端的数据包的目的地是组播IP (例如:224.0.1.1, 端口:9999)
接收端必须绑定该组播IP(224.0.1.1),端口还要注册发送端的目的端口9999 ,这样即可接收该组播消息。
DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP。
UDP如何实现广播
使用广播地址:255.255.255.255
具体操作:
发送端发送的数据包的目的地写的是广播地址、且指定端口。 (255.255.255.255 , 9999)
本机所在网段的其他主机的程序只要注册对应端口就可以收到消息了。(9999)
TCP通信模式演示:
![](https://img-blog.csdnimg.cn/img_convert/b8da5d24a60bc622ed52e4be30232cd9.png)
注意:在java中只要是使用java.net.Socket类实现通信,底层即是使用了TCP协议
Socket
构造器 | 说明 |
public Socket(String host , int port) | 创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。 |
Socket类成员方法
方法 | 说明 |
OutputStream getOutputStream() | 获得字节输出流对象 |
InputStream getInputStream() | 获得字节输入流对象 |
ServerSocket(服务端)
构造器 | 说明 |
public ServerSocket(int port) | 注册服务端端口 |
ServerSocket类成员方法
方法 | 说明 |
public Socket accept() | 等待接收客户端的Socket通信连接 连接成功返回Socket对象与客户端建立端到端通信 |
单元测试
单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。
![](https://img-blog.csdnimg.cn/img_convert/7df1f83702a60e5e3707be819f8e568c.png)
JUnit优点
JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
Junit可以生成全部方法的测试报告。
单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
![](https://img-blog.csdnimg.cn/img_convert/bfef73f1f626ee382d16f691fd10727d.png)
JUnit可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。
JUnit可以生测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
JUnit单元测试的实现过程是什么样的?
必须导入Junit框架的jar包。
定义的测试方法必须是无参数无返回值,且公开的方法。
测试方法使用@Test注解标记。
Junit常用注解(Junit 4.xxxx版本)
注解 | 说明 |
@Test | 测试方法 |
@Before | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@After | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeClass | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
@AfterClass | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
开始执行的方法:初始化资源。
执行完之后的方法:释放资源。
Junit常用注解(Junit 5.xxxx版本)
注解 | 说明 |
@Test | 测试方法 |
@BeforeEach | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@AfterEach | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeAll | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
@AfterAll | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
开始执行的方法:初始化资源。
执行完之后的方法:释放资源。
反射概述
反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
反射的关键:
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
![](https://img-blog.csdnimg.cn/img_convert/3fff215e57d3d8cf660fabfaa00d723b.png)
反射的基本作用、关键?
反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分。
反射的核心思想和关键就是:得到编译以后的class文件对象。
反射概述
反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制
反射的关键:
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分
![](https://img-blog.csdnimg.cn/img_convert/42263d43c4f6c34f78760fa32c557380.png)
![](https://img-blog.csdnimg.cn/img_convert/e2fb63cfec25fdb436d30b9fc7975964.png)
![](https://img-blog.csdnimg.cn/img_convert/7a8a4db4c9915cb61fa6438385df1614.png)
方式一:Class c1 = Class.forName(“全类名”);
方式二:Class c2 = 类名.class
方式三:Class c3 = 对象.getClass();
使用反射技术获取构造器对象并使用
![](https://img-blog.csdnimg.cn/img_convert/c634dfcfaeafa0e684c9e3d8eba10dd5.png)
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
Class类中用于获取构造器的方法
方法 | 说明 |
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造器对象,存在就能拿到 |
Constructor类中用于创建对象的方法
符号 | 说明 |
T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
使用反射技术获取成员变量对象并使用
![](https://img-blog.csdnimg.cn/img_convert/98b4c3612a1859eeeefaffa3f091f17e.png)
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
Class类中用于获取成员变量的方法
方法 | 说明 |
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
Field类中用于取值、赋值的方法
获取成员变量的作用依然是在某个对象中取值、赋值
符号 | 说明 |
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值。 |
![](https://img-blog.csdnimg.cn/img_convert/ce9fc1ef76a432794a563c668cef6ecf.png)
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
Class类中用于获取成员方法的方法
方法 | 说明 |
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
使用反射技术获取方法对象并使用
获取成员方法的作用依然是在某个对象中进行执行此方法
Method类中用于触发执行的方法
符号 | 说明 |
Object invoke(Object obj, Object... args) | 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
反射的作用-绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
ArrayList<Integer> list = new ArrayList<>();list.add(100);// list.add(“黑马"); // 报错list.add(99);
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
注解概述、作用
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
对Java中类、方法、成员变量做标记,然后进行特殊处理
自定义注解 --- 格式
自定义注解就是自己做一个注解来使用。
public @interface 注解名称 { public 属性类型 属性名() default 默认值 ;}
特殊属性
value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!
但是如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的。
元注解
元注解:注解注解的注解。
元注解有两个:
@Target: 约束自定义注解只能在哪些地方使用,
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE, 局部变量
@Retention:申明注解的生命周期
SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
注解的解析
注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
与注解解析相关的接口
Annotation: 注解的顶级接口,注解都是Annotation类型的对象
AnnotatedElement:该接口定义了与注解解析相关的解析方法
方法 | 说明 |
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
T getDeclaredAnnotation(Class<T> annotationClass) | 根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力:
解析注解的技巧
注解在哪个成分上,我们就先拿哪个成分对象。
比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
动态代理
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。
关键步骤
1.必须有接口,实现类要实现接口(代理通常是基于接口实现的)。
3.创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。
![](https://img-blog.csdnimg.cn/img_convert/3f28280f475511a394dd68e47fe4e6d8.png)
非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。
可以为被代理对象的所有方法做代理。
可以在不改变方法源码的情况下,实现对方法功能的增强。
不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
XML概述
XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。
![](https://img-blog.csdnimg.cn/img_convert/3d00b050c3202319d55c3b0a73143dda.png)
XML的几个特点和使用场景
一是纯文本,默认使用UTF-8编码;二是可嵌套;
如果把XML内容存为文件,那么它就是一个XML文件。
XML的使用场景:XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的信息。
XML的语法规则
XML文件的后缀名为:xml
文档声明必须是第一行
![](https://img-blog.csdnimg.cn/img_convert/d978dd652046b21e6ca080b8993de91f.png)
XML的标签(元素)规则
标签由一对尖括号和合法标识符组成: <name></name>,必须存在一个根标签,有且只能有一个。
标签必须成对出现,有开始,有结束: <name></name>
特殊的标签可以不成对,但是必须有结束标记,如:<br/>
标签中可以定义属性,属性和标签名空格隔开,属性值必须用引号引起来<student id = “1”></name>
标签需要正确的嵌套
ML的其他组成
XML文件中可以定义注释信息:<!– 注释内容 -->
XML文件中可以存在以下特殊字符
< < 小于> > 大于& & 和号' ' 单引号" " 引号
XML文件中可以存在CDATA区: <![CDATA[ …内容… ]]>
文档约束:是用来限定xml文件中的标签以及属性应该怎么写。
以此强制约束程序员必须按照文档约束的规定来编写xml文件。
![](https://img-blog.csdnimg.cn/img_convert/7837b0e6bde9481cce20f3e693fb8f14.png)
文档约束-schema
schema可以约束具体的数据类型,约束能力上更强大。
schema本身也是一个xml文件,本身也受到其他约束文件的要求,所以编写的更加严谨
![](https://img-blog.csdnimg.cn/img_convert/a3ae407f4a6a1b8e839a3b6baa2b4141.png)
XML解析
使用程序读取XML中的数据
两种解析方式
SAX解析
DOM解析
Dom常见的解析工具
名称 | 说明 |
JAXP | SUN公司提供的一套XML的解析的API |
JDOM | JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。 |
dom4j | 是JDOM的升级品,用来读写XML文件的。具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom 技术,同时它也是一个开放源代码的软件,Hibernate也用它来读写配置文件。 |
jsoup | 功能强大DOM方式的XML解析开发包,尤其对HTML解析更加方便 |
![](https://img-blog.csdnimg.cn/img_convert/4458198e83051243ee63284eb9292506.png)
Dom4j解析XML-得到Document对象
SAXReader类
构造器/方法 | 说明 |
public SAXReader() | 创建Dom4J的解析器对象 |
Document read(String url) | 加载XML文件成为Document对象 |
Document类
方法名 | 说明 |
Element getRootElement() | 获得根元素对象 |
Dom4j解析XML的元素、属性、文本
方法名 | 说明 |
List<Element> elements() | 得到当前元素下所有子元素 |
List<Element> elements(String name) | 得到当前元素下指定名字的子元素返回集合 |
Element element(String name) | 得到当前元素下指定名字的子元素,如果有很多名字相同的返回第一个 |
String getName() | 得到元素名字 |
String attributeValue(String name) | 通过属性名直接得到属性值 |
String elementText(子元素名) | 得到指定名称的子元素的文本 |
String getText() | 得到文本 |
XPath介绍
XPath在解析XML文档方面提供了一独树一帜的路径思想,更加优雅,高效
XPath使用路径表达式来定位XML文档中的元素节点或属性节点。
示例
/元素/子元素/孙元素
//子元素//孙元素
Document中与Xpath相关的API如下:
方法名 | 说明 |
Node selectSingleNode("表达式") | 获取符合表达式的唯一元素 |
List<Node> selectNodes("表达式") | 获取符合表达式的元素集合 |
Xpath的四大检索方案
绝对路径
采用绝对路径获取从根节点开始逐层的查找/contactList/contact/name节点列表并打印信息
方法名 | 说明 |
/根元素/子元素/孙元素 | 从根元素开始,一级一级向下查找,不能跨级 |
相对路径
先得到根节点contactList
再采用相对路径获取下一级contact 节点的name子节点并打印信息
方法名 | 说明 |
./子元素/孙元素 | 从当前元素开始,一级一级向下查找,不能跨级 |
全文检索
直接全文搜索所有的name元素并打印
方法名 | 说明 |
//contact | 找contact元素,无论元素在哪里 |
//contact/name | 找contact,无论在哪一级,但name一定是contact的子节点 |
//contact//name | contact无论在哪一种,name只要是contact的子孙元素都可以找到 |
属性查找
在全文中搜索属性,或者带属性的元素
方法名 | 说明 |
//@属性名 | 查找属性对象,无论是哪个元素,只要有这个属性即可。 |
//元素[@属性名] | 查找元素对象,全文搜索指定元素名和属性名。 |
//元素//[@属性名=‘值’] | 查找元素对象,全文搜索指定元素名和属性名,并且属性值相等。 |
工厂设计模式
之前我们创建类对象时, 都是使用new 对象的形式创建,在很多业务场景下也提供了不直接new的方式 。
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一, 这种类型的设计模式属于创建型模式,它提供了一种获取对象的方式。
工厂设计模式的作用:
工厂的方法可以封装对象的创建细节,比如:为该对象进行加工和数据注入。
可以实现类与类之间的解耦操作(核心思想)。
装饰设计模式?
创建一个新类,包装原始类,从而在新类中提升原来类的功能。
装饰设计模式的作用:
作用:装饰模式指的是在不改变原类的基础上, 动态地扩展一个类的功能。
InputStream(抽象父类)FileInputStream(实现子类,读写性能较差)BufferedInputStream(实现子类,装饰类,读写性能高)
定义父类。
定义原始类,继承父类,定义功能。
定义装饰类,继承父类,包装原始类,增强功能!!