Java语法

Java初步

字节

  • 位(bit): 一个数字0或1
  • 字节(Byte): 8位=1字节,这是数据存储的最小单位
  • 1KB = 1024 Byte,1MB = 1024 KB

命令提示符

  • MS-DOS(Microsoft Disk Operating System)
  • 启动 :win + R
  • 切换盘符 : [盘符]:
  • 进入文件: cd [文件名]
  • 退回上一层:cd ..
  • 退回根目录:cd \
  • 显示文件列表: dir
  • 清空屏幕:cls
  • 推出cmd : exit

JVM、JRE、JDK

  • JVM : Java Virtual Machine,Java虚拟机,是Java程序的运行环境,系统不同,虚拟机版本不同
  • JRE : Java Runtime Environment, Java运行时环境,包含JVM核心类库
  • JDK : Java Development Kit, Java开发工具包,包含JRE开发者工具
  • JDK >> JRE >> JVM

Java环境配置

  • 新建变量:JAVA_HOME,值:jdk安装路径,直到bin的上一级
  • 添加Path:%JAVA_HOME%\bin ,%自动匹配%

Java入门

Java程序运行步骤

  • java源程序–>编译器–>java字节码文件–>JVM运行
  • javac.exe : 编译器
  • java.exe : 解释器

HelloWorld

  1. 编写:编写HelloWorld.java
  2. 编译:打开CMD,找到程序所在目录,输入javac HelloWorld.java,生成:HelloWorld.class 文件
  3. 运行:输入 java HelloWorld ,无需后缀,指定类名

注释

  • 单行注释 : //
  • 多行注释 : / /

关键字、标识符

  • 关键字 : 完全小写的字母(public)
  • 标识符 : ·在程序中,自己定义的内容,比如类的名字和变量的名
    • 命名规则:
      1. 由 英文字母、0-9、$(美元符)、_(下划线)组成
      2. 不能以数字开头
      3. 不能是关键字
    • 命名规范:
      1. 类名规范:首字母大写,后面每个单词首字母大写(大驼峰)
      2. 变量名规范:首字母小写,后面每个单词首字母大写(小驼峰)
      3. 方法名规范:同变量名

常量

  • 在程序运行期间,固定不变的量
  • 分类:
    1. 字符串常量:双引号引用的部分,例如:”abc”,”123”
    2. 整数常量:直接写上数字,没有小数点,例如:100,200
    3. 浮点数常量:直接写上数字,有小数点,例如:2.5,-3.14
    4. 字符常量:单引号引起来的单个字符,例如:’A’,’中’(没有’’,’AB’,有’ ‘)
    5. 布尔常量:只有两种取值,true、false
    6. 空常量:null,代表没有任何数据

变量

  • 程序运行期间,内容可以发生改变的量
  • 创建一个变量并且使用的格式:
    • 数据类型 变量名称 //创建了一个变量
    • 变量名称 = 数据值 //赋值交给左边的变量
  • 一步到位
    • 数据类型 变量名称 = 数据值;//创建一个变量的同时,立刻放入指定的数据值
  • 注意事项 :
    • 如果创建多个变量,变量之间的名称不可以重复
    • 对于float和long类型来说,字母f和L不要漏掉
    • 如果使用byte或者short类型的变量,那么右侧的数据值不能超过左侧类型的范围
    • 没有进行赋值的变量,不能直接使用
    • 变量使用不能超过作用域的范围
      【作用域:从定义变量的一行开始,一直到直接所属的大括号结束为止】

Java数据类型

基本数据类型

  • 整数型 byte short int long
  • 浮点型 float double
  • 字符型 char
  • 布尔型 boolean

    引用数据类型

  • 字符串
  • 数组
  • 接口
  • Lambda

    注意事项

  • 字符串不是基本类型,而是引用类型
  • 浮点型可能只是一个近似值,并非精确的值
  • 数据范围与字节数不一定相关,例如float数据范围比long更加广泛,但是float是4字节,long是8字节
  • 浮点数当中默认类型是double,如果要用float,需要加上一个后缀F
  • 整数 当中默认类型是int,如果要使用long,需要加上一个后缀L,推荐使用大写字母后缀

数据类型转换

  • 自动类型转换(隐式)

    1. 特点:代码不需要进行特殊处理,自动完成。
    2. 规则:数据范围从小到大,向上兼容
  • 强制类型转换(显式)

    1. 特点: 代码需要进行特殊格式处理,不能自动完成
    2. 格式:范围小的类型 范围小的变量名 = (范围小的类型) 原本范围大的数据
  • 注意事项:

    1. 强制类型转换一般不推荐使用,因为有可能发生精度损失(小数)、数据溢出(大数)
    2. byte/short/char 这三种类型都可以发生数学运算,例如加法”+”。
    3. byte/short/char 这三种类型在运算时,都会被首先提升为int类型,然后再计算。
    4. boolean类型不能发生数据类型转换
    5. 对于byte/short/char 三种类型来说,如果右侧赋值的数值没有超过范围,那么javac编译器会自动隐含地为我们补上一个(byte)(short)(char)。
      • 如果没有超过左侧范围,编译器自动补上强转。
      • 如果超过左侧范围,编译器会直接报错。
    6. 在给变量进行赋值时候,如果右侧地表达式当中全部都是常量,没有任何变量,那么编译器javac将会直接将若干个常量表达式计算得到结果。
  • 编译器的常量优化 : short result = 5 + 8; 等号右边全是常量,没有任何变量参与运算,编译之后,得到的.class字节码文件当中相当于直接就是 short result = 13;右侧的常量结果数值,没有超过左侧范围,所以正确。这种成为“编译器的常量优化”。常量计算在编译时已经计算并赋值。一旦表达式右侧有变量参与,则无法进行这种优化。

ASCII码

  • ASCII : American Standard Code for Information Interchange 美国信息交换标准代码
  • Unicode : 万国码,也是数字和符号的对照关系,开头0-127部分和ASCII完全一样,但是128开始包含有更多字符
  • ‘0’ - ‘48’ , ‘A’ - 65 , ‘a’ - 97

运算符

  • 进行特定操作的符号,例如:+
  • 表达式:用运算符连起来的式子,例如:a + b
  • 分类:
    • 算数运算符:+ 、 - 、 * 、 / 、 % 、 ++ 、 –
    • 赋值运算符:= 、 += 、-= 、*= 、/= 、%=
    • 比较运算符:== 、< 、 > 、 <= 、 >= 、 !=
    • 逻辑运算符:&& 、 || 、 !(取反)
    • 三目运算符:数据类型 变量名称 = 布尔类型表达式 ? 结果1 : 结果2 (true为结果1,false为结果2)

方法入门

  • 定义:若干语句功能的集合,将一个功能抽取出来,形成一个单独的功能,提高代码复用,减少冗余
  • 格式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
    修饰符 返回值类型 方法名(参数列表){
    方法体
    return ;
    }
    */
    public static void method(){
    System.out.println("这是一个方法");
    }
  • 调用: 方法不会自动运行,必须被调用

  • 注意:
    • 方法定义的先后顺序无所谓
    • 方法定义必须是挨着的,不能再一个方法的内部定义另外一个方法
    • 方法必须被调用才会执行

Java9 新特性

  • JShell : 适合片段代码的测试
  • 启动 : cmd + JShell
  • 退出 : /exit

流程控制

顺序

  • 顺序结构:根据编写顺序,从上到下执行。

判断

  • if
  • if…else
  • if…else if…else

选择

  • switch
    • 注意:多个case后面的数值不可以重复
    • switch小括号当中只能是下列数据类型
      • 基本数据类型 : byte/short/char/int
      • 引用数据类型 : String字符串、enum枚举
1
2
3
4
5
6
7
8
9
10
11
12
switch(){
case 常量值1:
语句1;
break;
case 常量值2:
语句2;
break;
...
default:
语句体:n+1;
break;
}
  • case的穿透性:如果case的后面不写break,将出现穿透现象,直接向后运行,直到遇到break;

循环

  • for
  • while
  • do-while:无条件执行一次循环体,具有一定风险性

    1
    2
    3
    4
    5
    初始化表达式
    do{
    循环体
    步进表达式
    }while(布尔表达式)
  • 区别:

    1. 如果条件判断从来没有满足过,for和while将执行0次,do-while至少循环一次
    2. for循环的变量在小括号当中定义,只有循环内可以使用

IDEA

快捷键

  • (自定义)自动补全快捷键:file-settings-keymap-dupulicate(创建副本)-main menu-code-completion-basic(ctrl+alt+?)
  • Alt+Enter : 导入包,自动修正代码
  • Ctrl+Y : 删除光标所在行
  • Ctrl+D : 复制光标所在行,插入光标位置下面
  • Ctrl+ALt+L : 格式化代码
  • Ctrl+/ : 单行注释
  • Ctrl+Shift+/ : 多行注释
  • Alt+Ins : 自动生成代码,toString,get,set方法
  • Alt+Shift+上下箭头 : 移动当前代码行

    项目结构

  • Project
  • Module
  • Package
  • Class

Java基础

方法

定义

  • 若干语句功能的集合
  • 参数:进入方法的数据
  • 返回值: 从方法中出来的数据

    格式

    1
    2
    3
    4
    修饰符 返回值类型 方法名称(参数类型 参数名称,...){
    方法体
    return 返回值;
    }
  • 修饰符: public static…

  • 返回值类型: void int
  • 方法名称 : 方法名字,小驼峰
  • 参数名称 : 进入方法的数据对应的变量名称
  • 方法体 : 方法需要做的事
  • return : 停止当前方法,将返回值还给调用处
  • 返回值 : 执行方法后得到的数据结果
  • 注意 :
    • return后面的”返回值“,必须和方法名称前面的返回值类型,保持对应

方法的调用

  1. 单独调用: 方法名称(参数),返回值为void,只能单独调用
  2. 打印调用: System.out.println(方法名称(参数));
  3. 赋值调用: 数据类型 变量名称 = 方法名称(参数);

方法的重载(Overload)

  • 在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
  • 参数列表: 个数不同,数据类型不同,顺序不同
  • 重载方法调用 : JVM通过方法的参数列表,调用不同的方法。
  • 注意 :
    • 与参数的名称无关
    • 与方法的返回值类型无关

注意

  • 方法应该定义在类中,但是不能在方法中再定义方法,不能嵌套
  • 方法定义的先后顺序无所谓
  • 方法必须被调用才能执行
  • 返回值类型必须和方法的返回值类型对应

数组

概念

  • 存储数据长度固定的容器,保证多个数据的数据类型要一致

特点

  • 数组是一种引用数据类型
  • 数组当中的多个数据类型一致
  • 数组的长度在程序运行期间不可改变

初始化

  • 在内存中创建一个数组,并且向其中赋予一些默认值
    • 动态初始化:指定长度
    • 静态初始化:指定内容
动态初始化
  • 数据类型[] 数组名称 = new 数据类型[数组长度]

  • 左侧数据类型:数组中保存的数据类型

  • 左侧的中括号:表示这是一个数组
  • 左侧数组名称:数组的名字
  • 右侧的new : 代表创建数组的动作
  • 右侧数据类型:必须和左边的数据类型保持一致
  • 右侧中括号的长度:数组当中,到底可以保存多少个数据,是一个int数字

    1
    int[] arrayA = new int[200];
  • 注意: 使用动态初始化数组的时候,其中的元素将会自动拥有一个默认值

    • 整数型:默认值0;
    • 浮点型:默认值0.0;
    • 字符型:’\u0000’;
    • 布尔型:false;
    • 引用型:null.
静态初始化
  • 数据类型[] 数组名称 = new 数据类型[]{元素1, 元素2, 元素3...}
  • 自动分配空间和容量

    1
    int[] arrayB = new int[]{5,10,15}
  • 注意: 静态初始化也有默认值,只不过系统自动马上将默认值替换成大括号中具体值

数组的访问

  • 直接打印数组名称,得到的是数组对应的,内存地址哈希值
  • 访问数组元素的格式: 数组名称 [索引值]

数组的内存

  • 栈(Stack):存放的都是方法中的局部变量。方法的运行一定要在栈中运行。
    • 局部变量:方法的参数,或者是方法{}内部的变量
    • 作用域:一旦超出作用域,立刻从栈内存当中消失
  • 堆(Heap): new出来的东西,都在堆中。
    • 堆内存里面的东西都有一个地址值:16进制
    • 堆内存里面的数据,都有默认值
  • 方法区(Method Area):存储.class相关信息,包含方法的信息
  • 本地方法栈(Native Method Stack):与操作系统相关
  • 寄存器(pc Register): 与CPU相关

数组的常见问题

  • 索引越界异常:如果访问数组元素的时候,索引编号不存在,将会发生数组索引越界异常:ArrayIndexOutOfBoundsException
  • 空指针:数组必须进行new初始化才能使用其中的元素。如果只是赋值了一个null,没有进行new创建,那么将会发生空指针异常:NullPointerException

数组的常用操作

  • 获取数组长度: 数组名称.length,数组一旦创建,程序运行期间,长度不可改变
  • 数组的遍历: 数组名称.fori–>自动补全遍历代码
  • 数组作为方法参数: 传递地址值
  • 数组作为方法返回值: 返回地址值,public static int[] 方法名return 数组名

注意

  • 方法的参数为基本类型时,传递的是数据值
  • 方法的参数为引用类型时,传递的时地址值

Java中级

面向对象思想

  • 面向过程: 当需要实现一个功能时,每一个步骤都需要具体描述出来,强调步骤
  • 面向对象:当需要实现一个功能时,不关心具体步骤,强调过程
    • 封装
    • 继承
    • 多态

      类和对象

      类和对象的关系

  • 类:一组相关属性行为的集合,可以看作是一类事物的模板,使用事物的属性特性和行为特征来描述该类事物。
    • 属性:该事物的状态信息
    • 行为:该事物能够做什么
  • 对象:一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。
  • 类与对象的关系:
    • 类是对象的模板,对象是类的实例
    • 类是一类事物的描述,抽象
    • 对象是一类事物的实例,具体

类的定义

  • 格式:

    1
    2
    3
    4
    public class ClassName{
    //成员变量
    //成员方法
    }
    • 成员变量(属性):对应事物的属性(事物的状态信息)
    • 成员方法(行为):对应事物的行为(事物能够做什么)

      对象的使用

  • 通常情况下,一个类不能直接使用,需要根据类创建一个对象,才能使用
    1. 导包:指出所需要使用的类,在什么位置:import 包名称.类名称
    2. 创建:类名称 对象名 = new 类名称();
    3. 使用:
      • 使用成员变量:对象名.成员变量名
      • 使用成员方法:对象名.成员方法名(参数)
    • 注意:
      • 当使用一个对象类型作为方法的参数时,传递的就是对象的地址值。
      • 当使用一个对象类型作为方法的返回值时,返回值其实就是对象的地址值。

成员变量与局部变量的区别

  • 定义的位置不一样
    • 局部变量:方法内部
    • 成员变量:方法外部,直接写在类当中
  • 作用的范围不一样
    • 局部变量:只有方法当中才可以使用
    • 成员变量:整个类可以使用
  • 默认值不一样
    • 局部变量:没有默认值,必须手动赋值
    • 成员变量:如果没有进行赋值,会有默认值,规则和数组一样
  • 内存位置不一样
    • 局部变量;位于栈内存中
    • 成员变量:位于堆内存中
  • 生存周期不一样
    • 局部变量:随着方法进栈儿诞生,随着方法出栈而消失
    • 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失

      封装

  • 封装性在Java当中的体现
    1. 方法就是一种封装
    2. 关键字private也是一种封装
  • 封装就是将一些细节信息隐藏起来,对于外界不可见

Private

  • private是一个权限修饰符,代表最小权限
  • 可以修饰成员变量和成员方法
  • private修饰后的成员变量和成员方法,只有在本类中访问,超过本类范围就不能被访问
  • 通过设置setXXX(),getXXX()方法来实现外部访问

This

  • this代表所在类的当前对象的引用(地址值),即对象自己的引用
  • 当方法的局部变量和类的成员变量重名的时候,根据就近原则,优先使用局部变量
  • 如果需要访问本类当中的成员变量,需要使用格式:this.成员变量名
  • 通过谁调用的方法,谁就是this

构造方法

  • 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。
  • 格式

    1
    2
    3
    public 类名称(参数类型 参数名称){
    方法体
    }
  • 注意事项:

    1. 构造方法的名称必须和所在类名称完全一致
    2. 构造方法不要写返回值类型,连void也不用写
    3. 构造方法不能return一个具体的返回值
    4. 如果没有编写任何构造方法,编译器会默认添加一个无参,无方法体的构造方法
    5. 一旦编写了至少一个构造方法,那么编译器将不再默认添加
    6. 构造方法也可以进行重载(方法名相同,参数列表不同)

标准类(JavaBean)

  • 格式
    • 所有成员变量都要使用private关键字修饰
    • 为每一个成员变量编写一对Getter/Setter方法(快捷键:Alt+insert/CODE->Generate生成器)
    • 编写一个无参数的构造方法(快捷键:Alt+insert->Constuctor构造器)
    • 编写一个全参数的构造方法
  • 这样的标准类也叫JavaBean
  • JavaBean:Java语言编写类的一种标准规范。

Java高级

常用API

  • API: Application Programming Interface.应用程序编程接口。Java API时一本程序员的字典,是JDK中提供给我们使用的类的说明文档。这些类将底层的代码实现封装起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们通过查询API的方式,来学习Java提供的类
  • API使用步骤:
    1. 打开帮助文档
    2. 点击显示,找到快速索引,看到输入框
    3. 输入需要查找的内容
    4. 看包,java.lang下的类不需要导包,其他需要
    5. 看类的解释和说明
    6. 学习构造方法
    7. 使用成员方法

Scanner类

  • 功能:可以实现键盘输入数据,到程序中
  • 引用类型一般使用步骤:
    1. 导包: import 包路径.类名称,如果需要使用的目标类和当前类位于同一个包下,则可以省略导包语句不写,只有java.lang包下的内容不需要导包,其他包都需要import语句
    2. 创建: 类名称 对象名 = new 类名称()
    3. 使用: 对象名·成员方法名()
  • 导包:import java.util.Scanner
  • 创建:Scanner sc = new Scanner(System.in),System.in表示从键盘进行输入
  • 使用:
    • 获取键盘输入的一个int数字:int num = sc.nextInt();
    • 获取键盘输入的一个字符串:String str = sc.next();

匿名对象

  • 创建对象时,只有创建对象的语句,却没有把对象地址赋值给某个变量
  • 格式:new 类名称(参数列表);,左边没有变量值和赋值号。
  • 应用场景:
    • 创建匿名对象直接调用方法,没有变量名
    • 匿名对象只能使用唯一的一次,下次再用不得不创建一个新对象,造成浪费
    • 匿名对象可以作为方法的参数和返回值
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
import java.util.Scanner;

/*
* 匿名对象 Anonymous
* 做参数
* 做返回值
* */
public class Anonymous {
public static void main(String[] args) {
// 做参数
System.out.println("输入的数字是:" + new Scanner(System.in).nextInt());

// 使用匿名对象作为参数
methodParam(new Scanner(System.in));

// 匿名对象作为返回值
Scanner sc = methodReturn();
System.out.println("第三次输入的是:"+sc.nextInt());
}

// 匿名对象做参数
public static void methodParam(Scanner sc) {
int anInt = sc.nextInt();
System.out.println("输入的第二个数字是:" + anInt);
}

// 匿名对象做返回值
public static Scanner methodReturn(){
return new Scanner(System.in);
}
}

Random类

  • 作用:产生随机数
  • 使用步骤:
    • 查看类:导包,import java.util.Random
    • 查看构造方法:创建,Random r = new Random()
    • 查看成员方法:使用,
      • 获取一个随机的int数字:int num = r.nextInt()
      • 获取一个随机的int数字:(参数代表范围,左闭右开区间),int num = r.nextInt(3),实代表的范围是 [0,3)

ArrayList类

  • ArrayList集合的长度是可以随便变化的
  • 导包:import java.util.ArrayList
  • 创建:ArrayList<E> list = new ArrayList<>();
  • 使用:成员方法

    • public boolean add():向集合当中添加元素,参数的类型和泛型一致
    • public E get(int index);从集合当中获取元素,参数是索引编号,返回值就是对应位置的元素
    • public E remove(int index):从集合中删除元素,参数是索引编号,返回值就是被删除掉的元素
    • public int size():获取集合的长度,返回值是集合中元素的个数
  • 注意:

    • 尖括号内的泛型只能是引用类型,不能是基本类型
    • 如果向集合ArrayList当中存储基本类型数据,必须使用基本类型对应的包装类

      • 基本类型 -> 包装类
      • int -> Integer
      • byte -> Byte
      • short -> Short
      • long -> Long
      • float -> Float
      • double -> Double
      • char -> Character
      • boolean -> Boolean
    • 从JDK1.5开始,支持自动装箱(基本类型->包装类型),自动拆箱(包装类型->基本类型)

String

  • 特点:

    • 字符串的内容永不可变
    • 字符串可以共享使用
    • 字符串效果上相当于是char[ ]字符数组,但其底层原理是byte[ ]字节数组。
  • 创建字符串:

    • 三种构造方法
      1. public String():创建一个空白字符串,不含任何内容
      2. public String(char[] array):根据字符数组的内容,来创建对应的字符串
      3. public String(byte[] array):根据字节数组的内容,来创建对应的字符串
    • 一种直接创建
      • String str = "Hello"; //右边直接用双引号
      • 直接写上双引号,就是字符串对象
字符串常量池
  • 对于常量类型来说,==判断的是值
  • 对于引用类型来说,==判断的是地址值
  • 双引号直接写的字符串在常量池当中,new的不在池当中
常用方法
字符串比较
  • public boolean equals(Object obj);//参数可以是任何对象,任何对象都能用object进行接受
    • 注意:
      • 任何对象都能用object进行接收
      • equals方法具有对称性,也就是a.equals(b)b.equals(a);
      • 如果比较双方一个常量一个变量,推荐常量写在前面,推荐:"abc".equals(str),不推荐str.equals("abc");防止str为null,产生空指针异常
  • public boolean equalsIgnoreCase(String str):忽略大小写进行内容比较
字符串获取
  • public int length():获取字符串当中含有的字符个数,拿到字符串的长度
  • public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串(concatenate级联)
  • public char charAt(int index):获取指定索引位置的单个字符(索引从0开始)
  • public int indexOf(String str):查找参数字符串在原本字符串当中首次出现的索引位置,如果不存在,返回-1
字符串截取
  • public String substring(int index):截取从参数位置一直到字符串末尾,返回新字符串
  • public String substring(int begin, int end):截取从begin开始,一直到end结束,中间的字符串,[begin, end),包含左边,不含右边
字符串转换、替换
  • public char[] toCharArray():将当前字符串拆分成为字符数组作为返回值
  • public byte[] getBytes():获取当前字符串底层的字节数组
  • public String replace(CharSequence oldString, CharSequence newString):将所有出现的老字符串替换成为新的字符串,CharSequence意思就是说可以接受字符串类型。
字符串分割方法
  • public String[] split(String regex):按照参数规则,将字符串切分为若干部分
  • 注意:split方法的参数其实是一个正则表达式如果要切分".",必须写成"\\."

static关键字

  • static用来修饰成员变量和成员方法,被修饰的成员是属于类,而不是仅仅属于某个对象自己,凡是本类的对象,都共享同一内容。
  • 一旦使用static修饰静态成员方法,那么这种方法就成为了静态方法,静态方法可以直接通过对象名进行调用,也可以直接通过类名称来调用;非静态方法必须通过创建类才能使用
  • static,推荐使用类名称进行调用
    • 静态变量:类名称.静态变量
    • 静态方法:类名称.静态方法()
  • 对于本类当中的静态方法,可以省略类名称,编译器会自动补全
  • 注意:
    1. 静态不能直接访问非静态,因为在内存当中先有静态内容,后有非静态内容
    2. 静态方法不能用this,因为this代表当前对象,通过谁调用方法,谁就是当前对象

静态代码块

  • 格式

    1
    2
    3
    4
    5
    public class 类名称{
    static{
    //静态代码块内容
    }
    }
  • 当第一次用到本类时,静态代码块执行唯一的一次

  • 静态内容总是优先于非静态,所以静态代码块构造方法先执行
  • 用途:用来一次性地对静态成员变量进行赋值

数组根据类:Arrays

  • 作用:与数组相关的工具类,实现数组的常见操作,所有方法都是静态方法,使用非常方便
  • 导包:java.util.Arrays
  • 使用:
    • public static String toString();将参数数组变成字符串(按照默认格式;[元素1,元素2,元素3…])
    • public static void sort(数组):按照默认升序对数组的元素进行排序。
      • 如果是数值,sort默认升序
      • 如果是字符串,sort默认按字母升序
      • 如果是自定义类,那么这个自定义的类需要有Comparable或者Comparator接口的支持。

数学工具类:Math

  • 与数学相关的工具类,里面提供了大量的静态方法,完成于数学运算相关的操作
  • 导包;java.util.Math
  • 使用:
    • public static double abs(double num):获取绝对值
    • public static double ceil(double num):向上取整
    • public static double floor(double num):向下取整
    • public static long round(double num):四舍五入
    • Math.PI:圆周率常量

继承

定义

  • 继承是多态的前提,如果没有继承,就没有多态
  • 主要解决的问题:共性抽取
  • 子类继承父类的属性行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类的非私有的属性和行为
  • 父类:基类、超类
  • 子类;派生类

    格式

  • 子类与父类的关系:is-a
  • 父类:(一个普通的类定义)

    1
    2
    3
    public class 父类名称{
    //...
    }
  • 子类:

    1
    2
    3
    public class 子类名称 extends 父类名称{
    //...
    }

特点

成员变量
  • 父类无法使用子类成员变量
  • 子类仅可以直接访问父类的非私有成员变量
  • 成员变成重名:
    • 直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找
    • 间接通过成员方法访问成员变量:方法属于谁就优先用谁,没有则向上找
  • 三种变量重名:
    • 局部变量:直接写成员变量名
    • 本类的成员变量:this.成员变量名
    • 父类的成员变量:super.成员变量名
成员方法
  • 成员方法不重名:正常使用
  • 成员方法重名:创建的对象是谁,优先用谁的方法
  • 注意:无论是成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类

方法重写

  • 重写(Override)【覆盖】:在继承关系当中,方法的名称一样,参数列表也一样
  • 对比:
    • 重载:Overload:方法名称相同,参数列表不同
    • 重写:Override:方法名称相同,参数列表也相同–覆盖
  • 特点:创建的是子类对象,则优先使用子类方法
  • 注意:
    1. 必须保证父子类之间的方法名称相同,参数列表也相同

      @Override // 写在方法前面,用来检测是不是有效的正确的覆盖重写(注解)

      // 这个注解就算不写,只要满足要求,也是正确的方法覆盖重写   
      
2. 子类方法的返回值必须**小于等于**父类方法的返回值范围
    > `java.lang.Object`类是所有类的公共最高父类,java.lang.String就是Object的子类

3. 子类方法的权限必须**大于等于**父类方法的权限修饰符
    > public > protected > (default)(不写) > private

构造方法

  • 继承关系中,父子类构造方法的访问特点;
    1. 子类构造方法当中有一个默认隐含的super()调用,所以一定是先调用父类构造,后执行子类的构造
    2. 子类构造可以通过super关键字来调用父类重载构造
    3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造

super关键字

  • 在子类的成员方法中,访问父类的成员变量:super.变量名
  • 在子类的成员方法中,访问父类的成员方法:super.方法名
  • 在子类的构造方法中,访问父类的构造方法:super();

继承的特征

  • Java语言是单继承的,一个类的直接父类只能有一个
  • Java语言可以多级继承(继承体系),顶层都是Object类
  • 一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类

抽象类

定义

  • 没有方法主体的方法称为抽象方法,包含抽象方法的类,必须是抽象类
    • 抽象类:包含抽象方法的类
    • 抽象方法:没有方法体的方法,加上abstract关键字,去掉方法体,分号结束

格式

  • 抽象方法:
    1
    2
    // 修饰符 abstract 返回值类型 方法名(参数列表);
    public abstract void method();

使用

  • 不能直接创建new抽象类对象
  • 必须用子类继承抽象父类
  • 子类必须覆盖重写抽象父类所有的抽象方法
  • 继承抽象类的子类必须重写父类所有的抽象方法,否则该子类也必须声明为抽象类,最终必须要有子类实现该父类的抽象方法,否则抽象父类到子类的创建全部失效,失去意义

注意

  • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象
  • 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类一定是抽象类
  • 抽象类的子类,必须重写抽象父类所有的抽象方法,否则,编译无法通过报错,除非该子类也是抽象类

接口

定义

  • 接口就是一种公共的规范标准
  • 接口是一种引用数据类型(数组、类、接口)
  • 格式

    1
    2
    3
    public interface 接口名称{
    // 接口内容
    }
  • 备注:换成关键字interface之后,编译生成的字节码文件仍然是:.java->.class

java版本

  • java7:
    1. 常量
    2. 抽象方法
  • java8:
    1. 默认方法
    2. 静态方法
  • java9
    1. 私有方法

使用

  • 接口不能直接使用,必须有一个“实现类”来“实现”接口

    • 格式:
      1
      2
      3
      public class 实现类名称 implements 接口名称{
      // ...
      }
  • 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法(否则它必须是一个抽象类)

    • 实现:去掉abstract关键字,加上方法体和大括号
  • 创建实现类的对象,进行使用

接口的抽象方法

  • 注意:
    1. 接口当中的抽象方法,修饰符必须是两个固定关键字:public abstract
    2. 这两个关键字修饰符,可以选择性省略
    3. 方法的三要素,可以随意定义

接口的默认方法

  • 从Java8开始,可以使用默认方法,默认方法可以被实现类继承
  • 作用:解决接口升级的问题(实现类可以重写默认方法)
  • 格式:
    1
    2
    3
    public default 返回值类型 方法名称(参数列表){
    // 方法体
    }

接口的静态方法

  • 从Java8开始,接口当中允许定义静态方法
  • 静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用
  • 格式:

    1
    2
    3
    public static 返回值类型 方法名称(参数列表){
    方法体
    }
  • 注意:不能通过接口实现类的对象来调用当中的静态方法

  • 正确用法:通过接口名称,直接调用其中的静态方法。
  • 格式:接口名称.静态方法名称(参数)

接口的私有方法

  • 我们需要抽取一个共有方法,用来解决多个默认方法之间重复代码的问题。但是这个共有方法不应该让实现类使用,应该是私有化的。
  • 从Java9开始,接口当中允许定义私有方法

    1. 普通私有方法:解决多个默认方法之间重复代码问题
      格式:

      1
      2
      3
      private 返回值类型 方法名称(参数列表){
      方法体
      }
    2. 静态私有方法:解决多个静态方法之间重复代码问题

      1
      2
      3
      private static 返回值类型 方法名称(参数列表){
      方法体
      }

接口的成员变量(常量)

  • 接口中可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。从效果上看,这其实就是接口的【常量】
  • 格式:
    public static final 数据类型 常量名称 = 数据值;
  • 一旦使用final关键字进行修饰,说明不可改变
  • 注意:
    • 接口当中的常量,可以省略public static final
    • 接口当中的常量,必须进行赋值,不能不赋值
    • 接口中,常量的名称,使用完全大写的字母,用下划线进行分隔(shift+F6)

总结

  • 在java9+版本中,接口的内容可以有

    1. 成员变量其实是常量,格式:[public] [static] [final] 数据类型 常量名称 = 数据值;

      • 注意:
        • 常量必须进行赋值,而且一旦赋值不能改变
        • 常量名称完全大写,用下划线进行分隔
    2. 接口中最重要的就是抽象方法,格式:
      [public] [abstract] 返回值类型 方法名称(参数列表)

      • 注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类
    3. 从Java8开始,接口里允许定义默认方法,格式:[public] default 返回值类型 方法名称(参数列表){方法体}

      • 注意:默认方法也可以被覆盖重写
    4. 从Java8开始,接口里允许定义静态方法,格式:[public] static 返回值类型 方法名称(参数列表){方法体}

      • 注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
    5. 从Java9开始,接口里允许定义私有方法,格式:

      • private 返回值类型 方法名称(参数列表){方法体}
      • private static 返回值类型 方法名称(参数列表){方法体}
        • 注意:private方法只有接口自己才能调用,不能被实现类或别人使用

注意

  • 接口不能有静态代码块
  • 接口不能有构造方法
  • 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口

    • 格式:
      1
      2
      3
      public class 实现类 implements 接口A,接口B{
      // 覆盖重写所有抽象方法
      }
  • 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可

  • 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类
  • 如果实现类所实现的多个接口,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
  • 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法(父类>>接口)

接口的多继承

  • 类与类之间是单继承的,直接父类只能有一个
  • 类与接口之间是多实现的,一个类可以实现多个接口
  • 接口与接口之间是多继承的
  • 使用:关键字extends
  • 注意
    • 多个父接口当中的抽象方法如果有重复,没关系
    • 多个父接口当中的默认方法如果有重复,那么子接口必须进行默认方法的覆盖重写,【而且要带着default关键字】
    • 实现类重写接口默认方法,不需要保留default关键字

多态

概述

  • 多态性:一个对象拥有多种形态
  • 代码当中体现多态性:【父类引用指向子类对象】
    • 格式:
    • 父类名称 对象名 = new 子类名称();
    • 或者
    • 接口名称 对象名 = new 实现类名();
  • 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误,如果有,执行的是子类重写后方法

多态中的成员变量

  • 直接通过对象名访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
  • 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。

多态中的成员方法

  • new的是谁,就优先用谁,没有则向上找
  • 注意:
    • 成员方法:编译看左,运行看右
    • 成员变量:编译看左,运行看左

多态的优点

  • 方法调用相同(编译时),用谁找谁(运行时),动态过程

引用类型转换

向上转型
  • 向上转型:多态本身是子类类型向父类类型向上转型的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型
  • 格式:父类名称 对象名 = new 子类名称();
  • 注意:向上转型一定是安全的。从小范围–>大范围
  • 缺陷:无法调用子类特有的内容
向下转型
  • 向下转型:父类类型向子类类型向下转型的过程(还原),这个过程是强制的
  • 格式:子类名称 对象名 = (子类名称)父类对象;
  • 含义:将父类对象,还原成原本的子类对象
  • 注意:
    1. 要还原的对象必须是原来创建的对象,才能向下转型
    2. 要还原的对象如果不是原来创建的对象,而是其他对象,就会报错(运行异常,ClassCastException)
instance of关键字
  • 作用:返回一个boolean值,判断前面的对象能不能当作后面类型的实例
  • 格式:变量名 instance of 数据类型

final关键字

  • 含义:代表最终、不可改变的
  • 使用:
    1. 修饰一个类,不能被继承
    2. 修饰一个方法,不能被重写
    3. 修饰一个局部变量,不能被重新赋值
    4. 修饰一个成员变量,不能被重新赋值

修饰类

  • 格式:

    1
    2
    3
    public final class 类名称(){
    // ...
    }
  • 作用:当前这个类不能有任何的子类(final类不能作为父类)

  • 注意:final类的所有成员方法都无法进行覆盖重写

修饰方法

  • 格式:

    1
    2
    3
    public final 返回值 方法名(参数列表){
    // 方法体
    }
  • 作用:当前方法就是最终方法,不能再被覆盖重写

  • 注意:对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾

修饰局部变量

  • 格式:

    1
    final 数据类型 数据名 = 数据值;
  • 作用:当前局部变量,不能进行更改,一次赋值,终生不变

  • 注意:对于基本类型,不可变说的是变量当中的数据不可变;对于引用类型来说,不可变说的是变量当中的地址值不可变

修饰成员变量

  • 格式:

    1
    final 数据类型 数据名 = 数据值;
  • 作用:当前成员变量,不能进行更改,但成员变量有默认值,用了final后必须进行手动赋值

  • 注意:
    1. 对于final的成员变量,要么使用直接赋值,要么使用构造方法赋值(二选一)
    2. 必须保证类当中所有重载构造方法,都最终会对final的成员变量进行赋值

权限修饰符

public protected (default) private
同一个类 YES YES YES YES
同一个包 YES YES YES NO
不同包子类 YES YES NO NO
不同包非子类 YES NO NO NO

内部类

  • 定义:将一个类A定义在另一个类B里面,A被称为内部类,B被称为外部类
  • 成员内部类:定义在类中方法外的类

  • 分类:

    1. 成员内部类
    2. 局部内部类(包含匿名内部类)
  • 注意:内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。比如,Person$Heart.class

成员内部类

  • 定义在一个类内成员方法外的类
  • 格式:

    1
    2
    3
    4
    5
    6
    修饰符 class 外部类名称{
    修饰符 class 内部类名称{
    //...
    }
    //...
    }
  • 使用:

    1. 直接:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称()
    2. 间接:在外部类的方法中,使用内部类:然后main只是调用外部类的方法
  • 注意;

    • 内用外,随意访问,外用内,需要内部类对象
    • 同名变量访问:(重名)外部类名称.this.外部类成员变量名

局部内部类

  • 定义在一个方法内部的类,“局部”:只有当前所属方法才能使用,出了方法无法使用
  • 格式:

    1
    2
    3
    4
    5
    6
    7
    修饰符 class 外部类名称{
    修饰符 返回值类型 外部类方法名称(参数列表){
    class 局部内部类名称{
    //...
    }
    }
    }
  • 使用:方法内创建局部类对象,main直接调用外部类方法

  • 注意:权限修饰符
    1. 外部类:public/(default)
    2. 成员内部类:public / protected / (default) / private
    3. 局部内部类:无法使用修饰符
局部内部类的final问题
  • 局部内部类,如果希望访问所在方法的局部变量,那么这份局部变量必须是有效final的(从Java8开始,只要局部变量事实不变,那么final关键字可以省略)
  • 原因:
    1. new出来的对象在堆内存当中
    2. 局部变量是跟着方法走到,在栈内存中
    3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失
    4. new出来的对象会在堆中持续存在,直到垃圾回收消失

匿名内部类

  • 如果接口的实现类(或者是父类的子类),只需要使用唯一的一次,那么这种情况就可以省略掉该类的定义,改用匿名内部类

  • 格式:

    1
    2
    3
    接口名称 对象名 = new 接口名称(){
    // 覆盖重写所有抽象方法
    };
  • 对”new 接口名称(){…};”解析:

    1. new代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {…}这才是匿名内部类的内容
  • 注意:
    1. 匿名内部类,在创建对象的时候,只能使用唯一一次,如果希望多次创建对象,而且内容一样,那么就必须使用单独的实现类
    2. 匿名对象,在调用方法的时候,只能调用一次,如果希望同一个对象,调用多次方法,必须要给对象取名
    3. 匿名内部类省略了实现类/子类,匿名对象省略了对象名称
    4. 匿名内部类和匿名对象不是一回事

常用API-2

Object类

  • 类Object是类层次结构的根(父)类。每个类都使用Object作为超(父)类,所有对象(包括数组)都实现这个类的方法
  • 导包:java.lang.Object
  • 使用
    • public String toString():返回该对象的字符串表示
    • public boolean equals(Object obj):指示其他某个对象是否与此对象”相等”
toString方法
  • toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值
  • 直接打印对象的名字,其实就是调用对象的toString
  • 覆盖重写
    • 在IDEA中,可以使用alt+insert,点击toString()
  • 看一个类是否重写了toString,直接打印这个类的对象即可,如果没有重写toString方法,那么打印的是对象的地址值
    equals方法
  • 如果没有重写equals方法,默认比较对象的地址值,如果需要比较内容,需要覆盖重写
  • 参数:
    1. Object obj:可以传递任意的对象
    2. this ;调用的对象
    3. obj ;参数
  • 基本数据类型:比较地址值
  • 引用数据类型;比较对象的地址值
  • 覆盖重写:
    • 问题:隐含一个多态,无法使用子类特有的内容(属性和方法)
    • 解决:向下转型(强转)
    • alt+insert ,equals() and hashCode()
Objects类
  • JDK7中添加的工具类,其中的方法是null-save(空指针安全)或null-tolerant(容忍空指针),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象
  • Objects.equals方法:防止出现空指针异常
    1
    2
    3
    public static boolean equals(Object a, Object b){
    return (a==b)||(a!=null&&a.equals(b));
    }

Date类

  • 表示特定的瞬间,精确到毫秒(千分之一秒 1000毫秒=1秒)
  • 毫秒值的作用:可以对时间和日期进行计算(0毫秒:1970年1月1日 00:00:00)
  • 中国属于东八区,会把时间+8小时
  • 导包:java.util.Date
    构造方法
  • public Date():获取当前系统的日期和时间
  • public Date(Long date):传递毫秒值,把毫秒值转换为Date日期
    成员方法
  • public long getTime():把日期转换为毫秒值(相当于System.currentTimeMillis()方法),返回自1970年1月1日00:00:00 GMT以来此Date对象表示的毫秒数
    DateFormat类
  • 定义:日期/时间格式化子类的抽象类,可以实现日期和文不之间的转换(Date->String)
  • 作用:格式化(日期->文本)、解析(文本->日期)
  • 导包:java.text.DateFormat
  • 成员方法:
    1. String format(Date date):按照指定的模式,把Date日期,格式化为符合模式的字符串
    2. Date parse(String source):把符合模式的字符串,解析(parse)为Date日期
  • DateFormat是抽象类,无法之间创建对象使用,可以使用DateFormat的子类(SimpleDateFormat)

    parse方法声明了一个异常叫ParseException,如果字符串和构造方法的模式不一样,那么程序就会抛出此异常,调用一个抛出了异常的方法,就必须处理这个异常,要么throws继续抛出这个异常,要么try catch自己处理

  • SimpleDateFormat:

    • java.text.SimpleDateFormat extends DateFormat
    • 构造方法:SimpleDateFormat(String pattern):用给定的模式和默认语言环境的日期格式符号构造
    • 注意:模式中的字母不能更改,连接模式的符号可以改

Calendar类

  • 本身是抽象类,已知子类GregorianCalendar,提供了很多操作日历字段的方法(YEAR\MONTH\DAT_OF_MONTH\HOUR)
  • Calendar类无法直接创建对象使用,里面有一个静态方法叫做getInstance(),此方法返回了Calendar类的子类对象
  • static Calendar getInstance() 使用默认时区和语言环境获得一个日历

    常用方法

  • public int get(int field):返回给定日历字段的值
  • public void set(int field, int value);将给定的日历字段设置为给定值
  • public abstract void add(int field, int amount);根据日历的规则,为给定的日历字段添加或减去指定的时间量
  • public Date getTime();返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象
  • 注意;int field;日历类的字段,可以使用Calendar类的静态成员变量获取

System类

  • 导包:java.lang.System
  • 获取与系统相关的信息或系统级操作
    常用方法
  • public static long currentTimeMillis();返回以毫秒为单位的当前时间
  • public static void arraycopy(Object src, int srcPos,Object dest, int destPos, int length);将数组中指定的数据拷贝到另一个数组中

StringBuilder类

  • 字符串缓冲区,可以提高字符串的操作效率(看成一个长度可以变化的字符串),底层也是一个数组,但是没有final修饰,可以改成长度
  • 在内存中始终是一个数组,占用空间少(byte[] value = new byte[16]),效率高,如果超出,自动扩容
  • java.lang.Builder
    构造方法
  • StringBuilder();构造一个不带任何字符的字符串生成器,其初始容量为16个字符
  • StringBuilder(String str);构造一个字符串生成器,并初始化为指定的字符串内容
    成员方法
  • public StringBuilder append(...);添加任意类型数据的字符串形式,并返回当前对象自身
  • public String toString();将当前StringBuilder对象转换为String对象
    注意
  • String->StringBuilder;可以使用StringBuilder的构造方法
  • StringBuilder->String;可以使用StringBuilder的toString方法

基本类型包装类

  • 使用一个类,将基本类型的数据装起来,在类中定义一些方法,这个类叫做包装类,我们可以使用类中的方法来操作这些基本类型的数据
    装箱与拆箱
  • 装箱;把基本类型的数据,包装到包装类中

    • 构造方法:
      1. Integer(int value);构造一个新分配的Integer对象,它表示指定的int值
      2. Integer(String s);构造一个新分配的Integer对象,它表示String参数所指示的int值(注意:传递的字符串,必须是基本类型的字符串,否则会抛出异常‘100’正确,‘0’异常)
    • 静态方法
      1. static Integer valueOf(int i);返回一个表示指定的int值的Integer对象
      2. static Integer valueOf(String s);返回保存指定的String的值的Integer对象
  • 拆箱;在包装类中取出基本类型的数据

    • 成员方法;int intvalue();以int类型返回该Integer值
自动装箱与自动拆箱
  • 基本类型的数据和包装类之间可以自动地相互转换(JDK1.5之后)
基本类型与字符串类型的转换
  • 基本类型->字符串;

    1. 基本类型的值+””
    2. 包装类的静态方法toString(参数),不是Object类的toString()方法,发生了重载
    3. String类的静态方法valueOf(参数)
  • 字符串->基本类型;

    1. 包装类的静态方法;parseXXX(“数值类型的字符串”)

集合

概述

  • 数组长度固定;集合长度可变
  • 数组存储同一类型元素;集合只能存储对象,类型可以不一致

框架

  • Collection接口;所有单列集合中共性的方法\所有的单列集合都可以使用共性的方法\没有索引的方法

    • List接口;有序的集合(存储和取出元素顺序相同)\允许存储重复元素\有索引,可以使用普通的for循环遍历

      • Vector集合
      • ArrayList集合
      • LinkedList集合
    • Set接口;不允许存储重复元素\没有索引(不能使用普通的for循环遍历)

      • TreeSet集合
      • HashSet集合
        • LinkedHashSet集合(存取一致)

Collection集合

常用方法
  • public boolean add(E e):把给定的对象添加到当前集合中 。
  • public void clear() ;清空集合中所有的元素。
  • public boolean remove(E e);把给定的对象在当前集合中删除。
  • public boolean contains(E e);判断当前集合中是否包含给定的对象。
  • public boolean isEmpty();判断当前集合是否为空。
  • public int size();返回集合中元素的个数。
  • public Object[] toArray();把集合中的元素,存储到数组中。

Iterator迭代器

  • 为了遍历集合中所有元素
  • java.util.Iterator
Iterator接口
  • 迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
  • 常用方法;

    • boolean hasNext();如果仍有元素可以迭代,返回true
    • E next() ;返回迭代的下一个元素
  • 注意:Iterator迭代器,是一个接口,无法直接使用,需要使用Iterator接口的实现类对象。Colletion接口中有一个方法,叫iterator(),返回的就是迭代器的实现类对象

  • 使用步骤;
    1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态),注意迭代器也有泛型,跟着集合走
    2. 使用Iterator接口中的方法hasNext判断还有没有下一个元素
    3. 使用Iterator接口中的方法next取出集合中的下一个元素
增强for循环
  • 底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写,jdk1.5之后
  • Collection<E>extends Iterable<E>;所有的单列集合都可以使用增强for
  • public interface Iterable<T>;实现这个接口允许对象成为”foreach”语句的目标

  • 格式

    1
    2
    3
    for(集合/数组的数据类型 变量名:集合名/数组名){
    sout(变量名);
    }
  • 注意;新for循环必须有被遍历的目标,目标只能是Collection或者是数组。新式for仅仅作为遍历操作出现

泛型(Generic)

概述
  • 一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型
  • 泛型可以看作是一个变量,用来接收数据类型
  • E e;Element元素
  • T t;Type类型
  • 创建集合对象的时候,就会确定泛型的数据类型
    优点
  • 避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
  • 把运行期间异常ClassCastException,提升到了编译时期的编译失败
  • 但是泛型是什么类型,只能存储什么类型的数据
    定义与使用
  • 含有泛型的类

    • 格式:
      1
      2
      3
      修饰符 class 类名<泛型>{
      //...
      }
  • 含有泛型的方法

    • 格式;
      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 普通方法
      修饰符 <泛型> 返回值类型 方法名(泛型 参数名,...){
      方法体;
      }

      // 静态方法
      修饰符 static <泛型> 返回值类型 方法名(泛型 参数名,...){
      方法体;
      }
  • 含有泛型的接口

    • 格式;

      1
      2
      3
      修饰符 interface 接口名<泛型>{
      抽象方法(泛型 参数名,...);
      }
    • 使用:

      1. 定义类时确定泛型的类型
      2. 始终不确定泛型的类型,直到创建对象时,确定泛型的类型
        泛型通配符
  • ?;代表任意的数据类型
  • 使用:
    • 不能创建对象使用
    • 只能作为方法的参数使用
  • 注意:泛型不存在继承关系 Collection list = new ArrayList();这种是错误的。
泛型的上限限定
  • ? extends E;代表使用的泛型只能是E的子类/本身
泛型的下限限定
  • ? super E;代表使用的泛型只能是E类型的父类/本身

List集合

  • java.util.List extends Collection
  • List接口继承Collection接口
    特点
  • 有序的集合,存储元素和取出元素的顺序是一致的
  • 有索引,包含了一些带索引的方法
  • 允许存储重复的元素
    常用方法
  • public void add(int index, E element);将指定的元素,添加到该集合中的指定位置上
  • public E get(int index);返回集合中指定位置的元素
  • public E remove(int index);移除列表中指定位置的元素,返回的是被移除的元素
  • public E set(int index, E element);用指定元素替换集合中指定位置的元素,返回以前在指定位置的元素
  • 注意:操作索引的时候,一定要防止索引越界异常
    子类
  • ArrayList;List接口的数组实现,元素增删慢,查找快。此实现是不同步的(多线程)。
  • LinkedList;List接口的链表列表实现。元素添加快,查找慢。
    • 双向链表
    • 含有大量操作首尾元素的方法,因此不要使用多态
    • 常用方法:
      • public void addFirst(E e):将指定元素插入此列表的开头。
      • public void addLast(E e):将指定元素添加到此列表的结尾。
      • public E getFirst() :返回此列表的第一个元素。
      • public E getLast() :返回此列表的最后一个元素。
      • public E removeFirst() :移除并返回此列表的第一个元素。
      • public E removeLast() :移除并返回此列表的最后一个元素。
      • public E pop() :从此列表所表示的堆栈处弹出一个元素。
      • public void push(E e) :将元素推入此列表所表示的堆栈。
      • public boolean isEmpty() :如果列表不包含元素,则返回true。
  • Vector;单线程,与ArrayList原理一样,已被ArrayList替代

Set集合

  • java.util.Set extends Collection
  • Set接口继承Collection接口
    特点
  • 不允于存储重复的元素

    原理;add方法会调用元素的hashCode方法和equals方法,判断元素值是否重复(前提;存储的元素必须重写hashCode和equals方法),哈希值不同,直接存入集合,哈希值相同,比较equals,true为相同值,不存入集合

  • 没有索引,没有带索引的方法,也不能使用普通的for循环遍历

    子类
    HashSet
  • 特点;
    1. 由哈希表支持,不保证迭代顺序
    2. 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
    3. 底层是一个哈希表结构(查询速度快)
  • 哈希值;
    • 是一个十进制的整数,由系统随机给出(对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)
    • 在Object类中由一个方法,可以获取对象的哈希值;int hashCode()
    • hashCode();public native int hashCode(),native代表该方法调用的是本地操作系统的方法
    • String类的哈希值;String类重写了Object类的hashCode()方法(”重地和通话”哈希值相同)
  • 数据结构;
    • 哈希表;
      • jdk1.8版本之前;哈希表= 数组+链表
      • jdk1.8版本之后;哈希表=数组+链表+红黑树(提高了查询速度)
        • 存储数据到集合中(先计算元素的哈希值)
        • 把元素进行分组,相同哈希值链接到一起
        • 哈希冲突:元素不同,哈希值相同
        • 如果链表的长度超过8位,那么就会把链表转换为红黑树
  • 存储自定义类型元素
    • 重写hashCode和equals方法
LinkedHashSet
  • java.util.LinkedHashSet extends HashSet,继承父类HashSet
  • 可预知迭代顺序的Set接口的哈希表和链接列表实现
  • 底层是一个哈希表(数组+链表/红黑树)+链表;多了一条链表记录元素的存储顺序,保证元素有序

可变参数

  • JDK1.5之后出现的新特性
  • 使用前提;当前方法的参数列表数据类型已经确定,但是参数的个数不确定,使用可变参数
  • 格式;修饰符 返回值类型 方法名(数据类型...变量名){}
  • 原理;可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数,传递的参数个数,可以是0个,1,2…多个
  • 注意;
    1. 一个方法的参数列表,只能有一个可变参数
    2. 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
    3. 终极写法;(Object...obj)

Collections工具类

  • java.util.Collections;集合工具类
    常用方法
  • public static <T> boolean addAll(Collection<T> c, T...elements);往集合中添加一些元素
  • public static void shuffle(List<?> list);打乱集合顺序
  • public static <T> void sort(List<T> list);将集合中元素按照默认规则(升序)排序
  • public static <T> void sort(List<T> list ,Comparetor<? super T>);将集合中元素按照指定规则排序
  • 注意;
    • sort(List<T> list)被排序的集合里面存储的元素,必须实现Comparable接口,重写接口中的compareTo()定义排序规则
    • Comparable接口的排序规则;自己(this)-参数;升序
  • ComparatorComparable的区别
    • Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
    • Comparator;找一个第三方的裁判
    • Comparator排序规则;o1-o2升序,o2-o1降序

Map集合

  • java.util.Map<k,v>
  • 特点;
    1. Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
    2. Map集合中的元素,key和value的数据类型可以相同,也可以不同
    3. Map集合中的元素,key是不允许重复的,value是可以重复的
    4. Map集合中的元素,key和value是一一对应的
  • Collection的区别:
    1. Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
    2. Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
    3. Collection中的集合称为单列集合,Map中的集合称为双列集合。
    4. 需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
Map常用子类
  • HashMap<K,V>

    1. HashMap底层是哈希表,查询速度快,元素的存储顺序不能保证一致
    2. 为了保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法
    3. JDK1.8之前;数组+单向链表
    4. JDK1.8之后;数组+单向链表/红黑树(链表的长度超过8时)
  • LinkedHashMap<K,V>

    1. 是HashMap的子类,底层是哈希表+链表
    2. 是一个有序的集合,存储元素和取出元素的顺序是一致的,需要重写hashCode()、equals()方法
  • 注意;Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。

Map常用方法
  • public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。

    • 返回值V:
      • 存储键值对的时候,key不重复,返回值V是null
      • 存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
  • public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

    • 返回值V:
      • key存在,返回被删除的值
      • key不存在,返回null
  • public V get(Object key) 根据指定的键,在Map集合中获取对应的值。

    • 返回值V:
      • key存在,返回对应value值
      • key不存在,返回null
  • boolean containsKey(Object key) 判断集合中是否包含指定的键。

  • public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
  • public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
Map遍历key找value方式
  • 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示:keySet()
  • 遍历键的Set集合,得到每一个键。
  • 根据键,获取键所对应的值。方法提示:get(K key)
Entry键值对对象
  • Map.Entry<K,V>;在Map接口中有一个内部接口Entry
  • 作用;当Map集合一创建,那么就会在Map集合中创建一个Entry对象,用来记录键与值(键值对对象,键与值得映射关系)
  • Entry对象的常用方法;
    • public K getKey():获取Entry对象中的键。
    • public V getValue():获取Entry对象中的值。
HashMap存储自定义类型键值
  • Map集合保证Key是唯一的:作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
LinkedHashMap<K,V>
  • LinkedHashMap<K,V>继承HashMap<K,V>
  • 底层原理;哈希表+链表(记录顺序)
HashTable<K,V>
  • 底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
  • HashTable不能存null值,null键
  • HashTable和Vector在jdk1.2之后被取代(HashMap,ArrayList)
  • HashTable的子类Properties依然在使用

JDK9对集合添加的优化

  • JDK9的新特性;
    • List接口、Set接口、Map接口;增加了一个静态方法of,可以给集合一次性添加多个元素
      • 前提;集合中存储的元素的个数已经确定,不再改变
    • 注意;
      • of方法值适用于List、Set、Map接口,不适用于接口的实现类
      • of方法的返回值是一个不能改变的集合,集合不能再使用add、put方法添加元素,会抛出异常
      • Set接口和Map接口再调用of方法的时候,不能有重复的元素,否则会抛出异常

Debug调试

  • Debug调试程序;
    • 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
  • 使用方式;
    • 在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
    • 右键,选择Debug执行程序
  • 执行程序;
    • f8;逐行执行程序
    • f7;进入到方法中
    • shift+f8;跳出方法
    • f9;跳到下一个断点,如果没有下一个断点,那么就结束程序
    • ctrl+f2;退出Debug模式,停止程序
    • Console;切换到控制台

异常与多线程

异常

  • 异常;程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止
  • 异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象,Javac处理异常的方式是中断处理
    异常体系
  • ThrowableJava.lang.Throwable
    • Error;工程师无法处理,只能尽力避免
    • Exception;编译期异常,由于使用不当导致,可以避免
      • RuntimeException;运行期异常,Java程序运行过程中出现的问题
异常处理
  • 常用关键字;try,catch,finally,throw,throws
throw
  • 在指定的方法中抛出指定的异常
  • 格式;throw new xxxException("异常产生的原因")
  • 注意;
    1. throw关键字必须写在方法内部
    2. throw关键字后边new的对象必须是Exception或者Exception的子类对象
    3. throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
    4. throw关键字后边创建的是RuntimeException或者是RuntimeException的子类对象,(运行期异常)可以不处理,默认交给JVM处理(打印异常对象,中断程序)
    5. throw关键字后边创建的是编译异常,我们就必须处理这个异常,要么throw,要么try...catch
声明异常throws
  • 异常处理的第一种方式,交给别人处理
  • 当方法内部抛出异常对象的时候,必须处理这个异常对象
  • 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理,最终交给JVM处理
  • 格式;

    1
    2
    3
    4
    5
    修饰符 返回值 方法名(参数列表) throws AAAException, BBBException...{
    throw new AAAException("产生原因");
    throw new BBBException("产生原因");
    ...
    }
  • 注意;

    1. throws关键字必须写在方法声明处
    2. throws关键字后边声明的异常必须是Exception或者是Exception的子类
    3. 方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常。(如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可)
    4. 调用了一个声明抛出异常的方法,我们就必须处理声明的异常。(要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM,要么try...catch自己处理异常)
捕获异常try…catch
  • 格式;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try{
    可能产生异常的代码
    }catch(异常类型 变量名){ //用来接收try中抛出的异常对象
    处理异常的代码(一般把异常信息记录到一个日志中)
    }
    ...
    catch(异常类型 变量名){

    }
  • 注意;

    1. try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
    2. 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码
    3. 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑
Throwable常用方法
  • public void printStackTrace();打印异常的详细信息
  • public String getMessage();获取发生异常的原因
  • public String toString();获取异常的类型和异常描述信息(不用)
finally代码块
  • 格式

    1
    2
    3
    4
    5
    6
    7
    try{
    可能发生异常的代码
    }catch(异常类型 变量名){

    }finally{
    无论是否出现异常都会执行
    }
  • 注意;

    1. finally 不能单独使用,必须和try一起使用
    2. finally 一般用于资源释放(资源回收),无论程序是否出现异常,最后都要释放资源(IO)
异常的注意事项
  • 多个异常使用捕获该如何处理
    1. 多个异常分别处理
    2. 多个异常一次捕获,多次处理

      注意;一个try,多个catch,如果catch定义的异常变量,具有父子类的关系,子类的异常变量必须写在上面,否则会报错

    3. 多个异常一次捕获(catch异常对象的父类),一次处理
  • 如果finally有return语句,永远返回finally中的结果,避免该情况
  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
  • 如果父类抛出了多个异常,子类重写父类方法时
    1. 抛出和父类相同的异常
    2. 抛出父类异常的子类异常
    3. 不抛
  • 父类没有抛出异常,子类也不能抛出异常,如果出现异常,只能捕获处理,不可抛出
自定义异常类
  • 格式

    1
    2
    3
    4
    5
    public class XXXException extends Exception|RuntimeException{
    添加一个空参数的构造方法
    添加一个带异常信息的构造方法

    }
  • 注意;

    1. 自定义异常类一般都是Exception结尾,说明该类是一个异常类
    2. 自定义异常类,必须的继承Exception或者RuntimeException
    3. 继承Exception;自定义的异常类是一个编译器异常,如果方法内抛出了编译器异常,必须处理这个异常,要么抛出要么捕获
    4. 继承RuntimeException;自定义的异常类是一个运行期异常,无需处理,交给JVM(中断处理)

多线程

并发与并行
  • 并发;指两个或多个事件在同一个时间段内发生(交替执行)
  • 并行;指两个或多个事件在同一时刻发(同时发生)
    线程与进程
  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
  • 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
    • 线程调度;
      • 分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间
      • 抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的是抢占式调度
    • 单线程程序:Java程序中只有一个线程
    • 主线程:执行主(main)方法的线程
      创建线程类
      方式一:创建Thread类的子类
  • java.lang.Thread;描述线程的类,要实现多线程程序,必须继承Thread类
  • 实现步骤:
    1. 创建一个Thread子类
    2. 在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程做什么)
    3. 创建Thread类的子类对象
    4. 调用Thread类中的方法start()方法,开启新的线程(开辟新的栈空间),执行run方法
      方式二:实现Runnable接口
  • java.lang.Runnable
  • 实现步骤:
    1. 创建一个Runnable接口的实现类
    2. 在实现类中重写Runnable接口的run方法,设置线程任务
    3. 创建一个Runnable接口的实现类对象
    4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
    5. 调用Thread类中的start方法,开启新的线程执行run方法
      实现Runnable接口创建多线程的好处
  • 避免了单继承的局限性

    一个类只能继承一个类,类继承了Thread类就不能继承其他的类,实现Runnable接口,还可以继承其他的类,实现其他的接口

  • 增强了程序的扩展性,降低了程序的耦合性(解耦)

    实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。实现类中,重写了run方法:用来设置线程任务;创建Thread类对象,调用start()方法:用来开启新线程

    Thread类
  • 构造方法
  • 常用方法
    • public String getName();获取当前线程名称
    • public void start();导致此线程开始执行,Java虚拟机调用此线程的run方法
    • public void run();此线程要执行的任务在此处定义代码
    • public static Thread currentThread();返回当前正在执行的线程对象的引
    • public static void sleep(long millis);使当前正在执行的线程以指定的毫秒数暂停
  • 设置线程名称
    • 使用Thread类中的方法void setName(String name)
    • 创建一个带参数的构造方法,参数传递线程的名称,调用父类的带参构造方法(super(name)),把线程名称传递给父类,让父类(Thread)给子线程改名字(Thread(String name)
    • 适合多个相同的程序代码的线程去共享同一个资源。
    • 可以避免java中的单继承的局限性。
    • 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
      匿名内部类方式实现线程的创建
  • 格式:

    1
    2
    3
    4
    new 父类/接口(){
    @Override
    重写父类/接口中的方法
    }
  • eg:

    1
    2
    3
    4
    5
    6
    new Thread(new Runnable() {
    @Override
    public void run() {
    // 线程任务
    }
    }).start();

线程安全

线程同步
  • 当使用多线程访问同一资源的时候,多个线程对资源有写操作,就容易出现线程安全问题
  • Java提供了同步机制(Synchronized)解决
    线程同步机制
    同步代码块
  • 格式:

    1
    2
    3
    synchronized(锁对象){
    需要同步操作的代码
    }
  • 注意;

    1. 同步代码块中的锁对象,可以使用任意的对象
    2. 必须保证多个线程使用的锁对象是同一个
    3. 锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行,其他线程被阻塞(BLOCKED)
    4. 频繁的上锁、解锁会降低程序效率,但是提供程序安全性
同步方法
  • 使用synchronized修饰的方法,就叫同步方法
  • 格式:

    1
    2
    3
    public synchronized 返回值类型 方法名(){
    可能会产生线程安全问题的代码
    }
  • 同步方法也会把方法内部打代码锁住

    同步锁是谁?对于非static方法,同步锁就是this。
    对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

Lock锁
  • java.util.concurrent.locks.Lock
  • Lock 实现了比 synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象
  • 创建对象
    • Lock 锁名 = new ReentrantLock();
  • 常用方法;
    • public void lock() :加同步锁。
    • public void unlock() :释放同步锁

线程状态

  • java.lang.Thread.State
    六种状态
线程状态 导致状态发生的条件
NEW(新建) 线程刚被创建,但是并未启动。还没调用start方法
Runnable(可运行) 线程可以在Java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于CPU
Blocked(锁阻塞) 当一个线程试图获取一个对象锁,而对象锁被其他的线程持有,则该线程进入Blocked状态;当线程持有锁时,该线程变成Runnable状态
Waiting(无限等待) 一个线程在等待另一个线程执行(唤醒)动作时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒
TimedWaiting(计时等待) 同waiting状态,有几个方法有超时参数,调用他们将进入TimeWaiting状态,这一状态将一致保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有:Thread.sleep(),Object.wait()
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
Timed Waiting 计时等待
  • Thread.sleep(long m);强制当前正在执行的线程休眠,线程进入Runnable/Blocked状态
  • 锁对象.wait(long m);在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程进入Runnable/Blocked状态
  • 注意;sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始立刻执行。
    Blocked 锁阻塞
  • 线程A与线程B代码中使用同一锁,如果线程A获取到锁,线程A进入到Runnable状态,那么线程B就进入到Blocked锁阻塞状态
    Waiting 无限等待
  • 等待唤醒
    • 只有锁对象才能调用void wait()void notify()方法
    • void notifyAll();全部唤醒

等待唤醒机制

线程间通信
  • 多个线程在处理同一个资源,但是处理的动作(线程的任务)却不同
  • 多个线程并发执行,CPU默认随机切换线程,为了让他们有规律执行,需要进行协调通信
  • 等待唤醒机制;使各个线程能有效利用资源
    等待唤醒机制
  • 多个线程间的一种协作机制
  • wait/notify就是线程间的一种协作机制
  • 等待唤醒中的方法:
    1. wait:线程不再活动,不再参与调度,进入wait set中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
    2. notify:选取所通知对象的 wait set中的一个线程释放
    3. notifyAll;释放所通知对象的 wait set上的全部线程。
  • 注意:
    1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
    2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
    3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法

线程池

  • 线程池;可以容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源
  • 优点;
    1. 降低资源消耗
    2. 提高响应速度
    3. 提高线程的可管理性
线程池的使用
  • jdk1.5之后提供
  • java.util.concurrent.Executors;线程池的工厂类,用来生成线程池
  • Executors类中的静态方法;

    • static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池

      • 参数;int nThreads;创建线程池中包含的线程数量
      • 返回值;ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口接收)
    • java.util.concurrent.ExecutorService;线程池接口

      • 用来从线程池中获取线程,调用start()方法,执行线程任务
      • submit(Runnable task)提交一个Runnable任务用于执行
    • 关闭/销毁线程池的方法
      • void shutdown()
  • 线程池的使用步骤
    1. 使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
    2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
    3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
    4. 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)

Lambda表达式

函数式编程思想
  • 面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
  • 函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
    Lambda
  • Java8
  • 实现多线程:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 匿名内部类实现多线程
    new Thread(new Runnable(){
    @Override
    public void run(){
    线程任务
    }
    }).start();

    // Lambda表达式实现多线程
    new Thread(()->{线程任务}).start();//启动线程
Lambda表达式标准格式
  • 组成;
    1. 一些参数
    2. 一个箭头
    3. 一段代码
  • 格式;
    • (参数列表)->{一些重写方法的代码}
  • 解释;
    • ();接口中抽象方法的参数列表,没有参数就空着,有参数就写处参数,多个参数用逗号分隔
    • ->;床底的意思,把参数传递给方法体{}
    • {};重写接口的抽象方法的方法体
Lambda省略格式
  • 小括号内参数的类型可以省略;
  • 如果小括号内有且仅有一个参,则小括号可以省略;
  • 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
Lambda使用前提
  • 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一
    时,才可以使用Lambda。
  1. 使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

    备注:有且仅有一个抽象方法的接口,称为函数式接口

File类与IO流

File类

概述
  • java.io.File是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作
  • File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法
    静态方法
  • static String pathSeparator ;与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
  • static char pathSeparatorChar ;与系统有关的路径分隔符。
  • static String separator ;与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。

    路径不要写死,win和分隔符不同
    eg:"C:"+File.separator+"develop"+File.separator+"a.txt"

  • static char separatorChar ;与系统有关的默认名称分隔符。
    路径
  • 绝对路径;是一个完整的路径,以盘符开始的路径
  • 相对路径;是一个简化的路径,相对指的是相对于当前项目的根目录
  • 注意:
    1. 路径不区分大小写
    2. 路径中的文件名称分隔符windows用的是反斜杠,但反斜杠是转义字符,所以两个反斜杠菜表示一个普通的反斜杠
      构造方法
  • public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的File实例。

    String pathname:字符串的路径名称

  • public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的File实例。

    String parent 父路径,String child 子路径

  • public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的File实例。

    File parent 父路径是File类,可以使用一些方法,String child

  • 注意;

    • 路径可以是文件结尾,也可以是文件夹结尾
    • 路径可以是相对路径,也可以是绝对路径
    • 路径可以是不存在的
    • 创建File对象,只是把字符串路径封装成File对象,不考虑路径的真实情况
      获取功能的方法
  • public String getAbsolutePath() :返回此File的绝对路径名字符串。
  • public String getPath() :将此File转换为路径名字符串。
  • public String getName() :返回由此File表示的文件或目录的名称。
  • public long length() :返回由此File表示的文件的长度。不存在,返回0。
    判断功能的方法
  • public boolean exists() :此File表示的文件或目录是否实际存在。
  • public boolean isDirectory() :此File表示的是否为目录。
  • public boolean isFile() :此File表示的是否为文件。
    创建删除功能的方法
  • public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    • 文件不存在,创建文件,返回true,文件存在,创建失败,返回false
    • 创建文件的路径必须存在,否则会抛出异常(IOException)
  • public boolean mkdir() :创建由此File表示的目录。(单级)

    只会创建目录,与名称是否存在后缀无关

  • public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录(多级)
  • public boolean delete() :删除由此File表示的文件或目录。
    • 删除成功,返回true,文件夹中有内容,不会删除,返回flase,构造方法中路径不存在,返回false
    • delete方法是直接在硬盘删除文件/文件夹,不走回收站,需要谨慎
    目录的遍历
  • public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
  • public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
    • list方法和listFiles方法遍历的是构造方法中给出的目录
    • 如果构造方法中给出的目录的路径不存在,会抛出空指针异常
    • 如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
文件过滤器
FileFilter
  • java.io.FileFilter接口;用于抽象路径名(File对象)的过滤器
  • 作用;用来过滤文件(File对象)
  • 抽象方法;用来过滤文件的方法
    • boolean accept(File pathname);测试指定抽象路径名是否应该包含在某个路径名列表中
      FilenameFilter
  • java.io.FilenameFilter接口;实现此接口的类实例可用于过滤文件名
  • 作用;用于过滤文件名
  • 抽象方法;用来过滤文件的方法
    • boolean accept(File dir, String name);测试指定文件是否应该包含在某一文件列表中

      参数;

      • File dir;构造方法中传递的被遍历的目录
      • String name;使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
  • 注意;两个过滤器接口没有实现类,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤规则

IO流

IO分类
  • 数据流向:
    • 输入流 :把数据从其他设备上读取到内存中的流。
    • 输出流 :把数据从内存中写出到其他设备上的流。
  • 数据类型;
    • 字节流 :以字节为单位,读写数据的流。
    • 字符流 :以字符为单位,读写数据的流。
      • 1字符=2字节 1字节=8位
  • 顶级父类:
类型 输入流 输出流
字节流 字节输入流
InputStream
字节输出流
OutputStream
字符流 字符输入流
Reader
字符输出流
Writer
字节流
字节输出流
  • java.io.OutputStream;抽象类表示字节输出流的所有类的超类
  • 常用方法;
    • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。

      注意;当完成流的操作时,必须调用此方法,释放系统资源

    • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
    • public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流(以二进制存入)。
      • 如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
      • 如果写的第一个字节是负数,那第一个字节回合第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
    • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
    • public abstract void write(int b) :将指定的字节输出流。
  • FileOutputStream类
    • 构造方法:
      • public FileOutputStream(File file) :创建文件输出流以写入由指定的 File对象表示的文件。
      • public FileOutputStream(String name) : 创建文件输出流以指定的名称写入文件。

        注意;当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

        写入数据的原理
  • 内存–>硬盘
  • Java抽象–>JVM–>OS–>OS调用写数据的方法–>把数据写入到文件中
    数据追加续写
  • public FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的
    文件。
  • public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件

    boolean append:追加写开关

    • true:创建对象不会覆盖原文件,继续在文件的末尾追加写数据
    • false;创建一个新文件,覆盖源文件
    换行写
  • Windows系统里,换行符号是\r\n

字节输入流
  • java.io.InputStream;抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中
  • 常用方法;
    • public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
    • public abstract int read() : 从输入流读取数据的下一个字节
    • public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
      • byte[];起到缓冲作用,存储每次读取到的多个字节,一般定义为1024(1kb)或1024的整数倍
      • 返回值int ;每次读取的有效字节个数
  • FileInputStream类
    • java.io.FileInputStream;从文件中读取字节
    • 构造方法;
      • FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
      • FileInputStream(String name) : 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
        读取数据的原理
  • 硬盘->内存
  • java程序->JVM->OS->OS读取数据的方法->读取文件
    关于中文
  • 1个中文:
    • GBK;占用2个字节
    • UTF-8;占用3个字节
字符流
字符输出流
  • java.io.Writer
  • 常用方法:
    • void write(int c) 写入单个字符。void write(char[] cbuf) 写入字符数组。- abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
    • void write(String str) 写入字符串。
    • void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
    • void flush() 刷新该流的缓冲。void close() 关闭此流,但要先刷新它。
  • FileWriter extends OutputStreamWriter extends Writer
    • 构造方法;
      • FileWriter(File file) : 创建一个新的 FileWriter,给定要读取的File对象。
      • FileWriter(String fileName) : 创建一个新的 FileWriter,给定要读取的文件的名称
    • 使用步骤;
      1. 创建FileWriter对象,构造方法中绑定要写入数据的目的地
      2. 使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转字节的过程)
      3. 使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
      4. 释放资源(会先把内存缓冲区的数据刷新到文件中)
字符输入流
  • java.io.Reader;抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。
  • 常用方法:
    • public void close() :关闭此流并释放与此流相关联的任何系统资源。
    • public int read() : 从输入流读取一个字符。
    • public int read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
  • FileReader类 extends InputStreamReader extends Reader

    • 构造方法:
      • FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。
      • FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称
    • 使用方法;
      1. 创建FileReader对象,构造方法中绑定要读取的数据源
      2. 使用FileReader对象中的方法read读取文件
      3. 释放资源
  • flush和close

    • flush :刷新缓冲区,流对象可以继续使用。
    • close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
注意
  • 字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流
    IO异常处理
  • jdk1.7之前使用try…catch finally处理流中的异常

    • 格式;
      1
      2
      3
      4
      5
      6
      7
      8
      try{
      可能产生出异常的代码
      }catch(异常类型 变量名){
      异常的处理逻辑
      }finally{
      一定会执行的代码
      (资源释放)
      }
  • JDK7新特性:在try的后边可以增加一个(),在括号中可以定义流对象,那么这个流对象的作用域就在try中有效。try中的代码执行完毕,会自动把流对象释放,不用写finally

    • 格式:
      1
      2
      3
      4
      5
      try(定义流对象;定义流对象;...){
      可能产生出异常的代码
      }catch(异常类型 变量名){
      异常的处理逻辑
      }
  • JDK9新特性;try的前面可以定义流对象,try后边的()中可以直接引入流对象的每次(变量名),在try代码执行完毕只会,流对象可以自动释放,不用写finally

    • 格式:
      1
      2
      3
      4
      5
      6
      7
      A a = new A();
      B b = new B();
      try(a,b){
      可能产生出异常的代码
      }catch(异常类型 变量名){
      异常的处理逻辑
      }

属性集

概述
  • java.util.Properties extends Hashtable<K,V> implements Map<K,V>
  • Properties表示一个持久的属性集。Properties可保存在流中或从流中加载。
  • Properties集合是一个唯一和IO流相结合的集合
    • 可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    • 可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
  • 属性列表中每个键及其对应值都是一个字符串:Properties集合是一个双列集合,key和value默认都是字符串
    Properties类
  • 构造方法:
    • public Properties();创建一个空的属性列表
  • 常用方法:
    • public Object setProperty(String key, String value) : 保存一对属性。
    • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。
    • public Set<String> stringPropertyNames() :所有键的名称的集合
  • 与流相关的方法

    • void store(OutputStream out, String comments);以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
    • void store(Writer writer, String comments);以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

      参数;

      • OutputStream out;字节输入流,不能写入中文
      • Writer writer;字符输出流,可以写中文
      • String comments;注释,用来解释说明保存的文件是做什么的,不能使用中文,默认unicode编码,一般使用””空字符串
    • void load(InputStream inStream) ;从输入流中读取属性void列表(键和元素对)。

    • void load(Reader reader) ;按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

      注意;

      • 存储键值对的文件中,键与值默认的链接符号可以使用=,空格(其他符合)
      • 存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
      • 存储键值对的文件中,键与默认值都是字符串,不用加引号

缓冲流

  • 字节缓冲流: BufferedInputStreamBufferedOutputStream
  • 字符缓冲流: BufferedReaderBufferedWriter
  • 缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
字节缓冲流
  • BufferedOutputStream;字节缓冲输出流

    • java.io.BufferedOutputStream.extends.OutputStream
    • 构造方法;
      • public BufferedOutputStream(OutputStream out); 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
      • public BufferedOutputStream(OutputStream out, int size);创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
    • 使用方法;
      1. 创建FileOutputStream对象,构造方法中绑定要输出的目的地
      2. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率
      3. 使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
      4. 使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
      5. 释放资源(会先调用flush方法刷新的数据,第4步可以省略)
  • BufferedInputStream;字节缓冲输入流

    • java.io.BufferedInputStream.extends.InputStream
    • 构造方法;
      • public BufferedInputStream(InputStream in); 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
      • public BufferedInputStream(InputStream in, int size);创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流
    • 使用方法;
      1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
      2. 创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象效率
      3. 使用BufferedInputStream对象中的方法read,读取文件
      4. 释放资源(会先调用flush方法刷新的数据,第4步可以省略)
字符缓冲流
  • BufferedWriter;字符缓冲输出流

    • java.io.BufferedWriter.extends.Writer
    • 构造方法;
      • public BufferedWriter(Writer out); 创建一个使用默认大小输出缓冲区的缓冲字符输出流
      • public BufferedWriter(Writer out, int size);创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
    • 特有的成员方法;
      • void newLine();写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
    • 使用方法;
      1. 创建FileWriter对象,构造方法中绑定要输出的目的地
      2. 调用字符缓冲输出流的方法write,吧数据写入到内存缓冲区中
      3. 调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
      4. 释放资源
  • BufferedReader;字符缓冲输入流

    • java.io.BufferedReader.extends.Reader
    • 构造方法;
      • public BufferedReader(Reader in); 创建一个使用默认大小输入缓冲区的缓冲字符输入流
      • public BufferedReader(Reader in, int size);创建一个使用指定大小输入缓冲区字符输入流
    • 特有的成员方法;
      • String readLine();读取一个文本行,读取一行数据
        • 行的终止符号;通过下列字符之一即可认为某行已终止(换行:\n,回车;\r,或回车后直接跟着换行\r\n
        • 返回值;包含该行内容的字符串,不包含任何终止符,如果已到达流末尾,则返回null
    • 使用方法;
      1. 创建字符缓冲输入流对象,构造方法中传递字符输入流
      2. 使用字符缓冲输入流对象中的方法read/readLine读取文本
      3. 释放资源

转换流

字符编码
  • 字符编码 Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。
    字符集
  • 字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
    InputStreamReader类
  • 字节–(解码)–>字符
  • java.io.InputStreamReader extends Reader;从字节流到字符流的桥梁,它读取字节,并使用指定的字符集将其解码为字符
  • 构造方法;
    • InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。- InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
      OutputStreamWriter类
  • 字符–(编码)–>字节
  • java.io.OutputStreamWriter extends Writer;从字符流到字节流的桥梁,使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
  • 构造方法;
    • OutputStreamWriter(OutputStream out) : 创建一个使用默认字符集的字符流。
    • OutputStreamWriter(OutputStream out, String charsetName) : 创建一个指定字符集的字符流。

序列化流

  • 序列化;把对象以流的方式,写入到文件中保存,也叫写对象
  • 反序列化;把文件中保存的对象,以流的方式读取出来,也叫读对象
    ObjectOutputStream类
  • 序列化。Java对象的原始数据类型写出到文件,实现对象的持久存储。
  • 构造方法;
    • public ObjectOutputStream(OutputStream out): 创建一个指定OutputStream的ObjectOutputStream。
  • 成员方法;
    • void writerObject(Object obj);将指定的对象写入ObjectOutputStream
      序列化操作
  • 一个对象要想序列化,必须满足两个条件
    • 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任
      何状态序列化或反序列化,会抛出NotSerializableException
    • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰(静态也不能被序列化)。
ObjectInputStream类
  • 反序列化。将之前使用ObjectOutputStream序列化的原始数据恢复为对象
  • 构造方法;
    • public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。
  • 成员方法;
    • Object readObject();从ObjectInputStream读取对象
反序列化操作
  • 类必须实现Serializable
  • 必须存在类对应的.class文件,如果找不到该类的class文件,则抛出一个
    ClassNotFoundException异常。
  • InvalidClassException
    • 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个 InvalidClassException 异常。发生这个异常的原因如下:
      1. 该类的序列版本号与从流中读取的类描述符的版本号不匹配
      2. 该类包含未知数据类型
      3. 该类没有可访问的无参数构造方法
    • Serializable 接口给需要序列化的类,提供了一个序列版本号。 static final long serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配

打印流

Knowledge is priceless, thanks for your support !