Student: I went and looked up the
Px and Colors classes, because you wouldn’t tell me about them. What’s going on with the word static in them? I can kind of read them but I don’t know what that part means. And also what’s going on with throw? /** A width, height, or radius: zero is fine (an empty picture), negative is not. */
static int size(String what, int v) {
if (v < 0) throw new IllegalArgumentException(what + " is negative: " + v);
return v;
}
}Prof: Great questions. Let’s take them one at a time. First
throw – this is Java’s built in way of reporting errors. When throw ... evaluates, it stops the current methods and all methods on the stack. It will report the error you created, and also the stack trace. This is called throwing an exception.Student: I’ve seen stack traces before! So you’re telling me if I give a negative number I’ll get an error from
size?Prof: That’s right.
Student: What is
IllegalArgumentException?Prof: Java has many built in exceptions – they are descriptions of errors that can happen. In this case, the
size method is just used to check if a number is non-negative. It’s used where we want to make sure a radius or a width is zero or more.Student: Huh, OK. Wait what’s
what?Prof: What?
Student: The parameter, it’s called
what, but it’s only used in the error message?Prof: That’s right – reporting errors is important! So important that sometimes we have information included just in case an error occurs.
Student: OK, so in this use in
Raster it’s just to say whether width or height was wrong? Raster(int width, int height) {
this.width = Px.size("width", width);
this.height = Px.size("height", height);
// ...Prof: That’s it – it assigns
width or height to the value after checking it.Student: And I’m seeing something now that I look at this code. Can I make a guess about
static?Prof: By all means!
Student: Well, I see that
size was declared static. And then we did one of those calls like we do on Math where it’s a capital thing before the dot, which ought to be a class. So is that how we call size because it’s static?Prof: Nice analysis! You got it. A
static method has no receiver. We usually use it for library methods that aren’t associated with any object instance. So a lot of Math and Arrays are like that, because they work directly on numbers or arrays without constructing an object first. And in the Pict library, that’s what Px is doing.Student: OK now I want to see what it does.
Prof: Try doing a call from another method, called from
main, to see the trace.In the book, the version of Java that runs cannot show line numbers. When running in a full-fledged programming environment, you’d see something like this:
$ javac Throw.java Pict.java $ java Throw Should be just 4 4 Exception in thread "main" java.lang.IllegalArgumentException: test is negative: -3 at Px.size(Pict.java:90) at Throw.anotherMethod(Throw.java:3) at Throw.main(Throw.java:6)
Student: Neat! So I can see that the stack has
Px.size, then anotherMethod, then main. Wait! That’s the same order as the stack in a memory diagram!Prof: Indeed – the stack trace captures exactly the stack we draw. To draw it the way we usually do:
Names (the stack)
Px.size("test", -3)what = testv = -3throws IllegalArgumentExceptionanotherMethod()main()top level
Heap
Student: Interesting. So those diagrams really are what’s happening.
Prof: They are pretty accurate!
Practice
Each of these is a call we have made somewhere in the book. Is it a
static method, or an instance method?Math.sqrt(2.0) is "trash".length() is Integer.parseInt("42") is dot.beside(square) is Arrays.equals(a, b) is raster.colorAt(0, 0) is Colors.named("red") is query.matches(img) is None of these methods is marked static yet. Which ones could be static, because they never use x or y (or this)? Select all that could.
record Point(double x, double y) {
double distanceTo(Point other) {
return Math.sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y));
}
Point flipped() { return new Point(y, x); }
Point origin() { return new Point(0, 0); }
double average(double a, double b) { return (a + b) / 2; }
boolean isOrigin() { return x == 0 && y == 0; }
}originoriginmakes a newPointout of nothing but numbers, so it doesn’t need a point to be called on:Point.origin().averageaverageonly uses its parametersaandb, so it could bestatic. (It hardly belongs inPointat all.)distanceTodistanceTousesxandy, the fields of the point it was called on.flippedflippedusesxandyto build the new point.isOriginisOriginasks about the fields of the point it was called on.