目录
第一章:什么是对象
1.继承
2.多态
第三章:对象无处不在
1.基本类型的默认值
2.Integer
第六章:初始化和清理
1.方法重载
2.垃圾收集器的工作原理
3.初始化顺序
4.JDK11新特性:局部变量类型推断
第七章:实现隐藏
1.新特性:模块
第八章:复用
8.5选择组合还是继承
8.9初始化及类的加载
第九章:多态
9.2.1 方法调用绑定
9.2.5 陷阱,字段和静态方法
9.3.1构造器的调用顺序
9.3.3 构造器内部的多态方法行为
9.4 协变返回类型
第十章 接口
10.2.1 默认方法
10.2.2 多继承
10.2.3 接口中的静态方法
10.3 抽象类与接口
编辑
注意点:普通类,抽象类,接口的默认访问权限。
10.6 组合接口时的名称冲突
10.7适配接口
10.12 新特性:密封类和密封接口
第十一章 内部类
11.7 嵌套类
11.8 为什么需要内部类
11.8.1 闭包与回调?
11.8.2 内部类与控制框架*
11.9 继承内部类
第12章 集合
12.11新特性:记录(record)类型
第十三章 函数式编程*
13.3.2 未绑定方法引用
13.5 高阶函数
13.6 闭包
13.8柯里化和部分求值
第十四章: 流*
第十五章: 异常
15.12 try-with_resource语句
15.14 其他可选方式
第十六章:代码检验
16.6 基准测试
16.7 分析与优化
16.8 样式检查
16.9 静态错误分析
第十八章:字符串
18.6 文本块
第二十一章:泛型
第一章:什么是对象
1.继承
纯替换关系:子类应该只重写基类已有的方法,而不创建新的方法。这种情况下,子类和基类的类型完全相同。可以用子类对象替换基类对象。子类和父类的关系叫做“is-a”关系。这是一种理想的继承方式。
有时候,需要为子类添加新的方法,从而扩展原有的接口,这种情况下,子类也能替换父类,但是并不完美,不能通过基类接口获取子类的新方法,所以用子类替换基类后,子类的新方法并不能起到任何作用。这种关系描述为“is-like-a”关系。
2.多态
在Java中,基类可以被不同的子类替代。从而体现出不同的状态。Java通过“后期绑定”机制,使得基类在被子类替换时也能够正确的执行对应的代码,而不需要程序员进行特殊处理。编译器在编译时并不知道具体该执行那一段代码。而是直到运行时才会确定哪一段代码会被调用。
对于非面向对象编译器而言,其生成的函数会调用“前期绑定”,前期绑定意味着编译器会生成对一个具体方法名的调用,该方法名决定了被执行代码的绝对地址。
第三章:对象无处不在
1.基本类型的默认值
如果基本类型是一个类的成员变量,那么即使你没有为基本变量赋值,Java也会为其设置一个默认值。
boolean false
char null)
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d
此外,如果基本类型作为一个局部变量,Java并不会为其设置默认值。在使用基本类型局部变量前,需要为其赋值,否则Java会抛出一个编译错误。
2.Integer
出于效率原因,Integer会通过享元模式来缓存范围,在-128~127内的对像。因此多次调用Integer.valueOf(127)生成的其实是一个同对象。
对 byte short chat进行算术运算,得到的结果是int类型。
int和long相加,得到的结果是long。也就是说,运算的结果类型是根据参与运算的最大类型而定的。
第六章:初始化和清理
1.方法重载
方法重载是根据参数的类型和参数的顺序的不同来区分的,不能根据返回值来区分。
如果有两个方法
void f()
int f() { return 1}
on java基础卷
int x = f(),x的类型能够告诉编译是我们想要调用哪个版本的f()
但如果是,f(),这样调用时,Java无法确定该调用哪个f()。由于存在这些问题,所以不能用返回的类型来区分重载方法。
2.垃圾收集器的工作原理
引用计数是一种简单但缓慢的垃圾收集技术。在这个方案里,每一个对象都包含一个引用计数并且每次该对象被引用时引用计数都会增加。每次引用离开作用域或设置为null时,引用计数都会减少。垃圾收集器遍历整个对象列表当他找到引用计数为零的对象时就会释放该存储空间(不过引用计数方案通常会在计算变为零时立即释放对象)。引用计数通常用于释放垃圾收集的一种工作方式,但他似乎并没有出现在任何JVM实现中。
更快的方案不是使用引用计数,而是基于这样一个想法任何没有被废弃的对象最终都能追溯到存在于栈或静态存储中的引用。因此,如果你从栈和静态存储区开始遍历所有引用,你就能找到所有存活的对象。
Jvm使用了一种自适应的垃圾收集方案,根据不同的垃圾收集算法来进行垃圾清理。

其中一种算法是“停止-复制”算法。将所有存活的对象复制到另外一个堆,并且将旧的堆删除。
“标记-清除”算法,从栈和静态存储开始遍历所有引用以查找存活对象每当他找到一个存活对象就会给该对象设置一个标志。标志过程完成后开始清除。在清除过程中没有标志的对象就会被释放。这种算法会让内存产生很多碎片,所以就需要通过重新排列对象来清除这些碎片。
JVM检测到没有新的垃圾产生或者产生的垃圾很少,会自动从停止复制算法切换为标志清除算法。如果产生的垃圾比较多,就会转换回停止复制算法,这就是JVM的自适应。
3.初始化顺序
1.初始化静态属性
2.初始化非静态属性
3.执行构造器
静态属性只有在对象被创建或者对象静态属性被调用时初始化一次。
4.JDK11新特性:局部变量类型推断
讯享网以下是一些错误的使用方式:
讯享网
类型推断十分适用于for循环
第七章:实现隐藏
1.新特性:模块
JDK 9的 Jigsaw 项目将 JDK 库拆分为一百多个平台模块。现在,当使用库组件时,你会仅仅获得该组件的模块及其依赖项,不会有不使用的模块。如果想继续使用隐藏的库组件,你必须启用"逃生舱口"( escape hatch ),它清楚地表明你这样做违反了库的预期设计,因此将来如果因为更新这个隐藏组件(甚至完全删除)而引起任何破坏,都要由你自己来负责。
java --list-modules 显示所有可用的模块。
java --describe-module java.base 查看模块的内容
第八章:复用
8.5选择组合还是继承
组合和继承都会将子对象放置在新类中(组合是显式执行此操作,而继承是隐式执行)。你可能想知道两者之间的区别,以及如何在两者之间做出选择。
当希望在新类中使用现有类的功能而不是其接口时,应该使用组合。也就是说,在新类中嵌入一个对象(通常是 private )来实现自己的特性。新类的用户看到的是新类定义的接口,而不是嵌入对象的接口。
当使用继承时,你通过现有的类来生成它的一个特殊版本。这通常意味着对通用类进行定制,使它可以用于特定需求。稍加思考你就会发现,用一个"交通工具"对象来组合成一部"汽车"是毫无意义的﹣﹣汽车并不包含交通工具,它是一种交通工具。继承是用来表示" is - a "关系的,而组合是用来表示" has - a "关系的。
确定是使用组合还是继承的最清晰方法之一,就是询问是否需要从新类向上转型到基类。如果必须向上转型,则继承是必要的,但如果不是,那就要再仔细想想自己是否的确需要继承。
8.9初始化及类的加载
了解包括继承的整个初始化过程。
- 加载父类静态属性及静态代码块
- 加载子类静态属性及静态代码块
- 加载父类非静态属性及代码块
- 加载父类构造器
- 加载子类非静态属性及代码块
- 加载子类构造器
以下是示例:

第九章:多态
动态方法调用允许一种类型表现出与另一种相似类型的区别。只要它们都继承自相同的基类型即可。通过基类来调用不同子类的相同方法,从而实现行为上的区别,这样就表现出了多态方法调用的差异性。
9.2.1 方法调用绑定
将一个方法的调用跟方法体绑定起来的动作称为绑定。在程序运行之前执行绑定,称为编译绑定。JAVA使用的是后期绑定(动态绑定或运行时绑定),也就是在编译时,编译器并不知道方法调用对应的是哪个方法体,而是在运行时,方法调用机制会自动根据对象类型,调用对应的方法体。
9.2.5 陷阱,字段和静态方法
字段不会表现出多态性。
如果一个方法是静态的,那他的行为就不是多态的。静态方法与类关联。而不是与单个对象关联。
9.3.1构造器的调用顺序

Sandwich 对象创建时的输出显示,一个复杂对象的构造器调用顺序如下。
1.基类的构造器被调用,递归地重复此步骤,一直到构造层次结构的根。根类先被构造,然后是下一个子类,以此类推,直到最底层的子类。
2.然后按声明的顺序来初始化成员。
3.最后执行子类构造器的方法体。
9.3.3 构造器内部的多态方法行为
创建子类对象时,会先调用基类构造器,如果基类有一个方法hello(),该方法在子类被重写了。如果基类构造器方法体中调用了hello(),那么实际执行的是被子类重写的hello()。如果子类的hello()中调用了子类的成员变量,那么可能会出现问题,因为此时子类的成员变量还没有被初始化。

初始化过程如下所示。
1.在发生任何其他事情之前,为对象分配的存储空间会先被初始化为二进制零。
2.如前面所述的那样调用基类的构造器。此时被重写的 draw ()方法会被调用(是的,这发生在 RoundGlyph 构造器被调用之前),由于第1步的缘故,此时会发现 radius 值为零。
3.按声明的顺序来初始化成员。
4.子类构造器的主体代码被执行。
9.4 协变返回类型
Java中,允许重写父类方法时,返回值的类型为该返回值的子类型。
第十章 接口
10.2.1 默认方法
在JAVA8,接口新加了默认方法。默认方法可以有方法体,也就是说可以在接口中直接实现方法,并且实现类不需要重写该方法便可以直接调用它。如果一个接口已经有了很多实现类,现在想在这个接口新添加一个方法,在JAVA8以前,只能添加抽象方法,这样所有实现类都要去实现这个新的方法,这样改动就很大。在JAVA8,可以在接口新增加一个默认方法,而不需要改动实现类。默认方法在上面的场景是非常有用的。JAVA8中集合的流(stream()方法)就是通过默认方法实现的。
在JDK9,接口里的default和statis方法都可以是private的。
10.2.2 多继承
JAVA中不允许继承多个基类,但是可以同时实现多个接口。在JAVA8,将接口与默认方法结合,意味着我们可以结合来自多个基类的行为。
10.2.3 接口中的静态方法
接口允许有静态方法。
10.3 抽象类与接口
注意点:普通类,抽象类,接口的默认访问权限。
普通类:类,方法,字段默认都是包访问级别。类级别只能是public或者包访问级别,不能声明为private和protected
抽象类:类,方法,字段默认都是包访问级别。
接口:
接口默认为包访问级别,所以一般会显示定义接口为public。
方法默认为public级别。
字段:默认为public static final
10.6 组合接口时的名称冲突

10.7适配接口
定义一个方法时,使用接口作为参数,这样就可以传入不同实现类来达到不同目的。这其实就是策略模式。对于不是该接口的实现类的其他类,也可以通过适配模式来实现该接口的调用。
10.12 新特性:密封类和密封接口

如果所有的子类都定义在同一个文件中,则不需要permits字句。
讯享网
我们应该优先使用类而不是接口。几乎任何一个要创建类的场景,都可以用一个接口和工厂来代替。但是我们不能够仅仅只是认为以后可能会用的多个实现而使用它,这样会让代码更加复杂。任何抽象都应该由正真的需求来驱动,要在必要的时候使用接口来重构代码,而不是一开始就是不加思索的使用它。
第十一章 内部类
11.3 使用.this和.new

11.7 嵌套类
在类的内部定义一个static内部类,这个类叫做嵌套类。嵌套类失去了对外部类的联系,不能调用外部类的非静态属性和方法。嵌套类可以定义static属性和方法,并且嵌套类里面还可以再定义一个嵌套类。在普通内部类,这是不允许的。
11.8 为什么需要内部类
内部类实际上支持了“多重实现继承”。可以在一个类中通过定义多个内部类的方式继承多个不同的普通类或者抽象类。
11.8.1 闭包与回调?
闭包是一个可调用的对象,它保留了来自它被调用时所在的作用域的信息。在JAVA中可以通过内部类和lambda表达式实现作用域。
11.8.2 内部类与控制框架*
通过模板方法,事件驱动系统和内部类,实现一个温室控制框架系统。
11.9 继承内部类

第12章 集合
12.11新特性:记录(record)类型
JDK16增加了record关键字,使用record关键字时,编译器会自动生成:
- 不可变的字段
- 一个规范构造器
- 每个元素都有的访问器方法
- equals()
- hashco()
- toString()
有了record关键字,我们自定义的类型用于作为Map的key时就不需要自己手动去实现equals()和hashCode()。

第十三章 函数式编程*
13.3.2 未绑定方法引用
未绑定方法引用指的是尚未关联到某个对象的普通(未静态)方法。

13.5 高阶函数
高阶函数指的是一个能接受函数作为参数或者能把函数当做返回值的函数。


13.6 闭包
在Java中,变量捕获(Variable Capturing)通常指的是在匿名内部类或Lambda表达式中,引用外部作用域的局部变量。当在一个匿名内部类或Lambda表达式中引用外部作用域的局部变量时,该局部变量会被捕获并保存下来,以便在内部类或Lambda表达式中访问和使用。
被匿名内部类或lambda表达式捕获的变量,要满足以下两种要求之一:这个变量被final修饰。如果这个变量不是被final修饰,要求事实上是最终的,即这个变量在被捕获之后没有被修改。
局部变量在方法调用结束后就会被销毁,所以lambda表达式使用外部作用域的局部变量时需要对局部变量进行捕获保存,所以这种情况下局部变量必须是final修饰或者它是实际上的最终变量。而如果lambda表达式引用的是外部作用域某个对象的字段,那么它有独立的生命周期,并不需要特殊的捕获,所以它是可变的。
闭包引用外部某个对象的字段
13.8柯里化和部分求值
柯里化的意思是 将一个接受多个参数的函数转变为一系列只接受一个参数的函数

第十四章: 流*
第十五章: 异常
Java中异常主要分为Error和Exception两类。前者是编译错误或系统错误,我们不用关心其捕获。后者主要代表运行时事故,需要进行捕获处理。RuntimeException是一个特殊的Exception。例如NullPointerException就是一个RuntimeException,Java会自动将它抛出,并且我们不需要将它包含在异常说明中。及不用在方法后面的throws声明它。
try catch应该放在循环里面,如果try catch在循环外面,如果出现异常,则整个循环将被中断。
15.12 try-with_resource语句
使用try-with_resource语句,可以帮助我们自动在try块结束时关闭资源。
15.14 其他可选方式
Java中的检查型异常饱受争议,在项目规模比较小时,检测型异常可以提高开发效率和代码质量。但项目规模过大时,检查型异常反而会降低开发效率,且代码质量并没有得到提高。不过,我们可以通过以下方式忽略检查型异常:
- 将异常抛出到控制台
也就是将代码一层一层往上抛,直到最上层。
- 使用RuntimeException将检查型异常包住
第十六章:代码检验
16.6 基准测试
使用JMH计算代码的耗时
16.7 分析与优化
可以使用jdk自带的Visualvm分析器来找到代码的哪一部分耗时比较多。
16.8 样式检查
CheckStyle样式检查器可以指出代码中不符合项目风格指南的地方。
16.9 静态错误分析
FindBugs可以发现Java静态类型检查无法发现的一些语法错误。
第十八章:字符串
18.6 文本块

第二十一章:泛型
泛型在运行时会将泛型参数类型擦除,并在边界--对象进入和离开某个方法的临界点。编译器会在编译时在临界点执行类型检查,并插入类型转换的代码。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/961.html