打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
1.如何反射以及作用

 

1          反射是什么?

动态语言通常指那些在运行时刻可以引入新的函数,已有的函数可以被删除,java从某些方面来说可以实现这样的动态性,就是有一点麻烦,先来看看可以实现"某些动态"功能的反射,主要是获得类的描述信息,并且可以直接方法或者修改字段等,如果要实现真正的动态,得用一些字节库,修改字节

 

反射可以获得类型的自身信息,一般一个类会包含以下元素

1.所属的package

2.父类信息,限定符等等

3.注解

4.字段

5.方法

6.构造方法

 

参考:

Java编程 的动态性,第 2部分: 引入反射

Java深度历险(七)——Java反射与动态代理

 

2          Class

 

一个class文件,在载入之后,会将这个类的信息存在方法区中,包括类名,直接朝超类,访问修饰符,常量池,字段信息,方法信息,另外也会在堆上创建一个class类的实例,来通过它方法区中的信息

 

关于class文件的载入,可以参考我的分类中类加载的分类

关于java字节码文件的信息,可以参考class文件和java语言规范的分类

 

 
  

通常来说,实例在年轻代上,类信息在永久区上,不过他们的结构是非常类似的,对于gc来说也是一样的(据说java7之内会考虑去掉方法区),

 

2.1    获得class一些基本信息

Class<List> clazz=List.class;

String[] array=new String[10];  //数组也是类

Class<? extends List> subClass=ArrayList.class.asSubclass(clazz);

       

/*

    * 获得类信息,包括类名/是否接口/

    * cast方法类似于(ArrayList)list,向下转型

    * asSubClass类似于向上转型

    *

*/

System.out.println(subClass.getCanonicalName()+","+

array.getClass().getCanonicalName()+","+array.getClass().getName());

 

System.out.println(subClass.getSimpleName()+","+array.getClass().getName());

System.out.println(subClass.isInterface());

System.out.println("isPublic:"+(subClass.getModifiers()&Modifier.PUBLIC)+

",isStatic:"+(subClass.getModifiers()&Modifier.STATIC)+

",isSyn:"+(subClass.getModifiers()&Modifier.SYNCHRONIZED));

 

输出============================================

java.util.ArrayList,java.lang.String[],[Ljava.lang.String;

ArrayList,[Ljava.lang.String;

false

isPublic:1,isStatic:0,isSyn:0

 

 

2.2    Modifier

Modifier,修饰符,用于指示一个类的成员的访问策略。就是指示一个类的某个方法,或者字段,更或者是成员类是否可以被外部哪些成员类访问。完整的信息有:

 

反射的MethodFieldConstructor都能够获得该modifier值,判断访问权限以及其他修饰符。可以通过getModifiers()获得:

它是一个类似二进制位的形式,所以(比如)要判断一个方法是private,不是static,那么将会这些写:

int ms=m.getModifiers();

System.out.println(((ms & Modifier.PRIVATE)==Modifier.PRIVATE) &&  ((ms & Modifier.STATIC)==Modifier.STATIC));

 

 

2.3    构造函数

 

public class ClassReflect implements SimpleInterface{

 

    public ClassReflect(){

    }

   

    public ClassReflect(String a){

    }

   

    public ClassReflect(String[] a,int b){

    }

}

/*

* 返回类本身所有构造器的Constructor对象,包括私有的,除了父类

* getConstructor返回的是类及其父类公有的构造函数

* 类似的还有FieldMethod

*/

Constructor<ClassReflect>[] con=

(Constructor<ClassReflect>[]) ClassReflect.class.getDeclaredConstructors();

System.out.println("这个类有"+con.length+"个构造函数,");

for(Constructor c:con){

    if(c.getParameterTypes().length==0)

        System.out.println("使用构造函数"+c+"创建实例"+c.newInstance());

    if(c.getParameterTypes().length==1)

        System.out.println("使用构造函数"+c+

"创建实例"+c.newInstance(new String[]{"hello world"}));

    if(c.getParameterTypes().length==2)

        System.out.println("使用构造函数"+c+

"创建实例"+c.newInstance(new String[]{"hello world"},2));

}

输出============================================

这个类有3个构造函数,

使用构造函数public org.bhsc.reflect.ClassReflect(java.lang.String[],int)创建实例org.bhsc.reflect.ClassReflect@1901437

使用构造函数public org.bhsc.reflect.ClassReflect(java.lang.String)创建实例org.bhsc.reflect.ClassReflect@1f6226

使用构造函数public org.bhsc.reflect.ClassReflect()创建实例org.bhsc.reflect.ClassReflect@64ea66

 

2.4       方法

/**

 * Method类用于获取类中的方法的信息以及访问这些方法的能力. */

public class MethodTest {

 

    public static void main(String args[]) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{

        Class<C1> clazz=C1.class;

        C1 c=clazz.newInstance();

        Method[] ms=clazz.getDeclaredMethods();

        for(Method m:ms){

            /*

             * 获得该方法的参数列表

             */

            Class[] cs=m.getParameterTypes();

            System.out.println("Method is :"+m.getName());

            /*

             * 注解的默认值

             */

            System.out.println(m.getDefaultValue());

            /*

             * 获取包括泛型在内的异常

             */

            Type[] ts=m.getGenericExceptionTypes();

            for(Type t:ts){

                System.out.println("GenericException is :"+t.toString());

            }

            //获取普通的异常

            Class[] cc=m.getExceptionTypes();

            for(Class cw:cc){

                System.out.println(cw.getCanonicalName());

            }

            /*

             * 方法的参数个数

             */

            if(cs.length==2){

                if(cs[0].getCanonicalName().equals("int")

                        && cs[1].getCanonicalName().equals("java.lang.Object[]")){

//如果是静态方法,可以不用传c

                    Object o=m.invoke(c, 123,new Object[]{"2"});

                    System.out.println("THE return Vlue is:"+o);

                }

            }

        }

    }

}

 

2.5    字段

/**

 * Field用于获取类中的字段信息以及访问这些字段的能力。

 * 包括各种getXXXsetXXX方法,或者是直接的get(obj),set(obj,value)方法

 *

 */

public class FieldTest {

 

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

    Class<C1> clazz=C1.class;

    C1 c1=clazz.newInstance();

       

    /*

     * Declared字符串获取所有的元素,不然就是获取公有元素

     */

    Field[] fs=clazz.getDeclaredFields();

for(Field field:fs){

field.setAccessible(true);

 

// hard code,不知道有没有更好的办法?

if (fie.getType().getCanonicalName().equals("int")

    || fie.getType().getCanonicalName().equals("java.lang.Integer"))

            fie.setInt(entery, Integer.valueOf(param.getFieldVaule().toString()));

else if (fie.getType().getCanonicalName().equals("boolean")

    ||  fie.getType().getCanonicalName().equals("java.lang.Boolean"))

        fie.setBoolean(entery, Boolean.valueOf(param.getFieldVaule().toString()));

else if (fie.getType().getCanonicalName().equals("String")

    || fie.getType().getCanonicalName().equals("java.lang.String"))

        fie.set(entery, param.getFieldVaule().toString());

else if (fie.getType().getCanonicalName().equals("long")

    || fie.getType().getCanonicalName().equals("java.lang.Long"))

        fie.setLong(entery, Long.valueOf(param.getFieldVaule().toString()));

 

resultString = "ok," + fie.get(entery);        

 

/*

* field.get(object)获得该对象的field字段

*/

System.out.println("Field is:"+field.getName()+"\t"

            +field.toGenericString()+"\t"+field.get(c1));

    }

}

}

 

2.6    注解

 

@Retention(RetentionPolicy.RUNTIME) 

public @interface AnnoHelloWord {

     public String value() default "hello";

}

 

@AnnoHelloWord

public class AnnoTest {

 

    public AnnoTest(String name) {

        this.name = name;

    }

 

    @AnnoHelloWord("你好")

    private String name;

 

    @Override

    @AnnoHelloWord

    public String toString() {

        System.out.println(this.name);

        return this.name;

    }

 

    public static void main(String[] args) {

        AnnoTest obj = new AnnoTest("nihao");

        Method[] ms = AnnoTest.class.getMethods();

        for (Method m : ms) {

       

            // 该类是否使用了注解

            if (m.isAnnotationPresent(AnnoHelloWord.class)) {

                // 获取注解

                AnnoHelloWord hw = m.getAnnotation(AnnoHelloWord.class);

                // System.out.println(hw.value());

                try {

                    System.out.println(hw.value() + " before...");

                    m.invoke(obj, new Object[] {});

                } catch (Exception e) {

                    e.printStackTrace();

                }

            }

        }

    }

}

 

2.7    泛型信息

参考Java中的泛型

Java 5Java类文件的格式做了修订,添加了Signature属性,用来包含不在JVM类型系统中的类型信息。比如以java.util.List接口为例,在其类文件中的Signature属性的声明是<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;; ,这就说明List接口有一个类型参数E。在运行时刻,JVM会读取Signature属性的内容并提供给反射API来使用

 

 

2.8    数组的反射

数组的特殊处理使用 java.lang.reflect.Array 类提供的静态方法的集合。该类中的方法使您能够创建新数组,获得数组对象的长度,读和写数组对象的索引值

public static void main(String[]args){

    Class<?>classType=Class.forName("java.lang.String");

    Object array=Array.newInstance(classType,10);

    Array.set(array,5,"hello");

    String s=(String)Array.get(array,5);

    System.out.println(s);

}

 

3          访问私有元素

Java中的私有方法从封装的角度来说,是不允许直接调用的,但是java还是提供了”后门“,让我们调用私有方法。

1         Class c = Class.forName("hidden.HiddenClass"); 

2                       

3                 // Object obj = c.newInstance(); // IllegalAccessExcetion 

4                 Constructor constructor = c.getDeclaredConstructor(); 

/**

*如果注释掉下面的方法,则不能调用

**/

5                 constructor.setAccessible(true); 

6                 Object obj = constructor.newInstance(); // new HiddenClass() 

 

1         Method method = c.getDeclaredMethod("invisible"); 

2                 method.setAccessible(true); 

3                 method.invoke(obj); // call HiddenClass.invisible() 

 

那么setAccessible到底是什么意思呢?字面上看起来,好像是将private变为public,其实不然。

 

3.1    setAccessibleAccessibleObject

 

AccessibleObjectConstructorFieldMethod三个类的基类:

先看下JDK中的说明:

它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 FieldMethod Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查

 

在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization两个特殊的私有方法 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

 

 

 

 

 

 

看了它的说明就可以明白了,setAccessible是启用和禁用访问安全检查的开关,并不是true的时候提升到public了。

由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的

 

有的时候这将会成为一个安全隐患,为此,我们可以启用java.security.manager来判断程序是否具有调用setAccessible()的权限。默认情况下,内核API和扩展目录的代码具有该权限,而类路径或通过URLClassLoader加载的应用程序不拥有此权限。例如:当我们以这种方式来执行上述程序时将会抛出异常

3.2    作用

1.在实际项目中,我们会使用很多其它第三方的包,有的时候是通过修改源代码完成你想要的功能,有的时候,是因为第三方包中仅仅因为某几个方法的访问属性被设置为private,或者只要修改private的字段值即可.这个时候,用这种反射的方法就可以很容易实现了.

     
2.另外一个场景就是从系统架构层来考虑数据封装.例如系统有一些元数据类,99%的情况下,我们只是提供get方法供其它应用层获得字段的值,如果把修改的set方法也提供出去,那么可能会影响到系统的可维护性.而在系统运行期间,又很难避免的要修改这些元数据的值.这种情况下,也可以通过这种反射的方式来实现.

 

3.3    性能

 

反射的性能

在我本机测试10E次,直接访问平均每次访问4ns,普通反射800ns,排除安全检查的访问38ns

4          作用

4.1    代理

 

参考之前的《静态代理与动态代理、CGLIB

简单的说,一般的代理模式都是为代理对象专门写一个类或者使用模板模式,这两个的缺点就是代理类太多,并且经常需要修改。

 

接口:

public interface Animal {

   void eat(String food);

   String type();

}

public interface Primate {

   void think();

}

实现类:

public class Monkey implements Animal,Primate{

   public void eat(String food) {

      System.out.println("the food is "+food+" !");

    

   }

   public String type() {

      String type="哺乳动物";

      System.out.println(type);

      return type;

   }

   public void think() {

      System.out.println("思考");

   }

}

 

Java中除了动态的修改字节码,是很难在某个方法调用之前知道即将调用这个方法的,因此动态代理实用了另一种方式,即提供接口(可以多个),动态的创建一个该接口的实现类,他只做1件事情:调用接口的方法之时调用Handler去处理该方法调用,应此在Handler中就可以很方面的去代理真正的代理对象

 

public class DynamicProxy {

 

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

      /*

       * 使用第一种方法来创建代理对象

       */

      Object proxy=Proxy.newProxyInstance(

   Monkey.class.getClassLoader(),

   Monkey.class.getInterfaces(),

         new AnimalInvocationHandler(new Monkey()));

      /*

       * 代理对象调用的时候,会调用回调的invoke方法,Method.args是调用invoke的时候传入的

       */

      Animal animal=(Animal)proxy;

      animal.eat("橡胶");

      animal.type();

           /*

*2中方式,动态创建了代理对象

*这个对象的构造函数必须接受一个Handler的参数,看下面的

*getConstructor方法就可以明白

       */

 

   Class<?> proxClass=Proxy.getProxyClass(

Monkey.class.getClassLoader(),

 Monkey.class.getInterfaces());

 

   proxy=proxClass.getConstructor(new

         Class[]{InvocationHandler.class}).

newInstance(new AnimalInvocationHandler(

new Monkey()));

      

       animal=(Animal)proxy;

       animal.eat("香蕉");

       animal.type();

      

       Primate pre=(Primate)proxy;

       pre.think();

   }}

 

很明显,这种方式的缺点就是不能代理类

4.2    框架(Hbiernate

作为一个ORM框架,我们已经习惯了使用Hibernate来查询,直接返回我们的DO对象

<resultMap id="trsRuleMap" class="trsRuleDO">

        <result property="id" column="ID"/>

        <result property="applicationName" column="APPLICATION_NAME"/>

        <result property="groupName" column="GROUP_NAME"/>

        <result property="type" column="TYPE"/>

        <result property="gmtCreate" column="GMT_CREATE"/>

        <result property="gmtModified" column="GMT_MODIFIED"/>

        <result property="operator" column="OPERATOR"/>

        <result property="status" column="STATUS"/>

</resultMap>

 

<select id="QaTrsRuleDAO.getTrsRuleDOByQuery" resultMap="trsRuleMap">

        select ID, APPLICATION_NAME, GROUP_NAME, TYPE, CONTENT, VERSION, MD5, GMT_CREATE, GMT_MODIFIED, OPERATOR, STATUS, testscript

    from trs_rules ...

</select>

在使用的时候,我们会配置这么一个Map,然后在sql中直接使用

 

Hibernate中就要使用反射

1、根据查询条件构造PreparedStament语句,该语句返回指定字段的值;

 

2Hibernate通过读取配置文件得到trsRuleDO类的属性列表list(是一个String数组)和列的对应关系;

 

3、创建trsRuleDO所属类的Class对象cc=trsRuleDO.getClass()

 

4、构造一个for循环,循环的次数为list列表的长度;

 

     4.1、读取list[i]的值,然后构造对应该属性的set方法;

 

     4.2、判断list[i]的类型XXX,调用PreparedStament语句中的getXXX(i),进而得到i出字段的值;

 

     4.3、将4.2中得到的值作为4.1中得到的set方法的参数,这样就完成了一个字段像一个属性的赋值,如此循环即可;

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Class类 和 class对象(运行时的类型信息)
java反射机制_Java交流
通过反射方式来调用某个对象的方法的步骤
java反射原理
java 反射 Field类
学习Spring必学的Java基础知识(1)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服