你还不会使用反射吗?——Java反射机制

Java反射机制

点击下载文档资料

概述

  • Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部消息,并能直接操作任意对象的内部属性及方法
  • 加载完类后,在堆内存的方法区会产生一个Class对象(一个类只有一个),这个对象包含了类的完整结构信息
  • 正常方式:引入需要的’包类’名称 ->通过new实例化 -> 取得实例化对象
  • 反射方式:实例化对象 -> getClass()方法 -> 得到完整的’包类’名称

Java反射提供的功能

  • 运行时判断任意一个对象所属的类
  • 运行时构造任意一个类的对象
  • 运行时判断任意一个类所拥有的成员变量和方法
  • 运行时判断获取泛型信息
  • 运行时调用任意一个对象的成员变量和方法
  • 运行时处理注解
  • 动态代理

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Construction:代表类的构造器

反射解决能不能调,封装是建议怎么调

理解Class类并获取Class实例

初步运用

@Test
public void test2() throws Exception {
    Class clazz = Person.class;
    //1.通过反射创建Person对象
    Constructor constructor = clazz.getConstructor(String.class, int.class);
    Person p = (Person)constructor.newInstance("Tom", 12);
    System.out.println(p.toString());
    //通过反射,调用对对象指定的属性、方法
    //调用属性
    Field age = clazz.getDeclaredField("age");
    age.set(p,10);
    System.out.println(p.toString());
    //调用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(p);
    System.out.println("*****************");
    //通过反射,可以调用类的私有结构
    //调用私有的方法
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person) cons1.newInstance("Jerry");
    System.out.println(p1);
    //调用私有的属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"韩坤");
    System.out.println(p1);
    //调用私有的方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String nation = (String) showNation.invoke(p1,"中国");
}

类的加载过程

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)

接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称

为类的加载。加载到内存的类,我们就称为运行时类,此运行时类,就作为Class的一个实例

加载到内存的运行时类,会缓存一定时间。在此时间之内,我们可以通过不同方式获取此运行时类

获取Class实例的方式(四种)

public void test03() throws Exception {
    //方式一:调用运行时类的属性 .class
    Class clazz = Person.class;
    //方式二:通过运行时类的对象
    Person p=new Person();
    Class clazz2 = p.getClass();
    //方式三:调用Class的静态方法,forName(String classpath)
    Class clazz3 = Class.forName("com.starrysky.java.Person");
    //方式四:使用类的加载器(了解)
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("com.starrysky.java.Person");
}

类的加载与ClassLoader的理解

类的加载过程

类的加载 => 类的链接 => 类的初始化

类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成

类的链接:将类的二进制数据合并到JRE中

类的初始化:JVM负责对类进行初始化

ClassLoader

类加载器

1590255619384

读取配置文件的两种方式
Properties properties = new Properties();
//此时文件默认在当前module下
//读取配置文件方式一,默认在当前模块找
FileInputStream inputStream = new FileInputStream("jdbc.properties");
properties.load(inputStream);
//读取配置文件方式二,默认在当前module的src下
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream stream = classLoader.getResourceAsStream("jdbc1.properties");
properties.load(stream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.println(user+"  "+password);

创建运行时类的对象

newInstance():调用此方法,创建对应的运行时类的对象, 要求运行时类必须提供空参的构造器 空参的构造器访问权限得够

Class clazz = Person.class;
Person o = (Person) clazz.newInstance();

获取运行时类的完整结构(了解)

属性

获取属性结构

方法作用
getFields()获取当前运行时类及其父类中声明为public访问权限的属性
getDeclaredFields()获取当前运行时类声明的所有属性(不考虑权限),不包含父类

获取权限修饰符、数据类型、变量名

方法作用
getModifiers()获取当前属性的权限修饰符
getType()获取当前属性的数据类型
getName()获取当前属性名

示例

Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f:declaredFields) {
    //权限修饰符
    int modifiers = f.getModifiers();
    //获取到的为属性,通过Modifier.toString()转为对应的类型
    System.out.print(Modifier.toString(modifiers)+"\t");
    //数据类型
    Class<?> type = f.getType();
    System.out.print(type.getName()+"\t");
    //变量名
    String name = f.getName();
    System.out.println(name+"\t");
    System.out.println();
}

方法

方法作用
getMethods()获取当前运行时类及其父类中声明为public访问权限的方法
getDeclaredMethods()获取当前运行时类声明的所有方法(不考虑权限),不包含父类
getModifiers()获取方法的权限修饰符
getReturnType()获取方法的返回值类型
getName获取方法的方法名
getParameterTypes()获取方法的参数列表
getExceptionTypes()获取方法抛出的异常

构造器

方法作用
getConstructors()获取当前运行时类中声明为public的构造器
getDeclaredConstructors()获取当前运行时类中所有的构造器

父类与泛型

方法作用
getSuperclass()获取运行时父类
getGenericSuperclass()获取运行时带泛型的父类

案例:获取运行时类带泛型父类的泛型

//获取运行时类带泛型父类的泛型
public void text04(){
    Class clazz = Person.class;
    //获取父类类型
    Type type = clazz.getGenericSuperclass();
    //转换
    ParameterizedType parameterizedType = (ParameterizedType) type;
    //获取泛型类型
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    //两种方式
    //  System.out.println(((Class)actualTypeArguments[0]).getName());
    System.out.println(actualTypeArguments[0].getTypeName());
    System.out.println(type);
}	

接口、包、类注解

方法作用
getInterfaces()获取运行时类实现的接口
getPackage()获取运行时类所在的包
getAnnotations()获取运行时类的注解

获取运行时类的指定结构

属性

方法作用
getField(String name)获取属性值为name的字段,只能获取到public
id.set(Ocject o,Object value)给当前字段设置值,指明为o的对象,值为value
getDeclaredField(String name)获取属性值为name的字段
setAccessible(true)开启修改权限

方法

方法作用
getDeclaredMethod(String name,Class[] class)获取方法名为name,形参列表为class的方法
invoke(Object o,Object… args)调用方法,o:方法调用者,args:给形参赋值的实参,incoke()方法返回值即为方法的返回值

调用静态方法

Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
showDesc.invoke(Person.class);

反射的应用:动态代理

代理设计模式的原理

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上

动态代理

动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象

静态代理举例

interface ClothFactory {
    void produceCloth();
}
//代理类
class ProxyClothFactory implements  ClothFactory{
    private ClothFactory factory;//就拿被代理类对象进行实例化

    public ProxyClothFactory(ClothFactory factory){
        this.factory=factory;
    }
    @Override
    public void produceCloth() {
        System.out.println("代理工厂做准备工作");
        factory.produceCloth();
        System.out.println("代理工厂做收尾工作");
    }
}
//被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("耐克工厂生产运动服");
    }
}
public class StaticProxyTest{
    public static void main(String[] args) {
        //创建被代理类的对象
        ClothFactory nike = new NikeClothFactory();
        //创建代理类的对象
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        proxyClothFactory.produceCloth();
    }
}

动态代理实例

interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I believe I can fly";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}
/*
要想实现动态代理,需要解决的问题
问题一: 如何根据加载到内存的被代理类,动态的创建一个代理类及对象
问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
 */
class ProxyFactory{
    //调用此方法,返回一个代理类的对象
    public static Object getProxyInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        //绑定obj
        handler.bind(obj);
        //参数一:当前被代理类对象的类加载器,
        //参数二:当前被代理类对象实现的所有接口
        //参数三:动态的通过代理类调用被代理类的同名方法,解决问题二
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);

    }
}
class MyInvocationHandler implements InvocationHandler{
    //当通过代理类的对象调用方法a时,就会自动的调用如下这个方法
    //将被代理类要执行的方法a的功能声明在invoke()中
    private Object obj;//需要使用被代理类的对象赋值
    public void bind(Object obj) {
        this.obj=obj;
    }
    @Override
    //proxy:代理类对象
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj,被代理类对象
        Object returnValue = method.invoke(obj, args);
        //被代理类对象调用方法的返回值,作为invoke()返回值
        return returnValue;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类对象
        Human proxyInstance  = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("好吃的");

    }
}

AOP动态代理简单实现

//改造于动态代理实例代码
.....
//相当于切面
class HumanUitl{
    public void method1() {
        System.out.println("=====================通用方法一============");
    }
    public void method2() {
        System.out.println("=====================通用方法二============");
    }
}
//
//proxy:代理类对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    HumanUitl humanUitl = new HumanUitl();
    humanUitl.method1();
    //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
    //obj,被代理类对象
    Object returnValue = method.invoke(obj, args);
    //被代理类对象调用方法的返回值,作为invoke()返回值
    humanUitl.method2();
    return returnValue;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值