|
~ Java for Web Page Production ~ Session 3 - Fundamentals of Programming and Object- Orientation |
| Introduction | Inheritance | Nested Classes |
| Session 2 Summary | JLabel example | Scope |
| Session 3 Overview | Adding 2nd label to TextBurst | =+[(-:] BREAK [:-)]+= |
| Code Style | Libraries | Pair Matching Game |
We have looked a little into the background behind object oriented programming, and some of the fundamentals of classes, objects, attributes, and methods.
Don't expect it all to sink straight in - it's pretty different to traditional programming languages, but the best way to learn is to go over it again, and look at some more examples to help it sink in.
So that's what we'll be doing this week.
We'll be exploring some new territory such as the idea of inheritance, Nested Classes, and importing classes so we can use them.
However, we'll be doing a lot more hands-on work to get us used to working with the Java language.
So here goes...
In Session 2, we covered:
Variables (Attributes)
Integers
Strings
Case-Sensitivity
Arrays
Testing variables with QuickCup
What are classes and objects
Methods and attributes
The bike class
Bike attributes
How a method is declared
Creating bike objects
Accessing objects - array example
Getting information back from a method - return values
Amending TextBurst example to alter its speed from HTML page
The getParameter method
The parseInt method (converting strings to integers)
Standard Java Documentation
In session 3, we will be looking at:-
Summary from Session 2 incl. recap on object orientation.
Session 3 overview
Code style - comments and indentation.
Inheritance and the Applet.
Example class - the JLabel.
Amending the TextBurst sample to add a second JLabel.
Scope
Libraries, and importing classes
Nested Classes
Adding a dynamo to our Bicycle
<<break>>
Create a simple 'pair matching' game using buttons
Summary and Further Sources
Code Style - comments and indentation
You may have noticed in the various Java source code listings, descriptive text littered around.
For example, at the beginning of the TextBurst.java file (open up the TextBurst project from within QuickCup to look at it) there's a copyright notice and a history of changes made to the source code. A history is useful to let you know how and why the program has changed over time, and if other people are going to use your code, possibly who changed it.
/*
** TextBurst sample applet
**
** by (c) Copyright 2005 Simon Huggins
**
** For demonstration purposes only.
...
** Change History
** ~~~~~~~~~~~~~~
** 17-Sep-2001 v1.0 Release version
** 24-Sep-2001 v1.1 Alter speed
** 30-Sep-2001 v1.2 Second label
*/
Comments are a useful way to record information in a source code listing to give information about the code. Another use is to place information near where something is happening to explain why the code is there, or what it does. e.g.
/* If the font size reaches max or 2, reverse the direction of shrinking / growing */
if ((fontSize==maxFontSize) || (fontSize==2)) fontDirection = -fontDirection;
To record a comment, start your comment anywhere in your code with the characters /* and then when your comment ends, put in the characters */
As you can see, these comments can go over several lines, or just one line.
If you want to add some comments to the end of a line, add the characters // to the end of your line, and then put in your comments. Note that the comments automatically stop at the end of the line, so this type of comment is no use if you have several lines of description to include. E.g.
theLabel.setFont(new Font("Helvetica",Font.BOLD,fontSize)); // change the font size
Using comments to try things out
Another use for comments is to test a variation without removing the original version. For example, we might want to look to see what the font would look like in italics instead of bold. We might 'comment out' the Font.BOLD parameter by making it into a comment, and then placing Font.ITALIC straight afterwards - e.g.
theLabel.setFont(new Font("Helvetica",/*Font.BOLD*/Font.ITALIC,fontSize)); // change the font size
This means that Font.BOLD will now be ignored. If we like the effect, we can then delete the comment. If we don't, we can delete the /* and *.Font.ITALIC to leave the original code.
Indentation
Indentation is used to offset blocks of code. Thus, you will find that lines that are aligned vertically down the page, are all in the same block of code, and so will be related.
Blocks of code tend to start with a { and end with a } and are typically used when defining a class, method, loop, or condition. If you don't know what this means, don't worry - we'll get onto this later.
You'll typically find that when you find a new { to define a new block, the following lines of code are indented a little further to the right, and when the } is reached, lines following this code are indented back to the left a little.
The indentation is not compulsory - people just do it to make following their programs a little easier. Typically, each extra level of indentation (e.g. block within a block) is represented by about two or three spaces (presses of the space bar).
Sometimes, if a line of code is too long, you may wish to continue it to the next line. It is common to see the following line indented a little, to show that the second line is really part of the first line. It's rare you'll see this, as most programmers will simplify the line of code by splitting it down into smaller steps.
Following on from our discussion in the last session about classes and methods, one other important part of object orientation is the idea of Inheritance.
In terms of our bike, we may have defined a nice bike which can be made to go faster by pushing the pedal, or slower by clasping the handle.
However, we may wish to have versions of the bicycle with customizations. For example, a different model with an electric motor.
Rather than specify the class all over again, it is possible to create a new class based on the original class, and just add a few bits to customize it.
Sounds great - less work!
The way this is done is to use the extends keyword when defining our class - e.g.
public class
ElectricBike extends Bicycle {
private
double batteryLevel = 100;
public
getBatteryLevel() { return batteryLevel; }
}
Magic! So if we wanted to create an ElectricBike object now:-
ElectricBike mynewbike = new ElectricBike();
system.out.writeln("Battery is
at "+mynewbike.getBatteryLevel());
system.out.writeln("And push the
pedal for speed "+mynewbike.pushOnPedal());
You can see that not only can you get at the methods defined in the ElectricBike class, you can also get access to those defined for the original Bicycle.
When people talk about inheritance, they often lapse into jargon - here's a few terms you may come across:-
Parent or Superclass - Bicycle is the parent or superclass of ElectricBike.
Child or Subclass - ElectricBike is the child or subclass of Bicycle.
Ancestor - If we had another class called SoupedUpElectricBike which extended the functionality of ElectricBike, then Bicycle would be the Ancestor of both ElectricBike and SoupedUpElectricBike, but only ElectricBike would be the parent of SoupedUpElectricBike.
Descendant - Conversely, both SoupedUpElectricBike and ElectricBike would be the descendants of Bicycle, but only EletricBike would be the child of Bicycle.
Don't worry too much about the terminology. If someone starts sprouting on about it, take a look back at your notes to see if it's any clearer!
Applet and Inheritance
When you create an applet, what you're really doing is taking a class called JApplet and extending its functionality to do something more useful than display a grey box on a web page. So you are creating a subclass of JApplet whenever you are creating a new applet of your own. If you look at the TextBurst example, you can see that the class is defined as follows:-
public class TextBurst extends JApplet implements ActionListener {
...
}
So, our TextBurst class is creating a new class, which takes on all the functionality of a JApplet class, and adds a bit more to do something useful.
Take your eyes away from the part that says implements ActionListener - this is reserved for the next session, where we start looking at the concept of the Interface and how it relates to event-handling in Java.
Example class - the JLabel
A lot of classes have been created for you to use in your programs.
For example, there is a class called JLabel. You could extend its functionality to do something else if you liked, but in general, we just want to put lots of different JLabel objects into our applet. Take the TextBurst example. First, we state that we will have an object called theLabel which will be an example of a JLabel class, holding its own data.
JLabel theLabel;
Then, we need to create a new JLabel to hold some text:-
theLabel = new JLabel(burstText,SwingConstants.CENTRE);
and finally, we add that label to the applet:-
getContentPane().add(theLabel);
All well and good. You can guess from the last statement that the getContentPane() method is a method inherited from (i.e. exists in) the JApplet class. This is the case. It passes back what is known as a Container object as used by the Applet, which is where all of your buttons and labels etc. go. Thus to add the JLabel to the Applet's container, you call a method called add from the Container class. Read through this a few times to get your head around it:-
JApplet defines a method called getContentPane(),
getContentPane() returns a value, which is an object. The object belongs to a class called Container which relates to areas on the screen that can hold various different 'controls' such as buttons, labels, edit-boxes and check-boxes.
The Container class has a method to add a 'control' to the container - the method is called add.
Thus, this statements gets the container for the applet, and adds a label to the applet. Phew! Reread this a few time to try and get your head around it!
Amending the TextBurst sample to add a second JLabel
We can add a second JLabel to the TextBurst class for effect. However, you can open the changed class and try it yourself, and we'll go through the changes made in class. You will need to open the TextBurst project from the session3 directory.
One important note - when adding the labels, we now have to state where in the container we want the label to go - NORTH for the top, SOUTH for the bottom. This will be explained in further details next session when we look at the Java Layout mechanism.
Try swapping the following two lines around:-
getContentPane().add(theLabel,BorderLayout.NORTH); // and add it to the applet at the top
and
getContentPane().add(theLabel2,BorderLayout.SOUTH); // and add at bottom of applet
Do you notice a difference in how the applet runs? Try swapping the lines back, until you see the difference.
Scope refers to where attributes and methods are available.
In general, each extra 'layer' of complexity has access to the previous layer's information. However, the next less complex layer does not have access to the more complex layer unless that layer is made public.
For example, our class Bicycle has a private attribute called speed. If we go outside of the class, and create an object of type Bicycle, then the details of how the Bicycle works are now hidden to us, and we communicate purely using the methods of the Bicycle class, as all of the attributes are hidden. An example that because the attributes are not made public, the simpler layer does not have access.
However, the pushOnPedal procedure goes into more depth - i.e. is within the Bicycle class, and so has access to the speed attribute of the Bicycle class. Similarly, because the Dynamo object is within the Bicycle class, it can access the attributes of the Bicycle class. It couldn't do that if it was declared outside of the class.
Another way of looking at scope is that if you are part of an object, then you should have access to the object's information as you are working with it, and can be trusted to do the right thing. However, if you are working outside of it, then you should only know how to feed information into and out of it - like a black box.
Libraries, and Importing Classes
Java is a monumental language in that it has a huge number of classes already defined. To use these classes, your program must ask to see them. For this task, you use the import keyword, followed by the class you wish to use. e.g.
import Bicycle;
to use the Bicycle class from another of your classes - we will be doing this later with a class called RideBike.
Classes are sometimes grouped together and stored in a single file. This file is called a library. To access all of the classes in a library, rather than naming each one individually, you can use the following notation:-
import java.awt.*
The * is shorthand for everything and is known as a wildcard. It is also used in various other aspects of computers, such as selecting filenames (an aside).
So, in this case, we will be able to get at every class in the java.awt library (this is the Java Abstract Windows Toolkit which we'll be discussing later in the course).
Technical note about class paths
Note that libraries must be able to be found in something called the CLASSPATH. This is an environment variable (which means a variable held outside of the Java application, and available to all applications on the computer) which gives a list of the directories in which classes (ie. files ending with .class or ZIP file libraries) may be found. To see what the CLASS PATH is currently set to, in the QuickCup application, hover your cursor over the CLASSPATH check-box. A tool-tip is shown which shows the current CLASSPATH environment variable. Note that each path is separated by a semi-colon (;). Also note, that usually one of them is the directory . (single dot) - this means the current directory. Thus, if you compile on class file, and then compile another class file in the same directory that imports the first class file, it will be found, because it is in the same directory. If you wish to use the current CLASSPATH in QuickCup, you will need to check the check-box. Alternatively, the default is set to standard subdirectories within the Java SDK path, and the current directory. You can add extra directories to this by typing a list of classes in the text box labelled Class Paths. Note that these are not semi-colon separated - type one path per line.
Nested Classes
If the main class were declared as private then it would not be able to be used outside the Bicycle.java file. As this would be pointless, this is not allowed.
Note also, that you can only have one class per file - it must match the filename for the file. So when would we use private classes then? Well, imagine that later we wanted to include more information about the dynamo within the class - for example, when it was put in, the force it exerts against the wheel to make it more difficult to pedal etc.
To make things neater, we may want to create a new class to represent the dynamo (which we have done in the next example).
However, we may not want the dynamo to be accessible outside the bike, because it's a part of the bike and shouldn't be tampered with. In this case, we may decide to create a private class within the Bicycle class. This is called an nested class because it's nested inside another class.
This means theoretically that you could construct your whole program within your applet (the outer class), just by creating attributes, methods, and nested classes within the applet to structure everything. Takes a bit to get your head around, that one!
In the following example, we have declared the Dynamo class inside the outer (Bicycle) class as follows:-
private class Dynamo {
private double brightness = 0.0;
public void setBrightness() { brightness = speed / 2; }
}
So when a new Dynamo object is created, it will have an attribute called brightness which is accessible only within the Dynamo - not by the Bicycle! Thus, the only way to set it is to use a method, in this case called setBrightness, which sets the brightness to be the bike's speed divided by 2.
Adding a dynamo to our bicycle
Take a look at the project named session3_apps.qjp - it contains a modified Bicycle class to include the dynamo as an inner class.
It also includes a new class called RideBike.java
If you look at this class, you can see that it imports the Bicycle class, and uses it, outputting the results to the console window.
Compile
the RideBike class as normal, but click on the
(Launch Java Application) button to launch the application - note this is not an
Applet, as it does not output to a web page.
Try taking out some of the comments (//) at the beginning of lines describing something that won't work, save, and try to compile again. Note the error message - does this make sense to you why this is an error? If not, ask the tutor.
As an exercise, you may like to try to create a method in both the inner and main Bicycle classes to get the dynamo value. Ask the tutor if you would like some help.
Creating a simple 'pair matching' game using buttons
The following listing shows a simple pair matching game (project named pairs.qjp) using another Java component called a JButton. This introduces some important topics such as branching (if statement), looping (for statement), event handling (the ActionListener interface), and random number generation (the random() method).
We shall discuss these in class a little this week, and go through them thoroughly next week.
/*
** Pairs sample applet
**
** by (c) Copyright 2005 Simon Huggins
**
** For demonstration purposes only.
**
** You may use this applet on your web site, but it cannot be
** sold in either its original or altered state.
** This original notice must be included in all versions of
** the applet distributed, even if altered.
**
** The author accepts no liability for any difficulties or loss
** arising from use of this applet. It is primarily intended for
** demonstration purposes.
**
** Change History
** ~~~~~~~~~~~~~~
** 01-Oct-2001 v1.0 Release version
*/
/*
** Import section - tells the compiler where to look for different classes
** used within this file
*/
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
/*
** Defines a class called Pairs which takes the basic JApplet
** class as a template, and adds to it to create our Applet.
** Also implementing ActionListener interface for mouse clicks
*/
public class Pairs extends JApplet implements ActionListener {
JButton buttons[]; // array of buttons to appear on grid
int buttonValues[]; // values of those buttons (hidden)
int squaresAcross = 3, squaresDown = 3; // default grid size
int firstSelection = -1; // first value index selected of pair
boolean oneSelected = false; // has first of pair been selected?
JButton firstButton,secondButton; // last pair of buttons selected
/*
** Initialize the applet - i.e. set up any values before processing begins
*/
public void init() {
/* Get number of buttons across and down from HTML parameters */
String squaresAcrossString = getParameter("squaresAcross");
if(squaresAcrossString != null) // check if the parameter was used
squaresAcross = Integer.parseInt(squaresAcrossString);
String squaresDownString = getParameter("squaresDown");
if(squaresDownString != null) // check if the parameter was used
squaresDown = Integer.parseInt(squaresDownString);
/* Set up a grid layout holding the given number of squares across and down */
getContentPane().setLayout(new GridLayout(squaresDown,squaresAcross));
int buttonCount = squaresDown * squaresAcross; // store this for easy reference
/* Set up the arrays to hold JButtons and their hidden values to match the grid */
buttons = new JButton[buttonCount];
buttonValues = new int[buttonCount];
/* Set up pairs of numbers in the buttonValues array in sequential order */
for (int i=0 ; i < buttonCount ; i++) buttonValues[i] = (int)((i+2)/2.0);
/* Randomize the ordering of buttonValues by swapping values around */
for (int i=0 ; i < buttonCount ; i++) {
int swapFirst = (int)(Math.random() * buttonCount);
int swapSecond = (int)(Math.random() * buttonCount);
int swapValue = buttonValues[swapFirst];
buttonValues[swapFirst] = buttonValues[swapSecond];
buttonValues[swapSecond] = swapValue;
}
/* Add all the buttons to the content pane from the buttons array.
Note that each button is givena name buttonx where x is the number
corresponding to the buttonValues array - this is used later */
for (int i=0;i<buttonCount;i++) {
buttons[i] = new JButton("?");
buttons[i].setName("button"+Integer.toString(i));
buttons[i].addActionListener(this);
getContentPane().add(buttons[i]);
}
}
/*
** ActionListener Interface method triggered by mouse clicks
*/
public void actionPerformed(ActionEvent event) {
/* First, check that the source that originated the action is a JButton */
if (event.getSource() instanceof JButton) {
/* It is, so put it into a variable called clickedButton for easy reference */
JButton clickedButton = (JButton)event.getSource();
/* Extract the buttonValue index from the name of the button (7th char to end) */
int buttonIndex = Integer.parseInt(clickedButton.getName().substring(6));
/* ... and put the hidden value into a variable called buttonValue*/
int buttonValue = buttonValues[buttonIndex];
/* if the text on the button is -MATCH- then this button has already been matched */
if (clickedButton.getText()=="-MATCH-") return;
/* if oneSelected is set to false, then pair or nothing selected */
if (!oneSelected) {
/* if pair selected (firstSelection no longer default value of -1)... */
if (firstSelection != - 1)
/* ... then if the text on the first button of the pair is -MATCH- */
if (firstButton.getText()!="-MATCH-") {
/* ... then clear the previous two buttons of their text */
firstButton.setText("?"); secondButton.setText("?");
}
/* remember the buttonValues index no of the first selection */
firstSelection = buttonIndex;
/* ... and the button itself */
firstButton = clickedButton;
/* Reveal the button's number - convert it to a string first */
clickedButton.setText(Integer.toString(buttonValue));
/* So now one button has been selected - remember this */
oneSelected = true;
return; // and return from the actionPerformed method - don't go any further.
}
/* However, if one button HAS been previously selected... */
/* if the button was pressed twice, ignore the click */
if (buttonIndex == firstSelection) return;
oneSelected = false; // reset back to false, as another has now been selected
/* if the button value from the first button is the same as the second button */
if (buttonValues[firstSelection]==buttonValue) {
/* It's a match, so set the first and second button's text to -MATCH- */
clickedButton.setText("-MATCH-");
firstButton.setText("-MATCH-");
return; // ... and return back from the actionPerformed method.
}
/* So the first and second buttons didn't match...
Reveal the number behind the second button (just clicked)... */
clickedButton.setText(Integer.toString(buttonValue));
/* ... and remember what the second button was, so we can clear its text later */
secondButton = clickedButton;
}
}
/*
** Define information about the applet - in Appletviewer, see Applet | Info menu
*/
public String getAppletInfo() {
return "Title: Pairs\nAuthor: Simon Huggins\nSimple Pairs Applet"; }
}
|
and the HTML portion that calls the applet is:-
<HTML>
<HEAD>
<TITLE>Pairs 1.0</TITLE>
</HEAD>
<BODY bgcolor="#FFFFC6">
<HR>
<p><big><b>Following will work on a web page run from Internet Explorer:-</b></big></p>
<p align="center">
<OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
width="700" height="100" align="baseline"
codebase="http://java.sun.com/products/plugin/1.3/jinstall-13-win32.cab#Version=1,3,0,0">
<PARAM NAME="code" VALUE="Pairs.class">
<PARAM NAME="codebase" VALUE="./">
<PARAM NAME="type" VALUE="application/x-java-applet;version=1.3">
<PARAM NAME="squaresAcross" VALUE="4">
<PARAM NAME="squaresDown" VALUE="4">
<PARAM NAME="scriptable" VALUE="false">
No Java 2 SDK, Standard Edition v 1.3 support for APPLET!!
</OBJECT>
</p>
<HR>
<A href="Pairs.java">The source.</A>
</BODY>
</HTML>
|
Try compiling and running the applet with the appletviewer. Try changing the squaresAcross and squaresDown parameters in the HTML web page to make the game harder/easier. Try changing the width and height parameters to see what happens to the buttons. Try dragging the edges of the appletviewer window to resize it, and see what happens to the buttons.
Note that this isn't yet a very sophisticated game. It has not scoring, and no feedback when you complete the game. You cannot restart the game, either, and you will need to quite to applet and restart it in order to play the game again.
| ??? | Unfortunately, there was not enough time to seek these out at the time of writing - check back on the web site later. |