2
头图

Hello, my name is Guide. The autumn recruitment is coming, I have refactored and improved the content of JavaGuide , and synchronized the latest update, hoping to help you.

The first two articles:

abnormal

Java exception class hierarchy diagram overview :

What is the difference between Exception and Error?

In Java, all exceptions have a common ancestor java.lang package Throwable class. Throwable The class has two important subclasses:

  • Exception : Exceptions that can be handled by the program itself can be caught by catch . Exception can be divided into Checked Exception (checked exception, must be handled) and Unchecked Exception (unchecked exception, can not be handled).
  • Error : Error is an error that cannot be handled by the program, We can't capture it by catch Capture via catch is not recommended. For example, Java virtual machine running error ( Virtual MachineError ), virtual machine memory is not enough error ( OutOfMemoryError ), class definition error ( NoClassDefFoundError ) and so on. When these exceptions occur, the Java Virtual Machine (JVM) typically chooses to terminate the thread.

What is the difference between Checked Exception and Unchecked Exception?

Checked Exception is a checked exception. During the compilation process of Java code, if the checked exception is not processed by the catch or throws keyword, it cannot pass the compilation.

For example, the following code for IO operation:

Except for RuntimeException and its subclasses, the other Exception class and its subclasses are all checked exceptions. Common checked exceptions are: IO related exceptions, ClassNotFoundException , SQLException .

Unchecked Exception means unchecked exception . During the compilation process of Java code, we can compile normally even if we do not handle unchecked exception.

RuntimeException and its subclasses are collectively referred to as unchecked exceptions.

  • NullPointerException (null pointer error)
  • IllegalArgumentException (parameter error such as method input parameter type error)
  • NumberFormatException (string to number malformed, subclass of IllegalArgumentException )
  • ArrayIndexOutOfBoundsException (Array out of bounds error)
  • ClassCastException (type conversion error)
  • ArithmeticException (arithmetic error)
  • SecurityException (security error such as insufficient permissions)
  • UnsupportedOperationException (unsupported operation error such as duplicate creation of the same user)
  • ......

What are the common methods of the Throwable class?

  • String getMessage() : return a brief description of when the exception occurred
  • String toString() : Returns the detailed information when the exception occurred
  • String getLocalizedMessage() : Returns the localized information of the exception object. Override this method with a subclass of Throwable to generate localized information. If the subclass does not override this method, the information returned by this method is the same as that returned by getMessage()
  • void printStackTrace() : Print on the console Throwable Exception information encapsulated by the object

How is try-catch-finally used?

  • try Block: Used to catch exceptions. It can be followed by zero or more catch blocks, if there is no catch block, it must be followed by a finally block.
  • * catch Block: Used to handle exceptions caught by try.
  • finally block: The statements in the finally block are executed regardless of whether the exception is caught or handled. When the ---c38116bcc6778b999f64b41b7c9517bc statement is executed before the method return statement when the ---c38116bcc6778b999f64b41b7c9517bc statement is encountered in the try block or the catch block, the ---1ea4e36e0ce5daa465---341 block will be executed before the method finally statement.

Code example:

 try {
    System.out.println("Try to do something");
    throw new RuntimeException("RuntimeException");
} catch (Exception e) {
    System.out.println("Catch Exception -> " + e.getMessage());
} finally {
    System.out.println("Finally");
}

output:

 Try to do something
Catch Exception -> RuntimeException
Finally

Note: Do not use return in a finally block! When both a try statement and a finally statement have return statements, the return statement in the try block is ignored. This is because the return value in the try statement will be temporarily stored in a local variable. When the return in the finally statement is executed, the value of this local variable becomes the return value in the finally statement.

It is clearly mentioned in the official jvm documentation :

If the try clause executes a _return_, the compiled code does the following:

  1. Saves the return value (if any) in a local variable.
  2. Executes a jsr to the code for the finally clause.
  3. Upon return from the finally clause, returns the value saved in the local variable.

Code example:

 public static void main(String[] args) {
    System.out.println(f(2));
}

public static int f(int value) {
    try {
        return value * value;
    } finally {
        if (value == 2) {
            return 0;
        }
    }
}

output:

 0

Will the code in finally be executed?

uncertain! In some cases, the code in finally will not be executed.

For example, if the virtual machine is terminated before finally, the code in finally will not be executed.

 try {
    System.out.println("Try to do something");
    throw new RuntimeException("RuntimeException");
} catch (Exception e) {
    System.out.println("Catch Exception -> " + e.getMessage());
    // 终止当前正在运行的Java虚拟机
    System.exit(1);
} finally {
    System.out.println("Finally");
}

output:

 Try to do something
Catch Exception -> RuntimeException

In addition, in the following two special cases, the code of the finally block will not be executed:

  1. The thread in which the program resides dies.
  2. Turn off the CPU.

Related issue: https://github.com/Snailclimb/JavaGuide/issues/190 .

🧗🏻 Advanced: From the perspective of bytecode analysis try catch finally the implementation principle behind this syntactic sugar.

How to use try-with-resources instead of try-catch-finally ?

  1. Scope (definition of resource): Any object that implements java.lang.AutoCloseable or java.io.Closeable
  2. Execution order of closing resources and finally blocks: In a try-with-resources statement, any catch or finally blocks run after the declared resource is closed

"Effective Java" clearly states:

In the face of resources that must be closed, we should always use try-with-resources instead of try-finally . The resulting code is shorter and cleaner, and the resulting exceptions are more useful to us. try-with-resources statement makes it easier to write code for resources that must be closed, which is almost impossible with try-finally .

Java 中InputStreamOutputStreamScannerPrintWriter close()方法To manually close, under normal circumstances, we use the try-catch-finally statement to achieve this requirement, as follows:

 //读取文本文件的内容
Scanner scanner = null;
try {
    scanner = new Scanner(new File("D://read.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}

Use the try-with-resources statement after Java 7 to transform the above code:

 try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

Of course, when multiple resources need to be closed, using try-with-resources is also very simple to implement. If you still use try-catch-finally it may bring many problems.

Multiple resources can be declared in a try-with-resources block by separating them with semicolons.

 try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
     BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
    int b;
    while ((b = bin.read()) != -1) {
        bout.write(b);
    }
}
catch (IOException e) {
    e.printStackTrace();
}

What should be paid attention to in abnormal use?

  • Do not define exceptions as static variables, because this will cause confusion in the exception stack. Every time an exception is thrown manually, we need to manually throw a new exception object.
  • The exception information thrown must be meaningful.
  • It is recommended to throw a more specific exception such as when the string is converted to a number in the wrong format, it should be thrown NumberFormatException instead of its parent class IllegalArgumentException .
  • After using the log to print the exception, don't throw the exception again (the two should not exist in a piece of code logic at the same time).
  • ......

Generics

What are generics? what's the effect?

Java Generics (Generics) is a new feature introduced in JDK 5. Using generic parameters can enhance the readability and stability of the code.

The compiler can detect generic parameters and specify the incoming object type through generic parameters. For example ArrayList<Persion> persons = new ArrayList<Persion>() this line of code indicates that the ArrayList object can only be passed in Persion object, and an error will be reported if other types of objects are passed in.

 ArrayList<E> extends AbstractList<E>

And, the native List return type is Object , you need to manually convert the type to use, and the compiler will automatically convert it after using generics.

What are the ways to use generics?

There are generally three ways to use generics: generic classes , generic interfaces , and generic methods .

1. Generic class :

 //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{

    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}

How to instantiate a generic class:

 Generic<Integer> genericInteger = new Generic<Integer>(123456);

2. Generic interface :

 public interface Generator<T> {
    public T method();
}

Implement a generic interface without specifying a type:

 class GeneratorImpl<T> implements Generator<T>{
    @Override
    public T method() {
        return null;
    }
}

Implement a generic interface, specifying the type:

 class GeneratorImpl<T> implements Generator<String>{
    @Override
    public String method() {
        return "hello";
    }
}

3. Generic method :

 public static < E > void printArray( E[] inputArray )
   {
         for ( E element : inputArray ){
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

use:

 // 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray  );
printArray( stringArray  );
Note: public static < E > void printArray( E[] inputArray ) is generally called a static generic method; in java a generic is just a placeholder and must be used after passing a type. A class can only pass type parameters when it is instantiated. Since the loading of static methods precedes the instantiation of classes, that is to say, the generics in the class have not passed the real type parameters, and the loading of static methods has been completed. So there is no way for static generic methods to use generics declared on the class. You can only use your own declaration <E>

Where are generics used in the project?

  • Custom interface general return result CommonResult<T> Through parameters T The data type of the result can be dynamically specified according to the specific return type
  • Definition Excel processing class ExcelUtil<T> for dynamic specification Excel exported data type
  • Build a collection tool class (refer to the Collections sort , binarySearch methods in ---59ae994d2f35cbf1962ced45a3f48b64---).
  • ......

reflection

What is reflection?

If you have studied the underlying principles of the framework or we have written the framework ourselves, you must be familiar with the concept of reflection.

Reflection is called the soul of the framework mainly because it gives us the ability to analyze classes at runtime and execute methods in them. Through reflection you can get all the properties and methods of any class, and you can also call these methods and properties.

Advantages and disadvantages of reflection mechanism

  • Advantages : It can make our code more flexible and provide convenience for various frameworks to provide out-of-the-box functions
  • Disadvantage : It gives us the ability to analyze operation classes at runtime, which also increases security issues. For example, the security check of generic parameters can be ignored (the security check of generic parameters occurs at compile time). In addition, the performance of reflection is also slightly worse, but it has little effect on the framework. Java Reflection: Why is it so slow?

Reflection application scenarios

As we usually write business code most of the time, we rarely come into contact with the scene of directly using the reflection mechanism.

However, that doesn't mean reflection is useless. On the contrary, it is because of reflection that you can use various frameworks so easily. Frameworks like Spring/Spring Boot, MyBatis, etc. use a lot of reflection mechanisms.

Dynamic proxies are also heavily used in these frameworks, and the implementation of dynamic proxies also relies on reflection.

For example, the following is the sample code for implementing dynamic proxy through JDK, which uses the reflection class Method to call the specified method.

 public class DebugInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after method " + method.getName());
        return result;
    }
}

In addition, implementations like annotations , a great tool in Java, use reflection.

Why when you use Spring, a @Component annotation declares a class as Spring Bean? Why do you read the value in the configuration file through a @Value annotation? How exactly does it work?

These are all because you can analyze classes based on reflection, and then get annotations on class/property/method/method parameters. After you get the annotation, you can do further processing.

annotation

Annotation (annotation) is a new feature introduced in Java5, which can be regarded as a special annotation, mainly used to modify classes, methods or variables.

The essence of the annotation is a special interface that inherits Annotation :

 @Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

public interface Override extends Annotation{

}

The annotation will only take effect after it is parsed. There are two common parsing methods:

  • Direct scanning during compilation: The compiler scans the corresponding annotations and processes them when compiling Java code. For example, if a method uses the @Override annotation, the compiler will detect whether the current method has been rewritten when compiling. The corresponding method of the parent class.
  • The runtime is processed by reflection : the annotations that come with the framework (such as @Value and @Component of the Spring framework) are processed by reflection.

JDK provides many built-in annotations (such as @Override , @Deprecated ), and we can also customize annotations.

I/O

What is serialization? What is deserialization?

If we need to persist Java objects, such as saving Java objects in files, or transmitting Java objects over the network, serialization is required in these scenarios.

simply put:

  • Serialization : The process of converting a data structure or object into a stream of binary bytes
  • Deserialization : The process of converting the binary byte stream generated during serialization into a data structure or object

For an object-oriented programming language such as Java, what we serialize is an object (Object), that is, an instantiated class (Class), but in a semi-object-oriented language such as C++, the struct (structure) definition is the data structure type, and class corresponds to the object type.

Wikipedia describes serialization as follows:

Serialization, in data processing in computer science, refers to the conversion of data structures or object states into a usable format (eg, as a file, buffered, or sent over a network) for subsequent use in the same or another format. In a computer environment, the process of restoring the original state. When retrieving the result of bytes in serialized format, it can be used to produce a copy with the same semantics as the original object. For many objects, such as complex objects that use a lot of references, this serialization reconstruction process is not easy. Object serialization in object orientation does not generalize the functions to which the original object was related. This process is also known as object marshalling. The reverse operation of extracting a data structure from a sequence of bytes is deserialization (also known as unmarshalling, deserialization, unmarshalling).

To sum up: The main purpose of serialization is to transfer objects over the network or to store objects in the file system, database, and memory.

<p style="text-align:right;font-size:13px;color:gray"> https://www.corejavaguru.com/java/serialization/interview-questions-1 </p>

What if some fields in Java serialization do not want to be serialized?

For variables that do not want to be serialized, use the transient keyword modification.

transient The function of the keyword is to prevent the serialization of variables modified with this keyword in the instance; when the object is deserialized, the variable value modified by transient will not be are persisted and restored.

About transient There are a few more notes:

  • transient Can only modify variables, not classes and methods.
  • transient modified variable, the variable value will be set to the default value of the type after deserialization. For example, if it is a modification of type int , the result after deserialization is 0 .
  • static Since the variable does not belong to any object (Object), it will not be serialized with or without the transient keyword modification.

Two common ways to get keyboard input

Method 1: Via Scanner

 Scanner input = new Scanner(System.in);
String s  = input.nextLine();
input.close();

Method 2: Via BufferedReader

 BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();

What are the types of IO streams in Java?

  • According to the flow direction of the flow, it can be divided into input flow and output flow;
  • According to the operation unit, it can be divided into byte stream and character stream;
  • According to the role of the flow, it is divided into node flow and processing flow.

The Java IO stream involves more than 40 classes. These classes look messy, but they are actually very regular, and there is a very close relationship between them. The more than 40 classes of the Java IO stream are derived from the following 4 abstract classes derived from the base class.

  • InputStream/Reader: The base class for all input streams, the former is a byte input stream and the latter is a character input stream.
  • OutputStream/Writer: The base class for all output streams, the former is a byte output stream and the latter is a character output stream.

The structure diagram is classified according to the operation mode:

IO-操作方式分类

Classification structure diagram by operation object:

IO-操作对象分类

Since there is a byte stream, why should there be a character stream?

The essence of the question wants to ask: whether it is file reading or writing or network transmission and reception, the smallest storage unit of information is bytes, so why are I/O stream operations divided into byte stream operations and character stream operations?

Answer: The character stream is obtained by converting bytes by the Java virtual machine. The problem is that this process is still very time-consuming, and if we don't know the encoding type, it is easy to have garbled characters. Therefore, the I/O stream simply provides an interface to directly manipulate characters, which is convenient for us to perform stream operations on characters. If audio files, pictures and other media files are better to use byte streams, if characters are involved, it is better to use character streams.

postscript

Focus on Java original dry goods sharing, the junior open source JavaGuide ("Java Learning + Interview Guide" covers the core knowledge that most Java programmers need to master. Prepare for Java interviews, JavaGuide is the first choice!), currently has 120k+ Stars.

Originality is not easy, welcome to like and share, welcome to follow my account in Sifu , I will continue to share original dry goods! Come on, go!

If this article is helpful to you, please like and share, it is very important for me to continue to share & create high-quality articles. Thanks 🙏🏻


JavaGuide
9k 声望1.9k 粉丝

你好,我是 Guide,开源项目 JavaGuide 作者。