首页 > 编程语言 >Java中如何创建不可变(immutable)类

Java中如何创建不可变(immutable)类

时间:2022-09-06 21:23:44浏览次数:47  
标签:Java String vArr int 创建 Immutable immutable 变类 final

什么是不可变类

1. 不可变类是指类的实例一经创建完成,这个实例的内容就不会改变。

2. Java中的String和八个基本类型的包装类(Integer, Short, Byte, Long, Double, Float,Boolean,Char)都是不可变类

3.不可变类 vs 不可变变量:

二者是不一样的。

不可变类是指类的实例内容不会改变,考虑如下代码:

1 String s = "ABC";
2 s = "BCD"
3 System.out.println("s:"+s);
4 //output s:BCD

 在line 2中我们对s变量进行了再次赋值,实际上是又创建了一个值为"BCD"的String 对象,并将s指向它。变化的是s指向的内存地址(或者简单的叫指针),值为"ABC" 与值为"BCD"的两个String 对象是没有变的

 不可变变量是用Final关键字修饰的变量,考虑如下代码:

1 final String s = "ABC";
2 s = "BCD" //此行报错,不能给final变量赋值
3 System.out.println("s:"+s);

我们将s变量用final关键字修饰,这时在s被初始化之后,就无法在line 2 再次给它赋值了,也就是说我们没办法改变final变量指向的内存地址

 

如何实现一个不可变类

1. 所有的成员变量声明为private final,防止初始化后被修改

2. 类声明为final,禁止继承,其实是防止类中的方法被重写

3. 不为成员变量提供setter方法

4. 如果类中包含可变对象,比如一个成员变量是数组,或者其他可变类,那么要有如下操作:

1)在构造方法中,如果构造方法会传入可变对象,我们要使用这个对象的copy来初始化我们的成员变量,而不是直接使用传入的对象。因为传入的是指针,传入的对象在外面可能会被修改,如果直接引用的话会导致我们的成员变量也间接被修改。

2) 在返回这些可变对象的getter方法中,返回对象的copy,而不是直接返回该对象(或者叫该对象的引用/指针)

实例:

 1 package org.adeline.learning;
 2 
 3 import java.util.Arrays;
 4 
 5 public final class Immutable {
 6     private final int vInt;
 7     private final String vStr;
 8     private final int[] vArr;
 9 
10     public Immutable(int vInt, String vStr, int[] vArr) {
11         this.vInt = vInt;
12         this.vStr = vStr;
13         //this.vArr = vArr; //不正确
14         this.vArr = vArr.clone();//使用传入数组的copy初始化
15     }
16 
17     public int[] getVArr() {
18         //return vArr; //不正确
19         return vArr.clone(); //返回数组的copy
20     }
21 
22     public static void main(String[] args) {
23         int[] arr = new int[]{3,4};
24         Immutable im = new Immutable(1,"2", arr);
25         int[] arr1 = im.getVArr();
26         Arrays.stream(arr1).forEach((e) -> {System.out.println(e);});
27         arr[0] = 33;
28         arr[1] = 44;
29         Arrays.stream(arr1).forEach((e) -> {System.out.println(e);});
30     }
31 
32 }

下面探讨一下为何类也需要声明为final. 考虑如下代码:

 1 public class ImmutableChild extends Immutable{
 2     private int[] vArr;
 3     public ImmutableChild(int vInt, String vStr, int[] vArr) {
 4         super(vInt, vStr, vArr);
 5         this.vArr = vArr;
 6     }
 7     @Override
 8     public int[] getVArr() { //父类中的方法被重写,返回的是子类中的vArr
 9         return vArr;
10     }
11 
12     public static void main(String[] args) {
13         Immutable imNG = new ImmutableChild(1,"2", new int[]{3,4});
14         imNG.getVArr()[0] = 33;
15         imNG.getVArr()[1] = 44;
16         Arrays.stream(imNG.getVArr()).forEach(e -> {System.out.println(e);});//output 33,44
17     }
18 }

我们把上面Immutable 类的final 声明去掉,ImmutableChild继承了Immutable类,重写了getVArr方法,返回自己的成员变量数组vArr,而这个子类里面的vArr是可变的,在main方法里面初始化时我们给其赋值{3,4},可以看到后面我们改成了{33,44}. 

在使用中,任何一个接受Immutable实例的地方都可以接受其子类ImmutableChild实例,并将它作为一个不可变的实例来操作,而实际上它是可变的,这样就有可能出错。

所以把不可变类声明为final是为了防止恶意继承,或者继承中考虑不周密导致的问题。

 

不可变类的优点与用途

1. 线程安全,省去了加锁的过程,因为对象内容不可变就不用担心线程同时对对象做修改

2. 拷贝效率高。当类不可变时, 拷贝其对象只需拷贝指针即可,而不用拷贝对象本身,不用担心会被修改

3. 可以作为HashMap的key,类不可变保证了Hashcode的稳定性。

 

当然,也要注意不可变类在使用过程中可能出现的内存浪费问题,比如大家都知道的最好不要用许多"+"连接String

 

标签:Java,String,vArr,int,创建,Immutable,immutable,变类,final
From: https://www.cnblogs.com/adeline-tech/p/16655521.html

相关文章

  • java复习随笔(十四)——类加载器、反射
    类加载器类加载当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外状况,JVM将会连续完......
  • Linux学习笔记:mkdir创建文件夹
    Linux学习笔记:mkdir创建文件夹文件夹,即目录,在linux中使用mkdir创建。语法:mkdirdir_name通过mkdir命令可以实现在指定位置创建以dir_name(指定的文件名......
  • 创建一个函数getDays,传递一个日期,返回这个日期是一年中的第几天
     function getDays(year,month,day){      //二月天数:判断是否是闰年      vartwo=year%4==0&&year%100!==0 ||year%400==0?29:28 ......
  • Java环境配置
    选择jdk1.8版本进行安装,选择一个方便记忆的目录进行安装右键计算机-->属性--打开高级系统设置---点击环境变量----对系统变量进行更改新建变量名JAVA_HOME变量值......
  • Java调用C++动态链接库——Jni
    最近项目需要,将C++的算法工程编译成动态链接库,交给Java后台当作函数库调用。就去了解了下Jni。使用起来还是比较方便的。1.  首先编写Java的调用类。例如:  public......
  • Java连接数据库进行操作
    importjava.sql.*;importjava.util.ResourceBundle;publicclassJBDCTEST{publicstaticvoidmain(String[]args){Connectionc=null;St......
  • java程序运行机制
    java程序运行机制编译型相当于有一个负责翻译的程序(编译器),将代码转换成计算机可执行的代码。执行速度快,对操作系统要求较低C/C++.java通过java编译器变为.class......
  • java邮件开发详解(一)
     地址:https://www.cnblogs.com/h--d/p/6138810.htmlJavaMail介绍 JavaMail是SUN提供给开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发类库,支......
  • 【Java基础】方法重写Override
    1.什么是方法重写子类对父类中同名同参数的方法进行重写覆盖。publicclassPerson{publicvoidsay(){System.out.println("父类中的方法");}}......
  • 创建虚拟环境
    win创建虚拟环境安装virtualenvpipinstallvirtualenvvirtualenvmyflaskmyflask就是虚拟环境的名字创建好的虚拟环境就在当前文件夹Linux创建虚拟环......