扩展
- 笔记。
- “不同族”基本数据类型转换。
- 基本数据类型和引用类型赋值时的底层分析。
- Socket套接字。
- 二进制相关概念、运算与应用。
- 浮点数二进制。
- 23种设计模式。
- 自定义注解。
- 泛型。
- Lambda表达式。
- 反射。
- 线程生命周期与线程通信。
- 线程池生命周期。(见【生命周期状态】一栏)
- @CallerSensitive注解。
- # [源码解析]Class。
- 异常处理。
- # volatile关键字。
- POI入门。
- 类加载。
- 常用关键字。
2、多态
2.1 继承
参考笔记一,P33.7、P34.1、P35.2/3;笔记二,P26.5。
- 子类拥有父类所有,只是无法使用
private修饰部分;父类拥有子类所有,只是无法使用子类扩展部分。故经过上溯或下溯转型,都可使用相应资源; - 只能先进行上溯转型,再进行下溯转型。由于上溯或下溯转型后,很难判断引用所指向的对象到底属于哪个类,故可用
instanceOf判断; - 若子类未重写父类方法,尽管子类拥有父类所有,但那些方法在根本上还是属于父类,因此子类调用的还是父类方法;
super并不代表父类引用,只是用于调用父类成员。getClass()是 Object 类的 final 方法(不可重写),故super.getClass()等同于this.getClass(),返回的是当前对象所属类的 Class 对象。
2.2 重载
参考笔记二,P26.6、P29.4。
参数列表不同、或返回值类型不同、或都不同的同名方法称之为“重载”。与访问权限无关,其作用是增加代码可读性。注意:
main()也可重载,默认情况下执行参数类型为String[]的main();- 若方法名、参数列表都相同,仅返回值类型不同,则不是“重载”,且不允许。
2.3 重写
参考笔记二,P29.3。
当子类方法与父类参数列表和返回值类型都相同的同名方法称之为“重写”。注意:
- 重写方法的访问权限不能低于被重写方法;
- 重写方法的异常范围不能大于被重写方法;
- 若子类某方法与父类某方法方法名、参数列表都相同,仅返回值类型不同,这不是“重写”,且不允许。
3、实例化相关
3.1 构造方法说明
参考笔记一,P34.3。
1:为什么JVM会默认为类创建无参构造方法?
因为实例初始化时需要。
2:为什么子类会默认调用父类无参构造方法?
因为子类拥有父类所有,这需要初始化父类,即调用构造方法。注意:无论子类或父类有没用自定义构造方法,子类都会隐式调用父类无参构造方法。
3:为什么super()必须在构造方法的第一行?
从第2点可知,子类初始化时会同时初始化父类,但并不能知道super()在第一行的原因。因为父类的初始化数据存储于子类内存空间(具体指堆),即便后续调用(先初始化子类,后初始化父类)也可访问。因此,super()在第一行的原因是:子类要为父类分配内存空间,自然要先知道父类占多少空间,即先初始化父类。
3.2 创建对象方法
参考笔记二,P39.7、P45.2。
1、new xx()
2、反射
3、xx.clone()
4、反序列化
其中,clone()属Object类方法,使用clone()的类必须实现Cloneable接口和重写clone()。其中,克隆的对象是一个新对象,克隆分为浅克隆 和深克隆 两种,两者的区别是当克隆的类中引用其他类时,深克隆会同时克隆引用类,而浅克隆不会,仍是同一个。深克隆的实现原理是在相应clone()内克隆引用类。
3.3 注意
- 若仅实例化子类,由于
this代表的是当前实例,故当在父类中使用this时,this代表的是子类实例,而非父类实例。 - 实例化时,会先从方法区中检查构造方法是否相符(相同),再初始化成员变量和成员方法。
6、断言
参考笔记二,P7.5。
6.1 什么是“断言”?
断言是类似异常处理的一种特殊语句,表现为一条语句。一般有3种形式:
1、assert a;
2、assert a: b;
3、assert(a);
a 是一个boolean表达式或boolean值,b 是一个基本数据类型变量或对象。
语句表达的意思是:如果 a 为false,抛出异常,打印 b 作为异常信息,程序终止。
断言的作用是保证程序的正确性(判断是否可执行成功),常用于开发和测试阶段(实际上一般很少用)。
与异常处理不同的是,断言是先验条件(先执行 assert,后运行程序;而异常处理是后验条件(先运行程序,后处理异常),比如文件操作中比较常见的 FileNotFoundException,就是先运行程序,再发现文件不存在抛出的异常。
6.2 为什么assert无效?
由于断言会影响程序性能,故“断言校验”是默认关闭的。因此使用断言需要先打开断言:
输入:
-enableassertions
或
-ea
7、VM 参数
推荐一篇博文《深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析》(转发)。
目前我暂未作研究,大家可以查阅这篇博文。
9、序列化
参考笔记一,P1、P2.3。
9.1 什么是“序列化”?
“序列化”指将对象转为字节序列、实现将对象持久化(永久保存)到磁盘、使对象能在网络 或进程间 传递的过程。
(注:实现序列化的类必须实现Serializable接口;若此类引用了其他类,则相应类也必须实现此接口)
特点:
- 持久性。
将对象转为字节序列存于磁盘,即使JVM死机,对象仍然存于磁盘。当JVM宕机时,可以通过反序列化还原对象,并且,序列化的对象占据更小的磁盘空间; - 序列化的对象在网络上传输会更快捷、更安全;
- 可实现在进程间传递对象。
“进程间”是什么意思?因为无论程序复杂或简单,在启动时,JVM都只会创建一个进程,而进程间无法直接传输对象。
9.2 序列化ID
示例:
private static final long serialVersionUID = -5809782578272943999L;
这就是序列化ID,其在类加载时生成,用于作为类在JVM内存中的唯一标识。其作用是通过比较字节序列中的序列化ID和实体类中的序列化ID是否一致来判断是否为同一个类,即是否可反序列化,从而还原对象。
9.3 测试
“序列化”与“反序列化*”是一种实现对象持久化的概念,具体实现方法任意。如下示例中使用
ObjectOutputStream实现序列化,使用ObjectInputStream实现反序列化是其中一种实现方法。
1、序列化:将对象 Person 序列化到 Person.txt 文件中。
示例:
@AllArgsConstructor
class Person implements Serializable {
private static final long serialVersionUID = -5809782578272943999L;
private Integer id;
private String name;
}
class TestSerializable {
public static void main(String[] args) throws Exception {
Person person = new Person(1001, "yuchen");
System.out.println(person);// 打印:Person(id=1001, name=yuchen)
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("C:\\Users\\于辰\\Downloads\\新建文件夹\\Person.txt")));
oos.writeObject(person);
}
}
测试结果:
字节序列文件:
字节序列:
2、反序列化:将 Person.txt 文件中的字节序列通过反序列化还原成 Person 对象。
示例:
class TestSerializable {
public static void main(String[] args) throws Exception {
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("C:\\Users\\于辰\\Downloads\\新建文件夹\\Person.txt")));
Person person = (Person) ois.readObject();
System.out.println(person);// 打印:com.neusoft.boot.Person@69d0a921
}
}
12、ORM
全称Object Relational Mapping(对象关系映射),指实体类与数据表一一对应,属性与字段一一对应,而实例/对象对应记录,即将对象“存入”数据表中。常用的ORM框架:mybatis、mybatis-plus、hibernate。
优点:
- 提高开发效率;
- 解耦合。如:客户需求变更,需要增删字段来实现功能,使用ORM框架,不用 sql 直接编码,能够像操作对象一样从数据库读取数据。
缺点:降低程序性能。
- 一般ORM框架系统都是多层的,系统层次多会降低程序性能。且ORM框架是完全面向对象,也会降低程序性能;
- ORM框架生成的 sql 语句一般很难写出高效的算法,只能先将对象提取到内存对象中(指
select到程序中),再进行过滤和加工,这会降低程序性能; - 在对象持久化时,ORM框架一般会持久化对象的所有属性,有时这是不需要的,这会降低程序性能。
注:“持久化”指将数据存储进数据库。
14、IPv4
参考笔记一,P75.2。
IPv4由4部分组成,包含网络地址 和主机地址,每部分解释为一个字节。故都是0 ~ 255的整数。因此,ipv4 占4个字节(32位)。
分类:(注:a指网络地址,b指主机地址)
- A类:a 占前8位,b 占后24位,a 的最高位必须是
0。因此,范围是:0.0.0.0 ~ 127.255.255.255; - B类:a 占前16位,b 占后16位,a 的最高位必须是
10。因此,范围是:128.0.0.0 ~ 191.255.255.255; - C类:a 占前24位,b 占后8位,a 的最高位必须是
110。因此,范围是:192.0.0.0 ~ 223.255.255.255; - D类:暂未知,a 的最高位必须是
1110。因此,范围是:224.0.0.0 ~ .239.255.255.255; - E类:暂未知,a 的最高位必须是
1111。因此,范围是:240.0.0.0 ~ 255.255.255.255。
17、编译与解释
参考笔记二,P20。
1:什么是“编译”?
“编译”是指将源代码一次性转换成目标程序的过程,编译只执行一次,故编译的着重点不是编译速度,而是目标程序的运行速度。因此,编译器一般都会尽可能多地集成优化技术,使生成的目标程序具有更高的执行速度。
特点:
- 对于相同的源代码,目标程序执行速度更快;
- 目标程序运行不需要编译器支持,在同类操作系统上使用灵活。
2:什么是“解释”?
“解释”是指将源代码逐条转换并同时运行的过程,不会生成目标程序,故解释的着重点是解释速度。因此,解释在每次程序运行时都需要解释器和源代码,也不能集成太多的优化技术,这样会降低程序的运行速度。
特点:
- 由于解释不会生成目标程序,因此解释执行需要保留源代码,这样也便于程序的纠错和维护;
- 只要有解释器,源代码在任何操作系统皆可运行,可移植性强。
3:“编译”与“解释”的运用:
高级语言按照执行方式分为静态语言和脚本语言。静态语言使用编译,如:C/C++、java;脚本语言使用解释,如:python、js、php。 在实际运用中,会将两者混合使用,以实现在保证程序逐条运行的同时保留目标程序,因为程序逐条运行能大大提升程序运行速度,保留目标程序便于程序纠错和维护。
最后
本文中的例子是为了方便大家理解和阐述知识点而简单举出的,旨在阐明知识点,并不一定有实用性,仅是抛砖引玉。
推荐一个Java学习平台 → HOW2J.CN。
本文持续更新中。。。