![]() |
University of Oxford |
Computing Services |
|
Getting started with Java |
|
| Author: Barry Cornelius Date: last updated 5th June 2004; first created in 1996 |
0001: public class HWTion { // HWTion.java
0002: public static void main(String[ ] args) {
0003: System.out.println("Hello World!");
0004: }
0005: }
Note: the line numbers should be ignored: they do not form
part of the program.
javac HWTion.javaThis produces a file of bytecodes in the file HWTion.class.
java HWTion
javac-- HWTion.java java-- HWTion
0006: import java.applet.Applet; // HWLet.java
0007: import java.awt.Graphics;
0008: public class HWLet extends Applet {
0009: public void paint(Graphics rGraphics) {
0010: rGraphics.drawString("Hello World!", 50, 25);
0011: }
0012: }
javac HWLet.javaThis produces a file of bytecodes in the file HWLet.class.
0013: <HTML> 0014: <HEAD> 0015: <TITLE> HWLet example </TITLE> 0016: </HEAD> 0017: <BODY> 0018: Before the output from the applet. 0019: <APPLET CODE="HWLet.class" WIDTH=150 HEIGHT=25> 0020: </APPLET> 0021: After the output from the applet. 0022: </BODY> 0023: </HTML>
0024: <HTML> <HEAD> <TITLE> Square demo </TITLE> </HEAD>
0025: <BODY>
0026: <P> Start. </P>
0027: <SCRIPT LANGUAGE="JavaScript">
0028: <!-- hide this script from some browsers
0029: function mysquare(myarg) {
0030: document.write("<P> Hello once more </P>");
0031: document.write("<P> <code>myarg</code> is: ", myarg, "</P>");
0032: return myarg*myarg;
0033: } ;
0034: document.write("<P> Value returned is: ", mysquare(7), "</P>");
0035: // end of hide -->
0036: </SCRIPT>
0037: <P> Finish. </P>
0038: </BODY> </HTML>
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: }
| 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 |
| name | smallest negative value | smallest positive value |
| float | -1.40239846E-45 | +1.40239846E-45 |
| double | -4.94065645841246544E-324 | +4.94065645841246544E-324 |
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.
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;
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.
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).
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.
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.
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++.
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);
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;
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.
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:
0120: Point clonePoint; 0121: clonePoint = new Point(myPoint);
The == operator in the following condition is asking whether the two reference variables refer to the same object:
0122: if ( myPoint == otherPoint ) { ... } else { ... }
0123: if ( myPoint.equals(clonePoint) ) { ... } else { ... }
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;
0125: if ( myPoint == null ) { ... } else { ... }
Note: whilst null appears to be a keyword of the language,
it is technically the
null literal.
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.
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.
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 };
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);
0150: Point[ ] vertices = { new Point(100,100), new Point(300,100),
0151: new Point(300,400), new Point(100,400) };
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];
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.
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.
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.
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:
If you want the output to move on to the next line after the
value has been printed, use
println
instead of
print.
As output to a BufferedWriter stream is buffered, the stream will need to be closed when you have finished using it:
0182: filout.close();
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;
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.
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.
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);
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);
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;
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.
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.
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
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.
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
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);
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.");
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
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: }
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.