通過class文件對象,去使用該文件中的成員變量Field,構造方法Constructor,成員方法Method。

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