zoukankan      html  css  js  c++  java
  • Reflection 反射

    Reflection 反射

    反射的定义

    反射机制:

    • 在程序运行时可以加载,探知,使用编译期间完全未知的类
    • 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个加载的类,都能够知道这个类的所有属性和方法;同时任意一个对象也都能够调用它的任意一个方法和属性。同时一个类只有一个反射对象,比如多次调用Class.forName得到的也是相同对象

    形象的说:

    使用以下反射代码来获取一个类的对象
    Class c = Class.forName("com.company.project.ClassName");
    当加载完成后,堆内存中就产生了一个ClassName类型的对象,通过这个对象我们就可以看到类的结构,这个就时反射
    

    反射的作用

    • 动态加载类、动态获取类的信息(属性、方法、构造器)
    • 动态构造对象
    • 动态调用类和对象的任意方法、构造器
    • 动态调用和处理属性
    • 获取泛型信息
    • 处理注解

    Class类

    public final class Class extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement

    Class类的类表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注解是一种接口。 每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。 原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象。Class没有公共构造函数。 相反, Class对象由Java虚拟机自动构建,因为加载了类,并且通过调用类加载器中的defineClass方法。

    简单的说就是:

    • Class类十分特殊,用来表示java中类型 (class/interface/enum/annotation/primitive type/void)本身。
    • Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个 Class对象
    • JVM来创建Class对象,当加载器(class loader)的defineClass()被 JVM调用,JVM 便自动产生一个Class 对象
    • Class类是Reflection反射的基础,任何要动态加载,运行的类,都必须先获得对于的Class对象

    Class类的对象获取

    • .getClass()方法
    • Class.forName()方法--使用最多
    • .class语法
    package JavaCore.Reflection;
    
    /*******************************************************************************
     * @Description: 获取Class对象
     * @Aha-eureka:  Class类是Reflection反射的基础,任何要动态加载,运行的类,都必须先获得对于的Class对象
     ******************************************************************************/
    public class Reflection_GetClass {
    
        public static void main( String[] args ) throws ClassNotFoundException {
            String path = "JavaCore.Reflection.User_Demo";
    
            //获取Class的几种方式
            {
                //一个类只有一个Class对象
                Class clazz1 = Class.forName(path);
                Class clazz11=Class.forName(path);
    
                System.out.println(clazz1.hashCode());//相同hashcode
                System.out.println(clazz11.hashCode());//相同hashcode
            }
    
            {
                User_Demo user_demo = new User_Demo();
                Class clazz2 = user_demo.getClass();
    
                Class clazz3 = User_Demo.class;
    
                System.out.println(clazz2 == clazz3);//true
            }
        }
    }
    

    获取基本数据类型的Class对象

    Class类可以直接获取基本数据类型的Class对象,但是在数组中,多维的数组就是不同的class对象,不同类型的数组也是不同的class对象

        int[] arr0 = new int[5];
        int[] arr01 = new int[15];
        long[] arr02 = new long[15];
        int[][] arr1 = new int[5][5];
    
        System.out.println(arr0.getClass() + "   " + arr0.getClass().hashCode());//class [I   920011586
        System.out.println(arr01.getClass() + "   " + arr01.getClass().hashCode());//class [I   920011586
        System.out.println(arr02.getClass() + "   " + arr02.getClass().hashCode());//class [J   2017354584
        System.out.println(arr1.getClass() + "   " + arr1.getClass().hashCode());//class [[I   2017354584
    

    Class API

    方法 含义 备注
    public String getName() 返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String 返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String
    public Field getField(String name) 获取指定属性名的属性 注意只能获得public属性
    public Field[] getFields() 获取所有的公共方法的属性数组
    public Field getDeclaredField(String name) 返回指定的名字的属性包括私有属性
    public Field[] getDeclaredFields() 返回所有的属性数组包括私有的
    同理...Method/Constructor
    同理...DeclaredMethods/Constructors
    public Mehod getDeclaredMethod(String name,Class<?>...parameterTypes 返回指定参数类型的方法,Class<?>...parameterTypes可变参数参入的是对于类型的Class类 针对方法的多态性(重载)进行针对的获取,即对于方法有传参则必须传入对于的Class对象
    public Constructor getDeclaredConstructor(Class<?>... parameterTyes) 获取指定传参的构造器
    package JavaCore.Reflection;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Reflection_Dynamic {
    
        static Class clazz;
        static{
            String path = "JavaCore.Reflection.User_Demo";
            try {
                clazz=Class.forName(path);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public static void useReflection() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
            //这里newInstance实际就是在调用对于类的无参构造器,如果对于的类没有无参构造器则会抛出InstantiationException
            User_Demo demo = (User_Demo) clazz.newInstance();
    
            //使用特定的构造器新建对象
            Constructor constructor = User_Demo.class.getDeclaredConstructor(String.class, int.class);
            User_Demo demo1 = (User_Demo) constructor.newInstance("Swagger", 26);
            System.out.println(demo1);
    
            /**
             * 使用反射调用普通方法
             * 1.先获取到Class对象,并获取一个新的对象A
             * 2.然后通过Class对象获得方法对象Method
             * 3.使用Method.invoke(Object A,args...)方法调用类的方法
             */
            User_Demo A = (User_Demo) clazz.newInstance();
            Method method = clazz.getDeclaredMethod("setName", String.class);
            method.setAccessible(true);
            method.invoke(A, "Swagger1");
            System.out.println(A);
    
            /**
             * 通过反射操作属性
             *
             */
            User_Demo fDemo = (User_Demo) clazz.newInstance();
            Field f = clazz.getDeclaredField("name");
            f.setAccessible(true);//使用这个setAccessible(true)来设置可以访问私有方法和属性
            f.set(fDemo, "Ranger");//
            System.out.println(fDemo);
        }
    }
    

    反射操作泛型

    Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据 的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部 擦除。
    为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class 类中的类型但是又和原始类型齐名的类型

    反射操作注解

    public static void main(String[] args) {
    		try {
    			Class clazz = Class.forName("类的相对路径和类名");
    			
    			//获得类的所有有效注解
    			Annotation[] annotations=clazz.getAnnotations();
    			for (Annotation a : annotations) {
    				System.out.println(a);
    			}
    			//获得类的指定的注解SxtTable就是一个注解
    			SxtTable st = (SxtTable) clazz.getAnnotation(SxtTable.class);
    			System.out.println(st.value());
    			
    			//获得类的属性的注解,columnName(),type(),length()就是SxtTable第一的注解方法或者属性,因为注解就是一种特殊的接口,他的属性值就是调用其方法
    			Field f = clazz.getDeclaredField("studentName");
    			SxtField sxtField = f.getAnnotation(SxtField.class);
    			System.out.println(sxtField.columnName()+"--"+sxtField.type()+"--"+sxtField.length());
    			
    			//根据获得的表名、字段的信息,拼出DDL语句,然后,使用JDBC执行这个SQL,在数据库中生成相关的表
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		
    	}
    

    反射的性能问题

    反射访问相对正常的访问大概要慢30倍

    setAccessible

    setAccessible设为true:取消反射时的对象安全检查,false则进行安全检查;
    当禁止安全检查时就能:
    1.访问私有属性
    2.同时也能提高反射的运行速度

    如果setAccessible(true)禁用安全检查则能将反射访问效率提升4倍,所以当频繁的调用反射时可以禁用安全检查来提升效率

    本博客为Swagger-Ranger的笔记分享,文中源码地址: https://github.com/Swagger-Ranger
    欢迎交流指正,如有侵权请联系作者确认删除: liufei32@outlook.com

  • 相关阅读:
    转载:ORA-01438: 值大于为此列指定的允许精度
    PLSQL碰到pls-00103的错误解决办法
    spring 的xml配置使用p标签简化
    spring aop实现拦截接口请求打印日志
    response 设置头的类型 (转)
    js或者ext js获取返回值
    ext grid 前台grid加载数据碰到数据重复只显示一条
    关于struts和Spring 结合到一起之后存在ACtion创建单实例还是多
    SVN提交碰到的问题奇怪的问题
    关于项目使用可配置的properties 文件的实现
  • 原文地址:https://www.cnblogs.com/Swagger-Ranger/p/10669865.html
Copyright © 2011-2022 走看看