深色模式
Java 基础知识
基本数据类型
8种基本数据类型
byte
short
int
long
float
double
char
boolean
自动拆箱
自动拆箱的场景:
- 包装类型赋值给基本类型
- 包装类型与基本类型比较、运算
- 包装类型之间进行运算
自动装箱
自动装箱的场景:
- 基本类型赋值给包装类型。
自动装箱实际上调用了valueOf()
方法,该方法涉及到装箱缓存机制。
装箱缓存机制
Byte
、 Short
、 Integer
、 Long
、 Character
、 Boolean
装箱时,会优先使用常量池缓存:
java
// 数值区间在 -128 至 +127,会使用常量池中的缓存包装类型对象
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
// 数值区间在 -128 至 +127,会使用常量池中的缓存包装类型对象
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
}
// 数值区间在 -128 至 +127,会使用常量池中的缓存包装类型对象
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// 数值区间在 -128 至 +127,会使用常量池中的缓存包装类型对象
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
// 数值区间在 0 至 +127,会使用常量池中的缓存包装类型对象
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
// 值为 true 或 false,会使用常量池中的缓存包装类型对象
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
Integer的缓存范围可以通过调整JVM参数修改,其它几种的缓存范围,不可以修改。
装箱缓存将会产生以下效果:
java
Integer a = 10;
Integer b = 10;
assert(a == b); // true
Integer c = 200;
Integer d = 200;
assert(c == d); // false
如果不想使用缓存,可以使用new
关键字创建对象,此时不会自动装箱。
java
Integer a = new Integer(10);
Integer b = new Integer(10);
assert(a == b); // false
String
类
String
不可变的好处
- 可以实现字符串常量池。
- 不可变所以线程安全。
String
被用作标识符、url、path等,不可变就避免了安全问题。- 不可变所以不必重新计算hashcode,方便用作
HashMap
的key。
StringBuilder
与 StringBuffer
StringBuilder
不是线程安全的,但是效率高。StringBuffer
线程安全,效率低。
字符串常量池
参考:https://www.cnblogs.com/Andya/p/14067618.html
通过以下示例来理解常量池的行为:
java
String str1 = "123";
String str2 = "123";
String str3 = "123";
String str4 = new String("123");
String str5 = new String("123");
String str6 = new String("123");
assert(str1 == str2); // true
assert(str2 == str3); // true
assert(str3 == str4); // false
assert(str4 == str5); // false
assert(str5 == str6); // false
在JVM中的存储如下:
String.intern()
方法
intern()
方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
Java 关键字
final
关键字
final
变量不可以重新赋值
final
方法不可以被重写
final
类不可以被继承
static
关键字
static
变量属于类,只有1份
static
方法不能是抽象的
static
代码块在类初始化时执行1次
static
内部类可以直接创建其对象,而非 static
内部类必须依赖外部类的实例才可以创建对象。
对象初始化顺序
- 父类(静态变量、静态语句块)
- 子类(静态变量、静态语句块)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)
Object类
clone()
默认为浅克隆。
没有实现Clonable
接口的类,调用clone()
方法会抛出异常。
实现深克隆的方式
第一种方式:内部未使用引用类型,或引用类型都实现了Cloneable
接口。
第二种方式:实现Serializable
接口,通过序列化深克隆。
toString()
wait()
必须在同步代码中调用,即必须持有锁。
会释放锁。
notify()
必须在同步代码中调用,即必须持有锁。
随机唤醒一个线程。
notifyAll()
必须在同步代码中调用,即必须持有锁。
唤醒所有线程。
equals()
比较两个对象是否”相同“,如果”相同“,则这两个对象的 hashCode 应该相等。
==
和 equals()
==
:对于基本类型,比较值。对于引用类型,比较引用的内存地址。
equals()
:默认使用了 ==
,实际上,我们重写该方法用来比较对象值是否相同。
hashCode()
hashCode 相等, equals()
不一定为 true
。
finalize()
对象被GC前,调用该方法。
getClass()
获取该对象的类型的对象。(每个类型都有一个与之对应的 Class
对象。)
Java 抽象类与接口
抽象类
不能直接实例化
可以没有抽象方法,但有抽象方法的类就必须声明为抽象
接口
不能直接实例化
只能有抽象方法,但Java 8以后可以有默认方法实现
接口的成员默认为 public
,也只能是public
。
接口的字段默认是 static final
的。
Java 异常
继承关系
Throwable
Error
:错误错误是应用程序无法处理的。
Exception
:异常RuntimeException
:运行时异常IOException
:非运行时异常
异常的分类
可查异常(非运行时异常)
除了
RuntimeException
及其子类,其它异常都是可察异常要么
try-catch
捕获异常,要么throws
抛出异常不可查异常(运行时异常)
运行时异常
RuntimeException
及其子类
异常处理机制
对于一个可查异常,要么抛出,要么捕获。
捕获异常( try-catch-finally
)
try
:用于捕获异常。其后可接零个或多个catch
块,如果没有catch
块,则必须跟一个finally
块。catch
:用于处理try
捕获到的异常。finally
:无论是否捕获或处理异常,finally
块里的语句都会被执行。注意:当在try
块或catch
块中遇到return
语句时,finally
语句块将在方法返回之前被执行。
抛出异常
throws
:在方法声明中抛出异常,可抛出多个。throw
:在方法体中抛出异常对象。
Java 泛型
三种泛型
泛型类
静态方法无法访问类上定义的泛型。
泛型方法
优先使用泛型方法而不是泛型类去解决问题。
泛型接口
泛型擦除
- 泛型参数只保留到编译时期。
- 泛型参数在运行时会被擦除。
- 擦除到该泛型的第一个边界,由
extends
定义,如果没有extends
则边界就是Object
类型。
泛型通配符
参考:深入理解Java泛型 -- 掘金https://juejin.im/post/5b614848e51d45355d51f792#heading-13
- 上界通配符
<? extends T>
- 下界通配符
<? super T>
<?>
无限通配符
Java 反射
什么是Java反射
Java 反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种 动态的获取信息 以及 动态调用对象的方法 的功能称为 java 的反射机制。
使用反射获取类的信息
- 反射需要用到的几个类:
Class
,Field
,Method
,Constructor
。 - 获取类名
getName()
- 获取所有public变量
getFields()
- 获取所有声明的变量(包括
public
与非public
)getDeclaredMethods()
- 获取某个
public
变量getField(String name)
- 获取某个声明的变量
getDeclaredField(String name)
- 获取访问修饰符
getModifiers()
- 获取所有
public
方法getMethods()
- ...
使用反射访问类的私有属性
参考:Java反射由浅入深 -- 掘金https://juejin.im/post/598ea9116fb9a03c335a99a4
- 访问私有方法
- 修改私有变量
- 修改私有常量