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

    1. 介绍

    反射是一种能够在程序运行时动态访问、修改某个类中任意属性和方法的机制。

    具体:
    对于任意一个类,都能够知道这个类的所有属性和方法
    对于任意一个对象,都能够调用它的任意一个方法和属性

    在运行时,当加载完类之后,JVM在堆内存中会自动产生一个Class类型的对象,这个对象包含了完整的类的结构信息


    这个Class对象就像一面镜子,透过这个镜子看到类的结构

     

    那么,如何得到这个Class对象呢?以下可否

    Class c = new Class(); 

    答案是不行的,因为Class的构造函数定义为私有的,只有JVM可以访问

    private Class(ClassLoader loader) {
        classLoader = loader; 
    } 

    Class对象获取的三种方式

    Class c1 = Code.class; 
    Class c2 = code1.getClass(); 
    Class c3 = Class.forName("com.trigl.reflect.Code");

    举例

    public class TestStudent {
        public static void main(String[] args) throws ClassNotFoundException {
            Student student = new Student(1, "Jack");
            Class class1 = student.getClass();
            Class class2 = Student.class;
            Class class3 = Class.forName("com.example.refs.Student");
    
            System.out.println(class1);
            System.out.println(class2);
            System.out.println(class3);
        }
    }

    输出

    class com.example.refs.Student
    class com.example.refs.Student
    class com.example.refs.Student

    2. Java反射相关操作

    本文以Student类为例:

    接口

    package com.example.refs;
    
    public interface InterFace {
        void read();
    }

    package com.example.refs;
    
    public class Student implements InterFace{
        private int id;
        public String name;
        public Student() {}
        public Student(int id, String name) {
            this.id = id;
            this.name = name;
        }
        public void setId(int id) {
            this.id = id;
        }
        private int getId() {
            return id;
        }
        @Override
        public void read() {}
    }

    以下具体介绍下具体的用法

    2.1 类名称、包名

    public class ClassName {
        public static void main(String[] args) {
            Class<?> class2 = Student.class;
            System.out.println("getSimpleName:" +class2.getSimpleName());
            System.out.println("getName:" + class2.getName());
            System.out.println("getPackage:" + class2.getPackage());
        }
    }

    输出

    getSimpleName:Student
    getName:com.example.refs.Student
    getPackage:package: com.example.refs
    

    2.2 方法

    public Method getDeclaredMethod(String name, Class<?>...parameterTypes) // 得到本类所有的方法(public/private/proteceted...)
    public Method getMethod(String name, Class<?>...parameterTypes) //得到该类所有的public方法,包括父类

    举例

    public class MethodTest {
        public static void main(String[] args) {
            Class<?> class1 = Student.class;
            Method[] methods = class1.getMethods();
            Method[] declaredMethods = class1.getDeclaredMethods();
    
            for (Method method : methods) {
                System.out.println("getMethods:      " + method);
            }
            System.out.println();
            for (Method method : declaredMethods) {
                System.out.println("getDeclaredMethods:      " + method);
            }
        }
    }

    结果

    getMethods:      public void com.example.refs.Student.read()
    getMethods:      public void com.example.refs.Student.setId(int)
    getMethods:      public final void java.lang.Object.wait() throws java.lang.InterruptedException
    getMethods:      public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    getMethods:      public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    getMethods:      public boolean java.lang.Object.equals(java.lang.Object)
    getMethods:      public java.lang.String java.lang.Object.toString()
    getMethods:      public native int java.lang.Object.hashCode()
    getMethods:      public final native java.lang.Class java.lang.Object.getClass()
    getMethods:      public final native void java.lang.Object.notify()
    getMethods:      public final native void java.lang.Object.notifyAll()
    
    getDeclaredMethods:      public void com.example.refs.Student.read()
    getDeclaredMethods:      private int com.example.refs.Student.getId()
    getDeclaredMethods:      public void com.example.refs.Student.setId(int)   

    指定方法  

    public class MethodChangeVal {
        public static void main(String[] args) throws IllegalAccessException, InstantiationException,
            NoSuchMethodException, InvocationTargetException {
            Class clazz = Student.class;
            Object obj = clazz.newInstance();
            Method methodGet = clazz.getDeclaredMethod("getId");
            Method methodSet = clazz.getDeclaredMethod("setId", int.class);
    
            methodSet.invoke(obj, 123);
            System.out.println(methodGet.invoke(obj));
        }
    }

    异常

    Exception in thread "main" java.lang.IllegalAccessException: Class 
    com.example.refs.MethodChangeVal can not access a member of class
    com.example.refs.Student with modifiers "private"

    原因:setId为私有方法,利用反射时会进行安全检查,用setAccessible(true)不进行安全检查,可以直接对调用私有方法、修改私有变量

    public class MethodChangeVal {
        public static void main(String[] args) throws IllegalAccessException, InstantiationException,
            NoSuchMethodException, InvocationTargetException {
            Class clazz = Student.class;
            Object obj = clazz.newInstance();
            Method methodGet = clazz.getDeclaredMethod("getId");
            Method methodSet = clazz.getDeclaredMethod("setId", int.class);
            methodGet.setAccessible(true);
            methodSet.invoke(obj, 123);
            System.out.println(methodGet.invoke(obj));
        }
    }

    2.3 构造函数

    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
    public Constructor<T> getConstructor(Class<?>... parameterTypes) 
    Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数 
    Constructor<?>[] publicConstructors = class1.getConstructors();
    //获取class对象public构造函数
    Constructor<?> constructor = class1.getDeclaredConstructor(String.class);
    //获取指定声明构造函数
    Constructor publicConstructor = class1.getConstructor(String.class);
    //获取指定声明的public构造函数

    举例

    public class ConstuctorTest {
        public static void main(String[] args) {
            Class class2 = Student.class;
            Constructor<?>[] constructors = class2.getConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor + ", Name:" + constructor.getName());
            }
        }
    }

    结果

    public com.example.refs.Student(), Name:com.example.refs.Student
    public com.example.refs.Student(int,java.lang.String), Name:com.example.refs.Student

    2.4 成员变量

    getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
    getFields()获得某个类的所有的公共(public)的字段,包括父类。

    举例

    public class FieldTest {
        public static void main(String[] args) {
            Class<?> aClass = Student.class;
            Field[] declardFields = aClass.getDeclaredFields();
            Field[] fields = aClass.getFields();
            for (Field field : declardFields) {
                System.out.println("declaredField: " + field);
            }
            System.out.println();
            for (Field field : fields) {
                System.out.println("field: " + field);
            }
        }
    }

    结果

    declaredField: private int com.example.refs.Student.id
    declaredField: public java.lang.String com.example.refs.Student.name
    
    field: public java.lang.String com.example.refs.Student.name

    2.5 修饰符

    举例

    public class ModifierTest {
        public static void main(String args[]) {
            Class aClass = Student.class;
            int modifier = aClass.getModifiers();
            System.out.println("modifier:" + modifier);
            System.out.println(String.format("isAbstract: %s", Modifier.isAbstract(modifier)));
            System.out.println(String.format("isPublic: %s", Modifier.isPublic(modifier)));
            System.out.println(String.format("isStatic: %s", Modifier.isStatic(modifier)));
            System.out.println(String.format("isFinal: %s", Modifier.isFinal(modifier)));
            System.out.println(String.format("isSynchronized: %s", Modifier.isSynchronized(modifier)));
        }
    }

    结果

    modifier:1
    isAbstract: false
    isPublic: true
    isStatic: false
    isFinal: false
    isSynchronized: false

    2.6 父类

    public class GetParent {
    public static void main(String[] args) {
        Class class2 = Student.class;
        System.out.println(class2.getSuperclass());
        }
    }

    结果

    class java.lang.Object

    2.7 接口

    public class InterfaceTest {
        public static void main(String[] args) {
            Class<?> clazz = Student.class;
            Class<?>[] inters = clazz.getInterfaces();
            for(Class<?> classIn : inters) {
                System.out.println(classIn);
            }
        }
    }

    输出

    interface com.example.refs.InterFace

    2.8 创建对象实例

    举例用2种方法创建

    public class NewInstanceTest {
        public static void main(String[] args) throws IllegalAccessException, InstantiationException,
            NoSuchMethodException, InvocationTargetException {
            Class clazz = Student.class;
            Object obj = clazz.newInstance();
    
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            Object obj2 = constructor.newInstance();
    
            Constructor<?> constructor2 = clazz.getDeclaredConstructor(int.class, String.class);
            Object obj3 = constructor2.newInstance(1, "HanMeimei");
    
            System.out.println(obj);
            System.out.println(obj2);
            System.out.println(obj3);
        }
    }

    结果

    com.example.refs.Student@72ea2f77
    com.example.refs.Student@33c7353a
    com.example.refs.Student@681a9515
    

    2.9 注解

    自定义一个注解

    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation {
        public String name();
        public String value();

    :RetentionPolicy.RUNTIME 这个表示运行期注解,这样在反射的时候才能取到

    举例

    public class AnnotationTest {
        @MyAnnotation(name="someName",  value = "Hello World")
        public void doSomething() {
        }
    
        @Deprecated
        public void delFunc() {
            System.out.println("Hello");
        }
    
        public static void main(String[] args) throws NoSuchMethodException {
            Class clazz = AnnotationTest.class;
            Method method = clazz.getMethod("doSomething");
            Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
    
            for (Annotation annotation : declaredAnnotations) {
                if (annotation instanceof MyAnnotation) {
                    MyAnnotation myAnnotation = (MyAnnotation)annotation;
                    System.out.println("name:" + myAnnotation.name());
                    System.out.println("value:" + myAnnotation.value());
                } else {
                    System.out.println(annotation);
                }
            }
        }
    }

    结果

    name:someName
    value:Hello World

    2.10 泛型

    参数类型、返回值类型举例

    public class GenericTest {
        public void test01(Map<String, Student> map, List<Student> list) {
            System.out.println("test01");
        }
        public Map<Integer, Student> test02() {
            System.out.println("test02");
            return null;
        }
        public static void main(String[] args) throws NoSuchMethodException {
            Method method = GenericTest.class.getMethod("test01", Map.class, List.class);
            Type[] types = method.getGenericParameterTypes();
            for (Type type : types) {
                System.out.println("#:" + type);
                if (type instanceof ParameterizedType) {
                    Type[] genericType = ((ParameterizedType)type).getActualTypeArguments();
                    for (Type ontGenericType : genericType) {
                        System.out.println("泛型类型:" + ontGenericType);
                    }
                }
            }
    
    
            Method method2 = GenericTest.class.getMethod("test02");
            Type returnType = method2.getGenericReturnType();
            System.out.println("
    Returntype" + returnType);
            if (returnType instanceof ParameterizedType) {
                Type[] genericTypes = ((ParameterizedType)returnType).getActualTypeArguments();
                for (Type type : genericTypes) {
                    System.out.println("返回值,泛型类型:" + type);
                }
            }
    
        }
    }

    结果

    #:java.util.Map<java.lang.String, com.example.refs.Student>
    泛型类型:class java.lang.String
    泛型类型:class com.example.refs.Student
    #:java.util.List<com.example.refs.Student>
    泛型类型:class com.example.refs.Student
    
    Returntypejava.util.Map<java.lang.Integer, com.example.refs.Student>
    返回值,泛型类型:class java.lang.Integer
    返回值,泛型类型:class com.example.refs.Student
    

    2.11 数组

    public class ArrayTest {
        public static void main(String[] args) {
            int[] intArray = (int[])Array.newInstance(int.class, 3);
            Array.set(intArray, 0, 123);
            Array.set(intArray, 1, 124);
            Array.set(intArray, 2, 125);
            for (int i = 0; i < Array.getLength(intArray); ++i) {
                System.out.println(String.format("array[%s]:%s", i, Array.get(intArray, i)));
            }
            Class aClass = intArray.getClass();
            System.out.println("intArray是否是数组类型:" + aClass.isArray());
            System.out.println("intArray成员类型:" + aClass.getComponentType());
        }
    }

    结果

    array[0]:123
    array[1]:124
    array[2]:125
    intArray是否是数组类型:true
    intArray成员类型:int 

    3. 分析

    3.1 使用场景

    • 操作因访问权限限制的属性和方法
    • 实现自定义注解
    • 动态加载第三方jar包
    • 按需加载类,节省编译和初始化APK的时间

    3.2 优缺点

    优点:灵活、自由度高:不受类的访问权限限制

    缺点

    • 性能问题:通过反射访问、修改类的属性和方法时会远慢于直接操作,但性能问题的严重程度取决于在程序中是如何使用反射的。如果使用得很少,不是很频繁,性能将不是问题
    • 安全性问题:反射可以随意访问和修改类的所有状态和行为,破坏了类的封装性,如果不熟悉被反射类的实现原理,随意修改可能导致潜在的逻辑问题
    • 兼容性问题:因为反射会涉及到直接访问类的方法名和实例名,不同版本的API如果有变动,反射时找不到对应的属性和方法时会报异常

    3.3 说明

    • 通过反射访问方法比实例慢很多
    • 有用到反射的类不能被混淆
    • 反射存在性能问题,但使用不频繁、按需使用时,对程序性能影响并不大
    • 反射存在安全性问题,因为可以随意修改类的所有状态和行为(包括private方法和实例)
    • 使用反射访问Android的API时需要注意因为不同API版本导致的兼容性问题

    3.4 性能对比

    不使用反射、启用安全检查、启用安全检查进行对比

    public class TestReflect {
        @Test
        public void testNoneReflect() {
            Student oneStudent = new Student(1, "HanMeimei");
            long start = System.currentTimeMillis();
            for (long i = 0; i < Integer.MAX_VALUE; ++i) {  oneStudent.setId(1);     }
            long count = System.currentTimeMillis() - start;
            System.out.println("没有反射, 共消耗 <" + count + "> 毫秒");
        }
    
        @Test
        public void testNotAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
            InvocationTargetException {
            Student oneStudent = new Student(1, "HanMeimei");
            Method method = Class.forName("com.example.refs.Student").getMethod("setId", int.class);
            long start = System.currentTimeMillis();
            for (long i = 0; i < Integer.MAX_VALUE; ++i) {   method.invoke(oneStudent, 1);      }
            long count = System.currentTimeMillis() - start;
            System.out.println("启用安全检查, 共消耗 <" + count + "> 毫秒");
        }
    
        @Test
        public void testUseAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Student oneStudent = new Student(1, "HanMeimei");
            Method method = Class.forName("com.example.refs.Student").getMethod("setId", int.class);
            method.setAccessible(true);
    
            long start = System.currentTimeMillis();
            for (long i = 0; i < Integer.MAX_VALUE; ++i) {  method.invoke(oneStudent, 1);       }
            long count = System.currentTimeMillis() - start;
            System.out.println("取消安全检查, 共消耗 <" + count + "> 毫秒");
        }
    }

    结果对比

    差异 耗时(ms)
    没用反射 952
    取消安全检查 4283
    启用安全检查 14892
  • 相关阅读:
    Nginx缓存[proxy cache、memcache]
    Nginx重写规则
    同步异步,阻塞非阻塞 和nginx的IO模型
    cookie & session
    HTTP状态码
    web简单的整体测试
    关于 如何用电脑的adb连接Mumu模拟器
    关于社保断交一个月的影响
    关于androidStudio的下载
    可以直接拿来用的android开源项目研究
  • 原文地址:https://www.cnblogs.com/kaituorensheng/p/7398069.html
Copyright © 2011-2022 走看看