码迷,mamicode.com
首页 > 编程语言 > 详细

JavaSE:反射总结(Reflection)

时间:2015-03-13 00:28:21      阅读:275      评论:0      收藏:0      [点我收藏+]

标签:javase   记录   reflection   反射   动态代理   

今天来进行反射的总结,在JavaSE应用的几个部分里,我认为反射是非常重要的一个内容,因为我们在后续学习框架,以及编程思想上的理解都离不开它。内容不是很多,但是需要理解。
1.为什么要有反射?
某种情况下,我们需要在运行时才得知并使用一个编译时完全未知的类,创建其对象,调用其方法和属性。
2.反射:
被视为动态语言的关键,允许程序在执行期间借助Reflection API取得任何类的内部信息,并直接操纵任意对象的内部属性和方法。
3.类加载的过程:
当程序主动使用某个类时,如果该类还未被加载到内存,系统会通过以下三个步骤来对类进行初始化。
①类的加载:
将.class文件读入到内存中,并为之创建一个java.lang.Class对象。有类加载器来完成
②类的连接:
将类的二进制数据合并到JRE中。
③类的初始化:
JVM负责对类进行初始化。
扩展:
类加载的特性:
在虚拟机生命周期中一个类只被加载一次
加载原则:延迟加载,能少加载就少加载,因为虚拟机的空间是有限的
类加载的时机:
1)第一次创建对象时
2)调用静态方法时要加载类,访问静态属性时会加载类
3)加载子类必先加载父类
4)创建对象引用不加载类
5)子类调用父类的静态方法时:
①当子类没有覆盖父类的静态方法时 ,只加载父类,不加载子类
②当子类覆盖父类的静态方法时,加载父类, 也加载子类
6)访问静态常量,如果编译器可以直接计算出常量的值,则不会加载类,负责加载类。

4.获取Class类对象的实例:

Class clazz = Person.class();

Person p = new Person();
Class clazz = p.getClass();

String className = "com.qh.review.Person";
Class clazz = Class.forName(className);

String className = "com.qh.review.Person";
ClassLoader cl = Person.class.getClassLoader();
class clazz = cl.loadClass(className );

5.类加载器
①获取系统类加载器

ClassLoader cl = ClassLoader.getSystemClassLoader();
System.out.println(cl);

注意:负责加载classpath下的所有jar包
②获取扩展类加载器

ClassLoader cl2 = cl.getParent();
System.out.println(cl2);

注意:负责加载jre/lib/ext下的所有jar包

ClassLoader cl3 = cl2.getParent();
System.out.println(cl3);

注意:负责加载核心类库,无法直接获取

获取当前类由哪个类来加载:

ClassLoader cl = Person.class.ClassLoader();
System.out.println(cl);

6.通过反射来获取对象实例

Person person = Person.class().newInstance();

注意: 这里这个newInstance()实际上调用的是类的构造器。可以通过声明一个带参的构造器来测试。
7.操作属性,这里的代码,我就把我今天回顾的内容粘过来了,大家不想看的可以往下跳过。想看的可作一个参考。

// 操作属性
    @Test
    public void test4() {
        // 先获取具体类的Class对象
        Class<?> clazz = null;
        try {
            clazz = Class.forName("com.qh.review2.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 通过clazz来获取类属性---->public 1,protected 4, private 2, default 0
        Field[] fields = clazz.getFields();// 只能获取访问控制修饰符为public的属性.访问控制修饰符为1即为public
        for (Field field : fields) {
            System.out.println(field.getName());// phone
            System.out.println(field.getType());// 1
            System.out.println(field.getModifiers()); // class java.lang.String
        }
        System.out.println("---------------------------------------------");
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field.getName());
            System.out.println(field.getModifiers());
            System.out.println(field.getType());
            System.out.println("************");
        }
        // 对某一对象的属性进行操作:一个类确定后,就不能修改其修饰符,数据类型,名称。同时操作私有属性的时候,需要修改访问权限。
        Field name = null;
        Person person = null;

        try {
            person = (Person) clazz.newInstance();
            name = clazz.getDeclaredField("name");
            name.setAccessible(true);
            name.set(person, "张三");
        } catch (InstantiationException | IllegalAccessException
                | NoSuchFieldException | SecurityException e) {
            e.printStackTrace();
        }
        System.out.println(person);
    }

8.操作方法

@Test
    public void test5() {
        // 获取所有方法--->方法的修饰符 返回的数值不确定。??
        Method[] methods = clazz.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println("访问控制修饰符:" + methods[i].getModifiers());
            System.out.println("返回值类型:" + methods[i].getReturnType());
            System.out.println("方法名:" + methods[i].getName());
            Class<?>[] parameterType = methods[i].getParameterTypes();
            for (Object paramObject : parameterType) {
                System.out.println("参数类型:" + paramObject);
            }
            Class<?>[] exceptionTypes = methods[i].getExceptionTypes();
            for(Object eType : exceptionTypes){
                System.out.println("异常类型:" + eType);
            }
            Type[] grnericTypes =  methods[i].getGenericParameterTypes();
            for(Type type : grnericTypes){
                System.out.println("泛型类型:" + type);
            }
            System.out.println("------------------------------------------");
        }
        // 操作方法:--->方法只能调用。
        System.out.println("操作方法");
        try {
            Person person = clazz.newInstance();
            Method getName = clazz.getDeclaredMethod("getName");

            System.out.println(getName.invoke(person));
        } catch (InstantiationException | IllegalAccessException
                | NoSuchMethodException | SecurityException
                | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

我把获取类对象提到外面了,要不每次还得写。
重点: 这里一定要注意方法的调用。后面的动态代理和AOP要用到。
8.获取父类泛型类型,感觉这个还是蛮重要的。前段时间我写的jdbc中就用到。我还是写一写吧。

Class clazz = Person.class();
Type type = clazz.getGenericSuperClass();
ParameterizedType types = (ParameterizedType)type;
for(Type t : types){
    System.out.println(t);
}

这些东西都要熟记,给一个记事本就能写出来,并且别出错。我就是这样要求我的。
9.还有一些类型,这里我就不说了,大家可以参看关于Reflection的API文档

接下来写三个应用:
在写动态代理之前,我们先回顾一下静态代理怎么写?
1.静态代理
代理类和被代理类实现同一个接口,在代理类中通过代理类对象调用接口中的方法,但实际上还是通过被代理类对象调用方法来完成相应的功能。
汗,竟然没我上午总结的好,来看看。
一个接口,一个被代理类,一个代理类,代理类和被代理类都可以实现接口中的方法
被代理类通过代理类来执行代理类中的方法。

interface Factory{
    void produce();
}
//被代理类
class NikeFactory implements Factory{
    public void produce(){
        System.out.println("生产NIKE衣服。。");
    }
}
//代理类
class NikeProxy implements Factory{
    NikeFactory nf = null;//实际上要调用方法的代理类对象
    public NikeProxy(NikeFactory nf){
        this.nf = nf;//初始化这个对象
    }
    public void produce(){
        nf.produce();//实际上是代理类对象来生产的衣服
    }
}

public class Proxy{
    public void main(String[] args){
        NikeFactory nf = new NikeFactory();
        NikeProxy np = new NikeProxy(nf);
        np.produce();//生产NIKE衣服。。
    }
}

懂了么?实际上动态代理和AOP逻辑上和这个是一样的。只不过静态代理只能是针对特定的对象。
2.动态代理:

interface Factory{//还是和刚刚一样
    void produce();
}

class NikeFactory implements Factory{//还是和刚刚一样
    public void produce(){
        System.out.println("生产NIKE衣服。。");
    }
}
//来看代理类
class MyInvocationHandler implements InvocationHandler{
    Object obj = null;//还是这个代理类对象
    //需要一个代理类对象,当然是通过反射的方式获取喽!
    public Object getProxy(Object obj){
        this.obj = obj;//初始化一下
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this);
    }
    //实现的方法没记住,粘一下。
    //代理类执行被代理类方法时,转换为对此方法的调用。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {//还记得我上面通过反射操作方法中的注意事项吗?回头看看吧。

        return method.invoke(obj, args);//同上,要不你这里理解不了。
    }
}

public class TestProxy{
    public void main(String[] args){
        NikeFactory nf = new NikeFactory();
        MyInvocationHandler mih = new MyInvocationHandler();
        Object obj = mih.getProxy(nf);//返回的代理类对象
        Factory f = (Factory)obj;//实现了Factory接口
        f.produce();//是不是在生产NIKE的衣服?
    }
}

3.AOP(Aspect Oriented Programming)面向切面编程。到现在为止,这是我见到的第一个面向切面编程的例子,可能只是掌握了其形式,对其内涵还没有理解。会随着以后的学习增加对其的理解。
来看一张图:
技术分享
其实具体的内容和动态代理没什么两样。代码就不写了。给大家粘出来看看。

interface Human {
    void walk();

    void fly();
}

class SuperMan implements Human {

    @Override
    public void walk() {
        System.out.println("走走走走走啊走");
    }

    @Override
    public void fly() {
        System.out.println("I belive I can fly");
    }

}

class HuManUtil {
    public void method1() {
        System.out.println("method 1  !!!!");
    }

    public void method2() {
        System.out.println("method 2  !!!!");
    }
}

// 获取代理对象
class ManInvocationHandler implements InvocationHandler {
    Object obj;

    public Object getProxy(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        HuManUtil h1 = new HuManUtil();
        h1.method1();
        Object returnVal = method.invoke(obj, args);
        h1.method2();
        return returnVal;
    }

}

public class TestAOP {
    public static void main(String[] args) {
        SuperMan sm = new SuperMan();
        ManInvocationHandler mih = new ManInvocationHandler();
        Object obj = mih.getProxy(sm);
        Human proxy = (Human) obj;
        proxy.walk();
        System.out.println();
        proxy.fly();


    }
}
  • 多个代码段中有相同重复的代码,我们将这些重复的代码提取出来,
  • 封装成方法,但是这个方法就是固定的,增加了与多段代码的耦合性。
  • 我们理想的方式就是封装出来的方法是不固定的,动态的,
  • 这样就大大降低了代码之间的耦合性,代码之间的依赖性。
    这是我现在理解的面向切面编程。
    好了,今天就到这里了。

JavaSE:反射总结(Reflection)

标签:javase   记录   reflection   反射   动态代理   

原文地址:http://blog.csdn.net/sloverpeng/article/details/44229589

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!