-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexceptions.tex
More file actions
409 lines (339 loc) · 16.3 KB
/
Copy pathexceptions.tex
File metadata and controls
409 lines (339 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
\chapter{Exceptions}
\label{ch:exceptions}
\index{exception}
\index{drive@\texttt{.drive()}}
\index{car@\texttt{Car}}
%\section{Recognizing error conditions}
Let's go back and revisit our \texttt{.drive()} method from the \texttt{Car}
example on p.~\pageref{fig:carClassCodePreExceptions}. It looked like this:
\vspace{-.2in}
\begin{Verbatim}[fontsize=\footnotesize,samepage=true,frame=single]
class Car {
...
void drive(int numMiles) {
double galsBurned = numMiles / this.gasMileage;
this.galsRemaining -= galsBurned;
this.odo += numMiles;
}
}
\end{Verbatim}
\vspace{-.2in}
The shrewd reader (and driver!)~will realize that this method is a bit
optimistic: when told to drive \texttt{numMiles}, it blindly does so, even if
there's not enough gas to get that far. We ought to guard against this kind of
wishful thinking by \textit{not permitting} a drive that's outside our range.
If told to drive 1000 miles when we only have enough gas to go 200, we'll just
say ``no.'' That's way better than ending up with a negative gas tank!
% and wreaking unknown havoc later in the program!
The first step in implementing this kind of defensive coding is to figure out
\textit{when} to refuse to carry out orders. That's not too hard in this case:
our local \texttt{galsBurned} variable is exactly what we need: if it turns
out to be higher than the gas remaining in the tank, we are officially in
Nonsense Land. A simple \texttt{if} statement can take care of that.
\section{Bad ways of handling error conditions}
The second step is figuring out \textit{what to do} when this occurs. Most
people's first instinct is to blare out a siren:
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
// Inadequate approach #1
class Car {
...
void drive(int numMiles) {
double galsBurned = numMiles / this.gasMileage;
if (galsBurned > this.galsRemaining) {
System.out.println("Not enough gas!!");
}
this.galsRemaining -= galsBurned;
this.odo += numMiles;
}
}
\end{Verbatim}
\index{console}
\index{state!illegal}
This is done in the hopes that someone will hear us and be alarmed. The
problem is, this error will go to the console, where it may or may not ever be
seen; and even if someone notices it, we've still already done the dirty deed.
We have a \texttt{Car} object with an \textbf{illegal state}: a negative gas
level.
Slightly better, but still not good enough, is to print the error and also
refuse to carry out orders:
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
// Inadequate approach #2
class Car {
...
void drive(int numMiles) {
double galsBurned = numMiles / this.gasMileage;
if (galsBurned > this.galsRemaining) {
System.out.println("Not enough gas!!");
return; // <--- NOTICE THIS!
}
this.galsRemaining -= galsBurned;
this.odo += numMiles;
}
}
\end{Verbatim}
\index{return@\texttt{return}}
Now, in addition to printing the error, we also \texttt{return} from the
method prematurely, instead of carrying out the nonsensical operation.
\index{client code}
The problem with this approach is that \textit{the client code is not alerted
that anything went wrong.} We'll see some actual client code in action in the
next section, but for now just realize that whoever called
\texttt{.drive(1000)} is none the wiser. The client merrily chugs along
thinking that the thousand-mile drive was plain sailing, oblivious to the fact
that no such drive actually occurred.
\section{The right way: \texttt{throw}ing and \texttt{catch}ing}
\index{throw@\texttt{throw}}
\index{catch@\texttt{catch}}
The right way to handle this is with Java's \texttt{Exception} mechanism. We
don't return prematurely, as above; instead, \textit{we don't return at all.}
Exceptions are Java's way of allowing a method \textit{not} to return, but
rather to raise a big red flag to indicate that carrying out the method just
flat didn't work. It's the only responsible thing to do.
Our first step is to ``throw an exception'' instead of returning. Here's how:
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
// Correct approach (not finished yet)
class Car {
...
void drive(int numMiles) {
double galsBurned = numMiles / this.gasMileage;
if (galsBurned > this.galsRemaining) {
throw new Exception("Not enough gas!!"); // NOTICE!
}
this.galsRemaining -= galsBurned;
this.odo += numMiles;
}
}
\end{Verbatim}
Operationally, throwing an exception has the same immediate effect as
returning: the method instantly terminates and returns control back to the
client code. The differences, as we'll see in the next section, are that the
client code is aware that something unusual happened, does not get a return
value, and can take evasive action.
If you try to compile the above code, though, you get an error, which says:
\begin{Verbatim}[samepage=true,fontsize=\scriptsize]
error: unreported Exception; must be caught or declared to be thrown
\end{Verbatim}
It's actually nice of Java to give this error, and here's why. Inside our
method, we've created a possibility that we won't return at \textit{all}, and
will instead barf because of an insoluble problem. Java requires that if we do
that, we 'fess up and declare that this is a possibility. That prevents
unwitting programmers from blithely calling our method and not accounting for
the fact that it might not even run to completion.
Fixing it is simple; we just change the first line of the function to:
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
void drive(int numMiles) throws Exception {
...
\end{Verbatim}
That first line now says: ``you can call me on a \texttt{Car}, pass me an
integer argument, and get no return value. \textit{But} there's a possibility
that it won't work, and you should be aware of that.'' It's only honest, and
as we'll see, it allows the code that uses \texttt{Car}s to properly deal with
the problem.
\section{Calling a method that \texttt{throws} \texttt{Exception}}
\index{drive@\texttt{.drive()}}
The only remaining fly in our ointment (don't worry; he's easily swatted) is
that when client code \textit{calls} \texttt{.drive()}, it might not
necessarily run to completion. In fact, if we compile the above main program,
we'll get the same kind of compile error that we did when we were midway
through implementing the Exception-throwing stuff. It'll say we're being
unconscionably remiss by refusing to deal with the error that might occur
any time we tell one of our cars to drive.
\index{try/catch@\texttt{try}/\texttt{catch}}
The Java way to handle this is with a \textbf{try/catch block}. Essentially,
this just builds a little scaffolding around our call to suspicious methods
like \texttt{.drive()} so that if an exception is indeed ``thrown'' when we
call it, we can ``catch'' it and do something sensible. Here's what the code
looks like:
\label{originalExceptionExample}
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
public static void main(String args[]) {
...
// Caravan to Disneyworld! (Grammy's meeting us there.)
minivan.fillUp();
try {
minivan.drive(500);
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}
...
}
\end{Verbatim}
Instead of simply calling \texttt{minivan.drive()} and throwing caution to the
wind, we put that code in a \textbf{try block}. The code in a \texttt{try}
block is executed normally, step-by-step, just like anywhere else in a Java
program. But the \texttt{try} block has one or more (here just one)
contingency plans connected to the bottom of it, which can handle any special
(or ``exceptional'') conditions. In this case, that call to drive the minivan
500 miles through traffic will either work in its entirety and have the
desired effect, or it will abort in the middle of it and control will be
immediately transferred to the relevant catch block below it. The flow
continues in that \textbf{catch block}, in this case printing the message of
the \texttt{Exception} and then terminating the program.
What to do in each exceptional situation depends on the situation itself.
Sometimes, there are meaningful things one can do in response to an error:
like if a network connection fails, the code can retry connecting; or if a
checking account withdrawal fails, the system can cut over to the savings
account and cover the amount from those funds instead. In our case, if our
program tracked things like routes and desired destinations, a caught
exception would indicate that we need to find a new, temporary destination
other than the one we're currently seeking: one that has gas so we can fill
up.
\index{throws@\texttt{throws}}
Once all the method calls that are defined as ``\texttt{throws Exception}''
have been enclosed in try/catch blocks that at least nominally handle the
errors, the code compiles again and it can hopefully run error-free.
\section{Different kinds of exceptions}
By the way, throughout this chapter I've been referring to ``vanilla''
exceptions. By that, I mean we've literally used the plain word
``\texttt{Exception}'' in all the code samples. Sometimes you'll see code in
the real world that refers to specific \textit{kinds} of exceptions. Things
like:
\begin{compactitem}
\item \texttt{FileNotFoundException}
\item \texttt{DivideByZeroException}
\item \texttt{ArrayIndexOutOfBoundsException}
\item \texttt{IOException}
\item \texttt{NullPointerException}
\item ...
\end{compactitem}
You can probably tell from the names that these entities represent certain
specific kinds of error conditions: a misspelled filename, division by zero,
falling off the end of an array, \textit{etc.} Besides readability, one reason
these are useful is that Java allows you to catch different kinds of errors in
different catch blocks, so you can handle them differently.
Figure~\ref{fig:multipleExceptions} gives you the idea.
\begin{figure}[ht]
\centering
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
public static void main(String args[]) {
...
minivan.fillUp();
try {
minivan.drive(500);
} catch (OutOfGasException e1) {
...find nearest gas station...
} catch (FlatTireException e2) {
...stop car and get out the jack...
} catch (EarthquakeSwallowedTheCarException e3) {
...pray...
} catch (Exception e4) {
...print an error and stop program...
}
...
}
\end{Verbatim}
\caption{Multiple catch blocks, to handle different kinds of errors.}
\label{fig:multipleExceptions}
\end{figure}
Important: only \textit{one} catch block (at most) will get executed when this
code runs. Depending on what specifically went wrong, it could trigger the code
in the first, second, third, or fourth. (Or, if the \texttt{.drive()} was
successful, none of them will be triggered, obviously.) This is convenient
because your error-handling code can be organized.
Notice that the last of the four catch blocks in
Figure~\ref{fig:multipleExceptions} is for a ``vanilla'' exception. This is
like a default catch-all (no pun intended): it will be triggered only if none
of the specific three previously-mentioned errors occurred.
\pagebreak
The way to throw a special type of exception is the same as throwing a
vanilla one; you just ``\texttt{throw new OutOfGasException()}'' instead of
``\texttt{throw new Exception()}''. To actually create your own special kind of
exception, we'll need the material from
chapters~\ref{ch:inheritance}-\ref{ch:inheritance2}, so stay tuned.
\section{Stack traces}
\index{stack trace}
\index{printStackTrace@\texttt{.printStackTrace()}}
One more thing before we leave this chapter on exceptions. It often proves
useful to look at an \texttt{Exception}'s \textbf{stack trace}, which is
accomplished by calling \texttt{.printStackTrace()} on the
\texttt{Exception}, often in the \texttt{catch} block. For instance, suppose we
change the original try/catch block (p.~\pageref{originalExceptionExample}) to
this:
\begin{Verbatim}[samepage=true,fontsize=\footnotesize,frame=single]
try {
minivan.drive(500);
} catch (Exception e) {
e.printStackTrace(); <--- NOTICE THIS LINE
System.exit(1);
}
\end{Verbatim}
Now, what happens if there isn't enough gas in the old minivan to drive 500
miles? Here's what:
\begin{Verbatim}[fontsize=\small,samepage=true,frame=none]
java.lang.Exception: Not enough gas!!
at Car.drive(Car.java:37)
at Car.main(Car.java:57)
\end{Verbatim}
At first, this output is an eyeful, and causes some students to look away in
horror. But I'm going to encourage you to be brave and look again, and realize
how much useful information is here. This output is called the exception's
\textbf{stack trace}, by which we mean a readout of \textit{what the stack
looked like at the moment the exception was thrown.}
Just as we saw in Chapter~\ref{ch:memoryMatters}, Java maintains a stack of
frames, one for each method that's in progress. Whenever a method returns, the
top stack frame gets popped off the top; whenever a method is called, a new
stack frame gets pushed on the top. At any point in time, the state of the
running program is nicely captured by its stack.
One of the most helpful debugging tools you could imagine would be a precise
readout of the exact line of every function the program is currently ``in'' at
the instant the error occurred. And this is exactly what the stack trace is.
The above output tells us:
\begin{compactenum}
\item The exception was thrown on line 37 of \texttt{Car.java}. This was in
the \texttt{.drive()} method, which was called from...
\item ...line 57 of \texttt{Car.java}. This was in the \texttt{main()} method.
\end{compactenum}
In this case, there were only two active methods when the exception was
encountered. Other cases are of course more complicated. Suppose your stack
trace looked like this:
\begin{Verbatim}[fontsize=\small,samepage=true,frame=none]
BattleException: Power not charged.
at Hyperbeam.useAgainst(Hyperbeam.java:24)
at Snorlax.attack(Snorlax.java:43)
at Pokemon.takeTurn(Pokemon.java:181)
at Simulator.battle(Simulator.java:215)
at Simulator.main(Simulator.java:33)
\end{Verbatim}
Without even knowing anything about how this program works, you can tell a
\textit{ton} about the circumstances in which it crashed. Specifically:
\begin{compactenum}
\item An exception was thrown on line 24 of \texttt{Hyperbeam.java}. This was
in the \texttt{.useAgainst()} method, which was called from...
\item ...line 43 of \texttt{Snorlax.java}. This was in the \texttt{.attack()}
method, which was called from...
\item ...line 181 of \texttt{Pokemon.java}. This was in the
\texttt{.takeTurn()} method, which was called from...
\item ...line 215 of \texttt{Simulator.java}. This was in the
\texttt{.battle()} method, which was called from...
\item ...line 33 of \texttt{Simulator.java}. This was in the \texttt{main()}
method.
\end{compactenum}
This is usually your first line of defense when debugging: read the stack
trace of the \texttt{Exception}, reconstruct exactly what was going on when
the error occurred, and then set about figuring out why it was caused.
\index{stack trace}
\index{printStackTrace@\texttt{.printStackTrace()}}
By the way, you can also print a stack trace from any place in your code, even
if no error condition has arisen or exception is thrown. If you just type:
\vspace{-.15in}
\begin{Verbatim}[fontsize=\small,samepage=true,frame=single]
...
new Exception().printStackTrace();
...
\end{Verbatim}
\vspace{-.15in}
anywhere you please, then whenever that line of code is encountered, a stack
trace like those above will be spewed to your screen. It's often quite helpful
when you're scratching your head saying, ``okay, I know something's going wrong
in this method... ...but how did I even \textit{get} here?'' The stack trace
tells you exactly how.
\bigskip
\index{C++}
\index{segmentation fault (``seg fault'')}
Oh, I should also mention that these stack trace outputs are a Java thing.
C++'s equivalent output for many of these kinds of error cases is simply this:
\begin{Verbatim}[fontsize=\small,samepage=true,frame=none]
Segmentation fault (core dumped)
\end{Verbatim}
I think you'll agree, that's not nearly as helpful. $\smiley$