Java Notes

  C.A. Bertulani

These notes are intended to give an introduction to the Java programming language. They have been adapted and extended from several sources (see credits at the end of these notes). The material can be easily covered in 3 - 4 days for the reader with some experience in programming. At the end of that the reader will be able to write and run his own applets.

Table of Contents

I - Introduction

These notes assume that you have the Java Development Kit (JDK) version 1.1 or later from Sun (http://www.javasoft.com) and a simple text editor such as NotePad.  The instructions below will help you with that.

 I.1 - Installing Java

·  The Java compiler is available from Sun for Windows  and Windows NT, for UNIX,  and MacIntoshes.

·  The basic Java environment consists of a web browser that can play Java applets, a Java compiler to turn to Java source code into byte code, and a Java interpreter to run Java programs. These are the three key components of a Java environment. You'll also need a text editor like NotePad.

·  Sun has made the Java Developers Kit available for its supported platforms. It includes an applet viewer that will let you view and test your applets. The JDK also includes the javac compiler, the java interpreter, the javaprof profiler, the javah header file generator (for integrating C into your Java code), the Java debugger and limited documentation. However most of the documentation for the API and the class library is on Sun's web site.

·  Windows Installation Instructions
The Windows release is a self extracting archive. You will need about 50 megabytes of free disk space to install the JDK. Execute the file by double-clicking on it in the File Manager or by selecting Run... from the Program Manager's File menu and typing the path to the file. This will unpack the archive. The full path is unimportant. It could be your C: drive. If this is the case the files will live in C:\java. If you unpacked it somewhere else just replace C:\ by the full path to the java directory in what follows. You will need to add C:\java\bin directory to your PATH environment variable

·  Unix Installation Instructions
If you're on a shared system at a university or an Internet service provider, there's a good chance Java is already installed. Ask your local support staff how to access it. Otherwise follow these instructions.
The Unix release is a compressed tar file. You will need about 80 megabytes of disk space to uncompress and untar the JDK. You do this with the commands:
% uncompress JDK-1_0_2-solaris2-sparc.tar.Z
% tar xvf JDK-1_0_2-solaris2-sparc.tar
The exact file name may be a little different if you're retrieving the release for a different platform such as Irix or if the version is different. You can untar it in your home directory, or, if you have root privileges, in some convenient place like /usr/local where all users can have access to the files. However root privileges are not necessary to install or run Java. Untarring the file creates all necessary directories and sub-directories. The exact path is unimportant. It could be in /usr/local. If a sysop already installed it, this is probably where it lives. (Under Solaris it's also possible the sysop put it into /opt.) If this is the case the files live in /usr/local/java. If you unpacked it somewhere else, just replace /usr/local by the full path to the java directory in what follows. If you installed it in your home directory, you can use ~/java and ~/hotjava instead of a full path.
You now need to add /usr/local/java/bin directory to your PATH environment variable. You use one of the following commands depending on your shell.
csh, tcsh:
 % set path=($PATH /usr/local/java/bin)
sh:
 % PATH=($PATH /usr/local/java/bin); export $PATH
You should also add these lines to the end of your .profile and .cshrc files so you won't have to do this every time you login.

 I.2 - What is Java?

·  Java is a "simple, object-oriented, distributed, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, and dynamic language."

·  Java was made to look and act much like C and C++, since so many people are already familiar with those languages. However, to simplify matters, several features common to C and C++ were not included. Among those are pointers, header files, and goto's. Java is not 100% object-oriented like SmallTalk, but it is much more so that C++.

·  Distributed: Java was designed with today's distributed networks in mind. It is capable of network communication and handling socket calls.

·  Interpreted: Java code is compiled into byte-code, which is somewhat similar to a binary executable. Instead of being run by your operating system, though, the byte-code is run by the Java Virtual Machine, a byte-code interpreter. The advantage is that the Java code doesn't have to be directly translated into the system-specific machine code. Instead, the Java Virtual Machine acts as an intermediary which will be the same on every system. So, the end result is code that acts the same on every computer.

·  Robust: Java is a strongly typed language, and so will catch most errors long before run-time. Also, since Java doesn't support pointers, code written in Java is much more reliable. Another feature adding to Java's robustness is its exception handling. When an error occurs, the code is able to deal with it on the fly, allowing for a high rate of recovery.

·  Secure: Java was designed with the internet in mind. As such, it has to be secure to be usable at all. Java applets are not allowed to do any file I/O. Also, the lack of pointers prevents a large amount of potential problems. The internet is a dangerous place. There is really no way to be completely secure without being useless. Java has tried to find a middle ground between being unprotected and being unusable.

·  Portable: Because Java runs on the Java Virtual Machine, and not the individual system itself, Java code is easily portable to any system capable of running the virtual machine.

·  Performance: Java is approximately 20 times slower than C. However, it is more than fast enough for most of its more commons uses such as applets and multimedia. Java does allow you, though, to import native C code for the times when speed is essential.

·  Multithreaded: Java can run several simultaneous process threads. This allows the user to easily code programs with can do more than one thing at once. Such multitasking capability is invaluable for use in building GUI's (Graphic Unit Interface) and other such applications.

·  Dynamic: Java was built to be able to adapt to the continually evolving environment. It can dynamically load in classes when they are needed, and it can even got out over the 'Net to find them.

·  Initially computer memories were so small and the machines so slow, that program efficiency was the primary concern.  Algorithms were very closely tied to the capabilities of the specific machine they ran on. This is called machine language programming. The toggling of individual memory locations (by switch or other means) is called a first-generation language. In a first generation language there is almost no abstraction.

·  As computers grew in power and memory, it was no longer possible for a programmer to keep track of what was happening at every location in the machine's physical memory. Card readers and assembly language were invented to make programming more feasible. In assembly language the programmer uses mnemonic codes like MOV to represent particular bit sequences. These codes mapped directly to individual instructions on the CPU, and memory was still addressed directly. One code meant exactly one CPU instruction.

·  The first high-level programming language, Fortran, was invented to spare programmers from the pains of dealing with keeping track of the location of their variables in memory.  Fortran was the first example of a third-generation language. In a third generation language you tell the computer the algorithms and data structures it should use to calculate the results you want; but you use more abstract logical and mathematical operators rather than directly manipulating addresses in memory and CPU instructions. In a third generation language, statements represent several machine instructions. Which instructions they represent may even depend on their context. These languages may be compiled or interpreted. In either case your program code needs to be translated into equivalent machine instructions. This level of abstraction made considerably more powerful algorithms and data structures possible.

·  Java is a very advanced third generation language. Most of the other computer languages you're probably familiar with, Fortran, Basic, C, C++, Cobol, Pascal, as well as most of the one's you're not familiar with (AppleScript, Frontier, Eiffel, Modula-3, ADA, PL/I, etc.) are also third-generation languages (or 3GL's for short). Fourth generation languages (or 4GL's for short) moved the abstraction level a step higher. In these languages you tell the computer what results you want rather telling it how to calculate those results. For instance you would ask for the total sales for the year, without specifying the loops necessary to sum all the sales of all the salespeople. SQL is the most popular fourth generation language.

·  Pascal and C were the next widely successful languages. They made possible a style of programming known as structured programming. Structured programming languages have many different flow control constructs (switch statements, while loops, and more) as well as tools for more complicated data structures (structs, records and pointers).  Finally they have subroutines with local variables that are capable of splitting the code into more manageable and understandable chunks. These languages proved more capable of writing larger, more maintainable programs. However they too began to bog down when faced with the need to share code among programmers and to write very large (greater than 50,000 line) programs.

·  The third generation of 3GL's (3.3 GL's) began to take hold in the late 80's. These were the object oriented languages. Although object oriented languages had been around since the late 1960's, it wasn't until the late 80's that computer hardware became fast enough and memory cheap enough to support them. (Object oriented programming is not a panacea. It adds a speed penalty over C or Fortran code, and often requires twice as much memory.)

 I.3 - Object-Oriented Programming

·  Object-Oriented Programming, or OOP, is a method of coding which results in better, more reliable and more reusable code.

·  Objects. "The fundamental idea behind object-oriented languages is to combine into a single unit both data and the functions that operate on that data. Such a unit is called an object."

·  Code in OOP. Let's say we have to write code that will model a store. We have to keep track of customers, how much money the customers have, and how much the customers have bought. We have to keep track of the sales clerks, where they are and when their shifts are. And we have to keep track of the merchandise, how much we have and where it is.
First we'll look at a Structural method. To keep track of the customers and their money and purchases, we'll need an array of structures. Same for the clerks, and same for the merchandise. That pretty much takes care of the data.
Now, we'll need a function to spend a customer's money, one to add a customer's purchases, one to move the clerks, one to update the clerks' shifts, one to move merchandise, and one to sell the merchandise. Each of these functions will be globally available and will be called by a larger control structure. Also, all of the data will be visible to all of the functions.
Now, let's look at an OOP implementation. We'll define three objects, Customer, Clerk, and Merchandise. The Customer object will have a Money variable and a Purchases array. The Customer object will also have a Spend method and a Buy method to update the money and purchases, respectively.
The Clerk object will have a Location variable and Shift variable. It will have a Move method and a CheckShift method.
The Merchandise object will have a Price variable and a Location variable. It will have Move and Sell methods.
We will have to have three arrays, one for each object type. The methods will be visible to the control structure, but the data will be hidden, or encapsulated, within each object.
So, what's the difference? They both have pretty much the same stuff; it's just arranged a little differently.
Well, the different arrangement is what makes OOP more powerful. The first obvious advantage is that the data is hidden. You cannot accidentally alter the data for a customer while fiddling with something else. You can only access one of the object's data by directly going through that object.
Another advantage is reusability of the code. Say, for instance, we now have to simulate an assembly line. One of the things we'll need to keep track of is where the workers are, and who's currently working. We can just borrow the Clerk object from our current model and plop it in our new project without any modification! It already knows how to keep track of where it is, and when it's shifts begin and end. That's all we need for an assembly-line worker.
Try doing that with the Structured code! You'd have to copy out the code for the structure, the code for each of the relevant functions, and modify the functions to fit the new array name, along with some other house keeping.
OOP allows you to bundle together related data and functions so that they can be easily moved around and reused.
Yet another advantage is in maintenance. Say we want to be more specific now. We want to model a grocery store. And, we don't want to keep track of just "merchandise." We want to keep track of meat, fruits and vegetables, and dry goods.
With the structured method, we'd have to trade the merchandise array for three separate item arrays. We would then have to write three new structures to handle the different data to be kept for each item. We would also have to modify all the functions which used the merchandise array to now work with the three new arrays, which could be a pain by itself!

·  Inheritance. With OOP we could take advantage of a property called inheritance.
Inheritance allows an object to inherit code from another object, saving the time of writing all new code.
We could create three new objects, Meat, Fruit_N_Veggies and Dry_Goods. Each of these new objects can be a child of our previous Merchandise class. So, each will automatically inherit all of the data and function contained in Merchandise.
Then, we could further specify each new object. For example, for Meat, we could add a SellDate variable and a Spoiled method so that we can measure losses from spoilage. Now, here's the really cool part. Since they are all children of Merchandise, they can all still go in the Merchandise array. We don't have to make a new array for each of them.

·  Polymorphism. When we create a new Clerk, we want to be able to initialize his or her starting position and shifts. So, when we create a Clerk, we can do this:
Clerk new_clerk = new Clerk();
new_clerk.Location = "aisle 9";
new_clerk.Shift = "12pm-12am";
But, this is rather long-winded and clumsy. We can define something called a constructor, which we'll discuss later, which will allow us to do the same thing like this:
Clerk new_clerk = new Clerk("aisle 9", "12pm-12am");
Now, isn't that better? But, the code for initializing the object is in the constructor, so we haven't really changed anything.
Here is where the power of polymorphism comes in. What if we don't want to always assign both the shift and the location at the same time? What if we want to be able to just assign the location and leave the shift until later? We can do that without any problem. We can define another constructor so that we can do this:
Clerk new_clerk = new Clerk("aisle 9");
Now, look at the two initializations by constructor. They both call Clerk() (which is the name of the constructor). How can they both be named the same, but use different arguments?
The answer is polymorphism! In OOP you can declare a method or constructor more than once, each with a different set of arguments, but the same name. When you call the method, the compiler knows from the argument list, called the signature, which version of the method you mean.
This may not be a major code-saving feature, but it reduces confusion by 100%. Instead of add_int(), add_float(), add_short(), etc., you can just have an add() which will know by the arguments which one you need.

1. Main Features of the Java Language

1.1 - Primitive Data Types

·  In Java Integer numeric types are 8-bit byte, 16-bit short, 32-bit int, and 64-bit long. A byte is a 8 binary digit. The representation of decimal numbers increases by a factor of 2 with each increasing bit. Thus, a n-bit type can hold (2n -1) decimal numbers. Thus a byte can hold decimals from -128 to 127. A long is an integer type variable that can hold up to 9,223,372,036,854,775,807.

·  A Float is a single-precision floating point numeric type with a 32-bit IEEE size (IEEE - Institute of Electrical and Electronics Engineers, New York, www.ieee.org).  To represent a real number in binaries, part of the binaries are use for the mantissa (significant digits) and the other part for the exponent of 10. The largest number a float can hold is 3.4028235E38.

·  A Double is double-precision floating point. The largest double value in Java is 1.7976931348623157E308.
Character data types are 16-bit byte, running from 0 through 65,535.
A Java boolean variable assumes the value true or false. A Java boolean type can't  be converted to any numeric type.

 1.1.1 - Declaration and initialization of data types

·  One declares a data type as follows
int Integername;
short IntegerShort;
char ThisChar;
. . . . .

·  After declaration, one initializes a data type as
IntegerName = 125;
IntegerShort = 32;
ThisChar = 'Q';   // Characters are defined within single quotes

·  Once can also declare and initialize the data types simultaneously.  The program below declares, initializes,  and prints out the largest numbers hold by the data types in Java.

·  Compile and Run it. Copy this program to your java work directory, compile it (in the DOS window type: javac MaxVariablesDemo.java and run it by typing javaMaxVariablesDemo). For the moment, don't worry much about the verbosity occurring in the first 2 lines of the code and on the printout lines. But, notice that from some name definitions (syntax) it is quite straightforward to understand what the program wants to do. To make an output to a file type java MaxVariablesDemo > OutputFileName.out To be able to compile and run from your directory, you have to set the PATH to it. Follow the instructions from the JDK installation to find out how change your PATH environment variable.
public class MaxVariablesDemo {   // defines the class
    public static void main(String args[]) { // the main method inside the class
        // integers
        byte largestByte = Byte.MAX_VALUE;  // MAX_VALUE is of course the maximum value
        short largestShort = Short.MAX_VALUE;
        int largestInteger = Integer.MAX_VALUE;
        long largestLong = Long.MAX_VALUE;
        // real numbers
        float largestFloat = Float.MAX_VALUE;
        double largestDouble = Double.MAX_VALUE;
        // other primitive types
        char aChar = 'S';
        boolean aBoolean = true;
        // display them all
        System.out.println("The largest byte value is " + largestByte);
        System.out.println("The largest short value is " + largestShort);
        System.out.println("The largest integer value is " + largestInteger);
        System.out.println("The largest long value is " + largestLong);
        System.out.println("The largest float value is " + largestFloat);
        System.out.println("The largest double value is " + largestDouble);
        if (Character.isUpperCase(aChar)) {
            System.out.println("The character " + aChar + " is upper case.");
        } else {
            System.out.println("The character " + aChar + " is lower case.");
        }
        System.out.println("The value of aBoolean is " + aBoolean);
    }
}
Its output yields:
The largest byte value is 127
The largest short value is 32767
The largest integer value is 2147483647
The largest long value is 9223372036854775807
The largest float value is 3.4028235E38
The largest double value is 1.7976931348623157E308
The character S is upper case.
The value of aBoolean is true

·  The Java language has some predefined libraries, or packages. This program only needs the standard library. When we type Byte.MAX_VALUE we access the maximum value of the object Byte, defined in this package. System.out.println is a function (in Java we say Method) contained in this package, which prints out everything written inside double quotes. The numeric value of the variable is written after the quotation by concatenating its name with a plus sign. Finally, the method Character.isUpperCase() checks if the variable aChar is uppercase or not. You will see several methods like this along these notes. They are all inherited from the Java packages and their action are most of times easy to figure out from their names. Try to memorize their name definitions.

1.2 Blocks and Scope

·  Blocks are important both syntactically and logically. Without the braces the code wouldn't compile. The compiler would have trouble figuring out where one method or class ended and the next one began. Similarly it would be very difficult for someone else reading your code to understand what was going on. For that matter it would be very difficult for you, yourself to understand what was going on. The braces, or curly brackets { }, are used to group related statements together. In the broadest sense everything between matching braces is executed as one statement (though depending not necessarily everything inside the braces is executed every time).

·  Blocks can be hierarchical. One block can contain one or more subsidiary blocks. In this case we have one outer block that defines the MaxVariablesDemo class. Within the MaxVariablesDemo block we have a method block called main.

·  A variable's scope is the block of code within which the variable is accessible and determines when the variable is created and destroyed.  The variable inside the block is a local variable.

·  You can declare local variables anywhere in a method or within a block of code in a method. In MaxVariablesDemo, all of the variables declared within the main method are local variables. The scope of each variable -- the code that can access each variable -- extends from the declaration of the variable to the end of the main method (indicated by the first right curly bracket } that appears in the program code). As soon as the variable goes out of scope, it does not exist and is a candidate for the garbage collector. The garbage collector is a part of the Java interpreter that deletes from memory all unused objects. Ex:
{
some code here  // no x defined here
{
more code here
int x  = 2;
}
y = x;  // WRONG. No x defined in this block
int x = 3;  // OK. This x is different from the one in the above block
more code here
}

1.3 - Comments

·  Comments can appear anywhere in a source file. Comments are identical to those in C and C++. Everything between /* and */ , or after //, is ignored by the compiler and everything on a line after two consecutive slashes is also thrown away. Therefore the following program is, as far as the compiler is concerned, identical to the first one:
// This is the Hello World program in Java
class HelloWorld {
    public static void main (String args[]) {
      /* Now let's print the line Hello World */
      System.out.println("Hello World");
  }
}
which yields
Hello World

1.4 - Operators

·  An operator performs a function on either one, two, or three operands. An operator that requires one operand is called a unary operator. For example, ++ is a unary operator that increments the value of its operand by 1. An operator that requires two operands is a binary operator. For example, = is a binary operator that assigns the value from its right-hand operand to its left-hand operand. And finally a ternary operator is one that requires three operands. The Java programming language has one ternary operator, ?:, which is a short-hand if-else statement.

 1.4.1 - Arithmetic Operators

Operator

Use

Description

+

op1 + op2

Adds op1 and op2

-

op1 - op2 

Subtracts op2 from op1

*

op1 * op2

Multiplies op1 by op2

/

op1 / op2

Divides op1 by op2

%

op1 % op2

Computes the remainder of dividing op1 by op2

Ex:
int i = 37;
int j = 42;
double x = 27.475;
double y = 7.22;
System.out.println("    i % j = " + (i % j)); // prints  i % j = 37
System.out.println("    x % y = " + (x % y)); // prints x % y = 5.815

Operator

Use

Description

++

op++

Increments op by 1; evaluates to the value of op before it was incremented 

++

++op

Increments op by 1; evaluates to the value of op after it was incremented 

--

op--

Decrements op by 1; evaluates to the value of op before it was decremented 

--

--op

Decrements op by 1; evaluates to the value of op after it was decremented 

Ex: Copy this program to a file named AutoInc.java and run it to understand the action of post- (pre-) (in) decrement operators.
// Demonstrates the ++ and -- operators.
public class AutoInc {
  public static void main(String[] args) {
    int i = 1;
    prt("i : " + i);
    prt("++i : " + ++i); // Pre-increment
    prt("i++ : " + i++); // Post-increment
    prt("i : " + i);
    prt("--i : " + --i); // Pre-decrement
    prt("i-- : " + i--); // Post-decrement
    prt("i : " + i);
  }
  static void prt(String s) {  // to save typing
    System.out.println(s);
  }
}
which yields
i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1

 1.4.2 - Relational and logical operators

Operator

Use

Returns true if

>

op1 > op2 

op1 is greater than op2

>=

op1 >= op2

op1 is greater than or equal to op2

<

op1 < op2

op1 is less than op2

<=

op1 <= op2 

op1 is less than or equal to op2

==

op1 == op2

op1 and op2 are equal

!=

op1 != op2 

op1 and op2 are not equal 

 

Operator

Use

Returns true if

&&

op1 && op2

op1 and op2 are both true, conditionally evaluates op2

||

op1 || op2

either op1 or op2 is true, conditionally evaluates