为 String 对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来表示。这时,应该考虑使用 StringBuffer 类,它允许修改,而不是每个不同的字符 串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。 例如我们要在构造器中对一个名叫 s 的 String 引用变量进行初始化,把它设置为初始值, 应当这样做: public class Demo { private String s; ... public Demo { s = Initial Value; } ... } 而非 = new String(Initial Value); 后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String 对象不可改变, 所以对于内容相同的字符串, 只要一个 String 对象来表示就可以了。 也就说,多次调用上面的构造器创建多个对象,他们的 String 类型属性 s 都指向同一个对 象。 上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同 一个 String 对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否 相同。 至于为什么要把 String 类
设计成不可变类,是它的用途决定的。其实不只 String,很 多 Java 标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不 可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因 为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每
个不同的状态都要一个对象来代表,可能会造成性能上的
问题。所以 Java 标准类库还提供 了一个可变版本,即 StringBuffer。 问题四:final 关键字到底修饰了什么? final 使得被修饰的变量不变,但是由于对象型变量的本质是“引用”,使得“不变” 也有了两种含义:引用本身的不变,和引用指向的对象不变。 引用本身的不变: final StringBuffer a=new final StringBuffer b=new a=b;//编译期错误 引用指向的对象不变: final StringBuffer a=new StringBuffer(immutable); StringBuffer(not immutable); s
StringBuffer(immutable);
a.append( broken!); //编译通过 可见,final 只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引 用只能指向初始指向的那个对象, 改变它的指向会导致编译期错误。 至于它所指向的对象的 变化,final 是不负责的。这很类似==操作符:==操作符只负责引用的“值”相等,至于这 个地址所指向的对象内容是否相等,==操作符是不管的。 理解 final 问题有很重要的含义。 许多程序漏洞都基于此----final 只能保证引用永远 指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程 共享或修改, 一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。 一个 错误的解决方法就是在此对象新建的时候把它声明为 final,意图使得它“永远不变”。其 实那是徒劳的。 问题五:到底要怎么样初始化! 本问题讨论变量的初始化,所以先来看一下
Java 中有哪些种类的变量。 1. 类的属性,或者叫值域 2. 方法里的局部变量 3. 方法的参数 对于第一种变量,Java 虚拟机会自动进行初始化。如果给出了初始值,则初始化为该 初始值。如果没有给出,则把它初始化为该类型变量的默认初始值。 int 类型变量默认初始值为 0 float 类型变量默认初始值为 0.0f double 类型变量默认初始值为 0.0 boolean 类型变量默认初始值为 false char 类型变量默认初始值为 0(ASCII 码) long 类型变量默认初始值为 0 所有对象引用类型变量默认初始值为 null,即不指向任何对象。注意数组本身也是对 象,所以没有初始化的数组引用在自动初始化后其值也是 null。 对于两种不同的类属性,