java反射与动态代理

反射与动态代理

1、反射

1.1什么是反射

这个问题我也不是很清楚,所以就从官网粘来一段,具体如下:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

1.2 反射能做什么

就如官方解释中说的那样:对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
说是这么说,我们来看看具体的实现吧:

反射获取对象或类的全类名

这里先上代码:

	@Test
    public void reflect1(){
        JButton btn = new JButton();
        String name = btn.getClass().getName();
        System.out.println(name);
        System.out.println(JButton.class.getName());
    }

运行结果我就不展示了,之后的代码片段也请自行尝试,毕竟,看懂和会写是两码事。
注意看两次获取类名的区别:

  • 第一次是创建对象后获取的类名:对象名.getClass().getName();
  • 第二次是直接用类名获取全类名: 类名.class.getName();

如果你们自己敲了一遍代码的话,就会发现调用getName()方法的对象类型为Class类型。
一些人可能会说:这看着也没啥用呀。
对于这部分人,我的建议是:先别管它有没有用,简单记一下它能做什么就够了。

获取Class对象的三种方法
  • 方法一:Class.forName(“全类名”)
  • 方法二:类名.class
  • 方法三:对象.getClass()
    具体实现如下
@Test
    public void reflect2() throws ClassNotFoundException {
        //通过“全类名”获得Class
        Class c1 = Class.forName("Test1");//连接数据库时
        //Class
        Class c2 = Test1.class;
        //getClass
        Class c3 = new Test1().getClass();

        System.out.println(c1.getName());
        System.out.println(c2.getName());
        System.out.println(c3.getName());
    }
获取父类与接口

可以使用一个已知的类或对象获取该类的所有父类与接口信息
获取父类信息

Class对象.getSuperclass()

获取实现的接口

Class对象.getInterfaces();

千万别忘记,java语言只能实现单继承;但可以实现多个接口!!

测试代码如下:

@Test
    public void reflect3() throws Exception{
        Class c1 = JButton.class;
        //获得父类
        Class superclass = c1.getSuperclass();
        String name = superclass.getName();
        System.out.println(name);
        //获得实现的接口
        Class[] interfaces = c1.getInterfaces();
        for (Class anInterface : interfaces) {
            System.out.println(anInterface.getName());
        }
    }
获取类的所有构造方法以及方法中的参数类型

获取构造方法

Class对象.getConstructors();

获取方法中的参数类型

方法对象.getParameterTypes();

构造方法可以有多个,构造方法中的参数也可以有多个。

测试代码如下:

@Test
    public void reflect4() throws Exception{
        Class c1 = Book.class;
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            Class[] parameterTypes = constructor.getParameterTypes();
            System.out.print(constructor.getName()+"(");
            for (Class parameterType : parameterTypes) {
                System.out.print(parameterType.getName()+",");
            }
            System.out.println(")");
        }
    }

以上代码是打印成我们常见的构造方法的形式,想直接看效果的话可以自行修改,最主要的方法就是获取构造方法和参数类型的方法。

根据获取的构造方法创建对象

我们都知道,构造方法是用来创建对象的,既然用反射获取了类的构造方法,那能不能用获取到的构造方法来创建这个类的对象呢?
答案是可以!
使用到的方法

构造方法对象.newInstance();

该方法默认创建一个Object类型的对象,但可以强转成许需要类型的对象。
代码如下:

@Test
    public void reflect5() throws Exception{
        Class c1 = Book.class;

        Constructor[] constructors = c1.getConstructors();
        Book book1 = (Book) c1.newInstance();
    }
获取类的全部属性

获取所有非私有属性

Class对象.getFields()

获取指定非私有属性

Class对象.getField(String 属性名)

获取所有属性(包括私有)

Class对象.getDeclaredFields()

获取指定的属性(包括私有)

Class对象.getDeclaredField(String 属性名)

Field类这里可以理解为属性类,可以用它做如下操作:
获取属性的类型

Field对象.getType()

获取属性的名字

Field对象.getName()

获取属性的访问修饰符对应的数字

Field对象.getModifiers()
//说明:一个类的访问修饰符是以整型的数字存储的

将获取到的访问修饰符数字转换为对应访问修饰符的名字

Field.toString(int 访问修饰符对应的整型数字)

设置类的属性

设置属性值
可以使用上面的获取属性的方法获得Field类对象(属性对象),使用Field与该类的对象可以设置属性值。

Field对象.set(Object 本类的对象, Object 属性的值)

得到属性值

Field对象.get(Object 本类的对象)

如果要设置的属性为私有属性,需要先设置成允许访问

Field对象.setAccessible(true);
Field对象.set(Object 本类的对象, Object 属性的值);

测试代码如下:

@Test
    public void reflect8() throws Exception{
        Class c1 = Book.class;
        Object o = c1.newInstance();
        Field author = c1.getDeclaredField("author");

        //如果设置的属性的访问修饰符为private,可以调用方法setAccessible(true),
        //true 允许访问   ;   false 不允许
        author.setAccessible(true);
        author.set(o,"吴承恩");
        
        System.out.println(author.get(o));
    }
获取类的方法

获取类的所有方法(不包括私有)

Class对象.getMethods()

获取指定方法(不包括私有)

Class对象.getMethod(String 方法名, … 方法参数类型)

获取类的所有方法(包括私有)

Class对象.getDeclaredMethods()

获取指定方法(包括私有)

Class对象.getDeclaredMethod(String 方法名, … 方法参数类型)

执行获取的方法
通过上述操作可以获取到Method对象(方法对象),使用该对象可以执行获取到的方法。

Method对象.invoke(Object 对象名, Object … 参数名)

2、动态代理

说实话,这个我也不是特别理解,所以直接上代码:
主要的实现

用代理工厂将我们创建的对象转换为代理对象,然后用代理对象调用方法;
代理对象与我们创建的对象本质上属于同一种类;
调用的invoke方法为我们重写后的invoke方法

创建一个用于测试的接口Student接口

public interface Student {
    void write();
    void copy();
}

实现上面的接口

public class StudentImpl implements Student {
    @Override
    public void write() {
        System.out.println("自己写作业");
    }
    @Override
    public void copy() {
        System.out.println("抄别人的作业");
    }
}

实现InvocationHandler接口,重写Invoke()方法

public class HomeWorkHandler implements InvocationHandler {

    private Object target;

    public HomeWorkHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("打开作业本");
        Object object = method.invoke(target);
        System.out.println("合上作业本");

        return object;
    }
}

创建代理工厂,用于生成代理对象
用到的方法:

public class ProxyFactory {
    public static Object getProxy(Object target){
        HomeWorkHandler homeWorkHandler = new HomeWorkHandler(target);
        Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                homeWorkHandler);
        return object;
    }
}

进行测试

@Test
public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
       Student target = new StudentImpl();
       Object student = ProxyFactory.getProxy(target);
       Method copy =
               student.getClass().getMethod("copy");
       copy.invoke(student);
   }

代理对象调用类中的方法使用的是反射中的调用方法,区别在于代理对象走的是重写后的invoke方法,反射走的是未被重写的invoke方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值