This is a long document, for a single web page (about a dozen printed pages). In order that it not be any longer, it contains a number of links pointing to other pages where the more detailed information that would otherwise have to appear at that point can be found.

Different Kinds of Java Programs

Java is a truly object-oriented programming language, so any Java program will contain at least one class, and any Java program may be viewed as a collection of classes. Programs may be classified as follows:

A Java GUI (Graphical User Interface) may contain

Java Program Structure

In the typical situation

Java Programming Style and Documentation

As with any programming language, so with Java. People do not agree on the style to be used. The style we will use is documented elsewhere. It is neither unorthodox in any way, nor difficult to apply consistently. Note, however, that it is not the same as that used by Sun Microsystems.

Java, unlike most other languages, has a development tool called javadoc, and a particular comment style, designed to support code documentation directly, and to be processed by this tool. This does not, however, in any way remove the need for the usual kinds of style and documentation considerations that must be applied to all code.

Program Entities

From "smallest" to "largest", the entities that comprise a Java program are:

Comments

Java provides three kinds of comments, two directed at human readers, and a third which, although human readable, is directed more at the javadoc utility, which reads these comments and produces HTML files which document in a standard way the code in which these special comments are found:

Identifiers and Reserved Words

An identifier is the name of a programmer-defined entity. In Java, any identifier must begin with a letter, which may be followed by any number of letters and/or digits. In this context the underscore (_) and the dollar sign ($) are considered to be letters, though their use in identifier names is discouraged.

Like most programming languages, Java has certain identifiers that cannot be used in any way other than the way they were intended to be used by the Java designers. They are called reserved words, or sometimes just keywords, and you can find a list of them here. You can also find a table comparing C++ and Java keywords here.

Primitive Data Types and Literal Values

Type Compatibility and Type Conversions

It is often extremely useful to know when you can use a value of one type even though a value of a different type is formally called for, as well as when you can safely convert a value from one type to another. In other words, it is important to understand the notions of type compatibility and type conversion.

A type T1 is compatible with a type T2 if a value of type T1 can appear wherever a value of type T2 is expected, and vice versa.

The term type conversion refers to the conversion of a value of one type to a value of another type. Generally conversion between numerical types is permitted, as well as between values of type char and integer values within a certain range. A conversion from a "narrower" (smaller) type to a "wider" (larger) type will take place automatically without complaint from the compiler, while a conversion from a wider to a narrower type must be explicitly cast.

Conversion between object (reference) types is also possible, but trickier. More on this below.

Variable Declarations and Initializations for Primitive Data Types

C++ programmers will be very comfortable with declarations and initializations of primitive-type variables in Java, since they follow essentially the same patterns. A few examples follow, along with a couple of heads-up notes in the accompanying comments.

A particular note to remember is that the default values shown are only gives to class instance variables, while local variables in a method are not automatically initialized to these default values.

boolean b;  // default value is false
char c;     // default value is '\u0000'
byte ib;    // default value is 0
short is;   // default value is 0
int ii;     // default value is 0
long il;    // default value is 0
float f;    // default value is 0.0
double d;   // default value is 0.0

char ch1 = '\u0041'; // Digits are hex, so what's the character?
char ch2 = '\141';   // Digits are octal, so what's the character?

// Multiple declarations/initializations can appear on same line
int i, j = 7, k;

// Multiple variables can be initialized to same value
double a = b = c = 4.5;

// Java is more strongly typed than C++
float pi  = 3.14;  // compile-time error
double pi = 3.14;  // OK
float pi  = 3.14f; // also OK

Constant Definitions for Primitive Data Types

The Java keyword final is somewhat analogous to const in C++, though it does not have as many varied and potentially confusing uses as its C++ counterpart. Here are some examples of its use for defining named constants, which also illustrate the all-caps convention for the names themselves (with the underscore as word-separator).

// Define some constants
final int MAX_NUM_GUESSES = 10;
final float TAX_RATE = 0.15f;
final char BLANK_SPACE = ' ';

// Both a and b are final
final int a = 1, b = 2;

Input/Output

Java I/O is, or at least can be, quite complicated, given the enormous number of classes available to help Java programs perform it. In any case, we have opted to put our discussion of I/O on a page all its own, and you can find that discussion here.

Expressions and Operators

For the most part, the basic Java operators are similar to those of C++, and similar expressions behave similarly. As usual, you need to know, or be able to look up quickly

For a table of the Java operators, see here.

Here are some notes on the Java operators:

Statement Types

For the most part, Java has the usual assortment of statements that programmers have come to expect, and for C++ programmers in particular much will be familiar. We give below a short summary list of the available statements. For a table giving more details and some examples, see here.

Assignment Behavior

Abnormal Exit from a Java Program

If you want to get out of a Java program, no questions asked, use the following statement, in which the value of n can be used by another program at the operating system level as a "status code" giving information about how your program terminated:

System.exit(n);
A status code of 0 (zero) normally means successful termination, while a non-zero status code on exit often (but not always) indicates a problem of some kind.

Methods

A Java method looks quite a bit like a C++ function. But note that

Java methods can be overloaded. That is, a class can have several methods with the same name, as long as the parameter lists are different.

Classes and Objects

A Java class definition looks quite a bit like a C++ class definition. But note that

A class may have four different kinds of members:

Note that

For creating and initializing objects, classes need constructors, and it is important to remember that

To actually create and use objects, we proceed something like this:

// Invoke the default constructor
SomeClass obj1 = new SomeClass();

// Invoke a non-default constructor
SomeClass obj2 = new SomeClass(parameter_list);

// Invoke some methods
obj1.method1();               // method1 has a void return type
obj1.method2(parameter_list); // method2 has a void return type
int i = obj2.method1();       // method1 has an int return type

Access Modifiers and Rules ("Visibility Rules" or "Scope Rules")

The members (fields and methods) of a class may be given different levels of access from other parts of a program. This helps to provide "data hiding" and supports encapsulation.

Suppose a class is in a given package. Ranging from most to least accessible, the options for access to a method or variable in that class are:

Several tables showing essentially the same information in various forms can be seen here.

Garbage Collection and Object Finalization

These are two very different things, and must not be confused.

One of the strengths of Java is that the programmer does not have to deal directly with the allocation and de-allocation of memory for objects created "on the heap". Even though each use of new creates an object on the heap, when the program is finished with that object it can simply ignore the object from then on, and let the Java "garbage collector" take care of retrieving the memory occupied by the object and returning it to the free store.

This means that, although not impossible, memory leaks in Java are much less common and much less of a problem that they are in C++. For all practical purposes, the average programmer can assume they are not a problem.

On the other hand, memory is only one of the resources that an object might be "consuming" during its lifetime. For example, an object might have several open files at its disposal. In such cases, it is the responsibility of the programmer to deal appropriately with these resources when the program ceases to use that particular object. This is done through a process called "object finalization".

You should be aware of the potential need to perform this activity for one or more of your objects, but again we do not discuss the matter further, for the moment, except to make the following observation: The C++ notion of a "destructor" being automatically invoked to destroy an object and "clean up after it" does not exist in Java, but there is a finalize() method that you may need to redefine (override) to do what needs to be done for your object.

Value Types vs. Reference Types

Now that we have presented both primitive types and classes, it's a good time to emphasize the difference between the notions of "value type" and "reference type".

First of all, the primitive types are "value types". Sometimes we say they have "value semantics". Essentially we mean that when we deal with a value of a primitive type we are in fact dealing with its actual value, or at least a copy of it. For example, when we assign a primitive value, we get a copy of the value, and when we pass a primitive value as a parameter, we again get a copy of the actual value.

On the other hand, when we deal with objects of a class type, we often deal only with a "handle" or "reference" to the object, and not with the object itself. In this case the "reference semantics" mean, for example, that when we assign an object, it is only the reference that gets assigned, and when we pass an object as a parameter it is only (a copy of) the reference that is passed not (a copy of) the object itself.

This is an important distinction, and one that can cause some confusion until you have it clear in your mind.

Primitive types cannot be converted to reference types (which are 32 bit pointers), or vice versa. Certain conversions from one reference type to another are possible, but the ones of most interest are those performed up and down a class hierarchy. More on this below.

Wrapper Classes for Primitive Types

Values of primitive types are not, of course, objects. Sometimes, however, it may be convenient, or even necessary, to supply a primitive value where an object is expected. For this reason, and others, Java supplies so-called wrapper classes for each of the primitive types. The following table shows, on the second line the name of the wrapper class corresponding to the primitive type immediately above it on the first line.

boolean  char       byte  short  int      long  float  double
Boolean  Character  Byte  Short  Integer  Long  Float  Double

Note that, except for the Character and Integer wrapper classes, the name of the wrapper class is just the capitalized name of the primitive class.

The simplest way to think of a wrapper class is that it provides a way to get an object whose data member is a primitive value of whatever type you wish. Note that although these classes have methods, as we soon see, none of them permit the primitive data value stored in the object to be changed once the object has been created. We describe this by saying that objects of these wrapper class types are immutable.

The following examples show the sorts of things one might do with the Integer class, its constructors and its methods (including some static ones). Check the on-line Java documentation from Sun for the analogous capabilities of the other wrapper classes.

// Create an Integer object from a primitive int value
int i = 6;
Integer intObj1 = new Integer(i);

// Create an Integer object from a quoted string
Integer intObj2 = new Integer("123");
// Note that "abc" in place of "123" would cause a run-time
// exception to be thrown, since it is not a valid int value.

// Retrieve the primitive value and use it in a calculation
System.out.println(intObj2.intValue() + 7);

// Use a static method to get an int from a String
System.out.println(Integer.parseInt("7325") * 3);

// Use another static method to get an Integer object from a String
System.out.println(Integer.valueOf("456").intValue()+4);

Arrays and Strings

In Java, both arrays and strings are first-class objects, unlike the primitive arrays, and the strings based on character arrays, that are common to both C and C++ (though C++ also provides more sophisticated structures, of course). We provide a discussion of these two very important classes on a separate page, which is available here.

Subclasses and Inheritance

Java permits single inheritance. That is, a class may inherit from one (and only one) base class. The syntax is shown below.

class Base
{
    // Definition of class Base
}

class Derived extends Base
{
    // Definition of class Derived
    // All members of Base automatically included
}

The "Mother of All Classes" is class Object, from which any class automatically (and implicitly) inherits, provided it does not explicitly inherit from some other class.

If a class definition is qualified with the final keyword, this means that the class may not be used as a base class for inheritance.

When defining the body of a constructor for a derived class, a call to a constructor of the base class can be made, provided that call is the first statement in the body of the derived class. The syntax uses the keyword super as follows:

super(parameter_list_for_desired_base_class_constructor);

Constructors and Initialization: The Full Story
(adapted from Niemeyer and Knudsen)

This section describes how constructors are "chained together" to build an object, and when instance variable initialization occurs. Note that the rule has three parts, which are applied repeatedly for each successive constructor invoked. The three parts are distinguished by the nature of the first statement in the constructor.

Polymorphism, Upcasting and Downcasting, and Shadowing

Polymorphism refers to the principle that specific behavior (obtained by calling a specific method) can vary, depending on the actual type of the object. We can also say that it is the ability of different objects to respond to the same message (method call), but each in its own way. Or, we can say that polymorphism is a way of giving a method one name that is shared up and down a class hierarchy.

In Java, all instance methods are polymorphic by default. (Recall that in C++, polymorphism is not the default, and must be arranged via virtual functions.)

Since the result of a method call like x.doIt(); clearly depends on what x refers to, several calls to this same method will generally produce different results if x is made to refer to a different object before each call is made.

However, x has a particular reference type, of course, so we cannot just assign, willy-nilly, any old object to x. So what can we do, and why would we want to do it?

Well, for example, if x is of a certain base class type, we can assign any object of any type derived (directly or indirectly) from that base class type to x. If both classes have a method with the same name, and we call that method after the assignment, the question then arises: Which version of the method is actually called. This is the question that polymorphism answers as follows: it's always the method from the derived class.

By the way ...

Here are some things to keep in mind about the relationships between variables and methods in derived and base classes, and the behavior of objects of these classes when some of the member names are the same:

All methods that are declared final, private or static do not use dynamic method lookup, and hence are more efficient. In this connection, observe that all methods of a class declared to be final are implicitly final themselves.

Do not confuse overloading, shadowing and overriding.

Abstract Classes

Interfaces

Inner Classes