The Java syntax

Java syntax is largely inspired by the C language. You can find a good introduction to C here, and a summary of the syntax here. A recommended reading is Kernighan & Ritchie's The C Programming Language.

Differences in the base syntax between C and Java

Java is an object-oriented programming language. It uses the class as the basic building block of a program (C uses the function). Java comes with an extensive library of classes organized in packages. See the Sun Java tutorials and Java API reference to get a feel of the Java library.

To define a class use:

class MyClass extends BaseClass implements SomeInterface
   //Fields, constructors and methods go here
To make a package for your classes put this at the top of your source files:
package com.javasyntax.mypackage;
To use classes defined in another package use one of:
import java.text.*;        //Import all classes in package java.text
import java.util.HashMap;  //Import class HashMap in package java.util myfile = new"test");
                           //Use class File in package

By convention variables and functions start with a lower case and classes start with an upper case. By convention composed names have each successive word capitalized. Examples:

StringBuilder stringBuilderVariable;
By convention package names are all lowercase with dot (.) as separator. By convention constants are all upper case with underscore (_) as separator. Examples:

Pitfalls of the Java language

Despite its relative maturity and large codebase, Java still offers some pitfalls that can trap the programmer who is not aware of them. We will discuss some below.

Reference passing

In Java, primitive type variables are stored as values and object variables are stored as references. This has direct implications on the way you program. For example, if the class has a method that returns an array object:

Element[] getElements();
There are different cases to consider Note that because the caller receives a reference to the array, in the second case the caller can modify the array. To prevent this you need either to return a copy of the array in getElements() (which amounts to the first case) or document that the array returned by getElements should not be modified. In the latter case the class relies on the caller to make a copy of the array before modifying it.

The same considerations apply to arguments of functions.

Pass by value

Since primitive types are passed by value, the callee cannot modify a primitive variable passed by the caller. You can use a Java programming trick to allow a primitive type to be modified. For example:

int[] pair = {4, 8};
swapValues(pair);   //Swaps the values => pair == {8, 4}
By having the values inside an array, the callee can modify the contents of the array because it is passed as a reference. This is also useful for String arguments that are immutable.

Co-variance of array types

In Java arrays are covariant. That means that a function declared with a Number[] parameter can take a Double[] argument. However, setting a value in that array can result in a type error at runtime. Consider:

void resetFirstValue(Number[] values)
    if (values != null && values.length > 0)
        values[0] = new Integer(0);  //Reset first value to 0
That compiles fine, but the following will result in a runtime error:
resetFirstValue(new Double[] {1.0, 2.0, 3.0});
The Java runtime detects that you are inserting an Integer value in a Double[] array, and Integer is not a subtype of Double. Ideally the type system would inform you that will not work at compile time so you don't have to go and change your code when that happens at the eleventh hour in a deployed customer system. In reality the worst can happen when you use covariance.

[Java 5] Recursive types

Java 5 introduced generic types into the language. This helps reasoning about types and type safety, hence improving the quality of code. But at times it can become hard to comprehend. Consider the following class in the standard Java library

class Enum<E extends Enum<E>>
What does that mean?

Well simply put, considering a class MyEnum extending Enum, the superclass is Enum<MyEnum>. That way the methods in Enum "know" about MyEnum and can use the type in their signature. Note that you can not actually extend Enum in Java. Implement your own recursive class to experiment with the feature.

[Java 5] Type erasure

One constraint self-imposed by the designers of Java when they introduced generic types was to allow older Java programs to compile without error. At the same time, it was important that the Java library use generics to fulfill the promise of better and safer code in new programs.

The compromise that emerged was to use type erasure for implementing generics. So even though Java 5 introduces all these checks that your program is properly typed, internally the runtime cannot know whether the program is valid in regards to generic types. The additional type information is erased.

The result is that type casting a generic type is basically unsafe. Casting errors rely on runtime type information, which is simply too incomplete in the case of generic types. For example:

Integer first(List values)
    if (values != null && values.size() > 0)
        List<Integer> intValues = values;
        return intValues.get(0);
    return null;
Note that Java does not even require explicit casting. This will cause a runtime error in intValues.get(0):
first(Arrays.asList("Hello", "World!"));


Java uses a garbage collector to reclaim unused memory. That way the programmer does not have to worry about freeing memory that was allocated, making the programmer more productive and the code less prone to memory errors.

If you are used to C++ style of constructors and destructors, you may be tempted to use the Object finalize() method to perform clean-up operations on an object before its memory is freed. However note the following pitfalls with the finalize() method

Additionally, the finalize() method should of course restrain from creating a new reference to the object being finalized.

Garbage collection in the Sun JVM

The typical platform for running Java programs is the Sun Java Virtual Machine. This is available on most operating systems. The Sun JVM has a notion of new and old objects, which impacts the likeliness of the object memory being collected. Programs usually create numerous short-lived objects which memory is soon reclaimed. Then there are objects which are kept longer in memory because the program holds a reference to them. As they age they go in the old object pool which is seldom examined by the garbage collector.

Finally there are permanent objects that are always kept in memory. Classes created by reflection enter in this category. As a consequence always keep the number of such classes within reasonable limits.

Use the -X options of the Java runtime to tweak the object ageing system for your specific application footprint.

Error in finally

If an error occurs in a finally clause, it is thrown to a higher level. That is not generally what the programmer wants. A good habit is to put the instructions of the finally clause inside a try block.

Interrupted thread

In Java methods such as Thread.sleep() or Object.wait() can throw InterruptedException. This is meant for the code that contains these methods to allow the current activity to terminate early or report a status, so that the application does not stay in apparent standstill. It can be triggered by an alarm or by some other thread.

When an InterruptedException is caught, the interrupted status of the thread is usually reset to false. One possible course of action is to perform some cleanup work and re-throw the exception. In the run() method of Runnable you cannot re-throw, so the correct pattern is to restore the interrupt status to true and terminate:

public void run()
    try {
        while (true)
            //Do something and wait
    catch (InterruptedException e) { 
        //Clean up then restore the interrupted status:
Another option is to continue working, keeping track of the fact that the thread was interrupted in a variable:
public void run()
    boolean wasInterrupted = false;
    try {
        while (true)
            try {
                //Do something and wait
            catch (InterruptedException e) {
                //Report status and register interrupt:
                wasInterrupted = true;
    finally {
    	//Restore the interrupted status
        if (wasInterrupted)

Partially initialized object

It is not advisable to call non-private functions in an object constructor, unless the class is final. The reason is that subclasses may redefine the non-private function to access fields that do not exist in the superclass. Since the superclass constructor is called first this can result in ugly issues.

A simple example:

class Word
    String word;
    Word(String w) { word = trim(w); }
    String trim(String s) { return s.replaceAll("\\W", ""); }

public class ExtendedWord extends Word
    ExtendedWord(String w) { super(w); }
    String trimPattern = "\\P{Alpha}";
    String trim(String s) { return s.replaceAll(trimPattern, ""); }
This causes a NullPointerException in the ExtendedWord constructor, because at the time the superclass constructor calls trim() the trimPattern in ExtendedWord is still null.

[Java 5] Automatic boxing of primitive types

Java 5 introduced the automatic boxing and unboxing of primitive types, which makes it convenient to work with Integer, Double or Boolean values. Unfortunately it combines poorly with automatic casting arithmetic.

To illustrate the problem, let us see what happens when inserting an object in a map which is either an Integer or a Double. In previous versions of Java you would do

HashMap map = new HashMap();
map.put("x", (useDoubles? new Double(x): new Integer(x)));
In Java 5, that will always insert a Double value in the map. The reason is that Java automatically makes a double of an expression that includes an int and a double. With automatic boxing and unboxing, new Double(x) and new Integer(x) are converted to a double and an int respectively, then boxed back into a Double (as per previous rule) for putting into the map.

The workaround in that case is to replace the ?: operator with if ... else.

Numerical precision

Because floating point numbers are stored in binary and not decimal format, it is recommended to use DecimalFormat to convert a number to a string with a definite precision. That allows for rounding errors, eg. when converting a float value into a double. For example:

double rate = 0.10f;  //Store float value into a double
System.out.println("The rate is " + rate);
The output of this code is "The rate is 0.10000000149011612". A solution is to use:
String printedRate = new DecimalFormat("0.00").format(rate);

IEEE 754 floating point numbers

Although Java partially conforms to IEEE 754, there are notable differences. One difference is that the least significant bit is always rounded towards zero, while the standard also provides up and down. Also Java does not signal operations that result in NaN, overflow or underflow. For example 0.0 / 0.0 does not throw an exception. The function Double.doubleToRawLongBits() allows to determine the actual bit pattern of the number when it is NaN.

Unicode encoding

The Unicode standard supports most scripts in use around the world. It also defines some transformations on text, for example converting a string to uppercase.

At the time Java was designed, all Unicode characters could fit in two bytes, so that is what Java uses as character type. Unfortunately there are several issues with this approach

To address the first point, Java breaks the four-byte Unicode characters into two surrogate "characters". However each half has no meaning by itself.

Corrections, suggestions e-mail me