一:什麼是反射
在認識反射之前,首先回顧下什麼是正向處理。如果我們需要執行個體化一個對象,首先需要導入這個類的包,在這個包中找這個類:
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() {
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");
}
}
類加載器
什麼是類加載器:
類加載器:
通過一個類的全名稱來擷取此類的二進制位元組流,實作這個操作的代碼子產品為類加載器。
通過 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大類加載器的關系:
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程式中預設的類加載器。
雙親委派模型:
雙親委派模型的工作流程是:如果一個類加載器收到了類加載請求,它首先不會自己去嘗試加載這個類,而是把這
個請求委托給父類加載器去完成,每一個層次的類加載器都是如此。是以,所有的加載請求都應當傳送到頂層的BootStrap加載器中,隻有當父加載器回報無法完成這個加載請求時(在自己搜尋範圍中沒有找到此類),子加載器才會嘗試自己去加載。
雙親委派模型的意義:
假如自定義java.lang.Object類,會因為雙親委派模型由Bootstarp加載器加載,Bootstarp加載的是Java自定義的java.lang.Object,那麼自定義的java.lang.Object類就不會加載。