Java Programming Thought Notes - Handling Errors Through Exceptions 2

Java standard exception

The basic concept of exceptions is to use a name to represent the problem that occurs, and the name of the exception should be easy to understand. Not all exceptions are defined in the java.lang package. Some exceptions are used to support packages such as util, net, and io. These exceptions can be identified by their full names or from their parent classes. For example, all output/input exceptions are inherited from java.io.IOException.

RuntimeException

NullPointerException: Java automatically throws a NullpointerException if the call is made on a null reference.
There are many types of runtime exceptions that are automatically thrown by the Java Virtual Machine, so there is no need to list them in the exception specification. These exceptions are inherited from the RuntimeException class.

public class NeverCaught {
    static void f(){
        throw new RuntimeException("From f()");
    }
    static void g(){
        f();
    }

    public static void main(String[] args) {
        g();
    }
}/*
Exception in thread "main" java.lang.RuntimeException: From f()
    at thinking2.NeverCaught.f(NeverCaught.java:5)
    at thinking2.NeverCaught.g(NeverCaught.java:8)
    at thinking2.NeverCaught.main(NeverCaught.java:12)
*/

RuntimeException is a special case. For this type of exception, the compiler does not need an exception description, and its output is reported to System.err. All RuntimeExceptions are not caught and go directly to main(), then the exception's printStackTrace() will be called before the program exits. )method.
Only exceptions of RuntimeException and its subclasses can be ignored in the code, and the handling of other types of exceptions are enforced by the compiler. The reason is that RuntimeException represents a compilation error:
1. Unforeseen errors
2. As a programmer, errors should be checked in the code.

Use finally for processing

finally clause: Exceptions in try blocks get executed regardless of whether they are thrown or not. This usually applies outside of memory reclamation.

public class ThreeException extends Exception {
}
public class FinallyWorks {
    static int count = 0;

    public static void main(String[] args) {
        while (true){
            try {
                if(count++ == 0){
                    throw new ThreeException();
                }
                System.out.println("No exception");
            }catch (ThreeException e){
                System.out.println("ThreeException");
            }finally {
                System.out.println("In finally clause");
                if(count == 2){
                    break;
                }
            }
        }
    }
}/*
ThreeException
In finally clause
No exception
In finally clause
*/

The finally clause is always executed whether or not an exception is thrown.
If try is placed in a loop, it establishes a condition that must be met before the program continues to execute, in order to achieve the setting when breaking java's exception does not allow us to return to the point where the exception was thrown. You can also add a static counter so that the loop can try a certain number of times before giving up.

what is finally used for

The finally clause is used when resources other than memory are to be restored to their original state (including open files or network links, etc.).

public class Switch {
    private boolean state = false;
    public boolean read(){
        return state;
    }
    public void on(){
        state = true;
        System.out.println(this);
    }
    public void off(){
        state = false;
        System.out.println(this);
    }
    @Override
    public String toString(){
        return state ? "on" : "off";
    }
}
public class OnOffException1 extends Exception {
}
public class OnOffException2 extends Exception {
}
public class OnOffSwitch {
    private static Switch sw = new Switch();
    public static void f() throws OnOffException1,OnOffException2{}

    public static void main(String[] args) {
        try{
            sw.on();
            f();
            sw.off();
        } catch (OnOffException1 e) {
            System.out.println("OnOffException1");
            sw.off();
        } catch (OnOffException2 e) {
            System.out.println("OnOffException2");
            sw.off();
        }
    }
}/*
on
off
*/

The purpose of the program is that the switch must be off when main ends, so add a call to off in each exception handler

public class WithFinally {
    static Switch sw = new Switch();

    public static void main(String[] args) {
        try{
            sw.on();
            OnOffSwitch.f();
        } catch (OnOffException2 onOffException2) {
            System.out.println("OnOffException2");
        } catch (OnOffException1 onOffException1) {
            System.out.println("OnOffException1");
        } finally {
            sw.off();
        }
    }
}/*
on
off
*/

Here sw.off() is moved to one place and guaranteed to be executed in all cases.
Even if the exception is not caught by the current exception handler, the exception handling mechanism executes the finally clause before jumping to a higher level exception handler:

public class FourException extends Exception {
}
public class AlwaysFinally {
    public static void main(String[] args) {
        System.out.println("Entering first try block");
        try{
            System.out.println("Entering second try block");
            try{
                throw new FourException();
            } finally {
                System.out.println("finally in 2nd try block");
            }
        } catch (FourException e) {
            System.out.println("Caught FourException in 1st try block");
        } finally {
            System.out.println("finally in 1st try block");
        }
    }
}/*
Entering first try block
Entering second try block
finally in 2nd try block
Caught FourException in 1st try block
finally in 1st try block
*/

The finally clause is also executed when break and continue statements are involved. Note that if you use the finally clause with the labelled break and continue, you don't need the goto statement in java.

use finally in return

Because the finally clause is always executed, you can return from multiple points within a method, and you can be sure that important cleanup will still be performed:

public class MultipleReturns {
    public static void f(int i){
        System.out.println("Initialization that requires cleanup");
        try{
            System.out.println("Point 1");
            if(i == 1) {return;}
            System.out.println("Point 2");
            if(i == 2) {return;}
            System.out.println("Point 3");
            if(i == 3) {return;}
            System.out.println("End");
        } finally {
            System.out.println("Perforing cleanup");
        }
    }

    public static void main(String[] args) {
        for(int i = 1; i <= 4; i++){
            f(i);
        }
    }
}/*
Initialization that requires cleanup
Point 1
Perforing cleanup
Initialization that requires cleanup
Point 1
Point 2
Perforing cleanup
Initialization that requires cleanup
Point 1
Point 2
Point 3
Perforing cleanup
Initialization that requires cleanup
Point 1
Point 2
Point 3
End
Perforing cleanup
*/

Disadvantage: Abnormal loss

Java's exception implementation is also flawed. Exceptions, as a sign of program error, should never be ignored, but they can still be easily ignored. This happens when the finally clause is used in some special way:

public class VeryImportantException extends Exception {
    @Override
    public String toString() {
        return "A very important exception";
    }
}
public class HoHumException extends Exception {
    @Override
    public String toString() {
        return "A trivial exception";
    }
}
public class LostMessage {
    void f() throws VeryImportantException{
        throw new VeryImportantException();
    }
    void dispose() throws HoHumException{
        throw new HoHumException();
    }

    public static void main(String[] args) {
        try{
            LostMessage lm = new LostMessage();
            try {
                lm.f();
            } finally {
                lm.dispose();
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}/*
A trivial exception
*/

As you can see from the output, the VeryImportException is gone, it's replaced by a HoHumException in the finally clause. This is quite a serious flaw.
A simpler way to lose the exception is to return from the finally clause:

public class ExceptionSilencer {
    public static void main(String[] args) {
        try{
            throw new RuntimeException();
        } finally {
            return;
        }
    }
}

Even if the program throws an exception, there will be no output.

Unusual restrictions

When overriding a method, only those exceptions listed in the base class method's exception specification can be thrown. This restriction is useful because it means that the code used by the base class works when applied to objects of its derived classes, and exceptions are no exception.

public class BaseballException extends Exception {
}
public class Foul extends BaseballException {
}
public class Strike extends BaseballException {
}
public abstract class Inning {
    public Inning() throws BaseballException{}
    public void event() throws BaseballException{

    }
    public abstract void atBat() throws Strike, Foul;
    public void walk(){}
}
public class StormException extends Exception {
}
public class RainedOut extends Exception {
}
public class PopFoul extends Foul {
}
public interface Storm {
    void event() throws RainedOut;
    void rainHard() throws RainedOut;
}

public class StormyInning extends Inning implements Storm {
    public StormyInning() throws RainedOut, BaseballException{}
    public StormyInning(String s) throws Foul, BaseballException{}
    //! void walk() throws PopFoul{}
    //public void event() throws RainedOut {}
    @Override
    public void rainHard() throws RainedOut {}

    @Override
    public void event(){}

    @Override
    public void atBat() throws PopFoul {}

    public static void main(String[] args) {
        try {
            StormyInning si = new StormyInning();
            si.atBat();
        } catch (PopFoul e) {
            System.out.println("Pop foul");
        } catch (RainedOut e) {
            System.out.println("Rained out");
        } catch (BaseballException e) {
            System.out.println("Generic baseball exception");
        }

        try {
            Inning i = new StormyInning();
            i.atBat();
        } catch (Strike e) {
            System.out.println("Strike");
        } catch (Foul e) {
            System.out.println("Foul");
        } catch (RainedOut e) {
            System.out.println("Rained out");
        } catch (BaseballException e) {
            System.out.println("Generic baseball exception");
        }
    }
}

The interface Storm is worth noting because it contains a method event() defined in Inning and a method rainHard() not defined in Inning. Both methods throw the new exception RainedOut. If the StormmyInning class extends the Inning class and implements the Storm interface, then the event method in Storm cannot change the exception interface of the event method in Inning. Otherwise, it would be impossible to tell whether the correct exception was caught when using the base class. Of course, if the method defined in the interface is not from the base class, such as rainHard, then it doesn't matter what kind of exception the method throws.
Exception restrictions have no effect on constructors. However, because the base class constructor must be called in one way or another, the exception specification of the derived class constructor must include the base class constructor's exception specification.
Derived class constructors cannot catch exceptions thrown by base class constructors.
Inning.walk does not declare exceptions.
The overridden event method shows that the derived class method can throw no exception, even if it is an exception defined by the base class. Again this is because, if the base class method throws an exception, doing so will not break the existing program.

Constructor

finally executes the cleanup code every time. If the constructor fails halfway through its execution, perhaps some parts of the object have not been successfully created that were to be cleaned up in the finally clause.

public class InputFile {
    private BufferedReader in;
    public InputFile(String fname) throws Exception {
        try {
            in = new BufferedReader(new FileReader(fname));
        } catch (FileNotFoundException e) {
            System.out.println("Could not open " + fname);
            throw e;
        } catch (Exception e) {
            try {
                in.close();
            } catch (IOException e2) {
                System.out.println("in.close() unsuccessful");
            }
            throw e;
        } finally {

        }
    }

    public String getLine() {
        String s;
        try {
            s = in.readLine();
        } catch (IOException e) {
            throw new RuntimeException("readLine() failed");
        }
        return s;
    }

    public void dispose() {
        try {
            in.close();
            System.out.println("dispose() successful");
        } catch (IOException e2) {
            throw new RuntimeException("in.close() failed");
        }
    }
}

If the FileReader constructor fails, a FileNotFoundException will be thrown. For this exception, there is no need to close the file because the file is not open yet. And any other catch clauses that catch the exception must close the file because the file was already open by the time they caught the exception. close also throws an exception, so even though it's already in another catch clause block, another layer of try-catch is needed.
Since finally is executed every time the constructor completes, it really shouldn't be the place to call close to close the file, we want the file to remain open for the lifetime of the InputFile object.
The getLine method calls readLine which can throw an exception, but the exception is already handled within the method, so getLine will not throw an exception. Here, the getLine method converts the exception to RuntimeException, indicating a programming error.
When the user does not need the InputFile object, he must call the dispose method, which will release the system resources occupied by the BufferedReader and/or FileReader objects.
For classes that may throw exceptions during construction and require cleanup, the safest way to use them is to use nested try clauses:

public class Cleanup {
    public static void main(String[] args) {
        try {
            InputFile in = new InputFile("E:\\IDEAFile\\untitled\\src\\thinking2\\Cleanup.java");
            try {
                String s;
                int i = 1;
                while ((s = in.getLine()) != null) {

                }
            } catch (Exception e) {
                System.out.println("Caught Exception in main");
            } finally {
                in.dispose();
            }
        } catch (Exception e) {
            System.out.println("InputFile construction failed");
        }
    }
}/*
dispose() successful
*/

The construction of the InputFile object is valid in its own try block, and if the construction fails, the outer catch clause is entered and dispose is not called. However, if the construction is successful, we definitely want to make sure the object can be cleaned up, so create a new try block right after construction. The finally that performs the cleanup is associated with the inner try block. In this way, the finally clause is not executed when the construction fails, but is always executed when the construction succeeds.
Immediately after creating the object to clean up, enter a try-catch block:

public class NeedsCleanup {
    private static long counter = 1;
    private final long id = counter++;
    public void dispose() {
        System.out.println("NeedsCleanup " + id + " disposed");
    }
}
public class ConstructionException extends Exception {
}
public class NeedsCleanup2 extends NeedsCleanup {
    public NeedsCleanup2() throws ConstructionException {}
}
public class CleanupIdiom {
    public static void main(String[] args) {
        // Section 1
        NeedsCleanup nc1 = new NeedsCleanup();
        try {
            // ...
        } finally {
            nc1.dispose();
        }

        // Section 2
        NeedsCleanup nc2 = new NeedsCleanup();
        NeedsCleanup nc3 = new NeedsCleanup();
        try {
            // ...
        } finally {
            nc2.dispose();
            nc3.dispose();
        }

        // Section 3
        try {
            NeedsCleanup2 nc4 = new NeedsCleanup2();
            try {
                NeedsCleanup2 nc5 = new NeedsCleanup2();
                try {
                    // ...
                } finally {
                    nc5.dispose();
                }
            } catch (ConstructionException e) {
                System.out.println(e);
            } finally {
                nc4.dispose();
            }
        } catch (ConstructionException e) {
            System.out.println(e);
        }
    }
}/*
NeedsCleanup 1 disposed
NeedsCleanup 2 disposed
NeedsCleanup 3 disposed
NeedsCleanup 5 disposed
NeedsCleanup 4 disposed
*/

In Section 2, you can see that objects with constructors that cannot fail can be grouped together for construction and cleanup.
Section 3 shows how to deal with objects that have constructors that can fail and need to be cleaned up. Each construct must be enclosed in its own try-finally block, and each object construct must be followed by a try-finally block to ensure cleanup.

abnormal match

When an exception is thrown, the exception handling system will find the nearest handler in the order in which the code was written. Once it finds a matching handler, it considers the exception handled and doesn't look any further.
The lookup does not require that the thrown exception exactly matches the exception declared by the handler. Objects of derived classes can also match handlers of their base classes:

public class Annoyance extends Exception {
}
public class Sneeze extends Annoyance {
}
public class Human {
    public static void main(String[] args) {
        try {
            throw new Sneeze();
        } catch (Sneeze sneeze) {
            System.out.println("Caught Sneeze");
        } catch (Annoyance e) {
            System.out.println("Caught Annoyance");
        }

        try {
            throw new Sneeze();
        } catch (Annoyance e) {
            System.out.println("Caught Annoyance");
        }
    }
}/*
Caught Sneeze
Caught Annoyance
*/

If you put the catch clause that captures the base class at the top, you can block all the exceptions of the derived class first:

try {
            throw new Sneeze();
        } catch (Annoyance e) {
            System.out.println("Caught Annoyance");
        } catch (Sneeze sneeze) {
            System.out.println("Caught Sneeze");
        }

The compiler will then see that Sneeze's catch clause will never be executed, so he will report the error to you.

other options

Separate error-handling code from where the error occurred. This allows you to focus on what you want to accomplish in one piece of code, and how to handle errors in another piece of code.
"Checked exceptions" (those that are forced to be checked at compile time) force a catch clause to be added when the error might not be ready to be handled, which leads to the problem of "harmful if swallowed": Programmers often inadvertently "eat" exceptions.

Pass the exception to the console

The easiest way to protect exception messages without writing much code, is to pass them from mian() to the console:

public class MainException {
    public static void main(String[] args) throws Exception {
        FileInputStream file = new FileInputStream("MainException.java");
        file.close();
    }
}

By passing the Exception to the console, there is no need to write a try-catch clause in mian.

Convert "checked exception" to "unchecked exception"

When calling other methods in a common method, consider that you don't know how to handle this exception, but you don't want to swallow it, or print some useless messages, you can directly wrap the checked exception into a RuntimeException:

public class WrapCheckedException {
    void throwRuntimeException(int type) {
        try {
            switch (type) {
                case 0 : throw new FileNotFoundException();
                case 1 : throw new IOException();
                case 2 : throw new RuntimeException("where am I?");
                default : return;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
public class SomeOtherException extends Exception {
}

public class TurnOffChecking {
    public static void main(String[] args) {
        WrapCheckedException wce = new WrapCheckedException();
        wce.throwRuntimeException(3);
        for (int i = 0; i < 4; i++) {
            try {
                if(i < 3){
                    wce.throwRuntimeException(i);
                }else {
                    throw new SomeOtherException();
                }
            } catch (SomeOtherException e) {
                System.out.println("SomeOtherException: " + e);
            } catch (RuntimeException re) {
                try {
                    throw re.getCause();
                } catch (FileNotFoundException e) {
                    System.out.println("FileNotFoundException: " + e);
                } catch (IOException e) {
                    System.out.println("IOException: " + e);
                } catch (Throwable e) {
                    System.out.println("Throwable: " + e);
                }
            }
        }
    }
}/*
FileNotFoundException: java.io.FileNotFoundException
IOException: java.io.IOException
Throwable: java.lang.RuntimeException: where am I?
SomeOtherException: thinking2.SomeOtherException
*/

The code for WrapCheckedException.throwRuntimeException() can generate different types of exceptions. These exceptions are caught and wrapped into RuntimeException objects, so they become the cause of these runtime exceptions.
In TurnOffChecking, throwRuntimeException can be called without a try block because it does not throw the checked exception. But when you are ready to catch the exception, you can still use the try block to catch any exception, such as SomeOtherException, RuntimeException is caught at the end. Then throw the result of getCause (that is, the original exception that was wrapped), so that the original exception is extracted, and then they can be handled with their own catch clause.

Abnormal usage guide

Exceptions should be used in the following situations:
1. Handle the problem at the appropriate level (catch the exception only if you know how to handle it)
2. Fix the problem and re-invoke the method that produced the exception
3. Do a little tinkering and then bypass the exception from happening 4.
Calculate with other data to replace the value expected to be returned by the
method
Do as much as possible in the runtime environment, and then throw different exceptions to higher layers
7. Terminate the program
8. Simplify (if your exception pattern makes the problem too complicated, it is very painful to use)
9. Make library classes and programs safer

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324726996&siteId=291194637