The document discusses arrays in Java. It explains that an array is a container that holds a fixed number of values of the same type. Each element in an array has a numeric index, starting from 0. Arrays are declared with the element type followed by square brackets. The new operator is used to create an array and allocate memory. Individual elements are accessed using their index within square brackets. The length property returns the size of the array. The System class contains methods like arraycopy() for copying arrays. The Arrays class provides additional utility methods for common array operations like searching, sorting, and comparison.
Download as DOCX, PDF, TXT or read online on Scribd
0 ratings0% found this document useful (0 votes)
150 views
Java 2301
The document discusses arrays in Java. It explains that an array is a container that holds a fixed number of values of the same type. Each element in an array has a numeric index, starting from 0. Arrays are declared with the element type followed by square brackets. The new operator is used to create an array and allocate memory. Individual elements are accessed using their index within square brackets. The length property returns the size of the array. The System class contains methods like arraycopy() for copying arrays. The Arrays class provides additional utility methods for common array operations like searching, sorting, and comparison.
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 30
UNIT II
1. Explain array in detail.
An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created. After creation, its length is fixed. You have seen an example of arrays already, in the main method of the "Hello World!" application. This section discusses arrays in greater detail.
An array of 10 elements.
Each item in an array is called an element, and each element is accessed by its numerical index. As shown in the preceding illustration, numbering begins with 0. The 9th element, for example, would therefore be accessed at index 8. The following program, ArrayDemo, creates an array of integers, puts some values in the array, and prints each value to standard output.
class ArrayDemo { public static void main(String[] args) { // declares an array of integers int[] anArray;
// allocates memory for 10 integers anArray = new int[10];
// initialize first element anArray[0] = 100; // initialize second element anArray[1] = 200; // and so forth anArray[2] = 300; anArray[3] = 400; anArray[4] = 500; anArray[5] = 600; anArray[6] = 700; anArray[7] = 800; anArray[8] = 900; anArray[9] = 1000;
System.out.println("Element at index 0: " + anArray[0]); System.out.println("Element at index 1: " + anArray[1]); System.out.println("Element at index 2: " + anArray[2]); System.out.println("Element at index 3: " + anArray[3]); System.out.println("Element at index 4: " + anArray[4]); System.out.println("Element at index 5: " + anArray[5]); System.out.println("Element at index 6: " + anArray[6]); System.out.println("Element at index 7: " + anArray[7]); System.out.println("Element at index 8: " + anArray[8]); System.out.println("Element at index 9: " + anArray[9]); } } The output from this program is: Element at index 0: 100 Element at index 1: 200 Element at index 2: 300 Element at index 3: 400 Element at index 4: 500 Element at index 5: 600 Element at index 6: 700 Element at index 7: 800 Element at index 8: 900 Element at index 9: 1000 In a real-world programming situation, you would probably use one of the supported looping constructs to iterate through each element of the array, rather than write each line individually as in the preceding example. However, the example clearly illustrates the array syntax. You will learn about the various looping constructs (for, while, and do-while) in the Control Flow section. Declaring a Variable to Refer to an Array The preceding program declares an array (named anArray) with the following line of code: // declares an array of integers int[] anArray; Like declarations for variables of other types, an array declaration has two components: the array's type and the array's name. An array's type is written as type[], where type is the data type of the contained elements; the brackets are special symbols indicating that this variable holds an array. The size of the array is not part of its type (which is why the brackets are empty). An array's name can be anything you want, provided that it follows the rules and conventions as previously discussed in the naming section. As with variables of other types, the declaration does not actually create an array; it simply tells the compiler that this variable will hold an array of the specified type. Similarly, you can declare arrays of other types: byte[] anArrayOfBytes; short[] anArrayOfShorts; long[] anArrayOfLongs; float[] anArrayOfFloats; double[] anArrayOfDoubles; boolean[] anArrayOfBooleans; char[] anArrayOfChars; String[] anArrayOfStrings; You can also place the brackets after the array's name: // this form is discouraged float anArrayOfFloats[]; However, convention discourages this form; the brackets identify the array type and should appear with the type designation. Creating, Initializing, and Accessing an Array One way to create an array is with the new operator. The next statement in the ArrayDemo program allocates an array with enough memory for 10 integer elements and assigns the array to the anArray variable. // create an array of integers anArray = new int[10]; If this statement is missing, then the compiler prints an error like the following, and compilation fails: ArrayDemo.java:4: Variable anArray may not have been initialized. The next few lines assign values to each element of the array: anArray[0] = 100; // initialize first element anArray[1] = 200; // initialize second element anArray[2] = 300; // and so forth Each array element is accessed by its numerical index: System.out.println("Element 1 at index 0: " + anArray[0]); System.out.println("Element 2 at index 1: " + anArray[1]); System.out.println("Element 3 at index 2: " + anArray[2]); Alternatively, you can use the shortcut syntax to create and initialize an array: int[] anArray = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; Here the length of the array is determined by the number of values provided between braces and separated by commas. You can also declare an array of arrays (also known as a multidimensional array) by using two or more sets of brackets, such as String[][] names. Each element, therefore, must be accessed by a corresponding number of index values. In the Java programming language, a multidimensional array is an array whose components are themselves arrays. This is unlike arrays in C or Fortran. A consequence of this is that the rows are allowed to vary in length, as shown in the following MultiDimArrayDemo program: class MultiDimArrayDemo { public static void main(String[] args) { String[][] names = { {"Mr. ", "Mrs. ", "Ms. "}, {"Smith", "Jones"} }; // Mr. Smith System.out.println(names[0][0] + names[1][0]); // Ms. Jones System.out.println(names[0][2] + names[1][1]); } } The output from this program is: Mr. Smith Ms. Jones Finally, you can use the built-in length property to determine the size of any array. The following code prints the array's size to standard output: System.out.println(anArray.length); Copying Arrays The System class has an arraycopy() method that you can use to efficiently copy data from one array into another: public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) The two Object arguments specify the array to copy from and the array to copy to. The three int arguments specify the starting position in the source array, the starting position in the destination array, and the number of array elements to copy. The following program, ArrayCopyDemo, declares an array of char elements, spelling the word "decaffeinated." It uses the System.arraycopy() method to copy a subsequence of array components into a second array:
System.arraycopy(copyFrom, 2, copyTo, 0, 7); System.out.println(new String(copyTo)); } } The output from this program is: caffein Array Manipulations Arrays are a powerful and useful concept used in programming. Java SE provides methods to perform some of the most common manipulations related to arrays. For instance, the ArrayCopyDemo example uses the arraycopy() method of the System class instead of manually iterating through the elements of the source array and placing each one into the destination array. This is performed behind the scenes, enabling the developer to use just one line of code to call the method. For your convenience, Java SE provides several methods for performing array manipulations (common tasks, such as copying, sorting and searching arrays) in the java.util.Arrays class. For instance, the previous example can be modified to use the CopyOfRange() method of the java.util.Arrays class, as you can see in the ArrayCopyOfDemo example. The difference is that using the CopyOfRange() method does not require you to create the destination array before calling the method, because the destination array is returned by the method:
class ArrayCopyOfDemo { public static void main(String[] args) {
System.out.println(new String(copyTo)); } } As you can see, the output from this program is the same (caffein), although it requires fewer lines of code. Some other useful operations provided by methods in the java.util.Arrays class, are: Searching an array for a specific value to get the index at which it is placed (the binarySearch() method). Comparing two arrays to determine if they are equal or not (the equals() method). Filling an array to place a specific value at each index (the fill() method). Sorting an array into ascending order. This can be done either sequentially, using the sort() method, or concurrently, using the parallelSort() method introduced in Java SE 8. Parallel sorting of large arrays on multiprocessor systems is faster than sequential array sorting. 2. Multiplication of two matrix using java program.
import java.util.Scanner;
class MatrixMultiplication { public static void main(String args[]) { int m, n, p, q, sum = 0, c, d, k;
Scanner in = new Scanner(System.in); System.out.println("Enter the number of rows and columns of first matrix"); m = in.nextInt(); n = in.nextInt();
int first[][] = new int[m][n];
System.out.println("Enter the elements of first matrix");
for ( c = 0 ; c < m ; c++ ) for ( d = 0 ; d < n ; d++ ) first[c][d] = in.nextInt();
System.out.println("Enter the number of rows and columns of second matrix"); p = in.nextInt(); q = in.nextInt();
if ( n != p ) System.out.println("Matrices with entered orders can't be multiplied with each other."); else { int second[][] = new int[p][q]; int multiply[][] = new int[m][q];
System.out.println("Enter the elements of second matrix");
for ( c = 0 ; c < p ; c++ ) for ( d = 0 ; d < q ; d++ ) second[c][d] = in.nextInt();
for ( c = 0 ; c < m ; c++ ) { for ( d = 0 ; d < q ; d++ ) { for ( k = 0 ; k < p ; k++ ) { sum = sum + first[c][k]*second[k][d]; }
multiply[c][d] = sum; sum = 0; } }
System.out.println("Product of entered matrices:-");
for ( c = 0 ; c < m ; c++ ) { for ( d = 0 ; d < q ; d++ ) System.out.print(multiply[c][d]+"\t");
System.out.print("\n"); } } } }
3. String in detail Strings, which are widely used in Java programming, are a sequence of characters. In the Java programming language, strings are objects. The Java platform provides the String class to create and manipulate strings. Creating Strings The most direct way to create a string is to write: String greeting = "Hello world!"; In this case, "Hello world!" is a string literala series of characters in your code that is enclosed in double quotes. Whenever it encounters a string literal in your code, the compiler creates a String object with its valuein this case, Hello world!. As with any other object, you can create String objects by using the new keyword and a constructor. The String class has thirteen constructors that allow you to provide the initial value of the string using different sources, such as an array of characters: char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.' }; String helloString = new String(helloArray); System.out.println(helloString); The last line of this code snippet displays hello.
Note: The String class is immutable, so that once it is created a String object cannot be changed. The String class has a number of methods, some of which will be discussed below, that appear to modify strings. Since strings are immutable, what these methods really do is create and return a new string that contains the result of the operation.
String Length Methods used to obtain information about an object are known as accessor methods. One accessor method that you can use with strings is the length() method, which returns the number of characters contained in the string object. After the following two lines of code have been executed, len equals 17: String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); A palindrome is a word or sentence that is symmetricit is spelled the same forward and backward, ignoring case and punctuation. Here is a short and inefficient program to reverse a palindrome string. It invokes the String method charAt(i), which returns the i th character in the string, counting from 0.
public class StringDemo { public static void main(String[] args) { String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); char[] tempCharArray = new char[len]; char[] charArray = new char[len];
// put original string in an // array of chars for (int i = 0; i < len; i++) { tempCharArray[i] = palindrome.charAt(i); }
String reversePalindrome = new String(charArray); System.out.println(reversePalindrome); } } Running the program produces this output: doT saw I was toD To accomplish the string reversal, the program had to convert the string to an array of characters (first for loop), reverse the array into a second array (second for loop), and then convert back to a string. The String class includes a method, getChars(), to convert a string, or a portion of a string, into an array of characters so we could replace the first for loop in the program above with palindrome.getChars(0, len, tempCharArray, 0); Concatenating Strings The String class includes a method for concatenating two strings: string1.concat(string2); This returns a new string that is string1 with string2 added to it at the end. You can also use the concat() method with string literals, as in: "My name is ".concat("Rumplestiltskin"); Strings are more commonly concatenated with the + operator, as in "Hello," + " world" + "!" which results in "Hello, world!" The + operator is widely used in print statements. For example: String string1 = "saw I was "; System.out.println("Dot " + string1 + "Tod"); which prints Dot saw I was Tod Such a concatenation can be a mixture of any objects. For each object that is not a String, its toString() method is called to convert it to a String.
Note: The Java programming language does not permit literal strings to span lines in source files, so you must use the + concatenation operator at the end of each line in a multi-line string. For example: String quote = "Now is the time for all good " + "men to come to the aid of their country."; Breaking strings between lines using the + concatenation operator is, once again, very common in print statements.
Creating Format Strings You have seen the use of the printf() and format() methods to print output with formatted numbers. The String class has an equivalent class method, format(), that returns a String object rather than a PrintStream object. Using String's static format() method allows you to create a formatted string that you can reuse, as opposed to a one-time print statement. For example, instead of System.out.printf("The value of the float " + "variable is %f, while " + "the value of the " + "integer variable is %d, " + "and the string is %s", floatVar, intVar, stringVar); you can write String fs; fs = String.format("The value of the float " + "variable is %f, while " + "the value of the " + "integer variable is %d, " + " and the string is %s", floatVar, intVar, stringVar); System.out.println(fs);
4. Package with example Packages are used in Java in order to prevent naming conflicts, to control access, to make searching/locating and usage of classes, interfaces, enumerations and annotations easier, etc. A Package can be defined as a grouping of related types(classes, interfaces, enumerations and annotations ) providing access protection and name space management. Some of the existing packages in Java are:: java.lang - bundles the fundamental classes java.io - classes for input , output functions are bundled in this package Programmers can define their own packages to bundle group of classes/interfaces, etc. It is a good practice to group related classes implemented by you so that a programmer can easily determine that the classes, interfaces, enumerations, annotations are related. Since the package creates a new namespace there won't be any name conflicts with names in other packages. Using packages, it is easier to provide access control and it is also easier to locate the related classes. Creating a package: When creating a package, you should choose a name for the package and put a package statement with that name at the top of every source file that contains the classes, interfaces, enumerations, and annotation types that you want to include in the package. The package statement should be the first line in the source file. There can be only one package statement in each source file, and it applies to all types in the file. If a package statement is not used then the class, interfaces, enumerations, and annotation types will be put into an unnamed package. Example: Let us look at an example that creates a package called animals. It is common practice to use lowercased names of packages to avoid any conflicts with the names of classes, interfaces. Put an interface in the package animals: /* File name : Animal.java */ package animals;
interface Animal { public void eat(); public void travel(); } Now, put an implementation in the same package animals: package animals;
/* File name : MammalInt.java */ public class MammalInt implements Animal{
public void eat(){ System.out.println("Mammal eats"); }
public void travel(){ System.out.println("Mammal travels"); }
public int noOfLegs(){ return 0; }
public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } } Now, you compile these two files and put them in a sub-directory called animals and try to run as follows: $ mkdir animals $ cp Animal.class MammalInt.class animals $ java animals/MammalInt Mammal eats Mammal travels The import Keyword: If a class wants to use another class in the same package, the package name does not need to be used. Classes in the same package find each other without any special syntax. Example: Here, a class named Boss is added to the payroll package that already contains Employee. The Boss can then refer to the Employee class without using the payroll prefix, as demonstrated by the following Boss class. package payroll;
public class Boss { public void payEmployee(Employee e) { e.mailCheck(); } } What happens if Boss is not in the payroll package? The Boss class must then use one of the following techniques for referring to a class in a different package. The fully qualified name of the class can be used. For example: payroll.Employee The package can be imported using the import keyword and the wild card (*). For example: import payroll.*; The class itself can be imported using the import keyword. For example: import payroll.Employee; 5. Java-Doc Comments with example Javadoc Comments are specific to the Java language and provide a means for a programmer to fully document his / her source code as well as providing a means to generate an Application Programmer Interface (API) for the code using the javadoc tool that is bundled with the JDK. These comments have a special format which we will discuss in this section and then in the following section we will look at how to use the javadoc tool to generate an API. The Format of Javadoc Comments A Javadoc comment precedes any class, interface, method or field declaration and is similar to a multi- line comment except that it starts with a forward slash followed by two atserisks (/**). The basic format is a description followed by any number of predefined tags. The entrie comment is indented to align with the source code directly beneath it and it may contain any valid HTML. Generally paragraphs should be separated or designated with the <p> tag. As an example consider: /** * A Container is an object that contains other objects. * @author Trevor Miller * @version 1.2 * @since 0.3 */ public abstract class Container {
/** * Return the number of elements contained in this container. * @return The number of objects contained */ public abstract int count();
/** * Clear all elements from this container. * This removes all contained objects. */ public abstract void clear();
/** * Accept the given visitor to visit all objects contained. * @param visitor The visitor to accept */ public abstract void accept(final Visitor visitor);
/** * Return an iterator over all objects conatined. * @return An iterator over all objects */ public abstract Iterator iterator();
/** * Determine whether this container is empty or not. * @return <CODE>true</CODE> if the container is empty: * <CODE>count == 0</CODE>, <CODE>false</CODE> * otherwise */ public boolean isEmpty() { return (this.count() == 0); }
/** * Determine whether this container is full. * @return <CODE>true</CODE> if conatiner is full, * <CODE>false</CODE> otherwise */ public boolean isFull() { return false; }
}
We will now discuss the descriptions of a Javadoc comment first before looking at the different tags and their uses. Descriptions The description should give a concise summary of the item being commented. It should be written in simple and clear English using correct spelling and grammar. Punctuation is required. There are some important style guidelines to bear in mind: The first sentence The first sentence of the description is the most important part of the entire description. It should be a short and concise summary of the item being commented. This is due to the fact that the Javadoc tool copies the first sentence to the appropriate class or package summary page, which implies that the first sentence should be compact and can stand on its own. Take a look at the example above again and you'll see that the first sentence is a brief descriptive summary of each item. The use of the <code>tag The use of the <code> tag is greatly encouraged and should be used for all Java keywords, names and code samples. you'll notice this in the comments of the last two methods of the class in the example above. Omission of parenthesis When referring to a method that has no parameters or a method which has multiple forms (method overloading) it is acceptable and even encouraged to simply omit the parenthesis. Consider the following example: The <code>add</code> method inserts items into the vector.
This is the correct way of doing it as opposed to the incorrect way in the next example:The <code>add()</code> method inserts items into the vector Explain Inheritance in detail. Different kinds of objects often have a certain amount in common with each other. Mountain bikes, road bikes, and tandem bikes, for example, all share the characteristics of bicycles (current speed, current pedal cadence, current gear). Yet each also defines additional features that make them different: tandem bicycles have two seats and two sets of handlebars; road bikes have drop handlebars; some mountain bikes have an additional chain ring, giving them a lower gear ratio. Object-oriented programming allows classes to inherit commonly used state and behavior from other classes. In this example, Bicycle now becomes the superclass of MountainBike, RoadBike, and TandemBike. In the Java programming language, each class is allowed to have one direct superclass, and each superclass has the potential for an unlimited number of subclasses:
The syntax for creating a subclass is simple. At the beginning of your class declaration, use the extends keyword, followed by the name of the class to inherit from: class MountainBike extends Bicycle { // new fields and methods defining a mountain bike would go here } This gives MountainBike all the same fields and methods as Bicycle, yet allows its code to focus exclusively on the features that make it unique. This makes code for your subclasses easy to read. However, you must take care to properly document the state and behavior that each superclass defines, since that code will not appear in the source file of each subclass. Java Inheritance defines an is-a relationship between a superclass and its subclasses. This means that an object of a subclass can be used wherever an object of the superclass can be used. Class Inheritance in java mechanism is used to build new classes from existing classes. The inheritance relationship is transitive: if class x extends class y, then a class z, which extends class x, will also inherit from class y. For example a car class can inherit some properties from a General vehicle class. Here we find that the base class is the vehicle class and the subclass is the more specific car class. A subclass must use the extends clause to derive from a super class which must be written in the header of the subclass definition. The subclass inherits members of the superclass and hence promotes code reuse. The subclass itself can add its own new behavior and properties. The java.lang.Object class is always at the top of any Class inheritance hierarchy.
weight = m; } public static void main(String args[]) { MatchBox mb1 = new MatchBox(10, 10, 10, 10); mb1.getVolume(); System.out.println("width of MatchBox 1 is " + mb1.width); System.out.println("height of MatchBox 1 is " + mb1.height); System.out.println("depth of MatchBox 1 is " + mb1.depth); System.out.println("weight of MatchBox 1 is " + mb1.weight); } } Output Volume is : 1000.0 width of MatchBox 1 is 10.0 height of MatchBox 1 is 10.0 depth of MatchBox 1 is 10.0 weight of MatchBox 1 is 10.0 1. Private members of the superclass are not inherited by the subclass and can only be indirectly accessed. 2. Members that have default accessibility in the superclass are also not inherited by subclasses in other packages, as these members are only accessible by their simple names in subclasses within the same package as the superclass. 3. Since constructors and initializer blocks are not members of a class, they are not inherited by a subclass. 4. A subclass can extend only one superclass class Vehicle {
// Instance fields int noOfTyres; // no of tyres private boolean accessories; // check if accessorees present or not protected String brand; // Brand of the car // Static fields private static int counter; // No of Vehicle objects created // Constructor Vehicle() { System.out.println("Constructor of the Super class called"); noOfTyres = 5; accessories = true; brand = "X"; counter++; } // Instance methods public void switchOn() { accessories = true; } public void switchOff() { accessories = false; } public boolean isPresent() { return accessories; } private void getBrand() { System.out.println("Vehicle Brand: " + brand); } // Static methods public static void getNoOfVehicles() { System.out.println("Number of Vehicles: " + counter); } }
public static void main(String[] args) { new Car().printCarInfo(); } }
Output Constructor of the Super class called Car number: 10 No of Tyres: 5 accessories: true Brand: X Number of Vehicles: 1 6. Polymorphism o The dictionary definition of polymorphism refers to a principle in biology in which an organism or species can have many different forms or stages. This principle can also be applied to object-oriented programming and languages like the Java language. Subclasses of a class can define their own unique behaviors and yet share some of the same functionality of the parent class. o Polymorphism can be demonstrated with a minor modification to the Bicycle class. For example, a printDescription method could be added to the class that displays all the data currently stored in an instance. o public void printDescription(){ o System.out.println("\nBike is in gear " + this.gear + " with a cadence of " + o this.cadence + " and travelling at a speed of " + this.speed + ". "); o } o To demonstrate polymorphic features in the Java language, extend the Bicycle class with a MountainBike and a RoadBike class. For MountainBike, add a field for suspension, which is a String value that indicates if the bike has a front shock absorber, Front. Or, the bike has a front and back shock absorber, Dual. o Here is the updated class: o public class MountainBike extends Bicycle{ o private String suspension; o o public MountainBike(int startCadence, int startSpeed, int startGear, String suspensionType){ o super(startCadence, startSpeed, startGear); o this.setSuspension(suspensionType); o } o o public String getSuspension(){ o return this.suspension; o } o o public void setSuspension(String suspensionType){ o this.suspension = suspensionType; o } o o public void printDescription(){ o super.printDescription(); o System.out.println("The MountainBike has a " + getSuspension() o + " suspension."); o } o } o Note the overridden printDescription method. In addition to the information provided before, additional data about the suspension is included to the output. o Next, create the RoadBike class. Because road or racing bikes have skinny tires, add an attribute to track the tire width. Here is the RoadBike class: o public class RoadBike extends Bicycle{ o private int tireWidth; // In millimeters (mm) o o public RoadBike(int startCadence, int startSpeed, int startGear, int newTireWidth){ o super(startCadence, startSpeed, startGear); o this.setTireWidth(newTireWidth); o } o o public int getTireWidth(){ o return this.tireWidth; o } o o public void setTireWidth(int newTireWidth){ o this.tireWidth = newTireWidth; o } o o public void printDescription(){ o super.printDescription(); o System.out.println("The RoadBike has " + getTireWidth() o + " MM tires."); o } o o } o Note that once again, the printDescription method has been overridden. This time, information about the tire width is displayed. o To summarize, there are three classes: Bicycle, MountainBike, and RoadBike. The two subclasses override the printDescription method and print unique information. o Here is a test program that creates three Bicycle variables. Each variable is assigned to one of the three bicycle classes. Each variable is then printed. o public class TestBikes { o public static void main(String[] args){ o Bicycle bike01, bike02, bike03; o o bike01 = new Bicycle(20, 10, 1); o bike02 = new MountainBike(20, 10, 5, "Dual"); o bike03 = new RoadBike(40, 20, 8, 23); o o bike01.printDescription(); o bike02.printDescription(); o bike03.printDescription(); o o } o } o The following is the output from the test program: o Bike is in gear 1 with a cadence of 20 and travelling at a speed of 10. o o Bike is in gear 5 with a cadence of 20 and travelling at a speed of 10. o The MountainBike has a Dual suspension. o o Bike is in gear 8 with a cadence of 40 and travelling at a speed of 20. o The RoadBike has 23 MM tires. o The Java virtual machine (JVM) calls the appropriate method for the object that is referred to in each variable. It does not call the method that is defined by the variable's type. This behavior is referred to as virtual method invocation and demonstrates an aspect of the important polymorphism features in the Java language.
7. Dynamic binding and static binding have to do with inheritance. In Java, any derived class object can be assigned to a base class variable. For instance, if you have a class named Animal from which you derived the class Dog, you can do this:
Animal myAnimal = new Dog();
The variable on the left is type Animal, but the object on the right is type Dog. As long as the variable on the left is a base class of Dog, you are allowed to do that. Being able to do assignments like that sets up what is called "polymorphic behavior": if the Dog class has a method that is the same as a method in the Animal class, then the version of the method in the Dog class will be called. For instance, if both classes define a method called show(), and you do this:
myAnimal.show();
the version of show() in the Dog class will be called. Even though you are using an Animal variable type to call the method show(), the version of show() in the Animal class won't be executed. Instead, it is the version of show() in the Dog class that will be executed. The type of the object that is assigned to the Animal variable determines the method that is called.
So, when the compiler scans the program and sees a statement like this:
myAnimal.show();
it knows that myAnimal is of type Animal, but the compiler also knows that myAnimal can be a reference to any class derived from Animal. Therefore, the compiler doesn't know what version of show() that statement is calling. It's not until the assignment:
Animal myAnimal = new Dog();
is executed that the version of show() is determined. Since the assignment doesn't occur until runtime, it's not until runtime that the correct version of show() is known. That is known as "dynamic binding" or "late binding": it's not until your program performs some operation at runtime that the correct version of a method can be determined. In Java, most uses of inheritance involve dynamic binding.
"Static binding" or "early binding" occurs when the compiler can readily determine the correct version of something during compile time, i.e. before the program is executed. In Java, member variables have static binding because Java does not allow for polymorphic behavior with member variables. That means if both the Animal class and the Dog class have a member variable with the same name, it's the base class version that is used. For instance, if you set up the necessary ingredients for polymorphic behavior:
Animal myAnimal = new Dog();
and both the Animal and Dog classes have a String member variable 'type', then if you do this:
String str = myAnimal.type;
the value of 'type' can be fully determined by the compiler. Because polymorphic behavior is not allowed for member variables, that statement is referring to the value of 'type' in the Animal class--not the Dog's value for 'type'. The result is: with member variables, it's the type of the variable(e.g. myAnimal) that determines which version is called--not the type of the object the variable refers to(e.g. Dog). When the compiler is able to figure out the correct version of something during compilation, that's known as static binding. 8. Here is an example of both dynamic and static binding: class Animal { public String type = "mammal";
public void show() { System.out.println("The animal is a: " + type); } }
class Dog extends Animal { public String type; //same member variable name as in base class
public Dog(String type) { this.type = type; }
public void show() //same method signature as in base class { System.out.println("The dog is a: " + type); } }
public class DemoStaticBinding { public static void main(String[] args) { Animal doggie = new Dog("daschund"); doggie.show(); // "The dog is a: daschund" (dynamic binding) System.out.println("The type is: " + doggie.type); //"The type is: mammal" (static binding) } } 9. final Keyword The final keyword has slightly different meanings depending on the context, but in general it says This cannot be changed. You might want to prevent changes for two reasons: design or efficiency. Because these two reasons are quite different, its possible to misuse the final keyword. The following sections discuss the three places where final can be used: for data, methods and for a class. Final data Many programming languages have a way to tell the compiler that a piece of data is constant. A constant is useful for two reasons: 1. It can be a compile-time constant that wont ever change. 2. It can be a value initialized at run-time that you dont want changed. In the case of a compile-time constant the compiler is allowed to fold the constant value into any calculations in which its used; that is, the calculation can be performed at compile time, eliminating some run-time overhead. In Java, these sorts of constants must be primitives and are expressed using the final keyword. A value must be given at the time of definition of such a constant. A field that is both static and final has only one piece of storage that cannot be changed. When using final with object handles rather than primitives the meaning gets a bit confusing. With a primitive, final makes the value a constant, but with an object handle, final makes the handle a constant. The handle must be initialized to an object at the point of declaration, and the handle can never be changed to point to another object. However, the object can be modified; Java does not provide a way to make any arbitrary object a constant. (You can, however, write your class so that objects have the effect of being constant.) This restriction includes arrays, which are also objects. Heres an example that demonstrates final fields: //: FinalData.java // The effect of final on fields
class Value { int i = 1; }
public class FinalData { // Can be compile-time constants final int i1 = 9; static final int I2 = 99; // Typical public constant: public static final int I3 = 39; // Cannot be compile-time constants: final int i4 = (int)(Math.random()*20); static final int i5 = (int)(Math.random()*20);
Value v1 = new Value(); final Value v2 = new Value(); static final Value v3 = new Value(); //! final Value v4; // Pre-Java 1.1 Error: // no initializer // Arrays: final int[] a = { 1, 2, 3, 4, 5, 6 };
public void print(String id) { System.out.println( id + ": " + "i4 = " + i4 + ", i5 = " + i5); } public static void main(String[] args) { FinalData fd1 = new FinalData(); //! fd1.i1++; // Error: can't change value fd1.v2.i++; // Object isn't constant! fd1.v1 = new Value(); // OK -- not final for(int i = 0; i < fd1.a.length; i++) fd1.a[i]++; // Object isn't constant! //! fd1.v2 = new Value(); // Error: Can't //! fd1.v3 = new Value(); // change handle //! fd1.a = new int[3];
fd1.print("fd1"); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData(); fd1.print("fd1"); fd2.print("fd2"); } } ///:~ Since i1 and I2 are final primitives with compile- time values, they can both be used as compile-time constants and are not different in any important way. I3 is the more typical way youll see such constants defined: public so theyre usable outside the package, static to emphasize that theres only one, and final to say that its a constant. Note that final static primitives with constant initial values (that is, compile-time constants) are named with all capitals by convention. Also note that i5 cannot be known at compile time, so it is not capitalized. Just because something is final doesnt mean that its value is known at compile-time. This is demonstrated by initializing i4 and i5 at run-time using randomly generated numbers. This portion of the example also shows the difference between making a final value static or non- static. This difference shows up only when the values are initialized at run-time, since the compile-time values are treated the same by the compiler. (And presumably optimized out of existence.) The difference is shown in the output from one run: fd1: i4 = 15, i5 = 9 Creating new FinalData fd1: i4 = 15, i5 = 9 fd2: i4 = 10, i5 = 9
UNIT 3
The Object class The Object class sits at the top of the class hierarchy tree in the Java development environment. Every class in the Java system is a descendent (direct or indirect) of the Object class. The Object class defines the basic state and behavior that all objects must have, such as the ability to compare oneself to another object, to convert to a string, to wait on a condition variable, to notify other objects that a condition variable has changed, and to return the object's class. The equals Method Use the equals to compare two objects for equality. This method returns true if the objects are equal, false otherwise. Note that equality does not mean that the objects are the same object. Consider this code that tests two Integers, one and anotherOne, for equality: Integer one = new Integer(1), anotherOne = new Integer(1);
if (one.equals(anotherOne))
System.out.println("obje cts are equal"); This code will display objects are equal even though one and anotherOne reference two different, distinct objects. They are considered equal because they contain the same integer value. Your classes should override this method to provide an appropriate equality test. Your equals method should compare the contents of the objects to see if they are functionally equal and return true if they are. The getClass Method The getClass method is a final method (cannot be overridden) that returns a runtime representation of the class of this object. This method returns a Class object. You can query the Class object for a variety of information about the class, such as its name, its superclass, and the names of the interfaces that it implements. The following method gets and displays the class name of an object: void PrintClassName(Object obj) {
System.out.println("The Object's class is " + obj.getClass().getName() ); } One handy use of the getClass method is to create a new instance of a class without knowing what the class is at compile time. This sample method creates a new instance of the same class as obj which can be any class that inherits from Object (which means that it could be any class): Object createNewInstanceOf(Ob ject obj) { return obj.getClass().newInstan ce(); } The toString Method Object's toString method returns a String representation of the object. You can use toString to display an object. For example, you could display a String representation of the current Thread like this: System.out.println(Threa d.currentThread().toStrin g()); The String representation for an object is entirely dependent on the object. The String representation of an Integer object is the integer value displayed as text. The String representation of a Thread object contains various attributes about the thread, such as its name and priority. For example, the previous of code above display the following: Thread[main,5,main] The toString method is very useful for debugging and it would behoove you to override this method in all your classes. Object Methods Covered I n Other Lessons or Sections The Object class provides a method, finalize that cleans up an object before its garbage collected. This method's role during garbage collection is discussed in this lesson in Cleaning Up Unused Objects. Also, Writing a finalize Method shows you how to write override the finalize method to handle the finalization needs for you classes. The Object class also provides five methods that are critical when writing multithreaded Java programs: notify notifyAll wait (three versions) These methods help you ensure that your threads are synchronized and are covered in Threads of Control . Take particular note of the page titled Synchronizing Threads. Reflection Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them. The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program. One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.
A Simple Example To see how reflection works, consider this simple example:
import java.lang.reflect.*;
public class DumpMethods { public static void main(String args[]) { try { Class c = Class.forName(args[0]); Method m[] = c.getDeclaredMethods(); for (int i = 0; i < m.length; i++) System.out.println(m[i].toString()); } catch (Throwable e) { System.err.println(e); } } }
For an invocation of: java DumpMethods java.util.Stack the output is: public java.lang.Object java.util.Stack.push( java.lang.Object) public synchronized java.lang.Object java.util.Stack.pop() public synchronized java.lang.Object java.util.Stack.peek() public boolean java.util.Stack.empty() public synchronized int java.util.Stack.search(java.lang.Object)
Interfaces In Java an interface is similar to an abstract class in that its members are not implemented. In interfaces, _none_ of the methods are implemented. There is no code at all associated with an interface. For example, from my personal library: public interface Comparable { boolean less(Object m); boolean greater(Object m); boolean lessEqual(Object m); boolean greaterEqual(Object m); } All instance methods are implicitly public and abstract. You can mark them as such, but are discouraged from doing so as the marking is considered obsolete practice. The interfaces themselves need not be public and several interfaces in the standard libraries are not public and thus used only internally. An interface creates a protocol that classes may implement. Note that one can extend an interface (to get a new interface) just as you can extend a class. One can actually extend several interfaces. Interfaces thus enjoy the benefits of multiple inheritance. (Classes do not.) There are almost no disadvantages to multiple inheritance of interface (small name conflict problems are one exception). There are large disadvantages to multiple inheritance of implementation as in C++. These include efficiency considerations as well as the semantic difficulty of determining just what code will be executed in some circumstances. The Polynomial class that implements Comparable will need to implement all of the functions declared in the interface. public class Polynomial implements Comparable { . . . boolean less(Object m){ . . . } boolean greater(Object m){ . . . } boolean lessEqual(Object m){ . . . } boolean greaterEqual(Object m){ . . . }
Polynomial multiply(Polynomial P){ . . . } . . . } A class may choose to implement any number of interfaces. A class that implements an interface must provide bodies for all methods of that interface. Also, I expect that an abstract class can choose to implement part of an interface leaving the rest for non-abstract subclasses. I can't find this in the documentation, however. Anyone that needs to know can, of course, construct a simple example and try it to see if the compilers accept it. I call this technique "probing" and use it often when I'm not sure about how something works. In Java, with its more complete definition than other languages, this should be an even more valuable technique, since compilers should differ very little (actually not at all). The usefulness of interfaces goes far beyond simply publishing protocols for other programmers. Any function can have parameters that are of interface type. Any object from a class that implements the interface may be passed as an argument. class Foo { Vector bar(Vector v, Comparable c){...} ... } One can apply bar to a Vector and a Polynomial, since Polynomial implements Comparable. The body of instance method (member function) bar will only be able to apply Comparable methods to parameter c. Dynamic Binding assures that the actual class methods for the object passed will be applied. Other instance methods of objects passed, such as the multiply method from Polynomial cannot be used within bar. Therefore the bar function is polymorphic in its second parameter as many actual types may be passed for this value. Note that classes implementing Comparable don't need to be otherwise related to each other. This means that a class that uniformly uses an interface type for some method parameters, behaves very much like a class template in C++. We can also have variables (not just parameters) of type Comparable, or in general, of any interface type. These variables may refer to any object from any class that implements the interface. These variables may have only members defined within the interface applied to them however. They may also be passed to methods that name that interface as a parameter type. If you have a variable of some interface type and you know that it refers to an object of a specific class, say Polynomial, then you can cast it to Polynomial. A run-time check will be inserted to guarantee correctness. If you need to check the type of any reference, you can use the instanceof operator. Use of instanceof should be rare. If you find yourself using it often, you haven't yet absorbed object-oriented programming, and the power of the dynamic binding principle. If you cast a reference incorrectly, then at runtime the ClassCastException will be thrown. Note _importantly_ that a cast can never make an incorrect program correct. It won't fix up problems. It simply affirms what must otherwise be true. Notice that C++ templates are a form of _implicit_ interface. When you define a class template in C++ and then use the template parameter in the body of the class, the uses of that name impose requirements on the actual arguments that can be used to instantiate the template. If you apply operator< to such a parameter, then the actual argument needs to support operator<. In Java we have the advantage that such interfaces are explicit. We name the interface and the interface defines a list of requirements that the objects must implement. Note, however, that adhering to an interface requires saying that the class implements it in its class header. Adherence to interfaces is, therefore, explicit rather than implicit as with C++ templates. A method in Java that has a parameter of interface type is nearly the same as a function template in C++. A class that uses interfaces to type any variables or parameters behaves very similarly to a class template in C++. Just think of the interface names as if they were template arguments. If a "template" puts _no_ restrictions on an object, then just use type Object rather than an interface type. One of the very important interfaces in Java (in package java.util) is interface Enumeration { boolean hasMoreElements(); Object nextElement(); } If an object implements Enumeration, we can use the object to control a while loop. Enumeration e = ... while(e.hasMoreElements())
doSomethingWith(e.nextElement()); Collection objects like stacks and hashtables return enumeration objects so that we can process all elements of the collection with a while loop without needing access to the internal storage mechanism of the collection. Vector, for example has an instance method public final synchronized Enumeration elements(); that returns an enumeration over the elements of the Vector. Enumerations are called iterators in other languages. To implement an enumeration properly, you must guarantee that each call of nextElement will return an element of the collection not yet returned, and that hasMoreElements will be true if and only if not all elements have yet been returned by nextElement. Therefore to print all elements in Vector V, you can write Enumeration e = V.elements(); while(e.hasMoreElements()) println("Value is " + e.nextElement()); Object Cloning The clone() Method In Java, the way to make an identical copy of an object is to invoke clone() on that object. When you invoke clone(), it should either: 1. return an Object reference to a copy of the object upon which it is invoked, or 2. throw CloneNotSupportedExce ption Because clone() is declared in class Object, it is inherited by every Java object. Object's implementation of clone() does one of two things, depending upon whether or not the object implements the Cloneable interface. If the object doesn't implement the Cloneable interface, Object's implementation of clone() throws a CloneNotSupportedException. Otherwise, it creates a new instance of the object, with all the fields initialized to values identical to the object being cloned, and returns a reference to the new object. The Cloneable interface doesn't have any members. It is an empty interface, used only to indicate cloning is supported by a class. Class Object doesn't implement Cloneable. To enable cloning on a class of objects, the class of the object itself, or one of its superclasses other than Object, must implement the Cloneable interface. In class Object, the clone() method is declared protected. If all you do is implement Cloneable, only subclasses and members of the same package will be able to invoke clone() on the object. To enable any class in any package to access the clone() method, you'll have to override it and declare it public, as is done below. (When you override a method, you can make it less private, but not more private. Here, the protected clone() method in Object is being overridden as a public method.) // In Source Packet in file clone/ex1/CoffeeCup.java class CoffeeCup implements Cloneable {
private int innerCoffee;
public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { // This should never happen throw new InternalError(e.toString()); } }
public void add(int amount) { innerCoffee += amount; }
public int releaseOneSip(int sipSize) { int sip = sipSize; if (innerCoffee < sipSize) { sip = innerCoffee; } innerCoffee -= sip; return sip; }
public int spillEntireContents() { int all = innerCoffee; innerCoffee = 0; return all; } } You could make a copy of this CoffeeCup class, which implements Cloneable, as follows: // In Source Packet in file clone/ex1/Example1.java class Example1 { public static void main(String[] args) {
CoffeeCup original = new CoffeeCup(); original.add(75); // Original now contains 75 ml of coffee CoffeeCup copy = (CoffeeCup) original.clone(); copy.releaseOneSip(25); // Copy now contains 50 ml of coffee
// Figure 15-1 shows the heap at this point in the program
int origAmount = original.spillEntireContents(); int copyAmount = copy.spillEntireContents(); System.out.println("Original has " + origAmount + " ml of coffee."); System.out.println("Copy has " + copyAmount + " ml of coffee."); } } In this example, a new CoffeeCup object is instantiated and given an initial 75 ml of coffee. The clone() method is then invoked on the CoffeeCup object. Because class CoffeeCup declares a clone() method, that method is executed when clone() is invoked on the CoffeeCup object referred to by the original reference. CoffeeCup's clone() does just one thing: invoke the clone() method in CoffeeCup's superclass, Object. The first thing Object's clone() does is check to see whether the object's class implements the Cloneable interface. This test passes because CoffeeCup, the object's class, does indeed implement Cloneable. The clone() method then creates a new instance of CoffeeCup, and initializes its one field, innerCoffee, to 75--the same value it has in the CoffeeCup object being cloned. Object's clone()returns a reference to the new object, which is then returned by CoffeeCup's clone(). The reference returned by clone() refers to a CoffeeCup object, but the reference itself is of type Object. The code above downcasts the returned reference from Object to CoffeeCup before assigning it to local variable copy. At this point, both CoffeeCup objects-- original and copy-- contain 75 ml of coffee. Finally, 25 ml is removed from the copy, so it ends up with only 50 ml of coffee. A graphical representation of the result inside the Java Virtual Machine of executing the first four statements in main() is shown in Figure 15-1. (As mentioned in the last chapter, the native pointer to class information shown here is just one potential way a Java Virtual Machine could connect instance data to its class information.)
Figure 15-1. Cloning a CoffeeCup. CoffeeCup's clone() implementation surrounds the call to Object's clone implementation with a try block so it can catch CloneNotSupportedException. This exception should never actually be thrown by Object's clone(), because in this case, CoffeeCup correctly implements Cloneable. If CoffeeCup's clone() didn't explicitly catch it, however, then clone() would have to declare in a throws clause that it may throw CloneNotSupportedException. This would force any method invoking clone() on a CoffeeCup object to deal with the exception, either by explicitly catching it or declaring it in their own throws clause. Thus, CoffeeCup's clone() catches CloneNotSupportedException to make it simpler for other methods to invoke clone() on a CoffeeCup.
Inner Classes Static Inner Classes Consider the following Java code fragment: public class A { int y;
public static class B { int x;
void f () {} } } This fragment defines the class A which contains an static inner class B. A static inner class behaves like any ``outer'' class. It may contain methods and fields, and it may be instantiated like this: A.B object = new A.B (); This statement creates an new instance of the inner class B. Given such an instance, we can invoke the f method in the usual way: object.f(); Note, it is not necessarily the case that an instance of the outer class A exists even when we have created an instance of the inner class. Similarly, instantiating the outer class A does not create any instances of the inner class B. The methods of a static inner class may access all the members (fields or methods) of the inner class but they can access only static members (fields or methods) of the outer class. Thus, f can access the field x, but it cannot access the field y. Non-Static Inner Classes By default, an inner class is non-static: public class A { int y;
public class B { int x;
void f () {} } } This fragment defines the class A which contains a non- static inner class B. A non-static inner class can be instantiated only inside a non-static method of the outer class. This is because every instance of a non-static inner class must be associated with an instance of the outer class. In a sense, every instance of a non-static inner class exists ``inside'' an instance of the outer class. A single instance of the outer class may have associated with it more than one instance of the inner class. Because an instance of a non-static inner class has an associated instance of the outer class, the methods of the inner class can access directly any of the members (fields or methods) of the outer class instance. For example, the f method defined above can access both x and y directly. The Java keyword this can be used in a non-static method to refer to the current object instance. Thus in the method f, this refers to an instance of the inner B class. Every non-static inner class is associated with an instance of the outer class. To access the outer class instance inside the method f we write A.this.
Proxies Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. To create a proxy for some interface Foo: InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });
or more simply: Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
A dynamic proxy class (simply referred to as a proxy class below) is a class that implements a list of interfaces specified at runtime when the class is created, with behavior as described below. A proxy interface is such an interface that is implemented by a proxy class. A proxy instance is an instance of a proxy class. Each proxy instance has an associated invocation handler object, which implements the interface InvocationHandler. A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance, a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments. The invocation handler processes the encoded method invocation as appropriate and the result that it returns will be returned as the result of the method invocation on the proxy instance. A proxy class has the following properties: Proxy classes are public, final, and not abstract. The unqualified name of a proxy class is unspecified. The space of class names that begin with the string "$Proxy" should be, however, reserved for proxy classes. A proxy class extends java.lang.reflect.Proxy. A proxy class implements exactly the interfaces specified at its creation, in the same order. If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. Otherwise, the package of a proxy class is also unspecified. Note that package sealing will not prevent a proxy class from being successfully defined in a particular package at runtime, and neither will classes already defined by the same class loader and the same package with particular signers. Since a proxy class implements all of the interfaces specified at its creation, invoking getInterfaces on its Class object will return an array containing the same list of interfaces (in the order specified at its creation), invoking getMethods on its Class object will return an array of Method objects that include all of the methods in those interfaces, and invoking getMethod will find methods in the proxy interfaces as would be expected. The Proxy.isProxyClass method will return true if it is passed a proxy class-- a class returned by Proxy.getProxyClass or the class of an object returned by Proxy.newProxyInstance-- and false otherwise. The java.security.ProtectionDomain of a proxy class is the same as that of system classes loaded by the bootstrap class loader, such as java.lang.Object, because the code for a proxy class is generated by trusted system code. This protection domain will typically be granted java.security.AllPermission. Each proxy class has one public constructor that takes one argument, an implementation of the interface InvocationHandler, to set the invocation handler for a proxy instance. Rather than having to use the reflection API to access the public constructor, a proxy instance can be also be created by calling the Proxy.newInstance method, which combines the actions of calling Proxy.getProxyClass with invoking the constructor with an invocation handler.
Graphics programming The Canvas class Displaying graphics on a component Drawing lines Drawing rectangles Drawing ovals Drawing images
The Canvas class The first thing you will need is the Canvas class. This class is used to create an area in a frame to be used for displaying graphics. NOTE: All the classes you will need to display graphics (as well as frames) are located in the java.awt package. Canvas class methods: void setSize(width, height) - Sets the size of the canvas void setBackground(Color c) - Sets the background color of the canvas void setForeground(Color c) - Sets the text color of the canvas Add a canvas to a frame just like you would any other component: Canvas C1 = new Canvas(); C1.setSize(120,120); C1.setBackground(Color.white); Frame F1 = new Frame(); F1.add(C1); F1.setLayout(new FlowLayout()); F1.setSize(250,250); F1.setVisible(true); Displaying graphics on a component Now that you have a Canvas (an area to display graphics on) how do you actually display those graphics? With the paint() method of the Frame class. The paint() method takes one attribute - an instance of the Graphics class. The Graphics class contain methods which are used for displaying graphics. The Graphics class lets a component draw on itself. Syntax: public void paint(Graphics g){ //methods for drawing graphics here; } Drawing lines To draw lines, the drawLine() method of the Graphics class is used. This method takes four numeric attributes - the first two indicating the x/y starting point of the line, the last two indicating the x/y ending point of the line. Example: public void paint(Graphics g){ //draw a line starting at point 10,10 and ending at point 50,50. g.drawLine(10, 10, 50, 50); } Drawing rectangles To draw rectangles, the drawRect() method is used. This method takes four numeric attributes - the first two indicating the x/y starting point of the rectangle, the last two indicating the width and height of the rectangle. Example: public void paint(Graphics g){ //draw a rectangle starting at 100,100 width a width and height of 80 g.drawRect(100, 100, 80, 80); } Filling a rectangle By default a rectangle will have no color on the inside (it will just look like a box). You can use the fillRect() method to fill a rectangle. The fillRect() method has four numeric attributes indicating the x/y starting position to begin filling and the height and width. Set these values the same as you did for the drawRect() method to properly fill the rectangle. Example: public void paint(Graphics g){ //draw a rectangle starting at 100,100 width a width and height of 80 g.drawRect(100, 100, 80, 80); g.fillRect(100, 100, 80, 80); } Something's missing... The rectangle is filled, but we didn't set a color for it! To do this, we will use the setColor() method. g.setColor(Color.orange); Drawing ovals To draw ovals, the drawOval() method is used. This method takes four numeric attributes - the first two indicating the x/y starting point of the oval, the last two indicating the width and height of the oval. Fill an oval with the fillOval() method which also takes four numeric attributes indicating the starting position to begin filling and the height and width. Set these values the same as you did for the drawOval() method to properly fill the oval. Example: public void paint(Graphics g){ g.setColor(Color.gray); //draw an oval starting at 20,20 with a width and height of 100 and fill it g.drawOval(20,20, 100, 100); g.fillOval(20,20, 100, 100); } Displaying images To display images, the Image class is used together with the Toolkit class. Use these classes to get the image to display. Use the drawImage() method to display the image. Example: public void paint(Graphics g){ Image img1 = Toolkit.getDefaultToolkit().getImage("sky.jpg"); //four attributes: the image, x/y position, an image observer g.drawImage(img1, 10, 10, this); } An entire Java graphics program: import java.awt.*; class GraphicsProgram extends Canvas{ public GraphicsProgram(){ setSize(200, 200); setBackground(Color.white); } public static void main(String[] argS){ //GraphicsProgram class is now a type of canvas //since it extends the Canvas class //lets instantiate it GraphicsProgram GP = new GraphicsProgram(); //create a new frame to which we will add a canvas Frame aFrame = new Frame(); aFrame.setSize(300, 300); //add the canvas aFrame.add(GP); aFrame.setVisible(true); } public void paint(Graphics g){ g.setColor(Color.blue); g.drawLine(30, 30, 80, 80); g.drawRect(20, 150, 100, 100); g.fillRect(20, 150, 100, 100); g.fillOval(150, 20, 100, 100); Image img1 = Toolkit.getDefaultToolkit().getImage("sky.jpg"); g.drawImage(img1, 140, 140, this); } } What it will look like:
Frames and components Java's Abstract Windowing Toolkit provides windows containers that allow you to create separate windows for your applications. When used with a Web browser, they can run outside the main window (unlike panels which are nested within the applet window.) Frames have a title bar and if they are created by an applet, they should be destroyed BEFORE you close the applet that created them so that you can reclaim the resources they were using. This is how you create a frame with a title: Frame window = new Frame("This is the Frames's Title Bar!"); There are several things you must be concerned with after a frame is created in order for it to be visible on the screen: Choosing a layout manager. Resizing the frame: Unlike a panel, a frame must be given a size before you can see it. window.resize(300,200); Making the frame visible window.show(); If you want to hide the frame again, in order to show a different frame for example, use the hide method. window.hide(); Once a frame has been created and the show method has been called, it can be resized, maximized, and minimized just like any other window. When you are finished with the frame, you should always use the dispose method to get rid of it. Some of you system's resources are used when the frame is running and they will be restored when you get rid of it. window.dispose();
Notice that the frame can be minimized, resized or maximized but not closed by clicking on the X button or control icon on the top left and right of the frame. The applet's event handling routines cannot detect or deal with events that occur to the frame. This stems from the object oriented nature of Java. As you can see in the diagram below the Frame class will not handle many of the events that occur to the frame we've created.
The new frame inherits handlers for the Maximize, Minimize and Resize events from the Frame class but no others. Any other frame events or actions would need to be handled by a class that extends the Frame class. We will write this type of class in the next section.
The code for the Applet is listed below import java.applet.*; import java.awt.*;
public class frames extends Applet
{ Frame window = new Frame("This is the Frame's Title Bar!"); Button btn = new Button("Create a new Frame"); Import all the facilities of the AWT and applet that Java has to offer.
Create an applet called frames.
Create an instance of the frame class, initializing the title bar. Create an instance of the button class with a label. public void init() { add(new Label("Hit this button to")); add(btn); add(new Label(".")); The init method adds a label The button created above is added to the applet. add(new Label("The new Frame is independent of the applet.")); add(new Label("You can maximize and minimize it by using")); add(new Label("the buttons on the top right or the control icon.")); add(new Label("on the top left. You will not be able to close it.")); add(new Label("You must use the applet's button to do that.")); add(new Label("In order to handle Frame events you need to ")); add(new Label("create a separate class for it.")); window.setLayout(new FlowLayout()); window.add(new Label("This is the Frame.")); window.add(new Label("You can resize it, move it")); window.add(new Label("and perform other Windows operations.")); } Labels are added to explain the behavior of the frame.
The layout for the frame named window is set for FlowLayout. The default FlowLayout is center, top to bottom. Using a layout manager for frames is required. Labels are added to the newly created frame. public boolean action(Event evt, Object whatAction) {
if((evt.target instanceof Button)) { String buttonLabel = (String) whatAction; if (buttonLabel == "Destroy the Frame") { window.hide(); window.dispose(); btn.setLabel("Create a new Frame"); return true; } if (buttonLabel == "Create a new Frame") { window.resize(300,200); window.show(); btn.setLabel("Destroy the Frame"); When an action takes place this method tests for it.
If the action was an instance of Button, the string on the button is stored in buttonLabel
If the string on the button is "Destroy the Frame", hide the frame named window and dispose of it. Change the label on btn to "Create a new Frame" and return true.
If the string on the button is "Create a return true; } } return false; } } new Frame", resize to 300x200 and show the frame named window. Change the label on btn to "Destroy the Frame" and return true
otherwise return false.
Working with 2D shapes
User Space vs. Device Space Let's start by defining the difference between user space and device space. In most computer graphics environments, each pixel is numbered. If you draw a square at, say (20, 20), then the top left corner of the square will begin at approximately the twentieth pixel from the left edge, or axis, of the drawing space and at the twentieth pixel from the top axis of the drawing space. Coordinates that are offset from axes are called Cartesian coordinates. The location of Java 2D objects are also specified using Cartesian coordinates. The beginning of the space occurs in the upper left side of a hypothetical rectangle that increases to the right and downward. Java 2D, however, defines coordinates in units (72 units to an inch), and rendering occurs in a hypothetical plane called the user space. Note that we use the term units and not pixels. The latter term implies that the output device is a computer screen. When an object is drawn to the output device, such as a screen or printer, the results may have to be scaled to ensure that the object is the same size. After all, a shape that appears as four inches wide on the screen should also appear four inches wide when it is printed on a piece of paper. A computer monitor typically uses 72 pixels per inch, so each Java 2D unit is conveniently equivalent to a pixel. However, a computer printer may use 300, 600, or even 1200 or more dots per inch. In this case, the Java 2D graphics engine has to scale its user space to a new device space when the object is "drawn" to the printer output device. Graphics, Lines, and Shapes
The easiest Java 2D primitives to learn are lines and shapes, so let's start there. Let's assume that we are writing the code for the inner rendering routine of a custom Swing component. With the older AWT classes, you would use the methods of the java.awt.Graphics class to draw the lines and shapes you wanted on a screen. public paint(Graphics g) {
This graphics capability was very limited. Fonts were limited; shapes could be drawn with only one pixel thickness; and image support was rudimentary. With the added graphics capabilities of the Java 2D API, on the other hand, graphics are much more robust. We now create an implementation of the abstract java.awt.Shape class, a Line2D, and pass this to the more sophisticated rendering capabilities of the Graphics2D class. With the Java 2 platform, you can do this by casting the Graphics class that is passed in to your custom component's paint methods to a java.awt.Graphics2D object, using it to render the appropriate shapes: public paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
Line2D line = new Line2D.Double(10, 10, 40, 40); g2.setColor(Color.blue); g2.setStroke(new BasicStroke(10)); g2.draw(line); Rectangle2D rect = new Rectangle2D.Double(20, 20, 100, 100); g2.draw(rect); g2.setPaint( new GradientPaint(0, 0, Color.blue, 50, 25, Color.green, true)); g2.fill(rect);
}
If you've been using the old Graphics routines in your Swing components, you're in luck. You don't have to explicitly call upon the draw() and fill() methods of Graphics2D on the Line2D and Rectangle2D objects to get the job done. You can still invoke methods such as Graphics.drawLine() and Graphics.drawRect() -- the same functionality is invoked in either case. With Java 2D, the object passed into the paintComponent() method is the same object, whether it is cast to a Graphics or a Graphics2D. So casting to Graphics simply allows the use of more familiar methods to access to the same Java 2D rendering functionality. For example, let's assume that you wanted to render a rectangle. You likely want to use the Rectangle2D class in the Java 2D libraries. However, you cannot do the following: Rectangle2D wrong = new Rectangle2D(x, y, w, h); // Won't compile
Instead, you must instantiate the rectangle by using one of Rectangle2D's inner classes, Double or Float, as shown here: Rectangle2D right1 = new Rectangle2D.Double(x, y, w, h); Rectangle2D right2 = new Rectangle2D.Float(x, y, w, h);
In addition, if you need to use the older integer-based coordinates, you can also write this: Rectangle2D old = new Rectangle2D.Rectangle(x, y, w, h);
The use of a Float or a Double inner class is consistent with a number of other 2D lines and shapes as well. For example, here are the constructors for the Line2D class: public Line2D.Float() public Line2D.Float(float x1, float y1, float x2, float y2) public Line2D.Float(Point2D p1, Point2D p2) public Line2D.Double() public Line2D.Double(float x1, float y1, float x2, float y2) public Line2D.Double(Point2D p1, Point2D p2)
Also, here is the QuadCurve2D class, which represents a quadratic curve segment -- that is, a single control point between the two endpoints that the curve will "bend around": public QuadCurve2D.Float() public QuadCurve2D.Float(float x1, float y1, float ctrlx, float ctrly, float x2, float y2) public QuadCurve2D.Double() public QuadCurve2D.Double(float x1, float y1, float ctrlx, float ctrly, float x2, float y2)
Here is CubicCurve2D, which represents a cubic curve segment. It is much like a QuadCurve2D, except that it has two control points instead of one: public CubicCurve2D.Float() public CubicCurve2D.Float(float x1, float y1, float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, float x2, float y2) public CubicCurve2D.Double() public CubicCurve2D.Double(double x1, double y1, double ctrlx1, double ctrly1, double ctrlx2, double ctrly2, double x2, double y2)
Figure 1 shows each of these primitives in action. Here is Rectangle2D, which was covered earlier. public Rectangle2D.Float(float x, float y, float width, float height) public Rectangle2D.Double(double x, double y, double width, double height)
The class RoundRectangle2D is the same as Rectangle2D, except that the corners are rounded off:
public RoundRectangle2D.Float(float x, float y, float width, float height, float arcw, float arch) public RoundRectangle2D.Double(double x, double y, double width, double height, double arcw, double arch)
This is the Ellipse2D class: public Ellipse2D.Float(float x, float y, float w, float h) public Ellipse2D.Double(double x, double y, double w, double h)
With the Arc2D class, there are three different types of arcs that you can create. Arc2D.OPEN will simply leave the arc open as a curved line; Arc2D.PIE will create lines from either endpoint that meet in the center of the arc's ellipse (it is shown in Figure 2). Arc2D.CHORD will simply connect the endpoints with a straight line. Here are the constructors: public Arc2D.Float() public Arc2D.Float(int type) public Arc2D.Float(float x, float y, float w, float h, float start, float extent, int type) public Arc2D.Float(Rectangle2D ellipseBounds, float start, float extend, int type) public Arc2D.Double() public Arc2D.Double(int type) public Arc2D.Double(double x, double y, double w, double h, double start, double extent, int type) public Arc2D.Double(Rectangle2D ellipseBounds, double start, double extend, int type)
Let's use this simple example to draw a Java 2D ellipse to the screen: public void drawMyEllipse(Graphics2D g) {
Ellipse2D myEllipse = new Ellipse2D.Double(10.0, 10.0, 200.0, 100.0);
This example takes in a Graphics2D object and creates a simple ellipse 100 units high by 200 units wide at (x,y) coordinate (10, 10), then paints the ellipse's outline (five pixels wide, thanks to the stroke) in red, with the inside of the ellipse in white. The result is identical to the ellipse shown in Figure 2 above.