Day14 JavaSE 反射機制(上)

JavaSE 反射機制(上)

前言:反射概述

前提:jvm已加載類。

現在給jvm一個類名,能不能知道類的具體信息?能,通過反射機制。

電腦的反射機制,就是通過一個抽象的類名能夠在自己記憶(加載類的內存)中找到相匹配的類的具體信息。

Java Reflection

Reflection(反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。

  • Java反射機制提供的功能
    • 在運行時判斷任意對象所屬的類
    • 在運行時構造任意一個類的對象
    • 在運行時判斷任意一個類所具有的成員變量和方法
    • 在運行時調用任意一個對象的成員變量和方法
    • 生成動態代理
  • 反射相關的主要API:
    • java.lang,Class 代表一個類
    • java.lang.reflect.Method 代表類的方法
    • java.lang.reflect.Field 代表類的成員變量
    • java.lang.reflect.Constructor 代表類的構造方法

一、Class類

在Object類中定義了以下方法,此方法將被所有子類繼承:

public final Class getClass()

以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看很好理解,即:可以通過對象反射求出類的名稱。

Class類

反射可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪個接口。對於每個類而言,JRE都爲其保留一個不變的Class類型的對象。一個Class對象包含了特定某個類的有關信息。

  • Class 概述

    • Class本身也是一個類

    • Class對象只能由系統建立對象

    • 一個類在JVM中只能有一個Class實例

    • 一個Class對象對應的是一個加載到JVM中的一個.class文件

    • 每個類的實例都會記得自己是由哪個Class實例所生成

    • 通過Class可以完整地得到一個類中的完整結構

  • 實例化Class類對象(四種方法)

    • 通過類名.class創建指定類的Class實例
    • 通過一個類的實例對象.getClass()獲取對應實例對象的類的Class實例
    • 通過Class的靜態方法forName()來獲取類的Class實例(常用!)
    • ClassLoader(不做介紹)
  • Class 類的常用方法(!!!)

    方法名 功能
    static Class forName(String name) 根據類的全類名(包名+類名)獲取Class對象
    Object newInstance() 創建目標類對象
    getName() 獲取全類名
    Class getSuperclass() 獲取父類的Class對象
    Class[] getInterfaces() 獲取所有實現的接口,返回類的數組
    ClassLoader getClassLoader() 獲取類的類加載器
    Constuctor[] getConstructors() 獲取所有的公有構造器(public修飾 )
    Constructor[] getDeclaredConstructors() 獲取類的所有構造方法(包括公有私有)
    getModifiers() 獲取構造方法的修飾符(public–>1; private–>2)
    Class[] getParameterTypes() 獲取構造方法的參數類型,參數個數爲返回數組的元素個數

二、Class類常用方法案例

以上方法的案例展示:(其中包含父類、兩個接口、子類以及測試類)

package com.reflection;
//父類
public class Person {
    public String name;
    int age;
}
package com.reflection;
//接口:Move
public interface Move {
    void moveType();
}
package com.reflection;
//接口:Study
public interface Study {
    void studyInfo();
}
package com.reflection;
//子類(繼承父類,並實現兩個接口)
public class Student extends Person implements Move, Study{

    String school;

    //無參構造
    public Student(){
        System.out.println("調用的是public Student()");
    }
    //有參構造
    public Student(String school){
        System.out.println("調用的是public Student(String school)");
        this.school = school;
    }
    //私有有參構造
    private Student(String name, int age){
        System.out.println("調用的是private Student(String name, int age)");
        this.name = name;
        this.age = age;
    }
    void showInfo(){
        System.out.println("學校是:" + this.school);
    }
    @Override
    public void moveType() {
        System.out.println("騎自行車上學");
    }

    @Override
    public void studyInfo() {
        System.out.println("學習中學知識");
    }
}
package com.reflection;
//測試類
import java.lang.reflect.Constructor;

public class Test1 {
    public static void main(String[] args) {

        try {
            Class clazz = Class.forName("com.reflection.Student"); //獲取指定類的Class實例

            Class superClazz = clazz.getSuperclass(); //獲取父類

            System.out.println("父類: " + superClazz.getName());

            Class[] interfaces = clazz.getInterfaces();//獲取當前類的所有接口
            for (Class i : interfaces) {
                System.out.println("接口: " + i.getName());
            }
            System.out.println("===============================================");

            //獲取公有構造器,內含獲取參數類型
            //修飾符爲1,表示public修飾; 修飾符爲2,表示private修飾
            Constructor[] cons = clazz.getConstructors(); //僅能求出公有的構造方法
            for (Constructor con : cons) {
                System.out.println("構造方法名稱: " + con.getName() + "的修飾符是: " + con.getModifiers());

                Class[] paramClazz = con.getParameterTypes(); //獲取構造方法的參數類型,參數個數爲數組元素個數
                for (Class pc : paramClazz) {
                    System.out.println("構造方法: " + con.getName()+ "的參數類型是: " + pc.getName());
                }
                System.out.println();
            }
            System.out.println("===============================================");

            //獲取所有構造器(公有+私有),內含獲取參數類型
            Constructor[] cons1 = clazz.getDeclaredConstructors(); //獲取類的所有構造方法,包括公有的和私有的
            for (Constructor c : cons1) {
                System.out.println("構造方法名稱: " + c.getName() + "的修飾符是: " + c.getModifiers());
                Class[] paramClazz = c.getParameterTypes(); //獲取構造方法的參數類型,參數個數爲數組元素個數
                for (Class pc : paramClazz) {
                    System.out.println("構造方法: " + c.getName()+ "的參數類型是: " + pc.getName());
                }
                System.out.println();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
/*運行結果:
父類: com.reflection.Person
接口: com.reflection.Move
接口: com.reflection.Study
===============================================
構造方法名稱: com.reflection.Student的修飾符是: 1
構造方法: com.reflection.Student的參數類型是: java.lang.String

構造方法名稱: com.reflection.Student的修飾符是: 1

===============================================
構造方法名稱: com.reflection.Student的修飾符是: 2
構造方法: com.reflection.Student的參數類型是: java.lang.String
構造方法: com.reflection.Student的參數類型是: int

構造方法名稱: com.reflection.Student的修飾符是: 1
構造方法: com.reflection.Student的參數類型是: java.lang.String

構造方法名稱: com.reflection.Student的修飾符是: 1

*/

三、通過反射創建一個對象

Person類、Student類、Move接口、Study接口 同(二)中案例。

現編寫Test2類如下:

package com.reflection;

import java.lang.reflect.Constructor;

public class Test2 {
    public static void main(String[] args) {
        try {
            Class clazz1 = Class.forName("com.reflection.Student");

            //如何用反射的構造方法來創建對象
            try {
                Object obj = clazz1.newInstance(); //相當於調用Student類的無參公有構造方法
                Student stu = (Student)obj;
                System.out.println(stu.school);
                System.out.println("---------------------------------------");

                Constructor c1 = clazz1.getConstructor(String.class);//指定獲取有一個參數爲String類型的共有的構造方法
                Student stu1 = (Student)c1.newInstance("第一中學"); //通過newInstance實例話對象,相當於調用public Student(String school)
                System.out.println(stu1.school);
                System.out.println("---------------------------------------");

                //調用私有構造方法,通過反射機制強制調用
                Constructor c2 = clazz1.getDeclaredConstructor(String.class,int.class); //指定獲取有兩個參數(String, int)的構造方法

                c2.setAccessible(true); //解除私有封裝

                Student stu2 = (Student)c2.newInstance("zhangsan",12);
                System.out.println("---------------------------------------");

            } catch (Exception e){
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
/*運行結果
調用的是public Student()
null
---------------------------------------
調用的是public Student(String school)
第一中學
---------------------------------------
調用的是private Student(String name, int age)
---------------------------------------
*/

注: 注意各個構造器的調用方式。

寫在最後

祝你有個好心情,無論何時何地!

To Demut and Dottie!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章