Java Language


Java Pitfalls All Versions

Java SE 1.0
Java SE 1.1
Java SE 1.2
Java SE 1.3
Java SE 1.4
Java SE 5
Java SE 6
Java SE 7
Java SE 8
Java SE 9 (Early Access)

This draft deletes the entire topic.

expand all collapse all

Examples

  • 13

    (This pitfall applies equally to all primitive wrapper types, but we will illustrate it for Integer and int.)

    When working with Integer objects, it is tempting to use == to compare values, because that is what you would do with int values. And in some cases this will seem to work:

    Integer int1_1 = Integer.valueOf("1");
    Integer int1_2 = Integer.valueOf(1);
    
    System.out.println("int1_1 == int1_2: " + (int1_1 == int1_2));          // true
    System.out.println("int1_1 equals int1_2: " + int1_1.equals(int1_2));   // true
    

    Here we created two Integer objects with the value 1 and compare them (In this case we created one from a String and one from an int literal. There are other alternatives). Also, we observe that the two comparison methods (== and equals) both yield true.

    This behavior changes when we choose different values:

    Integer int2_1 = Integer.valueOf("1000");
    Integer int2_2 = Integer.valueOf(1000);
    
    System.out.println("int2_1 == int2_2: " + (int2_1 == int2_2));          // false
    System.out.println("int2_1 equals int2_2: " + int2_1.equals(int2_2));   // true
    

    In this case, only the equals comparison yields the correct result.

    The reason for this difference in behavior is, that the JVM maintains a cache of Integer objects for the range -128 to 127. (The upper value can be overridden with the system property "java.lang.Integer.IntegerCache.high"). For values in this range, the Integer.valueOf() will return the cached value rather than creating a new one.

    Thus, in the first example the Integer.valueOf(1) and Integer.valueOf("1") calls returned the same cached Integer instance. By contrast, in the second example the Integer.valueOf(1000) and Integer.valueOf("1000") both created and returned new Integer objects.

    The == operator for reference types tests for reference equality (i.e. the same object). Therefore, in the first example int1_1 == int2_1 is true because the references are the same. In the second example int2_1 == int2_2 is false because the references are different.

  • 8

    A common mistake for Java beginners is to use the == operator to test if two strings are equal. For example:

    public class Hello {
        public static void main(String[] args) {
            if (args.length > 0) {
                if (args[0] == "hello") {
                    System.out.println("Hello back to you");
                } else {
                    System.out.println("Are you feeling grumpy today?");
                }
            }
        }
    }
    

    The above program is supposed to test the first command line argument and print different messages when it and isn't the word "hello". But the problem is that it won't work. That program will output "Are you feeling grumpy today?" no matter what the first command line argument is.

    When you use == to test strings, what you are actually testing is if two String objects are the same Java object. Unfortunately, that is not what string equality means in Java. In fact, the correct way to test strings is to use the equals(Object) method. For a pair of strings, that will test to see if they consist of the same characters in the same order ... which is what we usually want.

    public class Hello2 {
        public static void main(String[] args) {
            if (args.length > 0) {
                if (args[0].equals("hello")) {
                    System.out.println("Hello back to you");
                } else {
                    System.out.println("Are you feeling grumpy today?");
                }
            }
        }
    }
    

    But it actually gets worse. The problem is that == will give the expected answer in some circumstances. For example

    public class Test1 {
        public static void main(String[] args) {
            String s1 = "hello";
            String s2 = "hello";
            if (s1 == s2) {
                System.out.println("same");
            } else {
                System.out.println("different");
            }
        }
    }
    

    Surprisingly (perhaps), this will print "same", even though we are testing the strings the wrong way. Why is that? Because the Java Language Specification (Section 3.10.5: String Literals) stipulates that any two string >>literals<< consisting of the same characters will actually be represented by the same Java object. Hence, the == test will give true for equal literals. (The string literals are "interned" and added to a shared "string pool" when your code is loaded ... but that is actually an implementation detail.)

    To add to the confusion, the Java Language Specification also stipulates that when you have a compile-time constant expression that concatenates two string literals, that is equivalent to a single literal. Thus:

        public class Test1 {
        public static void main(String[] args) {
            String s1 = "hello";
            String s2 = "hel" + "lo";
            String s3 = " mum";
            if (s1 == s2) {
                System.out.println("1. same");
            } else {
                System.out.println("1. different");
            }
            if (s1 + s3 == "hello mum") {
                System.out.println("2. same");
            } else {
                System.out.println("2. different");
            }
        }
    }
    

    This will output "1. same" and "2. different". In the first case, the + expression is evaluated at compile time and we compare one String object with itself. In the second case, it is evaluated at run time and we compare two different String objects

    In summary, using == to test strings in Java is almost always incorrect, but it is not guaranteed to give the wrong answer.

  • 7

    Every time a program opens a resource, such as a file or network connection, it is important to free the resource once you are done using it. Similar caution should be taken if any exception were to be thrown during operations on such resources. One could argue that the FileInputStream has a finalizer that invokes the close() method on a garbage collection event; however, since we can’t be sure when a garbage collection cycle will start, the input stream can consume computer resources for an indefinite period of time. The resource must be closed in a finally section of a try-catch block:

    Java SE 7
    private static void printFileJava6() throws IOException {
        FileInputStream input;
        try {
            input = new FileInputStream("file.txt");
            int data = input.read();
            while (data != -1){
                System.out.print((char) data);
                data = input.read();
            }
        } finally {
            if (input != null) {
                input.close();
            }
        }
    }
    

    Since Java 7 there is a really useful and neat statement introduced in Java 7 particularly for this case, called try-with-resources:

    Java SE 7
    private static void printFileJava7() throws IOException {
        try (FileInputStream input = new FileInputStream("file.txt")) {
            int data = input.read();
            while (data != -1){
                System.out.print((char) data);
                data = input.read();
            }
        }
    }
    

    The try-with-resources statement can be used with any object that implements the Closeable or AutoClosable interface. It ensures that each resource is closed by the end of the statement. The difference between the two interfaces is, that the close() method of Closeable throws an IOException which has to be handled in some way.

    In cases, where the resource is opened before but should the safely be closed after use, one can assign it to a local variable inside the the try-with-resources

    Java SE 7
    private static void printFileJava7(InputStream extResource) throws IOException {
        try (InputStream input = extResource) {
            ... //access resource
        }
    }
    

    The local resource variable created in the try-with-resources constructor is effectively final.

I am downvoting this example because it is...

Syntax

Syntax

Parameters

Parameters

Remarks

Remarks

Still have a question about Java Pitfalls? Ask Question

Topic Outline