Introduction to Java
Java is a modern,
platform independent programming language that is object-oriented for
reusability of code modules.
Why Use Java
Why
should one use Java as an authoring language?
·
object
oriented programming
·
Java
applications are platform independent
·
Java
is security conscious and enforces it through a virtual machine paradigm
·
Java
is easy to learn as no user programmed memory management or pointers are
involved
The system
environment variables path and classpath will have
to be set within AUTOEXEC.BAT to point at the Java binary file
installation directories.
MSDOS: |
EDIT Autoexec.bat |
Windows 9x: |
Start -- Run -- Sysedit |
Windows XP: |
Start - Control Panel - System - Advanced - Environment
Variables |
Test Examples
To test that the developer's
environment has been installed correctly use the following simple HelloWorld application.
Your First Java Application
/*
* The HelloWorldApp class implements an application that
* displays "Hello World!" to the standard output device.
*/
public class HelloWorldApp
{
public static void main(String[] args)
{
// Display "Hello World!"
System.out.println("Hello World!");
}
}
Save the file as HelloWorldApp.java.
Next compile the source file
into bytecode by using the command javac HelloWorldApp.java. If an error message stating Not
Found occurs, type the
command javac.
If there is still an error message it means that the path needs setting. If there is no error at
this point then the .java file is missing or there was a typing error. Most
typing errors are due to case sensitivity. In Java, UPPER and lower case ARE different.
Finally run the application
with the Java interpreter by using the command Java HelloWorldApp.
Books
·
Java 2 - By Example Friesen, QUE
·
Teach Yourself Java Cadenhead & Lemay, Sams
·
Java 2 - Complete Reference Naughton, Osbourne
What Are Objects
An object is a self-contained entity which has its own private
collection of properties (ie. data)
and methods (ie. operations) that
encapsulate functionality into a reusable and dynamically loaded structure.
After a class definition has been
created as a prototype, it can be
used as a template for creating new classes that add functionality. Objects are
programing units of a particular class. Dynamic loading implies that
applications can request new objects of a particular class to be supplied on an
'as needed' basis. Objects provide the extremely useful benefit of reusable code that minimizes development
time.
The Basic Structure of a
Java Application
A Java application resembles programs in most compiled languages. Code in
object format resides on the user's machine and is executed by a run-time
interpreter that normally has to be user installed.
/**
* The HelloWorldApp class implements an
application
* that displays "Hello World!" to
the standard output.
*/
public
class HelloWorldApp
{
public static void main(String[] args)
{
// Display Hello World! now
System.out.println("Hello
World!");
}
}
The first four lines is a
comment on the application's function. Comments are not required but are
extremely useful as documentation within the source. Other notes and doc files
may get lost but these stay right where they are most useful. A long comment
starts with a /* or /** and ends with a */ Short one line comments begin with // and end with the <return>.
The fifth line starts with
the reserved word public. This
indicates that objects outside the object can invoke (or use) it. The reserved
word class indicates that we are
building a new object with the name that follows. HelloWorldApp is the object name (your choice) and is case
sensitive.
Java
'style' is to capitalize the first letter of each word only. The line concludes
with an opening curly bracket that marks the start of the object definition.
static
indicates that it calls the class and not an 'instance' of the class. The
concept of 'instance' will be discussed later. The method's name is main and the reserved word void indicates that no result is
returned back.
Line eight invokes the
println method of the system.out object. What is to be printed is passed in the
argument as a string parameter.
Finally closing curly
brackets are used for the main and for the object definition.
Syntax & Grammar
Java like many languages (eg. C, JavaScript, awk, perl) is based on a common syntax and grammar developed by the Bell Labs in the 60's. This makes it easy to cross over from one language to another based on program requirements, resources and politics.
Syntax Notation
The specification of a class
has the following syntax:
["public"] ["abstract"|"final"]"class" class_name ["extends" object_name]
"{"
// properties declarations
// behavior declarations
"}"
Essentially syntax defines
the 'rules' which the compiler will use to check your programs for compilation.
Whether they execute correctly is a whole different issue
Lexical Structure
The lexical structure of a
programming language is the set of elementary rules that specify how you write
a program. It is the lowest level syntax of a language and specifies such
things as what identifier names look like. Some of the basic rules for Java
are:
·
Java is case sensitive.
·
Whitespace, tabs, and newline characters are ignored
except when part of string constants. They can be added as needed for
readability.
·
Statements terminate in semicolons! Make sure to always terminate statements with a semicolon.
·
Reserved
words (or keywords) have special meanings within the language syntax.
·
Literals are data constants. They
can be either numbers, characters or strings. Examples of numbers are true
(boolean), 123 (integer) and 1.2 (floating point). Examples of character
literals are 'a' and '\t'. Examples of strings are "hello world" and
"hi\n\How are You?".
·
Identifiers are names for variables
and functions. The first character must be a letter, underscore
or dollar sign. Following characters can also include digits. Letters are A to
Z, a to z, and Unicode characters above hex 00C0. Java style normally uses an
initial capital letter for a class identifier, all uppercase for constants and
lowercase for method and variable identifiers. Note: an identifier must not be from the Java reserved word list.
·
Single line comments begin with //
·
Block comments begin with /* and end with */ [preferred]
·
Documentary comments begin with /** and end with **/
Literal Constants
Literal constants are values that do not change within a
program. Numeric constants default to integer or double unless a suffix is
appended. Note that a character can be represented by an ASCII equivalent. Some
literal constant types and examples are:
· Boolean: true, false · Integer: 5, 0xFF (hexadecimal) ·
Long
Integer: 5l, 0xFFl (hexadecimal) |
· Double Precision: 2.543, 8e12, -4.1E-6 · Floating Point: 2.543f, 8e12f · Char (ie. character): 'c', '\f', 65 ·
String:
"Fred", "Fred and Ethel" |
Escape Characters
Escape (aka backslash)
characters are used inside literal strings to allow print formatting as well as
preventing certain characters from causing interpretation errors. Each escape
character starts with a backslash. The available character sequences are:
Seq |
Name |
Seq |
Name |
\b |
backspace |
\f |
formfeed |
\t |
horizontal tab |
\" |
double quote |
\n |
newline |
\' |
single quote |
\r |
carriage return |
\\ |
backslash |
\### |
Latin encoded character |
\uHHHH |
Unicode encoded character |
Variables
Variables are temporary data holders. Variable
names (or identifiers) must begin with a letter, underscore or dollarsign, use
ASCII or Unicode characters and underscore only, and are case sensitive.
They can not be one of the Java reserved words. Variables
are declared with a datatype. Java is a strongly
typed language as the variable can only take a value that matches its declared
type.
byte x,y,z; /* 08bits long, not assigned, multiple declaration */
short numberOfChildren; /* 16bits long */
int counter; /* 32bits long */
long WorldPopulation; /* 64bits long */
float pi; /* 32bit single precision */
double avagadroNumber; /* 64bit double precision */
boolean signal_flag; /* true or false only */
char c; /* 16bit single Unicode character */
Variables can be made constant
or read only by prepending the modifier final to the
declaration. Java convention uses all uppercase for final variable
names.
Arrays
Arrays allow you to store
several related values in the same variable (eg. a set of marks).
int i []; /* one dimension array */
char c[][]; /* two dimension array */
float [] f; /* geek speak way */
Bowl shelfA[]; /* array of objects */
Note that the brackets are
left blank. Declaration of an array only forms a prototype or
specification for the array. Array memory allocation is done
explicitly with the new reserved word and will require known static bounds
(ie. number of elements).
Operators, Expressions, Conditions
Operators are actions that manipulate, combine or
compare variables. They fall into several categories as follows:
Arithmetic: + - * / % (modulus) ++ (increment) -- (decrement)
Assignment: = += -= \= %=
Array Allocation: new (eg. int a[] = new int[10])
String Concatenation: +
Comparison: == != > >= < <=
Boolean Comparison: ! & | ^ && || (&& are short circuit ops)
Bitwise Comparison: ~ & | ^ (xor) << >> >>>
Bitwise Assignment: &= |= ^= (xor) <<= >>= >>>=
Cast: (var_type)
Conditional: (expr1) ? expr2 : expr3
Statements, Blocks and Scope
Statements are complete program instructions made
from constants, variables, expressions and conditions. Statements always
end with a semicolon. A program contains one or more statements.
Execution blocks are the
sections of code that start and end with curly brackets. Variables maintain
their definition (or 'scope') until the end of the execution block
that they are defined in. This is the reason why variable declaration
and assignment can be a two step process.
Assignment Statements
Assignment
statements use an
assignment operator to store a value or the result of an expression in a
variable. Memory allocation is done at the time of assignment. Simple
examples include first_name =
"Fred"; and count
+=;
Local variables must
be assigned a value prior to use.
Arrays are allocated memory dynamically
based on their array size through the use of the new reserved
word.
intArray = new int[5]; //previously declared
int markArray = new int[9]; //declaration and allocation at same time
int grades = new int[maxMarks]; //maxMarks must evaluate to positive integer
Conditional Statements
Conditional
statements execute a
block or set of other statements only if certain conditions are met.
The condition is always enclosed in round brackets. The statements to
be performed if the condition is true are enclosed in curly brackets. For
example:
if (value > 5) { x = 7 };
Occasionally you may want to
perform some actions for the false outcome of the condition as well. The else
keyword is used to separate branches.
if (name == "Fred")
{
x = 4;
}
else {
x = 20;
};
Loops
For statements allow a set of statements to be repeated
or looped through a fixed number of times. The round bracket contains
initializer(s) ; conditional test ; incrementer(s). If more than one
initializer or incrementer is used they are separated with commas. The test
condition is checked prior to executing the block. The incrementing is
completed after executing the block. For example to output #1 #2 #3
etc. on separate rows you could write:
int i;
for (i=1; i<=15; i++)
{
document.writeln("#"+i);
};
While statements allow a set of statements to be repeated
or looped until a certain condition is met. The test condition is checked prior
to executing the block. For example to output #1 #2 #3 etc. on separate
rows you could write:
int i = 0;
while (i<=5)
{
document.writeln("#"+i);
i = i + 1;
};
Do While statements allow a set of statements to be repeated
or looped until a certain condition is met. The test condition is
checked after executing the block. For example to output #1 #2 #3 etc.
on separate rows you could write:
int i = 1;
do {
document.writeln("#"+i);
i = i + 1;
} while (i<=5);
The Switch Statement
Switch (or case)
statements are used
to select which statements are to be executed depending on a variable's value
matching a label. Note that the variable can only be int or char
datatype.
char yourchoice;
yourchoice = (char) System.in.read();
switch (yourchoice)
{
case '1': System.out.println("You typed a 1"); break;
case '2': System.out.println("You typed a 2"); break;
case '3': System.out.println("You typed a 3"); break;
case '4': System.out.println("You typed a 4"); break;
default : System.out.println("Oops!, that was an invalid entry.");
};
Continue, Break and Return
Continue statements are used in looping statements to force
another iteration of the loop before reaching the end of the current one. The
following is a trivial example. Most often the continue is used as part of a
conditional statement that only happens in certain cases.
int x = 0;
while (x < 10)
{
x++;
System.out.println(x);
continue;
// you will never get to this point!!
};
Break statements are used in looping and 'switch'
statements to force an abrupt termination or exit from the loop or switch. In
the following example the loop is never completed. Once again the normal use of
break is as part of a conditional statement.
int x = 0;
while (x < 10)
{
x++;
System.out.println(x);
break;
// you will never get to this point!
};
Return statements are used to force a quick exit from a method.
They are also used to pass values back from methods.
Command Line Arguments
Command line arguments are
accessible through the args array (assuming that args is the
name used in the class header [NOTE: This is a Java coding
convention!]). The array does not include the interpreter java
command or the class name and the count starts at 0. Each argument is passed as
a string but may be converted to other types as in the following:
int w = Integer.parseInt(args[0]);
// or sometimes you want the argument to be a character type
char s = args[1].charAt(0) // uses first letter of argument only
The number of arguments
passed in can be determined by using the args.length property. This is
very important if there is a variable number of arguments or when checking for
required arguments.
NOTE: All double quote marks are consumed by
the Java command line parser and are not passed on to the argument array!
Project: Metric Converter
At this point we need to
have a project that allows you to test your knowledge of syntax without getting
too involved in a user interface or the complexities of object manipulation. A
toy metric converter should be just about right as it allows three basic stages
of application development; IPO, branching, and switch selection.
The goal of the first stage
is to input a temperature in degrees Fahrenheit (geezer's unit), calculate the
value in Celsius (modern way) and then display the answer. This tests your
ability to get some input, do a math process and finally
display the output. It also gives some practice working with the
development environment and doing compilations.
Our basic input
procedure will be to get the Fahrenheit reading from the command line using the
method Integer.parseInt(args[0]) which returns an integer from the
first argument on the command line.
The math processing
part is really easy. This is the basic principle for the formula deg_C=((deg_F-32)
* 5)/9. Since division may produce a real number with a decimal value, you
should cast the input number into a double and work with double
precision floating point numbers.
Once the answer is
calculated, it can be output to your display by using the method System.out.println(deg_C)
The second stage is to
modify the application so that the conversion can be made in either direction.
This will require a logical branch based on some additional input. Let's say
the input has to look like 40 F or 10 C
char units = args[1].charAt(0);
units = Character.toUpperCase(units);
if (units == 'F')
{
//do F --> C conversion
}
else
{
//do C --> F conversion
}
Also the command line should
be checked to make sure that the number of arguments is correct (which we
failed to do in stage one). A simple error message can be printed after checking
the count such as:
if (args.length != 2)
{
System.out.println ("Usage: Metric_2 ## F|C");
return;
}
The third stage is to use the units argument to select
the appropriate conversion routine (temperature, mass, distance, etc.) and switch
to routines accordingly. For our program just add routines to convert lbs to
kilograms and kg to lbs. This stage maxes out the limits of programming in the
small. In later tutorials we will use GUI objects to allow for interactive
input as well as using custom objects to encapsulate each conversion.
switch (units)
{
case 'F': { /* conversion routine */ } ; break;
case 'C': { /* conversion routine */ } ; break;
case 'L': { /* conversion routine */ } ; break;
case 'K': { /* conversion routine */ } ; break;
default : System.out.println("Oops! I do not understand the units...");
}
Encapsulation
Encapsulation
is the ability of an object to place a boundary around its properties (ie. data) and methods (ie. operations). Programs written in
older languages suffered from side effects where variables sometimes had their contents
changed or reused in unexpected ways. Some older languages even allowed
branching into procedures from external points. This led to 'spaghetti' code
that was difficult to unravel, understand and maintain. Encapsulation is one of
three basic principles within object oriented programming languages.
Object variables can be
hidden completely from external access. These private variables can only be seen or modified by
use of object accessor and mutator methods. Access to other object variables can be allowed but with tight
control on how it is done. Methods can also be completely hidden from external
use. Those that are made visible externally can only be called by using the
object's front door (ie. there is no 'goto' branching concept).
Class Specification
A class specifies the properties (data) and
methods (actions) that objects can work with. It is a template or prototype for
each of many objects made to the class design. The syntax for a class is:
["public"] ["abstract"|"final"]"class" class_name
["extends" object_name] ["implements" interface_name]
"{"
// properties declarations
// behavior declarations
"}"
The first optional group
indicates the visibility or scope of accessibility from other objects. public means visible everywhere. The default
(ie. omitted) is package (aka friendly) or visible within the current package only.
The second optional group
indicates the capability of a class to be inherited or extended by other
classes. abstract classes must be extended and final classes can never be extended by inheritance. The
default (ie. omitted) indicates that the class may or may not be extended at
the programmers discretion.
The third option of extends is described in the tutorial on
inheritance.
The fourth option of implements is described in the tutorial on
interfaces.
Let's use a simple box as an
example of a class specification. The box has length, width and height properties
as well as a method for displaying the volume. An example of how this might be
specified in Java is:
public class Box
{
// what are the properties or fields
private int length;
private int width;
private int height;
// what are the actions or methods
public void setLength(int p)
{length = p;}
public void setWidth(int p)
{width = p;}
public void setHeight(int p)
{height = p;}
public int displayVolume()
{System.out.println(length*width*height);}
}
Note1: There is no main
method in a class defining template!
Note2: Class names
begin with a capital. Convention uses lowercase for all other names.
Properties (aka Field
Variables)
Classes without properties
or behaviour are not very useful. Properties in Java are sometimes called field
variables. To declare a
property use the following syntax:
[ "public" |
"private" | "protected" ] [ "final" ]
[ "static" |
"transient" | "volatile" ]
data_type var_name [ = var_initializer ]
";"
The items in the first
optional group indicate the 'visibility' or accessibility from other objects. public means visible everywhere (global). private indicates accessible only to this class
and nested classes. protected means visible to this class or inherited (ie.
extended) classes only. The default (keyword omitted) is friendly or visible within the current package
(folder) only.
final
indicates continuous retention and unchangeable after initial assignment (ie.
it is read only or constant).
The third optional group
indicates how long a value is retained in the variable. static indicates that the value is shared by all members of the class and exists
for all runtime. Static members can be referenced without creating an instance
of the class. transient prevents the variable from being transferred during a serial operation
such as file i/o. volatile is used in threading to prevent overwrite issues.
The data_type is one of the primitive types and can be
optionally initialized.
Types of Methods
Class behaviours are
represented in Java by methods. To declare a method use the following syntax:
[ "public" | "private" | "protected" ] [ "final" ]
[ "static" | "abstract" | "native" ]
return_data_type method_name "(" parameter_list ")"
"{"
// some defining actions
"}"
Accessibility keywords are
the same as for properties.
static methods
are shared and retained. abstract methods must be redefined on inheritance. native methods are written in C but accessible
from Java.
The return_data_type defines the type of value that the
calling routine receives from the object (the reply message in object
terminology). It can be any of the primitive types or the reserved word void (default value) if no message is to be
returned. The statement return variablename; is used to declare the value to be returned to
the calling routine.
The parameter_list can contain from zero to many entries of datatype
variablename pairs. Entries
are separated by commas. Some examples are:
public static void example1() { }
public static int add2(int x) { x+=2; return x; }
public static double example3(int x, double d) { return x*d; }
public static void example4(int x, int y, boolean flagger) { }
Constructor methods allow class objects to be created with fields initialized to values as
determined by the methods' parameters. This allows objects to start with values
appropriate to use (eg. salary set to a base level or employeeNumber set to an
incrementing value to guarantee uniqueness). For our simple box class:
public Box() // default box is point
{length = 0; width = 0; height = 0;}
public Box(int l,int w,int h) // allows giving initial size
{length = l; width = w; height = h;}
Note that there is no class
keyword or return datatype keyword. Also the method name is the same as the
class name. This is what marks the fragment as a constructor method. If no
constructor method is defined for a class, a default constructor is automatically used to initialize all
fields to 0, false or unicode(0) as appropriate to the datatype. Constructors
can be overloaded in the same way that instance methods are.
One clever programming
device is to declare the constructor with no parameters as private and use it
to initialize all properties. Then other constructors can first call it using this() and then do their own specific property
validations/initialization.
Accessor (or observer) methods read property (ie field variable) values and are
conventionally named getFoobar() or whatever the property is called.
Mutator (or transformer) methods set property values and are often named setFoobar()
etc. Mutators can be used to ensure that the property's value is valid in both
range and type.
It is good programming
practice to include accessor and mutator methods for each property in the class. They are
perfect example of object encapsulization. The exceptions to writing accessor/mutator
methods for each property is for those that are used only within the class
itself or for properties that are set in more complex ways.
Helper methods are those routines that are useful within the class methods but not
outside the class. They can help in code modularization. Normally they are
assigned private access to restrict use.
Overloading and Recursion
Overloaded methods are methods with the same name
signature but either a different number of parameters or different types
in the parameter list. For example 'spinning' a number may mean increase it,
'spinning' an image may mean rotate it by 90 degrees. By defining a method for
handling each type of parameter you achieve the effect that you want.
Overridden methods are methods that are redefined within an inherited class.
Recursive methods are methods that are defined in terms of themselves. A classic recursion
is factorials where n factorial is the product of n and all the products before
it down to one. In Java this would be programmed as:
class Factorial
{ int fact(int n)
{ int result;
if(n==1) return 1;
result = fact(n-1) * n;
return result;
}
}
Object Creation and Destruction
To create an object of a particular class use the
creation method after the particular class has already been specified. For
example, now that there is a constructor for the Box class you can make
specific instances or discrete copies of a box by using the assignment operator and the new memory allocation operator as in:
Box box_1 = new Box(3,4,5);
Note: Once a class has been created, a datatype exists with the same name.
You do not need to destroy or remove an object when it is no longer needed.
Java automatically flags unused objects and applies garbage
collection when appropriate.
However you may occasionally need to use the finalize method to insure that a non-Java resource
such as a file handle or a window font character is released first. The general
form is:
void finalize()
{
//cleanup code goes here
}
Inner Classes
Inner classes
are classes nested inside another class. They have access to the outer class
fields and methods even if marked as private. Inner classes are used primarily
for data structures, helper classes, and event handlers.
A brief example of how an inner class can be used as a data structure is:
public class Main2
{
class Person
{
// inner class defines the required structure
String first;
String last;
}
// outer class creates array of person objects with specific properties
// the objects can be referenced by personArray[1].last for example
Person personArray[] = {new Person(), new Person(), new Person()};
}
Suppose you need to write a traffic simulation program that watches cars going past an intersection. Each car has a speed, a maximum speed, and a license plate that uniquely identifies it. In traditional programming languages you'd have two floating point and one string variable for each car. With a class you combine these into one thing like this.
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
These variables (licensePlate
,
speed
and maxSpeed
) are called the member
variables, instance variables, or fields of the
class.
Fields tell you what a class is and what its properties are.
An object is a specific instance of a class with particular values (possibly mutable) for the fields. While a class is a general blueprint for objects, an instance is a particular object.
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
To instantiate an object in Java, use the keyword new
followed by a call to the class's
constructor. Here's how you'd create a new Car
variable called c
:
Car c;
c = new Car();
The first word, Car
,
declares the type of the variable c
.
Classes are types and variables of a class type need to be declared just like
variables that are ints or doubles.
The equals sign is the assignment operator and new
is the construction operator.
Finally notice the Car()
method. The parentheses tell you this is a method and not a data type like the Car
on the left hand side of the
assignment. This is a constructor, a method that creates a new instance of a
class. You'll learn more about constructors shortly. However if you do nothing,
then the compiler inserts a default constructor that takes no arguments.
This is often condensed into one line like this:
Car c = new Car();
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
Once you've constructed a car, you want to do something with it. To access
the fields of the car you use the .
separator. The Car
class has
three fields
licensePlate
speed
maxSpeed
Therefore if c
is a Car
object, c
has three fields as well:
c.licensePlate
c.speed
c.maxSpeed
You use these just like you'd use any other variables of the same type. For instance:
Car c = new Car();
c.licensePlate = "New York A45 636";
c.speed = 70.0;
c.maxSpeed = 123.45;
System.out.println(c.licensePlate + " is moving at " + c.speed +
"kilometers per hour.");
The .
separator selects a
specific member of a Car
object by name.
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
The next program creates a new car, sets its fields, and prints the result:
class CarTest {
public static void main(String args[]) {
Car c = new Car();
c.licensePlate = "New York A45 636";
c.speed = 70.0;
c.maxSpeed = 123.45;
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
This program requires not just the CarTest
class but also the Car
class. To make them work together put the Car
class in a file called Car.java. Put the CarTest
class in a file called CarTest.java. Put both these files in the same
directory. Then compile both files in the usual way. Finally run CarTest
. For example,
% javac Car.java
% javac CarTest.java
% java CarTest
New York A45 636 is moving at 70.0 kilometers per hour.
Note that Car
does not
have a main()
method so you
cannot run it. It can exist only when called by other programs that do have main()
methods.
Fields can (and often should) be initialized when they're declared, just like local variables.
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
}
The next program creates a new car and prints it:
class CarTest2 {
public static void main(String[] args) {
Car c = new Car();
System.out.println(c.licensePlate + " is moving at " + c.speed +
"kilometers per hour.");
}
}
For example,
$ javac Car.java
$ javac CarTest2.java
$ java CarTest
is moving at 0.0 kilometers per hour.
Data types aren't much use unless you can do things with them. For this purpose classes have methods. Fields say what a class is. Methods say what a class does. The fields and methods of a class are collectively referred to as the members of the class.
The classes you've encountered up till now have mostly had a single method, main()
. However, in general classes can
have many different methods that do many different things. For instance the Car
class might have a method to make
the car go as fast as it can. For example,
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
}
The fields are the same as before, but now there's also a method called floorIt()
. It begins with the Java
keyword void
which is the
return type of the method. Every method must have a return type which will
either be void
or some data
type like int
, byte
, float
,
or String
. The return type
says what kind of the value will be sent back to the calling method when all
calculations inside the method are finished. If the return type is int
, for example, you can use the method
anywhere you use an int
constant. If the return type is void
then no value will be returned.
floorIt
is the name of
this method. The name is followed by two empty parentheses. Any arguments
passed to the method would be passed between the parentheses, but this method
has no arguments. Finally an opening brace ( {
) begins the body of the method.
There is one statement inside the method
this.speed = this.maxSpeed;
Notice that within the Car
class the field names are prefixed with the keyword this
to indicate that I'm referring to
fields in the current object.
Finally the floorIt()
method is closed with a }
and the class is closed with another }
.
Outside the Car
class,
you call the floorIt()
method just like you reference fields, using the name of the object you want to
accelerate to maximum and the .
separator as demonstrated below
class CarTest3 {
public static void main(String args[]) {
Car c = new Car();
c.licensePlate = "New York A45 636";
c.maxSpeed = 123.45;
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
c.floorIt();
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
The output is:
New York A45 636 is moving at 0.0 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
The floorIt()
method is
completely enclosed within the Car
class. Every method in a Java program must belong to a class. Unlike C++
programs, Java programs cannot have a method hanging around in global space
that does everything you forgot to do inside your classes.
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
void floorIt() {
speed = maxSpeed;
}
}
Within the Car
class, you
don't absolutely need to prefix the field names with this.
like this.licensePlate
or this.speed
.
Just licensePlate
and speed
are sufficient. The this.
may be implied. That's because the
floorIt()
method must be
called by a specific instance of the Car
class, and this instance knows what its data is. Or, another way of looking at
it, the every object has its own floorIt()
method.
class Car {
String licensePlate = ""; // member variable
double speed; = 0.0; // member variable
double maxSpeed; = 123.45; // member variable
boolean isSpeeding() {
double excess; // local variable
excess = this.maxSpeed - this.speed;
if (excess < 0) return true;
else return false;
}
}
The licensePlate
, speed
and maxSpeed
variables of the Car
class, belong to a Car
object, not to any individual method. They are defined outside of any methods
but inside the class and are used in different methods. They are called member
variables or fields.
Member variable, instance variable, and field are different words that mean the same thing. Field is the preferred term in Java. Member variable is the preferred term in C++.
A member is not the same as a member variable or field. Members include both fields and methods.
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
Setter methods, also known as mutator methods, merely set the value of a
field to a value specified by the argument to the method. These methods almost
always return void
.
One common idiom in setter methods is to use this.name
to refer to the field and give the
argument the same name as the field. For example,
class Car {
String licensePlate; // e.g. "New York A456 324"
double speed; // kilometers per hour
double maxSpeed; // kilometers per hour
// setter method for the license plate property
void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
// setter method for the maxSpeed property
void setMaximumSpeed(double maxSpeed) {
if (maxSpeed > 0) this.maxSpeed = maxSpeed;
else this.maxSpeed = 0.0;
}
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
class
CarTest5 {
public static void main(String args[]) {
Car c = new Car();
c.setLicensePlate("New York A45
636");
c.setMaximumSpeed(123.45);
System.out.println(c.licensePlate + "
is moving at " + c.speed +
" kilometers per hour.");
for (int i = 0; i < 15; i++) {
c.accelerate(10.0);
System.out.println(c.licensePlate +
" is moving at " + c.speed +
" kilometers per hour.");
}
}
}
Here's the output:
$ java
CarTest5
New York
A45 636 is moving at 0.0 kilometers per hour.
New York
A45 636 is moving at 10.0 kilometers per hour.
New York
A45 636 is moving at 20.0 kilometers per hour.
New York
A45 636 is moving at 30.0 kilometers per hour.
New York
A45 636 is moving at 40.0 kilometers per hour.
New York
A45 636 is moving at 123.45 kilometers per hour.
For example, the following getLicensePlate()
method returns the current value of the licensePlate
field in the Car
class.
String getLicensePlate() {
return this.licensePlate;
}
A method like this that merely returns the value of an object's field or property is called a getter or accessor method.
It is not possible to return more than one value from a method. You cannot,
for example, return the licensePlate
,
speed
and maxSpeed
fields from a single method.
You could combine them into an object of some kind and return the object.
However this would be poor object oriented design.
The right way to solve this problem is to define three separate methods, getSpeed()
, getMaxSpeed()
, and getLicensePlate()
, each of which returns
its respective value. For example,
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// getter (accessor) methods
String getLicensePlate() {
return this.licensePlate;
}
double getMaxSpeed() {
return this.maxSpeed;
}
double getSpeed() {
return this.speed;
}
// setter method for the license plate property
void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
// setter method for the maxSpeed property
void setMaximumSpeed(double maxSpeed) {
if (maxSpeed > 0) this.maxSpeed = maxSpeed;
else this.maxSpeed = 0.0;
}
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
A constructor creates a new instance of the class. It initializes all the variables and does any work necessary to prepare the class to be used. In the line
Car c = new Car();
Car()
is the constructor.
A constructor has the same name as the class.
If no constructor exists Java provides a generic one that takes no arguments
(a noargs constructor), but it's better to write your own. You make
a constructor by writing a method that has the same name as the class. Thus the
Car constructor is called Car()
.
Constructors do not have return types. They do return an instance of their own class, but this is implicit, not explicit.
The following method is a constructor that initializes license plate to an empty string, speed to zero, and maximum speed to 120.0.
Car() {
this.licensePlate = "";
this.speed = 0.0;
this.maxSpeed = 120.0;
}
Better yet, you can create a constructor that accepts three arguments and use those to initialize the fields as below.
Car(String licensePlate, double speed, double maxSpeed) {
this.licensePlate = licensePlate;
this.speed = speed;
if (maxSpeed > 0) this.maxSpeed = maxSpeed;
else this.maxSpeed = 0.0;
if (speed > this.maxSpeed) this.speed = this.maxSpeed;
if (speed < 0) this.speed = 0.0;
else this.speed = speed;
}
Or perhaps you always want the initial speed to be zero, but require the maximum speed and license plate to be specified:
Car(String licensePlate, double maxSpeed) {
this.licensePlate = licensePlate;
this.speed = 0.0;
if (maxSpeed > 0) this.maxSpeed = maxSpeed;
else this.maxSpeed = 0.0;
}
The next program uses the constructor to initialize a car rather than setting the fields directly.
class CarTest7 {
public static void main(String args[]) {
Car c = new Car("New York A45 636", 123.45);
System.out.println(c.getLicensePlate() + " is moving at " + c.getSpeed() +
" kilometers per hour.");
for (int i = 0; i < 15; i++) {
c.accelerate(10.0);
System.out.println(c.getLicensePlate() + " is moving at " + c.getSpeed()
+ " kilometers per hour.");
}
}
}
You no longer need to know about the fields licensePlate
, speed
and maxSpeed
. All you need
to know is how to construct a new car and how to print it.
You may ask whether the setLicensePlate()
method is still needed since it's now set in a constructor. The general answer
to this question depends on the use to which the Car
class is to be put. The specific question is whether
a car's license plate may need to be changed after the Car
object is created.
Overloading is when the same method or operator can be used on many different types of data. For instance the + sign is used to add ints as well as concatenate strings. The plus sign behaves differently depending on the type of its arguments. Therefore the plus sign is inherently overloaded.
Methods can be overloaded as well. System.out.println()
can print a double
, a float
, an int
, a long
,
or a String
. You don't do
anything different depending on the type of number you want the value of.
Overloading takes care of it.
Programmer-defined classes can overload methods as well. To do this simply write two methods with the same name but different argument lists.
public class Car {
private String licensePlate; // e.g. "New York A456 324"
private double speed; // kilometers per hour
private double maxSpeed; // kilometers per hour
// constructors
public Car(String licensePlate, double maxSpeed) {
this.licensePlate = licensePlate;
this.speed = 0.0;
if (maxSpeed >= 0.0) {
this.maxSpeed = maxSpeed;
}
else {
maxSpeed = 0.0;
}
}
public Car(String licensePlate, double speed, double maxSpeed) {
this.licensePlate = licensePlate;
if (maxSpeed >= 0.0) {
this.maxSpeed = maxSpeed;
}
else {
maxSpeed = 0.0;
}
if (speed < 0.0) {
speed = 0.0;
}
if (speed <= maxSpeed) {
this.speed = speed;
}
else {
this.speed = maxSpeed;
}
}
// other methods...
}
Normally a single identifier refers to exactly one method or constructor. When as above, one identifier refers to more than one method or constructor, the method is said to be overloaded.
If there are no arguments to the constructor, or two or three arguments that aren't the right type in the right order, then the compiler generates an error because it doesn't have a method whose signature matches the requested method call. For example
Error: Method Car(double) not found in class Car.
Car.java line 17
Code reusability is claimed to be a key advantage of object-oriented languages over non-object-oriented languages. Inheritance is the mechanism by which this is achieved. An object can inherit the variables and methods of another object. It can keep those it wants, and replace those it doesn't want.
In this example you
begin by defining a more general MotorVehicle class.
public
class MotorVehicle {
private String licensePlate; // e.g. "New York A456 324"
private double speed; // kilometers per hour
private double maxSpeed; // kilometers per hour
private String make; // e.g.
"Harley-Davidson", "Ford"
private String model; // e.g. "Fatboy",
"Taurus"
private int
year; // e.g. 1998,
1999, 2000, 2001, etc.
private int
numberPassengers; // e.g. 4
// constructors
public MotorVehicle(String licensePlate,
double maxSpeed,
String make, String model, int year, int
numberOfPassengers) {
this(licensePlate, 0.0, maxSpeed, make,
model, year, numberOfPassengers);
}
public MotorVehicle(String licensePlate,
double speed, double maxSpeed,
String make, String model, int year, int
numberOfPassengers) {
this.licensePlate = licensePlate;
this.make = make;
this.model = model;
this.year = year;
this.numberPassengers = numberOfPassengers;
if (maxSpeed >= 0.0) {
this.maxSpeed = maxSpeed;
}
else {
maxSpeed = 0.0;
}
if
(speed < 0.0) {
speed = 0.0;
}
if (speed <= maxSpeed) {
this.speed = speed;
}
else {
this.speed = maxSpeed;
}
}
// getter (accessor) methods
public String getLicensePlate() {
return this.licensePlate;
}
public String getMake() {
return this.make;
}
public String getModel() {
return this.model;
}
public int getYear() {
return this.year;
}
public int getNumberOfPassengers() {
return this.numberPassengers;
}
public int getNumberOfPassengers() {
return this.numberWheels;
}
public double getMaxSpeed() {
return this.maxSpeed;
}
public double getSpeed() {
return this.speed;
}
// setter method for the license plate
property
protected void setLicensePlate(String
licensePlate) {
this.licensePlate = licensePlate;
}
// accelerate to maximum speed
// put the pedal to the metal
public void floorIt() {
this.speed = this.maxSpeed;
}
public void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
The MotorVehicle
class
has all the characteristics shared by motorcycles and cars, but it leaves the
number of wheels unspecified, and it doesn't have a numberDoors
field since not all motor
vehicles have doors.
Next you define two subclasses of MotorVehicle
,
one for cars and one for motorcycles. To do this you use the keyword extends
.
public class Motorcycle extends MotorVehicle {
private int numberWheels = 2;
// constructors
public Motorcycle(String licensePlate, double maxSpeed,
String make, String model, int year, int numberOfPassengers) {
this(licensePlate, 0.0, maxSpeed, make, model, year, numberOfPassengers);
}
public Motorcycle(String licensePlate, double speed, double maxSpeed,
String make, String model, int year, int numberOfPassengers) {
// invoke superclass constructor
super(licensePlate, speed, maxSpeed, make, model, year,
numberOfPassengers);
}
public int getNumberOfWheels() {
return this.numberWheels;
}
}
public class Car extends MotorVehicle {
private int numberWheels = 4;
private int numberDoors;
// constructors
public Car(String licensePlate, double maxSpeed,
String make, String model, int year, int numberOfPassengers,
int numberOfDoors) {
this(licensePlate, 0.0, maxSpeed, make, model, year, numberOfPassengers,
numberOfDoors);
}
public Car(String licensePlate, double speed, double maxSpeed,
String make, String model, int year, int numberOfPassengers) {
this(licensePlate, speed, maxSpeed, make, model, year,
numberOfPassengers, 4);
}
public Car(String licensePlate, double speed, double maxSpeed,
String make, String model, int year, int numberOfPassengers,
int numberOfDoors) {
super(licensePlate, speed, maxSpeed, make, model,
year, numberOfPassengers);
this.numberDoors = numberOfDoors;
}
public int getNumberOfWheels() {
return this.numberWheels;
}
public int getNumberOfDoors() {
return this.numberDoors;
}
}
It may look like these classes aren't as complete as the earlier ones, but
that's incorrect. Car
and Motorcycle
each inherit the members of
their superclass, MotorVehicle
.
Since a MotorVehicle
has a
make, a model, a year, a speed, a maximum speed, a number of passengers, cars
and motorcycles also have makes, models, years, speeds, maximum speeds, and
numbers of passengers. They also have all the public methods the superclass
has. They do not have the same constructors, though they can invoke the
superclass constructor through the super
keyword, much as a constructor in the same class can be invoked with the this
keyword.
Java contains an extensive library of pre-written classes you can use in your programs. These classes are divided into groups called packages.
The Java 1.1 packages
java.applet
java.awt
java.awt.datatransfer
java.awt.event
java.awt.image
java.awt.peer
java.beans
java.io
java.lang
java.lang.reflect
java.math
java.net
java.rmi
java.rmi.dgc
java.rmi.registry
java.rmi.server
java.security
java.security.acl
java.security.interfaces
java.sql
java.text
java.util
java.util.zip
Each package defines a number of classes, interfaces, exceptions, and errors.
Packages can be split into sub-packages. for example, the java.lang
package has a sub-package
called java.lang.reflect
.
These are really completely different packages. A class in a sub-package has no
more access to a class in the parent package (or vice versa) than it would to a
class in a completely different package.
Fully qualified names like java.net.URL
are not the most convenient thing to have to type. You can use the shorter
class names like URL
without
the java.net
part if you
first import the class by adding an import
statement at the top of the file. For example,
import java.net.URL;
import java.net.MalformedURLException;
public class URLSplitter {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
try {
URL u = new URL(args[i]);
System.out.println("Protocol: " + u.getProtocol());
System.out.println("Host: " + u.getHost());
System.out.println("Port: " + u.getPort());
System.out.println("File: " + u.getFile());
System.out.println("Ref: " + u.getRef());
}
catch (MalformedURLException e) {
System.err.println(args[i] + " is not a valid URL");
}
}
}
Interfaces are the next level of abstraction. An interface is like a class
with nothing but abstract
methods and final
, static
fields. All methods and fields of
an interface must be public.
However, unlike a class, an interface can be added to a class that is
already a subclass of another class. Furthermore an interface can apply to
members of many different classes. For instance you can define an Import
interface with the single method calculateTariff()
.
public interface Import {
public double calculateTariff();
}
You might want to use this interface on many different classes, cars among
them but also for clothes, food, electronics and moore. It would be
inconvenient to make all these objects derive from a single class. Furthermore,
each different type of item is likely to have a different means of calculating
the tariff. Therefore you define an Import
interface and declare that each class implements Import
.
The syntax is simple. Import
is declared public so that it can be accessed from any class. It is also
possible to declare that an interface is protected so that it can only be
implemented by classes in a particular package. However this is very unusual.
Almost all interfaces will be public. No interface may be private because the
whole purpose of an Interface is to be inherited by other classes.
The interface
keyword
takes the place of the class
keyword. Line 3 looks like a classic method definition. It's public (as it must
be). It's abstract, also as it must be. And it returns a double
. The method's name is calculateTariff()
and it takes no
arguments. The difference between this method and a method in a class is that
there is no method body. That remains to be created in each class that
implements the interface.
You can declare many different methods in an interface. These methods may be
overloaded. An interface can also have fields, but if so they must be final
and static
(in other words constants).
To actually use this interface you create a class that includes a public double
calculateTariff()
method and declare
that the class implements Import
.
For instance here's one such class:
public class Car extends MotorVehicle implements Import {
int numWheels = 4;
public double calculateTariff() {
return this.price * 0.1;
}
}
One of the advantages of interfaces over classes is that a single class may
implement more than one interface. For example, this Car
class implements three interfaces: Import
, Serializable
, and Cloneable
import java.io.*;
public class Car extends MotorVehicle
implements Import, Serializable, Cloneable {
int numWheels = 4;
public double calculateTariff() {
return this.price * 0.1;
}
}
Serializable
and Cloneable
are marker interfaces from the
class library that only add a type to a class, but do not declare any
additional methods.
Consider this program:
public class HelloThere {
public static void main(String[] args) {
System.out.println("Hello " + args[0]);
}
}
Suppose it's run like this:
$ java HelloThere
Notice that's there's no args[0]
.
Here's what you get:
$ java HelloThere
java.lang.ArrayIndexOutOfBoundsException: 0
at HelloThere.main(HelloThere.java:5)
This is not a crash. The virtual machine exits normally. All memory is cleaned up. All resources are released.
Why use exceptions instead of return values?
if
-else
blocks checking return values.) Traditional programming languages set flags or return bad values like -1 to indicate problems. Programmers often don't check these values.
Java throws Exception
objects to indicate a problem. These cannot be ignored.
public class HelloThere {
public static void main(String[] args) {
try {
System.out.println("Hello " + args[0]);
}
catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Hello Whoever you are.");
}
}
}
public class HelloThere {
public static void main(String[] args) {
int repeat;
try {
repeat = Integer.parseInt(args[0]);
}
catch (ArrayIndexOutOfBoundsException e) {
// pick a default value
repeat = 1;
}
catch (NumberFormatException e) {
// print an error message
System.err.println("Usage: java HelloThere repeat_count" );
System.err.println(
"where repeat_count is the number of times to say Hello" );
System.err.println("and given as an integer like 1 or 7" );
return;
}
for (int i = 0; i < repeat; i++) {
System.out.println("Hello");
}
}
}