从编译到运行

总体过程

Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤。

一个Java程序,需要经过编译和运行两步,才能看到程序实现的效果。

在这里插入图片描述

1. 编译+运行

编译是由编译器完成的,将源码一次性翻译成字节码,编译完成后生成中间的字节码文件,也就是 .class文件

运行是由解释器完成的,将字节码一句一句地解释为机器码并执行。运行的过程是解释执行字节码的过程。

小纸条

Java虚拟机和解释器??

解释器是解释型语言中解释执行代码的东西,常见的解释型语言有 Python、JavaScript、PHP 等等。不同语言使用的解释器不同,而Java语言中Java虚拟机是解释器的一种实现方式。

2. 字节码

我们需要编译,是需要生成Java 虚拟机可以理解的代码,也就是字节码。字节码,是Java虚拟机可以理解的代码,简短点理解就是,字节码是代码😅。

因为字节码是面向虚拟机,不面向任何特定的处理器,所以使得Java语言有可移植的特点,不需要重新编译就可以在不同的操作系统上运行,也就是Java语言可以跨平台。而现在,Docker 之类的可以容易实现跨平台,其他语言也可以。

2.1 .class文件

Java 程序经过编译,会生成中间的字节码文件。原始的Java程序是 .java 结尾的文件,编译之后,会生成一堆 .class 结尾的字节码文件。一个 Java 文件会生成一个对应的.class 文件,两个文件同名,后缀不同。

单从外观上来看,编译完成之后,和之前的差异是多了 out 或者 target 文件夹,在这些文件夹中有大量的.class文件。比如,单文件项目生成的 .class 文件如下:

在这里插入图片描述

Maven 项目生成的 .class 文件如下:

在这里插入图片描述

2.2 Class对象

当类加载器把一个类加载到内存时,会在内存中创建一个 Class对象。一个类的Class对象和类的普通Java对象实例同名。

Java中每个类都对应一个Class对象,同一个类不会产生多个相同的Class对象

Class对象,包含了和类相关的信息。Class对象是用来创建所有的普通的对象.java 文件中类的对象实例)。当一个 类的Class对象未被加载时,类加载器会查询同名的 .class 文件,将其载入内存。Class对象 被载入内存后,会被用来创建这个类的所有对象。

小纸条

类加载器加载的过程,就是查找 .class文件,创建一个 Class对象

Class对象载入内存后的使用情况是Java虚拟机中的内存模型部分,也就是运行时的数据区域。

2.3 获取Class对象引用

因为Class对象中包含类的信息,所以如果想要在运行时使用类的类型信息,可以从Class对象获取到,这样就需要先得到Class对象的引用。

在拿到一个类的 Class对象的引用后,可以直接知道对应的这个类的信息。比如,

Class example = LinkedList.class;    // 推荐,获取 LinkedList 类的 Class对象引用
example.getName();    // 获得类名(包括包名)
example.getSimpleName();    // 获得类名(不包括包名)
example.isInterface();    // 是否时一个接口
example.getSuperclass();    // 查询直接基类

一般情况下获取一个类的Class对象引用:通过类创建一个对象实例,然后获取Class对象引用。比如,

// 创建一个 LinkedList 对象实例
List<Integer> list = new LinkedList<>();
// 获取 LinkedList 的Class对象引用
Class c = list.getClass();

这种用法为了获取Class对象引用而创建一个对象,是消耗资源,降低效率的。

直接获取Class对象引用的方法有以下几种:

方法备注1备注2
类名.class需要知道类的明确名字在加载完之后不会自动初始化该Class对象
对象名.getClass()需要先创建一个对象
ClassLoader.getSystemClassLoader().loadClass()需要知道类的全名,包括包名
Class.forName()需要知道类的全名,包括包名在加载完之后会自动初始化该Class对象

通过Class对象引用可以操纵Class对象Class对象和普通对象涉及的一些函数如下图:

在这里插入图片描述

获取Class对象引用的代码例子如下:

// 1. 知道具体类的情况
Class example1 = LinkedList.class;
// 2. 传入类的路径。需要处理异常
Class example2 = Class.forName("java.util.LinkedList");
// 3. 通过对象实例instance.getClass()
Class example3 = new LinkedList().getClass();
// 4. 通过类加载器xxxClassLoader.loadClass()传入类路径获取,Class 对象不会进行初始化。
// 需要处理异常
Class example4 = ClassLoader.getSystemClassLoader().loadClass("java.util.LinkedList");

小纸条

初始化Class对象,是初始化静态方法和静态块。

2.4 反射

Java 在运行中识别对象和类的信息的方法有两种:

  • 一种是传统的RTTI (run-time type identification,运行时类型识别),假设在编译时已经知道所有的类型。

  • 一种是反射,在运行时才发现和使用类的信息。在反射机制中,是在运行时打开和检查的class文件class文件在编译时是不可获取的。

如果想要在运行时分析类以及执行类中方法,就需要用到反射。通过反射可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。反射,是在需要创建动态的代码的时候会有用,一般不需要直接使用反射。

反射是直接通过Class对象引用来获取类的信息以及类中的方法,而不需要通过创建一个对象实例再获得Class对象引用从而获取类的信息。比如,下面的代码:

// 获取 LinkedList 类的 Class对象引用
Class example = LinkedList.class;
// 获取 LinkedList 类的属性和方法
Method[] methods = example.getMethods();    // 获取该类的所有方法
Constructor[] constructors = example.getConstructors();   // 获取该类的所有构造函数
Field[] fields = example.getFields();    // 获取该类的所有public属性

Class类和java.lang.reflect 类库一起支持了反射(具体怎么支持才实现的,不知道😂),这样,通过类库就可以在运行时获取某个类的信息,比如变量、构造函数、方法。比如下图(下图中的Student类只是为了说明反射中可以获取到的东西,实际中不这么设置属性)

在这里插入图片描述

通过反射获取类的信息的方法比如下面:

Class example = Student.class;      // 获取Student对象引用
// 获取类的所有public变量
Field[] fields = example.getFields();      // [public java.lang.String code.input.Student.name]
// 类的第一个public变量的信息
fields[0].getName();        // 变量名是 name
fields[0].getType();        // 变量类型名是 java.lang.String
fields[0].getDeclaringClass().getName();    // 声明的类名是 code.input.Student

3. 类加载

在编译完成之后,是在运行期间需要做的事情。类型的加载和连接就是在运行期间。

在虚拟机可以真正运行代码之前,需要先把.class文件加载进虚拟机中,这样才能运行和使用这些.class文件,生成虚拟机可以使用的对象。加载的过程,就是把.class文件加载到内存,根据.class文件在内存中创建Class对象,并对数据进行校验、解析、初始化,最终形成虚拟机可以直接使用的Java类型,这也是Java虚拟机的类加载机制

类加载是由类加载器完成的,选择用哪个类加载器来加载类是使用双亲委派机制来确定的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值