暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java 反射

陌淮缘 2016-11-11
252

1. 反射的概念

    反射的概念是由 Smith 在 1982 年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力, 并能根据自身行为的状态和结果, 调整或修改应用所描述行为的状态和相关的语义。Java 中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码链接。反射允许我们在编写与执行时,使我们的程序代码能够接入装载到 JVM 中的类的内部信息,而不是源代码中选定的类协作的代码。这使反射成为构建灵活的应用的主要工具。 但需注意的是:如果使用不当,反射的成本很高。

2.Class反射机制

    指的是可以于运行时加载,探知和使用编译期间完全未知的类.

    程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能调用他的任意一个方法和属性;

    加载完类之后, 在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象), 这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射。

    每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类.

3.Class对象的获取方式

    2.1对象的getClass()方法;

    2.2类的.class(最安全/性能最好)属性;

    2.3运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用).

4.反射中常用的类

    java.lang.Reflect;

    java.lang.reflect.Field;

    java.lang.reflect.Modifier;

    java.lang.reflect.Constructor;

    java.lang.reflect.Method;

5.从Class中获取信息

  Class
类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,这里只是做简单的介绍,详细请参考JDK文档

  • 获取类内信息

获取内容方法签名
构造器Constructor<T> getConstructor(Class<?>... parameterTypes)

包含的方法Method getMethod(String name, Class<?>... parameterTypes)
包含的属性Field getField(String name)
包含的Annotation
<A extends Annotation> A getAnnotation(Class<A> annotationClass)
内部类Class<?>[] getDeclaredClasses()
外部类Class<?> getDeclaringClass()
所实现的接口Class<?>[] getInterfaces()
修饰符int getModifiers()
所在包Package getPackage()
类名String getName()
简称String getSimpleName()
  • 一些判断类本身信息的方法

判断内容方法签名
注解类型?boolean isAnnotation()
使用了该Annotation
修饰?
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
匿名类?boolean isAnonymousClass()
数组?boolean isArray()
枚举?boolean isEnum()
原始类型?boolean isPrimitive()
接口?boolean isInterface()
obj
是否是该Class
的实例
boolean isInstance(Object obj)

    属性: 

 getField()  

得到类中指定的public修饰的属性

getFields()

得到类中的所有public修饰的属性

getDeclaredField()

得到类中的指定属性

getDeclaredField()

得到类中的所有属性

   构造方法:

getConstructor()

得到类中指定的public修饰的构造方法

getConstructors()

得到类中所有public修饰的构造方法

getDeclaredConstructor()

得到类中指定的构造方法

getDeclaredConstructors()

得到类中所有的构造方法

    方法:

getMethod()

得到类中的指定的public修饰的方法(包含继承父类的public方法)

getMethods()

得到类中的所有的public修饰的方法(包含继承父类的public方法)

getDeclaredMethod()

得到当前类中指定的方法

getDeclaredMethods()

得到当前类中的所有方法

    父类:

getSuperclass()

得到当前类继承的父类

    父接口:

getInterfaces()

得到当前类的所有父接口

6.反射的基本使用

    示例:反编译的简单示例

    package com.kawa.reflect;

    import java.lang.reflect.*;

    public class AntiCompilation {

    public static void main(String[] args) throws Exception {

        // 加载类

        Class c = Class.forName("com.kawa.reflect.User");

        Field[] fields = c.getDeclaredFields(); // 获取类中所有属性

        // 创建StringBuffer对象

        StringBuffer sb = new StringBuffer();

        //拼接类名

        sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n");

        sb.append("\n\t// Fields\n");

        //1.拼接属性

        for (Field field : fields) {

            String mod = Modifier.toString(field.getModifiers());

            if ((mod != null) && (!"".equals(mod))) {

                sb.append("\t" + Modifier.toString(field.getModifiers()) + " ");        //得到属性访问修饰符

                sb.append( field.getType().getSimpleName() + " " );                        //得到属性数据类型

                sb.append(  field.getName() + ";\n");                                                //得到属性名

            } else {

                sb.append("\t" + field.getType().getSimpleName() + " "

                        + field.getName() + ";\n");

            }

        }

        sb.append("\n\t// Constructors\n");

        //2.拼接构造方法

        Constructor[] ctors = c.getDeclaredConstructors();

        for (Constructor ctor : ctors) {

            sb.append("\t"+Modifier.toString(ctor.getModifiers()) + " ");                   //得到构造方法访问修饰符

            sb.append(c.getSimpleName() + "(");                                                        // 得到构造方法名

            Class[] params = ctor.getParameterTypes();                                             //  得到构造方法参数类型列表

            for (int i = 0; i < params.length; i++) {

                if (i == params.length - 1) {

                    sb.append(params[i].getSimpleName() + " ");

                } else {

                    sb.append(params[i].getSimpleName() + " ,");

                }

            }

            sb.append("){}\n");

        }

        sb.append("\n\t// Methods\n");


        //3.拼接所有方法

        Method[] methods = c.getDeclaredMethods();

        for (Method method : methods) {

            sb.append("\t"+Modifier.toString(method.getModifiers()) + " ");     // 得到方法访问修饰符

            Class returnType = method.getReturnType();

            sb.append(returnType.getSimpleName() + " ");                                 // 得到方法返回值类型

            sb.append(method.getName() + "(");                                                 // 得到方法名

            Class[] params = method.getParameterTypes();                                //  得到方法参数类型列表

            for (int i = 0; i < params.length; i++) {

                if (i == params.length - 1) {

                    sb.append(params[i].getSimpleName()+" ");

                } else {

                    sb.append(params[i].getSimpleName()+" ,");

                }

            }

            sb.append("){}\n");

        }


        //come over

        sb.append("}");

        // 打印类

        System.out.println(sb);

    }

}

User类示例:

class User {

    // Field

    public String name;

    private String id;

    public int age;

    protected String addr;

    boolean sex;


    // Constructor

    public User(){}

    public User(String id,String name){

        this.id = id;

        this.name = name;

    }


    //Method

    public String toString(){

        return "User["+id+","+name+"]";

    }

    // 登录

    public boolean login(String name, String pwd) {

        if ("admin".equals(name) && "123456".equals(pwd)) {

            System.out.println("登录成功!");

            return true;

        }else{

            System.out.println("登录失败!");

            return false;

        }

    }


    // 退出

    public void logout() {

        System.out.println("系统已安全退出!");

    }


    //私有方法 显示

    private void show(){

        System.out.println("这是私有显示方法!");

    }

    protected void show2(){

    }

}

7.通过反射创建对象

    7.1使用Class
对象的newInstance()
方法来创建该Class
对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器(要求类中要有无参构造方法)).

        eg:Object user= c1.newInstance();

    7.2先使用Class
对象获取指定的Constructor
对象, 再调用Constructor对象的newInstance()
方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例).

        eg:

            Class c = Class.forName("com.kawa.reflect.User");

            //得到指定构造方法

            Constructor ctor = c.getDeclaredConstructor(String.class,String.class);

            //通过构造方法创建对象

            Object obj = ctor.newInstance("1001","张三");

8.通过反射给对象属性赋值

    //8.1私有属性

        / /创建对象  

        Object o = c.newInstance();

        //id 是private修饰的  当属性是私有的时,此时我们可以进行打破封装

        //获取指定属性 可以进行赋值和读取

        Field idF = c.getDeclaredField("id");       

        //打破封装

        idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。

        //赋值

        idF.set(o, "101");

    //8.2公共属性

        Field ageF = c.getField("age");        //得到公共属性

        ageF.set(o, 18);

9.通过反射调用方法

         //9.1:调用公共方法

        //获取某个特定的方法 通过:方法名+形参列表

        Method login = c.getDeclaredMethod("login", String.class,String.class);

        //通过反射机制执行login方法   调用o对象的login方法,传递"admin""123456"参数,方法的执行结果是flag

        Object flag = login.invoke(o, "admin","123456");

        //9.2:调用私有方法

        //获取特定私有方法

        Method show = c.getDeclaredMethod("show");

        //打破封装

        show.setAccessible(true);

        //通过反射执行方法

        show.invoke(o);

10.反射的小结

     10.1: 安全性和反射

            在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可能希望框架能够        全面介入代码,无需考虑常规的介入限制。但是,在其它情况下,不受控制的介入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。

    10.2:反射的两个缺点

        反射是一种强大的工具,但也存在一些不足。

            1.性能问题。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并且它满足我们的要求。 用于字段和方法接入时反射要慢于直接代码。 性能问题的程度取决于程序中是如何使用反射的。 如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。

            2. 使用反射会模糊程序内部实际要发生的事情。 程序人员希望在源代码中看到程序的逻辑, 反射等绕过了源代码的技术会带来维护问题。 反射代码比相应的直接代码更复杂。 解决这些问题的最佳方案是保守地使用反射         ——仅在它可以真正增加灵活性的地方——记录其在目标类中的使用。




文章转载自陌淮缘,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论