Home of The JavaSpecialists' Newsletter

114Compile-time String Constant Quiz

Posted: 2005-09-16Category: LanguageJava Version: AllDr. Heinz M. Kabutz
 

Abstract: 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 :)

Birthday and Anniversary Special: Since the 30th November 2017 is the 17th anniversary of our newsletter and my birthday is coming up on the 4th December, we are giving away a 30% discount on our new Data Structures for Java 9 Course (Late 2017 Edition). Whether you are a seasoned Java programmer or you just want to get ready for your next job interview, this course will help you. Besides detailed lectures, the course has over 130 questions that will help you discover what you missed.

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

 

Related Articles

Browse the Newsletter Archive

About the Author

demo

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

Nobody ever wants to call a Java performance consultant, but with first-hand experience repairing and improving commercial Java applications - JavaSpecialists are a good place to start...

Threading Emergency?

If your system is down, we will review it for 15 minutes and give you our findings for just 1 € without any obligation.