整型
浮点
char
一个 char 肯定只有2个字节,但一个字符可能有2个或4个字节。
Unicode
Universal Character Set,缩写 UCS,俗称 Unicode,是ISO制定的字符集,包含世界上所有文字和符号。
Unicode字符分为17组,每组65536个,总共1114112个。第1组称为BMP,其余16组称为SP。
代码点(Code Point) 俗称“码点”,是 Unicode 代码空间中的一个值,代表一个字符,比如 “知” 对应码点 U+77E5。
UTF
UCS Transformation Format,缩写 UTF,俗称“编码规则”。
UTF 有3套方案,区别在于存储空间、编码效率的取向:
UTF-8 变长8位。用8位的倍数位来表示一个字符,可以是8位、16位、24位、32位(4个字节),取向存储空间;
UTF-16 变长16位,可以是16位、32位。存储空间、编码效率的折中,是 java 默认编码规则;
UTF-32 定长32位,可以表示 16777216 个字符,远超 Unicode 1114112 个,取向编码效率。
代码单元(Code Unit) 在具体编码形式中的最小单位。比如 UTF-16 中一个代码单元为 16 bits,UTF-8 中一个代码单元为 8 bits。
char 装不下怎么办
char 真实的含义是描述了UTF-16编码中的一个代码单元,而不是一个字符。
当字符用 UTF-16 编码后,一个 char 存不下,那就只能使用两个char来表示一个字符。
其中 U+D800 ~ U+DBFF 用于第一个代码单元,U+DC00 ~ U+DFFF 用于第二个代码单元。
Java 可以很迅速的知道这个char是代表一个字符的编码,还是一个字符的第一部分或者第二部分。对于需要用两个char来表示的字符,单拿出任何一个char来看都是没有意义的,他们也不会映射任何Unicode的字符集,属于保留的空闲区域。当然也正是这样保留区域的设计才使得变长编码变得可能。
String
常量池(constant pool)在编译期被确定,并保存于.class文件中,主要分为Class文件常量池、运行时常量池、全局字符串常量池、基本类型包装类对象常量池。
JDK6 常量池位于持久代,很容易被 String#intern() 撑爆 Perm 导致内存溢出。
JDK7 常量池位于 Java Heap,相对没那么容易溢出。常量池在运行期被装载到 Java Heap,并且可以扩充。
String#intern() 就是扩充常量池的一个方法:当在常量池中找不到字符串常量时,1.7前 intern() 会拷贝一个副本到常量池,但从1.7开始,只是在常量池创建一个引用指向堆上的字符串对象。
String s1 = "tom";
String str = s1 + "cat";
System.out.println(str == "tomcat"); // false
拼接 字符串常量 和 变量,编译器会调用StringBuilder.append()在堆上创建新的对象。
final String s1 = "tom";
String str = s1 + "cat";
System.out.println(str == "tomcat"); // true
拼接 字符串字面量 和 常量,编译阶段直接会合成为一个字符串。
String str = "tom" + "cat";
System.out.println(str == "tomcat"); // true
拼接 字符串字面量,编译阶段直接会合成为一个字符串。
String s1 = "tomcat";
String s2 = new String(s1);
System.out.println(s1 == s2); // false
常量池已有 “tomcat” ,但 new 都会在堆上创建一个新的字符串对象,因为要满足 immutable。
String s1 = new String("tomcat");
s1.intern();
String s2 = "tomcat";
System.out.println(s1 == s2); // false
执行 s1.intern(),常量池已经有 “tomcat”,s1存在与堆上。
赋值 s2,拿到常量池中的对象,肯定不等于s1。
String s1 = new String("tom") + new String("cat");
s1.intern();
String s2 = "tomcat";
System.out.println(s1 == s2); // true
执行 s1.intern(),常量池没有 “tomcat”,于是在常量池中生成一个对堆中 “tomcat” 的引用。
赋值 s2,拿到常量池中对堆 “tomcat” 的引用,所以 s1 == s2。
enum
声明一个枚举 Season:
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
经过编译后:
public abstract class Season { // 生成和枚举同名的抽象类
Season SPRING = new Season$1();
Season SUMMER = new Season$2();
Season AUTUMN = new Season$3();
Season WINTER = new Season$4();
final class Season$1 extends Season { // 真正的实现类
Season$1("SPRING", 0); // 调用 Enum类 的构造方法
}
// 其它内部类同理
Season[] $VALUES = new Season[4]; // values() 会用上
$VALUES[0] = SPRING;
$VALUES[1] = SUMMER;
$VALUES[2] = AUTUMN;
$VALUES[3] = WINTER;
public static Season[] values(){
return (Season[]) $VALUES.clone(); // 利用了 $VALUES
}
public static Season valueOf(String s){
return (Season) Enum.valueOf(Season.class, s); // Enum.valueOf 的本质是反射
}
}
枚举 经过编译后生成 同名抽象类,抽象类的 内部类 才是真正实现类。
Enum#clone 用 final 修饰并禁止克隆,Enum#readObject 禁止反序列化复制枚举,从而保证了枚举变量是单例的。
Exception
如果 catch 中 return 了,finally 还会执行吗?从指令的角度解释