如何在java中创建不可变类

如何在java中创建不可变类,java,Java,如何在java中创建不可变类。 如果学生类有关系(地址),如何创建不可变类。 我想使下面的类不可变 final public class Student { private final Address add; private final int sid; public Student(int sid, String name, Address add) { super();

如何在java中创建不可变类。 如果学生类有关系(地址),如何创建不可变类。 我想使下面的类不可变

  final public class Student {
        private final Address add;
            private final int sid;
            public Student(int sid, String name, Address add) {
                super();
                this.sid = sid;
                this.name = name;
                this.add = add;
            }
            private final String name;
            public int getSid() {
                return sid;
            }
            public final String getName() {
                return name;
            }
            @Override
            public String toString() {
                return "Student [add=" + add + ", name=" + name + ", sid=" + sid + "]";
            }
            public Address getAdd() {
                return add;
            }


        }

        //I want to make the class below immutable
        public class Address {
            public int getAid() {
                return aid;
            }
            public String getStreet() {
                return street;
            }
            @Override
            public String toString() {
                return "Address [aid=" + aid + ", street=" + street + "]";
            }
            int aid;
            String street;
            public Address(int aid, String street) {
                super();
                this.aid = aid;
                this.street = street;
            }

        }


        public class First {
        public static void main(String[] args) {
            Address myAdd=new Address(179,"Maihill");
            Student st=new Student(99,"anoj",myAdd);
            System.out.println(st.toString());
            myAdd.aid=2376;
            System.out.println(st);
            System.out.println("***************");
            Address pAdd=st.getAdd();
            //Here modified address instance then how we can make immutable.
                pAdd.aid=788;
            System.out.println(st);

        }
        }
这里我们可以修改地址实例。
请给我一个想法

不可变的关键点是:

  • 无设置器方法
  • 使变量成为私有的和最终的
  • 使用Collections.unmodifiableList返回列表-从不返回任何可变字段;始终返回字段的副本(如果合适,请返回深度)或不可变版本
  • 期末考试
  • 如果变量在类内部更改,则此更改不可见,并且在类外部没有影响(包括影响
    equals()
    hashcode()

在您的班级
地址中
您应该将字段
设置为私有
(应该)和
最终
(必须),如下所示-

public final class Address {       // so no sub-classes can be made.
  private final int aid;           // private and final.
  private final String street;     // private and final.
  // as before.
}

您也不能使用setter方法,但当字段是最终字段时,这不是什么大问题(因为任何setter方法都会产生编译器错误)。

您使Student半不可变:

  • 它的属性是最终的
  • 它们是不可变的类型(地址除外)
  • 它们在构造函数中初始化
您应该将相同的东西应用于
地址
类,使其成为不可变的,然后
学生
的所有状态都将是不可变的。因此,它将是:

public final class Address {
    private final int aid;
    private final String street;

    public Address(int aid, String street) {
        this.aid = aid;
        this.street = street;
    }


    public int getAid() {
        return aid;
    }

    public String getStreet() {
        return street;
    }

    ....
}
幸运的是,你没有任何可修改的类型(一些最有名的是“代码>日期<代码>,<代码>集合> <代码> s和<代码>地图< /代码>),否则你也应该考虑它们。 若你们有任何可变属性,你们应该在构造函数中复制保护它,并且你们应该返回一个不可修改的或者状态泄露的副本

例如,如果您的
学生
班级具有
生日
属性,您应该执行以下操作:

public final class Student {
    private final Date birthDate;

    public Student(int sid, String name, Address address, Date birthDate) {
        this.sid = sid;
        this.name = name;
        this.address = address;
        this.birthDate = (birthDate == null) ? null : new Date(birthDate.getTime());
    }

    public Date getBirthDate() {
       return (birthDate == null) ? null : new Date(birthDate.getTime());
    }

    ....

}

够了<声明的code>final不能进行变异,因为构造函数中有必需的参数,所以getter是多余的

final public class Student {

    public final Address add;
    public final int sid;
    public final String name;

    public Student(int sid, String name, Address add) {
        super();
        this.sid = sid;
        this.name = name;
        this.add = add;
    }

    @Override
    public String toString() {
        return "Student [add=" + add + ", name=" + name + ", sid=" + sid + "]";
    }
}
address
studentId/id
将是更好的字段名称。

要使类不可变,请遵循以下五条规则: 摘自有效Java-第三版-第4章

  • 不要提供修改对象状态的方法
  • 确保类不能扩展。这可以防止粗心或恶意的子类通过表现为对象的状态已更改而损害类的不变行为。防止子类化通常是通过使类成为最终类提供私有构造函数来实现的
  • 使所有字段成为最终字段。这以系统强制执行的方式清楚地表达了您的意图。此外,如果对新创建的实例的引用在没有同步的情况下从一个线程传递到另一个线程,则必须确保行为正确,如内存模型中所述
  • 所有字段设置为私有。这会阻止客户端访问字段引用的可变对象并直接修改这些对象。虽然技术上允许不可变类具有包含原语值或对不可变对象的引用的公共final字段,但不建议这样做,因为这样可以避免在以后的版本中更改内部表示
  • 确保以独占方式访问任何可变组件。如果您的类有任何引用可变对象的字段,请确保该类的客户端无法获取对这些对象的引用切勿将此类字段初始化为客户端提供的对象引用或从访问者返回该字段。在构造函数、访问者和
    readObject
    方法中制作防御性副本(第50项)
  • java-示例不可变类
    首先,我们需要讨论java中什么是不可变的。
    在Java中,immutable意味着您的状态在初始化后不会改变。不可变类的最好例子是字符串

    我们也可以创建自己的不可变类,您必须执行以下步骤

    • 将该类声明为最终类:

      Why? : As per the java final class can not be extended.
      
    • 将所有字段声明为私有字段

      Why? : Because private member  have not direct access out side of the class
      
    • 不要为该私有字段提供setter方法

      Why? : If you provide the setter method for the private members so you can access it out side of the class.    
      
    • 将所有字段设为最终字段

      Why?: As per the java final variable can be assigned only once.    
      
    • 使用深度复制通过构造函数初始化所有字段

                  import java.util.HashMap;
          import java.util.Iterator;
      
          public final class ImmutableClassExample {
          private final int id;   
          private final String name;  
          private final HashMap<String,String> testMap;   
          public int getId() {
              return id;
          }
      
      
          public String getName() {
          return name;
          }
      
          /**
          * Accessor function for mutable objects
          */
          public HashMap<String, String> getTestMap() {
          //return testMap;
          return (HashMap<String, String>) testMap.clone();
          }
      
          /**
              * Constructor performing Deep Copy
              * @param i
              * @param n
              * @param hm
          */
      
          public ImmutableClassExample(int i, String n, HashMap<String,String> hm){
              System.out.println("Performing Deep Copy for Object initialization");
              this.id=i;
              this.name=n;
              HashMap<String,String> tempMap=new HashMap<String,String>();
              String key;
              Iterator<String> it = hm.keySet().iterator();
              while(it.hasNext()){
                  key=it.next();
                  tempMap.put(key, hm.get(key));
              }
              this.testMap=tempMap;
          }
      
      
        /**
          * Constructor performing Shallow Copy
          * @param i
          * @param n
       * @param hm
       */
      /**
      public ImmutableClassExample(int i, String n, HashMap<String,String> hm){
      System.out.println("Performing Shallow Copy for Object initialization");
      this.id=i;
      this.name=n;
      this.testMap=hm;
      } 
      
      */
          /**
          * To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes
          * @param args
      */
      public static void main(String[] args) {
          HashMap<String, String> h1 = new HashMap<String,String>();
          h1.put("1", "first");
          h1.put("2", "second");
      
      String s = "original";
      
      int i=10;
      
      ImmutableClassExample ce = new ImmutableClassExample(i,s,h1);
      
          //Lets see whether its copy by field or reference
          System.out.println(s==ce.getName());
          System.out.println(h1 == ce.getTestMap());
          //print the ce values
          System.out.println("ce id:"+ce.getId());
          System.out.println("ce name:"+ce.getName());
          System.out.println("ce testMap:"+ce.getTestMap());
          //change the local variable values
          i=20;
          s="modified";
          h1.put("3", "third");
          //print the values again
              System.out.println("ce id after local variable change:"+ce.getId());
              System.out.println("ce name after local variable change:"+ce.getName());
              System.out.println("ce testMap after local variable change:"+ce.getTestMap());
      
              HashMap<String, String> hmTest = ce.getTestMap();
              hmTest.put("4", "new");
      
              System.out.println("ce testMap after changing variable from accessor 
              methods:"+ce.getTestMap());
      
              }
      
          }
      
      import java.util.HashMap;
      导入java.util.Iterator;
      公共最终类ImmutableClassExample{
      私有最终int id;
      私有最终字符串名;
      私有最终HashMap testMap;
      公共int getId(){
      返回id;
      }
      公共字符串getName(){
      返回名称;
      }
      /**
      *可变对象的访问器函数
      */
      公共HashMap getTestMap(){
      //返回testMap;
      return(HashMap)testMap.clone();
      }
      /**
      *正在执行深度复制的构造函数
      *@param i
      *@param n
      *@param-hm
      */
      公共ImmutableClassExample(int i、字符串n、HashMap hm){
      System.out.println(“为对象初始化执行深度复制”);
      这个.id=i;
      this.name=n;
      HashMap tempMap=新的HashMap();
      字符串键;
      迭代器it=hm.keySet().Iterator();
      while(it.hasNext()){
      key=it.next();
      tempMap.put(key,hm.get(key));
      }
      this.testMap=tempMap;
      }
      /**
      *执行浅复制的构造函数
      *@param i
      *@param n
      *@param-hm
      */
      /**
      公共ImmutableClassExample(int i、字符串n、HashMap hm){
      System.out.println(“为对象初始化执行浅拷贝”);
      这个.id=i;
      this.name=n;
      testMap=hm;
      } 
      */
      /**
      *测试浅拷贝的后果,以及如何通过创建不可变类的深拷贝来避免浅拷贝
      *@param args
      */
      公共静态void main(字符串[])
      
                  import java.util.HashMap;
          import java.util.Iterator;
      
          public final class ImmutableClassExample {
          private final int id;   
          private final String name;  
          private final HashMap<String,String> testMap;   
          public int getId() {
              return id;
          }
      
      
          public String getName() {
          return name;
          }
      
          /**
          * Accessor function for mutable objects
          */
          public HashMap<String, String> getTestMap() {
          //return testMap;
          return (HashMap<String, String>) testMap.clone();
          }
      
          /**
              * Constructor performing Deep Copy
              * @param i
              * @param n
              * @param hm
          */
      
          public ImmutableClassExample(int i, String n, HashMap<String,String> hm){
              System.out.println("Performing Deep Copy for Object initialization");
              this.id=i;
              this.name=n;
              HashMap<String,String> tempMap=new HashMap<String,String>();
              String key;
              Iterator<String> it = hm.keySet().iterator();
              while(it.hasNext()){
                  key=it.next();
                  tempMap.put(key, hm.get(key));
              }
              this.testMap=tempMap;
          }
      
      
        /**
          * Constructor performing Shallow Copy
          * @param i
          * @param n
       * @param hm
       */
      /**
      public ImmutableClassExample(int i, String n, HashMap<String,String> hm){
      System.out.println("Performing Shallow Copy for Object initialization");
      this.id=i;
      this.name=n;
      this.testMap=hm;
      } 
      
      */
          /**
          * To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes
          * @param args
      */
      public static void main(String[] args) {
          HashMap<String, String> h1 = new HashMap<String,String>();
          h1.put("1", "first");
          h1.put("2", "second");
      
      String s = "original";
      
      int i=10;
      
      ImmutableClassExample ce = new ImmutableClassExample(i,s,h1);
      
          //Lets see whether its copy by field or reference
          System.out.println(s==ce.getName());
          System.out.println(h1 == ce.getTestMap());
          //print the ce values
          System.out.println("ce id:"+ce.getId());
          System.out.println("ce name:"+ce.getName());
          System.out.println("ce testMap:"+ce.getTestMap());
          //change the local variable values
          i=20;
          s="modified";
          h1.put("3", "third");
          //print the values again
              System.out.println("ce id after local variable change:"+ce.getId());
              System.out.println("ce name after local variable change:"+ce.getName());
              System.out.println("ce testMap after local variable change:"+ce.getTestMap());
      
              HashMap<String, String> hmTest = ce.getTestMap();
              hmTest.put("4", "new");
      
              System.out.println("ce testMap after changing variable from accessor 
              methods:"+ce.getTestMap());
      
              }
      
          }
      
      record Rectangle(double length, double width) {}
      
      var rectangle = new Rectangle(7.1, 8.9);
      System.out.print(rectangle.length()); // prints 7.1
      
      public Rectangle {
      
          if (length <= 0.0) {
            throw new IllegalArgumentException();
          }
        }
      
      record Rectangle(double length, double width) {
        
        public double area() {
          return this.length * this.width;
        }
      }
      
      record Rectangle(double length, double width) {
        
        static double aStaticField;
       
        static void printRectanglesIntersect(Rectangle rectangleA, Rectangle rectangleB) {
          System.out.println("Checking Rectangle intersection..");
        }
      }
      
      @Value
      @Builder
      public class Immutable {
      
          private String str;
          private int value;
          private List<String> strings;
      
      }