Java Notes
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.
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.
· 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.
· 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.)
· 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.
· 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.
· 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.
· 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
}
· 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
· 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.
|
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
|
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 |