Appearance
Exceptions in Java
Various errors always occur during the running of computer programs.
There are some errors caused by users. For example, the user is expected to enter an age of type int
, but the user's input is abc
:
java
String s = "abc";
int n = Integer.parseInt(s); // NumberFormatException!
The program wants to read and write the contents of a file, but the user has deleted it:
java
String t = readFile("C:\\abc.txt"); // FileNotFoundException!
There are also some errors that occur randomly and can never be avoided. for example:
- The network was suddenly disconnected and the remote server could not be connected;
- Memory is exhausted and the program crashes;
- The user clicks "Print", but there is no printer;
- …
Therefore, a robust program must handle a wide variety of errors.
The so-called error means that when the program calls a certain function, if it fails, it means an error.
How does the caller know that the call failed? There are two methods:
Method 1: Agree to return an error code.
For example, when processing a file, if 0
is returned, it indicates success, and other integers are returned, indicating the agreed error code:
java
int code = processFile("C:\\test.txt");
if (code == 0) {
// ok:
} else {
// error:
switch (code) {
case 1:
// file not found:
case 2:
// no read permission:
default:
// unknown error:
}
}
Because the error code of type int
is used, it is very troublesome to deal with it. This approach is common in low-level C functions.
Method 2: Provide an exception handling mechanism at the language level.
Java has a built-in exception handling mechanism, and exceptions are always used to indicate errors.
Exception is a class
, so it has type information itself. Exceptions can be thrown anywhere, but they only need to be caught at the upper level, so that they are separated from the method call:
java
try {
String s = processFile(“C:\\test.txt”);
// ok:
} catch (FileNotFoundException e) {
// file not found:
} catch (SecurityException e) {
// no read permission:
} catch (IOException e) {
// io error:
} catch (Exception e) {
// other error:
}
Because Java's exception is class
, its inheritance relationship is as follows:
┌───────────┐
│ Object │
└───────────┘
▲
│
┌───────────┐
│ Throwable │
└───────────┘
▲
┌─────────┴─────────┐
│ │
┌───────────┐ ┌───────────┐
│ Error │ │ Exception │
└───────────┘ └───────────┘
▲ ▲
┌───────┘ ┌────┴──────────┐
│ │ │
┌─────────────────┐ ┌─────────────────┐┌───────────┐
│OutOfMemoryError │... │RuntimeException ││IOException│...
└─────────────────┘ └─────────────────┘└───────────┘
▲
┌───────────┴─────────────┐
│ │
┌─────────────────────┐ ┌─────────────────────────┐
│NullPointerException │ │IllegalArgumentException │...
└─────────────────────┘ └─────────────────────────┘
It can be seen from the inheritance relationship: Throwable
is the root of the exception system, and it inherits from Object
. Throwable
has two systems: Error
and Exception
. Error
represents a serious error, and the program generally cannot do anything about it, for example:
OutOfMemoryError
: memory exhaustedNoClassDefFoundError
: Unable to load a ClassStackOverflowError
: stack overflow
Exception
is a runtime error that can be caught and handled.
Some exceptions are part of application logic processing and should be caught and handled. For example:
NumberFormatException
: Numeric type format errorFileNotFoundException
: File not foundSocketException
: Failed to read network
There are also some exceptions caused by incorrect programming of program logic, and the program itself should be repaired. For example:
NullPointerException
: Calling a method or field on anull
objectIndexOutOfBoundsException
: Array index out of bounds
Exception
are divided into two categories:
RuntimeException
and its subclasses;Non-RuntimeException
(includingIOException
,ReflectiveOperationException
etc.)
Java stipulates:
- Exceptions that must be caught include
Exception
and its subclasses, but do not includeRuntimeException
and its subclasses. This type of exception is called Checked Exception. - Exceptions that do not need to be caught include
Error
and its subclasses,RuntimeException
and its subclasses.
Notice
The compiler does not impose mandatory catching requirements for RuntimeException and its subclasses. This does not mean that the application itself should not catch and handle RuntimeException. Whether it needs to be captured should be analyzed in detail.
catch exception
To catch exceptions, use try...catch
statement, put the code that may cause exceptions in try {...}
, and then use catch
to capture the corresponding Exception
and its subclasses:
java
// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
byte[] bs = toGBK("中文");
System.out.println(Arrays.toString(bs));
}
static byte[] toGBK(String s) {
try {
// Convert String to byte[] using specified encoding:
return s.getBytes("GBK");
} catch (UnsupportedEncodingException e) {
// If the system does not support GBK encoding, UnsupportedEncodingException will be caught:
System.out.println(e); // Print exception information
return s.getBytes(); // Try using the default encoding
}
}
}
If we do not catch UnsupportedEncodingException
, compilation failure will occur:
java
// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
byte[] bs = toGBK("中文");
System.out.println(Arrays.toString(bs));
}
static byte[] toGBK(String s) {
return s.getBytes("GBK");
}
}
The compiler will report an error with a message similar to: unreported exception UnsupportedEncodingException; must be caught or declared to be thrown, and accurately indicate that the statement that needs to be caught is return s.getBytes("GBK");
This means that Checked Exceptions like UnsupportedEncodingException
must be caught.
This is because the String.getBytes(String)
method definition is:
java
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
...
}
When defining a method, use throws Xxx
to indicate the types of exceptions that may be thrown by the method. The caller must be forced to catch these exceptions when calling, otherwise the compiler will report an error.
In toGBK()
method, because the String.getBytes(String)
method is called, UnsupportedEncodingException
must be caught. We can also not capture it, but use throws at the method definition to indicate that toGBK()
method may throw UnsupportedEncodingException
, so that toGBK()
method can pass the compiler check:
java
// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
byte[] bs = toGBK("中文");
System.out.println(Arrays.toString(bs));
}
static byte[] toGBK(String s) throws UnsupportedEncodingException {
return s.getBytes("GBK");
}
}
The above code will still get a compilation error, but this time, the compiler will not prompt the problem of calling return s.getBytes("GBK");
but byte[] bs = toGBK("中文");
;. Because in the main()
method, toGBK()
is called, UnsupportedEncodingException
that it declares may be thrown is not caught.
The fix is to catch the exception in the main()
method and handle it:
java
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
try {
byte[] bs = toGBK("中文");
System.out.println(Arrays.toString(bs));
} catch (UnsupportedEncodingException e) {
System.out.println(e);
}
}
static byte[] toGBK(String s) throws UnsupportedEncodingException {
// Convert String to byte[] using specified encoding:
return s.getBytes("GBK");
}
}
It can be seen that as long as the Checked Exception declared by the method is not captured at the calling layer, it must also be captured at a higher calling layer. All uncaught exceptions must eventually be caught in the main()
method, and try
will not be missed. This is guaranteed by the compiler. The main()
method is also the last opportunity to catch Exception
.
If it is test code, the above writing method is a little troublesome. If you don’t want to write any try
code, you can directly define main()
method as throws Exception
:
java
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws Exception {
byte[] bs = toGBK("中文");
System.out.println(Arrays.toString(bs));
}
static byte[] toGBK(String s) throws UnsupportedEncodingException {
// Convert String to byte[] using specified encoding:
return s.getBytes("GBK");
}
}
Because the main()
method declares that Exception
may be thrown, it also declares that all Exception
may be thrown, so there is no need to catch it internally. The price is that once an exception occurs, the program will exit immediately.
There are also some children's shoes that like to "digest" exceptions inside toGBK()
:
java
static byte[] toGBK(String s) {
try {
return s.getBytes("GBK");
} catch (UnsupportedEncodingException e) {
// do nothing
}
return null;
}
This method of not processing after catching is very bad. Even if nothing can be done, the exception must be recorded first:
java
static byte[] toGBK(String s) {
try {
return s.getBytes("GBK");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
All exceptions can call printStackTrace()
method to print the exception stack. This is a simple and useful method to quickly print exceptions.
Summary
Java uses exceptions to represent errors and catch exceptions through try ... catch
;
Java exceptions are class
and inherit from Throwable
;
Error
is a serious error that does not need to be caught, Exception
is a handleable error that should be caught;
RuntimeException
does not need to be forced to catch, non-RuntimeException
(Checked Exception) needs to be forced to catch, or declared with throws
;
Catching exceptions without handling them is not recommended.