Java Core Technology Interview Essentials (Lecture 2) | What is the difference between Exception and Error?

Are there programs in the world that will never go wrong? Maybe this will only appear in the dreams of programmers. With the birth of programming languages ​​and software, abnormal situations entangle us like a shadow. Only by correctly handling the unexpected can the reliability of the program be guaranteed.

The Java language provides a relatively complete exception handling mechanism at the beginning of its design. This is one of the reasons why Java has become so popular, because this mechanism greatly reduces the threshold for writing and maintaining reliable programs. Nowadays, the exception handling mechanism has become the standard configuration of modern programming languages.

The question I want to ask you today is, please compare Exception and Error. In addition, what is the difference between runtime exception and general exception?


Typical answer

Both Exception and Error inherit the Throwable class. In Java, only instances of the Throwable type can be thrown (throw) or caught (catch). It is the basic component type of the exception handling mechanism.

Exception and Error reflect the classification of different exceptions by the designers of the Java platform. Exception is an unexpected situation that can be expected during the normal operation of the program, which may and should be caught and handled accordingly.

Error refers to a situation that is unlikely to occur under normal circumstances. Most errors will cause the program (such as the JVM itself) to be in an abnormal and unrecoverable state. Since it is an abnormal situation, it is inconvenient and does not need to be captured. Common ones such as OutOfMemoryError are all subclasses of Error.

Exception is divided into checked and unchecked exceptions. Checkable exceptions must be explicitly captured in the source code. This is part of the compile-time check. The uncheckable Error I introduced earlier is Throwable not Exception.

Unchecked exceptions are so-called runtime exceptions, similar to NullPointerException, ArrayIndexOutOfBoundsException, etc., which are usually logic errors that can be avoided by coding. The specific needs to determine whether to capture is required, and it is not mandatory at compile time.

Test site analysis

Analyzing the difference between Exception and Error is to examine the Java processing mechanism from a conceptual point of view. Generally speaking, it is still at the level of understanding, as long as the interviewer elaborates clearly.

In our daily programming, how to deal with exceptions is a test of knowledge. I think we need to master two aspects.

First, understand the design and classification of Throwable, Exception, and Error. For example, master the most widely used subcategories, and how to customize exceptions.

Many interviewers will further ask some details, for example, what Error, Exception or RuntimeException do you know? I drew a simple class diagram, and listed typical examples, can give you as a reference, at least do the basic knowledge.

Some of these sub-types, it is best to focus on understanding, for example, what is the difference between NoClassDefFoundError and ClassNotFoundException, this is also a classic entry topic.

Second, understand the elements and practices of operating Throwable in the Java language. It is necessary to master the most basic syntax, such as try-catch-finally block, throw, throws keywords, etc. At the same time, we must also know how to deal with typical scenarios.

Exception handling code is more cumbersome. For example, we need to write a lot of cookie-cutter capture code, or do some resource recovery work in finally. With the development of the Java language, some more convenient features have been introduced, such as try-with-resources and multiple catch. For details, please refer to the following code snippets. During compilation, corresponding processing logic is automatically generated, for example, objects that extend AutoCloseable or Closeable are automatically closed according to the conventions.

try (BufferedReader br = new BufferedReader(…);
     BufferedWriter writer = new BufferedWriter(…)) {// Try-with-resources
// do something
catch ( IOException | XEception e) {// Multiple catch
   // Handle it
} 

Knowledge expansion 

Most of the previous talks are conceptual things. Now let me talk about some practical choices. I will analyze it in conjunction with some code use cases.

Let's start with the first one. The following code reflects what is wrong in exception handling?

try {
  // 业务代码
  // …
  Thread.sleep(1000L);
} catch (Exception e) {
  // Ignore it
}

 Although this code is very short, it violates the two basic principles of exception handling.

First, try not to catch general exceptions like Exception, but rather catch specific exceptions, in this case InterruptedException thrown by Thread.sleep().

This is because in daily development and cooperation, we often have more opportunities to read code than to write code. Software engineering is the art of collaboration, so we are obligated to make our code intuitively reflect as much information as possible, and it is general. Exception and the like hide our purpose. In addition, we must also ensure that the program does not catch exceptions that we do not want to catch. For example, you may prefer RuntimeException to be spread instead of being caught.

Furthermore, unless you think it over, don't catch Throwable or Error. It is difficult to ensure that we can handle OutOfMemoryError correctly.

Second, don't swallow abnormalities. This is something to pay special attention to in exception handling, because it is likely to cause weird situations that are very difficult to diagnose.

Swallowing exceptions is often based on the assumption that this code may not occur, or it does not matter to ignore the exceptions, but do not make such assumptions in the product code!

If we do not throw the exception, or do not output to the log (Logger), the program may end in an uncontrollable way in the subsequent code. No one can easily determine where the exception was thrown and what caused the exception.

Let's take a look at the second code

try {
   // 业务代码
   // …
} catch (IOException e) {
    e.printStackTrace();
}

 This code is used as a piece of experimental code, it has no problems, but in the product code, it is usually not allowed to deal with it. Do you think about why this is?

Let's take a look at the documentation of printStackTrace(), which begins with "Prints this throwable and its backtrace to the standard error stream". The problem lies here. In a slightly more complex production system, STERR is not a suitable output option, because it is difficult for you to judge where the output is going.

Especially for distributed systems, if an exception occurs, but the stack trace cannot be found, this is purely an obstacle to diagnosis. Therefore, it is best to use the product log and output it to the log system in detail.

Let's look at the following code snippet next to experience the Throw early, catch late principle.

public void readPreferences(String fileName){
   //...perform operations... 
  InputStream in = new FileInputStream(fileName);
   //...read the preferences file...
}

 If fileName is null, then the program will throw a NullPointerException, but because the problem is not exposed in the first time, the stack information may be very puzzling and often requires relatively complicated positioning. This NPE is just an example. In the actual product code, there may be various situations, such as failure to obtain configuration. When a problem is discovered, it can be thrown out as soon as possible, which can reflect the problem more clearly.

We can modify it to make the question "throw early", and the corresponding exception information is very intuitive.

public void readPreferences(String filename) {
  Objects. requireNonNull(filename);
  //...perform other operations... 
  InputStream in = new FileInputStream(filename);
   //...read the preferences file...
}

As for "catch late", it is actually a problem we often worry about. After catching the exception, what should we do? The worst way to deal with it is the "swallowing abnormality" I mentioned earlier, which is actually to cover up the problem. If you really don't know how to deal with it, you can choose to keep the original exception's cause information and throw it directly or construct a new exception to throw. At a higher level, because of the clear (business) logic, it is often clearer what the appropriate processing method is.

Sometimes, we will customize exceptions as needed. At this time, in addition to ensuring that sufficient information is provided, there are two more points to consider:

  • Whether it needs to be defined as Checked Exception, because the original intention of this type of design is to recover from abnormal situations. As an exception designer, we often have sufficient information to classify.
  • While ensuring that the diagnostic information is sufficient, also consider avoiding the inclusion of sensitive information, because that may lead to potential security issues. If we look at the Java standard library, you may notice something like java.net.ConnectException, the error message is similar to "Connection refused (Connection refused)", and does not include the specific machine name, IP, port, etc. An important consideration is information security. A similar situation exists in the log. For example, user data is generally not output to the log.

There is a kind of debate in the industry (it can even be regarded as a certain degree of consensus) that the Checked Exception of the Java language may be a design error. Opponents have listed several points:

  • The assumption of Checked Exception is that we catch the exception and then resume the program. However, in most cases, it is impossible to recover. The use of Checked Exception has greatly deviated from the original design purpose.
  • Checked Exception is not compatible with functional programming. If you have written Lambda/Stream code, I believe you will have a deep understanding.

Many open source projects have adopted this practice, such as Spring, Hibernate, etc., and even reflected in the design of new programming languages, such as Scala. If you are interested, you can refer to:

http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/

Let's look at Java's exception handling mechanism from a performance perspective. Here are two things that may be relatively expensive:

  • The try-catch code segment will cause additional performance overhead, or put it another way, it often affects the JVM to optimize the code, so it is recommended to capture only the necessary code segment, and try not to cover the entire code with a big try; At the same time, it is not a good idea to use exceptions to control the code flow. It is far less efficient than our usual conditional statements (if/else, switch).
  • Every time Java instantiates an Exception, it will take a snapshot of the current stack, which is a relatively heavy operation. If it happens very frequently, this overhead cannot be ignored.

Therefore, for some underlying libraries that pursue extreme performance, one way is to try to create an Exception that does not take a stack snapshot. This itself is controversial, because the assumption for this is that when I create an exception, I know if the stack is needed in the future. The question is, is it actually possible? It may be possible on a small scale, but in a large-scale project, doing so may not be a sensible choice. If a stack is needed, but this information is not collected, in complex situations, especially distributed systems like microservices, this will greatly increase the difficulty of diagnosis.

When our service slows down and throughput decreases, checking for the most frequently occurring Exception is also a way of thinking. Regarding the problem of diagnosing the slowness of the background, I will discuss it systematically in the following Java Performance Basic Module.

Today, I briefly summarized the mechanism of Java exception handling from a common conceptual problem of exception handling. And combined with the code, analyzed some generally recognized best practices, and some of the industry's latest consensus on abnormal usage. Finally, I analyzed the abnormal performance overhead, I hope it helps you.


Other classic answers

The following answer comes from a netizen who is lost and knows how to return:

When I was comparing dishes, I heard "What is the difference between NoClassDefFoundError and ClassNotFoundException, which is also a classic entry topic." In this paragraph, I thought I would talk about the difference between the two. I think this difference is just dry stuff! The article has more conclusive language and is not specific

Netizen Mao Maoxiong replied:

NoClassDefFoundError is an error (Error), and ClassNOtFoundException is an exception. The handling of errors and exceptions in Java is different. We can recover programs from exceptions but should not try to recover programs from errors.

Reasons for ClassNotFoundException:

Java supports the use of the Class.forName method to dynamically load classes. If the class name of any class is passed to this method as a parameter, it will cause the class to be loaded into the JVM memory. If this class is not found in the classpath, Then it will throw a ClassNotFoundException exception at runtime.

Reasons for ClassNotFoundException:

Java supports the use of the Class.forName method to dynamically load classes. If the class name of any class is passed to this method as a parameter, it will cause the class to be loaded into the JVM memory. If this class is not found in the classpath, Then it will throw a ClassNotFoundException exception at runtime.

The main reasons for ClassNotFoundException are:

Java supports the use of reflection to dynamically load classes at runtime. For example, when using the Class.forName method to dynamically load classes, you can pass the class name as a parameter to the above method to load the specified class into the JVM memory. If the class is in the class If the path is not found, then ClassNotFoundException will be thrown at runtime at this time.

To solve this problem, you need to ensure that the required class and the packages it depends on exist in the classpath. The common problem is that the class name is written incorrectly.

Another cause of ClassNotFoundException is that when a class has been loaded into memory by a certain class loader, another class loader tries to dynamically load this class from the same package. By controlling the dynamic class loading process, the above situation can be avoided.

The reasons for NoClassDefFoundError are:

If the JVM or ClassLoader instance tries to load the class (which can be called by a normal method, or it may be created by using new), the definition of the class cannot be found. The class to be searched exists when it is compiled, but it cannot be found when it is run. At this time, it will cause NoClassDefFoundError.

The cause of this problem may be that some classes are missed during the packaging process, or the jar package is damaged or tampered with. The solution to this problem is to find classes that exist in the classpath during development but are not in the classpath during runtime.

The following is the answer from the netizen adrian-jser:

If you drive up the mountain and the car breaks down, you take out the toolbox and repair it, and then continue on the road (the exception is caught, recover from the exception, and continue to run the program), the car breaks down, and you don’t know how to fix it, call to tell the repairer The dealership, tell you what the problem is, and ask the dealership to come and fix it. (Under the current logical background, you don't know what kind of processing logic is to throw exceptions to a higher business layer for processing). When you call, be as specific as possible, not just saying that my car can't move. It is difficult for the car repair shop to locate your problem. (To replenish specific exceptions, you cannot catch general exceptions similar to Exception). Another situation is that if you drive up the mountain and the mountain collapses, can you still repair it? (Error: Cause your operating environment to enter an abnormal state, which is difficult to recover)

The following answer comes from netizen Ouyang Tian:

1.Error: system error, virtual machine error, we can't handle it, and we don't need to deal with it.

2.Exception, an exception that can be caught and processed. That is, either catch the exception and deal with it, or continue to throw the exception.

3. RuntimeException, a frequently occurring error, can be caught and dealt with. It may not be caught or thrown. ArrayIndexOutOfBoundsException like this exception can not be caught, why? In a program, many arrays are used, and if you use one capture once, it is very tiring.

4. When inheriting an exception, when rewriting the method, either no exception is thrown, or the exact same exception is thrown.

5. When a try is followed by many catches, small exceptions must be caught first and then large exceptions.

6. If an exception occurs, the console prints many lines of information, which is caused by multi-layer method calls in the program. The key is to look at the type and line number.

7. Upload and download cannot throw exceptions. The upload and download must be closed.

8. The exception is not an error. The exception control code flow is not conducive to the simple and easy-to-read code.

9. Try catch finally execution flow, mixed with return, break, continue, etc. Pay attention to the order of code execution. It's not impossible, but the more powerful the person, the easier to understand the code.

The following is the answer from the netizen:

Error refers to unpredictable errors that may cause the program to crash; exception refers to the foreseeable abnormality in the operation of the program, and exceptions are divided into check exceptions and general exceptions. Check exceptions need to be displayed and captured in the program and processed , General exceptions can be handled by program coding, such as array out of bounds, null pointers, etc.; two basic principles of exception handling: do not capture general exception information, such as directly capture Exception, which will increase the difficulty of code reading; do not swallow Exceptions; printing exception information is a relatively heavy operation, which will slow down the program; try catch should preferably include the code that needs to be checked for exceptions, and do not include too long code, which will reduce the optimization efficiency of the JVM; this is to learn this lesson Part of the summary

 

 

Guess you like

Origin blog.csdn.net/qq_39331713/article/details/114025666