Learn Java from Ben Franklin
Write better Java code using Franklin’s learning techniques
In his autobiography (see Resources for a link), Ben Franklin describes how he worked to improve his writing. Franklin found prose he judged to be excellent, wrote a short encapsulation of each sentence’s main idea (he called them hints; here I will use the term summaries), put them aside for several days, and then tried to recreate the original using only his hints. He then compared his work with the original, making corrections where he fell short, and sometimes finding that he seemed to have improved on the original. People learning Java can do the same: find a model Java program, briefly summarize each line or section, put the notes away for a while, and then attempt to recreate the original from the notes. Once the program is finished, compare it to the original and make corrections where necessary; perhaps you’ll discover you have improved the original program. In this article, I’ll provide several exercises for applying Franklin’s learning techniques to Java.
Exercise 1: Using summaries
Consider the following demonstration program, which creates a file and writes three lines of text to that file:
import java.io.*;
public class WriteTest2 {
public static void main(String[] args) {
try {
FileWriter fw = new FileWriter("test.txt");
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter pw = new PrintWriter(bw);
pw.println("This is a test");
pw.println("This is only a test");
pw.println("In the event of a real emergency it would be too late");
pw.close();
bw.close();
fw.close();
}
catch (Exception e) {
System.out.println(e.toString());
}
}
}
One way to summarize the text is:
main()
try
FileWriter object
BufferedWriter object
PrintWriter object
Write one lame joke to file
close FileWriter object
close BufferedWriter object
close PrintWriter object
catch
Now you put the notes and original program away for a few days, after which you try to write a program based on the list of summaries. Then you compare your version of the program to the original version. If you see a deficiency in your version, make a note. You may even have the satisfaction of discovering that your version is superior.
While the above code, as well as other example code in this article, is relatively simple, you can use the exercises presented here with much more difficult programs. I chose relatively easy-to-understand programs so that audiences of mixed Java skills could easily grasp them. Rest assured, however, that those with more advanced skills can do these same exercises using more complex Java programs.
Exercise 2: Randomly ordering summaries
Franklin also created an even more challenging exercise: instead of writing the summaries in order, he put them in random order. As with the previous exercise, he would put the notes away for a while, then try to recreate the original from the unordered summaries. Similarly, you can randomize the summary order of Java programs that you will later recreate. For example, you can try to recreate the above program based on the following random list of summaries:
catch
PrintWriter object
close BufferedWriter object
FileWriter object
Write one lame joke to file
close PrintWriter object
main()
BufferedWriter object
close FileWriter object
try
To help create random lists, the following program takes a text file and creates a new file with the original file’s lines in random order. The program should be run from the command prompt in the form of “Scrambler file1.ext file2.ext
.” Here file1.ext
is the original file, and file2.ext
is the randomized file. If the output file already exists, it will be overwritten without any warning, so take care in choosing an output filename.
import java.io.*;
import java.util.*;
public class Scrambler {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Must have two arguments in the form of:");
System.out.println("Scrambler file1.ext file2.ext");
}
else {
String inputFile = args[0];
String outputFile = args[1];
Scrambler sc = new Scrambler();
sc.scramble(inputFile, outputFile);
}
}
public void scramble(String input, String output) {
try {
FileReader fr = new FileReader(input);
BufferedReader br = new BufferedReader(fr);
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(output)));
List list = new ArrayList();
String text;
text = br.readLine();
while (text != null) {
list.add(text);
text = br.readLine();
}
fr.close();
br.close();
int x = list.size();
Collections.shuffle(list);
for ( int I = 0 ; I < x ; I++) {
String s = (String) list.get(I);
System.out.println(s);
pw.println(s);
}
pw.close();
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
If you can understand the general workings of the above program, you may want to make a list of this program’s summaries for your own Java learning exercise.
Transforming exercises
Another method Franklin used was to change text to verse (poetry), lay it aside for several days, and then change the verse back to text. You can do similar exercises with Java — for example, you can:
- Change applets to applications
- Change programs that read and write to files from a Reader/Writer model to programs that use
InputStream
/OutputStream
- Change threaded applications that subclass
Thread
to those that create a class that implementsRunnable
Exercise 3: Combining declarations
Let’s start with a simple example. When creating a chain of input or output streams, you can create separate objects that get chained, as in the first program listing:
FileWriter fw = new FileWriter("test.txt");
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter pw = new PrintWriter(bw);
Alternatively, you can combine all the declarations into one line. One exercise, then, would be to change the first program into one whose chain of output streams is declared in one line. A sample solution is given below:
import java.io.*;
public class WriteTest {
public static void main(String[] args) {
try {
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("test2.txt")));
pw.println("This is a test");
pw.println("This is only a test");
pw.println("In the event of a real emergency it would be too late");
pw.close();
}
catch (Exception e) {
System.out.println(e.toString());
}
}
}
Exercise 4: Writer and OutputStream
Another exercise you can do is to change a program that uses the Writer
model to one that uses the OutputStream
model, yet produces the same output. Then, set the OutputStream
model aside for some days before changing it back to a program using the original Writer
model. Starting with the version of the program immediately above, you might come up with:
import java.io.*;
public class WriteTest {
public static void main(String[] args) {
try {
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new
FileOutputStream("test3.txt")));
dos.writeBytes("This is a testrn");
dos.writeBytes("This is only a testrn");
dos.writeBytes("In the event of a real emergency it would be too latern");
dos.close();
}
catch (Exception e) {
System.out.println(e.toString());
}
}
}
Similarly, you could put this program away for some days before changing it back to the Writer
model.
Exercise 5: Extending Thread vs. implementing Runnable
When working with Thread
s, you can extend Thread
or implement Runnable
. Therefore, as an exercise, you can transform a program that extends Thread
into one that implements Runnable
, and vice versa. The simple program below extends Thread
and counts down from 5 to 0, waiting one second between each count:
public class ThreadDemo extends Thread {
public static void main(String[] args) {
ThreadDemo t = new ThreadDemo();
t.start();
}
public void run() {
for ( int i = 5 ; i > -1 ; i--) {
System.out.println(i);
try {
sleep(1000);
}
catch(InterruptedException e) {
System.out.println(e.toString());
}
}
}
}
One way to change the above program into one that implements Runnable
is shown below:
public class ThreadDemo2 implements Runnable {
public static void main(String[] args) {
ThreadDemo2 td = new ThreadDemo2();
Thread t = new Thread(td);
t.start();
}
public void run() {
for ( int I = 5 ; I > -1 ; I--) {
System.out.println(I);
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
System.out.println(e.toString());
}
}
}
}
As with the previous exercise, you would set your “Runnable” version aside for a few days, then attempt to change it back to a program that extends Thread
.
Exercise 6: Applets and applications
An inherently more difficult exercise is to convert applets to applications and back again. Consider the following applet:
import java.applet.*;
import java.awt.*;
public class AppletDemo extends Applet {
Button button1 = new Button("Button 1");
Button button2 = new Button("Button 2");
public void init() {
add(button1);
add(button2);
}
}
To view the applet on my machine, I used the following HTML code:
<HTML>
<BODY>
<APPLET CODE = "AppletDemo.class" WIDTH=400 HEIGHT=300> </APPLET>
</BODY>
</HTML>
Transforming the applet into an application requires several changes. For the sample answer below, I extended Frame
, changed the Frame
‘s layout manager to FlowLayout
, added a main()
method, and created an inner class to listen for window closings. Obviously, there are other ways to change the above applet into an application, so the program below is not the only correct answer.
import java.awt.*;
import java.awt.event.*;
public class ApplicationDemo extends Frame {
Button button1 = new Button("Button 1");
Button button2 = new Button("Button 2");
public void init() {
setLayout(new FlowLayout());
add(button1);
add(button2);
}
public static void main(String[] args) {
ApplicationDemo ad = new ApplicationDemo();
ad.setSize(400, 300);
ad.addWindowListener(ad.new WindowClosingListener());
ad.init();
ad.setVisible(true);
}
class WindowClosingListener extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
}
Other transformation exercises
This is in no way an exhaustive list of transformation exercises. Java is such a rich language that there is often more than one way to accomplish the same task — that circumstance will usually yield a transformation exercise.
Interestingly, the Sun Certified Developer for Java 2 Platform exam (see Resources for a link) offers different strategies to achieve similar results: RMI or serialization, choice of data structure (e.g. array, Vector
, or HashMap
), choice of layout manager (or null layout), and so on. It isn’t that one strategy is right and the rest are wrong; generally, each strategy has advantages and disadvantages. The exam requires that the test-taker know the trade-offs of the various strategies. Franklin’s transformation exercises would be easily applicable to these many alternative strategies, and anyone attempting to master these topics will probably find that performing these exercises is very helpful.
Common sense
When employing these exercises, it doesn’t hurt to apply a little common sense. When Ben Franklin used his exercises to learn to write better, he was already an avid reader and an aspiring writer. Similarly, these techniques will be most useful to those who make a habit of reading Java programs and who can write, albeit imperfectly, Java programs.
Before you consider a program complete, you should try to compile it. If the compiler doesn’t like it, you shouldn’t either! Sometimes it may be helpful to compile the programs you will be recreating or transforming before you start working with them. Sadly, programs that will not run correctly occasionally find their way into print — there is no need to try to recreate something that doesn’t work in the first place.
Finally, this may sound a little corny, but Franklin started on his exercises after someone pointed out deficiencies in his writing. It may not be pleasant to have someone point out flaws in your Java code, but if someone does, it may provide the motivation to do what Franklin did and improve your skills.
Conclusion
Ben Franklin used his techniques to improve his writing. His autobiography, for example, is considered a literary classic. Similarly, you can use adaptations of his techniques — and a little common sense — to become a better Java writer.