Chapter 5 is about exceptions. These puzzle occur in these usage exceptions. In this article, we assume that you have some knowledge about Java exception handling mechanisms, of course, you can also get some discussions about the exception mechanism here. Exception is for locatingProgramError and EnhancementCodeJava exception mechanism is also based on C ++. Because of the high usage rate, we often discuss some usage experiences. We agree that exceptions and program control structures should not be confused. This can easily cause problems. Several puzzle mentioned this time are very difficult and involve some JVM processing items. At present, I cannot help it.
Puzzle 36 is indecisive
Public class indecisive {public static void main (string [] ARGs) {system. out. println (demo-();} static Boolean demo-() {try {return true;} finally {return false ;}}}
Is the final print true or false? First, it should be clear that the finally statement block will be executed after the execution of the try statement block ends (why do we emphasize the end?), whether the try is normal or unexpected. An unexpected conclusion means that a statement such as continue, break, or return may be called in try. But try .. in the finally structure, the final state is consistent with that of the finally statement block. In this example, if try ends unexpectedly after return, the result is discarded and the finally statement is executed, so the last print is false.
We are inspired not to use the structure control in the try statement, or use try catch finally as the statement structure control.
Puzzle 37 incredible
Import Java. io. ioexception; public class arcane1 {public static void main (string [] ARGs) {try {system. out. println ("Hello World");} catch (ioexception e) {system. out. println ("I 've never seen println fail! ");}}}
What will the first one print?
If you are curious, you can copy the code and find an error. The cause is that ioexception is a checked check exception, but it is impossible to throw this exception in the try statement, so the compiler will report an error. The solution is to remove try catch or write a method to declare throw ioexception and call it in the try statement block. This is an evil checked type exception. For more information about why it is sometimes difficult to use, see here.
Public class arcane2 {public static void main (string [] ARGs) {try {// if you have nothing nice to say, say nothing} catch (exception e) {system. out. println ("This can't happen ");}}}
With the above experience, this should also report an error. but in fact, no error is reported. When exceptions of the exception, throwable, or unchecked type are caught, the try statement block does not need to throw an exception. Obviously, this code will not print anything.
Interface type1 {void F () throws clonenotsupportedexception;} interface type2 {void F () throws interruptedexception;} interface type3 extends type1, type2 {} public class arcane3 implements type3 {public void F () {system. out. println ("Hello World");} public static void main (string [] ARGs) {type3 T3 = new arcane3 (); t3.f ();}}
Having read thinking in Java's explanation of exceptions, you must be impressed with the limitations on Java exception inheritance. it is a subclass or implemented interface class. It is not allowed to throw exceptions not declared in the base class or interface. This is to make the code of the base class continue to be used.
In this example, we may think that f needs to declare an exception or handle it, but actually it does not, the original reference to Java puzzlers is that "the set of checked type exceptions that a method can throw is the intersection of checked type exceptions to be thrown by all types declarations it applies ", obviously, the dataset in this example is empty, so no other exception can be thrown.
Puzzle 38 unwelcomed guests
Public Class Unwelcomeguest { Public Static Final Long Guest_user_id =-1 ; Private Static Final Long User_id; Static { Try {User_id =Getuseridfromenvironment ();} Catch (Idunavailableexception e) {user_id = Guest_user_id; system. Out. println ( "Logging in as guest" );}} Private Static Long Getuseridfromenvironment () Throws Idunavailableexception { Throw New Idunavailableexception (); // Simulate an error } Public Static Void Main (string [] ARGs) {system. Out. println ( "User ID:" + User_id );}} Class Idunavailableexception Extends Exception {}
This is a program used to determine the user ID type. user_id is an uninitialized final type, but there are two assignments in the program. Although it seems logical, the Java compiler adopts a Conservative policy for final assignment, because it is difficult to check whether final values are assigned multiple times, so the restrictions are imposed at the compilation stage. This ensures that the program is secure. Bloch recommends a method to reconstruct the code: Add a method to return user_id. getuseridorguest () is used to handle exceptions. In the end, only one value is returned to be assigned to user_id. this is a good habit, and the code has better readability. One inspiration is to use a method to replace the assignment processing logic, making the work more focused.
Puzzle 39 hello, goodbye
Public class hellogoodbye { Public static void main (string [] ARGs) { try {system. out. println ( "Hello World" ); system. exit ( 0 ) ;} finally {system. out. println ( "Goodbye World" ) ;}}
You may think of puzzle 36. That's right, so you may think that the printed results are Hello world and goddbye world, but unfortunately the latter cannot be printed. We note that finally ensures that the execution of the try statement ends, whether it is normal or unexpected. However, in this example, system. Exit (0) forces the VM to close all threads and finally cannot continue execution. Here, Bloch details the operations executed by system. Exit. First, it executes the function of disabling the hook. This function is to release resources outside the VM. We can use the addshutdownhook method to perform these operations.
Puzzle 40 reluctant Constructor
Public class reluctant {private reluctant internalinstance = new reluctant (); Public reluctant () throws exception {Throw new exception ("I'm not coming out ");} public static void main (string [] ARGs) {try {reluctant B = new reluctant (); system. out. println ("surprise! ");} Catch (exception ex) {system. Out. println (" I told you so ");}}}
Stackoverflowerror occurs when we run the above Code. Of course, this is an error and catch cannot be captured. Of course, this is not the key to a program. One of the keys is that the program will fall into an infinite recursion. Because of the Java initialization order, the initialization of member variables should be performed before the constructor (for Java initialization order, see here ). Therefore, this is an endless loop, which of course leads to stackoverflowerror. The second key is that the constructor must declare all the checked type exceptions thrown during initialization.
In this example, we are inspired to note the Java initialization sequence. If an exception occurs during the initialization of the member variables, it will be thrown to the constructor.
Puzzle 41 domain and stream
Import Java. Io .* ; Public Class Copy { Static Void Copy (string SRC, string DEST) Throws Ioexception {inputstream in = Null ; Outputstream out = Null ; Try {In = New Fileinputstream (SRC); Out = New Fileoutputstream (DEST ); Byte [] Buf = New Byte [1024 ]; Int N; While (N = in. Read (BUF)> 0 ) Out. Write (BUF, 0 , N );} Finally { If (In! = Null ) In. Close (); If (Out! = Null ) Out. Close ();}} Public Static Void Main (string [] ARGs) Throws Ioexception { If (ARGs. length! = 2 ) System. Out. println ( "Usage: Java copy <source> <DEST>" ); Else Copy (ARGs [ 0], argS [1 ]) ;}}
In this example, if we copy the data of the input stream to the output stream, will it succeed?
The answer is uncertain. We think the program may be complete, but please note in. Close in finally. This is also a method that can throw an exception. So this is the problem. The simplest solution is to add try. Catch, but this style is not good. You can use the closeable interface to reconstruct this part of code. (Fileinputstream and fileoutputstream both implement this excuse), so you can write a closestreamwhenexception () to process it. This is more elegant.
Puzzle 42 throwing exceptions for Loops
Public Class Loop { Public Static Void Main (string [] ARGs ){ Int [] [] Tests = {6, 5, 4, 3, 2, 1}, {1, 2 },{ 1, 2, 3}, {1, 2, 3, 4}, {1}}; Int N = 0 ; Try { Int I = 0 ; While ( True ){ If (Thirdelementisthree (tests [I ++ ]) N ++ ;}} Catch (Arrayindexoutofboundsexception e ){ // No more tests to process } System. Out. println (n );} Private Static Boolean Thirdelementisthree ( Int [] ){ Return A. length> = 3 & A [2] = 3 ;}}
What will this print? We think it may be 2, but the result is 0.
First, this is a very bad code, because it tries to end the loop by throwing an exception. There are two disadvantages: 1 is poor code readability, 2 is very inefficient. So why is it 0? Check the thirdelementisthree method because it has thrown an exception when processing {1, 2. On the contrary, experienced programmers will think that they will not throw an exception because. the length is already smaller than 3, so the array in the other expression will not be out of bounds. However, note that the value & instead of & is used here. We know that when the value of & operator is false on the left, then he stops the computation. But & different, although this logic and operator are overloaded when processing boolean values, they still need to calculate two operands, even if a <3, he will also calculate a [2], which directly leads to cross-border calculation.
Puzzle 43 is exceptionally dangerous
Public class thrower { Public static void main (string [] ARGs) {sneakythrow ( New exception (" This Is A checked exception " ));} /* * provide a body for this method to make it throw the specified exception. * You must not use any deprecated methods. */ Public static void sneakythrow (throwable t) {}
This requires you to implement a method that can throw exceptions by bypassing the compiler, that is, you can throw exceptions without being checked by the compiler. This is really a pain point, taking advantage of a reflection defect. Store a throwable through static variables, because the newinstance method can pass the exception thrown by the non-parameter constructor. I don't want to introduce this. It doesn't make much sense. It's totally a Java design issue.
Puzzle 44 Delete class
Class missing {missing (){}}
Public class strange1 {public static void main (string [] ARGs) {try {missing M = new missing ();} catch (Java. lang. noclassdeffounderror ex) {system. out. println ("got it! ");}}}
Public class strange2 {public static void main (string [] ARGs) {missing m; try {M = new missing ();} catch (Java. lang. noclassdeffounderror ex) {system. out. println ("got it! ");}}}
The author mentioned this example at the beginning of this chapter, which is really difficult. In this example, we normally compile the above three pieces of code, which is certainly no problem. But after compilation, we will delete missing. class. What will happen during the operation?
We think it is possible that strange1 will capture noclassdeffounderror and print got it, while strange2 will directly throw noclassdeffounderror. However, the results are the opposite. How can this be explained? The following is the bytecode generated by JVM: strange1.main
0: New
3: DUP
4: invokespecial #3; // method missing. "<init>" :() V
7: astore_1
8: goto 20
11: astore_1
12: getstatic #5; // field system. Out: ljava/IO/printstream;
15: LDC #6; // string "got it! "
17: invokevirtual #7; // method printstream. println: (string); V
20: Return
Exception table:
From to target type
0 8 11 class Java/lang/noclassdeffounderror
The only difference between strange. Main bytecode and above is 11: astore_2.
The purpose of this command is to save the caught exception to the command of the capture parameter ex. strange1 stores it in VM variable 1, while strange2 stores it in VM variable 2. When the JVM loads main, it needs to execute link operations similar to C. Both main have a connection point, which aggregates the control flow. The above main connection point is command 20. if try ends normally, the command 8, Goto to 20 will be executed, and the catch statement block will end from the command 17 to the command 20.
The problem lies here. Now there are two paths reaching the link point. Because each of the two paths has an astore (command 7 and command 11), strange1.main is in Path 1 (try to return, M is stored with VM variable 1, and an exception is stored with VM variable 1 In Path 2 (catch to return). Therefore, variable types must be merged. Therefore, it is necessary to check the super classes of missing and noclassdeffounderror. Because the missing. class has been deleted, a noclassdeffounderror exception is thrown. Because it still occurs before the main execution, it cannot be captured.
This is indeed a little exaggerated.
Puzzle 45 exhausting experiments
Public class Workout { Public static void main (string [] ARGs) {workhard (); system. out. println ( "it's nap time. ") ;} private static void workhard () { try {workhard () ;} finally {workhard () ;}}
This example is an almost infinite loop. We find that workhard will be called continuously after try until the java stack is full. When an exception is thrown, finally will be executed, continue to call itself. How can we analyze this problem? First of all, let's assume that the java stack is deeply rooted in 2 (of course not like this), which can be explained as follows:
In fact, every try finally is like two branches of a binary tree. For a child node, the left child is the try child, and the right child is the finally node. Every leaf will generate an stackoverflow exception operation, assuming that the java stack is 1024 deep, according to the nature of the binary tree, the total number of calls is 2 ^-1. In this way, the program is similar to the infinite loop.
This chapter describes the puzzles about exceptions. Some of them are inspired by the style of code writing, and some are difficult problems such as puzzle 44, I think it is necessary to take a look at JVM-related books next, which helps to analyze some very difficult problems.