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
- 编写:编写HelloWorld.java
 - 编译:打开CMD,找到程序所在目录,输入
javac HelloWorld.java,生成:HelloWorld.class 文件 - 运行:输入 
java HelloWorld,无需后缀,指定类名 
注释
- 单行注释 : //
 - 多行注释 : / /
 
关键字、标识符
- 关键字 : 完全小写的字母(public)
 - 标识符 : ·在程序中,自己定义的内容,比如类的名字和变量的名
- 命名规则: 
- 由 英文字母、0-9、$(美元符)、_(下划线)组成
 - 不能以数字开头
 - 不能是关键字
 
 - 命名规范:
- 类名规范:首字母大写,后面每个单词首字母大写(大驼峰)
 - 变量名规范:首字母小写,后面每个单词首字母大写(小驼峰)
 - 方法名规范:同变量名
 
 
 - 命名规则: 
 
常量
- 在程序运行期间,固定不变的量
 - 分类:
- 字符串常量:双引号引用的部分,例如:”abc”,”123”
 - 整数常量:直接写上数字,没有小数点,例如:100,200
 - 浮点数常量:直接写上数字,有小数点,例如:2.5,-3.14
 - 字符常量:单引号引起来的单个字符,例如:’A’,’中’(没有’’,’AB’,有’ ‘)
 - 布尔常量:只有两种取值,true、false
 - 空常量: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,推荐使用大写字母后缀
 
数据类型转换
自动类型转换(隐式)
- 特点:代码不需要进行特殊处理,自动完成。
 - 规则:数据范围从小到大,向上兼容
 
强制类型转换(显式)
- 特点: 代码需要进行特殊格式处理,不能自动完成
 - 格式:范围小的类型 范围小的变量名 = (范围小的类型) 原本范围大的数据
 
注意事项:
- 强制类型转换一般不推荐使用,因为有可能发生精度损失(小数)、数据溢出(大数)
 - byte/short/char 这三种类型都可以发生数学运算,例如加法”+”。
 - byte/short/char 这三种类型在运算时,都会被首先提升为int类型,然后再计算。
 - boolean类型不能发生数据类型转换
 - 对于byte/short/char 三种类型来说,如果右侧赋值的数值没有超过范围,那么javac编译器会自动隐含地为我们补上一个(byte)(short)(char)。
- 如果没有超过左侧范围,编译器自动补上强转。
 - 如果超过左侧范围,编译器会直接报错。
 
 - 在给变量进行赋值时候,如果右侧地表达式当中全部都是常量,没有任何变量,那么编译器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  | switch(){  | 
- case的穿透性:如果case的后面不写break,将出现穿透现象,直接向后运行,直到遇到break;
 
循环
- for
 - while
 do-while:无条件执行一次循环体,具有一定风险性
1
2
3
4
5初始化表达式
do{
循环体
步进表达式
}while(布尔表达式)区别:
- 如果条件判断从来没有满足过,for和while将执行0次,do-while至少循环一次
 - 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后面的”返回值“,必须和方法名称前面的返回值类型,保持对应
 
 
方法的调用
- 单独调用: 方法名称(参数),返回值为void,只能单独调用
 - 打印调用: System.out.println(方法名称(参数));
 - 赋值调用: 数据类型 变量名称 = 方法名称(参数);
 
方法的重载(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
4public class ClassName{
//成员变量
//成员方法
}- 通常情况下,一个类不能直接使用,需要根据类创建一个对象,才能使用
- 导包:指出所需要使用的类,在什么位置:
import 包名称.类名称 - 创建:
类名称 对象名 = new 类名称(); - 使用:
- 使用成员变量:
对象名.成员变量名 - 使用成员方法:
对象名.成员方法名(参数) 
 - 使用成员变量:
 
- 注意:
- 当使用一个对象类型作为方法的参数时,传递的就是对象的地址值。
 - 当使用一个对象类型作为方法的返回值时,返回值其实就是对象的地址值。
 
 
 - 导包:指出所需要使用的类,在什么位置:
 
成员变量与局部变量的区别
- 定义的位置不一样
- 局部变量:方法内部
 - 成员变量:方法外部,直接写在类当中
 
 - 作用的范围不一样
- 局部变量:只有方法当中才可以使用
 - 成员变量:整个类可以使用
 
 - 默认值不一样
- 局部变量:没有默认值,必须手动赋值
 - 成员变量:如果没有进行赋值,会有默认值,规则和数组一样
 
 - 内存位置不一样
- 局部变量;位于栈内存中
 - 成员变量:位于堆内存中
 
 - 生存周期不一样
 - 封装性在Java当中的体现
- 方法就是一种封装
 - 关键字private也是一种封装
 
 - 封装就是将一些细节信息隐藏起来,对于外界不可见
 
Private
private是一个权限修饰符,代表最小权限- 可以修饰成员变量和成员方法
 - 被
private修饰后的成员变量和成员方法,只有在本类中访问,超过本类范围就不能被访问 - 通过设置
setXXX(),getXXX()方法来实现外部访问 
This
this代表所在类的当前对象的引用(地址值),即对象自己的引用- 当方法的局部变量和类的成员变量重名的时候,根据就近原则,优先使用局部变量
 - 如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量名 - 通过谁调用的方法,谁就是
this 
构造方法
- 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。
 格式
1
2
3public 类名称(参数类型 参数名称){
方法体
}注意事项:
- 构造方法的名称必须和所在类名称完全一致
 - 构造方法不要写返回值类型,连void也不用写
 - 构造方法不能return一个具体的返回值
 - 如果没有编写任何构造方法,编译器会默认添加一个无参,无方法体的构造方法
 - 一旦编写了至少一个构造方法,那么编译器将不再默认添加
 - 构造方法也可以进行重载(方法名相同,参数列表不同)
 
标准类(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使用步骤:
- 打开帮助文档
 - 点击显示,找到快速索引,看到输入框
 - 输入需要查找的内容
 - 看包,java.lang下的类不需要导包,其他需要
 - 看类的解释和说明
 - 学习构造方法
 - 使用成员方法
 
 
Scanner类
- 功能:可以实现键盘输入数据,到程序中
 - 引用类型一般使用步骤:
- 导包: 
import 包路径.类名称,如果需要使用的目标类和当前类位于同一个包下,则可以省略导包语句不写,只有java.lang包下的内容不需要导包,其他包都需要import语句 - 创建: 
类名称 对象名 = new 类名称() - 使用: 
对象名·成员方法名() 
 - 导包: 
 - 导包:
import java.util.Scanner - 创建:
Scanner sc = new Scanner(System.in),System.in表示从键盘进行输入 - 使用:
- 获取键盘输入的一个int数字:
int num = sc.nextInt(); - 获取键盘输入的一个字符串:
String str = sc.next(); 
 - 获取键盘输入的一个int数字:
 
匿名对象
- 创建对象时,只有创建对象的语句,却没有把对象地址赋值给某个变量
 - 格式:
new 类名称(参数列表);,左边没有变量值和赋值号。 - 应用场景:
- 创建匿名对象直接调用方法,没有变量名
 - 匿名对象只能使用唯一的一次,下次再用不得不创建一个新对象,造成浪费
 - 匿名对象可以作为方法的参数和返回值
 
 
1  | import java.util.Scanner;  | 
Random类
- 作用:产生随机数
 - 使用步骤:
- 查看类:导包,
import java.util.Random - 查看构造方法:创建,
Random r = new Random() - 查看成员方法:使用,
- 获取一个随机的int数字:
int num = r.nextInt() - 获取一个随机的int数字:(参数代表范围,左闭右开区间),
int num = r.nextInt(3),实代表的范围是 [0,3) 
 - 获取一个随机的int数字:
 
 - 查看类:导包,
 
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[ ]字节数组。
 
创建字符串:
- 三种构造方法
- public String():创建一个空白字符串,不含任何内容
 - public String(char[] array):根据字符数组的内容,来创建对应的字符串
 - 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,推荐使用类名称进行调用- 静态变量:类名称.静态变量
 - 静态方法:类名称.静态方法()
 
 - 对于本类当中的静态方法,可以省略类名称,编译器会自动补全
 - 注意:
- 静态不能直接访问非静态,因为在内存当中先有静态内容,后有非静态内容
 - 静态方法不能用
this,因为this代表当前对象,通过谁调用方法,谁就是当前对象 
 
静态代码块
格式
1
2
3
4
5public 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
3public class 父类名称{
//...
}子类:
1
2
3public class 子类名称 extends 父类名称{
//...
}
特点
成员变量
- 父类无法使用子类成员变量
 - 子类仅可以直接访问父类的非私有成员变量
 - 成员变成重名:
- 直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找
 - 间接通过成员方法访问成员变量:方法属于谁就优先用谁,没有则向上找
 
 - 三种变量重名:
- 局部变量:直接写成员变量名
 - 本类的成员变量:this.成员变量名
 - 父类的成员变量:super.成员变量名
 
 
成员方法
- 成员方法不重名:正常使用
 - 成员方法重名:创建的对象是谁,优先用谁的方法
 - 注意:无论是成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类
 
方法重写
- 重写(Override)【覆盖】:在继承关系当中,方法的名称一样,参数列表也一样
 - 对比:
- 重载:Overload:方法名称相同,参数列表不同
 - 重写:Override:方法名称相同,参数列表也相同–覆盖
 
 - 特点:创建的是子类对象,则优先使用子类方法
 - 注意:
- 必须保证父子类之间的方法名称相同,参数列表也相同
@Override // 写在方法前面,用来检测是不是有效的正确的覆盖重写(注解)
// 这个注解就算不写,只要满足要求,也是正确的方法覆盖重写 
 - 必须保证父子类之间的方法名称相同,参数列表也相同
 
2. 子类方法的返回值必须**小于等于**父类方法的返回值范围
    > `java.lang.Object`类是所有类的公共最高父类,java.lang.String就是Object的子类
3. 子类方法的权限必须**大于等于**父类方法的权限修饰符
    > public > protected > (default)(不写) > private
构造方法
- 继承关系中,父子类构造方法的访问特点;
- 子类构造方法当中有一个默认隐含的
super()调用,所以一定是先调用父类构造,后执行子类的构造 - 子类构造可以通过
super关键字来调用父类重载构造 super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造
 - 子类构造方法当中有一个默认隐含的
 
super关键字
- 在子类的成员方法中,访问父类的成员变量:
super.变量名 - 在子类的成员方法中,访问父类的成员方法:
super.方法名 - 在子类的构造方法中,访问父类的构造方法:
super(); 
继承的特征
- Java语言是
单继承的,一个类的直接父类只能有一个 - Java语言可以
多级继承(继承体系),顶层都是Object类 - 一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
 
抽象类
定义
- 没有方法主体的方法称为抽象方法,包含抽象方法的类,必须是抽象类
- 抽象类:包含抽象方法的类
 - 抽象方法:没有方法体的方法,加上abstract关键字,去掉方法体,分号结束
 
 
格式
- 抽象方法:  
1
2// 修饰符 abstract 返回值类型 方法名(参数列表);
public abstract void method(); 
使用
- 不能直接创建new抽象类对象
 - 必须用子类继承抽象父类
 - 子类必须覆盖重写抽象父类所有的抽象方法
 - 继承抽象类的子类必须重写父类所有的抽象方法,否则该子类也必须声明为抽象类,最终必须要有子类实现该父类的抽象方法,否则抽象父类到子类的创建全部失效,失去意义
 
注意
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象
 - 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
 - 抽象类中,不一定包含抽象方法,但是有抽象方法的类一定是抽象类
 - 抽象类的子类,必须重写抽象父类所有的抽象方法,否则,编译无法通过报错,除非该子类也是抽象类
 
接口
定义
- 接口就是一种公共的规范标准
 - 接口是一种引用数据类型(数组、类、接口)
 格式
1
2
3public interface 接口名称{
// 接口内容
}备注:换成关键字interface之后,编译生成的字节码文件仍然是:.java->.class
java版本
- java7:
- 常量
 - 抽象方法
 
 - java8:
- 默认方法
 - 静态方法
 
 - java9
- 私有方法
 
 
使用
接口不能直接使用,必须有一个“实现类”来“实现”接口
- 格式:
1
2
3public class 实现类名称 implements 接口名称{
// ...
} 
- 格式:
 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法(否则它必须是一个抽象类)
- 实现:去掉abstract关键字,加上方法体和大括号
 
- 创建实现类的对象,进行使用
 
接口的抽象方法
- 注意:
- 接口当中的抽象方法,修饰符必须是两个固定关键字:
public abstract - 这两个关键字修饰符,可以选择性省略
 - 方法的三要素,可以随意定义
 
 - 接口当中的抽象方法,修饰符必须是两个固定关键字:
 
接口的默认方法
- 从Java8开始,可以使用默认方法,默认方法可以被实现类继承
 - 作用:解决接口升级的问题(实现类可以重写默认方法)
 - 格式:
1
2
3public default 返回值类型 方法名称(参数列表){
// 方法体
} 
接口的静态方法
- 从Java8开始,接口当中允许定义静态方法
 - 静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用
 格式:
1
2
3public static 返回值类型 方法名称(参数列表){
方法体
}注意:不能通过接口实现类的对象来调用当中的静态方法
- 正确用法:通过接口名称,直接调用其中的静态方法。
 - 格式:
接口名称.静态方法名称(参数) 
接口的私有方法
- 我们需要抽取一个共有方法,用来解决多个默认方法之间重复代码的问题。但是这个共有方法不应该让实现类使用,应该是私有化的。
 从Java9开始,接口当中允许定义私有方法
普通私有方法:解决多个默认方法之间重复代码问题
格式:1
2
3private 返回值类型 方法名称(参数列表){
方法体
}静态私有方法:解决多个静态方法之间重复代码问题
1
2
3private static 返回值类型 方法名称(参数列表){
方法体
}
接口的成员变量(常量)
- 接口中可以定义“成员变量”,但是必须使用
public static final三个关键字进行修饰。从效果上看,这其实就是接口的【常量】 - 格式:
public static final 数据类型 常量名称 = 数据值; - 一旦使用
final关键字进行修饰,说明不可改变 - 注意:
- 接口当中的常量,可以省略
public static final - 接口当中的常量,必须进行赋值,不能不赋值
 - 接口中,常量的名称,使用完全大写的字母,用下划线进行分隔(shift+F6)
 
 - 接口当中的常量,可以省略
 
总结
在java9+版本中,接口的内容可以有
成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;- 注意:
- 常量必须进行赋值,而且一旦赋值不能改变
 - 常量名称完全大写,用下划线进行分隔
 
 
- 注意:
 接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表)- 注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类
 
从Java8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表){方法体}- 注意:默认方法也可以被覆盖重写
 
从Java8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表){方法体}- 注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
 
从Java9开始,接口里允许定义私有方法,格式:
private 返回值类型 方法名称(参数列表){方法体}private static 返回值类型 方法名称(参数列表){方法体}- 注意:private方法只有接口自己才能调用,不能被实现类或别人使用
 
注意
- 接口不能有静态代码块
 - 接口不能有构造方法
 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
- 格式:  
1
2
3public class 实现类 implements 接口A,接口B{
// 覆盖重写所有抽象方法
} 
- 格式:  
 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可
- 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类
 - 如果实现类所实现的多个接口,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
 - 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法(父类>>接口)
 
接口的多继承
- 类与类之间是单继承的,直接父类只能有一个
 - 类与接口之间是多实现的,一个类可以实现多个接口
 - 接口与接口之间是多继承的
 - 使用:关键字
extends - 注意
- 多个父接口当中的抽象方法如果有重复,没关系
 - 多个父接口当中的默认方法如果有重复,那么子接口必须进行默认方法的覆盖重写,【而且要带着
default关键字】 - 实现类重写接口默认方法,不需要保留
default关键字 
 
多态
概述
- 多态性:一个对象拥有多种形态
 - 代码当中体现多态性:
【父类引用指向子类对象】- 格式:
 - 父类名称 对象名 = new 子类名称();
 - 或者
 - 接口名称 对象名 = new 实现类名();
 
 - 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误,如果有,执行的是子类重写后方法
 
多态中的成员变量
- 直接通过对象名访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
 - 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
 
多态中的成员方法
- new的是谁,就优先用谁,没有则向上找
 - 注意:
- 成员方法:编译看左,运行看右
 - 成员变量:编译看左,运行看左
 
 
多态的优点
- 方法调用相同(编译时),用谁找谁(运行时),动态过程
 
引用类型转换
向上转型
- 向上转型:多态本身是子类类型向父类类型向上转型的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型
 - 格式:
父类名称 对象名 = new 子类名称(); - 注意:向上转型一定是安全的。从小范围–>大范围
 - 缺陷:无法调用子类特有的内容
 
向下转型
- 向下转型:父类类型向子类类型向下转型的过程(还原),这个过程是强制的
 - 格式:
子类名称 对象名 = (子类名称)父类对象; - 含义:将父类对象,还原成原本的子类对象
 - 注意:
- 要还原的对象必须是原来创建的对象,才能向下转型
 - 要还原的对象如果不是原来创建的对象,而是其他对象,就会报错(运行异常,ClassCastException)
 
 
instance of关键字
- 作用:返回一个boolean值,判断前面的对象能不能当作后面类型的实例
 - 格式:
变量名 instance of 数据类型 
final关键字
- 含义:代表最终、不可改变的
 - 使用:
- 修饰一个类,不能被继承
 - 修饰一个方法,不能被重写
 - 修饰一个局部变量,不能被重新赋值
 - 修饰一个成员变量,不能被重新赋值
 
 
修饰类
格式:
1
2
3public final class 类名称(){
// ...
}作用:当前这个类不能有任何的子类(final类不能作为父类)
- 注意:final类的所有成员方法都无法进行覆盖重写
 
修饰方法
格式:
1
2
3public final 返回值 方法名(参数列表){
// 方法体
}作用:当前方法就是最终方法,不能再被覆盖重写
- 注意:对于类、方法来说,
abstract关键字和final关键字不能同时使用,因为矛盾 
修饰局部变量
格式:
1
final 数据类型 数据名 = 数据值;
作用:当前局部变量,不能进行更改,一次赋值,终生不变
- 注意:对于基本类型,不可变说的是变量当中的数据不可变;对于引用类型来说,不可变说的是变量当中的地址值不可变
 
修饰成员变量
格式:
1
final 数据类型 数据名 = 数据值;
作用:当前成员变量,不能进行更改,但成员变量有默认值,用了final后必须进行手动赋值
- 注意:
- 对于final的成员变量,要么使用直接赋值,要么使用构造方法赋值(二选一)
 - 必须保证类当中所有重载构造方法,都最终会对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被称为外部类 成员内部类:定义在类中方法外的类
分类:
- 成员内部类
 - 局部内部类(包含匿名内部类)
 
- 注意:内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和
$符号。比如,Person$Heart.class 
成员内部类
- 定义在一个类内成员方法外的类
 格式:
1
2
3
4
5
6修饰符 class 外部类名称{
修饰符 class 内部类名称{
//...
}
//...
}使用:
- 直接:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称() - 间接:在外部类的方法中,使用内部类:然后main只是调用外部类的方法
 
- 直接:
 注意;
- 内用外,随意访问,外用内,需要内部类对象
 - 同名变量访问:(重名)
外部类名称.this.外部类成员变量名 
局部内部类
- 定义在一个方法内部的类,“局部”:只有当前所属方法才能使用,出了方法无法使用
 格式:
1
2
3
4
5
6
7修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
//...
}
}
}使用:方法内创建局部类对象,main直接调用外部类方法
- 注意:权限修饰符
- 外部类:public/(default)
 - 成员内部类:public / protected / (default) / private
 - 局部内部类:无法使用修饰符
 
 
局部内部类的final问题
- 局部内部类,如果希望访问所在方法的局部变量,那么这份局部变量必须是
有效final的(从Java8开始,只要局部变量事实不变,那么final关键字可以省略) - 原因:
- new出来的对象在堆内存当中
 - 局部变量是跟着方法走到,在栈内存中
 - 方法运行结束之后,立刻出栈,局部变量就会立刻消失
 - new出来的对象会在堆中持续存在,直到垃圾回收消失
 
 
匿名内部类
如果接口的实现类(或者是父类的子类),只需要使用唯一的一次,那么这种情况就可以省略掉该类的定义,改用
匿名内部类格式:
1
2
3接口名称 对象名 = new 接口名称(){
// 覆盖重写所有抽象方法
};对”new 接口名称(){…};”解析:
- new代表创建对象的动作
 - 接口名称就是匿名内部类需要实现哪个接口
 - {…}这才是匿名内部类的内容
 
- 注意:
- 匿名内部类,在创建对象的时候,只能使用唯一一次,如果希望多次创建对象,而且内容一样,那么就必须使用单独的实现类
 - 匿名对象,在调用方法的时候,只能调用一次,如果希望同一个对象,调用多次方法,必须要给对象取名
 - 匿名内部类省略了实现类/子类,匿名对象省略了对象名称
 - 匿名内部类和匿名对象不是一回事
 
 
常用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方法,默认比较对象的地址值,如果需要比较内容,需要覆盖重写
 - 参数:
- Object obj:可以传递任意的对象
 - this ;调用的对象
 - obj ;参数
 
 - 基本数据类型:比较地址值
 - 引用数据类型;比较对象的地址值
 - 覆盖重写:
- 问题:隐含一个多态,无法使用子类特有的内容(属性和方法)
 - 解决:向下转型(强转)
 - alt+insert ,equals() and hashCode()
 
 
Objects类
- JDK7中添加的工具类,其中的方法是null-save(空指针安全)或null-tolerant(容忍空指针),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象
 - Objects.equals方法:防止出现空指针异常
1
2
3public 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 - 成员方法:
- String format(Date date):按照指定的模式,把Date日期,格式化为符合模式的字符串
 - 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方法
 
基本类型包装类
- 使用一个类,将基本类型的数据装起来,在类中定义一些方法,这个类叫做包装类,我们可以使用类中的方法来操作这些基本类型的数据
装箱与拆箱
 装箱;把基本类型的数据,包装到包装类中
- 构造方法:
- Integer(int value);构造一个新分配的Integer对象,它表示指定的int值
 - Integer(String s);构造一个新分配的Integer对象,它表示String参数所指示的int值(注意:传递的字符串,必须是基本类型的字符串,否则会抛出异常‘100’正确,‘0’异常)
 
 - 静态方法
static Integer valueOf(int i);返回一个表示指定的int值的Integer对象static Integer valueOf(String s);返回保存指定的String的值的Integer对象
 
- 构造方法:
 拆箱;在包装类中取出基本类型的数据
- 成员方法;
int intvalue();以int类型返回该Integer值 
- 成员方法;
 
自动装箱与自动拆箱
- 基本类型的数据和包装类之间可以自动地相互转换(JDK1.5之后)
 
基本类型与字符串类型的转换
基本类型->字符串;
- 基本类型的值+””
 - 包装类的静态方法toString(参数),不是Object类的toString()方法,发生了重载
 - String类的静态方法valueOf(参数)
 
字符串->基本类型;
- 包装类的静态方法;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();如果仍有元素可以迭代,返回trueE next();返回迭代的下一个元素
注意:Iterator迭代器,是一个接口,无法直接使用,需要使用Iterator接口的实现类对象。Colletion接口中有一个方法,叫iterator(),返回的就是迭代器的实现类对象
- 使用步骤;
- 使用集合中的方法
iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态),注意迭代器也有泛型,跟着集合走 - 使用Iterator接口中的方法hasNext判断还有没有下一个元素
 - 使用Iterator接口中的方法next取出集合中的下一个元素
 
 - 使用集合中的方法
 
增强for循环
- 底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写,jdk1.5之后
 Collection<E>extends Iterable<E>;所有的单列集合都可以使用增强forpublic interface Iterable<T>;实现这个接口允许对象成为”foreach”语句的目标格式
1
2
3for(集合/数组的数据类型 变量名:集合名/数组名){
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 <泛型> 返回值类型 方法名(泛型 参数名,...){
方法体;
} 
- 格式;
 含有泛型的接口
- ?;代表任意的数据类型
 - 使用:
- 不能创建对象使用
 - 只能作为方法的参数使用
 
 - 注意:泛型不存在继承关系 Collection
 
泛型的上限限定
? 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
- 特点;
- 由哈希表支持,不保证迭代顺序
 - 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
 - 底层是一个哈希表结构(查询速度快)
 
 - 哈希值;
- 是一个十进制的整数,由系统随机给出(对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)
 - 在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…多个
 - 注意;
- 一个方法的参数列表,只能有一个可变参数
 - 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
 - 终极写法;
(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)-参数;升序
 
 Comparator和Comparable的区别- Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
 - Comparator;找一个第三方的裁判
 - Comparator排序规则;o1-o2升序,o2-o1降序
 
Map集合
- java.util.Map<k,v>
 - 特点;
- Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
 - Map集合中的元素,key和value的数据类型可以相同,也可以不同
 - Map集合中的元素,key是不允许重复的,value是可以重复的
 - Map集合中的元素,key和value是一一对应的
 
 - 和
Collection的区别:Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。Collection中的集合称为单列集合,Map中的集合称为双列集合。- 需要注意的是,
Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。 
 
Map常用子类
HashMap<K,V>;- HashMap底层是哈希表,查询速度快,元素的存储顺序不能保证一致
 - 为了保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法
 - JDK1.8之前;数组+单向链表
 - JDK1.8之后;数组+单向链表/红黑树(链表的长度超过8时)
 
LinkedHashMap<K,V>;- 是HashMap的子类,底层是哈希表+链表
 - 是一个有序的集合,存储元素和取出元素的顺序是一致的,需要重写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值
 
 
- 返回值V:
 public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。- 返回值V:
- key存在,返回被删除的值
 - key不存在,返回null
 
 
- 返回值V:
 
public V get(Object key)根据指定的键,在Map集合中获取对应的值。- 返回值V:
- key存在,返回对应value值
 - key不存在,返回null
 
 
- 返回值V:
 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方法的时候,不能有重复的元素,否则会抛出异常
 
 
 - List接口、Set接口、Map接口;增加了一个
 
Debug调试
- Debug调试程序;
- 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
 
 - 使用方式;
- 在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
 - 右键,选择Debug执行程序
 
 - 执行程序;
- f8;逐行执行程序
 - f7;进入到方法中
 - shift+f8;跳出方法
 - f9;跳到下一个断点,如果没有下一个断点,那么就结束程序
 - ctrl+f2;退出Debug模式,停止程序
 - Console;切换到控制台
 
 
异常与多线程
异常
- 异常;程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止
 - 异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象,Javac处理异常的方式是
中断处理异常体系
 Throwable;Java.lang.ThrowableError;工程师无法处理,只能尽力避免Exception;编译期异常,由于使用不当导致,可以避免RuntimeException;运行期异常,Java程序运行过程中出现的问题
异常处理
- 常用关键字;
try,catch,finally,throw,throws 
throw
- 在指定的方法中抛出指定的异常
 - 格式;
throw new xxxException("异常产生的原因") - 注意;
- throw关键字必须写在方法内部
 - throw关键字后边new的对象必须是Exception或者Exception的子类对象
 - throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
 - throw关键字后边创建的是RuntimeException或者是RuntimeException的子类对象,(运行期异常)可以不处理,默认交给JVM处理(打印异常对象,中断程序)
 - throw关键字后边创建的是编译异常,我们就必须处理这个异常,要么
throw,要么try...catch 
 
声明异常throws
- 异常处理的第一种方式,交给别人处理
 - 当方法内部抛出异常对象的时候,必须处理这个异常对象
 - 可以使用
throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理,最终交给JVM处理 格式;
1
2
3
4
5修饰符 返回值 方法名(参数列表) throws AAAException, BBBException...{
throw new AAAException("产生原因");
throw new BBBException("产生原因");
...
}注意;
throws关键字必须写在方法声明处throws关键字后边声明的异常必须是Exception或者是Exception的子类- 方法内部如果抛出了多个异常对象,那么
throws后边必须也声明多个异常。(如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可) - 调用了一个声明抛出异常的方法,我们就必须处理声明的异常。(要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM,要么
try...catch自己处理异常) 
捕获异常try…catch
格式;
1
2
3
4
5
6
7
8
9try{
可能产生异常的代码
}catch(异常类型 变量名){ //用来接收try中抛出的异常对象
处理异常的代码(一般把异常信息记录到一个日志中)
}
...
catch(异常类型 变量名){
}注意;
- try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
 - 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码
 - 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑
 
Throwable常用方法
public void printStackTrace();打印异常的详细信息public String getMessage();获取发生异常的原因public String toString();获取异常的类型和异常描述信息(不用)
finally代码块
格式
1
2
3
4
5
6
7try{
可能发生异常的代码
}catch(异常类型 变量名){
}finally{
无论是否出现异常都会执行
}注意;
finally不能单独使用,必须和try一起使用finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要释放资源(IO)
异常的注意事项
- 多个异常使用捕获该如何处理
- 多个异常分别处理
 - 多个异常一次捕获,多次处理
注意;一个try,多个catch,如果catch定义的异常变量,具有父子类的关系,子类的异常变量必须写在上面,否则会报错
 - 多个异常一次捕获(catch异常对象的父类),一次处理
 
 - 如果finally有return语句,永远返回finally中的结果,避免该情况
 - 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
 - 如果父类抛出了多个异常,子类重写父类方法时
- 抛出和父类相同的异常
 - 抛出父类异常的子类异常
 - 不抛
 
 - 父类没有抛出异常,子类也不能抛出异常,如果出现异常,只能捕获处理,不可抛出
 
自定义异常类
格式
1
2
3
4
5public class XXXException extends Exception|RuntimeException{
添加一个空参数的构造方法
添加一个带异常信息的构造方法
}注意;
- 自定义异常类一般都是Exception结尾,说明该类是一个异常类
 - 自定义异常类,必须的继承Exception或者RuntimeException
 - 继承Exception;自定义的异常类是一个编译器异常,如果方法内抛出了编译器异常,必须处理这个异常,要么抛出要么捕获
 - 继承RuntimeException;自定义的异常类是一个运行期异常,无需处理,交给JVM(中断处理)
 
多线程
并发与并行
- 并发;指两个或多个事件在同一个时间段内发生(交替执行)
 - 并行;指两个或多个事件在同一时刻发(同时发生)
线程与进程
 - 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
 - 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
 java.lang.Thread;描述线程的类,要实现多线程程序,必须继承Thread类- 实现步骤:
 java.lang.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);使当前正在执行的线程以指定的毫秒数暂停
 - 设置线程名称
 格式:
1
2
3
4new 父类/接口(){
重写父类/接口中的方法
}eg:
1
2
3
4
5
6new Thread(new Runnable() {
public void run() {
// 线程任务
}
}).start();
线程安全
线程同步
- 当使用多线程访问同一资源的时候,多个线程对资源有写操作,就容易出现线程安全问题
 - Java提供了同步机制(Synchronized)解决
线程同步机制
同步代码块
 格式:
1
2
3synchronized(锁对象){
需要同步操作的代码
}注意;
- 同步代码块中的锁对象,可以使用任意的对象
 - 必须保证多个线程使用的锁对象是同一个
 - 锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行,其他线程被阻塞(BLOCKED)
 - 频繁的上锁、解锁会降低程序效率,但是提供程序安全性
 
同步方法
- 使用
synchronized修饰的方法,就叫同步方法 格式:
1
2
3public synchronized 返回值类型 方法名(){
可能会产生线程安全问题的代码
}同步方法也会把方法内部打代码锁住
同步锁是谁?对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
Lock锁
java.util.concurrent.locks.Lock- Lock 实现了比 synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象
 - 创建对象
Lock 锁名 = new ReentrantLock();
 - 常用方法;
public void lock():加同步锁。public void unlock():释放同步锁
 
线程状态
| 线程状态 | 导致状态发生的条件 | 
|---|---|
| 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就是线程间的一种协作机制
 - 等待唤醒中的方法:
- wait:线程不再活动,不再参与调度,进入wait set中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
 - notify:选取所通知对象的 wait set中的一个线程释放
 - notifyAll;释放所通知对象的 wait set上的全部线程。
 
 - 注意:
- wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
 - wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
 - wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法
 
 
线程池
- 线程池;可以容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源
 - 优点;
- 降低资源消耗
 - 提高响应速度
 - 提高线程的可管理性
 
 
线程池的使用
- 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()
 
 
- 线程池的使用步骤
- 使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
 - 创建一个类,实现Runnable接口,重写run方法,设置线程任务
 - 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
 - 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
 
 
Lambda表达式
函数式编程思想
- 面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
 - 函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
Lambda
 - Java8
 - 实现多线程:
1
2
3
4
5
6
7
8
9
10// 匿名内部类实现多线程
new Thread(new Runnable(){
public void run(){
线程任务
}
}).start();
// Lambda表达式实现多线程
new Thread(()->{线程任务}).start();//启动线程 
Lambda表达式标准格式
- 组成;
- 一些参数
 - 一个箭头
 - 一段代码
 
 - 格式;
(参数列表)->{一些重写方法的代码}
 - 解释;
();接口中抽象方法的参数列表,没有参数就空着,有参数就写处参数,多个参数用逗号分隔->;床底的意思,把参数传递给方法体{}{};重写接口的抽象方法的方法体
 
Lambda省略格式
- 小括号内参数的类型可以省略;
 - 如果小括号内有且仅有一个参,则小括号可以省略;
 - 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
 
Lambda使用前提
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一
时,才可以使用Lambda。 
- 使用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;与系统有关的默认名称分隔符。路径
- 绝对路径;是一个完整的路径,以盘符开始的路径
 - 相对路径;是一个简化的路径,相对指的是相对于当前项目的根目录
 - 注意:
 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
注意;
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对象)
 - 抽象方法;用来过滤文件的方法
 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类:- 内存–>硬盘
 - 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程序->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,给定要读取的文件的名称
 - 使用步骤;
- 创建FileWriter对象,构造方法中绑定要写入数据的目的地
 - 使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转字节的过程)
 - 使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
 - 释放资源(会先把内存缓冲区的数据刷新到文件中)
 
 
- 构造方法;
 
字符输入流
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 ,给定要读取的文件的名称
 - 使用方法;
- 创建FileReader对象,构造方法中绑定要读取的数据源
 - 使用FileReader对象中的方法read读取文件
 - 释放资源
 
 
- 构造方法:
 flush和close
- flush :刷新缓冲区,流对象可以继续使用。
 - close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
 
注意
- 字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流
IO异常处理
 jdk1.7之前使用try…catch finally处理流中的异常
- 格式;
1
2
3
4
5
6
7
8try{
可能产生出异常的代码
}catch(异常类型 变量名){
异常的处理逻辑
}finally{
一定会执行的代码
(资源释放)
} 
- 格式;
 JDK7新特性:在try的后边可以增加一个(),在括号中可以定义流对象,那么这个流对象的作用域就在try中有效。try中的代码执行完毕,会自动把流对象释放,不用写finally
- 格式:
1
2
3
4
5try(定义流对象;定义流对象;...){
可能产生出异常的代码
}catch(异常类型 变量名){
异常的处理逻辑
} 
- 格式:
 JDK9新特性;try的前面可以定义流对象,try后边的()中可以直接引入流对象的每次(变量名),在try代码执行完毕只会,流对象可以自动释放,不用写finally
- 格式:
1
2
3
4
5
6
7A 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);按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。注意;
- 存储键值对的文件中,键与值默认的链接符号可以使用=,空格(其他符合)
 - 存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
 - 存储键值对的文件中,键与默认值都是字符串,不用加引号
 
缓冲流
- 字节缓冲流: 
BufferedInputStream,BufferedOutputStream - 字符缓冲流: 
BufferedReader,BufferedWriter - 缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
 
字节缓冲流
BufferedOutputStream;字节缓冲输出流java.io.BufferedOutputStream.extends.OutputStream- 构造方法;
public BufferedOutputStream(OutputStream out); 创建一个新的缓冲输出流,以将数据写入指定的底层输出流public BufferedOutputStream(OutputStream out, int size);创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
 - 使用方法;
- 创建
FileOutputStream对象,构造方法中绑定要输出的目的地 - 创建
BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率 - 使用
BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中 - 使用
BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中 - 释放资源(会先调用flush方法刷新的数据,第4步可以省略)
 
 - 创建
 
BufferedInputStream;字节缓冲输入流java.io.BufferedInputStream.extends.InputStream- 构造方法;
public BufferedInputStream(InputStream in); 创建一个新的缓冲输出流,以将数据写入指定的底层输出流public BufferedInputStream(InputStream in, int size);创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流
 - 使用方法;
- 创建
FileInputStream对象,构造方法中绑定要读取的数据源 - 创建
BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象效率 - 使用
BufferedInputStream对象中的方法read,读取文件 - 释放资源(会先调用flush方法刷新的数据,第4步可以省略)
 
 - 创建
 
字符缓冲流
BufferedWriter;字符缓冲输出流java.io.BufferedWriter.extends.Writer- 构造方法;
public BufferedWriter(Writer out); 创建一个使用默认大小输出缓冲区的缓冲字符输出流public BufferedWriter(Writer out, int size);创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
 - 特有的成员方法;
void newLine();写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
 - 使用方法;
- 创建
FileWriter对象,构造方法中绑定要输出的目的地 - 调用字符缓冲输出流的方法write,吧数据写入到内存缓冲区中
 - 调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
 - 释放资源
 
 - 创建
 
BufferedReader;字符缓冲输入流java.io.BufferedReader.extends.Reader- 构造方法;
public BufferedReader(Reader in); 创建一个使用默认大小输入缓冲区的缓冲字符输入流public BufferedReader(Reader in, int size);创建一个使用指定大小输入缓冲区字符输入流
 - 特有的成员方法;
String readLine();读取一个文本行,读取一行数据- 行的终止符号;通过下列字符之一即可认为某行已终止(换行:
\n,回车;\r,或回车后直接跟着换行\r\n) - 返回值;包含该行内容的字符串,不包含任何终止符,如果已到达流末尾,则返回null
 
- 行的终止符号;通过下列字符之一即可认为某行已终止(换行:
 
 - 使用方法;
- 创建字符缓冲输入流对象,构造方法中传递字符输入流
 - 使用字符缓冲输入流对象中的方法read/readLine读取文本
 - 释放资源
 
 
转换流
字符编码
- 字符编码 Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。
字符集
 - 字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
InputStreamReader类
 - 字节–(解码)–>字符
 java.io.InputStreamReader extends Reader;从字节流到字符流的桥梁,它读取字节,并使用指定的字符集将其解码为字符- 构造方法;
 - 字符–(编码)–>字节
 java.io.OutputStreamWriter extends Writer;从字符流到字节流的桥梁,使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。- 构造方法;
OutputStreamWriter(OutputStream out): 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream out, String charsetName): 创建一个指定字符集的字符流。
 
序列化流
- 序列化;把对象以流的方式,写入到文件中保存,也叫写对象
 - 反序列化;把文件中保存的对象,以流的方式读取出来,也叫读对象
ObjectOutputStream类
 - 序列化。Java对象的原始数据类型写出到文件,实现对象的持久存储。
 - 构造方法;
public ObjectOutputStream(OutputStream out): 创建一个指定OutputStream的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 异常。发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
 - 该类包含未知数据类型
 - 该类没有可访问的无参数构造方法
 
 - Serializable 接口给需要序列化的类,提供了一个序列版本号。 
static final long serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配 
- 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个 InvalidClassException 异常。发生这个异常的原因如下: