main 方法

public class Solution {
public static void main(String[] args) {
Person person = new Person();
person.hello();
}
} class Person {
public void hello() {
System.out.println("hello");
}
}

源文件名是 Solution.java,这是因为文件名必须与 public 类的名字相匹配。在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。

在这个示例程序中包含两个类:Person 类和带有 public 访问修饰符的 Solution 类。Solution 类包含了 main 方法。

当编译这段源代码的时候,编译器将在目录下创建两个 class 文件:Solution.class 和 Person.class。

将程序中包含 main 方法的类名提供给字节码解释器,以便启动这个程序:java Solution。字节码解释器开始运行 Solution 类的 main 方法中的代码。

理解方法调用

下面假设要调用 x.f(args)。下面是调用过程的详细描述:

  • 编译时:

    1. 编译器査看对象变量的声明类型和方法名,然后获得所有可能被调用的候选方法。
    2. 编译器査看调用方法时提供的参数类型,然后获得需要调用的方法名字和参数类型。
  • 运行时:
    • 如果方法是 private 方法、static 方法、final 方法或者构造器方法,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式称为静态绑定(static binding)。与此对应的是,调用的方法依赖于对象变量 x 的实际类型,并且在运行时实现动态绑定。
    • 虚拟机预先为每个类创建了一个方法表(method table),方法表中列出了所有方法的签名和实际调用的方法。这样一来,在真正调用方法的时候,虚拟机仅查找方法表就行了。

1、编译器査看对象变量的声明类型和方法名,然后获得所有可能被调用的候选方法。假设调用 x.f(param),且对象变量 x 被声明为 C 类型。需要注意的是:有可能存在多个名字为 f,但参数类型不一样的方法。例如,可能存在方法 f(int) 和方法 f(String)。编译器将会一一列举所有 C 类中名为 f 的方法和其父类中访问属性为 public 且名为 f 的方法(父类的私有方法不可访问)。至此,编译器已获得所有可能被调用的候选方法。

2、接下来,编译器将査看调用方法时提供的参数类型,然后获得需要调用的方法名字和参数类型。如果在所有名为 f 的方法中存在一个与提供的参数类型完全匹配,就选择这个方法。这个过程被称为重载解析(overloading resolution)。例如,对于调用 x.f("Hello") 来说,编译器将会挑选 f(String),而不是 f(int)。由于允许类型转换(int 可以转换成 double,Manager 可以转换成 Employee 等),所以这个过程可能很复杂。如果编译器没有找到与参数类型匹配的方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误。至此,编译器已获得需要调用的方法名字和参数类型。

如果方法是 private 方法、static 方法、final 方法或者构造器方法,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式称为静态绑定(static binding)。与此对应的是,调用的方法依赖于对象变量 x 的实际类型,并且在运行时实现动态绑定。在我们列举的示例中,编译器采用动态绑定的方式生成一条调用 f(String) 的指令。

当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与对象变量 x 所引用的对象的实际类型最合适的那个类的方法。假设 x 的实际类型是 D,D 是 C 类的子类。如果 D 类定义了 f(String) 方法,虚拟机就直接调用 D 类的 f(String) 方法;否则(D 类中没有定义 f(String) 方法),将在 D 类的父类中寻找 f(String),以此类推。


每次调用方法都要进行搜索,时间开销相当大。因此,虚拟机预先为每个类创建了一个方法表(method table),方法表中列出了所有方法的签名(方法名、参数类型)和实际调用的方法。这样一来,在真正调用方法的时候,虚拟机仅查找方法表就行了。

在前面的例子中,虚拟机搜索 D 类的方法表,以便寻找与调用 f(Sting) 相匹配的方法。这个方法既有可能是 D.f(String),也有可能是 C.f(String),这里的 C 是 D 的父类。这里需要提醒一点,如果调用 super.f(param),编译器将对对象变量父类的方法表进行搜索。

方法调用的示例

public static void main(String[] args) {
Employee e = new Manager("Carl Cracker", 80000, 1987, 12, 15);
System.out.println("salary=" + e.getSalary());
}

现在,查看一下调用 e.getSalary() 的详细过程。对象变量 e 被声明为 Employee 类型。Employee 类只有一个名叫 getSalary() 的方法,这个方法没有参数。因此,在这里不必担心重载解析的问题。

由于 getSalary() 不是 private 方法、static 方法,也不是 final 方法,所以将采用动态绑定。虚拟机为 Employee 和 Manager 两个类生成方法表。


在 Employee 的方法表中,列出了这个类定义的方法。实际上,下面列出的方法并不完整,Employee 类有一个父类 Object,Employee 类从这个父类中还继承了许多方法,在此我们略去了 Object 的方法。

Employee:
getName() -> Employee.getName()
getSalary() -> Employee.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(doubl e)

Manager 的方法表稍微有些不同。其中有三个方法是继承而来的,一个方法是重新定义的(方法的重写),还有一个方法是新增加的。

Manager:
getName() -> Employee.getName()
getSalary() -> Manager.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(doubl e)
setBonus(double) -> Manager.setBonus(double)

在运行时,调用 e.getSalary() 的解析过程为:

  1. 首先,虚拟机提取对象变量 e 的实际类型的方法表。既可能是 Employee、Manager 的方法表,也可能是 Employee 类的其他子类的方法表。
  2. 接下来,虚拟机搜索对象变量 e 的实际类型的方法表。此时,虚拟机已经知道应该调用哪个方法。
  3. 最后,虚拟机调用方法。

动态绑定有一个非常重要的特性:无需对现存的代码进行修改,就可以对程序进行扩展。假设增加一个新类 Executive,并且对象变量 e 有可能引用这个类的对象,我们不需要对包含调用 e.getSalary() 的代码进行重新编译。如果 e 恰好引用一个 Executive 类的对象,就会自动地调用 Executive.getSalary() 方法。

参考资料

《Java核心技术卷一:基础知识》(第10版)第 5 章:继承 5.1.6 理解方法调用

理解Java程序的执行的更多相关文章

  1. 怎么优化JAVA程序的执行效率和性能?

    现在java程序已经够快的了,不过有时写出了的程序效率就不怎么样,很多细节值得我们注意,比如使用StringBuffer或者StringBuilder来拼接或者操作字符串就比直接使用String效率高 ...

  2. [转帖]浅析java程序的执行过程

    浅析java程序的执行过程 转帖来源: https://www.cnblogs.com/wangjiming/p/10315983.html 之前学习过 这一块东西 但是感觉理解的不深刻. copy一 ...

  3. 003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程

    003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程 Java程序长啥样? 首先编写一个Java程序 记事本编写程序 打开记事本 1.wi ...

  4. Java程序的执行过程

    Java程序的执行过程 编译器将 Java 源代码编译成字节码class文件 类加载到 JVM 里面后,执行引擎把字节码转为可执行代码 执行的过程,再把可执行代码转为机器码,由底层的操作系统完成执行

  5. 一个Java程序的执行过程(转)

    我们手工执行java程序是这样的:  1.在记事本中或者是UE的文本编辑器中,写好源程序:  2.使用javac命令把源程序编译成.class文件:    编译后的.class(类字节码)文件中会包含 ...

  6. 浅析java程序的执行过程

    在研究任何一门语言时,无论是面向过程的c,c++(面向过程和面向对象),还是面向对象的.net,java等,弄清语言执行过程至关重要.  何为语言执行过程? 所谓语言执行过程,指对于任何一门语言,如j ...

  7. 深入理解JAVA虚拟机 虚拟机执行子系统

    class类文件的结构 java的class类文件中存在两种结构:无符号数和表.最小的存储单元是8个字节. 无符号数是基本的数据类型,用来描述数字,UTF-8编码的字符串,索引引用. 表示多个无符号数 ...

  8. 从零自学Java-2.初步理解Java程序使如何工作的

    1.学习Java应用程序是如何工作的 2.构成一个应用程序 3.向应用程序传递参数 4.学习Java程序是如何组织的 5.在应用程序中创建一个对象 程序Root:输出225的正平方根 package ...

  9. 使用eclipse创建java程序可执行jar包

    一.eclipse中,在要打成jar包的项目名上右击,出现如下弹出框,选择“export”: 二.在接下来出现的界面中点击“jar file”,然后next: 三.在接下来出现的界面中,如图所示勾选上 ...

  10. 如何理解Java程序使用Unicode字符集编写

    Java采用UTF-16编码作为内码,也就是说在JVM内部,文本是用16位码元序列表示的,常用的文本就是字符(char)和字符串(String)字面常量的内容.注:UTF-16是Unicode字符集的 ...

随机推荐

  1. Linux命令之定时调度-crontab

    以上cron命令格式为:* * * * * [root run-parts] command 前面五个*号代表时间,用数字,数字的取值范围和含义如下: 第一个*:分钟 (0-59)[整数] 第二个*: ...

  2. 007使用IDEA开发

    007使用IDEA开发 1.什么叫IDE? 集成开发环境(IDE,Integrated Development Environment)是用于提供程序开发环境的应用程序,一般包括代码编辑器.编译器.调 ...

  3. 多线程JUC练习

    package com.aliyun.test.learn; import java.util.concurrent.*; import java.util.concurrent.locks.Reen ...

  4. PYinstall打包程序出现编码错误的解决 'utf-8' codec can't decode byte 0xce in position 171: invalid continuation b

    网上说,先执行,再打包 chcp 65001 试过没有用. 解决方案: 把import的包批量注释,然后寻找是import那个文件导致. 虽然注释会导致程序运行出错,但是打包才不管你能不能运行. 最后 ...

  5. python xlwings实用操作记录

    import xlwings as xw tfile="test.xlsx" newfile="new.xlsx" app = xw.App(visible=F ...

  6. C# 读取json文件并转为List集合

    using (System.IO.StreamReader file = System.IO.File.OpenText(pathForSaving)) { using (JsonTextReader ...

  7. baodoumi yml配置

    上次因为自增,发现配置没生效, 修改后如下 # 配置mybatis-plus, 参考:com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropert ...

  8. 网络基础-OSI七层模型

    什么是OSI模型 OSI模型(或 Open Systems Interconnection Model开放系统互连模型)是网络中使用的绝对基础模型.这个关键模型提供了一个框架,规定所有联网设备将如何发 ...

  9. ARP欺骗工具-arpspoof

    arpspoof arpspoof是dsniff下的一个ARP欺骗工具 大概原理: 两台主机HostA 和 HostB想要进行通信的流程,那么主机A将需要知道自己的ip,mac 以及主机B的ip, m ...

  10. 对使用网站模板编写自己的jsp页面的收获

    收获与问题 先感叹一句模板的强大,模板只要按照我的上一篇博客的步骤进行修改,我们就能拥有一个自己写好看许多的界面了. 我目前套用模板还不是很熟练,套用的速度还需要加快,不过目前的我,已经掌握了基本要领 ...