《HardCore Java》读书笔记系列--之三

第三章 Immutable Type


    在到处都是引用的Java世界里,要构造一个不可变类型可没有想像中的那么简单。

1.Fundamentals

public class ImmutablePerson
{
    private String name;
    private int age;

    public ImmutablePerson(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName(){
        return this.name;
    }

    public int getAge(){
        return this.age;
    }
}
    这个类看似是个不可变类型,其实不然。我们完全可以用Reflection来改变ImmutablePerson类型的对象。要使其成为真正的不可变类型,

应该这样改:
public class ImmutablePerson
{
    private final String name;
    private final int age;

    public ImmutablePerson(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName(){
        return this.name;
    }

    public int getAge(){
        return this.age;
    }
}
    不要以为final就可以解决所有的问题,这只是针对原始类型(String虽不是原始类型,但其表现更像是原始类型),要不看看下面的这个类:
public class Circle
{
    private final Point point;

    public Circle(Point point){
        this.point = point;
    }

    public Point getPoint(){
        return this.point;
    }
}
这个程序至少有两个漏洞(对不变类型来说)。首先是它的构造函数,看下面的调用:
 public void test(){
        Point p = new Point(20,30);
        Circle c = new Circle(p);
        System.out.println(c.getPoint().toString());

        p.x = 25;
        p.y = 35;

        System.out.println(c.getPoint().toString());
    }
这是运行结果:
java.awt.Point[x=20,y=30]
java.awt.Point[x=25,y=35]
原因就在于你给外人留下了‘把柄’。同样,方法getPoint()也存在同样的问题,这次你把‘把柄’主动送给了别人。要注意final只能防止给变量赋新值,不能阻止你调用变量的方法来改变变量。

要想不给别人留下把柄,最好是每次赋值时都拷贝这个变量,如下:
public class Circle
{
    private final Point point;

    public Circle(Point point){
        this.point = new Point(point);
    }

    public Point getPoint(){
        return new Point(this.point);
    }
}

其实很多时候即使是非不可变类型也要这么做,给他人留下‘把柄’可不是什么好习惯。

2.The String Trap


在java中String是一个不可变类型,每次改变String的操作都会导致新的String对象生成,如果在程序中大量对String操作,会消耗掉大量的内存,如:
    public String toString(String[] array){
        String field = "[";
        for(int i=0;i<array.length;++i){
            field += array[i] + ",";
        }
        field += "]";
        return field
    }
循环里的每次‘+’操作都会产生一个新的String对象,所以对String的操作并不象想像中的那么‘好用’。
    要对大量的字符串做连接最好是使用StringBuffer,如上例可以这样改:
    public String toString(String[] array){
        StringBuffer field = new StringBuffer();
        field.append("[");
        for(int i=0;i<array.length;++i){
            field.append(array[i] + ",");
        }
        field.append("]");
        return field.toString();
    }
但事实上这里仍然有问题,因为StringBuffer的文档中说在使用StringBuffer时,如果使用‘+’来连接String,那么java虚拟机会自动创建一个StringBuffer来做这个连接(在我的jdk1.5中我并没有看到类似这个的提示,哪位如果找到了告诉我),也就是说上例中每次循环你都重新创建了一个StringBuffer类。遗憾的是在jdk1.5中这个说法似乎并不成立,而且我也没有找到作者所说的提示,可能只是在jdk1.5以前的版本中是这样的吧(手中没有jdk1.4的文档了),这是我对这个说法的测试:
public class  Test
{
    public void asString1(String[] array){
        String field = "[";
        for(int i=0;i<array.length;++i){
            field += "["+i+"]"+array[i] + ",";
        }
        field += "]";
    }
    public void asString2(String[] array){
        StringBuffer field = new StringBuffer();
        field.append("[");
        for(int i=0;i<array.length;++i){
            field.append("["+i+"]"+array[i] + ",");
        }
        field.append("]");
    }
    public void asString3(String[] array){
        StringBuffer field = new StringBuffer();
        field.append("[");
        for(int i=0;i<array.length;++i){
            field.append("[");
            field.append(i);
            field.append("]");
            field.append(array[i]);
            field.append(",");
        }
        field.append("]");
    }

    public static void main(String[] args){
        Test t= new Test();
        String[] array = new String[5000];
        for(int i=0;i<array.length;++i){
            array[i] = new String("test " + i);
        }
        long time = System.currentTimeMillis();
        t.asString1(array);
        System.out.println(System.currentTimeMillis()-time);
        time = System.currentTimeMillis();
        t.asString2(array);
        System.out.println(System.currentTimeMillis()-time);
        time = System.currentTimeMillis();
        t.asString3(array);
        System.out.println(System.currentTimeMillis()-time);
    }
}
在我机器(AMD1800+)上的运行的结果如下:
E:/program>java Test
40418
20
40

可见第三种方法要比第二种方法费时,这与书中所说并不相符。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值