我的目标是使Java对象不可变。我有一个班级Student
。我用以下方式对其进行编码以实现不变性:
public final class Student {
private String name;
private String age;
public Student(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}
我的问题是,实现Student
课程不变性的最佳方法是什么?
答案 0 :(得分:66)
严格来说,你的课不是一成不变的,它只是有效的一成不变的。要使其不可变,您需要使用final
:
private final String name;
private final String age;
虽然差异看起来很微妙,it can make a significant difference in a multi-threaded context。不可变类本质上是线程安全的,只有在安全发布时,有效不可变类才是线程安全的。
答案 1 :(得分:56)
制作不可变类时必须考虑几件事情:
final
- 您已经private
和final
- 在代码中进行适当的更改List
或Date
,那么让他们final
就不够了。你应该从他们的getters
返回一份防御性副本,这样他们的状态就不会被调用方法变异。对于第4点,假设您的班级中有Date
字段,那么该字段的getter应如下所示:
public Date getDate() {
return new Date(this.date.getTime());
}
当你的可变字段本身包含一些可变字段,并且反过来可以包含一些其他可变字段时,制作防御性副本会变得很头疼。在这种情况下,您需要迭代地复制每个副本。我们将此可变字段的迭代副本命名为 Deep Copy 。
自己实施深层复制可能很麻烦。但是,将这个问题区分开来,一旦你发现自己陷入了制作深度防御性副本的要求,就应该再次考虑你的课堂设计。
答案 2 :(得分:4)
答案 3 :(得分:3)
使用final
关键字:
private final String name;
private final String age;
答案 4 :(得分:3)
将变量设为私有,并且没有setter方法可用于原始数据类型。如果我的班级有任何对象集合?
使用集合对象使任何类不可变?
使用extends collection类编写您自己的集合对象,并遵循私有变量而不遵循setter方法。或者返回集合对象的clone对象。
public final class Student {
private StudentList names;//Which is extended from arraylist
public Student() {
names = DAO.getNamesList()//Which will return All Student names from Database its upto you how you want to implement.
}
public StudentList getStudentList(){
return names;//you need to implement your own methods in StudentList class to iterate your arraylist; or you can return Enumeration object.
}
public Enumeration getStudentNamesIterator(
Enumeration e = Collections.enumeration(names);
return e;
}
public class StudentList extends ArrayList {
}
答案 5 :(得分:2)
这很好,但我也会创建字段final
。
此外,我会将年龄设为int
或double
而不是字符串。
答案 6 :(得分:2)
您的示例已经是不可变对象,因为Student类中的字段只能在实例初始化时设置。
要使对象不可变,您必须执行以下步骤:
答案 7 :(得分:2)
稍微扩展答案。
final
与Immutable
不同,但如果您以某种方式使用它,则可以使用final
使某些内容不可变。
某些类型是不可变的,因为它们代表不变的值而不是可变状态的对象。字符串,数字等是不可变的。最后,通常我们的对象归结为最终引用不可变值的数据结构,但我们通过为相同的字段名称分配新值来更改数据结构。
因此,为了使某些东西真正不可变,你需要确保最终一直使用final,直到你到达每个字段到达组合树底部的每个值。否则,某些东西可能会从你的对象下面改变,并且它不是真正完全不可变的。
答案 8 :(得分:1)
回答为时已晚,但可能有助于其他有这个问题的人。
我认为此链接有助于提供更多帮助 阅读更多:http://javarevisited.blogspot.com/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html#ixzz40VDQDDL1
答案 9 :(得分:0)
它已经是不可变的 - 一旦初始化它就无法更改内容,因为你还没有制作setter。您可以在变量中添加最终关键字。
答案 10 :(得分:0)
将所有变量设为final
,并在设置某个字段时,使其返回对新Student
对象的引用,并使用String
中新设置的值。
答案 11 :(得分:0)
您可以按照此示例中显示的指南(Google中的第一个结果): http://www.javapractices.com/topic/TopicAction.do?Id=29
答案 12 :(得分:0)
这里有一些规则,它们有助于在Java中使类不可变: 1.构造后不能修改不可变对象的状态,任何修改都应该导致新的不可变对象。 2. Immutable类的所有字段都应该是final。 3.必须正确构造物体,即在施工过程中物体参考不得泄漏。 4.对象应该是最终的,以便限制子类来改变父类的不变性。
示例:
public final class Contacts {
private final String name;
private final String mobile;
public Contacts(String name, String mobile) {
this.name = name;
this.mobile = mobile;
}
public String getName(){
return name;
}
public String getMobile(){
return mobile;
}
}
答案 13 :(得分:0)
根据Strategy for Defining Immutable Objects
不提供“ SELECT t1.id, t1.name, t2.letter_count, t2.avg_number
FROM table1 t1 INNER JOIN
(SELECT t2.t1_id, AVG(t2.number) as avg_number
FROM table2 t2
GROUP BY t2.t1_id
) t2
ON t2.t1_id = t1.id LEFT JOIN
(SELECT t3.t2_id, COUNT(t3.letter) as letter_count
FROM table3 t3
WHERE t3.letter IN ('a', 'b', 'c')
GROUP BY t3.t2_id
) t3
ON t3.t1_id = t1.id
ORDER BY t3.letter_count DESC, t2.avg_number DESC;
”方法-修改字段或
字段引用的对象。
设置所有字段setter
和final
。
不允许子类覆盖方法。最简单的方法是将该类声明为private
。
一种更复杂的方法是将final
设为私有并在工厂方法中构造实例。
如果实例字段包含对可变对象的引用,则不允许更改这些对象:
不要提供修改可变对象的方法。
b。不要共享对可变对象的引用。永远不要存储对传递给构造函数的外部可变对象的引用;如有必要,创建副本,并存储对副本的引用。同样,在必要时创建内部可变对象的副本,以避免在方法中返回原始对象。
答案 14 :(得分:0)
您可以使用作为 Java SE 16 一部分引入的 JEP 395: Records 功能以简洁的方式创建不可变类。
如果您已经浏览了上面的链接,那么您一定已经想出可以简单地做到这一点
record Student(String name, String age) { }
反过来你得到的是:
final class Student
。Student(String name, String age)
相同的规范构造函数。private final
字段、name
和 age
及其对应的具有相同名称和返回类型的 public
访问器方法。equals
、hashCode
和 toString
方法。Student.java
record Student(String name, String age) { }
Main.java
class Main{
public static void main(String[] args) {
Student s1 = new Student("Bharat", "10 Years");
Student s2 = new Student("Arvind", "10 Years");
System.out.println(s1);
System.out.println(s1.equals(s2));
System.out.println(s1.age().equals(s2.age()));
}
}
输出:
Student[name=Bharat, age=10 Years]
false
true
答案 15 :(得分:-1)
使类或变量成为最终的
Public final class constants
{
private final String name;
private final String mobile;
// code
}