• Java中的final关键字


    final关键字表示的不可变的。下面讨论final关键字使用的三种场合:数据、方法以及类。 

    final数据

    1、final属性

        程序中经常需要用到一些“常数”。常数主要应用于两个方面:

    • 编译期常数,永远不会改变
    • 在运行期初始化一个值,不希望它发生改变。

        对于编译期的常数,计算可以在编译期间提前执行,可以将常数值直接用于程序中。Java中,这种常数必须是基本数据类型。前置关键字final声明。定义时必须提供一个值

    class Person {
        final String name;  // name未初始化,编译出错
    }

        如果对对象句柄使用final,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象,而且不能将句柄指向另一个对象。

    class Person {
        String name = "张三";
    }
    
    public class FinalDemo {
    
        public static void main(String[] args) {
            final Person p = new Person();
            p = new Person();   // Error:无法为最终变量p分配值
        }
    }

    然而,对象本身是可以修改的。

    class Person {
        String name = "张三";
    }
    
    public class FinalDemo {
        public static void main(String[] args) {
            final Person p = new Person();
            p.name = "萧萧弈寒";
        }
    }

    一个可能的结果:

    name = fd1,i1 = 0, i2 = 6

    name = fd2,i1 = 8, i2 = 6

    i1,i2是在运行期间随机产生的数据。

    2、空白final

        Java1.1允许创建“空白final”,它们属于特殊字段。尽管被声明为final,但是却未得到一个初始值。即便如此,空白final还是必须在使用之前得到初始化。 示例:

    class Person {}
    
    public class FinalDemo {
        final int i;
        final Person p;
    
        FinalDemo() {
            i = 1;
            p = new Person();
        }
    
        FinalDemo(int x) {
            i = x;
            p = new Person();
        }
    
        public static void main(String[] args) {
            FinalDemo fd = new FinalDemo();
        }
    }

    现在强行要求对final进行赋值处理,要么在定义字段时使用一个表达式,要么在每个构建器中。

    3、用final修饰参数

        查了一些资料,很多人都说用final修饰方法参数是防止参数在调用时被修改。个人认为这种说法其实有两种理解:一种是变量的实际值不会被修改,另一种是在方法内部不能被修改。无论是基本参数类型还是引用类型,前一种说法都是错误的。因为Java是值传递

    public class FinalDemo {
    
        static void f(final int i) {
            i++;    // 无法为final变量赋值,编译错误
        }
    
        public static void main(String[] args) {
            int x = 10;
            f(x);   //
        }
    }

     ①处调用的f方法只是将x的值赋给了i,实际上i和x是两个变量。

    再看下面的例子:

    class Person {
        String name = "张三";
    }
    
    public class FinalDemo {
    
        public static void main(String[] args) {
            final Person p = new Person();
            changeName(p);
            System.out.println(p.name);
        }
    
        static void changeName(final Person p) {
            p.name = "萧萧弈寒";
        }
    }

    【运行结果】:

    萧萧弈寒

    由此说明,final并不能阻止changeName()方法改变p的内容。接下来,我们删除修饰参数的final,然后在changeName方法体内改变p指向的实例:

    class Person {
        String name = "张三";
    }
    
    public class FinalDemo {
    
        public static void main(String[] args) {
            final Person p = new Person();
            p.name = "萧萧弈寒";
            changeName(p);
            System.out.println(p.name);
        }
    
        static void changeName(Person p) {
            p = new Person();
        }
    }

    【运行结果】:
    changeName中的name:张三
    萧萧弈寒

    我们可以看出,虽然方法体内的p指向了其他对象,但是对于main方法中的p并没有影响。原因还是Java是值传递的。具体的请参考Java值传递还是引用传递?

    final方法

        final方法主要有两个方面的作用:一种是防止任何继承类覆盖方法。若希望一个方法的行为在继承期间保持不变,不可被覆盖和改写,就可以采取这种做法。另一种是提高程序执行的效率。将一个方法设成final后,编译器就会忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。它会用方法主体内实际代码的一个副本来替换方法调用。这样可以避免方法调用时的系统开销。若方法体太大,可能效率也得不到提升。

    class Human {
        public final void show() {
            //...
        }
    }
    public class Man extends Human{
        public void show() {}   //Cannot override the final method from Human
    }

        类内所有的private方法都自动成为final。由于不能访问一个private方法,所以它绝对不会被覆盖。

    final类

        如果整个类都是final,就表明这个类不允许被继承。或者出于安全方面的理由,不希望进行子类化。除此之外,或许还考虑执行效率的问题,确保涉及这个类各对象的所有行动都要尽可能地有效。

    final class Human {
    }
    
    public class Man extends Human{ // The type Man cannot subclass the final class Human
    }

    注意:数据成员既可以是final,也可以不是。无论类是否被定义成final,应用于final的规则同样适用于数据成员。

    将类定义成final后,结果只是禁止被继承。由于禁止了继承,所以一个final类中的所有方法都默认为final。 

  • 相关阅读:
    不可变类
    单例类
    二叉树的三种遍历
    先序创建二叉树
    【笔记】 mysql与php的连接以及非select的例子
    今日思考之 20200730:非阻塞(NIO)到底带来了什么改变?
    jdk源码学习之: Object#equals() 和 Object#hashCode()
    异想天开 之 快递行业与高并发、高吞吐
    分享系列 之 BIO NIO AIO
    挖坑:epoll 函数如何能准确知道哪些 FD 是活跃的呢?
  • 原文地址:https://www.cnblogs.com/xiaoxiaoyihan/p/4974273.html
Copyright © 2020-2023  润新知