Java
JavaSE
环境
JDK/JRE/JVM
JDK:Java Development Kit,Java语言软件开发工具包
JRE:Java Runtime Environment,Java运行环境
JVM:Java Virtual Machine,Java虚拟机
JDK = JRE + Java开发工具集
JRE = JVM + JavaEE标准类库
包含关系:JDK { JRE (JVM,JavaEE ) }
开发工具集
javac.exe、java.exe、javadoc.exe
运行过程:
- .java文件 (源文件)→ javac.exe (编译)→ .class文件(字节码文件)→ java.exe(运行)→ 结果
- 编译完源程序后,生成一个或多个字节码文件,然后使用JVM中的类的家在其和解释器对生成的字节码文件进行解释运行,需要将字节码文件对应类加载到内存中,涉及到内存解析
字节码文件中不包含注释内容
path变量
JAVA_HOME = bin的上一层目录
path = %JAVA_HOME%\bin
基础
文档注释:
- /**
*/ - 注释内容可以被javadoc解析,生成网页形式的说明文档
API:application programming interface
- 应用程序编程接口(Java提供的一系列类库)
文件:一个源文件中可以声明多个class,只能有一个类声明为public(与文件名同名的类)
程序的入口:main()方法,格式是固定的
- public static void main( )
- 有多少类,编译后就生成多少个字节码文件。字节码文件名与类名一一对应
应用程序 = 算法 + 数据结构
编码/解码
编码:将字符串转换为字节数据
解码:编码的逆过程
常见编码表:
- ASCII:美国标准信息交换码
- 用一个字节的7位可以表示
- ISO8859-1:拉丁码表/欧洲码表
- 用一个字节的8位表示
- GB2312:中国的中文编码表
- 最多两个字节编码所有字符
- GBK:中国的中文编码表升级,融合了更多的中文文字符号
- 最多两个字节编码
- Unicode:国际标准码,融合了目前人类使用的所有字符
- 为每个字符分配唯一的字符码
- 所有的文字都用两个字节来表示
- UTF-8:变长的编码方式
- 可用1-4个字节来表示一个字符
命名规范
包名:多单词组成时,所有字母都小写
- xxxyyyzzz
类名,接口名:多单词组成时,首字母都大写
- XxxYyyZzz
变量名,方法名:多单词组成时,第一个单词首字母小写,其余单词首字母大写
- xxxYyyZzz
常量名:所有首字母都大写,多单词组成时,每个单词用下划线连接
- XXX_YYY_ZZZ
可用特殊符号 _ $
Java采用unicode字符集,标识符可以用中文
变量使用时先得声明和赋值
取消转译
\ ,eg:\n,\“
变量分类
按数据类型
基本数据类型(primitive type):
- 数值型:
- 整数类型:
- byte:1字节,-128-127(2^8~2^8-1)补码
- short:2字节
- int:4字节(21亿),常用
- long:8字节
- 赋值时以“L”或“l”结尾(否则会识别为int型)
- 浮点数型:
- float:单精度,4字节,表述范围比long大
- 赋值时以“F”或“f”结尾
- double:双精度,8字节,常用
- float:单精度,4字节,表述范围比long大
- 整数类型:
- 字符型(char):一个字符(1~2字节),声明时需要一对单引号
- 识别转译字符
- 可使用unicode编码,eg:\u0123
- 布尔型(boolean):只有“true”或“false”
引用数据类型(reference type):
- 类(class)(字符串是类类型的)
- 接口(interface)
- 数组([array])
基本数据类型运算规则
自动类型提升:
- byte / short / char➡️int➡️long➡️float➡️double
- 表示数范围小的向大的靠拢
强制类型转换:
- 自动类型提升运算的逆运算
- 需要强转符( )
整型常量默认为int型,浮点型常量默认为double型
进制
二进制以 0b 或 0B开头
八进制以 0 或 0开头
十六进制以 0x或 0X开头
计算机底层都以补码的方式存储数据
保留字
goto
const
标识符命名规则
特殊符_ &
数字不可以开头
不可以使用关键字和保留字(可以包含)
严格区分大小写,长度无限制
标识符不能包含空格
运算符
%取模,eg:7 % 5 ➡️ 2
运算结果的正负与被模数相同
++X,先运算后取值,eg:a = 2; b = ++a ➡️ a = 3; b = 3
X++,先取值后运算,eg:a = 2; b = a++ ➡️ a = 3; b = 2
1 |
|
赋值运算符
+=,-=,*=,/=,&=不会改变原有数值类型
比较运算符的结果都为boolean型
< < >= <= 只能使用在数值类型的数据之间
== != 可以使用在其他引用类型变量之间
对于引用数据类型来说,比较的是它们的地址值是否相同
1 |
|
逻辑运算符
&逻辑与
&&短路与a&&b时,a为false,b不进行运算
|逻辑或
||短路或a&&b时,a为true,b不进行运算
!逻辑非
^逻辑异或a与b不一样为true
x++==2先比较后运算
位运算符
<<左移:
- 3<<2 = 12 ➡️ 011<<2 = 01100 = 12
‘>>’右移:
- 3>>1 = 1 ➡️ 011>>1 = 01 = 1
- 整型变量,左移1位乘2,右移1位除2
‘>>>>’无符号右移:
- 被移位二进制最高位空缺都拿0补
- &与运算eg: 6 & 3 = 2 ➡️ 110 & 011 = 010 = 2
- |或运算eg: 6|3 = 7 ➡️ 110 | 011 = 111 = 7
- ^异或运算eg: 6 ^ 3 = 5 ➡️ 110 ^ 011 = 101 = 5
- ~取反运算eg: ~6 = -7 ➡️ ~0110 = 1001 = -0111 = -7
1 |
|
三元运算符
结果为boolean
类似if
(条件表达式)? 表达式1 : 表达式2
表达式1和表达式2的要求是一致的(数值,字符串。。。),如果不一致会自动类型提升
都可改写成if else(if else 不一定能改写成三元运算符)
效率比if else高
随机数
int xxx = (int)(Math.random() [0.0, 1.0)
公式:[a, b] : (int)(Math.random() * (b - a + 1) + a
流程控制
分支结构
if-else:语句只有一行时可省略大括号
switch-case:根据switch表达式中的值
- 依次匹配各个case中的常量
- 匹配成功则进入相应case结构执行语句
- 如没遇到break;则仍然向下执行其他case至末尾结束为止
- 表达式只short/int/枚举类型/String
- case后只能跟常量
- break是可选的
- default是可选的且位置不固定
- case的执行语句相同可以合并
- 优先使用
循环结构
for:循环条件是boolean类型的
- 增强for循环:for (数组/集合元素类型 局部变量 : 数组/集合对象) {}
while:while和for能互相转换
无限循环for(;;)或while(true)
do while:至少会执行一次循环体
- break结束当前循环
- 可以使用标识跳出指定循环
- continue结束当次循环
- 可以使用标识跳出指定循环的当次循环
时间
System.currentTimeMillis();
获取当前时间到1970/01/01-00:00:00的长度(ms)
数组
有序可重复的
多个相同类型数据按一定顺序排列的集合
属于引用型数据变量
创建数组对象会在内存中开辟一整块连续的空间
长度一旦确定无法更改
一旦定义好,元素的类型也就确定了
角标从0开始(SQL表从1开始)
对于添加/删除/插入等操作效率低
一维数组
静态初始化:new一次就新建一个数组
数组初始化和数组元素赋值操作同时进行
int[] ids = new int[]{1001,1002,1003,1004};
1
2
3
4
5
动态初始化:数组初始化和数组元素赋值操作分开进行
```java
String[] names = new String[5];
字符索引:charAt()
获取字符串长度:length
默认初始化值:
- 整型:0
- 浮点型:0.0
- char型:0(不是’0’)
- boolean型:false(false:0,true:1)
- 引用数据类型:null(不是“null”)
- (引用类型存放数据要么是null,要么是地址值)
array1 = array2;
- 赋值array1变量为array2
- 它们使用一个数组
- 不是复制数组
二维数组
由数组构成的数组(第一个是行,第二个是列)
静态初始化:
1 |
|
动态初始化1:
1 |
|
动态初始化2:
1 |
|
字符串长度:
1 |
|
默认初始化值:
1 |
|
常见异常
数据角标越界异常:ArrayIndexOutOfBoundsExcetion
空指针异常:NullPointerException
数组为Null
排序算法
算法优劣:
- 时间复杂度
- 空间复杂度
稳定性:若A,B相等,排序后A,B的先后次序保持不变,则是稳定的
算法分类:
- 内部排序:不用借助外部存储器,所有排序操作都在内存中完成
- 外部排序:需要借助外部存储器,由多次内部排序组成
十大内部排序算法:
- 选择排序:
- 直接选择排序
- 堆排序
- 交换排序:
- 冒泡排序:时间复杂度:O(n^2)
- 快速排序:时间复杂度:O(nlogn)
- (平均时间最快)
- 插入排序:
- 直接插入排序
- 折半插入排序
- Shell(希尔)排序
- 归并排序
- 桶式排序
- 基数排序
内存简化结构
虚拟机栈:VM Stack
- 局部变量
- 一个线程一份
堆:Heap
- new出来的结构:
- 对象,数组
- 对象的(非static的)属性
- 一个进程一份
- 分为:
- 新生区
- 养老区
- 永久储存区(方法区)
方法区:Method Area
- 类的加载信息
- 常量池:
- 静态域:
- 一个进程一份
程序计数器:Program Counter Register
- 一个线程一份
本地方法栈:Native Method Stack
面向对象
对象:
实际存在在该类事物的每个个体
也称为实例(instance)
对象的三要素:
属性(对象是什么)
方法(对象能做什么)
事件(对象如何响应)
面向对象的重点:类的设计(也就是类的成员的设计)
面向对象结构:
属性(成员变量,field,域,字段)
方法(成员方法,函数,method)
面向过程与面向对象:
面向过程:
- 强调功能行为
- 以函数为最小单位,考虑怎么做
面向对象:
- 强调具备了功能的对象
- 以类/对象为最小单位,考虑谁来做
类及类的成员:属性,方法,构造器,代码块,内部类
面向对象的三大特征:封装,继承,多态
其他关键字:this,super,abstract,interface,static,final,package,import
创建类的对象 = 类的实例化
万事万物皆对象:在Java中,都将功能结构等封装到类中,通过类的事计划来调用具体的功能结
构:Scanner,String等
文件:File
网络资源:URL
Java与前后端交互时,前后端的结构在Java层面交互时都体现为类,对象
类和对象的使用:
创建类,设计类的成员
创建类的对象
通过”对象.属性“或”对象.方法“调用对象的结构
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的(非static的)属性
如果修改一个对象属性a,则不影响另外一个对象属性a的值
类/成员
类
对一类事物的表描述
抽象的,概念上的定义
类也是对象
String
String字符串属于引用数据类型,声明时使用“”
String可以和8种基本基本数据类型变量做运算(包括boolean),只能是连接运算+
String不能用 == 比较,要用 xxx.equals() 比较
String转换char:
1 |
|
Scanner
键盘获取不同类型的变量:
导包:import java.util.Scanner;(写在类前面)
Scanner实例化Scanner scan = new Scanner(System.in);
调用Scanner类的相关方法来获取制定类型的变量
(next() / nextXxx())除了String都是后者
没提供char型
输入数据类型与要求不匹配时会报异常InputMismatchException
Arrays
1 |
|
Object
Java的根父类
类没有声明其父类,默认父类为java.lang.Object类
Object所有功能具有通用性
Tread
包装类
使基本数据类型变量具有类的特征
父类Number:
- byte → Byte
- short → Short
- int → Integer
- 内部定义IntegerCache结构,其中定义Integer[]数组
- 保存-128~127范围的整数
- long → Long
- float → Float
- double → Double
- boolean → Boolean
- 空值为null
- true(忽略大小写)为true
- 其他为false
- char → Character
基本数据类型与包装类的转换:
- 基本数据类型 → 包装类:调用包装类的构造器
- 包装类 → 基本数据类型:.intValue()
- 自动装箱/拆箱:基本数据类型与包装类自动转换
- Integer内部定义IntegerCache结构,其中定义Integer[]数组用来保存-128~127范围的整数。如果使用自动装箱的方式且赋值的范围在[-128,127]中时,可以直接使用数组中的元素,不用new/销毁,提高效率,此时两个范围在[-128,127]且相等的数比较运算为true,其他为false
基本数据类型/包装类与String类型的转换:
- 基本数据类型/包装类 → String类型:连接运算(+ “”)
- 调用String类的方法:valueOf(Xxx xxx)
- String类型 → 基本数据类型/包装类:
- 调用包装类的方法:Xxx.parseXxx(String string)
内部类
将一个类A声明在另一个类B中:
- 类A:“内部类”
- 类B:”外部类“
分类:
- 成员内部类:
- 放入类中
- 分为:
- 静态内部类:
- 创建实例:ClassA.ClassAa xxx = new ClassA.ClassAa();
- 非静态内部类:
- 创建实例:
- ClassA xxx = new ClassA();
- ClassA.ClassAa yyy = xxx.new ClassAa();
- 静态内部类:
- 调用外部类的方法:ClassA.this.method();
- 字节码文件:外部类名$内部类名.class
- 局部内部类:可放入方法,代码块,构造器内
- 字节码文件:外部类名$数字内部类名.class
- 在局部内部类的方法中调用局部内部类所声明的方法中的局部变量时,此局部变量需声明为final的
属性
按是否使用static修饰分为:
- 静态属性(静态变量):
- 创建类的多个对象,多个对象共享同一个静态变量
- 当通过某个对象修改静态变量时,会导致其他对象调用此静态变量时时修改过的
- 非静态属性(实例变量):
- 创建类的多个对象,每个对象都独立的拥有一套类中的非静态属性
- 当修改其中一个对象的非静态属性时,不会导致其他对象中同样的属性值的修改
属性与局部变量的相同点:
- 格式相同
- 先声明后使用
- 有其对应作用域
属性与局部变量的不同点:
- 在类中的声明位置不同:
- 属性直接在类的一对{}内
- 局部变量声明在:方法内,方法形参,代码块内,构造器形参,构造器内部的变量
- 权限修饰符不同:
- 常用的权限修饰符:private,public,缺省,protected
- 属性:可以在声明属性时,指明权限,使用权限修饰符
- 局部变量:不可以使用权限修饰符
- 默认出初始化值(隐式赋值)不同:类的属性都有初始化值
- 整型:byte/short/int/lang:0
- 浮点型:float/double:0.0
- 布尔型:boolean:false
- 字符型:char:0 或 ‘\u0000’
- 引用数据类型:String:null
局部变量没有初始化值
形参在调用时赋值即可
加载的位置不一样:
- 非static属性加载到堆空间中
- 局部变量加载到栈空间中
赋值先后顺序
- 默认初始化
- 显式初始化
- 构造器初始化
- 通过“对象.方法”/“对象.属性”赋值
变量
成员变量:
- 实例变量:不以static修饰
- 只可以被对象调用
- 类变量:以static修饰
- 可以被类和对象调用
- 局部变量:
- 形参(方法构造器中定义的变量)
- 方法局部变量(在方法内定义)
- 代码块局部变量(在代码块内定义)
方法
描述类应该具有的功能
方法的声明:
1 |
|
权限修饰符:private,public,缺省,protected
返回值类型:
- 有返回值:
- 在方法声明时必须指定返回值类型
- 需要return返回制定类型的变量或常量
- 没返回值:
- 返回值类型为void
- 不需要使用return或只能return;
- return:
- 结束方法
- 返回制定数据
- return关键字后面不可声明执行语句
方法名:属于标识符
形参列表:可以声明0个至多个
- 格式:数据类型1 形参1,数据类型2 形参2,……
方法的使用:可以调用当前类的属性,方法
递归方法:
- 方法A中调用方法A
- 方法中不可以定义方法
方法重载
在同一个类中允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
通过对象调用方法时,确定方法名和参数列表来指定方法
“早绑定”
方法重写
override/overwrite
子类可以对父类同名同参的方法,进行覆盖操作
执行程序时子类的方法会覆盖父类的方法
子类中的叫重写的方法,父类中的叫被重写的方法
子类重写修饰符不小于父类被重写的方法的修饰符
子类不能重写父类private方法(不构成重写)
“晚绑定”
返回值类型:
父类被重写方法返回值为void时,子类重写方法返回值只能是void
父类被重写方法返回值为A类时,子类重写方法返回值可为A类或A类的子类
父类被重写方法返回值为基本数据类型时,子类重写方法返回值只能是相同的基本数据类型
子类重写方法抛出的异常类型不大于父类被重写方法抛出的异常类型
(形参) throws 异常的类型 {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
子父类中同名同参的方法要么都声明为非static的(考虑重写)
要么都声明为static的(静态方法不可以重写)
##### 可变形参
传入的参数个数可以是0个至多个
可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
可变个数形参的方法与本类中方法名相同,形参相同的数组二者不能共存
可变个数形参在方法的形参中只能声明在末尾(最多只能声明一个)
相当于数组:
```java
public void show(String ... strs) {
for (int num = 0;num < strs.length;num++) {
System.out.println(strs[num]);
}
}
jdk5.0新增内容:
格式:数据类型 … 变量名
public void show(String ... strs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
- 方法参数的传递机制:值传递
- 变量赋值:
- 基本数据类型:赋值变量保存的数据值
- 引用数据类型:赋值变量保存数据的地址值(含变量的数据类型)
- 形参:方法定义时,声明的小括号内的参数
- 实参:方法调用时,实际传递给形参的数据
- 传递机制:
- 如果参数时基本数据类型,实参赋给形参的是实参真实存储的数据值
- 如果参数时引用数据类型,实参赋给形参的是实参存储数据的地址值
- 递归方法:包含了一种隐式的循环
##### main
程序的入口
普通的静态方法
形参可以与控制台交互
##### equals
==与equals()的区别:
- ==:运算符
- 可以使用在基本数据类型变量和引用数据类型中
- 基本数据类型变量:比较两个变量数据是否相等(有自动类型提升)
- 引用数据类型变量:比较两个对象的地址值是否相同
- equals():是方法而非运算符
- 只能用在引用数据类型中
- 在Object中,定义与==相同
- 在String,Date,File,包装类等都重写次方法
- 比较两个对象的实体内容是否相同
##### toString
输出一个对象的引用时,就是调用当前对象的toString()
在String,Date,File,包装类等都重写次方法来返回实体内容信息
##### wait
##### notify
##### notifyAll
#### 构造器
构造方法,constructor
作用:创建对象
- 创建类的对象:new + 构造器
- Person p = new Person();
如果没有显示的定义类的构造器,则系统默认提供一个空参的构造器
定义构造器的格式:修饰权限符 类名(型参列表){}
初始化对象的属性:
```java
public Person1(int n) {
age = n;
}
构造器也可以重载
一旦显示定义类的构造器,系统就不再提供默认的空参构造器
一个类中至少会有一个构造器
代码块
又称初始化块
用来初始化类,对象
如果要修饰的话只能用static修饰
静态代码块:
- 随着类的加载而执行
- 可以在创建类时对类的属性等进行初始化
非静态代码块:
- 随着对象的创建而执行
- 每创建一个对象就执行一次
- 可以在创建对象时对对象的属性等进行初始化
如果一个类中定义了多个代码块,则按照声明的先后顺序执行
静态代码块的执行优先于非静态代码块
先后顺序:由父及子,静态先行
- 默认初始化 → 显示初始化/代码块中赋值 → 构造器中初始化 → 对象.属性/方法赋值
特性
封装和隐藏
高内聚,低耦合
把该隐藏的隐藏起来,该暴露的暴露出来
赋值操作收到属性的数据类型和存储范围制约
实际问题中往往给属性赋值加入额外的限制条件
这个条件不能在属性声明中体现,只能通过方法进行条件的添加,同时需要避免用户在用“对象.属性”的方式对属性进行赋值
(将属性声明为私有的)此时针对属性就体现了封装性
封装性的体现:
- 将类的属性xxx私有化private
- 同时提供公共public的方法来获取getXxx和设置setXxx次属性的值
- 属性,方法,类,构造器私有化 (单例模式将构造器私有化)
权限修饰符:
- Java提供了4种权限修饰来修饰类及类的内部结构
- 体现类及类的内部结构在被调用时的可见性的大小:
- public > protected > 缺省 > private
4种权限都可以修饰类的内部结构:属性,方法,构造器,内部类
修饰类只能用缺省和public
修饰符 | 内部类 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | T | |||
缺省 | T | T | ||
protected | T | T | T | |
public | T | T | T | T |
继承性
extends:延展/扩展
减少代码的冗余,提高代码的复用性,便于功能的扩展
为多态性的使用提供了前提
一旦子类A继承父类B以后,子类A中就获取了父类B中所有的声明结构:属性/方法
子类不可直接调用父类声明为私有的属性/方法
子类可以定义自己的属性/方法:实现功能的扩展
子类和父类的关系不同于子集和集合的关系
格式:class A extends B {}
- A:子类/派生类/subclass
- B:父类/超类/基类/superclass
- 子类→父类(实线箭头)
规定:
- 就近原则
- 一个父类可以有多个子类
- Java类的单继承性:一个类只能有一个父类
- 类可以多层继承:直接父类,间接父类
- 所有类都直接/间接继承于java.lang.Object类(除java.lang.Object)
子类实例化
子类继承父类后就获取类父类中声明的属性或方法(继承性)
创建子类的对象,在堆空间中就会加载所有父类中声明的属性
通过子类构造器创建子类对象时,一定会直接/间接调用其父类的构造器
直到调用了java.lang.Object类中空参的构造器
虽然创建子类对象调用了父类的构造器,但始终只创建过new的一个子类对象
多态性
一个事物的多种形态,实现代码的通用性
对象的多态性:父类的引用指向子类的对象
调用子父类同名同参的方法时执行的是子类重写方法(虚拟方法调用)
虚拟方法调用:
- 编译期,只能调用父类中父类中声明的方法
- 运行期,实际执行子类重写的方法
- 编译看左边,运行看右边
- 只适用于方法,不适用于属性
- “晚绑定”
内存中实际加载了子类特有的属性和方法
变量声明为父类类型,编译时只能调用父类声明的属性方法
子类特有的属性方法不能调用
可使用强制类型转换符向下转型
使用强转时可能出现ClassCastException的异常
向上/向下转型
向上转型:多态
向下转型:用向下转型解决声明为父类类型的变量无法使用子类特用属性和方法的问题
关键字
package
包
更好的实现项目中类的管理
使用package声明类或接口所属的包,声明在源文件首行
属于标识符,需遵循标识符的命名规范(全小写),见名知意
每点一次代表一层文件目录
同一个包下不能命名同一个接口或类
import
导入
在源文件中显示的导入指定包下的类,接口
声明在包和类的中间
可以使用xxx.*的方式导入xxx包下的所有结构(不包括其子包下的结构)
使用java.lang包和本包里的类和接口可以省略定义
源文中使用了不同包下的同名的类,则至少有一类需要以全类名的方式显示
import static 导入指定类或接口中的静态结构:属性或方法
this
可以用来修饰属性,方法;构造器
一般省略
除非方法形参和类的属性重名时,使用this.xxx表名此变量不是形参
this:当前对象/当前正在创建的对象
this.xxx:当前对象的xxx属性
this.xxx():当前对象的xxx方法
在类的构造器中可以显示使用this.(形参列表)调用本类中其他构造器
this.(形参列表)必须在当前构造器的首行(最多声明一个)
就近原则
super
理解为:父类的
可以用来调用属性,方法,构造器
可以在子类的方法/构造器中使用super.属性/方法的方式显式调用父类的
通常省略,当同名时必须显式使用
可以在子类构造器中显式调用父类指定的构造器:super(形参列表);
super(形参列表);必须声明在子类构造器的首行(this,super二选一)
默认super();
instanceof
为了避免向下转型时出现的异常
a instanceof A:判断对象a是否是类A的实例,是为true错为false
a instanceof A为true,a instanceof B也为true,则B为A的父类
接口
interface
和类是并列关系
不能实例化
通过让类去实现(implements)的方式来使用,如果实现类实现接口中所有抽象方法则可实例化
类可实现多个接口,弥补了Java单继承性的局限性
格式:class ClassA extends ClassB implements InterfaceA, InterfaceB
接口与接口间可以多继承
具体使用体现多态性
可以看作是一种规范
定义
JDK7
全局常量:public static final(书写时可省略)
- 若子类继承父类和实现接口声明了同名的属性会报错,必须显式区分
抽象方法:public abstract(书写时可省略)
JDK8
静态方法:public static void method(){}(书写时可省略)
- 只能通过接口调用
默认方法:public default void method(){}(书写时可省略)
- 通过实现类的对象可以调用接口中的默认方法
- 可以被重写/实现
- 若子类继承父类和实现接口声明了同名同参且没被重写的方法,默认调用父类的方法(类优先原则)
- 若实现类实现了多个接口,且多个接口都定义了同名同参且的默认方法,实现类没有重写次方法的情况下会报错(接口冲突),则必
- 须重写此方法
- 调用接口中的默认方法:InterfaceX.super.method()
JDK9
方法访问权限修饰符可声明为private
修饰符
static
静态的
可修饰属性、方法、代码块、内部类:
- 修饰属性:
- 静态变量(静态属性,类变量)
- 随着类的加载而加载(早于对象的创建)
- 由于类只会加载一次,则静态变量在内存中也只会存在一份:方法区的静态域中
- 类中的常量也常被声明为静态的
- 修饰方法:
- 静态方法,只能调静态结构
- 随着类的加载而加载,可以通过类.方法的方式进行调用
- 静态方法只能调用静态的方法或属性,非静态方法则否可以用
- 静态方法内不能使用this、super关键字
- 操作静态属性的方法通常设置为静态
- 工具类中的方法习惯上声明为静态方法
final
最终的
可修饰类,方法,变量:
- 修饰类:此类不能被继承
- eg:string
- 修饰方法:此方法不能被重写
- eg:getclass()
- 修饰变量:变量成为常量
- 可以显示初始化,代码块中初始化,构造器中初始化属性
static final:
- 修饰属性:全局常量
- 修饰方法
abstract
“抽象的”
可修饰类/方法:
- 修饰类:”抽象类“
- 此类不能实例化
- 让子类对象实例化完成相关操作
- 修饰方法:“抽象方法”
- 只有方法声明没有方法体:{}
- 包含抽象方法的类一定是抽象类
- 子类重写父类所有抽象方法后才可实例化,否则子类仍为抽象类
native
匿名
匿名对象
创建的对象没有显示的赋给一个变量名
匿名对象只能调用一次
匿名类
匿名子类的非匿名对象:
1 |
|
匿名子类的匿名对象:
1 |
|
JavaBean
一种Java语言写成的可重用组件
指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get,set方法
UML
+:public类型
-:private类型
#:protected类型
方法的写法:方法类型 方法名(参数名:参数类型):返回值类型
JUnit单元测试
要求:
- 公共类
- 有公共的无参构造器
声明的单元测试方法:
- 权限是public
- 没有返回值
- 没有形参
- 方法声明上需要声明注解@Test
- import org.junit.Test;
- 声明完成后就可移在方法体内测试相关的代码
设计模式
MVC
将整个程序分为三个层次:
- 数据模型层model:主要处理数据
- 视图模型层view:显示数据
- 控制器层controller:处理业务逻辑
类中声明另外一个类为关联关系
单例模式
“singleton”
需要手写
某个类只能存在一个对象实例
减少系统性能开销
构造器为私有的,只用调用该类的某个静态方法创建对象
饿汉式:
- 私有化类的构造器
- 内部创建类的静态对象
- 提供公共的静态方法返回类的对象
- 优点:线程安全的
- 缺点:对象加载时间过长
懒汉式:
- 私有化类的构造器
- 声明当前类的静态对象,没有初始化
- 声明公共静态的返回当前类对象的方法
- 优点:延迟对象的的创建
- 缺点:目前的写法导致线程不安全
模版方法
“template”
适用于整体步骤固定通用,某些部分易变的情况
代理模式
“proxy”
安全代理,远程代理等
用到接口
工厂模式
“factory”
实现了创建者与调用的分类
用到接口
异常处理
体系结构
java.lang.Throwable
Error:java.lang.Error
- Java虚拟机无法解决的严重问题
- 一般不编写针对性的代码进行处理
- eg:
- StackOverflowError
- OOM
Exception:java.lang.Exception
- 其他因编程错误或偶然的外在因素导致的一般性问题
- 可以使用针对性的代码进行处理
- 分为:
- 编译时异常(受检异常):
- IOException
- FileNotFoundException
- ……
- ClassNotFoundException
- ……
- IOException
- 运行时异常(非受检异常):
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
- ……
- 编译时异常(受检异常):
处理机制
异常对象的产生:系统自动生成
手动生成并抛出:Throw,表示抛出一个异常类的对象,生成异常对象的过程
抓抛模型:
- 过程1:“抛”
- 程序正常执行过程中一旦出现异常就会在异常处生成一个对应异常类的对象并将其抛出
- 一旦抛出对象后剩余代码将不再执行
- 过程2:“抓”
- 异常的处理方式:
- try-catch-finally
- throws
- 异常的处理方式:
try-catch-finally
真正的处理异常
1 |
|
可以被嵌套
子异常类必须声明在父异常类上
Try结构中的变量出了结构后不能再被调用
一旦try中的异常对象匹配到某个catch时,就进入其中进行处理,完成后跳出try-catch结构
finally是可选的
即使catch中出现异常/try-catch中有return,finally中语句仍然执行
数据库连接/输入输出流/网络编程Socket等资源,JVM不能自动回收,需要手动进行资源释放,就需要声明在finally中
主要用来处理编译时异常,使其不报错,但运行时仍可能报错(将编译时出现的异常延迟到运行时可能出现)
常用方法:
- String getMessage();
- printStackTrace();
Java8:可实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过
1 |
|
Java9:可在try子句中使用已经初始化过的资源,此时的资源是final的
1 |
|
throws
只将异常抛给方法调用者
属于异常处理的一种方式
throws + 异常类型1,异常类型n
卸载方法声明处,指明执行此方法时可能会抛出的异常类型
一旦执行的方法出现的异常满足throws的异常类型时就会被抛出,后续代码不被执行
子类重写方法抛出的异常类型不大于父类被重写方法抛出的异常类型
使用情景
若父类被重写的方法没有throws方式处理异常,则子类重写方法也不能使用throws,只能用try-catch-finally
若执行方法a中递进的调用其他方法执行,则可以使用throws方法,执行的方法a可以使用try-catch-finally方法
自定义异常类
继承于现有的异常结构:
- RuntimeException
- Exception
提供全局常量:serialVersionUID
提供重载的构造器
线程
程序:program
- 一段静态的代码
进程:process
- 正在运行的程序
- 作为资源分配的单位
线程:thread
- 程序内部的一条执行路径
- 作为调度和执行的单位
- 每个线程拥有独立的运行栈和程序计数器
- 分类:
- 守护线程
- 用户线程
并行:“多人做多事”
并发:“一人做多事”
多线程创建方式
start():
- 启动当前线程
- 调用当前线程的run()
常用方法:
- start()
- run()
- currentTread():静态方法,返回执行当前代码的线程
- getName()
- setName()
- yield():释放当前CPU的执行权,不释放锁
- join():在线程a中调用线程b的join(),此时线程a进入阻塞状态直到线程b完全执行完后才恢复
- stop()过时:强制结束当前线程
- sleep(long milliTime):阻塞milliTime毫秒,不释放锁
- isAlive():判断当前线程是否存活
优先级:
- MAX_PRIORITY:10
- MIN_PRIORITY:1
- NORM_PRIORITY:5
- getPriority():获取当前线程优先级
- setPriority(int i):设置当前线程优先级
- 高优先级的线程只是有较高概率情况下会被执行
继承Thread类
- 继承Thread类
- 重写run()
- 创建子类的对象
- 通过此对象调用start()
若要再启动一个线程,必须重新创建一个Thread子类对象
实现Runnabe接口
- 创建一个实现了Runnable接口的类
- 实现类实现抽象方法run()
- 创建实现类的对象
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
- 通过此Thread类的对象调用start()
实现Callable接口
- 创建Callable的实现类
- 实现call方法
- 创建Callable实现类的对象
- 将此对象作为参数传递到FutureTask的构造器中,创建FutureTask的对象
- 将FutureTask的对象传递到Thread的构造器中,创建Thread对象并start()
- 可选获取call返回值
JDK5
功能比Runnable更强大:
- 有返回值
- 可抛出异常
- 支持泛型
- 需要借助FutureTask类
使用线程池
提供制定线程数量的线程池
执行指定线程的操作,需要提供实现Runnable/Callable接口实现类的对象:
execute()适用于Runnable
callable()适用于Callable
关闭连接池
JDK5.0
提高响应速度
降低资源消耗
便于线程管理
方式比较
优先选择实现Runnable接口方式:
- 没有单继承性局限
- 更适合出了多线程共享数据的情况
Thread类也实现Runnable接口
线程状态
新建:new
就绪:
- start()
- yield()
- 失去CPU执行权
- sleep()时间到
- join()结束
- 获取同步锁
- notify()/notifyAll()
- resume()[过时]
运行:获得CPU执行权
阻塞:
- sleep()
- join()
- 等待同步锁
- wait()
- suspend()过时
死亡:
- 执行完run()
- stop()
- 出现异常且没处理
线程同步
解决线程安全问题
操作同步代码时相当于单线程过程,效率较低
优先使用顺序:
- Lock
- 同步代码块
- 同步方法
synchronized方法
自动释放同步
同步代码块方式:
1 |
|
- 共享数据:多个线程共同操作的变量, 不能多不能少
- 任何一个类的对象都可充当锁,要求多个线程必须共用同一把锁
- 继承类方法可用ClassX.class,慎用this
- 实现接口方法可用this,ClassX.class
同步方法方式:
- 若操作共享数据的代码完整的声明在一个方法中,可将此方法声明为同步的
- 同步监视器:
- 不需要显示的声明
- 继承类方法:把需要同步的方法声明为静态的
Lock方法
手动启动(lock)/释放同步(unlock)
1 |
|
线程死锁
不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源
线程通信
synchronized方法
wait():使调用此方法的线程进入阻塞状态,释放锁
notify():唤醒一个被wait的线程,若多个线程被wait则唤醒优先级高的
notifyAll():唤醒所有被wait的线程
被定义在Object类中
都得在同步方法/同步代码块中
调用者必须是当前的同步监视器
常用类
字符串类
String
声明为fianl,不可被继承
实现Serializable接口,支持序列化
实现Comparable接口,可以比较大小
不可变的字符序列:
- 通过字面量方式给字符串赋值,字符串值的声明在字符串常量池中
- 字符串常量池中不会存储相同内容的字符串
- 对字符串重新赋值是需要重写指定内存区域复制,不能使用原有的value进行赋值
通过new + 构造器的方式赋值时,数据存在堆空间中
常量与常量的拼接在常量池中
变量与常量的拼接在堆中
转换:
- 与基本数据类型:
- String → int:Integer.parseInt()
- int → String:String.valueOf()
- 与char[]:
- String → char[]:toCharArray()
- char[] → String:String(array)
- 与byte[]:
- String → byte[]:getBytes()
- byte[] → String:String(array)
- 编码:字符串 → 字节
StringBuffer
可变的字符序列
线程安全的(效率低)
底层创建了一个长度是 str.length() + 16 的数组
若增加字符超出数组长度,则创建一个新的容量为原来容量2倍+2的数组(value.length << 1 + 2)并复制到新的数组中
为避免多次扩容,建议使用StringBuffer(int capacity)构造器
StringBuilder
可变的字符序列
线程不安全的(效率高)
其它同StringBuffer
执行效率:StringBuilder > StringBuffer > String
时间日期类JDK1
Date
JDK1.0
toString():显示当前的年月日时分秒
getTime():获取当前对象应对的时间戳
util.Date
- sql.Date(util.Date的子类)
SimpleDateFormat
对日期Date类的格式化和解析
实例化:new + 构造器
- 方式之一:SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd GGG hh:mm:ss aaa”)
格式化:日期 → 字符串
- sdf.format(date)
解析:格式化的逆过程
- sdf.parse(“yyyy-MM-dd GGG hh:mm:ss aaa”)
- 字符串必须符合SimpleDateFormat识别的格式(通过构造器参数体现),否则会抛异常
Calendar
JDK1.1 - 一定程度上替换Date类
日历抽象类
具有可变性
实例化:
- 方法1:创建其子类GregorianCalendar类的对象
- 方法2:调用其静态方法getInstance()
时间日期类JDK8
LocalDate/LocalTime/LocalDateTime
分别表示使用ISO-8601日历系统的日期/时间/日期和时间
类似于Calendar
使用.of()构造器创建对象时没有偏移量
有不可变性
Instant
时间线上的瞬时点
自1970/01/01-00:00:00(UTC开始的毫秒数)
类似于Date
DateTimeFormatter
格式化解析日期/时间
类似于SimpleDateFormat
常用自定义格式.ofPattern(“yyyy-MM-dd hh:mm:ss”)构建对象
比较器类
Java中的对象只能比较==或!=,若想比较<或>需要使用Comparable/Comparator接口
Comparable
自然排序
一旦指定,实现类的对象在任何都可比较大小
String/包装类等实现了Comparable接口,重写了compareTo()方法后进行从小到大的排列
重写compareTo(obj)的规则:
- 当前对象this大于形参对象obj,返回正整数
- 当前对象this小于形参对象obj,返回负整数
- 当前对象this大于形参对象obj,返回零
自定义类若需要排序,可以实现Comparable接口,重写compareTo()方法,在compareTo()方法指明如何排序
Comparator
定制排序
临时性的比较(常用作匿名实例化)
适用类型:
- 元素的类型没实现Comparable接口且不方便修改代码
- 实现了Comparable接口排序规则不适合当前的操作
重写compareTo(obj1, obj2)的规则:
- 当前对象obj1大于形参对象obj2,返回正整数
- 当前对象obj1小于形参对象obj2,返回负整数
- 当前对象obj1大于形参对象obj2,返回零
其它类
System
“系统的工具类”
方法:
currentTimeMilis():获取当前时间到1970/01/01-00:00:00的长度(ms)
- 称为时间戳
void exit(int status):在图形界面编程中实现程序的退出功能等
- status为0代表正常退出
Math
数学工作类
BigInteger/BigDecimal
数据长度过长/数据精度要求高时使用
BigInteger可以表示不可变的任意精度的整数
BigDecimal可以表示不可变的任意精度的浮点数
枚举类
JDK5
类的对象只有有限个且确定的
当需要定义一组常量时强烈建议使用枚举类
若枚举类中只有一个对象则是单例模式的一种实现方式
定义
自定义枚举类:
- JDK5前
- 私有化类的构造器
- 使用private final修饰对象属性
- 提供多个public static final的对象
enum关键字定义:
- JDK5后
- 默认继承于java.lang.Enum
- 一开始就需要提供当前枚举类的对象,用逗号隔开,末尾分号结束,后面步骤同上
1 |
|
常用方法
values():返回枚举类型的对象数组,可以很方便地遍历所有枚举值
valueOf(String str):把字符串转换为对应的枚举类对象,字符串必须是枚举类对象的名称(区分大小写)
- 若没有str的枚举类对象则抛异常IllegalArgumentException
toString():返回当前枚举类对象常量的名称
实现接口
使用关键字定义的枚举类实现接口的情况
情况1:所有枚举类实现统一方法
- 实现接口,在enum类中实现抽象方法
情况2:每个枚举类的对象分别实现接口中的抽象方法
- 在枚举类的对象后面加上{}并在其中分别实现抽象方法
注解
Annotation
JDK5
代码里的特殊标记
没有成员定义的注解称为标记
若注解有成员,使用时需要指明成员的值
可修饰包,类,构造器,方法,成员变量,参数,局部变量的声明
框架 = 注解 + 反射 + 设计模式
使用示例
生成文档相关的注解
编译时进行格式检查(JDK内置的三个基本注解):
- @Override:限定重写父类方法,只能用于方法
- @Deprecated:表示修饰的元素已过时
- @SuppressWarnings:抑制编译器警告
跟踪代码依赖性,实现替代配置文件功能
自定义注解
参照@SuppressWarnings
声明为@interface
内部定义成员,通常使用value表示
可以使用default定义成员的默认值
若自定义注解没有成员表明是一个标识作用
自定义注解必须配上注解的信息处理流程(使用反射)才有意义
常指明两个元注解:@Retention,@Target
元注解
用于修饰其它注解
@Retention:指定所修饰注解的生命周期
- SOURCE:不保留在字节码文件中
- CLASS(默认):保留在字节码文件中,运行时不加载到内存中
- RUNTIME(可通过反射获取):运行时加载到内存中
@Target:指定所修饰注解能用于修饰哪些程序元素
@Documented:指定所修饰注解将被javadoc工具提取成文档
- 默认情况下javadoc不包括注解
@Inherited:指定所修饰注解会有继承性
- 若某类使用了被@Inherited修饰的注解,则子类将自动具有该注解
@Repeatable:
- JDK8
- 可重复注解
- 在MyAnnotation上声明@Repeatable,成员值为MyAnnotation.class
- MyAnnotation的@Target/@Inherited/@Retention等元注解与MyAnnotations相同
通过反射获取注解
JDK8新特性
可重复注解
类型注解:
- 作为参数声明在@target中
- TYPE_PARAMETER:该注解能写在类型变量的声明语句中(如泛型声明)
- TYPE_USE:该注解能写在使用类型的任何语句中
集合
集合/数组都是对多个数据进行存储操作的结构,简称Java容器
内存层面的存储,不涉及到持久化的存储
解决数组存储数据方面的弊端
Collection接口
单列数据,用于存储一个一个的对象
添加数据obj时,所在类需要重写equals()
方法
1 |
|
与数组转换
集合 → 数组:toArray()
数组 → 集合:Arrays.asList()
迭代器
迭代器Iterator接口
每次调用iterator()方法都会得到全新的迭代器对象
迭代器模式就是为容器而生
返回Iterator接口的实例用于遍历集合元素:
- iterator.next():指针下移后返回当前元素
- iterator.hasNext():判断指针后是否还有数据元素
- iterator.remove():遍历的时候删除集合中的元素
- 若还未调用next()/已经调用了remove()方法,再调用remove()会报illegalStateExcetion
List接口
”动态数组“
Collection的子接口
元素有序,可重复的集合
主要实现类:
ArrayList:List的主要实现类
线程不安全(效率高)
使用Object[]数组存储
建议使用带参构造器ArrayList (int capacity)
常用方法:
1
2
3
4
5
6
7
8void add(int index, object element)
boolean addAll(int index, Collection elements)
Object get(int index)
int indexOf(Object obj):返回obj在集合中首次出现的位置
int lastIndexOf(Object obj):返回obj在集合中末次出现的位置
Object remove(int index):移除指定index位置的元素并返回
Object set(int index, Object element)
List sublist(int fromIndex, int toIndex):返回从fromIndex到toIndex位置左闭又开的子合集源码分析:
- 创建数组:
- JDK7:创建长度为10的Object[]数组
- 类似于“饿汉式”
- JDK7:创建长度为10的Object[]数组
- JDK8:底层Object[]数组初始化为{}
- 第一次调用add()时,底层才创建长度为10的数组
- 将数据添加进数组
- 类似于“懒汉式”
- 此方法节省内存空间
- 创建数组:
若添加导致数组容量不够则扩容:
- 默认情况扩容为原来容量的1.5倍
- 将原数组内的数据复制到新数组中
LinkedList:对于频繁插入/删除操作,效率比ArrayList高
- 使用双向链表存储
- 源码分析:内部声明Node类型的first和last属性
- 其中Node定义:
- Node类型的next
- Node类型的prev
- 其中Node定义:
Vector:List的古老实现类(基本不用)
- 线程安全(效率低)
- 使用Object[]存储
- 创建时底层创建长度为10的数组
- 扩容时默认扩容为原来数组长度的2倍
Set接口
Collection的子接口
没有定义额外的新方法
向Set中添加的属性的所属类一定要重写hashCode()和equals()且这两个方法经可能保持一致性:
- 对象中用作equals()方法比较的Field都应该用来计算hashCode
特性:
- 无序性:
- 不等于随机性
- 存储的数据在底层数组中并非按照数组索引的顺序添加
- 根据数据的哈希值进行添加
- 不可重复:
- 添加的元素按照equals()判断是不能返回true(相同的元素只能添加一个)
- 确定性
- 互异性
添加元素的过程:以HashSet为例,在其中添加元素a
- 调用a所在类的hashCode()方法来计算a的哈希值
- 使用此哈希算出在HashSet底层数组中存放的位置(索引位置)
- 判断此位置上又没有其他元素:
- 无其它元素:添加成功
- 有其它元素b/链表形式存在的多个元素:依次比较a与b/多个元素的哈希值
- 不同:添加成功,与已经存在指定索引位置上数据以链表方式存储
- 相同:调用a所在类的equals()方法
- 返回true:添加失败
- 返回false:添加成功,与已经存在指定索引位置上数据以链表方式存储
- JDK7:a放入数组,指向原元素
- JDK8:原元素指向a
- (七上八下)
主要实现类:
- HashSet:Set接口的主要实现类
- 线程不安全的
- 可以存储null值
- LinkedHashList:HashSet的子类
- 添加数据时还有两个引用来记录数据的前后数据
- 遍历内部数据时可以按照添加的顺序遍历
- 若要频繁的遍历操作,LinkedHashSet效率高于HashSet
- TreeSet:保存的所有元素是相同类的对象
- 底层使用二叉红黑树存储
- 可以按照添加对象的指定属性进行排序(默认从小到达排列)
- 比较两个对象是否相同的标准:
- 自然排序:compareTo()返回0
- 定制排序:compare()返回0
Map接口
双列数据,用于存储一对一对(键值对)的数据(key - value):
- Entry:存储一对key和value两个属性
- 无序不可重复的
- 使用Set存储
- key:
- 无序不可重复的
- key所在的类需重写equals()/hashCode()(以hashMap为例)
- 使用Set存储
- value:
- 无序可重复的
- 使用Collection存储
- 类似于y = f(x)
重要常量
1 |
|
方法
1 |
|
实现类
HashMap:Map的主要实现类(类似于HashSet)
- 线程不安全(效率高)
- 可存储null的key和value
- 底层结构:
- JDK7:数组 + 链表
- JDK8:数组 + 链表 + 红黑树
- 当数组索引位置上以链表形式存在的元素数据 > 8 && 数组长度 > 64时,此索引位置上所有数据改为红黑树存储
- 底层实现原理:
- 实例化:
- JDK7:底层创建长度为16的一维数组Entry[] table
- JDK8:底层未创建长度为16的一维数组Node[] table,直至首次调用put()时才创建
- 添加键值对:
- 调用键值对key值所在类的hashCode()计算keyX哈希值
- 通过此哈希值算出在Entry数组中的存放位置
- 位置为空:添加成功
- 位置非空(该位置上存在一或多个数据(以链表存储)):此键值对与目标键值对key值的哈希值逐一比较
- 不同:添加成功,与已经存在指定索引位置上数据以链表方式存储
- 相同:调用此键值对key所在类的equals()比较
- 不同:添加成功,与已经存在指定索引位置上数据以链表方式存储
- 相同:使用键值对value值替换相同key的value值(修改功能)
- 扩容:超出临界值且此索引位置上有数据时,默认扩容为2倍原容量并将原有数据复制到新数组
- 实例化:
LinkedHashMap:HashMap的子类
- 内部提供Entry替换HashMap中的Node
- 可按照添加顺序进行遍历Map元素
- 适用于频繁的便利操作
Hashtable:Map的古老实现类
- 线程安全(效率高)
- 不可存储null的key和value
Properties:Hashtable的子类
- 常用来处理配置文件
- key和value都是String类型
SortedMap:Map的子接口
TreeMap:实现SortedMap接口
- 底层使用红黑树
- 按照添加的key-value进行排序/实现遍历(按key排序)
- Key必须时同一个类创建的对象
Java9方法
快速创建只读集合
使用of()方法创建
使用Map.ofEntries(Map.entry()…)方法创建
Java10方法
快速创建只读集合
copyOf():先判断来源集合是不是AbstractImmutableList类型的
- 是:直接返回
- 否:调用of创建一个新的集合
Collections工具类
操作Collection/Map
1 |
|
泛型
Generic
把元素的类型设计成一个参数,这个参数称为泛型
允许在定义类/接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型,这个类型参数将在使用时确定
泛型类型必须是类
默认为Object
集合与泛型:
- 集合接口/集合类在JDK5都带有泛型结构
- 实例化集合类时可以指明泛型类型
- 在集合类/接口中凡是使用到类的泛型位置都指定为实例化的泛型类型
自定义
泛型类/接口:
- 声明构造器时不需要加<>
- 泛型不同引用不能互相赋值
- 静态方法中不能用类的泛型
- 异常类不能声明为泛型类
不能直接new T[]:使用(T[]) new Object[int] - 若自定义类/接口中带有泛型,建议实例化时指明泛型
- 子类继承有泛型的父类:
- 若指明类型:子类实例化时不再需要指明泛型
- 若不指明类型:子类仍然是泛型类
泛型方法:
- 方法中出现的泛型结构与类的泛型参数没有任何关系
- 泛型方法与所属的类是否泛型无关
- 可以声明为静态:泛型参数在调用方法时才确定
1 |
|
继承
类A为类B的父类
- G与G不具有子父类关系,二者为并列关系
- A
与B 具有子父类关系 - 接口同理
通配符
list<?>不能向其添加除null之外的数据
读取的数据类型为Object
限制条件的通配符
1 |
|
IO流
Java对于数据的输入/输出操作以“流(stream)”的方式进行
分类:
- 按数据单位:
- 字节流(8bit):主要处理非文本数据
- 字符流(16bit):主要处理文本数据
- 按流向:
- 输入流:数据 → 程序
- 输出流:程序 → 数据
- 按角色:
- 节点流:直接与特定的目标相连
- 处理流:套接在已有流的基础上
抽象基类
字节流 | 字符流 | |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
常用类
文件流
节点流
流程:
- 实例化File类的对象,指明要操作的文件
- 提供具体的流
- 数据读入/写出
- 关闭流
- 为了保证流资源一定可以执行关闭操作,需要用try-catch-finally处理
- 读入的文件一定要存在,否则会报FileNotFoundException
FileInputStream:
FileOutputStream:
FileReader:
- read():返回读入的一个字符,若达到文件末尾返回-1
- close():关闭流
FileWriter:
- 输出操作对应的File:
- 不存在:会自动创建此文件
- 存在:以构造器为准
- FileWriter(file ,
):覆盖原文件 - FileWriter(file ,true):原文件后面添加
- FileWriter(file ,
缓冲流
处理流
内部提供缓冲区,默认大小为8Kb
提高流的读取/写入速度
先关外层流,再关内层流:
- 关闭外层流的同时也会关闭内层流,内层流的关闭可以省略
- flush():刷新缓冲区
BufferedInputStream:
BufferedOutputStream:
BufferedReader:
- String readLine():一次读一行
- newLine():新一行(回车)
BufferedWriter:
转换流
字符流/处理流
提供字节流和字符流之间的转换
InputStreamReader:将InputStream转换为Reader(解码)
- 构造器参数2指明字符集(取决于保存源文件时使用的字符集)
OutputStreamWriter:将Writer转换为OutputStream(编码)
标准IO流
处理流
System.in:标准输入流,默认从键盘输入
- 字节流
System.out:标准输出流,默认从控制台输出
可使用System类的setIn(InputStream is)/setOut(PrintStream ps)重新指定输入/输出流
打印流
处理流/输出流
提供一系列重载的print()/println()方法
System.out返回PrintStream的实例
PrintStream
PrintWriter
数据流
处理流
用于读取/写出基本数据类型的变量/字符串
DataInputStream
DataOutputStream
对象流
处理流/字节流
用于存取/读取基本数据类型数据/对象
不能序列化static/transient修饰的成员变量
序列化:将内存中的Java对象保存到磁盘中/通过网络传输
ObjectInputStream:读取(反序列化)
ObjectOutputStream:保存(序列化)
对象可序列化的要求:
- 实现两个接口之一:
- Serializable:
- 对象所在类提供一个全局常量(序列版本号):serialVersionUID
- 对象所在类的其它内部属性均可序列化(默认情况基本数据类型都可序列化)
- Externalizable:
- Serializable:
随机(任意)存取文件流
RandomAccessFile:
- 声明在java.io
- 直接继承java.lang.Object
- 同时实现DataInput/DataOutput接口(输入/输出流)
- 作为输出流时:
- 写出文件不存在:自动创建
- 写出文件存在:对原有文件内容进行覆盖(默认情况从头覆盖)
- 创建实例需要指定一个mode参数来指定此对象的访问模式:
- r:以只读方式打开
- rw:打开以便读取和写入
- rwd:打开以便读取和写入,同步文件内容的更新
- Rws:打开以便读取和写入,同步文件内容和元数据的更新
- long getFilePointer():获取文件记录指针的当前位置
- void seek(long pos):将文件记录指针定位到pos位置
NIO
“New IO”/“Non-Blocking IO”
面向缓冲区,基于通道的IO操作
更高效的方式进行文件读写操作
NIO.2
JDK7
增强对文件处理/文件系统特性的支持
Path:替换原有File类
Paths:
Files:操作文件/文件目录的工具类
File类
一个对象代表一个文件/文件目录
声明在java.io包下
若要读取/写入文件内容,需要使用IO流
File类对象常会作为参数传递到流的构造器中指明读取写入的“终点”
JUnit单元测试法的相对路径在当前Module下
main方法的相对路径在当前Project下
构造器
1 |
|
方法
常量:
1 |
|
获取:
1 |
|
重命名:
1 |
|
判断:
1 |
|
创建:
1 |
|
删除:
1 |
|
网络编程
InetAddress:此类的对象代表一个IP地址
- 构造器私有化
- 实例化方式:
- getByName()
- getLocalHost()
TCP
Socket
ServerSocket
客户端:
- 创建Socket对象,指明服务器IP及其端口号
- 获取输出流用于输出数据
- 写出数据
- 资源关闭
服务器:
- 创建ServerSocket对象,指明自己的端口号
- 调用accept()表示自己接收来自客户端的socket
- 获取输入流
- 读取数据
- 资源关闭
read()为阻塞式的方法,传完数据需要socket.shutdownOutput()指明数据发送完毕
UDP
DatagramSocket
DatagramPacket
反射
Reflection
通过反射可以调用类的私有化结构
Class实例对应加载到内存中的一个运行时类
特征:动态性
正常方式:
引入需要的“包类”名称
通过new实例化
取得实例化对象
反射方式:
实例化对象
getClass()方法
得到完整的包名
主要API
1 |
|
Class对象类型
Class:外部类/成员(成员内部类,静态内部类)/局部内部类/匿名内部类
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
只要元素类型与维度一样,就是同一个Class
加载过程
- 程序经过javac.exe命令后会生成一个/多个字节码文件(.class结尾)
- 使用java.exe命令对某个字节码文件进行解释运行(将此字节码文件加载到内存中,此过程称为类的加载)
- 加载到内存中的类称为“运行时类”,次运行时类就作为Class的一个实例(类本身也是Class的对象/Class的实例对应着一个运行时类)
- 加载到内存中的运行时类会缓存一定的时间,在此时间之内可以通过不同的方式来获取此运行时类
获取Class实例
调用运行时类的属性
1 |
|
通过运行时类的对象
1 |
|
调用Class静态方法
(常用)
1 |
|
使用类的加载器
(了解)
ClassLoader
1 |
|
读取配置文件
使用ClassLoader
方式1:
1 |
|
方式2:
1 |
|
创建运行时类对象
newInstance()
创建对应的运行时类的对象
内部调用了运行时类的空参构造器
运行时必须提供空参构造器且权限满足使用需求
获取结构
获取属性结构:
- Field[] getFields():获取当前运行时类及其父类中声明为public的所有属性
- Field[] getDeclaredFields():获取当前运行时类声明的所有属性(不包含父类)
- int getModifiers():获取权限修饰符
- 使用Modifier.toString(modifier)转为String型
- Class getType():获取数据类型
- String getName():获取变量名
- int getModifiers():获取权限修饰符
获取方法结构:
- Method[] getMethods():获取当前运行时类及其父类中声明为public的所有方法
- Method[] getDeclaredMethods():获取当前运行时类声明的所有方法(不包含父类)
- Annotation[] getAnnotations():获取注解(Runtime)
- int getModifiers():获取权限修饰符
- 使用Modifier.toString(modifier)转为String型
- Class getReturnType():获取返回值类型
- String getName():获取方法名
- Class[] getParameterTypes():获取形参类型
- Class[] getExceptionTypes():获取异常
获取构造器结构:
- Constructor[] getConstructors():获取当前运行时类声明为public的所有构造器
- Constructor[] getDeclaredConstructors():获取当前运行时类声明的所有构造器
获取父类:
- Class getSuperclass()
- 获取带泛型的父类:
- Type getGenericSuperclass()
- 获取带泛型的父类的泛型:
- 使用(ParameterizedType)强转
- Type[] getActualTypeArguments
- 获取带泛型的父类的泛型:
- Type getGenericSuperclass()
1 |
|
获取接口:
- Class[] getInterfaces()
- Class[] getSuperclass().getInterfaces():获取其父类实现的接口
获取所在的包:Package getPackage()
获取注解:Annotation[] getAnnotations()
调用运行时类指定结构
属性
getField方法:(不常用)
- 创建运行时类的对象:aClass.newInstance()
- 获取指定的(public)属性:clazz.getField(String fieldName)
- 设置当前属性的值:set()
- 参数1:设置哪个对象的属性(对象)
- 参数2:设置此属性的值
- 获取当前属性的值:get()
- 参数1:获取哪个对象的属性
getDeclaredField方法:(常用)
- 创建运行时类的对象:aClass.newInstance()
- 获取指定的属性:aClass.getDeclaredField(String fieldName)
- 设置当前属性是可访问的:fieldName.setAccessible(true)
- 获取/设置当前属性的值
方法
创建运行时类的对象:aClass.newInstance()
获取指定的某个方法:aClass.getDeclaredMethod()
- 参数1:指明获取方法的名称
- 参数2:指明获取方法的形参列表
设置当前属性是可访问的:method.setAccessible(true)
调用指定方法:invoke()
- 参数1:方法调用者
- 参数2:为形参赋值
返回值即为对应类中调用方法的返回值(没有返回值则为null)
构造器
获取指定构造器:aClass.getDeclaredConstructor()
- 参数:指明构造器的形参列表
设置当前构造器是可访问的:constructor.setAccessible(true)
调用此构造器创建运行时类的对象:constructor.newInstance()
动态代理
代理模式:
- 用一个代理将对象包装起来,用该代理对象取代原始对象
- 任何对原始对象的调用都要通过代理
- 代理对象决定是否/何时将方法调用转到原始对象上
静态代理:代理类和被代理类在编译期间就确定下来,不利于程序的扩展
动态代理:客户通过代理类调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象
- 通过Proxy.newProxyInstance()实现根据加载到内存中的被代理类动态创建一个代理类及其对象
- 通过InvocationHandler接口的实现类及其方法invoke()实现通过代理类的对象调用方法a时动态调用被代理类的同名方法
1 |
|
Java8
Lambda表达式
匿名函数
一段可以传递的代码
Lambda作为函数式接口的实例
->:Lambda操作符/箭头操作符
->左边:Lambda形参列表
数据类型可以省略,由编译器推断得出(类型推断”)
1
Comparator<Integer> c2 = (o1, o2) -> Integer.compare(o1, o2);
若只有一个参数,参数的小括号可以省略
1
Runnable r2 = () -> System.out.println("Runnable2");
->右边:Lambda体
- 若只有一条语句,return/大括号都可省略
1 |
|
函数式接口
“FunctionalInterface”
只声明了一个抽象方法
可在一个接口上使用@FunctionalInterface注解,这样可以检查它是否是一个函数式接口
匿名实现类都可以用Lambda表达式来写
实际开发中若需要定义函数式接口,可先查看JDK是否提供满足需求的函数式接口
四大核心函数式接口
Consumer
对类型为T的对象应用操作:void accept(T t)
1
2
3
4
5
6
7
8
9
10
11
12
13
14public void consumerTest() {
happyTime(500.0, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("money: " + aDouble);
}
});
happyTime(600.0, money -> System.out.println("money: " + money));
}
private void happyTime(Double money, Consumer<Double> consumer) {
consumer.accept(money);
}
Supplier
- 返回类型为T的对象:T get()
Function<T, R>:函数型接口
- 对类型为T的对象应用操作并返回结果,结果是R类型的对象:R apply(T t)
Predicate
确定类型为T的对象是否满足某约束并返回boolean值:boolean test(T t)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public void predicateTest() {
List<String> list = Arrays.asList("a", "b", "c", "a");
System.out.println(filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return "a".equals(s);
}
}));
System.out.println(filterString(list, s -> "a".equals(s)));
System.out.println(filterString(list, "a"::equals));
}
//根据给定规则过滤集合中的字符串,规则由Predicate方法决定
private List<String> filterString(List<String> list, Predicate<String> predicate) {
List<String> filterList = new ArrayList<>();
for (String s : list) {
if (predicate.test(s)) {
filterList.add(s);
}
}
return filterList;
}
引用
方法引用
当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用
方法引用与接口中的抽象方法的形参列表、返回值类型相同
方法引用就是Lambda表达式,Lambda表达式作为函数式接口的实例,所以方法引用也是函数式接口的实例
三种主要使用情况:
对象::非静态方法名:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致
1
2
3
4
5
6Employee e = new Employee(1001, "Tom", 23, 5600);
Supplier<String> s1 = () -> e.getName();
System.out.println(s1.get());
Supplier<String> s2 = e::getName;
System.out.println(s2.get());类::静态方法名:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致
1
2
3
4
5Comparator<Integer> c1 = (i1, i2) -> i1.compareTo(i2);
System.out.println(c1.compare(1, 2));
Comparator<Integer> c2 = Integer::compareTo;
System.out.println(c2.compare(1, 2));类::非静态方法名:当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数) 时可用
// Comparator中的int compare(T t1,T t2) // String中的int t1.compareTo(t2) Comparator<String> comparator = String::compareTo; // Function中的R apply(T t) // Employee中的String getName(); Function<Employee, String> function = Employee::getName;
1
2
3
4
5
6
7
8
9
10
11
12
#### 构造器引用
函数式接口的抽象方法的形参列表和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属的类的类型
类名::new
```java
//Supplier中的T get()
Supplier<Employee> supplier = Employee::new;
//Function中的R apply(T t)
Function<Integer, Employee> function = Employee::new;
数组引用
1 |
|
数组类型[]::new
1 |
|
StreamAPI
类似于使用SQL执行的数据库查询
Collection是一种静态的内存数据结构,Stream是有关计算的(与CPU打交道)
特点:
- Stream自己不会存储元素
- Stream不会改变源对象,会返回一个持有结果的新Stream
- Stream操作是延迟执行的,会等到需要结果的时候才执行
流程
创建操作
一个数据源(集合、数组等),获取一个流
通过集合获取:
- default Stream
stream():返回一个顺序流 - default Stream
parallelStream():返回一个并行流
1 |
|
通过数组获取:
- 调用Arrays类的静态方法
- static
Stream stream(T[] array):返回一个流
1 |
|
通过Stream的of():public static
1 |
|
创建无限流:(了解)
迭代:public static
Stream iterate(final T seed, final UnaryOperator f) 1
Stream.iterate(0, i -> i + 2).limit(10).forEach(System.out::println);
生成:public static
Stream generate(Supplier s) 1
Stream.generate(Math::random).limit(10).forEach(System.out::println);
中间操作
一个中间操作链,对数据源的数据进行处理
筛选与切片:
filter(Predicate p):接收Lambda,从流中排除某些元素
1
employees.stream().filter(e -> e.getSalary() > 7000).forEach(System.out::println);
distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素
1
employees.stream().distinct().forEach(System.out::println);
limit(long maxSize):截断流,使其元素不超过给定数量
1
employees.stream().limit(3).forEach(System.out::println);
skip(long n):跳过元素,返回一个扔掉了前n个元素的流
- 若流中元素不足n个则返回一个空流
- 与limit(n)互补
1
employees.stream().skip(3).forEach(System.out::println);
映射:
map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上并将其映射成一个新元素
1
2List<String> list = Arrays.asList("AA", "BB", "CC", "DD");
list.stream().map(s -> s.toLowerCase(Locale.ROOT)).forEach(System.out::println);flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
- map(Function f)相当于list1.add(list2)
- flatMap(Function f)相当于list1.addAll(list2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14public void mapTest() {
Stream<Stream<Character>> streamStream = list.stream().map(IntermediateTest::fromStringToStream);
streamStream.forEach(s -> s.forEach(System.out::print));
Stream<Character> characterStream = list.stream().flatMap(IntermediateTest::fromStringToStream);
characterStream.forEach(System.out::print);
}
public static Stream<Character> fromStringToStream(String s) {
List<Character> list = new ArrayList<>();
for (Character c : s.toCharArray()) {
list.add(c);
}
return list.stream();
}
排序:
- sorted():产生一个新流,按自然顺序排序
- sorted(Comparator com):产生一个新流,按比较器顺序排序
1 |
|
终止操作
一旦执行终止操作就执行中间操作链并产生结果,之后不会再被使用
匹配与查找:
1 |
|
归约:
1 |
|
收集:
1 |
|
1 |
|
Java9方法
1 |
|
Optional类
避免空指针
创建Optional类对象的方法:
- Optional.of(T t):创建一个Optional实例,t必须非空
- Optional.empty():创建一个空的Optional实例
- Optional.ofNullable(T t):t可以为null
判断Optional容器中是否包含对象:
- boolean isPresent():判断是否包含对象
- void ifPresent(Consumer<? super T> consumer):若有值,执行Consumer接口的实现代码并且该值会作为参数传给它
获取Optional容器的对象:
- T get():如果调用对象包含值,返回该值,否则抛异常
- 可与of()搭配使用
- T orElse(T other):如果有值则将其返回,否则返回指定的other对象
- 可与ofNullable()搭配使用
- T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
1 |
|
后续方法
boolean isEmpty():判断value是否为空
- JDK 11
ifPresentOrElse(Consumer<?super T> action, Runnable emptyAction):
- Value非空,执行参数1功能
- value为空,执行参数2功能
- JDK 9
Optional
- value非空,返回对应的Optional;
- value为空,返回形参封装的Optional
- JDK 9
Stream
- value非空,返回仅包含此value的Stream
- 否则返回一个空的Stream
- JDK 9
T orElseThrow():
- value非空,返回value
- 否则抛异常NoSuchElementException
- JDK 10
Java9
模块化系统
Jigsaw/Modularity
指定哪些部分可以暴露,哪些部分隐藏
在src中使用module-info.java定义:
- exports:控制着哪些包可以被其它模块访问到
- 所有不被导出的包默认都被封装在模块里面
- requires:指明对其它模块的依赖
REPL工具
REPL:read-evaluate-print-loop
即写即得、快速运行
jShell命令
在JShell环境下语句末尾的“;”可选,推荐还加上提高代码可读性
变量/方法可覆盖
Tab可补全代码
钻石操作符改进
能够与匿名实现类共同使用钻石操作符
String存储结构变更
String不再使用char[]存储,改成byte[]加上编码标记,节约一些空间
StringBuffer/StringBuilder同理
InputStream加强
可使用transferTo()将数据直接传输到OutputStream
1 |
|
改进Nashorn
为Java提供轻量级的Javascript运行时
JDK9包含一个用来解析Nashorn的ECMAScript语法树的API
Java11被设为“过时的”
Java10
局部变量类型推断
局部变量的显示类型声明,常常被认为是不必须的
减少啰嗦/形式的代码,避免信息冗余,对齐变量名,更容易阅读
处理var时,编译器先是查看表达式右边部分,并根据右边变量值的类型进行推断,作为左边变量的类型,然后将该类型写入字节码当中
Var不是关键字,除了不能用它作为类名,其他的都可以
1 |
|
不能使用:
- 初始值为null
- Lambda表达式
- 方法引用
- 数组静态初始化
- 方法的返回类型
- 方法的参数类型
- 构造器的参数类型
- 属性
- catch块
Java11
String新增方法
判断字符串是否为空白:” “.isBlank(); // true
去除首尾空白:” Javastack “.strip(); // “Javastack”
去除尾部空格:” Javastack “.stripTrailing(); // “ Javastack”
去除首部空格:” Javastack “.stripLeading(); // “Javastack “
复制字符串:”Java”.repeat(3);// “JavaJavaJava”
行数统计:”A\nB\nC”.lines().count(); // 3
局部变量类型推断升级
错误的形式:
1 |
|
正确的形式:
1 |
|
HTTP客户端API
使用HttpClient替换HttpURLConnection
更简化的编译运行程序
通过java命令直接编译运行文件
执行源文件中的第一个类, 第一个类必须包含主方法
不可以使用其它源文件中的自定义类
废弃Nashorn引擎
废除Nashorn javascript引擎,有需要的可以考虑使用GraalVM
ZGC
实验性的
优势:
- GC暂停时间不会超过10ms
- 既能处理几百兆的小堆,也能处理几个T的大堆(OMG)
- 和G1相比,应用吞吐能力不会下降超过15%
- 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
- 初始只支持64位系统
其它新特性
Unicode 10
Deprecate the Pack200 Tools and API
新的Epsilon垃圾收集器
完全支持Linux容器(包括Docker)
支持G1上的并行完全垃圾收集
最新的HTTPS安全协议TLS 1.3
Java Flight Recorder(相当于黑匣子)