看了视频,讲师就只说“编译看左边,运行看右边”,讲的跟玄学似的;我可不能那么肤浅!于是看了很多博客,现摘下来做个整合,整我一头汗,图书馆还不开空调,热死我了@H_502_1@
JVM
-
Java源代码被编译器编译成class文件(不是底层操作系统可以直接执行的二进制指令)。因此,我们需要一种平台可以解释class文件并运行它。而做到这一点的正是JVM@H_502_1@
-
实际上,JVM是一种解释执行class文件的规范技术,各个提供商都可以根据规范,在不同的底层平台上实现不同的JVM@H_502_1@
-
JVM实现的基本结构图
-
运行时数据区@H_502_1@
当JVM运行一个程序时,需要在内存存储许多东西。比如字节码、程序创建的对象、传递的方法参数、返回值、局部变量等等。JVM会把这些东西都组织到几个"运行时数据区"中便于管理@H_502_1@
多态引入
对象的转型
- 向上转型upcasting
- 向下转型downcasting(造型)
- obj instanceof T
对象的类型
- 编译时类型
- 运行时类型
- 运行时类型由实际赋给该变量的对象决定
- 若编译时类型和运行时类型不一致,就出现了对象的多态性
绑定
静态绑定
- 静态绑定过程
@H_502_1@
- 如果一个方法有static、private、final修饰或者是构造方法,那就都是"前期绑定"
- 所有的静态方法都是"前期绑定",因为静态方法可以通过类名进行访问,而不会用到引用的对象的实际类型信息,因此在编译时就可以通过类型信息确定是哪一个具体的方法
- 成员变量也属于"前期绑定"
- 总结调用一个方法的过程(摘自Java核心技术卷1)
- 编译器查看对象的声明类型和方法名。假设调用x.f(param),且隐式参数 x 声明为 Father 类的对象。需要注意的是:有可能存在多个名字为 f,但参数类型不一样的方法。例如,可能存在方法f(int)和方法f(String)。编译器将会一一列举所有Father类中名为f的方法和其超类中访问属性为 public 且名为 f 的方法(超类的私有方法不可访问)。至此,编译器已获得所有可能被调用的候选方法
- 接下来,编译器将查看调用方法时提供的参数类型。如果在所有名为 f 的方法中存在一个与提供类型完全匹配,就选择这个方法。这个过程被称为重载解析。例如:对于调用x.f("Hello")来说,编译器将挑选出f(String),而不是f(int)。由于允许类型转换(int可以转换成double,Manager可以转换成Employee,Circle可以转换成Object,等等)所以这个过程可能很复杂。如果编译器没有找到与参数类型匹配的方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误。至此,编译器已获得需要调用的方法名字和参数类型。
- Java中 [重载方法] 的选择是"静态绑定"
动态绑定
这个绑定发生在程序运行之中,根据对象的具体类型进行绑定的,那么这种绑定称为"动态绑定"@H_502_1@
- 动态绑定是基于对象的实际类型而非对象的引用类型!(摘自Java编程思想)
@H_502_1@
- 再提【方法区】
@H_502_1@
- 动态绑定过程
@H_502_1@
- 对于如下的调用方法,JVM是如何定位的呢?
@H_502_1@
小结:overload / overwrite
code
向上转型 → 编译时类型 & 运行时类型 → 动态绑定@H_502_1@
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
// 多态是编译时行为还是运行时行为?
public class Test {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
public class Test2 {
public static void main(String[] args) {
Base base = new Sub();
base.add(1, 2, 3); // "sub_1"; 可变参数和数组不构成[重载]! → 它俩是[重写]!
Sub s = (Sub)base;
s.add(1,2,3); // "sub_2"
}
}
class Base {
public void add(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends Base {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。