A few days ago, I saw a classic interview question in the headlines, which triggered some thinking. And that’s what triggered this article.

background

Please look at the topic:

public class Main { public static void main(String[] args) { Integer a = 1; Integer b = 2; System.out.println("a=" + a + ",b=" + b); swap(a, b); System.out.println("a=" + a + ",b=" + b); } private static void swap(Integer numa, Integer numb) {Copy the code

After seeing this problem instantly feel pit. Why write a swap? It would not be simpler to implement the following:

public static void main(String[] args) {
        Integer a = 1; 
        Integer b = 2;
        System.out.println("a=" + a + ",b=" + b);
        Integer tmp = a;
        a = b;
        b = tmp;
        System.out.println("a=" + a + ",b=" + b);
    }
Copy the code

Output:

a=1,b=2
a=2,b=1
Copy the code

Perfect exchange. But please note that this is an interview question and it is intended to test some knowledge. So implement swap honestly. Some of you might think that Integer is a packing type, and it’s a boxing and unboxing operation on an Int. It’s actually an object. Since it is an object, don’t you just change the reference of the object? Let’s first look at the implementation:

private static void swap(Integer numa, Integer numb) {
       Integer tmp = numa;
       numa = numb;
       numb = tmp;
       System.out.println("numa=" + numa + ",numb=" + numb);
}
Copy the code

Output:

a=1,b=2
numa=2,numb=1
a=1,b=2
Copy the code

Not surprisingly, it didn’t work. What’s the reason? A skilled technician can see that the problem is a confusion of parameters and arguments

The difference between JAVA parameters and arguments:

A parameter is exactly what its name suggests: a formal parameter, used to define a method and used to receive arguments passed by the caller. Parameter The virtual machine allocates memory units only when a method is called, and the allocated memory units are freed when the method call ends. As a result, parameters are only valid inside the method, so changes made to the reference object do not affect outside the method.

Arguments are just as their name suggests: the actual arguments that are passed to the method when called. Arguments are pre-assigned before being passed to other methods. In this example, numa and numb are the parameters to the swap method, a and b are the arguments to the swap method

Note: In a value pass call, you can only pass an argument to a parameter, not reverse the value of the parameter to the argument. During a function call, the value of the parameter changes, but the value of the argument does not. In the reference-by-call mechanism, the address referenced by the argument is actually passed to the parameter, so any changes that occur on the parameter will also occur on the argument variable. So what are value passing and reference passing

Value passing and reference passing

Before we talk about value passing and reference passing, let’s take a look at what Java data types are

JAVA data type

Data types in Java fall into two broad categories, primitive types and object types. There are also two types of variables: primitive and reference types. Primitive variables hold original values, that is, the value they represent is the value itself. The original value usually corresponds to the stack area in memory, while reference variables hold reference values, which refer to the address of the memory space. Represents a reference to an object, not the object itself. The object itself is stored at the address represented by the reference value. The referenced object corresponds to an area of heap memory on memory. Basic types include: byte, short, int, long, char, and float, double, Boolean the eight basic data types Reference types include class type, interface types and arrays

The difference between the base type and reference type of a variable

Basic data types are allocated space when they are declared

int a; // The vm allocates 4 bytes of memory when it declares a variable, although it does not assign a value: String STR; Str.length (); str.length(); str.length(); // This operation will report an error because there is no memory area allocated on the heap, and a = 1; This operation will not report an error.Copy the code

All right, Java data types are done, so let’s move on to value passing and reference passing. Start by memorizing the concept that basic types of variables are value passed; Variables that reference types combine the parameters and arguments mentioned earlier.

Value passed

When a method is called, the actual parameter passes its value to the corresponding formal parameter. The function receives a copy of the original value. There are two equal basic types in memory: the actual parameter and the formal parameter

reference

Also called address passing, address passing. Method calls, the actual parameter references (address, rather than the value of the parameter) is passed to the method of the corresponding formal parameters, the function receives the original value of the memory address In the method, parameters and the same argument, point to the same block of memory address, during the implementation of the method will influence to the operation of the reference to the actual object Through the example to talk:

static class Person {
       int age;
       Person(int age) {
           this.age = age;
       }
   }
   
   private static void test() {
       int a = 100;
       testValueT(a);
       System.out.println("a=" + a);
       Person person = new Person(20);
       testReference(person);
       System.out.println("person.age=" + person.age);
   }
   
   private static void testValueT(int a) {
       a = 200;
       System.out.println("int testValueT a=" + a);
   }
   
   private static void testReference(Person person) {
       person.age = 10;
   }
Copy the code

Output:

int testValueT a=200
a=100
person.age=10
Copy the code

See that the value passed a has not changed, while the reference passed persion. Age has changed someone said

private static void testReference(Person person) {
        person = new Person(100);
}
Copy the code

Why is the output person.age still 20? I mean know what a reference type is? The method changes the address reference of the parameter to another object. This does not change the object. It does not affect the reference of the external argument to the original object.

One might wonder, following the example at the beginning of this article, how Integer is also a reference type. In fact similar String, Integer, Float, Double, Short, Byte, Long, class Character and so on basic packing type. For example, Integer has an internal value that records the value of the basic type int, but it does not provide a method to modify it. It is also final and cannot be changed by conventional means. So even though they’re referential, we can think of them as value passing, and this is just thinking of them as actually referential passing, address passing.


Ok, that’s the basics, but let’s get back to the interview questions


Return to the chase

private static void swap(Integer numa, Integer numb) {
        Integer tmp = numa;
        numa = numb;
        numb = tmp;
        System.out.println("numa=" + numa + ",numb=" + numb);
 }
Copy the code

By reviewing the basics, it is clear that the above approach to substitution is not feasible. Interger is a reference type, but the above operation only changes the reference of the parameter, not the corresponding object of the argument.

So the idea is, we’re going to change the value property inside Integer by special means

private static void swap(Integer numa, Integer numb) { Integer tmp = numa; try { Field field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(numa, numb); Set (numb, TMP); } catch (Exception e) {e.printStackTrace(); numa = numa; }}Copy the code

Output result:

a=1,b=2
a=2,b=2
Copy the code

Question again? Why did the change in the value of A succeed and the change in the value of B fail?

Set (numb, TMP); TMP has changed to 2 after the previous line of code. So how do you break it? TMP does not refer to NUMa

private static void swap(Integer numa, Integer numb) { int tmp = numa.intValue(); / / TMP is defined as the basic data types try {Field Field = Integer. Class. GetDeclaredField (" value "); field.setAccessible(true); field.set(numa, numb); Set (numb, TMP); } catch (Exception e) { e.printStackTrace(); }}Copy the code

In this case, the changes to numa will not cause TMP to change

a=1,b=2
a=2,b=2
Copy the code

Why is that? Are you going crazy? Are we on the wrong track? Before you worry, let’s look at this example: just change the value of a to 129 and the value of b to 130

public static void main(String[] args) { Integer a = 129; Integer b = 130; System.out.println("a=" + a + ",b=" + b); swap(a, b); System.out.println("a=" + a + ",b=" + b); } private static void swap(Integer numa, Integer numb) { int tmp = numa.intValue(); try { Field field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(numa, numb); field.set(numb, tmp); } catch (Exception e) { e.printStackTrace(); }}Copy the code

Running results:

a=129,b=130
a=130,b=129
Copy the code

Do you doubt life? Are we on the right track? Why do I just change the number? Let’s modify the program a little bit

public static void main(String[] args) { Integer a = new Integer(1); Integer b = new Integer(2); System.out.println("a=" + a + ",b=" + b); swap(a, b); System.out.println("a=" + a + ",b=" + b); } private static void swap(Integer numa, Integer numb) { int tmp = numa.intValue(); try { Field field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(numa, numb); field.set(numb, tmp); } catch (Exception e) { e.printStackTrace(); }}Copy the code

Running results:

a=1,b=2
a=2,b=1
Copy the code

Ah? Why are 1 and 2 ok? We must assume that it has something to do with the unboxing of Integer

Packing, unpacking concept

Integer indicates the boxing operation

Why is Integer a = 1 different from Integer a = new Integer(1)?

public Integer(int value) { this.value = value; } /** * Returns an {@code Integer} instance representing the specified * {@code int} value. If a new {@code Integer} instance is not * required, this method should generally be used in preference to * the constructor {@link #Integer(int)}, as this method is likely * to yield significantly better space and time performance by * caching frequently requested values. * * This method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param i an {@code int} value. * @return an {@code Integer} Instance representing {@code I}. * @since 1.5 */ public static Integer valueOf(int I) {if (I >= integerCache.low &&i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }Copy the code

ValueOf is recommended to initialize an Interger because we have a cache of -128-127 numbers and we define Integer a = 1 to do this, Set (numb, TMP); set(numb, TMP); Integercache. cache value changes before and after we call swap

Reflection before modification:

Reflection modified



Before reflection is modified

IntegerCache.cache[128]=0
IntegerCache.cache[129]=1
IntegerCache.cache[130]=2
Copy the code

Modified by reflection

IntegerCache.cache[128]=0
IntegerCache.cache[129]=2
IntegerCache.cache[130]=2
Copy the code

Set (numb, TMP) TMP = 1; numb = 1; numb = 1; numb = 1

Integer testA = 1;
Integer testB = 1;

Integer testC = 128;
Integer testD = 128;
System.out.println("testA=testB " + (testA == testB) + ",\ntestC=testD " + (testC == testD));
Copy the code

Output result:

testA=testB true,
testC=testD false
Copy the code

In this small example, the numbers between -128 and 127 are cached so that testA and testB refer to the same object in the same memory region. TestC testD is greater than 127 so it’s not cached, which is equivalent to two Integer objects, two objects in the heap. The two objects are not equal. In the previous example we passed

Integer a = new Integer(1);
Integer b = new Integer(2);
Copy the code

That’s why we have no problem with our switching algorithm.

So so far ourswapThe method can be improved
private static void swap(Integer numa, Integer numb) { int tmp = numa.intValue(); try { Field field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(numa, numb); field.set(numb, new Integer(tmp)); } catch (Exception e) { e.printStackTrace(); }}Copy the code

Set (numb, TMP); set(numb, new Integer(TMP));

So far, we have passed the interview, and there is still a question I have not answered. Set (numb, TMP) integer.valueof () and field.set(numb, new Integer(TMP)). When integer.value is assigned an int, the JVM detects that int is not an Integer and executes integer.valueof (). Numb. Set (numb, new Integer(TMP)) is set to Integer.

Over Thanks