java 不可变类

java不可变类

不可变类的意思是创建该类的实例后,该类的属性是不可改变的。java中的8种包装类型和java.lang.String都是不可变类。如果需要创建自定义的不可变类,需要遵守如下规则:
1,类属性使用private和final修饰符来修饰。
2,提供带参的构造方法,用于初始化类中的属性。
3,为该类属性只提供get方法,不提供set方法,因为final修饰的属性是不许允被修改的。
4,如果有必要,重写Object类中的equals()和hashCode()方法。
下面我们来定义一个不可变类。代码如下:

package fly.zxy.CollectionJava;

public class Address {
    //使用privae final修饰属性
    private final String detail;
    private final String postCode;

    public Address(){
        this.detail = "";
        this.postCode = "";
    }
    //提供带参的构造函数来初始化属性
    public Address(String detail, String postCode){
        this.detail = detail;
        this.postCode = postCode;
    }

    //重写equals方法
    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        }

        if(obj!=null && obj instanceof Address){
            Address a = (Address) obj;
            if( this.getDetail().equals(a.getDetail()) && this.getPostCode().equals(a.getPostCode()) && this.hashCode()==obj.hashCode() ){
                return true;
            }
        }

        return false;
    }

    @Override
    public int hashCode() {
        return this.getDetail().hashCode() + this.getPostCode().hashCode();
    }

    public static void main(String [] args){
        Address ar1 = new Address("创新城","20011");
        Address ar2 = new Address("创新城","20001");

        System.out.println(ar1.equals(ar2));
    }
    //只提供get方法
    public String getDetail() {
        return detail;
    }
    public String getPostCode() {
        return postCode;
    }
}

对于上面的Address类,当我们程序创建了该类的实例后,是无法修改detail和postCode属性的。与不可变类对应的是可变类,我们大部分时候创建的类都是可变的。特别是JavaBean,我们提供了get和set方法来获取和改变属性。

当使用fianl修饰引用类型变量时,表示该引用类型变量不能被重写赋值(引用不能被改变),但是引用所指向的对象内容是可以被改变的。这就产生了一个问题:当创建不可变类时如果它包含的引用类型属性是可变的,那么其属性对象本身的值依然是可变的,那么这个可变类其实是失败的。注意上面的Address类的属性类型都是String类型,而String是不可变类。

下面程序定义了一个不可变类Person,但因为Person包含一个引用Name类型属性name,Name是可变类,所以导致Person变成了不可变类。代码如下:

package fly.zxy.CollectionJava.finalClass;

public class Person {

    private final Name name;
    private final int age;

    public Person(Name name, int age){
        this.name = name;
        this.age = age;
    }

    public static void main(String [] args){
        Name n1 = new Name("zhangxue","yuan");
        Person p1 = new Person(n1, 25);
        //=>p1:zhangxueyuan
        System.out.println("p1:"+p1);

        //通过n1更改 firstName属性的值为 "AB"
        n1.setFirstName("AB");
        //=>p1:AByuan
        System.out.println("p1:"+p1);

        //通过getName()获取的Name对象来更改firstName属性值为"CD"
        Name n2 = p1.getName();
        n2.setFirstName("CD");
        //=>p1:CDyuan
        System.out.println("p1:"+p1);
    }

    public String toString(){
        return this.name.getFirstName()+this.name.getLastName();
    }

    public Name getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}

class Name{

    private String firstName;
    private String lastName;

    public Name(String firstName, String lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

从上面的例子中我们可以发现Person的name对象的firstName被修改了。这破坏了设计Person类的初衷。
为了保持Person的不可变,必须保护好name属性,让外界无法访问到name属性所指向的Name对象。我们可以这样更改Person类,代码如下:

package fly.zxy.CollectionJava.finalClass;

public class Person {

    private final Name name;
    private final int age;

    public Person(Name name, int age){
        //相当于创建一个 name 对象的副本,然后在赋给this.name
        this.name = new Name(name.getFirstName(), name.getLastName());
        this.age = age;
    }

    public static void main(String [] args){
        Name n1 = new Name("zhangxue","yuan");
        Person p1 = new Person(n1, 25);
        //=>p1:zhangxueyuan
        System.out.println("p1:"+p1);

        //通过n1更改 firstName属性的值为 "AB"
        n1.setFirstName("AB");
        //=>p1:AByuan
        System.out.println("p1:"+p1);

        //通过getName()获取的Name对象来更改firstName属性值为"CD"
        Name n2 = p1.getName();
        n2.setFirstName("CD");
        //=>p1:CDyuan
        System.out.println("p1:"+p1);
    }

    public String toString(){
        return this.name.getFirstName()+this.name.getLastName();
    }

    public Name getName() {
        //创建一个name对象的副本并返还
        return new Name(name.getFirstName(), name.getLastName());
    }
    public int getAge() {
        return age;
    }
}

class Name{

    private String firstName;
    private String lastName;

    public Name(String firstName, String lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值