1、Clone方法
1.1、不使用clone方法
Employee tobby = newEmployee(“CMTobby”,5000);
Employee cindyelf = tobby;
这样的操作只是浅复制,tobby和cindyelf指向内存中同一个Employee类型对象,tobby.setSalary改变salary值之后,cindyelf.getSalary获取的就是salary改变之后的值。
1.2、使用clone方法
注意:
- 调用clone方法的对象类必须实现(implements)clonable接口,否则的话就会抛出CloneNotSupportedException,同时为了能是其他类能调用这个类的clone方法,最好将clone方法改为public
这里调用的clone相当于调用的是父类Object的方法,父类方法是protected,很显然不能这样调用。
这里因为MyObject重写了方法,虽然还是protected,但是test1与MyObject在同一个包里面,所以可以调用。
- Object类中就有clone方法(protected),但是Object又没有实现clonable接口,所以Object不能直接调用clone方法
- 执行clone方法时首先会判断是否实现clonable接口,否则抛出错误
如果自定义类使用super.clone()方法调用object默认的clone方法,这样只是得到的一个浅复制,即基本类型直接复制一个值到复制类中,复制类中对基本类型数据改变不会影响被复制类中基本类型的值;而对于引用类型,只会复制一个引用,指向的还是被复制类中的引用对象,在复制类中对引用的改变会改变被复制类中引用变量的内容。
如果自定义类自己定义深复制,则需要新建一个引用对象,将被复制类中的引用变量的内容全部复制过来。
2、相等性判断
2.1、==
在java中利用“==”比较变量时,系统使用变量在栈中所存的值进行比较,所以:
- 对于基本变量,就是比较变量的内容值
- 对于引用类型变量,则比较的是所指向的对象的地址
- 对于java.net.URL类,联网状态下直接比较url地址字符串;如果联网状态下则比较对应的IP地址
2.2、equals
equals只能应用于引用变量(String除外,String比较的值内容),Object类中equals方法的定义如下:
boolean equals(Object o){
return this==o;
}
所以使用系统默认的equals方法时,还是比较的地址值,而不是对象内容值。
为了能比较对象内容的值,则必须在自定义类中对equals进行重写,同时最好也要将hashcode也进行重写(需要注意hashcode的特性——每次new一个object,这个object的hashcode是永远不同的,这样的话可能会影响equals的比较,关于hashcode具体的介绍见:http://fhuan123.iteye.com/blog/1452275)。
重写的一般步骤:
CloneObject obj = new CloneObject();
obj.clone(); // Compile ok
publicboolean equals(Object o){
//重写equals方法,后面最好重写hashCode方法
if(this == o) //这样效率高
returntrue;
if(o ==null)
returnfalse;
if( !(oinstanceof CloneObject))
returnfalse;
final CloneObject co = (CloneObject)o;
// 变量具体比较操作
3、instanceof、getclass()判断类对象
publicclass InstanceofGetclassDiffextends parent{
publicstatic void main(String[] args)
{
InstanceofGetclassDiffa = newInstanceofGetclassDiff();
parentb = newparent();
System.out.println(ainstanceof parent); //输出true
System.out.println(binstanceof InstanceofGetclassDiff); //输出false
System.out.println(a.getClass() == b.getClass());//输出false
}
}
class parent{}
可以看出instanceof可以判断继承关系,即“子类实例 instanceof 父类名称”这个模式,而getClass无法判断继承关系,只是简单的判断是否是一类。推广到Equals方法中的应用,当子类中自定义Equals时,则最好用getClass方法,反之如果所有子类都继承父类的Equals方法,则用instanceof。
4、String对象
4.1、Java内存分析
- 栈(Stack) :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中)。
- 堆(heap):存放所有new出来的对象,在运行期创建。
- 常量池(constant pool):在堆中分配出来的一块存储区域,存放储显式的String常量和基本类型常量(float、int等)。另外,可以存储不经常改变的东西(public static final),在编译期就创建。常量池中的数据可以共享。
- 静态存储:存放静态成员(static定义的)。
4.2、String str =“abc”
String a = "abc";
String b = "abc";
分析:第一行代码执行后在常量池(constantpool)中创建了一个值为abc的String对象,第二行执行时,因为常量池中存在"abc"所以就不再创建新的String对象了。
4.3、String str =new String(“abc”)
String c = new String("xyz");
String d = new String("xyz");
分析:第一行中Class被加载时,"xyz"被作为常量读入,在常量池(constantpool)里创建了一个共享的值为"xyz"的String对象;然后当调用到new String("xyz")的时候,会在堆(heap)里创建这个new String("xyz")对象;第二行执行时,由于常量池(constant pool)中存在"xyz"所以不再创建"xyz",然后创建新的new String("xyz")。
4.4、String str =“123” + new String(“abc”)
这行复制语句表示的是先在常量池(constantpool)里创建了一个共享的值为"xyz"的String对象(编译期确定);new String(“abc”),不能在编译期完成,需要在堆中创建对象,通过“+”操作生成一个新的字符串(在常量池中的值是“123abc”?),同时返回一个String类型变量引用。
String s0=”kvill”;
String s1=new String(”kvill”);
String s2=”kv” + new String(“ill”);
System.out.println( s0==s1 ); //false
System.out.println( s0==s2 ); //false
System.out.println( s1==s2 ); //false
- String.intern()应用
返回的是String变量值内容在常量池中值的引用。
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
System.out.println("s1 == s2");//true