Did you know? Programmers convert coffee to code.

If you like my articles, sponsor me a coffee.

At the other end of the link I post some results of my other hobbies, like playing bass. It is worth checking it out ;)

April’s fool — foolish Java games not for production

I’ve read an article in the German Javamagazin from Arno Haase and I think I’ll show you the foolish games of Java on the 1st of April.

The title of the article is “More or less final” and the main thought is about final variables in Java — and they are not as final as we might think. So I adapted some of the examples and I encourage you to think about the results of the following code snippet before reading on.

<br />
public static void main(String[] args) throws Exception<br />
{<br />
    String s1 = new StringBuilder(&quot;Welcome&quot;).toString();<br />
    String s2 = s1.intern();</p>
<p>    System.out.println(s1 != &quot;Welcome&quot;);<br />
    System.out.println(&quot;Welcome&quot;.equals(s1));<br />
    System.out.println(s2 == &quot;Welcome&quot;);<br />
    System.out.println(&quot;Welcome&quot;.equals(s2));<br />
    magick();<br />
    System.out.println(&quot;Welcome&quot;);<br />
    System.out.println(s1);<br />
    System.out.println(s2);</p>
<p>    System.out.println(&quot;-------------&quot;);</p>
<p>    integerTricks();<br />
    System.out.println((Integer)5 * 3);<br />
    System.out.println(Integer.valueOf(5));<br />
}<br />

Note: Do not use anything in a production code. Developers expect to have final variables that are really final and after some months you won’t remember why you created this reflection method — and what it really does.

OK, I admit without knowing what magick() and integerTricks() do is it less easy to figure out the results of the print statements. However, if you think about Java and the immutable variables (as String) and Integer behavior, the differences between “equals()” and “==” you’d think the result would be:

<br />
true<br />
true<br />
false<br />
true<br />
Welcome<br />
Welcome<br />
Welcome<br />
-------------<br />
15<br />
5<br />

Line 4 in the “main method” listing does something strange to get the “is the same object” operator (==) return true for s2. If you read the Javadoc you’ll see, that the String class maintains it’s own String pool and you’ll get the same object back for two Strings if they are equal.

The result of the example code is as follows (and not the same as expected):

<br />
true<br />
true<br />
true<br />
true<br />
Goodbye<br />
Welcome<br />
Goodbye<br />
-------------<br />
33<br />
11<br />

Yes, the two methods magick() and integerTricks() do something strange with the code.

String pool

<br />
private static void magick() throws Exception<br />
{<br />
    final Field field = String.class.getDeclaredField(&quot;value&quot;);<br />
    field.setAccessible(true);<br />
    field.set(&quot;Welcome&quot;, &quot;Goodbye&quot;.toCharArray());<br />
}<br />

The magick() method alternates the array (or global pool) of strings which the JVM creates at load-time to save some memory. Strings, which are composed at runtime (i.e. with StringBuilders) will not be stored automatically in this pool. And the String.intern() method forces the String to this pool. As the magick() method operates on  the pool of Strings the StringBuilder-created String stays “Welcome” although the intern and the previously printed one gets “Goodbye”.

Integer cache

<br />
private static void integerTricks() throws ClassNotFoundException, Exception<br />
{<br />
    final Class cls = Class.forName(&quot;java.lang.Integer$IntegerCache&quot;);<br />
    final Field field = cls.getDeclaredField(&quot;cache&quot;);<br />
    field.setAccessible(true);<br />
    final Integer[] cache = (Integer[])field.get(null);</p>
<p>    cache[133] = 11;<br />
}<br />

Integer objects have an internal cache too. This is maintained for a high-performace auto-boxing of ints into Integer objects. The default caching is from -128 to 127 enabled so the 133th element in this array is the number 5. Because we change the value of 5 to 11 every auto-boxing (as in the main method) and Integer.valueOf(5) results in an unexpected 11 — so (Integer) 5 * 3 yields 33 instead of 15.

Conclusion

The Java Reflection API allows modification of final fields too — so be careful with your final variables. They are not as final as you might think. So as in my note at the beginning: do not use this in a real application. The only meaningful scenario (I agree with Arno Haase) is de-serialization of objects. A generic code does not know which constructor to call with which values if it tries to reconstruct an object state from byteflow. But it can create an “empty” object and fill the fields via the Reflection API. And after the object is fully initialized the JLS guarantees the same visibility options as if the Object would have been created along the “normal way”.

And as always: the sources are found in my GitHub repository.

Share the knowledge!
GHajba

Senior developer, consultant, author, mentor, apprentice. I love to share my knowledge and insights what I achieve through my daily work which is not trivial -- at least not for me.

Click Here to Leave a Comment Below

>
%d bloggers like this: