天天看點

反射(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();  //需要一隻鉛筆
    }
}
           
反射(Constructor、Field、Method、類加載器)

反射與類

取得類的包名:

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);
    }
}
           
反射(Constructor、Field、Method、類加載器)

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);
        }
    }
}

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

反射應用:

實作任意類屬性設定。

如果希望對一個類的屬性進行設定,當輸入字元串是"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() {
        return name;
    }

    public String getJob() {
        return job;
    }

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

    public void setJob(String job) {
        this.job = job;
    }
    public String toString()
    {
         if(job==null)
          return "name :"+name;
        return "job:"+job;
    }
}
           

EmpAction.java

package CODE.反射與簡單Java類;

import java.lang.reflect.InvocationTargetException;

public class EmpAction {
    private Emp emp=new Emp();
    public void setValue(String value) throws Exception {
     BeanOperation.setBeanValue(this,value);
    }
    public Emp getEmp()
    {
        return emp;
    }
}
           

BeanOperation .java :

package CODE.反射與簡單Java類;
//


import javax.management.ObjectName;
import java.lang.reflect.Method;

import java.lang.reflect.InvocationTargetException;

public class BeanOperation {

    public static void setBeanValue(Object ActionObj ,String str) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // emp.name:pick|emp.job:cooker
        String tmp[]=str.split("\\|");
        //emp.name:pick
        //emp.jon:cooker
        for(int i=0;i<tmp.length;i++)
        {
            String msg[]=tmp[i].split(":");
            String attribute=msg[0];   //屬性字首 emp.name
            String value=msg[1];   //屬性值  pick
            //取得真實類
            String realClassName=attribute.split("\\.")[0]; //emp
            String attrValue=attribute.split("\\.")[1];  //name
            Object realClass=getRealClass(ActionObj,realClassName);
            //獲得真實類後setValue
            setValue(realClass,attrValue,value);
            System.out.println(realClass);
        }
    }
    public static Object getRealClass(Object ActionObj,String realClassName ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object obj=null;
        Class<?> actionCls=ActionObj.getClass();  //類對象
        String methName="get"+upFirst(realClassName);  //取得要通路的方法名稱
        Method method=actionCls.getMethod(methName);   //通過反射獲得方法
        obj=method.invoke(ActionObj);  //調用獲得方法
        return obj;
    }
    public static String upFirst(String str)
    {
        return str.substring(0,1).toUpperCase()+str.substring(1);
    }
    public static  void setValue(Object realclass,String arrtrValue,String value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> cls=realclass.getClass();
        String MethodName="set"+upFirst(arrtrValue);
        Method setMethod=cls.getMethod(MethodName,String.class);
        setMethod.invoke(realclass,value);
    }
}


           

Test類:

package CODE.反射與簡單Java類;

import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws Exception {
        EmpAction empAction=new EmpAction();
        empAction.setValue("emp.name:pick|emp.job:cook");
    }
}
           
反射(Constructor、Field、Method、類加載器)

類加載器

什麼是類加載器:

類加載器:

通過一個類的全名稱來擷取此類的二進制位元組流,實作這個操作的代碼子產品為類加載器。

通過 Class類的getClassLoader可以擷取到類加載器:

public ClassLoader getClassLoader() ;

編寫一個簡單的反射程式,來觀察ClassLoader的存在:

package CODE.反射;
public class Loader {
    public static void main(String[] args) {
        Class<?> cls=Loader.class;
        System.out.println(cls.getClassLoader()); //[email protected]
        System.out.println(cls.getClassLoader().getParent());//[email protected]
        System.out.println(cls.getClassLoader().getParent().getParent()); //null
    }
}
           

可以看到有2個類加載器:ExtClassLoader和AppClassLoader。

實際 上在JDK中内置有3大類加載器。

3大類加載器的關系:

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

1.Bootstarp(啟動類加載器):

  • 使用c++實作,是JVM的一部分,(無法用,是以上述代碼為空),其他所有類加載器均使用Java實作。
  • 負責将存放于Java_HOME\lib目錄下的能被JVM識别的類庫(tr.jar—存放了Java所有基礎類庫,java.lang,java.util)加載到JVM中
  • 啟動類加載器無法被Java程式直接引用

    如Object包由Bootstarp加載。

2.ExtClassLoader(擴充類加載器)

  • 使用Java實作,并且可以被Java程式直接引用
  • 加載Java_Home\lib\ext目錄下能被識别的類庫。

3.AppClassLoader(應用程式類加載器)

  • 負責加載使用者路徑(classPath)上指定的類庫
  • 如果使用者程式中沒有自定義類加載器,則此加載器就是Java程式中預設的類加載器。

雙親委派模型:

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

雙親委派模型的工作流程是:如果一個類加載器收到了類加載請求,它首先不會自己去嘗試加載這個類,而是把這

個請求委托給父類加載器去完成,每一個層次的類加載器都是如此。是以,所有的加載請求都應當傳送到頂層的BootStrap加載器中,隻有當父加載器回報無法完成這個加載請求時(在自己搜尋範圍中沒有找到此類),子加載器才會嘗試自己去加載。

雙親委派模型的意義:

假如自定義java.lang.Object類,會因為雙親委派模型由Bootstarp加載器加載,Bootstarp加載的是Java自定義的java.lang.Object,那麼自定義的java.lang.Object類就不會加載。