整型

// TODO
装箱。-127~128

浮点

// TODO
Float、Double 在内存中如何存储

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开始,只是在常量池创建一个引用指向堆上的字符串对象。

  1. String s1 = "tom";
  2. String str = s1 + "cat";
  3. System.out.println(str == "tomcat"); // false

拼接 字符串常量 和 变量,编译器会调用StringBuilder.append()在堆上创建新的对象。

  1. final String s1 = "tom";
  2. String str = s1 + "cat";
  3. System.out.println(str == "tomcat"); // true

拼接 字符串字面量 和 常量,编译阶段直接会合成为一个字符串。

  1. String str = "tom" + "cat";
  2. System.out.println(str == "tomcat"); // true

拼接 字符串字面量,编译阶段直接会合成为一个字符串。

  1. String s1 = "tomcat";
  2. String s2 = new String(s1);
  3. System.out.println(s1 == s2); // false

常量池已有 “tomcat” ,但 new 都会在堆上创建一个新的字符串对象,因为要满足 immutable。

  1. String s1 = new String("tomcat");
  2. s1.intern();
  3. String s2 = "tomcat";
  4. System.out.println(s1 == s2); // false

执行 s1.intern(),常量池已经有 “tomcat”,s1存在与堆上。
赋值 s2,拿到常量池中的对象,肯定不等于s1。

  1. String s1 = new String("tom") + new String("cat");
  2. s1.intern();
  3. String s2 = "tomcat";
  4. System.out.println(s1 == s2); // true

执行 s1.intern(),常量池没有 “tomcat”,于是在常量池中生成一个对堆中 “tomcat” 的引用。
赋值 s2,拿到常量池中对堆 “tomcat” 的引用,所以 s1 == s2。

enum

声明一个枚举 Season:

  1. public enum Season {
  2. SPRING, SUMMER, AUTUMN, WINTER;
  3. }

经过编译后:

  1. public abstract class Season { // 生成和枚举同名的抽象类
  2. Season SPRING = new Season$1();
  3. Season SUMMER = new Season$2();
  4. Season AUTUMN = new Season$3();
  5. Season WINTER = new Season$4();
  6. final class Season$1 extends Season { // 真正的实现类
  7. Season$1("SPRING", 0); // 调用 Enum类 的构造方法
  8. }
  9. // 其它内部类同理
  10. Season[] $VALUES = new Season[4]; // values() 会用上
  11. $VALUES[0] = SPRING;
  12. $VALUES[1] = SUMMER;
  13. $VALUES[2] = AUTUMN;
  14. $VALUES[3] = WINTER;
  15. public static Season[] values(){
  16. return (Season[]) $VALUES.clone(); // 利用了 $VALUES
  17. }
  18. public static Season valueOf(String s){
  19. return (Season) Enum.valueOf(Season.class, s); // Enum.valueOf 的本质是反射
  20. }
  21. }

枚举 经过编译后生成 同名抽象类,抽象类的 内部类 才是真正实现类。
Enum#clone 用 final 修饰并禁止克隆,Enum#readObject 禁止反序列化复制枚举,从而保证了枚举变量是单例的。

Exception

如果 catch 中 return 了,finally 还会执行吗?从指令的角度解释

参考文献