I use Java, but not very often. Therefore I'm using this page to remind myself of a few of Java's infinitely-many features.
Command-line applications in Java are enough like those in C/C++ that we might be able to fake it if we could just get a trivial program to compile and run. OK. Put the following in a file called "Jcopy.java". Compile it with "javac Jcopy.java".
// Jcopy - byte copier with buffering
//
// compile with:
// javac Jcopy.java
//
// execute with:
// java Jcopy source-file target-file
//
// Points to note:
// You can use the FileInput/OutputStreams without buffering, but
// since that means you're going to the disk for each byte, that's
// insanely slow. Of course it would make more sense to read more
// than one byte at a time, but the buffered I/O stream classes do
// that for you invisibly. On the other hand, you have to call
// the flush method to ensure that everything gets written to disk.
//
// --------------------------------------------------------------------
import java.io.*;
import java.util.*;
public class Jcopy
{
public static void main (String args[])
{
File inFile, outFile;
BufferedInputStream in;
BufferedOutputStream out;
int value;
long startTime, stopTime;
// We want to time this, so we read the system clock before doing anything else.
startTime = System.currentTimeMillis();
// Bail out if user didn't supply names of source & target files.
int argCount = args.length;
if (argCount != 2) {
System.out.println("usage: java Jcopy source target");
System.exit(0);
}
// Bail out if we can't read from the named input file.
inFile = new File(args[0]);
if (!inFile.exists() || !inFile.canRead()) {
System.out.println("Can't read " + args[0] + ".");
System.exit(0);
}
// Open output file - we don't care if it exists, since we haven't turned on the
// append flag and will therefore write over any file with the supplied name.
outFile = new File(args[1]);
// Create the stream classes, and copy input to output.
try {
in = new BufferedInputStream(
new FileInputStream(inFile));
out = new BufferedOutputStream(
new FileOutputStream(outFile));
while ((value = in.read()) != -1) {
out.write(value);
}
out.flush();
} catch (Exception ex) {
System.out.println("I/O error.");
System.exit(0);
}
// Done. Read the time again and calculate elapsed time.
stopTime = System.currentTimeMillis();
System.out.println("Elapsed time = " +
(double)(stopTime - startTime) / 1000.);
}
}
Here are two Swing-based GUI applications of the "Hello, World" genre. The first, while following Java's unbreakable rule that all code gets wrapped up in a class, pays little additional homage to object oriented programming. Only the JFrame object gets a name. We add a text label and an event handler for the window without creating any new object names.
// --------------------------------------------------------------------
// Test1.java - the app class subclasses nothing, and the main
// method does everything.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test1
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setBounds(50, 50, 100, 100);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}});
frame.getContentPane().add(new JLabel("Test 1"));
frame.setVisible(true);
}
}
In this example, we do exactly the same thing, but make the class/object structure explicit. The application class is now a subclass of JFrame (it "is a" JFrame, while Test1 "has a" JFrame). The private classes that now represent the text label and event handler could have been made public, but that would have meant putting them in separate files, adding still more overkill to an already silly example.
// --------------------------------------------------------------------
// Test2.java - the app class extends JFrame, and the main
// method just calls a constructor.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test2 extends JFrame
{
public Test2()
{
setBounds(50, 50, 100, 100);
addWindowListener(new Test2WindowListener());
getContentPane().add(new Test2Label());
setVisible(true);
}
public static void main(String[] args)
{
Test2 frame = new Test2();
}
}
class Test2WindowListener extends WindowAdapter
{
public void windowClosing(WindowEvent evt)
{
System.exit(0);
}
}
class Test2Label extends JLabel
{
Test2Label()
{
super("Test 2");
}
}
A Timer lets you schedule a TimerTask for delayed and/or periodic execution. TimerTask is an abstract class for which a run method must be defined.
// --------------------------------------------------------------------
// TimeMachine.java - The main method sets up the timer, and the timer
// task's run method simply calls another static method in the main
// application class.
import java.util.*;
public class TimeMachine
{
public static void main(String args[])
{
TTask tt = new TTask();
Timer t = new Timer();
t.schedule(tt, 500, 500);
}
public static void doIt()
{
System.out.println("did it!");
}
}
class TTask extends TimerTask
{
public void run()
{
TimeMachine.doIt();
}
}
Again, we can make this a bit shorter by eliminating a couple of names and making the TimerTask anonymous. Even if it looks a bit obscure, it's a highly-reuseable idiom.
// --------------------------------------------------------------------
import java.util.*;
public class TimeMachine2
{
public static void main(String args[])
{
new Timer().schedule(
new TimerTask() {
public void run()
{
doIt();
}
}, 500, 500
);
}
public static void doIt()
{
System.out.println("did it!");
}
}
One interesting thing we can do with a timer is have our main loop watching a separate thread:
// --------------------------------------------------------------------
// Here we have a timer-driven main task polling the value of a
// counter running as a separate thread:
import java.util.*;
public class ThreadFun
{
static long val;
static MyThread mt = new MyThread();
public static void main(String args[])
{
mt.start();
new Timer().schedule(
new TimerTask() {
public void run()
{
checkIt();
}
}, 500, 500
);
}
public static void checkIt()
{
val = mt.getValue();
System.out.println("Current value is " + val);
if (val > 150) {
mt.signalDone();
try {
mt.join(1000);
} catch(Exception ex) {
}
System.exit(0);
}
}
}
class MyThread extends Thread
{
long theValue;
boolean done;
public void run()
{
theValue = 0;
done = false;
while (!done) {
try {
sleep(200);
} catch(Exception ex) {
}
theValue += 1;
}
}
public long getValue()
{
return theValue;
}
public void signalDone()
{
done = true;
}
}
We've already done some stuff with binary files, so let's demonstrate what we can do with text files. I'm not especially happy about the way I have to include so much in one try/catch block to avoid "may not be initialized" errors, but that's probably something I'm doing wrong.
// --------------------------------------------------------------------
// TextTwiddler.java - text I/O and simple string parsing.
import java.util.*;
import java.io.*;
public class TextTwiddler
{
public static void main(String args[])
{
String inName = "input.txt";
String outName = "output.txt";
File inFile = new File(inName);
File outFile = new File(outName);
boolean done = false;
int lineCount = 0;
int tokenCount;
String lineIn = null;
String firstName, lastName;
int age;
float bats;
BufferedReader br;
BufferedWriter bw;
StringTokenizer st;
if (!inFile.exists()) {
System.out.println(inName + " does not exist.");
System.exit(0);
} else if (outFile.exists()) {
System.out.println(outName + " already exists.");
System.exit(0);
}
try {
br = new BufferedReader(new FileReader(inFile));
bw = new BufferedWriter(new FileWriter(outFile));
while (!done) {
lineIn = br.readLine();
if (lineIn == null) {
done = true;
} else {
st = new StringTokenizer(lineIn);
tokenCount = st.countTokens();
if (tokenCount == 0) {
done = true;
} else {
lineCount += 1;
firstName = st.nextToken();
lastName = st.nextToken();
age = Integer.parseInt(
st.nextToken());
bats = Float.parseFloat(
st.nextToken());
bw.write("<player>\n" +
"<player_id>" +
lineCount +
"</player_id>\n");
bw.write("<first_name>" +
firstName +
"</first_name>\n");
bw.write("<last_name>" +
lastName +
"</last_name>\n");
bw.write("<age>" +
age +
"</age>\n");
bw.write("<batting_average>" +
bats +
"</batting_average>\n" +
"</player>\n");
}
}
}
br.close();
bw.close();
} catch (Exception ex) {
System.out.println(ex.toString());
System.exit(0);
}
}
}
Finally, here's a more interesting GUI application:
// SimpleView.java - a minimal image viewer.
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
// -----------------------------------------------------------------------------
public class SimpleView
{
public static final Color BG_CYAN = new Color(0x0066CC);
public static final Color DARK_TRIM = new Color(0x003366);
public static void main(String[] args)
{
int wScreen, hScreen;
String imageFileName;
final JFrame theFrame;
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
wScreen = dim.width;
hScreen = dim.height;
theFrame = new JFrame("SimpleView");
theFrame.getContentPane().setLayout(null);
theFrame.setBounds(0, 0, wScreen, hScreen);
theFrame.setBackground(DARK_TRIM);
// Turn off the default window closer so we can get confirmation
// from the user:
theFrame.setDefaultCloseOperation(
javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
theFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
int status = JOptionPane.showConfirmDialog(
theFrame.getContentPane(),
"All done?",
"SimpleView",
JOptionPane.YES_NO_OPTION);
if (status == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
});
// Put the display and control panels into the window:
ViewerPanel viewer = new ViewerPanel();
viewer.setColor(DARK_TRIM);
viewer.setBounds(180, 0, wScreen - 180, hScreen);
theFrame.getContentPane().add(viewer);
ControlPanel controls = new ControlPanel(viewer);
controls.setBackground(BG_CYAN);
controls.setBounds(0, 0, 180, hScreen);
theFrame.getContentPane().add(controls);
theFrame.setVisible(true);
}
}
// -----------------------------------------------------------------------------
// A ViewerPanel is a JPanel upon which we paint an image if one has been
// selected...
class ViewerPanel extends JPanel
{
boolean imageLoaded;
BufferedImage img;
Color panelColor;
int wPanel, hPanel;
int wImage, hImage;
public ViewerPanel()
{
imageLoaded = false;
img = null;
panelColor = Color.white;
wPanel = 0;
hPanel = 0;
wImage = 0;
hImage = 0;
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setColor(panelColor);
wPanel = getWidth();
hPanel = getHeight();
g2.fillRect(0, 0, wPanel, hPanel);
if (imageLoaded) {
int x0 = (wPanel - wImage) / 2;
int y0 = (hPanel - hImage) / 2;
g2.drawImage(img, x0, y0, this);
}
}
public void setColor(Color _panelColor)
{
panelColor = _panelColor;
}
public void loadImage(String imgName)
{
Image theImage = new ImageIcon(imgName).getImage();
wImage = theImage.getWidth(null);
hImage = theImage.getHeight(null);
img = new BufferedImage(wImage, hImage,
BufferedImage.TYPE_INT_RGB);
Graphics g = img.createGraphics();
g.drawImage(theImage, 0, 0, null);
g.dispose();
imageLoaded = true;
repaint();
}
public boolean isImageLoaded()
{
return imageLoaded;
}
}
// -----------------------------------------------------------------------------
// A ControlPanel is a JPanel upon which we place a couple of buttons that
// control this program's (very limited) functions. To each button we add
// one of the "ActionListener" callback objects defined below. The null
// LayoutManager lets us specify button size/placement explicitly.
class ControlPanel extends JPanel
{
ViewerPanel v;
public ControlPanel(ViewerPanel _v)
{
v = _v;
setBounds (0, 0, 200, 600);
setBackground(new Color(0x33ddff));
setLayout(null);
JButton openButton = new JButton("Browse Files");
openButton.addActionListener(new ImageGetter(v));
openButton.setBounds(40, 5, 120, 30);
add(openButton);
JButton quitButton = new JButton("Quit");
quitButton.addActionListener(new ExitListener(v));
quitButton.setBounds(40, 40, 120, 30);
add(quitButton);
}
}
// -----------------------------------------------------------------------------
// Respond to presses of the "Browse Files" button by popping up a
// file browser gadget.
class ImageGetter implements ActionListener
{
ViewerPanel v;
File f;
int status, w, h;
BufferedImage bufImg;
boolean isImageReady;
public ImageGetter(ViewerPanel _v)
{
v = _v;
f = null;
isImageReady = false;
}
public void actionPerformed(ActionEvent ae)
{
JFileChooser c = new JFileChooser();
status = c.showOpenDialog(v);
if (status == JFileChooser.APPROVE_OPTION) {
isImageReady = false;
f = c.getSelectedFile();
v.loadImage(f.getAbsolutePath());
}
}
}
// -----------------------------------------------------------------------------
// React to presses of the "Quit" button by confirming that the user really
// does want to exit.
class ExitListener implements ActionListener
{
static final String msg = "Are you sure you want to quit?";
Container c;
public ExitListener(Container _c)
{
c = _c;
}
public void actionPerformed(ActionEvent ae)
{
int status = JOptionPane.showConfirmDialog(c, msg,
"SimpleView",
JOptionPane.YES_NO_OPTION);
if (status == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
}