java编程思想读书笔记 第十三章 字符串(上)

来源:互联网 发布:安卓乳摇软件 编辑:程序博客网 时间:2024/05/16 08:57

1.不可变字符串
String对象是不可变的。String类中每一个看起来会修改String值的方法,实际上是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未变。
看看下面的代码:

public class Immutable {    public static String upcase(String s) {        return s.toUpperCase();    }    public static void main(String[] args) {        String q = "howdy";        System.out.println(q); // howdy        String qq = upcase(q);        System.out.println(qq); // HOWDY        System.out.println(q); // howdy q还是q    }}

当q传给upcase()方法时,实际传递的是引用的一个拷贝。其实,每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。
2.重载“+”与StringBuilder
String对象是不可变的,你可以给一个String对象加任意多的别名。因为String对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此,也就不回对其他的引用有什么影响。
不可变性会带来一定的效率问题。为String对象重载的“+”操作符就是一个例子。重载的意思是,一个操作符应用于特定的类时,被赋予了特殊的意义(用于String的“+”与“+=”是java中仅有的两个重载过的操作符,而java并不允许程序员重载任何操作符)。操作符“+”可以用来连接String:

public class Test {    public static void main(String[] args) {        String mango ="mango";        String s = "abc" + mango + "def" + 47;        System.out.println(s);    }}

String有一个append()方法,它会生成一个新的String对象,以包含“abc”与mango连接好的字符串。然后该对象再与“def”相连,生成另一个新的String对象。
在使用”+”操作符时,编译器自动引入了java.lang.StringBuilder类,避免了连加情况下产生大量需要回收的垃圾(每+一次就会产生一个新的字符串)。因此,当为一个类编写toString()方法时,如果字符串操作简单,可以信赖编译器,它会为你合理的构造最终字符串结果。但是如果要在toString()中使用循环,那么最好自己创建一个StringBuilder对象,结合append()用它来构造最终结果。
例子如下:

public class UsingStringBuilder {    public static Random random = new Random(47);    public String toString(){        StringBuilder result = new StringBuilder("[");        for (int i = 0; i < 25; i++) {            result.append(random.nextInt(100));            result.append(",");        }        // 书上的result.length()-2是得不到它那个结果的,得到的结果是[58,55,93,61,61,29,68,0,22,7,88,28,51,89,9,78,98,61,20,58,16,40,11,22,]        result.delete(result.length()-1, result.length());        result.append("]");        return result.toString();    }    public static void main(String[] args) {        UsingStringBuilder usb = new UsingStringBuilder();        System.out.println(usb);    }}

输出:[58,55,93,61,61,29,68,0,22,7,88,28,51,89,9,78,98,61,20,58,16,40,11,22,4]
最终的结果是用append()语句一点点拼接起来的。如果你想走捷径,例如append(a+”:”+c),那编译器就会掉入陷阱,从而为你另外创建一个StringBuilder对象处理括号内的字符串操作。
StringBuilder提供了丰富而全面的方法,包括insert()、replace()、subString()甚至reverse(),但最常用的还是append()和toString(),delete()方法。StringBuilder是Java SE5引入的,在这之前还有Java的StringBuffer。StringBuffer是线程安全的,因此开销会大一些。StringBuilder字符串操作会更快一些。
3.无意识的递归
java中的每个类从基本上都是继承自Object,标准容器类自然也不例外。因此容器类都有toString()方法,并且覆写了该方法,使得它生成的String结果能够表达容器自身,以及容器所包含的对象。例如ArrayList.toString(),它会遍历ArrayList中包含的所有对象,调用每个元素上的
toString()方法。
如果希望toString()方法打印出对象的内存地址,不可以使用this关键字。如果这样写:

public class InfiniteRecursion {    public String toString() {    // return " InfiniteRecursion address: " + this + "\n";     return " InfiniteRecursion address: " + super.toString() + "\n";    }    public static void main(String[] args) {        List<InfiniteRecursion> v = new ArrayList<InfiniteRecursion>();        for (int i = 0; i < 3; i++) {            v.add(new InfiniteRecursion());            System.out.println(v);        }    }}

输出为:
[ InfiniteRecursion address: com.test.InfiniteRecursion@6bbc4459
]
[ InfiniteRecursion address: com.test.InfiniteRecursion@6bbc4459
, InfiniteRecursion address: com.test.InfiniteRecursion@152b6651
]
[ InfiniteRecursion address: com.test.InfiniteRecursion@6bbc4459
, InfiniteRecursion address: com.test.InfiniteRecursion@152b6651
, InfiniteRecursion address: com.test.InfiniteRecursion@544a5ab2
]

编译器会试图将”+”后面的this转换为String,此时调用toString()函数,导致无穷递归。因此如果想要打印出内存地址,应该调用Object.toString()方法,所以调用super.toString()就可以了。
4.String上的操作
以下是String对象具备的一些基本方法。
这里写图片描述
这里写图片描述
从图中可以看出,当需要改变字符串的内容时,String类的方法都会返回一个新的String对象。同时,如果内容没有发生改变,String的方法只是返回指向原对象的引用而已。这可以节约存储空间以及避免额外的开销。

0 0
原创粉丝点击