JAVA Language Fundamentals
JAVA Language Fundamentals
I. Identifiers are names of variables, functions, classes etc. The name used as an identifier must follow the following rules in JavaTM technology. o Each character is either a digit, letter, underscore(_) or currency symbol ($,, or ) o First character cannot be a digit. o The identifier name must not be a reserved word. A keyword or reserved word in Java technology has special meaning and cannot be used as a user defined identifier. The list of keywords in Java technology is given below. It is important to completely remember this list as you can expect a question in Java Certification exam related to this. abstract boolean break byte case catch char class const continue default do double else extends final finally float for goto if implements import instanceof int interface long native new null package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while assert enum It is important to note the following const and goto are not currently in use. null, true, and false are reserved literals but can be considered as reserved words for the purpose of exam. C. It is important to understand that Java language is case-sensitive. So even though super is a keyword, Super is not. D. All the Java technology keywords are in lower case. E. strictfp is a new keyword added in Java 1.2. assert is added in Java 1.4 and enum in Java 5.0 F. The list of keywords as defined by Sun is present here. A literal in Java technology denotes a constant value. So for example 0 is an integer literal, and 'c' is a character literal. The reserved literals true and false are used to represent boolean literals. "This is a string" is a string literal. Integer literals can also be specified as octal (base 8), or hexadecimal (base 16). Octal and hexadecimal have 0 and 0x prefix respectively. So 03 and 0x3 are representation of integer three in octal and hexa-decimal respectively. Java technology supports three type of comments A. A single line comment starting with // B. A multi-line comment enclosed between /* and */ C. A documentation or javadoc comment is enclosed between /** and */. These comments can be used to generate HTML documents using the javadoc utility, which is part of Java language.
A. B.
II.
III.
V.
VI.
VII.
Java technology supports the following primitive types - boolean (for representing true or false), a character type called char, four integer types (byte, short, int and long) and two floating point types (float and double). The details of these types are given below Data types Width (in bytes) Minimum value Maximum Value byte 1 -27 27 - 1 short 2 -215 215-1 int 4 -231 231 - 1 long 8 -263 263 - 1 char 2 0x0 0xffff float 4 1.401298e-45 3.402823e+38 double 8 4.940656e-324 1.797693e+308 Corresponding to all the primitive type there is a wrapper class defined. These classes provide useful methods for manipulating primitive data values and objects. Data types Wrapper class int Integer short Short long Long byte Byte char Character float Float double Double Instance variables (data members of a class) and static variables are initialized to default values. Local variables (i.e. variables defined in blocks or inside member functions) are not initialized to default values. Local variables must be explicitly initialized before they are used. If local variables are used before initialization, compilation error gets generated. The defaults for static and instance variables are given in the table below. Data types Default Values boolean false char '\u0000' Integer types (byte, short, int, long) 0 Floating types (float, double) 0.0F or 0.0 D Object References null
public static void main(String args[]) { int i; System.out.println(i); }
XIII.
XIV.
XV.
In this example printing of i generates a compilation error because local variable i is used before being initialized. The initialization of instance and static variables is an important concept both for understanding of Java language, and for Java Certification exam. A Java source file has the following elements in this specific order.
o
An optional package statement. All classes and interfaces defined in the file belong to this package. If the package statement is not specified, the classes defined in the file belong to a default package. An example of a package statement is package testpackage;
Zero or more import statements. The import statement makes any classes defined in the specified package directly available. For example if a Java source file has a statement importing the class "java.class.Button", then a class in the file may use Button class directly without providing the names of the package which defines the Button class. Some examples of import statement are import java.awt.*; // All classes in the awt package are imported. import java.applet.Applet;
Any number of class and interface definitions may follow the optional package and import statements.
If a file has all three of the above constructs, they must come in the specific order of package statement, one or more import statements, followed by any number of class or interface definitions. Also all the above three constructs are optional. So an empty file is a legal Java file.
II.
The Java interpreter executes a method called main, defined in the class specified in command line arguments. The main method is the entry point for execution of a class. In Java technology the main method must have the following signature public static void main(String args[])
The java interpreter is invoked with the name of the class as an argument. The class name is followed by possible set of arguments for the main function of the class. When a Java program is invoked then the Java interpreter name "java" and the class name are not passed to the main() method of the class. The rest of the command line arguments are passed as an array of String. For example invoking
java Sky blue gray
would invoke main method of Sky class with an array of two elements - blue and gray.
Following are some of the commonly used JavaTM technology operators Multiplication (*), Addition (+), Subtraction (-), logical and (&&) Conditional Operator ?:, Assignment (=), left shift (<<), right shift (>> and >>>), Equality comparison (==), Non-equality comparison (!=). Conversion rules in Assignments In the description below, I am giving basic conversion rules for assignment when source and destination are of different types. If source and destination are of the same type, assignment happens without any issues. II. If source is of smaller size than destination but source and destination are of compatible types, then no casting is required. Implicit widening takes place in this case. An example is assigning an int to a long. III. If source and destination are of compatible types, but source is of larger size than destination, explicit casting is required. In this case, if no casting is provided then the program does not compile. Floating point numbers Decimal numbers (for example 1.3) are of type double by default. To make them of type float they must be followed by F (say, 1.3F). The equality operator The equality operator (==) when applied to objects return true if two objects have same reference value, false otherwise. The example below illustrates this -String str1 = String str2 = String str3 = boolean test1 boolean test2 "first string"; new String("first string"); "first string"; = (str1 == str2); = (str1 == str3);
I.
In the example above, test1 is set to false because str1 and str2 point to different references. As str1 and str3 point to the same reference, test2 gets set to true. When a string is initialized without using the new operator, and with an existing string, then the new string also points to the first string's location. So in the example above, str1 and str3 point to the same pool of memory and hence test2 gets set to true. The string str2 on the other hand is created using the new operator and hence points to a different block of memory. Hence test1 gets set to false. The conditional operators && and || Operator && returns true if both operands are true, false otherwise. Operator || returns false if both operands are false, true otherwise. The important thing to note about these operators is that they are short-circuited. This means that the left operand is evaluated before the right operator. If the result of the operation can be evaluated after computing the left operand, then the right side is not computed. In this respect these operators are different from their bit-wise counterparts - bit-wise and (&), and bit-wise or (|). The bit-wise operators are not short-circuited. This means both the operands of bit-wise operator are always evaluated independent of result of evaluations. Storing integral types
All the integer types in Java technology are internally stored in two's complement. In two's complement, positive numbers have their corresponding binary representation. Two's complement representation of negative numbers is generated using the following three step process First get the binary representation of the number. Then interchange zeros and ones in the binary representation. Finally add one to the result. So for example two's complement of -18 would be (assuming one byte representation) o Converting 18 to binary -- 0001 0010 o Interchanging 0s and 1s -- 1110 1101 o Adding 1 -- 1110 1110
So 1110 1110 would be binary representation of -18 using two bytes and using two's complement representation. The shift operators The shift left operator in Java technology is "<<". There are two operators for doing the right shift - signed right shift (>>) and zero fill right shift (>>>). The left shift operator fills the right bits by zero. The effect of each left shift is multiplying the number by two. The example below illustrates this int i = 13; // i is 00000000 00000000 00000000 0000 1101 i = i << 2; // i is 00000000 00000000 00000000 0011 0100
After this left shift, i becomes 52 which is same as multiplying i by 4 Zero fill shift right is represented by the symbol >>>. This operator fills the leftmost bits by zeros. So the result of applying the operator >>> is always positive. (In two's complement representation the leftmost bit is the sign bit. If sign bit is zero, the number is positive, negative otherwise.) The example below illustrates applying the operator >>> on a number.
int b = 13; // 00000000 00000000 00000000 0000 1101 b = b >>> 2; // b is now 00000000 00000000 00000000 0000 0011
So the result of doing a zero fill right shift by 2 on 13 is 3. The next example explains the effect of applying the operator >>> on a negative number.
int b = -11; //11111111 11111111 11111111 1111 0101 b = b >>> 2; // b now becomes 00111111 11111111 11111111 1111 1101
So the result of applying zero fill right shift operator with operand two on -11 is 1073741821.
Signed right shift operator (>>) fills the left most bit by the sign bit. The result of applying the signed shift bit has the same sign as the left operand. For positive numbers the signed right shift operator and the zero fill right shift operator both give the same results. For negative numbers, their results are different. The example below illustrates the signed right shift.
int b = -11; // 11111111 11111111 11111111 1111 0101 b = b >> 2; // 11111111 11111111 11111111 1111 1101 (2's complement of -3) // Here the sign bit 1 gets filled in the two most significant bits.
It is important to note that the size of the array is not included in the declaration. Memory is allocated for an array using the new operator as shown below.
anArray = new int[10];
The declaration and memory allocation may be combined together as shown below.
int anArray[] = new int[10];
The elements of the array are implicitly initialized to default values based on array types (0 for integral types, null for objects etc.). This is true for both local arrays as well as arrays which are data members. In this respect arrays are different from normal variables. Variable defined inside a method are not implicitly initialized, where as array elements are implicitly initialized. Array Initializations Arrays are initialized using the syntax below
int intArray[] = {1,2,3,4};
The length operator can be used to access the number of elements in an array (for example - intArray.length). Multidimensional Arrays The following are legal examples of declaration of a two dimensional array.
int[] arr[]; int[][] arr;
When creating multi-dimensional arrays the initial index must be created before a later index. The following examples are legal.
int arr[][] = new int[5][5]; int arr[][] = new int[5][];
Class Fundamentals A class defines a new type and contains methods and variables. The example below illustrates a simple class.
class City { String name; // member variable String getName() // member method { return name; } public static void main(String arg[]) { } }
Method overloading JavaTM technology allows two methods to have the same name as long as they have different signatures. The signature of a method consists of name of the method, and count and type of arguments of the method. Thus as long as the argument types of two methods are different, they may be over-loaded (have the same name). Class constructors Constructors are member methods that have same name as the class name. The constructor is invoked using the new operator when a class is created. If a class does not have any constructors then Java language compiler provides an implicit default constructor. The implicit default constructor does not have any arguments and is of the type class_name() { }
If a class defines one or more constructors, an implicit constructor is not provided. The example below gives a compilation error.
class Test { int temp; Test(int x) { temp = x;
} public static void main() { Test t = new Test(); /* This would generate a compilation error, as there is no constructor without any arguments. */ } }
Some of the important methods defined in the Object class are given below. These methods are available to all Java classes. boolean equals(Object obj) - The equals method in Object class returns true if two references point to the same object. Some classes like String and Boolean overload this method. The difference between the equals function and the equality operator is covered here. II. String toString() - The function is used to convert objects to String. If a subclass does not override this method, the method returns a textual representation of the object, which has the following format : <name of the class>@<hash code value of the object>". III. The following methods related to threads are also defined in Object class void notify() void notifyall() void wait(long timeout) throws InteruptedException void wait(long timeout, int nanosec) throws InteruptedException void wait() throws InteruptedException
I.
Wrapper classes
Corresponding to all the primitive types Java technology defines wrapper classes. Some examples of these wrapper classes are - Character, Boolean, Integer, Double. Important methods in the Math class Some of the methods defined in the Math class are used frequently. These are explained below. Besides the functionality, it is important to understand the arguments and return type of these functions. static double ceil(double(d)) : The method ceil returns the smallest double value equal to a mathematical integer, that is not less than the argument. For example,
: The method floor returns the largest double value equal to a mathematical integer, that is not greater than the argument. For example,
static double floor(double(d))
and static long round(double d) : The method round returns the integer closest to the argument.
static int round (float f)
String class The String class is used to implement immutable character strings. This means that the character string cannot be changed once it has been created. Some of the important methods are explained below. int length() - The number of characters in the String class are returned by the length() method.
String substring(int startIndex) String substring(int startIndex, int endIndex)
The method substring extracts a substring from a string. The method extracts a string from the startIndex to the index endIndex - 1. If endIndex is not specified then string till the end of input string is returned. The example below illustrates this
String str = "I am a string"; int len = str.length(); String str2 = str.substring(2,5);
After the above statements str2 contains the string "am ". The string str still has the same value "I am a string". The variable len has value 13. StringBuffer class The StringBuffer class implements mutable strings. This means that the characters stored in the string and the capacity of the string can be changed. Garbage Collection Java technology's Garbage collection is complex. In this section I am only giving a brief overview of Garbage Collection. Java technology supports automatic garbage collection. This means that the programmer does not need to free the memory used by objects. Java technology's runtime environment can claim the memory from the objects that are no longer in use. Objects that are not being referred become candidates for garbage collection. It is important to note that these objects are candidates only. Java technology does not guarantee that Garbage collection would happen on these objects. Before actually freeing up the memory, garbage collector invokes the finalize() method of the Object being freed. The System.gc() method can be invoked by the program to suggest to Java technology that the Garbage Collector be invoked. However there is no guarantee when the garbage collection would be invoked. There is also no guarantee on the order in which objects will be garbage collected. The example illustrates when a string Object becomes available for Garbage Collection.
public class GCTest { public static void main(String args[]) { String a,b; String c = new String("test"); a = c; c = null; // The String "test" is not yet //available for GC as a still points to "test" b = new String("xyz"); b = c; // String "xyz" is now available for GC. a = null; //String "test" is now available for GC. } }
Collections
Collections A Collection allows a group of objects to be treated as a single unit. Collections define a set of core interfaces. These are
Collections also provide implementation for these interfaces. Core Interfaces The Object hierarchy of Core Interfaces defined in Collections is given below.
Figure: Core Interfaces of Collections Collection Interface The Collection interface is the root of Collection hierarchy, and is used for common functionality across all collections. There is no direct implementation of Collection interface. Set Interface The Set interface is used to represent a group of unique elements. It extends the Collection interface. The class HashSet implements the Set interface. SortedSet Interface The SortedSet interface extends the Set interface. It provides extra functionality of keeping the elements sorted. So SortedSet interface is used to represent collections consisting of unique, sorted elements. The class TreeSet is an implementation of interface SortedSet. List Interface The list interface extends the Collection interface to represent sequence of numbers in a fixed order. Classes ArrayList, Vector and LinkedList are implementation of List interface. Map Interface The Map Interface is a basic interface that is used to represent mapping of keys to values. Classes HashMap and Hashtable are implementations of Map interface. SortedMap Interface The SortedMap Interface extends Map interface and maintains their mappings in key order. The class TreeMap implements SortedMap interface. The table below gives the list of Collection interfaces and the classes that implement them. Interface Set SortedSet List Map SortedMap Class Implementation HashSet TreeSet ArrayList, Vector, LinkedList HashMap, Hashtable TreeMap
java.io package Classes related to input and output are present in the JavaTM language package java.io . Java technology uses "streams" as a general mechanism of handling data. Input streams act as a source of data. Output streams act as a destination of data. File class The file class is used to store the path and name of a directory or file. The file object can be used to create, rename, or delete the file or directory it represents. The File class has the following constructors File(String pathname); // pathname could be file or a directory name File(String dirPathname, String filename); File(File directory, String filename);
The File class provides the getName() method which returns the name of the file excluding the directory name.
String getName();
Byte Streams The package java.io provides two set of class hierarchies - one for handling reading and writing of bytes, and another for handling reading and writing of characters. The abstract classes InputStream and OutputStream are the root of inheritance hierarchies handling reading and writing of bytes respectively. Figure : InputStream class hierarchy (partial)
Figure :
read and write methods InputStream class defines the following methods for reading bytes int read() throws IOException int read(byte b[]) throws IOException int read(byte b[], int offset, int length) throws IOException
Subclasses of InputStream implement the above mentioned methods. OutputStream class defines the following methods for writing bytes void write(int b) throws IOException void write(byte b[]) throws IOException void write(byte b[], int offset, int length) throws IOException
Reader and Writer classes Similar to the InputStream and OutputStream class hierarchies for reading and writing bytes, Java technology provides class hierarchies rooted at Reader and Writer classes for reading and writing characters. A character encoding is a scheme for internal representation of characters. Java programs use 16 bit Unicode character encoding to represent characters internally. Other platforms may use a different character set (for example ASCII) to represent characters. The reader classes support conversions of Unicode characters to internal character shortage. Every platform has a default character encoding. Besides using default encoding, Reader and Writer classes can also specify which encoding scheme to use.
The table below gives a brief overview of key Reader classes. CharArrayReader The class supports reading of characters from a character array. The class supports reading of characters from a byte input InputStreamReader stream. A character encoding may also be specified. The class supports reading of characters from a file using FileReader default character encoding.
The table below gives a brief overview of key Writer classes. The class supports writing of characters from a character array. The class supports writing of characters from a byte output OutputStreamReader stream. A character encoding may also be specified. The class supports writing of characters from a file using FileWriter default character encoding. CharArrayWriter
The example below illustrates reading of characters using the FileReader class.
//Create a FileReader class from the file name. FileReader fr = new FileReader("filename.txt"); int i = fr.read(); //Read a character
Questions on Assertions
This topic is part of SCJP 1.4 exam but not SCJP 1.2 exam. 1. What happens when the following code is compiled and run. Select the one correct answer.
for(int i = 1; i < 3; i++) for(int j = 3; j > i; j--) assert i!=j {System.out.println(i); }
A. B. C. D. E.
The class compiles and runs, but does not print anything. The number 1 gets printed with AssertionError The number 2 gets printed with AssertionError The number 3 gets printed with AssertionError The program generates a compilation error.
2. What happens when the following code is compiled and run. Select the one correct answer.
for(int i = 1; i < 3; i++) for(int j = 3; j >= 1; j--) assert i!=j : i;
A. B. C. D. E.
The class compiles and runs, but does not print anything. The number 1 gets printed with AssertionError The number 2 gets printed with AssertionError The number 3 gets printed with AssertionError The program generates a compilation error.
3. What happens when the following code is compiled and run. Select the one correct answer.
for(int i = 1; i < 4; i++) for(int j = 1; j < 4; j++) if(i < j) assert i!=j : i;
A. B. C. D. E.
The class compiles and runs, but does not print anything. The number 1 gets printed with AssertionError The number 2 gets printed with AssertionError The number 3 gets printed with AssertionError The program generates a compilation error.
4. Which of the following statement is true about the assert statement. Select the one correct answer.
A. If a Java class contains assert statements, then it must be compiled with -1.4 option. B. When a program having assertions is run, -assertion option must be specified, otherwise the assertions get ignored. C. A possible syntax of assert statement is assert logical_expression If logical_expression evaluates to true, the program generates an AssertionError. D. The program terminates on its first AssertionError.
Answers to questions on Assertions 1. e. The condition in assert statement must be followed by a semi-colon. 2. b. When i and j are both 1, assert condition is false, and AssertionError gets generated. 3. a. When the if condition returns true, the assert statement also returns true. Hence AssertionError does not get generated. 4. d. The option A is incorrect, as the Java compiler option is -source 1.4 . The option B is incorrect, as the runtime option is -ea or -enableassertions. If the logical expression evaluates to false, then the program generates an AssertionError, hence C is incorrect. For more questions on Assertions, you may want to visit Khalid Mughal's sample chapter on Assertions.
Questions on Collections
1. TreeMap class is used to implement which collection interface. Select the one correct answer. A. Set B. SortedSet C. List D. Tree E. SortedMap 2. Name the Collection interface implemented by the Vector class. 3. Name the Collection interface implemented by the Hashtable class. 4. Name the Collection interface implemented by the HashSet class. 5. Which of these are interfaces in the collection framework. Select the two correct answers. A. Set B. List C. Array
D. Vector E. LinkedList 6. Which of these are interfaces in the collection framework. Select the two correct answers. A. HashMap B. ArrayList C. Collection D. SortedMap E. TreeMap 7. What is the name of collection interface used to maintain non-unique elements in order. 8. What is the name of collection interface used to maintain unique elements. 9. What is the name of collection interface used to maintain mappings of keys to values. 10. Is this true or false. Map interface is derived from the Collection interface. A. True B. False Answers to questions on Collections 1. 2. 3. 4. 5. 6. 7. 8. 9. e List Map Set a,b c,d List Set Map
3. Which of these are legal ways of accessing a File named "file.tst" for reading. Select the two correct answers. A. FileReader fr = new FileReader("file.tst"); B. FileInputStream fr = new FileInputStream("file.tst"); InputStreamReader isr = new InputStreamReader(fr, "UTF8"); C. FileReader fr = new FileReader("file.tst", "UTF8"); D. InputStreamReader isr = new InputStreamReader("file.tst"); 4. Name the class that allows reading of binary representations of Java primitives from an input byte stream. 5. Which of these classes are abstract. Select the three correct answers. A. FilterWriter B. Reader C. InputStream D. CharArrayReader E. DataInputStream 6. Name the exception thrown by the read method defined in InputStream class Answers to questions on Files and I/O 1. d 2. a 3. a, b. FileReader class uses the default character encoding, hence c is incorrect. InputStreamReader character class does not have a constructor that takes file name as an argument. Hence d is incorrect. 4. DataInpiutStream 5. a,b,c 6. IOException
Questions on Classes
1. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
2. 3. protected class example { 4. public static void main(String args[]) { 5. String test = "abc"; 6. test = test + test; 7. System.out.println(test); 8. } 9. } 10.
The class does not compile because the top level class cannot be protected. The program prints "abc" The program prints "abcabc" The program does not compile because statement "test = test + test" is illegal. 11. A top level class may have only the following access modifier. Select the one correct answer.
A. B. C. D.
A. package B. friendly C. private D. protected E. public 12. Write down the modifier of a method that makes the method available to all classes in the same package and to all the subclasses of this class. 13. Select the one most appropriate answer. A top level class without any modifier is accessible to A. any class B. any class within the same package C. any class within the same file D. any subclass of this class. 14. Is this True or False. In Java an abstract class cannot be sub-classed. 15. Is this True or False. In Java a final class must be sub-classed before it can be used. 16. Which of the following are true. Select the three correct answers. A. A static method may be invoked before even a single instance of the class is constructed. B. A static method cannot access non-static methods of the class. C. Abstract modifier can appear before a class or a method but not before a variable. D. final modifier can appear before a class or a variable but not before a method. E. Synchronized modifier may appear before a method or a variable but not before a class. Answers to questions on classes in Java 1. 2. 3. 4. 5. 6. 7. a e protected b False False a, b, c. final modifier may appear before a method, a variable or before a class.
If you didnot do well in these questions, you may want to take a look at the sample chapter from Simon Roberts book on classes and objects.
2.
3. 4.
5.
A. arr.length B. arr.length - 1 C. arr.size D. arr.size - 1 E. arr.length() F. arr.length() - 1 Which of these statements are legal. Select the three correct answers. A. int arr[][] = new int[5][5]; B. int []arr[] = new int[5][5]; C. int[][] arr = new int[5][5]; D. int[] arr = new int[5][]; E. int[] arr = new int[][5]; Write the expression to access the number of elements in a one dimensional array arr. The expression should not be assigned to any variable. Which of these array declarations and initializations are legal? Select the two correct answers. A. int arr[4] = new int[4]; B. int[4] arr = new int[4]; C. int arr[] = new int[4]; D. int arr[] = new int[4][4]; E. int[] arr = new int[4]; What will the result of compiling and executing the following program. Select the one correct answer.
6. 7. class Test { 8. 9. public static void main(String args[]) { 10. 11. int arr[] = new int[2]; 12. 13. System.out.println(arr[0]); 14. 15. } 16. 17. } 18.
A. The program does not compile because arr[0] is being read before being initialized. B. The program generates a runtime exception because arr[0] is being read before being initialized. C. The program compiles and prints 0 when executed. D. The program compiles and prints 1 when executed. E. The program compiles and runs but the results are not predictable because of un-initialized memory being read. 19. Which of the following are legal declaration and definition of a method. Select all correct answers. A. void method() {}; B. void method(void) {}; C. method() {};
D. method(void) {}; E. void method {}; 20. Which of the following are valid constructors within a class Test. Select the two correct answers. A. test() { } B. Test() { } C. void Test() { } D. private final Test() { } E. abstract Test() { } F. Test(Test t) { } G. Test(void) { } 21. What is the result of compiling and running the following class. Select the one correct answer.
22. 23. 24. 25. class Test { 26. 27. public void methodA(int i) { 28. 29. System.out.println(i); 30. 31. } 32. 33. public int methodA(int i) { 34. 35. System.out.println(i+1); 36. 37. return i+1; 38. 39. } 40. 41. public static void main(String args[]) { 42. 43. Test X = new Test(); 44. 45. X.methodA(5); 46. 47. } 48. 49. } 50. 51. 52.
Select the one correct answer. A. The program compiles and runs printing 5. B. The program compiles and runs printing 6. C. The program gives runtime exception because it does not find the method Test.methodA(int)
D. The program give compilation error because methodA is defined twice in class Test. Answers to questions on Declarations 1. 2. 3. 4. 5. 6. 7. a a, b, c arr.length c, e. The size of the array should not be specified when declaring the array. c a b, f. A constructor must have the same name as the class, hence a is not a constructor. It must not return any value, hence c is not correct. A constructor cannot be declared abstract or final. 8. d
A. The line labeled 1. B. The line labeled 2. C. The line labeled 3. D. The line labeled 4. E. All the lines are correct and the program compiles. 14. Which of these assignments are valid. Select the four correct answers. A. short s = 28; B. float f = 2.3; C. double d = 2.3; D. int I = '1'; E. byte b = 12;
15. What gets printed when the following program is compiled and run. Select the one correct answer.
16. 17. class test { 18. public 19. 20. 21. 22. 23. 24. } 25. } 26. static void main(String args[]) { int i,j,k,l=0; k = l++; j = ++k; i = j++; System.out.println(i);
A. 0 B. 1 C. 2 D. 3 27. Which of these lines will compile? Select the four correct answers. A. short s = 20; B. byte b = 128; C. char c = 32; D. double d = 1.4;; E. float f = 1.4; F. byte e = 0; 28. The signed right shift operator in Java is --. Select the one correct answer. A. <<; B. >> C. >>>; D. None of these. 29. What gets printed on the standard output when the class below is compiled and executed. Select the one correct answer.
30. 31. public class ShortCkt { 32. public static void main(String args[]) { 33. int i = 0; 34. boolean t = true; 35. boolean f = false, b; 36. b = (t && ((i++) == 0)); 37. b = (f && ((i+=2) > 0)); 38. System.out.println(i); 39. } 40. } 41.
A. 0 B. 1 C. 2 D. 3 42. What gets printed on the standard output when the class below is compiled and executed. Select the one correct answer.
43. 44. public class ShortCkt {
45. 46. 47. 48. 49. 50. 51. 52. 53. } 54.
public static void main(String args[]) { int i = 0; boolean t = true; boolean f = false, b; b = (t & ((i++) == 0)); b = (f & ((i+=2) > 0)); System.out.println(i); }
A. 0 B. 1 C. 2 D. 3 55. What gets printed on the standard output when the class below is compiled and executed. Select the one correct answer.
56. 57. public class ShortCkt { 58. public static void main(String args[]) { 59. int i = 0; 60. boolean t = true; 61. boolean f = false, b; 62. b = (t || ((i++) == 0)); 63. b = (f || ((i+=2) > 0)); 64. System.out.println(i); 65. } 66. } 67.
A. 0 B. 1 C. 2 D. 3 68. What gets printed on the standard output when the class below is compiled and executed. Select the one correct answer.
69. 70. public class ShortCkt { 71. public static void main(String args[]) { 72. int i = 0; 73. boolean t = true; 74. boolean f = false, b; 75. b = (t | ((i++) == 0)); 76. b = (f | ((i+=2) > 0)); 77. System.out.println(i); 78. } 79. } 80.
A. 0 B. 1 C. 2 D. 3 81. Which operator is used to perform bitwise inversion in Java. Select the one correct answer.
A. ~ B. ! C. & D. | E. ^ 82. What gets printed when the following program is compiled and run. Select the one correct answer.
83. 84. 85. public class test { 86. public static void main(String args[]) { 87. byte x = 3; 88. x = (byte)~x; 89. System.out.println(x); 90. } 91. } 92. 93.
A. 3 B. 0 C. 1 D. 11 E. 252 F. 214 G. 124 H. -4 94. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
95. 96. public class test { 97. public static void main(String args[]) { 98. int x,y; 99. x = 3 & 5; 100. y = 3 | 5; 101. System.out.println(x + " " + y); 102. } 103. } 104.
71 37 17 31 13 73 75 105. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
106. 107. public class test { 108. public static void main(String args[]) { 109. int x,y;
A. B. C. D. E. F. G.
13 35 51 36 17 15 116. Which operator is used to perform bitwise exclusive or. A. & B. ^ C. | D. ! E. ~ 117. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
118. 119. public class test { 120. public static void main(String args[]) { 121. boolean x = true; 122. int a; 123. if(x) a = x ? 1: 2; 124. else a = x ? 3: 4; 125. System.out.println(a); 126. } 127. } 128.
A. B. C. D. E. F.
1 2 3 4 129. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
130. 131. public class test { 132. public static void main(String args[]) { 133. boolean x = false; 134. int a; 135. if(x) a = x ? 1: 2; 136. else a = x ? 3: 4; 137. System.out.println(a); 138. } 139. } 140.
A. B. C. D.
A. 1 B. 2 C. 3
D. 4 141. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
142. 143. public class test { 144. public static void main(String args[]) { 145. int x, y; 146. 147. x = 5 >> 2; 148. y = x >>> 2; 149. System.out.println(y); 150. } 151. } 152.
5 2 80 0 64 153. What gets displayed on the screen when the following program is compiled and run. Select the one correct answer.
154. 155. public class test { 156. public static void main(String args[]) { 157. int x; 158. 159. x = -3 >> 1; 160. x = x >>> 2; 161. x = x << 1; 162. System.out.println(x); 163. } 164. } 165.
A. B. C. D. E.
1 0 7 5 23 2147483646 166. Which of the following are correct. Select all correct answers. A. Java provides two operators to do left shift - << and <<<. B. >> is the zero fill right shift operator. C. >>> is the signed right shift operator. D. For positive numbers, results of operators >> and >>> are same. 167. What is the result of compiling and running the following program. Select one correct answer.
168. 169. public class test { 170. public static void main(String args[]) { 171. int i = -1; 172. i = i >> 1; 173. System.out.println(i);
A. B. C. D. E. F.
63 -1 0 1 127 128 255 177. What all gets printed when the following gets compiled and run. Select the two correct answers.
178. 179. public class example { 180. public static void main(String args[]) { 181. int x = 0; 182. if(x > 0) x = 1; 183. 184. switch(x) { 185. case 1: System.out.println(1); 186. case 0: System.out.println(0); 187. case 2: System.out.println(2); 188. break; 189. case 3: System.out.println(3); 190. default: System.out.println(4); 191. break; 192. } 193. } 194. } 195.
A. B. C. D. E. F. G.
0 1 2 3 4 196. What happens when the following class is compiled and run. Select one correct answer.
197. 198. public class test { 199. public static void main(String args[]) { 200. int x = 0, y = 1, z; 201. if(x) 202. z = 0; 203. else 204. z = 1; 205. 206. if(y) 207. z = 2; 208. else 209. z = 3; 210. System.out.println(z); 211. } 212. }
A. B. C. D. E.
213.
The program prints 0 The program prints 1 The program prints 2 The program prints 3 The program does not compile because of problems in the if statement. 214. Which all lines are part of the output when the following code is compiled and run. Select the nine correct answers.
215. 216. public class test { 217. public static void main(String args[]) { 218. for(int i = 0; i < 3; i++) { 219. for(int j = 3; j >= 0; j--) { 220. if(i == j) continue; 221. System.out.println(i + " " + j); 222. } 223. } 224. } 225. } 226.
A. B. C. D. E.
00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 The program does not print anything. 227. Which all lines are part of the output when the following code is compiled and run. Select the one correct answer.
228. 229. public class test { 230. public static void main(String args[]) { 231. for(int i = 0; i < 3; i++) { 232. for(int j = 3; j <= 0; j--) { 233. if(i == j) continue; 234. System.out.println(i + " " + j); 235. } 236. } 237. } 238. } 239.
A. B. C. D. E. F. G. H. I. J. K. L. M. N. O. P. Q.
00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 The program does not print anything. 240. Which all lines are part of the output when the following code is compiled and run. Select the six correct answers.
241. 242. public class test { 243. public static void main(String args[]) { 244. for(int i = 0; i < 3; i++) { 245. for(int j = 3; j >= 0; j--) { 246. if(i == j) break; 247. System.out.println(i + " " + j); 248. } 249. } 250. } 251. } 252.
A. B. C. D. E. F. G. H. I. J. K. L. M. N. O. P. Q.
00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 253. Which all lines are part of the output when the following code is compiled and run. Select the six correct answers.
A. B. C. D. E. F. G. H. I. J. K. L. M. N. O. P.
254. 255. public class test { 256. public static void main(String args[]) { 257. outer: for(int i = 0; i < 3; i++) { 258. for(int j = 3; j >= 0; j--) { 259. if(i == j) continue outer; 260. System.out.println(i + " " + j); 261. } 262. } 263. } 264. } 265.
00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 266. Which all lines are part of the output when the following code is compiled and run. Select the three correct answers.
267. 268. public class test { 269. public static void main(String args[]) { 270. outer : for(int i = 0; i < 3; i++) { 271. for(int j = 3; j >= 0; j--) { 272. if(i == j) break outer; 273. System.out.println(i + " " + j); 274. } 275. } 276. } 277. } 278.
A. B. C. D. E. F. G. H. I. J. K. L. M. N. O. P.
A. B. C. D. E. F. G. H. I.
00 01 02 03 10 11 12 13 20
J. K. L. M. N. O. P.
21 22 23 30 31 32 33
Answers to questions on Operators and Assignments 1. 2. 3. 4. c. It is not possible to assign an integer to a character in this case without a cast. a, c, d, e. 2.3 is of type double. So it cannot be assigned to a float without a cast. b a, c, d, f. If RHS (Right hand side) is an integer within the correct range of LHS (Left hand side), and if LHS is char, byte, or short, no cast is required. A decimal number is a double by default. Assigning it to float requires a cast. 5. b 6. b. In the second assignment to variable b, the expression (i+=2) does not get evaluated. 7. d 8. c 9. d 10. a 11. h 12. c 13. f 14. b 15. a 16. d 17. d 18. f 19. d 20. b 21. a, c 22. e. The expression in the if statement must evaluate to a boolean. 23. b, c, d, e, g, h, i, j, l 24. q 25. b, c, d, g, h, l 26. b, c, d, g, h, l 27. b, c, d
A. number_1 B. number_a C. $1234 D. -volatile 2. Which of these are not legal identifiers. Select the four correct answers. A. 1alpha B. _abcd C. xy+abc D. transient E. account-num F. very_long_name 3. Which of the following are keywords in Java. Select the two correct answers. A. friend B. NULL C. implement D. synchronized E. throws 4. Which of the following are Java keywords. Select the four correct answers. A. super B. strictfp C. void D. synchronize E. instanceof 5. Which of these are Java keywords. Select the five correct answers A. TRUE B. volatile C. transient D. native E. interface F. then G. new 6. Using up to four characters, write the Java representation of octal literal 6. 7. Using up to four characters, write the Java representation of integer literal 3 in hexadecimal. 8. Using up to four characters, write the Java representation of integer literal 10 in hexadecimal. 9. What is the minimum value of char type. Select the one correct answer. A. 0 B. -215 C. -28 D. -215 - 1 E. -216 F. -216 - 1 10. How many bytes are used to represent the primitive data type int in Java. Select the one correct answer. A. 2
B. 4 C. 8 D. 1 E. The number of bytes to represent an int is compiler dependent. 11. What is the legal range of values for a variable declared as a byte. Select the one correct answer. A. 0 to 256 B. 0 to 255 C. -128 to 127 D. -128 to 128 E. -127 to 128 F. -215 to 215 - 1 12. The width in bits of double primitive type in Java is --. Select the one correct answer. A. The width of double is platform dependent B. 64 C. 128 D. 8 E. 4 13. What would happen when the following is compiled and executed. Select the one correct answer.
14. 15. public class Compare { 16. public static void main(String args[]) { 17. int x = 10, y; 18. if(x < 10) 19. y = 1; 20. if(x>= 10) y = 2; 21. System.out.println("y is " + y); 22. } 23. } 24.
A. The program compiles and prints y is 0 when executed. B. The program compiles and prints y is 1 when executed. C. The program compiles and prints y is 2 when executed. D. The program does not compile complaining about y not being initialized. E. The program throws a runtime exception. 25. What would happen when the following is compiled and executed. Select the one correct answer.
26. 27. class example { 28. int x; 29. int y; 30. String name; 31. public static void main(String args[]) { 32. example pnt = new example(); 33. System.out.println("pnt is " + pnt.name + 34. " " + pnt.x + " " + pnt.y); 35. } 36. } 37.
A. The program does not compile because x, y and name are not initialized. B. The program throws a runtime exception as x, y, and name are used before initialization. C. The program prints pnt is 0 0. D. The program prints pnt is null 0 0. E. The program prints pnt is NULL false false 38. The initial value of an instance variable of type String that is not explicitly initialized in the program is --. Select the one correct answer. A. null B. "" C. NULL D. 0 E. The instance variable must be explicitly assigned. 39. The initial value of a local variable of type String that is not explicitly initialized and which is defined in a member function of a class. Select the one correct answer. A. null B. "" C. NULL D. 0 E. The local variable must be explicitly assigned. 40. Which of the following are legal Java programs. Select the four correct answers. A. // The comments come before the package
package pkg; import java.awt.*; class C{} package pkg; import java.awt.*; class C{} package pkg1; package pkg2; import java.awt.*; class C{} package pkg; import java.awt.*; import java.awt.*; class C{} import java.awt.*; package pkg; class C {}
B. C.
D. E. F.
41. Which of the following statements are correct. Select the four correct answers. A. A Java program must have a package statement. B. A package statement if present must be the first statement of the program (barring any comments). C. If a Java program defines both a package and import statement, then the import statement must come before the package statement. D. An empty file is a valid source file. E. A Java file without any class or interface definitions can also be compiled.
F. If an import statement is present, it must appear before any class or interface definitions. 42. What would be the results of compiling and running the following class. Select the one correct answer.
43. 44. class test { 45. public static void main() { 46. System.out.println("test"); 47. } 48. } 49.
A. The program does not compile as there is no main method defined. B. The program compiles and runs generating an output of "test" C. The program compiles and runs but does not generate any output. D. The program compiles but does not run. 50. Which of these are valid declarations for the main method? Select the one correct answer. A. public void main(); B. public static void main(String args[]); C. static public void main(String); D. public static void main(String ); E. public static int main(String args[]); 51. Which of the following are valid declarations for the main method. Select the three correct answers. A. public static void main(String args[]); B. public static void main(String []args); C. final static public void main (String args[]); D. public static int main(String args[]); E. public static abstract void main(String args[]); 52. What happens when the following program is compiled and executed with the command - java test. Select the one correct answer.
53. 54. class test { 55. public static void main(String args[]) { 56. if(args.length > 0) 57. System.out.println(args.length); 58. } 59. } 60.
A. The program compiles and runs but does not print anything. B. The program compiles and runs and prints 0 C. The program compiles and runs and prints 1 D. The program compiles and runs and prints 2 E. The program does not compile. 61. What is the result of compiling and running this program? Select the one correct answer.
62. 63. public class test { 64. public static void main(String args[]) { 65. int i, j;
int k = 0; j = 2; k = j = i = 1; System.out.println(k); }
A. The program does not compile as k is being read without being initialized. B. The program does not compile because of the statement k = j = i = 1; C. The program compiles and runs printing 0. D. The program compiles and runs printing 1. E. The program compiles and runs printing 2. 74. What gets printed on the standard output when the class below is compiled and executed by entering "java test lets see what happens". Select the one correct answer.
75. 76. public class test { 77. public static void main(String args[]) { 78. System.out.println(args[0]+" "+args[args.length-1]); 79. } 80. } 81.
A. The program will throw an ArrayIndexOutOfBounds exception. B. The program will print "java test" C. The program will print "java happens"; D. The program will print "test happens" E. The program will print "lets happens" 82. What gets printed on the standard output when the class below is compiled and executed by entering "java test lets see what happens". Select the one correct answer.
83. 84. public class test { 85. public static void main(String args[]) { 86. System.out.println(args[0]+" "+args[args.length]); 87. } 88. } 89.
A. The program will throw an ArrayIndexOutOfBounds exception. B. The program will print "java test" C. The program will print "java happens"; D. The program will print "test happens" E. The program will print "lets happens" 90. What all gets printed on the standard output when the class below is compiled and executed by entering "java test lets see what happens". Select the two correct answers.
91. 92. public class test { 93. public static void main(String args[]) { 94. System.out.println(args[0]+" "+args.length); 95. }
96. } 97.
A. java B. test C. lets D. 3 E. 4 F. 5 G. 6 98. What happens when the following program is compiled and run. Select the one correct answer.
99. 100. public class example { 101. int i = 0; 102. public static void main(String args[]) { 103. int i = 1; 104. i = change_i(i); 105. System.out.println(i); 106. } 107. public static int change_i(int i) { 108. i = 2; 109. i *= 2; 110. return i; 111. } 112. } 113.
The program does not compile. The program prints 0. The program prints 1. The program prints 2. The program prints 4. 114. What happens when the following program is compiled and run. Select the one correct answer.
115. 116. public class example { 117. int i = 0; 118. public static void main(String args[]) { 119. int i = 1; 120. change_i(i); 121. System.out.println(i); 122. } 123. public static void change_i(int i) { 124. i = 2; 125. i *= 2; 126. } 127. } 128.
A. B. C. D. E.
A. B. C. D. E.
The program does not compile. The program prints 0. The program prints 1. The program prints 2. The program prints 4.
129. What happens when the following program is compiled and run. Select the one correct answer.
130. 131. public class example { 132. int i[] = {0}; 133. public static void main(String args[]) { 134. int i[] = {1}; 135. change_i(i); 136. System.out.println(i[0]); 137. } 138. public static void change_i(int i[]) { 139. i[0] = 2; 140. i[0] *= 2; 141. } 142. } 143.
The program does not compile. The program prints 0. The program prints 1. The program prints 2. The program prints 4. 144. What happens when the following program is compiled and run. Select the one correct answer.
145. 146. public class example { 147. int i[] = {0}; 148. public static void main(String args[]) { 149. int i[] = {1}; 150. change_i(i); 151. System.out.println(i[0]); 152. } 153. public static void change_i(int i[]) { 154. int j[] = {2}; 155. i = j; 156. } 157. } 158.
A. B. C. D. E.
A. B. C. D. E.
The program does not compile. The program prints 0. The program prints 1. The program prints 2. The program prints 4.
Answers to questions on Language Fundamentals 1. 2. 3. 4. a, b, c a, c, d, e d, e a, b, c, e. Please note that strictfp is a new keyword in Java 2. See Sun's site for more details.
5. 6. 7. 8.
b, c, d, e, g Any of the following are correct answers - 06, 006, or 0006 Any of the following are correct answers - 0x03, 0X03, 0X3 or 0x3 Any of the following are correct answers - 0x0a, 0X0a, 0Xa, 0xa, 0x0A, 0X0A, 0XA, 0xA 9. a 10. b 11. c 12. b 13. d. The variable y is getting read before being properly initialized. 14. d. Instance variable of type int and String are initialized to 0 and null respectively. 15. a 16. e 17. a, b, d, e 18. b, d, e, f 19. d 20. b 21. a, b, c 22. a 23. d 24. e 25. a 26. c, e 27. e 28. c 29. e 30. c
5. public static void main(String args[]) { 6. for(int i = 0; i < 2; i++) { 7. for(int j = 2; j>= 0; j--) { 8. if(i == j) break; 9. System.out.println("i=" + i + " j="+j); 10. } 11. } 12. } 13. } 14.
A. i=0 j=0 B. i=0 j=1 C. i=0 j=2 D. i=1 j=0 E. i=1 j=1 F. i=1 j=2 G. i=2 j=0 H. i=2 j=1 I. i=2 j=2 15. What gets printed when the following code is compiled and run with the following command java test 2 Select the one correct answer.
16. 17. public class test { 18. public static void main(String args[]) { 19. Integer intObj=Integer.valueOf(args[args.length-1]); 20. int i = intObj.intValue(); 21. 22. if(args.length > 1) 23. System.out.println(i); 24. if(args.length > 0) 25. System.out.println(i - 1); 26. else 27. System.out.println(i - 2); 28. } 29. } 30. 31.
A. test B. test -1 C. 0 D. 1 E. 2 32. In Java technology what expression can be used to represent number of elements in an array named arr ? 33. How would the number 5 be represented in hex using up-to four characters. 34. Which of the following is a Java keyword. Select the four correct answers. A. extern B. synchronized C. volatile
D. friend E. friendly F. transient G. this H. then 35. Is the following statement true or false. The constructor of a class must not have a return type. A. true B. false 36. What is the number of bytes used by Java primitive long. Select the one correct answer. A. The number of bytes is compiler dependent. B. 2 C. 4 D. 8 E. 64 37. What is returned when the method substring(2, 4) is invoked on the string "example"? Include the answer in quotes as the result is of type String. 38. Which of the following is correct? Select the two correct answers. A. The native keyword indicates that the method is implemented in another language like C/C++. B. The only statements that can appear before an import statement in a Java file are comments. C. The method definitions inside interfaces are public and abstract. They cannot be private or protected. D. A class constructor may have public or protected keyword before them, nothing else. 39. What is the result of evaluating the expression 14 ^ 23. Select the one correct answer. A. 25 B. 37 C. 6 D. 31 E. 17 F. 9 G. 24 40. Which of the following are true. Select the one correct answers. A. && operator is used for short-circuited logical AND. B. ~ operator is the bit-wise XOR operator. C. | operator is used to perform bitwise OR and also short-circuited logical OR. D. The unsigned right shift operator in Java is >>. 41. Name the access modifier which when used with a method, makes it available to all the classes in the same package and to all the subclasses of the class. 42. Which of the following is true. Select the two correct answers. A. A class that is abstract may not be instantiated.
B. The final keyword indicates that the body of a method is to be found elsewhere. The code is written in non-Java language, typically in C/C++. C. A static variable indicates there is only one copy of that variable. D. A method defined as private indicates that it is accessible to all other classes in the same package. 43. What all gets printed when the following program is compiled and run. Select the two correct answers.
44. 45. public class test { 46. public static void main(String args[]) { 47. int i, j=1; 48. i = (j>1)?2:1; 49. switch(i) { 50. case 0: System.out.println(0); break; 51. case 1: System.out.println(1); 52. case 2: System.out.println(2); break; 53. case 3: System.out.println(3); break; 54. } 55. } 56. } 57. 58.
A. B. C. D. 59.
0 1 2 3
What all gets printed when the following program is compiled and run. Select the one correct answer.
60. 61. public class test { 62. public static void main(String args[]) { 63. int i=0, j=2; 64. do { 65. i=++i; 66. j--; 67. } while(j>0); 68. System.out.println(i); 69. } 70. } 71. 72.
A. 0 B. 1 C. 2 D. The program does not compile because of statement "i=++i;" 73. What all gets printed when the following gets compiled and run. Select the three correct answers.
74. 75. public class test { 76. public static void main(String args[]) { 77. int i=1, j=1; 78. try {
79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. } 99. 100.
i++; j--; if(i/j > 1) i++; } catch(ArithmeticException e) { System.out.println(0); } catch(ArrayIndexOutOfBoundsException e) { System.out.println(1); } catch(Exception e) { System.out.println(2); } finally { System.out.println(3); } System.out.println(4); }
0 1 2 3 4 101. What all gets printed when the following gets compiled and run. Select the two correct answers.
102. 103. public class test { 104. public static void main(String args[]) { 105. int i=1, j=1; 106. try { 107. i++; 108. j--; 109. if(i == j) 110. i++; 111. } 112. catch(ArithmeticException e) { 113. System.out.println(0); 114. } 115. catch(ArrayIndexOutOfBoundsException e) { 116. System.out.println(1); 117. } 118. catch(Exception e) { 119. System.out.println(2); 120. } 121. finally { 122. System.out.println(3); 123. } 124. System.out.println(4); 125. } 126. } 127.
A. B. C. D. E.
128.
0 1 2 3 4 129. What all gets printed when the following gets compiled and run. Select the two correct answers.
130. 131. public class test { 132. public static void main(String args[]) { 133. String s1 = "abc"; 134. String s2 = "abc"; 135. if(s1 == s2) 136. System.out.println(1); 137. else 138. System.out.println(2); 139. if(s1.equals(s2)) 140. System.out.println(3); 141. else 142. System.out.println(4); 143. } 144. } 145. 146.
A. B. C. D. E.
1 2 3 4 147. What all gets printed when the following gets compiled and run. Select the two correct answers.
148. 149. public class test { 150. public static void main(String args[]) { 151. String s1 = "abc"; 152. String s2 = new String("abc"); 153. 154. if(s1 == s2) 155. System.out.println(1); 156. else 157. System.out.println(2); 158. if(s1.equals(s2)) 159. System.out.println(3); 160. else 161. System.out.println(4); 162. } 163. } 164. 165.
A. B. C. D.
A. B. C. D.
1 2 3 4
166. Which of the following are legal array declarations. Select the three correct answers. A. int i[5][]; B. int i[][]; C. int []i[]; D. int i[5][5]; E. int[][] a; 167. What is the range of values that can be specified for an int. Select the one correct answer. A. The range of values is compiler dependent. B. -231 to 231 - 1 C. -231-1 to 231 D. -215 to 215 - 1 E. -215-1 to 215 168. How can you ensure that the memory allocated by an object is freed. Select the one correct answer. A. By invoking the free method on the object. B. By calling system.gc() method. C. By setting all references to the object to new values (say null). D. Garbage collection cannot be forced. The programmer cannot force the JVM to free the memory used by an object. 169. What gets printed when the following code is compiled and run. Select the one correct answer.
170. 171. public class test { 172. public static void main(String args[]) { 173. int i = 1; 174. do { 175. i--; 176. } while (i > 2); 177. System.out.println(i); 178. } 179. } 180. 181.
0 1 2 -1 182. Which of these is a legal definition of a method named m assuming it throws IOException, and returns void. Also assume that the method does not take any arguments. Select the one correct answer. A. void m() throws IOException{} B. void m() throw IOException{} C. void m(void) throws IOException{} D. m() throws IOException{} E. void m() {} throws IOException 183. Which of the following are legal identifier names in Java. Select the two correct answers.
A. B. C. D.
%abcd $abcd 1abcd package _a_long_name 184. At what stage in the following method does the object initially referenced by s becomes available for garbage collection. Select the one correct answer.
185. 186. void method X() { 187. String r = new String("abc"); 188. String s = new String("abc"); 189. r = r+1; //1 190. r = null; //2 191. s = s + r; //3 192. } //4 193. 194.
A. B. C. D. E.
A. B. C. D. E.
Before statement labeled 1 Before statement labeled 2 Before statement labeled 3 Before statement labeled 4 Never.
195. String s = new String("xyz"); Assuming the above declaration, which of the following statements would compile. Select the one correct answer. A. s = 2 * s; B. int i = s[0]; C. s = s + s; D. s = s >> 2; E. None of the above. 196. Which of the following statements related to Garbage Collection are correct. Select the two correct answers. A. It is possible for a program to free memory at a given time. B. Garbage Collection feature of Java ensures that the program never runs out of memory. C. It is possible for a program to make an object available for Garbage Collection. D. The finalize method of an object is invoked before garbage collection is performed on the object. 197. If a base class has a method defined as void method() { } Which of the following are legal prototypes in a derived class of this class. Select the two correct answers. A. void method() { } B. int method() { return 0;} C. void method(int i) { } D. private void method() { }
198. In which all cases does an exception gets generated. Select the two correct answers.
int i = 0, j = 1;
if((i == 0) || (j/i == 1)) if((i == 0) | (j/i == 1)) if((i != 0) && (j/i == 1)) if((i != 0) & (j/i == 1)) 199. Which of the following statements are true. Select the two correct answers. A. The wait method defined in the Thread class, can be used to convert a thread from Running state to Waiting state. B. The wait(), notify(), and notifyAll() methods must be executed in synchronized code. C. The notify() and notifyAll() methods can be used to signal and move waiting threads to ready-to-run state. D. The Thread class is an abstract class. 200. Which keyword when applied on a method indicates that only one thread should execute the method at a time. Select the one correct answer. A. transient B. volatile C. synchronized D. native E. static F. final 201. What is the name of the Collection interface used to represent elements in a sequence (in a particular order). Select the one correct answer. A. Collection B. Set C. List D. Map 202. Which of these classes implement the Collection interface SortedMap. Select the one correct answers. A. HashMap B. Hashtable C. TreeMap D. HashSet E. TreeSet F. Vector 203. Which of the following are true about interfaces. Select the two correct answers. A. Methods declared in interfaces are implicitly private. B. Variables declared in interfaces are implicitly public, static, and final. C. An interface can extend any number of interfaces. D. The keyword implements indicate that an interface inherits from another. 204. Assume that class A extends class B, which extends class C. Also all the three classes implement the method test(). How can a method in a class A invoke
A. B. C. D.
the test() method defined in class C (without creating a new instance of class C). Select the one correct answer. A. test(); B. super.test(); C. super.super.test(); D. ::test(); E. C.test(); F. It is not possible to invoke test() method defined in C from a method in A. 205. What is the return type of method round(double d) defined in Math class. 206. What gets written on the screen when the following program is compiled and run. Select the one right answer.
207. 208. public class test { 209. public static void main(String args[]) { 210. int i; 211. float f = 2.3f; 212. double d = 2.7; 213. i = ((int)Math.ceil(f)) * ((int)Math.round(d)); 214. 215. System.out.println(i); 216. } 217. } 218.
4 5 6 6.1 9 219. Is the following statement true or false. As the toString method is defined in the Object class, System.out.println can be used to print any object. A. true B. false 220. Which of these classes defined in java.io and used for file-handling are abstract. Select the two correct answers. A. InputStream B. PrintStream C. Reader D. FileInputStream E. FileWriter 221. Name the collection interface used to represent collections that maintain unique elements. 222. What is the result of compiling and running the following program.
223. 224. public class test { 225. public static void main(String args[]) { 226. String str1="abc"; 227. String str2="def"; 228. String str3=str1.concat(str2); 229. 230. str1.concat(str2);
A. B. C. D. E.
System.out.println(str1);
abc def abcabc abcdef defabc abcdefdef 235. Select the one correct answer. The number of characters in an object of a class String is given by A. The member variable called size B. The member variable called length C. The method size() returns the number of characters. D. The method length() returns the number of characters. 236. Select the one correct answer. Which method defined in Integer class can be used to convert an Integer object to primitive int type. A. valueOf B. intValue C. getInt D. getInteger 237. Name the return type of method hashCode() defined in Object class, which is used to get the unique hash value of an Object. 238. Which of the following are correct. Select the one correct answer. A. An import statement, if defined, must always be the first non-comment statement of the file. B. private members are accessible to all classes in the same package. C. An abstract class can be declared as final. D. Local variables cannot be declared as static. 239. Name the keyword that makes a variable belong to a class, rather than being defined for each instance of the class. Select the one correct answer. A. static B. final C. abstract D. native E. volatile F. transient 240. Which of these are core interfaces in the collection framework. Select the one correct answer. A. Tree B. Stack C. Queue D. Array E. LinkedList F. Map 241. Which of these statements are true. Select the two correct answers.
A. B. C. D. E. F.
A. B. C. D.
For each try block there must be at least one catch block defined. A try block may be followed by any number of finally blocks. A try block must be followed by at least one finally or catch block. If both catch and finally blocks are defined, catch block must precede the finally block.
Answers to Sample Test 1 1. b 2. b, c, f 3. d. Note that the program gets one command line argument - 2. args.length will get set to 1. So the condition if(args.length > 1) will fail, and the second check if(args.length > 0) will return true. 4. arr.length 5. Any of these is correct - 0x5, 0x05, 0X05, 0X5 6. b, c, f, g 7. a 8. d 9. "am" 10. a, c. Please note that b is not correct. A package statement may appear before an import statement. A class constructor may be declared private also. Hence d is incorrect. 11. a 12. a 13. protected 14. a, c 15. b, c 16. c 17. a, d, e 18. d, e 19. a, c 20. b, c 21. b, c, e 22. b 23. d 24. a 25. a 26. b, e . The option c is incorrect because a Java identifier name cannot begin with a digit. 27. d 28. c 29. c, d 30. a, c 31. b, d 32. b, c
33. c 34. c 35. c 36. b, c 37. f 38. long 39. e 40. a 41. a, c 42. Set 43. a 44. d 45. b 46. int 47. d 48. a 49. f 50. c, d
1. Which of the following are Java keywords? Select the three correct answers. A. external B. implement C. throw D. void E. integer F. private G. synchronize H. unsigned
2. Which of the following are legal definitions of the main method that can be used to execute a class. Select the one correct answer. A. public void main(String args) B. public static int main(String args[]) C. public static void main(String args[]) D. static public void MAIN(String args[]) E. public static void main(string args[]) F. public static void main(String *args)
3. Which of these are legal array declarations or definitions? Select the two correct answers. A. int[] []x[]; B. int *x; C. int x[5]; D. int[] x = {1,2,3};
4. Name the collection interface used to represent a sequence of numbers in a fixed order. 5. The class Hashtable is used to implement which collection interface. Select the one correct answer. A. Table B. List C. Set D. SortedSet E. Map
6. What gets printed when the following program is compiled and run? Select the one correct answer.
7. 8. 9. 10. class test { 11. 12. public static void main(String args[]) { 13. 14. int i; 15. 16. do { 17. 18. i++; 19. 20. }
21. 22. 23. 24. 25. 26. 27. 28. } 29. 30. 31.
A. B. C. D. E.
The program does not compile as i is not initialized. The program compiles but does not run. The program compiles and runs but does not print anything. The program prints 0. The program prints 1.
32. What gets printed when the following program is compiled and run? Select the one correct answer.
33. 34. 35. 36. class xyz { 37. 38. static int i; 39. 40. public static void main(String args[]) { 41. 42. 43. 44. while (i < 0) { 45. 46. i--; 47. 48. } 49. 50. System.out.println(i); 51. 52. } 53. 54. } 55. 56. 57.
A. B. C. D. E.
The program does not compile as i is not initialized. The program compiles but does not run. The program compiles and runs but does not print anything. The program prints 0. The program prints 1.
58. What gets printed when the following program is compiled and run? Select the one correct answer.
59. 60. 61. 62. class xyz { 63. 64. 65. 66. public static void main(String args[]) { 67. 68. int i,j,k; 69. 70. for (i = 0; i < 3; i++) { 71. 72. for(j=1; j < 4; j++) { 73. 74. for(k=2; k<5; k++) { 75. 76. if((i == j) && (j==k)) 77. 78. System.out.println(i); 79. 80. } 81. 82. } 83. 84. } 85. 86. } 87. 88. } 89. 90. 91.
A. B. C. D. E.
0 1 2 3 4
92. Using up to four characters what is the Java representation of the number 23 in hex? 93. What gets printed when the following program is compiled and run? Select the one correct answer.
94. 95. 96.
97. class test { 98. 99. static boolean check; 100. 101. public static void main(String args[]) { 102. 103. int i; 104. 105. if(check == true) 106. 107. i=1; 108. 109. else 110. 111. i=2; 112. 113. 114. 115. if(i=2) i=i+2; 116. 117. else i = i + 4; 118. 119. System.out.println(i); 120. 121. } 122. 123. } 124. 125. 126.
A. B. C. D. E.
127. Select the one correct answer. The smallest number that can be represented using short primitive type in Java is A. 0 B. -127 C. -128 D. -16384 E. -32768 F. The smallest number is compiler dependent.
128. Given the following declarations, which of the assignments given in the options below would compile. Select the two correct answers.
129.
130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141.
A. B. C. D. E.
t = (boolean) i; f = d; d = i; i = 5; f = 2.8;
142. What gets printed when the following program is compiled and run. Select the one correct answer.
143. 144. 145. 146. public class incr { 147. 148. public static void main(String args[]) { 149. 150. int i , j; 151. 152. i = j = 3; 153. 154. int n = 2 * ++i; 155. 156. int m = 2 * j++; 157. 158. System.out.println(i + " " + j + " " + n + " " + m); 159. 160. } 161. 162. } 163. 164. 165.
A. B. C. D. E. F.
166. Given two non-negative integers a and b and a String str, what is the number of characters in the expression str.substring(a,b) . Select the one correct answer. A. a + b B. a - b C. b - a - 1 D. b - a + 1 E. b - a F. b
167. What is the result of compiling and running the following program. Select the one correct answer.
168. 169. 170. 171. class test { 172. 173. public static void main(String args[]) { 174. 175. char ch; 176. 177. String test2 = "abcd"; 178. 179. String test = new String("abcd"); 180. 181. if(test.equals(test2)) { 182. 183. if(test == test2) 184. 185. ch = test.charAt(0); 186. 187. else 188. 189. ch = test.charAt(1); 190. 191. } 192. 193. else { 194. 195. if(test == test2) 196. 197. ch = test.charAt(2); 198. 199. else 200. 201. ch = test.charAt(3); 202. 203. } 204. 205. System.out.println(ch); 206. 207. }
A. B. C. D.
213. What is the result of compiling and running the following program. Select the one correct answer.
214. 215. 216. 217. class test { 218. 219. public static void main(String args[]) { 220. 221. int i,j=0; 222. 223. for(i=10;i<0;i--) { j++; } 224. 225. switch(j) { 226. 227. case (0) : 228. 229. j=j+1; 230. 231. case(1): 232. 233. j=j+2; 234. 235. break; 236. 237. case (2) : 238. 239. j=j+3; 240. 241. break; 242. 243. 244. 245. case (10) : 246. 247. j=j+10; 248. 249. break; 250. 251. default : 252. 253. break; 254.
255. 256. 257. 258. 259. 260. 261. } 262. 263. 264.
} System.out.println(j); }
A. B. C. D. E. F.
0 1 2 3 10 20
265. What is the number displayed when the following program is compiled and run.
266. 267. 268. 269. class test { 270. 271. public static void main(String args[]) { 272. 273. test test1 = new test(); 274. 275. System.out.println(test1.xyz(100)); 276. 277. } 278. 279. public int xyz(int num) { 280. 281. if(num == 1) return 1; 282. 283. else return(xyz(num-1) + num); 284. 285. } 286. 287. } 288. 289. 290.
291.
Which of the following statements are true. Select the one correct answer. A. Arrays in Java are essentially objects. B. It is not possible to assign one array to another. Individual elements of array can however be assigned.
C. Array elements are indexed from 1 to size of array. D. If a method tries to access an array element beyond its range, a compile warning is generated.
292. Which expression can be used to access the last element of an array. Select the one correct answer. A. array[array.length()] B. array[array.length() - 1] C. array[array.length] D. array[array.length - 1]
293. What is the result of compiling and running the following program. Select the one correct answer.
294. 295. 296. 297. class test { 298. 299. public static void main(String args[]) { 300. 301. int[] arr = {1,2,3,4}; 302. 303. call_array(arr[0], arr); 304. 305. System.out.println(arr[0] + "," + arr[1]); 306. 307. } 308. 309. static void call_array(int i, int arr[]) { 310. 311. arr[i] = 6; 312. 313. i = 5; 314. 315. } 316. 317. } 318. 319. 320.
A. B. C. D.
321. Which of the following statements are correct. Select the one correct answer. A. Each Java file must have exactly one package statement to specify where the class is stored. B. If a Java file has both import and package statement, the import statement must come before package statement. C. A Java file has at least one class defined. D. If a Java file has a package statement, it must be the first statement (except comments).
322. What happens when the following program is compiled and then the command "java check it out" is executed. Select the one correct answer.
323. 324. 325. 326. class check { 327. 328. public static void main(String args[]) { 329. 330. System.out.println(args[args.length-2]); 331. 332. } 333. 334. } 335. 336. 337.
A. The program does not compile. B. The program compiles but generates ArrayIndexOutOfBoundsException exception. C. The program prints java D. The program prints check E. The program prints it F. The program prints out
338. What all gets printed when the following code is compiled and run. Select the three correct answers.
339. 340. 341. 342. class test { 343. 344. public static void main(String args[]) { 345. 346. int i[] = {0,1}; 347. 348. try {
349. 350. 351. 352. 353. 354. 355. 356. 357. 358. 359. 360. 361. 362. 363. 364. 365. 366. 367. 368. 369. 370. 371. 372. 373. 374. 375. 376. } 377. 378. 379.
i[2] = i[0] + i[1]; } catch(ArrayIndexOutOfBoundsException e1) { System.out.println("1"); } catch(Exception e2) { System.out.println("2"); } finally { System.out.println(3); } System.out.println("4"); }
A. B. C. D.
1 2 3 4
380. A program needs to store the name, salary, and age of employees in years. Which of the following data types should be used to create the Employee class. Select the three correct answers. A. char B. boolean C. Boolean D. String E. int F. double
381. To make a variable defined in a class accessible only to methods defined in the classes in same package, which of the following keyword should be used. Select the one correct answer. A. By using the keyword package before the variable. B. By using the keyword private before the variable. C. By using the keyword protected before the variable. D. By using the keyword public before the variable. E. The variable should not be preceded by any of the above mentioned keywords.
382. In implementing two classes Employee and Manager, such that each Manager is an Employee, what should be the relationship between these classes. Select the one correct answer. A. Employee should be the base class of Manager class. B. Manager should be the base class of Employee class. C. Manager class should include the Employee class as a data member. D. Employee class should include Manager class as a data member. E. The Manager and Employee should not have any relationship.
383. Select the one most appropriate answer. What is the purpose of method parseInt defined in Integer class. A. The method converts an integer to a String. B. The method is used to convert String to an integer, assuming that the String represents an integer. C. The method is used to convert String to Integer class, assuming that the String represents an integer. D. The method converts the Integer object to a String.
384. What should be done to invoke the run() method on a thread for an object derived from the Thread class. Select the one correct answer. A. The run() method should be directly invoked on the Object. B. The start() method should be directly invoked on the Object. C. The init() method should be directly invoked on the Object. D. The creation of the object using the new operator would create a new thread and invoke its run() method.
385.
What is the default priority of a newly created thread. A. MIN_PRIORITY (which is defined as 1 in the Thread class.)
B. NORM_PRIORITY (which is defined as 5 in the Thread class.) C. MAX_PRIORITY (which is defined as 10 in the Thread class.) D. A thread inherits the priority of its parent thread.
Answers to Sample Test 2 1. c, d, f 2. c. The main method must be static and return void. Hence a and b are incorrect. It must take an array of String as argument. Hence e and f are incorrect. As Java is case sensitive, d is incorrect. 3. a, d 4. List 5. e. The collection interface Map has two implementation HashMap and Hashtable. 6. a. Local variables are not initialized by default. They must be initialized before they are used. 7. d. The variable i gets initialized to zero. The while loop does not get executed. 8. c. During various iterations of three loops, the only time i, j and k have same values are when all of them are set to 2. 9. 0x17 or 0X17. 10. e. The statement "i=2" evaluates to 2. The expression within the if block must evaluate to a boolean. 11. e. The range of short primitive type is -32768 to 32767. 12. c,d. Java does not allow casts between boolean values and any numeric types. Hence a is incorrect. Assigning double to a float requires an explicit cast. Hence b and e are incorrect. 13. a 14. e 15. b. Both Strings test and test2 contain "abcd" . They are however located at different memory addresses. Hence test == test2 returns false, and test.equals(test2) returns true. 16. d. The for loop does not get executed even once as the condition (i < 0) fails in the first iteration. In the switch statement, the statement j = j +1; gets executed, setting j to 1. As there is no break after this case, the next statement also gets executed setting j to 3. 17. 5050. The recursive function xyz essentially sums up numbers 1 to num. This evaluates to (num * (num + 1))/2. 18. a. Java supports assignment of one array to another. Hence b is incorrect. Array elements are indexed from 0. Hence c is incorrect. A method that accesses array elements out of its range does not generate a compilation error. Hence d is incorrect. 19. d. array.length gives the number of elements in the array. As indexes in Java start from 0, d is the correct answer. 20. c. In the invocation of call_array, the first element is invoked using call-by-value, and the second using call-by-reference.
21. d. import statement, package statement and class definitions are all optional in a file. Hence a and c are incorrect. If both import and package statements are present in a file, then package statement must appear before the import statement. Hence b is incorrect. 22. e. The args array consists of two elements "it" and "out". args.length is set to two. 23. a,c,d. The exception ArrayIndexOutOfBoundsException is generated as the main method tries to access i[2]. Hence 1 gets printed. After this finally block gets excuted, before the program exits. 24. d,e,f 25. e. A data member that does not have public/protected/private is accessible to all methods in the same package. 26. a. The Manager and Employee share as "is a" relationship - A Manager is an Employee. This is captured by making Employee the base class of Manager. 27. b. The method int parseInt(Sting s) returns the integer value corresponding to input String, assuming that the input string represents an integer in base 10. 28. b. The start() method invokes the run() method when the thread is ready to execute. 29. d
JAVA EXCEPTIONS
Contents
Error Handling Exceptions o How to Catch Exceptions o More About the Finally Clause o Checked and Unchecked Exceptions o Exception Hierarchy o Choices o Test Yourself #1 o How to Define and Throw Exceptions o Test Yourself #2 Summary Answers to Self-Study Questions
Error Handling
Runtime errors can be divided into low-level errors that involve violating constraints, such as:
out-of-bounds array access divide by zero attempt to open a non-existent file for reading bad cast (e.g., casting an Object that is actually a Boolean to Integer)
call to Stack's "pop" method for an empty stack call to "factorial" function with a negative number call to List's nextElement method when hasMoreElements is false
Logical errors can lead to low-level errors if they are not detected. Often, it is better to detect them (to provide better feedback). Errors can arise due to:
User error (for example, providing a bad file name or a poorly formatted input file). A good program should be written to anticipate these situations, and should deal with them. For example, given a bad file name, an interactive program could print an error message and prompt for a new name. Programmer error (i.e., a buggy program). These errors should be detected as early as possible to provide good feedback. For some programs it may be desirable to do some recovery after detecting this kind of error; for example, writing out current data.
Note that recovery is often not possible at the point of the error (because the error may occur inside some utility function that doesn't know anything about the overall program or what error recovery should involve). Therefore, it is desirable to "pass the error up" to a level that can deal with it. There are several possible ways to handle errors:
Write an error message and quit. This doesn't provide any recovery. Return a special value to indicate that an error occurred. This is the usual approach for C functions (which often return 0 or -1 to signal an error). However: o It doesn't work if the function also returns a value on normal completion and all values are possible (i.e., there is no special value that can be used to signal an error). o It requires that calling code check for an error. This can reduce the efficiency of the code, and is often omitted by programmers out of laziness or carelessness. o It can sometimes make the code more clumsy. For example, if function g might return an error code, one would have to write something like:
o o o ret = g(x); if (ret == ERROR_CODE) { ... } else f(ret);
instead of just:
f(g(x));
Use a reference parameter or a global variable to hold an error code. This solves the first problem of the previous approach, but not the second or third ones. Use exceptions. This seems to be the method of choice for modern programming languages.
Exceptions
Idea:
When an error is detected, an exception is thrown. That is, the code that caused the error stops executing immediately, and control is transferred to the catch clause for that exception of the first enclosing try block that has such a clause. The try block might be in the current function (the one that caused the error), or it might be in some function that called the current function (i.e., if the current function is not prepared to handle the exception, it is "passed up" the call chain). If no currently active function is prepared to catch the exception, an error message is printed and the program stops.
Exceptions can be built-in (actually, defined in one of Java's standard libraries) or userdefined. Here are some examples of built-in exceptions with links to their documentation:
ArithmeticException (e.g., divide by zero) ClassCastException (e.g., attempt to cast a String Object to Integer) IndexOutOfBoundsException NullPointerException FileNotFoundException (e.g., attempt to open a non-existent file for reading)
Notes: 1. Each catch clause specifies the type of one exception, and provides a name for it (similar to the way a function header specifies the type and name of a parameter). Java exceptions are objects, so the statements in a catch clause can refer to the thrown exception object using the specified name. 2. The finally clause is optional. 3. In general, there can be one or more catch clauses. If there is a finally clause, there can be zero catch clauses. Example (a program that tries to open a file named by the first command-line argument for reading)
public static void main(String[] args) { InputStream istream; File inputFile; try { inputFile = new File(args[0]); istream = new InputStream(inputFile); // may throw FileNotFoundException } catch (FileNotFoundException ex) { System.out.println("file " + args[0] + " not found"); } }
Notes: 1. The program really should make sure there is a command-line argument before attempting to use args[0]. 2. Also, it probably makes more sense to put the try block in a loop, so that if the file is not found the user can be asked to enter a new file name, and a new attempt to open the file can be made. 3. As is, if the user runs the program with a bad file name foo, the message "file foo not found" will be printed, and the program will halt. 4. If there were no try block and the program were run with a bad file name foo, a more complicated message, something like this:
5. 6. 7. 8. java.io.FilenotFoundException: foo at java.io.FileInputStream ... at ... at Test.main ...
would be printed. (Actually, if there were no try/catch for the FileNotFoundException, the program wouldn't compile because it fails to list that exception as one that might be thrown. We'll come back to that issue later...)
A finally clause is usually included to make sure that some clean-up (e.g., closing opened files) is done. A finally clause always executes when its try block executes (whether or not there is an exception). Furthermore, if the finally clause includes a transfer of control statement (return, break, continue, throw) then that statement overrides any transfer of control initiated in the try or in a catch clause. First, let's assume that the finally clause does not include any transfer of control. Here are the situations that can arise: 1. No exception occurs during execution of the try, and no transfer of control is executed in the try. => The finally clause executes, then the statement following the try block. 2. No exception occurs during execution of the try, but it does execute a transfer of control. => The finally clause executes, then the transfer of control takes place. 3. An exception does occur during execution of the try, and there is no catch clause for that exception. => The finally clause executes, then the uncaught exception is "passed up" to the next enclosing try block, possibly in a calling function. 4. An exception does occur during execution of the try, and there is a catch clause for that exception. The catch clause does not execute a transfer of control. => The catch clause executes, then the finally clause, then the statement following the try block. 5. An exception does occur during execution of the try, there is a catch clause for that exception, and the catch clause does execute a transfer of control. => The catch clause executes, then the finally clause, then the transfer of control takes place. If the finally block does include a transfer of control, then that takes precedence over any transfer of control executed in the try or in an executed catch clause. So for all of the cases listed above, the finally clause would execute, then its transfer of control would take place. Here's one example:
try { return 0; } finally { return 2; }
Note that this is rather confusing! The moral is that you probably do not want to include transfer-of-control statements in both the try statements and the finally clause, or in both a catch clause and the finally clause.
the exception must be declared in the method header, using a throws clause, or the code that might cause the exception to be thrown must be inside a try block with a catch clause for that exception.
So in general, you must always include some code that acknowledges the possibility of a checked exception being thrown. If you don't, you will get an error when you try to compile your code.
Exception Hierarchy
+--------+ | Object | +--------+ | | +-----------+ | Throwable | +-----------+ / \ / \ +-------+ +-----------+ | Error | | Exception | +-------+ +-----------+ / | \ / | \ \________/ \______/ \ +------------------+ unchecked checked | RuntimeException | +------------------+ / | | \ \_________________/ unchecked
most of the built-in exceptions (e.g., NullPointerException, IndexOutOfBoundsException) are unchecked. IOExceptions (e.g., FileNotFoundException) are checked user-defined exceptions should usually be checked, so they should be subclasses of Exception.
2. Catch the exception, then re-throw it or throw another exception. 3. Ignore the exception (let it "pass up" the call chain). Note that if your code might cause a checked exception to be thrown; i.e.,:
your code throws a checked exception, or your code ignores a checked exception that might be thrown by a called function
then your function must include a throws clause listing all such exceptions. For example:
public static void main(String[] args) throws FileNotFoundException, EOFException { // an uncaught FileNotFoundException or EOFException may be thrown here }
Only uncaught checked exceptions need to be listed in a function's throws clause. Unchecked exceptions can be caught in a try block, but if not, they need not be listed in the function's throws clause. TEST YOURSELF #1 Consider the following program (assume that comments are replaced with actual code that works as specified):
class TestExceptions { static void e() { // might cause any of the following unchecked exceptions to be thrown: // Ex1, Ex2, Ex3, Ex4 } static void d() { try { e(); } catch (Ex1 ex) { System.out.println("d caught Ex1"); } } static void c() { try { d(); } catch (Ex2 ex) { System.out.println("c caught Ex2"); // now cause exception Ex1 to be thrown } } static void b() { try { c(); } catch (Ex1 ex) {
System.out.println("b caught Ex1"); } catch (Ex3 ex) { System.out.println("b caught Ex3"); } } static void a() { try { b(); } catch (Ex1 ex) { System.out.println("a caught Ex1"); } catch (Ex4 ex) { System.out.println("a caught Ex4"); // now cause exception Ex1 to be thrown } } public static void main(String[] args) { a(); } }
Assume that this program is run four times. The first time, function e throws exception Ex1, the second time, it throws exception Ex2, etc. Foe each of the four runs, say what is printed; if an uncaught exception is thrown, say what happens. solution
Java exceptions are objects. Define an exception by defining a class, for example: public class EmptyStackException extends Exception { } Note: New exceptions must be subclasses of Throwable; as discussed above, they are usually subclasses of Exception (so that they are checked). The exceptions you define do not have to be public classes; however, remember that if you do not make them public, then they can only used in the package in which they are defined.
Throw an exception using a throw statement: public class Stack { ... public Object Pop() throws EmptyStackException { if (Empty()) throw new EmptyStackException(); ...
} } Note:
o
Exceptions are objects, so you cannot simply throw "EmptyStackException" -- you must use "new" to create an exception object. Since the Pop method might throw the (checked) exception EmptyStackException, that must be included in Pop's throws clause. TEST YOURSELF #2
Question 1: Assume that function f might throw exceptions Ex1, Ex2, or Ex3. Complete function g, outlined below, so that:
If the call to f causes Ex1 to be thrown, g will catch that exception and print "Ex1 caught". If the call to f causes Ex2 to be thrown, g will catch that exception, print "Ex2 caught", and then will throw an Ex1 exception.
static void g() throws ... { try { f(); } catch ( ... ) { ... } ... }
Part A. Assume that variable X is an array of int that has been initialized to be of length 3. For each of the following calls to function f, say what (if anything) is printed by f, and what, if any, uncaught exceptions are thrown by f. A. f(0, X, "hi"); B. f(10, X, ""); C. f(10, X, "bye"); D. f(10, X, null); Part B. Why doesn't f need to have a throws clause that lists the uncaught exceptions that it might throw? solution
Summary
Code that detects errors often does not know how to handle them. Therefore, we need a way to "pass errors up". The best approach is to use exceptions. Java provides both built-in and user-defined exceptions. Exceptions are caught using a try block: try { // statements (including function calls) that might cause an exception } catch ( exception-1 id1 ) { // code to handle the first kind of exception } catch ( exception-2 id2 ) { // code to handle the second kind of exception } ... } finally { // code that will execute whenever this try block does }
Exceptions are thrown using a throw statement. If an exception is thrown in code that is not inside a try block, or is in a try block with no catch clause for the thrown exception, the exception is "passed up" the call stack. Some exceptions are checked and some are unchecked. If a function might throw one or more checked exceptions, they must be listed in the function's throws clause. Exceptions are objects; they are defined as classes (the class name is the name of the exception) that extend the Exception class.
Test Yourself #2
Question 1: static void g() throws Ex1, Ex3 { try { f(); } catch (Ex1 ex1) { System.out.println("Ex1 caught"); } catch (Ex2 ex2) { System.out.println("Ex2 caught"); throw new Ex1(); } Question 2: Part A. A. f(0, X, "hi"); nothing printed throws ArithmeticException B. f(10, X, ""); prints "in finally clause" throws StringIndexOutOfBoundsException C. f(10, X, "bye"); prints "array error", "in finally clause" throws InternalError D. f(10, X, null); }
block" Part B.
Function f doesn't need to have a throws clause that lists the uncaught exceptions that it might throw because only uncaught CHECKED exceptions need to be listed in a function's throws clause. The uncaught exceptions that f might throw are all UNCHECKED exceptions
JAVA THREADS
Contents
Overview Simple Example Test Yourself #1 Synchronization o Synchronized Methods o Synchronized Statements o Test Yourself #2 Communicating Among Threads o The wait method o The notify and notifyAll methods o Test Yourself #3 o Other useful methods The Runnable Interface Summary Answers to Self-Study Questions
Overview
So far, all of the code we've written has been sequential: when a program runs, one statement after another is executed, and when a method is called, the caller waits until the method returns before going on to the next statement. Another approach is to let multiple methods be active simultaneously. In Java, this approach involves the use of threads. Threads can be implemented by extending the Thread class, which is defined in java.lang. (A second way to implement threads - by implementing the Runnable interface - is discussed below.) A Thread object is run by calling its start method. The start method spawns a new thread of control, and then returns (without waiting for the new thread to finish). Therefore, it is possible to have many different threads running at the same time, by calling each thread's start method in turn.
When a new thread is started, its run method is called (by the Java virtual machine, not by your program). The thread exists until its run method returns. Mechanisms are provided to prevent threads from interfering with each other (e.g., trying to read and write the same data), and to allow communication among threads.
Simple Example
Here is a simple example Java program that creates two threads; one thread computes and prints the sums of even numbers, and the other computes and prints the sums of odd numbers:
class printSum extends Thread { // run method prints, for each k in the range start..end, the sum // of the even numbers from 1 to k if "even" is true; otherwise, // it prints the sum of the odd numbers from 1 to k // private data members int start, end; boolean even; // true means print sum of even numbers, // false means print sum of odd numbers // constructor: set start, end, and even public printSum(int s, int e, boolean ev) { start = s; end = e; even = ev; } // run method; print sums of even or odd numbers depending on // the value of "even" public void run() { for (int k=start; k<=end; k++) { int sum = 0; for (int j=1; j<= k; j++) { if (even && j/2 * 2 == j) sum += j; else if (!even && j/2 * 2 != j) sum += j; } if (even) System.out.println("even sum: " + sum); else System.out.println("odd sum: " + sum); } } } public class threadDemo1 { public static void main(String[] args) { printSum s1 = new printSum(1, 100, true); printSum s2 = new printSum(1, 100, false); s1.start(); s2.start(); for (int k=1; k<=100; k++) System.out.println("in main"); }
This example code creates two threads (s1 and s2), and then calls their start methods; this in turn causes the threads' run methods to be invoked. The main program then goes on to print "in main" 100 times. Conceptually, the two printSum threads, as well as the main thread, run simultaneously. However, if the program is run on a single-processor machine, only one thread will actually be running at any one time. It is up to the JVM to schedule multiple threads (you can influence this by setting their priorities). It may be that the first thread gets such a large block of time that when you run the example program you will get most or even all of the output from the first thread before you see any output from the second thread. You might be able to get some interleaving by increasing the second argument to the first thread's constructor function. Another way to get interleaving is to cause the threads to "sleep" periodically, using the sleep method of the Thread class:
for (int k=start; k<=end; k++) { int sum = 0; for (int j=1; j<= k; j++) { if (even && j/2 * 2 == j) sum += j; else if (!even && j/2 * 2 != j) sum += j; } if (even) System.out.println("even sum: " + sum); else System.out.println("odd sum: " + sum); sleep(100); }
The call to sleep causes the thread to wait for 100 milliseconds (1/10 of a second) after each iteration of the loop. However, this code is not quite correct: the sleep method can throw an InterruptedException, and a thread's run method is not allowed to throw any exceptions (because Thread.run, which is being overridden, throws no exceptions). Therefore, we must add code to catch that exception:
for (int k=start; k<=end; k++) { int sum = 0; for (int j=1; j<= k; j++) { if (even && j/2 * 2 == j) sum += j; else if (!even && j/2 * 2 != j) sum += j; } if (even) System.out.println("even sum: " + sum); else System.out.println("odd sum: " + sum); try { sleep(100); } catch (InterruptedException e) { return; } }
TEST YOURSELF #1 Define a class called PrintWord that extends the Thread class. The PrintWord constructor should take two arguments: a string to be printed, and the number of microseconds to sleep after printing the string. The PrintWord class's run method should print the appropriate string followed by a space, and then should sleep for the appropriate time. If the sleep is interrupted, the run method should return.
Write a program that creates two PrintWord threads, using different strings and different sleep times, then calls their start methods. solution
Synchronization
Some real-world tasks are better modeled by a program that uses threads than by a normal, sequential program. For example, consider a bank whose accounts can be accessed and updated by any of a number of automatic teller machines (ATMs). Each ATM could be a separate thread, responding to deposit and withdrawal requests from different users simultaneously. Of course, it would be important to make sure that two users did not access the same account simultaneously. This is done in Java using synchronization, which can be applied to individual methods, or to sequences of statements.
Synchronized Methods
One or more methods of a class can be declared to be synchronized. When a thread calls an object's synchronized method, the whole object is locked. This means that if another thread tries to call any synchronized method of the same object, the call will block until the lock is released (which happens when the original call finishes). In general, if the value of a field of an object can be changed, then all methods that read or write that field should be synchronized to prevent two threads from trying to write the field at the same time, and to prevent one thread from reading the field while another thread is in the process of writing it. Here is an example of a BankAccount class that uses synchronized methods to ensure that deposits and withdrawals cannot be performed simultaneously, and to ensure that the account balance cannot be read while either a deposit or a withdrawal is in progress. (To keep the example simple, no check is done to ensure that a withdrawal does not lead to a negative balance.)
public class BankAccount { private double balance; // constructor: set balance to given amount public BankAccount( double initialDeposit ) { balance = initialDeposit; } public synchronized double Balance( ) { return balance; } public synchronized void Deposit( double deposit ) { balance += deposit; }
Note that the BankAccount's constructor is not declared to be synchronized. That is because it can only be executed when the object is being created, and no other method can be called until that creation is finished. Static methods can also be synchronized. In that case, another thread cannot simultaneously call any other synchronized static method; it can however call synchronized non-static methods of any object of that class.
Synchronized Statements
Sometimes synchronized methods are not sufficient to prevent two threads from interfering with each other. For example, an ATM thread that is asked to perform a withdrawal from a given bank account might first use the Balance method to determine whether the account has enough money, and then use the Withdrawal method to perform the withdrawal. If two ATM threads are asked to withdraw money from the same account, the following sequence of events might take place: 1. 2. 3. 4. ATM 1 calls Balance, which returns $1,000 ATM 2 calls Balance, which returns $1,000 ATM 1 calls Withdraw, removing $600 from the account. ATM 2 calls Withdraw, removing an additional $500 from the account.
Since in both cases, the amount returned by the Balance method was more than the requested withdrawal amount, both ATM threads went ahead with their withdrawals. The fact that the Balance and Withdrawal methods were synchronized did not prevent the account from being overdrawn. The problem is that the third event (ATM 1 withdraws $600) invalidated the result of the second event (ATM 2 learns that the current balance is $1,000). This kind of problem can be prevented by locking an object for the duration of a sequence of statements. The syntax is as follows: synchronized ( <object-to-be-locked>; ) { <sequence-of-statements> } For example, the following code might be used by an ATM thread to withdraw D dollars from a BankAccount B:
synchronized ( B ) { if ( D > B.Balance() ) { ReportInsuffucientFunds(); }
else { B.Withdraw( D ); }
While the ATM thread is executing these synchronized statements, no other thread can call any synchronized non-static method of object B, nor can another thread execute synchronized statements that use object B as the object-to-be-locked. Let us reconsider the example above in which two ATM threads are both requested to withdraw money from the same account, now assuming that the synchronized statement list given above is used. In this case, the following sequence of events might take place: 1. ATM 1 starts its synchronized list of statements (now ATM 2 cannot start its synchronized list of statements until ATM 1 has finished executing the whole list). 2. ATM 1 calls Balance, which returns $1,000. 3. ATM 1 calls Withdraw, removing $600 from the account. 4. ATM 1 is finished with its synchronized list of statements. 5. ATM 2 starts its synchronized list of statements. 6. ATM 2 calls Balance, which returns $400. 7. The current balance is less than the requested withdrawal amount, so ATM 2 calls its ReportInsufficientFunds method. 8. ATM 2 is finished with its synchronized list of statements. TEST YOURSELF #2 Consider the following two class outlines, with line numbers included for reference:
1. public class Synch { 2. 3. public static void FirstStaticFn() { ... } 4. public synchronized static void SecondStaticFn() { ... } 5. public synchronized static void ThirdStaticFn() { ... } 6. 7. public void FirstFn() { ... } 8. public void SecondFn() { ... } 9. public synchronized void ThirdFn() { ... } 10. public synchronized void FourthFn() { ... } 11. } 12. 13. public class T extends Thread { 14. private Synch s; 15. 16. // constructor 17. public T( Synch syn ) { 18. s = syn; 19. } 20. 21. public void run() { 22. Synch.FirstStaticFn(); 23. Synch.SecondStaticFn(); 24. Synch.ThirdStaticFn();
25. 26. 27. 28. 29. 30. 40. 41. 42. 43. }
Assume that one Synch object S is created, and is passed to the constructor functions of two T objects, whose start methods are then called. Fill in the following table to show which statements in the two T threads might execute simultaneously. STATEMENT STATEMENTS THAT MIGHT BE EXECUTED BY FIRST EXECUTED SIMULTANEOUSLY BY T THREAD SECOND T THREAD 22 23 24 26 27 28 40
solution
Note:
1. The object that calls wait must be locked by the current thread. (This means that the method that contains the call to wait must be synchronized, or must have been called from a synchronized method, or from a synchronized statement list in one of the current thread's methods.) If the current thread does not hold a lock on the object that calls wait, an IllegalMonitorStateException will be thrown. 2. It is important to put the test for the condition inside a loop, not just inside an if. Just because the waiting thread is reactivated (by another thread calling notify or notifyAll) does not guarantee that the condition has been satified. 3. When wait is called, the current thread's lock on the object that calls wait is released. When the thread is reactivated (due to another thread calling notify or notifyAll) the lock is reacquired. This means that if another thread has acquired the lock while the first thread was waiting, the first thread may not begin executing right away when its wait is ended by a call to notify or notifyAll. 4. The wait method can throw an InterruptedException, so a call to wait must either be inside a try block that catches that exception, or the enclosing method must include InterruptedException in its throws clause. 5. The wait method can also be called with a time (in milliseconds, with an optional second parameter to specify additional nanoseconds). In this case, the wait ends either when the thread is notified, or when the given time has elapsed, whichever comes first. For example:
6. 7. wait(100); wait(100, 5) // wait at most 100 milliseconds // wait at most 100 milliseconds + 5 nonoseconds
Write two classes named SetVals and GetVals, both of which extend Thread, and both of which work on the same Queue object:
The run method of the SetVals class should get characters (as integers) from the standard input, and store each corresponding Integer in the queue. On end of input (the value read from the standard input is -1), a -1 should be stored in the queue and the run method should return. The run method of the GetVals class should remove each integer value from the queue and, as long as the value is not -1, should write the corresponding character to the standard output. When a -1 is dequeued, the run method should return. Write a main method that performs the following steps: 1. Create a Queue object Q. 2. Create and start a SetVals thread and a GetVals thread, passing Q to both thread constructors. Note: When you run your program, you will probably want to redirect the standard input to be read from a file. solution
Thus, every Thread implements the Runnable interface, since every Thread includes a run method. There are two ways to create new kinds of threads: 1. Define a new class that extends the Thread class.n 2. Define a new class that implements the Runnable interface, and pass an object of that class to a Thread's constructor. An advantage of the second approach is that the new class can be a subclass of any class, not just of the Thread class. Here is a very simple example just to illustrate how to use the second approach to creating threads:
class myThread implements Runnable { public void run() { System.out.println("I'm running!"); } } public class tstRunnable { public static void main(String[] args) { myThread my1 = new myThread(); myThread my2 = new myThread(); new Thread(my1).start(); new Thread(my2).start(); } }
Summary
Java includes the Thread class and the Runnable interface to permit you to write multithreaded programs. In both cases, you need to define a class that includes a run method, and you need to start the thread by calling its start method. While some programming problems are more naturally addressed using multiple threads, it is important to consider how threads will interact to prevent unintended interference or deadlock. Locking of objects (via synchronized methods and statement lists) helps prevent multiple threads from interfering, and methods are also provided to allow threads to communicate with each other.
Test Yourself #2
Stmt executed by first thread 22 23 24 26 27 28 40 Stmts that might be executed simultaneously by second thread 22,23,24,26,27,28,(30,)40 22,26,27,28,(30,)40 22,26,27,28,(30,)40 22,23,24,26,27,28,(30,)40 22,23,24,26,27,28,(30,)40 22,23,24,26,27 22,23,24,26,27
Test Yourself #3
class SetVals extends Thread { private Queue Q; // constructor public SetVals(Queue oneQ) { Q = oneQ; } public void run() {
Integer ch; int c; do { c = GetNextChar(); ch = new Integer(c); Q.Enqueue(ch); } while (c != -1); } int GetNextChar() { int c; try { // read char or eof c = System.in.read(); } catch (java.io.IOException e) { return -1; } return c; } } class GetVals extends Thread { private Queue Q; // constructor public GetVals(Queue oneQ) { Q = oneQ; } public void run() { Integer ch; int c; char oneChar; try { do { ch = (Integer)Q.Dequeue(); c = ch.intValue(); if (c != -1) { oneChar = (char)c; System.out.print(oneChar); } } while (c != -1); } catch (InterruptedException e) { return; } }
public class Queue { class QueueNode { private Object ob; private QueueNode prev; // constructors public QueueNode( Object o ) { ob = o;
prev = null; } public QueueNode( Object o, QueueNode n ) { ob = o; prev = n; } // accessors public Object Ob() { return ob; } public QueueNode GetLink() { return prev; } // modifiers public void SetLink( QueueNode n ) { prev = n; } } private QueueNode head, tail; // constructor public void Queue() { head = tail = null; } public synchronized void Enqueue( Object ob ) { QueueNode n = new QueueNode(ob); if (tail == null) { head = tail = n; } else { tail.SetLink(n); tail = n; } notifyAll(); } public synchronized Object Dequeue() throws InterruptedException { while (head == null) { wait(); } Object ob = head.Ob(); head = head.GetLink(); return ob; } public static void main(String[] args) { Queue Q = new Queue(); SetVals set = new SetVals(Q); GetVals get = new GetVals(Q); get.start(); set.start(); }
JAVA I/O
Contents
Overview Ascii Output Test Yourself #1 Ascii Input o Test Yourself #2 o StreamTokenizer o Test Yourself #3 Summary Answers to Self-Study Questions
Overview
Most Java I/O is defined using streams (random access I/O is also provided via the class java.io.RandomAccessFile, but that will not be discussed here). An input stream brings in information to your program from some source, and an output stream sends information from your program to some destination. The source of an input stream can be the standard input, a file, a pipe, or the program's memory. Similarly, the destination of an output stream can be the standard output, the standard error, a file, a pipe, or memory. (Only the use of standard input/output/error and files are discussed here. See a Java reference book for a more in-depth discussion of Java I/O.) The package java.io provides two class hierarchies that support streams; one for byte streams and the other for character streams. (Don't forget to include import java.io.*; in code that uses these classes!) Only byte streams were included in the original version of Java; character streams were added to handle characters that require more than 1 byte of storage (for example, Chinese characters). Therefore, you should usually use character streams for I/O; however, there are some cases when it is useful to use byte streams. (The standard input, output, and error streams -- System.in, System.out, and System.err -- are all byte streams. Byte streams are also appropriate when reading/writing binary files, but that will not be discussed here.) Which particular class you use for I/O will depend on where you want to read from or write to (e.g., the standard input or output vs. a file), and whether you want to read or write "human-readable" data (i.e., ascii characters) or binary data.
Ascii Output
As we have seen before, the simplest way to produce human-readable (ascii) output is to use System.out.print and System.out.println to write to standard output (these methods are overloaded to handle the output of all primitive types, as well as the output of nonprimitive types using their toString methods). Similarly, you can use System.err.print and System.err.println to write to standard error. To write human-readable output to a file, you should use a PrintWriter. The PrintWriter class includes the following methods:
print: This method is overloaded to write the value of any of the primitive types. It can also be used to write the value of a non-primitive type; in that case, it calls the object's toString method and then writes the string. println: The same as print, but write a newline character, too. write: Given an int argument, write it as a character. flush: If output is being buffered, flush the buffer. close: Flush the buffer and close the output stream. checkError: Return true iff an error has occurred during output.
Note that although standard output and standard error (System.out and System.err) provide the same methods, they are not PrintWriters; they are PrintStreams, which are byte streams rather than character streams. You can convert System.out to a PrintWriter by using System.out as the argument to the PrintWriter constructor (and similarly for System.err): new PrintWriter(System.out) You would need to do this, for example, if you wanted to pass System.out as an argument to a method whose formal parameter was of type PrintWriter. Not every PrintWriter writes to a file; for example, if you convert System.out to a PrintWriter as described above, it will still write to the standard output. To create a PrintWriter that does write to a file, you must first create a FileWriter. The FileWriter class has a constructor that takes a String as its argument (the name of the file), and another constructor that takes a File as its argument. So to create a FileWriter to write to the file output.text, you could use either of the following code fragments:
// fragment 1 FileWriter fw = new FileWriter("output.text"); // fragment 2 File outFile = new File("output.text"); FileWriter fw = new FileWriter(outFile);
The advantage of first creating a File, is that you can test its properties before trying to write to it. The File class includes the following methods:
canWrite: true iff the file can be written exists: true iff the file already exists isDirectory: true iff the file is a directory isFile: true iff the file is a "normal" file (e.g., not a directory)
Efficiency must also be considered when writing to a file. If you create a PrintWriter from a FileWriter, the file will be accessed every time you call the print or println methods. It is more efficient to use buffering, which can be done by creating a BufferedWriter from your FileWriter, and using the BufferedWriter as the argument to the PrintWriter's constructor. For example, here's how you might create a PrintWriter to write to the file output.text:
File outFile; PrintWriter pw; outFile = new File("output.text"); if (! outFile.exists() || (outFile.isFile() && outFile.canWrite())) { pw = new PrintWriter(new BufferedWriter(new FileWriter(outFile))); } else { System.err.println("ERROR: Cannot write to file output.text." ); }
TEST YOURSELF #1 1. Write a program that writes the numbers from 1 to 10, one per line, to the file named on the command line if that file does not already exist, and otherwise gives an error message. 2. Read more about the File class. Write a program that counts the number of "normal" (non-directory files) in the directory named on the command-line, and in all subdirectories, and writes the total to standard output. solution
Ascii Input
Two reasonable ways to read ascii values are: 1. Read each character and parse the input yourself (i.e., put the characters together to form numbers, names, etc). 2. Use a StreamTokenizer to read one token at a time (skipping whitespace). To read one character at a time from standard input, use System.in.read() (which returns the next byte of data as an int, or the value -1 if the end of input has been reached). To read one character at a time from a file, create a BufferedReader from a FileReader
from a File, and use the BufferedReader's read method. For example, the following code counts the number of characters in the input file input.text:
File inFile; BufferedReader br; inFile = new File("input.text"); if (!inFile.exists() || !inFile.canRead()) { System.err.println("ERROR: cannot read file input.text"); } else { int ch, total=0; br = new BufferedReader(new FileReader(inFile)); while ((ch = br.read()) != -1) total++; System.out.println(total + " chars"); br.close();
TEST YOURSELF #2 Write a program that copies the first file named on the command line to the second file named on the command line, removing all comments. A comment starts with two slashes (//) and continues to the end of the current line. For example, if the input file is:
Once upon a time // a typical beginning... there was a girl who liked division like 1/2. // handle the single slash! // This comment must go, so there will be a blank line in the output The end.
Once upon a time there was a girl who liked division like 1/2. The end.
solution
StreamTokenizer
The StreamTokenizer class provides the method nextToken, which skips whitespace, and returns an integer value indicating the type of the next token in the input. It also sets the StreamTokenizer field ttype to the same value. For tokens that are words, the nextToken method sets the sval field of the StreamTokenizer to the actual value of the token, and for tokens that are numbers, the nextToken method sets the nval field of the StreamTokenizer to the actual value of the token. There are four predefined values for token types:
TT_WORD: The token was a word; the String field sval contains the actual string.
TT_NUMBER: The token was a number; the double field nval contains the actual number. TT_EOL: The token was end-of-line. (Note: end-of-line is treated as a token -rather than as whitespace -- only if the method eolIsSignificant has been called with the argument true.) TT_EOF: The end of file has been reached.
If the end of file has not been reached, and the next token is a character that is not a newline, and is not part of a word or a number, the value returned by nextToken is the character itself. The StreamTokenizer class also provides a pushBack method, which causes the next call to nextToken to return the same token again, and a lineno method, which returns the current line number. To create a StreamTokenizer to read from standard input, call the StreamTokenizer constructor function with System.in as its argument. To create a StreamTokenizer to read from a file, call the StreamTokenizer constructor function with a BufferedReader, created using a FileReader associated with the file. (As for FileWriter, you can create a FileReader using a String -- the file name -- or using a File object.) For example, the following code uses a StreamTokenizer to read the file grades.text, which contains a list of student names, each followed by a list of grades. It computes the average for each student, and writes the students' names and their average grades to the file averages.text:
BufferedReader br = new BufferedReader(new FileReader("grades.text")); StreamTokenizer input = new StreamTokenizer(br); PrintWriter output = new PrintWriter(new BufferedWriter(new FileWriter( "averages.text"))); // read info for each student until end-of-file while (input.nextToken() != StreamTokenizer.TT_EOF) { if (input.ttype != StreamTokenizer.TT_WORD) { // bad data in input -- give error msg and quit System.err.println("Bad input on line " + input.lineno()); System.err.println("Student name expected."); System.exit(1); } String name = input.sval; int grade, total = 0; int numGrades = 0; // read all grades for current student while (input.nextToken() == StreamTokenizer.TT_NUMBER) { grade = (int)input.nval; total += grade; numGrades++; }
// put back the current token (which should be EOF or WORD) // to be read again in the main loop input.pushBack(); // write student name and average output.println(name + "\t" + (double)total/numGrades); } // close input and output files br.close(); output.close();
StreamTokenizer methods are provided to define which characters make up words (the default is upper- and lower-case letters and digits), which characters are whitespace (the default is spaces, tabs, and newlines), which characters delimit quoted strings, and which characters start single-line comments (such comments are treated as whitespace). TEST YOURSELF #3 Write a program that reads a file of sums and products (one per line) and writes the values of the expressions to another file. The two file names are to be supplied on the command line. For example, if the input file contains:
23 + 3 .5 * 24 12 + 1.6
solution
Summary
Shown below is part of the inheritance hierarchy related to Java I/O (all of these classes are defined in the package java.io). The classes that implement character streams are shown in red; those that implement byte streams are shown in blue.
The following tables summarize how to construct one class from another, by giving the types of the arguments to the classes's constructor functions. (Note: these tables only provide partial information; in many cases there are other constructors as well.) CHARACTER STREAMS CONSTRUCTOR ARG CLASS TYPES BufferedWriter Writer FileWriter File or String
PrintWriter OutputStream or Writer BufferedReader Reader FileReader File or String OTHER CONSTRUCTOR ARG CLASS TYPES File String StreamTokenizer InputStream or Reader
Question 2. // count the number of "normal" (non-directory) files in the directory named // on the command line and in all subdirectories; write the total to standard // output import java.io.*; class CountFiles { // return the number of "normal" (non-directory) files in the given // directory and in all subdirectories static int Count(File dir) { if (!dir.isDirectory()) { System.err.println("File " + dir.getName() + " is not a directory."); System.exit(1); } int total = 0; String [] files = dir.list(); String thisDir = dir.getPath(); // count non-directory files in this directory and recurse for each // that IS a directory for (int k=0; k<files.length; k++) { File f = new File(thisDir + "/" + files[k]); if (!f.isDirectory()) total++; else total += Count(f); } return total; } public static void main(String[] args) { if (args.length < 1) { System.err.println("Please supply a directory name."); System.exit(1); } File dir = new File(args[0]); if (!dir.isDirectory()) { System.err.println("File " + args[0] + " is not a directory."); } System.out.println("total number of normal files: " + Count(dir)); } }
Test Yourself #2
// Copy from input (1st file named on command line) to output (2nd file named // on command line) removing all comments. A comment starts with a double // slash and continues to end of line. import java.io.*; class RemoveComments { public static void main(String[] args) throws IOException { if (args.length < 2) { System.err.println("Please supply an input file name and an output f ile name."); System.exit(1); } File inFile = new File(args[0]); File outFile = new File(args[1]); BufferedReader br = new BufferedReader(new FileReader(inFile)); BufferedWriter bw = new BufferedWriter(new FileWriter(outFile)); int ch; while ((ch = br.read()) != -1) { if (ch == '/') { // here after seeing one slash; read another character // and decide what to do based on what it is ch = br.read(); switch (ch) { case -1: // end-of-file: write out the slash we just read bw.write('/'); break; case '/': // another slash -- read (and ignore) chars until // or eol ch = br.read(); while (ch != -1 && ch != '\n') ch = br.read(); if (ch == '\n') bw.write(ch); break; default: bw.write('/'); bw.write(ch); }
eof
} br.close(); bw.close(); } }
} else bw.write(ch);
Test Yourself #3
// Read a file of sums and products (one per line) and write the values of the // expressions to another file. The two file names are expected to be given // as command-line arguments import java.io.*; class Arithmetic { public static void main(String[] args) throws IOException { if (args.length < 2) { System.err.println("Please supply an input file name and an output f ile name."); System.exit(1); } BufferedReader br = new BufferedReader(new FileReader(args[0])); StreamTokenizer input = new StreamTokenizer(br); PrintWriter output = new PrintWriter(new BufferedWriter(new FileWriter(a rgs[1]))); // read sums and products while (input.nextToken() != StreamTokenizer.TT_EOF) { if (input.ttype != StreamTokenizer.TT_NUMBER) { // bad data in input -- give error msg and quit System.err.println("Bad input on line " + input.lineno()); System.err.println("Should start with a number."); System.exit(1); } double result, arg1 = input.nval, arg2; int val = input.nextToken(); switch (val) { case '+': case '*': if (input.nextToken() != StreamTokenizer.TT_NUMBER) { System.err.println("Bad input on line " + input.lineno()); System.err.println("A number should follow + or *."); System.exit(1); } arg2 = input.nval; if (val == '+') result = arg1 + arg2; else result = arg1 * arg2; output.println(result); break; default: System.err.println("Bad input on line " + input.lineno()); System.err.println("A + or a * should follow the first number"); System.exit(1);
JAVA PACKAGES
Contents
Overview Examples How the Java Compiler Finds Files The CLASSPATH environment variable
Overview
Every class is part of some package. All classes in a file are part of the same package. You can specify the package using a package declaration: package name ; as the first (non-comment) line in the file.
Multiple files can specify the same package name. If no package is specified, the classes in the file go into a special unnamed package (the same unnamed package for all files). If package name is specified, the file must be in a subdirectory called name (i.e., the directory name must match the package name). You can access public classes in another (named) package using: package-name.class-name You can access the public fields and methods of such classes using: package-name.class-name.field-or-method-name You can avoid having to include the package-name using: import package-name.*; or
import package-name.class-name; at the beginning of the file (after the package declaration). The former imports all of the classes in the package, and the second imports just the named class. You must still use: class-name to access the classes in the packages, and class-name.field-or-method-name to access the fields and methods of the class; the only thing you can leave off is the package name.
Examples
Assume that you are working in a directory called Javadir, and that you create four files, whose contents are shown below.
file 1 package ListPkg; public class List { ... } class ListNode {...} file 2 package ListPkg; public class NoNextItemException { ... } file 3 public class Test { ... } class Utils { ... } file 4 class Test2 { ... }
Here are the directories and file names you must use:
File 1 must be in a subdirectory named ListPkg, in a file named List.java. File 2 must also be in the ListPkg subdirectory, in a file named NoNextItemException.java. File 3 must be in a file named Test.java (in the Javadir directory). File 4 can be in any .java file (in the Javadir directory).
And here are the classes that can be accessed by the code in each file:
Files 1 and 2: o The code in the first two files (ListPkg/List.java and ListPkg/NoNextItemException.java) can access the classes defined in the
same package (List, ListNode, and NoNextItemException). (No access was specified for those classes, so they get the default, package access.) o The code in files 1 and 2 cannot access class Test, even though it is a public class. The problem is that Test is in an unnamed package, so the code in the ListPkg package has no way to import that package, or to name class Test. o The code in files 1 and 2 cannot access classes Utils and Test2, because they have default (package) access, and are in a different package. Files 3 and 4: o The code in file 3 (Test.java) can access classes ListPkg.List, ListPkg.NoNextItemException, Test, Utils, and Test2 (the first two because they are public classes in a named package, and the last three because they are in the same, unnamed package, and have either public or package access). Note however, that if the code in Test.java uses the class Test2, and that class is not in a file called Test2.java, then the file that contains class Test2 must be compiled first, or else the class will not be found. o The code in file 4 (the file that contains class Test2) can access the same classes as the code in file 3 (Test.java).
Directory/FileName ListPkg/List.java
Can Access List, ListNode, NoNextItemException List, ListNode, NoNextItemException ListPkg.List, ListPkg.NoNextItemException, Test, Utils, Test2 ListPkg.List, ListPkg.NoNextItemException, Test, Utils, Test2
ListPkg/NoNextItemException.java
Test.java
any-name.java
the name of the class the names of imported packages (if any)
to try to locate the class definition. For example, assume that you are working in directory Javadir, which contains one file named Test.java:
import ListPkg.*; public class Test { List L; ... }
Since List is not defined in Test.Java, and since there is no file List.java in the current directory, the compiler will look for List.java in the ListPkg subdirectory (since Test.java imports the ListPkg package). Now suppose that the ListPkg subdirectory contains two files: List.java and ListNode.java, both part of the ListPkg package. Also assume that List.java uses the ListNode class defined in ListNode.java. If you try to compile just List.java in the ListPkg subdirectory, you will get an error, because the compiler will try to find the file ListNode.java in a "ListPkg" subdirectory of the current directory, rather than looking in the current directory itself. There are (at least) three ways to solve this problem: 1. Always compile a package from the parent directory. For example, compile List.java from Javadir, rather than from Javadir/ListPkg; in the Javadir directory, type: javac ListPkg/List.java 2. Always compile all files in a package at the same time; for example, in the directory Javadir/ListPkg type: javac *.java 3. Make a circular link from the package subdirectory to itself; for example, in the directory Javadir/ListPkg type: ln -s . ListPkg
Including the dot and the colon before the directory tells the compiler also to look in the directory in which the compile is being done. Note that you should set the CLASSPATH JAVA vs C++
Contents
Simple Example Java Program o Test Yourself #1 Files o Test Yourself #2 Java Types o C++ Arrays vs Java Arrays Test Yourself #3 Test Yourself #4 o C++ Classes vs Java Classes o Aliasing Problems in Java Test Yourself #5 o Type Conversion Test Yourself #6 Answers to Self-Study Questions
In Java, every variable, constant, and function (including main) must be inside some class. Here's a simple example program:
class Test { public static void main( String [] args ) { System.out.println("Hello world!"); } }
Things to note: 1. There is no final semi-colon at the end of the class definition. 2. Function main is a member of class Test. 3. In general, main must: o Be inside some class (there can be more than one main function -- there can even be one in every class!) o Be public static void. o Have one argument: an array of String. This array contains the commandline arguments. You can use args.length to determine the number of arguments (the number of Strings in the array). 4. To write to standard output, you can use either of the following:
5. 6.
The former prints the given expression followed by a newline, while the latter just prints the given expression. Like the C++ << operator, these functions can be used to print values of any type. For example, all of the following are fine:
System.out.print("hello"); System.out.print(16); System.out.print(5.5 * .2); // print a String // print an integer // print a floating-point number
The + operator can be useful when printing. It is overloaded to work on Strings as follows: If either operand is a String, it
1.
converts the other operand to a String (if necessary) 2. creates a new String by concatenating both operands Example
int x = 20, y = 10; System.out.println("x: " + x + "\ny: " + y);
This is because the argument to println is an expression of the form: op1 + op2 + op3 + op4 The only operator is +, so the expression is evaluated left-to-right (if there were another operator with higher precedence, the sub-expression involving that operator would be evaluated first). The leftmost sub-expression is: "x: " + x One operand (the left one) is a String, so the other operand is converted from an int to a String, and the two strings are concatenated, producing the value:
"x: 20" Evaluation of the argument to println continues, producing as the final value the String shown above (note that \n means the newline character):
x: 20 y: 10
solution
Object Files: .o
created by: contain the compiler, when called w/ -c flag; for example: g++ -c main.cc compiles main.cc creating main.o : object code (not executable) source code is compiled, but not linked/loaded
Executable Files
created by: contain : loaded. just by typing name : the compiler (no -c flag) executable code Code is compiled if necessary, then linked and These are the files that you can actually run, the name of the file. default = a.out any other name is possible via the -o flag; for example: g++ main.o -o test creates an executable named "test"
A Java programmer deals with source files and bytecode files (no executable files): Source Files: .java
created by : you (the programmer) contain : Java source code (one or more classes per file) restrictions : (1) each source file can contain at most one public class (2) if there is a public class, then the class name and file name must match Examples If a source file contains the following: public class Test { ... } class Foo { ... } class Bar {... } then it must be in a file named Test.java If a source file contains the following: class Test { ... } class Foo { ... } class Bar {... } then it can be in any ".java" file
Small digression:
Every function must be part of a class. Every class is part of a package (packages are discussed in a later set of notes.). A public class can be used in any package. A non-public class can only be used in its own package.
the Java interpreter names : for each class in a source file (both public and nonpublic classes), the compiler creates one ".class" file, where the file name is the same as the class name Example If a source file contains the following: public class Test { ... } class Foo { ... } class Bar {... } then after compiling you will have three files: Test.class Foo.class Bar.class
Here's how to compile and run the example "hello world" program given above, assuming that it is in a file named "tst.java": 1. to compile the source code, type: javac tst.java 2. to run the interpreter using the name of the class whose main function you want to run, type: java Test Remember, when compiling a program, you type the full file name, including the ".java" extension; when running a program, you just type the name of the class whose main function you want to run. TEST YOURSELF #2 Write a complete Java program that uses a loop to sum the numbers from 1 to 10 and prints the result like this:
The sum is: xxx
Note: Use variable declarations, and a for or while loop with the same syntax as in C++. Make sure that you are able to compile and execute your program! solution
Java Types
In Java, there are two "categories" of types: primitive types and reference types: Primitive Types boolean same as bool in C++ char holds one character
8-bit signed integer 16-bit signed integer 32-bit signed integer 64-bit signed integer floating-point number double precision floating-point number Reference Types arrays classes
Notes: 1. no struct, union, enum, unsigned, typedef 2. arrays and classes are really pointers!!
In C++, when you declare an array, storage for the array is allocated. In Java, when you declare an array, you are really only declaring a pointer to an array; storage for the array itself is not allocated until you use "new":
C++ int A[10]; // A is an array of length 10 A[0] = 5; // set the 1st element of array A JAVA int [] A; // A is a pointer to an array A = new int [10]; // now A points to an array of length 10 A[0] = 5; // set the 1st element of the array pointed to by A
In both C++ and Java you can initialize an array using values in curly braces. Here's example Java code:
int [] A = {1, 222, 0}; length 3 222, and 0 // A points to an array of // containing the values 1,
In Java, a default initial value is assigned to each element of a newly allocated array if no initial value is specified. The default value depends on the type of the array element: Type Value boolean false char '\u0000'
byte, int, short, int, long, float, double 0 any pointer null In Java, an out-of-bounds array index always causes a runtime error. In Java, you can determine the current length of an array (at runtime) using ".length":
int [] A = new int[10]; ... A.length ... evaluates to 10 A = new int[20]; ... A.length ... // this expression
// now it evaluates to 20
TEST YOURSELF #3 Write a Java function called NonZeros, using the header given below. NonZeros should create and return an array of integers containing all of the non-zero values in its parameter A, in the same order that they occur in A.
public static int[] NonZeros( int [] A )
Write a complete Java program that includes a main function as well as the NonZeros function. The main function should test NonZeros by creating several arrays, and calling NonZeros with each array. It should print the array it passes to NonZeros as well as the returned array. So for example, when you run your program, your output might look like this (if your NonZeros function is implemented correctly):
passing passing passing passing [0,1,2,3,2] got back [1,2,3,2] [0,0] got back [] [22,0,-5,0,126] got back [22,-5,126] [1,0] got back [1]
solution
In Java, you can copy an array using the arraycopy function. Like the output function println, arraycopy is provided in java.lang.System, so you must use the name System.arraycopy. The function has five parameters: src: the source array (the array from which to copy) srcPos: the starting position in the source array dst: the destination array (the array into which to copy) dstPos: the starting position in the destination array count: how many values to copy
Here's an example:
int [] A, B; A = new int[10]; -- code to put values into A -B = new int[5]; System.arraycopy(A, 0, B, 0, 5) // copies first 5 values from A to B System.arraycopy(A, 9, B, 4, 1) // copies last value from A into // last element of B
Note that the destination array must already exist (i.e., new must already have been used to allocate space for that array), and it must be large enough to hold all copied values (otherwise you get a runtime error). Furthermore, the source array must have enough values to copy (i.e., the length of the source array must be at least srcPos+count). Also, for arrays of primitive types, the types of the source and destination arrays must be the same (so for example, you cannot copy from an array of int to an array of double or vice versa). For arrays of non-primitive types, System.arraycopy(A, j, B, k, n) is OK if the assignment B[0] = A[0] would be OK. The arraycopy function also works when the source and destination arrays are the same array; so for example, you can use it to "shift" the values in an array:
int [] A = {0, 1, 2, 3, 4}; System.arraycopy(A, 0, A, 1, 4);
As in C++, Java arrays can be multidimensional. For example, a 2-dimensional array is an array of arrays. Two-dimensional arrays need not be rectangular. Each row can be a different length. Here's an example:
int [][] A; A = new int[5][]; yet A[0] = new int [1]; A[1] = new int [2]; A[2] = new int [3]; A[3] = new int [5]; A[4] = new int [5];
// A is a two-dimensional array // A now has 5 rows, but no columns // // // // // A's A's A's A's A's first row has 1 column second row has 2 columns third row has 3 columns fourth row has 5 columns fifth row also has 5 columns
TEST YOURSELF #4 For each of the following code fragments, fill in the number of the picture that best illustrates the value of A after the code executes, or fill in "error" to indicate that executing the code causes a runtime error. (In the pictures, a diagonal line indicates a null pointer.) Code Corresponding Picture or Error
int [] A; int [] A = new int [4]; int [][] A = new int[4][3]; int [][] A = new int[4][]; A[1] = new int[4]; A[3] = new int[2]; int [] A = new int[4]; int [] B = {0,1,2,3,4,5,6,7,8,9}; System.arraycopy(B,2,A,0,4); int [] A = new int[4]; int [] B = {2,3,4}; System.arraycopy(B,0,A,0,4); int [] A = new int[4]; int [] B = {0,1,2,3,4,5,6,7,8,9}; System.arraycopy(B,8,A,0,4); int [] A = {1,1,1,1}; int [] B = {2,2,2}; System.arraycopy(A,0,B,1,2); System.arraycopy(B,0,A,0,3); int [] A = new int[4]; int [] B = {0,1,2,3,4,5,6,7,8,9}; System.arraycopy(B,0,A,0,10); int [][] A = new int[4][3]; int [] B = {1,2,3,4,5,6,7,8,9,10}; System.arraycopy(B,0,A[0],0,3); System.arraycopy(B,1,A[1],0,3); System.arraycopy(B,2,A[2],0,3); System.arraycopy(B,3,A[3],0,3); (1) PICTURES (2) (3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
(12)
(13)
(14)
(15)
solution
In C++, when you declare a variable whose type is a class, storage is allocated for an object of that class, and the class's constructor function is called to initialize that instance of the class. In Java, you are really declaring a pointer to a class
object; no storage is allocated for the class object, and no constructor function is called until you use "new". Assume that you have defined a List class as follows:
class List { public void AddToEnd(...) { ...} ... } C++
List L; // L is a List; the List constructor function is called to // initialize L. List *p; // p is a pointer to a List; // no list object exists yet, no constructor function has // been called p = new List; // now storage for a List has been allocated // and the constructor function has been called L.AddToEnd(...) // call L's AddToEnd function p->AddToEnd(...) // call the AddToEnd function of the List pointed to by p
JAVA List L; // L is a pointer to a List; no List object exists yet L = new List(); // now storage for a List has been allocated // and the constructor function has been called; // note that you must use parentheses even when you are not // passing any arguments to the constructor function L.AddToEnd(...) // no -> operator in Java -- just use .
B: | -|-----> | | | +--+ +---+---+ A[0] = 5; +--+ +---+---+---+ A: | -|-----> | 5 | | | +--+ +---+---+---+ +--+ +---+---+---+ A: | -|-----> | 5 | | | +--+ +---+---+---+ ^ +--+ | B: | -|-------+ +--+ +--+ ** B[0] = 2; value of A[0] too! +---+---+---+ *** NOTE | | the
B = A;
+---+---+---+ changed,
Problem 2: In Java, all parameters are passed by value, but for arrays and classes the actual parameter is really a pointer, so changing
inside the function does change the actual parameter's element or field. For example:
void f( int [] A ) { A[0] = 10; // change an element of parameter A A = null; // change A itself } void g() { int [] B = new int [3]; B[0] = 5; f(B); *** B is not null here, because B itself was passed by value *** however, B[0] is now 10, because function f changed the first element *** of the array }
In C++, similar problems can arise when a class that has pointer data members is passed by value. This problem is addressed by the use of copy constructors, which can be defined to make copies of the values pointed to, rather than just making copies of the
pointers. In Java, the solution is to use the arraycopy operation, or to use a class's clone operation. Cloning will be discussed later. TEST YOURSELF #5 For each of the following Java code fragments, say whether it causes a compile-time error, a run-time error, or no error. If there is an error, explain why.
1. int A[5]; 2. int [] A, B; B = 0; 3. int [] A = {1,2,3}; int [] B; B = A; 4. int [] A; A[0] = 0; 5. int [] A = new int[20]; int [] B = new int[10]; A = B; A[15] = 0;
solution
Type Conversion
Java is much more limited than C++ in the type conversions that are allowed. Here we discuss conversions among primitive types. Conversions among class objects will be discussed later. Booleans cannot be converted to other types. For the other primitive types (char, byte, short, int, long, float, and double), there are two kinds of conversion: implicit and explicit. Implicit conversions: An implicit conversion means that a value of one type is changed to a value of another type without any special directive from the programmer. A char can be implicitly converted to an int, a long, a float, or a double. For example, the following will compile without error:
char c = 'a'; int k = c; long x = c; float y = c; double d = c;
For the other (numeric) primitive types, the basic rule is that implicit conversions can be done from one type to another if the range of values of the first type is a subset of the range of values of the second type. For example, a byte can be converted to a short, int, long or float; a short can be converted to an int, long, float, or double, etc.
Explicit conversions: Explicit conversions are done via casting: the name of the type to which you want a value converted is given, in parentheses, in front of the value. For example, the following code uses casts to convert a value of type double to a value of type int, and to convert a value of type double to a value of type short:
double d = 5.6; int k = (int)d; short s = (short)(d * 2.0);
Casting can be used to convert among any of the primitive types except boolean. Note, however, that casting can lose information; for example, floating-point values are truncated when they are cast to integers (e.g., the value of k in the code fragment given above is 5), and casting among integer types can produce wildly different values (because upper bits, possibly including the sign bit, are lost). So use explicit casting carefully! TEST YOURSELF #6 Fill in the table below as follows:
If the declaration will compile as is, put a check in the second column, and write the value of the declared variable in the last column. If the declaration will not compile as is, but can be made to compile by adding an explicit cast, rewrite the declaration with the correct explicit cast in the third column, and write the value of the declared variable in the last column. If the declaration will not compile, and cannot be fixed by adding an explicit cast, put a check in the fourth column.
The first row has been filled in as an example. Declaration Correct Rewrite with cast double d = 5; X int k = 5.6; long x = 5.4; short n = 99999; int b = true; char c = 97; short s = -10.0; solution
Never correct
==> ==>
Test Yourself #2
class Test { public static void main( String[] args ) { int sum = 0; for (int k=1; k<=10; k++) sum += k; System.out.println("The sum is: " + sum); } }
Test Yourself #3
class Test { public static int[] NonZeros( int[] A ) { // count # nonzero values int nonz = 0; for (int k=0; k<A.length; k++) if (A[k] != 0) nonz++; // allocate and fill new array int[] result = new int[nonz]; int j = 0; // index of next element of new array to fill in for (int k=0; k<A.length; k++) { if (A[k] != 0) { result[j] = A[k]; j++; } } return result;
public static void PrintArray( int[] A ) { System.out.print("["); for (int k=0; k<A.length; k++) { System.out.print(A[k]); if (k < (A.length - 1)) System.out.print(" "); } System.out.print("]"); } public static void main(String[] args) { int[] A = {0,1,2,3,2}; System.out.print("passing "); PrintArray(A); A = NonZeros(A); System.out.print(" got back "); PrintArray(A); System.out.println(); int[] B = {0,0}; System.out.print("passing "); PrintArray(B); A = NonZeros(B);
System.out.print(" got back "); PrintArray(A); System.out.println(); int[] C = {1,0}; System.out.print("passing "); PrintArray(C); A = NonZeros(C); System.out.print(" got back "); PrintArray(A); System.out.println();
} }
Test Yourself #4
CODE ---int [] A; int [] A = new int[4]; int [][] A = new int[4][3]; int [][] A = new int[4][]; A[1] = new int[4]; A[3] = new int[2]; int [] A = new int[4]; int [] B = {0,1,2,3,4,5,6,7,8,9}; System.arraycopy(B,2,A,0,4); int [] A = new int[4]; int [] B = {2,3,4}; System.arraycopy(B,0,A,0,4); int [] A = new int[4]; int [] B = {0,1,2,3,4,5,6,7,8,9}; System.arraycopy(B,8,A,0,4); int [] A = {1,1,1,1}; int [] B = {2,2,2}; System.arraycopy(A,0,B,1,2); System.arraycopy(B,0,A,0,3); int [] A = new int[4]; int [] B = {0,1,2,3,4,5,6,7,8,9}; System.arraycopy(B,0,A,0,10); int [][] A = new int[4][3]; int [] B = {1,2,3,4,5,6,7,8,9,10}; System.arraycopy(B,0,A[0],0,3); System.arraycopy(B,1,A[1],0,3); System.arraycopy(B,2,A[2],0,3); System.arraycopy(B,3,A[3],0,3); CORRESPONDING PICTURE --------------------9 6 3
14
error
error
12
error
10
Test Yourself #5
int A[5]; Compile-time error: Can't specify array dimension in a declaration. This is C/C++ syntax. int [] A, B; B = 0; Compile-time error: Incompatible type for =. Can't convert int to int[]. B is an array reference, not an int, and 0 is not equiv to null as in C/C++. int [] A = {1,2,3}; int [] B; B = A; No errors. int [] A; A[0] = 0; Compile-time error: Variable A may not have been initialized. The array was never allocated. int [] A = new int[20]; int [] B = new int[10]; A = B; A[15] = 0; Runtime error: ArrayIndexOutOfBoundsException: 15 A now references the same array as B, which only has length 10
Test Yourself #6
Declaration Correct Rewrite with cast Never correct Variable's value ----------------------------------------------------------------------------double d = 5; X 5.0 int k = 5.6; int k = (int) 5.6 5 long x = 5.4; long x = (long) 5.4 5 short n = 99999; short n = (short) 99999 -31073 int b = true; X char c = 97; X 'a' short s = -10.0; short s = (short) -10.0 -10
variable to the parent of the "ListPkg" subdirectory, not to the ListPkg subdirectory itself.
Console Input o Integer Input o Java Exceptions Console Output o Streams File Input o Parsing Strings File Output
Introduction
Input is any information that is needed by your program to complete its execution. There are many forms that program input may take. Some programs use graphical components like a popup dialog box to accept and return the character string that is typed by the user. You are certainly familiar with programs that are controlled simply by clicking the mouse in a specific area of the screen. Still other programs, like word processing programs, get some of their input from a file that is stored on the computer's floppy or hard disk drive. Some programs, like web browsers, get their data from a network connection, while others get data from devices like scanners, digital cameras and microphones. The possibilities are limited only by computer scientists' imagination. Output is any information that the program must convey to the user. The information you see on your computer screen is being output by one or more programs that are currently running on your computer. When you decide to print a document, a program is told to send some output to the printer. Any sound that your computer makes is because some program sent output to the speakers on your computer. The possibilities for program output are also limited only by our imaginations. Throughout the semester, we have been performing input and through the use of a Scannerobject connected to System.in and output through the use of a PrintStream object (System.out). There are several potential error conditions that may occur when a program needs to get input from the user. If a user enters letters when a program is expecting numbers, an exception (error) will occur if the program assumes incorrectly that it has a valid integer to use in calculations. Programs must be written to survive bad input by the user. One way to do this is to ensure that only valid input is accepted. Standard Java classes do not ensure that only valid input is accepted. They are designed to be very flexible to support the wide variety of input and output options available now and in the future. This flexibility comes at the cost of increased complexity.
Console Input
The console window is the window that is automatically launched when you run a program from within Eclipse. Console input is any input that is entered in the console window instead of typing it into a field or dialog box that pops up in a window. For example, when the nextLine method is called, the program waits for the user to enter information. Whatever the user types is returned to the program in the form of a String object. There are many ways to get information from the user. In many cases, the user must be told that they should enter some information. This is known as prompting the user. A user prompt is a line of text that is output to the user that explains what information they should input next. We can prompt the user by displaying information in a dialog box, a program frame, or even the console window. All programs that require the user to input information while the program is running must prompt the user for that information in some manner. When a program is waiting for input at the console, there is sometimes a blinking cursor in the console window indicating that the user should type some information. But, this is not always the case. The user will only know what information to type if the program describes that information in the form of a user prompt. (See Console Output for more information on user prompts.) The use of several of the Java I/O classes may be required to successfully receive input that is typed by the user. The java.io package contains most, if not all, of the classes you will need to use. The java.util package also contains some classes that are useful for input and output. Don't worry, you won't need to use all 50+ classes. But, you will need to learn about and use at least a couple of them. Either use the fully qualified name shown or import the specified package to use either of these classes.
input device and the computer or program. java.util.Scanner - used to read the input available from an InputStream object.
The Scanner class has a method called nextLine that returns a line of text as typed by the user. There are two available constructors for creating a Scanner object. For console input, we will use the one that requires only one argument, an instance of an InputStream object. We will use the System.in object as our InputStream object and then use that object to create an instance of the Scanner class.
1. 2. 3. 4.
Use the System.in object to create a Scanner object. Display a prompt to the user for the desired data. Use the Scanner object to read a line of text from the user. Do something interesting with the input received from the user.
Would you like to see some code? I thought so. Here it is:
// 1. Create a Scanner using the InputStream available. Scanner scanner = new Scanner( System.in ); // 2. Don't forget to prompt the user System.out.print( "Type some data for the program: " ); // 3. Use the Scanner to read a line of text from the user. String input = scanner.nextLine(); // 4. Now, you can do anything with the input string that you need to. // Like, output it to the user. System.out.println( "input = " + input );
That's a lot of code for one line of input. Is there a shorter way?
Yes. Subtle bugs are introduced into your program when you connect more than one Scannerobject to the single InputStream object System.in. So, Java programmers create only one instance of the Scanner connected to System.in for use throughout their entire program. All keyboard operations will use that single shared Scanner object. The code below is placed with other class data members and is not inside any method.
// 1. Create a single shared Scanner object for keyboard input. // This must be done in only one class of your program. // All keyboard input must be handled through that one class. private static Scanner scanner = new Scanner( System.in );
/** A Java program that demonstrates console based input and output. */
public class MyConsoleIO { // Create a single shared Scanner for keyboard input private static Scanner scanner = new Scanner( System.in ); // Program execution starts here public static void main ( String [] args ) { // Prompt the user System.out.print( "Type some data for the program: " ); // Read a line of text from the user. String input = scanner.nextLine(); // Display the input back to the user. System.out.println( "input = " + input ); } // end main method } // end MyConsoleIO class
Integer input
Getting data from the user isn't so hard after all. But, it does require some additional work. There may be even more work to do, if you want to get an integer (or other numeric value) from the user. If the user types in "123", that will be still be returned as a String object by the nextLine method of Scanner. You will need to parse (convert) the String object into an int value if you wish to store it in an int variable or data member. Here's one way to do this: 1. Get a String of characters that is in an integer format, e.g., "123".
String input = scanner.nextLine(); above. // from console input example
2. Use the Integer class to parse the string of characters into an integer.
int number = Integer.parseInt( input ); into an int value // converts a String
The Integer class contains conversion methods for changing String data into int values and vice versa. The Integer class is one of several wrapper classes that are defined in the standard Java API. Wrapper classes have class methods for parsing and are also used when you need to store a primitive value as an object. In our case, we needed to convert a String object into an int value. The parseInt method of the Integer class performs this action. Be sure to review the Integer class javadoc for more information about this and other methods of the Integer class. Here's another way to do this:
As you can see, the Scanner class contains a method named nextInt that returns the next input data available as an int value, that is, if the next input in the input stream is a valid integer format. If the next input is not a valid integer format, an InputMismatchException is thrown. You may be wondering why are there two ways to read integers from the user. This flexibility allows you, the programmer, the option of which form is best for your program. The correct form to use depends upon what you wish to do with the rest of the line that remains after an integer is read from the current input line. The first example should be used if the only thing available on the current input line is the integer value. The second example should be used if there are multiple data values to read on the same input line.
Console Output
We have used System.out.print(...) and System.out.println(...) statements for displaying simple text messages to the user. This is an important output alternative, since graphic user interface (GUI) objects are not readily available in some programming environments. You may of course write your own GUI classes if they're not available, but that is beyond the scope of this course. It is much more likely that you will simply use the available output options of the programming environment that you are working in. Most programming languages have the ability to display a string of characters to the screen or some other standard display device. We call this console output because the string of characters appears in a console window. The System.out object is an instance of the PrintStream class, which is a type of OutputStream.
Streams
A stream object is used to store information needed to connect a computer program to an input or output device. There is a PrintStream object that adds functionality to output
streams. The PrintStream class extends the OutputStream class and contains definitions for all of the versions of the print and println methods that we use to display information like user prompts or results of calculations. Console output in Java is very easy because the print and println methods will work with any type of data. There is a separate version of each of these methods in the PrintStream class so that this is possible. There is also a version of the print and println methods that will print information for any object. But, how did we get a PrintStream object in the first place? The java.lang.System class creates three different I/O streams automatically for us when our application begins execution. Each of these streams is public and static so that we can access them directly without having to create an instance of the System class. We have already used the InputStream object named System.in in the discussion on console input. The other two stream objects are named System.out and System.err. Each of these objects is an instance of the PrintStream class and is available for use in displaying information to the computer screen. For example, if the following variables are defined,
int x = 3; double rate = 5.5; boolean playing = true; String phrase = "The winner is ";
We can also print other types of data, including other objects, using the print and println methods. The following code fragment shows the command syntax for printing a Wanderer object. The class name Wanderer is used as an example. You can replace Wanderer with any class name that is defined in your program.
Wanderer wanderer1 = new Wanderer( "Wilma", Color.orange ); System.out.println( wanderer1 );
In this case, the program prints out some cryptic information about the Wanderer object. It is the class name, an @ symbol and the hexidecimal representation of the hashcode. The output looks like the following for the first Wanderer object I created.
Wanderer@13fac
Each object created has its own hashcode that can be used to distinguish it from other objects. However, hashcodes are not very readable for most users, so there is a way for the programmer to redefine what information is printed. The information that is displayed when an object is printed using the print method is defined by an instance method named toString. Every class has a version of the toString method already defined that returns the information as described above. All classes inherit this method from the java.lang.Object class. To redefine the toString method, you override the default version by defining a method with the same visibility and method signature as the inherited version of the method. The toString method of my Wanderer class can be overridden as follows:
/** * Returns a string representing this * instance of the Wanderer class. */ public String toString() { String coords = "(" + myLoc.getX() + "," + myLoc.getY() + ")"; return myName + " is at " + coords; }
Now, when the print or println method is used to print a Wanderer object, the new version of the toString method will be called instead of the version defined in the Object class. The String that is printed by the println method will look something like this:
Wilma is at (11,3)
Each class can (and should!) override the toString method to return a String of characters that is more descriptive of the object than the default version provided by the Object class. This method can be called by any method that needs a String that describes the object. The print or println methods of the PrintStream class should be adequate to produce most screen output. The only other type of output we will cover in this course is file output. Other output devices require more specialized output objects and won't be covered in this course.
File Input
As mentioned above, data can be read from a variety of different sources, including data files stored on devices such as hard disk drives and floppy drives. The file will need to be opened and a Scanner will be attached to the file object. The process is actually very similar to the console input example above. The difference is that the Scanner will be
created from a File object instead of an InputStream object. This section focuses on inputting characters rather than data bytes. The discussion and examples in this document explain the procedure when the file to be read is a text file that has valid ASCII characters to represent the data. There is one new Java I/O class to learn about:
java.io.File
You will also have to inform the compiler that you are calling a method that may cause a checked exception to occur. Creating a Scanner object from a File object may cause a FileNotFoundException. This is a checked exception, so be sure to catch the exception or add the throws FileNotFoundException clause to the method header. (Since FileNotFoundException is a subclass of IOException, you could put throws IOException in the method header instead of throws FileNotFoundException.) See the Java Exceptions web page for more information on handling checked exceptions. Here's a code fragment to illustrate reading a text file:
System.out.print( "Enter the filename: " ); for a file name String fileName = scanner.nextLine(); from the user File file = new File( fileName ); object if ( file.exists() ) file exists { create a the file // Prompt the user // get a file name // create a File // check that the // before trying to // Scanner to read
// Create a Scanner from the file. // This statement can cause a FileNotFoundException. Scanner inFile = new Scanner( file );
// For each line in the file, read in the line and display it with the line number int lineNum = 0; // Use the results of calling the hasNext method to // determine if you are at the end of the file before // reading the next line of the file. while ( inFile.hasNext() ) { line = inFile.nextLine(); // read the next line // Output the line read to the screen for the user System.out.println( ++lineNum + ": " + line ); } // When we're done reading the file,
Don't forget to add import java.io.* (as well as java.util.* for the Scanner class). Add the throws IOException clause to any method that creates a File object. See the Checked Exceptions section of the Java Exceptions page for more information. Do not assume that the nextLine will return valid data. You should test the return value of calling the hasNext method before trying to use that data. Close the File object when you're done reading data from the file. This can be done by calling the close method of the Scanner object. Java Exceptions describes an alternative file input example that catches exceptions instead of using the throws IOException clause. If you do catch the exception, do not add the throws IOException clause to the method header.
Strings in Java can be parsed using the split method of the String class. ( StringTokenizer can also be used to parse a string; we won't be covering it here). This just gives a brief overview (and some examples) of some of the common (and easiest) ways to use the split method; for more detailed information see the Java API documentation for split. Issues to consider when parsing a string:
What are the delimiters (and how many are there)? How should consecutive delimiters be treated?
Example 1
You want to divide up a phrase into words where spaces are used to separate words. For example
the music made it hard to concentrate
In this case, you have just one delimiter (space) and consecutive delimiters (i.e., several spaces in a row) should be treated as one delimiter. To parse this string in Java, you do
String phrase = "the music made it hard concentrate"; String delims = "[ ]+"; String[] tokens = phrase.split(delims); to
Note that
the general form for specifying the delimiters that we will use is "[delim_characters]+" . (This form is a kind of regular expression. You don't need to know about regular expressions - just use the template shown here.) The plus sign (+) is used to indicate that consecutive delimiters should be treated as one. the split method returns an array containing the tokens (as strings). To see what the tokens are, just use a for loop:
for (int i = 0; i < tokens.length; i++) System.out.println(tokens[i]);
You should find that there are seven tokens: "the", "music", "made", "it", "hard", "to", "concentrate"
Example 2
Suppose each string contains an employee's last name, first name, employee ID#, and the number of hours worked for each day of the week, separated by commas. So
Smith,Katie,3014,,8.25,6.5,,,10.75,8.5
represents an employee named Katie Smith, whose ID was 3014, and who worked 8.25 hours on Monday, 6.5 hours on Tuesday, 10.75 hours on Friday, and 8.5 hours on Saturday. In this case, you have just one delimiter (comma) and consecutive delimiters (i.e., more than one comma in a row) should not be treated as one. To parse this string, you do
String employee = "Smith,Katie,3014,,8.25,6.5,,,10.75,8.5"; String delims = "[,]"; String[] tokens = employee.split(delims);
After this code executes, the tokens array will contain ten strings (note the empty strings): "Smith", "Katie", "3014", "", "8.25", "6.5", "", "", "10.75", "8.5" There is one small wrinkle to be aware of (regardless of how consecutive delimiters are handled): if the string starts with one (or more) delimiters, then the first token will be the empty string ("").
Example 3
Suppose you have a string containing several English sentences that uses only commas, periods, question marks, and exclamation points as punctuation. You wish to extract the individual words in the string (excluding the punctuation). In this situation you have several delimiters (the punctuation marks as well as spaces) and you want to treat consecutive delimiters as one
String str = "This is a sentence. This is a question, right? Yes! It is."; String delims = "[ .,?!]+"; String[] tokens = str.split(delims);
All you had to do was list all the delimiter characters inside the square brackets ( [ ] ).
Example 4
Suppose you are representing arithmetic expressions using strings and wish to parse out the operands (that is, use the arithmetic operators as delimiters). The arithmetic operators that are allowed are addition (+), subtraction (-), multiplication (*), division (/), and exponentiation (^) and parentheses will not be allowed (to make it a little simpler). This situation is not as straight-forward as it might seem. There are several characters that have a special meaning when they appear inside [ ]. These characters are: ^ - [ and two &s in a row(&&). In order to use one of these characters, we need to put \\ in front of the character:
String expr = "2*x^3 - 4/5*y + z^2"; String delims = "[+\\-*/\\^ ]+"; // so the delimiters are: / ^ space String[] tokens = expr.split(delims); + - *
File Output
Writing data to a file is similar to writing data to the screen. You will open a file for writing and then print to that file any data that you would like to store there. You must remember to close the file or risk having some data not be written and saved to the file. We will use of the java.io.PrintStream class for file output. When you intend to write data to a file, you should consider what is the appropriate action to take if the file already exists. The safest option is to ask the user what to do, and then allow the user to choose overwrite the file, choose a different filename, or cancel the operation. The example shown below assumes that the file opened by the PrintStream object will be overwritten if it already exists. If you do not want to overwrite the file if it already exists, then you must create and test a File object first. The exists method of the File class will return true if the file already exists. The nice thing about using a PrintStream object is that you are already familiar with the print and println methods that are defined for all PrintStream objects. One important thing to be aware of when doing file output using a PrintStream is that it does not do automatic line flushing. When writing information to a file, it is more efficient to wait until there is a bunch of information to write and then write it all at once than to write one piece of information to the file, then write the next piece, and so on. This process is call buffering the place where the information is stored until it gets written to the file is called the data buffer, or just buffer. When the buffer gets sufficiently full, the buffer is flushed and the information in the buffer is written to the file. If something "bad" should happen before the buffer has been flushed (for example, the program crashes), then the information in the buffer is lost and the file will not contain all the information that you may expect it to. However, even though calls to print and println won't automatically flush the buffer, we can force the buffer to flush by calling the flush method (or by closing the file using the close method). You should always flush the buffer before your code calls another method and you should always remember to close your files in order to prevent losing information. Here's an example file output:
// Create a PrintStream attached to a file named "out.txt". // This will overwrite the file if it already exists java.io.PrintStream ps = new java.io.PrintStream( "out.txt" );
// Buffer some data to write to the file (doesn't actually write until flush) ps.print( "Some test data that will be written when flush is called."); // Flush all buffered data to the file. ps.flush(); // Buffer some more data. ps.println( data ); // Close the file (by closing the PrintStream). // Also flushes any remaining buffered output. ps.close();
Conclusion
Input and output using the standard Java library of classes can be somewhat complex. By using and experimenting with each of the techniques presented here, you will be able to perform some of the most common input and output operations in your Java programs. PlanetIO.java has examples of console based input and output and file based input and output, written by Beck Hasti. ConsoleIO_Scanner.java has additional examples of console based input and output, originally written by Jim Skrentny. FileIO_Scanner.java has an additional examples of file based input and output using using a StringTokenizer (as opposed to the split method of the String class), originally written by Jim Skrentny.
Copyright 2007-8 Beck Hasti (updates), 2002 Deb Deppeler (original content) Last update on
JAVA CLASSES
Contents
Fields, Methods, and Access Levels Simple Example Java Class Static vs Non-Static Fields and Methods Final Fields and Methods Test Yourself #1 Some Useful Built-in Classes o String o Object
private: accessible only in this class (package): accessible only in this package protected: accessible only in this package and in all subclasses of this class public: accessible everywhere this class is available
(package): class objects can only be declared and manipulated by code in this package public: class objects can be declared and manipulated by code in any package
Note: for both fields and classes, package access is the default, and is used when no access is specified.
Notes
Object: Object-oriented programming involves inheritance. In Java, all classes (built-in or user-defined) are (implicitly) subclasses of Object. Using an array of Object in the List class allows any kind of Object (an instance of any class) to be stored in the list. However, primitive types (int, char, etc) cannot be stored in the list. Constructor function: As in C++, constructor functions in Java: o Are used to initialize each instance of a class. o Have no return type (not even void). o Can be overloaded; you can have multiple constructor functions, each with different numbers and/or types of arguments. If you don't write any constructor functions, a default (no-argument) constructor (that doesn't do anything) will be supplied. If you write a constructor that takes one or more arguments, no default constructor will be supplied (so an attempt to create a new object without passing any arguments will cause a compile-time error). It is often useful to have one constructor call another (for example, a constructor with no arguments might call a constructor with one argument, passing a default value). The call must be the first statement in the constructor. It is performed using this as if it were the name of the method. For example: this( 10 ); is a call to a constructor that expects one integer argument.
Initialization of fields: If you don't initialize a field (i.e., either you don't write any constructor function, or your constructor function just doesn't assign a value to that field), the field will be given a default value, depending on its type. The values are the same as those used to initialize newly created arrays (see the "Java vs C++" notes). Access Control: Note that the access control must be specified for every field and every method; there is no grouping as in C++. For example, given these declarations: public int x; int y; only x is public; y gets the default, package access.
class. (In fact, there is a copy of the field even if there are no instances of the class.) For example, we could add the following field to the List class:
static int numLists = 0;
Now every time a new List object is created, the numLists variable is incremented; so it maintains a count of the total number of Lists created during program execution. Every instance of a List could access this variable (could both read it and write into it), and they would all be accessing the same variable, not their own individual copies. A method should be made static when it does not access any of the non-static fields of the class, and does not call any non-static methods. (In fact, a static method cannot access non-static fields or call non-static methods.) Methods that would be "free" functions in C++ (i.e., not members of any class) should be static methods in Java. Also, methods that are logically associated with a particular class, but that only manipulate the class's static fields should be static. For example, if we wanted a function to print the current value of the numLists field defined above, that function should be defined as a static method of the List class. A public static field or method can be accessed from outside the class using either the usual notation:
class-object.field-or-method-name
or using the class name instead of the name of the class object:
class-name.field-or-method-name
For example, if the numLists field is public, and there is a variable L of type List, the numLists field can be accessed using either L.numLists or List.numLists. Similarly, if the List class includes a public static method PrintNumLists, then the method can be called using either L.PrintNumLists() or List.PrintNumLists(). The preferred way to access a static field or a static method is using the class name (not using a class object). This is because it makes it clear that the field or method being accessed is static.
The assignment statement in the constructor function would change to: TEST YOURSELF #1
A. This program must be in a file called Test.java. will create one new file called Test.class. B. This program can be in any .java file. Compiling one new file called Test.class. C. This program must be in a file called Test.java. will create two new files called Test.class and main.class. D. This program can be in any .java file. Compiling two new files called Test.class and main.class.
Question 2: Which of the following correctly describes what happens when the program is compiled and run?
A. There will be constructor with arguments. B. There will be with no arguments. C. There will be D. There will be E. There will be a compile-time error because there is no no a run-time error because there is no constructor no errors; the output will be: 10 20 30 40 no errors; the output will be: 30 20 30 40 no errors; the output will be: 30 40 30 40
solution
String
to create a String:
String S1 = "hello", // initialize from a string literal S2 = new String("bye"), // use new and the String constructor S3 = new String(S1); // use new and a different constructor
to concatenate:
String S1 = "hello" + "bye", S2 = S1 + "!", S3 = S1 + 10; // the int 10 will be converted to a String, // because the other operand of + is a // String
Note: in all of the examples above, a new string is created and the variable (S1, S2, or S3) is set to point to that new string. For example, the expression S1 + "!" does not change what S1 points to, it creates a new string that contains "hellobye!".
boolean equals(Object S) // return true if S is a non-null String that // contains the same characters as this String String substring( -- several forms -- ) // see documentation toLowerCase() toUpperCase() Lots more -- see documentation!
Object
default value your // is not very useful so you should define
// own toString method boolean equals(Object ob) // return true iff this object is the same as // ob // note: uses pointer equality for user// defined classes; you may want to // redefine this method More -- see documentation!
One such class for each primitive type. Use these "wrapper" classes when you need an Object. For example, think about the List class. Suppose you try this:
List L = new List(); L.AddToEnd( 10 );
You get a compile-time error saying that type int cannot be converted to type Object. That's because the AddToEnd method takes an Object as its parameter, and a primitive type like int is not an Object. You can fix this using:
L.AddToEnd( new Integer( 10 ) );
This creates a new Integer object, with the value 10, and passes that object to the AddToEnd method. Note that if you want to retrieve a particular kind of object from a list, you must use a cast. For example, assume that the List class also includes methods to allow a user of the class to iterate through the items in a list; i.e., we think of every list as having a "current pointer", and methods are provided to get the current item, and to move the current pointer:
public void firstElement() point to the // set the current pointer to
// first item on the list public Object NextElement() // return the item pointed to by the current // pointer; also advance the current pointer public boolean hasMoreElements() // true iff the current pointer has // not fallen off the end of the list
If you do this:
L.AddToEnd( new Integer( 10 ) ); L.firstElement(); Integer K = L.nextElement();
you'll get a compile-time error saying that you must use an explicit cast to convert an Object to an Integer. That's because the return type of nextElement is Object, and you're trying to assign that into variable K, which is an Integer. Here's what you need to do:
Integer K = (Integer)L.nextElement();
You can get back a (primitive) value from these wrapper classes using the method xxxValue, where xxx is the primitive type. For example:
Integer K = new Integer( 10 ); int x = K.intValue(); Boolean B = new Boolean( true ); boolean b = B.boolValue();
These classes also provide some useful constants: Integer.MAX_VALUE (the largest int that can be represented), Integer.MIN_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Double.POSITIVE_INFINITY, etc. TEST YOURSELF #2
public static void main(String[] args) { Integer n = new Integer(5), m = new Integer(6); Swap(n, m); System.out.println("n = " + n + "; m = " + m); } }
The person who wrote this program expected the output to be: n = 6; m = 5. However, the actual output is: n = 5; m = 6. Explain why. Question 2: Consider the following function. This function was intended to create and return a string that is the reverse of its parameter. (For example, if parameter S = "hello", the function should return "olleh".) However, there are several problems with the function as written.
static String Reverse(String S) { String newS = ""; char c = S[0]; while (c) { newS = c + newS; c++; } return newS; }
First, identify each problem (write the bad code and give a brief explanation of why it is incorrect). Then give a new, correct version of function Reverse. solution
Question 2: D. There will be no errors; the output will be: 30 20 30 40 (There is no need for a constructor with no arguments because there are no uses of "new Test" with no arguments.)
Test Yourself #2
Question 1: Why does this program produce the output n = 5; m = 6? In Java, all parameters are passed by value, so changes to the parameters themselves in the function do not affect the values that were passed. The Swap function changes its parameters, j and k, making them point to new Integers, but that has no effect on the values that were passed (variables n and m). Question 2: static String Reverse(String S) { String newS = ""; char c = S[0]; // can't index Strings in Java while (c) { // not a boolean loop condition // (but this would work in C/C++) newS = c + newS; c++; // this doesn't help fetch the next char in S // (increments ascii value of c) } return newS; } Correct version: static String Reverse(String S) { String newS = ""; for (int i=0; i<S.length(); i++) { newS = S.charAt(i) + newS; } return newS; }
INHERITANCE o Casting
Test Yourself #1 Dynamic Dispatch Not for Static Methods Not for Fields Test Yourself #2 Using Super o Abstract Classes and Methods o Inheritance Summary INTERFACES Test Yourself #3 Answers to Self-Study Questions
o o
INHERITANCE Casting
Recall that inheritance implements the "is-a" relationship. For example, in the Introduction to Inheritance notes, class RaceHorse was defined to be a subclass of Horse (because every RaceHorse is a Horse). Therefore, it makes sense that a RaceHorse can be used in any context that expects a Horse. For example:
Horse h; RaceHorse r = new RaceHorse(); h = r;
Variable h is of type Horse, so in an assignment of the form h = ... the right-hand side of the assignment should be of type Horse, too. However, since a RaceHorse is-a Horse, it is OK for the right-hand side to be of type RaceHorse, as in the above example. Note that every Horse is not a RaceHorse, so in general, a Horse cannot be used when a RaceHorse is expected. For example, the following code causes a compile-time error:
Horse h = new Horse(); RaceHorse r = h;
Here are three more examples of code that sets a Horse variable or parameter to point to a RaceHorse object:
(1) Horse h = new RaceHorse(); points to a (2) // h is of type Horse, but it // RaceHorse object public static void f( Horse h ) { ... } ... RaceHorse r = new RaceHorse(); f( r ); // f's formal parameter h is of type Horse, but the actual // parameter points to a RaceHorse object
(3) public static RaceHorse g() { // return a pointer to a RaceHorse object } ... Horse h = g(); // h is of type Horse, but it now points to a RaceHorse // object
If you know that at a particular point in your code a Horse variable is really pointing to a RaceHorse object, then you can use that variable in a context that expects a RaceHorse, but you must provide a cast. Note that there are two kinds of errors that can arise if you get this wrong: 1. missing cast => compile-time error 2. incorrect cast => runtime error (ClassCastException is thrown) Examples: Assume that we have the following declarations of function f and variables h1 and h2: public static void f( RaceHorse r ) { ... } Horse h1 = new RaceHorse(); Horse h2 = new Horse(); Now consider the following three calls to f: 1. f(h1); // compile-time error (missing cast) 2. f( (RaceHorse)h1 ); // fine! h1 really does point to a RaceHorse 3. f( (RaceHorse)h2 ); // runtime error (bad cast) h2 points to a Horse Note that when you use a cast you must think about what expression you are casting, and perhaps use parentheses (if that expression is part of a larger expression). For example, suppose variable h is of type Horse but actually points to a RaceHorse. You can call h's WinRace method (which is a RaceHorse method, but not a Horse method), but you have to use a cast, like this: ((RaceHorse)h).WinRace(); The parentheses tell the compiler that you are casting just variable h to be of type RaceHorse. If you omit the parentheses: (RaceHorse)h.WinRace(); // NO! This doesn't work!! the compiler will think you are casting the result of the call h.WinRace() to be of type RaceHorse. TEST YOURSELF #1 Assume that classes List, Horse, and RaceHorse have been defined as usual, and that the following statements have been executed:
List L = new List(); Horse h = new Horse(); L.AddToEnd(h);
L.firstElement();
Question 1: Fill in the following table by specifying, for each of the assignments:
the type of the left-hand side, the type of the right-hand side, whether the statement causes a compile-time error, a run-time error, or no error. Type of lhs Type of rhs Error?
Question 2: For each of the following method invocations, say whether the code causes a compile-time error, a run-time error, or no error.
((Horse)h).Age(); ((RaceHorse)h).Age();
solution
Dynamic Dispatch
Suppose that both the Horse and RaceHorse classes define the method public void Train(); i.e., the RaceHorse class overrides the Horse class's Train method. Given variable h of type Horse (but remember, it might actually point to a RaceHorse object) which version of the method is called when the code h.Train() is executed? The answer is that it depends on what h actually points to (it does not depend on the declared type of h). If h points to a Horse object, then the Horse version of the method is called; if it points to a RaceHorse object, then the RaceHorse version is called. Note that this code might be executed many times, and that sometimes variable h might be pointing to a Horse, while at other times it might be pointing to a RaceHorse. So each time the code is executed it must be determined again which version of the Train method to call. This approach to determining which version of an overridden method to call based on what the variable actually points to at the time of the call (rather than on the type of the variable) is called dynamic dispatch (or sometimes, dynamic binding). In C++, you get dynamic dispatch only if: 1. the method call is via a pointer; e.g.:
h->Train(); rather than via the object itself; e.g.: h.Train(); (except that if the variable is a reference parameter, then it is really a pointer, so the call h.Train() could use dynamic dispatch in that case) 2. the method being called was declared virtual Note that in Java all non-primitive types are pointers, so point (1) above does not apply (all method calls are via pointers), and that all non-static methods are (implicitly) virtual, so point (2) does not apply, either. (But it is important to note that static methods do not have dynamic dispatch. So if the Horse and RaceHorse classes both have a static method named foo, then if variable h is of type Horse, h.foo() will always call the Horse version, even if h is pointing to a RaceHorse object. This is one reason why static methods should really be called by using the class name rather than a variable name; e.g., Horse.foo() or RaceHorse.foo() -- it makes it clear which method is being called, and you won't be fooled into thinking that dynamic dispatch applies in this case.) Also note that dynamic dispatch applies only to methods, not fields. For example, if Horse and RaceHorse both have a field named myValue (i.e., the RaceHorse class hides the myValue field defined by the Horse class), then if variable h is of type Horse, the expression h.myValue always refers to the field defined in the Horse class, even if h actually points to a RaceHorse object. TEST YOURSELF #2 Consider the following program:
class Horse { public void Print() { System.out.println("Horse"); } } class RaceHorse extends Horse { public void Print() { System.out.println("RaceHorse"); } } class Test { public static void DoPrint( Horse h ) { h.Print(); } public static void main(String[] args) { Horse x = new Horse();
Using Super
It is possible for a subclass to "request" its superclass's version of an overridden method by using "super.xxx", where xxx is the name of the method. For example, assume as above that both Horse and RaceHorse define the Train method. Normally, a call to Train from inside a RaceHorse method would call the RaceHorse version; however, the Horse version can be called by using super.Train. For example, the RaceHorse class could include a Nurture method, defined as follows (assuming that the Horse class has provided a GetAge method to access the myAge field):
public void Nurture() { if (GetAge() < 3) super.Train(); // train young horses using the Horse method else Train(); // train older horses using the RaceHorse method }
| v +--> | | | | | | | | | | +---
+----------+ | if k < N | +----------+ | | | +-------+ v | +----------+ | | sum += k | | +----------+ | | | v | +---------+ | | k++ | | +---------+ | +---------+ | v +-----------+ | write sum | +-----------+
You might decide to use inheritance to define the different kinds of nodes, since different kinds of nodes need to store different information; for example: condition node: store the boolean expression and two outgoing edges read node: store the variable being read and one outgoing edge assign node: store the variable being assigned to, the expression being assigned, and one outgoing edge Here's a reasonable class hierarchy for flowchart nodes:
Node / \ Statement Condition / | \ Assign Read Write
It will be useful to have a Print method for flowcharts, which means that every node should have a Print method. However, there is no reasonable Print method that will work for all nodes. Therefore, this is a time to use abstract methods! Here's the syntax:
abstract class Node { abstract public void Print(); header } class Condition extends Node { public void Print() { // actual code goes here } } // no body, just the function
Notes: 1. If a class includes an abstract method, the class must be declared abstract, too (otherwise you get a compile-time error). 2. An abstract class cannot be instantiated:
3. Node n; // OK -- just a pointer to a Node, no attempt to create a Node object 4. n = new Condition(); // OK -- Condition is not an abstract class 5. n = new Node(); // Error! Can't instantiate an abstract class
6. A subclass of an abstract class that does not provide bodies for all abstract methods must also be declared abstract:
7. Statement 8. Print, so 9. class
class Statement extends Node { // Error! does not } // implement it must // be an abstract
10. A subclass of a non-abstract class can override a (non-abstract)method of its superclass, and declare it abstract. In that case, the subclass must be declared abstract.
Inheritance Summary
a subclass can always be used in a context that expects the superclass, but not vice-versa (the latter requires a cast) all non-static methods use dynamic dispatch a subclass method can use "super" to call the superclass's version of an overridden method (i.e., to avoid dynamic dispatch) a class can provide just the header for a method without providing the body; such methods are abstract, and a class that contains an abstract method must also be declared abstract (and cannot be instantiated)
INTERFACES
Motivation: Some objects have more than one "is-a" relationship. For example, consider designing classes to represent the people associated with a university:
Person / | \ Student Staff Faculty | TA
Note that a TA is certainly a student, but in some ways, a TA is also staff (e.g., a TA gets paid, receives benefits, etc). Some languages (e.g., C++) allow multiple inheritance; for example, the TA class could be a subclass of both the Student class and the Staff class. However, there are problems with multiple inheritance, and so Java forbids it, providing interfaces instead. An interface is similar to a class, but can only contain:
public, static, final fields (i.e., constants) public, abstract methods (i.e., just method headers, no bodies)
Here's an example:
public interface Employee { void RaiseSalary( double d ); double GetSalary(); }
Note that both methods are implicitly public and abstract (those keywords can be provided, but are not necessary). A class can implement one or more interfaces (in addition to extending one class). It must provide bodies for all of the methods declared in the interface, or else it must be abstract. For example:
public class TA implements Employee { void RaiseSalary( double d ) { // actual code here } double GetSalary() { // actual code here } }
Public interfaces (like public classes) must be in a file with the same name. Many classes can implement the same interface (e.g., both the TA class and the Staff class can implement the Employee interface). An interface can be a "marker":
no fields or methods used only to "mark" a class as having a property testable via the instanceof operator
interface GoodClass { } class C1 implements GoodClass { ... } ... C1 x = new C1(); if (x instanceof GoodClass) ... // this condition evaluates to true
For example:
Interfaces provide a way to group similar objects. For example, you could write a function with a parameter of type Employee, and then call that function with either a TA or a Staff object. If you hadn't used the Employee interface (e.g., if you simply wrote RaiseSalary and GetSalary methods for the TA and Staff classes), writing such a function would be very clumsy. TEST YOURSELF #3 Consider the following program:
interface Employee { public void RaiseSalary(); } interface Musician { public Play(); } class Test implements Employee, Musician { public void RaiseSalary() { System.out.println("raising"); } public void Play() { System.out.println("playing"); } public static void main(String[] args) { Test x = new Test(); x.RaiseSalary(); x.Play(); }
True or false: 1. An attempt to compile this program fails because class Test tries to implement two interfaces at once ______. 2. An attempt to compile this program fails because class Test only implements interfaces, it does not extend any class ______. 3. An attempt to compile this program fails because the definitions of the two interfaces and the class are all in the same file ______. 4. It is optional for class Test to implement the methods RaiseSalary and Play ______. solution
Question 1:
Question 2: For each of the following method invocations, say whether the code causes a compile-time error, a run-time error, or no error. ((Horse)h).Age(); no error ((RaceHorse)h).Age(); runtime error (Note: for some versions of Java, the runtime error msg gives the wrong line number!)
Test Yourself #2
What is printed when this program is executed? Horse RaceHorse RaceHorse
Test Yourself #3
F 1. An attempt to compile this program fails because class Test tries to implement two interfaces at once. F 2. An attempt to compile this program fails because class Test only implements interfaces, it does not extend any class. F 3. An attempt to compile this program fails because the definitions of the two interfaces and the class are all in the same file. F 4. It is optional for class Test to implement the methods RaiseSalary and Play.
INTRODUCTION TO INHERITANCE
Contents
Motivation and Simple Example Benefits of Inheritance Inheriting Fields and Methods o Fields
Methods Constructors Test Yourself #1 Extending the Object Class o Cloning o Test Yourself #2 Summary Answers to Self-Study Questions
o
Suppose you have a class Horse with the following methods: o int Age() // return this horse's age o String Owner() // return this horse's owner o double Value() // return this horse's value o void Birthday() // increment this horse's age, and adjust its value And suppose you want a class RaceHorse that has all of the methods a Horse has, but also keeps track of how many races this horse has won; i.e., provides the following additional methods: o int RacesWon() // return the number of races won by this horse o void WinRace() // increment the number of races won by this horse It would be reasonable to define RaceHorse as a subclass of Horse:
class RaceHorse extends Horse { // new fields private int myNumRacesWon; // new methods public int RacesWon() { public void WinRace() { }
In general, inheritance is used to implement an "is-a" relationship, not a "has-a" relationship. For example, a racehorse is a horse, as is a workhorse, so a reasonable inheritance hierarchy is:
Horse / \ RaceHorse WorkHorse
(In this hierarchy, Horse is the base class; it is the superclass of both RaceHorse and WorkHorse, and both RaceHorse and WorkHorse are subclasses of Horse.) On the other
hand, since a horse has a head, a body, and a tail, and a head has a nose and a mouth, the following would not be a good inheritance hierarchy:
Horse / | \ Head Body Tail / \ Nose Mouth
It may make sense to define classes Horse, Head, Body, Tail, Nose, and Mouth, but the way they should be related is that the Head class has two fields of type Nose and Mouth, respectively, and the Horse class has three fields, of type Head, Body, and Tail. The main benefit of inheritance is code reuse:
No need to replicate class methods (the subclass can inherit those of the superclass). No need to replicate functions with parameters of the superclass type; they will work fine when passed actual parameters of the subclass type (because every object of the subclass type IS-A object of the superclass type, too!)
Note: You should probably not use inheritance if you don't want most of the existing class's methods unchanged, or if functions with parameters of that type will not still work correctly when passed actual parameters of the new subclass type. Here's an example: Consider a List class with the following operations:
void AddToEnd( Object ob ) // add ob to the end of this list void Print() // print all objects in this list boolean Lookup( Object ob ) // return true iff ob is on this list
DList (a double-ended list, that permits objects to be added to either the front or the end of the list. SortedList (a list in which the objects are maintained in sorted order).
Assume that the List class is implemented using a linked list. The DList class can reuse the List methods Print and Lookup. For efficiency reasons it is probably desirable to implement the DList class using a doubly linked list, so it will not be able to reuse the AddToEnd method, because that method adds a node with just two fields (the object and a pointer to the next list node), and for a DList the method must add a node with three fields (the object, a pointer to the next list node, and a pointer to the previous list node). Nevertheless, because it can reuse both of the other methods (in addition to defining a new method AddToFront), and because functions with List parameters should work fine given DList objects, it seems reasonable to define the DList class as a subclass of List. The SortedList class cannot reuse either the Lookup method or the AddToEnd method. (A new version of Lookup should be defined for the SortedList class that is more efficient than the List version by takeing advantage of the fact that the list
is sorted. As for AddToEnd, adding a new object to the end of a list does not in general keep it sorted; therefore, this method is simply not appropriate for a SortedList.) However, this is a bigger problem for SortedList than it was for DList. For DList, the AddToEnd method is still appropriate, it just requires a modified implementation. For SortedList, AddToEnd is not a legal operation, and an attempt to call that method would have to result in an error. That also means that functions with List parameters might not work for SortedLists. Therefore, it does not seem reasonable to define the SortedList class as a subclass of List. Note that in general you should think about the whole class hierarchy that you will need before defining any classes (i.e., design your base class so that it can be extended to create the subclasses you want). This is not always easy, but it should pay off in the long run.
Every RaceHorse has four fields: 3 inherited fields and 1 new one. Notes: 1. An inherited field can be hidden (by defining a new field with the same name). For example, if we add the line: private double myAge; to the RaceHorse class, every RaceHorse will have five fields, one of which (int myAge) is hidden. However, it is a bad idea to hide fields because it can lead to confusion and incorrect code. 2. Private fields are inherited, but cannot be accessed by the methods of the subclass. For example, suppose we decide that the RaceHorse's WinRace method should increase that horse's value is well as incrementing the number of races won. If we add the following to the RaceHorse class: public void WinRace() {
myNumRacesWon++; myValue += 1000.0; } we get a compile-time error, because the myValue field is a private field of the Horse class (so cannot be accessed in a RaceHorse method). The same thing happens if the superclass field has package access and the subclass is in a different package. To address this problem, fields can be defined to be protected, which means that subclass methods can access them, or the superclass can provide public or protected functions to access and modify its private fields. For example, if the Horse class includes: protected double GetValue() { return myValue; } protected void SetValue( double d ) { myValue = d; } then the RaceHorse class can implement the WinRace method like this: public void WinRace() { myNumRacesWon++; SetValue( GetValue() + 1000.0); }
Methods
Each superclass method (except its constructors) can be either
o o o
inherited: If no method with the same name is (re)defined in the subclass, then the subclass has that method with the same implementation as in the superclass. overloaded: If the subclass defines a method with the same name, but with a different number of arguments or different argument types, then the subclass has two methods with that name: the old one defined by the superclass, and the new one it defined. overridden: If the subclass defines a method with the same name, and the same number and types of arguments, then the subclass has only one method with that name: the new one it defined. Example:
class Horse { ... public double GetValue() { return myValue; } public void Birthday() { // increment horse's age, recompute its value } public void Sell( String newOwner ) { // change the myOwner field }
class RaceHorse extends Horse { ... public void Birthday( double d ) { // increment horse's age, set value to given amount } public void Sell( String newOwner ) { // change the myOwner field and set the myNumRacesWon field to 0 } }
One GetValue method (inherited from the Horse class). Two Birthday methods (one with no arguments inherited from the Horse class, and one with one argument overloaded in the RaceHorse class). One Sell method (the one defined in the RaceHorse class, which overrode the Sell method defined in the Horse class).
Constructors
A subclass's constructors always call a superclass constructor, either explicitly or implicitly. If there is no explicit call (and no call to another of the subclass constructors), then the no-argument version of the superclass constructor is called before executing any statements. Example:
/ in | public Horse() { myValue = 0; } // no-arg constructor superclass | public Horse( double d ) { myValue = d; } // 1-arg constructor \ / in | public RaceHorse() {myNumRacesWon = 0; } // no-arg constructor subclass | \ / in main | RaceHorse r = new RaceHorse(); \
When the code "r = new RaceHorse()" is executed, the following happens:
1.
RaceHorse's no-arg constructor is called 2. Horse's no-arg constructor is called 3. myValue is set to 0 4. myNumRacesWon is set to 0
And here is an example to show both a call to a superclass constructor, and also how one subclass constructor can call another subclass constructor:
public RaceHorse( double d ) { super( d ); // call Horse's 1-arg constructor myNumRacesWon = 0; } public RaceHorse() { this( 2000.0 ); // call to RaceHorse's 1-arg constructor }
Notes: If you want to call a superclass constructor or another of the subclass constructors, that call must be the first statement. 2. If you write no constructors for a class, you will get a default no-argument constructor that just calls the superclass's no-argument constructor.
1.
is a subclass of the Point class has a (protected) field of type String called color (in addition to fields x and y) has 3 public constructors: 0 args: x and y are set to 0; color is set to "red"; 2 int args: x and y are set to the given values; color is set to "red" 2 int args and 1 String arg: x, y, and color are set to the given values
solution
1. 2. 3. 4.
public String toString(): Returns a String representation of the object. It is used, for example, by System.out.print to print an object. The default version of toString is not very useful, so you should override this method whenever you want to provide a String representation of your class objects. public boolean equals(Object ob): Returns true iff the object (pointed to by "this") and ob are the same. The default version uses pointer equality; i.e., it returns true only if "this" and "ob" contain the same address. You may want to override this method to provide a more liberal notion of equality. For example, the String class overrides equals so that it returns true for two Strings that contain the same sequence of characters. public int hashCode(): Returns an integer for this object suitable for use as a hash code (e.g., for use with the Hashtable class defined in javil.util). This method should be overridden whenever the equals method is, so that hashCode returns the same value for two "equal" objects. protected Object clone(): Returns a copy of this object (note that no constructor is called for the new object). The default version just copies the values of all fields (i.e., a "shallow" copy). That is probably not what you want when your class has fields that contain pointers (i.e., arrays or classes). So in that case you should override the clone method to do a deep copy -- clone all pointer fields.
Cloning
To permit your object to be cloned you must declare that your object implements the Cloneable interface. (See the notes on INTERFACES.) For example: public class List implements Cloneable { private Object items[]; // a pointer field! ... } If you forget to do this, an attempt to clone will cause the exception CloneNotSupportedException to be thrown. Given the declaration above, List methods will be able to clone List objects; however, users of the List class will not be able to call clone because it is a protected method of the Object class. Also, the (default) clone method will just
copy the value of items, creating a new List object whose items field points to the same array (not good!). For example:
List L1 = new List(); List L2 = L1.clone() +--+ +--------------+ List L1: | -|--> | +--+ | +-----------+ +--+ | items: | -|--|----> | | | | | | +--+ | +-> +-----------+ | ... | | +--------------+ | | +--+ +--------------+ | List L2: | -|--> | +--+ | | L2 points to a newly allocated +--+ | items: | -|--|--+ List object, but its items | +--+ | field points to the same | ... | array as L1's items field! +--------------+
To avoid these two problems, you should redefine the clone method for class List as follows:
public class List implements Cloneable { private Object items[]; public Object clone() { try { List tmp = (List)super.clone(); tmp.items = (Object[])items.clone(); return tmp; } catch (CloneNotSupportedException ex) { // cannot get here: List implements Cloneable, so do arrays throw new InternalError(ex.toString()); } } }
Notes on this example code: The clone method is overridden, including changing its access to public so that users of the List class can clone Lists. o Every definition of clone should first call super.clone() to create the right kind of object (and copy its fields). o Because the return type of clone is Object, the result of the call super.clone() must be cast to List to permit the assignment into variable tmp.
o
Because the call to super.clone only does a shallow copy of the "items" field, that call should be followed by code that clones the array pointed to by items. o We know that a list is Cloneable, but all the compiler knows is that the clone method might throw the CloneNotSupportedException; so we must put the call to super.clone inside a try block.
o
Make your class implement Cloneable. Redefine the clone method: make it public start with the call "super.clone()" inside a try block for every field of your object that is really a pointer, clone that field (in the same try block).
Question 1: What is printed when this program is run? Question 2(a): Write a new version of the program in which main uses cloning to copy from p1 to p2 (instead of assignment). Be sure to include all appropriate changes to the Point class. Question 2(b): What is printed when your new version of the program is run?
Question 3: Now suppose that the x,y coordinates of a Point are stored in an array, rather than in two int fields; i.e., suppose we replace: private x, y; with: private int [] coords = new int[2]; and that we change the SetPoint method to:
public void SetPoint(int a, int b) { coords[0] = a; coords[1] = b; }
and that we change the toString method appropriately. Write a new version of the clone method so that there is no aliasing when a Point is cloned. solution
Summary
use inheritance when: implementing an "is-a" relationship (not a "has-a" relationship), and fields and (most) methods of the superclass will be used, not overridden, and functions that work on the superclass will still work on the subclass o remember that all fields will be inherited inherited fields can be hidden, but that is a bad idea private fields are inherited but can only be accessed by the methods of the superclass protected or public fields can be accessed by methods of the subclass o methods can be inherited, or overloaded, or overridden o subclass constructors always call a supergraph constructor, either explicitly or implicitly
o
Test Yourself #2
Question 1: What is printed when this program is run? (20,30)
Question 2(a): Write a new version of the program in which main uses cloning to copy from p1 to p2 (instead of assignment). Be sure to include all appropriate changes to the Point class. class Point implements Cloneable { // fields and constructors as before... public Object clone() { try { Point tmp = (Point) super.clone(); return tmp; } catch (CloneNotSupportedException ex) { throw new InternalError(); } } public static void main(String[] args) { ... Point p2 = (Point) p1.clone(); ... } } Question 2(b): What is printed when the new version of this program is run? (10, 20) Question 3: Write a new version of the clone method so that there is no aliasing when a Point is cloned. public Object clone() { try { Point tmp = (Point) super.clone(); tmp.coords = (int[]) coords.clone(); return tmp; } catch (CloneNotSupportedException ex) { throw new InternalError(); } }
Overview Hello World Example o Test Yourself #1 Example Using Listeners o Listeners o Test Yourself #2
Example Using Layout o Layouts o Test Yourself #3 Graphics Answers to Self-Study Questions
Overview
Java's Abstract Windowing Toolkit (AWT) provides support for programs that use Graphical User Interfaces (GUIs), rather than simply communicating with the user via the keyboard or via files. The AWT includes classes for commonly used objects like windows, labels, buttons, and checkboxes. For more in-depth coverage of the AWT, see chapter 13 of Java Programming.
Things to note: 1. The HelloWorld class extends the Frame class. A Frame corresponds to a window on your screen with a title and a border. The way it looks will depend on
how your system is set up to display windows (e.g., it may have controls to permit resizing, etc). 2. In general, a Frame can contain components (like labels and buttons). In this example, the Frame contains a single Label object named text. The Label is added to the Frame using the Frame's add method: "add(text);" as part of the HellowWorld class's constructor function. Because the Frame contains no other objects, the Label expands to fill the whole Frame. 3. The constructor function must also set the size of the Frame: "setSize( 300, 100 );" (the size is in pixels: 300 wide and 100 high -- the default size is 0 by 0), and must make the Frame visible: "setVisible( true );" 4. Because this is an application program (not an applet), you must still include a main method. All the main method has to do is create a HelloWorld object, using new: "new HelloWorld();" TEST YOURSELF #1 Modify the HelloWorld program in each of the following ways: 1. Instead of always writing "Hello World!" in the new window, write the (single) string that is provided as a command-line argument. So for example, if the user types java HelloWorld fishsticks then the new window contains the text "fishsticks", instead of "Hello World!". 2. A Label is a subclass of the Component class. Read about the Component methods, and use the appropriate ones so that the background color of the new window is red, and the color of the text is blue. (Don't forget that the values for the colors are members of the Color class, so you need to use Color.red, not just red.) solution
public class Hello1 extends Frame { private Label text; private Button exitButton; // constructor public Hello1() { // create a label containing "Hello World!", with the text centered // and add the label to the top of the Frame text = new Label("Hello World!", Label.CENTER); add(text, "North"); // create the exit button and add it to the bottom of the Frame exitButton = new Button("exit"); add(exitButton, "South"); // create a listener for the exit button that will cause a // reaction when the button is clicked (using the mouse), and // associate that listener with the exit button ExitButtonListener exitListener = new ExitButtonListener(); exitButton.addActionListener(exitListener); // set the size of the frame and make it visible setSize( 300, 100 ); setVisible( true ); } // define the listener for the exit button class ExitButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the exit button is clicked; // end execution of this program System.exit(0); } } // main public static void main( String[] args ) { new Hello1(); } }
Listeners
When you write a Graphical User Interface, you usually want your program to respond to user actions, like pushing a button or typing in text. The mechanism that the Java AWT provides to support this is the creation of Listeners, which have methods that are invoked in response to user actions. Each component can have an associated Listener of the appropriate type that will respond to user actions involving that component. There are 3 main kinds of Listeners:
1. ActionListener, associated with a Button 2. ItemListener, associated with a Checkbox or a List 3. TextListener, associated with a TextField or a TextArea These Listeners are all interfaces (defined in java.awt.event). To create your own Listener, you must define a class that implements one of these interfaces. For example, in the Hello1 program, the line:
class ExitButtonListener implements ActionListener {
starts the definition of class ExitButtonListener, which is an implementation of the ActionListener interface. Each kind of Listener has a method (which you must define) that is called when a particular user action is performed on a component associated with that Listener:
ActionListener (associated with a Button) has method actionPerformed. This method is called when the button is pressed (clicked on with the mouse). ItemListener (associated with a Checkbox or a List) has method itemStateChanged. This method is called when the state of the Checkbox is changed (it is selected or deselected using the mouse), or when a List item is selected (using the mouse). For a Checkbox, the method getState can be used to determine whether the Checkbox is "on" or "off"; for a List, the method getSelectedItem can be used to determine which List item was selected. TextListener (associated with a TextField or a TextArea) has method textValueChanged. This method is called when a change is made to the text (a character is added, or the text is edited). The method getText can be used to determine the current text in the TextField or TextArea.
The components that can have associated Listeners all have methods called addXXXListener, where XXX is the kind of Listener (Action, Item, or Text). To associate a Listener with a component, call the component's addXXXListener method with the Listener object as the argument. For example, the call:
exitButton.addActionListener(exitListener);
associates the ActionListener named exitListener with the Button object named exitButton. TEST YOURSELF #2 Modify the Hello1 program so that the Frame contains two components: 1. A TextField that initially contains "Hello World!". (Note that the user will be able to modify the text in this TextField.) 2. A button labeled "reset". When the user clicks this button with the mouse, the text in the TextField is reset to contain "Hello World!". (Note: to reset the text, you will need to use a method of the TextField's parent class.) solution
// create a panel to hold the two buttons; // put the buttons on the panel and add the panel to the bottom of // the Frame buttonPanel = new Panel(); buttonPanel.setLayout(new GridLayout(1,2)); buttonPanel.add(resetButton); buttonPanel.add(exitButton); add(buttonPanel, "South"); // create listeners for the list of fonts and for the two buttons ResetButtonListener resetListener = new ResetButtonListener(); ExitButtonListener exitListener = new ExitButtonListener(); ListItemListener listListener = new ListItemListener(); // use the addXXXListener methods of the font list and the buttons // to associate each listener with the appropriate object resetButton.addActionListener(resetListener); exitButton.addActionListener(exitListener); fontList.addItemListener(listListener); // arrange the components in the Frame and make it visible pack(); setVisible(true); } class ResetButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the reset button is clicked; // change the font back to plain msgLabel.setFont(plainFont); } } class ExitButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the exit button is clicked; // end execution of this program System.exit(0); } } class ListItemListener implements ItemListener { public void itemStateChanged(ItemEvent e) { // this method will be called when one item on the list is // selected using the mouse; // get the selected list item and set the label's font accordingly String S = fontList.getSelectedItem(); if (S.equals("bold")) { msgLabel.setFont(boldFont); } else if (S.equals("italic")) {
} }
This example uses more components, uses Listeners more extensively, and also introduces layouts, discussed below. First, though, consider the following issue, brought up by this example: If you have several components of the same type (e.g., several Buttons) and you want to respond to user actions involving each of those components, you have two choices: 1. Write a different Listener for each component, specialized to respond to user actions on that particular component. This is the approach that was taken in the ChangeFont program, where two different ActionListeners were defined for the two buttons (the "reset font" button and the "exit" button). 2. Write a single Listener for all of the components. In the method that is called in response to a user action, determine which component was actually involved, and respond accordingly. Each Listener method (actionPerformed, itemStateChanged, and textValueChanged) has a parameter that is a subclass of the class java.util.EventObject. This class includes the method getSource, which returns the actual object to which the user action was applied. For example, the following ActionListener could be associated with both the "reset font" and the "exit" buttons:
3. 4. class ButtonListener implements ActionListener { 5. public void actionPerformed(ActionEvent e) { 6. // determine which button was clicked and do the right thing 7. Object source = e.getSource(); 8. if (source == resetButton) msgLabel.setFont(plainFont); 9. else System.exit(0); 10. } 11. }
Layouts
Frames and Panels are both subclasses of the Container class. These classes can contain sub-components (like Buttons, Lists, and Panels), and they have associated layout mechanisms (schemes for organizing their sub-components).
There are 5 kinds of layouts (only the first 3 will be discussed here): 1. 2. 3. 4. 5. Border Layouts Flow Layouts Grid Layouts Grid-Bag Layouts Card Layouts
A container that uses the Border Layout can contain up to 5 sub-components, organized as shown below:
+----------------------+ | | | North | | | +----------------------+ | | | | | West | Center | East | | | | | +----------------------+ | | | South | | | +----------------------+
When a component is added to a container that uses the Border Layout, the area in which to position the component can be specified. For example, in the program above, the statement "add(msgLabel, "North");" is used to add the Label component to the top (North) of the Frame. If one or more of the 5 areas are not used, the other areas expand to fill up the space. The Border Layout is the default layout for Frames. A container that uses the Flow Layout can contain any number of sub-components. They are organized in rows across the container. If there is not enough room for all of the subcomponents in a single row, then a second row is started, etc. Whether the items in an incomplete row are left-justified, right-justified, or centered can be specified when the Flow Layout is selected (see below). The Flow Layout is the default layout for Panels. The sub-components in a Grid Layout are organized into an N-by-M grid, where N and M are specified when the Grid Layout is selected. When a component is added to a container using the Grid Layout, it goes into the next column of the current row (or the first column of the next row if the current row is full); it is not possible to leave empty spaces or to add components out of order. A particular layout is selected for a container using the container's setLayout method. The argument to setLayout is a LayoutManager object. For example, the statement: buttonPanel.setLayout(new GridLayout(1,2)); sets the layout of the buttonPanel object to be a Grid Layout that contains 1 row and 2 columns. The statement: buttonPanel.setLayout(new FlowLayout( FlowLayout.CENTER ));
sets the layout of the buttonPanel object to be a Flow Layout in which items in an incomplete row are centered. TEST YOURSELF #3 Modify the ChangeFont program in each of the following ways: 1. Change the program so that the Frame it creates uses a Flow Layout (rather than the default Border Layout). (Remember that when you add components to the Frame under the Flow Layout you simply add them, you do not specify which area of the Frame they should occupy.) Run the two versions of ChangeFont, and compare what happens when you resize the newly created window. 2. Change the program so that the Frame it creates contains Checkboxes to control the fonts rather than a List. In particular, the center part of the Frame should contain a Panel that in turn contains 3 Checkboxes labeled: "bold font", "italic font", and "bold italic font". Make the 3 Checkboxes part of a CheckboxGroup so that at most one Checkbox can be in the "selected" position. (If no Checkboxes are selected, the font should be plain font.) solution
Graphics
Java.awt also provides support for simple graphics. In particular, every component has a paint method, which is called to display the component on the screen. It is possible to redefine the paint method for any class that extends a component class. For example, here is a program that defines the class Draw as a subclass of Frame, and redefines the paint method to draw a stick figure:
import java.awt.*; public class Draw extends Frame { // constructor public Draw() { // set the size of the frame and make it visible setSize( 300, 300 ); setVisible( true ); } public void paint(Graphics g) { g.setColor(Color.red); g.fillArc(150, 40, 20, 20, 0, 360); g.drawLine(160, 60, 160, 110);
g.drawLine(160, 110, 145, 160); g.drawLine(160, 110, 175, 160); g.drawLine(130, 75, 190, 75); g.setColor(Color.green); g.drawString("This is George", 110, 180); } // main public static void main( String[] args ) { new Draw(); }
Note that the paint method has one parameter, a Graphics object. In the example, the actual parameter is the "graphics context" associated with the Frame. By calling the various draw methods of the Graphics object, you can cause various shapes to appear in the Frame. As mentioned above, you can redefine the paint method of any subclass of a Component. So, for example, you could create a button with the stick figure on it (in addition to the button label) by defining a subclass of Button, and redefining its paint method:
import java.awt.*; class MyButton extends Button { // 1-arg constructor: s is the button label public MyButton(String s) { super(s); } public void paint(Graphics g) { g.setColor(Color.red); g.fillArc(150, 20, 20, 20, 0, 360); g.drawLine(160, 40, 160, 90); g.drawLine(160, 90, 145, 140); g.drawLine(160, 90, 175, 140); g.drawLine(130, 55, 190, 55); }
public class PaintButton extends Frame { private MyButton b = new MyButton("George"); // constructor public PaintButton() { // add the button add(b); // set the size of the frame and make it visible setSize( 300, 300 ); setVisible( true ); } // main public static void main( String[] args ) {
new PaintButton(); } }
More typically, a subclass of the Canvas component (a blank rectangle) is used for drawing.
// create a label containing "Hello World!", with the text centered text = new Label("Hello World!", Label.CENTER); // set the background and foreground colors of the label text.setBackground(Color.red); text.setForeground(Color.blue); // add the label to the Frame add(text); // set the size of the frame and make it visible setSize( 300, 100 ); setVisible( true ); } // main public static void main( String[] args ) { new HelloWorld2(); }
Test Yourself #2
// modified Hello1 program that includes a TextField instead of a Label, and // a "reset" button to change the displayed text back to "Hello World!" import java.awt.*; import java.awt.event.*; public class Hello1 extends Frame { private TextField text; private Button resetButton; // constructor public Hello1() { // create a TextField containing "Hello World!", // and add it to the top of the Frame text = new TextField("Hello World!"); add(text, "North"); // create the reset button and add it to the bottom of the Frame resetButton = new Button("reset"); add(resetButton, "South"); // create a listener for the reset button, and associate that // listener with the reset button ResetButtonListener resetListener = new ResetButtonListener(); resetButton.addActionListener(resetListener); // set the size of the frame and make it visible setSize( 300, 100 );
setVisible(true); } // define the listener for the reset button class ResetButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the reset button is clicked; // change the text back to "Hello World!" text.setText("Hello World!"); } } public static void main(String [] args) { new Hello1(); } }
Test Yourself #3
Question 1. // modified ChangeFont program that uses a Flow Layout for the Frame // instead of the default Border Layout import java.awt.*; import java.awt.event.*; public class ChangeFont1 extends Frame { private Font plainFont, boldFont, italicFont, boldItalicFont; private List fontList; private Panel buttonPanel; private Button resetButton, exitButton; private Label msgLabel; // constructor public ChangeFont1() { // give this frame a Flow Layout setLayout( new FlowLayout(FlowLayout.LEFT)); // create the actual fonts to be used plainFont = new Font("plain", Font.PLAIN, 10); boldFont = new Font("bold", Font.BOLD, 10); italicFont = new Font("italic", Font.ITALIC, 10); boldItalicFont = new Font("bolditalic", Font.BOLD + Font.ITALIC, 10); // create the Label whose font will change and add it to the frame msgLabel = new Label("This text will change font"); msgLabel.setFont(plainFont); add(msgLabel); // create the List of fonts and add it to the Frame fontList = new List();
fontList.add("bold"); fontList.add("italic"); fontList.add("bold italic"); add(fontList); // create the reset and exit buttons resetButton = new Button("reset font"); exitButton = new Button("exit"); // create a panel to hold the two buttons; // put the buttons on the panel and add the panel to the Frame buttonPanel = new Panel(); buttonPanel.setLayout(new GridLayout(1,2)); buttonPanel.add(resetButton); buttonPanel.add(exitButton); add(buttonPanel); // create listeners for the list of fonts and for the two buttons ResetButtonListener resetListener = new ResetButtonListener(); ExitButtonListener exitListener = new ExitButtonListener(); ListItemListener listListener = new ListItemListener(); // use the addXXXListener methods of the font list and the buttons // to associate each listener with the appropriate object resetButton.addActionListener(resetListener); exitButton.addActionListener(exitListener); fontList.addItemListener(listListener); // arrange the components in the Frame and make it visible pack(); setVisible(true); } class ResetButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the reset button is clicked; // change the font back to plain msgLabel.setFont(plainFont); } } class ExitButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the exit button is clicked; // end execution of this program System.exit(0); } } class ListItemListener implements ItemListener { public void itemStateChanged(ItemEvent e) { // this method will be called when one item on the list is // selected using the mouse;
// get the selected list item and set the label's font accordingly String S = fontList.getSelectedItem(); if (S.equals("bold")) { msgLabel.setFont(boldFont); } else if (S.equals("italic")) { msgLabel.setFont(italicFont); } else if (S.equals("bold italic")) { msgLabel.setFont(boldItalicFont); } } } public static void main(String [] args) { new ChangeFont1(); } } Question 2. // modified ChangeFon program that uses checkboxes instead of a list to // display the font choices import java.awt.*; import java.awt.event.*; public class ChangeFont2 extends Frame { private Font plainFont, boldFont, italicFont, boldItalicFont; private Panel buttonPanel; private Panel fontPanel; private Button resetButton, exitButton; private Label msgLabel; private CheckboxGroup checkBoxes; private Checkbox boldBox, italicBox, boldItalicBox; // constructor public ChangeFont2() { // create the actual fonts to be used plainFont = new Font("plain", Font.PLAIN, 10); boldFont = new Font("bold", Font.BOLD, 10); italicFont = new Font("italic", Font.ITALIC, 10); boldItalicFont = new Font("bolditalic", Font.BOLD + Font.ITALIC, 10); // create the Label whose font will change and add it at the // top of the Frame msgLabel = new Label("This text will change font"); msgLabel.setFont(plainFont); add(msgLabel, "North"); // create a Panel to hold the CheckboxGroup // and add it in the middle of the Frame fontPanel = new Panel();
add(fontPanel, "Center"); // create the CheckboxGroup and its Checkboxes // all Checkboxes are initially "off" (unpushed) checkBoxes = new CheckboxGroup(); boldBox = new Checkbox("bold font", checkBoxes, false); italicBox = new Checkbox("italic font", checkBoxes, false); boldItalicBox = new Checkbox("bold italic font", checkBoxes, false); // add the Checkboxs to the Panel fontPanel.add(boldBox); fontPanel.add(italicBox); fontPanel.add(boldItalicBox); // create the reset and exit buttons resetButton = new Button("reset font"); exitButton = new Button("exit"); // create a panel to hold the two buttons; // put the buttons on the panel and add the panel to the bottom of // the Frame buttonPanel = new Panel(); buttonPanel.setLayout(new GridLayout(1,2)); buttonPanel.add(resetButton); buttonPanel.add(exitButton); add(buttonPanel, "South"); // create listeners for the checkboxes and for the two buttons ResetButtonListener resetListener = new ResetButtonListener(); ExitButtonListener exitListener = new ExitButtonListener(); BoxListener boxListener = new BoxListener(); // use the addXXXListener methods of the checkboxes and the buttons // to associate each listener with the appropriate object resetButton.addActionListener(resetListener); exitButton.addActionListener(exitListener); boldBox.addItemListener(boxListener); italicBox.addItemListener(boxListener); boldItalicBox.addItemListener(boxListener); // arrange the components in the Frame and make it visible pack(); setVisible(true); } class ResetButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the reset button is clicked; // change the font back to plain msgLabel.setFont(plainFont); } }
class ExitButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { // this method will be called when the exit button is clicked; // end execution of this program System.exit(0); } } class BoxListener implements ItemListener { public void itemStateChanged(ItemEvent e) { // this method will be called when one checkbox is // pushed using the mouse; // determine which checkbox it was and set the font accordingly Checkbox source = (Checkbox)e.getSource(); if (source == boldBox) { msgLabel.setFont(boldFont); } else if (source == italicBox) { msgLabel.setFont(italicFont); } else if (source == boldItalicBox) { msgLabel.setFont(boldItalicFont); } else { // shouldn't get here System.err.println("unknown checkbox"); System.exit(1); } } } public static void main(String [] args) { new ChangeFont2(); } }