1.什么是反射
* JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
* 对于任意一个对象,都能够调用它的任意一个方法和属性;
* 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明 的,想要获取任何东西都可以。
*网上查阅得到理解
如何理解反射?简单的一句话解释,将传统的开发思路反向逆转。
传统的方式是通过类创建对象:类 ---> 对象。
反射就是将这个过程逆转,通过对象得到类:对象 ---> 类。
通过对象得到的这个类该如何表示?
使用Class类来表示,此类是Java反射的源头,是用来描述其他类的类,Class类的每一个实例化对象就是对其他类的描述。
在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()。
也就是说每一个类,都可以调用getClass()方法获取对应的Class对象,用来描述目标类,我们将这个Class类叫做目标类的运行时类。
* 要想解剖一个类,必须先要获取到该类的字节码文件对象。
* 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
*获取字节码对象的三种方式:
➢ a:Object类的getClass()方法,判断两个对象是否是同一个字节码文件
➢ b:静态属性class,锁对象
➢c:Class类中静态方法forName(),读取配置文件
2.反射演示
(1)
package com.hbsi.reflect;
import com.hbsi.demo.Person;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
Class clazz1 = Class.forName("com.hbsi.demo.Person");//读取配置文件
Class clazz2 = Person.class;//当做静态方法的锁对象
Class clazz3 = new Person().getClass();//判断是否是同一个字节码对象
System.out.println(clazz1==clazz2);
System.out.println(clazz2==clazz3);
}
}
(2)通过反射获取构造方法并使用
Class类的newInstance()方法 | 使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了 |
Class类的getConstructor(String.class,int.class)方法 | 可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数,然后再调用Constructor类的newInstance("张三",20)方法创建对象 |
package com.hbsi.demo;
//无參构造方法 alt shift s c 有參alt shift s o equal方法 alt shift s h
public class Person {
private String name;
private int age;
//其他省略不写
public void eat(){
System.out.println("今天吃了一顿金钱豹");
}
public void eat(int num){
System.out.println("今天吃了"+num +"顿金钱豹");
}
}
package com.hbsi.reflect;
//通过反射获取构造方法并使用
import java.lang.reflect.Constructor;
import com.hbsi.demo.Person;
/* * Class类的newInstance()方法是使用该类无参的构造函数创建对象,
如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)
方法获取一个指定的构造函数然后再调用Constructor类的newInstance("张三",20)方法创建对象*/
public class Demo4 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.hbsi.demo.Person");
// Class类的newInstance()方法是使用该类无参的构造函数创建对象,
// Person p = (Person) clazz.newInstance();
// System.out.println(p);
Constructor c = clazz.getConstructor(String.class, int.class);//获取有参构造
Person p = (Person) c.newInstance("张三", 23);//通过有参构造创建对象
System.out.println(p);
}
}
(3)通过反射获取成员变量并使用
Class.getField(String)方法 | 可以获取类中的指定字段(可见的) |
Class.getDeclaedField(String)方法 | 可以获取类中的指定字段(私有的) |
通过set(obj, "李四")方法可以设置指定对象上该字段的值, 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值
mport java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import com.hbsi.demo.Person;
//Field
//* Class.getField(String)方法可以获取类中的指定字段(可见的),
//如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")
//方法可以设置指定对象上该字段的值, 如果是私有的需要先调用setAccessible(true)设置访问权限,
//用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值
//通过反射获取成员变量并使用
public class Demo5 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.hbsi.demo.Person");
Constructor c = clazz.getConstructor(String.class,int.class);
Person p = (Person) c.newInstance("张三",11);
//Field f = clazz.getField("name");//获取姓名字段 共有的
Field f = clazz.getDeclaredField("name");//暴力反射获取私有字段
f.setAccessible(true);//去除私有权限
f.set(p,"李四");
f.get(p);
System.out.println(p);
}
}
(4)通过反射获取方法并使用
getMethod | 返回一个 |
getDeclaredMethod | 返回一个 |
package com.hbsi.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import com.hbsi.demo.Person;
//通过反射获取方法
//Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)
//方法可以获取类中的指定方法,调用invoke(Object, Object...)可以调用该方法,Class.getMethod("eat")
//invoke(obj) Class.getMethod("eat",int.class) invoke(obj,10)
public class Demo6 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.hbsi.demo.Person");
Constructor c = clazz.getConstructor(String.class,int.class);
Person p = (Person) c.newInstance("张三",11);
Method m =clazz.getMethod("eat");//获取eat方法、
m.invoke(p);//执行eat方法
Method m1 =clazz.getMethod("eat",int.class);//获取eat方法
m1.invoke(p,10);//执行eat方法
}
}
(5)通过反射越过泛型检查
* ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现呢?
package com.hbsi.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
//* ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现呢?
//泛型只在编译期间有效,运行期会被擦出掉,字节码文件是运行期 所有不会报错
public class Demo7 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(111);
Class clazz = Class.forName("java.util.ArrayList");//获取字节码对象
Method m = clazz.getMethod("add", Object.class);//获取list的add方法
m.invoke(list, "abc");
System.out.println(list);
}
}
(6)反射(动态代理的概述和实现)
* A:动态代理概述
* 代理:本来应该自己做的事情,请了别人来做,被请的人就是代理对象。
* 举例:春节回家买票让人代买
* 真实对象:被代理的对象
*代理对象: 代理对象代理真实对象,达到增强真实对象功能的目的。 用户调用代理对象,代理对象调用真实对象。
* 动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
* 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* 最终会调用InvocationHandler的方法
* InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
我们来举一个卖电脑的例子
➢接口SaleComputer
package com.hbsi.proxy;
public interface SaleComputer {
public String sale(double money);
public void show();
}
➢实现类
package com.hbsi.proxy;
public class Lenovo implements SaleComputer{
@Override
public String sale(double money) {
// TODO Auto-generated method stub
System.out.println("花了"+money+"元买了一台电脑");
return "联想电脑";
}
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("展示电脑.......");
}
}
➢动态代理测试类(对sale()方法进行增强)
package com.hbsi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//代理对象代理真实对象,达到增强真实对象功能的目的。
/**
*
* @author lbq 静态代理:有一个类文件描述成代理模式 动态代理:在内存中形成代理类,并不能看到这个代理类文件
*/
public class ProxyTest {
public static void main(String[] args) {
// 1.代理对象和真实对象实现相同的接口
// 2.通过Proxy.newProxyInstance() 获取代理对象
// 3.使用代理对象调用方法
// 4.增强方法
//真实对象 目标类
Lenovo lenovo = new Lenovo();
// 代理类 将目标类(切入点) 和切面类(通知)结合 ------>切面
//lenovo.getClass().getInterfaces()代理对象实现的接口们 和真实对象一样
SaleComputer proxy_lenovo =(SaleComputer)Proxy.newProxyInstance(lenovo.getClass().getClassLoader(),
lenovo.getClass().getInterfaces(), new InvocationHandler() {
//代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
// proxy:代理对象
// method:代理对象调用的方法,被封装为对象
// args:代理对象调用的方法时,传递的实际参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
if(method.getName().equals("sale")){
//1.增强参数
double money = (double)args[0];
money = money*0.85;
System.out.println("专车接送你");//2,增强方法体
//使用真实对象调用该方法
String obj = (String) method.invoke(lenovo, money);
System.out.println("免费送货");//增强方法体
//3.增强返回值
return obj+"_鼠标垫";
}else{
Object obj = method.invoke(lenovo, args);
return obj;
}
}
});
//代理对象调用方法
String computer = proxy_lenovo.sale(8000);
System.out.println(computer);
proxy_lenovo.show();
}
}
结果展示:专车接送你
花了6800.0元买了一台电脑
免费送货
联想电脑_鼠标垫
展示电脑.......