JavaSE(十)之反射


開始接觸的時候可能大家都會很模糊到底什么是反射,大家都以為這個東西不重要,其實很重要的,幾乎所有的框架都要用到反射,增加靈活度。到了后面幾乎動不動就要用到反射。

首先我們先來認識一下對象

學生----->抽象----->Student

表示學生   Student = .......

那我們的反射中的Class呢?

類型----->抽象----->Class(反射的入口破)

java.lang.class    表示java中的類型

  Class c = Student.Class

  Class c = int.Class

  Class c =int[].Class

一、反射(Reflection)的概述

1.1、定義

  反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法 對於任意一個對象,都能夠調用它的任意一個方法和屬性, 這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。

  即:在"運行時",通過反射機制可以動態獲得到和該類型相關的各種信息。    

  java通常是先有類后有對象,有對象就可以調用方法或屬性。反射其實是通過Class對象來調用類里面的方法。
  通過反射可以調用私有方法和私有屬性。大部分框架都是運用反射原理。

1.2、Class類型     java.lang.Class類

  Class是對java中所有類型的抽象。即一個Class類型對象可以表示出java中任意一種類型。每種類型在加載到內存后,內存中都會生產一個與之對應的Class類型對象(有且只有一個),用來表示該類型。

  每個類型都有且只有一個Class類型對象與之對應,通過這個Class類型對象就可以獲得到該類型中的各種信息。Class類是Java反射的入口.。

  1)表示基本類型
              Class c = int.class;
              System.out.println(c.isPrimitive());//true
              System.out.println(c.getName());//int
            
              注:其他基本類型的情況類似
  2)表示類類型
              注:s.getClass()方法返回的是變量s所指向對象的實現類型的Class對象。
              Student s = new Student();
              Class c1 = s.getClass();
              Class c2 = Student.class;
              System.out.println(c1 == c2);//true
            
              //p指向的對象實際類型是Student
              Person p = new Student();
              Class c1 = p.getClass();//c1表示Student類型
              Class c2 = Person.class;//c2表示Person類型
              System.out.println(c1 == c2);//false
  3)表示接口類型
              Action a = new Student();
              Class c1 = a.getClass();//c1表示Student類型
              Class c2 = Action.class;//c2表示Action類型
              System.out.println(c1 == c2);//false
              System.out.println(c2.isInterface());//true
        
  4)表示數組類型
              int[] a = new int[4];
              Class c1 = a.getClass();
              Class c2 = int[].class;
              System.out.println(c1 == c2);//true
              System.out.println(c1.isArray());//true

              Class c3 = c1.getComponentType();//c3表示該數組是使用什么類型聲明出來的
              System.out.println(c3.getName());//int
           
              Student[] a = new Student[4];
              Class c1 = a.getClass();
              Class c2 = Student[].class;
              System.out.println(c1 == c2);//true
              System.out.println(c1.isArray());//true

              Class c3 = c1.getComponentType();//c3表示該數組是使用什么類型聲明出來的
              System.out.println(c3.getName());//com.briup.test.Student

1.3、獲取一個類類型的Class對象的三種方式

  想要使用反射機制,就必須要先獲取到該類的字節碼文件對象(.class),通過字節碼文件對象,就能夠通過該類中的方法獲取到

  我們想要的所有信息(方法,屬性,類名,父類名,實現的所有接口等等),每一個類對應着一個字節碼文件也就對應着一個Class類型的對象,也就是字節碼文件對象

    1)使用Class類中的forName方法獲得

          Class clazz1 = Class.forName("全限定類名");  //通過Class類中的靜態方法forName,直接獲取到一個類的字節碼文件對象,此時該類還是源文件階段,並沒有變為字節碼文件。

          這種方法很靈活,只需一個String類型參數即可,而String類型的數據改變起來很容易,注意該方法是會拋出異常。

    2)使用類名獲得

        Class clazz2  = Person.class;    //當類被加載成.class文件時,此時Person類變成了.class,在獲取該字節碼文件對象,也就是獲取自己, 該類處於字節碼階段。

    3)使用對象調用getClass方法獲得    

        Class clazz3 = p.getClass();    //通過類的實例獲取該類的字節碼文件對象,該類處於創建對象階段。

          getClass是Object中的final修飾的方法,每個對象都可以調用而且不能重寫 

    注:以上三種方法獲得的同一個對象(==比較),因為每個類型內存都有且只有一個Class類型對象。

1.4、反射機制中的常見類的含義

  java.lang包下:
    Class  類        對java中所有類型抽象而得來的
    Package類        對java中所有包抽象而得來的

  java.lang.reflect包下:
    Modifier    類    對java中所有修飾符抽象而得來的
    Field        類    對java中所有屬性抽象而得來的
    Method        類    對java中所有方法抽象而得來的
    Constructor 類    對java中所有構造器抽象而得來的
    Array        類    提供了對數組對象的動態訪問
    ParameterizedType接口  在反射中表示參數化類型
    例如:List<String> Point<Long,Long>等這種帶泛型的類型

二、反射機制獲取類中的信息

2.1、使用Class類型對象獲得類中的信息

1.獲得該類所處的包的信息
            Student s = new Student();
            Class c = s.getClass();
            System.out.println(c.getPackage().getName());

        2.獲得該類的修飾符信息
            //每個修飾符對應一個int值
            //如果有多個修飾符則int值相加
            Student s = new Student();
            Class c = s.getClass();
            System.out.println(c.getModifiers());

            System.out.println(Modifier.PUBLIC);
            System.out.println(Modifier.FINAL);

        3.獲得該類的名字
            Student s = new Student();
            Class c = s.getClass();
            System.out.println(c.getName());
        
        4.獲得該類的父類的Class對象
            Student s = new Student();
            Class c = s.getClass();
            //superclass表示其父類型的Class對象
            Class superclass = c.getSuperclass();
            System.out.println(superclass.getName());

            例如:
            Class c = Object.class;
            Class superclass = c.getSuperclass();
            System.out.println(superclass.getName());
            //運行報錯,因為Object沒有父類
            

            例如:
            //判斷c1是不是c2的子類型
            //判斷c3是不是c2的子類型
            Class c1 = Student.class;
            Class c2 = Person.class;
            Class c3 = String.class
            System.out.println(c2.isAssignableFrom(c1));//true
            System.out.println(c2.isAssignableFrom(c3));//false
        
        5.獲得該類所實現的接口類型的Class對象
            Student s = new Student();
            Class c = s.getClass();
            Class[] interfaces = c.getInterfaces();
            for(Class clazz:interfaces){
                System.out.println(clazz.getName());
            }

            例如:
            //判斷c1是不是c2的實現類
            //判斷c3是不是c2的實現類
            Class c1 = Student.class;
            Class c2 = Action.class;
            Class c3 = String.class
            System.out.println(c2.isAssignableFrom(c1));//true
            System.out.println(c2.isAssignableFrom(c3));//false
    
        
        6.獲得該類中所有的屬性
            Student s = new Student();
            Class c = s.getClass();
            Field[] declaredFields = c.getDeclaredFields();
            
            for(Field f:declaredFields){
                System.out.println(f.getModifiers());
                System.out.println(f.getType().getName());
                System.out.println(f.getName());
            }
            注:
            getDeclaredFields()方法返回類中聲明的屬性,包括私有的
            getFields()方法只返回類中public修飾的屬性,包括繼承的
    
            
            例如:
            //獲得某個指定的屬性(也包括私有屬性)
            Student s = new Student();
            Class c = s.getClass();
            Field f = c.getDeclaredField("score");
            System.out.println(f.getModifiers());
            System.out.println(f.getType().getName());
            System.out.println(f.getName());


        7.獲得該類中所有的方法
            Student s = new Student();
            Class c = s.getClass();
            Method[] declaredMethods = c.getDeclaredMethods();
            for(Method m:declaredMethods){
                System.out.println(m.getModifiers());
                System.out.println(m.getReturnType().getName());
                System.out.println(m.getName());
                System.out.println(Arrays.toString(m.getParameterTypes()));
                System.out.println(Arrays.toString(m.getExceptionTypes()));
            }

            注:
            getDeclaredMethods()方法返回類中聲明的方法,包括私有的
            getMethods()方法只返回類中public修飾的方法,包括繼承的
            

            例如:
            //獲得某個指定的方法(也包括私有方法)
            Student s = new Student();
            Class c = s.getClass();
            Method m = c.getDeclaredMethod("print");
            System.out.println(m.getModifiers());
            System.out.println(m.getReturnType().getName());
            System.out.println(m.getName());
            System.out.println(Arrays.toString(m.getParameterTypes()));
            System.out.println(Arrays.toString(m.getExceptionTypes()));


        8.獲得該類中所有的構造器
            Student s = new Student();
            Class c = s.getClass();
            Constructor[] declaredConstructors = c.getDeclaredConstructors();
            for(Constructor con:declaredConstructors){
                System.out.println(con.getModifiers());
                System.out.println(con.getName());
                System.out.println(Arrays.toString(con.getParameterTypes()));
                System.out.println(Arrays.toString(con.getExceptionTypes()));
            }

            注:
            getDeclaredConstructors()方法返回類中所有構造器
            getConstructors()方法只返回類中public修飾的構造器
            
            
            例如:
            //獲得某個指定的構造器(也包括私有構造器)
            Student s = new Student();
            Class c = s.getClass();
            Constructor con = c.getDeclaredConstructor(double.class);
            System.out.println(con.getModifiers());
            System.out.println(con.getName());
            System.out.println(Arrays.toString(con.getParameterTypes()));
            System.out.println(Arrays.toString(con.getExceptionTypes()));


        9.獲得父類型中的泛型的真實類型
            因為泛型類的泛型參數在編譯期會被擦除,所以我們不能再運行期間直接拿到該泛型的實際類型,但是可以通過子類的Class對象來獲取父類的泛型類型。

            例如: 不通過子類不能獲得泛型實際類型
            public class GenericTest<T,S>{
                public T name;
                public S say(T t,S s){
                    return s;
                }
            }
            
            main:
                GenericTest<String,Integer> t = new GenericTest<String,Integer>();
                Class c = t.getClass();
                Field field = c.getDeclaredField("name");
                System.out.println(field.getType());
                System.out.println(field.getGenericType());
                //輸出結果:
                    class java.lang.Object
                    T
                
                System.out.println("-------------------------");
                Method method = c.getMethod("say", Object.class,Object.class);
                System.out.println(method.getReturnType());
                System.out.println(method.getGenericReturnType());
                //輸出結果:
                class java.lang.Object
                S

                System.out.println("-------------------------");
                System.out.println(Arrays.toString(method.getParameterTypes()));
                System.out.println(Arrays.toString(method.getGenericParameterTypes()));
                //輸出結果:
                [class java.lang.Object, class java.lang.Object]
                [T, S]
            

            例如: 通過子類可以獲得父類中泛型的實際類型
            public class GenericTest<T,S>{
                public T name;
                public S say(T t,S s){
                    return s;
                }
            }
            public class Sub entends GenericTest<String,Integer>{}
            
            main:
                Class c = Sub.class;

                //獲得父類類型,包含泛型參數信息 
                Type superType = c.getGenericSuperclass();

                //判斷父類類型是不是屬於ParameterizedType類型
                //ParameterizedType表示帶泛型的類型
                if(superType instanceof ParameterizedType){
                    //強轉,並調用方法獲得泛型參數的實例類型
                    ParameterizedType pt = (ParameterizedType)superType;

                    Type[] actualTypeArguments = pt.getActualTypeArguments();

                    //循環遍歷,並強轉為Class類型,因為Type接口中沒有任何方法
                    for(Type t:actualTypeArguments){
                        Class clazz = (Class)t;
                        System.out.println(clazz.getName());
                    }
                }
                
            

            例如: 通過子類可以獲得實現接口中泛型的實際類型        
                Class c = Sub.class;
                Type[] types = c.getGenericInterfaces();
                for(Type t:types){
                    if(t instanceof ParameterizedType){
                        ParameterizedType pt = (ParameterizedType)t;

                        Type[] actualTypeArguments = pt.getActualTypeArguments();

                        for(Type type:actualTypeArguments){
                            Class clazz = (Class)type;
                            System.out.println(clazz.getName());
                        }

                    }
                }


        10.獲得類中的注解
            使用反射也可以獲得類中的注解.(在之后的內容在來了解)
Class類型獲取對象類中信息

2.2、反射的常用操作

 1 public class Student{
 2             private long id;
 3             private String name;
 4 
 5             private static int age;
 6             
 7             get/set
 8 
 9             public static void say(){
10                 System.out.println("say..");
11             }

  1)使用反射的方式調用構造器創建類的對象

    默認方式:必須調用無參構造器
        Class c = Student.class;
        Student s = (Student)c.newInstance();


             
   通用方式:獲得構造器對象,並調用該構造器

          
     注:getConstructor方法和newInstance方法的參數都是可變參數
                例如:獲得無參構造器並調用創建對象
                Class c = Student.class;
                Constructor constructor = c.getConstructor();
                Student o = (Student)constructor.newInstance();
                System.out.println(o);

                例如:獲得有參構造器並調用創建對象
                Class c = Student.class;
                Constructor constructor = c.getConstructor(long.class,String.class);
                Student o = (Student)constructor.newInstance(1L,"tom");
                System.out.println(o);


  2)使用反射的方式訪問對象中的屬性
                例如:
                Student s = new Student();
                Class c = s.getClass();
                Field[] declaredFields = c.getDeclaredFields();
                for(Field f:declaredFields){

                    //設置私有屬性可以被訪問
                    f.setAccessible(true);

                    //判斷屬性是否為static,靜態屬性的訪問不需要對象
                    if(Modifier.isStatic(f.getModifiers())){
                        System.out.println(f.getName() +" = "+f.get(null));
                    }else{
                        System.out.println(f.getName() +" = "+f.get(s));
                    }
                }

                注:Field類中的get方法可以獲得屬性值,set方法可以給屬性設置值。
     
  3)使用反射的方式調用對象中的方法
                例如:
                Student s = new Student();
                Class c = s.getClass();
            
                Method m1 = c.getMethod("setName",String.class);
                //m1表示Student中的setName方法
                //調用對象s中的m1方法,並且傳參"tom"
                //s.setName("tom");
                m1.invoke(s, "tom");
            
                Method m2 = c.getMethod("getName");
                String name = (String)m2.invoke(s);
                System.out.println("name = "+name);
            
                //調用靜態方法 不需要對象
                Method m3 = c.getMethod("say");
                m3.invoke(null);

  4)使用反射的方式動態操作數組
                注:
                Object[] o1 = new int[1];//編譯報錯
                long[]      o2 = new int[1];//編譯報錯
                int[]     o3 = new int[1];//編譯通過
            
                Object[] o1 = new 任意引用類型[1];//編譯通過

                例如:

  //要求:傳任意類型"數組",把數組長度擴大1倍並返回
                //注意這里不能收Object[],
                //因為Object[] o = new Integer[4];編譯通過
                //但是Object[] o = new int[4];      編譯報錯
                //那么使用Object類中就可以接收任意類型的數組了
                public static Object arrayCopy(Object obj){
                    //代碼
                }

                實現:
                public static Object arrayCopy(Object obj){
                    Class c = obj.getClass();
                    Object newArray = null;
                    if(c.isArray()){
                        int len = Array.getLength(obj);
                        Class<?> type = c.getComponentType();
                        newArray = Array.newInstance(type, len*2);
                        for(int i=0;i<len;i++){
                            Object value = Array.get(obj, i);
                            Array.set(newArray, i, value);
                        }
                    }
                    return newArray;
                } 
數組長度擴大一倍

三、Class的API詳解

3.1、通過字節碼對象創建實例對象

         

 

3.2、獲取指定構造器方法

  constructor 如果沒有無參構造,只有有參構造如何創建實例呢?看下面

 

         

 

  總結上面創建實例對象:Class類的newInstance()方法是使用該類無參的構造函數創建對象, 如果一個類沒有無參的構造函數, 就不能這樣創建了,可以調用Class類的getConstructor(String.class,int.class)方法

    取一個指定的構造函數然后再調用Constructor類的newInstance("張三",20)方法創建對象

  獲取全部構造方法  

 

              

3.3、獲取成員變量並使用 

  Field對象

    獲取指定成員變量

 

          

 

         Class.getField(String)方法可以獲取類中的指定字段(可見的), 如果是私有的可以用getDeclaedField("name")方法獲取,通過set(obj, "李四")方法可以設置指定對象上該字段的值,

          如果是私有的需要先調用setAccessible(true)設置訪問權限,用獲取的指定的字段調用get(obj)可以獲取指定對象中該字段的值

 

    獲取全部成員變量

 

        

3.4、獲得方法並使用 

           Method

 

          

 

        Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以獲取類中的指定方法,    

          如果為私有方法,則需要打開一個權限。setAccessible(true);

        用invoke(Object, Object...)可以調用該方法,

        跟上面同理,也能一次性獲得所有的方法

 

                      

3.5、獲得該類的所有接口

  Class[] getInterfaces():確定此對象所表示的類或接口實現的接口

  返回值:接口的字節碼文件對象的數組

3.6、獲取指定資源的輸入流

  InputStream getResourceAsStream(String name)  

  return:一個 InputStream 對象;如果找不到帶有該名稱的資源,則返回 null

  參數:所需資源的名稱,如果以"/"開始,則絕對資源名為"/"后面的一部分。

四、兩個實例來全面了解反射

實例1:

package corejava.test;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;

class A{
    private int id;
    private String name="Tom";
    private  A(int a,String name){
        id=a;
    }
    @Override
    public String toString() {
        return "A [id=" + id + ", name=" + name + "]";
    }
}

public class Test1 {
    
    public static Object createObject(String classname) throws Exception {
        Class<?> name = Class.forName(classname);
        return createObject(name);
    }
    public static Object createObject(Class<?>c) throws Exception{
        Object object=null;
        try {
            object=c.newInstance();
        } catch (InstantiationException e) {
//            如果此 Class 表示一個抽象類、接口、數組類、基本類型或 void; 
//            或者該類沒有 null 構造方法; 或者由於其他某種原因導致實例化失敗。
            
//            2、數組類、基本類型  能夠創建對象
            
            if(c.isArray()){
                object=Array.newInstance(c.getComponentType(),5);
            }
            else if(c.isPrimitive()){
                
                if(c == boolean.class){
                    object=false;
                }else if(c==void.class){
                    throw new Exception(c.getName()+"不能創建對象!");
                }else {
                    object=0;
                }
            }else {
//                3、沒有 null 構造方法
                
                Constructor<?>[] constructors = c.getConstructors();
                
                if(constructors.length==0) throw new Exception(c.getName()+"沒有Public構造器!");
                
                Class<?>[] parameterTypes = constructors[0].getParameterTypes();
                Object[] parameters=new Object[parameterTypes.length];
                for(int i=0;i<parameters.length;i++){
                    parameters[i]=createObject(parameterTypes[i]);
                }
                object=constructors[0].newInstance(parameters);
            }
//            1、其他某種原因、void、抽象類、接口時拋出異常
            if(object==null)throw new Exception(c.getName()+"不能創建對象!");
        }
        return object;
    }
    public static void main(String[] args) throws Exception {
        
        System.out.println(createObject(A.class));
        System.out.println("ssss");
//        System.out.println(new String());
//        System.out.println("ssss");
    }

}
實例1

實例2:

package corejava.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.nio.file.spi.FileSystemProvider;

import static java.lang.System.out;

final class B{
    private int id;
    private String name="Tom";
    private static String mm="Tom";
    private  B(int a,String name){
        id=a;
    }
    @Override
    public String toString() {
        return "B [id=" + id + ", name=" + name + "]";
    }
    public int f(String name,A a){
        return id;}
}
public class Test2 {

    public static void printClass(Class<?> c) throws Exception{
        //1、包名;
        out.println(c.getPackage()+";");
        //2、修飾符 +class+類名{
        out.println(Modifier.toString(c.getModifiers())+
                " class "+c.getSimpleName()+" {");
        
        //3、修飾符 +屬性類型+屬性名;
        for(Field f:c.getDeclaredFields()){
            out.print("\t"+Modifier.toString(f.getModifiers())+
                    " "+f.getType().getSimpleName()+" "+f.getName());
            f.setAccessible(true);
            if(Modifier.isStatic(f.getModifiers()))
                out.print(" = "+f.get(null));
            out.println(";");
        }
        
        //4、修飾符 +類名+( 參數類型+參數名字){  }
        for(Constructor<?> constructor:c.getDeclaredConstructors()){
            out.print("\t"+Modifier.toString(constructor.getModifiers())+" "+
                    c.getSimpleName()+"(");    
            
            Parameter[] ps = constructor.getParameters();
            for(int i=0;i<ps.length;i++){
                out.print(ps[i].getType().getSimpleName()+" "
                    +ps[i].getName());
                if(i<ps.length-1)out.print(",");
            }
            out.println("){}");
        }
        out.println();
        //5、修飾符 +返回類型+方法名++( 參數類型+參數名字){  }
        for(Method m:c.getDeclaredMethods()){
            out.print("\t"+Modifier.toString(m.getModifiers())+" "+
                    m.getReturnType().getSimpleName()+" "+
                    m.getName()+"(");    
            
            Parameter[] ps = m.getParameters();
            for(int i=0;i<ps.length;i++){
                out.print(ps[i].getType().getSimpleName()+" "
                    +ps[i].getName());
                if(i<ps.length-1)out.print(",");
            }
            out.println("){}");
            out.println();
        }
        //6、}
        out.println("}");
        
    }
    public static void main(String[] args) throws Exception {
        printClass(Object.class);
    }

}
實例2

 

覺得不錯的“點個推薦”哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM