java反射機制

參考博客:https://blog.csdn.net/sinat_38259539/article/details/71799078

1、反射的概述

JAVA反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法(指的是全部,任何的私有,靜態,等等……均可以被獲取到);對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法,因此先要獲取到每個字節碼文件對應的Class類型的對象。
以上的總結就是什麼是反射
反射就是把java類中的各類成分映射成一個個的Java對象——很重要,後面的事情都是根據這個目標來進行的。
例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術能夠對一個類進行解剖,把一個個組成部分映射成一個個對象
     (其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)
如圖是類的正常加載過程:反射的原理在與class對象。
熟悉一下加載的時候:Class對象的由來是jvm虛擬機經過去本地磁盤找到相應從class文件,將class文件讀入內存中併爲之建立一個Class對象。
如圖:

2、下面,咱們先來介紹一下Class類(基於1.7的API)

 

Class 類的實例表示正在運行的 Java 應用程序中的類和接口。也就是jvm中有N多的實例每一個類都有該Class對象(包括基本數據類型)java

Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的defineClass方法自動構造的。也就是這不須要咱們本身去處理建立,JVM已經幫咱們建立好了。框架

Class類中有64個方法,具體用到哪一個再詳細介紹哪一個。jvm

 

三.獲取一個類的Class對象的3種方法

  3.1 Object類下的getClas()方法

    3.2任何的數據類型(包括基本數據類型)都有一個「靜態的」calss屬性ide

    3.3經過Class類的靜態方法:  Class.forName(String className)  //className是全路徑名——包名+類名,可是並非磁盤的絕對路徑 (經常使用)測試

  public void f1() throws ClassNotFoundException { //獲取class對象
        /* * 1.經過對象來獲取字節碼對象 * 缺點:我對象都有了。。。還要字節碼對象作啥子*/ Student student=new Student();  //這樣一建立 其實建立了2個對象 student對象 以及Student類對應的class對象
        Class stuClass1=student.getClass(); /* * 2.經過每一個類型都有的「靜態的」class屬性來獲取 * 缺點:依賴性太強,得導入相應的類的包;不然編譯不過*/ Class stuClass2=Student.class; /* * 經常使用,須要掌握   * Class forName(String className) 注意:全限類名*/ Class stuClass3=Class.forName("com.kylin.FanShe.Student"); System.out.println(stuClass1==stuClass2); System.out.println(stuClass1==stuClass3); System.out.println(stuClass2==stuClass3); //結果3個都是true,說明——一個類只有一個class對象
    }

四,經過class對象獲取一個類的構造方法以及經過構造方法建立對象

   經過calss對象來獲取構造器,主要分獲取一批構造器和獲取單個構造器;     
    1.獲取一批構造器       
      1.1獲取全部 「共有的」構造器         
        public Constructor[] getConstructors();全部"公有的"構造方法; 這個Constructor是 反射包裏的 java.lang.reflect.Constructor
      1.2獲取全部的(任何訪問類型--包訪問/共有/保護/私有)的構造器
          public Constructor[] getDeclaredConstructors():獲取全部的構造方法(包括私有、受保護、默認、公有)
      2.獲取一個構造器(若是是無參構造器,那麼參數寫null或者不寫
   2.1獲取單個 共有的構造器
      public Constructor getConstructor(Class ... paramterTypes) ;//獲取單個的「公有的」構造方法(參數是構造器參數的類型;注意是構造器參數的類型!
     2.2.獲取單個任何類型的構造器
      public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"能夠是私有的,或受保護、默認、公有
   3.經過構造方法對象來建立對象
      Object newInstance(構造器的參數) //構造方法的一個方法,用於建立對象,返回Object類型 須要本身手動的類型轉換一下

Student.java
package com.kylin.FanShe;

public class Student {
    private String name;  //姓名
    private String sex;  //性別
    private String id;   //學號
    private int age; //年齡

    //4種訪問類型的構造器   包訪問類型  共有 保護 私有

    Student(){
        System.out.println("包訪問類型的構造器");
    }
    //共有
    public Student(String name){
        this.name=name;
        System.out.println("共有訪問類型的構造器,且名字爲:"+name);
    }

    public Student(String id, int age){
        this.id=id;
        this.age=age;
        System.out.println("共有的構造器,id,年齡"+id+" "+age);
    }
    //保護
    protected Student(String name,String sex){
        this.name=name;
        this.sex=sex;
        System.out.println("保護類型的構造器,且名字爲:"+name+"性別:"+sex);
    }
    //私有
    private Student(String name,String id,String sex){
        this.name=name;
        this.id=id;
        this.sex=sex;
        System.out.println("私有的構造器:"+name+" "+id+" "+sex);
    }



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

獲取構造方法代碼: (一開始引入了:import java.lang.reflect.Constructor)
  public static void main(String []args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

//        獲取class對象
        Class clazz=Class.forName("com.kylin.FanShe.Student");
        //1.1獲取全部的公有的構造器
        Constructor []constructors=clazz.getConstructors();
        for (Constructor constructor:constructors){
            System.out.println(constructor);
        }
          /*
            * 結果:
            *   public com.kylin.FanShe.Student(java.lang.String,int)
                public com.kylin.FanShe.Student(java.lang.String)
            * */
        System.out.println("-----------------------------美麗的分割線-------------------------------");
        //1.2獲取全部任何類型的構造方法
        Constructor []allConstructors=clazz.getDeclaredConstructors();
        for (Constructor constructor:allConstructors){
            System.out.println(constructor);
        }
        /*
        * 結果:
        *   private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)
            protected com.kylin.FanShe.Student(java.lang.String,java.lang.String)
            public com.kylin.FanShe.Student(java.lang.String,int)
            public com.kylin.FanShe.Student(java.lang.String)
            com.kylin.FanShe.Student()*/

        System.out.println("-----------------------------美麗的分割線-------------------------------");

        //2.1獲取某個公有的 構造器
        Constructor myPubConstructor=clazz.getConstructor(String.class);  //參數是:構造器參數的類型
        System.out.println(myPubConstructor);
//        輸出:public com.kylin.FanShe.Student(java.lang.String)

        System.out.println("-----------------------------美麗的分割線-------------------------------");
        //2.2獲取某個任何類型的構造器
        Constructor myEveConstructor=clazz.getDeclaredConstructor(String.class,String.class,String.class);
        System.out.println(myEveConstructor);
//        輸出:private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)

        //經過構造方法來建立對象
        Student student= (Student) myPubConstructor.newInstance("name");
        System.out.println(student.getName());


//          這樣會拋出異常,由於一個私有的構造器不能被用來建立對象 因此須要設置訪問權限
//        Student student1= (Student) myEveConstructor.newInstance("1","22","333");
//        System.out.println(student1.getName()+" "+student1.getId()+" "+student1.getSex());
        myEveConstructor.setAccessible(true);//暴力訪問
        Student student1= (Student) myEveConstructor.newInstance("111","222","333");
        System.out.println(student1.getName()+" "+student1.getSex()+" "+student1.getId());

    }

  輸出結果:this

public com.kylin.FanShe.Student(java.lang.String,int)
public com.kylin.FanShe.Student(java.lang.String)
-----------------------------美麗的分割線-------------------------------
private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)
protected com.kylin.FanShe.Student(java.lang.String,java.lang.String)
public com.kylin.FanShe.Student(java.lang.String,int)
public com.kylin.FanShe.Student(java.lang.String)
com.kylin.FanShe.Student()
-----------------------------美麗的分割線-------------------------------
public com.kylin.FanShe.Student(java.lang.String)
-----------------------------美麗的分割線-------------------------------
private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String)
共有訪問類型的構造器,且名字爲:name
name
私有的構造器:111 222 333
111 333 222

 五,經過class對象獲取成員變量並調用

teacher.javaspa

package com.kylin.FanShe;

public class Teacher {
    public String name;
    public String name1;
    String school;
    protected String sex;
    private int age;

    public Teacher(){

    }
    @Override
    public String toString(){
        return "Teacher[name="+name+",school=" +school+
                ",sex="+sex+",age="+age+",name1="+name1;
    }

}

 

測試獲取成員變量而且調用.net

package com.kylin.FanShe;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

//獲取成員變量
public class GetFiled {
    /*
    *  獲取成員變量並調用:
     *
     * 1.批量的
     *         1).Field[] getFields():獲取全部的"公有字段"
     *         2).Field[] getDeclaredFields():獲取全部字段,包括:私有、受保護、默認、公有;
     * 2.獲取單個的:
     *         1).public Field getField(String fieldName):獲取某個"公有的"字段;
     *         2).public Field getDeclaredField(String fieldName):獲取某個字段(能夠是私有的)
     *
     *      設置字段的值:
     *         Field --> public void set(Object obj,Object value):
     *                     參數說明:
     *                     1.obj:要設置的字段所在的對象;
     *                     2.value:要爲字段設置的值*/

    public static void main(String []args) throws ClassNotFoundException, NoSuchFieldException, 
    NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//獲取class對象 Class clazz=Class.forName("com.kylin.FanShe.Teacher"); //獲取全部的共有的屬性 Field []fields=clazz.getFields(); for (Field field:fields){ System.out.println(field); } System.out.println("-----------------------------美麗分割線-------------------------"); //獲取全部的屬性 (任何訪問類型) Field []allFields=clazz.getDeclaredFields(); for (Field field:allFields){ System.out.println(field); //這也是一個對象來的 } System.out.println("-----------------------------美麗分割線-------------------------"); //獲取指定的共有屬性而且賦值給對象 Field nameField=clazz.getField("name"); System.out.println(nameField); Teacher teacher= (Teacher) clazz.getConstructor(null).newInstance(); nameField.set(teacher,"蘇桃"); System.out.println(teacher); System.out.println("-----------------------------美麗分割線-------------------------"); //獲取指定的任何類型(這裏獲取私有來測試)的屬性 而且賦值給對象 Field priAgeField=clazz.getDeclaredField("age"); //女人的年齡是祕密嘛 System.out.println(priAgeField); //私有的須要設置權限 這裏是暴力訪問 priAgeField.setAccessible(true); priAgeField.set(teacher,20); System.out.println(teacher); } }

輸出:3d

 

public java.lang.String com.kylin.FanShe.Teacher.name
public java.lang.String com.kylin.FanShe.Teacher.name1
-----------------------------美麗分割線-------------------------
public java.lang.String com.kylin.FanShe.Teacher.name
public java.lang.String com.kylin.FanShe.Teacher.name1
java.lang.String com.kylin.FanShe.Teacher.school
protected java.lang.String com.kylin.FanShe.Teacher.sex
private int com.kylin.FanShe.Teacher.age
-----------------------------美麗分割線-------------------------
public java.lang.String com.kylin.FanShe.Teacher.name
Teacher[name=蘇桃,school=null,sex=null,age=0,name1=null
-----------------------------美麗分割線-------------------------
private int com.kylin.FanShe.Teacher.age
Teacher[name=蘇桃,school=null,sex=null,age=20,name1=null

 六,使用class對象獲取成員方法而且調用(調用確定得是該類型的對象或者子類才能夠調用)

people.javacode

package com.kylin.FanShe;

public class People {

    public void show1(String s){
        System.out.println("調用了:公有的,String參數的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("調用了:受保護的,無參的show2()");
    }
    void show3(){
        System.out.println("調用了:默認的,無參的show3()");
    }
    private String show4(int age) {
        System.out.println("調用了,私有的,而且有返回值的,int參數的show4(): age = " + age);
        return "abcd";
    }

    }

獲取成員方法而且調用

/*
       獲取成員方法並調用
                * 1.批量的:
               *         public Method[] getMethods():獲取全部"公有方法";(包含了父類的方法也包含Object類)
                *         public Method[] getDeclaredMethods():獲取全部的成員方法,包括私有的(不包括繼承的)
     2.獲取單個的:
               *         public Method getMethod(String name,Class<?>... parameterTypes):
              *                     參數:
                *                         name : 方法名;
             *                         Class ... : 形參的Class類型對象
            public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

            *      調用方法:
               *         Method --> public Object invoke(Object obj,Object... args):
               *                     參數說明:
               *                     obj : 要調用方法的對象,這個對象必定要是和方法同一類/或者子類也應該能夠的;
                *                     args:調用方式時所傳遞的實參;
               ):
                */

        public static void main(String[] args) throws Exception {
            //1.獲取Class對象
            Class clazz = Class.forName("com.kylin.FanShe.People");
            //2.獲取全部公有方法
            System.out.println("***************獲取全部的」公有「方法*******************");
            Method[] methodArray = clazz.getMethods();
            for (Method m : methodArray) {
                System.out.println(m);
            }
            System.out.println("***************獲取全部的方法,包括私有的*******************");
            methodArray = clazz.getDeclaredMethods();
            for (Method m : methodArray) {
                System.out.println(m);
            }
            System.out.println("***************獲取公有的show1()方法*******************");
            Method pubMethod = clazz.getMethod("show1", String.class);
            System.out.println(pubMethod);
            //實例化一個Student對象
            People people= (People) clazz.getConstructor().newInstance();
            pubMethod.invoke(people,"劉德華");  //方法對象被調用執行,則輸出了那句話
            System.out.println("***************獲取私有的show4()方法******************");
            Method priMethod = clazz.getDeclaredMethod("show4", int.class);
            System.out.println(priMethod);
            priMethod.setAccessible(true);//解除私有限定
            System.out.println("11111111111111111111111");

            //拋出異常 多是得該類的對象才能夠調用
//            Object object=new Object();

            People people1= (People) clazz.getConstructor().newInstance();
            priMethod.invoke(people1, 20);//須要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
//            System.out.println("返回值:" + result);


        }

 總結:

  反射能夠獲取到一個類的全部的東西——靜態的非靜態的,私有的公有的……只要是在類中的,或者繼承於父類的東西,均可以被獲取成一個對象,而後進行調用……固然也能夠獲取main方法等等,這裏主要介紹了經過類的字節碼對象獲取構造方法,成員變量,方法等……反射同時也是不少框架實現的基礎,要好好琢磨……

相關文章
相關標籤/搜索