java反射基础

一、获取Class对象

获取一个类对应的Class对象,有以下三种方式

  • 使用Class的forName方法,该方法需要一个字符串参数,该字符串是该类的全限定名
  • 调用该类的class属性来获取该类对应的Class对象
  • 调用该类某个对象的getClass()方法,该类是Object类的方法,所有的java对象都是可以调用的

具体的代码如下:

public class Hello {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> aClass = Class.forName("com.ali.reflect.Hello");
        Class<Hello> helloClass = Hello.class;
        Class<? extends Hello> aClass1 = new Hello().getClass();

        System.out.println(aClass);        //class com.ali.reflect.Hello
        System.out.println(helloClass);    //class com.ali.reflect.Hello
        System.out.println(aClass1);      //class com.ali.reflect.Hello
    }
}

二、从Class类中获取信息

从类对应的Class对象可以获取哪些信息呢?主要如下:

  • 类所包含的构造方法
  • 类所包含的普通方法
  • 类上用到的annotation
  • 类所包含的内部类
  • 类所在的外部类
  • 类所实现的接口
  • 类所继承的父类
  • 类的修饰符、所在包、类名等基本信息
  • 判断该类是否为接口、枚举、注解等类型

三、使用反射创建对象并操作对象

1、创建对象

使用Class对象来生成类的对象一般需要以下三步:

1.获取该类的Class对象
2.利用Class对象的getConstructor方法类获取指定的构造器
3.利用Constructor的newIntance方法来创建一个类的对象

代码如下:

public class Hello {
    public static void main(String[] args) throws {
        Class<?> aClass = Class.forName("com.ali.reflect.Hello");  //第一步
        Constructor<?> constructor = aClass.getConstructor();     //第二步 
        Object o = constructor.newInstance();                    //第三步
        System.out.println(o);                                  
    }
}

2、操作对象调用方法

使用Class对象来调用类里面对象的方法一般需要以下几步:
1.获取该类的Class对象
2.利用Class对象的getMethod方法获取到指定的方法,getMethod方法返回的是一个Method对象
3.调用Method对象的invoke(Object obj,Object... args)方法,invoke方法的第一个参数obj是该方法的主调,后面的arg是执行方法所需要的实参

具体代码如下:

public class Hello {

    public void info(){
        System.out.println("info");
    }

    public static void main(String[] args) {
        Class<?> aClass = Class.forName("com.ali.reflect.Hello");          //第一步,获取Class对象
        Method info = aClass.getMethod("info");                            //第二步,根据getMethod获取Method对象
        System.out.println(info);
        info.invoke(new Hello());                                          //第三步,调用invoke方法,主调者是Hello的对象,info方法没有参数,所以只传入一个参数
    }
}

3、访问成员变量

使用Class对象给类对象里面的成员变量设置一般需要如下几步:

1.获取类对象的Class对象
2.根据getDeclaredField方法获取到类对象的具体某个属性,getDeclaredField的返回值是一个Field对象
3.调用Field对象的setAccessible方法,并设置为ture,其意义是根据反射访问该成员变量时取消访问权限检查
4.调用get(Object obj)或者set(Object obj,xxx val)方法,get方法的意义是获取obj对象的该成员变量的值,set方法的意义是将obj对象的该成员变量设置为val

具体代码如下:

public class Hello {

    private String name;

    public static void main(String[] args) {
        Class<?> aClass = Class.forName("com.ali.reflect.Hello");                    //第一步,获取Class对象
        Hello hello = new Hello();          
        Field name = aClass.getDeclaredField("name");                                //第二步,获取到name属性对象的Field对象
        name.setAccessible(true);                                                    //第三步,取消访问权限检查
        name.set(hello,"jack");                                                      //第四步,将hello对象的name属性设置为jack
        System.out.println(hello.name);
    }
}

4、利用反射生成JDK动态代理

java主要利用reflect包下的Proxy类和InvocationHandler接口来生成jdk动态代理类或动态代理对象

Proxy提供了两个方法来创建动态代理类和动态代理对象:

  • getProxyClass 创建一个动态代理类所对应的Class对象,

    • 该代理类将实现第二个参数interfaces指定的多个接口。
    • 第一个参数ClassLoader指定生成动态代理类的类加载器
  • newProxyInstance

    • 直接生成一个动态代理类对象
    • 该代理类将实现第二个参数interfaces指定的多个接口。
    • 指定代理对象里面的每一个方法时都会被替换执行InvacationHandler对象的invoke方法,也就是说代理对象里面有多少个方法,invoke方法就会被执行多少遍。

代码如下:

//定义一个接口,接口里面有两个方法
public interface Persion {
    void walk();
    void sayHello(String name);
}

/**
  *自定义一个InvocationHandler,实现里面的invoke方法,将来生成的代理对象里面会实现接口Persion里面的两个方法方法,
  *但是在执行代理方法的时候,其实是在执行下面的invoke方法
*/
public class MyInvokationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //第一个参数proxy:代表动态代理对象
        //第二个参数method:代表正在执行的方法
        //第三个参数args:代表调用目标方法时传入的实参
        System.out.println("------正在执行的方法-----:"+method);
        if (args != null){
            System.out.println("下面执行该方法时传入的实参为:");
            for (Object o:args){
                System.out.println(o);
            }
        }else {
            System.out.println("调用该方法没有实参");
        }
        System.out.println("------------------------");
        return null;
    }
}

public class ProxyTest {

    public static void main(String[] args) {
		//创建一个InvocationHandler对象
        InvocationHandler invocationHandler=new MyInvokationHandler();
		//使用指定的InvocationHandler来生成一个动态代理对象
        Persion persion =(Persion) Proxy.newProxyInstance(Persion.class.getClassLoader(), new Class[]{Persion.class}, invocationHandler);
		调用动态代理对象里面的waik方法和sayHello方法
        persion.walk();
        persion.sayHello("hello java");
    }
}

/*

下面为MyInvokationHandler的打印:


------正在执行的方法-----:public abstract void com.ali.proxy.Persion.walk()
调用该方法没有实参
------------------------
------正在执行的方法-----:public abstract void com.ali.proxy.Persion.sayHello(java.lang.String)
下面执行该方法时传入的实参为:
hello java
------------------------

*/

从上面的打印可以看出,不管是执行代理对象的walk方法,还是执行代理对象的sayHello方法,实际上都是执行MyInvokationHandler对象的invoke方法。

5、动态代理和AOP

代码如下:

//定义一个接口
public interface Dog {
    void info();
    void run();
}

//定义一个实现类,实现Dog接口,也就是本文中需要被代理的对象
public class BigDog implements Dog{
    @Override
    public void info() {
        System.out.println("我很大");
    }

    @Override
    public void run() {
        System.out.println("我跑的快");
    }
}

public class MyInvocationHandle implements InvocationHandler {
    //需要被代理的对象
    private Object target;
    public void setTarget(Object target){
        this.target=target;
    }

    //执行动态代理的所有方法,都会被替换成执行下面的invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("-----我是在调用之前执行的-----");

        //这一行很关键,已target作为主调来执行menthod的invoke方法,target就是被代理对象,也就是执行被代理对象里面的方法
        Object result = method.invoke(target, args);

        System.out.println("-----我是在调用之后执行的-----");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        //创建一个InvocationHandler对象
        MyInvocationHandle myInvocationHandle=new MyInvocationHandle();
        Dog dog=new BigDog();
        //为InvocationHandler对象设置target,也就是设置被代理对象
        myInvocationHandle.setTarget(dog);

        //生成一个代理对象
        Dog proDog=(Dog) Proxy.newProxyInstance(dog.getClass().getClassLoader(),dog.getClass().getInterfaces(),myInvocationHandle);
        proDog.info();
        proDog.run();

        /**
         * 很明显我们发现一个问题,JDK的动态代理只能为接口创建代理对象,换句话说
         * 也就是说,被代理的对象必须要作为一个接口的实现类,在这种情况下,我们才能使用JDk动态代理
         */
    }

/*    下面这些为代码中的打印

-----我是在调用之前执行的-----
我很大
-----我是在调用之后执行的-----
-----我是在调用之前执行的-----
我跑的快
-----我是在调用之后执行的-----

*/

posted @ 2021-12-18 14:05  提莫_队长  阅读(36)  评论(0编辑  收藏  举报