通過(guò)class文件對(duì)象,去使用該文件中的成員變量Field,構(gòu)造方法Constructor,成員方法Method。
1.獲取class文件對(duì)象
獲取class文件對(duì)象的方式:
1:Object類的getClass()方法
2:數(shù)據(jù)類型的靜態(tài)屬性class
3:Class.forName(String className);
** 注意:**在開(kāi)發(fā)是我們一般用第三種,因?yàn)榈谌N是一個(gè)字符串,而不是一個(gè)具體的類名。這樣我們就可以把這樣的字符串配置到配置文件中。
** Demo:輔助類Person**
package com.hust.jianshu.reflectDemos.acamy;
public class Person {
private String name;
int age;
public String address;
public Person() {
}
private Person(String name) {
this.name = name;
}
Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println("show");
}
public void method(String s) {
System.out.println("method " + s);
}
public String getString(String s, int i) {
return s + "---" + i;
}
private void function() {
System.out.println("function");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address
+ "]";
}
}
** Demo:獲取class文件對(duì)象的幾種方式**
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1
Person p = new Person();
Class c = p.getClass();
Person p2 = new Person();
Class c2 = p2.getClass();
System.out.println(p == p2);// false
System.out.println(c == c2);// true
// 方式2
Class c3 = Person.class;
// int.class;
// String.class;
System.out.println(c == c3);//true
// 方式3
// ClassNotFoundException
Class c4 = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");
System.out.println(c == c4);//true
}
}
2.獲取構(gòu)造方法
通過(guò)反射既可以獲取公有構(gòu)造方法,也可以暴力訪問(wèn)私有構(gòu)造方法。同時(shí)可以單個(gè)獲取,也可以批量獲取。
** Demo:通過(guò)反射獲取構(gòu)造方法并使用**
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 獲取字節(jié)碼文件對(duì)象
Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");
// 獲取多個(gè)構(gòu)造方法
// public Constructor[] getConstructors():所有公共構(gòu)造方法
// public Constructor[] getDeclaredConstructors():所有構(gòu)造方法
Constructor[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
// 獲取單個(gè)構(gòu)造方法
// 獲取無(wú)參構(gòu)造方法對(duì)象
// public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 參數(shù)表示的是:你要獲取的構(gòu)造方法的構(gòu)造參數(shù)個(gè)數(shù)及數(shù)據(jù)類型的class字節(jié)碼文件對(duì)象
Constructor con = c.getConstructor();// 返回的是構(gòu)造方法對(duì)象
// public T newInstance(Object... initargs)
// 使用此 Constructor 對(duì)象表示的構(gòu)造方法來(lái)創(chuàng)建該構(gòu)造方法的聲明類的新實(shí)例,并用指定的初始化參數(shù)初始化該實(shí)例。
Object obj = con.newInstance();
System.out.println(obj);
// 獲取帶參構(gòu)造方法對(duì)象
// public Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor con1 = c.getConstructor(String.class, int.class,
String.class);
// 通過(guò)帶參構(gòu)造方法對(duì)象創(chuàng)建對(duì)象
// public T newInstance(Object... initargs)
Object obj1 = con1.newInstance("林青霞", 27, "北京");
System.out.println(obj1);
// 獲取私有構(gòu)造方法對(duì)象
// NoSuchMethodException:每個(gè)這個(gè)方法異常
// 原因是一開(kāi)始我們使用的方法只能獲取公共的,下面這種方式就可以了。
Constructor con2 = c.getDeclaredConstructor(String.class);
// 用該私有構(gòu)造方法創(chuàng)建對(duì)象
// IllegalAccessException:非法的訪問(wèn)異常。
// 暴力訪問(wèn)
con2.setAccessible(true);// 值為true則指示反射的對(duì)象在使用時(shí)應(yīng)該取消Java語(yǔ)言訪問(wèn)檢查。
Object obj2 = con2.newInstance("風(fēng)清揚(yáng)");
System.out.println(obj2);
}
}
3.獲取成員變量
>通過(guò)反射可以獲取所有類型的成員變量,并且在創(chuàng)建對(duì)象后可以對(duì)獲取的成員變量設(shè)置值
** Demo:通過(guò)反射獲取成員變量并使用**
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 獲取字節(jié)碼文件對(duì)象
Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");
// 獲取多個(gè)的成員變量
// Field[] fields = c.getFields();所有公共成員變量
Field[] fields = c.getDeclaredFields();// 所有成員變量
for (Field field : fields) {
System.out.println(field);
}
// 通過(guò)無(wú)參構(gòu)造方法創(chuàng)建對(duì)象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
// 獲取單個(gè)的成員變量
// 獲取address并對(duì)其賦值
Field addressField = c.getField("address");
// public void set(Object obj,Object value)
// 將指定對(duì)象變量上此 Field 對(duì)象表示的字段設(shè)置為指定的新值。
addressField.set(obj, "北京"); // 給obj對(duì)象的addressField字段設(shè)置值為"北京"
System.out.println(obj);
// 獲取name并對(duì)其賦值
// NoSuchFieldException
Field nameField = c.getDeclaredField("name");
// IllegalAccessException
nameField.setAccessible(true);
nameField.set(obj, "林青霞");
System.out.println(obj);
// 獲取age并對(duì)其賦值
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 27);
System.out.println(obj);
}
}
4.獲取成員方法
通過(guò)反射可以獲取對(duì)象的所有類型成員變量,并調(diào)用
** Demo:通過(guò)反射獲取成員方法并使用**
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 獲取字節(jié)碼文件對(duì)象
Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");
// 獲取所有的方法
// Method[] methods = c.getMethods(); // 獲取自己的包括父親的公共方法
Method[] methods = c.getDeclaredMethods(); // 獲取自己的所有的方法
for (Method method : methods) {
System.out.println(method);
}
Constructor con = c.getConstructor();
Object obj = con.newInstance();
// 獲取單個(gè)方法并使用
// public void show()
// public Method getMethod(String name,Class<?>... parameterTypes)
// 第一個(gè)參數(shù)表示的方法名,第二個(gè)參數(shù)表示的是方法的參數(shù)的class類型
Method m1 = c.getMethod("show");
// public Object invoke(Object obj,Object... args)
// 返回值是Object接收,第一個(gè)參數(shù)表示對(duì)象是誰(shuí),第二參數(shù)表示調(diào)用該方法的實(shí)際參數(shù)
m1.invoke(obj); // 調(diào)用obj對(duì)象的m1方法
System.out.println("----------");
// public void method(String s)
Method m2 = c.getMethod("method", String.class);
m2.invoke(obj, "hello");
System.out.println("----------");
// public String getString(String s, int i)
Method m3 = c.getMethod("getString", String.class, int.class);
Object objString = m3.invoke(obj, "hello", 100);
System.out.println(objString);
// String s = (String)m3.invoke(obj, "hello",100);
// System.out.println(s);
System.out.println("----------");
// private void function()
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
}
}
5.反射應(yīng)用舉例
5.1 通過(guò)反射越過(guò)泛型檢查
集合里面的泛型是給編繹器看的,在編繹成字節(jié)碼后已經(jīng)去掉了該類型,變成時(shí)論的Object類型,可以通過(guò)反編繹工具驗(yàn)證一下。正是由于集合里面的泛型有這種機(jī)制,我們才可以利用反射得到字節(jié)碼文件,越過(guò)泛型檢查
** Demo:**在ArrayList<Integer>集合中添加一個(gè)字符串?dāng)?shù)據(jù)
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 創(chuàng)建集合對(duì)象
ArrayList<Integer> array = new ArrayList<Integer>();
// array.add("hello");
array.add(10);
Class c = array.getClass(); // 集合ArrayList的class文件對(duì)象
Method m = c.getMethod("add", Object.class);
m.invoke(array, "hello"); // 調(diào)用array的add方法,傳入的值是hello
m.invoke(array, "world");
m.invoke(array, "java");
System.out.println(array);
}
}
5.2 反射工具類
** Demo: 反射工具類**
import java.lang.reflect.Field;
public class ToolDemo {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, IllegalArgumentException, IllegalAccessException {
Person p = new Person();
Tool t = new Tool();
t.setProperty(p, "name", "林青霞");
t.setProperty(p, "age", 27);
System.out.println(p);
System.out.println("-----------");
Dog d = new Dog();
t.setProperty(d, "sex", '男');
t.setProperty(d, "price", 12.34f);
System.out.println(d);
}
}
class Tool {
public void setProperty(Object obj, String propertyName, Object value)
throws NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
// 根據(jù)對(duì)象獲取字節(jié)碼文件對(duì)象
Class c = obj.getClass();
// 獲取該對(duì)象的propertyName成員變量
Field field = c.getDeclaredField(propertyName);
// 取消訪問(wèn)檢查
field.setAccessible(true);
// 給對(duì)象的成員變量賦值為指定的值
field.set(obj, value);
}
}
class Dog {
char sex;
float price;
@Override
public String toString() {
return sex + "---" + price;
}
}
class Person {
private String name;
public int age;
@Override
public String toString() {
return name + "---" + age;
}
6.動(dòng)態(tài)代理
代理:本來(lái)應(yīng)該自己做的事情,卻請(qǐng)了別人來(lái)做,被請(qǐng)的人就是代理對(duì)象。
舉例:春季回家買票讓人代買
動(dòng)態(tài)代理:在程序運(yùn)行過(guò)程中產(chǎn)生的這個(gè)對(duì)象,動(dòng)態(tài)代理其實(shí)就是通過(guò)反射來(lái)生成一個(gè)代理
** Demo:反射實(shí)現(xiàn)動(dòng)態(tài)代理類**
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
UserDao ud = new UserDaoImpl();
System.out.println(ud.getClass().getName());// ...UserDaoImpl
ud.add();
ud.delete();
ud.update();
ud.find();
System.out.println("-----------");
// 我們要?jiǎng)?chuàng)建一個(gè)動(dòng)態(tài)代理對(duì)象
// Proxy類中有一個(gè)方法可以創(chuàng)建動(dòng)態(tài)代理對(duì)象
// 我準(zhǔn)備對(duì)ud對(duì)象做一個(gè)代理對(duì)象
MyInvocationHandler handler = new MyInvocationHandler(ud);
// public static Object newProxyInstance(ClassLoader loader,Class<?>[]
// interfaces,InvocationHandler h)
// ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
// Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
// InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
.getClassLoader(), ud.getClass().getInterfaces(), handler);
// 創(chuàng)建的代理對(duì)象是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)對(duì)象,它并不是我們的InvocationHandler類型,
// 也不是我們定義的那組接口的類型,而是在運(yùn)行是動(dòng)態(tài)生成的一個(gè)對(duì)象,并且命名方式都是這樣的形式,
// 以$開(kāi)頭,proxy為中,最后一個(gè)數(shù)字表示對(duì)象的標(biāo)號(hào)。
System.out.println(proxy.getClass().getName());// ...$Proxy0
proxy.add();
proxy.delete();
proxy.update();
proxy.find();
}
}
// 每一個(gè)動(dòng)態(tài)代理類都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口,
// 并且每個(gè)代理類的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,
// 當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,
// 這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的invoke 方法來(lái)進(jìn)行調(diào)用。
class MyInvocationHandler implements InvocationHandler {
private Object target; // 目標(biāo)對(duì)象
public MyInvocationHandler(Object target) {
this.target = target;
}
// proxy:代表動(dòng)態(tài)代理對(duì)象
// method:代表正在執(zhí)行的方法
// args:代表調(diào)用目標(biāo)方法時(shí)傳入的實(shí)參
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("權(quán)限校驗(yàn)");
Object result = method.invoke(target, args);
System.out.println("日志記錄");
return result; // 返回的是代理對(duì)象
}
}
class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("刪除功能");
}
@Override
public void update() {
System.out.println("修改功能");
}
@Override
public void find() {
System.out.println("查找功能");
}
}
interface UserDao {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void find();
}