認識發射
反射指的是對象的反向處理操作,根據對象倒推類的組成。既然是反向處理,我們先來觀察一下"正"的操作。在默認情況下,必須要先導入一個包,而後才能產生類的實例化對象
範例:觀察正常處理
import java.util.Date;
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date();
}
}
以上是我們正常的關於對象的處理流程:根據**包名.類名找到類
** 。所謂的"反"指的是根據對象來取得對象的來源信息,而這個"反"的操作核心的處理就在於Object類的一個方法: 取得Class對象
:
* @return The {@code Class} object that represents the runtime
* class of this object.
* @jls 15.8.2 Class Literals
*/
public final native Class<?> getClass();
在反射的世界裏面,看重的不再是一個對象,而是對象身後的組成(類、構造、普通、成員等)。
class對象的三種實例化方式
Class類是描述整個類的概念,也是整個反射的操作源頭,在使用Class類的時候需要關注的依然是這個類的對象。 任何一個類的class對象由JVM加載類後產生(該對象在JVM中全局唯一),用戶只能調用指定方法來取得該對象。
而這個類的對象的產生模式一共有三種:
- 任何類的實例化對象可以通過調用Object類提供的
getClass()
取得該類的Class對象 - “
類名稱.Class
”可以直接根據某個具體類來取得其Class對象 - 調用Class類的靜態方法
:public static Class<?> Class.forName(String className) throws ClassNotFoundException
傳入類的全名稱來取得其Class對象
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 通過類對象.getClass()
System.out.println(date.getClass());
// 通過類名稱.class
System.out.println(Date.class);
// 通過調用Class類提供的靜態方法forName(String className)
try {
System.out.println(Class.forName("java.util.Date"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在以上給出的三個方法中我們可以發現,除了第一種方法會產生Date類的實例化對象之外,其他的兩種都不會產生 Date類的實例化對象。
於是取得了Class類對象有一個最直接的好處:可以通過反射實例化對象,在Class類中定義有如下方法:
public T newInstance()
throws InstantiationException, IllegalAccessException
範例:反射實例化對象
import java.util.Date;
public class Test {
public static void main(String[] args) throws Exception{
// 取得類的實例化對象
Class<Date> cls = (Class<Date>)Class.forName("java.util.Date");
// 通過反射取得Date類實例化對象
Date date = cls.newInstance(); // 實例化對象,等價於 new java.util.Date()
System.out.println(date);
}
}
現在發現除了關鍵字new之外,對於對象的實例化模式有了第二種做法,通過反射進行。
反射與工廠設計模式
工廠設計模式曾經給過原則:如果是自己編寫的接口,要想取得本接口的實例化對象,最好使用工廠類來設計。但是也需要知道傳統工廠設計所帶來的問題。
傳統工廠類:https://blog.csdn.net/sifanchao/article/details/83420681
傳統工廠類在實際開發之中根本用不到。(問題就在於new)。每增加一個接口的子類就需要修改工廠類。
如果要想解決關鍵字new帶來的問題,最好的做法就是通過反射來完成處理,因爲Class類可以使用newInstance()
實例化對象,同時Class.forName()
能夠接收類名稱。
interface IFruit{
void eat();
}
class Apple implements IFruit {
@Override
public void eat() {
System.out.println("吃蘋果");
}
}
class Orange implements IFruit{
@Override
public void eat() {
System.out.println("吃橘子");
}
}
class FruitFactory {
public FruitFactory() {}
public static IFruit getInstance(String className) {
IFruit fruit = null;
try {
fruit = (IFruit) Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return fruit;
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
IFruit fruit = FruitFactory.getInstance("www.bit.MyTest.Apple");
fruit.eat();
}
}
引入反射後,每當新增接口子類,無需去修改工廠類代碼就可以很方便的進行接口子類擴容。
反射與類操作
利用反射可以做出一個對象具備的所有操作行爲,最爲關鍵的是這一切的操作都可以基於Object進行。
取得父類信息
在java中任何的程序類都一定會有父類,在Class類中就可以通過如下方法來取得父類或者實現的父接口:
- 取得類的包名稱
public Package getPackage() {
return Package.getPackage(this);
}
- 取得父類的Class對象
public native Class<? super T> getSuperclass();
- 取得實現的父接口
public Class<?>[] getInterfaces()
範例:取得包名稱、父類、父接口
interface IFruit{}
interface IMessage{}
class Person{}
class Student extends Person implements IFruit,IMessage{}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Student.class; // 取得Class類對象
// 取得Package名稱
System.out.println(cls.getPackage().getName());
// 取得父類名稱
System.out.println(cls.getSuperclass().getName());
// 取得實現的父接口對象
Class<?>[] iclass = cls.getInterfaces();
for (Class<?> classes: iclass){
System.out.println(classes.getName());
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
www.bit.MyTest
www.bit.MyTest.Person
www.bit.MyTest.IFruit
www.bit.MyTest.IMessage
反射調用構造—Constructor(描述類中構造方法)
- 取得類中指定
public權限
參數類型的構造
@CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.PUBLIC);
}
- 取得類中指定參數(
包含private權限
)類型的構造
@CallerSensitive
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.DECLARED);
}
- 取得類中
public權限
的所有參數類型構造
@CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(true));
}
- 取得類中所有參數(
包含private權限
)類型構造
@CallerSensitive
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(false));
}
範例:取得類中構造信息
import java.lang.reflect.Constructor;
class Person {
public Person(){}
protected Person(String name){}
private Person(String name,int age){}
public Person(String name,int age,String id){}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
// 取得類中指定`public權限`參數類型的構造
Constructor<?> constructor1 = cls.getConstructor(String.class.getClasses());
System.out.println(constructor1);
System.out.println("------------------------------------");
// 取得類中`public權限`的所有參數類型構造
Constructor<?>[] constructor2 = cls.getConstructors();
for (Constructor<?> constructor : constructor2){
System.out.println(constructor);
}
System.out.println("------------------------------------");
// 取得類中指定參數(`包含private權限`)類型的構造
Constructor<?> constructor3 = cls.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor3);
System.out.println("------------------------------------");
// 取得類中所有參數(`包含private權限`)類型構造
Constructor<?>[] constructors = cls.getDeclaredConstructors();
for (Constructor<?> constructor : constructors){
System.out.println(constructor);
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public www.bit.MyTest.Person()
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
public www.bit.MyTest.Person()
------------------------------------
private www.bit.MyTest.Person(java.lang.String,int)
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
private www.bit.MyTest.Person(java.lang.String,int)
protected www.bit.MyTest.Person(java.lang.String)
public www.bit.MyTest.Person()
Process finished with exit code 0
- Constructor類提供了實例化對象的方法
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
在定義簡單java類的時候一定要保留有一個無參構造
範例:觀察Class實例化對象的問題
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
System.out.println(cls.newInstance());
}
}
Exception in thread "main" java.lang.InstantiationException: www.bit.MyTest.Person
at java.lang.Class.newInstance(Class.java:427)
at www.bit.MyTest.Test1.main(Test1.java:150)
Caused by: java.lang.NoSuchMethodException: www.bit.MyTest.Person.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
Process finished with exit code 1
Class類通過反射實例化類對象的時候,只能夠調用類中的無參構造。如果現在類中沒有無參構造則無法使用Class 類調用,只能夠通過明確的構造調用實例化處理。
範例:通過Constructor類實例化對象
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
// 取得指定參數類型的構造方法對象
System.out.println(cls.getConstructor(String.class,int.class).newInstance("改革開放",40));
}
}
反射調用普通方法
- 取得類中指定
public權限
參數類型的普通方法
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
- 取得類中指定參數(
包含private權限
)類型的普通方法
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
- 取得類中
public權限
的所有參數類型普通方法
@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
- 取得類中所有參數(
包含private權限
)類型普通方法
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
範例:取得一個類中的全部普通方法
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods){
System.out.println(method);
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.toString()
public java.lang.String www.bit.MyTest.Person.getName()
public void www.bit.MyTest.Person.setName(java.lang.String)
public void www.bit.MyTest.Person.setAge(int)
public int www.bit.MyTest.Person.getAge()
Process finished with exit code 0
上面程序編寫的簡單java類中的getter、setter方法採用的都是明確的對象調用。
而現在有了反射機制處理之後,即使你沒有明確的Person類型對象(依然需要實例化對象,Object對象描述,所有 的普通方法必須在有實例化對象之後纔可以進行調用),就可以通過反射調用。
- Method類中有提供調用類中普通方法的API
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
// 1.拿到Person的Class對象
Class<?> cls = Person.class;
// 2.創建Person實例化對象
Person person = (Person) cls.newInstance();
// 3.拿到setName的Method對象
Method setNameMethod = cls.getMethod("setName", String.class);
// 4.通過invoke進行調用,相當於Person對象.setName("改革開發")
setNameMethod.invoke(person,"改革開發");
Method getNameMethod = cls.getMethod("getName");
// 相當於Person對象.getName()
System.out.println(getNameMethod.invoke(person));
}
}
此類操作的好處是:不再侷限於某一具體類型的對象,而是可以通過Object類型進行所有類的方法調用。
反射調用類中屬性
在之前已經成功的實現了類的構造調用、方法調用,除了這兩種模式之外還有類中屬性調用。
前提:類中的所有屬性一定在類對象實例化之後纔會進行空間分配,所以此時如果要想調用類的屬性,必須保證有實例化對象。通過反射的newInstance()
可以直接取得實例化對象(Object類型)
- 取得類中指定
public權限
參數類型的屬性
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
- 取得類中指定參數(
包含private權限
)類型的屬性
@CallerSensitive
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
- 取得類中
public權限
的所有參數類型屬性
@CallerSensitive
public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyFields(privateGetPublicFields(null));
}
- 取得類中所有參數(
包含private權限
)類型屬性
@CallerSensitive
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
範例:取得類中全部屬性
import java.lang.reflect.Field;
class Person {
public String name;
public int age;
protected String id;
}
class Student extends Person{
private String school;
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("www.bit.MyTest.Student");
{
Field[] fields = cls.getFields();
for (Field field : fields){
System.out.println(field);
}
}
System.out.println("-----------------------");
{
Field[] fields = cls.getDeclaredFields();
for (Field field : fields){
System.out.println(field);
}
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.name
public int www.bit.MyTest.Person.age
-----------------------
private java.lang.String www.bit.MyTest.Student.school
Process finished with exit code 0
因爲在實際開發之中,屬性基本上都會進行封裝處理,所以沒有必要去關注父類中的屬性。也就是說以後所取得的屬性都以本類屬性爲主。
而後就需要關注屬性的核心描述類:java.lang.reflect.Field,在這個類之中有兩個重要方法:
- 設置屬性內容 :
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
- 取得屬性內容 :
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
- 取得屬性的類型
public Class<?> getType()
- 動態設置封裝
Constructor、Method、Field類都是AccessibleObject
子類。
AccessibleObject提供動態設置封裝方法(在本次JVM進行中有效且只能通過反射調用)
public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
}
setAccessible(true)
取消了Java的權限控制檢查(注意不是改變方法或字段的訪問權限),調用了類的private屬性,並且能夠修改private成員變量的值
import java.lang.reflect.Field;
class Person {
public Person(){}
public String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
Person person = (Person) cls.getConstructor().newInstance();
Field nameField = cls.getField("name");
nameField.set(cls,"劉德華");
System.out.println(nameField.get(cls));
System.out.println(nameField.getType().getName());
Field ageField = cls.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(cls,45);
System.out.println(ageField.get(cls));
System.out.println(ageField.getType().getName());
}
}