A Very Brief Introduction to Java

These notes are meant for someone who is already familiar with programming who wants a brief introduction to the Java programming language.  This is not meant to be a comprehensive Java reference.  More complete information on Java can be found in The Java Tutorial, an online tutorial by the developers of Java.

You should also look at Pascal/Java/C Comparisons, a table indicating and discussing some of the differences between Pascal, Java, and C; this table can give you a quick idea of the proper syntax for some common kinds of Java statements.  More complete syntax information is available in The Java Language Specification.   Some example Java programs are in Queue.java and QTest.java.  QTest.java can be run as either an applet or an application and contains lots of comments.

Please let me know about mistakes, places where I've been unclear, or additional topics that should be included.

Object Oriented Programming

Object Oriented Programming is a style of programming based on objects, classes, and inheritance.  An object is a software bundle of data and related operations.  A class is a template that defines objects of a certain kind.  Using a class, one can create several objects where each is an instance of this class.  Classes can be defined in terms of other classes.  A class inherits from its superclass

Classes, Fields, and Methods

A Java program consists of a number of interacting classes.  Predefined classes in Java include String, Vector, Stack, Hashtable, and many others.  A Stack is a subclass of Vector which is a subclass of Object.  All classes in Java are subclasses of the class Object. 

Java classes have fields (these are the variables) and methods.   To refer to the field field within the instance instance, the syntax is instance.field.  A method call looks like instance.methodName(args); this calls the version of methodName that "resides within" the given instance.  Methods always have parentheses; a method call with no arguments looks like instance.methodName().

Methods can be overloaded (i.e., the same method name can be used to refer to several different methods).  If x.methodName(y,z) is used then the system looks for a method methodName that belongs to instance x.  Note that x, y, and z are not treated symmetrically in Java: the method actually chosen during a call is based on the true class of the data stored in x, while for y and z we use types based on the declarations of y and z.

All fields and all methods are part of some class.  For instance, mathematical constants, such as pi and e, and standard mathematical functions, such as the trigonometric functions, reside in the class java.lang.Math.

How a Java Program Runs

For an application, you have to tell the runtime system which class is to be run.   The runtime system looks for a method within this class that looks like public static void main (String[] args) and runs this method.

For an applet, the web page tells the runtime system which class is to be run.   The necessary code is downloaded and the runtime system looks for the following methods within the class:

These methods are automatically run by the system at the appropriate times.

A single class can include the methods for running applets as well as the main-method used for running as an application.  When doing a large program, consisting of many interacting classes, it can often be useful to have main-methods, containing debugging code, in several classes.

The Special Names this and super

The name this when used within a method (one that is not static) refers to the current object.  Note that without the use of this, there is no way to access the current object from within a method.  The name super also refers to the current object, but it is treated as a member of the superclass of the current object.  For instance:

class Thing extends SuperThing {
    int x, y;
    ...
    public void reset (int x) {
        this.x = x;    // this.x is Thing's x
                       // while x is the parameter x.
    }
}

Note that in the above example, if the parameter was called z then x and this.x would both refer to Thing's x.

Within another of Thing's methods, this.reset(5) would call the reset method shown above while super.reset(5) would call a different method that presumably exists in class SuperThing.

Constructors and the Keyword new

Constructors handle initialization when a new object is created.  A constructor is like a method, but it has a different syntax:

A constructor can only be called with a special syntax using the keyword new.

Vector v = new Vector();
Queue q = new Queue();

The first action of a constructor is to create an instance of its class; this is done automatically and requires no explicit code.  In general, the remainder of a constructor is then used to properly prepare the instance's fields.  Within the constructor's code, the newly created instance can be referred to by using the keyword this.

Packages and Java's API

Java allows related classes to be grouped into packages.  To use a class that is part of a package you have to import that class.  This is done with an import statement, usually placed near the beginning of the file in which the class is used.  For instance, to use Vector we would place the following statement at the beginning of our file.

import java.util.Vector;

Much of Java's functionality resides within the packages that are part of Java.   The Java API (Applications Programming Interface) describes the packages that are part of Java.  Documentation on the API is accessible from within J++ and you should take a look at it.  In particular you should check out

java.lang which contains Integer, Float, Double, Math, Object, String, StringBuffer, and System among other classes.  The entire java.lang package is always imported automatically.

java.util which contains Vector, Stack, BitSet, Hashtable, and Enumeration.

java.io which contains classes useful for file I/O.

java.awt, the Abstract Windowing Toolkit.

Standard Output

In Java, printing to the standard output is handled by the method System.out.println; this is a method that takes a single argument of type String.  System is a class that is part of the java.lang package.  The java.lang package is always imported automatically so the class System is always available.  The class System is basically a standard place to hold various items related to the system.  In particular, System.out represents the standard output and println is a method of the file System.out.

int x = 42;
System.out.println("The answer is " + x);

will print:

The answer is 42

Objects that appear with the string concatenation operator (+) are, in general, automatically converted into Strings.  This works for primitive types and also for user-defined objects as long as the user-defined object has a toString() method.

Modifiers for Fields (Variables) and Methods

For fields and methods, access is controlled by modifiers.  Here are possible access modifiers:

Modifier Access
private accessible only from within the class itself
no modifier (= "friendly") accessible from within the same package
protected accessible from within the same package and from subclasses
public accessible from everywhere

Additional modifiers include final and static.  A final variable is constant; its value can't be changed.  A final method can't be overridden by a method of the same name declared in a subclass.  Static implies just one copy for the entire class.  A static variable is associated with the class itself rather than any specific instance of that class.  A static method is also associated with the class itself rather than an instance.  In practice, this means that "this" can't be used within a static method.  Constants associated with a class are usually declared as both static and final (i.e., there is just one copy and it can't be changed).

Modifiers for Classes & Inner Classes

The access modifiers for a standard class are "friendly" (= no modifier) and public.   These have roughly the same meanings as above: a friendly class is accessible from within the same package and a public class is accessible everywhere.

Additional modifiers include abstract and final.  For an abstract class, you don't have to specify code for all of its methods.  An abstract class cannot be instantiated.  For a final class, no subclasses are allowed.   Many of the standard Java classes are declared to be final.

Java also allows inner classes.  An inner class is defined within another class.  Such classes are often  used as helper classes, defining objects that are not used outside of local use.  For instance, we might define an inner class called Node to be used within a LinkedList class.  Another common use is for user-interface code where it's common to define anonymous inner classes as Event listeners; see the documentation on event handling.  Inner classes can have more kinds of modifiers than standard classes.  For instance, private (accessible only locally) and static (associated with the whole class rather than a single instance) are valid for inner classes. 

File Names versus Class Names

Java is tied into the file system more intimately than most programming languages.   For instance, the code for a public class named MyClass must reside in a file called MyClass.java.  The code for a class that is not public (a "friendly" class) can reside within a file with other classes.

Java packages are also closely tied into the file system.  A public class called MyClass in package myPackage must reside in a file called MyClass.java that is within a folder called myPackage.

Interfaces

A Java interface allows a kind of pseudo-class to be created.  An interface has a name and has methods, but no code is specified within the method bodies; only the method name and method arguments are specified.  Code can be written in terms of the interface with the interface's name being used within declarations in the same way that a class name can be used.  In a class declaration, a class can implement an interface:
    class MyClass implements MyInterface {...}.  
When this is done you are, in effect, telling the Java compiler that all of the interface's methods are going to be implemented in this class; the compiler checks to ensure that this is true.  An instance of a class declared in this way can be used anywhere that an instance of the interface is called for.  

The Java interface corresponds nicely to the concept of Abstract Data Type that is used in the design of data structures: we know what operations are possible, but not how they are implemented.  It is often easier to maintain/modify code that is designed using interfaces, since differing data structures can be used without having to rewrite very much of the code.

Primitive Types versus Objects

In Java, primitive types (int, float, char, etc.) are weird. 

Why are primitive types so different from Objects?  It has to do with the way they are stored.  Objects use references (pointers) while primitive types are stored directly. 

For a method-call, every argument is copied to the corresponding local parameter for the method.   For primitive types, we copy the value.  For Objects, we copy the value of the pointer.  Here's an example method:

void change (int j, Vector v) {
    j = 4;
    v.addElement("hello");
}

What happens when this method is called in the following piece of code?

int i = 10;
Vector vv = new Vector();
change(i,vv);

After this code runs, i has the value 10 and vv is a Vector that contains "hello".  The variable i is unchanged because its value is copied to j, then j is changed, but the changed value is never copied back into i.  The variable vv still points at the same Vector, but a new element has been added to the Vector.   Think about what would happen if the line v=null were added to the end of the change method.

Note that there is no equivalent to Pascal's var parameters.  Thus, in Java it's impossible to write a method swap(i,j) that swaps the value of two ints.

Wrapper Classes

What do you do if you need to store, say, an int within a Vector?  An int is not an Object so it can't be placed directly into a Vector.  The solution is to create a wrapper that is an Object.  Here's the code for such a wrapper class:

class MyInteger {
    private int value;
    public MyInteger (int iValue) {
        value = iValue;
    }
    public int getValue () {
        return value;
    }
}

If we wished to place, say, 5 into an existing Vector v, it could be done this way:

v.addElement(new MyInteger(5));

This creates an Object of type MyInteger that holds the int value 5 and places into the next position in the Vector.

You don't actually have to create wrapper classes for the primitive types.  Java provides a full set of them, including java.lang.Integer, java.lang.Float, java.lang.Double, etc.  These classes also include some handy code for converting to and from Strings and converting between numeric types.

Declaring, Instantiating, and Initializing

In Java, there is a distinction made between declaring an Object, instantiating an Object, and initializing an Object.  For example,

String [] alpha;

declares alpha to be an array of Strings, but allocates no space for this array.   At this point, alpha has the value null.

alpha = new String[5];

allocates space for alpha.  Alpha can now hold 5 Strings, but the Strings themselves are not initialized.  Alpha[3], for example, is now null.

alpha[3] = "abc";

initializes alpha[3] to be the String "abc".  Note that alpha[3] is not a valid reference to a String until all three of these steps are complete.

For another example, consider

Vector v;
v = new Vector();

The first statement declares the variable v to be of type Vector, but does nothing else.  At this point, the value of v is null.  The second statement calls the Vector constructor which allocates space and initializes a Vector.  At this point, v holds a newly initialized Vector.  These two statements can be combined.  The code

Vector v = new Vector();

will declare the variable v and cause it to hold a newly initialized Vector.

Finding the Length of an Array

If your array is called myArray then its length can be discovered by using: myArray.length
Length is a special "field" that exists for each array indicating how many elements it has; the elements run from 0 to length-1.

Type Conversions

There are often occasions when you wish to convert a value from one type into another one.  The syntax for this is simple: just put the new type name (in parentheses) in front of whatever it is that you want to convert.

Widening numeric conversions are automatic.  Narrowing numeric conversions must be explicit.  For example

float x = 5;

is OK, while

float pi = 3.1415926;
int p = pi;    // illegal

is illegal.  To convert pi into an appropriate integer, we should use:

int p = (int) pi;    // rounds toward 0.

Some conversions are done without complaint even though information is being lost:

long g = 123456789;
int m = g;    // Silently drops higher bits.

It is often necessary to convert the types of Objects.  Note that runtime checking is done to ensure that the conversion is legal (i.e., you can only convert an Object into a String if that Object was actually created as a String).  Here is an example using type conversion for an Object.

Vector v = new Vector();
v.addElement(new Integer(4));
if (v.elementAt(0).intValue() == 4)    // This is an error.
    ...

The problem in the if-statement is that v.elementAt(0) returns an Object (even though it was an Integer when it was placed in the Vector).  We need to convert it back into an Integer.  Here's one way to do this.

if ( ( (Integer)v.elementAt(0) ).intValue() == 4)

Coding Conventions

There are a number of Java coding conventions that are followed in the Java-code produced by Sun.  You should follow these same conventions. 

Java Pitfalls

Some of these are well-known C pitfalls.  Java's syntax is largely based on C so some of the same pitfalls are available.

Let me know please if you have additional pitfalls to suggest.  I'll add them to the list.