Interface inheritance
Interface: method definitions but no code
Can inherit multiple interfaces
public interface Drawable {
public void setColor(Color c);
}
public class C implements ImageObserver, Drawable {
}
Strings
Strings are totally different from C
String s1 = "Test";
String s2 = s1 + " string";
if (s2.equals("Test string")) {
System.out.println("String is "+ s2 +"length "+s2.length());
}
char c = s2.charAt(3);
System.out.println(s2.toUpperCase());
int n = Integer.parseInt("123");
System.out.println("n is "+n);
Arrays
int x[] = new int[20];
x[5] = 42;
int y[] = {1,2,3,4,5};
Arrays are bound checked.
x[20] = 42; // throws ArrayIndexOutOfBoundsException
Multidimensional arrays
String z[][] = new String[20][20];
Non-rectangular arrays
int t[][] = new int[3][];
t[0] = new int[2];
t[1] = new int[30];
t[2] = new int[100];
t[1][15] = 42;
Exceptions
Exceptions provide an error handling mechanism
try {
System.out.println(a/b);
} catch (ArithmeticException e) {
System.out.println("Had a problem!");
}
Exceptions propagate upwards until a catch block gets them
Can throw exceptions:
throw new ArithmeticException();
Can have multiple catch blocks:
try {
} catch (IOException e) {
} catch (ArithmeticException e) {
} catch (Exception e) { // most general exception
}
Methods must specify exceptions they throw
public Foo() throws IOException { ... }
Multi-threading
Threads run in same address space
Useful for long computations, preventing GUI from
blocking, updating image in background
Thread example
class ThreadExample extends Thread {
int count=0;
public void run() {
while (count<100) {
System.out.println("hi "+(count++));
try {
sleep((int)(5000*Math.random()));
} catch (InterruptedException e) {}
}
}
};
ThreadExample t1 = new ThreadExample();
t1.start();
ThreadExample t2 = new ThreadExample();
t2.start();
Source
Compiling and running
Example1.java:
import java.applet.*;
import java.awt.*;
public class Example1 extends Applet {
public void paint(Graphics g) {
g.drawString("Hello world",75,100);
}
};
example1.html:
<applet code="Example1.class" width=200 height=200>
</applet>
Compile with "javac Example1.java"
Display with "appletviewer example1.html"
Or view on web page with a browser
Standalone application
import java.awt.*;
class StandaloneDemo extends Frame {
public static void main(String args[]) {
System.out.println("We have "+args.length+" args");
StandaloneDemo sd = new StandaloneDemo();
sd.start();
}
public void start() {
resize(200,200);
show();
}
public void paint(Graphics g) {
g.drawString("Hello world",60,100);
}
}
Run with "java StandaloneDemo"
Safety
- Runtime bytecode verifier
- Null reference checking
- Array bounds checking
- Garbage collection
- Exceptions for faults
- Access to files, network controlled by runtime environment
Bytecodes
Compiled to interpreted architecture neutral stack-based byte codes
int m1(int a, int b) {
if (a>1) {
return a+b;
} else {
return a/b;
}
}
Method int m1(int,int)
0 iload_1 // push a
1 iconst_1 // push 1
2 if_icmple 9 // if a<=1 goto 9
5 iload_1 // push a
6 iload_2 // push b
7 iadd // add
8 ireturn // return a+b
9 iload_1
10 iload_2
11 idiv
12 ireturn // return a/b
Distributed objects
- Communication framework
- Compound documents
- Components
Components
|
|
Canvas canvas = new Canvas();
Button button = new Button("A Button");
TextField tf = new TextField("A Textfield");
TextArea ta = new TextArea("TextArea", 5, 20);
List list = new List(10, true);
Choice choice = new Choice();
CheckboxGroup box = new CheckboxGroup();
Label label = new Label("A Label");
Scrollbar sbar = new Scrollbar();
|
Source
Component Layout
Default: FlowLayout
public void init() {
Button but1 = new Button("button1");
Button but2 = new Button("button2"); ...
setLayout(new FlowLayout());
add(but1);
add(but2);
add(but3);
add(but4);
add(but5);
add(but6);
}
Source
GridBagLayout
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
setLayout(gbl);
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.BOTH;
gbl.setConstraints(but1, gbc);
add(but1);
gbl.setConstraints(but2,gbc);
add(but2);
gbc.gridwidth=GridBagConstraints.REMAINDER;
gbl.setConstraints(but3,gbc);
add(but3);
gbc.gridwidth=2;
gbl.setConstraints(but4,gbc);
add(but4);
gbc.gridwidth=GridBagConstraints.REMAINDER;
gbl.setConstraints(but5,gbc);
add(but5);
gbl.setConstraints(but6,gbc);
add(but6);
Source
Nested layout
Panel p1 = new Panel();
p1.setLayout(gbl);
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
gbc.gridwidth=GridBagConstraints.REMAINDER;
gbl.setConstraints(but1,gbc); p1.add(but1);
gbl.setConstraints(but2,gbc); p1.add(but2);
gbl.setConstraints(but3,gbc); p1.add(but3);
Panel p2 = new Panel();
p2.setLayout(gbl);
gbl.setConstraints(but4,gbc); p2.add(but4);
gbl.setConstraints(but5,gbc); p2.add(but5);
setLayout(gbl);
gbc.gridwidth=1;
gbl.setConstraints(p1,gbc); add(p1);
gbc.gridwidth=GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.BOTH;
gbl.setConstraints(p2,gbc); add(p2);
Source
Events
Propagated events for mouse, keys, GUI actions, focus.
public boolean handleEvent(Event evt) {
area.appendText(""+evt+"\n\n");
return true;
}
Source
Event convenience methods
Default handleEvent calls:
mouseEnter, mouseExit, mouseMove, mouseDown, mouseDrag, mouseUp
keyDown, keyUp
gotFocus, lostFocus.
action
Return true if you process the event.
Mouse events
public class Scribble extends Applet {
int lastx, lasty;
public boolean mouseDown(Event e, int x, int y) {
lastx = x; lasty = y;
return true;
}
public boolean mouseDrag(Event e, int x, int y) {
Graphics g = getGraphics();
g.drawLine(lastx,lasty,x,y);
lastx = x; lasty = y;
return true;
}
}
Source
Buttons
Button b1 = new Button("Button 1");
Button b2 = new Button("Button 2");
public void init() {
add(b1);
add(b2);
}
public boolean action(Event evt, Object obj) {
if (evt.target.equals(b1)) {
System.out.println("Button 1 pressed");
return true;
} else if (evt.target.equals(b2)) {
System.out.println("Button 2 pressed");
return true;
} else {
return super.action(evt,obj);
}
}
Source
Other buttons
Choice ch = new Choice();
Checkbox ckbx = new Checkbox("Test");
CheckboxGroup radio = new CheckboxGroup();
Checkbox r1 = new Checkbox("A",radio,true);
Checkbox r2 = new Checkbox("B",radio,false);
Checkbox r3 = new Checkbox("C",radio,false);
public void init() {
ch.addItem("Red");
ch.addItem("Green");
add(ch); add(ckbx); add(r1); add(r2); add(r3);
}
public boolean action(Event evt, Object obj) {
if (evt.target.equals(ch)) {
System.out.println("Choice " +ch.getSelectedIndex()+ch.getSelectedItem());
return true;
} else if (evt.target.equals(ckbx)) {
System.out.println("Checkbox "+ckbx.getState());
return true;
} else if (evt.target instanceof Checkbox) {
System.out.println("Radio:" + ((Checkbox)evt.target).getLabel());
return true;
} else {
return super.action(evt,obj);
}
}
Source
Scrollbar
Events are SCROLL_LINE_UP, SCROLL_LINE_DOWN, SCROLL_PAGE_UP,
SCROLL_PAGE_DOWN, SCROLL_ABSOLUTE
Scrollbar sb = new Scrollbar(Scrollbar.HORIZONTAL,0,10,0,100);
Label l = new Label(" ");
public boolean handleEvent(Event evt) {
if (evt.target.equals(sb)) {
if (evt.id==Event.SCROLL_ABSOLUTE) {
System.out.println("ABSOLUTE "+evt.arg);
}
l.setText(""+sb.getValue());
return true;
} else {
return super.handleEvent(evt);
}
}
Source
List and TextField
List l = new List();
TextField tf = new TextField("Default");
public void init() {
l.addItem("Red");
l.addItem("Green");
l.addItem("Blue");
add(l); add(tf);
}
public boolean action(Event evt, Object obj) {
if (evt.target.equals(l)) {
System.out.println("List " +l.getSelectedIndex());
tf.setText(l.getSelectedItem());
return true;
} else if (evt.target.equals(tf)) {
System.out.println("Text "+tf.getText());
return true;
} else {
return super.action(evt,obj);
}
}
Source
Graphics
public void paint(Graphics g) {
g.setColor(Color.red);
g.drawLine(0,0,100,100);
g.setColor(Color.green);
g.drawOval(50,50,100,20);
g.setColor(Color.blue);
g.drawArc(150,50,40,20,10,90);
int x[] = {110,150,130}; int y[] = {80,20,60};
g.fillPolygon(new Polygon(x,y,3));
g.setFont(new Font("Helvetica",Font.BOLD,24));
g.fillRect(250,0,10,40);
g.drawString("String", 300,20);
}
Source
Double buffering
- Make an image
- Write to the image, repaint
- Paint image to screen
- Override update to prevent flicker
Image img = null;
Graphics img_g = null;
public void init() {
img = createImage(size().width, size().height);
img_g = img.getGraphics();
img_g.setColor(Color.black);
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
g.drawImage(img,0,0,this);
}
public boolean mouseDrag(Event e, int x, int y) {
img_g.drawLine(lastx,lasty,x,y);
repaint();
...
}
Source
Image processing
public class BugEyes extends Applet implements ImageObserver {
public boolean imageUpdate(Image origimg, int infoflags, int x, int y, int w, int h) {
repaint();
return true;
}
Image img = getImage(getDocumentBase(),"foo.gif");
g.drawImage(img,x,y,this);,
int pix[] = new int[width*height];
Image piximg = createImage(new MemoryImageSource(width, height, pix, 0, width));
Source
Animation
public class Anim1 extends Applet implements Runnable {
Thread my_thread = null;
Image img;
public void init() {
img = getImage(getDocumentBase(),"foo.gif");
my_thread = new Thread(this);
my_thread.start();
}
int x=0,y=0,xdir=2,ydir=3;
public void paint(Graphics g) {
g.drawImage(img, x,y,null);
}
public void run() {
while (true) {
x += xdir;
if (x<2 || x>size().width-20) { xdir = -xdir; }
y += ydir;
if (y<2 || y>size().height-20) { ydir = -ydir; }
repaint();
try {
my_thread.sleep(20);
} catch (InterruptedException e) {}
}
}
}
Source
More information
Assignment
In the assignment, you modify a simple networked drawing program
Running the assignment program
Running the demo program
Standalone
- Save the HTML file as draw.html and the java file as Draw.java.
- Compile Draw.java with "javac Draw.java".
- Run the applet with "appletviewer draw.html".
- Click on buttons to select drawing modes and draw with the mouse.
- You should also be able to run the applet by loading draw.html into
Netscape.
Networked
The drawing program also has a mode where you can connect multiple drawing
applets together. Anything drawn in one applet will appear in all the
others. To do this, we use a helper program. This helper program accepts
connections on port 4321. Any input on a connection is echoed to all the
other connections. Since this is like a simple "chat" program, I call it
"chatServer", although we'll be using it to broadcast graphics events
rather than text.
- Save the chatServer program in chatServer.java.
- Compile it with "javac chatServer.java".
- Run it with "java chatServer". Note that it runs as a standalone program,
not an applet.
- To see what chatServer does, do "telnet localhost 4321" from a couple
windows (or "telnet name-of-host 4321" from a different machine). Note
that anything you type into one session is echoed to the rest. Exit
telnet with control-] and quit.
- With chatServer still running, start up a couple drawing applets
("appletviewer draw.html")
- Connect each applet to chatServer by hitting "return" in the connection
window. If the applet is running on a different machine from chatServer,
you need to explicitly enter the hostname.
- Now draw in one applet, and the results should be echoed to the other
applets.
- Kill the chatServer.
The program internals
The program is a drawing applet that lets you draw lines, ovals, text, and
colored circles on the screen. In addition, it allows you to connect
multiple drawing programs together over the network to provide a
collaborative drawing environment.
The program is structured as multiple object-oriented classes:
Draw: this is the top-level class and extends the Java Applet class. This
class provides the top-level window and the user interface. The init()
method sets things up, and then the class just waits for user-interface
events.
DrawingCanvas: this class extends the Java Canvas class. It provides the
area in which you draw. It provides a double-buffered drawing area
(through backingImg and backingGraphics). Mouse and key events are passed
to an object that does the actual drawing.
DrawObject: this is the superclass that does most of the work. A
DrawObject takes mouse events and draws the appropriate stuff on the
DrawingCanvas. In addition, this class provides the remote drawing
support. This class is subclassed to provide the different operations:
DrawLine, DrawCircle, etc. Each of these classes must implement the
mouseDown, mouseUp, mouseDrag, and keyPress methods and do the appropriate
thing.
DrawConnection: this class sits around and waits for incoming remote
drawing invocations. It extends the Java Thread class, so it runs as a
separate thread; otherwise the program would hang while we wait for
incoming data.
The user interface
The user interface is defined in Draw. The screen is divided into three
parts: a Panel of buttons (buttonpanel) to the left, the DrawingCanvas to
the right, and a Panel of option controls on the bottom. The buttonpanel
contains buttons for the different drawing modes. The optionpanel
contains a Choice to select the color, a Checkbox to select fill mode, a
TextField to specify the network connection, and various Labels.
Network connections
To connect multiple drawing applets together, things get a little tricky
because of the Java security model. An applet can't listen on random
sockets, but a stand-alone application can. So I've made a stand-alone
application to do the listening and tie the servers together.
Now, what you do is have all the draw programs connect to "chatServer";
each one sends drawing operations to the chatServer and the others all
receive these operations. To link up, type the hostname in the
"connection" window in the draw window and hit return.
Note: an applet running with appletviewer can connect to any host, but an
applet running under Netscape can only connect to the same host that is
providing the applet. I.e., if you're loading this page locally and it
says "//localhost/...", you have to specify "localhost" as the hostname.
If you're getting the pages off foo.bar.edu, you have to run chatServer on
foo.bar.edu and specify foo.bar.edu for the hostname.
The Assignment
-
The drawing program currently only supports three colors. Add support for
additional colors. You will have to do more addItem calls to add the
colors to the Choice component. You will also need to update the event
handling code to do the right thing when these are selected. Finally, you
need to add a setColor call to DrawCircle and DrawText to make color
selection work here.
-
Add support to change the size, name ("Helvetica", "TimesRoman", "Courier") and
style (Font.PLAIN, Font.BOLD, Font.ITALIC) of text. You can use a Choice or
a TextEntry for input, or even a Scrollbar for the size. Checkboxes could
select the style. Add fields to Options to
hold these. You'll make a new Font(name,style,size) and then do a setFont
on the appropriate graphics.
Updating the font in the middle of a string will be tricky,
since there isn't an easy way to erase the old string, so you can just do
the update on a mouseDown.
-
Add the missing functionality to remote operation. In particular: the
option code needs to handle more colors and needs to transmit the fill
flag. DrawText needs to send the appropriate events. While implementing
this, think about remote object invocation in Java and what it lacks.
Note that you have to implement remote method invocation yourself; a
distributed object model would let you invoke remote objects
transparently, without all the fuss of encoding data into a stream.
-
The drawRender class puts a block of pixels on the screen; currently this
is just a colored circle. Make the drawRender class do something more
interesting; use some of your previous graphics knowledge to render
something.
-
Extend the program to draw splines. Make a class DrawSpline that
subclasses DrawObject and add a button to activate it. The simplest
approach is to collect four mouse clicks and then draw the Bezier spline
that uses these points as control points. Hint: define an array of four
ints with "int xpts[] = new int[4];" for the x and y control points. You
can use graphics.drawLine to draw the line segments.
Advanced: make the current spline editable. When you get a mouseDown, see
if it is close to any of the control points. If so, let the user drag the
control point around and redraw the spline dynamically. Note: this gets
tricky if you want to avoid messing up the screen. The xor trick won't
work well since you're likely to draw some pixels twice. One solution:
save the current drawing image before you start displaying the spline.
Then, whenever you update the spline, draw the old background and then the
spline. Code to do this:
class DrawSpline extends DrawObject {
Image saveImg = null;
public DrawSpline(...) {
// We get an image buffer of the proper size
saveImg = parent.createImage(parent.size().width, parent.size().height);
// Tricky bit: we tell Draw to update our image rather than the screen image
parent.update(saveImg.getGraphics());
}
void drawTheSpline() {
graphics.drawImage(saveImg,0,0,null); // redraw what was there before
// now render the spline segments with graphics.drawLine(...);
}
}