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.Throwable
Error
;工程师无法处理,只能尽力避免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 异常。发生这个异常的原因如下: