final不可变性

news2024/11/26 20:22:31

一、什么是不可变性(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

谢谢关注!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/643470.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我的C++学习笔记

声明&#xff1a; 写本篇博客的目的是为了整理自己在找工作时学习的C相关知识点&#xff0c;博客整体内容会分为两种风格&#xff0c;第一章基础部分是以常见C面试问题解答的形式呈现&#xff1b;其余部分是知识点层层递进的方式展现&#xff0c;比较系统。其中&#xff0c;在第…

Avalon 学习系列(三)—— 数据和指令同步

Avalon 有很多个指令&#xff0c;通过这些指令可以对 DOM 进行一些事件操作、或者样式修改。 ms-duplex Avalon 实现数据与视图的同步的方式是用 ms-duplex 将元素跟数据绑定在一起&#xff0c;如果有其中一个的值改变另一个值也将改变。 ms-duplex 是 avalon 的双向绑定属性…

OpenCV(C++)创建图片绘制图形(矩形、圆、文字、线段等等)

一、OpenCV介绍 OpenCV 是基于开源许可证的跨平台计算机视觉库,提供了一组丰富、广泛的图像处理和计算机视觉算法。OpenCV 支持多种编程语言,包括 C++、Python、Java 等,可以运行在 Linux、Windows、Mac OS 等平台上。 OpenCV 能够在图像上绘制各种几何形状、文本和曲线,…

学习ESP32笔记

学习ESP32笔记 1.platform IO插件的下载&#xff08;提前安装好python&#xff0c;不然在中间的一部分会一直报错&#xff09; VS Code下载platform IO时&#xff0c;开加速器&#xff08;VPN&#xff09;&#xff0c;并且关闭防火墙 这一步比较慢&#xff0c;大概等十来分钟…

Kendo UI for jQuery---02.开始---01.使用 Kendo UI for jQuery 的第一步

使用 Kendo UI for jQuery 的第一步 欢迎来到 Kendo UI for jQuery 入门的第一步指南&#xff01; 本指南演示如何通过添加所需资源和初始化 Kendo UI 网格来开始使用套件。 该过程借鉴了以下里程碑&#xff1a; 1.下载控件 2.添加所需的 JavaScript 和 CSS 文件 3.将网格绑…

如何使用postman做接口测试

常用的接口测试工具主要有以下几种&#xff1a; Postman: 简单方便的接口调试工具&#xff0c;便于分享和协作。具有接口调试&#xff0c;接口集管理&#xff0c;环境配置&#xff0c;参数化&#xff0c;断言&#xff0c;批量执行&#xff0c;录制接口&#xff0c;Mock Server…

【每日挠头算法题(6)】二叉树的所有路径|神奇字符串

欢迎~ 一、二叉树的所有路径思路&#xff1a;深度优先搜索具体代码如下&#xff1a; 二、神奇字符串思路&#xff1a;模拟双指针具体代码如下&#xff1a; 总结 一、二叉树的所有路径 点我直达~ 思路&#xff1a;深度优先搜索 使用深度优先搜索&#xff1a;即二叉树的前序遍历…

设计模式(十二):结构型之享元模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

编码生成矩阵与检错监督矩阵

本专栏包含信息论与编码的核心知识&#xff0c;按知识点组织&#xff0c;可作为教学或学习的参考。markdown版本已归档至【Github仓库&#xff1a;https://github.com/timerring/information-theory 】或者公众号【AIShareLab】回复 信息论 获取。 文章目录 线性分组码基本概念…

Elasticsearch快速入门及使用

Elasticsearch快速入门及使用 一.Elasticsearch是什么二.基本概念1.index (索引)2. type (类型)3.Document (文档) 三.为什么Elasticsearch可以从海量数据里快速检索出数据四.Elasticsearch安装1.解压2.运行3.显示以下内容就是启动成功14.Kibana可视化软件安装 五.入门(基本的操…

如何编写有效的接口测试?

在所有的开发测试中&#xff0c;接口测试是必不可少的一项。有效且覆盖完整的接口测试&#xff0c;不仅能保障新功能的开发质量&#xff0c;还能让开发在修改功能逻辑的时候有回归的能力&#xff0c;同时也是能优雅地进行重构的前提。编写接口测试要遵守哪些原则&#xff1f;测…

unity Ignis - Interactive Fire(完美模拟:森林火灾、草原火灾、建筑火灾)

Ignis 可以把任何物体、植被或带皮带骨的网状物转换为可燃物体&#xff0c;它就会自动着火。然后&#xff0c;火焰可以蔓延&#xff0c;点燃其他物体&#xff0c;被粒子或光线熄灭&#xff0c;或者自然烧尽。也可以被粒子点燃。还会收到风力影响WindZone。 WindZone文档&#…

轻量级性能测试工具 wrk 如何使用?

项目设计之初或者是项目快要结束的时候&#xff0c;大佬就会问我们&#xff0c;这个服务性能测试的结果是什么&#xff0c;QPS 可以达到多少&#xff0c;RPS 又能达到多少&#xff1f;接口性能可以满足未来生产环境的实际情况吗&#xff1f;有没有自己测试过自己接口的吞吐量&a…

磁盘详解(一文搞懂磁盘)

目录 一.磁盘的结构 二.磁盘的分类 2.1按照磁头是否可以移动分类 2.2按照盘片是否可以更换分类 三.磁盘的读写过程 四.磁盘的调度 4.1FCFS先来先服务算法 4.2SSTF最短寻找时间优先 4.3 SACN扫描算法 4.4C-SACN循环扫描算法 4.5 SPTF&#xff08;最短定位时间优先&…

Maxwell安装使用

​欢迎光临我的博客查看最新文章: https://river106.cn 1、Maxwell简介 Maxwell 是由美国Zendesk开源&#xff0c;用Java编写的MySQL实时抓取软件。读取 MySQL binlogs 并将修改行字段的更新写入 Kafka, Kinesis, RabbitMQ, Google Cloud Pub/Sub 或 Redis (Pub/Sub or LPUSH)…

基于Aidlux的停车标志检测(可修改为coco 80类目标检测)

●项目名称 基于Aidlux的停车标志检测&#xff08;可修改为coco 80类目标检测&#xff09; ●项目简介 本项目在Aidlux上部署检测停车标志检测&#xff0c;并可在源码上修改coco 80类目标检测索引直接检测其他79类目标&#xff0c;可以直接修改、快速移植到自己的项目中。 ●…

【学习笔记】Java——消息队列kafka

kafka 1、Kafka combines three key capabilities&#xff1a;2、kafka是如何工作的&#xff1a;3、Kafka APIS&#xff1a;4、kafka集群 1、Kafka combines three key capabilities&#xff1a; To publish (write) and subscribe to (read) streams of events, including co…

中国游戏等“春”来

对于游戏行业来说&#xff0c;2023年将是压力依旧的一年&#xff0c;但或许也是转型调优的希望之年。 压力在于&#xff0c;互联网流量见顶&#xff0c;用户付费意愿降低&#xff0c;行业整体进入下行周期&#xff0c;彻底进入存量竞争时代。《2022年中国游戏产业报告》显示&a…

相机标定实战之双目标定

相机标定原理 文章目录 相机标定原理前言一、采集图像二、基于Matlab单双目标定流程采集棋盘图 三、基于OpenCV-Python双目标定流程检测棋盘格角点对角点进行亚像素精细化单目标定双目标定双目校正保存标定参数读取标定参数代码示例 参考 前言 相机标定可以说是计算机视觉/机器…

哪个公司的 CEO 不想拥有一个自己的数字克隆?

⚠️ FBI Warning&#xff1a;本文纯属作者自娱自乐&#xff0c;数字人的观点不代表 CEO 本人的观点&#xff0c;请大家不要上当受骗&#xff01;&#xff01; 哪个公司的 CEO 不想拥有一个自己的数字克隆&#xff1f; 想象&#x1f914;一下&#xff0c;如果 CEO 数字克隆上线…