這篇文章主要講解了“Java中Class類和Object類的詳細(xì)講解”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java中Class類和Object類的詳細(xì)講解”吧!
成都創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),交城企業(yè)網(wǎng)站建設(shè),交城品牌網(wǎng)站建設(shè),網(wǎng)站定制,交城網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,交城網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
Java程序在運(yùn)行時(shí),Java運(yùn)行時(shí)系統(tǒng)一直對(duì)所有的對(duì)象進(jìn)行所謂的運(yùn)行時(shí)類型標(biāo)識(shí),即所謂的RTTI。
這項(xiàng)信息紀(jì)錄了每個(gè)對(duì)象所屬的類。虛擬機(jī)通常使用運(yùn)行時(shí)類型信息選準(zhǔn)正確方法去執(zhí)行,用來(lái)保存這些類型信息的類是Class類。Class類封裝一個(gè)對(duì)象和接口運(yùn)行時(shí)的狀態(tài),當(dāng)裝載類時(shí),Class類型的對(duì)象自動(dòng)創(chuàng)建。
說(shuō)白了就是:
Class類也是類的一種,只是名字和class關(guān)鍵字高度相似。Java是大小寫敏感的語(yǔ)言。
Class類的對(duì)象內(nèi)容是你創(chuàng)建的類的類型信息,比如你創(chuàng)建一個(gè)shapes類,那么,Java會(huì)生成一個(gè)內(nèi)容是shapes的Class類的對(duì)象
Class類的對(duì)象不能像普通類一樣,以 new shapes() 的方式創(chuàng)建,它的對(duì)象只能由JVM創(chuàng)建,因?yàn)檫@個(gè)類沒(méi)有public構(gòu)造函數(shù)
/* * Private constructor. Only the Java Virtual Machine creates Class objects. * This constructor is not used and prevents the default constructor being * generated. */ //私有構(gòu)造方法,只能由jvm進(jìn)行實(shí)例化 private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
Class類的作用是運(yùn)行時(shí)提供或獲得某個(gè)對(duì)象的類型信息,和C++中的typeid()函數(shù)類似。這些信息也可用于反射。
看一下Class類的部分源碼
//Class類中封裝了類型的各種信息。在jvm中就是通過(guò)Class類的實(shí)例來(lái)獲取每個(gè)Java類的所有信息的。 public class Class類 { Class aClass = null; // private EnclosingMethodInfo getEnclosingMethodInfo() { // Object[] enclosingInfo = getEnclosingMethod0(); // if (enclosingInfo == null) // return null; // else { // return new EnclosingMethodInfo(enclosingInfo); // } // } /**提供原子類操作 * Atomic operations support. */ // private static class Atomic { // // initialize Unsafe machinery here, since we need to call Class.class instance method // // and have to avoid calling it in the static initializer of the Class class... // private static final Unsafe unsafe = Unsafe.getUnsafe(); // // offset of Class.reflectionData instance field // private static final long reflectionDataOffset; // // offset of Class.annotationType instance field // private static final long annotationTypeOffset; // // offset of Class.annotationData instance field // private static final long annotationDataOffset; // // static { // Field[] fields = Class.class.getDeclaredFields0(false); // bypass caches // reflectionDataOffset = objectFieldOffset(fields, "reflectionData"); // annotationTypeOffset = objectFieldOffset(fields, "annotationType"); // annotationDataOffset = objectFieldOffset(fields, "annotationData"); // } //提供反射信息 // reflection data that might get invalidated when JVM TI RedefineClasses() is called // private static class ReflectionData<T> { // volatile Field[] declaredFields; // volatile Field[] publicFields; // volatile Method[] declaredMethods; // volatile Method[] publicMethods; // volatile Constructor<T>[] declaredConstructors; // volatile Constructor<T>[] publicConstructors; // // Intermediate results for getFields and getMethods // volatile Field[] declaredPublicFields; // volatile Method[] declaredPublicMethods; // volatile Class<?>[] interfaces; // // // Value of classRedefinedCount when we created this ReflectionData instance // final int redefinedCount; // // ReflectionData(int redefinedCount) { // this.redefinedCount = redefinedCount; // } // } //方法數(shù)組 // static class MethodArray { // // Don't add or remove methods except by add() or remove() calls. // private Method[] methods; // private int length; // private int defaults; // // MethodArray() { // this(20); // } // // MethodArray(int initialSize) { // if (initialSize < 2) // throw new IllegalArgumentException("Size should be 2 or more"); // // methods = new Method[initialSize]; // length = 0; // defaults = 0; // } //注解信息 // annotation data that might get invalidated when JVM TI RedefineClasses() is called // private static class AnnotationData { // final Map<Class<? extends Annotation>, Annotation> annotations; // final Map<Class<? extends Annotation>, Annotation> declaredAnnotations; // // // Value of classRedefinedCount when we created this AnnotationData instance // final int redefinedCount; // // AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations, // Map<Class<? extends Annotation>, Annotation> declaredAnnotations, // int redefinedCount) { // this.annotations = annotations; // this.declaredAnnotations = declaredAnnotations; // this.redefinedCount = redefinedCount; // } // } }
我們都知道所有的java類都是繼承了object這個(gè)類,在object這個(gè)類中有一個(gè)方法:getclass().這個(gè)方法是用來(lái)取得該類已經(jīng)被實(shí)例化了的對(duì)象的該類的引用,這個(gè)引用指向的是Class類的對(duì)象。
我們自己無(wú)法生成一個(gè)Class對(duì)象(構(gòu)造函數(shù)為private),而 這個(gè)Class類的對(duì)象是在當(dāng)各類被調(diào)入時(shí),由 Java 虛擬機(jī)自動(dòng)創(chuàng)建 Class 對(duì)象,或通過(guò)類裝載器中的 defineClass 方法生成。
//通過(guò)該方法可以動(dòng)態(tài)地將字節(jié)碼轉(zhuǎn)為一個(gè)Class類對(duì)象 protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError { return defineClass(name, b, off, len, null); }
我們生成的對(duì)象都會(huì)有個(gè)字段記錄該對(duì)象所屬類在CLass類的對(duì)象的所在位置。如下圖所示:
cdn.xitu.io/2019/4/7/169f69b16efd7dd8?w=436&h=373&f=jpeg&s=31804">
請(qǐng)注意,以下這些方法都是值、指某個(gè)類對(duì)應(yīng)的Class對(duì)象已經(jīng)在堆中生成以后,我們通過(guò)不同方式獲取對(duì)這個(gè)Class對(duì)象的引用。而上面說(shuō)的DefineClass才是真正將字節(jié)碼加載到虛擬機(jī)的方法,會(huì)在堆中生成新的一個(gè)Class對(duì)象。
第一種辦法,Class類的forName函數(shù)
public class shapes{}
Class obj= Class.forName("shapes"); 第二種辦法,使用對(duì)象的getClass()函數(shù)
public class shapes{} shapes s1=new shapes(); Class obj=s1.getClass(); Class obj1=s1.getSuperclass();//這個(gè)函數(shù)作用是獲取shapes類的父類的類型
第三種辦法,使用類字面常量
Class obj=String.class; Class obj1=int.class; 注意,使用這種辦法生成Class類對(duì)象時(shí),不會(huì)使JVM自動(dòng)加載該類(如String類)。==而其他辦法會(huì)使得JVM初始化該類。==
生成不精確的object實(shí)例
==獲取一個(gè)Class類的對(duì)象后,可以用 newInstance() 函數(shù)來(lái)生成目標(biāo)類的一個(gè)實(shí)例。然而,該函數(shù)并不能直接生成目標(biāo)類的實(shí)例,只能生成object類的實(shí)例==
Class obj=Class.forName("shapes"); Object ShapesInstance=obj.newInstance(); 使用泛化Class引用生成帶類型的目標(biāo)實(shí)例
Class obj=shapes.class; shapes newShape=obj.newInstance(); 因?yàn)橛辛祟愋拖拗?,所以使用泛化Class語(yǔ)法的對(duì)象引用不能指向別的類。
Class obj1=int.class; Class<Integer> obj2=int.class; obj1=double.class; //obj2=double.class; 這一行代碼是非法的,obj2不能改指向別的類 然而,有個(gè)靈活的用法,使得你可以用Class的對(duì)象指向基類的任何子類。 Class<? extends Number> obj=int.class; obj=Number.class; obj=double.class; 因此,以下語(yǔ)法生成的Class對(duì)象可以指向任何類。 Class<?> obj=int.class; obj=double.class; obj=shapes.class; 最后一個(gè)奇怪的用法是,當(dāng)你使用這種泛型語(yǔ)法來(lái)構(gòu)建你手頭有的一個(gè)Class類的對(duì)象的基類對(duì)象時(shí),必須采用以下的特殊語(yǔ)法 public class shapes{} class round extends shapes{} Class<round> rclass=round.class; Class<? super round> sclass= rclass.getSuperClass(); //Class<shapes> sclass=rclass.getSuperClass(); 我們明知道,round的基類就是shapes,但是卻不能直接聲明 Class < shapes >,必須使用特殊語(yǔ)法 Class < ? super round >
這個(gè)記住就可以啦。
這部分主要參考http://ihenu.iteye.com/blog/2233249
Object類是Java中其他所有類的祖先,沒(méi)有Object類Java面向?qū)ο鬅o(wú)從談起。作為其他所有類的基類,Object具有哪些屬性和行為,是Java語(yǔ)言設(shè)計(jì)背后的思維體現(xiàn)。
Object類位于java.lang包中,java.lang包包含著Java最基礎(chǔ)和核心的類,在編譯時(shí)會(huì)自動(dòng)導(dǎo)入。Object類沒(méi)有定義屬性,一共有13個(gè)方法,13個(gè)方法之中并不是所有方法都是子類可訪問(wèn)的,一共有9個(gè)方法是所有子類都繼承了的。
先大概介紹一下這些方法
1.clone方法 保護(hù)方法,實(shí)現(xiàn)對(duì)象的淺復(fù)制,只有實(shí)現(xiàn)了Cloneable接口才可以調(diào)用該方法,否則拋出CloneNotSupportedException異常。 2.getClass方法 final方法,獲得運(yùn)行時(shí)類型。 3.toString方法 該方法用得比較多,一般子類都有覆蓋。 4.finalize方法 該方法用于釋放資源。因?yàn)闊o(wú)法確定該方法什么時(shí)候被調(diào)用,很少使用。 5.equals方法 該方法是非常重要的一個(gè)方法。一般equals和==是不一樣的,但是在Object中兩者是一樣的。子類一般都要重寫這個(gè)方法。 6.hashCode方法 該方法用于哈希查找,重寫了equals方法一般都要重寫hashCode方法。這個(gè)方法在一些具有哈希功能的Collection中用到。 一般必須滿足obj1.equals(obj2)==true??梢酝瞥鰋bj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就滿足equals。不過(guò)為了提高效率,應(yīng)該盡量使上面兩個(gè)條件接近等價(jià)。 7.wait方法 wait方法就是使當(dāng)前線程等待該對(duì)象的鎖,當(dāng)前線程必須是該對(duì)象的擁有者,也就是具有該對(duì)象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設(shè)定一個(gè)超時(shí)間隔,如果在規(guī)定時(shí)間內(nèi)沒(méi)有獲得鎖就返回。 調(diào)用該方法后當(dāng)前線程進(jìn)入睡眠狀態(tài),直到以下事件發(fā)生。 (1)其他線程調(diào)用了該對(duì)象的notify方法。 (2)其他線程調(diào)用了該對(duì)象的notifyAll方法。 (3)其他線程調(diào)用了interrupt中斷該線程。 (4)時(shí)間間隔到了。 此時(shí)該線程就可以被調(diào)度了,如果是被中斷的話就拋出一個(gè)InterruptedException異常。 8.notify方法 該方法喚醒在該對(duì)象上等待的某個(gè)線程。 9.notifyAll方法 該方法喚醒在該對(duì)象上等待的所有線程。
大部分情況下,Java中通過(guò)形如 new A(args..)形式創(chuàng)建一個(gè)屬于該類型的對(duì)象。其中A即是類名,A(args..)即此類定義中相對(duì)應(yīng)的構(gòu)造函數(shù)。通過(guò)此種形式創(chuàng)建的對(duì)象都是通過(guò)類中的構(gòu)造函數(shù)完成。
為體現(xiàn)此特性,Java中規(guī)定:在類定義過(guò)程中,對(duì)于未定義構(gòu)造函數(shù)的類,默認(rèn)會(huì)有一個(gè)無(wú)參數(shù)的構(gòu)造函數(shù),作為所有類的基類,Object類自然要反映出此特性,在源碼中,未給出Object類構(gòu)造函數(shù)定義,但實(shí)際上,此構(gòu)造函數(shù)是存在的。
當(dāng)然,并不是所有的類都是通過(guò)此種方式去構(gòu)建,也自然的,并不是所有的類構(gòu)造函數(shù)都是public。
private static native void registerNatives();
registerNatives函數(shù)前面有native關(guān)鍵字修飾,Java中,用native關(guān)鍵字修飾的函數(shù)表明該方法的實(shí)現(xiàn)并不是在Java中去完成,而是由C/C++去完成,并被編譯成了.dll,由Java去調(diào)用。
方法的具體實(shí)現(xiàn)體在dll文件中,對(duì)于不同平臺(tái),其具體實(shí)現(xiàn)應(yīng)該有所不同。用native修飾,即表示操作系統(tǒng),需要提供此方法,Java本身需要使用。
具體到registerNatives()方法本身,其主要作用是將C/C++中的方法映射到Java中的native方法,實(shí)現(xiàn)方法命名的解耦。
既然如此,可能有人會(huì)問(wèn),registerNatives()修飾符為private,且并沒(méi)有執(zhí)行,作用何以達(dá)到?其實(shí),在Java源碼中,此方法的聲明后有緊接著一段靜態(tài)代碼塊:
private static native void registerNatives(); static { registerNatives(); }
protected native Object clone() throwsCloneNotSupportedException;
看,clode()方法又是一個(gè)被聲明為native的方法,因此,我們知道了clone()方法并不是Java的原生方法,具體的實(shí)現(xiàn)是有C/C++完成的。clone英文翻譯為"克隆",其目的是創(chuàng)建并返回此對(duì)象的一個(gè)副本。
形象點(diǎn)理解,這有一輛科魯茲,你看著不錯(cuò),想要個(gè)一模一樣的。你調(diào)用此方法即可像變魔術(shù)一樣變出一輛一模一樣的科魯茲出來(lái)。配置一樣,長(zhǎng)相一樣。但從此刻起,原來(lái)的那輛科魯茲如果進(jìn)行了新的裝飾,與你克隆出來(lái)的這輛科魯茲沒(méi)有任何關(guān)系了。
你克隆出來(lái)的對(duì)象變不變完全在于你對(duì)克隆出來(lái)的科魯茲有沒(méi)有進(jìn)行過(guò)什么操作了。Java術(shù)語(yǔ)表述為:clone函數(shù)返回的是一個(gè)引用,指向的是新的clone出來(lái)的對(duì)象,此對(duì)象與原對(duì)象分別占用不同的堆空間。
明白了clone的含義后,接下來(lái)看看如果調(diào)用clone()函數(shù)對(duì)象進(jìn)行此克隆操作。
首先看一下下面的這個(gè)例子:
package com.corn.objectsummary; import com.corn.Person; public class ObjectTest { public static void main(String[] args) { Object o1 = new Object(); // The method clone() from the type Object is not visible Object clone = o1.clone(); } }
例子很簡(jiǎn)單,在main()方法中,new一個(gè)Oject對(duì)象后,想直接調(diào)用此對(duì)象的clone方法克隆一個(gè)對(duì)象,但是出現(xiàn)錯(cuò)誤提示:"The method clone() from the type Object is not visible"
why? 根據(jù)提示,第一反應(yīng)是ObjectTest類中定義的Oject對(duì)象無(wú)法訪問(wèn)其clone()方法?;氐絆bject類中clone()方法的定義,可以看到其被聲明為protected,估計(jì)問(wèn)題就在這上面了,protected修飾的屬性或方法表示:在同一個(gè)包內(nèi)或者不同包的子類可以訪問(wèn)。
顯然,Object類與ObjectTest類在不同的包中,但是ObjectTest繼承自O(shè)bject,是Object類的子類,于是,現(xiàn)在卻出現(xiàn)子類中通過(guò)Object引用不能訪問(wèn)protected方法,原因在于對(duì)"不同包中的子類可以訪問(wèn)"沒(méi)有正確理解。
"不同包中的子類可以訪問(wèn)",是指當(dāng)兩個(gè)類不在同一個(gè)包中的時(shí)候,繼承自父類的子類內(nèi)部且主調(diào)(調(diào)用者)為子類的引用時(shí)才能訪問(wèn)父類用protected修飾的成員(屬性/方法)。 在子類內(nèi)部,主調(diào)為父類的引用時(shí)并不能訪問(wèn)此protected修飾的成員。?。╯uper關(guān)鍵字除外)
于是,上例改成如下形式,我們發(fā)現(xiàn),可以正常編譯:
public class clone方法 { public static void main(String[] args) { } public void test1() { User user = new User(); // User copy = user.clone(); } public void test2() { User user = new User(); // User copy = (User)user.clone(); } }
是的,因?yàn)榇藭r(shí)的主調(diào)已經(jīng)是子類的引用了。
上述代碼在運(yùn)行過(guò)程中會(huì)拋出"java.lang.CloneNotSupportedException",表明clone()方法并未正確執(zhí)行完畢,問(wèn)題的原因在與Java中的語(yǔ)法規(guī)定:
clone()的正確調(diào)用是需要實(shí)現(xiàn)Cloneable接口,如果沒(méi)有實(shí)現(xiàn)Cloneable接口,并且子類直接調(diào)用Object類的clone()方法,則會(huì)拋出CloneNotSupportedException異常。
Cloneable接口僅是一個(gè)表示接口,接口本身不包含任何方法,用來(lái)指示Object.clone()可以合法的被子類引用所調(diào)用。
于是,上述代碼改成如下形式,即可正確指定clone()方法以實(shí)現(xiàn)克隆。
public class User implements Cloneable{ public int id; public String name; public UserInfo userInfo; public static void main(String[] args) { User user = new User(); UserInfo userInfo = new UserInfo(); user.userInfo = userInfo; System.out.println(user); System.out.println(user.userInfo); try { User copy = (User) user.clone(); System.out.println(copy); System.out.println(copy.userInfo); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } //拷貝的User實(shí)例與原來(lái)不一樣,是兩個(gè)對(duì)象。 // com.javase.Class和Object.Object方法.用到的類.User@4dc63996 // com.javase.Class和Object.Object方法.用到的類.UserInfo@d716361 //而拷貝后對(duì)象的userinfo引用對(duì)象是同一個(gè)。 //所以這是淺拷貝 // com.javase.Class和Object.Object方法.用到的類.User@6ff3c5b5 // com.javase.Class和Object.Object方法.用到的類.UserInfo@d716361 }
總結(jié): clone方法實(shí)現(xiàn)的是淺拷貝,只拷貝當(dāng)前對(duì)象,并且在堆中分配新的空間,放這個(gè)復(fù)制的對(duì)象。但是對(duì)象如果里面有其他類的子對(duì)象,那么就不會(huì)拷貝到新的對(duì)象中。
==深拷貝和淺拷貝的區(qū)別==
淺拷貝 淺拷貝是按位拷貝對(duì)象,它會(huì)創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址 ,因此如果其中一個(gè)對(duì)象改變了這個(gè)地址,就會(huì)影響到另一個(gè)對(duì)象。
深拷貝 深拷貝會(huì)拷貝所有的屬性,并拷貝屬性指向的動(dòng)態(tài)分配的內(nèi)存。當(dāng)對(duì)象和它所引用的對(duì)象一起拷貝時(shí)即發(fā)生深拷貝。深拷貝相比于淺拷貝速度較慢并且花銷較大。 現(xiàn)在為了要在clone對(duì)象時(shí)進(jìn)行深拷貝, 那么就要Clonable接口,覆蓋并實(shí)現(xiàn)clone方法,除了調(diào)用父類中的clone方法得到新的對(duì)象, 還要將該類中的引用變量也clone出來(lái)。如果只是用Object中默認(rèn)的clone方法,是淺拷貝的。
那么這兩種方式有什么相同和不同呢?
new操作符的本意是分配內(nèi)存。程序執(zhí)行到new操作符時(shí), 首先去看new操作符后面的類型,因?yàn)橹懒祟愋?,才能知道要分配多大的?nèi)存空間。
分配完內(nèi)存之后,再調(diào)用構(gòu)造函數(shù),填充對(duì)象的各個(gè)域,這一步叫做對(duì)象的初始化,構(gòu)造方法返回后,一個(gè)對(duì)象創(chuàng)建完畢,可以把他的引用(地址)發(fā)布到外部,在外部就可以使用這個(gè)引用操縱這個(gè)對(duì)象。
而clone在第一步是和new相似的, 都是分配內(nèi)存,調(diào)用clone方法時(shí),分配的內(nèi)存和源對(duì)象(即調(diào)用clone方法的對(duì)象)相同,然后再使用原對(duì)象中對(duì)應(yīng)的各個(gè)域,填充新對(duì)象的域,
填充完成之后,clone方法返回,一個(gè)新的相同的對(duì)象被創(chuàng)建,同樣可以把這個(gè)新對(duì)象的引用發(fā)布到外部。
==也就是說(shuō),一個(gè)對(duì)象在淺拷貝以后,只是把對(duì)象復(fù)制了一份放在堆空間的另一個(gè)地方,但是成員變量如果有引用指向其他對(duì)象,這個(gè)引用指向的對(duì)象和被拷貝的對(duì)象中引用指向的對(duì)象是一樣的。當(dāng)然,基本數(shù)據(jù)類型還是會(huì)重新拷貝一份的。==
4.public final native Class<?> getClass();
getClass()也是一個(gè)native方法,返回的是此Object對(duì)象的類對(duì)象/運(yùn)行時(shí)類對(duì)象Class<?>。效果與Object.class相同。
首先解釋下"類對(duì)象"的概念:在Java中,類是是對(duì)具有一組相同特征或行為的實(shí)例的抽象并進(jìn)行描述,對(duì)象則是此類所描述的特征或行為的具體實(shí)例。
作為概念層次的類,其本身也具有某些共同的特性,如都具有類名稱、由類加載器去加載,都具有包,具有父類,屬性和方法等。
于是,Java中有專門定義了一個(gè)類,Class,去描述其他類所具有的這些特性,因此,從此角度去看,類本身也都是屬于Class類的對(duì)象。為與經(jīng)常意義上的對(duì)象相區(qū)分,在此稱之為"類對(duì)象"。
public class getClass方法 { public static void main(String[] args) { User user = new User(); //getclass方法是native方法,可以取到堆區(qū)唯一的Class<User>對(duì)象 Class<?> aClass = user.getClass(); Class bClass = User.class; try { Class cClass = Class.forName("com.javase.Class和Object.Object方法.用到的類.User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(aClass); System.out.println(bClass); // class com.javase.Class和Object.Object方法.用到的類.User // class com.javase.Class和Object.Object方法.用到的類.User try { User a = (User) aClass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
此處主要大量涉及到Java中的反射知識(shí)
5.public boolean equals(Object obj);
與equals在Java中經(jīng)常被使用,大家也都知道與equals的區(qū)別:
==表示的是變量值完成相同(對(duì)于基礎(chǔ)類型,地址中存儲(chǔ)的是值,引用類型則存儲(chǔ)指向?qū)嶋H對(duì)象的地址);
equals表示的是對(duì)象的內(nèi)容完全相同,此處的內(nèi)容多指對(duì)象的特征/屬性。
實(shí)際上,上面說(shuō)法是不嚴(yán)謹(jǐn)?shù)模嗟闹皇浅R?jiàn)于String類中。首先看一下Object類中關(guān)于equals()方法的定義:
public boolean equals(Object obj) { return (this == obj); }
由此可見(jiàn),Object原生的equals()方法內(nèi)部調(diào)用的正是==,與==具有相同的含義。既然如此,為什么還要定義此equals()方法?
equals()方法的正確理解應(yīng)該是:判斷兩個(gè)對(duì)象是否相等。那么判斷對(duì)象相等的標(biāo)尺又是什么?
如上,在object類中,此標(biāo)尺即為==。當(dāng)然,這個(gè)標(biāo)尺不是固定的,其他類中可以按照實(shí)際的需要對(duì)此標(biāo)尺含義進(jìn)行重定義。如String類中則是依據(jù)字符串內(nèi)容是否相等來(lái)重定義了此標(biāo)尺含義。如此可以增加類的功能型和實(shí)際編碼的靈活性。當(dāng)然了,如果自定義的類沒(méi)有重寫equals()方法來(lái)重新定義此標(biāo)尺,那么默認(rèn)的將是其父類的equals(),直到object基類。
如下場(chǎng)景的實(shí)際業(yè)務(wù)需求,對(duì)于User bean,由實(shí)際的業(yè)務(wù)需求可知當(dāng)屬性u(píng)id相同時(shí),表示的是同一個(gè)User,即兩個(gè)User對(duì)象相等。則可以重寫equals以重定義User對(duì)象相等的標(biāo)尺。
ObjectTest中打印出true,因?yàn)閁ser類定義中重寫了equals()方法,這很好理解,很可能張三是一個(gè)人小名,張三豐才是其大名,判斷這兩個(gè)人是不是同一個(gè)人,這時(shí)只用判斷uid是否相同即可。
如上重寫equals方法表面上看上去是可以了,實(shí)則不然。因?yàn)樗茐牧薐ava中的約定:重寫equals()方法必須重寫hasCode()方法。
public native int hashCode()
hashCode()方法返回一個(gè)整形數(shù)值,表示該對(duì)象的哈希碼值。
hashCode()具有如下約定:
1).在Java應(yīng)用程序程序執(zhí)行期間,對(duì)于同一對(duì)象多次調(diào)用hashCode()方法時(shí),其返回的哈希碼是相同的,前提是將對(duì)象進(jìn)行equals比較時(shí)所用的標(biāo)尺信息未做修改。在Java應(yīng)用程序的一次執(zhí)行到另外一次執(zhí)行,同一對(duì)象的hashCode()返回的哈希碼無(wú)須保持一致;
2).如果兩個(gè)對(duì)象相等(依據(jù):調(diào)用equals()方法),那么這兩個(gè)對(duì)象調(diào)用hashCode()返回的哈希碼也必須相等;
3).反之,兩個(gè)對(duì)象調(diào)用hasCode()返回的哈希碼相等,這兩個(gè)對(duì)象不一定相等。
即嚴(yán)格的數(shù)學(xué)邏輯表示為: 兩個(gè)對(duì)象相等 <=> equals()相等 => hashCode()相等。因此,重寫equlas()方法必須重寫hashCode()方法,以保證此邏輯嚴(yán)格成立,同時(shí)可以推理出:hasCode()不相等 => equals()不相等 <=> 兩個(gè)對(duì)象不相等。 可能有人在此產(chǎn)生疑問(wèn):既然比較兩個(gè)對(duì)象是否相等的唯一條件(也是沖要條件)是equals,那么為什么還要弄出一個(gè)hashCode(),并且進(jìn)行如此約定,弄得這么麻煩? 其實(shí),這主要體現(xiàn)在hashCode()方法的作用上,其主要用于增強(qiáng)哈希表的性能。 以集合類中,以Set為例,當(dāng)新加一個(gè)對(duì)象時(shí),需要判斷現(xiàn)有集合中是否已經(jīng)存在與此對(duì)象相等的對(duì)象,如果沒(méi)有hashCode()方法,需要將Set進(jìn)行一次遍歷,并逐一用equals()方法判斷兩個(gè)對(duì)象是否相等,此種算法時(shí)間復(fù)雜度為o(n)。通過(guò)借助于hasCode方法,先計(jì)算出即將新加入對(duì)象的哈希碼,然后根據(jù)哈希算法計(jì)算出此對(duì)象的位置,直接判斷此位置上是否已有對(duì)象即可。(注:Set的底層用的是Map的原理實(shí)現(xiàn))
在此需要糾正一個(gè)理解上的誤區(qū):對(duì)象的hashCode()返回的不是對(duì)象所在的物理內(nèi)存地址。甚至也不一定是對(duì)象的邏輯地址,hashCode()相同的兩個(gè)對(duì)象,不一定相等,換言之,不相等的兩個(gè)對(duì)象,hashCode()返回的哈希碼可能相同。
因此,在上述代碼中,重寫了equals()方法后,需要重寫hashCode()方法。
public class equals和hashcode方法 { @Override //修改equals時(shí)必須同時(shí)修改hashcode方法,否則在作為key時(shí)會(huì)出問(wèn)題 public boolean equals(Object obj) { return (this == obj); } @Override //相同的對(duì)象必須有相同hashcode,不同對(duì)象可能有相同hashcode public int hashCode() { return hashCode() >> 2; } }
7.public String toString();
toString()方法返回該對(duì)象的字符串表示。先看一下Object中的具體方法體: public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
toString()方法相信大家都經(jīng)常用到,即使沒(méi)有顯式調(diào)用,但當(dāng)我們使用System.out.println(obj)時(shí),其內(nèi)部也是通過(guò)toString()來(lái)實(shí)現(xiàn)的。
getClass()返回對(duì)象的類對(duì)象,getClassName()以String形式返回類對(duì)象的名稱(含包名)。Integer.toHexString(hashCode())則是以對(duì)象的哈希碼為實(shí)參,以16進(jìn)制無(wú)符號(hào)整數(shù)形式返回此哈希碼的字符串表示形式。
如上例中的u1的哈希碼是638,則對(duì)應(yīng)的16進(jìn)制為27e,調(diào)用toString()方法返回的結(jié)果為:com.corn.objectsummary.User@27e。
因此:toString()是由對(duì)象的類型和其哈希碼唯一確定,同一類型但不相等的兩個(gè)對(duì)象分別調(diào)用toString()方法返回的結(jié)果可能相同。
8/9/10/11/12. wait(...) / notify() / notifyAll()
一說(shuō)到wait(...) / notify() | notifyAll()幾個(gè)方法,首先想到的是線程。確實(shí),這幾個(gè)方法主要用于java多線程之間的協(xié)作。先具體看下這幾個(gè)方法的主要含義:
wait():調(diào)用此方法所在的當(dāng)前線程等待,直到在其他線程上調(diào)用此方法的主調(diào)(某一對(duì)象)的notify()/notifyAll()方法。
wait(long timeout)/wait(long timeout, int nanos):調(diào)用此方法所在的當(dāng)前線程等待,直到在其他線程上調(diào)用此方法的主調(diào)(某一對(duì)象)的notisfy()/notisfyAll()方法,或超過(guò)指定的超時(shí)時(shí)間量。
notify()/notifyAll():?jiǎn)拘言诖藢?duì)象監(jiān)視器上等待的單個(gè)線程/所有線程。
wait(...) / notify() | notifyAll()一般情況下都是配套使用。下面來(lái)看一個(gè)簡(jiǎn)單的例子:
這是一個(gè)生產(chǎn)者消費(fèi)者的模型,只不過(guò)這里只用flag來(lái)標(biāo)識(shí)哪個(gè)線程需要工作
public class wait和notify { //volatile保證線程可見(jiàn)性 volatile static int flag = 1; //object作為鎖對(duì)象,用于線程使用wait和notify方法 volatile static Object o = new Object(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { //wait和notify只能在同步代碼塊內(nèi)使用 synchronized (o) { while (true) { if (flag == 0) { try { Thread.sleep(2000); System.out.println("thread1 wait"); //釋放鎖,線程掛起進(jìn)入object的等待隊(duì)列,后續(xù)代碼運(yùn)行 o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread1 run"); System.out.println("notify t2"); flag = 0; //通知等待隊(duì)列的一個(gè)線程獲取鎖 o.notify(); } } } }).start(); //解釋同上 new Thread(new Runnable() { @Override public void run() { while (true) { synchronized (o) { if (flag == 1) { try { Thread.sleep(2000); System.out.println("thread2 wait"); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread2 run"); System.out.println("notify t1"); flag = 1; o.notify(); } } } }).start(); } //輸出結(jié)果是 // thread1 run // notify t2 // thread1 wait // thread2 run // notify t1 // thread2 wait // thread1 run // notify t2 //不斷循環(huán) }
從上述例子的輸出結(jié)果中可以得出如下結(jié)論:
1、wait(...)方法調(diào)用后當(dāng)前線程將立即阻塞,且適當(dāng)其所持有的同步代碼塊中的鎖,直到被喚醒或超時(shí)或打斷后且重新獲取到鎖后才能繼續(xù)執(zhí)行;
2、notify()/notifyAll()方法調(diào)用后,其所在線程不會(huì)立即釋放所持有的鎖,直到其所在同步代碼塊中的代碼執(zhí)行完畢,此時(shí)釋放鎖,因此,如果其同步代碼塊后還有代碼,其執(zhí)行則依賴于JVM的線程調(diào)度。
在Java源碼中,可以看到wait()具體定義如下:
public final void wait() throws InterruptedException { wait(0); }
且wait(long timeout, int nanos)方法定義內(nèi)部實(shí)質(zhì)上也是通過(guò)調(diào)用wait(long timeout)完成。而wait(long timeout)是一個(gè)native方法。因此,wait(...)方法本質(zhì)上都是native方式實(shí)現(xiàn)。
notify()/notifyAll()方法也都是native方法。
Java中線程具有較多的知識(shí)點(diǎn),是一塊比較大且重要的知識(shí)點(diǎn)。后期會(huì)有博文專門針對(duì)Java多線程作出詳細(xì)總結(jié)。此處不再細(xì)述。
protected void finalize();
finalize方法主要與Java垃圾回收機(jī)制有關(guān)。首先我們看一下finalized方法在Object中的具體定義:
protected void finalize() throws Throwable { }
我們發(fā)現(xiàn)Object類中finalize方法被定義成一個(gè)空方法,為什么要如此定義呢?finalize方法的調(diào)用時(shí)機(jī)是怎么樣的呢?
首先,Object中定義finalize方法表明Java中每一個(gè)對(duì)象都將具有finalize這種行為,其具體調(diào)用時(shí)機(jī)在:JVM準(zhǔn)備對(duì)此對(duì)形象所占用的內(nèi)存空間進(jìn)行垃圾回收前,將被調(diào)用。由此可以看出,此方法并不是由我們主動(dòng)去調(diào)用的(雖然可以主動(dòng)去調(diào)用,此時(shí)與其他自定義方法無(wú)異)。
Object類和Class類沒(méi)有直接的關(guān)系。
Object類是一切java類的父類,對(duì)于普通的java類,即便不聲明,也是默認(rèn)繼承了Object類。典型的,可以使用Object類中的toString()方法。
Class類是用于java反射機(jī)制的,一切java類,都有一個(gè)對(duì)應(yīng)的Class對(duì)象,他是一個(gè)final類。Class 類的實(shí)例表示,正在運(yùn)行的 Java 應(yīng)用程序中的類和接口。
感謝各位的閱讀,以上就是“Java中Class類和Object類的詳細(xì)講解”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java中Class類和Object類的詳細(xì)講解這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
網(wǎng)頁(yè)名稱:Java中Class類和Object類的詳細(xì)講解
文章源于:http://m.newbst.com/article28/jodhjp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、網(wǎng)站改版、標(biāo)簽優(yōu)化、網(wǎng)站內(nèi)鏈、App設(shè)計(jì)、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)