如果对象在构造后无法更改,则该对象是不可变的。不可变对象不会以任何方式暴露其他对象来修改其状态; 对象的字段仅在构造函数内初始化一次,并且永远不会再次更改。

在本文中,我们将定义在Java中创建不可变类的典型步骤,并阐明开发人员在创建不可变类时所犯的常见错误。

1.不可变类的用法

如今,每个软件应用程序的“必备”规范都是分布式的,而多线程多线程应用程序总是会给开发人员带来麻烦,因为开发人员需要保护其对象的状态,使其免受多个线程的并发修改。同时,为此,开发人员通常在修改对象状态时使用Synchronized块。

对于不可变类,状态永远不会被修改; 状态的每次修改都会产生一个新实例,因此每个线程都会使用不同的实例,开发人员不会担心并发修改。

2.一些流行的不可变类

String是Java中最流行的不可变类。初始化后,其值无法修改。像 trim(),substring(),replace()这样的操作总是返回一个新实例而不影响当前实例,这就是我们通常调用 trim()的原因如下:

String alex = "Alex";

alex = alex.trim();

JDK的另一个例子是包装类,如:Integer,Float,Boolean ......这些类不会修改它们的状态,但是每次尝试修改它们时它们都会创建一个新实例。

Integer a =3;

a += 3;

在调用+ = 3之后,创建一个新实例,其值为:6,第一个实例丢失。

3.我们如何创建一个不可变类

要创建不可变类,您应该按照以下步骤操作:让你的课成为最终,这样其他课程就无法扩展它。

使所有字段成为最终字段,以便它们仅在构造函数中初始化一次,之后从不修改。

不要暴露setter方法。

在公开修改类状态的方法时,必须始终返回该类的新实例。

如果该类包含一个可变对象:在构造函数中,确保使用传递参数的克隆副本,并且永远不要将可变字段设置为通过构造函数传递的实例,这是为了防止传递对象的客户端在之后修改它。

确保始终返回该字段的克隆副本,并且永远不会返回真实对象实例。

3.1。简单的不可变类

让我们按照上面的步骤创建我们自己的不可变类(ImmutableStudent.java)。

package com.programmer.gate.beans;

public final class ImmutableStudent {

private final int id;

private final String name;

public ImmutableStudent(int id, String name) {

this.name = name;

this.id = id;

}

public int getId() {

return id;

}

public String getName() {

return name;

}

}

上面的类是一个非常简单的不可变类,它不包含任何可变对象,也不会以任何方式暴露其字段; 这些类通常用于缓存目的。

3.2。将可变对象传递给不可变类

现在,让我们的示例复杂一点,我们创建一个名为Age的可变类,并将其作为字段添加到ImmutableStudent:

package com.programmer.gate.beans;

public class Age {

private int day;

private int month;

private int year;

public int getDay() {

return day;

}

public void setDay(int day) {

this.day = day;

}

public int getMonth() {

return month;

}

public void setMonth(int month) {

this.month = month;

}

public int getYear() {

return year;

}

public void setYear(int year) {

this.year = year;

}

}

package com.programmer.gate.beans;

public final class ImmutableStudent {

private final int id;

private final String name;

private final Age age;

public ImmutableStudent(int id, String name, Age age) {

this.name = name;

this.id = id;

this.age = age;

}

public int getId() {

return id;

}

public String getName() {

return name;

}

public Age getAge() {

return age;

}

}

因此,我们在不可变类中添加了一个新的Age类型的可变字段,并在构造函数中将其分配为正常。

让我们创建一个简单的测试类,并验证ImmutableStudent不再是不可变的:

public static void main(String[] args) {

Age age = new Age();

age.setDay(1);

age.setMonth(1);

age.setYear(1992);

ImmutableStudent student = new ImmutableStudent(1, "Alex", age);

System.out.println("Alex age year before modification = " + student.getAge().getYear());

age.setYear(1993);

System.out.println("Alex age year after modification = " + student.getAge().getYear());

}

运行上面的测试后,我们得到以下输出:

Alex age year before modification = 1992

Alex age year after modification = 1993

我们声称ImmutableStudent是一个不可变类,其状态在构造之后永远不会被修改,但是在上面的例子中,我们甚至可以在构造Alex对象之后修改Alex的年龄。如果我们回到ImmutableStudent构造函数的实现,我们发现age字段被分配给Age参数的实例,因此每当在类外部修改引用的Age时,更改将直接反映在Alex的状态上。查看我的 Pass by value或通过参考文章来更深入地理解这个概念。

为了解决这个问题并使我们的类再次成为不可变的,我们按照上面提到的步骤#5来创建一个不可变类。因此,我们修改构造函数以克隆Age的传递参数并使用它的克隆实例。

public ImmutableStudent(int id, String name, Age age) {

this.name = name;

this.id = id;

Age cloneAge = new Age();

cloneAge.setDay(age.getDay());

cloneAge.setMonth(age.getMonth());

cloneAge.setYear(age.getYear());

this.age = cloneAge;

}

现在,如果我们运行测试,我们得到以下输出:

Alex age year before modification = 1992

Alex age year after modification = 1992

正如你现在所看到的,亚历克斯的时代在建设之后从未受到影响,我们的课程又回归到不可改变的状态。

3.3。从不可变类中返回可变对象

但是,我们的类仍然存在泄漏并且不是完全不可变的,让我们采取以下测试场景:

public static void main(String[] args) {

Age age = new Age();

age.setDay(1);

age.setMonth(1);

age.setYear(1992);

ImmutableStudent student = new ImmutableStudent(1, "Alex", age);

System.out.println("Alex age year before modification = " + student.getAge().getYear());

student.getAge().setYear(1993);

System.out.println("Alex age year after modification = " + student.getAge().getYear());

}

输出:

Alex age year before modification = 1992

Alex age year after modification = 1993

再次根据步骤#4,当从不可变对象返回可变字段时,您应该返回它们的克隆实例而不是该字段的实际实例。

所以我们修改getAge()以返回对象年龄的克隆:

public Age getAge() {

Age cloneAge = new Age();

cloneAge.setDay(this.age.getDay());

cloneAge.setMonth(this.age.getMonth());

cloneAge.setYear(this.age.getYear());

return cloneAge;

}

现在,类变得完全不可变,并且没有为其他对象提供修改其状态的方法或方法。

Alex age year before modification = 1992

Alex age year after modification = 1992

4。结论

不可变类提供了许多优点,尤其是在多线程环境中正确使用时。唯一的缺点是它们比传统类消耗更多内存,因为每次修改它们都会在内存中创建一个新对象......但是,开发人员不应高估内存消耗,因为与这些内容相比,它可以忽略不计类的类型。

最后,如果一个对象只能向其他对象呈现一个状态,则无论它们如何以及何时调用其方法,它都是不可变的。如果是这样,线程安全的任何定义都是线程安全的。

setyear java_如何在Java中创建不可变类相关推荐

  1. java创建一个不可变对象_如何在Java中创建不可变类?

    java创建一个不可变对象 Today we will learn about the immutable class in Java. What are immutable classes? The ...

  2. 如何在Java中创建一个新的List

    本文翻译自:How to make a new List in Java We create a Set as: 我们创建一个Set为: Set myset = new HashSet() How d ...

  3. idea中java文件怎么运行_Java入门基础篇-如何在Java中创建只读文件

    本文选自千锋教育<Java语言程序设计>,如需转载请注明出处,谢谢! 1.如何创建只读文件 要使文件只读,我们只要将文件属性更改为只读就行:可以使用File类的setReadOnly()方 ...

  4. java 运费_如何在Java中创建运费成本计算器

    我正在创建计算器来计算运费.代码是这样的:如何在Java中创建运费成本计算器 class ShippingCalc { public static void main(String[] args) { ...

  5. 字符串压缩 java_如何在Java中压缩字符串?

    如何在Java中压缩字符串? 我使用GZIPOutputStream或ZIPOutputStream压缩字符串(我的2222235278130938882小于20),但压缩结果比原始字符串长. 在某个 ...

  6. java creat uid_关于uniqueidentifier:如何在Java中创建唯一ID?

    本问题已经有最佳答案,请猛点这里访问. 我正在寻找在Java中创建唯一ID作为String的最佳方法. 任何指导表示赞赏,谢谢. 我应该提到我正在使用Java 5. 看看这个stackoverflow ...

  7. 如何在Java中创建内存泄漏?

    我刚刚接受采访,并被要求使用Java造成内存泄漏. 不用说,我对如何开始创建它一无所知. 一个例子是什么? 解决方案: 这是在纯Java中创建真正的内存泄漏(运行代码无法访问但仍存储在内存中的对象)的 ...

  8. 关于java中的不可变类(转)

    如何在Java中写出Immutable的类? 要写出这样的类,需要遵循以下几个原则: 1)immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象. 2)Immuta ...

  9. 多线程线程池的实现java_如何在Java中实现线程池

    多线程线程池的实现java 线程是独立程序的执行路径. 在java中,每个线程都扩展java.lang.Thread类或实现java.lang.Runnable. 多线程是指在一个任务中同时执行两个或 ...

最新文章

  1. windows环境下,mysql的root密码丢失后重置方法
  2. 5G 信令流程 — 5GC 的连接管理(CM,Connection Management)
  3. opencv的K近邻算法
  4. Python3之多线程学习
  5. linux中级-JAVA企业级应用TOMCAT实战
  6. 3种语言白色汇汇通微盘程序源码
  7. 【英语学习】【Level 08】U04 What I love L4 Take your sweet time
  8. linux 修改ramdisk内容,在Linux下使用RamDisk
  9. JAVA实现二叉树带权路径长度和_哈夫曼树的构建与最小带权路径长度
  10. Java 中 byte 类型初始化问题
  11. SecureCRT for Mac(强大的终端SSH工具)
  12. IPMI接口和BMC控制器
  13. 快速安装vs2015社区版
  14. 各代iphone尺寸_iphone 各型号设备的屏幕尺寸
  15. 如何使用图灵机器人实现自动回复?
  16. 概念学习(Concept learning)
  17. 用php求常见图形的面积,技巧:数学图形面积计算的十种方法!
  18. 大家来找茬源码(微擎) -- 流量主
  19. BUUCTF-[安洵杯 2019]easy_web1
  20. 使用串口发送实现ACX720开发板时钟显示

热门文章

  1. Intel 64/x86_64/IA-32/x86处理器 - SIMD指令集 - SSE扩展(9) - 64位整型指令(MMX指令集扩展)
  2. Intel 64/x86_64/IA-32/x86处理器 - 通用指令(1) - 数据传输指令
  3. python chromedriver_Linux下搭建Python3.7+Selenium+Chrome+Chromedriver
  4. 快速傅立叶变换(FFT)的海面模拟
  5. centos8 安装geany
  6. UE3 Lightmass静态全局光照
  7. STL之set_union、set_intersection、set_difference、set_symmetric_difference
  8. EL4.1配置文件管理浅谈(1)
  9. PostgreSQL SPI 中的错误处理
  10. Canvas or SVG?一张好图,两手准备,就在 ECharts 4.0