I put together my previous articles to become Github, welcome everyone big guy star github.com/crisxuan/be… This article has been submitted

Final is a key word in Java. It is also a very important key word in Java. The class, method and variable modified by final have different meanings. Finally is also a keyword, but we can use finally in conjunction with other keywords to do some combination operations; Finalize is an undesirable method. It is one of the ancestors of objects, and Finalize is not recommended anymore. In this article, Cxuan will start with these three keywords and give you a simple and profound understanding of these three keywords from the perspective of usage, application and principle.

Final, finally and Finalize

I believe that all of you are experienced programmers, the basic keyword final need not be said. However, still want to take care of the little white reader, after all, we all come from the little white.

Final decorates classes, properties, and methods

Final can be used to modify classes that are not inherited by other classes, that is, classes that are modified by final are unique. As shown below.

We first defined a FinalUsage class that uses the final modifier, and then we defined a FinalUsageExtend class that wanted to extend FinalUsage, and when we did that, the compiler wouldn’t let us do that, It tells us that we cannot inherit from the FinalUsage class. Why? Don’t worry, this is Java convention, there are some why not necessary, just abide by the line.

Final can be used to modify methods. Methods that are final cannot be overridden. Let’s first demonstrate how to modify without the final keyword

As shown above, we inherit the FinalUsage class using the FinalUsageExtend class and provide an override of the writeArticle method. The key to overrides is consistency between the @Override annotation and method modifiers, names, and return values.

Note: Many programmers ignore @override when overriding methods, which makes code more difficult to read and is not recommended.

When we use the final modifier, the method cannot be overridden, as shown below

When we declare the writeArticle method as void, the overridden method will report an error and cannot overwrite the writeArticle method.

Final can modify variables, and variables modified by final cannot be modified once they are defined, as shown below

The error the compiler prompts is that it cannot inherit a class that is modified by final.

We’re using the String String, which is final by default, but it doesn’t really make sense to use final because strings can’t be overwritten.

Let’s rewrite this and use primitive data types to illustrate

As you can also see, the compiler still gives a hint that age cannot be overridden, thus proving that final modified variables cannot be overridden.

In Java, there are not only basic data types, but also reference data types, so what happens when reference types are modified with final? Let’s look at the following code

Start by constructing a Person class

public class Person {
    int id;
    String name;
    get() and set(a).toString(a). }Copy the code

Then we define a final Person variable.

static final Person person = new Person(25."cxuan");

public static void main(String[] args) {

  System.out.println(person);
  person.setId(26);
  person.setName("cxuan001");
  System.out.println(person);
}
Copy the code

If we change the id and name of person, the compiler does not return an error.

This is because the reference type of the final modifier only guarantees that the reference to the object will not change. The data inside the object can be changed. This involves allocating objects in memory, which we’ll talk about later.

Finally guarantees that the program will be executed

Finally is the mechanism by which a program is guaranteed to execute. It is also a keyword in Java. In general, finally is not used alone. The finally block of code

try{
  lock.lock();
}finally {
  lock.unlock();
}
Copy the code

This is a lock/unlock code example. After lock is locked, the unlock operation is performed in finally. Because finally ensures that the code must be executed, some important code is usually put in finally, such as unlock operation, stream close operation, connection release operation, etc.

When lock.lock() raises an exception, it can also be used with a try… catch… Finally used together

try{
  lock.lock();
}catch(Exception e){
  e.printStackTrace();
}finally {
  lock.unlock();
}
Copy the code

try… The finally notation was used before JDK1.7, which introduced a new operation to close a stream: try… with… Resources: Java introduced the try-with-resources declaration, simplifying the try-catch-finally to try-catch. This is a syntactic candy, not an extra syntax. try… with… Resources are still converted to a try-catch-finally statement at compile time.

Syntactic sugar, also known as sugar-coated grammar, is a type of grammar added to a computer language that has no effect on the language’s functionality, but is more user-friendly for programmers. In general, using syntactic sugar increases the readability of programs and thus reduces the chance of errors in program code.

In Java, there are some syntactic sugar to simplify programmer use, which we’ll talk about later.

Finalize the role of

Finalize is a method of the ancestor Object class, which is designed to ensure that objects complete the collection of certain resources before garbage collection. Finalize is no longer recommended and has been explicitly marked deprecated in JDK 1.9.

Understand final, finally, and Finalize in depth

The final design

Many programming languages have some way of telling the compiler that a piece of data is invariant. Sometimes constant data is useful, for example

  • A compile-time constant that never changes. Example: Static final int num = 1024
  • A value that is initialized at runtime and that you do not want to change

Final design conflicts with abstract design because the abstract keyword mainly modifies abstract classes, which need to be implemented by concrete classes. Final means inheritance is forbidden and there is no problem with implementation. Because a subclass can only implement the methods of its parent class if it inherits.

All private ina class is implicitly specified as final, and using final in private-modified code makes no extra sense.

Blank final

Java allows blank final, which is declared final without assigning it a value to initialize it. But the compiler needs to initialize final anyway, so that initialization is left to the constructor, and blank final gives final more flexibility. The following code

public class FinalTest {

   final Integer finalNum;
   
   public FinalTest(a){
       finalNum = 11;
   }
   
   public FinalTest(int num){
       finalNum = num;
   }

    public static void main(String[] args) {
        new FinalTest();
        new FinalTest(25); }}Copy the code

Initializing different finals in different constructors makes finalNum more flexible.

There are two main ways to use final: immutability and efficiency

  • Immutable: Immutable means locking a method (not locking it), but preventing other methods from overwriting it.
  • Efficiency: This is especially true for earlier versions of Java, where declaring a method final in the early implementations of Java allowed the compiler to call that method insteadEmbedded call, but without significant performance optimization. In Java5/6, the hotspot virtual machine automatically detects the embedded calls and optimizes them away, so there is one main way to use final modifications: immutable.

Note that final is not Immutable; Immutable is true.

Final is not truly Immutable because objects referenced by the final keyword can be changed. If we really want an object to be immutable, we usually need the corresponding class to support immutable behavior, such as this code

final List<String> fList = new ArrayList();
fList.add("Hello");
fList.add("World");
List unmodfiableList = List.of("hello"."world");
unmodfiableList.add("again");
Copy the code

The list. of method creates an immutable List. Immutable Immutable is a good choice in many cases. In general, to implement Immutable mutable means to be mutable requires the following considerations

  • Declare the class final to prevent other classes from extending.
  • Declare member variables (including instance variables and class variables) inside a class asprivatefinalDo not provide methods that modify member variables, i.e. setter methods.
  • When constructing an object, it is usually useddeep-cloneThis helps prevent other people from making changes to the input object when the object is assigned directly.
  • Adhere to thecopy-on-writePrinciple, create a private copy.

Does final improve performance?

Whether or not final can improve performance has long been a point of debate in the industry. Many 􏲠􏳁 books, 􏰧􏰨, show that performance can be improved in particular scenarios, 􏱃􏱄. For example, final can be used to help JJMS inline methods, and the 􏳂 compiler can be modified to compile FOR 􏰧􏰨. But many of these conclusions are based on assumptions.

R big this answer may give us some conclusion www.zhihu.com/question/21…

The efficiency of accessing local variables is the same whether they are declared with or without the final keyword modifier.

For example, the following code (with no final version)

static int foo(a) {
  int a = someValueA();
  int b = someValueB();
  return a + b; // Access local variables here
}
Copy the code

Final version

static int foo(a) {
  final int a = someValueA();
  final int b = someValueB();
  return a + b; // Access local variables here
}
Copy the code

Compiling with Javac gives the same results.

Invokestatic someValueA:()I istore_0 invokestatic someValueB:()I istore_1 iload_0 read a iload_1 // Read the value of b iadd ireturnCopy the code

The bytecode is the same because the reference type is used above.

If it’s a constant, let’s see

/ / take final
static int foo(a){

  final int a = 11;
  final int b = 12;

  return a + b;

}

/ / don't take final
static int foo(a){

  int a = 11;
  int b = 12;

  return a + b;

}
Copy the code

If we compile the two foo methods separately, we will find the following bytecode

Comparing the bytecode on the left with code that is not final and the bytecode on the right with code that is final.

  • Int a = 11 or int a = 12 are treated as constants with or without final modifier.
  • A + b without final is treated as a variable on the return; A + B with final modifier is treated as a constant.

In fact, this level of difference only affects simpler JVMS, because such VMS rely heavily on the interpreter to execute the bytecode in the original Class file. High-performance JVMS (such as HotSpot, J9, etc.) are not affected.

Therefore, most of the impact of final on performance optimization can be directly ignored, and we use final more because of its immutability.

Deeper understanding finally

We talked briefly above about the use of finally, which ensures that the statement in finally is executed after the code in the try block completes execution. Whether or not an exception is thrown in the try block.

So let’s take a closer look at finally, what its bytecode is, and the nature of when finally is executed.

  • First we know that a finally block is executed only if a try block is executed, and that finally does not exist on its own.

This doesn’t need to be explained too much, but it’s a well-known rule. Finally must be used with a try block or a try catch block.

  • Second, the finally block is executed after leaving the try block or before the control transition statement (return/continue/break) when the try block is not complete

When finally is executed, let’s use return as an example to see if this is true.

Here’s the code

static int mayThrowException(a){
  try{
    return 1;
  }finally {
    System.out.println("finally"); }}public static void main(String[] args) {
  System.out.println(FinallyTest.mayThrowException());
}
Copy the code

As evidenced by the result of the execution, finally is executed before return.

When finally has a return value, it returns directly. It’s not going to return a try or a catch.

static int mayThrowException(a){
  try{
    return 1;
  }finally {
    return 2; }}public static void main(String[] args) {
  System.out.println(FinallyTest.mayThrowException());
}
Copy the code
  • Before the finally statement is executed, the control transfer statement stores the return value ina local variable

Look at the code below

static int mayThrowException(a){
  int i = 100;
  try {
    return i;
  }finally{ ++i; }}public static void main(String[] args) {
  System.out.println(FinallyTest.mayThrowException());
}
Copy the code

Return I is executed before ++ I, and returns the value of I temporarily, finally.

The essence of the finally

Let’s look at a piece of code

public static void main(String[] args) {

  int a1 = 0;
  try {
    a1 = 1;
  }catch (Exception e){
    a1 = 2;
  }finally {
    a1 = 3;
  }

  System.out.println(a1);
}
Copy the code

What is the output of this code? The answer is 3. Why is that?

With that in mind, let’s take a look at the bytecode of this code

The Exception table is an Exception table. Each entry in the Exception table represents an Exception generator. The Exception generator consists of a pointer From, a pointer To, and a pointer To. A Target pointer and the type of exception that should be caught.

So there are three ways to execute this code

  • If an exception belonging to Exception or its subclasses occurs in the try block, jump to catch processing
  • If an exception occurs in the try block that is not an exception or its subclasses, jump to finally processing
  • If a new exception occurs in the catch block, jump to finally processing

We haven’t talked about the nature of finally yet. If you look closely at the bytecodes above, you’ll see that finally actually places the a1 = 3 bytecodes iconst_3 and istore_1 after the try and catch blocks. So this code right here looks like this

public static void main(String[] args) {

  int a1 = 0;
  try {
    a1 = 1;
		// finally a1 = 3
  }catch (Exception e){
    a1 = 2;
    // finally a1 = 3
  }finally {
    a1 = 3;
  }
  System.out.println(a1);
}
Copy the code

Exception table Exception table Exception table Exception and error are subclasses of Throwable

public static void main(String[] args) {
  int a1 = 1;
  System.out.println(a1);
}
Copy the code

For example, above we used a very simple program to verify, after compiling let’s look at its bytecode

As you can see, there is no exception table.

Will finally be executed

We have discussed all the above cases where finally must be executed, so will finally be executed? I’m afraid not.

In addition to machine room power outage, machine room explosion, machine room water, machine room lightning strike, forced shutdown, unplug, there are several circumstances in which finally can not be executed.

  • Call the system.exit method

  • Call runtime.geTruntime ().halt(exitStatus)

  • JVM downtime

  • If the JVM reaches an infinite loop (or other uninterrupted, non-terminating statement) in a try or catch block

  • Whether the operating system forcibly terminated the JVM process; For example, run the kill -9 PID command on UNIX

  • If the host system crashes; For example, power supply failure, hardware error, or operating system crash will not be executed

  • If the finally block is executed by a daemon thread, all non-daemon threads exit before the finally call.

Is Finalize really useless

We gave a brief introduction to finalize method above and showed that it is a bad practice. So what is the timing of finalize call? Why is Finalize useless?

As we know, a significant difference between Java and C++ is that Java can automatically manage memory. In Java, due to the automatic collection mechanism of GC, there is no guarantee that finalize methods will be executed in time (garbage object collection time is uncertain), and there is no guarantee that they will be executed.

In other words, the execution period of Finalize is uncertain, and we cannot rely on Finalize method to help us with garbage collection. It may be the case that GC is not triggered before We run out of resources, so it is recommended to use the method of display release when resources are used up, such as the close method. In addition, Finalize methods also swallow exceptions.

Finalize works like this: Once the garbage collector is ready to release the storage space occupied by objects, the Finalize method will be called first, and the memory occupied by objects will be reclaimed only when the next garbage collection action occurs. Garbage collection is only about memory.

We do not advocate finalize method in daily development, where finalize method can be used, try… Finally will handle it better.

Hello, I’m Cxuan, a technical person. I wrote a total of six PDFS

Summary of Java Core Technology, Summary of HTTP Core, Basic Knowledge programmers must know, Summary of Operating System Core, Java Core Foundation 2.0, Summary of Java Interview Questions

Now I put out the Baidu link to you, you can click the link below to get

Link: pan.baidu.com/s/1mYAeS9hI… Password: p9rs