概念简述
- Java语言:跨平台的语言(write once ,run anywhere)
- 当Java源代码成功编译成字节码后,如果想在不同的平台上面运行,则无须再次编译
- 这个优势不再那么吸引人了。Python、PHP、 Perl、Ruby、 Lisp等有强大的解释器。
- 跨平台似乎已经快成为一门语言必选的特性。
- Java语言:跨平台的语言(write once ,run anywhere)
- 当Java源代码成功编译成字节码后,如果想在不同的平台上面运行,则无须再次编译这个优势不再那么吸引人了。Python、PHP、 Perl、Ruby、 Lisp等有强大的解释器。跨平台似乎已经快成为一门语言必选的特性。
- 想要让一个Java程序正确地运行在JVM中,Java源码就必须要被编译为符合JVM规范的字节码。
- 前端编译器的主要任务就是负责将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件。
- javac是一种能够将Java源码编译为字节码的前端编译器。
- Javac编译器在将Java源码编译为一个有效的字节码文件过程中经历了4个步骤,分别是词法解析、语法解析、语义解析以及生成字节码。
-
泛泛地讲,一个语言是否高效(程序性能是否高效),跟语言本身并没有太大关系,主要有关系的是编译器,java语言最初没有编译器,只有解释器,逐行执行的解释器效率较低,后期引入了JIT编译器(热点代码探测技术),大大提升了效率
-
前端编译器vs后端编译器
- Java源代码的编译结果是字节码,那么肯定需要有一种编译器能够将Java源码编译为字节码,承担这个重要责任的就是配置在path环境变量中的javac编译器
- javac是一种能够将Java源码编译为字节码的前端编译器。
- HotSpot VM并没有强制要求前端编译器只能使用javac来编译字节码,其实只要编译结果符合JVM规范都可以被JVM所识别即可。在Java的前端编译器领域,除了javac之外,还有一种被大家经常用到的前端编译器,那就是内置在Eclipse中的ECJ (EclipseCompiler for Java)编译器。和Javac的全量式编译不同,ECJ是一种增量式编译器。
- 在Eclipse中,当开发人员编写完代码后,使用“Ctrl+S”快捷键时,ECJ编译器所采取的编译方案是把未编译部分的源码逐行进行编译,而非每次都全量编译。因此ECJ的编译效率会比javac更加迅速和高效,当然编译质量和javac相比大致还是一样的。
- ECJ不仅是Eclipse的默认内置前端编译器,在Tomcat中同样也是使用ECJ编译器来编译jsp文件。由于ECJ编译器是采用
- 默认情况下, IntelliJ IDEA 使用 javac编译器。(还可以自己设置为AspectJ编译器ajc)
- 前端编译器并不会直接涉及编译优化等方面的技术,而是将这些具体优化细节移交给HotSpot的JIT编译器负责。
Integer和String 相等判断
Integer
Integer有一个缓存,范围为-128~127,Integer i1 = 10,在字节码中实质是调用了Integer.valueOf,在此范围内返回的就是IntergerCache中的Integer对象,否则会返回新的Integer对象
- true
- i1和i2在缓存范围内,使用的是内部缓存数组中的同一个对象,返回true
Integer i1 = 10; Integer i2 = 10; System.out.println(i1 == i2);//true - false
- i3和i4不在缓存范围内,他们是两个对象,false
Integer i3 = 128; Integer i4 = 128; System.out.println(i3 == i4);//false - true
- Integer x = 5;调用了调用Integer.valueOf 从数组中返一个Integer对象
- y 是 基本数据类型的5
- x == y的比较虽然表面看是引用类型和基本类型的比较,但是x会自动拆箱为基本数据类型,最终的比较就是x和y的值进行的比较,显然相等,true
Integer x = 5;//调用Integer.valueOf 从数组中返回 int y = 5; // 基本数据5 System.out.println(x == y);//true
String
- false
- str是通过StringBuilder通过append拼接之后toString返回的一个字符串,而StringBuilder的toString实质上就是new了一个String
- str1是通过字面量方式加载到内存,保存到局部变量表中
- 所以str 不等于 str1
String str = new String("hello") + new String("world"); String str1 = "helloworld"; System.out.println(str == str1);//false - false: 两个new的String不相等
String str2 = new String("helloworld"); System.out.println(str == str2);//false
类初始化相关
- new Son()先执行父类Father的构造函数
- Father的构造函数调用了print(),由于子类重写了,所以打印的是子类son的x,从字节码看,这个时候x为0
- 然后Son调用自己的构造器,此时x为30
- 最后Father的x被赋值为20,f.x = 20
- 实测如果 Son f = new Son(); 那么f.x = 40
- 打印结果为
Son.x = 0 Son.x = 30 20 - 成员变量(非静态的)的赋值过程
- ① 默认初始化
- ② 显式初始化 /代码块中初始化
- ③ 构造器中初始化
- ④ 有了对象之后,可以“对象.属性”或"对象.方法"的方式对成员变量进行赋值。
class Father {
int x = 10;
public Father() {
this.print();
x = 20;
}
public void print() {
System.out.println("Father.x = " + x);
}
}
class Son extends Father {
int x = 30;
// float x = 30.1F;
public Son() {
this.print();
x = 40;
}
public void print() {
System.out.println("Son.x = " + x);
}
}
public class SonTest {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.x);
}
}