反射之初認識

除了int等基本類型外,Java的其餘類型所有都是class(包括interface)。
仔細思考,咱們能夠得出結論:class(包括interface)的本質是數據類型(Type)。
而class是由JVM在執行過程當中動態加載的。JVM在第一次讀取到一種class類型時,將其加載進內存。
每加載一種class,JVM就爲其建立一個Class類型的實例,並關聯起來。注意:這裏的Class類型是一個名叫Class的class

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

以String類爲例,當JVM加載String類時,它首先讀取String.class文件到內存,而後,爲String類建立一個Class實例並關聯起來:
Class cls = new Class(String);

這個Class實例是JVM內部建立的,若是咱們查看JDK源碼,能夠發現Class類的構造方法是private,只有JVM能建立Class實例,咱們本身的Java程序是沒法建立Class實例的
因此,JVM持有的每一個Class實例都指向一個數據類型(class或interface)

 

因爲JVM爲每一個加載的class建立了對應的Class實例,並在實例中保存了該class的全部信息,包括類名、包名、父類、實現的接口、全部方法、字段等,所以,若是獲取了某個Class實例,咱們就能夠經過這個Class實例獲取到該實例對應的class的全部信息。java

這種經過Class實例獲取class信息的方法稱爲反射(Reflection)。apache

 

如何獲取一個classClass實例?有三個方法:數組

方法一:直接經過一個class的靜態變量class獲取:spa

Class cls = String.class

方法二:若是咱們有一個實例變量,能夠經過該實例變量提供的getClass()方法獲取:code

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

方法三:若是知道一個class的完整類名,能夠經過靜態方法Class.forName()獲取:對象

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

由於Class實例在JVM中是惟一的,因此,上述方法獲取的Class實例是同一個實例。能夠用==比較兩個Class實例:blog

Class cls1 = String.class;
String s = "hello";
Class cls2 = s.getClass();
System.out.println(cls1 == cls2); // true

由於反射的目的是爲了得到某個實例的信息。所以,當咱們拿到某個Object實例時,咱們能夠經過反射獲取該Objectclass信息:接口

 1 public class Demo{
 2     public static void main(String[] args) {
 3         printObjectInfo("wang");
 4     }
 5     
 6     public static void printObjectInfo(Object object){
 7         Class cls = object.getClass();
 8         System.out.println(cls);  //class java.lang.String
 9     }
10 }

要從Class實例獲取某個class的基本信息內存

 1 public class Demo12{
 2     public static void main(String[] args) {
 3         //字符串實例
 4         printClassInfo("".getClass());
 5         System.out.println("****************");
 6         //Runnable接口
 7         printClassInfo(Runnable.class);
 8         System.out.println("****************");
 9         printClassInfo(java.time.Month.class);
10         System.out.println("****************");
11         printClassInfo(int[].class);
12         System.out.println("****************");
13         printClassInfo(long.class);
14     }
15     
16     static void printClassInfo(Class cls){
17         //class全限定類名
18         System.out.println("Class name:" + cls.getName());
19         //class類名
20         System.out.println("Simple name:" + cls.getSimpleName());
21         //包名
22         if(cls.getPackage() != null){
23             System.out.println("Package name:" + cls.getPackage().getName());
24         }
25         //判斷該類是不是接口
26         System.out.println("is interface:" + cls.isInterface());
27         //判斷該類是不是枚舉
28         System.out.println("is enum:" + cls.isEnum());
29         //判斷該類是不是數組
30         System.out.println("is array:" + cls.isArray());
31         //判斷該類是不是基本類型
32         System.out.println("is primitive:" + cls.isPrimitive());
33     }
34 }

若是獲取到了一個Class實例,咱們就能夠經過該Class實例來建立對應類型的實例字符串

//獲取String的Class對象
Class cls = String.class;
//建立一個String對象
String s = (String)cls.newInstance();

上述代碼至關於new String()。經過Class.newInstance()能夠建立類實例,

它的侷限是:只能調用public的無參數構造方法。帶參數的構造方法,或者非public的構造方法都沒法經過Class.newInstance()被調用

 

動態加載

JVM在執行Java程序的時候,並非一次性把全部用到的class所有加載到內存,而是第一次須要用到class時才加載。

 1 public class Demo{
 3     public static void main(String[] args){
 4         if(args.length > 0){
 5             create(args[0]);
 6         }
 7     }
 8     
 9     static void create(String name){
10         Person p = new Person(name);
11     }
12     
13 }

當執行Demo.java時,因爲用到了Demo,所以,JVM首先會把Demo.class加載到內存。然而,並不會加載Person.class,除非程序執行到create()方法,JVM發現須要加載Person類時,纔會首次加載Person.class。若是沒有執行create()方法,那麼Person.class根本就不會被加載

這就是JVM動態加載class的特性

動態加載class的特性對於Java程序很是重要。利用JVM動態加載class的特性,咱們才能在運行期根據條件加載不一樣的實現類。例如,Commons Logging老是優先使用Log4j,只有當Log4j不存在時,才使用JDK的logging.利用JVM動態加載特性,大體的實現代碼以下:

 1 //Commons logging優先使用Log4j
 2     LogFactory factory =  null;
 3     if(isClassPresent("org.apache.logging.log4j.Logger")){
 4         factory = createLog4j();
 5     }else{
 6         factory = createJdkLog();
 7     }
 8     
 9     boolean isClassPresent(String name){
10         try{
11             Class.forName(name);
12             return true;
13         }catch(Exception e){
14             return false;
15         }
16         
17     }

這就是爲何咱們只須要吧Log4j的jar包放到classpath中,Commons Logging就會自動使用Log4j的緣由

小結

1.JVM爲每一個加載的classinterface建立了對應的Class實例來保存classinterface的全部信息;

2.獲取一個class對應的Class實例後,就能夠獲取該class的全部信息;

3.經過Class實例獲取class信息的方法稱爲反射(Reflection);

4.JVM老是動態加載class,能夠在運行期根據條件來控制加載class。

相關文章
相關標籤/搜索