1. Question - What the program would output?
int i = 127; boolean b = (Integer.valueOf(i) == Integer.valueOf(i)); System.err.println(b); i = 128; b = (Integer.valueOf(i) == Integer.valueOf(i)); System.err.println(b);
We can immediately know the answer after we look at the source code.
public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
We can know it doesn't cache all Integer values, as this may consume too much memory, so it just caches the numbers between [-128, 127] in the static IntegerCache class, for other int numbers, each time it would return a new Integer. Class Long also only caches the numbers between [-128, 127].
The output of the previous program would be obvious now: true and false.
2. Autobox and Auto-unbox
2. 1 How is it implemented in JDK?
Simply put, when we call "Integer wrapper = 2;", the java compile would translate it to "Integer wrapper = Integer.valueOf(wrapper);".
When we call "int i = wrapper;", the java compile would translate it to "int i = wrapper.intValue();".
You can verify this by using javap to look. at the compiled java class: javap -c IntegerTester.
Long.valueOf(0L).equals(0)?
2.2 What would be the output of the following program?
Long l = 0L; Integer i = 0; System.out.println(l.equals(i)); System.out.println(i.equals(l));
The program above is same as:
So let's look at the JDK source code again:
public final class Integer extends Number implements Comparable<Integer> { public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; } } public final class Long extends Number implements Comparable<Long> { public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; } }
From the code, we can see if the type of parameter is not same, these method would return false.
So the output of previous program would be false, false.
Autobox and auto-unbox are good features, as it will convert the primitive type or wrapper type to the needed one, and we can write less code, but we should use it carefully, as usually, it may create object under the hood, if we are unaware of this, it may cause big performance penalty.
public void test(Integer i) { while (i < 0) { --i; } }
In the previous program, in each step, JVM actually does this: it calls i.intValue(), subtracts it by one, and then create a new Integer value.
For JVM, it would be same as:
while (i.intValue() < 0) {
i = Integer.valueOf(i.intValue() - 1);
}
So in each step, we would unnecessary create one Integer value and call intValue methods twice.
Try to use javap to look at the compiled class file.
If we know this, we can change our code like the below, it would be faster.
public void test(Integer i) { int j = i; while (j < 0) { --j; } i = j; }
In our code, it's to better use primitive type as often as possible.
In Integer class there are many other interesting and useful methods, some methods are listed below, is it cool and a little confusing? Try to figure it out.
public final class Integer extends Number implements Comparable<Integer> { public static int reverseBytes(int i) { return ((i >>> 24) ) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | ((i << 24)); } public static int reverse(int i) { // HD, Figure 7-1 i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555; i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333; i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f; i = (i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24); return i; } public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x55555555); i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); i = (i + (i >>> 4)) & 0x0f0f0f0f; i = i + (i >>> 8); i = i + (i >>> 16); return i & 0x3f; } }
Reference: