综合知识
static
可以在每个类中写一个静态main函数,便于做测试
构造函数
《1》重载,在java中可以定义相同函数名,参数类型不同或参数个数不同的函数,进行函数重载,只是返回值不同的话不行
《2》若已经定义了构造函数,不管是有参,还是无参,那么编译器默认的构造函数便不会执行。
《3》可以在一个构造函数中去调用另外一个构造函数,采用this(...)
eg: 在class1类中 public class1(double d1,){ this("123")} 在public class1(doucle d1) 中调用了 public class1(String s1) 构造函数
《4》初始化块
普通初始化块:{。。。}
静态初始化块:static {。。。}
普通初始化块在对象创建时隐式调用,静态初始化块在类加载时隐式调用,前者可以调用多次,后者就一次
顺序:静态初始化块|静态属性初始化 > 普通初始化块|普通属性初始化 > 构造器
final
final修饰类:final类不能被继承,所以final类的方法也没有机会被覆盖。
final修饰方法:final方法不能被子类修改,但能被继承。意义:可以防止子类对final方法进行修改,高效,编译器在遇到final方法时转入内嵌。
final修饰变量:用final修饰的成员变量只能被赋值一次,赋值完后就不能被修改
final修饰参数:可以读取参数,但是不能对参数值进行修改。
参考:https://www.cnblogs.com/chihirotan/p/6044056.html
https://blog.csdn.net/sunbinkang/article/details/79016829
finally 代码块是用于执行重要代码 (如关闭连接、流等) 的代码块。无论是否处理异常 finally代码块总会被执行。finally 代码块紧随 try 代码块或 catch 代码块。
final、finally和finalize区别
final
final 用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖和类不可被继承。
final 属性:被final修饰的变量不可变(引用不可变)
final 方法:不允许任何子类重写这个方法,但子类仍然可以使用这个方法
final 参数:用来表示这个参数在这个函数内部不允许被修改
final 类:此类不能被继承,所有方法都不能被重写
finally
在异常处理的时候,提供 finally 块来执行任何的清除操作。如果抛出一个异常,那么相匹配的 catch 字句就会执行,然后控制就会进入 finally 块,前提是有 finally 块。例如:数据库连接关闭操作上
finally 作为异常处理的一部分,它只能用在 try/catch 语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。(×)(这句话其实存在一定的问题,还没有深入了解,欢迎大家在 issue 中提出自己的见解)
异常情况说明:
在执行 try 语句块之前已经返回或抛出异常,所以 try 对应的 finally 语句并没有执行。
我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。那有人说了,在一般的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的
当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行
还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。
finalize
finalize() 是 Object 中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它 finalize() 方法,让此对象处理它生前的最后事情(这个对象可以趁这个时机挣脱死亡的命运)。要明白这个问题,先看一下虚拟机是如何判断一个对象该死的。
可以覆盖此方法来实现对其他资源的回收,例如关闭文件。
判定死亡
Java 采用可达性分析算法来判定一个对象是否死期已到。Java中以一系列 "GC Roots" 对象作为起点,如果一个对象的引用链可以最终追溯到 "GC Roots" 对象,那就天下太平。
否则如果只是A对象引用B,B对象又引用A,A B引用链均未能达到 "GC Roots" 的话,那它俩将会被虚拟机宣判符合死亡条件,具有被垃圾回收器回收的资格。
最后的救赎
上面提到了判断死亡的依据,但被判断死亡后,还有生还的机会。
如何自我救赎:
对象覆写了 finalize() 方法(这样在被判死后才会调用此方法,才有机会做最后的救赎);
在 finalize() 方法中重新引用到 "GC Roots" 链上(如把当前对象的引用 this 赋值给某对象的类变量/成员变量,重新建立可达的引用).
需要注意:
finalize() 只会在对象内存回收前被调用一次 (The finalize method is never invoked more than once by a Java virtual machine for any given object. )
finalize() 的调用具有不确定性,只保证方法会调用,但不保证方法里的任务会被执行完(比如一个对象手脚不够利索,磨磨叽叽,还在自救的过程中,被杀死回收了)。
finalize()的作用
虽然以上以对象救赎举例,但 finalize() 的作用往往被认为是用来做最后的资源回收。
基于在自我救赎中的表现来看,此方法有很大的不确定性(不保证方法中的任务执行完)而且运行代价较高。所以用来回收资源也不会有什么好的表现。
综上:finalize() 方法并没有什么鸟用。
至于为什么会存在一个鸡肋的方法:书中说 “它不是 C/C++ 中的析构函数,而是 Java 刚诞生时为了使 C/C++ 程序员更容易接受它所做出的一个妥协”。
可变参数
每个方法只能有一个可变参数,可变参数只能在最后一个。
无法通过改变可变参数的类型来重载方法,可变参数最后编译器处理成数组,可变参数就是数组。
public class Test {
private void func1(String... strings) {
int len=strings.length;
System.out.println(len);
}
public static void main(String[] args) {
Test test=new Test();
test.func1("1","2");
}
}
参考:https://mp.weixin.qq.com/s/ynC7aVS81Fpxyi61F0_2yw
基本类型
参考:https://mp.weixin.qq.com/s/zP2Bh8k5bY71vdATY5G8tw
深入理解java四种访问权限
https://mp.weixin.qq.com/s/jK56sZkuvXhwEfxKOKTCaQ

protected,对于子类是可访问的,不管是不是同包,对于外包的非子类是不可见的。
包访问权限 default 对于同包的类都可以访问,对于外包,不管是不是子类都不能访问。
抽象访问的访问权限有一个限制,就是不能是私有的,不然子类无法去继承继承实现抽象方法。
接口内的访问权限默认都是public的,不需要我们再去写。
编译时与运行时
方法重载:方法名相同,根据参数类型,个数的不同来确定使用哪个方法,在编译时就已经确定了,编译时多态。
方法覆盖:继承或实现接口时对方法进行覆盖。只有在运行时才能确定,运行时多态。
transient 关键字
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。
java 初始化顺序
在 Java 语言中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建象。
初始化一般遵循3个原则:
静态对象(变量)优先于非静态对象(变量)初始化,静态对象(变量)只初始化一次,而非静态对象(变量)可能会初始化多次;
父类优先于子类进行初始化;
按照成员变量的定义顺序进行初始化。 即使变量定义散布于方法定义之中,它们依然在任何方法(包括构造函数)被调用之前先初始化;
加载顺序
父类(静态变量、静态语句块)
子类(静态变量、静态语句块)
父类(实例变量、普通语句块)
父类(构造函数)
子类(实例变量、普通语句块)
子类(构造函数)
实例
class Base {
// 1.父类静态代码块
static {
System.out.println("Base static block!");
}
// 3.父类非静态代码块
{
System.out.println("Base block");
}
// 4.父类构造器
public Base() {
System.out.println("Base constructor!");
}
}
public class Derived extends Base {
// 2.子类静态代码块
static{
System.out.println("Derived static block!");
}
// 5.子类非静态代码块
{
System.out.println("Derived block!");
}
// 6.子类构造器
public Derived() {
System.out.println("Derived constructor!");
}
public static void main(String[] args) {
new Derived();
}
}
结果是:
Base static block!
Derived static block!
Base block
Base constructor!
Derived block!
Derived constructor!
字节与字符的区别
理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:
| 类型 | 概念描述 | 举例 |
|---|---|---|
| 字符 | 人们使用的记号,抽象意义上的一个符号。 | '1', '中', 'a', '$', '¥', …… |
| 字节 | 计算机中存储数据的单元,一个 8 位的二进制数,是一个很具体的存储空间。 | 0x01, 0x45, 0xFA, …… |
| ANSI 字符串 | 在内存中,如果“字符”是以 ANSI 编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为 ANSI 字符串或者多字节字符串。 | "中文123" (占7字节) |
| UNICODE 字符串 | 在内存中,如果“字符”是以在 UNICODE 中的序号存在的,那么我们称这种字符串为 UNICODE 字符串或者宽字节字符串。 | L"中文123" (占10字节) |
字节与字符区别
它们完全不是一个位面的概念,所以两者之间没有“区别”这个说法。不同编码里,字符和字节的对应关系不同:
| 类型 | 概念描述 |
|---|---|
| ASCII | 一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单元,一般为 8 位二进制数,换算为十进制。最小值 0,最大值 255。 |
| UTF-8 | 一个英文字符等于一个字节,一个中文(含繁体)等于三个字节 |
| Unicode | 一个英文等于两个字节,一个中文(含繁体)等于两个字节。符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占 1 个字节的大小,中文句号“。”占 2 个字节的大小。 |
| UTF-16 | 一个英文字母字符或一个汉字字符存储都需要 2 个字节(Unicode扩展区的一些汉字存储需要4个字节) |
| UTF-32 | 世界上任何字符的存储都需要 4 个字节 |
参考资料:字符,字节和编码 - Characters, Bytes And Encoding
深拷贝与浅拷贝
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
换言之,浅拷贝仅仅复制所拷贝的对象,而不复制它所引用的对象。

深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

参考资料
细说 Java 的深拷贝和浅拷贝 - 承香墨影 - SegmentFault 思否
(基础系列)object clone 的用法、原理和用途 - 掘金
Java的四个基本特性,对多态的理解,在项目中哪些地方用到多态
Java的四个基本特性
抽象:
抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
封装:
通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。
我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
继承:
继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
多态:
多态性是指允许不同子类型的对象对同一消息作出不同的响应。
多态的理解(多态的实现方式)
方法重载(overload):实现的是编译时的多态性(也称为前绑定)。
方法重写(override):实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西。
要实现多态需要做两件事:
方法重写(子类继承父类并重写父类中已有的或抽象的方法);
对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
项目中对多态的应用
举一个简单的例子,在物流信息管理系统中,有两种用户:订购客户和卖房客户,两个客户都可以登录系统,
他们有相同的方法 Login,但登陆之后他们会进入到不同的页面,也就是在登录的时候会有不同的操作,两种客
户都继承父类的 Login 方法,但对于不同的对象,拥有不同的操作。
面相对象开发方式优点(B65)
较高的开发效率:可以把事物进行抽象,映射为开发的对象。
保证软件的鲁棒性:高重用性,可以重用已有的而且在相关领域经过长期测试的代码。
保证软件的高可维护性:代码的可读性非常好,设计模式也使得代码结构清晰,拓展性好。
什么是重载和重写
重载:重载发生在同一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载。
重写:重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。
根据不同的子类对象确定调用的那个方法。

面向对象开发的六个基本原则,在项目中用过哪些原则
六个基本原则(参考《设计模式之禅》)
单一职责(Single Responsibility Principle 简称 SRP):
一个类应该仅有一个引起它变化的原因。在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一职责。
里氏替换(Liskov Substitution Principle 简称 LSP):
任何时候子类型能够替换掉它们的父类型。子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多的对象当成能力少的对象来用当然没有任何问题。
依赖倒置(Dependence Inversion Principle 简称 DIP):
要依赖于抽象,不要依赖于具体类。要做到依赖倒置,应该做到:①高层模块不应该依赖底层模块,二者都应该依赖于抽象;②抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
接口隔离(Interface Segregation Principle 简称 ISP):
不应该强迫客户依赖于他们不用的方法 。接口要小而专,绝不能大而全。臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高度内聚的。
最少知识原则(Least Knowledge Principle 简称 LKP):
只和你的朋友谈话。迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。
开闭原则(Open Closed Principle 简称 OCP):
软件实体应当对扩展开放,对修改关闭。要做到开闭有两个要点:
①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;
②封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而换乱。
其他原则
合成聚和复用:优先使用聚合或合成关系复用代码
面向接口编程
优先使用组合,而非继承
一个类需要的数据应该隐藏在类的内部
类之间应该零耦合,或者只有传导耦合,换句话说,类之间要么没关系,要么只使用另一个类的接口提供的操作
在水平方向上尽可能统一地分布系统功能
项目中用到的原则
单一职责、开放封闭、合成聚合复用(最简单的例子就是String类)、接口隔离
instanceof
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
public class Main {
public static void main(String[] args) {
Object testObject = new ArrayList();
displayObjectClass(testObject);
}
public static void displayObjectClass(Object o) {
if (o instanceof Vector)
System.out.println("对象是 java.util.Vector 类的实例");
else if (o instanceof ArrayList)
System.out.println("对象是 java.util.ArrayList 类的实例");
else
System.out.println("对象是 " + o.getClass() + " 类的实例");
}
}
hashcode,==,equals
概念
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
View Code说明:
String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
hashCode()介绍
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode
我们先以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与该位置其他已经加入的对象的 hashcode 值作比较,
如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。
但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。
如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head first java》第二版)。
这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
通过我们可以看出:hashCode() 的作用就是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。
这个哈希码的作用是确定该对象在哈希表中的索引位置。**hashCode()在散列表中才有用,在其它情况下没用**。
在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
hashCode()与 equals()的相关规定
如果两个对象相等,则 hashcode 一定也是相同的
两个对象相等,对两个对象分别调用 equals 方法都返回 true
两个对象有相同的 hashcode 值,它们也不一定是相等的
因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
Java hashCode() 和 equals()的若干问题解答
位运算符
Java 定义了位运算符,应用于整数类型 (int),长整型 (long),短整型 (short),字符型 (char),和字节型 (byte)等类型。
下表列出了位运算符的基本运算,假设整数变量A的值为60和变量B的值为13
A(60):0011 1100
B(13):0000 1101
| 操作符 | 名称 | 描述 | 例子 |
|---|---|---|---|
| & | 与 | 如果相对应位都是 1,则结果为 1,否则为 0 | (A&B)得到 12,即 0000 1100 |
| | | 或 | 如果相对应位都是 0,则结果为 0,否则为 1 | (A|B)得到 61,即 0011 1101 |
| ^ | 异或 | 如果相对应位值相同,则结果为 0,否则为 1 | (A^B)得到49,即 0011 0001 |
| 〜 | 非 | 按位取反运算符翻转操作数的每一位,即 0 变成 1,1 变成 0 | (〜A)得到-61,即1100 0011 |
| << | 左移 | (左移一位乘2)按位左移运算符。左操作数按位左移右操作数指定的位数。左移 n 位表示原来的值乘 2n | A << 2得到240,即 1111 0000 |
| >> | (右移一位除2)有符号右移,按位右移运算符。左操作数按位右移右操作数指定的位数 | A >> 2得到15即 1111 | |
| >>> | 无符号右移 | 无符号右移,按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充 | A>>>2得到15即0000 1111 |
String有重写Object的hashcode和toString吗?如果重写equals不重写hashcode会出现什么问题?
String 有重写 Object 的 hashcode 和 toString吗?
String 重写了 Object 类的 hashcode 和 toString 方法。
当 equals 方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相对等的两个对象必须有相同的 hashCode
object1.equals(object2) 时为 true, object1.hashCode() == object2.hashCode() 为 true
object1.hashCode() == object2.hashCode() 为 false 时,object1.equals(object2) 必定为 false
object1.hashCode() == object2.hashCode() 为 true时,但 object1.equals(object2) 不一定定为 true
重写 equals 不重写 hashcode 会出现什么问题
在存储散列集合时(如 Set 类),如果原对象.equals(新对象),但没有对 hashCode 重写,即两个对象拥有不同的 hashCode,则在集合中将会存储两个值相同的对象,从而导致混淆。因此在重写 equals 方法时,必须重写 hashCode 方法。
原码、补码、反码是什么
机器数
一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号,正数为 0,负数为 1。
比如,十进制中的数 +3 ,计算机字长为 8 位,转换成二进制就是 00000011。如果是 -3 ,就是 10000011 。那么,这里的 00000011 和 10000011 就是机器数。
真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位 1 代表负,其真正数值是 -3 而不是形式值 131(10000011 转换成十进制等于 131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001 的真值 = +000 0001 = +1,1000 0001 的真值 = –000 0001 = –1
原码
原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。比如如果是 8 位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符号位。因为第一位是符号位,所以 8 位二进制数的取值范围就是:[1111 1111 , 0111 1111],即:[-127 , 127]
原码是人脑最容易理解和计算的表示方式
反码
反码的表示方法是:
正数的反码是其本身;
负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原= [11111110]反
可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算。
补码
补码的表示方法是:
正数的补码就是其本身;
负数的补码是在其原码的基础上,符号位不变,其余各位取反, 最后+1。(反码的基础上 +1)
[+1] = [0000 0001]原 = [0000 0001]反 = [0000 0001]补
[-1] = [1000 0001]原 = [1111 1110]反 = [1111 1111]补
对于负数,补码表示方式也是人脑无法直观看出其数值的。 通常也需要转换成原码在计算其数值。
参考资料:
原码, 反码, 补码 详解 - ziqiu.zhang - 博客园
正确使用 equals 方法
Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
举个例子:
// 不能使用一个值为null的引用类型变量来调用非静态方法,否则会抛出异常
String str = null;
if (str.equals("SnailClimb")) {
...
} else {
..
}
运行上面的程序会抛出空指针异常,但是我们把第二行的条件判断语句改为下面这样的话,就不会抛出空指针异常,else 语句块得到执行。:
"SnailClimb".equals(str);// false
不过更推荐使用 java.util.Objects#equals(JDK7 引入的工具类)。
Objects.equals(null,"SnailClimb");// false
我们看一下java.util.Objects#equals的源码就知道原因了。
public static boolean equals(Object a, Object b) {
// 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。
return (a == b) || (a != null && a.equals(b));
}
Reference:Java中equals方法造成空指针异常的原因及解决方案
每种原始类型都有默认值一样,如int默认值为 0,boolean 的默认值为 false,null 是任何引用类型的默认值,不严格的说是所有 Object 类型的默认值。
可以使用 == 或者 != 操作来比较null值,但是不能使用其他算法或者逻辑操作。在Java中null == null将返回true。
不能使用一个值为null的引用类型变量来调用非静态方法,否则会抛出异常
参考:


浙公网安备 33010602011771号