国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

java Class對象

2019-11-14 22:39:24
字體:
來源:轉載
供稿:網友
java Class對象目錄

1Class對象

 普通的Class對象

 泛化的Class對象

2類型轉換前先做檢查

 instanceof 運算符的陷阱

 Class.isInstance()

3反射

 動態代理

1.Class對象1.1普通的Class對象

Class對象是一個特殊的對象,是用來創建其它對象的對象(這里的其他對象就是指:java類的實例)。其實Class對象就是java類編譯后生成的.class文件,它包含了與類有關的信息。

每當第一次使用一個類時,JVM必須使用“類加載器”子系統加載該類對象的Class對象。一旦這個類的Class對象被載入內存,它就被用來創建這個類的所有對象。當我們使用new關鍵字創建一個類的第一個對象的時候,JVM會幫助我們加載該類Class對象,但是當我們想自己加載這個類的Class對象怎么辦呢?實際上有3種方法:

  1. Class.forName("類名字符串") (注意:類名字符串必須是全稱,包名+類名)
  2. 類字面常量法:類名.class
  3. 實例對象.getClass()
 1 class Candy{ 2     static{System.out.View Code

image

從輸出中可以看出,Class對象僅在需要的時候才被加載(我們在Java 對象及其內存控制一文中說過:static初始化是在類加載時進行的)。

為什么沒有打印出“Loading Candy”呢?因為,使用類字面常量法創建對Class對象的引用時,不會自動的初始化該Class對象。

1.2泛化的Class對象

由于普通Class引用指向的是它所指向的對象的確切類型。在Java引入泛型的概念之后,Java SE5的設計者將Class引用的類型通過使用泛型限定變得更具體了。再看我們上面的程序,一個Class classType既可以指向Candy類的Class對象也可以指向Gum類的Class對象還可以指向Cookie類的Class對象。這就很像我們編程時使用Object作為引用變量的類型一樣。當我們用泛型限定了上面代碼的classType之后,便會有錯誤出現了:

image

與使用普通的Class對象相比,使用泛型的Class對象,在調用newIntance方法時返回的不在是一個Object類型而是該對象的確切類型:

image

現在當我們需要放寬條件,即需要創建一個Class引用,它被限定為某種類型,或者該類型的任何子類型,這是我們就需要使用通配符"?"與extends關鍵字相結合,創建一個“范圍”:

1 public class NumberClassObj {2     public static void main(String[] args) {3         Class<? extends Number> numType=int.class;4         numType=double.class;5         numType=Number.class;6     }7 }
View Code

當然,在這里調用newIntance方法的話返回的就是Number對象了。

到了這里,是不是有些朋友就會想,那我們是不是就可以使用泛型創建一個Class引用,它指向“某個類,它是一個類的超類”?當然可以:

 1 public class SuperClassObj { 2     interface Inter{ 3         public void sayHello(); 4     } 5     class Base{ 6          7     } 8     class Sub extends Base implements Inter{ 9         @Override10         public void sayHello() {11             System.out.println("hello everyone");12         }    13     }14     public static void main(String[] args) throws InstantiationException, IllegalaccessException {15         Class<? super Sub> superOfSub=Base.class;16         superOfSub=Inter.class;17     }18 }
View Code

但是這樣創建出來的Class對象,在調用newInstance方法時將返回Object對象:

image

這是為什么呢?我的理解是:因為它表示的是一個很模糊的概念,編譯器找不到一個合適的類型與之對應,并且沒有這樣一個.class文件。(不知道理解的是否合適,請各位大神不吝賜教)

2.類型轉換前先做檢查2.1instanceof 運算符的陷阱

instanceof運算符的前一個操作數通常是引用類型的變量,后面一個操作數通常是一個類(也可以是接口),它用于判斷前面的對象是否是后面的類或其子類、實現類的實例。是則返回true;否則,返回false。

Java規范,使用instanceof運算符有一個限制:instanceof運算符前面操作數的編譯時類型必須是如下3種情況:

①與后面的類相同;

②是后面類的父類;

③是后面類的子類。

如果前面操作數的編譯時類型與后面的類型沒有任何關系,程序將沒法通過編譯,只有通過編譯后才能考慮它的運算結果是true還是false。

wpsF1AD.tmp3fe65b55-f08f-4bd7-a46b-051666793332

對于上面這段代碼,是不是有點暈了,為什么Math math=(Math)str;這里沒有出現編譯錯誤,而是在下面出現了編譯錯誤呢?其實是這樣的:當編譯器編譯Java程序時,編譯器無法檢查引用變量實際引用對象的類型,它只檢查該變量的編譯類型。對于math instanceof String而言math編譯類型為Math,Math既不是String類型,也不是String類型的父類更不是String類型的子類,因此程序沒法通過編譯。至于math實際引用對象的類型是什么,編譯器并不關心。至于Math math=(Math)str;沒有出現編譯錯誤,這和強制類型轉換機制有關。對于Java的強制類型轉換而言,也可以分為編譯、運行兩個階段類分析它。

在編譯階段:強制類型轉換要求被轉換變量的編譯時類型必須是如下3種情況:

①被轉換變量的編譯時類型與目標類型相同;

②被轉換變量的編譯時類型是目標類型的父類;

③被轉換變量的編譯時類型是目標類型的子類,這種情況下可以自動向上轉型,無須強制轉換。

Math math=(Math)str;沒有提示編譯錯誤的原因是,str編譯時的類型是目標類型(Math)的父類,所以編譯是正確的,可見,強制類型轉換的編譯階段只關心引用變量的編譯時類型,至于該引用變量實際引用對象的類型,編譯器并不關心,也沒法關心。

在運行階段:被轉換變量所引用對象的實際類型必須是目標類型的實例,或者是目標類型的子類的實例、實現類的實例,否則在運行時將引發java.lang.ClassCastException異常。

再如:

1 Object obj=new Integer(10);2 String str=(String)obj;3 System.out.println(str);
View Code

這段代碼中由于obj變量實際引用的變量的類型是Integer,而Integer既不是String的子類也不是父類,所以在運行階段拋出了java.lang.ClassCastException異常。

Instanceof有一個額外的功能:它可以確保第一個操作數所引用的對象不是null,當第一個操作數所引用的對象為null時,instanceof運算符返回false,而不會報異常。

2.2 Class.isInstance()

Class.isInstance方法提供了一種動態測試對象的途徑。它與instanceof表達式的區別是:

Class.isInstance方法更加適合泛類型的檢測(如代理,接口,抽象類等規則),常與泛化Class對象出現,而instanceof表達式適合直接類型的檢查,常與普通的Class對象出現。

3.反射

前面講了獲取(手動加載)一個類的Class對象一些方法,我們在使用new創建一個對象的時候JVM首先會檢查該類的Class對象是否被加載,沒有的話,JVM會自動幫我們加載,那我們為什么還要手動加載呢?原因就是創建一個對象的方法不止使用new這一種,本節我們講述的反射就是另外一種創建類實例的方法,但是JVM不會為反射創建對象自動加載Class對象。因此我們需要手動加載。

大家都知道,要讓Java程序能夠運行,那么就得讓Java類要被Java虛擬機加載。Java類如果不被Java虛擬機加載,是不能正常運行的。現在我們運行的所有的程序都是在編譯期的時候就已經知道了你所需要的那個類的已經被加載了。

Java的反射機制是在編譯并不確定是哪個類被加載了,而是在程序運行的時候才加載、探知、自審。使用在編譯期并不知道的類,直到運行時才得知名稱的class,這樣的特點就是反射。

反射在使用JDBC、開發第三方插件、開發框架的時候用的比較多。

Java反射機制所需要的類、接口等都在java.lang.reflect包中。仔細查看該包的內容相信一定會熟練運用該技能的。這里就不展開講解各個API了。

3.1動態代理

說到了反射,我們就順便再了解一下動態代理。它是以反射為基礎的。

什么是代理呢?代理就是用來代替“實際”對象的對象。這里的“實際”對象被稱為委托類。它主要用在:不允許直接訪問某些類時;對訪問要做特殊處理時等。或者,要對原方法進行統一的擴展,例如加入日志記錄。

代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等,因此它充當著“中間人”的角色,它與委托類具有相同點接口。一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。

根據代理的創建方式,可以將代理類分為:

靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。

動態代理:通過反射機制動態生成。

我們先來看一個靜態代理:

 1 public interface Interface { 2     void doSomething(); 3     void someElse(String str); 4 } 5 public class RealObject implements Interface { 6     @Override 7     public void doSomething() { 8         System.out.println("doing something"); 9     }10     @Override11     public void someElse(String str) {12         System.out.println("someElse "+str);13     }14 }15 public class SimpleProxy implements Interface {16     private Interface delegate;17     public SimpleProxy(Interface delegate){18         this.delegate=delegate;19     }20     @Override21     public void doSomething() {22         System.out.println("SimpleProxy doSomething");23         delegate.doSomething();24     }25 26     @Override27     public void someElse(String str) {28         System.out.println("SimpleProxy someElse "+str);29         delegate.someElse(str);30     }31 }32 public class Business {33     public static void consumer(Interface inter){34         inter.doSomething();35         inter.someElse("bonobo");36     }37     public static void main(String[] args) {38         consumer(new RealObject());39         System.out.println("-----------------------");40         consumer(new SimpleProxy(new RealObject()));41     }42 }
View Code

image

從上面代碼中我們可以發現:相比SimpleProxy,RealObject類更專注于特定的功能,它將一些“額外”的操作(在這里指的就是System.out.println("SimpleProxy doSomething");和System.out.println("SimpleProxy someElse "+str);)從代碼中分離出來,這就使得這些“額外”的操作的變化非常容易(比如我要變為System.out.println("AA");或者直接使用RealObject定義的功能),這也體現了設計模式的核心思想:封裝變化點。RealObject類的核心功能是不變的,變的是那些“額外”的操作,因此,將這些“額外”的操作封裝起來不就可以了。

再來看一下動態代理:

 1 public interface Interface { 2     void doSomething(); 3     void someElse(String str); 4 } 5 public class RealObject implements Interface { 6     @Override 7     public void doSomething() { 8         System.out.println("doing something"); 9     }10     @Override11     public void someElse(String str) {12         System.out.println("someElse "+str);13     }14 }15 public class DynamicProxyHandler implements InvocationHandler {16     private Object proxied;17     public DynamicProxyHandler(Object proxied){18         this.proxied=proxied;19     }20     @Override21     public Object invoke(Object proxy, Method method, Object[] args)22             throws Throwable {23         System.out.println("proxy: "+proxy.getClass()+".method: "+method+",args: "+args);24         if(args!=null)25             for(Object arg : args)26                 System.out.println("  "+arg);27         return method.invoke(proxied, args);28     }29 }30 public class SimpleDynamicProxy {31     public static void consumer(Interface inter){32         inter.doSomething();33         inter.someElse("bonobo");34     }35     public static void main(String[] args) {36         RealObject realObj=new RealObject();37         consumer(realObj);38         System.out.println("-----------------");39         Interface proxy=(Interface)Proxy.newProxyInstance(40                 Interface.class.getClassLoader(),41                 new Class[]{Interface.class},42                 new DynamicProxyHandler(realObj));43         consumer(proxy);44     }45 }
View Code

查看JDK:

image


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 哈尔滨市| 赣榆县| 额敏县| 类乌齐县| 义乌市| 永定县| 涟水县| 汉中市| 鄂托克前旗| 滁州市| 镇康县| 堆龙德庆县| 巴南区| 温宿县| 正安县| 通山县| 龙州县| 桓台县| 全州县| 久治县| 尤溪县| 和平区| 高密市| 呼图壁县| 双峰县| 芷江| 莱阳市| 新野县| 洛扎县| 香河县| 休宁县| 行唐县| 精河县| 元阳县| 桓台县| 托克逊县| 柘荣县| 古蔺县| 凤庆县| 若羌县| 浮梁县|