Java反射

Posted jihite

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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("\\nReturntype" + 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

 

以上是关于Java反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景