1
头图

Preface

For the final keyword in Java, we can first understand it from the literal meaning, Baidu translation shows as follows:

image-20210721135747993

Final means final in English and cannot be changed. So the corresponding expression in Java also means this, you can use the final keyword to modify variables, methods, and classes. No matter what it is used to modify, its original meaning is "it cannot be changed". This is something we need to keep in mind. Why can't it be changed? It's nothing more than design needs or can improve efficiency. After keeping in mind the immutable design concept of final, come to understand the usage of final keywords, and it will be natural.

text

Modified variable

First we look at an example
    public static void main(String[] args) {
        String a = "hello1";
        final String b = "hello";
        String d = "hello";
        String c = b + 1;
        String e = d + 1;
        System.out.println(a == c);
        System.out.println(a == e);
    }
}

Output result:

true
false

Process finished with exit code 0

get this result? Let's analyze it:

  1. The variable a refers to hello1 in the string constant pool;
  2. Variable b is final modified. The value of variable b has been determined at compile time. In other words, it is known in advance what the content of variable b is, which is equivalent to a compile-time constant;
  3. The variable c is obtained by b + 1. Since b is a constant, it is directly equivalent to using the original value of b hello for calculation when using b. Therefore, the generated value of c is also a constant, a is a constant, and c is also a constant. Both are hello1 hello1 string is generated in the constant pool in Java, so a and c are equal;
  4. d points to hello constant pool, but since d is not a final modification, that is to say, when d is used, the value of d will not be known in advance, so it is different when calculating e. If e is used, The reference calculation of d, the access of variable d needs to be carried out at runtime through linking, so this calculation will generate hello1 on the heap, so the final e points to hello1 heap, so a and e are not equal.

Conclusion: a and c are hello1 in the constant pool, and e is hello1 heap.

Variables modified by the final keyword are called constants, which means that they cannot be changed. Variables are basic data types, which cannot be changed and are easy to understand

image-20210721141153061

You can see that the basic variables are immutable if they are modified by final

What about reference types? It is impossible to change the reference address or the content of the object?

We first construct an entity class: Student

public class Student {

    private String name;

    public Student(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Then create a Person object based on:

image-20210721140707005

As you can see, first modify an object p with the final keyword, and then point the p object to another new object. An error is found, which means that the final modified reference type cannot change its reference address.

Then we change the name property of the p object:

image-20210721140827750

Found that the program did not report an error, the output result is also

Conclusion: A variable modified by final cannot change its reference address, but its internal properties can be changed.

Modification method

Methods modified by the final keyword cannot be overridden.

There are two reasons for using the final method:

  1. The first reason is to lock the method to prevent any inherited classes from modifying its meaning. This is for design considerations: you want to ensure that the behavior of the method remains the same in inheritance and will not be overwritten.
  2. The second reason is efficiency. In the early implementation of Java, if a method is declared as final, it is agreed that the compiler will convert all calls to the method into inline calls. Inline calls can improve the efficiency of method calls, but If the method is large, inline calls will not improve performance. In the current Java version (after JDK1.5), the virtual machine can be optimized automatically, without the need to use the final method.

Therefore, the final keyword only uses its modification method when it is explicitly forbidden to overwrite the method.

PS: "Java Programming Thoughts" pointed out that all private methods in the class are implicitly designated as final, so for private methods, we explicitly declare final has no effect. But we create a parent class and declare a private method in the parent class. The subclass can override the private method of the parent class. Why?

Parent class: Teacher.class

public class Teacher {

    private void study(){
        System.out.println("teacher");
    }
}

Subclass: Student.class

public class Student extends Teacher{

    private void study(){
        System.out.println("student");
    }
}

In fact, take a closer look, is this way of writing a method coverage? We cannot call the say() method of the parent class through the form of polymorphism:

image-20210721142636082

And, if we add @Override annotation in the say() method of the subclass, an error will also be reported.

image-20210721142835191

So this form is not considered method coverage.

Final modified methods cannot be overridden by subclasses, but they can be used and overloaded by subclasses.

Parent class: A.class

public class A {

    public int a = 0;

    public int getA() {
        return a;
    }

    public final void setA(int a) {
        System.out.println("before set:A = " + this.a);//必须加this,不加就会使用传入的a
        this.a = a;
        System.out.println("after set:A = " + a);
    }

}

Subclass: B.class

image-20210722084410618

public class B extends A {

    public B() {
        super.setA(2);//正确,可以使用父类的final方法
        setA();//调用本类自己方法
    }

    public final void setA() {
        System.out.println("before set:super a = " + a);
        super.a++;
        System.out.println("after set:super a = " + a);
    }
}

have a test:

   public static void main(String[] args) {
        B b = new B();
    }

Output result:

before set:A = 0
after set:A = 2
before set:super a = 2
after set:super a = 3

Process finished with exit code 0

Conclusion: The method modified by the final keyword cannot be overridden, but it can be used and overloaded by subclasses.

Modification

The final modified class means that the class cannot be inherited.

image-20210721150533949

  1. In other words, when you don't want a certain class to have subclasses, use the final keyword to modify it. And because it is a class modified with final, all methods in the class are also implicitly referred to as final methods.
  2. There is the most obvious class String in the JDK, which is decorated with final. One reason why it is important to decorate the String class with final is the constant pool.

Easter eggs

Interview question: Tell me about the difference between final, finally, and finalize?

finally

The finally keyword is generally used in exceptions. It is used in conjunction with try catch to indicate that the content in finally will be executed regardless of whether an exception occurs.

1. The order of execution when there is a return in try

The return statement is not the final exit of the function. If there is a finally statement, finally will be executed after the return (the value of return will be temporarily stored in the stack, waiting for finally execution and then return)

2. The position of return and exception acquisition statements

  1. Case one (return in try, no return in finally)
public class TryTest {

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

    private static int test() {
        int num = 10;
        try {
            System.out.println("try");
            return num += 80;
        } catch (Exception e) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20:" + num);
            }
            System.out.println("finally");
        }
        return num;
    }
}

Output result:

try
num>20:90
finally
90

Process finished with exit code 0

analysis: "return num += 80" is split into two statements, "num = num+80" and "return num". The "num = num+80" statement in try is executed and saved. Before the "return num" in try is executed, the statement in finally is executed, and then 90 is returned.

  1. Case two (return in both try and finally)
public class TryTest {

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

    private static int test() {
        int num = 10;
        try {
            System.out.println("try");
            return num += 80;
        } catch (Exception e) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20:" + num);
            }
            System.out.println("finally");
            return 100;
        }
    }
}

Output result:

try
num>20:90
finally
100

Process finished with exit code 0

analysis: the return in the try is "covered" and dropped, so it is no longer executed.

  1. Case three (there is no return in finally, but the return value num is changed in finally)
public class TryTest {

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

    private static int test() {
        int num = 10;
        try {
            System.out.println("try");
            return num;
        } catch (Exception e) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20:" + num);
            }
            System.out.println("finally");
            num = 100;
        }
        return num;
    }
}

Output result:

try
finally
10

Process finished with exit code 0

analysis: Although the return value num is changed in finally, because the value of num is not returned in finally, after executing the statement in finally, the test() function will get the value of num returned in try, and try The value of num in is still the value retained before the program enters the finally code block, so the return value obtained is 10. And the return statement at the end of the function will not be executed.

  1. Situation four: (wrap the value of num in the Num class)
public class TryTest {

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

    private static Num test() {
        Num num = new Num();
        try {
            System.out.println("try");
            return num;
        } catch (Exception e) {
            System.out.println("error");
        } finally {
            if (num.num > 20) {
                System.out.println("num.num>20:" + num.num);
            }
            System.out.println("finally");
            num.num = 100;
        }
        return num;
    }
}

class Num {
    public int num = 10;
}

Output result:

try
finally
100

Process finished with exit code 0

analysis: If the return data is a reference data type, and the change of the attribute value of the reference data type in finally works, the return statement in the try returns the value of the attribute after the change in finally.

finalize

Finalize() is a method of the Object class. Java technology runs using the finalize() method to do the necessary cleanup work before the garbage collector clears the object from the memory. This method is called when the garbage collector determines that there is no reference to this object. The finalize() method is to override the finalize() method of the subclass called on the object before the garbage collector deletes the object to clean up system resources or perform other cleanup operations.

public class FinalizeTest {

    public static void main(String[] args) {
        Person p =  new Person("小马",55);
        p = null;//此时堆当中的Person对象就没有变量指向了,就变成了垃圾,等到垃圾回收机制调用的finalize()的时候会输出
        System.gc();
    }
}

class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("执行finalize()回收对象");
    }
}

image-20210722093628216

Summarize

The benefits of using final keywords:
  1. Final methods are faster than non-final.
  2. The final keyword improves performance. Both JVM and Java applications cache final variables.
  3. Final variables can be safely shared in a multi-threaded environment without additional synchronization overhead.
  4. Using the final keyword, JVM will optimize methods, variables and classes.

end

I am a code farmer who is being beaten and working hard to advance. If the article is helpful to you, remember to like and follow, thank you!


初念初恋
175 声望17 粉丝