一 反射机制
通过字节码文件对象,去使用成员变量,构造方法,成员方法
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
(老师讲过,反射的好处:通过配置文件,将来那里配的是谁,运行的就是谁,别人不知道运行的是什么类)
二 类的字节码文件(类名.class)封装到字节码对象Class中
Class类:
成员变量 Field
将类中的成员属性抽象成字段,并将字段抽象成字段对象Field。其中包含:成员属性的名称、大小、类型、访问权限
构造方法 Constructor
将构造函数封装成构造器。构造器中包含了构造函数的名称、类型、参数列表等信息
成员方法 Method
将类中的方法封装成对象,包含方法的名称、类型、参数列表等
同时,字节码对象Class还提供了对这些成员的操作。如:getXXX( )等。
三 获得类的字节码对象
三种方法可以获得类的相对应的字节码对象:
Person p = new Person();
Class c = p.getClass();
Class c2 = Person.class;
Class c3 = Class.forName("cn.itcast_01.Person");
一般我们到底使用谁呢?
A:自己玩 任选一种,第二种比较方便
B:开发 第三种
为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
四 通过反射获取构造方法
代码1:
public static void main(String[] args) {
Class c = Class.forName("cn.itcast_01.Person");
// public Constructor[] getConstructors():所有公共构造方法
// public Constructor[] getDeclaredConstructors():所有构造方法
Constructor[] cons = c.getConstructors();
for(Constructor con: cons){
System.out.println(con);
}
// public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
}
代码2:
带参数的
public static void main(String[] args) {
Class c = Class.forName("cn.itcast_01.Person");
Constructor con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("林青霞", 27, "北京");
System.out.println(obj);
}
代码3:
通过反射获取私有构造方法并使用
public static void main(String[] args) {
Class c = Class.forName("cn.itcast_01.Person");
Constructor con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object obj = con.newInstance("风清扬");
System.out.println(obj);
}
五 通过反射获取成员变量
代码1:
public static void main(String[] args) {
Class c = Class.forName("cn.itcast_01.Person");
// Field[] fields = c.getFileds();
// Field[] fields = c.getDeclaredFields();
Constructor con = c.getConstructor();
Object obj = con.newInstance();
Field addressField = c.getFiled("address");
addressField.set(obj, "北京");
System.out.println(obj);
//暴力访问,取消字段的权限检查,强行将所访问的字段变为公共的
Field nameField = c.getDeclaredFiled("name");
nameField.setAccessible(true);
nameField.set(obj, "林青霞");
System.out.println(obj);
Field ageField = c.getDeclaredFiled("age");
ageField.setAccessible(true);
ageField.set(obj, 27);
System.out.println(obj);
}
六 通过反射获取成员方法
代码1:
public static void main(String[] args) {
Class c = Class.forName("cn.itcast_01.Person");
Method[] methods = c.getMethods();
Method[] methods = c.getDeclaredMethods();
Constructor con = c.getConstructor();
Object obj = con.newInstance();
Method m1 = c.getMethod("show");
m1.invoke(obj);
Method m2 = c.getMethod("method", String.class);
m2.invoke(obj, "hello");
Method m3 = c.getMethod("getString", String.class, int.class);
Object objString = m3.invoke(obj, "hello", 100);
System.out.println(objString);
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
}
七 案例
1 现有ArrayList<Integer>的一个对象,想在这个集合中添加一个字符串数据,如何实现呢?
public static void main(String[] args) {
ArrayList<Integer> array = new ArrayList<Integer>();
Class c = array.getClass();
Method m1 = c.getDeclaredMethod("add", Object.class);
m1.setAccessible(true);
m1.invoke(array, "hello");
System.out.println(array);
}
2 通过配置文件运行类中的方法
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
FileReader fr = new FileReader("class.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
// 由此可以看出,获取字节码文件对象的第三种方法更常用
Class c = Class.forName(className);
Constructor con = c.getConstructor();
Object obj = con.newInstance();
Method method = c.getMethod(methodName);
method.invoke(obj);
}
八 动态代理
在程序运行过程中产生的这个对象
而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
代码:
public interface StudentDao {
public abstract void login();
public abstract void regist();
}
public class StudentDaoImpl implements StudentDao {
public void login() {
System.out.println("登录功能");
}
public void regist() {
System.out.println("注册功能");
}
}
public class MyInvocationHandler implements InvocationHandler{
private Object target; // 目标对象
public MyInvocationHandler(Object target){
this.target = target;
}
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; // 返回的是代理对象
}
}
public static void main(String[] args) {
// 我们要创建一个动态代理对象
// Proxy类中有一个方法可以创建动态代理对象
// public static Object newProxyInstance(ClassLoader loader,Class<?>[]
// interfaces,InvocationHandler h)
// 我准备对sd对象做一个代理对象
StudentDao sd = new StudentDaoImpl();
MyInvocationHandler handler = new MyInvocationHandler(sd);
StudentDao proxy = (StudentDao)Proxy.newProxyInstance(sd.getClass()
.getClassLoader(), sd.getClass().getInterfaces(), handler);
proxy.login();
proxy.regist();
}