Java核心知识点总结
【入门程序、常量、变量】
1. Java语言用途
1 | 开发网站的后台业务逻辑,例如:传统行业和电商 |
2. 十进制和二进制的转换规则
1 | 十进制转二进制:辗转相除法,一直到商为0为止,取余数,将余数倒着连接就是二进制 |
3.计算机存储单元
1 | 最小的存储单元是字节,一个字节 = 8 bit,100Mbps ≈ 12MB/s |
4. Java的跨平台性?
1 | 因为Java运行在虚拟机上,不同的系统对应不同版本的虚拟机(JVM) |
5. JDK、JRE和JVM
1 | JDK:是开发工具包,能进行开发,运行环境 |
6. 环境变量配置的意义
1 | 配置环境变量的意义是让操作系统知道去哪里找java命令,使计算机能够在任意目录下运行java和javac这两个命令 |
7. Java程序编写的三个步骤
1 | 编写:程序员编写代码 |
8. 常用命令提示符
功能 | 输入 |
---|---|
切换盘符 | 盘符名称: |
进入文件夹 | cd 文件夹1\文件夹2\文件夹3 |
返回上一级 | cd .. |
直接回根路径 | cd \ |
查看当前文件夹内容 | dir |
清屏 | cls |
退出 | exit |
9. 注释及其格式
1 | 注释是对代码进行解释说明的文字,提高代码的可读性 |
java中的注释分三种:
- 单行注释: // 文字
- 多行注释: /* 文字 */
- 文档注释: /** 文字 */
10. 关键字
1 | 关键字:被java语言赋予了特殊的含义的单词,Java一共定义了50个关键字。 |
目前已学的关键字可分为4类:(更新至Day05课程)
定义数据类型:class byte short int long float double char boolean void
定义数据值类型:true false null
定义流程控制:if else switch case default while do for break continue return
其他类型:public static new package import
11. 标识符及其定义规范
1 | 标识符:自己定义的单词 |
硬性要求:
- 标识符包含英文26字母(大小写)、数字0-9、$、_
- 不能以数字开头
- 不能是关键字
软性建议:
- 类名:大驼峰(单词的首字母全部大写)
- 变量名:小驼峰(如果有多个单词,那么第一个单词首字母小写,后面单词首字母大写)
- 方法名:小驼峰
12. 数据类型
1 | java是强类型语言, 对于每一种数据类型都规定了明确的取值范围。 |
四类 | 八种 | 字节数 | 表示范围 |
---|---|---|---|
整型 | byte | 1 | -128~127 |
整型 | short | 2 | 正负3万多 |
整型 | *int * | 4 | 正负21亿多 |
整型 | long | 8 | 正负19位数 |
布尔型 | boolean | 1 | true, false |
字符型 | char | 2 | 一个字符’A’,’a’ |
浮点型 | float | 4 | 正负38位数 |
浮点型 | double | 8 | 正负308位数 |
13. 常量及其分类
1 | 常量:在程序的执行过程中, 其值不可以发生改变的量 |
- 整数常量:所有整数
- 浮点数常量:所有小数
- 字符常量:被单引号括起来的内容, 里面只能装单个字
- 字符串常量:被双引号括起来的内容
- 布尔常量:true、false
- 空常量:null
14. 变量及其定义格式
1 | 变量:在程序的执行过程中, 其值在某个范围内可以发生改变的量。变量就像一个容器,可以不断修改其中的内容。 |
三种定义格式:
- 一步到位 :
int a = 10;
- 分开定义,赋值:
int a;
a = 10;
- 简便格式:
int a = 10, b = 20, c = 30;
变量需注意的细节:
- 变量不赋值,不能使用
- 变量名不能重复定义
- 变量赋值的范围,不能超过数据类型的最大表示范围
- 变量有作用域,作用域是一对大括号
- long类型数据需加上标识L,float类型数据需加上标识F
【数据类型转换、运算符、方法入门】
1. 数据类型转换
1 | 自动类型转换:是程序自动完成的,将小的数据类型转换成大的数据类型 |
2. 精度损失和数据溢出
1 | 精度损失:当一个浮点数转为整数的时候,会发生精度损失,精度损失是直接舍弃小数部分 |
3. 运算时数据类型的转换规则
1 | 范围小的类型向范围大的类型提升,byte、short、char 运算时直接提升为int |
4. 编译器的两点优化
1 | 优化一:对于byte/short/char三种类型来说,如果右侧赋值的数值没有超过范围,那么javac编译器将会自动隐含地为 |
1 | 优化二:在给变量进行赋值的时候,如果右侧的表达式当中全都是常量,没有任何变量,那么编译器javac将会直接将若干个常量表达式计算得到结果。但是注意:一旦表达式当中有变量参与,那么就不能进行这种优化了。 |
5. 加号的三种用法
1 | 1.数值运算 |
6. ASCII码表
美国标准信息交换码(American Standard Code for Information Interchange )
1 | 人类定义的一个字符和计算机中二进制存储的对照关系表,是所有编码表的核心。 |
7. 运算符
类型 | 符号 |
---|---|
算数运算符 | + - * / % ++ -- |
赋值运算符 | = += -= *= /= %= |
比较运算符 | > < == >= <= != |
逻辑运算符 | & ` |
三元运算符 | 数据类型 变量名 = 条件判断?表达式A:表达式B |
运算符注意事项:
- 除法,不会出现小数,会将小数部分舍弃
+
符号在遇到字符串的时候,表示连接、拼接的含义- 比较运算符返回的一定是布尔值true、false
- 逻辑运算符符号两边一定是布尔值
- 短路效果:如果已经得到结果,那么不会进行后面的操作(双写
&
|
得到短路效果)- 三元运算符的结果必须被使用,必须同时保证表达式A和表达式B都符合左侧数据类型的要求。
8. ++、–的使用场景
1 | 单独操作: 就是自身完成+1或者是-1的动作; |
9. a += 1和a = a + 1的区别
1 | a += 1 等价于 a = (a的数据类型)(a+1),当中存在隐含了一个强制类型转换的过程 |
10. 定义方法的好处
1 | 1. 将代码按照功能进行划分,提高代码的可读性 |
11. 方法定义和调用的注意事项
1 | 1. 不能嵌套定义,方法中不能定义方法 |
【流程控制语句】
1. if语句的三种格式
1 | 1.if--一种情况的判断 |
2. 程序的健壮性
1 | 健壮性:我们需要尽可能多的考虑程序出现的情况,并给出解决方案 |
3. switch语句的格式
1 | public static void main(String[] args) { |
switch语句的注意事项:
- switch可以接受的4+2种数据类型:4种基本:byte/short/char/int ; 2种引用:String/enum
- case后面的表达式不能重复
- case语句的穿透效果:没有break会往下穿透
- switch语句很灵活,前后顺序可颠倒
4. 三种循环语句的格式
(1)for循环
1 | 格式: for(初始化语句;条件判断语句;步进语句){ |
(2)while循环
1 | 格式: 初始化语句; |
(3)do..while循环
1 | 格式: 初始化语句; |
5. 三种循环语句的区别
1 | 1. for循环的初始化表达式出了循环不能使用,while循环可以使用 |
6. break和continue
1 | break:在switch语句中,表示遇到break,switch语句结束;在循环语句中,表示跳出循环,继续往下执行代码 |
7. 死循环的格式
1 | while(true); |
【Idea、方法】
1. 什么是方法
1 | 方法就是经常使用的一部分代码抽取成的代码块 |
2. 方法的定义格式
1 | 修饰符 返回值类型 方法名(参数列表){ |
3. return的作用
1 | 1. 将返回值返回给方法的调用处 |
4.方法的调用的方式
1 | 1. 单独调用:适用于有返回值的和无返回值的方法,有返回值的方法单独调用没有意义 |
5. 方法的执行流程
1 | 1. 以main方法为起点,虚拟机调用main方法,main方法中调用其他方法 |
6. 方法的三要素
1 | 1. 返回值类型:看需求,说是想让你给出结果,还是直接在方法中打印 |
7. 方法的重载
1 | 方法的重载:是指方法名称相同,但参数列表不同的一组方法。用户希望相似的功能,只需要记一个方法名称。 |
方法重载的三个相关:
- 参数列表的个数
- 参数列表的数据类型
- 参数列表的参数顺序
方法重载的三个无关:
- 和参数的变量名称无关
- 和方法的返回值类型无关
- 和方法的修饰符无关
8. IDEA 常用快捷键
功能 | 快捷键 |
---|---|
调出结果窗口 | Alt + 4 |
自动导包+修正代码 | Alt + Enter |
删除光标所在行 | Ctrl + Y |
往下复制一行 | Ctrl + D |
格式化代码 | Ctrl + Alt + L |
自动生成常用方法 | Alt + Insert |
移动当前代码行 | Alt + Shift + 上下箭头 |
选中一个变量的所有使用处 | Shift + F6 |
快速生成for循环 | XXX.fori |
【数组】
1. 什么是数组
1 | 数组是Java中的一种容器,用于存储数据 |
- 数组中存储的数据类型一致
- 数组是引用数据类型
- 数组的长度一旦确定,不可改变
2. 数组的初始化
1 | 动态初始化:int[] array = new int[3]; |
3. 索引
1 | 索引就是数组元素的编号,从0开始,到length-1为止 |
赋值:数组名[索引值] = 值;
访问:数据类型 变量名 = 数组名[索引值];
4. Java内存的5个组成部分
1 | 1.栈(Stack):方法运行时使用的内存,比如main方法运行,进入方法栈中执行,存放方法中的局部变量。 |
5. 数组越界索引异常
1 | 系统访问了数组中不存在的索引,将抛出数组越界索引异常。 |
1 | public static void main(String[] args) { |
6. 数组空指针异常
1 | 给数组赋值null之后,数组将不会保存数组的内存地址,也不允许再操作数组了,运行时会抛出空指针异常。 |
1 | public static void main(String[] args) { |
7. 方法的参数类型区别
1 | 方法的参数为基本类型时,传递的是数据值,值不会受到影响;方法的参数为引用类型时,传递的是地址值,所以在方法中 |
8. 数组的直接打印
1 | 直接打印数组名称,得到的是数组对应的内存地址哈希值 |
9. 数组的内存执行流程
- 编写代码,编译代码,生成.class字节码文件
- 字节码文件,将信息加载到方法区中,方法区中有类的方法信息
- JVM虚拟机去找程序的入口——main方法
- main方法进栈执行
- main方法中定义的变量,会在栈内存中生成
- 在堆内存中,开辟了一块空间,将空间中的数值赋默认值,JVM将数组的内存地址赋值给引用类型变量
- 打印数组名称,找到堆内存中的地址值.
- 先通过数组名找到堆内存中的地址值,然后通过索引值找到对应的数据值,在数据值进行修改
- main方法会出栈/弹栈
- 堆内存中的两块区域,没有变量去引用了,堆内存中的内容会被JVM垃圾回收机制回收
10. 数组的反转
1 | public static void main(String[] args) { |
【类与对象、封装、构造方法】
1. 面向对象
1 | 就是当我们需要做一件事情的时候,不是去自己亲力亲为的考虑每一个细节,而是找到能做这件事情的人,帮我们做事 |
2. 类与对象
1 | 类:是一个模板,描述了现实事物的信息 |
类是对象的模板,对象是类的实体
3. 类描述的信息
1 | 属性:事物具备什么特征,在代码中叫做成员变量 |
4. 对象的使用方式
1 | 导包:通常不用考虑,IDEA会进行自动导包 |
5. 成员变量和局部变量区别
成员变量 | 局部变量 | |
---|---|---|
类中位置 | 类中,方法外 | 方法内 |
作用范围 | 类中 | 方法中 |
初始化值 | 有默认值 | 无默认值 |
内存中位置 | 堆内存 | 栈内存 |
生命周期 | 随着对象存在 | 随着方法存在 |
6. 封装
1 | 就是将细节隐藏起来,对外只暴露实现方式 |
7. private关键字
1 | 将成员变量私有,只允许本类访问,不允许外界访问,对外提供Getter/Setter方法 |
8. this关键字
1 | this代表所在类的当前对象的引用(地址值),即对象自己的引用。 |
9. 成员变量的默认值
数据类型 | 默认值 | |
---|---|---|
基本类型 | 整数 | 0 |
浮点数 | 0.0 | |
字符 | ‘/u0000’ | |
布尔 | false | |
引用类型 | 数组,类,接口 | null |
10. 构造方法注意事项
- 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样
- 构造方法不要写返回值类型,连void都不写
- 构造方法不能return一个具体的返回值
- 如果你不提供构造方法,系统会给出无参数构造方法。
- 如果你提供了构造方法,系统将不再提供无参数构造方法。
- 构造方法是可以重载的,既可以定义参数,也可以不定义参数。
11. 标准代码——JavaBean
1 | public class Student { |
12. 对象的内存图流程
- 方法区加载类的方法信息
- JVM去找main方法,main方法进栈执行
- 在栈内存中生成一个变量Phone one,在堆内存中开辟一块空间,内含成员变量和成员方法
- 对成员变量进行赋值
- 调用成员方法,方法进栈执行,执行完毕之后,方法出栈
- 最后main方法出栈
- 垃圾回收机制回收堆内存中的两块区域
【Scanner类、Random类、ArrayList类】
1. API的使用方式
(Application Programming Interface) 应用程序编程接口
1 | (1)看包路径 -->导包 |
2. Scanner类使用步骤
1 | Scanner sc = new Scanner(System.in); |
3. Random类使用步骤
1 | Random ran = new Random(); |
4. 匿名对象
1 | 匿名对象就是没有变量名接受的对象,new 类名(); |
5. 数组与集合的不同
数组 | 集合 | |
---|---|---|
运行期间长度 | 不可变 | 可变 |
存储数据类型 | 基本+引用数据类型 | 引用数据类型 |
直接打印 | 地址值 | 集合的内容 |
可操作方法 | 查、改 | 增、删、改、查 |
获取长度 | 数组名.length | 集合名称.size() |
6. ArrayList类
1 | ArrayList集合是Java中的一种容器,底层是数组,默认初始长度是10 |
特点:
- 长度是可变的
- 只能存储引用数据类型,如果想存储基本数据类型,要使用其包装类
- 直接打印显示的是集合的内容
7. ArrayList中的常用方法
1 | public boolean add(E e):向集合当中添加元素,参数的类型和泛型一致,返回值是boolean(Always true) |
8. 包装类
基本类型 | 基本类型包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
自动装箱:基本类型 –>包装类型
自动拆箱:包装类型 –>基本类型
【String类、static关键字、Arrays类、Math类】
1. String类的三个特点
1 | 1. 字符串不变:字符串的值在创建后不能被更改。 |
2. 创建字符串的1+3种方法
1 | 一种直接创建:直接写上双引号,就是字符串对象 |
字符串常量池:直接写“ ”的字符串,在常量池中,如果常量池中有相同的字符串,那么不会创建新的,而是使用以前的。双引号直接写的字符串在常量池当中,new出来的不在池当中。
1 | public static void main(String[] args) { |
3.String常用方法
判断
1 | public boolean equals(Object anObject):将此字符串与指定对象进行比较。 |
获取
1 | public int length () :返回此字符串的长度。 |
转换
1 | public char[] toCharArray () :将此字符串转换为新的字符数组。 |
分割
1 | public String[] split(String regex) :将此字符串按照给定的regex(规则)拆分为字符串数组。 |
String可以比,接,截,割,替,从内容获取索引,从索引获取内容
equal, concat, substring, split, replace, indexof, charAt
4. 正则表达式
正则表达式也是一个字符串,是专门解决字符串规则匹配的工具,用来定义匹配规则
表达式 | 含义 |
---|---|
x | 字符 x |
\\ | 反斜线字符 |
[abc] | a、b 或 c(简单类) |
[^abc] | 任何字符,除了 a、b 或 c(否定) |
[a-zA-Z] | a 到 z 或 A 到 Z,两头的字母包括在内(范围) |
\w | 单词字符:[a-zA-Z_0-9] |
X? | X,一次或一次也没有 |
X* | X,零次或多次 |
X+ | X,一次或多次 |
X{n} | X,恰好 n 次 |
X{n,} | X,至少 n 次 |
X{n,m} | X,至少 n 次,但是不超过 m 次 |
5. static关键字
1 | static关键字可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。 |
静态存储在方法区中,既不在堆中,也不在栈中(方法中有一块独立的静态区)
类变量
1 | 当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。 |
静态方法
1 | 当static修饰成员方法时,该方法称为类方法,习惯称为静态方法。建议使用类名来调用,而不需要创建类的对象。 |
静态方法调用的注意事项:
- 静态方法可以直接访问类变量和静态方法。
- 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
- 静态方法中,不能使用this关键字。
静态代码块
1 | 类中方法外,使用static修饰的代码块{ }。 |
当第一次用到本类时,静态代码块执行唯一的一次,优先于main方法和构造方法的执行。
作用:给类变量进行初始化赋值。
6. Arrays类
1 | public static String toString(int[] a) :返回指定数组内容的字符串表示形式。 |
1 | public static void main(String[] args) { |
备注:如果是数值,sort方法默认按照升序从小到大
如果是字符串,sort方法默认按照字母升序
如果是自定义的类型,那么这个自定义的类需要有Comparable或者Comparator接口的支持
1 | // 冒泡排序法代码 |
7. Math类
1 | public static double abs(double a):获取绝对值 |
【继承、super、this、抽象类】
1. 继承
1 | 就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。 |
语法格式:子类 extends 父类
继承的作用:提高代码复用性,类与类之间产生了关系,是多态的前提。
继承的特点:
- 一个子类只能有一个直接父类,不能有多个直接父类
- 继承可以支持多级继承,一个子类只能有一个直接父类,但可以有多个间接父类
- 一个父类可以有多个子类
2. 抽象类
1 | 对子类共性的内容进行抽取,有可能包含抽象方法的类 |
语法格式:abstract关键字
抽象类的作用:
- 为子类提供便利:抽象类中可以定义一些方法,子类继承之后可以直接使用
- 对子类进行约束:抽象类中的抽象方法,子类继承之后,必须重写,否则子类也是一个抽象类
注意事项:
- 抽象类不能创建对象,如果要创建,需要创建的是子类(抽象类的对象调用方法无方法体,无意义)
- 抽象类中可以包含构造方法,用于初始化父类成员
- 抽象类中可以没有抽象方法,但是只要类中有抽象方法,那这个类一定是一个抽象类
- 子类继承抽象类必须重写全部抽象方法,否则该子类也是一个抽象类
3. 继承后的特点
成员变量重名
1 | 使用super关键字区分 |
成员方法重名——重写(Override)
1 | 子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。 |
方法重写的注意事项:
- 必须保证父子类之间方法的名称相同,参数列表也相同。
- @Override:写在方法前面,用来检测是不是有效的正确覆盖重写,也可以不写。
- 子类方法的返回值必须【小于等于】父类方法返回值的范围。
- 子类方法的权限必须【大于等于】父类方法的权限修饰符。
备注:public > protected >(default) > private,(default)不是关键字,是什么都不写,留空。
构造方法
构造方法注意事项:
- 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
- 子类构造可以通过super关键字来调用父类重载构造。
- super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
4. super关键字的三种用法
1 | (1)在子类的成员方法中,访问父类的成员变量 |
5. this关键字的三种用法
1 | (1)在本类的成员方法中,访问本类的成员变量 |
注意:super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
【接口、多态】
1. 接口的定义
1 | 接口,是Java语言中一种引用类型,是方法的集合,使用interface关键字,是一种公共的规范标准。 |
作用:(1)提供功能的拓展(2)提出约束
定义格式:public interface 接口名称{接口内容}
注意:接口也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
2. 接口包含的内容
常量(Java 7)
1 | 接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。 |
注意事项:
- 三个修饰符可以省略,但是不写也默认有
- 接口当中的常量,必须进行赋值;不能不赋值。
- 接口中常量的名称,使用完全大写的字母,用下划线进行分割:
public static final int NUM_OF_PEOPLE = 10
;
抽象方法(Java 7)
1 | public abstract 返回值 方法名称(参数列表); |
public abstract 可以省略不写
默认方法(Java 8)
1 | public default 返回值类型 方法名称(参数列表){方法体} |
接口中的默认方法用于接口的升级和修改,可以保证所有的实现类不必被强制要求重写抽象方法。
如果重写使用重写的方法,如果不重写使用默认方法。
静态方法(Java 8)
1 | public static 返回值类型 方法名称(参数列表){方法体} |
注意:不能通过接口实现类的对象来调用接口当中的静态方法!因为一个实现类可能实现多个接口,通过对象调用接口的静态方法有可能方法名重复导致冲突。正确用法:通过接口名称,直接调用其中的静态方法。
私有方法(Java 9)
1 | (1)普通私有方法:private 返回值 方法名称(参数列表){} |
解决多个默认方法和静态方法代码重复度过高问题,同时避免该方法被接口的实现类使用。
从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。
3. 接口的多实现
1 | 一个类可以实现多个接口 |
注意事项:
- 有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。
- 有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。
- 存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
- 当一个类既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近
选择执行父类的成员方法。
4. 接口的特点
1 | 1. 一个接口能继承另一个或者多个接口,使用 extends 关键字,子接口继承父接口的方法。 |
5. 多态
1 | 父类引用指向子类对象,一个对象的多种形态 |
多态使用的前提条件:必须以继承或者实现为前提条件(用父类或者接口去接收对象都行)
多态的表现形式:父类类型 变量名 = new 子类(); 接口类型 变量名 = new 实现类();
6. 多态成员的访问
1 | 多态成员变量的访问方式:编译看左边,运行也看左边 |
7. 多态的好处和弊端
1 | 好处:提高代码复用性 |
8. 引用、对象、对象名称的区分
1 | Animal a = new Cat(); |
9. 向上转型和向下转型
1 | 向上转型:向上转型一定是安全的,因为左父右子,从小范围转向了大范围 |
对象的向下转型,其实是一个【还原】的动作。由哪个子类转成的父类类型,再转回去要注意,不能转为其
他子类类型,否则会报错。(ClassCastException)
10. instanceof 关键字
1 | 格式:变量名 instanceof 数据类型 |
instanceof 可用于判断对象属于哪一个实例,一般都在方法中使用。
对传入的父类类型的对象进行实例判断,强转回子类,目的是调用子类特有的方法
1 | public class Test { |
【final、权限、内部类、引用类型】
1. final 关键字
1 | 被final关键字修饰的类、方法和变量不可改变。有以下四种主要用法: |
修饰类
1 | final class 类名 { |
修饰方法
1 | 修饰符 final 返回值类型 方法名(参数列表){ |
修饰局部变量
1 | 因为局部变量无初始化默认值,可以先定义后赋值。{final int a; a = 10;} |
修饰成员变量
1 | 1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。{final int a = 10;} |
2. 权限修饰符
public | protected | (default) | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包的无关类 | √ |
3. 成员内部类
1 | 定义在类中方法外的类。 |
访问特点:
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须要建立内部类的对象。
- 创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
注意:
- 内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。比如,Outer$Heart.class
- 如果内部类和外部类的变量出现了重名现象,那么在内部类调用外部类变量的格式是:
外部类名称.this.对象名
1 | public class Outer { |
4. 类的权限修饰符
1 | 定义一个类的时候,权限修饰符规则: |
5. 局部内部类的final问题
1 | //局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。 |
原因:
- new出来的对象在堆内存当中。
- 局部变量是跟着方法走的,在栈内存当中。
- 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
- 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
6. 匿名内部类
1 | 是内部类的简化写法。它的本质是一个带具体实现的【父类或者父接口的】匿名的子类对象。 |
1 | //格式 |
匿名内部类特点:
匿名内部类的好处:不用编写实现类,就能创建实现类对象。
匿名内部类的弊端:创建的这个实现类对象,模板只能使用一次。
匿名内部类和匿名对象不是一回事,但是可以【匿名内部类】+【匿名对象】组合使用
【Object类、常用API】
1. Object类
1 | java.lang.Object类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。如果一个类没有特别指定父类, 那么默认则继承自Object类。 |
2. toString方法
1 | toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。 |
3. equals方法
1 | 调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。 |
4. Objects类
1 | 在JDK7添加了一个Objects工具类,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。 |
public static boolean equals(Object a, Object b)
:判断两个对象是否相等
1 | //源码 |
5. Date类
1 | 表示特定的瞬间,精确到毫秒。 |
注意:
- 时间原点:1970年1月1日 00:00:00(英国格林威治时间)
- 中国属于东八区,会把时间增加8个小时
两个构造方法:
- Date():返回当前时间的日期对象
- Date(long date):返回一个从1970年1月1日 0点0分0秒 + 毫秒值 所对应的日期对象
一个成员方法:
- getTime():将一个日期对象,转为对象的毫秒值表示
6. DateFormat类
1 | DateFormat用于将日期对象格式化成指定的字符串表示,或者将一个字符串解析成Date对象。 |
构造方法:public SimpleDateFormat(String pattern)
格式化:按照指定的格式,从Date对象转换为String对象:
public String format(Date date)
解析:按照指定的格式,从String对象转换为Date对象:
public Date parse(String source)
格式规则
字母 | 日期或时间元素 |
---|---|
y | 年 |
M | 年中的月份 |
D | 年中的天数 |
d | 月份中的天数 |
E | 星期中的天数 |
H | 一天中的小时数(0-23) |
m | 小时中的分钟数 |
s | 分钟中的秒数 |
S | 毫秒数 |
7. Calendar类
1 | Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。 |
常用方法:
public static Calendar getInstance()
: 通过静态方法创建对象public int get(int field)
:返回给定日历字段的值public abstract void add(int field, int amount)
:根据日历的规则,为给定的日历字段添加或减去指定的时间量。public Date getTime()
:返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。public void set(int field, int value)
:将给定的日历字段设置为给定值。 (也可以同时设置年月日:set(int year, int month, int day)
)
Calendar类中提供很多成员常量,代表给定的日历字段:
字段值 | 含义 |
---|---|
YEAR | 年 |
MONTH | 月(月份特殊,西方是0-11表示12个月,可以+1使用) |
DAY_OF_MONTH | 月中的天(几号) |
HOUR | 时(12小时制) |
HOUR_OF_DAY | 时(24小时制) |
MINUTE | 分 |
SECOND | 秒 |
DAY_OF_WEEK | 周中的天(西方是从星期日开始每周的第一天,可以-1使用) |
8. 日期、字符串、毫秒值和日历的相互转换
1 | SimpleDateFormat.parse(s) Date.getTime(d) |
Date类是另外三种时间格式连接的桥梁,相互之间转换时都需要通过Date类。
9. System类
1 | public static long currentTimeMillis() :返回以毫秒为单位的当前时间,经常用来测试程序性能。 |
参数名称 | 参数类型 | 参数含义 |
---|---|---|
src | Object | 源数组 |
srcPos | int | 源数组索引起始位置 |
dest | Object | 目标数组 |
destPos | int | 目标数组索引起始位置 |
length | int | 复制元素个数 |
10. StringBuilder类
1 | StringBuilder又称为可变字符序列,它是一个类似于String的字符串缓冲区,支持可变的字符串,可以提高字符串的操作效率。底层也是一个数组,但是没有被final修饰,可以改变长度在数组中加入新内容。 |
构造方法:
public StringBuilder()
:构造一个空的StringBuilder容器。public StringBuilder(String str)
:构造一个StringBuilder容器,并将字符串添加进去。常用方法:
- public StringBuilder append(…) :添加任意类型数据的字符串形式,并返回当前对象自身。
- public String toString() :将当前StringBuilder对象转换为String对象。
11. 基本类型与字符串之间的转换
基本类型转换为String
- 基本类型的值 + “” (推荐方法)
static String toString(基本数据类型):String s = Integer.toString(int i)
static String valueOf(基本数据类型):String s = String.valueOf(int i)
String转换为基本类型
除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型。
例如:Integer类:
static int parseInt(String s)
【Collection、泛型】
1. Collection
1 | 单列集合类的根接口,有两个重要的子接口,分别是java.util.List和java.util.Set |
List
特点:元素有序、元素可重复
主要实现类:
java.util.ArrayList
和java.util.LinkedList
Set
特点:元素无序,而且不可重复
主要实现类:
java.util.HashSet
和java.util.LinkedHashSet
2. 集合和数组的区别
长度不同
- 集合:集合的长度是可变的,因为集合的底层就是数组,当增删元素的时候,会进行数组的扩容
- 数组:数组的长度在运行期间不可变,一旦创建,就固定
存储的数据类型不同
- 集合:只能存储引用数据类型,如果想存储基本数据类型,需要存储对应的包装类
- 数组:基本数据类型和引用数据类型都可以存储
3. Collection 常用功能
1 | Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合: |
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()
: 把集合中的元素,存储到数组中。
4. Iterator接口
1 | 【迭代器】Iterator是一种通用的Collection集合元素的获取方式,通过判断集合中是否有元素,有则取出,继续判断,直到把集合中的元素全部取出为止。 |
获取迭代器的方法:
collection.iterator()
:由于接口不能直接实例化,使用集合的Iterator方法获取常用的两个方法:
public E next()
:返回迭代的下一个元素。public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。并发修改异常:迭代器的底层实现原理,运用到指针。在使用迭代器的过程中,修改了集合的长度,就会抛出该异常。使用Iterator接口的一个子接口ListIterator接口可以解决这个问题。
1 | //代码展示: |
5. ListIterator接口
1 | Iterator接口有一个子接口ListIterator接口,其中定义了add方法和remove方法,可以对集合添加\删除元素。由迭代器自己添加\删除的不会抛出异常。 |
使用步骤:
- 创建的集合首先不能使用Collection,因为Collection获取不了ListIterator接口,需要使用List接口获取ListIterator接口实现类
- 调用listIterator()方法,获取listIterator接口实现类
- 使用hasNext和next方法进行迭代
- 使用接口实现类的add方法进行添加,remove方法进行删除元素。注意:不要使用集合进行添加删除
1 | //代码展示 |
6. 增强for
1 | 增强for循环是基于迭代器设计的一种高级for循环,专门用于遍历数组和集合。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。格式: |
7. 泛型
1 | 在定义类或方法时,对于未知类型的数据进行占位,用于后期接收数据类型,以便预支使用的一种未知的数据类型。 |
泛型的好处:
- 避免了类型转换的麻烦,使用API时更加直观简洁。
- 把运行期异常,提升到了编译期
含有泛型的类
定义格式:
修饰符 class 类名<代表泛型的变量> {}
确定泛型:在创建对象的时候
含有泛型的方法
定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){}
确定泛型:调用方法时
含有泛型的接口
定义格式:
修饰符 interface 接口名<代表泛型的变量> {}
确定泛型:1、定义类时确定泛型的类型; 2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
8. 泛型通配符
1 | 不知道使用什么类型来接收的时候,此时泛型可以使用“?”表示,“?”表示未知通配符。 |
受限泛型:泛型没有继承概念,JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
- 格式:
类型名称 <? super 类 > 对象名称
- 意义:
只能接收该类型及其父类型
【List、Set、数据结构、Collections】
1. 数据结构
栈
1 | stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作 |
特点:先进后出
- 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
- 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
队列
1 | 也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。 |
特点:先进先出
数组
1 | 数组是有序的元素序列,在内存中开辟一段连续的空间,并在此空间存放元素 |
特点:有索引值,查询快,增删慢
链表
1 | 链表中的每一个元素也称之为一个节点,一个节点包含了一个数据源,两个指针域(存储地址):一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 |
特点:查询慢,增删快
- 单项链表:链表中只有一条链,不能保证元素的顺序(存储元素和取出元素的顺序可能不一致)
- 双向链表:链表中有两条链子,有一条链子是专门记录元素的顺序,是一个有序的集合
红黑树
1 | 是一种比较平衡的二叉树 |
特点:速度特别快,趋近平衡树,查询叶子节点最大次数和最小次数不能超过2倍
约束:
- 节点可以是红色的或者黑色的
- 根节点是黑色的
- 叶子节点(特指空节点)是黑色的
- 每个红色节点的子节点都是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
2. List 接口
特点:
- 有序的集合,存储元素和取出元素的顺序是一致的(存储123 取出123)
- 有索引,包含了一些带索引的方法
- 允许存储重复的元素
1 | 常用方法: |
3. LinkedList 集合
1 | LinkedList是一个双向链表,查询慢,增删快,包含了大量操作首尾元素的方法。 |
1 | - public void addFirst(E e):将指定元素插入此列表的开头。 |
4. HashSet 集合
1 | 是Set接口的一个实现类,存储的元素不可重复,并且元素都是无序的(即存取顺序不一致),没有索引,不能使用普通的for循环遍历。HashSet根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。 |
HashSet集合存储数据的结构(哈希表):
- jdk1.8版本之前:哈希表 = 数组 + 链表
- jdk1.8版本之后:哈希表 = 数组 +链表/红黑树;
(当链表长度超过阈值(8)时,将链表转换为红黑树提高查询的速度)
5. LinkedHashSet 集合
1 | HashSet是无序的,LinkedHashSet是有序的 |
特点:底层是一个哈希表(数组+链表/红黑树)+ 链表:多了一条链表(记录元素的存储顺序),保证元素有序
6. 重写HashCode()方法
为什么需要重写HashCode()方法?
如果两个对象需要判断是否内容相同,可以调用equals方法进行比较,但如果一个对象的字段过多,那就会偏频繁的进行字段的比较,非常的耗费性能。我们可以对Object类继承过来的hashCode方法进行覆盖重写,不让他生成地址值,而是根据我们对象的内容,生成hash值进行比较。因为比较hash值比equals方法容易得多。如果hash值相同,再调用equals方法进行内容比较。
哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
重写HashCode()进行比较的步骤:
- 重写Object类中继承过来的HashCode()方法,自定义,根据对象的内容生成的哈希值
- 我们在进行集合元素存储的时候,比如向HashSet集合添加元素的时候,会先调用HashCode()方法,生成哈希值,不同对象生成的哈希值可能相同(虽然概率比较低)
- 哈希值不同,对象的内容肯定不同;哈希值相同,对象的内容有可能相同,有可能不同
- 比较哈希值,如果不同,直接存;如果相同,再调用equals方法进行内容比较
7. 可变参数
1 | 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数 |
原理:可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数,传递的参数个数,可以是0个(不传递),1,2,…多个
注意事项:
- 一个方法的参数列表,只能有一个可变参数
- 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
- 可变参数的终极写法:Object…obj
8. Collections 工具类
1 | public static <T> boolean addAll(Collection<? super T> c, T... elements): |
9. Comparable/Comparator 接口
Comparable和Comparator的区别:
- Comparable:自己(this)和别人(参数)比较,在源代码类中需要实现Comparable接口,重写比较的规则compareTo方法,耦合度太高
- Comparator:在需要做排序的时候去选择的Comparator,相当于找一个第三方的裁判
- 排序规则:this - 参数:升序;参数 - this:降序
1 | //Comparable使用示例,在源码类中实现Comparable接口 |
1 | //Comparator使用示例,在Collections.sort方法中实现Comparator接口的匿名内部类 |
1 | //Comparator如果想实现更多规则,示例如下: |
【Map、Debug】
1. Map接口
1 | Collection 中的集合称为单列集合, Map 中的集合称为双列集合。 |
特点:
- 是一个双列集合,一个元素包含两个值(一个key,一个value)
- Map集合中的元素,key和value的数据类型可以相同,也可以不同
- Map集合中的元素,key是不允许重复的,value是可以重复的
- Map集合中的元素,key和value是一一对应的
2. Map的常用子类
HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需
要重写键的hashCode()方法、equals()方法。LinkedHashMap:存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
HashTable:底层也是一个哈希表,是一个线程安全的集合,单线程集合,速度慢。不能存储空值,空键。Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合取代了。
3. Map的常用方法
1 | public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。 |
4. Entry键值对对象
1 | 一对key(键)+value(值)称做Map中的一个Entry(项),Entry将键值对的对应关系封装成了对象,即键值对对象。 |
1 | //Entry相关方法 |
5. Map的两种遍历方式
1 | public static void main(String[] args) { |
6. JDK9对集合添加的优化
1 | Java 9给List接口,Set接口和Map接口增加了一个静态的方法of,可以给集合一次性添加多个元素。 |
1 | public class HelloJDK9 { |
注意:
- of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如
HashSet,ArrayList等;- of方法的返回值时一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常;
- Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常
7. Debug追踪
1 | 使用IDEA的断点调试功能,可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug。 |
使用方式:
- 在行号的右边,鼠标左键单击,添加断点(哪里有bug添加到哪里)
- 右键选择Debug执行程序
- 程序就会停留在添加的第一个断点处
执行程序:
- F7:逐句执行程序(进入到方法中)
- F8:逐行执行程序(不进入方法中)
- Shift + F8:跳出方法
- F9:跳到下一个断点,如果没有下一个断点,那么就结束程序
- Ctrl + F2:退出Debug模式,停止程序
【异常、线程】
1. 异常
1 | 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处 |
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
2. Throwable体系
- Error:严重错误,无法通过处理的错误,只能事先避免,好比绝症。(内存不够用或和系统相关)
- Exception:异常,产生后程序员可以通过代码的方式纠正,使程序继续运行,好比感冒。
1 | //Throwable常用方法: |
3. 异常的分类
- 编译时期异常:必须要处理。在编译时期就会检查,如果没有处理异常,则编译失败。
- 运行时期异常:可以不处理。在运行时期检查异常,在编译时期,运行异常不会被编译器报错。
4. 异常产生过程解析
1 | // 定义一个对给定的数组通过给定的索引获取元素的方法 |
过程解析:getElement方法由于没有找到4索引,导致运行发生了异常,接下来JVM会:
- Jvm会根据异常产生的原因创建一个异常对象,这个对象包含了异常产生的(内容,原因,位置) new ArrayIndexOutOfBoundsException(“4”);
- 在getElement方法中,没有异常的处理逻辑(try…catch),那么JVM就会把异常对象抛出给方法的调用者main方法来处理这个异常
- main方法接受后也没有处理异常的逻辑,继续把对象抛给main方法的调用者JVM处理
- JVM收到这个异常对象,做了两件事:
- 把异常对象(内容、原因、位置)以红色的字体打印在控制台
- JVM会终止当前正在执行的Java程序—>中断处理
5. throw关键字
1 | throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。例如: |
throw将异常抛出,也就是将问题返回给该方法的调用者。对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续将问题声明出去,使用throws声明处理。
注意:
- throw关键字必须写在方法的内部
- throw关键字后边new的对象必须是Exception或者Exception的子类对象
- throw关键字抛出RuntimeException或者是其子类对象,我们可以不处理,默认交给JVM处理
- throw关键字后边创建的是编译异常,我们就必须处理这个异常(try…catch)
6. Objects非空判断
1 | public static <T> T requireNonNull(T obj) :查看指定引用对象不是null |
7. throws声明异常
1 | 声明异常:如果方法内通过throw抛出编译时异常,而没有捕获处理,那么必须通过throws进行声明,将问题标识出来。 |
1 | //代码演示: |
子父类的异常:子类抛出的异常在数量和继承关系上不能超出父类
8. try…catch捕获异常
1 | 捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。 |
1 | //代码演示 |
9. finally代码块
1 | 因为异常会引发程序跳转,导致有些语句执行不到。但有一些特定的代码必须执行,将这些代码放在finally代码块中是一定会被执行的。 |
1 | //代码演示 |
注意:
- finally不能单独使用。
- 如果finally有return语句,永远返回finally中的结果,避免该情况。
- 当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。
10. 多个异常使用捕获
1 | //一般多个异常一次捕获,多次处理。 |
注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异
常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
11. 自定义异常
1 | //格式: |
注意:
- 自定义异常类一般都是以Exception结尾,说明该类是一个异常类
- 所有的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法
- 自定义异常类,必须继承Exception/RuntimeException
- 继承RuntimeException:那么就是一个运行期异常,无需处理,交给虚拟机
- 继承Exception:那么就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try…catch
12. 并发与并行
1 | 并发:指两个或多个事件在同一个时间段内发生(交替执行)。 |
13. 线程与进程
1 | 进程:一个应用程序可以同时运行多个进程,每个进程都有一个独立的内存空间,是系统运行程序的基本单位。 |
线程调度:
- 分时调度:
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。- 抢占式调度:
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
【线程、同步】
1. Thread类
1 | //构造方法: |
1 | //常用方法: |
2. 创建线程-继承Thread类
1 | 创建多线程的第一种方式:创建Thread类的子类 |
实现步骤:
- 创建一个Thread类的子类,并重写Thread类中的run方法,设置线程任务
- 创建Thread类的子类对象
- 调用线程对象的start()方法,开启新的线程,执行run方法
1 | //代码演示 |
3. 创建线程-Runnable接口
1 | 创建多线程的第二种方式:Thread构造器实现Runnable接口 |
实现步骤:
- 创建一个Runnable接口的实现类
- 在实现类中重写Runnable接口的run方法,设置线程任务
- 创建一个Runnable接口的实现类对象
- 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
- 调用Thread类中的start方法,开启新的线程执行run方法
1 | //代码演示 |
4. Runnable接口具有的优势
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
5. 线程安全
1 | 当多个线程对象访问同一个资源,并且多个线程中对资源有写的操作,就容易产生线程安全问题。 |
6. 同步代码块
1 | synchronized 关键字可以用于方法中的某个区块中,表示对这个区块的资源实行互斥访问。 |
1 | //代码演示 |
同步锁:对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
- 锁对象可以是任意类型。
- 多个线程对象要使用同一把锁。
- 在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着。
7. 同步方法
1 | 使用synchronized修饰的方法,就叫做同步方法,保证一个线程执行该方法的时候,其他线程只能在方法外等着。 |
1 | //代码演示 |
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
8. Lock锁
1 | java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外Lock更强大,更体现面向对象。 |
1 | //代码演示 |
9. 六种线程状态
线程状态 | 说明 |
---|---|
新建状态(New) | 线程刚被创建,但是还没调用start方法启动。 |
运行状态(Runnable) | 线程可以在java虚拟机中运行的状态。 |
阻塞状态(Blockd) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态。 |
计时等待状态(Timed waiting) | 一个线程在等待另一个线程调用notify或者notifyAll方法(唤醒)动作时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒的。 |
无限等待状态(Waiting) | 同waiting状态,这一状态将一直保持到超时期满或者接收到唤醒通知。 |
死亡状态(Terminated) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
10. 等待与唤醒方法
1 | 进入到TimeWaiting(计时等待)有两种方式: |
wait和notify方法是来源于Object类,不是Thread类,等待和唤醒都是Object的方法
【线程池、Lambda表达式】
1. 等待唤醒机制
1 | 一个线程进行了规定操作后,通过wait()方法进入等待状态,等待其他线程执行完他们的指定代码过后再通过notify()方法将其唤醒,是线程间的一种协作机制。 |
注意:
- wait方法与notify方法必须要由同一个锁对象调用。
- wait方法与notify方法必须要在同步代码块或者是同步函数中使用,因为要通过锁对象调用这2个方法。
- 被唤醒的线程不一定能立即恢复执行,需要再次获取锁后才能在从 wait() 方法之后的地方恢复执行。
2. 线程池
1 | 容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。 |
线程池的好处:
- 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
- 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
线程池使用步骤:
- 用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
- 创建一个类,实现Runnable接口,重写run方法,设置线程任务
- 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
- 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
1 | //代码展示 |
3. Lambda表达式
1 | Lambda表达式的标准格式为:(参数类型 参数名称) ‐> { 代码语句 } |
使用前提:
- 必须有函数式接口——只包含一个抽象方法的接口
- 必须有上下文引用(必须有接口作为数据类型接收)
省略格式(可推导,可省略):
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
1 | //代码演示 |
【File类、递归】
1. File类
1 | File类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。 |
构造方法
1 | public File(String pathname) :通过将给定的路径名字符串创建新的 File实例。 |
获取的方法
1 | public String getAbsolutePath() :返回此File的绝对路径名字符串。 |
判断的方法
1 | public boolean exists() :此File表示的文件或目录是否实际存在。 |
创建删除的方法
1 | public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。 |
遍历的方法
1 | public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。 |
调用listFiles方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历。
2. 绝对路径和相对路径
1 | 绝对路径:从盘符开始的路径,这是一个完整的路径。 |
File.pathSeparator
:获取当前系统路径分隔符;(Windows用” ; “ Linux用” : “)File.separator
:获取当前系统文件名称分隔符;(Windows用”反斜杠\“ Linux用”正斜杠/“)
3. 递归
1 | 递归:指在当前方法内调用自己的这种现象。 |
注意事项:
- 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。(StackOverflowError)
- 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。(StackOverflowError)
- 构造方法,禁止递归。
1 | //代码演示 #暴力删除# |
1 | //代码演示 #阶乘# |
4. 文件过滤器
1 | public File[] listFiles():返回指定目录中的子目录和文件。 |
1 | //定义一个方法,输出文件夹里的所有的java文件 |
【字节流、字符流】
1. IO流
1 | 输入流 :把数据从其他设备上读取到【内存中】的流。 |
顶级父类们 | 输入流 | 输出流 |
---|---|---|
字节流 | 字节输入流【InputStream】 | 字节输出流【OutputStream】 |
字符流 | 字符输入流【Reader】 | 字符输出流【Writer】 |
2. 字节输出流【OutputStream】
1 | java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。 |
1 | //OutputStream 基本方法: |
FileOutputStream
1 | //构造方法:true 表示追加数据, false 表示清空原有数据 |
3. 字节输入流【InputStream】
1 | java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。 |
1 | //InputStream 基本方法: |
FileInputStream
1 | //构造方法 |
1 | //文件复制代码演示 |
4. 字符输入流【Reader】
1 | java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。 |
1 | //Reader 基本方法: |
FileReader
1 | //构造方法 |
1 | //代码演示 |
5. 字符输出流【Writer】
1 | java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。 |
1 | //Writer 基本方法 |
FileWriter
1 | //构造方法 |
注意:
使用FileWriter中的方法write,是把数据写入到内存缓冲区中,需要刷新缓冲区,才能将数据保存到文件中。
- flush :刷新缓冲区,流对象可以继续使用。
- close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
6. IO异常的处理
JDK7前处理
1 | //代码演示 |
JDK7的处理
1 | //JDK7优化后用()包裹流对象语句,确保每个流对象在结束使用后关闭 |
JDK9的改进
1 | //JDK9中使用引入对象的方式,同样可以确保每个流对象在结束使用后自动关闭 |
7. Properties类
1 | java.util.Properties继承于Hashtable,使用键值结构存储数据,每个键及其对应值都是一个字符串。 |
1 | //构造方法 |
1 | //代码演示 |
【缓冲流、转换流、序列化流】
1. 缓冲流
字节缓冲流
1 | public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。 |
字符缓冲流
1 | public BufferedReader(Reader in) :创建一个 新的缓冲输入流。 |
1 | //通过缓冲流复制文件,代码演示: |
2. 字符集
1 | 也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。 |
- ASCII字符集 :基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。
- GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了
21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。- Unicode字符集 :为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国
码,其中最为常用的是UTF-8编码:
- 128个US-ASCII字符,只需一个字节编码。
- 拉丁文等字符,需要二个字节编码。
- 大部分常用字(含中文),使用三个字节编码。
- 其他极少使用的Unicode辅助字符,使用四字节编码。
3. 转换流
InputStreamReader类
1 | 转换流java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定 |
OutputStreamWriter类
1 | 转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符 |
1 | //将GBK编码的文件转换为UTF-8编码,代码演示: |
4. 序列化
1 | Java提供了一种对象序列化的机制,使【内存中对象的数据】与【硬盘中文件里的数据】可以相互转化。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。 |
ObjectOutputStream类
1 | java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。 |
序列化操作注意事项:
- 要实现序列化的类必须实现
java.io.Serializable
接口,否则会抛出NotSerializableException异常。- 被瞬态
transient
或静态static
修饰的属性不会被序列化。
ObjectInputStream类
1 | ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。 |
serialVersionUID序列版本号:Serializable 接口给需要序列化的类,提供了一个序列版本号,目的在于验证序列化的对象和对应类是否版本匹配。如果对类进行和修改,那么其序列标本号也会发生修改。可通过在类中写死序列版本号,使修改后的类依然能够匹配序列化的对象。
1 | //序列化代码演示 |
5. 打印流
1 | java.io.PrintStream 类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。 |
PrintStream类
1 | //构造方法 |
【网络编程】
1. 软件结构
- C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
- B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
2. 网络通信协议
1 | 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。 |
分层 | 协议 |
---|---|
应用层 | HTTP/FTP/TFTP/SMTP/SNMP/DNS |
传输层 | TCP/UDP |
网络层 | ICMP/IGMP/IP/ARP/RARP |
数据链路层+物理层 | 由底层网络定义的协议 |
3. TCP协议
1 | TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前, |
三次握手建立连接:在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
- 客户端向服务器端发出连接请求,等待服务器确认。
- 服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 客户端再次向服务器端发送确认信息,确认连接。
四次挥手断开连接:客户端与服务器之间的四次交互后断开,保证了数据的完整性。
- 客户端先向服务器发送断开请求,问询服务器是否可以断开(说明客户端没有数据要传输了)
- 服务器向客户端发送数据,需要客户端确认(说明服务器没有数据要传输了)
- 客户端再次问询服务器是否可以断开连接
- 断开连接
4. UDP协议
1 | UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。 |
5. IP地址
1 | IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设 |
IP地址分类:
- IPv4:是一个32位的二进制数,通常被分为4个字节,表示成a.b.c.d 的形式,例如
192.168.65.100
,最多可以表示42亿个。有资料显示,全球IPv4地址在2011年2月分配完毕。- IPv6:采用128位地址长度,每16个字节一组,分成8组十六进制数,解决了网络地址资源数量不够的问题。表示成:
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
。
- 查看本机IP地址,在控制台输入:
ipconfig
- 检查网络是否连通,在控制台输入:
ping 空格 IP地址
- 本机IP地址:
127.0.0.1
6. 端口号
1 | 网络的通信,本质上是两个进程(应用程序)的通信,端口号可以标识设备中的不同进程(应用程序)。 |
用两个字节表示的整数,它的取值范围是0~65535。
0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。
常用端口号:
- 网络端口 https:443 http:80
- 数据库 mysql:3306 oracle:1521
- Tomcat服务器:8080
【协议+ IP地址+ 端口号】 三元组合可以标识网络中的进程,进程间的通信可以利用这个标识与其它进程交互。
7. Socket类
1 | java.net.Socket类表示客户端。创建Socket对象向服务器发出连接请求,服务器响应请求,两者建立连接开始通信。 |
1 | //构造方法 |
8. ServerSocket类
1 | java.net.ServerSocket类表示服务端。创建ServerSocket 对象,相当于开启一个服务,并等待客户端的连接。 |
1 | //构造方法 |
1 | //客户端代码演示 |
9. 文件上传优化分析
1 | //文件上传案例服务端代码演示 |
① 文件名称写死的问题
1 | 服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下: |
② 循环接收的问题
1 | 服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件,代码如下: |
③ 效率问题
1 | 服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,代码如下: |
【函数式接口】
1. 函数式接口
1 | 有且仅有一个抽象方法的接口。 |
@FunctionalInterface注解:使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。
2. Lambda的延迟执行
1 | //无论level是多少,都会先把字符串拼接并传入方法内。如果level不符合要求,拼接操作就白做了 |
3. Supplier接口
1 | java.util.function.Supplier<T>接口用来获取一个泛型参数指定类型的对象数据。 |
1 | //代码演示 |
4. Consumer接口
1 | java.util.function.Consumer<T> 接口与Supplier接口相反,是消费一个数据,其数据类型由泛型决定。 |
1 | //代码演示 |
5. Predicate接口
1 | java.util.function.Predicate<T>接口返回一个boolean值结果,用于对某种类型的数据进行判断。 |
1 | //代码演示 |
6. Function接口
1 | java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据。 |
1 | //代码演示 |
【Stream流、方法引用】
1. 流式思想
1 | Stream(流)是一个来自数据源(集合、数组等)的元素队列,元素是特定类型的对象,形成一个队列。 |
Stream流支持并行,效率高
2. 获取流
1 | //单列集合中,Collecion接口中加入了stream方法用于获取流 |
备注: of 方法的参数其实是一个可变参数,所以支持数组。
3. 常用方法
- 延迟方法:返回值类型仍然是Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
- 终结方法:返回值类型不再是Stream 接口自身类型的方法,因此不再支持类似StringBuilder 那样的链式调用。终结方法包括count 和forEach 方法。
forEach:逐一处理
1 | void forEach(Consumer<? super T> action); |
filter:过滤
1 | Stream<T> filter(Predicate<? super T> predicate); |
map:映射
1 | <R> Stream<R> map(Function<? super T, ? extends R> mapper); |
count:统计个数
1 | long count(); |
limit:取用前几个
1 | Stream<T> limit(long maxSize); |
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。
skip:跳过前几个
1 | Stream<T> skip(long n); |
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
concat:组合
1 | static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) |
collect:将流元素生成集合
1 | List<T> list = stream.collect(Collectors.toList()); |
toArray:将流元素生成数组
1 | Object[] array = stream.toArray(); |
1 | //代码演示 |
注意:Stream分为三类方法:
- 初始操作,将数据源转换为Stream流
- 中间操作,调用之后返回值也是Stream对象
- 终止操作,调用之后返回值不是Streasm对象
流中的数据只能被消费一次,流在操作过程中,如果执行的是初始操作、中间操作,那么实际上不会对流元素进行处理,只有在进行Stream的终止操作的时候才执行
4. 方法引用
1 | 双冒号::为引用运算符,它所在的表达式被称为方法引用,是简化Lambda的书写。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。前提是括号内的所有内容都是依赖于一个对象的某个方法实现的,并不是所有的Lambda表达式都能被简化为方法引用。 |
1 | //代码演示 |
【单元测试、反射、注解】
1. 单元测试
1 | 测试分类: |
白盒测试步骤:
- 定义一个测试类,类名为被测试类名+Test(CalculatorTest)
- 定义一个测试方法,可以独立运行,方法名为test+被测方法名(testAdd() )
方法的返回值:void;参数列表:空参
- 给方法加@Test
- 导入junit依赖环境
注意事项:
- 使用断言操作
Assert.assertEquals(期望的结果,运算的结果)
来处理结果- 红色结果为失败,绿色结果为成功
- @Before:修饰的方法会在测试方法之前被自动执行
- @After:修饰的方法会在测试方法执行之后自动被执行
1 | //代码演示 |
2. 反射
1 | 反射:将类的各个组成部分封装为其他对象,这就是反射机制。通过反射,可以在程序运行过程中操作这些对象,是框架的基础。运用反射可以解耦,提高程序的可拓展性。 |
获取Class对象
1 | 1. Class.forName("全类名"):用于配置文件 |
操作成员变量对象
1 | //获取成员变量对象 |
操作构造方法对象
1 | //获取构造方法对象 |
操作成员方法对象
1 | //获取成员方法对象 |
3. “框架”案例
1 | // 配置文件ClassMessage.properties: |
4. 注解
1 | 注解(Annotation),也叫元数据。一种代码级别的说明,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。JDK1.5及以后版本引入的特性。 |
作用分类:
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
- 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
JDK中预定义的一些注解:
- @override:方法重写注解
- @Deprecated:过期注解
- @SuppressWarnings:压制警告,将当前所有的类警告都去除
一般传递参数all @SuppressWarnings(“all”)
5. 自定义注解
1 | //格式: |
注解本质上就是一个接口,该接口默认继承Annotation接口:
public interface MyAnno extends java.lang.annotation.Annotation {}
注解接口中的要求:
- 接口中的抽象方法称为属性,返回值类型为:
①基本数据类型;②String;③枚举;④注解;⑤以上类型的数组
- 定义了属性,在使用时需要给属性赋值,若定义时已经给了默认值,可以不用赋值,如果只有一个属性需要赋值,并且属性的名称是value,则赋值的时候,value的字样可以省略
- 数组赋值时,需要使用{}包裹,如果数组中只有一个值,则{}省略
6. 元注解
1 | 用于描述注解的注解。 |
@Target(ElementType):描述注解能够作用的位置
ElementType取值:
- ElementType.TYPE:可以作用于类上
- ElementType.METHOD:可以作用于方法上
- ElementType.FIELD:可以作用于成员变量上
@Retention(RetentionPolicy):描述注解被保留的阶段
RetentionPolicy取值:
- RetentionPolicy.RUNTIME:当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
- RetentionPolicy.CLASS:当前被描述的注解,会保留到class字节码文件中,不会被JVM读取
- RetentionPolicy.SOURCE:当前被描述的注解,不会被class字节码文件保留
@Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承
7. ”测试框架“案例
1 | public static void main(String[] args) throws IOException { |