反射完美解读

参考

张孝祥系列

《thinking in java 》Bruce Eckel

场景

反射原理与实战

分析

要理解反射得要弄清楚关键字 Class ,因为 反射就是:

通过类的字节码把类中的各种成分(方法、成员变量以及类与包等)动态映射成相应的java类(MethodField等),而类的字节码正是用 Class来表示的。
一、Class 的理解

首先,从宏观上来了解Class。  类 是对具有相似特征的对象们的一种抽象,表示一类事物,一种东西。比如,"人" 这个 类  - 相似特征:有眼睛,鼻子; 会说话。Java的字节码也是一类事物,也具有一些相似特征:都是由 pacakge、Method以及 Field等成分组成。我们用 Class 这个关键字来表示这样一类 字节码 事物。

再 从功能上来了解Class。java 程序中要用到某个具体类型的对象时:首先从硬盘上(或者网络等其他地方)把该类的二进制代码加载到内存中来,再用这份二进制代码复制一个个具体所用到的对象。程序中要用到多小个类,那么内存中就得加载多少份字节码。

二、反射的理解
定义:通过类的字节码把类中的各种成分(方法、成员变量以及类与包等)动态映射成相应的java类(MethodField等)。 程序在编译时并不知道具体要处理的类时什么,但是需要该类的一些方法与属性,这在一些开源框架比如说 Spring中用的很多(纯属瞎猜,有待后续源码 分析进一步验证)。通常在配置文件中配置相关的类信息,Sping容器通过反射获取并操作该类的相关方法与属性。 
三、实验

package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * Created by pengyucheng on 16-4-13.
 * 一、Class 类认识   二、 反射
 * 定义:通过类的字节码把类中的各种成分(方法、成员变量以及类与包等)动态映射成相应的java类(Method、Field等)
 */
public class ReflectTest
{
    public static  void main(String[] args) throws Exception
    {
        String str1 = 理解Class();
        构造方法反射();
        成员变量的反射();
        普通方法的反射(args[0], "abc");
    }
    /**
     * 反射前奏: Class的理解之获取类字节码(这里以String类为例)的三种方式
     * String str = "123456"
     * 1、str.getClass()
     * 2、Class.forName("java.lang.String") : 获取类的字节码 1、有则从JVM中获取  2、没有则从硬盘上加载并放缓存到JVM中
     * 3、String.class
     * @throws ClassNotFoundException
     */
    private static String 理解Class() throws ClassNotFoundException
    {
        String str1 = "abc";
        Class cls1 = str1.getClass();
        Class cls2 = Class.forName("java.lang.String"); //用的最多,动态获取类的字节码
        Class cls3 = String.class;
        System.out.println(cls1==cls2);
        System.out.println(cls1==cls3); // 具体数据类型(这里是String类型)的字节码在内存中只有一份

        System.out.println(cls1.isPrimitive());
        System.out.println(int.class.isPrimitive());
        System.out.println(int.class == Integer.class);
        System.out.println(int.class == Integer.TYPE);
        System.out.println(int[].class.isPrimitive());
        System.out.println(int[].class.isArray());
    }
    /**
     * 构造方法的反射  之 用放射模拟 new String(new StringBuffer("abc"));
     * @throws NoSuchMethodException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws java.lang.reflect.InvocationTargetException
     */
    private static void 构造方法反射() throws NoSuchMethodException, InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException
    {
        Constructor constructor = String.class.getConstructor(StringBuffer.class);
        String str = (String)constructor.newInstance(new StringBuffer("abc"));
        System.out.println(str.charAt(2));

        //简写-省略中间获取 Constructor的过程
        String.class.newInstance();
    }
    /**
     * 成员变量的反射
     *
     * 暴力反射:private属性的访问。
     * @throws Exception
     */
    private static void 成员变量的反射() throws Exception
    {
        ReflectPoint rf = new ReflectPoint(2,3);
        Field fieldx = rf.getClass().getField("x");
        System.out.print(fieldx.get(rf));

        Field fieldy = rf.getClass().getDeclaredField("y");//暴力反射
        fieldy.setAccessible(true);
        System.out.print(fieldy.get(rf));

        ReflectPoint rf2 = new ReflectPoint(2,3);
        changeStringValue(rf2);
        System.out.println(rf2);
    }
    /*
     * 反射实现
     * 1、普通方法的反射 之 反射实现 str1.charAt(1)的效果
     * 2、对接受数组参数的方法的静态反射调用
     */
    private static void 普通方法的反射(String arg, String str1) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException
    {
        Method method = String.class.getMethod("charAt",int.class);
        System.out.println(method.invoke(str1,1));
        System.out.println(method.invoke(str1,new Object[]{2,3}));

        //正常调用
        TestArguments.main(new String[]{"12","33"});

        //静态方法调用:编译时不知道调用哪个类的main方法时用
        String startClassName = arg;
        Method methodMain = Class.forName(startClassName).getMethod("main",String[].class);
        methodMain.invoke(null,new Object[]{new String[]{"111","222"}});
    }
    /**
     * @throws Exception
     */
    private static void changeStringValue(Object obj) throws Exception
    {
        Field[] fields = obj.getClass().getFields();
        for (Field field: fields )
        {
            if (field.getType() == String.class)
            {
                String oldValue = (String) field.get(obj);
                String newValue = oldValue.replace('b','a');
                field.set(obj,newValue);
            }
        }
    }
}
class TestArguments
{
    public static  void main(String[] args)
    {
        for (String str: args)
        {
            System.out.println(str);
        }
    }
}
四、执行结果

true
true
false
true
false
true
false
true
c
23aallaasketaallitcast
b
12
33
111
222

总结

反射强大,但是不能用的太多? Bruce Eckel说它不怎么面向对象 。 

你可能感兴趣的:(反射完美解读)