一、clone
- 淺克?。簞?chuàng)建一個(gè)新對象,新對象的屬性和原來對象完全相同,對于非基本類型屬性,仍指向原有屬性所指向的對象的內(nèi)存地址。
- 深克?。簞?chuàng)建一個(gè)新對象,屬性中引用的其他對象也會(huì)被克隆,不再指向原有對象地址。
總之深淺克隆都會(huì)在堆中新分配一塊區(qū)域,區(qū)別在于對象屬性引用的對象是否需要進(jìn)行克?。ㄟf歸性的)。
case:
public class Main {
public static void main(String[] args) {
// 創(chuàng)建父親(LiSi),兒子(LiWu),孫子(LiLiu)并關(guān)聯(lián)
Son father = new Son();
father.setName("LiSi");
Son son = new Son();
son.setName("LiWu");
Son grandSon = new Son();
grandSon.setName("LiLiu");
father.setSon(son);
son.setSon(grandSon);
// 調(diào)用clone方法
Son fatherCopy = null;
try {
fatherCopy = (Son) father.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
boolean flag1 = fatherCopy==father;
boolean flag2 = fatherCopy.getSon() == son;
boolean flag3 = fatherCopy.getSon().getSon() == grandSon;
// 比較克隆后的地址
System.out.println(flag1);// false
System.out.println(flag2);// true
System.out.println(flag3);// true
// 比較Name
flag1= fatherCopy.getName()==father.getName();
flag2 = fatherCopy.getSon().getName() == son.getName();
flag3 = fatherCopy.getSon().getSon().getName() == grandSon.getName();
System.out.println(flag1);// true
System.out.println(flag2);// true
System.out.println(flag3);// true
}
}
如上,如果對象實(shí)現(xiàn)Cloneable并重寫clone方法不進(jìn)行任何操作時(shí),調(diào)用clone是進(jìn)行的淺克隆。
既然實(shí)現(xiàn)Cloneable接口并重寫clone接口只能進(jìn)行淺克隆。但是如果類的引用類型屬性(以及屬性的引用類型屬性)都進(jìn)行淺克隆,直到?jīng)]有引用類型屬性或者引用類型屬性為null時(shí),整體上就形成了深克隆。既對象的引用類型屬性和屬性的應(yīng)用類型屬性都實(shí)現(xiàn)Coloneable,重寫clone方法并在clone方法中進(jìn)行調(diào)用。
protected Object clone() throws CloneNotSupportedException {
Son result = (Son) super.clone();
if (son != null) {
result.son = (Son) son.clone();
}
return result;
}
二、equals and hashCode
equals
equals 方法既然是基類 Object 的方法,我們創(chuàng)建的所有的對象都擁有這個(gè)方法,并有權(quán)利去重寫這個(gè)方法。該方法返回一個(gè) boolean 值,代表比較的兩個(gè)對象是否相同,這里的相同的條件由重寫 equals 方法的類來解決。
String str1 = "abc";
String str2 = "abc";
str1.equals(str2);//true
顯然 String 類一定重寫了 equals 方法否則兩個(gè) String 對象內(nèi)存地址肯定不同。
public boolean equals(Object anObject) {
//首先判斷兩個(gè)對象的內(nèi)存地址是否相同
if (this == anObject) {
return true;
}
// 判斷連個(gè)對象是否屬于同一類型。
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
//長度相同的情況下逐一比較 char 數(shù)組中的每個(gè)元素是否相同
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;}
hashCode
hashCode()定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode() 函數(shù)。
hashCode()的作用是獲取hash值,也稱為散列碼;它實(shí)際上是返回一個(gè)int整數(shù)。這個(gè)hash值的作用是確定該對象在哈希表中的索引位置。也就是說hash值要在一些數(shù)據(jù)結(jié)構(gòu)中才能顯現(xiàn)作用。hashcode一般用在hashmap、hashset里,判斷要把數(shù)據(jù)放在數(shù)組的什么位置。
下面,我們以HashSet為例,來深入說明hashCode()的作用。
假設(shè),HashSet中已經(jīng)有1000個(gè)元素。當(dāng)插入第1001個(gè)元素時(shí),需要怎么處理?因?yàn)镠ashSet是Set集合,它不允許有重復(fù)元素。 “將第1001個(gè)元素逐個(gè)的和前面1000個(gè)元素進(jìn)行比較”?顯然,這個(gè)效率是相等低下的。散列表很好的解決了這個(gè)問題,它根據(jù)元素的散列碼計(jì)算出元素在散列表中的位置,然后將元素插入該位置即可。對于相同的元素,自然是只保存了一個(gè)。 由此可知,若兩個(gè)對象相等,它們的散列碼一定相等;但反過來不一定。在散列表中,
1、如果兩個(gè)對象相等,那么它們的hashCode()值一定要相同。這里的相等是指,通過equals()比較兩個(gè)對象時(shí)返回true。
2、如果兩個(gè)對象hashCode()相等,它們并不一定相等。
轉(zhuǎn)自:http://www.itdecent.cn/p/cf164837f8ed
三、getClass
getClass方法是獲得Class對象的方法之一
class A{
public void func(){
}
}
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(a.getClass()); // class A
}
}
反射學(xué)習(xí)
所謂反射,可以理解為在運(yùn)行時(shí)期獲取對象類型信息的操作。傳統(tǒng)的編程方法要求程序員在編譯階段決定使用的類型,但是在反射的幫助下,編程人員可以動(dòng)態(tài)獲取這些信息,從而編寫更加具有可移植性的代碼。嚴(yán)格地說,反射并非編程語言的特性,因?yàn)樵谌魏我环N語言都可以實(shí)現(xiàn)反射機(jī)制,但是如果編程語言本身支持反射,那么反射的實(shí)現(xiàn)就會(huì)方便很多。
Java反射機(jī)制主要提供了以下功能: 在運(yùn)行時(shí)判斷任意一個(gè)對象所屬的類;在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對象;在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法;在運(yùn)行時(shí)調(diào)用任意一個(gè)對象的方法;生成動(dòng)態(tài)代理。
補(bǔ)充:獲取class對象的三種方式
- 使用forName()方法:
Class<?> clazz = Class.forName(className); - 調(diào)用屬性:
Class<?> clazz = Person.class; - 利用對象調(diào)用Object的getClass方法
Person p = new Person(); Class<?> clazz = p.getClass();
四、toString
toString()是Object類的一個(gè)公有方法,而所有類都繼承自O(shè)bject類。所以所有類即使不實(shí)現(xiàn)toString方法,也會(huì)存在從Object類繼承來的toString。
類可以實(shí)現(xiàn)toString方法,在控制臺(tái)中打印一個(gè)對象會(huì)自動(dòng)調(diào)用對象類的toString方法。打印的是對象名 + @換為十六進(jìn)制的對象的哈希值
public class Test {
public static void main(String[] args){
Car car1 = new Car("寶馬",300000.00);
Car car2 = new Car("奔馳",500000.00);
System.out.println(car1); // Car@4554617c
System.out.println(car2); // Car@74a14482
}
}
我們也可以實(shí)現(xiàn)自己從寫toString方法在控制臺(tái)中顯示關(guān)于類的有用信息。
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
// 再次運(yùn)行上面的Test代碼得
// Car{name='寶馬', price=300000.0}
// Car{name='奔馳', price=500000.0}
八種基本數(shù)據(jù)類型沒有toString()方法,只能使用相應(yīng)的包裝類才能使用toString()。