Java反射知识详解

在Android中使用反射基本上是为了调用系统api私有方法,导致都忘记了反射强大的功能。所以梳理了一下,其实发现很多都有在用。

Java反射的作用

要让Java程序运行起来,必须让Java类被Java虚拟机加载,否则不能运行。但是Java反射机制在编译时并不确定哪个类被加载了,而是在程序运行的时候才加载,即使用了在编译期并不知道的类。 比较灵活,在一些框架中使用的比较多(一些框架需要处理很多不同的逻辑,写死可能代码量就大了)。

那么总结的作用有三个:

  • 调用私有方法,调用私有属性。比如AbstractList.removeRange等等。
  • 动态创建对象,例如动态加载网络上的class文件,比较灵活。
  • 动态代理,比较灵活,特别是代理的方法比较多的时候,如果使用普通的静态代理就需要为每一个方法写相应的代理方法,但是动态代理只需要在invoke方法中处理就行了。

动态代理写一个例子。 模仿Retrofit写一个(Retrofit的源码没有读过,只是了解使用了动态代理,当然也没有我的demo这么粗糙),返回是什么请求类型以及url。

// 定义GET请求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {

    String value() default "no url";
}

// 定义POST请求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
    String value() default "no url";
}

// 业务逻辑需要处理的需求
public interface RequestApi {

    @GET(value = "http://www.baidu.com")
    String getLoginInfo(); 

    @POST(value = "http://www.google.com")
    String doLogin(); 
}

// Demo
public class RequestProxyDemo {

    public static void main(String args[]) {
        RequestApi api = (RequestApi) Proxy.newProxyInstance(RequestApi.class.getClassLoader(),
                new Class[]{RequestApi.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Annotation[] annotations = method.getAnnotations();

                        StringBuilder stringBuilder = new StringBuilder();
                        if (annotations == null || annotations.length != 1) {
                            return null;
                        }
                        stringBuilder.append("这是一个");
                        String value = "";
                        if (annotations[0].annotationType() == POST.class) {
                            stringBuilder.append("POST");
                            value = ((POST)annotations[0]).value();
                        } else if (annotations[0].annotationType() == GET.class) {
                            stringBuilder.append("GET");
                            value = ((GET)annotations[0]).value();
                        }
                        stringBuilder.append("请求,请求的url: ").append(value);
                        return stringBuilder.toString();
                    }
                });

        System.out.println(api.doLogin());
        System.out.println(api.getLoginInfo());
    }
}

获取Class对象方法

使用反射的第一步,需要获取Class对象。总结有四种方法,其中1,2方法是通过已加载了的Class来获取,一般的作用是用来调用不能访问的私有属性和私有方法。3,4则直接通过class文件来动态的生成class对象,比如Android中一个按钮点击的操作是加载某个路径下面的class文件然后执行一个方法,现在想要改具体实现效果就可以只用改class文件不需要重新打包一个apk。

  • 类名.class
  • 对象.getClass()
  • Class.forName(“”),可以用来动态的加载class文件。比如加载网络下载的class文件等等。
  • 使用ClassLoader.loadClass(“”)

加载本地Class的一个demo。

javac NetworkClass.java 生成.class文件,同时删除NetworkClass.java文件。

public class NetworkClass
{
  private String name;

  public NetworkClass(String name)
  {
    this.name = name;
  }

  public String getName()
  {
    return this.name;
  }
}

Demo调用class文件。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class NetworkClassSample {

    public static void main(String[] args) {
        try {
            // 加载NetworkClass。注意需要把NetworkClassSample.java和NetworkClass.class放在同一个目录里。
            Class clazz = Class.forName("NetworkClass");
            // 获取getName方法
            Method method = clazz.getDeclaredMethod("getName");
            Constructor constructor = clazz.getConstructor(String.class);
            // 创建对象
            Object object = constructor.newInstance("xixi");
            method.setAccessible(true);
            // 调用方法
            System.out.println(method.invoke(object));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

常用API

Class.java,主要是为了获取一些Class的信息,包括构造函数、方法、成员变量。

forName() // 通过class的名字获取Class对象
getClassLoader() // 获取Class加载器
getConstructor(Class<?>... parameterTypes) // 通过参数类型获取指定public构造函数
getDeclaredConstructor(Class<?>... parameterTypes) // 通过参数获取指定构造函数(包含私有构造函数) 
getEnclosingConstructor() // 返回的是本地或者匿名类的构造方法
getField(String name) // 返回某个公共成员变量的域
getDeclaredField(String name) // 返回某个成员变量的域
getMethod(String name, Class<?>... parameterTypes) // 获取某个指定的公共方法
getDeclaredMethod(String name, Class<?>... parameterTypes) // 获取某个指定的方法
getEnclosingMethod(String name, Class<?>... parameterTypes) // 获取某个指定的本地或者匿名的方法

Field.java,获取到Field对象以后就可以获取、修改对应的成员变量的值了。

get(Object obj) // 拿到obj对象该成员变量的值。
getInt(Object obj) // getFloat()、getLong()等八种基本类型的值

set(Object obj, Object value) // 设置obj对象该成员变量的值是value。
setInt(Object obj, int value) // setFloat()、setLong()等八种基本类型的值

Method.class,方法类,可以用来触发方法。

invoke(Object obj, Object... args) // 调用obj对象的该方法,后面对应的是参数值。

Proxy.java,代理类。

// 用来生成代理类的对象,注意这里的interfaces只能是接口并且数量不能超过65535。
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 

推荐

理解ClassLoader

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值