反射(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<