• 从编译到运行


    总体过程

    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();    // 查询直接基类
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

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

    这种用法为了获取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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    小纸条

    初始化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属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3. 类加载

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

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

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

  • 相关阅读:
    23软考备考已开始,网络工程师知识点速记~(2)
    springmvc:设置后端响应给前端的json数据转换成String格式
    卷积层数量过多的缺点,卷积积分的被积函数
    内点法(interior point method)求解二次规划,附python代码
    [单片机框架][bsp层][N32G4FR][bsp_gpio] GPIO配置和使用
    数学建模——插值拟合
    Thinkpad T14升级Windows11ver22h2失败问题解决小记
    2022-11-11 myql Block Nested-Loop and Batched Key Access Joins
    Dive into TensorFlow系列(2)- 解析TF核心抽象op算子
    VSCode中打开md文件的智能提示
  • 原文地址:https://blog.csdn.net/qq_35439539/article/details/126344527