目录
五、final原理
首页 Java java教程 Java中final实现原理的深入分析(附示例)

Java中final实现原理的深入分析(附示例)

Nov 27, 2018 pm 04:56 PM
final

本篇文章给大家带来的内容是关于Java中final实现原理的深入分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。

一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。

一、final变量

final成员变量表示常量,只能被赋值一次,赋值后值不再改变(final要求地址值不能改变)

当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。

final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。

二、final方法

使用final方法的原因有两个。

第一个原因是把方法锁定,以防任何继承类修改它的含义,不能被重写;

第二个原因是效率,final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。

(注:类的private方法会隐式地被指定为final方法)

三、final类

当用final修饰一个类时,表明这个类不能被继承。

final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

四、final使用总结

final关键字的好处:

(1)final关键字提高了性能。JVM和Java应用都会缓存final变量。

(2)final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。

(3)使用final关键字,JVM会对方法、变量及类进行优化。

关于final的重要知识点

1、final关键字可以用于成员变量、本地变量、方法以及类。

2、final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。

3、你不能够对final变量再次赋值。

4、本地变量必须在声明时赋值。

5、在匿名类中所有变量都必须是final变量。

6、final方法不能被重写。

7、final类不能被继承。

8、final关键字不同于finally关键字,后者用于异常处理。

9、final关键字容易与finalize()方法搞混,后者是在Object类中定义的方法,是在垃圾回收之前被JVM调用的方法。

10、接口中声明的所有变量本身是final的。

11、final和abstract这两个关键字是反相关的,final类就不可能是abstract的。

12、final方法在编译阶段绑定,称为静态绑定(static binding)。

13、没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。

14、将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。

15、按照Java代码惯例,final变量就是常量,而且通常常量名要大写。

16、对于集合对象声明为final指的是引用不能被更改,但是你可以向其中增加,删除或者改变内容。

五、final原理

最好先理解java内存模型 Java并发(二):Java内存模型

对于final域,编译器和处理器要遵守两个重排序规则:

1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

(先写入final变量,后调用该对象引用)

原因:编译器会在final域的写之后,插入一个StoreStore屏障

2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

(先读对象的引用,后读final变量)

编译器会在读final域操作的前面插入一个LoadLoad屏障 

示例1:

public class FinalExample {
    int i; // 普通变量
    final int j; // final 变量
    static FinalExample obj;
    public void FinalExample() { // 构造函数
        i = 1; // 写普通域
        j = 2; // 写 final 域
    }
    public static void writer() { // 写线程 A 执行
        obj = new FinalExample();
    }
    public static void reader() { // 读线程 B 执行
        FinalExample object = obj; // 读对象引用
        int a = object.i; // 读普通域         a=1或者a=0或者直接报错i没有初始化
        int b = object.j; // 读 final域      b=2
    }
}
登录后复制

第一种情况:写普通域的操作被编译器重排序到了构造函数之外

而写 final 域的操作,被写 final 域的重排序规则“限定”在了构造函数之内,读线程 B 正确的读取了 final 变量初始化之后的值。

写 final 域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的 final 域已经被正确初始化过了,而普通域不具有这个保障。

第二种情况:读对象的普通域的操作被处理器重排序到读对象引用之前

而读 final 域的重排序规则会把读对象 final 域的操作“限定”在读对象引用之后,此时该 final 域已经被 A 线程初始化过了,这是一个正确的读取操作。

读 final 域的重排序规则可以确保:在读一个对象的 final 域之前,一定会先读包含这个 final 域的对象的引用。

示例2:如果 final 域是引用类型

对于引用类型,写 final 域的重排序规则对编译器和处理器增加了如下约束:

在构造函数内对一个 final 引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

public class FinalReferenceExample {
    final int[] intArray; // final 是引用类型
    static FinalReferenceExample obj;
    public FinalReferenceExample() { // 构造函数
        intArray = new int[1]; // 1
        intArray[0] = 1; // 2
    }
    public static void writerOne() { // 写线程 A 执行
        obj = new FinalReferenceExample(); // 3
    }
    public static void writerTwo() { // 写线程 B 执行
        obj.intArray[0] = 2; // 4
    }
    public static void reader() { // 读线程 C 执行
        if (obj != null) { // 5
            int temp1 = obj.intArray[0]; // 6  temp1=1或者temp1=2,不可能等于0
        }
    }
}
登录后复制

 假设首先线程 A 执行 writerOne() 方法,执行完后线程 B 执行 writerTwo() 方法,执行完后线程 C 执行 reader () 方法。

 

在上图中,1 是对 final 域的写入,2 是对这个 final 域引用的对象的成员域的写入,3 是把被构造的对象的引用赋值给某个引用变量。这里除了前面提到的 1 不能和 3 重排序外,2 和 3 也不能重排序。

JMM 可以确保读线程 C 至少能看到写线程 A 在构造函数中对 final 引用对象的成员域的写入。即 C 至少能看到数组下标 0 的值为 1。而写线程 B 对数组元素的写入,读线程 C 可能看的到,也可能看不到。JMM 不保证线程 B 的写入对读线程 C 可见,因为写线程 B 和读线程 C 之间存在数据竞争,此时的执行结果不可预知。

以上是Java中final实现原理的深入分析(附示例)的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

在Java中,仅使用final关键字可以定义一个常量吗? 在Java中,仅使用final关键字可以定义一个常量吗? Sep 20, 2023 pm 04:17 PM

常量变量是其值固定且程序中只存在一个副本的变量。一旦你声明了一个常量变量并给它赋值,你就不能在整个程序中再次改变它的值。与其他语言不同,Java不直接支持常量。但是,你仍然可以通过声明一个变量为静态和final来创建一个常量。静态-一旦你声明了一个静态变量,它们将在编译时加载到内存中,即只有一个副本可用。Final-一旦你声明了一个final变量,就不能再修改它的值。因此,你可以通过将实例变量声明为静态和final来在Java中创建一个常量。示例 演示classData{&am

Java中final、finally、finalize的区别 Java中final、finally、finalize的区别 Feb 19, 2024 pm 12:16 PM

Java中final、finally、finalize的区别,需要具体代码示例在Java编程中,经常会遇到final、finally、finalize这三个关键词,它们虽然拼写相似,但却有不同的含义和用法。本文将详细解释这三个关键词的区别,同时给出代码示例以帮助读者更好地理解。一、final关键字final关键字可以用于类、方法和变量。它的作用是使被修饰的类

java final关键字的作用是什么 java final关键字的作用是什么 Nov 25, 2022 pm 04:26 PM

在java中,final可以用来修饰类、方法和变量。final修饰类,表示该类是无法被任何其他类继承的,意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。final修饰类中的方法,表示该类是无法被任何其他类继承的,不可以被重写;也就是把该方法锁定了,以防止继承类对其进行更改。final修饰类中的变量,表示该变量一旦被初始化便不可改变。

Java中final对象的创建方式是什么? Java中final对象的创建方式是什么? Apr 11, 2024 pm 02:00 PM

Java中创建final对象有两种方法:声明final变量或使用final修饰符声明类。声明final变量时,对象通过初始化器创建;声明final类时,该类实例不可变。重要的是,final对象的引用仍然可以改变,但它们指向的对象不可变。

Java错误:滥用final关键字,如何解决和避免 Java错误:滥用final关键字,如何解决和避免 Jun 24, 2023 pm 09:00 PM

Java作为一种十分流行的编程语言,运用广泛,被广泛应用于互联网、移动设备等领域。对于Java开发者来说,常常会遇到一些错误或问题,其中之一便是滥用final关键字。在Java中,final关键字经常被用来修饰变量、方法、类等,它表示该属性在定义之后不可再改变。final关键字可以帮助开发者保证对象的不可变性、避免出现竞态条件等问题,但是滥用final关键字

PHP中的特殊语法:Static、Final、Abstract等关键字 PHP中的特殊语法:Static、Final、Abstract等关键字 May 11, 2023 pm 04:00 PM

PHP是一种流行的开源服务器端脚本语言,广泛应用于Web开发。PHP语言不仅易于学习和使用,而且支持多种编程范例、面向对象的编程和函数式编程等。在PHP中,有一些特殊的语法关键字,如Static、Final、Abstract等,这些关键字在面向对象编程中具有特殊的作用。本文将对这些关键字进行详细介绍。Static关键字在PHP中,Static关键字有两种用法

为什么在Java中构造函数不能是final的? 为什么在Java中构造函数不能是final的? Aug 20, 2023 pm 07:01 PM

每当你将一个方法声明为final时,你就不能覆盖它。也就是说,你不能为子类提供对超类的final方法的实现。也就是说,将一个方法声明为final的目的是防止从外部(子类)修改该方法。在继承中,当你扩展一个类时,子类会继承超类的所有成员,除了构造函数。换句话说,构造函数不能在Java中被继承,因此你不能覆盖构造函数。因此,在构造函数前面加上final没有意义。因此,Java不允许在构造函数前使用final关键字。如果你尝试将构造函数声明为final,将会产生一个编译时错误,提示“modifierf

Java中的Final与不可变性比较 Java中的Final与不可变性比较 Aug 26, 2023 pm 09:37 PM

Java中的“final”关键字可用于定义常量值以及防止变量、方法,或类被更改或覆盖。另一方面,不变性描述了对象在其存在过程中保持恒定状态的特征。对象形成后,其值就不会改变。变量、方法和类受到“final”关键字的限制,但不变性更进一步,保证了对象的整个状态得到保留。让我们在本文中了解最终与不变性之间的主要区别。Java最终版Java中的final关键字有几个特点:最终变量:初始化后其初始值无法修改。它们经常被用来声明不可更改或不变的值。最终方法:它们不能被子类修改,保证它们的行为一致。它们有助

See all articles