Summary

These notes discuss the basics of the Java programming language and its environment. No attempt is made to cover all of Java.

Java 1.0, 1.1, 1.2 and 2

Java is designed for use in interactive computing, and it is relatively easy to write applications that respond to events, such as mouse clicks, caused by the computer user.

The original definition of Java, version 1.0, went through a major revision in 1997, primarily to provide support for improved event handling. Java 1.1 differs from Java 1.0 in ways that are quite significant for event-handling programs, but that are benign for the level that we will explore Java. You will need to be aware of the difference, however, if you attempt to compile a Java 1.1 program that does event-handling using a Java 1.0 compiler.

The Java library, a large part of which is concerned with graphics and event handling, was given a major overhaul with Java 1.1. In some cases, function names were changed to provide a more uniform naming convention.

Java 1.2 introduced new library classes, and Java 2 introduced even more, including entire collections of library packages. The language was not changed, only the library.

The C Subset of Java

The designers of Java wanted to make it easy for programmers familiar with C to learn Java. Many parts of Java are borrowed directly from C. You must be careful, however. C is not a subset of Java, and Java is not a subset of C. The picture is more like this.

              /\  /\
             /  \/  \
            /   /\   \
          C/   /  \   \Java
           \   \  /   /
            \   \/   /
             \  /\  /
              \/  \/  
Here are some of the C features that are also part of Java. (In some cases, the syntax is not exactly like C, but very similar.)

Comments

/* A comment */
// A comment
You can use either the C or C++ form of comment.

Basic Types

Every data item in Java is either an object or is a member of one of the following basic types.
boolean
byte (8 bits, signed)
char (16 bit unicode)
short (16 bit signed integer)
int (32 bit signed integer)
long (64 bit signed integer)
float (32 bit floating point)
double (64 bit floating point)

Note that the definition of Java says that a long integer MUST have 64 bits, regardless of the architecture of the machine on which it runs. The same kind of requirement holds for other types.

Object types

Anything that is not a value of one of the basic types is an object. All objects are stored internally as pointers, or references. They refer to the object. There is no explicit notion of a pointer in Java. What you get are implicitly pointers.

Operators

Binary operators are as follows.

+ addition or unary +
- subtraction or unary -
* multiplication
/ division
% mod
<< left shift (bring in 0 on right)
>> right shift (sign bit extension)
>>> right shift (bring in 0 on left)
& bitwise 'and' of binary integers or booleans
| bitwise 'or' of binary integers or booleans
^ bitwise exclusive-or of binary integers or booleans
&& short-circuit 'and'.
|| short-circuit 'or'.
==, != equality and inequality
<, >, <=, >= order tests

Be careful with division. When you divide two integers, the result is rounded toward 0 to an integer. So expression 1/3 has value 0.

The precedence is as follows, from high to low. Operators on the same line have the same precedence.

	++ -- unary operators
	* / %
	+ -
	<< >> >>> 
	< <= > >=
	== !=
	&
	^
	|
	&&
	|| 

Declaring Variables

int x,y,z
int x = 2;
You may initialize variables when they are declared, and are encouraged to do so. A variable that is not initialized is set to a default value. The default value for object types is the special null object called null. Numeric variables are initialized by default to 0.

Assignment

x = x + 1;
Assignment. In general, v = e computes the value of expression e and stores it into variable v.

x++, x--, ++x, --x
Increment or decrement x. As in C, x++ and ++x are expressions. Each has the side-effect of adding one to x, but x++ yields as its value the value of x before the increment, and ++x yields the value of x after the increment. x-- and --x subtract one from x.

Arrays

int[] x = new int[10];
This declares an array x of 10 integers.

You may write

  int[] x;
This says that x refers to an array of integers, but DOES NOT ACTUALLY CREATE THE ARRAY. To create the array, you would write something like
	
  x = new int[n];
as was done in the initialization. The size of an array can be given by any expression, not just a constant.

x[i]
x[i] is the i-th member of array x. Subscripting is from 0. So an array x of length 3 has members x[0], x[1] and x[2].

x.length
x.length is the size of array x.

Defining functions

Functions look almost exactly as they do in C. The return type is given first, followed by the name of the function and its formal parameters, each with a type. To return a value from a function, use a return statement as shown in the following example.
  int f(int x, int y)
  {
    return x + y;
  }

You may declare local variables at any point within the body of a function.

To write a function that returns no value, declare the function to have return-type void, and do not include a return statement. (Or, say return;)

Conditionals

If statements in Java have the same form as in C. You write
  if(condition) {
    statements
  }
  else {
    statements
  }

The condition must have type boolean.

The else part can be omitted if nothing is to be done when the condition is false.

Expression a ? b : c is the C/Java conditional expression. It is equivalent to the Astarte expression If a then b else c %If

Loops

The while loop, for loop and do loop are the same as in C.
  while(condition) {
    statements
  }
  do {
    statements
  } while(condition)
  for(s1,e2,s3) {
    statements
  }
The for loop shown is equivalent to
    s1;
    while(e2) {
      statements
    s3;
}

Classes and Interfaces

A basic class definition has the form
	class Name {
          ... things in the class
        }
where Name is the name of the class. Typically, Name is capitalized. The word class is reserved, and all reserved words must be lower case.

Packaging

Java supports two main levels of packaging. A package is a collection of related classes put into a single file. The classes within a package form a second level of packaging.

Classes are used for packaging related data and functions, and for controlling access to global names. So a class is a kind of module, and we can discuss the module associated with a class.

Within a class, you can define variables and functions. The Java compiler makes two passes over the class definition, so that you can use a symbol before it is defined.

Each variable or function has an access mode that tells where it is visible. Modes are as follows.

private Visible only within this class definition.
public Visible to every other class.
protected This mode is discussed below. It indicates visibility to this class and all that inherit from this class.
(none) Visible to every class defined in the same package.

Using class variables and methods

Variables and functions have two kinds, static (or class) variables and functions, and nonstatic (or instance) variables and functions. The nonstatic ones are discussed below. Static variables and functions are simply things that are associated with the module of the class.

For example, suppose that we want to implements the concept of a stack. If we want to implement just one stack of integers, then we might define a class Stack1 as follows.

	class Stack1 {
	  private static int top = 0;  // The number of things in the stack.
	  private static int[] mems = new int[100];

	  public static void push(int x)
	  {
	    mems[top++] = x;	// ignores overflow possibility.
	  }

	  public static int pop()
	  {
	    return mems[--top];	// ignores underflow possibility.
 	  }
 	}
Now there is one stack, and it is called Stack1.

Within the class, you can refer to the variables and functions that are defined in the class by their names. Outside the class, you must add the name of the class, with a dot. So to push n onto the stack, and then pop it, you can write

	Stack1.push(n);
	int p = Stack1.pop();

Types and instance variables

Each class (with the exception of those that are marked as 'abstract') has an associated type. In Astarte, this type would be called a species. The type is defined by taking those variables that are not declared static, and considering them to be fields of the type. For example, class PairOfInts defined by
	class PairOfInts {
	  public int first, second;
        }
has an associated type, called PairOfInts. Each individual of this type has a representation that consists of two integers. Variables first and second are called instance variables, since they are associated with an instance of the type PairOfInts.

You can create a variable of type PairOfInts in the usual way.

	PairOfInts p;

But be careful. In Java, all variables that refer to non-primitive types such as PairOfInts are references. When variable p is created, it refers to the null object, not to a pair of integers. To make it refer to a new pair of integers, you must say

	p = new PairOfInts();
Typically, you declare a variable and make it refer to a new individual in a single line, such as
	PairOfInts p = new PairOfInts();

You can refer to the parts of p using dot notation. p.first is the 'first' field of the pair referred to by p.

If two variables refer to the same object, then changing what one refers to will change what the other refers to. For example, after

	PairOfInts p = new PairOfInts();
	PairOfInts q = p;
	q.first = 1;
	q.second = 2;
you will find that p.first is 1 and p.second is 2.

You can mix static and nonstatic definitions in a class definition. The static parts belong to the module, and the nonstatic parts belong to the type.

Instance Methods

In Java, the functions are also called methods. If you define a nonstatic method, then that method is associated with the type of the class, in a special way. A nonstatic method is called an instance method, since whenever it is used, it is tied to a particular instance of the type of the class.

Every instance method has an implicit first parameter, which you can refer to as 'this'. Within an instance method, you can refer to the nonstatic variables of a class (as well as to the static variables). You are implicitly referring to those fields in 'this'. For example, here is a definition of stacks that, instead of creating a single, fixed stack, creates an entire class of stacks.

	class Stack2 {
	  private int top;
	  private int[] mems;

	  public void push(int x) {
	    mems[top++] = x;	// ignores overflow possibility
	  }

	  public int pop() {
	    return mems[--top];  // ignores underflow possibility
	  }

	  public void init() {
	    top  = 0;
	    mems = new int[100];
	  }	
	}

Remember that the implicit parameter is called 'this'. The definition of function push might have been written as follows, but the shorter definition above is preferred.

	  public void push(int x) {
	    this.mems[this.top++] = x;	// ignores overflow possibility
	  }

Before using this kind of stack, you must create and initialize the stack.

	Stack2 s = new Stack2();
	s.init();
	s.push(2);
	s.push(3);
	int n = s.pop();
	int m = s.pop();
leaves stack s empty, with n = 3 and m = 2. You can create many stacks this way. Each call to the push or pop function must have its implicit first parameter given, using dot notation.

What you have seen now are the beginnings of object-oriented programming in Java. Stack s is an object. It responds to requests, and can change its internal state. When you write s.push(3), you are asking s to change itself by pushing 3 onto its internal stack. Note that you can refer to the same object by more than one name. Writing

	Stack2 s = new Stack2();
	Stack2 r = new Stack2();
	Stack2 t = r;
creates two stack objects, but allows you to refer to one of them as either r or t. If you make a request to t, you are making a request to the very same object as that referred to by r. But if you write
	t = s;
you do not change r. This just makes variable t refer to the same stack as s, leaving r unchanged.

Constructors

In the Stack2 example above, we needed to call an initialization function for the variables. It is not allowed to initialize instance variables the way static variables are initialized. For example, it would not be allowed to write
	class Stack2 {
	  private int top = 0;
	  private int[] mems - new int[100];
          ...
        }

The need to call a separate initializer is unpleasant. In fact, it is worse than unpleasant, it is dangerous. If the initializer is forgotten, then the stack will not work correctly.

Java provides a way around the initializer, by making it possible to create an implicit initializer, called a constructor. The constructor has the same name as the class, and is defined without mentioning a result type. Here is a new version of the stack class, with a constructor.

	class Stack3 {
	  private int top;
	  private int[] mems;

	  public Stack3() {
	    top  = 0;
	    mems = new int[100];
	  }	

	  public void push(int x) {
	    mems[top++] = x;	// ignores overflow possibility
	  }

	  public int pop() {
	    return mems[--top];  // ignores underflow possibility
	  }
	}
Now, when you write
	p = new Stack3();
the constructor is automatically run to initialize the stack.

You can have a constructor that has parameters, and you can have more than one constructor. For example, you might want the size of the stack to be a parameter.

	class Stack4 {
	  private int top;
	  private int[] mems;

	  public Stack4(int size) {
	    top  = 0;
	    mems = new int[size];
	  }	

	  public Stack4() {
	    this(100);
	  }	

	  public void push(int x) {
	    mems[top++] = x;	// ignores overflow possibility
	  }

	  public int pop() {
	    return mems[--top];  // ignores underflow possibility
	  }
	}

Notice that the constructor without parameters wants to run the constructor that has a parameter. It does so by calling the pseudo-function this.

Now

	p = new Stack4(200);
	q = new Stack4();
creates a stack p with 200 cells and another stack q with 100 cells.

Inheritance and Polymorphism

When you define a class, you get more than just a module and a type. You also get what would be called, in Astarte, a genus. A genus is a set of species. The genus associated with a class is a set of types that are associated with classes.

The type of a class is always a member of the genus of the class. When you define a class, you can indicate that its genus is a subset of the genus of one other class, by using an 'extends' phrase. For example, suppose that we want to create a new type of stack, but one that is a special kind of Stack4. You can define

	Class Stack4ext extends Stack4 {
	  ...
	}
We say that class Stack4ext is a subclass of class Stack4, and class Stack4 is called the base class of Stack4ext. You get two important things from this.
  1. You get polymorphism. If s has type Stack4ext, then you can use s in any setting where something of type Stack4 is expected.
  2. You get inheritance. The methods that are defined as part of class Stack4 are implicitly made available for objects of class Stack4ext.

    Instance variables are also inherited. Every instance of type Stack4ext implicitly has instance variables top and mems, which are defined in class Stack4. If you define any more instance variables in class Stack4ext, then an object of class Stack4ext has those variables as well as top and mems.

Note that possessing certain instance variables or methods is not the same as making those variables or methods visible. For example, consider the following definition of class Stack4ext, which provides a new method isEmpty that tells whether the stack is empty.

	Class Stack4ext extends Stack4 {
	  public boolean isEmpty()
	  {
	    return top == 0;
	  }
	}

This will not work with the previous definition of class Stack4. The problem is that, although objects of type Stack4ext have variables top and mems, those variables were declared private in class Stack4, so they are invisible in class Stack4ext. This situation can be remedied by making variables mems and top 'protected'. This indicates that they are visible inside class Stack4 and in any class that extends class Stack4, but not to general classes. So we write

	class Stack4 {
	  protected int top;
	  protected int[] mems;

	  public Stack4(int size) {
	    top  = 0;
	    mems = new int[size];
	  }	

	  public Stack4() {
	    this(100);
	  }	

	  public void push(int x) {
	    mems[top++] = x;	// ignores overflow possibility
	  }

	  public int pop() {
	    return mems[--top];  // ignores underflow possibility
	  }
	}

	class Stack4ext extends Stack4 {
	  public boolean isEmpty()
	  {
	    return top == 0;
	  }
	}

This is almost ready to use. There is still one problem. When a Stack4ext is created, we need to call the initializer for Stack4. That has not been done in the preceding definition. A modified definition of Stack4ext is as follows. It uses the 'super' call to call the constructor in its base class.

	Class Stack4ext extends Stack4 {

	  public Stack4ext(int size) {
	    super(size);
	  }

	  public boolean isEmpty()
	  {
	    return top == 0;
	  }
	}

Now you can do the following.

	Stack4ext s = new Stack4ext(100);
	int k;
	s.push(2);
	k = s.pop();
	if(s.isEmpty()) {
	  ...
	}

You can use the pop method with an object of type Stack4ext because that method is inherited by Stack4ext from Stack4.

What would happen if you did the following? Notice that s is created as an object of type Stack4ext, but is declared to be an object of type Stack4.

	Stack4 s = new Stack4ext(100);
	int k;
	s.push(2);
	k = s.pop();
	if(s.isEmpty()) {
	  ...
	}
The first line is fine, since s is a special kind of Stack4. When you write
	Stack4 s;
you do not mean that s must refer to something of type Stack4. Rather, you mean that s can refer to any object that is of a type that is in the genus Stack4. (We only have one name for the class, so that name must be used to refer to the module, the type and the genus.)

Expression s.isEmpty(), however, causes problems. Since s has been declared to refer to something in class Stack4, you cannot use operation isEmpty, which is only defined for class Stack4ext.

Overriding Methods

Sometimes, when you extend a class, you would like to redefine methods that would otherwise be inherited. For example, suppose that a class has an instance method that is supposed to return the name of the type of the object 'this'. In class C, you might have
	class C {
	  ...
	  String className() {
	    return "C";
	  }
	  ...
	}
But in class D, which extends C, you would like to replace the definition of function className. You can do so, just by giving a new definition.
	class D extends C {
	  ...
	  String className() {
	    return "D";
	  }
	  ...
	}

Abstract Classes and Methods

Sometimes, when you create a class, you do not know how to implement some of the methods. Instead, you rely on subclasses to define those methods. A method that is not implemented is called an abstract (or virtual) method. To create one, just use the word 'abstract'. For example, you might have a class Expression that represents expressions (as trees) whose result, if they were evaluated, would be integers. Suppose that every expression is supposed to be able to evaluate itself. Then you can define
	abstract class Expression {
	  public abstract int evaluate();
	}

Indicating that class Expression is abstract says that it is not allowed to create an Expression that is ONLY an Expression. You must create an object using a subclass of Expression. So

	Expression e = new Expression();
is not allowed. Class Expression is abstract because some of its functions are abstract, so there is no known way to compute them. To create a kind of expression, you would extend class Expression. So an expression that is just a number might go as follows.
	class Number extends Expression {
	  private int val;
  
	  public Number(int v) {
	    val = v;
	  }

	  public int evaluate() {
	    return val;
	  }
	}
and a class of expressions that are sums of other expressions would look like this.
	class Sum extends Expression {
	  private Expression e1, e1;

	  public Sum(Expression a, Expression b) {
	    e1 = a;
	    e2 = b;
	  }

	  public int evaluate() {
	    return e1.evaluate() + e2.evaluate();
	  }
	}

Interfaces

If class B extends class A, then A is called the base class of B. Java supports single inheritance. That means that each class can have only one base class. However, Java provides a facility that makes many cases of multiple inheritance possible.

Java provides something called an interface that is very similar to an abstract class. An interface is not allowed to have instance variables. It can only have functions. All of the functions of an interface are implicitly abstract, even if you don't say so. For example, the standard Java interface Runnable is defined as follows.

	interface Runnable {
	  public void run();
	}

You can use an interface as if it is an abstract class. For example, you can create a variable that is a reference to a Runnable object, as in

	Runnable r;

You cannot, of course, use expression new Runnable(), since Runnable is abstract.

You can indicate that a class is an implementation (similar to a subclass) of an interface in the heading of the class definition. For example, if class Rabbit is to be an implementation of Runnable, and also extends class Mammal, you write

	class Rabbit extends Mammal implements Runnable {
	...
	  public void run() {
	    ...
	  }
	}

Because class Rabbit implements Runnable, it takes on the responsibility of implementing abstract method run. Having created class Rabbit, you can write

	Runnable r = new Rabbit();
since any instance of class Rabbit is also an instance of Runnable.

A class can implement any number of interfaces.

Finality

Classes, methods and variables can be marked final. A final variable is a constant; it cannot be changed after it is initialized. A final method cannot be overridded. A final class cannot be extended. For example, to declare constants pi and e, you might write
	class Example {
          public static final double pi = 3.1415926536;
          public static final double e  = 2.7182818285;
	  ...
        }

Practical Issues

Naming and importing packages

If you want to refer to a name N defined in package P, you write P.N. For example, the class called Applet that is defined in package java.applet is called java.applet.Applet. So to extend that class, you might write
	class MyApplet extends java.applet.Applet
You can avoid the need to write these long names by using an import. Write
	import java.applet.Applet
to allow using the short name Applet for jaba.applet.Applet, or write
	import java.applet.*
to allow short names for everything in package java.applet.

Be careful about imports. You can only import a package, not a class. For example, there is a standard Java class called Math. It contains a collection of useful functions, including function abs that computes the absolute value of a number. To set y to the absolute value of x, you must write

	y = Math.abs(x);
You might be tempted to shorten this by writing
	import Math;
but that is not allowed, since math is a class, not a package.

The Application Program Interface

Java has an extensive standard library, including the popular Abstact Windows Toolkit and the more recent Swing class. There is extensive support for both graphical user interfaces and network programming. There is not anything close to the space here to describe the Java library. Several books are available.

Running Java

There are development environments available, but we describe only the basics here. A Java program is a package. It typically begins with some imports to get the packages that are used, and then contains a collection of class definitions.

To compile a Java program, use the Java compiler, javac. On stymie, it is in /export/stu/4510/java/bin. For example, type

javac myprog.java

When you compile a Java package, you get "compiled" versions that are in a special byte-code for the Java interpreter. You get a file for each class, with extension .class. To run a Java program, you use the Java interpreter, called java, on the main class. It must have a method called main, with the following heading.

	public static void main(String[] args)

Java and HTML

Web browsers have Java interpreters built into them. The browser will only, however, run a special kind of Java program called an applet. An applet is a class that extends the class Applet. The class Applet is responsible for setting up the environment (such as the graphics) in which the specific applet runs.

See file RobotSim.java for an example of an applet. File Robotsim.html runs that applet. It is a fairly crude simulation, and has not been provided with a way to start and stop it. Just back out of the page to stop it.