Computer Science 212 - Tutorial 4
Introduction to simple GUI's, classes that define objects, and inheritanceCopyright © 2005 by Dorothy L. Nixon. All rights reserved.
Table of contents
- First simple GUI examples
- Class hierarchies and class Object
- GUI with multiple instances and a custom component
- instance variables vs. static variables
- The this reference
- Scope and duration of variables
- Accessing fields using dot notation
- Another way for GUI objects to talk to each other
- Simple GUI version of our numbers-sorting program
- Layout managers
- Runtime exceptions vs. other exceptions
Example files (download as ZIP file here)
- ConsoleInput.java
- FieldDotDemo.java
- FieldDotDemoTest1.java
- PaintDemo.java
- PaintDemoPanel.java
- ScopeDemo.java
- SimpleGUI1.java
- SimpleGUI2.java
- SimpleGUI3.java
- SortingDemoExceptions.java
- SortingDemoGUI1.java
- TextFileInput.java
- TextFileOutput.java
- VariablesDemo.java
- VariablesDemo2.java
- VariablesDemoPanel.java
- VariablesDemo2Panel.java
- numbers.txt
- numbers2.txt
- numbers3.txt
- numbers4.txt
The example files may be downloaded in a ZIP file here.
Introduction to simple GUI's, classes that define objects, and inheritance
- First simple GUI examples
Compile SimpleGUI1.java and run it from your DOS (Command Prompt) window. This program displays a window containing both a large text area in the center and a small text field at the bottom, in both of which you can type and edit text. Experiment briefly with typing and editing text in both the text area and the text field. Then close the window by clicking on the little box with the "X."
This program has a bug, which we will fix later: When you close the window, the program doesn't quit. Your DOS window is now frozen. Type [Ctrl]-C (press the C key while holding down the Ctrl key) to make the program quit and thereby un-freeze your DOS window.
Look now at our source code, which contains only a main function. The window is an object of class JFrame, which is instantiated (created) and assigned to an object reference variable as follows:
JFrame window = new JFrame();The constructor above creates a JFrame object, but does not yet make the window appear on the screen. In order to have a window that actually appears on the screen, it is necessary, after the JFrame object has been instantiated, to call at least the following two instance methods of the JFrame object:
- setSize, or, alternatively, the pack method. Our program SimpleGUI1.java contains the following call to setSize, which specifies the size of the window in pixels (picture elements - the tiny dots that make up all images on your screen):
window.setSize(400, 250);- setVisible, or, alternatively, the show method. Our program SimpleGUI1.java contains (at the bottom of the main method) the following call to setVisible, which makes the window appear on the screen:
window.setVisible(true);Before the call to setVisible, we must first give to the window any GUI components we want it to display, in this case the text area and the text field. In order to put GUI components on a window, we must first obtain the window's content pane:
Container contentPane = window.getContentPane();The text area and the text field are then created and added to the content pane.
As mentioned earlier, the when the window closes, the program doesn't close. This problem is fixed in SimpleGUI2.java, which is identical to SimpleGUI1.java except that the following statement has been added:
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);Both versions of our program so far contain only a main method. However, when writing GUI programs, it's more common to write a class defining our window object as a special kind of JFrame, as is done in SimpleGUI3.java, rather than just using a generic JFrame, as we did in previous versions. In SimpleGUI3.java, class SimpleGUI3 has the following heading:
public class SimpleGUI3 extends JFrameClass SimpleGUI3 is being defined as a subclass of class JFrame. In other words, an object of class SimpleGUI3 will be a special kind of JFrame object, having all the capabilities of a generic JFrame object plus whatever additional properties we may define for a SimpleGUI3 in class SimpleGUI3 itself.
Class SimpleGUI3 has a constructor which contains the needed calls to setSize, getContentPane, setVisible, etc. Note the syntax of the calls to instance methods of class JFrame. For example:
setSize(400, 250);The syntax is simply methodName(arguments) rather than objectReference.methodName(arguments). Note that the JFrame instance methods are being called as if they were defined in class SimpleGUI3 itself. This is allowed because class SimpleGUI3 has inherited those methods from class JFrame. Furthermore, the JFrame methods are being called for the very object that is being created by the constructor in which those methods are being called. That's why the syntax is appropriate.
Note, however, that the instance methods of the other objects (the text area, the text field, and the content pane) are still called the same way they were in previous versions.
All these method calls have been placed in the constructor rather than in the main method. The main method now consists of only one line:
new SimpleGUI3();in which we instantiate an object of class SimpleGUI3 without even bothering to assign it to an object reference, because there is nothing else we need to do with it.
Defining the window as a subclass of JFrame is common because doing so often makes it easier to enable other objects in the program to talk back to the window, as we'll see in some later examples.
- Class hierarchies and class Object
Generate HTML documentation for class SimpleGUI3 and all our other example programs by typing, in your DOS window:
javadoc *.javaThen double-click on index.html to open it in a web browser, and then click on SimpleGUI3 in the column on the left. Near the top of the page for SimpleGUI3, you will see something like the following:
java.lang.Object | +--java.awt.Component | +--java.awt.Container | +--java.awt.Window | +--java.awt.Frame | +--javax.swing.JFrame | +--SimpleGUI3Above is the class hierarchy for class SimpleGUI3. It shows that class SimpleGUI3 is a subclass of class JFrame (in the Java library package javax.swing), which in turn is a subclass of class Frame (in package java.awt), which in turn is a subclass of class Window (in package java.awt), which in turn is a subclass of class Container (in package java.awt), which in turn is a subclass of class Component (in package java.awt), which in turn is a subclass of class Object (in package java.lang).
It is suggested that you briefly look up all the above classes in the Java documentation. Don't worry about the details of the many methods of these classes for now. Just glance at them to get a quick feel for what these classes do.
Class Object is the grand superclass of all classes in Java. If a class heading does not have an extends clause, then the class extends Object by default. Class Object is at the top of the class hierarchy for every class in Java.
Every class in the Java language automatically inherits some methods from class Object. One of these methods is hashCode, which gives us a unique (well, almost) ID number for every object, as demonstrated in another example later in this tutorial.
- GUI with multiple instances and a custom component
Our previous examples displayed just one window. Our next example will display more than one window. It will display three windows, which will be identical except for (1) their positions on the screen and (2) the numbers displayed on them.
Our previous example used two GUI components, a text area and a text field, that were placed on the window. Both JTextArea and JTextField are classes defined in the Java library.
In our next example, each window will display two text fields add a custom GUI component, a special kind of panel that will be defined by extending class JPanel. Our special panel will not only look different from a generic JPanel but will also talk to (call an instance method of) the two text fields, so that the text fields can display some information about the panel.
Compile PaintDemo.java and run it. The custom panel is defined in PaintDemoPanel.java. In the HTML documentation generated by Javadoc, we can see that class PaintDemoPanel has the following class hierarchy:
java.lang.Object | +--java.awt.Component | +--java.awt.Container | +--javax.swing.JComponent | +--javax.swing.JPanel | +--PaintDemoPanelClass PaintDemoPanel defines an instance method called paint, which takes one parameter, an object of class Graphics. A method with the exact same heading is defined in class Component, one of the classes in the above hierarchy. Therefore, class PaintDemoPanel COULD simply inherit the paint method without needing to define it. However, we have chosen to make class PaintDemoPanel's paint method do something different from what the paint method would ordinarily do on a JPanel. By defining its own paint method, class PaintDemoPanel has overridden the inherited paint method.
The paint method of any Component is called automatically (by some other method of class Component - you don't need to know the details) whenever the component needs to be painted. Examples of times when a GUI component needs to be painted include (1) when the window first appears, (2) when the window has been restored after being minimized, (3) when the window has been brought to the foreground, and (4) when the window has been uncovered by removing another window that was previously covering it.
Whenever an object of class PaintDemoPanel needs to be painted, the paint method will be called automatically, and the version of the paint method that will be executed will be the one defined in class PaintDemoPanel itself.
To paint itself on the screen, a GUI component uses an object of class Graphics. In our present example, our custom panel's paint method uses a Graphics object to draw two colored lines.
The Graphics object is created almost immediately before the paint method is called, and is then destroyed almost immediately after the paint method has finished executing, because the Graphics object takes up a lot of system resources. Therefore, a new and distinct Graphics object is used every time the component is painted.
Our three windows each display three numbers, one on the title bar and one in each of the two text fields. When all three windows are showing on the screen, a total of nine numbers are displayed, three per window. Observe that the nine numbers are all different from each other. Also, if you force one of the windows to paint itself, e.g. by minimizing and restoring it, or by covering it with another window and then uncovering it, then the number in the bottom text field changes, whereas the other two numbers remain the same.
The numbers are obtained by calling the hashCode method for various objects. As mentioned in an earlier section of this tutorial, hashCode is a method defined in class Object, the grand superclass of all classes in Java. Every class in the Java language inherits the hashCode method from class Object. Thus, the hashCode method can be called for any object of any class. For now, don't worry about the details of what a hash code is. Just think of it as a unique ID number for each object. (Technically the hash codes are NOT guaranteed to be unique for each object, but for all practical purposes they are likely to be unique.)
The number in the title bar, at the top of each window, is obtained by calling hashCode for the window itself (i.e. for each object of class paintDemo). The number in the top text field was obtained by calling hashCode for our custom panel. The number in the bottom text field was obtained by calling hashCode for a Graphics object.
Because each object has its own unique hash code, and because a new and distinct Graphics object is created each time a panel is painted, there will also be a new and distinct hash code for the Graphics object each time a panel is painted. That's why the number in the bottom text field changes each time the panel is painted.
On the other hand, the three windows and their panels continue to exist until you close the program. The are NOT destroyed and re-created every time you minimize them or cover them up. Thus their hash codes do not change as long as the program is still running, though they might be different next time you run the program.
On each window, in order for the two text fields to be able to display up-to-date information about both the panel itself and the panel's current Graphics object, the panel must be able to talk to (call an instance method of) the two text fields. So that the panel can talk to the text fields, the two text fields are passed as parameters to the constructor of class paintDemoPanel.
- instance variables vs. static variables
In PaintDemoPanel.java, the following two variables are defined inside class PaintDemoPanel but outside of any method:
/** * Text field for display of unique ID number * for this panel object */ private JTextField panelObjectNumberField; /** * Text field for display of unique ID number * for this panel's current Graphics object */ private JTextField graphicsObjectNumberField;Both these variables are given values in the constructor and then used in the paint method, where the setText instance method is called for each text field. (Thus the bottom text field displays an up-to-date hash code for the panel's current Graphics object each time the panel is painted.)
In Java, variables defined inside a class but outside of any method are known as fields. There are two kinds of fields:instance variables, which hold data for an individual object of the class, and static variables, which do not pertain to an individual object. Instance variables define the data stored in each of possibly many objects, each of which has its own region in memory, so that a given instance variable may have distinct values in distinct objects of the class. On the other hand, the static variables of a class define just one region in memory, NOT one per object.
In our example above, the two text fields are instance variables. They need to be, because each separate window has its own panel and its own text fields; thus each panel communicates with a distinct pair of text fields.
For an example of a program using both static and instance variables, compile and run VariablesDemo.java, which uses a custom panel defined in VariablesDemoPanel.java.
Each window displays, in a text field at the bottom, a count of the number of times the panel has been painted. Force one of the panels to paint itself, e.g. by minimizing the window or by covering the window with another window and then uncovering it. Observe that the paint count for that window goes up, whereas the paint counts for the other windows stay the same if you did not disturb them. The paint count for each individual window's panel is independent of the paint counts for the other windows' panels. Each panel object is maintaining its own distinct paint count. Hence, the paint count must be an instance variable.
And indeed, class VariablesDemoPanel has an instance variable paintCount, which is initialized to zero and incremented in the paint method, thereby counting the number of times a given VariablesDemoPanel object has been painted.
Look now at the main method of class VariablesDemo:
public static void main(String[] args) { // Create windows without yet making them // visible (and thus not yet painting their // panels): VariablesDemo window1 = new VariablesDemo(50, 50); VariablesDemo window2 = new VariablesDemo(350, 250); VariablesDemo window3 = new VariablesDemo(650, 450); // Make windows visible, thereby also // painting their panels for the first time: window1.setVisible(true); window2.setVisible(true); window3.setVisible(true); } // method mainFor reasons that will become clear later, the constructor for class VariablesDemo does NOT contain a call to setVisible. Therefore, setVisible must be called separately for each of the three windows after they are instantiated.
Try commenting out the two statements for one of the three windows. For example:
public static void main(String[] args) { // Create windows without yet making them // visible (and thus not yet painting their // panels): VariablesDemo window1 = new VariablesDemo(50, 50); // VariablesDemo window2 = new VariablesDemo(350, 250); VariablesDemo window3 = new VariablesDemo(650, 450); // Make windows visible, thereby also // painting their panels for the first time: window1.setVisible(true); // window2.setVisible(true); window3.setVisible(true); } // method mainWhen you run the program again, only two windows will appear. Furthermore, the top text field in each window will now say, "There are 2 of us," rather than "There are 3 of us."
So, there must be a variable, somewhere, keeping track of how many windows were created. Furthermore, this variable does not need to be stored in more than one location in memory. It has only one value at a time, for all the windows, NOT a possibly-distinct value for each window. So, a static variable can be used for this purpose.
Class VariablesDemoPanel has a static variable objectCount, which is initialized to zero and incremented in the constructor, thereby counting the number of VariablesDemoPanel objects that are instantiated (i.e. the number of times the constructor is called, creating a new object each time).
In the paint method of class VariablesDemoPanel, the values of both paintCount (an instance variable) and objectCount (a static variable) are output to text fields. The value of objectCount is output to the top text field, and the value of paintCount is output to the bottom text field.
The first call to paint occurs when the window is first made visible on the screen, i.e. when setVisible is called. Look again at the main method of class VariablesDemo and observe that the setVisible method is called, for all three windows, only after all three of them have been created. Thus, by the time setVisible is called for first window, the objectCount static variable of class PaintDemoPanel is already equal to 3. So the value 3 is displayed in the top text field of all three windows.
Besides talking to the two text fields, an object of class VariablesDemoPanel also talks to the window in order to display a message to the title bar. Observe that the constructor of class VariablesDemoPanel takes three parameters, the two text fields and the window (an object of class VariablesDemo). Within the the constructor of class VariablesDemoPanel is the following method call:
window.setTitle("Window #" + objectCount);Thus the title bar displays the number of panel objects that have been created so far, by the time the constructor of class PaintDemoPanel has been called for the panel object currently being created. So the title bar displays "Window #1" for the first window to be created, "Window #2" for the second window, and "Window #3" for the third window.
- The this reference
The constructor of class VariablesDemoPanel has the following heading:
public VariablesDemoPanel(VariablesDemo window, JTextField objectCountField, JTextField paintCountField)Note that one of the parameters is of type VariablesDemo. The VariablesDemo object is used within the constructor of class VariablesDemoPanel as follows:
window.setTitle("Window #" + objectCount);The above constructor of class VariablesDemoPanel will be called within the constructor of class VariablesDemo, where the object being created by the VariablesDemo constructor needs to pass itself as a parameter to the VariablesDemoPanel constructor, so that the VariablesDemoPanel object can talk back to that very same VariablesDemo object that created the VariablesDemoPanel object by calling its constructor. That can be accomplished, in the constructor of class VariablesDemo, as follows:
VariablesDemoPanel panel = new VariablesDemoPanel(this, topField, bottomField);The VariablesDemo object that is in process of being created by the constructor of class VariablesDemo can be thought of as this object, and can be referred to using the Java keyword this. In the example above, the this reference is needed so that the object being created by the VariablesDemo constructor can pass itself as a parameter to the VariablesDemoPanel constructor.
More generally, the this reference can be used as follows: Within an instance method, this refers to the object for which the instance method has been called. Within a constructor, this refers to the object which is currently being created by the constructor.
The this reference can be used within instance methods and constructors. The this reference cannot be used in a static method, because a static method is not called for a specific object. Recall that static methods are called using the syntax ClassName.methodName(arguments), not objectReference.methodName(arguments).
- Scope and duration of variables
So far, we've seen three kinds of variables, categorized based on where they are declared: (1) local variables, declared inside the body of a method; (2) parameters, declared in the heading of a method, and (3) fields, declared inside a class but not inside a method.
Local variables can be used only inside the method where they are declared. Likewise parameters, which are essentially local variables except that they get their initial values from outside the method when the method is called.
The portion of the program where a given variable can be used is referred to as its scope. For example, the scope of a parameter is the entire body of the method. The scope of a local variable is below the declaration within the block where the variable is declared.
Furthermore, local variables and parameters exist in memory only as long as the method is running. When the method is finished being executed, the memory that had been allocated for its parameters and local variables is deallocated.
Static variables exist during the entire time the program is running. Instance variables are allocated when an object is instantiated and continue exist for the lifetime of the object (that is, until the object is garbage-collected, a matter we'll discuss later).
Compile ScopeDemo.java and run it, and look at the source code.
In the constructor, a System.out.println statement outputs the value of i. But which i? Note that there are two distinct variables both named i: a local variable and an instance variable. Although they have the same name, they are two different variables referring to two separate memory locations. The System.out.println statement is within the scope of both variables; that is, within a portion of the program where both variables are allowed to be used. If a statement is within the scope of two variables with the same name, then a local variable or parameter takes precedence over the instance or static variable. So, the System.out.println statement in our constructor outputs the value of the local variable, which is 5.
On the other hand, in someMethod, the only variable named i that can be accessed there is the instance variable. So, that's the one that gets printed out. Its value is zero.
But where did the zero come from? The instance variable i was never initialized. In Java, all instance variables and static variables are automatically initialized to zero (or to null if they are object references). On the other hand, local variables are NOT initialized automatically; and, as we saw earlier, the compiler is very fussy about making sure you have given them values before you can use them in any other way.
A common mistake by novice programmers is to re-declare instance variables as local variables when trying to use an instance variable inside a method. When using an instance variable, DON'T declare it again inside a method. By doing that, you create a separate variable with the same name, thereby preventing yourself from accessing the instance variable.
- Accessing fields using dot notation
Look now at FieldDotDemo.java. See also FieldDotDemoTest1.java, which contains the following sample calls to the methods of class FieldDotDemo:
// Creating two new FieldDotDemo objects: FieldDotDemo a = new FieldDotDemo(2); FieldDotDemo b = new FieldDotDemo(5); // The nested if/else statement below should // print "a < b": if (a.compareTo(b) < 0) System.out.println("a < b"); else if (a.compareTo(b) == 0) System.out.println("a == b"); else System.out.println("a > b"); // Creating two more new FieldDotDemo objects, // one as an argument and another being returned // by the add3 method: FieldDotDemo c = FieldDotDemo.add3(a, b, new FieldDotDemo(4)); // The statement below should print "22": System.out.println(FieldDotDemo.getSumAll());Look now at the compareTo method of class FieldDotDemo:
public int compareTo(FieldDotDemo other) { return(i - other.i); // Here, plain "i" is the instance // variable i for this object, // whereas "other.i" is i for the // parameter object } // method compareToThe compareTo method compares the value of i for two distinct objects of class FieldDotDemo. The plain i belongs to the object for which the compareTo method was called, whereas other.i belongs to the object that was passed to the method as a parameter. For example, consider the following call to the compareTo method:
if (a.compareTo(b) < 0) System.out.println("a < b");In this case, the plain i used within class FieldDotDemo belongs to object a, whereas the other parameter of the compareTo method is a reference to the object that b refers to, and thus other.i is the i belonging to object b. In other words, the plain i instance variable refers to a memory location in the region of memory that has been allocated to object a, whereas other.i refers to a memory location in the region of memory that has been allocated for object b.
Similar dot notation, of the form objectReference.fieldName, can be used for the instance variables of objects that are used within a static method.
Without the dot notation, an instance variable can be accessed only within an instance method or a constructor of the class, not in a static method. In an instance method, an instance variable (without the dot notation) refers to a memory location in the region allocated to the object for which the method has been called. In a constructor, an instance variable (without the dot notation) refers to a memory location in the region allocated to the object currently being created by the constructor.
Another expression equivalent to using an instance variable without dot notation is to use it WITH dot notation, where the object reference is this. For example, in the constructor of class FieldDotDemo:
public FieldDotDemo(int i) { this.i = i; // Here, "this.i" is needed to // access instance variable i // as distinct from parameter i. sumAll += i; // Allowed. But, in a constructor or // instance method, it would be nicer to // refer to sumAll as FieldDotDemo.sumAll // to remind the programmer that it's a // static variable, not an instance variable. } // constructorAs in our previous example program ScopeDemo.java, we have two distinct variables witht the same name: the instance variable i and the parameter i. Inside the above constructor, if we use just i by itself, the variable accessed will be the parameter i. So, if we want to access the instance variable, we will need to refer to it as something other than just i by itself. Luckily we can also refer to it as this.i.
A static variable can be used, without dot notation, in any method of the class - a static method, an instance method, or a constructor. Nevertheless, it's customary to use dot notation with static variables, where the identifier before the dot is the class name, not an object reference. For example, in VariablesDemoPanel.java, the static variable objectCount is incremented in the constructor as follows:
VariablesDemoPanel.objectCount++;The reason for the dot notation, in this case, is to remind the programmer that objectCount is a static variable, not an instence variable. But it's not required by the compiler in order to access a static variable in the class where it has been declared.
If a field (whether static or instance) has been declared private, then it can be accessed - either directly or via dot notation - only within the class where it has been declared. If a field has NOT been declared private, then it can be accessed in at least some other classes using dot notation only.
As mentioned earlier, a field can be accessed without dot notation only inside the class where it has been declared. An instance variable can be accessed without dot notation only in instance methods and constructors of the class where it has been declared, whereas a static variable can be accessed without dot notation in any method of the class.
- Another way for GUI objects to talk to each other
Look again at VariablesDemo.java and VariablesDemoPanel.java. Recall that class VariablesDemoPanel contains method calls to display information in three other places besides the panel itself: (1) the window's title bar, (2) the text field at the top, and (3) the text field at the bottom. So that the panel can talk to these three other GUI components, the constructor of class VariablesDemoPanel takes three parameters to its constructor: the window and the two text fields.
Look now at VariablesDemo2.java and VariablesDemo2Panel.java. This program, with main method in VariablesDemo2.java, behaves exactly the same as VariablesDemo.java but is organized somewhat differently.
Look first at the constructor of class VariablesDemo2Panel. It takes only one parameter, the window, rather than three parameters, the window and two text fields. Thus an object of class VariablesDemo2Panel is unable to talk to the two text fields directly. Instead it talks to them indirectly via the window. To display text in the text fields, an object of class VariablesDemo2Panel must now call the setTopText and setBottomText methods of class VariablesDemo2. Note that class VariablesDemo2 now has references to the two text fields as instance variables, whereas class VariablesDemo2Panel does not have references to the two text fields as instance variables.
In this example, the way the program is organized in VariablesDemo2.java and VariablesDemo2Panel.java has no advantages over the way the program is organized in VariablesDemo.java and VariablesDemoPanel.java. However, suppose there were a bunch of different GUI components (say, a bunch of different buttons and menu items) that all needed to display messages to the same two text fields now and then. In that case the program could be written more concisely if the constructors of these various other classes didn't all have to take the window and two text fields as parameters to their constructors, but instead talked just to the window.
- Simple GUI version of our numbers-sorting program
Compile SortingDemoGUI1.java and run it. If you are using Java 1.4, compile it by typing:
javac -source 1.4 SortingDemoGUI1.javaand run it using, as a command-line argument, the filename of one of the following data files:
This program displays a window containing two text areas of equal size. The left text area displays the numbers from the data file. The right text area displays the same numbers in sorted order. At the bottom is a text field which tells you that a file with some specified filename has been created. The file will contain the numbers in sorted order.
Look now at the source code of SortingDemoGUI1.java. This program is a GUI version of SortingDemoExceptionsTest2.java, which was discussed in the section on Throwing runtime exceptions in Tutorial # 3. Note that, in SortingDemoGUI1.java, the array numbers and the subarray length lengthFilled are stored as instance variables of class SortingDemoGUI1. In both SortingDemoGUI1.java and SortingDemoExceptionsTest2.java, there are calls to the sort method of class SortingDemoExceptions, defined in SortingDemoExceptions.java.
- Layout managers
In various example GUI programs, we've used the add method, inherited from class Container, to put GUI components on a container. In some cases we used it to put GUI components on the content pane of a JFrame object (or an object of some subclass of class JFrame), and in other cases we used it to put components on a panel (JPanel object) which in turn was put on the content pane of a JFrame.
But what determines how the components are positioned on a container? This is done by a layout manager.
For the content pane of a JFrame, the default layout manager is BorderLayout, which divides the container up into five regions, as follows:
+--------------------------------------------------+ | NORTH | +------+------------------------------------+------+ | | | | | | | | | WEST | CENTER | EAST | | | | | | | | | +------+------------------------------------+------+ | SOUTH | +--------------------------------------------------+Each region can hold one component. If there are fewer than five components, the regions that contain components expand to fill the empty regions. For example, in SimpleGUI1.java, SimpleGUI2.java, and SimpleGUI3.java, there are just two components that have been added to the content pane: (1) a text area (an object of class JTextArea) in the CENTER region, and (2) a text field (class JTextField) in the SOUTH region. Because there is nothing in the WEST, NORTH, or EAST regions, the CENTER regions expands to fill these regions up. Similarly, in PaintDemo.java, VariablesDemo.java, and VariablesDemo2.java, there are text fields in both the NORTH and SOUTH regions and a text area in the CENTER region, which expands to fill the empty EAST and WEST regions.
Note that the title bar is not one of the regions. The title bar is unaffected by the layout manager.
Besides BorderLayout, another layout manager is GridLayout, which divides up the container into a specified number of rows and columns. For example, a GridLayout with 3 rows and 4 columns would look like this:
+------+------+------+------+ | | | | | +------+------+------+------+ | | | | | +------+------+------+------+ | | | | | +------+------+------+------+And a GridLayout with one row and two columns would look like this:
+-------------+-------------+ | | | | | | | | | | | | | | | | | | +-------------+-------------+For greater versatility, we can combine layout managers by nesting one layout manager inside another. In SortingDemoGUI1.java, we use a GridLayout with 2 rows and 1 column, nested inside the CENTER region of a BorderLayout which also has a component in the SOUTH region. Run the program and observe that it looks like the following:
+-------------+-------------+ | | | | | | | | | | | | | | | | | | +-------------+-------------+ | | +---------------------------+In the CENTER region of a JFrame subclass window's content pane, we've put a panel (class JPanel), for which we've specified a GridLayout with one row and two columns, as follows:
panel.setLayout(new GridLayout(1, 2));Two text areas (objects of class JTextArea) are added to the panel and thereby placed in the two regions. When the layout manager is a GridLayout, components are placed in positions determined by the order in which they are added, with the first component placed at the top left of the grid, then in the remaining cells of the top row, then left to right in other rows, top to bottom.
In SortingDemoGUI1.java we have a panel, layed out using a GridLayout, in the CENTER region of a window whose content pane has been layed out using the default BorderLayout. The panel fills up the WEST, NORTH, and EAST regions too, because there are no components there. There is a text field (JTextField) in the SOUTH region.
- Runtime exceptions vs. other exceptions
In the Java library documentation, on the page describing the java.lang package, observe that there are four lists of links: "Interface summary," "Class summary," "Exception summary," and "Error summary." Under "Exception summary," click on IllegalArgumentException, IndexOutOfBoundsException, and NullPointerException. Note that the exceptions are actually classes, but they are listed separately from other classes because of their special role.
At the top of the page for class IllegalArgumentException, note the following class hierarchy:
java.lang.Object | +--java.lang.Throwable | +--java.lang.Exception | +--java.lang.RuntimeException | +--java.lang.IllegalArgumentExceptionLike class IllegalArgumentException, classes IndexOutOfBoundsException and NullPointerException are also subclasses of RuntimeException.
Any exception class you use for the sole purpose of generating a programmer-friendly error message - which is the only way we have used exceptions so far - should be a subclass of RuntimeException. These exceptions are unchecked exceptions, meaning that the compiler allows you to throw them without also handling them.
Other kinds of exceptions, which are subclasses of Exception but NOT subclasses of RuntimeException, are known as checked exceptions, meaning that they cannot be thrown unless you also handle them. If you try to throw them without also handling them, the compiler will complain. An example of a checked exception is IOException. At this point in the course, you have not been taught how to handle exceptions, so you should not throw exceptions that need to be handled.
If an exception is being used for the sole purpose of generating a programmer-friendly error message, then there is no reason to handle it. As you will see later in the course, handling exceptions is sometimes useful for the purpose of generating user-friendly error messages, but not programmer-friendly error messages.