首页 > 其他分享 >final&不可变性

final&不可变性

时间:2023-06-13 22:24:46浏览次数:42  
标签:变量 final 线程 修饰 可变性 public 赋值

一、什么是不可变性(Immutable)

  • 如果对象在被创建后,状态就不能被修改,那么它就是不可变的
  • 这个对象不能被修改指:
    • 对象指向(引用)不可变
    • 字段不可变
    • 成员变量不可变

案列演示: person对象,age和name属性都不能再变

/**
 *      不可变的对象,演示其他类无法修改这个对象
 *      public也不行
 */
public class Person {
    final String name = "李白";
    final int age = 18;
}

测试修改被final修饰的类属性,发现编译都过不了

如果这个类,有一个可修改的成员变量,他不被final修饰,那么这个类就不具备不可变性,可以修改:

具有不可变性的对象,一定线程安全!!!我们不需要采取任何额外的安全措施,也可以保证线程安全

二、final的作用

1、早期

早起的Java版本中,会将final修饰的方法转为内嵌调用

内嵌调用:一个方法调用另外一个final方法,那么就把这个final方法里面的东西全部都挪过来。相当于只在同一个方法里调用完成整个工作。不是方法之间调来调去,减少性能损耗。

2、现在

  • 修饰类:防止被继承
  • 修饰方法:防止被重写
  • 修饰变量:防止被修改

final 天生 线程安全 ,不需要额外的同步开销。

三、final的3种用法

1、修饰变量

(1)含义

被final修饰的变量,意味着值不能被修改。如果修饰的是对象,那么对象的引用不能变,但是引用的那个对象自身的属性依然可以变化。比如下面这样改变对象的引用是不可以的:

        final Person p = new Person();
        p = new Person(); // 重新赋值一个别的对象是不允许的

但是修改对象的属性是可以的,如下:

        final Person p = new Person();
        p.score = 100; // 对象的属性是可以修改的

(2)3种变量的赋值时机

① final instance variable(类中的final属性)

属性被声明为final后,改变量则只能被赋值一次。且一旦被赋值,final的变量就不能在改变,无论如何也不会变。

类中的final属性允许在下面三种时机进行赋值:

1、声明变量时在等号右边直接赋值

public class FinalVariableDemo {
    private final int a = 10;
}

2、构造函数中赋值

public class FinalVariableDemo {
    private final int a;

    public FinalVariableDemo(int a) {
        this.a = a;
    }
}

3、类的初始化代码块中赋值(不常用)

public class FinalVariableDemo {
    private final int a;

    {
        a = 4; // 初始化代码块中赋值
    }
}

对于类中的final属性,必须在上面三种赋值方式中选择一种,否则就会提示错误:

image-20230612153742805

② final static variable(类中的static final属性)

类中的static final属性的赋值时机有两种,如下:

除了在声明变量的等号右边直接赋值外,static final变量还可以用static初始代码块赋值,但是不能用普通的初始化代码块赋值

1、声明变量时在等号右边直接赋值

public class FinalVariableDemo {
    private static final int a = 1;
}

2、static初始代码块赋值

public class FinalVariableDemo {
    private static final int a;
    static {
        a = 1;
    }
}
③ final local variable(方法中的final变量)

方法中的final变量的赋值时机和前面两种不同,由于这里的变量是在方法里的;所以没有构造函数,也不存在初始代码块。它不规定赋值时机,只要求在使用前必须赋值,这一点和方法中的其他非final变量的要求也是一样的。

(3)为什么要规定赋值时机?

如果在final变量在初始化时不赋值,而像普通变量一样自动会有一个默认值,那么以后再想赋值时,就意味着该变量从默认值变成你的赋值,这就意味着final变量的值修改了,违反了final不变性的原则了

2、修饰方法

(1)构造方法不允许final修饰

(2)不可被重写,也就是不能被override

即便是子类有同样名字的方法,那也不是重写的父类方法,这个和static不能被重写是一个道理

3、修饰类

修饰类时,表示类不可被继承

  • String就是被final修饰

四、final的注意点

  • final修饰对象的时候,只是对象的引用不可变,而对象本身的属性是可以变化的
  • 可以养成一个良好的编程习惯:对于要求不能变的变量或者属性,尽量使用final修饰,同时也能向其他开发者暗示该变量不能变。

五、不变性和final的关系

不变形并意味着,简单地用final修饰就是不可变

  • 对于基本数据类型,确实被final修饰后就具备不可变性
  • 但是对于对象类型,需要该对象保证自身被创建后,状态永远不会变才可以
    • 满足以下条件时,对象类型才满足不可变性
      • 对象创建后,其状态就不能修改
      • 所有属性都是final修饰的
      • 对象创建过程中没有发生逸出

如下所示,该类的对象创建之后,对象整体都是不可变的:

/**
 * 描述:     一个属性students是对象,但是整体不可变,其他类无法修改set里面的数据
 *            只在初始化过程中赋值,只提供读取的方法isStudent(String name)给外部,其他类没有修改的机会
 */
public class ImmutableDemo {

    private final Set<String> students = new HashSet<>();

    public ImmutableDemo() {
        students.add("李白");
        students.add("杜甫");
        students.add("白居易");
    }

    public boolean isStudent(String name) {
        return students.contains(name);
    }
}

六、栈封闭技术

在方法里新建的局部变量,实际上是存储在每个线程私有的栈空间,而每个栈空间是不能被其他线程所访问到的,所以不会有线程安全问题。这就是著名的“栈封闭”技术,是“线程封闭”技术的一种情况。

换句话说,方法内的变量,多个线程之间不共享,不会被其他线程所访问到

/**
 *      演示栈封闭的两种情况,基本变量 和 对象
 *      先演示线程争抢带来错误结果,然后把变量放到方法内,情况就变了
 */
public class StackConfinement implements Runnable {
    //类共享变量,被两个线程共享
    int index = 0;

    public void inThread(){
        //方法内局部变量,多个线程之间不共享
        int neverGoOut = 0;
        for (int i = 0; i < 10000; i++) {
            neverGoOut++;
        }
        System.out.println("栈内保护的数字是线程安全的: "+neverGoOut);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            index++;
        }
        inThread();
    }

    public static void main(String[] args) throws InterruptedException {
        StackConfinement r = new StackConfinement();

        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println(r.index);
    }
}

点我扫码关注微信公众号

文章来源:final&不可变性


个人微信:CaiBaoDeCai

微信公众号名称:Java知者

微信公众号 ID: JavaZhiZhe

谢谢关注!

标签:变量,final,线程,修饰,可变性,public,赋值
From: https://www.cnblogs.com/javazhizhe/p/17478844.html

相关文章

  • java关键字native、static、final详解
    native: native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。JNI是Java本机接口(JavaNativeInterface),是一个本......
  • 【Clickhouse】ReplaceingMergeTree引擎final实现合并去重探索
    前言在OLAP实践中,在有数据更新的场景中,比如存储订单数据,我们经常会用到ReplaceingMergeTree引擎来去重数据,以获取数据的最新状态。但是ReplaceingMergeTree引擎实现数据的去重合并的操作是异步的,这样在实际查询的时候,其实是仍然有一部分数据是未进行合并的。为了保证统计数据的准......
  • 8.8 final 关键词
    final定义不能被继承的类;不能被覆写的方法,常量最重要作用,定义全局常量publicclassHelloWorld{publicstaticfinalStringINFO="mldn";//定义全局常量;publicstaticvoidmain(Stringargs[]){StringstrA="www.mldn.cn";Stringstr......
  • VMware ESXi 6.7 U3 Final Unlocker & OEM BIOS 集成 REALTEK 网卡驱动和 NVMe 驱动 (
    VMwareESXi6.7U3Final最终版,集成驱动版。此版本解决的问题:VMwareHostClient无法将现有虚拟磁盘(VMDK)附加到虚拟机请访问原文链接:https://sysin.org/blog/vmware-esxi-6-sysin/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org2023-02-28,发布一个UIfix版......
  • Mac视频剪辑软件-Final Cut Pro v10.6.6中文版
    随着视频内容的不断发展和普及,越来越多的人开始将视频制作作为一种创作方式和表达形式。而想要制作高质量的视频,需要用到专业的视频编辑软件。其中,FinalCutPro是一款非常受欢迎的Mac上的视频剪辑软件,它具有丰富的功能和强大的性能,可以帮助用户轻松地完成复杂的视频制作任务。→......
  • 释放资源的方案一:try-catch-finally
          ......
  • 关于final关键字的几点疑惑和答案
    问题:被final修饰的变量在内存中的位置?问题:为什么被final修饰的变量必须进行立即初始化或者构造方法初始化?问题:为什么局部变量必须要进行显示初始化?问题:为什么方法内部类访问方法的局部变量时必须加final修饰符?final从字面上理解含义为“最后的,最终的”。在Java中也同样表示......
  • [Java] try catch finally注意点
    trycatchfinaly注意点finaly块中有return语句publicstaticvoidmain(String[]args){System.out.println(throwException());}publicstaticintthrowException(){try{inti=1/0;}catch(Exceptione){e.printStackTrace();......
  • final修饰符的作用
    final修饰变量final修饰变量的时候,表示该变量的值不可改变,成为常量。例如,圆类包含PI(圆周率)属性且此属性的值在任何一个实例中都不会变化将PI定义为常量更符合程序设计要求final数据类型变量名=值;解析:final这个单词翻译过来是最终的意思,Final修饰变量的时候,表示该变量的值不可改......
  • final
         ......