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 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.
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.
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:
public void init() // run when the applet is first loaded
public void start() // run when the applet appears on the screen
public void stop() // run when the applet goes off the screen
public void destroy()// run when terminating the applet
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 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 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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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)
There are a number of Java coding conventions that are followed in the Java-code produced by Sun. You should follow these same conventions.
/**
* A brief sentence describing what
* this method does. Possibly with
* some additional sentences describing
* this more fully.
*/
Comments in this form can be used by a program called javadoc to automatically produce html documentation for your code. There are some additional keywords (@param, @return, @exception, etc) that are important to javadoc if they appear at the beginning of a line within this kind of comment; documentation on these is available online in J++.
Comments that appear within a method are usually of this form:
// Brief description of what this piece of code does.
Some of these are well-known C pitfalls. Java's syntax is largely based on C so some of the same pitfalls are available.
=
when you mean ==
. For example, it's common to
attempt to translate the Pascal codeif i = 5 then ...
into the incorrect Java code
if (i = 5) ... // Incorrect
The correct code should look like
if (i == 5) ...
Note that unlike C, the incorrect version above is not, in general, a valid statement, so Java is a bit safer than C.
if (i == 1)
a = a+1 // Missing ;
else
a = a+2;
Let me know please if you have additional pitfalls to suggest. I'll add them to the list.