一,概述
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
以下主要介紹通過反射獲取私有的和公共的構造方法、成員變量、方法;
二,獲取任意類的字節碼對象的幾種方式
Class類:
Class 類的實例對象表示正在運行的 Java 應用程序中的類和接口;也就是jvm中有很多的實例,每個類都有唯一的字節碼對象(Class對象);通過唯一的字節碼對象就可以反射任意一個類中的構造,成員變量和方法;
Class 類沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機自動構造的;我們不需要創建,JVM已經幫我們創建了;
Class 對象用於提供類本身的信息,比如有幾種構造方法, 有多少屬性,有哪些普通方法;
三種方式獲取的字節碼對象是同一個;
private static void demo1() throws ClassNotFoundException {
//第一種
Class clazz = Class.forName("com.ang.Teacher");
//第二種
Class clazz2 = Teacher.class;
Teacher t = new Teacher();
//第三種
Class clazz3 = t.getClass();
System.out.println(clazz == clazz2);
System.out.println(clazz2 == clazz3);
}
被反射的對象:根據具體情況修改構造類型(私有的:private和公共的:public),各種情況都有註釋;
public class Teacher {
private String name;
private int age;
// private Teacher(){
//
// }
public Teacher(String name, int age) {
super();
this.name = name;
this.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;
}
@Override
public String toString() {
return "Teacher [name=" + name + ", age=" + age + "]";
}
private int add(int a,int b){
return a+b;
}
}
三,獲取有參,無參構造
獲取構造方法步驟
1,首先獲取要反射類的字節碼對象(Class)
2,通過字節碼對象獲取有參或無參構造器Constructor
- 獲取public類型有參構造
private static void demo2() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class clazz = Class.forName("com.ang.Teacher");
//通過Teacher空參構造創建對象,如果沒用空參構造就會報錯;而且必須是public類型的空參構造方法;
//Teacher t = (Teacher) clazz.newInstance(); //如果沒有空參構造或者空參構造是私有的不能使用此方法獲取對象
//通過有參構造獲取對象,有參構造必須是public類型的
Constructor c = clazz.getConstructor(String.class,int.class); //獲取有參構造器
Teacher t = (Teacher) c.newInstance("李四",50);//通過有參構造創建對象
System.out.println(t);
}
- 獲取public類型的無參構造
private static void demo3() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getConstructor();//獲取public類型的空參構造
Teacher t = (Teacher) c.newInstance();//通過空參構造獲取對象
t.setAge(40);
System.out.println(t);
}
- 獲取私有(private)有參構造
private static void demo6() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class<?> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(String.class,int.class);//獲取私有有參構造
c.setAccessible(true);//去除私有權限
Teacher t = (Teacher) c.newInstance("小高",50);
System.out.println(t);
}
四,通過反射獲取對象
步驟
1,首先獲取要反射類的字節碼對象(Class)
2,通過字節碼對象的newInstanc()方法獲取實例對象;(注意:需要類的無參構造是public或者默認生成的)
- public構造方法
//通過空參構造獲取對象
private static void demo5() throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("com.ang.Teacher");
//空參構造必須是public類型的
Teacher t = (Teacher) clazz.newInstance();
t.setName("趙敏");
System.out.println(t);
}
- 非public類型構造方法
private static void demo6() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class<?> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(); //獲取私有構造,方法參數是可變的,所以有參構造直接傳入參數類型的字節碼對象;
c.setAccessible(true); //去除私有權限
Teacher t = (Teacher) c.newInstance();
t.setAge(100);
System.out.println(t);
}
五,獲取私有成員變量(屬性值)並修改和使用
步驟
1,首先獲取要反射類的字節碼對象(Class)
2,根據反射創建實例
3,通過字節碼對象中的getField()或者getDeclaredField()方法獲取Field(提供有關類或接口的單個成員變量的信息)
4,通過Field就可以修改和使用成員變量了
- pubic類型的有參構造是,獲取私有成員變量
private static void demo4() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException,
NoSuchFieldException {
Class<?> clazz = Class.forName("com.ang.Teacher");
//通過有參構造獲取對象,有參構造必須爲public類型
Constructor c = clazz.getConstructor(String.class,int.class);//字節碼階段,構造方法的參數只能是字節碼對象;
Teacher t = (Teacher) c.newInstance("王豔",100); //創建實例對象
Field field = clazz.getDeclaredField("name");//獲取私有成員變量
field.setAccessible(true);//去除私有權限
field.set(t, "李代理"); //修改私有成員變量的值
System.out.println(t);
}
六,獲取私有方法
private static void demo7() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class<?> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(); //獲取私有無參構造
c.setAccessible(true); //去除私有權限
Teacher t = (Teacher) c.newInstance();//通過私有構造獲取實例
Method method = clazz.getDeclaredMethod("add", int.class,int.class);//字節碼階段反射有參方法是,需傳入參數類型的字節碼
method.setAccessible(true); //去除私有權限
int num = (Integer) method.invoke(t, 20,8);
System.out.println(num);
}
反射常用的幾個類:
1,Class:反射的入口,類的實例表示正在運行的 Java 應用程序中的類和接口。
2,Constructor :提供關於類的單個構造方法的信息以及對它的訪問權限。
3,Field :提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。反射的字段可能是一個類(靜態)字段或實例字段。
4,Method :提供關於類或接口上單獨某個方法(以及如何訪問該方法)的信息。所反映的方法可能是類方法或實例方法(包括抽象方法)。
七,案例
1,通過反射越過泛型檢查
private static void demo() throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
List<Integer> list = new ArrayList();
list.add(11);
list.add(22);
Class clazz = list.getClass();
Method m = clazz.getMethod("add", Object.class);
m.invoke(list, "哈嘍");
System.out.println(list);
}
打印輸出:
[11, 22, 哈嘍] //List泛型指定的是Integer,結果通過反射存入了String
2,通過反射寫一個通用的設置某個對象的某個屬性爲指定的值
package com.ang;
import java.lang.reflect.Field;
public class Tool {
public void setProperty(Object obj,String propertyName,Object values) throws Exception{
Class clazz = obj.getClass();
Field f = clazz.getDeclaredField(propertyName); //暴力反射獲取屬性
f.setAccessible(true); //去除私有權限
f.set(obj, values); //修改屬性值
}
}
private static void demos() throws Exception {
Student s = new Student("張無忌", 40);
System.out.println(s);
Tool t = new Tool();
t.setProperty(s, "name", "趙敏");
System.out.println(s);
}
打印輸出:可以看到,把同一個對象中的屬性值修改了
Student [name=張無忌, age=40]
Student [name=趙敏, age=40]
package com.ang;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [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;
}
}
3, 寫一個Properties格式的配置文件,配置指定類的完整名稱。再寫一個程序,讀取這個Properties配置文件,獲得類的完整名稱並加載這個類,用反射的方式運行run方法。
指定的類:
package com.ang;
public class Animal {
public void run(){
System.out.println("歡迎來到動物世界。。。");
}
}
properties:name.properties配置文件內容
com.ang.Animal
測試類:
public class Test {
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("name.properties"));
Class clz = Class.forName(br.readLine());
Animal a= (Animal) clz.newInstance();
a.run();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
運行結果:
歡迎來到動物世界。。。