What information from the imported class is stored inside the compiled class after compilation?

Imagine we have a class A.java. It uses B.java somehow, i.e. Imports it, calls its methods, uses its attributes, etc. We collect files.

Now I would like to have a deeper understanding of what we have.

  • Does A.class retain some information about B internally? What information is just the name, the method that is called, the final variables that are used? If after compilation there is not much information about B in A.class - why can't we compile A without B.class?

  • If we replace B.class in our kit with B.class, which is different - when does it work and when not?

  • If A.class works fine with the new B.class, can A.class use some of the information from B.class OLD that was included in A.class when it was compiled? That is, can we finally have the mixed B.class logic in our project?

  • Based on the answers to the questions above: can we somehow compile a class that depends on other classes, not knowing their exact implementation at compile time, and provide dependency only at runtime?

+5
source share
2 answers

The import keyword does not actually import anything. Basically this is just a way to refer to the full path of a class by its class name. Therefore when we speak

import java.lang.String; 

then we can refer to this class only by the name "String" in our code. At compile time, anywhere in the String class will be replaced by java.lang.String. What is it.

That is why, if you ever have more than one class with the same name, but in different packages, you can import no more than one of them. Another class that you should belong to by its fully qualified name.

To the questions:

1. Does A.class retain some information about B internally? just the name, the method that is being called, the final variables that are used? If after compilation there is not much information about B in A.class - why can't we compile A without B.class?

Not really. Class A will only use fully qualified class names and method signatures when invoking other classes. Using our String example, calling

  mystring.charAt(0) 

will compile to byte code that looks something like this:

  aload_1 [myString] iconst_0 invokevirtual java.lang.String.charAt(int) 

The internal state of other classes is not stored inside our class A, except for possible constants that are built-in. Therefore, be very careful when creating public final fields if the value may change in the future. (see below for work)

We need B.class when compiling class A, so that the compiler can verify that class B has the methods we want to use.

2. If we replace B.class in our kit with B.class, which is different - when does it work, and when not? the new B.class class will work if the fully qualified name (package, etc.) is identical, and it has the correct method signatures that we are trying to use. The new class B may add new methods and may even have different implementations of the methods we use. However, it should not change the method signature of the methods we use. If the old method no longer exists or its signature is different, we will get java.lang.LinkageError at compile time.

3. If A.class works fine with the new B.class, can A.class use some of the information from B.class OLD that was included in A.class at the time it was compiled? That is, can we finally have the mixed B.class logic in our project?

The only problem might be the built-in constants from old B. That's why the Java Coding Guides . Paragraph 31 (p. 115) reads: "Do not apply the public finale to constants whose value may appear in subsequent releases." Instead, make a getter method: for example:

 class BadFoo{ //bad, VERSION could be inlined //and later when we change the version, other classes will have the old value! public static final int VERSION =1; } class BetterFoo{ private static int version =1; //outside classes must call method getVersion() //field version can not be inlined at compile time. //JIT may inline at runtime which is OK public static final int getVersion(){ return version; } } 

4. Based on the answers to the questions above: can we somehow compile a class that depends on other classes, not knowing their exact implementation at compile time, and provide dependency only at runtime?

Yes, the code for the interfaces.

the interface should contain all the methods that need to be called. Our class A should only refer to the caller. If the instance of the object we want to call is passed, then class A does not know (or should not know) what the actual type is, and it does not need to know about it at compile time.

This is one of the main goals and benefits of dependency injection.

Even despite dependency injection, coding for interfaces has its advantages. For example, if we use Map instead of HashMap , we can change the code later to use a different map implementation, such as ConcurrentHashMap , only changing one place in our code. The rest of our code will work, since it only knows the map.

+3
source

This is covered in Chapter 13. Binary Compatibility Java Language Specifications.

Since this may be more specific than you want, I will give a brief summary:

If class A uses the function f class B , the file of class A must contain a symbolic link to f . When class A is loaded, but B does not provide f , it loads class A with java.lang.LinkageError , which prevents the use of A

The exception is compilation time constant expressions, which the compiler must embed, i.e. symbolic links must be replaced by their value at compile time, and class A will continue to use the original values, even if class B has been changed.

Another exception is that adding or removing annotations does not affect the correct binding of binary representations of programs.

No other information about B is included in A at compile time, and everything else about B can be changed without prejudice to its use by the already compiled class A

+2
source

Source: https://habr.com/ru/post/1204862/


All Articles