当前位置: 首页 > 面试题库 >

Java wrt类加载器中的“新”功能是什么?

弓晔
2023-03-14
问题内容

我无法在JLS / JVMSpec或SO中轻松找到它。我确定一定有人问过…

那么,“新”实际上是做什么的呢?假设我们在A中实例化一个B类:

class A {
    // ...
    new B();
    // ...
}

这等于

class A {
    // ...
    A.class.getClassLoader().loadClass("B's canonical name").newInstance();
    // ...
}

是不是还是在每种环境下都无法正常工作?

如果您能将我引到JLS / JVMSpec中的相应章节,将不胜感激。谢谢!

编辑:我们肯定不能叫B.class.getCanonicalName()loadClass()打电话,因为B的尚未加载。JVM必须根据导入语句来解析名称。


问题答案:

这等于

class A {
    // ...
    A.class.getClassLoader().loadClass("B's canonical

name”).newInstance();
// …
}

不,并非总是如此。

对于给定的名称空间,类加载仅执行一次,除非Class之前已卸载了该问题。因此,A.class.getClassLoader().loadClass("B's canonical name")在大多数情况下,等效表达式将仅执行一次。换句话说,如果您有两个表达式- new A()loadClass则将仅执行一次。

JVM将构造函数的调用视为方法调用,但这需要Java编译器的配合。JVM和编译器必须遵守Java虚拟机规范的3.9节,其中规定:

3.9特别命名的初始化方法

在Java虚拟机级别,每个构造函数(第2.12节)都作为具有特殊名称的实例初始化方法出现
<init>。该名称由编译器提供。因为该名称<init>
不是有效的标识符,所以不能直接用Java编程语言编写的程序中使用它。实例初始化方法只能由 invokespecial 指令在Java虚拟机内调用
,并且只能在未初始化的类实例上调用。实例初始化方法具有从其获得构造函数的访问权限(第2.7.4节)。

一个类或接口最多具有一个类或接口初始化方法,并通过调用该方法进行初始化(第2.17.4节)。类或接口的初始化方法是静态的,不带参数。它有特殊的名字<clinit>。该名称由编译器提供。因为该名称<clinit>不是有效的标识符,所以不能直接用Java编程语言编写的程序中使用它。Java虚拟机隐式调用类和接口初始化方法。绝对不能从任何Java虚拟机指令直接调用它们,而只能在类初始化过程中间接调用它们。

本节假定Class与该类有关的对象可用于当前线程。一旦Class对象是可用的,该方法<init>对应于与右组参数的构造,将被调用。

使用哪个类加载器加载类(如果尚未加载)的问题有些不同,并且与new关键字无关。它取决于一个类如何引用另一个类,即是否需要在运行时常量池中解析符号引用?Java虚拟机规范的第5.3节定义了这种情况下的行为:

5.3创建和加载

用名称N表示的类或接口C的创建包括Java虚拟机的方法区域(第3.5.4节)中特定于实现的内部C表示的构造。类或接口的创建由另一个类触发或接口D,它通过其运行时常量池引用C。

Java虚拟机使用以下三个过程之一来创建由N表示的类或接口C:

  • 如果N表示非数组类或接口,则使用以下两种方法之一来加载并创建C:

    • 如果D由引导类加载器定义,则引导类加载器将启动C的加载(第5.3.1节)。

    • 如果D是由用户定义的类加载器定义的,则该用户定义的类加载器将启动C的加载(第5.3.2节)。

  • 否则,N表示数组类。数组类是由Java虚拟机(第5.3.3节)而不是由类加载器直接创建的。但是,在创建数组类C的过程中使用D的定义类加载器。

注意If D was defined by a user-defined class loader, then that same user- defined class loader initiates loading of C上面引用中的句子。在表达式的上下文中new A(),加载了封闭类的类加载器将A根据VM Spec 进行加载;当然,假设引导类加载器未加载该封闭类。



 类似资料:
  • 问题内容: 在此页面中,我找到了新的JavaScript函数类型: 我已经知道了什么,以及做的,但不知道什么是注定的。它是什么? 问题答案: 这是一个生成器功能。 生成器是可以退出并稍后重新输入的函数。它们的上下文(变量绑定)将在重新进入时保存。 调用生成器函数不会立即执行其主体。而是返回该函数的迭代器对象。调用迭代器的方法时,将执行生成器函数的主体,直到第一个表达式指定要从迭代器返回的值,或者使

  • 问题内容: 我们知道我们可以使用以下方法覆盖System 类加载器: 那么,既然它本身是一个类,它是由谁加载的? 我们如何获得该“元”类加载器的类文件? 问题答案: 从Javadoc中获取: 如果在首次调用此方法时定义了系统属性“ java.system.class.loader”,则该属性的值将作为要作为系统类加载器返回的类的名称。 该类使用默认的系统类加载器加载, 并且必须定义一个公共构造函数

  • 问题内容: 该ProGuard的主页上列出的功能: 重新定位和预先验证Java 6的现有类文件,以充分利用Java 6更快的类加载速度。 它所指的Java 6有什么区别? 重要吗? 它会对通过默认类加载器的同步方面的多线程导致的速度下降产生影响吗? 问题答案: 如ProGuard 常见问题解答所提示: Java 6编译器将预验证信息添加到类文件中 查看“ 按类型检查的Java虚拟机规范 验证”部分

  • 如果我有一个内部类声明,例如: 其次是: A$B内部类也会加载吗?如果B内部类没有被声明为“静态”呢?

  • 问题内容: 跟进有关重新加载模块的问题,如何从已更改的模块重新加载特定功能? 伪代码: 问题答案: 您想要的是可能的,但是需要重新加载两件事……首先,但是您还必须这样做(假设是包含该语句的模块的名称)。 至于为什么…第一次加载时,将创建一个对象,其中包含一个对象。当您导入到模块,它存储到一个参考。当被调用时,对象被消隐,并且该模块重新执行。这意味着所有引用仍然有效,但是已经创建了一个新对象…因此,

  • JVM类加载机制主要有三种: 1、全盘负责 类加载器加载某个class时,该class所依赖的和引用其它的class也由该类加载器载入。 2、双亲委派 先让父加载器加载该class,父加载器无法加载时才考虑自己加载。 3、缓存机制 缓存机制保证所有加载过的class都会被缓存,当程序中需要某个class时,先从缓存区中搜索,如果不存在,才会读取该类对应的二进制数据,并将其转换成class对象,存入