<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java的基本使用之反射

      1、反射的概念

      反射就是Reflection,Java的反射是指程序在運行期可以拿到一個對象的所有信息。反射是為了解決在運行期,對某個實例一無所知的情況下,去調用其方法。

       

      2、Class實例

      除了int等基本類型外,Java的其他類型全部都是class(包括interface)。

      class是由JVM在執行過程中動態加載的,JVM在每次第一次讀取到一種class類型時,都將其加載進內存。每加載一種class,JVM就為其創建一個Class類型的實例,并關聯起來。注意:這里的Class類型是一個名叫Classclass。如下:

      public final class Class {
          private Class() {}
      }

      比如String類,當JVM加載String類時,它首先讀取String.class文件到內存,然后,為String類創建一個Class實例并關聯起來:

      Class cls = new Class(String);

      JVM持有的每個Class實例都指向一個數據類型(classinterface)。一個Class實例包含了該class的所有完整信息,包括類名、包名、父類、實現的接口、所有方法、字段等,因此,如果獲取了某個Class實例,我們就可以通過這個Class實例獲取到該實例對應的class的所有信息。這種通過Class實例獲取class信息的方法就稱為反射(Reflection)。

      JVM也為每一種基本類型都創建了Class,如 int 的 Class可以通過int.class訪問。

       

      2.1、如何獲取一個class的Class實例

      1)直接通過一個class的靜態變量class獲取

      Class cls = String.class;

      2)如果有class的一個實例變量,可以直接通過 class 的實例變量提供的getClass()方法獲取

      String s = "Hello";
      Class cls = s.getClass();

      3)如果知道class的完整類名,可以通過靜態方法Class.forName()獲取

      Class cls = Class.forName("java.lang.String");

      一種class的Class實例在JVM中是唯一的。

       

      如果獲取到了一個Class實例,我們就可以通過該Class實例來創建對應類型的實例:

      // 獲取String的Class實例:
      Class cls = String.class;
      // 創建一個String實例:
      String s = (String) cls.newInstance();  //創建了一個空字符串

      上述代碼相當于new String()。通過Class.newInstance()可以創建類實例,它的局限是:只能調用public的無參數構造方法。帶參數的構造方法,或者非public的構造方法都無法通過Class.newInstance()被調用。

       

      3、通過反射訪問類的字段

      3.1、獲取類的字段信息

      對任意的一個Object實例,只要我們獲取了它的Class,就可以獲取它的一切信息。我們可以通過Class實例獲取字段信息。

      Class類提供了以下幾個方法來獲取字段對象:

      • Field getField(name):根據字段名獲取某個public的field對象(包括父類)
      • Field getDeclaredField(name):根據字段名獲取當前類的某個field對象(不包括父類,也包含非public)
      • Field[] getFields():獲取所有public的field對象(包括父類)
      • Field[] getDeclaredFields():獲取當前類的所有field對象(不包括父類,也包含非public)

       代碼示例:

      public class Main {
          public static void main(String[] args) throws Exception {
              Class stdClass = Student.class;
              // 獲取public字段"score":
              System.out.println(stdClass.getField("score"));   //輸出public int Student.score
              // 獲取繼承的public字段"name":
              System.out.println(stdClass.getField("name"));  //輸出public java.lang.String Person.name
              // 獲取private字段"grade":
              System.out.println(stdClass.getDeclaredField("grade"));  //輸出private int Student.grade
          }
      }
      
      class Student extends Person {
          public int score;
          private int grade;
      }
      
      class Person {
          public String name;
      }

      一個Field對象包含了一個字段的所有信息:

      • getName():返回字段名稱,例如,"name";
      • getType():返回字段類型,也是一個Class實例,例如,String.class;
      • getModifiers():返回字段的修飾符,它是一個整型int,不同的bit表示不同的含義??梢酝ㄟ^調用Modifier.isFinal(int n)、Modifier.isPublic(int n)、Modifier.isProtected(int n)、Modifier.isPrivate(int n)、Modifier.isStatic(int n)來判斷返回的修飾符
      //以String類的value字段為例,它的定義如下:
      public final class String {
          private final byte[] value;
      }
      
      //通過反射獲取字段信息
      Field f = String.class.getDeclaredField("value");
      System.out.println(f.getName()); // "value"
      System.out.println(f.getType()); // class [B 表示byte[]類型
      
      int m = f.getModifiers();
      System.out.println(Modifier.isFinal(m));   //true
      System.out.println(Modifier.isPublic(m));   //false
      System.out.println(Modifier.isProtected(m));  //false
      System.out.println(Modifier.isPrivate(m));  //true
      System.out.println(Modifier.isStatic(m));   //false

       

      3.2、獲取類的實例的字段值

      我們可以通過反射拿到一個類實例對應的該字段的值。

      例如,下面對于一個Person實例,我們先拿到name字段對應的Field,再獲取這個實例的name字段的值:

      import java.lang.reflect.Field;
      public class Main {
          public static void main(String[] args) throws Exception {
              Object p = new Person("Xiao Ming");
              Class c = p.getClass();   //獲取Class實例
              Field f = c.getDeclaredField("name");   //獲取Field實例
      
              f.setAccessible(true);      //調用Field.setAccessible(true)使其可以訪問private字段,否則會報錯。也可以將 Person 的 name 字段改成public
              Object value = f.get(p);   //用Field.get(Object)獲取指定實例的指定字段的值。
              System.out.println(value); // "Xiao Ming"
          }
      }
      
      class Person {
          private String name;
          public Person(String name) {
              this.name = name;
          }
      }

      使用反射可以獲取private字段的值,但是反射是一種非常規的用法,使用反射,首先代碼非常繁瑣,其次,它更多地是給工具或者底層框架來使用,目的是在不知道目標實例任何信息的情況下,獲取特定字段的值。所以說類的封裝還是有意義的。

      setAccessible(true)可能會失敗。如果JVM運行期存在SecurityManager,那么它會根據規則進行檢查,有可能阻止setAccessible(true)。例如,某個SecurityManager可能不允許對javajavax開頭的package的類調用setAccessible(true),這樣可以保證JVM核心庫的安全。

       

      3.3、設置實例的字段值

      我們可以通過Field.set(Object, Object)來設置實例的字段值,其中第一個Object參數是指定的實例,第二個Object參數是待修改的值。

      下面代碼通過反射修改實例的name字段值:

      import java.lang.reflect.Field;
      public class Main {
          public static void main(String[] args) throws Exception {
              Person p = new Person("Xiao Ming");
              System.out.println(p.getName()); // "Xiao Ming"
              Class c = p.getClass();
              Field f = c.getDeclaredField("name");
              f.setAccessible(true);   //修改非public字段,需要先調用setAccessible(true)。
              f.set(p, "Xiao Hong");
              System.out.println(p.getName()); // "Xiao Hong"
          }
      }
      
      class Person {
          private String name;
          public Person(String name) {
              this.name = name;
          }
          public String getName() {
              return this.name;
          }
      }

       

      4、通過反射調用方法

      4.1、通過Class實例獲取方法

      我們可以通過Class實例獲取類的所有Method信息,Class類提供了以下幾個方法來獲取Method: 

      • Method getMethod(name, Class...):獲取某個publicMethod(包括父類)
      • Method getDeclaredMethod(name, Class...):獲取當前類的某個Method(不包括父類)
      • Method[] getMethods():獲取所有publicMethod(包括父類)
      • Method[] getDeclaredMethods():獲取當前類的所有Method(不包括父類)
      public class Main {
          public static void main(String[] args) throws Exception {
              Class stdClass = Student.class;
              // 獲取public方法getScore,參數為String:
              System.out.println(stdClass.getMethod("getScore", String.class));   //輸出public int Student.getScore(java.lang.String) 
      // 獲取繼承的public方法getName,無參數: System.out.println(stdClass.getMethod("getName")); //輸出public java.lang.String Person.getName()
      // 獲取private方法getGrade,參數為int: System.out.println(stdClass.getDeclaredMethod("getGrade", int.class)); //輸出private int Student.getGrade(int) } } class Student extends Person { public int getScore(String type) { return 99; } private int getGrade(int year) { return 1; } } class Person { public String getName() { return "Person"; } }

      一個Method對象包含一個方法的所有信息:

      • getName():返回方法名稱,例如:"getScore";
      • getReturnType():返回方法返回值類型,也是一個Class實例,例如:String.class;
      • getParameterTypes():返回方法的參數類型,是一個Class數組,例如:{String.class, int.class};
      • getModifiers():返回方法的修飾符,它是一個int,不同的bit表示不同的含義。

       

      4.2、調用方法

      Method實例調用invoke就相當于調用該方法。invoke() 方法的返回值是一個 Object 對象,invoke() 的第一個參數是對象實例,即在哪個實例上調用該方法,后面的可變參數要與方法參數一致,否則將報錯。如果方法沒有參數,那么invoke也沒有第二個參數。

      public class Main {
          public static void main(String[] args) throws Exception {
              //下面相當于是操作了 String r = s.substring(6);  // "world" 
      
              String s = "Hello world";   
              Method m = String.class.getMethod("substring", int.class);  // 獲取String substring(int)方法,參數為int:
              String r = (String) m.invoke(s, 6);  // 在s對象上調用該方法并獲取結果   
              System.out.println(r);
          }
      }

      4.2.1、調用靜態方法

      如果獲取到的Method表示一個靜態方法,調用靜態方法時,由于無需指定實例對象,所以invoke方法傳入的第一個參數永遠為null。我們以Integer.parseInt(String)為例:

      import java.lang.reflect.Method;
      public class Main {
          public static void main(String[] args) throws Exception {
              // 獲取Integer.parseInt(String)方法,參數為String:
              Method m = Integer.class.getMethod("parseInt", String.class);
              // 調用該靜態方法并獲取結果:
              Integer n = (Integer) m.invoke(null, "12345");
              // 打印調用結果:
              System.out.println(n);
          }
      }

      4.2.2、調用非public方法

      和Field類似,對于非public方法,我們雖然可以通過Class.getDeclaredMethod()獲取該方法實例,但直接對其調用將得到一個IllegalAccessException。為了調用非public方法,我們通過Method.setAccessible(true)允許其調用:

      import java.lang.reflect.Method;
      public class Main {
          public static void main(String[] args) throws Exception {
              Person p = new Person();
              Method m = p.getClass().getDeclaredMethod("setName", String.class);
              m.setAccessible(true);
              m.invoke(p, "Bob");
              System.out.println(p.name);
          }
      }
      
      class Person {
          String name;
          private void setName(String name) {
              this.name = name;
          }
      }

      此外,setAccessible(true)可能會失敗。如果JVM運行期存在SecurityManager,那么它會根據規則進行檢查,有可能阻止setAccessible(true)。例如,某個SecurityManager可能不允許對javajavax開頭的package的類調用setAccessible(true),這樣可以保證JVM核心庫的安全。

       

      4.3、多態

      使用反射調用方法時,仍然遵循多態原則:即總是調用實際類型的覆寫方法(如果存在)。

      如下:Person類定義了hello()方法,并且它的子類Student也覆寫了hello()方法,從Person.class獲取的Method,作用于Student實例時,調用的是Student類的方法

      import java.lang.reflect.Method;
      public class Main {
          public static void main(String[] args) throws Exception {
              // 獲取Person的hello方法:
              Method h = Person.class.getMethod("hello");
              // 對Student實例調用hello方法:
              h.invoke(new Student());   //輸出Student:hello 
          }
      }
      
      class Person {
          public void hello() {
              System.out.println("Person:hello");
          }
      }
      
      class Student extends Person {
          public void hello() {
              System.out.println("Student:hello");
          }
      }

       

      5、通過反射調用構造方法創建類實例

      我們可以通過 Class 提供的 newInstance() 方法來創建類的實例。但是 Class.newInstance() 只能調用該類的 public 無參數構造方法。如果構造方法帶有參數,或者不是public,就無法直接通過Class.newInstance()來調用。

      Person p = Person.class.newInstance();   //Class.newInstance()返回Object對象

       

      為了調用任意的構造方法,Java的反射API提供了Constructor對象,它包含一個構造方法的所有信息,可以創建一個實例。

      import java.lang.reflect.Constructor;
      public class Main {
          public static void main(String[] args) throws Exception {
              // 獲取構造方法Integer(int):
              Constructor cons1 = Integer.class.getConstructor(int.class);
              // 調用構造方法:
              Integer n1 = (Integer) cons1.newInstance(123);
              System.out.println(n1);
      
              // 獲取構造方法Integer(String)
              Constructor cons2 = Integer.class.getConstructor(String.class);
              Integer n2 = (Integer) cons2.newInstance("456");
              System.out.println(n2);
          }
      }

      通過Class實例獲取Constructor的方法如下:

      • getConstructor(Class...):獲取某個publicConstructor
      • getDeclaredConstructor(Class...):獲取某個Constructor,可獲取不是public修飾的構造函數,比如private修飾的;
      • getConstructors():獲取所有publicConstructor
      • getDeclaredConstructors():獲取所有的Constructor,不只是public修飾的。

      調用非publicConstructor時,必須首先通過 Class.setAccessible(true)設置允許訪問,才能調用 Class.newInstance() 方法來創建實例,否則會報錯。不過setAccessible(true)也可能會失敗。

       

      6、通過反射獲取繼承關系

      6.1、獲取父類的Class實例

      通過Class實例,我們可以獲取該實例 Class 實例的類的父類的 Class 實例:

      public class Main {
          public static void main(String[] args) throws Exception {
              Class i = Integer.class;
              Class n = i.getSuperclass();   //class java.lang.Number
              System.out.println(n);
              Class o = n.getSuperclass();  //class java.lang.Object
              System.out.println(o);
              System.out.println(o.getSuperclass());  //null
          }
      }

      運行上述代碼,可以看到,Integer的父類類型是Number,Number的父類是Object,Object的父類是null。除Object外,其他任何非interfaceClass都必定存在一個父類類型。

      對所有interfaceClass調用getSuperclass()返回的是null,獲取接口的父接口要用getInterfaces()

       

      6.2、獲取該類實現的接口interface

      由于一個類可能實現一個或多個接口,通過Class的getInterfaces()方法我們就可以查詢到實現的接口類型。如果一個類沒有實現任何interface,那么getInterfaces()返回空數組。

      例如,查詢Integer實現的接口:

      import java.lang.reflect.Method;
      public class Main {
          public static void main(String[] args) throws Exception {
              Class s = Integer.class;
              Class[] is = s.getInterfaces();
              for (Class i : is) {
                  System.out.println(i);  //interface java.lang.Comparable   interface java.lang.constant.Constable       interface java.lang.constant.ConstantDesc
              }
          }
      }

      getInterfaces()只返回當前類直接實現的接口類型,并不包括其父類實現的接口類型。

       

      6.3、根據Class實例判斷類是否可向上轉型(isAssignableFrom()) 

      根據兩個Class實例要判斷類的向上轉型是否成立,可以調用isAssignableFrom()

      // Integer i = ?
      Integer.class.isAssignableFrom(Integer.class); // true,因為Integer可以賦值給Integer
      // Number n = ?
      Number.class.isAssignableFrom(Integer.class); // true,因為Integer可以賦值給Number
      // Object o = ?
      Object.class.isAssignableFrom(Integer.class); // true,因為Integer可以賦值給Object
      // Integer i = ?
      Integer.class.isAssignableFrom(Number.class); // false,因為Number不能賦值給Integer

       

      7、動態加載

      JVM在執行Java程序的時候,并不是一次性把所有用到的class全部加載到內存,而是第一次需要用到class時才加載。例如:

      // Main.java
      public class Main {
          public static void main(String[] args) {
              if (args.length > 0) {
                  create(args[0]);
              }
          }
      
          static void create(String name) {
              Person p = new Person(name);
          }
      }

      當執行Main.java時,由于用到了Main,因此,JVM首先會把Main.class加載到內存。然而,并不會加載Person.class,除非程序執行到create()方法,JVM發現需要加載Person類時,才會首次加載Person.class。如果沒有執行create()方法,那么Person.class根本就不會被加載。這就是JVM動態加載class的特性。

      動態加載class的特性對于Java程序非常重要。利用JVM動態加載class的特性,我們才能在運行期根據條件加載不同的實現類。

       

      8、動態代理

      運用Java的動態代理,可以在每個類中的執行方法前后進行一些統一的操作,由此可以不必修改每個類中的每個方法。

      比如下面: 我們先寫一個被代理對象的接口和實現類。接口是Bird (鴨子是鳥類的一種),被代理的實現類是鴨子Duck

      //接口
      public interface Bird {
          String name="bird";
          public void say();
      }
      
      //實現類
      public class Duck implements Bird {
          @Override
          public void say() {
              System.out.println("鴨子。。。");
          }
      }

      再寫一個鴨子的代理類。需要注意4點:
      a. 代理類需實現 InvocationHandler 接口。

      b.代理類有一個Object的屬性,通過構造器注入被代理對象。

      c. 在代理類里面的invoke方法進行對被代理對象的調用,及調用前后的額外處理(你想在調用前后干啥就干啥唄)。

      d.寫一個獲取代理對象的get方法,方便獲取代理對象。這個代理對象執行被代理對象的方法時,會執行invoke方法。

      public class DuckProxy implements InvocationHandler {
          private Object duckObject;
          public DuckProxy(Object duckObject) {
              this.duckObject = duckObject;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              //在代理真實對象前我們可以添加一些自己的操作
              System.out.println("方法執行之前的操作");
      
              System.out.println("Method:" + method);
              Object returnValue = method.invoke(duckObject, args);   //執行增強后的方法,returnValue即為真實對象執行方法的返回值。當通過代理對象來調用真實對象的方法時,會自動地跳轉到代理對象關聯的handler對象的invoke方法來進行調用
      
              //在代理真實對象后我們也可以添加一些自己的操作
              System.out.println("方法執行之后的操作");
              return returnValue;
          }
      
          public Object getDuckProxy(){
              return Proxy.newProxyInstance(duckObject.getClass().getClassLoader(), duckObject.getClass().getInterfaces(), this);
          }
      }

      測試運行:

      //依次輸出:  方法執行之前的操作  Method:public abstract void proxyTest.Bird.say()   鴨子。。。   方法執行之后的操作
      public class Test{
          public static void main(String[] args){
              Bird bird = new Duck();
              Bird proxy = (Bird)new DuckProxy(bird).getDuckProxy();  //拿到代理對象
              proxy.say();
          }
      }

       

      8.1、動態代理直接在運行期創建接口實例

      我們可以通過Java標準庫提供的動態代理(Dynamic Proxy)的機制,在運行期動態創建某個接口interface的實例。這樣就可以不用編寫接口的實現類,直接在運行期創建接口的實例然后調用接口的方法。

      如下:我們仍然先定義了接口Hello,但是我們并不去編寫實現類,而是直接通過JDK提供的一個Proxy.newProxyInstance()創建了一個Hello接口對象。這種沒有實現類但是在運行期動態創建了一個接口對象的方式,我們稱為動態代碼。JDK提供的動態創建接口對象的方式,就叫動態代理。

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      public class Main {
          public static void main(String[] args) {
              InvocationHandler handler = new InvocationHandler() {   //定義一個InvocationHandler實例
                  @Override
                  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                      System.out.println(method);
                      if (method.getName().equals("morning")) {
                          System.out.println("Good morning, " + args[0]);
                      }
                      return null;
                  }
              };
              Hello hello = (Hello) Proxy.newProxyInstance(   //創建interface實例
                  Hello.class.getClassLoader(), // 傳入ClassLoader
                  new Class[] { Hello.class }, // 傳入要實現的接口
                  handler                     // 傳入處理調用方法的InvocationHandler
              ); 
      
              hello.morning("Bob");
              hello.say();
          }
      }
      
      interface Hello {
          void morning(String name);
          void say();
      }

      在運行期動態創建一個interface實例的方法如下:

      1. 先定義一個InvocationHandler實例,它負責實現接口的方法調用;
      2. 通過Proxy.newProxyInstance()創建interface實例,它需要3個參數:
        1. 使用的ClassLoader,通常就是接口類的ClassLoader;
        2. 需要實現的接口數組,至少需要傳入一個接口進去;
        3. 用來處理接口方法調用的InvocationHandler實例。
      3. 將Proxy.newProxyInstance()返回的Object強制轉型為接口。

      動態代理實際上是JDK在運行期動態創建class字節碼并加載的過程,其實就是JDK幫我們自動編寫了一個類(不需要源碼,可以直接生成字節碼),并不存在可以直接實例化接口的黑魔法。

       

      posted @ 2020-04-30 18:41  wenxuehai  閱讀(446)  評論(0)    收藏  舉報
      //右下角添加目錄
      主站蜘蛛池模板: 日本熟妇大乳| 国产高在线精品亚洲三区| 人妻换着玩又刺激又爽| 精品国产福利一区二区| 狠狠色丁香婷婷综合尤物| 精品人妻少妇嫩草av系列| 午夜在线不卡| 亚洲一区久久蜜臀av| 成人国产精品日本在线观看| 亚洲欧美日韩在线码| 香港日本三级亚洲三级| 99热久久这里只有精品| 四虎国产精品永久在线下载| 孕妇特级毛片ww无码内射| 久久精产国品一二三产品| 在线免费播放av日韩| 日本五十路熟女一区二区| 狂野欧美性猛交免费视频| 四虎女优在线视频免费看| 人妻精品动漫H无码中字| 中文字幕日韩精品有码| 一本久道中文无码字幕av| 国产成人精品无码一区二区| 国产精品毛片av999999| 在线中文字幕国产精品| 日韩欧美一中文字暮专区| 亚洲性一交一乱一伦视频| 欧美成人精品手机在线| 亚洲鸥美日韩精品久久| 亚洲综合av男人的天堂| 亚洲一本二区偷拍精品| 风韵丰满熟妇啪啪区老老熟妇 | 亚洲男人天堂av在线| 国产成人亚洲综合图区| 国产老熟女无套内射不卡| 人人澡人人妻人人爽人人蜜桃| 最新国产精品好看的精品| 人妻在线无码一区二区三区| 日本高清在线观看WWW色| 被黑人巨大一区二区三区| 风韵丰满熟妇啪啪区老熟熟女|