java反射 安全_Java反射

本文详细介绍了Java反射机制,包括如何获取Class对象、类的名称与包名、方法、构造器、成员变量、修饰符、父类与接口、创建对象实例以及注解。并探讨了反射在安全性和性能上的影响,提供了使用反射访问方法的性能对比测试。通过反射,开发者可以在运行时动态访问和修改类的属性和方法,但也需要注意其带来的性能和安全问题。
摘要由CSDN通过智能技术生成

1. 介绍

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

具体:

对于任意一个类,都能够知道这个类的所有属性和方法

对于任意一个对象,都能够调用它的任意一个方法和属性

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

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

65d72d6460fb88c3944dcddf9ab941ad.png

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

Class c = new Class();

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

privateClass(ClassLoader loader) {

classLoader=loader;

}

Class对象获取的三种方式

Class c1 = Code.class;

Class c2=code1.getClass();

Class c3= Class.forName("com.trigl.reflect.Code");

举例

public classTestStudent {public static void main(String[] args) throwsClassNotFoundException {

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);

}

}

输出

classcom.example.refs.Studentclasscom.example.refs.Studentclass com.example.refs.Student

2. Java反射相关操作

本文以Student类为例:

接口

packagecom.example.refs;public interfaceInterFace {voidread();

}

packagecom.example.refs;public class Student implementsInterFace{private intid;publicString name;publicStudent() {}public Student(intid, String name) {this.id =id;this.name =name;

}public void setId(intid) {this.id =id;

}private intgetId() {returnid;

}

@Overridepublic voidread() {}

}

fdce705f75e0b8b14e1e520aec104fe9.png

85d931613c828c2f2091aec3b2443fe8.png

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

2.1 类名称、包名

public classClassName {public static voidmain(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 classMethodTest {public static voidmain(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 classMethodChangeVal {public static void main(String[] args) throwsIllegalAccessException, 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 classMethodChangeVal {public static void main(String[] args) throwsIllegalAccessException, 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 getDeclaredConstructor(Class>... parameterTypes)public Constructor 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 classConstuctorTest {public static voidmain(String[] args) {

Class class2= Student.class;

Constructor>[] constructors =class2.getConstructors();for (Constructor>constructor : constructors) {

System.out.println(constructor+ ", Name:" +constructor.getName());

}

}

}

结果

publiccom.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 classFieldTest {public static voidmain(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 intcom.example.refs.Student.id

declaredField:publicjava.lang.String com.example.refs.Student.name

field:public java.lang.String com.example.refs.Student.name

2.5 修饰符

举例

public classModifierTest {public static voidmain(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 classGetParent {public static voidmain(String[] args) {

Class class2= Student.class;

System.out.println(class2.getSuperclass());

}

}

结果

class java.lang.Object

2.7 接口

public classInterfaceTest {public static voidmain(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 classNewInstanceTest {public static void main(String[] args) throwsIllegalAccessException, 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)

@interfaceMyAnnotation {publicString name();public String value();

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

举例

public classAnnotationTest {

@MyAnnotation(name="someName", value = "Hello World")public voiddoSomething() {

}

@Deprecatedpublic voiddelFunc() {

System.out.println("Hello");

}public static void main(String[] args) throwsNoSuchMethodException {

Class clazz= AnnotationTest.class;

Method method= clazz.getMethod("doSomething");

Annotation[] declaredAnnotations=method.getDeclaredAnnotations();for(Annotation annotation : declaredAnnotations) {if (annotation instanceofMyAnnotation) {

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 classGenericTest {public void test01(Map map, Listlist) {

System.out.println("test01");

}public Maptest02() {

System.out.println("test02");return null;

}public static void main(String[] args) throwsNoSuchMethodException {

Method method= GenericTest.class.getMethod("test01", Map.class, List.class);

Type[] types=method.getGenericParameterTypes();for(Type type : types) {

System.out.println("#:" +type);if (type instanceofParameterizedType) {

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 instanceofParameterizedType) {

Type[] genericTypes=((ParameterizedType)returnType).getActualTypeArguments();for(Type type : genericTypes) {

System.out.println("返回值,泛型类型:" +type);

}

}

}

}

结果

#:java.util.Map

泛型类型:class java.lang.String

泛型类型:class com.example.refs.Student

#:java.util.List

泛型类型:class com.example.refs.Student

Returntypejava.util.Map

返回值,泛型类型:class java.lang.Integer

返回值,泛型类型:class com.example.refs.Student

2.11 数组

public classArrayTest {public static voidmain(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 classTestReflect {

@Testpublic voidtestNoneReflect() {

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("没有反射, 共消耗 毫秒");

}

@Testpublic void testNotAccess() throwsClassNotFoundException, 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("启用安全检查, 共消耗 毫秒");

}

@Testpublic void testUseAccess() throwsClassNotFoundException, 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("取消安全检查, 共消耗 毫秒");

}

}

结果对比

差异

耗时(ms)

没用反射

952

取消安全检查

4283

启用安全检查

14892

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值