反射的講解

反射的定義:

  • Reflection (反射)是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於ReflectionAPI取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。
  • Class.forName(“java.lang.String”)
  • 加載完類之後,在堆內存的方法區就產生一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息我們可以通過這個對我們的形象稱之爲:反射

反射相關的主要API

java.lang.Class:代表一個類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變量
java.lang.reflect.Constructor

Class類

對象照鏡子後可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪些接口。 對於每個類而言,JRE 都爲其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特 定某個結構
(class/interface/enum/annotation/primitive type/void/[])的有關信息。

  • Class 本身也是一個類
  • Class 對象只能由系統建立對象
  • 一個加載的類在 JVM 中只會有一個Class實例
  • 一個Class對象對應的是一個加載到JVM中的一個.class文件
  • 每個類的實例都會記得自己是由哪個 Class 實例所生成
  • 通過Class可以完整地得到一個類中的所有被加載的結構
  • Class類是Reflection的根源,針對任何你想動態加載、運行的類,唯有先獲得相應的 Class對象

Class類是Reflection的根源,針對任何你想動態加載、運行的類,唯有先獲得相應的 Class對象
public final Class getClass()

Class常用方法

方法名 功能說明
static ClassforName(String name) 返回指定類名name的Class對象
Object newInstance() 調用缺省構造函數,返回Class對象的一個實例
getName() 返回此Class對象所表示的實體(類,接口,數組類或void)的名稱
Class getSuperClass() 返回當前Class對象的父類的Class對象
Class[] getinterfaces() 獲取當前Class對象的接口
ClassLoader getClassLoader 返回該類的類加載器
Construtor[] getConstructors() 返回一個包含某些Constructor
Method getMothed(String name,Class…) 返回一個Method對象,此對象的形參類型爲paramType
Field[] getDeclaredFields() 返回Field對象的一個數組

哪些類型可以有Class對象?

  • class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類。
  • interface:接口
  • []:數組
  • enum:枚舉
  • annotation:註解@interface
  • primitive type:基本數據類型
  • void
傳統的方式
public class Reflection {
    public static void main(String[] args) throws Exception {
        //通過實例化 new 對象 對屬性進行賦值
        Student2 s1 = new Student2(20, "小米", 01);
        Student2 s2 = new Student2(20, "小米", 01);
        System.out.println(s1.hashCode());//成員變量的屬性相同但是HashCode值不一樣
        System.out.println(s2.hashCode());//好比人相同的名字,年齡,號,但是不能代表是同一個人
        System.out.println(s1==s2);
        System.out.println(s1.equals(s2));

        Class class1 = Class.forName("org.westos.demo2.Student");
        Student student =(Student) class1.newInstance();
        student.setName("李華");
        student.setAge(20);
        student.setNumber(02);
        System.out.println(student.toString());

    }
}
class Student2 {
    private int age;
    private String name;
    private int number;

    public Student2() {}

    public Student2(int age, String name, int number) {
        this.age = age;
        this.name = name;
        this.number = number;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", number=" + number +
                '}';
   }

public class Reflection2 {
    public static void main(String[] args) throws Exception {
    Serializable是序列化:
    想把的內存中的對象狀態保存到一個文件中或者數據庫中時候
    想把對象通過網絡進行傳播的時候
    
        /*
        Class 類是實現Serializable接口
        public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
            private static final int ANNOTATION= 0x00002000;
            private static final int ENUM      = 0x00004000;
            private static final int SYNTHETIC = 0x00001000;

            private static native void registerNatives();
            static {
                registerNatives();
            }
         */

        //public final native Class<?> getClass();
        //native是一個本地方法

        //可執行 可實行的Executable;
        //採取多態的形式
        //把這個對象變成字節需要Serializable
        Tiger animals = new Tiger();
        System.out.println(animals.toString());
        //獲取Class 對象的幾種方式,其一
        Class class1 = animals.getClass();
        System.out.println(class1);
        //其二:
        Class class2 = Class.forName("org.westos.demo2.Tiger");
        System.out.println(class2);
        //其三:通過類.class 也可以獲得
        Class class3 = Tiger.class;
        System.out.println(class3);
        //其四:正對包裝類,內置基本類型,只對包裝類型有效
        Class type = Integer.TYPE;
        System.out.println(type);
        //獲得父類的類型
        Class class4 = class1.getSuperclass();
        System.out.println(class4);
    }
}
class Animals {
    public String name;

    public Animals() {}

    public Animals(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Animals{" +
                "name='" + name + '\'' +
                '}';
    }

}
class Tiger extends Animals {
    public Tiger() {
       this.name = "老虎";
    }
}

class Cat extends Animals {
    public Cat() {
        this.name = "貓";
    }

哪些類有Class類對象:

public class Reflection3 {
    public static void main(String[] args) {
        Class c1 = Object.class;//class 關鍵字
        Class c2 = Comparable.class;//接口
        Class c3 = String[].class;//數組
        Class c4 = String[][].class;//二維數組
        Class c5 = Override.class;//註解
        Class c6 = ElementType.class;//枚舉類型
        Class c7 = void.class;//void
        Class c8 = Integer.class;
        Class c9 = Class.class;
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
        //數組中,和長度無關,只要是一個維度並且類型相同,就是同一個class
        int[] a = new int[10];
        String[] b = new String[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        System.out.println(c10);
        System.out.println(c11);
        System.out.println(c10==c11);
    }
}

public class Reflection4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
/*
獲取類的信息
*/
Class<?> class1 = Class.forName(“org.westos.demo2.Student”);
System.out.println(class1.getName());//全類名 包名 + 類名
System.out.println(class1.getSimpleName());//類名
System.out.println("===============");
//獲取字段:get、set
Field[] fields = class1.getFields();//只能獲得public的屬性
System.out.println(fields.length);//0

    Field[] fields1 = class1.getDeclaredFields();//返回全部的屬性,包括獲得私有的
    System.out.println(fields1.length);
    for (Field field : fields1) {
        System.out.println(field);
    }
    Field name = class1.getDeclaredField("name");//獲取指定字段
    System.out.println(name);
    System.out.println("==============");

    //獲得方法:執行 invoke
    Method[] methods = class1.getMethods();
    System.out.println(methods.length);
    for (Method method : methods) {
        System.out.println("methods: " + method);//獲得的是當前類的方法和被繼承類的public方法
    }

    System.out.println("==============");
    Method[] declaredMethods = class1.getDeclaredMethods();
    System.out.println(declaredMethods.length);
    for (Method declaredMethod : declaredMethods) {   //獲得當前類的所有方法
        System.out.println("declaredMethods " + declaredMethod);
    }
    System.out.println("================");

    //如果只獲得方法的名字就會有問題:重載!(參數類型)
    Method setName = class1.getMethod("setName", String.class);
    Method setAge = class1.getMethod("setAge", int.class);
    System.out.println(setName);
    System.out.println(setAge);
    System.out.println("================");
    //獲得構造器:創建對象 newInstance()
    Constructor[] constructors = class1.getConstructors();
    System.out.println(constructors.length);
    for (Constructor constructor : constructors) {
        System.out.println(constructor);//獲得所有public的構造方法
    }
    System.out.println("================");

    Constructor[] constructors1 = class1.getDeclaredConstructors();
    System.out.println(constructors1.length);
    for (Constructor constructor : constructors1) {//獲得所有public構造方法
        System.out.println(constructor);
    }
    System.out.println("==================");
    //獲得指定的構造器:重載,只能通過參數名判斷 null指的是空參 有參的必須按照參數名順序
    Constructor constructor = class1.getConstructor(null);
    Constructor constructor2 = class1.getConstructor(int.class,String.class,int.class);
    System.out.println(constructor);
    System.out.println(constructor2);

}

}

class Student {
private int age;
private String name;
private int number;

public Student() {
}

public Student(int age, String name, int number) {
    this.age = age;
    this.name = name;
    this.number = number;
}

public int getAge() {
    return age;
}

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

public String getName() {
    return name;
}

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

public int getNumber() {
    return number;
}

public void setNumber(int number) {
    this.number = number;
}

@Override
public String toString() {
    return "Student{" +
            "age=" + age +
            ", name='" + name + '\'' +
            ", number=" + number +
            '}';
}

不加setAccessible()方法報錯與解決方法:

//System.out.println(s4.getName());//java.lang.IllegalAccessException: Class org.westos.demo2.Reflection5 can not access a member of class org.westos.demo2.Student with modifiers "private" 無法訪問私有化的構造方法,普通方法,和私有屬性。 // 解決方案: // 1. 將修飾類屬性的 private 改爲 public // 2. 調用setAccessible()方法,來設置或取消訪問檢查,以達到訪問私有對象的目的

public class Reflection5 {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("org.westos.demo2.Student");
        //創建對象,new 無參,有參
        Student s1 =(Student) c1.newInstance();//創建對象
        System.out.println(s1);
        System.out.println("==============");

        //通過指定構造器創建對象 new Student(20,"小明",01);
        Constructor declaredConstructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
        Student s2 =(Student) declaredConstructor.newInstance(20, "小明", 01);
        System.out.println(s2);
        System.out.println("==================");

        Student s3 =(Student) c1.newInstance();
        //s3.setName("小明");正常操作
        //1.獲得你要操作的方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //2.通過invoke 方法執行方法
        //一個class可能存在多個對象,這個方法需要找到是那個對象使用的,給他賦值;
        setName.invoke(s3,"小明");
        System.out.println(s3.getName());
        System.out.println("==================");

        //獲得字段
        Student s4 =(Student) c1.newInstance();
        Field name = c1.getDeclaredField("name");//反射無法直接破壞私有的
        //name.set(s4,"小明");
        //System.out.println(s4.getName());//java.lang.IllegalAccessException: Class org.westos.demo2.Reflection5 can not access a member of class org.westos.demo2.Student with modifiers "private"  無法訪問私有化的構造方法,普通方法,和私有屬性。
        //       解決方案:
        //    1. 將修飾類屬性的 private 改爲 public
        //    2. 調用setAccessible()方法,來設置或取消訪問檢查,以達到訪問私有對象的目的

        //顯示調用setAccessible爲true,則可以訪問private方法!

        name.setAccessible(true);
        name.set(s4,"小明");
        System.out.println(s4.getName());

    }
}

小結:

  • 在實際的操作中,取得類的信息的操作代碼,並不會經常開發。
  • 一定要熟悉java.lang.reflect包的作用,反射機制。
  • 如何取得屬性、方法、構造器的名稱,修飾符等
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章