Home » Java » Core Java » Printing arrays by hacking the JVM

About Peter Lawrey

Peter Lawrey

Printing arrays by hacking the JVM

Overview

One the most common gotchas in Java, is knowing how to print arrays. If an answer on how to print an array get more than 1000 upvotes, you have to wonder if there is a simpler way. Just about every other popular language has that simpler way, so it’s not clear to me why Java still does this.

Unlike other JDK classes, arrays don’t have a particularly sane toString() as it is inherited from Object.

It prints the type and address right?

Actually, it doesn’t print the address, it just looks as cryptic as one. It prints the internal representation of the type, and the hashCode() of the object. As all arrays are an Object, they have a hashCode() and a type and a synchronized lock, and every thing else an Object has, but no methods specific to an array. This is why the toString() isn’t useful for arrays.

What does it look like unhacked?

If I run the following program.

public class ObjectTest {
    boolean[] booleans = {true, false};
    byte[] bytes = {1, 2, 3};
    char[] chars = "Hello World".toCharArray();
    short[] shorts = {111, 222, 333};
    float[] floats = {1.0f, 2.2f, 3.33f, 44.44f, 55.555f, 666.666f};
    int[] ints = {1, 22, 333, 4_444, 55_555, 666_666};
    double[] doubles = {Math.PI, Math.E};
    long[] longs = {System.currentTimeMillis(), System.nanoTime()};
    String[] words = "The quick brown fox jumps over the lazy dog".split(" ");

    @Test
    public void testToString() throws IllegalAccessException {

        Map<String, Object> arrays = new LinkedHashMap<>();
        for(Field f : getClass().getDeclaredFields())
            arrays.put(f.getName(), f.get(this));
        arrays.entrySet().forEach(System.out::println);
    }
}

it prints.

booleans=[Z@277c0f21
bytes=[B@6073f712
chars=[C@43556938
shorts=[S@3d04a311
floats=[F@7a46a697
ints=[I@5f205aa
doubles=[D@6d86b085
longs=[J@75828a0f
words=[Ljava.lang.String;@3abfe836

I think that is obvious to everyone. o_O Like the fact that J is the internal code for a long and L is the internal code for a Java class. Also Z is the code for boolean when b is unused.

What can we do about it?

In this program it’s we end up having to write a special toString method for object needs to be called by our special method for printing a Map.Entry. Repeat this many times throughput your program and it’s just easier to avoid using arrays in Java because they are hard to debug.

What about hacking the JVM?

What we can do is change the Object.toString(). We have to change this class as it is the only parent of arrays we have access to. We cannot change the code for an array as it is internal to the JVM. There is no byte[] java class file for example for all the byte[] specific methods.

Take a copy of the source for java.lang.Object and replace the toString() with

     public String toString() {
        if (this instanceof boolean[])
            return Arrays.toString((boolean[]) this);
        if (this instanceof byte[])
            return Arrays.toString((byte[]) this);
        if (this instanceof short[])
            return Arrays.toString((short[]) this);
        if (this instanceof char[])
            return Arrays.toString((char[]) this);
        if (this instanceof int[])
            return Arrays.toString((int[]) this);
        if (this instanceof long[])
            return Arrays.toString((long[]) this);
        if (this instanceof float[])
            return Arrays.toString((float[]) this);
        if (this instanceof double[])
            return Arrays.toString((double[]) this);
        if (this instanceof Object[])
            return Arrays.deepToString((Object[]) this);
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

and in Java <= 8 we can add this class to the start of the bootclasspath by adding to the command line

    -Xbootclasspath/p:target/classes

(or wherever your classes have been compiled to) and now when we run our program we see

 booleans=[true, false]
bytes=[1, 2, 3]
chars=[H, e, l, l, o,  , W, o, r, l, d]
shorts=[111, 222, 333]
floats=[1.0, 2.2, 3.33, 44.44, 55.555, 666.666]
ints=[1, 22, 333, 4444, 55555, 666666]
doubles=[3.141592653589793, 2.718281828459045]
longs=[1457629893500, 1707696453284240]
words=[The, quick, brown, fox, jumps, over, the, lazy, dog]

just like in you would in just about any other language.

Conclusion

While this is a cool trick, the best solution is that they finally fix Java so it produces a sane output for arrays. It knows you need one and provides it, but hides it away in a class you have to google to find, so that every new Java developer has to have a WTF moment the first time they try to work with arrays.

Reference: Printing arrays by hacking the JVM from our JCG partner Peter Lawrey at the Vanilla Java blog.
(0 rating, 0 votes)
You need to be a registered member to rate this.
7 Comments Views Tweet it!
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

7
Leave a Reply

avatar
5 Comment threads
2 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
5 Comment authors
MasonJosé A. Romero L.Jarrod RobersonPeter LawreyYassinHajaj Recent comment authors

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
newest oldest most voted
Notify of
YassinHajaj
Guest
YassinHajaj

As much as I agree with the fact that it should be simplified

http://stackoverflow.com/a/8885343/5050667

This answer provides an argument in favor of not make it easy to print an array.

Peter Lawrey
Guest
Peter Lawrey

Not sure this is a particularly strong security feature. Anyone who can access the char[] can find a way to print it with a little effort.

YassinHajaj
Guest
YassinHajaj

I think it is just protect the dev from printing the array in a log or random file without noticing and that I could be accessed by people who does not have access to the code.

That was the point of the answer I think

Jarrod Roberson
Guest
Jarrod Roberson

Yeah, this is bad advice for many reasons. But the most fundamental reason is you should not be working with raw arrays unless there is a very specific need for it ( usually performance related ) and if you are trying to shave bytes/microseconds at that level then you already know how to print out an raw array of primitives.

If you are reading this, do not do what this is suggesting.

Peter Lawrey
Guest
Peter Lawrey

There are plenty of languages where working with array is a natural way to program, e.g. R, Matlab, so I found it interesting you objected to using arrays over hacking a core library (which shouldn’t be a natural thing to do in any language) I agree that the pain of working with arrays in Java is probably not worth the performance gain you get.

Note: Java 10 may support primitive types in generics. This means you will be able to have an ArrayList of int and Map of long to double for example.

José A. Romero L.
Guest
José A. Romero L.

This is nonsense. There’s a perfectly fine way to print arrays in java: import java.util.Arrays; public class ObjectTest { boolean[] booleans = {true, false}; byte[] bytes = {1, 2, 3}; char[] chars = “Hello World”.toCharArray(); short[] shorts = {111, 222, 333}; float[] floats = {1.0f, 2.2f, 3.33f, 44.44f, 55.555f, 666.666f}; int[] ints = {1, 22, 333, 4_444, 55_555, 666_666}; double[] doubles = {Math.PI, Math.E}; long[] longs = {System.currentTimeMillis(), System.nanoTime()}; String[] words = “The quick brown fox jumps over the lazy dog”.split(” “); @Test public void testToString() throws IllegalAccessException { System.out.println( “booleans=” + Arrays.toString( booleans ) ); System.out.println( “bytes=” + Arrays.toString(… Read more »

Mason
Guest
Mason

Bravo! Can’t get much simpler than that