Some of the surprises and principles of using Java auto-unboxing __java

Source: Internet
Author: User

We all know that after JDK 5 introduced the mechanism of automatic disassembly box, this is a good thing to write code. Integer object comparison between -128~127 these old stems do not mention, their own look at the source code can be understood, today we say something unexpected

A.java has class A,a method Add (int i), which is a primitive type, perfect match
class A {public
    static void Main (string[] args) {
        b.add (1);
    }
}

B.java
class B {public    
    static void add (int i) {
        System.out.println (i)
    }
}

Execute command

Javac A.java  //This will generate both A.class and B.class
java A//run successfully

And then we do one thing, we change the b slightly so that the B's Add method accepts the integer wrapper type

B.java
class B {public    
    static void Add (integer i) {///here, convert int i to Integer i and recompile
        System.out.println (i);
    }
}

Then recompile the B.java file, note: Only recompile B.java, the equivalent of Class B to do the upgrade, and caller A does not make any changes, A.class also do not regenerate. Then, we execute Java a command, and the result is not as we think it is, but the following error is reported

Exception in thread ' main ' Java.lang.nosuchmethoderror:b.add (I) V at
    A.main (a.java:4)
Yes, the automatic unboxing.

Nosuchmethoderror's error came out, a face black question mark: does not have an add (Integer i) method. How can I say I can't find a way. Say yes to the automatic unboxing.

There must be something wrong. With the question, I found a reply to the Java Automatic disassembly box of the big R:
The true meaning of Rednaxelafx nail

According to the R-large explanation, the Java automatic unboxing occurs at the compile time, that is, the moment Javac compiles, not the runtime. The author's subconscious thought, automatic unboxing will occur in the operating period, so it will feel that Nosuchmethoderror's mistake is simply unbelievable.

If the compiler finds that an automatic disassembly box is needed, it will automatically add integer.valueof () to you using the syntax sugar method, which turns the 1 in Class A into integer.valueof (1), which is then generated in the A.class file. But when we compile a file, the Add method of B accepts the int type, so there is no integer.valueof (1) in the A.class file, or add (int i) to be called in the A.class file.

Later, we changed the add (int i) method of the B file to add (Integer i), essentially deleting an old method, adding a whole new method, at this time A.class is still old, once the Java A,java virtual machine is run to find the Add in B ( int i) method, however, it has not found this method, because it has been deleted, leaving only the Add (Integer i) method, so will be reported Nosuchmethoderror. continue to uncover the underwear of the automatic disassembly box

We execute the following command to view the specific information of the A.class

Javap-verbose A.class

When the B class method is add (int i), the A.class information is as follows:

Class A minor version:0 major version:52 flags:acc_super Constant pool: #1 = MethodRef #4. #13 Java/lang/object. "
   <init> ":() v #2 = methodref #14. #15//B.add: (I) v #3 = Class #16//A               #4 = Class #17//Java/lang/object #5 = Utf8 <init> #6 = Utf8  () V #7 = Utf8 Code #8 = Utf8 linenumbertable #9 = Utf8 main #10  = Utf8 ([ljava/lang/string;) V #11 = Utf8 sourcefile #12 = Utf8 A.java #13 = Nameandtype #5: #6//"<init>":() V #14 = Class #18//B #15 = Nameandt Ype #19: #20//Add: (I) V #16 = Utf8 A #17 = Utf8 java/lang/object #18 = UTF
    8 B #19 = Utf8 Add #20 = Utf8 (I) V {A ();
   Descriptor: () V Flags: Code:stack=1, Locals=1, args_size=1 0:aload_0 1:invokespecial #1//Method ja Va/lang/object. " <init> ":() V 4:return linenumbertable:line 1:0 public static void main (java.lang.string[
    ]);
         Descriptor: ([ljava/lang/string;) V flags:acc_public, Acc_static code:stack=1, Locals=1, args_size=1 0:iconst_1 1:invokestatic #2//Method B.add: (I) V 4:return linenumbertable : Line 4:0 Line 5:4} sourcefile: "A.java"

Focus on the next 1:invokestatic #2这段, that is, the corresponding A.java Add (1) to invoke the logic of the Add method, #2指向常量池 the MethodRef #14. #15, and then put #14. #15继续展开. #18: #19, The final unfolding looks like the annotation. B.add: (I) V, this shows that the assembly layer, the runtime is looking for the add (int I) method.

Then, if we change the method of Class B to add (Integer i), the a.class of the recompile information is as follows:

Class A minor version:0 major version:52 flags:acc_super Constant pool: #1 = MethodRef #5. #14 Java/lang/object. "
   <init> ":() V #2 = MethodRef #15. #16//java/lang/integer.valueof: (I) Ljava/lang/integer; #3 = MethodRef #17. #18//B.add: (Ljava/lang/integer;) V #4 = Class #19//A #               5 = Class #20//Java/lang/object #6 = Utf8 <init> #7 = Utf8 () V #8 = Utf8 Code #9 = Utf8 linenumbertable #10 = Utf8 main #11 = Utf8 ([ljava/lang/string;) V #12 = Utf8 sourcefile #13 = Utf8 A.java #14 = Nameandtype #6: #7//"<init>":() V #15 = Class #21//Java/lang/integer #
  = Nameandtype #22: #23//valueof: (I) Ljava/lang/integer; #17 = Class #24//B #18 = NamEandtype #25: #26//Add: (Ljava/lang/integer) V #19 = Utf8 A #20 = Utf8 java/ Lang/object #21 = Utf8 Java/lang/integer #22 = Utf8 valueof #23 = Utf8 (I)
  Ljava/lang/integer;
    #24 = Utf8 B #25 = Utf8 Add #26 = Utf8 (Ljava/lang/integer;) V {A ();                   Descriptor: () V flags:code:stack=1, Locals=1, args_size=1 0:aload_0 1:invokespecial #1 Method Java/lang/object. " <init> ":() V 4:return linenumbertable:line 1:0 public static void main (java.lang.string[
    ]);
         Descriptor: ([ljava/lang/string;) V flags:acc_public, Acc_static code:stack=1, Locals=1, args_size=1
         0:iconst_1 1:invokestatic #2//Method java/lang/integer.valueof: (I) Ljava/lang/integer; 4:invokestatic #3//Method B.add: (Ljava/lang/integer) V 7:return linenumbertable:line 4:0 line 5:7} sourcefile: "A.java" 

The point is here.

     0:iconst_1
     1:invokestatic  #2                  //Method java/lang/integer.valueof: (I) Ljava/lang/integer;
     4:invokestatic  #3                  //Method B.add: (Ljava/lang/integer;) V
     7:return

Obviously a line of invokestatic #2, and this line is obviously integer.valueof (1) process, that is, the automatic boxing process, that is, the compiler automatically help us add a piece of code, after 4:invokestatic #3才去调用B类的add ( Integer i) method.

So, we subconsciously assume that the process of the disassembly box that will be executed during the run time, in fact, it is done at compile time; The runtime JVM will look for matching methods in strict accordance with the execution process in the class file, while the add (int i) and add (Integer i) methods are obviously not the same method, Of course I will report nosuchmethoderror. talk about code compatibility issues

The above cases are deliberately designed, in fact, in the real scene will encounter this problem. For example, we have referenced two different projects jar1 and JAR2 in our own projects, and jar1 references the Jar2 method, if Jar2 changes the method add (int i) to add (Integer i), and the jar1 we reference is old, This time our project again to adjust the method of Jar1, Jar1 new Jar2 method Add (int), will report Nosuchmethoderror.

In fact, this kind of problem reflects the problem of code compatibility, such as:

Class B Version1 class
B {public
    static void add (int i) {
        System.out.println (i)
    }
}

B class Version2
class B {public
    static void Add (Integer i) {
        System.out.println (i);
    }
}

B class from Version1 to Version2 upgrades, not a compatibility upgrade, add (int i) method and add (Integer i) is not the same method, Version2 version is equivalent to delete the original method, add a new method, If the history jar package is still calling the old method and has not been recompiled, and the JVM is loaded with Version2 Class B, the final result must be an error.

The upgrade of compatibility is to overload this method:

Class B Version3 class
B {public
    static void add (int i) {
        System.out.println (i);
    }
    public static void Add (Integer i) {
        System.out.println (i);
    }
}

There are a number of similar code compatibility issues, for example, we give others the RPC method, obviously can not arbitrarily delete the field, this is easy to understand, delete the field, other people running online application or the old API, they can not get the desired field will certainly be a problem.

The Add field is a friendly upgrade behavior, we add the field, the consumer with the old API although not see the existence of new fields, but the old fields are still available. For example, the server to provide clients with the JSON API, the client only care about the fields they need, the server added field online, and will not affect the use of the old version of the client, because the old version of the client in the JSON deserialization only based on the field name deserialization.

While the thrift of the server-side communication protocol is not the same as JSON, the thrift deserialization process is not based on the field name, but on the order of the Thrfit struct type:

Person Version1
struct person{
 1:i32 ID
 2:string//person name
}

version2
struct person{
 1:i32 ID
 2:i32 age
 3:string name
}

//person version3 struct person{1:i32
 ID
 2: String name
 3:i32 age
}

If the service provider upgrades person to Version2, then for the consumer who is still using version1, the person instance will deserialize the second age into name when it requests to be deserialized, which is clearly not the result we want, A friendly upgrade should be Version3, which does not affect the sorting of the preceding fields, adding fields after them, so that the old version of the API does not cause deserialization confusion.

The typical case of software industry compatibility is Microsoft's Windows operating system, someone has tried to put Windows 3.1 of many programs installed on Windows XP to install and use, unexpectedly found that still normal operation, and even very smooth, really marvel at the persistence of Windows compatibility. Some say that Windows is almost the best OS for industry compatibility, and this may be one of the big reasons why Windows can dominate the desktop market.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.