11.字符编码
System.out.println("系统默认编码:"+
System.getProperty("file.encoding");//获取本地系统默认编码
//系统默认编码:UTF-8 (从JDK1.9起)
ISO8859-1:一种国际通用的单字节编码,最多只能表示0~255的字符范围,主要在英文传输中使用;
GBK/GB2312:中文的国标编码,专门用来表示汉字,是双字节编码,如果在此编码器中出现了中文,则使用ISO8859-1编码。GBK可以表示简体中文和繁体中文,而GB2312只能表示简体中文,GBK兼容GBK2312;
UNICODE:十六进制编码,可以准确地表示出任何语言文字,此编码不兼容ISO8859-1编码;
UTF-8编码:由于UNICODE不支持ISO8859-1编码,而且占用空间较多,英文字母也需要双字节去编码,不便于存储和传输,于是产生了UTF编码。UTF兼容ISO8859-1编码,同时也可以用来表示所有语言字符,但是UTF编码是不定长的编码,每一个字符的长度从1~6个字节不等,一般在中文网页使用此编码,可以节省空间。
12.String.intern()
https://blog.csdn.net/tyyking/article/details/82496901
new String
都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。
JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。
Q:下列程序的输出结果:
String s1 = “abc”;
String s2 = “abc”;
System.out.println(s1 == s2);
A:true,均指向常量池中对象。
Q:下列程序的输出结果:
String s1 = new String(“abc”);
String s2 = new String(“abc”);
System.out.println(s1 == s2);
A:false,两个引用指向堆中的不同对象。
Q:下列程序的输出结果:
String s1 = “abc”;
String s2 = “a”;
String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);
A:false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。
Q:下列程序的输出结果:
String s1 = “abc”;
final String s2 = “a”;
final String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);
A:true,因为final变量在编译后会直接替换成对应的值,所以实际上等于s4=”a”+”bc”,而这种情况下,编译器会直接合并为s4=”abc”,所以最终s1==s4。
Q:下列程序的输出结果:
String s = new String(“abc”);
String s1 = “abc”;
String s2 = new String(“abc”);
System.out.println(s == s1.intern());
System.out.println(s == s2.intern());
System.out.println(s1 == s2.intern());
A:false,false,true。
13.类加载的准备阶段
https://www.cnblogs.com/chanshuyi/p/jvm_serial_07_jvm_class_loader_mechanism.html
当完成字节码文件的校验之后,JVM 便会开始为类变量分配内存并初始化。这里需要注意两个关键点,即内存分配的对象以及初始化的类型。
内存分配的对象:
Java 中的变量有「类变量
」和「类成员变量
」两种类型,「类变量」指的是被 static 修饰的变量,而其他所有类型的变量都属于「类成员变量」。在准备阶段,JVM 只会为「类变量」分配内存,而不会为「类成员变量」分配内存。「类成员变量」的内存分配需要等到初始化阶段才开始。
初始化的类型:
在准备阶段,JVM 会为类变量
分配内存,并为其初始化。但是这里的初始化指的是为变量赋予 Java 语言中该数据类型的零值,而不是用户代码里初始化的值。但如果一个变量是常量(被 static final 修饰)的话,那么在准备阶段,属性便会被赋予用户希望的值。
14. Java类体中只能有变量定义和成员方法的定义,不能有单独的语句,要放在方法体里
在Java 中创建类之后,还需要为类添加类体,类体主由成员变量和方法两部分组成。
在Java 的类中定义成员变量和方法的类声明格式如下:
其中:
. 成员变量名1、成员变量名2……成员变量名n 是类的成员变量,数据类型可以是基本的数据类型,也可以是对象类型。
. 成员方法名1 是类的成员方法,返回值类型是通过该方法获得值的数据类型,方法体是该方法要执行的语句,返回值就是调用该方法得到的值。
. 成员方法名2 是类的成员方法,void 表示该方法没有返回值,方法体是该方法要执行的语句。
15.类加载相关
https://www.cnblogs.com/chanshuyi/p/jvm_serial_07_jvm_class_loader_mechanism.html
对于静态字段,只有直接定义这个字段的类才会被初始化(执行静态代码块)。因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。
一个类的执行顺序
大概可以按照如下步骤:
1.确定类变量的初始值。
在类加载的准备阶段,JVM 会为类变量初始化零值,这时候类变量会有一个初始的零值。如果是被 final 修饰的类变量,则直接会被初始成用户想要的值。
2.初始化入口方法。
当进入类加载的初始化阶段后,JVM 会寻找整个 main 方法入口,从而初始化 main 方法所在的整个类。当需要对一个类进行初始化时,会首先初始化类构造器(),之后初始化对象构造器()。
3.初始化类构造器。
JVM 会按顺序收集类变量的赋值语句、静态代码块,最终组成类构造器由 JVM 执行。
4.初始化对象构造器。
JVM 会按照收集成员变量的赋值语句、普通代码块,最后收集构造方法,将它们组成对象构造器,最终由 JVM 执行。
如果在初始化 main 方法所在类的时候遇到了其他类的初始化,那么就先加载对应的类,加载完成之后返回。如此反复循环,最终返回 main 方法所在类。