java反射机制详解

java反射机制?

(一)概念

一般情况下我们获取一个对象的实例都是通过

Advisor a = new Advisor();来说去一个实例,可是如果我们知道了一个对象的接口,但是不知道实现这个接口的对象的时候,怎么能获取一个实例?或者如一般的定义,两个对象Studenta和Studentb,另一个对象Student需要使用Strudenta和Studentb,那么我们可以在student中New这两个对象,可是如果studenta改名字了,那么我们需要去接口中修改,这样的耦合关系对经常对不确定性的处理很不好,我们就需要考虑如何在不改变代码的情况下去适应这种变化,解耦,这样就想到的将这种关系配置起来,这样关系改变了,这需要去读取关系的变化,重新实例化.

(二)

当装在一个类的时候,会在方法去产生一个数据结构,该数据结构包含着装载类的信息。Java反射机制就是通过获取该装载信息,获取java.lang.class的对象,对该对象进行操作。字节码可以看成是数据流,该方法区就是该数据流的结构化表现。

package com.ticmy.reflect;
 
/**
 * 文章管理接口
 * @author Administrator
 */
public interface ArticleInterface {
    public void del(long id) throws Exception;
    public void add(String content) throws Exception;
    public void modify(long id, String content) throws Exception;
}
一个简单实现
package com.ticmy.reflect;
 
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
 
public class ArticleManage implements ArticleInterface {
    private final Map<Long, String> articles;
    private boolean needCheck;
    private final AtomicLong idIncr = new AtomicLong(1);
    public ArticleManage(boolean needCheck) {
        System.out.println("带参数的构造方法");
        this.needCheck = needCheck;
        articles = new ConcurrentHashMap<Long, String>();
    }
 
    public ArticleManage() {
        System.out.println("默认构造方法");
        this.needCheck = true;
        articles = new ConcurrentHashMap<Long, String>();
    }
 
    public void del(long id) throws Exception {
        System.out.println("删除文章");
        articles.remove(id);
    }
 
    public void add(String content) throws Exception {
        if((!isNeedCheck()) || checkContent()) {
            System.out.println("添加文章");
            articles.put(idIncr.getAndIncrement(), content);
        }
    }
 
    public void modify(long id, String content) throws Exception {
        if((!isNeedCheck()) || checkContent()) {
            System.out.println("修改文章内容");
            articles.put(id, content);
        }
    }
 
    public boolean checkContent() throws Exception {
        System.out.println("检查文章内容");
        Random random = new Random();
        int value = random.nextInt(100);
        if(value < 20) {
            //20%概率失败
            throw new Exception("文章内容不合法");
        }
        return true;
    }
 
    public boolean isNeedCheck() {
        return needCheck;
    }
}
1、生成一个ArticleInterface实现类的一个对象。
package com.ticmy.reflect;
public class Test {
    public static void main(String[] args) throws Exception {
        ArticleInterface inst = (ArticleInterface)Class.forName("com.ticmy.reflect.ArticleManage")
                .newInstance();
        inst.add("this is a short article");
    }
}
我们可能有以上代码来生成一个ArticleManage的实例。如果以前没接触过反射,肯定会纳闷,这里完全可以用ArticleInterface inst = new ArticleManage();这句来取代反射,用反射来创建对象的意义何在。如果在写这段代码的时候,已经知道要new ArticleManage了,可能确实意义不大。但假如在写代码的时候还不知道com.ticmy.reflect.ArticleManage的存在呢或者压根就是留给二次开发的人去做的呢?可能需要一个配置,里面配置着ArticleInterface实现类的全限定名,而此时在不知道其实现类的情况下,可以用上述方式来创建类的对象了。就好像写spring的那些人,他们根本不知道你要注入的对象类型,无论如何他们也无法在代码中使用new的,反射就能很好的解决这个问题。
上面的代码运行后会发现,newInstance调用后使用的是ArticleManage的默认构造方法,那么该如何使用带boolean参数的构造方法呢?这就是下面要说的内容。

(三)获取构造函数

Class类中有几个方法来获取构造函数:getConstructor(Class… parameterTypes),getCondtructors(),getDeclaredConstructor(Class… parameterTypes),getDeclaredConstructors()其中含有Declared的表示获取所有的构造方法,不管其是private,protected,public还是包内可见的。不带参数的表示获取所有能获取的构造方法(getConstructors获取所有public的构造方法,getDeclaredConstructors获取所有声明的构造方法),而带参数的表示构造方法的参数类型。我们在例子中声明的都是public构造方法,想要调的是带boolean参数的构造方法,因此我们可以用如下方式实现:

package com.ticmy.reflect;
import java.lang.reflect.Constructor;
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.ticmy.reflect.ArticleManage");
        Constructor<?> constructor = clazz.getConstructor(boolean.class);
        ArticleInterface inst = (ArticleInterface)constructor.newInstance(false);
        inst.add("this is a short article");
    }
}
(四)访问类中参数

可以通过Class中的getFields()、getField(String name)以及getDeclaredFields()、getDeclaredField(String name)来访问类的字段。方法名中带Declared与不带Declared的含义同构造方法。假如现在想改变private类型的needCheck的值,可以用getDeclaredField(String name)方法先获取字段对象,再修改其值:

package com.ticmy.reflect;
import java.lang.reflect.Field;
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.ticmy.reflect.ArticleManage");
        ArticleInterface inst = (ArticleInterface)clazz.newInstance();
        Field field = clazz.getDeclaredField("needCheck");
        field.setAccessible(true);
        field.setBoolean(inst, false);
        inst.add("this is a short article");
    }
}
获取到Field对象后,通过调用其setBoolean方法修改其值。其第一个参数为想要改变字段值的对象,若是static字段,该参数可为null;如果字段为其它类型,可以调用Field类的其它setXXX方法来修改。注意到代码中有一句field.setAccessible(true),如果没有这句会报以下错误,不允许修改private变量的值,将其设置为true,就允许改变了:
Exception in thread “main” java.lang.IllegalAccessException: Class com.ticmy.reflect.Test can not access a member of class com.ticmy.reflect.ArticleManage with modifiers “private”
(五)访问类中方法
Class类中提供了getMethods()、getMethod(String name, Class… parameterTypes)以及getDeclaredMethods()、getDeclaredMethod(String name, Class… parameterTypes),其情况类似于构造方法,毋庸多言。需要说的是其带参数的方法,第一个参数是方法名,后面是对应方法的参数类型。假如这里要调用modify方法,可以先通过getMethod(String name, Class… parameterTypes)获得对应的Method对象,然后通过invoke调用:

package com.ticmy.reflect;
import java.lang.reflect.Method;
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.ticmy.reflect.ArticleManage");
        ArticleInterface inst = (ArticleInterface)clazz.newInstance();
        Method method = clazz.getMethod("modify", long.class, String.class);
        method.invoke(inst, 3, "this is a short article");
    }
}
invoke的第一个参数表示调用的是哪个对象的modify方法,后面是ArticleInterface接口中modify方法按声明顺序排列的实际参数。
(六)反射机制与动态代理的结合

动态代理用于创建动态代理类,这些类不需要以java class文件的形式存在。动态代理相关的类为:java.lang.reflect.Proxy。可以通过其newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)方法创建一个代理类,其中,loader为定义代理类的类加载器,interfaces为生成的代理类要实现的接口列表,h为指派方法调用的调用处理程序。在ArticleManage示例类中,add和modify方法中都有if((!needCheck) || checkContent())的判断,或许还有更多方法需要这种判断,在每个方法里都写这样的判断实在是太麻烦,动态代理+方法反射调用就可以帮我们解脱痛苦:

package com.ticmy.reflect;
 
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
 
public class ArticleManage implements ArticleInterface {
    private final Map<Long, String> articles;
    private boolean needCheck;
    private final AtomicLong idIncr = new AtomicLong(1);
    public ArticleManage(boolean needCheck) {
        System.out.println("带参数的构造方法");
        this.needCheck = needCheck;
        articles = new ConcurrentHashMap<Long, String>();
    }
 
    public ArticleManage() {
        System.out.println("默认构造方法");
        this.needCheck = true;
        articles = new ConcurrentHashMap<Long, String>();
    }
 
    public void del(long id) throws Exception {
        System.out.println("删除文章");
        articles.remove(id);
    }
 
    public void add(String content) throws Exception {
        System.out.println("添加文章");
        articles.put(idIncr.getAndIncrement(), content);
    }
 
    public void modify(long id, String content) throws Exception {
        System.out.println("修改文章内容");
        articles.put(id, content);
    }
 
    public boolean checkContent() throws Exception {
        System.out.println("检查文章内容");
        Random random = new Random();
        int value = random.nextInt(100);
        if(value < 20) {
            //20%概率失败
            throw new Exception("文章内容不合法");
        }
        return true;
    }
 
    public boolean isNeedCheck() {
        return needCheck;
    }
}

package com.ticmy.reflect;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class Test {
    public static void main(String[] args) throws Exception {
        ArticleManage delegationObj = new ArticleManage();
 
        ArticleInterface inst = (ArticleInterface)Proxy.newProxyInstance(
                ArticleInterface.class.getClassLoader(),
                new Class[]{ArticleInterface.class},
                new ArticleManageInvocationHandler(delegationObj));
        inst.add("this is a short article");
 
    }
 
    static class ArticleManageInvocationHandler implements InvocationHandler {
        private ArticleManage delegationObj;
        public ArticleManageInvocationHandler(ArticleManage delegationObj) {
            this.delegationObj = delegationObj;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
            String methodName = method.getName();
            Object ret = null;
            long start = System.currentTimeMillis();
 
            if(methodName.equals("add")) {
                ret = method.invoke(delegationObj, args);
            } else {
                if((!delegationObj.isNeedCheck()) || delegationObj.checkContent()) {
                    ret = method.invoke(delegationObj, args);
                }
            }
            long end = System.currentTimeMillis();
            System.out.println("[方法调用][" + methodName + "]耗时:" + (end - start) + "ms");
            return ret;
        }
    }
}

看这部分

ArticleInterface inst = (ArticleInterface)Proxy.newProxyInstance(
         ArticleInterface. class .getClassLoader(),
         new Class[]{ArticleInterface. class },
         new ArticleManageInvocationHandler(delegationObj));

让生成的代理类实现ArticleInterface接口,这样就可以将newProxyInstance的对象强制转换成ArticleInterface类型了。在InvocationHandler中,可以看到其invoke方法参数中有个Method,这个Method对象是代理接口中的那些方法,例如这里将其强制转换成ArticleInterface后,假设调用了add方法,InvocationHandler中invoke方法的method就相当于ArticleInterface.class.getMethod(“add”, String.class)。在这个例子中,当调用inst的方法时,就会自动去调用ArticleManageInvocationHandler中的invoke方法。invoke方法的第三个参数,就是调用inst中方法时传的参数。invoke方法的第一个参数即为生成的代理类对象本身,很多时候这个参数用处不大。
既然调用inst的方法时会调用这个invoke方法,那就可以在里面做一些事情了。像上面在调用实际方法之前给其加了判断,为整个方法调用计时等等,有没有种面向切面的感觉?

这是很常用的一种编程模式,如在现实中很多操作需要验证用户是否已经登录,那么就可以通过这种方式来做。如果用了spring,可以使用spring的AOP来做,道理是类似的。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java反射机制是指在运行时动态地获取一个类的信息,并可以操作类的属性、方法和构造器等。Java反射机制可以使程序员在运行时动态地调用类的方法和属性,扩展类的功能,并可以实现注解、工厂模式以及框架开发等。 Java反射机制的原理如下:首先,Java编译器将Java源代码编译为字节码文件,字节码文件中包含着类的信息,这些信息包括类的名称、方法、属性和构造器等等。接着,Java虚拟机将字节码文件加载到内存中,然后通过类加载器将类加载到内存中形成一个类对象,这个类对象可以操作字节码文件中的信息。 使用Java反射机制的过程如下:首先获取类对象,通过类对象来获取类的构造器、属性、方法等信息,然后调用构造器来创建对象,通过属性获取和设置类的成员属性,通过方法调用类的方法等。 Java反射机制的优点是可以在运行时动态地得到类的信息,使得程序员在程序运行时能够对类进行更加灵活的操作,并可以使得程序更加通用化,同时也存在着一定的性能问题,因为Java反射机制需要Java虚拟机进行一定的额外处理,所以在程序运行时需要进行额外的时间和资源消耗。 总之,Java反射机制Java语言的一项重要特性,在Java开发中广泛应用,在代码编写、框架开发以及API开发中具有重要作用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值