unimportant logo identifying Barry Cornelius

University of Oxford

Computing Services

Getting started with Java

  Author: Barry Cornelius Date: last updated 5th June 2004; first created in 1996

Contents

1. Introduction

1.1. What is Java?

1.2. How is it executed?

1.3. What are Java applications?

1.4. What are Java applets?

1.5. APIs

1.6. A digression: what is JavaScript?

2. Declarations, statements, input and output

2.1. A simple Java program

0039: import java.io.BufferedReader;                               // Convert.java
0040: import java.io.InputStreamReader; import java.io.IOException;
0041: public class Convert {                                 /* BJC 960603 */
0042:    public static void main(String[ ] args) throws IOException {
0043:       System.out.println("type in the lowest Fahrenheit value");
0044:       BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
0045:       int lower = Integer.parseInt(input.readLine());
0046:       System.out.println("type in the number of lines: ");
0047:       int numOfLines = Integer.parseInt(input.readLine());
0048:       int upper = lower + numOfLines - 1;
0049:       for (int fahrenheit = lower; fahrenheit <= upper; fahrenheit++ ) {
0050:          float celsius = to_celsius(fahrenheit);
0051:          System.out.println(fahrenheit + " F is " + celsius + " C");
0052:       }
0053:    }
0054:    // function to convert a temperature from degrees Fahrenheit
0055:    // to degrees Celsius
0056:    private static float to_celsius(float fahr) {
0057:       return (fahr - 32.0F)*5.0F/9.0F;
0058:    }
0059: }

2.2. Primitive types

name purpose of the type default value examples of literal values
boolean logical values false false, true
char Unicode characters \u0000 ' ', 'A', '\101', '\u0041', '\'', '\t'
byte signed integers 0 use int literal values
short signed integers 0 use int literal values
int signed integers 0 0, 42, 2147483647
long signed integers 0 0L, 42L, 9223372036854775807L
float IEEE 754 floating pt. values 0.0 0.0F, 9.81F, 2.9979E8F, 6.6252e-34F
double IEEE 754 floating pt. values 0.0 0.0, 9.81, 1.0e100, 0.5E-100

Later we will see that the fields of a class declaration are initially given the default value given above, whereas a variable declared in a method declaration has no default value. However, a Java compiler will generate a compilation error for most attempts to use an uninitialized variable.

In Java, each of these types has a range that is defined by the language. So the range does not change as you move your Java source code from one platform to another. Here are the ranges:

name size (bits) smallest value of type largest value of type
boolean 1 N/A N/A
char 16 \u0000 \uFFFF
byte 8 -128 +127
short 16 -32768 +32767
int 32 -2147483648 +2147483647
long 64 -9223372036854775808 +9223372036854775807
float 32 -3.40282347E+38 +3.40282347E+38
double 64 -1.79769313486231570E+308 +1.79769313486231570E+308
Values of the types float and double that are small in magnitude are considered to be zero:

name smallest negative value smallest positive value
float -1.40239846E-45 +1.40239846E-45
double -4.94065645841246544E-324 +4.94065645841246544E-324

2.3. Declarations and initializers

In Java, a local variable declaration is a form of statement, and so such declarations may appear at any point in a block. Here is an example:

0060: char ch, separator, delimiter;

When a variable is declared, you can include an initializer that gives the variable its initial value. However, often the initial value of a variable can only be established after the execution of a few statements. In these cases, it is sometimes argued that less errors occur if the declaration is left until an appropriate initial value is known.

The initializer may be an expression that is calculated at runtime as is the case with the lower, numOfLines and upper variables in the program Convert given above.

If the value of a variable is never changed after it has been initialized, this can be (and should be) documented by using the final keyword. In the program Convert, this change could be made for the declarations of input, lower, numOfLines, upper and celsius, e.g.:

0061: final int upper = lower + numOfLines - 1;
Note that this particular use of final was not permitted in JDK 1.0.x.

2.4. Expressions

Java's operators are similar to those of C. For example, Java has the operators +, -, *, /, and % for performing arithmetic. Each of these has an associated assignment operator. For example, the statement: a += b; has the same meaning as the statement: a = a + b;

The operator ++ is a unary operator that is used to increment its operand which must be a variable. If it is used as a prefix operator, the value of the expression is the value of the variable after it has been incremented, whereas if it is used as a postfix operator, the value of the expression is the value of the variable before it has been incremented. The operator -- behaves like the operator ++ except that the variable is decremented instead of incremented.

Java also has the relational operators ==, !=, <, <=, >, >=. It has the two operators && and ||, that can be used to and and or boolean values. Like C, these two operators do short-circuit evaluation. Unlike C, full evaluation can be performed by using the operators & and | instead.

An expression that is preceded by the name of a type enclosed in parentheses is called a cast expression. The value of the expression is converted to a value of the type. Here are two examples:

0062: double speedOfLight = ...;
0063: int roughSpeedOfLight = (int)speedOfLight;
0064: int someInt = ...;
0065: char someChar = (char)someInt;

2.5. Statements

2.5.1. Blocks

There are many situations where the syntax of Java requires a single statement. A sequence of statements can be considered to be a single statement if they are turned into a block (which is called a compound statement in some other languages). This is done by surrounding the sequence of statements by { and }. Examples of this appear in the following section.

2.5.2. Conditional statements

Java has 2 kinds of conditional statements. Here are some examples of an if statement:

0066: if (a < 0)           if (a > b)             if (a > b) {
0067:    a = -a;              larger = a;            larger = a;
0068:                      else                      System.out.println("a>b");
0069:                         larger = b;         }
0070:                                             else {
0071:                                                larger = b;
0072:                                                System.out.println("a<=b");
0073:                                             }

Although C (and C++) allow the condition after the if to have an arithmetic value, this is not permitted in Java: the condition must be a boolean expression.

Here are two examples of a switch statement:

0074: switch ( dayNumber ) {                   switch ( dayNumber ) {
0075:    case 2: case 3: case 4:                  case 1:
0076:    case 5: case 6:                             readRatherHeavyNewspaper();
0077:       gotoWork();                              break;
0078:       doWork();                             case 7:
0079:       goHome();                                break;
0080:       watchTV();                            default:
0081:       gotoPub();                               gotoWork();
0082:       break;                                   doWork();
0083:    case 7:                                     goHome();
0084:       break;                                   watchTV();
0085:    case 1:                                     gotoPub();
0086:       readRatherHeavyNewspaper();        }
0087: }

Following the symbol switch, there should be an expression which is enclosed by parentheses. In the above examples, this expression is on the line:

0074: switch ( dayNumber ) {                   switch ( dayNumber ) {
and it just consists of the variable dayNumber. The expression should be of type char, byte, short, or int.

When the switch statement is executed, the expression is evaluated and then control is passed to the statement whose associated case label has a value equal to that of the expression. If there is no such statement, then control is passed to the statement associated with the default label if there is one; otherwise, control is passed to the statement following the switch statement.

A break statement must be executed if you wish to leave the switch statement before the last statement of the switch statement. So, normally, there will be a break statement just before each case label and before the default label (if there is one).

2.5.3. Looping statements

Java has 3 kinds of looping statements. Here is an example of a for statement:

0088: int numMonths = Integer.parseInt(input.readLine());
0089: int rainfallSum = 0;
0090: for ( int monthNum = 0; monthNum < numMonths; monthNum++ ) {
0091:    int figureForMonth = Integer.parseInt(input.readLine());
0092:    rainfallSum += figureForMonth;
0093: }
0094: System.out.println(rainfallSum);

The above example has the variable monthNum declared in the for statement itself. If you do this, then this variable can only be used within the for statement. If you leave out the type, then the variable must be declared elsewhere and the variable is similar to any other variable of the block containing the for statement.

Here is an example of a while statement:

0095: int rainfallSum = 0;
0096: int figureForMonth = Integer.parseInt(input.readLine());
0097: while ( figureForMonth >= 0 ) {
0098:    rainfallSum += figureForMonth;
0099:    figureForMonth = Integer.parseInt(input.readLine());
0100: }
0101: System.out.println(rainfallSum);

Here is an example of a do statement:

0102: int rainfallSum = 0;
0103: do {
0104:    int figureForMonth = Integer.parseInt(input.readLine());
0105:    if ( figureForMonth >= 0 )
0106:       rainfallSum += figureForMonth;
0107: } while ( figureForMonth >= 0 ) ;
0108: System.out.println(rainfallSum);

Although C (and C++) allow the condition of a while statement or a do statement to have an arithmetic value, this is not permitted in Java: the condition must be a boolean expression.

2.5.4. Other control statements

A break statement terminates the execution of a for, do, while or switch statement, and transfers control to the statement following that statement. A break statement may include a label, and this label indicates that it is the statement with that label that is to be terminated.

A continue statement transfers control to end of the current iteration of a for, do or while statement. A continue statement may include a label, and, if this is the case, control skips to the end of the loop that has this label.

Java also has try statements, catch clauses, finally clauses, and throw statements. These are all used for exception handling, and details about these will be given later.

2.6. Objects

2.6.1. Reference variables

Besides the primitive types that can be used for simple values, we often want to represent structured values. For example, we might want to represent a date in history, a point in two-dimensional space, and so on. In Java, a variable that is of a reference type is used to refer to a structured value.

For example, in order to represent a point in two-dimensional space, the package java.awt provides a ‘class declaration’ called Point. Such a class declaration automatically provides a reference type called Point, and we can declare a variable called myPoint to be of this reference type by the declaration:

0109: java.awt.Point myPoint;
To avoid having to repeat the package name every time we want to use Point, we can use an import declaration at the start of the file containing the Java source code:
0110: import java.awt.Point;
Having done this, we can declare the variable myPoint by:
0111: Point myPoint;

Such a declaration only introduces a reference variable, a variable that can refer to an object that contains the details about the point.

In some ways, a reference variable is like a pointer variable in Pascal, C or C++.

2.6.2. Creating an object

Having declared the reference variable, we ought to get it to refer to a Point object. This is done by using an assignment statement where the RHS contains a class instance creation expression:

0112: myPoint = new Point(100,200);
The creation expression new Point(100,200) uses a ‘constructor’ for the class Point to create an object of that class with x and y fields of 100 and 200. We will see later that we can do this because this kind of constructor has been provided by the designers of the java.awt package. Often a class provides several different constructors, e.g., as well as a constructor that has two parameters which are the x and y coordinates, the designers of the class Point could also have provided a constructor to construct a Point from a String:
0113: myPoint = new Point("100:200");
but they chose not to do this.

So we now have a variable called myPoint that refers to a point that has the x and y coordinates 100 and 200.

The above declaration of myPoint together with the above assignment statement can be shortened to a declaration that has an initializer:

0114: Point myPoint = new Point(100,200);

2.6.3. Referring to the fields of an object

We can use the dot notation to refer to the fields of an object, e.g., we can use myPoint.x and myPoint.y. For example, we could change the point being represented by 10 units in the x direction and 20 units in the y direction by the assignment statements:

0115: myPoint.x += 10;
0116: myPoint.y += 20;

Note that this is a little different to the languages Pascal, C and C++ where some extra syntax is used to say that we are dereferencing a pointer.

2.6.4. Applying methods to an object

The designers of the java.awt package have thought that we may want to move an existing point to a new point in space, and so they have provided a method to do this. A method is what would be called a function or procedure in other programming languages. The method that they have provided is called translate. So, instead of the above two assignment statements, we could write:

0117: myPoint.translate(10,20);
Note that the dot notation that we used above to refer to the two fields of a Point object is also used in the call of a method. You should look at this call in the following way: ‘apply the translate method with arguments 10 and 20 to the myPoint object’. Note: you would have written something like: translate(myPoint,10,20) in languages like Pascal and C.

2.6.5. Copying objects

Suppose we have:

0118: Point otherPoint;
0119: otherPoint = myPoint;
The assignment statement causes otherPoint to refer to the same object that myPoint refers to.

So, the above assignment statement does not produce a clone. The classes of the Core APIs use two different ways of enabling you to produce a clone of an object:

  • a class sometimes provides a method called clone;
  • a class sometimes provides a suitable constructor.
Although Point does not provide a clone method, it does provide a suitable constructor:
0120: Point clonePoint;
0121: clonePoint = new Point(myPoint);

2.6.6. Comparing objects

The == operator in the following condition is asking whether the two reference variables refer to the same object:

0122: if ( myPoint == otherPoint ) { ... } else { ... }

If, instead, you want to ask whether the two objects referred to by two reference variables have the same value, you can often use a method called equals:
0123: if ( myPoint.equals(clonePoint) ) { ... } else { ... }

2.6.7. The value null

If a reference variable has the value null, then this means that the variable does not currently refer to any object. An assignment statement can be used to indicate this:

0124: myPoint = null;

And you can test whether a reference variable does not refer to an object:
0125: if ( myPoint == null ) { ... } else { ... }
Note: whilst null appears to be a keyword of the language, it is technically the null literal.

2.6.8. Garbage collection

Pascal/C/C++ programs inadvertently dispose/free/delete objects which are still in use:

0126: var p, q:^integer;    int *p, *q;                 int *p, *q;
0127: new(p);               p = malloc(sizeof(int));    p = new int;
0128: p^ := 27;             *p = 27;                    *p = 27;
0129: q := p;               q = p;                      q = p;
0130: dispose(p);           free(p);                    delete p;
0131: writeln(q^);          printf("%d\n", *q);         cout << *q << endl;
And programs often cause memory leaks by not using dispose/free/delete on unwanted objects.

In Java, you do not delete objects: instead, Java has garbage collection. The garbage collector detects objects no longer in use, and reuses their space. Also, unlike C++, you do not have to provide destructors for classes.

2.7. Arrays

2.7.1. Introduction

In Java, an array is a collection of values that are of the same primitive type or of the same reference type. Since an array type is itself a reference type, arrays of arrays can be constructed.

2.7.2. An array of integers

The numbers of the days in a non-leap year on which each of the twelve months start are 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305 and 335. For example, March 1st is the 60th day of the year. Suppose we want to provide an array called monthStarts that contains this information.

In Java, there are two syntaxes for an array declaration. To keep C programmers happy, an array declaration can be written using the following syntax:

0132: int monthStarts[ ];
However, the following syntax is better:
0133: int[ ] monthStarts;

Note that the number of elements in the array is not included in this declaration. This is because this declaration only declares a reference variable that can be used to refer to an array object.

In order to create the actual array object we need to use an assignment statement that contains an array creation expression on its RHS:

0134: monthStarts = new int[12];

So this has set up monthStarts to be a reference variable that refers to an array of 12 integers, with indexes from 0 to 11.

You can access each individual element using the usual notation:

0135: monthStarts[0] = 1;
0136: monthStarts[1] = 32;
0137: ...
0138: monthStarts[11] = 335;

If an index is out of bounds, the exception ArrayIndexOutOfBoundsException will be thrown: details about ‘exception handling’ are given later.

As before, the creation expression can be used as an initializer:

0139: int[ ] monthStarts = new int[12];
0140: monthStarts[0] = 1;
0141: monthStarts[1] = 32;
0142: ...
0143: monthStarts[11] = 335;
This code can be abbreviated to:
0144: int[ ] monthStarts = { 1,32,60,91,121,152,182,213,244,274,305,335 };

2.7.3. An array of points

Suppose we want an array where each element is an object of class java.awt.Point. Perhaps we want an array to represent the four vertices of the rectangle (100,100), (300,100), (300,400) and (100,400). We can do this as follows:

0145: Point[ ] vertices = new Point[4];
0146: vertices[0] = new Point(100,100);
0147: vertices[1] = new Point(300,100);
0148: vertices[2] = new Point(300,400);
0149: vertices[3] = new Point(100,400);

Once again, this code can be abbreviated:
0150: Point[ ] vertices = { new Point(100,100), new Point(300,100),
0151:                       new Point(300,400), new Point(100,400) };

2.7.4. Flexible arrays

Within the square brackets of an array creation expression, there needs to be an expression indicating the number of elements that are required in the array object. This expression may be one whose value is not known until runtime. For example:

0152: BufferedReader input = ... ;
0153: int size = Integer.parseInt(input.readLine());
0154: int[ ] monthStarts = new int[size];

2.7.5. Even more flexible arrays

Having created an array object, the size of the array object is fixed. Suppose you are storing details about a collection of people, and suppose the size of the collection changes during the course of the execution of a program. It may be that you have no idea what the maximum size of the collection will be. Although you could arbitrarily choose a large value, this is wasteful of space, and no matter what value you choose, your program will fail if the value you choose is too small. In such situations, it is probably better to use a List, a Set or a Map. These are facilities that are provided by the Collections API of the Java 2 Platform. For more details, see ITS Guide 108 Advanced Java.

2.8. Methods

In Java, the word method is used instead of function, procedure or subroutine. The argument to println in:

0155: System.out.println(convertToCelsius(82.0));
is convertToCelsius(82.0). This is an example of a call of a method such as:
0156: private static double convertToCelsius(double fahr) {
0157:    return (fahr - 32.0)*5.0/9.0;
0158: }

Unlike C and C++, there is no default return type: you must specify it. If the method does not return a result, void should be used as the return type.

In Java, a parameter of a method behaves like a local variable of the method. It gets its initial value from the argument passed in the call. Any assignment to the parameter within the method only affects the value of the local variable. If a method does not assign a value to the parameter, this can be (and should be) documented by using the final keyword. However, note that this use of final was not permitted in JDK 1.0.x. Here is an example:

0159: private static double convertToCelsius(final double fahr) {
0160:    return (fahr - 32.0)*5.0/9.0;
0161: }

Unlike other languages, a method cannot change the value of the variable that is passed as an argument. So given:

0162: private static void silly(double p) {
0163:    p = p + 4.2;
0164:    System.out.println(p);
0165: }
the following code will not alter the value of the variable a:
0166: a = 2.7;
0167: silly(a);
0168: System.out.println(a);

If a method has no parameters, then it is declared with an empty parameter list:

0169: private static void m() { ... }
and a call has an empty argument list:
0170: m();

It is possible to declare several methods having the same name provided that they can be distinguished by the types of their parameters. This is called method overloading. Here is an example where the name min is declared twice:

0171: private static long min(long a, long b) { return a<b ? a : b; }
0172: private static Date min(Date a, Date b) { return a.before(b) ? a : b; }
At a call of min, the compiler can look at the arguments to see which min is required.

Note: the modifiers private and static will be discussed later.

2.9. Output and input

2.9.1. Attaching an output stream

To write values to a file called results, you can use a variable of the type PrintWriter. Suppose you want to use a variable called filout:

0173: PrintWriter filout = 
0174:       new PrintWriter(new BufferedWriter(new FileWriter("results")));
This declaration assumes that the following imports appear at the start of the file of source code:
0175: import java.io.BufferedWriter;
0176: import java.io.FileWriter;
0177: import java.io.PrintWriter;
If you want some output to be sent to the standard output, which is usually the screen, you can use System.out as an output stream. The variable out is a variable (of the type java.io.PrintStream) that is a ‘class variable’ of the class System which is defined in the package java.lang. Any class of this package is automatically available to a program without the need for any import declaration.

2.9.2. Outputting values to the output stream

In order to output a textual representation of a value, the print method should be applied to an object of the class PrintWriter or PrintStream. Here are two examples:

0178: filout.print("Hello World!");
0179: System.out.print("Hello World!");
The print method can be passed:
  • an argument of any primitive type;
  • an argument of any reference type for which the method toString is defined;
  • an expression that uses the string concatenation operator, e.g.:
    0180: int first = 42;
    0181: System.out.print("first has the value " + first);
    
If you want the output to move on to the next line after the value has been printed, use println instead of print.

2.9.3. Closing a file

As output to a BufferedWriter stream is buffered, the stream will need to be closed when you have finished using it:

0182: filout.close();

2.9.4. Attaching an input stream

To read values, you will need a variable of the type BufferedReader that is in the package java.io. To read from the standard input, which is normally the keyboard, you can use System.in as an input stream. So, if you want to use a variable called input for this input stream, you can use the following declaration:

0183: BufferedReader input = 
0184:       new BufferedReader(new InputStreamReader(System.in));
This declaration assumes that the following imports appear at the start of the file of source code:
0185: import java.io.BufferedReader;
0186: import java.io.InputStreamReader;

If, instead, you want to read values from a file called data, you can use:

0187: BufferedReader filin = new BufferedReader(new FileReader("data"));
This declaration assumes that the following imports appear at the start of the file of source code:
0188: import java.io.BufferedReader;
0189: import java.io.FileReader;

2.9.5. Reading a line of characters

The method readLine can be used to read in a line of characters from an input stream. It returns a value of type String. Here are two examples:

0190: String inputLine = input.readLine();
0191: String filinLine = filin.readLine();
The class String is defined in the package java.lang, and so it can be used in a program without the need for an import declaration.

2.9.6. Reading a value

If you would like to read a value into a variable whose type is one of the primitive types, you first need to call readLine to read in a line of characters and then call an appropriate method to parse the string. Here is an example where a value of type int is obtained from the keyboard:

0192: String line = input.readLine();
0193: int intVal = Integer.parseInt(line);
This can be abbreviated to:
0194: int intVal = Integer.parseInt(input.readLine());

Given a variable called line containing a String:

0195: String line = input.readLine();
values of the other primitive types can be obtained using the following statements:
0196: long longVal = Long.parseLong(line);
0197: float floatVal = Float.parseFloat(line);
0198: double doubleVal = Double.parseDouble(line);
0199: boolean booleanVal = new Boolean(line).booleanValue();
Note that the methods parseFloat and parseDouble were introduced into Java when the Java 2 Platform was released, and so, if you are using JDK 1.0.2 or JDK 1.1.x, you will have to use methods called floatValue or doubleValue instead (in a similar way in which booleanValue is used above).

The classes Integer, Long, Float, Double and Boolean are defined in the package java.lang, and so they can be used in a program without the need for any import declarations.

2.9.7. Handling more than one data item per line

You can use the class java.util.StringTokenizer if you want more than one data item per line. Suppose a line contains an int, followed by a float, followed by another int. You could use:

0200: String line = input.readLine();
0201: StringTokenizer tokens = new StringTokenizer(line);
0202: String token = tokens.nextToken();
0203: int firstInt = Integer.parseInt(token);
0204: token = tokens.nextToken();
0205: float theFloatVal = Float.parseFloat(token);
0206: token = tokens.nextToken();
0207: int secondInt = Integer.parseInt(token);

2.9.8. Flushing the output

If you want the user to type on the same line as a prompt, you will need to flush the output stream after outputting the prompt:

0208: BufferedReader input = 
0209:       new BufferedReader(new InputStreamReader(System.in));
0210: System.out.print("Type in an integer: ");
0211: System.out.flush();
0212: String line = input.readLine();
0213: int value = Integer.parseInt(line);

2.9.9. Dealing with java.io.IOException

If you are going to use the classes and methods from the java.io package, you will find that you are unable to compile your program unless it indicates what you want to happen if an exception called java.io.IOException occurs. Details about ‘exception handling’ are given later. So, to begin with, you may be happy for your program to crash if an IO exception occurs. This can be done by adding the clause throws IOException to the heading of any method that does IO. For example:

0214: public static void main(String[ ] args) throws IOException { ... }
This code assumes that the following import appears at the start of the file of source code:
0215: import java.io.IOException;

2.9.10. JDK Version 1.0.x

Many of the classes given above are not available if you use Version 1.0.x of the JDK. And the input-output facilities provided by Version 1.0.x can only handle byte streams. Those of JDK Version 1.1.x (and later) include support for character streams, i.e., streams containing 16-bit Unicode characters rather than just 8-bit bytes.

3. Handling strings

3.1. Creating an object of the class String

Although:

String tName = new String("James Gosling");
is the obvious way of creating a string object and making tName point to it, for strings there is an alternative syntax for the class instance creation expression. You can use "James Gosling" instead of using new String("James Gosling") as in:
String tName = "James Gosling";
So you have a choice here: both forms of syntax can be used to create new string objects.

A string literal can include characters that are non-graphic characters. This is done by using an escape sequence. An escape sequence is also necessary for putting a single quote, a double quote or a backslash in a string:

System.out.println("Lister glared at Rimmer.  \"You really are a smeghead\", he said.");

We will sometimes need to represent a string that has no characters. The string literal "" or the expression new String("") can be used. Such a string is called the empty string.

3.2. Applying methods to a String object

The class java.lang.String comes with a large number of methods for manipulating strings. A list of these methods is documented in the Method Detail section of javaapi:java/lang/String.html.

For example, if you want to access an individual character of a string, you can use a method called charAt. The value that is returned is of type char. You use an argument that is an int value to indicate the position of the character which you want to be returned. However, its value needs to be one less than the position of the character. So if you want the first character of the string to be returned, you need an argument with the value 0:

String tName = new String("James Gosling");
char tFirstChar = tName.charAt(0);
System.out.println("The first character of the name is: " + tFirstChar);
The println statement will output the line:
The first character of the name is: J

There is also a method that can be used to find out how many characters there are in a string:

String tName = new String("James Gosling");
int tNameLength = tName.length();
char tLastChar = tName.charAt(tNameLength - 1);
System.out.println("The last character of the name is: " + tLastChar);
This will output:
The last character of the name is: g

3.3. The exception StringIndexOutOfBoundsException

Many of the methods of the class String have an argument that is an integer that is the position of a character within a string. If you pass an argument that is invalid, the method will signify that it cannot handle this situation, by throwing an exception called StringIndexOutOfBoundsException. An exception is an occurrence of an exceptional circumstance, a situation that does not normally occur.

For example, if you call charAt with the value 5 when a string has 5 characters, your program will crash displaying lines like:

java.lang.StringIndexOutOfBoundsException: String index out of range: 5
        at java.lang.String.charAt(String.java)
        at StringIndexTest.main(StringIndexTest.java:6)

Instead of letting the program crash like this, we can include code in our program that will be executed when an exception occurs. Java has a statement called a try statement that is used to handle exceptions, and we will look at try statements later.

Java divides exceptions into two categories: checked exceptions and unchecked exceptions. A StringIndexOutOfBoundsException is an unchecked exception, and Java says that a program does not have to say what it wants to happen when an unchecked exception occurs.

3.4. Changing a String object

The class java.lang.String is rather unusual: none of its methods alter the object to which the method is being applied. The objects of the class are said to be immutable.

Instead of a method altering the value of a string object, it will produce a new string object. For example, consider:

String tToday = new String("1999-07-11");
tToday = tToday.replace('-', ':');
System.out.println(tToday);
First, a string object containing the string "1999-07-11" is created and tToday is made to point to it. Then the method replace is applied to the string object that is pointed to by tToday. This does not change that string object, but instead creates a new string object in which any occurrences of the '-' character are replaced by a ':' character. Then the value of tToday is changed. It is currently pointing to the first string object, and it is now altered to point to the new string object. There is now no variable pointing to the first string object: it is lost. Finally, the string that tToday points to is output by the call of the println method:
1999:07:11

3.5. Copying String objects

As was shown with the class java.awt.Point, you can make another variable refer to the same string by an assignment statement:

String tName = new String("James Gosling");
String tSameName = tName;
Both reference variables refer to the same object.

Earlier, when we used the String constructor, we passed a string literal as an argument. If you want a clone of a String object, then you can pass that String object as the argument of a String constructor:

String tName = new String("James Gosling");
String cloneName = new String(tName);

3.6. String concatenation

The class java.lang.String is unusual because an operator is defined in the language specifically for the concatenation of the values of two objects of this class:

String tFirstName = new String("James");
String tSurname = new String("Gosling");
String tName = tFirstName + tSurname;
The variable tName now points to a string object containing the string "JamesGosling". Perhaps that is not what we were after. So use this instead:
String tName = tFirstName + " " + tSurname;

The string concatenation operator is very flexible in that it will convert any operand (that is permitted) into a string. Here is an example:

Point tFirstPoint = new Point(100, 200);
String tLine = "The point has the value " + tFirstPoint;
System.out.println(tLine);
This will output:
The point has the value java.awt.Point[x=100,y=200]

If you have a long string literal, the string concatenation operator can be used to help in the layout of the text. For example, the statement:

System.out.println("Lister glared at Rimmer.  \"You really are a smeghead\", he said.");
can instead be written as:
System.out.println("Lister glared at Rimmer." +
                   "  \"You really are a smeghead\", he said.");

3.7. A program that uses these ideas about Strings

Suppose we want a program that takes a person's name arranged as FirstName Surname and outputs it in the format Surname, Initial where Initial is the first letter of the FirstName. We will also suppose that the output must be displayed in upper-case. Here is a program that does this for the name "James Gosling":

0216: public class SimpleString {                             // SimpleString.java
0217:    public static void main(final String[] pArgs) {
0218:       final String tName = new String("James Gosling");
0219:       System.out.println(tName);
0220:       final char tFirstChar = tName.charAt(0);
0221:       final int tPositionOfSpace = tName.indexOf(" ");
0222:       final String tSurname = tName.substring(tPositionOfSpace + 1);
0223:       String tLabel = tSurname + ", " + tFirstChar;
0224:       tLabel = tLabel.toUpperCase();
0225:       System.out.println(tLabel);
0226:    }
0227: }

The two printlns of this program produce the following output:

James Gosling
GOSLING, J

3.8. The class StringBuffer

Besides the class String, there is another class called StringBuffer (which is also in the java.lang package). When you wish to build up a string gradually by performing a lot of string manipulation, it is more efficient to use a StringBuffer rather than create a lot of String objects. If you have a StringBuffer variable called tStringBuffer, you can apply toString method to the variable in order to create a String from tStringBuffer:

0228: public static String reverse(String source) {
0229:    int charNum;
0230:    int numChars = source.length();
0231:    StringBuffer temp = new StringBuffer(numChars);
0232:    for (charNum = numChars-1; charNum>=0; charNum--) {
0233:       temp.append(source.charAt(charNum));
0234:    }
0235:    return temp.toString();
0236: }

4. Using classes for data abstraction

4.1. Introduction

Typically a program has to maintain several data structures each of which is manipulated in many different ways. It is best for the pieces of code that manipulate a particular data structure to be located in a small number of functions. And it would be desirable if the program could be written so that each data structure can only be accessed from its associated functions, i.e., it is not directly accessible to the rest of the program. In this way, we would then prevent a data structure from accidentally being misused. What we want is a way of building a wall around a data structure and the functions that manipulate it, and only allowing some of these functions to be accessible from outside the wall. Modern programming languages have a construct to do this: for example, Ada has packages, Fortran90 and Modula-2 both have modules, and C++ and Java both have classes.

In this section, we look at how to write our own class declarations.