| Sign In/My Account | View Cart |
Teaching Java the Extreme Way
Pages: 1, 2, 3
Like all teachers, I'm sure that I learn more when I teach than my students do. In constructing this example, I needed to learn about how classes are loaded in Java. The original version of the test harness was a double-clickable .jar file that the students needed to quit and restart each time they made adjustments to the Friend class. I added the button and found that I didn't really understand ClassLoaders. I thank Malcolm Davis and Bill Venners for their helpful suggestions -- a lot of what I know about programming in Java is the result of ongoing conversations with Malcolm and Bill. I've told my editor that any mistakes with the code are theirs -- but I'm sure he knows better. Check out Bill's Web site, www.artima.com, for more information on the Java Virtual Machine and class loading. In this section, I'll present the code for the harness.
As you saw in the figures, the application is a JFrame with a text area of some sort and a button. Here's the code for the JFrame.
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class HelloFrame extends JFrame{
private HelloFrame(){
setUpLookOfFrame();
setUpFramesComponents();
setVisible(true);
}
private void setUpFramesComponents() {
MessagePane reporter = new MessagePane();
getContentPane().add(reporter,BorderLayout.CENTER);
getContentPane().add(new TestButton(reporter),BorderLayout.SOUTH);
}
private void setUpLookOfFrame() {
setTitle("Hello World");
getContentPane().setLayout(new BorderLayout());
setSize(350,330);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args){
new HelloFrame();
}
}
In setUpFramesComponents(), the text area and button are created and added to the frame. The rest of the code just tweaks the look of the application.
The button doesn't do very much. It sits around and waits to be pressed. When it is pressed, it tells the text area to figure out what message to display.
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class TestButton extends JButton implements ActionListener {
private MessagePane reporter;
public TestButton(MessagePane reporter){
super("Press here to check progress");
addActionListener(this);
this.reporter = reporter;
setSelected(true);
}
public void actionPerformed(ActionEvent actionEvent){
reporter.testProgress();
}
}
The text area is a JEditorPane, so that HTML can be displayed. The testProgress() method contains a call to the custom class loader. There is an attempt to create an object of type Friend and then to invoke the getName() method. If the return type of getName() is a String, then we can go ahead and display the "congratulations" message. There are many places in this process where things could go wrong. For each case we can anticipate, we display an appropriate message. It is still possible that something we haven't anticipated will go wrong. We also display an appropriate message and provide a stack trace so that the instructor can try to figure out what went wrong.
import javax.swing.JEditorPane;
public class MessagePane extends JEditorPane{
public MessagePane(){
super("text/html","");
welcomeUser();
}
public void testProgress(){
try {
Class friend = (new FriendLoader()).getFriend();
String name = (String)friend.getMethod("getName",null).invoke
(friend.newInstance(),null);
if (name == null){
stringNotReturned();
} else greetFriend(name);
}
catch (NoSuchMethodException e) {
methodNotFound();
}
catch(ClassCastException e){
stringNotReturned();
}
catch (NullPointerException e){
classNotFound();
}
catch (Exception e){
e.printStackTrace();
unexpectedHappened();
}
}
private void welcomeUser(){ //...
}
private void greetFriend(String name){ //...
}
private void classNotFound(){ //...
}
private void methodNotFound(){ //...
}
private void stringNotReturned(){ //...
}
private void unexpectedHappened(){ //...
}
}
You can actually stop here and not create the class loader -- just replace the call to the class loader with a call to Class.forName(). Your application would be smaller, but it would require restarting it every time students want to check their work. This isn't a big deal, but you should probably get rid of the button and just call testProgress() in the constructor for HelloFrame.
The only public method in the FriendLoader class is getFriend().
The getFriend() method calls the overridden findClass() method to try to find and load the Friend class. The key to creating your own class loader is in overriding this
findClass() method. If you can find the class file, you then copy the byte code into an array that you pass, along with the class name, to the defineClass() method. Here's
what this looks like:
import java.io.*;
public class FriendLoader extends ClassLoader {
public Class getFriend() throws ClassNotFoundException {
return findClass("Friend");
}
protected Class findClass(String className) throws ClassNotFoundException {
byte classData[]= getFileData();
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(className, classData,0,classData.length);
}
private byte[] getFileData() {
BufferedInputStream bufferedInputStream = getBufferedInputStream();
ByteArrayOutputStream byteArrayOutputStream =
getByteOutputFromFileInput(bufferedInputStream);
return byteArrayOutputStream.toByteArray();
}
private BufferedInputStream getBufferedInputStream(){
BufferedInputStream bufferedInputStream = null;
try {
File friend = new File("Friend.class");
FileInputStream fileInputStream =
new FileInputStream(friend.getPath());
bufferedInputStream = new BufferedInputStream(fileInputStream);
}
catch (FileNotFoundException e){
e.printStackTrace();
}
return bufferedInputStream;
}
private ByteArrayOutputStream getByteOutputFromFileInput
(BufferedInputStream bufferedInputStream){
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
try {
int c = bufferedInputStream.read();
while(c != -1) {
byteArrayOutputStream.write(c);
c = bufferedInputStream.read();
}
}
catch (IOException e){
return null;
}
return byteArrayOutputStream;
}
}
As a next step, I'd like the students to write to an interface. In this case, I don't care what they call their class; in fact, I don't care how they store their name information. This will, of course, require some changes to the harness. All of this is a topic for another day. For now, if your goal is to teach object-oriented programming and you plan to use Java as your language in an introductory course, how might you change or extend this proposed initial assignment? Next month, we'll continue to look at the test first approach to programming.
Daniel H. Steinberg is the editor for the new series of Mac Developer titles for the Pragmatic Programmers. He writes feature articles for Apple's ADC web site and is a regular contributor to Mac Devcenter. He has presented at Apple's Worldwide Developer Conference, MacWorld, MacHack and other Mac developer conferences.
Return to ONJava.com.
Showing messages 1 through 6 of 6.
Why wouldn't you use JUnit?Ian,
That's a great question. I wanted to separate the idea of testing first from JUnit -- which I use every day and appreciate how it has improved the quality of my code. There are some people who feel that introducing the JUnit structure is a difficult way to begin a course. They feel that there's too much stuff that would need to be taught before the students are ready to begin.
For the most part, I agree with you. I think you can start right off with JUnit. Here's why I didn't.
In the first article I made a big fuss about what I didn't like about beginning with HelloWorld as a first program. I didn't want to turn around and say, "remember when I said not to teach them main(), and public, and static, and void, and arrays, and so on -- now teach your students TestCase and so on first."
My hope was that this lesson taught students to create a simple class that contained a single method that was able to respond to a method call. The code that the student sees is very clean and the prompts they receive should be instructive and helpful.
Thoughts?
Thanks, Daniel
For experienced programmers onlyPerhaps you misunderstood the article. The student code is the five lines listed on the bottom of page one of the article. The have to write a class using the code public class Friend{} and then put a single method in it that has the code public String getName(){ return "Daniel";} .
I'm not seeing the complexity in this assignment. Second, I do want them to think of OOP first. They will certainly benefit from having loops and conditionals in their bag of tricks -- but you don't have to begin there. I think a for loop is harder to understand than the method described above.
Third, the use of interfaces is an important concept to understand early. It sounds as if the courses you have taken are teaching procedural programing using the Java programming language. Feel free to follow-up with me via this forum or email.
Thanks, Daniel
incremental and cyclical steps. I use netbeans and
the code introspection and dynamic compilation
features mean that I never write code that won't
compile. (well almost never...)
I don't think that it's a disservice to new programmers
to suggest that they use a tool like netbeans. The
popups will teach them as much about the library
as they would reading the javadoc..
Wendell Nichols