|
|
A high-level programming language that offers, among other features,
- sophisticated graphics,
- easily constructed graphical user interfaces, and
- easy delivery to a wide audience.
| |
| |
How to construct and run a Java program |
|
|
Once Java is installed, the way a Java program is run varies from one
operating system to the next. You can find details on running Java under
Windows, Linux and MacOS.
However, the basic steps will be the same.
1. Use a text editor to write the program in a file called,
say, WelcomeToJava.java.
$ ls
WelcomeToJava.java
| |
|
2. Compile the program. This produces a file with a
.class extension containing Java byte code.
$ javac WelcomeToJava.java
$ ls
WelcomeToJava.class WelcomeToJava.java
| |
|
3. Execute the program. This causes the Java virtual machine
to execute the Java byte code.
$ java WelcomeToJava
Welcome to Java
|
|
| |
Anatomy of a simple program |
|
|
Here is what the file WelcomeToJava.java looks like.
public class WelcomeToJava {
public static void main(String[] args) {
System.out.println("Welcome to Java");
}
}
|
Note: |
The Java compiler does not worry about the spacing of the
code.
However, the way in which lines are broken and indented help a human
read the code more easily. It is wise to follow this or some other
style of writing code.
Many text editors will help you do it.
|
|
The definition of the public class WelcomeToJava must be in a
file named WelcomeToJava.java.
|
| |
|
public class WelcomeToJava {
public static void main(String[] args) {
System.out.println("Welcome to Java");
}
}
|
Note: |
Curly brackets { and } delimit blocks of code.
|
|
The declaration public class WelcomeToJava says that
the following block of code contains the definition of the class
WelcomeToJava.
Every piece of Java code you will write will be contained in a
class like this.
|
| |
| |
What's contained in the class WelcomeToJava? |
|
|
public static void main(String[] args) {
System.out.println("Welcome to Java");
}
One method whose name is main. In other
programming languages, methods are sometimes known as procedures or
subroutines.
The information contained in the declaration public static
void main(String[] args) is called the signature of
the method. The block of code which follows is the body of
the method.
When we run the program WelcomeToJava, the Java virtual
machine looks inside the class file WelcomeToJava.class for
a method named main with this signature and executes it.
| |
| |
What's contained in the main method? |
|
|
System.out.println("Welcome to Java");
A single instruction that causes the phrase "Welcome To Java" to
be printed.
| |
|
Using variables
public class Squaring {
public static void main(String[] arguments) {
int n;
n = 10;
int n2 = n * n;
System.out.println(n + " squared is " + n2);
}
}
|
Note: |
The type of every variable must be declared. The instruction
int n; declares that n is of type int,
a 32-bit signed integer.
|
|
The variable n is then initialized with n = 10;
|
|
Declaration and initialization can be combined in a single
instruction:
int n2 = n*n;
|
| |
|
Some common data types are
- boolean, whose value is either true or
false,
- int, a 32-bit signed integer,
- float, a 32-bit floating point number, and
- double, a 64-bit floating point number.
A variable is accessible within the smallest block of code containing
its declaration.
| |
| |
Controlling the flow of execution |
|
|
public class Factorial {
public static void main(String[] arguments) {
int n = Integer.parseInt(arguments[0]);
if (n < 0) {
System.out.println("Please enter a non-negative integer");
return;
}
int factorial = 1;
for (int i = n; i >= 1; i = i - 1) {
factorial = factorial * i;
}
System.out.println(n + "! = " + factorial);
}
}
|
Note: |
The boolean expression n < 0 evaluates to either true or false.
|
|
The block of code following the conditional is executed if the value
of the boolean expression is true.
|
|
This block of code may be followed with else { ... } .
|
|
The declaration of the for loop contains three statements:
the first is executed at the beginning of the first loop, the second is a
boolean expression to determine whether the loop should be executed
and the third is executed at the end of each loop.
|
| |
|
public class Fibonacci {
public static void main(String[] args) {
int N = 10;
int[] f;
f = new int[N];
f[0] = 0; f[1] = 1;
for (int i = 2; i < N; i++) {
f[i] = f[i-1] + f[i-2];
}
for (int i = 0; i < N; i++) System.out.println(f[i]);
}
}
|
Note: |
Arrays are declared with by adding square brackets [] to the
data type.
|
|
To initialize the array, use the new keyword and specify
the number of elements in the array.
|
|
Elements in the array are indexed by consecutive integers beginning at 0.
|
| |
|
public class PascalTriangle {
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
for (int c = 0; c <= n; c++) {
int bc = factorial(n)/factorial(c)/factorial(n-c);
System.out.print(bc + " ");
}
System.out.println();
}
public static int factorial(int k) {
int factorial = 1;
for (int i = k; i >= 1; i--) {
factorial = factorial * i;
}
return factorial;
}
}
|
Note: |
A method called factorial is defined with signature
int factorial(int k) .
|
|
The first occurrence of int in the signature declares that
this method will return an int.
|
|
Next comes the name of the method factorial.
|
|
(int k) declares this method to have one argument of type
int.
|
|
We can use this method by saying, for instance, int fact =
factorial(10);. This causes the block of code defined by
factorial to be executed with 10 replacing k.
|
| |
| |
Object-oriented programming |
|
|
The central feature of Java is that it is an
object-oriented language. Simply said, this gives us the
ability to define our own data types.
| |
|
An object is simply a structure that contains (a)
a collection of data and (b) some methods
that change or reflect the data.
Suppose we wish to work with Gaussian integers; that is, complex
numbers of the form
a + bi where a and
b are integers.
Just as we have the data type int, it would be convenient
to have a data type GaussianInteger. We will see how to
construct such a data type and how to use it.
| |
| |
Data and methods of GaussianInteger |
|
|
A Gaussian integer is defined by its real and imaginary parts,
both of which are integers. Therefore, a GaussianInteger
object will have two pieces of data: int real, imag;
For our purposes here, we will be interested in adding Gaussian
integers; therefore, a GaussianInteger object will
have a method by which we may add another GaussianInteger to
it resulting in a third GaussianInteger containing the sum.
| |
| |
How can we use GaussianInteger? |
|
|
We will see how to define GaussianIntegers in a
moment. First, however, let's see how we can use them by constructing
a program that adds the Gaussian integers x = 3-2i
and y = 2 + i.
Let's first consider how it would look if we added two integers.
public class Addition {
public static void main(String[] args) {
int x = 3;
int y = 2;
int sum = x + y;
System.out.println(sum);
}
}
The variables x and y are first declared and
initialized, their sum is computed and then printed.
| |
| |
And now with GaussianInteger |
|
|
public class GaussianAddition {
public static void main(String[] args) {
GaussianInteger x = new GaussianInteger(3, -2);
GaussianInteger y = new GaussianInteger(2, 1);
GaussianInteger sum = x.add(y);
System.out.println(sum);
}
}
|
Note: |
Variables x and y are declared to be of the data
type GaussianInteger.
|
|
They are initialized in a slightly different way using the new
keyword.
|
|
The sum is computed using the add method of x.
|
|
The sum is then printed.
|
| |
| |
Defining the data type GaussianInteger |
|
|
public class GaussianInteger {
int real, imag;
public GaussianInteger(int a, int b) {
real = a; imag = b;
}
public GaussianInteger add(GaussianInteger a) {
return new GaussianInteger(real + a.real, imag + a.imag);
}
public String toString() {
return real + " + " + imag + " i";
}
}
You should think of this class file as defining the template
for a GaussianInteger.
| |
|
We create a specific GaussianInteger through the process
of instantiation, initiated by the command
GaussianInteger x = new GaussianInteger(3, -2);
Several things happen:
- Some space is set aside in memory for the
GaussianInteger. In this space are locations for the data
(called instance fields) and methods (called instance methods)
specified by the template. We call this an instance of a
GaussianInteger object.
- The method
public GaussianInteger(int a, int b) {
real = a; imag = b;
}
is executed. This has the effect of initializing the data. This
method is called a constructor.
- The variable x contains the memory address of the
object.
| |
|
Here is what is in the memory location addressed by x.
| |
| |
Accessing an object's data and methods |
|
|
Now that we have the object x, we will want to access its
data and its methods.
- We can retrieve the value of the real field of x
using the syntax x.real.
- The add method of x is accessed in a similar
way:
x.add(y).
| |
|
In the file GaussianAddition.java
public class GaussianAddition {
public static void main(String[] args) {
GaussianInteger x = new GaussianInteger(3, -2);
GaussianInteger y = new GaussianInteger(2, 1);
GaussianInteger sum = x.add(y);
System.out.println(sum);
}
}
and in GaussianInteger.java
public class GaussianInteger {
int real, imag;
public GaussianInteger(int a, int b) {
real = a; imag = b;
}
public GaussianInteger add(GaussianInteger a) {
return new GaussianInteger(real + a.real, imag + a.imag);
}
public String toString() {
return real + " + " + imag + " i";
}
}
| |
|
- GaussianInteger may be easily reused in another program.
- We can work with a GaussianInteger as we think about
it: as an indecomposible entity.
- The details of how GaussianInteger work are hidden
away.
| |
|
The GaussianInteger objects we have been working with
represent specific elements, say x = 3-2i, in the
set of all Gaussian integers. Generally speaking, objects
should be thought of as a specific element in a larger set.
Oftentimes, there are fields and methods that belong, not to a
specific element, but to the entire set. For instance, the additive
identity is a special element in the set of Gaussian integers. In the
same way, multiplication may be thought of as a method belonging to
the entire set.
The static keyword allows us to define fields and methods
belonging to the set rather than a specific instance. We call such
fields and methods class fields and class methods.
| |
| |
Our next version of GaussianInteger |
|
|
public class GaussianInteger {
int real, imag;
public GaussianInteger(int a, int b) {
real = a; imag = b;
}
public GaussianInteger add(GaussianInteger a) {
return new GaussianInteger(real + a.real, imag + a.imag);
}
public String toString() {
return real + " + " + imag + " i";
}
public static final GaussianInteger ZERO = new GaussianInteger(0, 0);
public static GaussianInteger multiply(GaussianInteger a, GaussianInteger b) {
return new GaussianInteger(a.real*b.real - a.imag*b.imag,
a.real*b.imag + a.imag*b.real);
}
}
The keyword final indicates that the field ZERO
may not be reassigned.
| |
| |
How can we use class fields and methods? |
|
|
We can access class field ZERO using the syntax
GaussianInteger.ZERO.
Likewise, the class method multiply may be accessed using
GaussianInteger.multiply(x, y).
There is no real reason to make add an instance method
and multiply a class method. The example, as presented, is
meant merely to illustrate the principle of class methods. It is
possible to add another instance method, also called
multiply, defined like this:
public GaussianInteger multiply(GaussianInteger b) {
return GaussianInteger.multiply(this, b);
}
(The keyword this is how an object may refer to
itself.)
In fact, different methods may share the same name as long as
their argument lists are different. Likewise, we may have multiple
constructors as long as their argument lists are different.
| |
| |
Java's pre-defined classes |
|
|
We have now constructed a special class GaussianInteger
to help us work with Gaussian integers.
Java 1.4 provides us with 2991 (!) classes. How can we learn
about these?
- Classes that work together for some common aim are grouped
into 135 packages. A few packages are
- java.lang, core classes in Java,
- java.io, classes that handle input/output,
- java.awt, classes that construct graphical interfaces
and images
(awt stands for Abstract Windowing Toolkit ), and
- java.util, some basic utility classes.
- In fact, some packages are sub-packages of others. For
instance, java.awt.geom is a sub-package of java.awt
that provides some classes lending geometrical assistance for the
construction of images.
- All classes are well documented in the
Applications Programming Interface
(also here).
| |
|
If we use one of Java's pre-defined classes, the compiler needs to
know which package contains the class. Unless otherwise directed,
Java will only look through the package java.lang.
Suppose we want to use the
class Vector in the package java.util.
We can do one of two things:
- Declare the instance of Vector like this
java.util.Vector vector = new java.util.Vector();
or
- include an import statement before the class declaration
like this:
import java.util.Vector;
or
import java.util.*;
and declare the instance using
Vector vector = new Vector();
| |
|
It often happens that we have a class that we would like to modify
or add some new functionality to. Java provides a convenient way to do
this through inheritance.
For example, a typical data structure is a queue, a list
of data into which new entries are added on one side and removed from
the other.
An array is not useful here since the number of entries in the
queue may grow arbitrarily large. However, a Vector allows
us to maintain an array of objects of no fixed size. In other words,
we would like our queue to have the features of a Vector, but
also include some methods to add elements on one side and remove them
on the other.
| |
|
import java.util.Vector;
public class Queue extends Vector {
public void insertElement(Object obj) {
insertElementAt(obj, 0);
}
public boolean hasMoreElements() {
return size() > 0;
}
public Object removeElement() {
Object object = elementAt(size() - 1);
removeElementAt(size() - 1);
return object;
}
}
The keyword extends is important here: it says that
Queue inherits all of the fields and methods from
Vector. To provide the functionality of a queue, we add two
instance methods: insertElement and
removeElement.
| |
|
public class QueueOfGaussianIntegers {
public static void main(String[] args) {
Queue queue = new Queue();
queue.insertElement(new GaussianInteger(3, 2));
queue.insertElement(new GaussianInteger(2, 0));
GaussianInteger top = (GaussianInteger) queue.removeElement();
System.out.println(top);
queue.insertElement(new GaussianInteger(10, -1));
top = (GaussianInteger) queue.removeElement();
System.out.println(top);
top = (GaussianInteger) queue.removeElement();
System.out.println(top);
}
}
The result is
3 + 2 i
2 + 0 i
10 + -1 i
| |
|
If B extends A, we call B a subclass
of A and A the superclass of B.
- A class may only extend one class.
- Every class implicitly extends java.lang.Object.
- This means that the set of all classes forms a tree with
Object at the root.
- A class may redefine a method in a superclass to modify the
behavior of that method. This is called method overriding.
For instance, Object defines a method with signature
public String toString()
This method is called when an Object is printed. We
overrode this method in GaussianInteger to print something of
the form a+bi.
- If a class overrides a method in a superclass, it may access
the superclass method using the super keyword. For instance,
if a GaussianInteger wanted to call the toString()
of Object rather than its own toString() method, we
could say super.toString().
Likewise, we can call a constructor that has been overridden using
super( ... ). For technical reasons, however, this must be
the first instruction in the constructor.
| |
|
The data types we saw at the beginning--int,
float, double and boolean--are called primitive
data types to distinguish them from objects. The
behavior of the two classes of data types can be quite different.
If we say int n = 2, then the value of n is
set to 2.
However, if we say GaussianInteger x = new GaussianInteger(2,
-1), then the value of x is the memory address of the
GaussianInteger object instantiated.
Compare the behavior of the following fragments of code:
int m = -3;
int n = 2;
n = m;
m = 4;
System.out.println(n + " " + m);
GaussianInteger x = new GaussianInteger(2, -1);
GaussianInteger y = new GaussianInteger(3, 2);
y = x;
x.real = 2;
System.out.println(x + " " + y);
| |
|
Sometimes we would like to hide some of the fields and methods
in a class. For instance, we could add a field modulus
to our GaussianInteger class by modifying the constructor.
...
double modulus;
public GaussianInteger(int a, int b) {
real = a; imag = b;
modulus = Math.sqrt(real*real + imag*imag);
}
...
A problem arises if we say something like this:
...
GaussianInteger x = new GaussianInteger(2, 1);
x.modulus = 10;
...
| |
|
To remedy this, we may add the modifier private in the
declaration of modulus:
private double modulus;
and add a method to allow others to access the modulus
field:
public double getModulus() {
return modulus;
}
The private modifier means that modulus may only be
accessed from within its own instance. This is called a
visibility modifier.
By contrast, the public modifier would allow any
class to access a field or method.
There are also modifiers package and protected
that lie somewhere between these two extremes.
If a field or method does not have a visibility modifier, it is
set to package by default. This means that the field or
method is visible only within the package containing the class.
What is now wrong with our GaussianInteger class?
| |
|