开发者

What does "new" do in Java w.r.t. class loader?

开发者 https://www.devze.com 2023-04-11 17:37 出处:网络
I cannot easily find it in JLS/JVMSpec, nor in SO. I\'m sure it must\'ve been asked... So, what does \"new\" do actually? Suppose we instantiate a class B in A:

I cannot easily find it in JLS/JVMSpec, nor in SO. I'm sure it must've been asked...

So, what does "new" do actually? Suppose we instantiate a class B in A:

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

Is this equivalent to

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

?

Does it, or does it not work like that in every environment?

I'd be开发者_StackOverflow grateful if you can point me to the appropriate chapter in JLS/JVMSpec. Thanks!

Edit: surely we can't call B.class.getCanonicalName() in loadClass() call, since B's not loaded yet. JVM has to resolve the name based on the import statement.


Is this equivalent to

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

?

No, not always.

Class loading is performed only once for a given namespace, unless the Class in question has been previously unloaded. Therefore, the equivalent expression A.class.getClassLoader().loadClass("B's canonical name") will be executed only once in most scenarios. In other words, if you have two expressions - new A(), the loadClass will be performed only once.

Invocation of a constructor is treated as a method invocation by the JVM, but this requires the cooperation of a Java compiler. The JVM and the compiler have to adhere to section 3.9 of the Java Virtual Machine Specification, which states:

3.9 Specially Named Initialization Methods

At the level of the Java virtual machine, every constructor (§2.12) appears as an instance initialization method that has the special name <init>. This name is supplied by a compiler. Because the name <init> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Instance initialization methods may be invoked only within the Java virtual machine by the invokespecial instruction, and they may be invoked only on uninitialized class instances. An instance initialization method takes on the access permissions (§2.7.4) of the constructor from which it was derived.

A class or interface has at most one class or interface initialization method and is initialized (§2.17.4) by invoking that method. The initialization method of a class or interface is static and takes no arguments. It has the special name <clinit>. This name is supplied by a compiler. Because the name <clinit> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Class and interface initialization methods are invoked implicitly by the Java virtual machine; they are never invoked directly from any Java virtual machine instruction, but are invoked only indirectly as part of the class initialization process.

This section assumes that the Class object pertaining to the class in question is available to the current thread. Once the Class object is available, the method <init> corresponding to the constructor with the right set of arguments, will be invoked.

The question of which classloader will be used to load the class, if not already loaded, is a bit different, and has nothing to do with the new keyword. It depends on how one class references another i.e. does a symbolic reference need to be resolved in the runtime constant pool? The behavior in this context is defined in Section 5.3 of the Java Virtual Machine Specification:

5.3 Creation and Loading

Creation of a class or interface C denoted by the name N consists of the construction in the method area of the Java virtual machine (§3.5.4) of an implementation-specific internal representation of C. Class or interface creation is triggered by another class or interface D, which references C through its runtime constant pool.

...

The Java virtual machine uses one of three procedures to create class or interface C denoted by N:

  • If N denotes a nonarray class or an interface, one of the two following methods is used to load and thereby create C :

    • If D was defined by the bootstrap class loader, then the bootstrap class loader initiates loading of C (§5.3.1).

    • If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C (§5.3.2).

  • Otherwise N denotes an array class. An array class is created directly by the Java virtual machine (§5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.

Note the sentence - If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C in the above quote. In the context of the expression new A(), the classloader that loaded the enclosing class will be responsible for loading A in accordance with the VM Spec; this is of course, assuming that the enclosing class is not loaded by the bootstrap classloader.


To follow up on my comment, a line like

new A()

translates to

0:  new #2; //class A
3:  dup
4:  invokespecial   #3; //Method A."<init>":()V
7:  pop

And the stacktrace is:

  [1] java.net.URLClassLoader$1.run (URLClassLoader.java:202)
  [2] java.security.AccessController.doPrivileged (native method)
  [3] java.net.URLClassLoader.findClass (URLClassLoader.java:190)
  [4] sun.misc.Launcher$ExtClassLoader.findClass (Launcher.java:229)
  [5] java.lang.ClassLoader.loadClass (ClassLoader.java:307)
  [6] java.lang.ClassLoader.loadClass (ClassLoader.java:296)
  [7] sun.misc.Launcher$AppClassLoader.loadClass (Launcher.java:301)
  [8] java.lang.ClassLoader.loadClass (ClassLoader.java:248)
  [9] Loader.main (Loader.java:11)

So I guess you were pretty close in your guess.


I've found this link that mainly explains the concept of the "new" operator in Java. The main idea for me is this phrase:

"(..) The new operator instantiates a class by allocating memory for a new object and returning a reference to that memory. The new operator also invokes the object constructor. (..)"

I think that three things should be considered:

  1. The "new" operator is common in every environment but due to developers need for it. In Java's case the "new" operator also allocates the memory space for the object.
  2. Sometimes (in older compilers) it was not available and all declarations had to be made "the long way". So for retro-compatibility sake both statements are equivalent.
  3. There is always the case were you may need to override the "loadClass" or the "getClassLoader()"

"(..)Even though Java's new keyword is central to the language, there may be better ways of getting the job done.(..)"

Hope it helps ;)

0

精彩评论

暂无评论...
验证码 换一张
取 消