如何优雅的创建一个Java不可变对象类,JDK源码中也是这么干的!

如何在Java中创建不可变对象? 在Java中创建不可变对象是一个常见的编程实践,它有助于提高代码的可读性、可维护性和安全性。以下我将从技术难点、面试官关注点、回答吸引力和代码举例四个方面来详细阐述如何在Java中创建不可变对象。 阅读详情

前面有篇文章当介绍了Java的不可变对象的一些特性,以及它的一些好处,但是并没有介绍如何实现一个不可变对象类。今天就来看看如何实现一个不可变对象类。

Java中常用的不可变对象类

String类应该是我们最常使用的不可变对象类。其实除了String以外,在JDK中还定义了很多不可变对象类,比如:基础类型的封装类(Integer、Long等)、时间相关的类(LocalDate、LocalTime等)、不可修改集合(UnmodifiableMap、UnmodifiableList)。

如果我们想要实现一个自定义的不可变对象类,那么这些JDK提供的不可变对象类就是我们的基石,自定义的类需要基于这些基础类来实现。

首先我们需要分析一下JDK中的不可变对象类的特点,然后才好实现我们自己的不可变对象类。

  • 所有的class都被final修饰,防止被继承。
  • 所有的字段都被final修饰,防止被内部逻辑修改,或者被直接反射修改(String有一点点特别,不过不关键)。
  • 所有的字段都没有setter方法,任何涉及到对当前对象进行修改的操作都会重新创建一个新对象,原来的对象不会进行修改(用过String相关操作的朋友肯定很清楚)。
  • 字段的getter根据实际情况会选择直接返回字段的值或者拷贝一份进行返回(JDK中基本上都是直接返回字段值的)。
    上面提到的前3点基本上都没有任何疑问了,都是用来防止类字段被外部修改的。第四点提到了有时候可以直接返回字段的值,而有时候就只能拷贝一份数据进行返回就比较难以理解了。那么什么时候可以直接返回,什么时候需要进行拷贝以后才能返回呢?看完这篇文章基本就能弄明白了。

定义一个简单的不可变对象类

在这里插入图片描述
上面这个就是一个非常简单的不可变对象类。类被final修饰,字段被final修饰,没有setter方法,getter直接返回字段的值。它完全满足上面提到的不可变对象类的设计原则,也确实是一个不可变的对象类。

再加一点料

这种简单场景显然不能满足我们探索的欲望,接下来我们分析一个稍微复杂一点的场景。我们给Student类增加一个字段Age,Age的定义如下:
在这里插入图片描述

按照上面的不可变对象设计原则,我们可以把Student类就变成如下所示的了:
在这里插入图片描述

那么新的Student类是一个不可变对象类吗?它并不是!下面的代码就说明了这一点:
在这里插入图片描述
最后你会发现张三的年龄被修改了,这显然不是我们想要的。所以我们需要对Student类进行改造,改造后的Student类如下:
在这里插入图片描述
新的Student类显然不会出现上面的问题了,那么它已经是一个不可变对象类了吗?仍旧不是!下面的代码说明了这一点:
在这里插入图片描述

运行完这段代码后,你会发现张三的年龄还是被修改了,这显然也不是我们想要的效果。所以我们需要进一步的完善:
在这里插入图片描述
终于Student类切断了age字段与外部的联系了,马马虎虎算一个不可变对象类了(在万能的反射面前什么都不是)。

经过几次修改以后,终于出现了上面我们提到的getter方法需要进行拷贝后才能返回的情形了。当字段的类型(Age)不是不可变对象类的时候getter方法是不能直接返回字段值的,需要进行拷贝后返回。由于字段Age是可变的,所以在一般的反射面前是毫无招架之力的,同时由于每次获取获取的时候都会创建新对象,在高并发的时候内存GC也会成为问题(肯定有人和你说过不要在for循环中直接拼接String,而要用StringBuilder这种类来完成—不过String拼接现在已经会被编译器优化了,可以放心大胆的拼了)。

所以:不可变对象类的字段尽量不要是可变对象!

真正的不可变对象

上面实现了一个勉强算是不可变对象类,这显然还是无法满足我们探索的欲望的,我们希望的是实现一个真正的不可变对象类,想JDK里的类那么的严谨。所以我们需要进一步的优化:
在这里插入图片描述

这样我们的Student类就是一个完善的不可变对象类了,普通的反射是无法改变其不可变性,只有变态的二次反射(Field也是一个对象,它的final修饰可以通过反射干掉)才能修改它的值了。

需要使用到List、Map等集合时如何实现不可变?

上面实现的不可变对象类的字段都是普通的对象,那么如果一个不可变对象字段是List或者是Map时该如何处理呢?这种情况其实很好解决,JDK实际上是为我们提供了很多Unmodifiable开头的集合类的(jdk1.2的时候就提供了,不过八成很多人都不知道),顾名思义就是不能被修改的集合,当发生add、insert、remove的时候会直接抛出异常!这些类基本上都定义在Collections类中:
在这里插入图片描述
然而这些类在使用的时候是有一些坑的(具体什么坑?感兴趣的可以分析朋友可以分析它们的源码看看)。Google提供的Guava工具包为我们提供了更加好用的不可变集合类,我们可以选择使用它们,感兴趣的朋友可以去看看Guava的相关文档。
在这里插入图片描述

JDK中典型的不可变对象实现

LocalDateTime、LocalDate、LocateTime(它是Java8才有的类哦)是一组和上面示例很像的不可变对象类,他们的主要实现代码(已经删除了各种注释和无关的方法):

LocalDate类:
在这里插入图片描述
LocalTime类:
在这里插入图片描述
LocalDateTime类:
在这里插入图片描述

扫码关注
头条号:  微信公众号:

获取车辆VIN等OBD信息 获取车辆如下OBD信息: VIN:Vehicle Identification Number CALID:Calibration Identifications CVN:Calibration verification number 一、SAE J1939协议 通过请求 CALIBRATION INFORMATION (DM19),获取CVN和CALID。 发送请求:18EA00F9 00 D3 00 应答:18EC0000 10 14 00 03 FF 00 D3 00 数据,BYTE1=0x10,表明 阅读详情

相关推荐

基于STM32F407ZGT6与INMP441的I2S音频采集系统:从配置到实时波形显示

本文详细介绍了基于STM32F407ZGT6与INMP441麦克风模块的I2S音频采集系统设计与实现。从硬件选型、CubeMX配置到音频数据处理和上位机波形显示,全面解析了系统架构、DMA传输优化及常见问题解决方案,帮助开发者快速构建高精度音频采集系统。

weixin_33743661的博客 403

Java中怎么创建一个不可变对象?

不可变对象是指一旦创建后,其状态(即对象的数据)就不能被修改或改变的对象。这种对象具有一些重要的特性,如线程安全性、安全性、可重用性等。

Dingdangr的博客 885

Vivado实战:UltraScale/UltraScale+器件eFUSE寄存器配置与安全编程全解析

本文详细解析了Xilinx UltraScale/UltraScale+器件中eFUSE寄存器的配置与安全编程方法。从芯片唯一身份标识到加密密钥烧录,再到用户寄存器的灵活应用和安全控制寄存器的配置技巧,全面介绍了如何利用eFUSE功能提升硬件安全性。文章还提供了JTAG接口安全管理和NKZ文件备份方案,帮助开发者避免常见陷阱,确保安全编程的可靠性。

weixin_42535703的博客 429

什么是不可变对象(immutable object)?Java 中怎么创建一 个不可变对象?

不可变对象(Immutable Object)是指一旦创建后,其状态(即对象的数据)不能被修改或改变的对象。在 Java 中,创建不可变对象有几个关键点和方法。

weixin_45990682的博客 576

LayaNative源码编译

修改好后的LayaNative2.0需要重新编译生成Android和iOS所需要的动态库文件。

末之 1359

Linux 隐藏进程

这种方式属于DKoM(Direct Kernel Object Manipulation)攻击方式了,即直接内核对象操作技术。关于第一种hook方式可以参考上一篇文章。最后推荐一个rootkit工具。

chi's blog 2051

在Java中怎么创建不可变对象

在Java中,创建不可变对象是一种良好的编程实践,有助于避免数据不一致性,提高线程安全性,并允许缓存对象的哈希码等。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 7300

Chkdsk误删文件?5步教你从FOUND.000文件夹找回重要数据(附工具推荐)

本文详细介绍了当Chkdsk误删文件后,如何从FOUND.000文件夹中找回重要数据的5个步骤。包括理解Chkdsk与FOUND.000的关系、定位与访问FOUND.000文件夹、手动恢复CHK文件的技巧、使用UnCHK工具自动化恢复以及预防数据丢失的关键措施。适用于Windows用户解决数据丢失问题,特别是NTFS文件系统下的文件恢复。

weixin_30687051的博客 391

java如何创建不可变对象,如何在Java中创建具有可变对象引用的不可变类?

不可变的对象是那些状态一旦初始化就不能更改的对象。有时有必要根据要求创建一个不可变的类。例如,所有原始包装器类(Integer,Byte,Long,Float,Double,Character,Boolean和Short)在Java中都是不可变的。字符串类也是一个不变的类。要创建自定义不可变类,我们必须执行以下步骤将课程声明为最终课程,因此无法扩展。将所有字段设为私有,以便不允许直接访问。不要为变...

weixin_35784728的博客 443

BMP390L数字大气压传感器原理图设计,已量产(压力传感器)

在工业设备的室内定位、地下设施监测这类 GPS 信号盲区场景里,气压高度检测的 “靠谱程度” 直接决定了系统的可用性 —— 毕竟谁也不想在地下停车场导航时,设备把 1 楼误判成 3 楼。最近在项目里基于博世 BMP390L 做了硬件电路设计,正好聊聊这款工业级数字气压传感器的硬实力,以及原理图里的那些关键设计细节。

不脱发的程序猿 352

SAP ERP 系统订单行项目中的计划行(Scheduled Line)详解

*计划行(Scheduled Line)**是SAP系统中订单行项目(如采购订单、生产订单、销售订单等)的细分单元,用于记录和管理物料的供应或需求计划。每个订单行项目可以包含一个或多个计划行,每个计划行对应具体的交货日期、数量、需求类型(如采购申请、生产订单、库存调拨等)以及相关的物流控制参数。简单来说,计划行是系统对某一物料在特定时间点的供需状态的详细规划,它将订单分解为更细粒度的执行步骤,确保供应链各环节(如采购、生产、仓储)能够按计划协同工作。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 3391

如何阅读Java源码?

阅读Java源码的前提条件:1、技术基础在阅读源码之前,我们要有一定程度的技术基础的支持。假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃《Core Ja...

Java团长 2万+

使用R语言绘制Logistic回归模型的亚组分析森林图

亚组分析是一种常用的统计方法,用于探索不同亚组之间的差异,并确定是否存在某些变量对结果的调节作用。在本文中,我们将使用R语言来绘制Logistic回归模型的亚组分析森林图。在该图中,每个亚组都用一个点表示,点的位置表示亚组的均值,而线段表示亚组的置信区间。垂直参考线用于表示效应值为0的点,帮助我们判断亚组之间的差异。希望本文对你有所帮助,让你能够使用R语言绘制Logistic回归模型的亚组分析森林图。是一个二分类的变量,我们希望在亚组分析中探索它与响应变量之间的关系。函数来计算每个亚组的均值,并使用。

PixelNinja的博客 2002

如何优雅地打印Java数组?

在逛 Stack Overflow 的时候,发现了一些访问量像‎安第斯山一样高的问题,比如说这个:打印 Java 数组最优雅的方式是什么?访问量足足有 220W+,想不到啊,这么简单的问题竟然有这么多程序员被困扰过。 来回顾一下提问者的问题吧: 在 Java 中,数组虽然是一个对象,但并未明确的定义这样一个类,因此也就没有覆盖 toString() 方法的机会。如果尝试直接打印数组的话,输出的结...

沉默王二 2万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

JDK8新特性-Optional类

JDK8新特性-Optional类

Yyyy的博客 1736

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

jdk源码解析书籍推荐_Java源码阅读的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子。回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动。源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心。说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃《Core Java》,你是很难从中吸收到营养的,特别是《深入Java虚拟机》这类书,别人觉得好,未必适合现在的你。虽...

weixin_39976153的博客 1578

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

我去!这也太牛了,滴滴大牛都叹服的Java并发原理,深入剖析JDK源码!

并发编程 并发编程这四个字想必大家最近都在网上看到过有很多的帖子在讨论。我们都知道并发编程可选择的方式有多进程、多线程和多协程。在Java中,并发就是多线程模式。而多线程编程也一直是一个被广泛而深入讨论的领域。如果遇到复杂的多线程编程场景,大多数情况下我们就需要站在巨人的肩膀上利用并发编程框架——JDK Concurrent包来解决相关线程问题。 Concurrent包很复杂吗? 相信很多人对于Concurrent并发包都是一知半解,更别说Concurrent包源码了。(大牛另当别论)可以说要是C

如果频繁就私信一下呢 569

头一次见有人把JDK实现与源码解读的这么透彻_jdk是如何实现的

并发除了要考虑静态资源、死锁、资源公平性等问题,往往还需要考虑性能问题,在一些业务场景往往还会比较复杂,这些都给java coder们造成了不小的难题。如果你对concurrent包有一定理解就能解决了不少问题。需要有朋友需要Java并发JDK源码剖析只需要点击文末小卡片即可!Java学习包传送门。

weixin_42924914的博客 791

高效阅读JDK源码,保姆级JDK源码笔记真香

作为一名java的开发者,需要对JDK源码有一定的了解和学习,同时也是个人能力的体现,也是在面试中给自己的加分项目。因此个人总结学习JDK源码的相关知识的方法和JDK源码服务图。JDK 中的代码非常多,作为是初学者需要阅读一下基本的源码,参考互联网上各种意见,个人认为阅读JDK源码需要具备一下几点:熟悉了解JDK的使用 熟悉openJDK的编译 了解常见软件设计模式一,JDK源码是其它所有源码的基础,看懂了JDK源码再看其它的源码会达到事半功倍的效果。二,JDK源码中包含大量的数据结构知识,是学习数据结构很

老男孩的架构路 1340

从抽象工厂模式到JDK源码:设计模式让代码更优雅

定义创建产品的接口。

南 阳 的博客 1509

如何阅读Java源码

阅读Java源码的前提条件: 1、技术基础 在阅读源码之前,我们要有一定程度的技术基础的支持。 假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃《Core Java》,那样是很难有收获的,尤其是《深入Java虚拟机》这类书,或许别人觉得好,但是未必适合现在的你。 比如设计模式,许多Java源码当中都会涉及到。再比如阅读Spring源码的时候,势必要先对IOC,AOP,Jav...

Steven's blog 2233

JDK源码剖析!Java程序员修炼内功必备

如果你想快速理解JDK源码,最快捷的方式是阅读别人写好的讲解文章,站在巨人的肩膀上,你会看的更高更远。JDK 经过了这么多年的迭代,代码精简,设计模式穿插,运行效率也优化到了极致,是很多Java大牛积累的心血,所以如果你想提高代码能力,JDK 源码是你首要的学习方式。第3章 Lock与Condition。第6章 线程池与Future。

2401_89791076的博客 406

饿了么团队整理的【JDK源码】笔记火了,开放下载

对于人脑的认知来说,“代码一行行串行”当然最容易理解。但在多线程下,多个线程的代码交叉并行,要访问互斥资源,要互相通信。作为开发者,需要仔细设计线程之间的互斥与同步,稍不留心,就会写出非线程安全的代码。正因此,多线程编程一直是一个被广泛而深入讨论的领域! 今天就来分享一份饿了么架构师纯手打的Java并发实现原理:JDK源码剖析,由于这份笔记的内容过多,小编没办法全部为大家展示出来,有不尽完美之处,还望大家多多海涵,同时小编已经整理成PDF蓝光版,JDK源码剖析PDF蓝光版100%免费领取方式:转发这篇文

Java0258的博客 227

第一次见有人把JDK实现与源码解读的这么透彻

相信很多人对于Concurrent并发包都是一知半解,更别说Concurrent包源码了。(大牛另当别论)可以说要是Concurrent包与其源码有一定的了解的话是完全可以避免重复造轮子,也能避免因为使用不当而掉到“坑”里,更不会说停留于一个“似是而非”的阶段。

weixin_50205273的博客 773

java 优秀源码_是时候该开始读一读JDK源码了

为什么要读JDK源码当然不是为了装,毕竟谁没事找事虐自己 ...1、 面试跑不掉 。现在只要面试Java相关的岗位,肯定或多或少会会涉及JDK源码相关的问题。2、 弄懂原理才不慌 。我们作为JDK的使用者,虽然说天天用得很开心,但是有时候遇到问题还是得跟到底层源码去看看,才能帮助我们更好的弄懂原理,3、 学习优秀的代码、思想和模式 。JDK毕竟是一个优秀的代码库,我们天天用,源码也就在里面,作为一...

weixin_34596012的博客 245
上一篇: 一张图描述Spring AOP的全过程
下一篇: LeetCode算法题:007- 整数反转
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值