1. 程式人生 > >反射(Constructor、Field、Method、類載入器)

反射(Constructor、Field、Method、類載入器)

一:什麼是反射
在認識反射之前,首先回顧下什麼是正向處理。如果我們需要例項化一個物件,首先需要匯入這個類的包,在這個包中找這個類:

package CODE.反射;

import java.util.Date;

public class Fan {
    public static void main(String[] args) {
        Date date =new Date();
        System.out.println(date);
    }
}

例項化一個Date物件,需要根據包名.類名來找到Date類。
而反射是相反:是根據物件來取得物件的來源資訊。
也就是物件的反向處理。根據物件倒推類的組成。
反射核心處理在於Object類的方法:

//取得類的Class 物件(Class是一個類)
public final native Class<?> getClass();
package CODE.反射;

import java.util.Date;

public class Fan {
    public static void main(String[] args) {
        Date date =new Date();
        //取得data物件的Date類資訊
        System.out.println(date.getClass()); //class java.util.Date
} }

Class物件的3種例項化方法:
任何一個類的Class物件由JVM載入類後產生(該物件在JVM全域性唯一)。使用者呼叫指定方法來取得該類物件。

  • 任何類的物件可以通過Object類提供的getClass()取得該類Class物件
  • “類名稱.class”可以直接根據某個具體的類來取得Class物件
  • 呼叫Class類的靜態方法Class.forName(String className) throws
    ClassNotFoundException,傳入類的全名稱來取得其Class物件。
import java.util.Date;

public
class Fan { public static void main(String[] args) throws ClassNotFoundException { Date date =new Date(); Class<?> cls1 =date.getClass(); Class<?> cls2=Class.forName("java.util.Date"); Class<?> cls3=Date.class; System.out.println(cls1); //class java.util.Date System.out.println(cls1); //class java.util.Date System.out.println(cls1); //class java.util.Date } }

class java.util.Date 前面的class表明是一個類物件。
從理論和例項看出,只有第一種方式需要例項化物件來取得Class物件,其餘2種方式不會產生例項化物件。那麼取得了Class類物件後可以通過反射來例項化物件。

用反射來例項化物件
在Class類中有一個方法可以通過Class物件來例項化物件:

public T newInstance()  throws InstantiationException, IllegalAccessException

只能呼叫類中無參構造,且無參構造只能是public許可權。
只有當例項化出Class物件後才可以用Class物件調newInstance。
例:

////通過反射例項化物件
import java.util.Date;

public class Fan {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
       Class<?> cls1=Date.class;  //Class物件
       Object obj=cls1.newInstance(); //例項化物件,相當於new java.util.Date()
       System.out.println(obj);
    }
}

完善簡單工廠模式----利用反射
之前在寫工廠模式時,在Factory裡new物件,但是有一個缺陷,就是新增一個類,需要在Factory裡再新增一個分支來new物件。可以參考這篇部落格:
但是有了反射後,可以用Class 物件來例項化物件(有2種產生Class物件不需要例項化物件:Class.forName()和類名.class)
程式碼如下:

package CODE.反射;

//工廠模式---反射

interface Study
{
    void need();
}
class Pen implements Study
{
   public void need()
   {
       System.out.println("需要一直筆");
   }
}
class Book implements Study
{
    public void need()
    {
        System.out.println("需要一個本子");
    }
}
class Pencil implements Study
{
    public void need()
    {
        System.out.println("需要一隻鉛筆");
    }
}
class Factory1
{
    public static Study getStudy(String className)
    {
        Study study=null;
        try {
            //cls是一個Class類物件  通過className取得具體Class物件
            Class<?> cls=Class.forName(className);
            //通過反射例項化物件
            //因為newInstance返回的是Object,所以需要強轉稱為一個型別
            study=(Study)cls.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return study;
    }
}
public class FanSheFac {
    public static void main(String[] args)
    {
        Study study1=Factory1.getStudy("CODE.反射.Pen");
        study1.need();  //需要一直筆
        Study study2=Factory1.getStudy("CODE.反射.Pencil");
        study2.need();  //需要一隻鉛筆
    }
}

在這裡插入圖片描述

反射與類

取得類的包名:

 public Package getPackage() {
        return Package.getPackage(this);
    }

如:

import java.util.Date;

public class Fan {
    public static void main(String[] args)  {
        Class<?> cls1=Date.class;
        System.out.println(cls1.getPackage()); //package java.util, Java Platform API Specification, version 1.8
        System.out.println(cls1.getPackage().getName()); //java.util 利用getName取得具體包名
    }
}

1.反射取得父類、父介面資訊

  • 取得父類的Class物件:public native Class<? super T> getSuperclass();
  • 取得父介面的Class物件:public Class<?>[ ] getInterfaces();(介面在JVM中也是Class因為介面有多繼承,所以父介面可能有多個)
package CODE.反射;

//反射與類操作
//取得父類的Class物件
class A{}
interface IB{}
interface  IC{}
class ABC extends A implements IB,IC{}
public class FanClass {
    public static void main(String[] args) {
        Class<?> cls=ABC.class; //獲得Class物件

        //獲得父類Class物件
        Class<?> cls1=cls.getSuperclass();
        System.out.println("父類Class物件:"+cls1);
        //父類Class物件:class CODE.反射.A

        //獲得父介面Class物件
        Class<?>[] cls2=cls.getInterfaces();
        for(Class<?> clas:cls2)
        {
            System.out.println("父介面Class物件:"+clas);
            //父介面Class物件:interface CODE.反射.IB
            //父介面Class物件:interface CODE.反射.IC
        }
    }
}

2.反射呼叫類中構造方法–Constructor類(描述類中構造方法)

一個類有多個構造,如果要想取得類中構造方法,可以使用Class類提供的2個方法:

  • 取得指定引數型別的構造:
public Constructor<T> getConstructor(Class<?>... parameterTypes)   throws NoSuchMethodException, SecurityException
只能取得類中public許可權的指定構造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)    throws NoSuchMethodException, SecurityException
可以取得類中全部指定構造方法,包含私有構造

引數是型別的類物件,比如型別是String,那麼引數是String.class。

  • 取得類中所有構造方法
public Constructor<?>[] getConstructors() throws SecurityException
取得所有public許可權構造方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
取得所有許可權構造方法(包括私有許可權)

動態設定封裝(只能通過反射呼叫)
Constructor /Method/Field類都AccessibleObjet的子類。
AccessibleObjet提供動態設定封裝方法
public void setAccessible(boolean flag) throws SecurityException
setAccessible只能在本次JVM程序中有效,重新啟動時,private許可權方法依然不可以在外部使用,也就是說只是動態破壞了本次的封裝。

Constructor提供了例項化物件的方法:
public T newInstance(Object … initargs) :可以呼叫類中其他有參構造。

Constructor類的newInstance(Object … initargs)與Class類的newInstance( )區別:
1.Constructor類的例項化物件方法引數有可變引數,即可以有引數,也可以沒有引數,Class類提供的例項化物件方法必須是無參。
2.當有了Constructor物件後,也就是取得類的構造方法,假如構造方法是私有許可權,可以利setAccessible(true)動態破壞私有構造用newInstance例項化物件,而Class類的newInstance只有用public許可權構造方法來例項化物件。

程式碼如下:

package CODE.反射;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

////反射與構造
class Student
{
    private String name;
    private int sno;

    public Student()
    {
    }
    private Student(String  name, int sno) {
        this.name = name;
        this.sno = sno;
    }
    public String toString()
    {
        return "姓名:"+name+"學號"+sno;
    }
}
public class FanConstruct {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> cls=Student.class;
        Constructor<?> constructor=cls.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        //Constructor類提供的newInstance()方法例項化物件 ,引數是要設定的值
        //可以通過Constructor類提供的setAccessible使動態破壞許可權,設定為true後,private許可權可以在外部使用
        //但是也是在在本次JVM程序有效,重新啟動後依然是private許可權
        System.out.println(constructor.newInstance("pick",10)); //姓名:pick學號10
    }
}

3.反射呼叫類中普通方法-- - -Method(描述類中普通方法)

  • 描述類中指定名稱的普通方法
public Method getMethod(String name, Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException
取得本類和父類中public許可權指定方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException
取得本類任何許可權的指定方法(私有、公有、繼承許可權)

引數中String nama :方法名
parameterTypes:引數型別的類物件,如String.class ,既要傳方法名,又要傳型別是為了過載。

  • 取得類中全部普通方法
public Method[] getMethods() throws SecurityException 
取得本類和父類中所有public許可權方法
public Method[] getDeclaredMethods() throws SecurityException
取得本類中所有許可權方法(注意只有本類)

例:

package CODE.反射;

import java.lang.reflect.Method;

class Person1
{
    public void print1(){}
    private void print2(){}
    public void print3(String str){}
}
class Student1 extends Person1
{
    public void print2(int m){
    }
    private void print3(int a){}
}
public class FanSheMeth {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException {
        Class<?> cls=Student1.class;

        Student1 student1=(Student1)cls.newInstance(); //通過反射取得例項化物件
        Method method1[]=cls.getDeclaredMethods();
        Method method2[]=cls.getMethods();
        Method method3=cls.getMethod("print3",String.class);
        Method method4=cls.getDeclaredMethod("print3", int.class);
        System.out.println("getDeclaredMethods取得本類中所有許可權方法:");
        for(Method me1:method1)
        {
            System.out.println(me1);
        }
        System.out.println("getMethods取得本類和父類中所有public許可權方法");
        for(Method me2:method2)
        {
            System.out.println(me2);
        }
        System.out.println("getMethod取得本類和父類所有public許可權指定方法:");
        System.out.println(method3);
        System.out.println("getDeclaredMethod取得本類中public許可權指定方法");
        System.out.println(method4);
    }
}

在這裡插入圖片描述

Method類中提供呼叫類中普通方法的API:
public Object invoke(Object obj, Object… args)

如:

package CODE.反射;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Person
{
    private String name;
    private int age;
    public void print(String name,int age)
    {
        this.name=name;
        this.age=age;
        System.out.println("name:"+this.name+" age:"+this.age);
    }
    private void print(String str)
    {
        System.out.println(str);
    }
}

public class Meth
{
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> cls=Person.class;
        Person person=(Person)cls.newInstance();
        Method printMeth=cls.getMethod("print",String.class,int.class);
        printMeth.invoke(person,"pick",10); //name:pick age:10

        //Method物件調set
        Method printStr=cls.getDeclaredMethod("print", String.class); //取得本類本任意許可權方法
        printStr.setAccessible(true);  //動態破壞許可權,使私有許可權可見
        printStr.invoke(person,"nice day"); //nice day
    }
}

4.反射呼叫類中屬性----Field(描述類中普通屬性)

  • 取得類中指定屬性
public Field getField(String name) throws NoSuchFieldException, SecurityException
取得本類和父類中public指定許可權屬性
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException
取得本類任意許可權屬性(私有、公有、繼承、預設)
  • 取得類中所有屬性
public Field[] getFields() throws SecurityException
取得本類以及父類中所有public屬性
public Field[] getDeclaredFields() throws SecurityException
取得本類中全部普通屬性(包括私有屬性)

Field類提供設定與取得屬性:

設定屬性:
public void set(Object obj,Object value);
obj:例項化物件,value:具體的值
取得屬性:
pulic Object get(Object obj);
取得屬性的型別:
pubic Class<?> getType

對上述方法進行驗證和練習:

package CODE.反射;

import com.sun.corba.se.impl.orbutil.concurrent.Sync;

import java.lang.reflect.Field;

class Person2
{
    private String name;
    private int age;
    public int count=10;
}

class Student2 extends Person2
{
    public int num=1;
    private String sno; //課程號
}
public class FanSheField {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<?> cls=Student2.class;
        Student2 student2=(Student2)cls.newInstance();
        System.out.println("getField取得本類和父類中public許可權屬性:");
        Field count=cls.getField("count");
        count.set(student2,1);  //設定值
        System.out.println("Field取得本類公有屬性值: "+count+":"+count.get(student2));

        System.out.println("getDeclaredField取得本類任意許可權屬性");
        Field sno=cls.getDeclaredField("sno");
        sno.setAccessible(true);  //破壞私有許可權
        sno.set(student2,"3");
        System.out.println("Field取得本類私有許可權屬性值: "+sno+":"+sno.get(student2));
        System.out.println("取得屬性型別:"+sno.getType());

        System.out.println("getFields取得本類和父類所有公有許可權屬性");
        Field field1[]=cls.getFields();
        for(Field f1: field1)
        {
            System.out.println(f1);
        }

        System.out.println("getDeclaredFields取得本類所有許可權屬性");
        Field field2[]=cls.getDeclaredFields();
        for(Field f2:field2)
        {
            System.out.println(f2);
        }
    }
}

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
反射應用:
實現任意類屬性設定。
如果希望對一個類的屬性進行設定,當輸入字串是"emp.name:pick | emp.job:cook"時,emp是真實類中屬性,name和job是類中屬性名稱,“pick"和"cook"是要設定的值,兩個屬性用”|"進行分割,如果可以將屬性設定為此種格式,可以一次性將屬性設定完畢。
程式碼如下:
emp是真實類,EmpAction是面向使用者的類,BeanOperation是公共操作類(任意類通過這個類來設定屬性):
Emp.java

package CODE.反射與簡單Java類;

public class Emp {
    private String name;
    private String job;

    public String getName<