|
The Java Specialists' Newsletter
Issue 114 2005-09-16
Category:
Language
Java version: All Compile-time String Constant Quizby Dr. Heinz M. KabutzAbstract:
When we change libraries, we need to do a full recompile of our code, in
case any constants were inlined by the compiler. Find out which constants
are inlined in this latest newsletter.
Welcome to the 114th edition of The Java(tm) Specialists' Newsletter. I learned last
Tuesday that I had been nominated as a Java
Champion. The nomination was approved this Monday.
So now I am one of the world's first elected Java Champions.
I do not fully understand yet what that means, but I am
overwhelmed that I had been noticed, considering how small
our subscription base is :)
Upcoming Java Specialist Master Courses:
- please click here to sign up.
As from May 2010, we are also offering this course on the island of Crete. We
only accept 6 students per class in Crete, due to the size of our conference
room. Please book early to avoid disappointment!
San Jose CA, Mar 16-19 2010, $3500 Ottawa, Canada, Mar 22-25 2010, $3500 Oslo, Norway, Apr 13-16 2010, Kr 24500 Montreal, Canada, Apr 20-23 2010, $3500 Toronto, Canada, May 17-20 2010, $3500 Chania, Crete, May 25-28, Jun 29-Jul 2 or Aug 24-27 2010, €2500
In-house courses if these dates or locations do not suit you - click here for more information. Compile-time String Constant Quiz
This week's newsletter is based on a quiz sent to me by Clark
Updike from the John Hopkins University Applied Physics
Laboratory. Thanks Clark :)
Consider the following interface (remember that all fields in
an interface are automatically public static
final):
public interface StaticFinalTest {
String LITERAL = "Literal";
String LITERAL_PLUS = "Literal" + "Plus";
String LITERAL_NEW = new String("LiteralNew");
String LITERAL_CONCAT = "LiteralConcat".concat("");
}
And we can use this as follows:
public class StaticFinalTestClient {
public static void main(String[] args) {
System.out.println(StaticFinalTest.LITERAL);
System.out.println(StaticFinalTest.LITERAL_PLUS);
System.out.println(StaticFinalTest.LITERAL_NEW);
System.out.println(StaticFinalTest.LITERAL_CONCAT);
}
}
When we run the program, we see:
Literal
LiteralPlus
LiteralNew
LiteralConcat
Now Change StaticFinalTest, compile it, but do not compile
StaticFinalTestClient. If you in an IDE, you will have to
compile it from the command line.
public interface StaticFinalTest {
String LITERAL = "LiteralXXX";
String LITERAL_PLUS = "Literal" + "PlusXXX";
String LITERAL_NEW = new String("LiteralNewXXX");
String LITERAL_CONCAT = "LiteralConcat".concat("XXX");
}
Here is the quiz: What is the output? (scroll down for the
answer)
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
Literal
LiteralPlus
LiteralNewXXX
LiteralConcatXXX
At compile time, all final fields that have a
constant value, and are either primitive or String, get
inlined by the compiler. This includes of course the Strings
in our StaticFinalTest class. Since the NEW and CONCAT
values are not compile time literals they are not inlined.
So, when you change libraries, you have to recompile all your
code. This limits how you change between libraries. You
cannot simply swap out libraries at runtime, because
if you as soon as you use constants, they are inlined and
require a full recompile of your code.
Whilst I would think this is really widely understood, I have
met a number of Java programmers who did not realise this.
Unit Testing
Be careful when writing unit tests. If you look at the
following (incorrect) code, we can write unit tests that make
it appear correct:
public class Car {
private final String registrationNumber;
public Car(String registrationNumber) {
this.registrationNumber = registrationNumber;
}
public boolean equals(Object o) {
if (!(o instanceof Car)) return false;
return registrationNumber == ((Car) o).registrationNumber;
}
public int hashCode() {
return registrationNumber.hashCode();
}
}
The code is obviously incorrect, because it compares Strings
using the == operator, instead of equals(). But look at this
unit test, which one would you write?
import junit.framework.TestCase;
public class CarTest extends TestCase {
public void testIncorrect() {
assertEquals(
new Car("CET192233"),
new Car("CET192233"));
}
public void testCorrect() {
assertEquals(
new Car(new String("CET192233")),
new Car("CET192233"));
}
}
The first test is incorrect, since it compares the Car
objects with identical strings, so the equals() method
appears correct.
In JDK 1.1 and 1.0, final methods were inlined as
well if you compiled with the -O option. Since
Java 2, final methods are only inlined at runtime by the
hotspot compiler (if necessary).
Kind regards
Heinz
Language Articles
Related Java Course
|