Java反射

来源:互联网 发布:淘宝正品耐克 编辑:程序博客网 时间:2024/06/06 00:10

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.Studentclass com.example.refs.Studentclass 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:StudentgetName:com.example.refs.StudentgetPackage: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.InterruptedExceptiongetMethods:      public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptiongetMethods:      public final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptiongetMethods:      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.Studentpublic 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.iddeclaredField: public java.lang.String com.example.refs.Student.namefield: 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:1isAbstract: falseisPublic: trueisStatic: falseisFinal: falseisSynchronized: 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@72ea2f77com.example.refs.Student@33c7353acom.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:someNamevalue: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.StudentReturntypejava.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]:123array[1]:124array[2]:125intArray是否是数组类型:trueintArray成员类型: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