Difference between revisions of "Java gotchas"

From OWASP
Jump to: navigation, search
(Added link to JLS 5.1.7)
(Integer Caching)
Line 12: Line 12:
 
  But they do contain the same value
 
  But they do contain the same value
 
  These two are the same, because they use the same reference.
 
  These two are the same, because they use the same reference.
=== Autoboxing ===
+
=== Integer Caching===
Java 5's autoboxing and unboxing features add some new gotchas to testing for equalityConsider the following example:
+
Since Java 5 Integer caching was introduced.  When creating an Integer using the following code:
 +
 
 +
Integer myNumber = 10
 +
Or
 +
Integer myNumber = Integer.valueOf(10);
 +
 
 +
256 Integer objects are created in the range of -128 to 127 which are all stored in an Integer array.  This caching functionality can be seen by looking at the inner class, IntegerCache, which is found in Integer:
 +
 
 +
<pre>
 +
private static class IntegerCache
 +
{
 +
  private IntegerCache(){}
 +
 
 +
  static final Integer cache[] = new Integer[-(-128) + 127 + 1];
 +
 +
  static
 +
  {
 +
    for(int i = 0; i < cache.length; i++)
 +
    cache[i] = new Integer(i - 128);
 +
  }
 +
  }
 +
   
 +
public static Integer valueOf(int i)
 +
{
 +
final int offset = 128;
 +
if (i >= -128 && i <= 127) // must cache
 +
        {
 +
    return IntegerCache.cache[i + offset];
 +
}
 +
        return new Integer(i);
 +
}
 +
</pre>
 +
 
 +
So when creating an object using Integer.valueOf or directly assigning a value to an Integer within the range of -128 to 127 the same object will be returned. Therefore, consider the following example:
 +
 
 
  Integer i = 100;
 
  Integer i = 100;
 
  Integer p = 100;
 
  Integer p = 100;
Line 19: Line 53:
 
  if (i != p)  System.out.println("i and p are different.");
 
  if (i != p)  System.out.println("i and p are different.");
 
  if(i.equals(p))  System.out.println("i and p contain the same value.");
 
  if(i.equals(p))  System.out.println("i and p contain the same value.");
The output is:
+
 
 +
The output is:  
 
  i and p are the same.
 
  i and p are the same.
 
  i and p contain the same value.
 
  i and p contain the same value.
 +
 +
It is important to note that object i and p only equate to true because they are the same object, the comparison is not based on the value, it is based on object equality. If Integer i and p are outside the range of -128 or 127 the cache is not used, therefore new objects are created. When doing a comparison for value always use the “.equals” method. It is also important to note that instantiating an Integer does not create this caching. So consider the following example:
 +
 +
Integer i = new Integer (100);
 +
Integer p = new Integer(100);
 +
if(i==p) System.out.println(“i and p are the same object”);
 +
if(i.equals(p)) System.out.println(“ i and p contain the same value”);
 +
 +
In this circumstance, the output is only:
 
   
 
   
The code above shows that i == p because the values are first unboxed into the primitive int type, and then the comparison is performed.  But this is only true for Integer values >= -128 and <= 127!  In other words, if the above example is changed so that i = 200 and p = 200, then i == p evaluates to false and the output would be:
+
  i and p contain the same value
i and p are different.
+
 
  i and p contain the same value.
+
Remember that “==” is always used for object equality, it has not been overloaded for comparing unboxed values.
  
 
This behavior is documented in the [http://java.sun.com/docs/books/jls Java Language Specification] [http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7 section 5.1.7]. Quoting from there:
 
This behavior is documented in the [http://java.sun.com/docs/books/jls Java Language Specification] [http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7 section 5.1.7]. Quoting from there:
 
:If the value ''p'' being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let ''r1'' and ''r2'' be the results of any two boxing conversions of ''p''. It is always the case that ''r1'' == ''r2''.
 
:If the value ''p'' being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let ''r1'' and ''r2'' be the results of any two boxing conversions of ''p''. It is always the case that ''r1'' == ''r2''.
 
Notice in the above example that the declaration was:
 
Integer i = 100;
 
If this had been:
 
Integer i = new Integer(100);
 
Then the output would have been different:
 
i and p are different.
 
i and p contain the same value.
 
  
 
== Incrementing values ==
 
== Incrementing values ==

Revision as of 02:11, 25 June 2007

Equality

Object equality is tested using the == operator, while value equality is tested using the .equals(Object) method. For example:

String one = new String("abc");
String two = new String("abc");
String three = one;
if (one != two) System.out.println("The two objects are not the same.");
if (one.equals(two)) System.out.println("But they do contain the same value");
if (one == three) System.out.println("These two are the same, because they use the same reference.");

The output is:

The two objects are not the same.
But they do contain the same value
These two are the same, because they use the same reference.

Integer Caching

Since Java 5 Integer caching was introduced. When creating an Integer using the following code:

Integer myNumber = 10

Or

Integer myNumber = Integer.valueOf(10);

256 Integer objects are created in the range of -128 to 127 which are all stored in an Integer array. This caching functionality can be seen by looking at the inner class, IntegerCache, which is found in Integer:

 private static class IntegerCache 
 {
   private IntegerCache(){}
   
   static final Integer cache[] = new Integer[-(-128) + 127 + 1];
 
   static 
   {
     for(int i = 0; i < cache.length; i++)
     cache[i] = new Integer(i - 128); 
   }
 }
    
 public static Integer valueOf(int i) 
 {
	final int offset = 128;
	if (i >= -128 && i <= 127) // must cache 
        { 
	    return IntegerCache.cache[i + offset];
	}
        return new Integer(i);
 }

So when creating an object using Integer.valueOf or directly assigning a value to an Integer within the range of -128 to 127 the same object will be returned. Therefore, consider the following example:

Integer i = 100;
Integer p = 100;
if (i == p)  System.out.println("i and p are the same.");
if (i != p)   System.out.println("i and p are different.");	
if(i.equals(p))  System.out.println("i and p contain the same value.");

The output is:

i and p are the same.
i and p contain the same value.

It is important to note that object i and p only equate to true because they are the same object, the comparison is not based on the value, it is based on object equality. If Integer i and p are outside the range of -128 or 127 the cache is not used, therefore new objects are created. When doing a comparison for value always use the “.equals” method. It is also important to note that instantiating an Integer does not create this caching. So consider the following example:

Integer i = new Integer (100);
Integer p = new Integer(100);
if(i==p) System.out.println(“i and p are the same object”);
if(i.equals(p)) System.out.println(“ i and p contain the same value”);

In this circumstance, the output is only:

i and p contain the same value

Remember that “==” is always used for object equality, it has not been overloaded for comparing unboxed values.

This behavior is documented in the Java Language Specification section 5.1.7. Quoting from there:

If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

Incrementing values

Be careful of the post-increment operator:

 int x = 5;
 x = x++;
 System.out.println( x );

Prints 5.