1
00:00:03,064 --> 00:00:10,082
In this video, we're going to talk about
programmer defined exceptions. So, think

2
00:00:10,082 --> 00:00:14,084
about the following typical programming
scenario. You're deep into some fairly

3
00:00:14,084 --> 00:00:18,087
complex section of code and you come to a
place where you could experience an

4
00:00:18,087 --> 00:00:22,095
unexpected error. That there could be
something that could happen that would

5
00:00:22,095 --> 00:00:27,003
violate some important property of your
program. So, for example, maybe you're

6
00:00:27,003 --> 00:00:31,016
going to discover there's a place where
you could be out of memory or there's some

7
00:00:31,016 --> 00:00:35,049
data structure that doesn't satisfy some
variant. So, a list that's supposed to be

8
00:00:35,049 --> 00:00:39,026
sorted, that is not, or something like
that. And the question is, how do you

9
00:00:39,026 --> 00:00:43,408
handle these errors? How do you write code
that will handle the error gracefully and

10
00:00:43,408 --> 00:00:50,300
not make your program really, really ugly?
So, a popular solution to this problem in

11
00:00:50,300 --> 00:00:55,317
many languages including Java is add a new
kind of value to the language called an

12
00:00:55,317 --> 00:00:59,131
exception. And we'll have a couple of
control constructs for dealing with

13
00:00:59,131 --> 00:01:04,145
exceptions. Here is the two that are
probably the most popular. And these are,

14
00:01:04,145 --> 00:01:10,136
as they appear in Java. So, we can throw
exceptions and what this does is it causes

15
00:01:10,136 --> 00:01:15,108
an exception to be created at this point
wherever the throw occurs. And, that

16
00:01:15,108 --> 00:01:20,280
exception will simply propagate out of the
program. It will basically halt the

17
00:01:20,280 --> 00:01:25,902
execution of the program at that point and
any containing constructs will also throw

18
00:01:25,902 --> 00:01:31,226
the exception. So, the exception will, you
know, simply propagate up out of the code

19
00:01:31,226 --> 00:01:36,510
that, that's currently executing, until it
hits a try catch. So, how does this work?

20
00:01:36,510 --> 00:01:41,465
Well, we can try something. We can execute
some expression here and this will be some

21
00:01:41,465 --> 00:01:45,843
expression. And if this expression throws
an exception, if the throw occurs

22
00:01:45,843 --> 00:01:50,869
somewhere inside this expression, then we
will catch that exception and there can be

23
00:01:51,063 --> 00:01:56,624
a binding here to say what we are going to
call the exception value. So, this is like

24
00:01:56,624 --> 00:02:01,264
a let, we will grab the exception that
comes out of here. We'll call it X and

25
00:02:01,264 --> 00:02:06,154
then we can execute this piece of cleanup
code to how, to handle the exception in

26
00:02:06,154 --> 00:02:11,259
some way. And so, the basic idea behind
this thi design for handling exceptions,

27
00:02:11,259 --> 00:02:15,480
is that the place where you have the
exception, the place where you actually

28
00:02:15,480 --> 00:02:19,921
detect the exception, might be somewhere
deep inside the code and not a very good

29
00:02:19,921 --> 00:02:24,724
place for actually dealing with the
exception. So, what you want to do is get

30
00:02:24,724 --> 00:02:29,375
out of that part of the code, get back to
some higher level point where you can

31
00:02:29,375 --> 00:02:34,994
clean things up deal with the exception,
and then retry, perhaps, some larger block

32
00:02:34,994 --> 00:02:41,391
of code. Here's a little example of using
exceptions. So, here we have our main

33
00:02:41,391 --> 00:02:46,802
method. And what are we going to do? We're
going to have a try-block that just calls

34
00:02:46,802 --> 00:02:51,043
a function X. And if that raises a, an
exception, then we will catch the

35
00:02:51,043 --> 00:02:55,086
exception. In this case we don't do
anything with the exception, we just print

36
00:02:55,086 --> 00:02:59,603
out a message saying there was an error
and we terminate the program. So, we don't

37
00:02:59,603 --> 00:03:04,408
do anything very smart but we do catch the
exception and at least print out an error

38
00:03:04,408 --> 00:03:08,309
rather than just terminating. So, what
does x do? Well, X simply throws an

39
00:03:08,309 --> 00:03:13,604
exception. So this function X allocates an
exception object. This is just a value,

40
00:03:13,604 --> 00:03:18,175
it's just a class like everything else.
But it has a special property that it can

41
00:03:18,175 --> 00:03:22,767
be thrown. So, when we throw it, that's
when X terminates abnormally and we end up

42
00:03:22,767 --> 00:03:28,039
in the catch block of the try-catch
expression back in the main method. Now,

43
00:03:28,039 --> 00:03:32,846
in the last couple of slides, I gave you a
very informal description of how

44
00:03:32,846 --> 00:03:37,577
exceptions work and it, it might not have
been very clear and in fact it's hard I

45
00:03:37,577 --> 00:03:42,547
think to give a very clear description
without some kind of formal description of

46
00:03:42,547 --> 00:03:46,998
how exceptions are supposed to behave. But
fortunately, we've looked at operational

47
00:03:46,998 --> 00:03:51,622
semantics in this class and so now you're
familiar with those kinds of descriptions

48
00:03:51,622 --> 00:03:55,857
of language behavior and I can actually
describe you pretty succinctly how

49
00:03:55,857 --> 00:04:00,912
try-catch actually really works, alright.
So, we're going to give operational rules

50
00:04:00,912 --> 00:04:06,505
here for Try-Catch expressions. And I just
noticed, I just poin t out here that I had

51
00:04:06,505 --> 00:04:11,420
some kind of font problem and so I had to
write in the turnstiles by hand in this

52
00:04:11,420 --> 00:04:16,467
slide. So those handwritten characters are
all supposed to be the turnstile

53
00:04:16,467 --> 00:04:21,374
character. Now, to more important things
there's a distinction with exceptions.

54
00:04:21,374 --> 00:04:26,074
Okay, there are two kinds of states that
an exception object could be in. It could

55
00:04:26,074 --> 00:04:30,568
just be an ordinary value. So, when I say
new exception object in Java, so, when I

56
00:04:30,568 --> 00:04:35,091
say something new, something exception
class is just an ordinary value. At that

57
00:04:35,091 --> 00:04:39,066
point, it just behaves like any other
object. But then, there is a distinction

58
00:04:39,066 --> 00:04:43,536
when the object is thrown. So, when the
exception is actually thrown, it becomes a

59
00:04:43,536 --> 00:04:47,960
special kind of value and it gets treated
differently, alright. So, we're going to

60
00:04:47,960 --> 00:04:52,977
distinguish between an ordinary object V,
okay. And objects that have been thrown,

61
00:04:52,977 --> 00:04:58,660
okay, which are then active exceptions.
Alright? So, let's take a look at some of

62
00:04:58,660 --> 00:05:04,089
the operational rules for the exception
constructs. So, here's one rule for its

63
00:05:04,089 --> 00:05:09,619
try-catch-block. And what this rule says
is that if an expression evaluates to an

64
00:05:09,619 --> 00:05:14,266
ordinary value. If it doesn't throw an
exception, then the results of the

65
00:05:14,266 --> 00:05:19,402
try-catch-block is just that value. So,
the way the try-catch-block works is you

66
00:05:19,402 --> 00:05:24,472
evaluate the expression in the try-block.
If that terminates normally with a value,

67
00:05:24,472 --> 00:05:29,584
then the results of the whole expression
is just that value, alright? Now, the

68
00:05:29,584 --> 00:05:34,880
other possibility is that you'll evaluate
a try catch block and when you go to

69
00:05:34,880 --> 00:05:39,685
evaluate the expression of the try-block
E1, it will throw an exception. So, it

70
00:05:39,685 --> 00:05:44,408
could result in a thrown exception. And
so, in this case. Okay, that is that,

71
00:05:44,408 --> 00:05:49,498
excuse me, that is this case where E1
evaluates to one of these special values

72
00:05:49,498 --> 00:05:54,624
has been marked as a thrown exception.
What do we do in that case? Well, like

73
00:05:54,624 --> 00:05:59,579
unwrap the exception, we pull out value
that is in the thrown exception, alright.

74
00:05:59,579 --> 00:06:05,032
We bind it to some local name, alright,
that's named in the catch expression and

75
00:06:05,032 --> 00:06:10,044
then we evaluate the cleanup code. So,
with the ex ception value available, we

76
00:06:10,044 --> 00:06:15,345
evaluate E2 and whatever the result is of
E2, that is the result of the

77
00:06:15,345 --> 00:06:22,068
try-catch-block. How does throw work? It's
very simple. So, throw just takes an

78
00:06:22,068 --> 00:06:28,040
expression, it evaluates that expression
against some value V. And then, it marks

79
00:06:28,040 --> 00:06:33,209
that value as a thrown exception, as a
thrown exception so it wraps the value in

80
00:06:33,209 --> 00:06:37,910
this T thing and that indicate that this
exception now has been thrown. Now, the

81
00:06:37,910 --> 00:06:43,308
only other thing we need to talk about is
how the rest of the language, all the

82
00:06:43,308 --> 00:06:49,144
other constructs in the language deal with
these thrown exceptions. And that's very

83
00:06:49,144 --> 00:06:54,085
simple. We want those thrown exceptions to
simply propagate out of any other kind of

84
00:06:54,085 --> 00:07:00,006
expression. So, for example, we'll just do
one example here because the idea is the

85
00:07:00,006 --> 00:07:04,555
same for every other language construct.
Let's say, that we're evaluating E1 + E2,

86
00:07:04,555 --> 00:07:09,514
alright. So, the first thing we have to do
of course is to evaluate E1 and if that

87
00:07:09,514 --> 00:07:14,072
happens the thrown exception. So, if
something goes wrong in the evaluation of

88
00:07:14,072 --> 00:07:19,018
E1 and E1 evaluates to the thrown in
exception, well then we stop the

89
00:07:19,018 --> 00:07:24,311
evaluation of the plus right there. We
don't even evaluate E2, notice that E2 is

90
00:07:24,311 --> 00:07:28,610
not mentioned here above the line of one
of the things to be evaluated so that E1

91
00:07:28,610 --> 00:07:33,830
terminates normally with an exception,
then the results of the entire addition is

92
00:07:33,830 --> 00:07:38,817
that exception. And similarly, for all the
other constructs if, if there's, one of

93
00:07:38,817 --> 00:07:42,871
their sub-expressions results in an
exception. In fact, if the, if, as soon as

94
00:07:42,871 --> 00:07:47,301
one of their sub-expressions results in an
exception, they stop evaluating and

95
00:07:47,301 --> 00:07:51,353
propagate that exception up. The only
thing that will stop the exception from

96
00:07:51,353 --> 00:07:56,419
propagating all the way out to become the
result of the whole program is if it is

97
00:07:56,419 --> 00:08:02,551
caught in a try-catch-block. There are
many ways to implement exceptions and here

98
00:08:02,551 --> 00:08:06,606
is one simple way to do it. So, we
encounter a try expression, we will mark

99
00:08:06,606 --> 00:08:11,830
the current location in the stack. So, we
will mark the position in the stack where

100
00:08:11,830 --> 00:08:16,384
we encountered the try. So, for example,
here, say, is our s tack. Let's say that,

101
00:08:16,384 --> 00:08:20,925
you know, the stack is going this way. We
encounter a try expression here so we put

102
00:08:20,925 --> 00:08:25,170
some kind of marker in the stack to
indicate that there's a try that was

103
00:08:25,170 --> 00:08:29,349
encountered there. And then you go on, you
know, evaluating things inside of the try

104
00:08:29,349 --> 00:08:33,822
which might add more stuff to the stack.
Now, when we throw an exception, if down

105
00:08:33,822 --> 00:08:38,529
here, all of a sudden a throw occurs and
we're at this point in the execution,

106
00:08:38,529 --> 00:08:42,733
what's going to happen is we're going to
unwind the stacks. We're going to knock

107
00:08:42,733 --> 00:08:47,141
everything off the stack. We're going to
pop all of this stuff of the stack, so

108
00:08:47,141 --> 00:08:51,291
it's all gone, back to the first try. And
then we'll execute the corresponding

109
00:08:51,291 --> 00:08:56,063
catch. So, here we marked, you know, the
place and the code where there was a try

110
00:08:56,063 --> 00:09:00,207
and we can use that to find the expression
the piece of the code that has the

111
00:09:00,207 --> 00:09:05,004
corresponding catch-block and we'll unwind
the stack to that point and then begin

112
00:09:05,004 --> 00:09:09,156
evaluation of the catch. So at this
particular design it has the disadvantage

113
00:09:09,156 --> 00:09:14,997
that try actually cost something. So, even
if you don't throw an, even if you don't

114
00:09:14,997 --> 00:09:19,027
throw an exception, you still pay
something to execute a try-catch-block.

115
00:09:19,027 --> 00:09:24,020
You have to at least mark the stack and,
and remember to unmark it of course, when

116
00:09:24,020 --> 00:09:28,742
you pop things off the stack when you
leave the try-block. So, more complex

117
00:09:28,742 --> 00:09:33,662
techniques tries to reduce the cost of try
and throw and also trade off between them.

118
00:09:33,662 --> 00:09:38,406
And generally the thing you want to do is
because exceptions are probably relatively

119
00:09:38,406 --> 00:09:43,571
rare in, in most programs is to make the
cost of try as low as possible, possibly

120
00:09:43,571 --> 00:09:50,254
at the expense of making throws slightly
more expensive. Now, here's a little

121
00:09:50,442 --> 00:09:55,451
trivia question about Java. So, what
happens to an uncaught exception as thrown

122
00:09:55,451 --> 00:10:00,772
during object finalization? So, if you
don't know what object finalization is,

123
00:10:00,772 --> 00:10:06,197
well, when an object is collected, when an
object is garbage collected, it is

124
00:10:06,197 --> 00:10:11,955
possible to run a method on that object to
clean it up before the garbage collector

125
00:10:11,955 --> 00:10:16,241
actually deallocates it and this is called
the finali zation method. So, objects have

126
00:10:16,241 --> 00:10:20,628
finalization methods in, in Java and those
methods are essentially invoked by the

127
00:10:20,628 --> 00:10:25,039
garbage collector. Some garbage collector
discovers that some objects, this garbage

128
00:10:25,039 --> 00:10:29,396
is going to be clean it up, it will first
invoke the finalization method. And why

129
00:10:29,396 --> 00:10:33,605
would you wanted to do this unless, say,
we have an object and it might have, say,

130
00:10:33,605 --> 00:10:37,727
a file handle. It might have a pointer to
an open file or something like that. And

131
00:10:37,727 --> 00:10:41,915
now, when this object becomes unreachable,
it will be collected by the garbage

132
00:10:41,915 --> 00:10:45,902
collector. But if you don't close the
file, well, that's gonna cause problems.

133
00:10:45,902 --> 00:10:50,115
Having lots of open files that are hanging
around without the program using them it

134
00:10:50,115 --> 00:10:54,460
can cause problems later on, especially if
you run out of file handles. Usually,

135
00:10:54,460 --> 00:10:59,134
there's a fixed number of file handles
available from the operating system. So,

136
00:10:59,134 --> 00:11:03,126
the right thing to do is when this is
garbage collected is to first close the

137
00:11:03,126 --> 00:11:07,329
file and essentially get rid of this
pointer, okay, and then, deallocate the

138
00:11:07,329 --> 00:11:11,368
object, and that it was object
finalization is for. So, again, you can

139
00:11:11,368 --> 00:11:16,521
define the method in Java that will be run
by the garbage collector to kinda clean up

140
00:11:16,521 --> 00:11:21,509
any resources the object has before it's
actually deallocated. And now the question

141
00:11:21,509 --> 00:11:26,586
is, if that finalization method raises an
exception, who catches it? Because it was

142
00:11:26,586 --> 00:11:31,046
invoked by the garbage collector, it's
unpredictable when it's going to be

143
00:11:31,046 --> 00:11:35,504
invoked and it's not clear where that
exception should be handled. And the

144
00:11:35,504 --> 00:11:39,963
answer to the question is no one handles
that method or nothing handles that

145
00:11:39,963 --> 00:11:45,053
method. The exception is simply dropped.
And so, any exceptions that happen during

146
00:11:45,053 --> 00:11:51,282
object finalization that are not handled
within the finalization method itself are

147
00:11:51,282 --> 00:11:56,533
simply thrown away. One of the nice
innovations in Java is that exceptions are

148
00:11:56,533 --> 00:12:00,921
actually part of the method interface and
they are checked by the compiler. So, in,

149
00:12:00,921 --> 00:12:05,142
in the example that I gave at the
beginning of this lecture, we had a method

150
00:12:05,142 --> 00:12:09,725
X that could raise an exception, my
exception, and notice that the declaration

151
00:12:09,725 --> 00:12:13,874
of X actually declares that X can throw
that exception. So, it's part of the

152
00:12:13,874 --> 00:12:18,052
interfaced X, part of the checked
interfaced X that it can raise a

153
00:12:18,052 --> 00:12:22,949
particular exception. And why would you
want to check this at compile time? Well,

154
00:12:22,949 --> 00:12:27,322
the observation that was made, actually in
the original Java project was that there

155
00:12:27,322 --> 00:12:32,029
were many exceptions that could be raised
by Java programs and people easily lost

156
00:12:32,029 --> 00:12:36,700
rack of what possible exceptions could be
raised, they didn't know what exceptions

157
00:12:36,700 --> 00:12:41,425
they had to handle. And in fact, when they
added this to the language the compiler

158
00:12:41,425 --> 00:12:46,027
would actually enforce now that a method
declared every exception it could raise.

159
00:12:46,027 --> 00:12:50,658
They discovered lots of places in the, in
the compiler where there were exceptions

160
00:12:50,658 --> 00:12:56,796
being raised but not properly handled. So,
this led to better air handling in, in the

161
00:12:56,796 --> 00:13:01,903
Java compiler itself, and, and I think
people generally agree that this is een a

162
00:13:02,025 --> 00:13:07,649
good idea because it helps programmers to
write more robust code because they can

163
00:13:07,649 --> 00:13:13,071
see exactly which exceptions they have to
handle. Now, there are some exceptions to

164
00:13:13,071 --> 00:13:17,809
this rule. In particular there's, there's
some kinds of runtime errors that don't

165
00:13:17,809 --> 00:13:22,631
have to be part of the method signature
simply because it's very hard to check

166
00:13:22,631 --> 00:13:26,903
statistically that the method would never
raise them. So, things like dereferencing

167
00:13:26,903 --> 00:13:31,452
null or interprocedural overflow don't
have to be handled and declared in the

168
00:13:31,452 --> 00:13:37,472
interface. But for the most part any
exception that a, a method can raise has

169
00:13:37,472 --> 00:13:43,002
to be declared as part of its interface in
Java. And then there are other

170
00:13:43,002 --> 00:13:48,040
mundane-type rules about the particular
design for exceptions in Java. So, for

171
00:13:48,040 --> 00:13:53,254
example, throw has to be applied to an
object of type exception, it can't be

172
00:13:53,254 --> 00:13:59,227
applied to an object of an arbitrary type.
But overall exceptions are rather nicely

173
00:13:59,227 --> 00:14:04,417
done in Java and that this particular idea
of, of declaring the types of exceptions

174
00:14:04,417 --> 00:14:07,086
that a method can raise was a new idea in
Java.
