![]() | Monkeys at Keyboards: The Javanomicon © Michael James Heron | ||||
| Topic: Java Programming Level: 2 Version: delta | |||||
13 - Free Standing Applications | |||||
| Previous | Table of Contents | Next |
| Forum |
| Chapter Objectives |
By the end of this chapter, the reader will be able to:
|
So far, we've made use of JApplets as a vehicle for exploring some of the tools and techniques that Java makes available to us. The JApplet framework is powerful and requires only a little prior knowledge before it can be used successfully, and so it was an ideal way to begin to develop our programming abilities. However, JApplets have a number of limitations associated with them that mean they are not suitable for the development of all kinds of programs. In this chapter, we'll look at how we can develop 'proper' Java applications that do not need to be viewed through a web-browser. We will also discuss the applet sandbox and the general structure of a Java application.
An Applet, or a JApplet, is the kind of Java code we've been writing all along. We write a class that extends Applet or JApplet, then and we execute this class through the medium of an HTML page and a browser (or Java's applet viewer). An application on the other hand is 'stand alone'. It does not require a web page, although at this point we won't discuss how to set them up so they can run outside of our compiler - in other words, even though we're writing proper stand-alone applications, we'll still need our development environment before we can run them. Essentially, an application works just like an applet except it has a main method and does not require an HTML page. An event driven application is one which makes use of the Swing or AWT classes to provide a user interface. A console-based Java application is one where the input and output is provided through a purely textual interface.
In this chapter we'll look at how to implement applications in Java, but the question we must first ask ourselves is - why? Why should we look at ways of developing programs without using the applet structure? Why must we always be learning? Why? Why? WHY? Applets do indeed have a number of strengths. They are simple to set up and do not require any particularly great amount of knowledge before it is possible to make significant amounts of progress. They make it easy to deliver applications to users - all the developer has to do is make a webpage available, and anyone who browses that webpage can access their applet. This has a greatly positive effect on version control - if there is only one single place an applet can be accessed, then it only has to be updated in one place for changes to propagate to all of its users. However, applets do come at a price. Applets run in what is known as a sandbox, which we'll talk about later in this chapter. They are also limited in their expression as far as interfaces are concerned. Since they run in a browser window, we do not have the freedom of design that we get with genuine Java applications. Both applications and applets are powerful ways of developing software in Java, but each is best suited to particular kinds of development and it is important that an aspiring Java programmer understands how to make use of both frameworks when coding.
Java is a C-type language, and so the syntax is based very much on that of the C programming language. One of the conventions from C that has been adopted in Java is the use of a main method. The main method is the entry point for a Java application - all applications in Java must contain a class with a method called main. Applets and Japplets do not follow this rule - they work in a somewhat different way that we have discussed in previous chapters. The main method is where the execution begins - we begin from the first line of the method and then execute each line until we come to the last, at which point the method terminates. In a console based application (which we'll talk about a bit in a moment), this signals the end of the application. In an event-driven application, the main method is used primarily for setting up the class before Java's event model takes control. The syntax of the main method in Java is as follows:
All of this should now be familiar to you. The args parameter is passed by the Java virtual machine, and contains any arguments that were passed to the class when it was executed. Generally, unless Java programs are being run from the command line, you won't need to do anything with this parameter. The main method should be placed in the class which is most central to our application. We don't need to write any event handling code to get an application working - we can write a console based application with very little effort:
It may not look like much, but the class above is a Java application. It's not an event driven application, and so when all the code in the main method (in this case, just one line of code) is executed, the application terminates.
Other than the fact we don't have any of the event-handling tools available to us, we can write any Java application we want as a console-based project. We can make use of ArrayLists, we can incorporate all our own objects, anything. We call these kind of applications console based because they work within a console - this console may be the one provided by your Java compiler, or it may be Microsoft DOS, or it may be one of the Linux shells. Console based applications do not have an event-driven structure, and so they must be pre-emptive when seeking input. They can't sit idle and allow a user to enter whatever data they like in whatever order they want. Instead, the application must poll for information and then wait until the correct information is provided. Console applications are usually processing intensive with little provision for interaction. If an application requires only a few pieces of input before it goes off and computes for an hour or so, then it would make sense to implement it as a console based application. Input and output is textual - we've already seen System.out.println which allows us to print some text to the console. We read our input from the keyboard. The code to do this is not at all intuitive:
Once we have this line of code, we can then read keyboard input by calling readLine on our keyboard object. This is often very cumbersome and only suitable if a small amount of information is required, or if the output is of a very simple format. It is very tedious to format things like tables and columns using the provided text output classes. In this chapter, we will look primarily at event driven applications, since they provide us with a much more familiar context.
As with console based applications, our event driven applications are powered through a main method. Unlike console based applications, the main method exists primarily to setup the user interface before the event-driven framework takes over. This means that the application does not terminate when the main method has finished executing. For our event-driven applications, we make use of inheritance to extend a class called JFrame:
JFrame provides us with all the context that we need to setup our application in a familiar way. Within our application main method, we need to create an object - this object will be an instance of the class we are currently writing. But how can that be, if we haven't written it yet? It may be an offence against reason, but just trust me:
When we create an instance of our class, it sets up the 'window' that is going to appear when our application executed, and so we must set some basic configuration details. We use setSize to set the width and height of the window. We use setTitle to configure the text that will appear along the top, and we use setVisible to actually display the window:
When we compile and execute this program, we get the following result:
Look at that! It's a completely empty window! This may not seem very exciting, but we've set ourselves up with the blank canvas we need to actually start building an application. The best part is that we don't need to learn anything new to actually do this - we already know how everything is done!
We place event driven components onto an application in almost the same way we do with an applet. The only difference is that we can't put them in an init method. Or more accurately, we can but it won't do anything. The way init is called when an applet is viewed through a browser is part of the applet framework, and is not a part of the standard execution model of an application. Instead, we must add a constructor method to our class and configure our user interface there. Except for the fact that the code goes into this constructor and not into an init method, we set up an application's user interface in exactly the same way as we would set up the interface of an applet:
We also know how to implement ActionListener and setup our application to listen for the button being pressed:
And that's all there is to it! We need to follow a slightly different initial setup structure, but beyond that there is no difference in how we set up event driven applications and event driven applets. However, we do get considerably more power when using actual applications. For one thing, we can have multiple windows and thus implement far more complex user interfaces. For another, applets have a number of restrictions placed on them that render them unusable for certain kinds of application.
The actual low-level details of the Java sandbox are complicated and not really of interest to us at this point. However, we do need to appreciate some of the restrictions in order to understand why applications are so much more powerful. These restrictions are in place to provide a degree of security for those making use of applets through their browsers. Imagine if there were no security restrictions at all - any webpage you visit that includes an applet could delete all your files, or make copies of all your personal details, or upload questionable material to your hard-drive. There is virtually no limit to what nefarious schemes you would be opening yourself up to, merely by browsing the web. This is obviously unacceptable, and so the sandbox is there to ensure such scurrilous excesses do not occur. The sandbox is enforced by the Java Virtual Machine to restrict what actions applets are capable of performing. The principle of the sandbox works on the idea of trusted and untrusted code. By default, applets are untrusted. Untrusted code cannot:
All of these restrictions are in place for a very good reason. Alas, some of them make it rather difficult to use the applet structure for writing certain kinds of programs:
Because of the sandbox, we must make use of applications to write certain kinds of programs in Java. As we have seen, this is not a particularly odious problem - in fact, it's no more difficult than writing an applet, although it does require a little bit of extra understanding about the way Java works - primarily in the use of constructor methods for configuration, and the structure of the main method itself.
One thing that applications allow us to do that applets don't is to have more than one window - we can use this capability to develop powerful and effective user interfaces that are simply unobtainable using the traditional applet structure. You may have already guessed how this can be achieved in the syntax for setting up a basic free standing application. Consider the code in the main method of our example application above:
There is nothing to stop us using this exact same structure to create an instance of another class that extends JFrame and then displaying that in addition to the one we have displayed initially. Consider the following code:
And now consider a companion class called WindowTwo:
All of this code we've already seen, except for the call to dispose in the actionPerformed for WindowTwo - this effectively deletes the window from sight and from memory. Run the application, and we get the familiar window:
Press the button and we then get a second window:
Do as the nice window asks, and press the 'close me!' button to get rid of the second window. And now we have a multiple window application. Obviously there's no reason to be seen in this simple example as to why we would need a multiple window application, but those who have some experience with builder applications like Visual Basic will have some insight into how useful they can be.
The problem with designing a multiple window application is that it adds an extra level of complexity as to how they user may interact with our program. There's no guarantee they won't click on a window behind the one you want them to work with, and thus mess up any carefully planned progression you had in mind. If you think of the way that Windows works as an operating system, you have two kinds of windows. One you can click between without any problems - most of the applications running on the task bar are an example of this. However, if you click on the print button of an application like Microsoft Word, it will bring up a window that you cannot click off of - it is there until you do something to get rid of it. These two types of window are known as modal and non-modal. A window is modal if it monopolises a user's attention until they dismiss it. The rest are non-modal. In the example above, we have a non-modal window since the user can simply click on the main frame and the second one is hidden behind the first. Or even worse, click on the main frame and then on the button again to spawn a third window. And a fourth, and a fifth, and so on. Disaster! If we wish to create a modal window, we must make use of another Java class called JDialog which is designed with this functionality built into it. We can simply substitute JFrame for JDialog in the code for WindowTwo, and add an extra line of code into the constructor:
Now when the user attempts to click away from our second window, they will be unable to do so. They have been rendered powerless by our modal kryptonite!
Usually, we want some method of communicating between different windows in an application... alas, with the framework we saw above, there's no way for window two to communicate with window one. Let's say for example we were going to prompt the user for their name in WindowTwo:
Now, how do we get this information back to WindowOne? Alas, we can't... WindowTwo has no way of knowing about WindowOne. We could provide ourselves with a method to store and return the name, but this is a JFrame and there's no guarantee the user will have entered the name when we call the method. We could make it a JDialog, but then the window will be removed when the user presses the button. It's a dilemma! The best way to implement communication is via a shared object that is passed to the second window's constructor method. The pass by reference system will ensure that they are both working with a 'live' version of the data and so there is no worry about passing information to and from the windows. Obviously such a strategy increases the coupling of a project - one must be careful to ensure that communication between windows is as rare as possible to ensure that the relationship between classes does not become too difficult to effectively manage. Let's consider our project written with a new class, UserDetails:
Then we setup WindowTwo to expect a parameter of type UserDetails in its constructor:
And then when we create the instance of WindowTwo in the actionPerformed of WindowOne, we pass in a UserDetails object:
Now, as soon as the user enters their name and presses the button, our first window has access to the information because it is stored in the shared object resource. This kind of strategy allows for easy communication between different windows, but the developer must still be careful to ensure that appropriate consistency checking is still done... with a JDialog we can be sure that the user had to enter their name before they could get back to the main application. However, sometimes we need to make use of a JFrame and it's still possible that when the first window goes to get the user name, it's not there. Appropriate error checking must still be in place.
The functionality implemented above is not very complicated. In fact, it seems kind of a waste to have a separate class at all... it would be much easier for us to control the communication if it just looked as if the buttons were in a different window... is there a way that we could control both windows from a single application? Indeed there is... although as a strategy it leads to low cohesion. For very simple interfaces though, it offers a convenient way to implement multi-window functionality without the problems associated with separate classes per window. It all comes down to using the add method of the frame. We've been using this to draw components on the screen of an applet and an application for many chapters now. There's nothing to stop us drawing directly onto a second frame and adding things to that. Really, if all we want to do is display the components in a different frame, we just need an empty window that we can add to, so we just create an instance of the base JFrame or JDialog:
In the constructor method for our main window, we setup all of the components we are going to want for both windows. However, we only add the ones we want for the main window. For example:
Here we add the openWindowTwo button to the main container, but although we setup the closeWindowTwo JButton and the yourName JTextField, we don't add them to anything. They exist only in the computer's memory. Next, when the openWindowTwo button is pressed, we setup the second window by creating an instance of the appropriate base class (JFrame or JDialog), getting its content pane, and then adding the relevant components to that:
Remember that the closeWindowTwo button has this as an action listener object... this points to the main window. So, whenever the button is pressed, it calls actionPerformed on MainWindow rather than on the JDialog in which it is housed. This means we can put the event handling code in MainWindow:
Now we have a two window application that has its communication handled by a single class. As was indicated above, this leads to low cohesion (although ironically, it also leads to low coupling), but for simple functionality and non-complex layouts, it can be the best way to handle communication between otherwise unrelated windows.
Applications also provide us with a new set of events that were not of use or interest when developing an applet. Applications can be minimised, maximised and closed - there are events that are associated with all of these actions, and we register interest in them by adding a WindowListener to our application. We implement this in the same way we do with ActionListener and AdjustmentListener - but unlike both of these listener classes we need to implement multiple methods to make use of this functionality:
We don't need to actually do anything with these methods, but we need to provide them in our code before the compiler will allow us to implement the WindowListener class. We also need to add a WindowListener to our application - we do this in the constructor at the same time we setup our various components:
The names of these methods should be self explanatory, at least as far as when they are triggered. It is usually not a good idea to put anything that flares up a modal window in these methods - Java gets a little screwy when the focus changes since that in itself triggers another event. As we discussed above, console based applications terminate their execution when they reach the end of the main method. Swing applications do not - in fact, if we want to end the execution of a Swing application then we must explicitly write the code for termination. The code we need for this is:
This is a call that terminates the application and gets rid of all windows spawned during its execution. Applets provide no similarly neat method of terminating execution. By default, when you click on the X icon of a Java window, it just hides the frame. It's still there in the background, lurking and consuming resources. If you want something else to happen, you must add handling code to the relevant event to ensure that the appropriate action is taken - either a call to dispose if you just want rid of that window, or a call to System.exit (0) if you want the entire application to terminate.
WindowListeners are great if you're looking to provide genuinely useful functionality... however, in many cases we just want to make sure that when we click on the X icon, our application terminates. Luckily, Java provides us with a very simple mechanism for achieving this... each JFrame provided a method called setDefaultCloseOperation that we can use to specify what should happen on a close event. By default, this is set to hide an application rather than terminate it. We can easily change this within our own applications... we just call the appropriate method and tell it that we want our application to exit when we close it:
Now, whenever we click the X icon, our application will terminate properly, and we don't need to worry about implementing all of the methods that go along with a WindowListener.
The multiple window applications we've been looking at so far are examples of an SDI architecture (single document interface). All of the windows are independent of each other. Many applications however provide an MDI (multiple document interface) architecture... for example, word processors. All of the 'open files' are accessed within a single instance of a larger context application. We can implement this same functionality in Java through the use of the JDesktopPane class and the JInternalFrame class. Functionally a JDesktopPane is identical to any component we've setup in the past. We create an instance of it, and we add it to the content pane of a JFrame or JApplet. However, it does nothing until it's combined with an instance (or usually, more than one instance) of a JInternalFrame. Similarly, the JInternalFrame class has all of the methods we usually associate with standard frames... the only difference is that they work within another larger window (the JDesktopPane). We create an instance of JInternalFrame with a five parameter constructor:
Through the use of this constructor we can control how much access we want the user to have in the placement. We must explicitly set the JInternalFrame object as being visible (as usual), but we also have to say where we want it drawn and at what size. If we don't set a size, it will be drawn at size zero and be invisible. If we don't specify a location, it will be drawn at the top left of the JDesktopPane. The JDesktopPane object has a range of useful methods - one of these is getAllFrames which returns an array of all the JInternalFrames contained within. Another is getSelectedFrame which returns the instance of JInternalFrame that is currently selected. Let's look at a simple example of this process... it'll set up five internal frames within a larger JDesktopPane. We won't do anything with the panes, but we'll provide a pair of buttons for changing the state of the desktop. One button will add a new frame... another will close all the open frames:
Running this gives us a neat MDI application (even if it doesn't do anything):
As for creating an MDI application that actually does something useful, it's just as straightforward as setting up an SDI application that does something useful. We can either choose to create separate classes that extend JInternalFrame and then add those to the JDesktopPane, or we can configure them from within the main application as we saw in section 8.7. For example, we can setup a simple MDI word-processor by adding a JTextArea to each frame we setup:
This would give us an application within which we could accomplish the following:
We will be working almost exclusively with SDI applications for the rest of this book, but if you should feel the desire to develop a proper MDI application you now can see that it's not something that should invoke Fear or Terror. It's actually only a little bit trickier than setting up a standard SDI architecture.
JApplets and JFrame are two different way of approaching event driven programming in Java. They each have their strengths and weaknesses, but allow for the developer to make use of a wide range of components when developing ways for the user to interact with a program. Java applications extend the model by allowing for a developer to set up multi-window applications at the cost of a little additional complexity. JApplets provide an easy framework from which to hang event driven components - however, they work within a sandbox that makes them unsuitable for implementing solutions to many real world problems. Java applications require an additional level of understanding to set up, but allow for genuinely powerful programs to be developed. As usual, this power comes with a price, and the cost of an application is that it makes the deployment of your program to end-users a more problematic task.
Exercise OneHave you ever visited one of those horrible web-pages that keep bringing up windows when you click on a button? You know you have - perhaps you still wake up at night, screaming from the memories. Or perhaps that's just me. Why not create an application that does exactly that? Exercise TwoCreating multi-window applications opens up a range of new problems. For example, how do you communicate information from one window to the other? Consider ways in which this could be done. Exercise ThreeMany applications have a 'splash screen' that appears when an application is loaded up. How could you implement one of these using the knowledge you have gained up until this point? Further ReadingThe following table details further reading on the topic in this chapter, and also any external resources that you may find useful.
|
| Previous | Table of Contents | Next |
© 2004-2006 Michael James Heron