摘要:中的反射反射能够分析类所拥有的能力的程序称为反射。获取类的名称获取类的修饰符获取类所在的包获取父类的属性获取类的构造器等等获得的构造器的使用获取构造器有两种方法。
Java中的反射
反射:能够分析类所拥有的能力的程序称为反射。
反射的作用当我们在使用一个已有的类的时候,在主代码的main()方法中使用别的已有的类的时候,如果被使用的类发生了改变,那么导致我们的main()方法中也要修改大量的代码,以适应被使用的类的修改。这个时候,如果在main()方法使用了反射(reflect),那么就可以通过被使用的类的名字来获取这个被使用的类的所有情况(方法、Field等),这样就不必修改我们的主程序了。只需在被使用类的配置文件中写上被使用类的名字,在mian()方法中通过读取这个配置文件获得这个被使用类的名字即可通过反射的方法使用这个类。即使修改了要使用的类的名字或者实现,所有的修改也只体现在这个类的配置文件中,将需要修改的地方减至最少,提升了类的可扩展性。
反射作用使用示例:
被使用类与使用类:
import java.io.*; interface eatable { public void eat(); } class person implements eatable { public void eat() { System.out.println("person EAT"); } } class animal implements eatable { public void eat() { System.out.println("animal EAT"); } } public class ClassTest { public static void main(String[] args)throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("file.txt")) )); Class className = Class.forName(br.readLine()); //使用了anima类 eatable ea= (eatable)className.newInstance(); ea.eat(); } }
被使用类的配置文件:file.txt
animal
各种框架里面大量使用了反射,但是只对用户暴露了类名与该类的配置文件,防止被修改。
Java类的class属性程序运行的时候,每一个类(如Person类)在加载的时候,JVM都会为这个类创建一个static的class成员属性,这个class属性里面记载的都是这个程序里面所有Person类对象的信息。
同时我们可以通过Person类的class属性获得这个Person类的所有情况,如:Person类里面的所有方法对象,Person类里面的所有属性,Person类里面的所有构造方法对象。
这个class属性的类型就是Class.
获取类的Class对象。
Java中的所有类型包括基本类型(int, long, float等等),即使是数组都有与之关联的Class类的对象。
如果你在编译期知道一个类的名字的话,那么你可以使用如下的方式获取一个类的Class对象:
Class myObjectClass = MyObject.class;
如果你在编译期不知道类的名字,但是你可以在运行期获得到类名的字符串,那么你则可以这么做来获取Class对象:
String className = ... ;//在运行期获取的类名字符串
Class class = Class.forName(className);
在使用Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。例如MyObject类位于com.jenkov.myapp包,那么他的全名就是com.jenkov.myapp.MyObject。
类的class属性的作用用于动态获取类中的成员:
1. 可以获取这个类的所有方法对象(注意:Java中一切都是对象,包括方法,一个类中的成员,即使连方法也是对象。) 可以通过这个方法对象来使这个方法被执行,将一个类对象作为方法对象的参数传入即可。
2. 也可以获取这个类的所有成员。
3. 获取类的名称
4. 获取类的修饰符
5. 获取类所在的包
6. 获取父类的class属性
7. 获取类的构造器
等等
获取构造器有两种方法。
获取构造器的参数
使用构造器来创建类对象
访问类的私有变量即私有方法访问私有变量
要想获取私有变量你可以调用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法。Class.getField(String name)和Class.getFields()只会返回公有的变量,无法获取私有变量。下面例子定义了一个包含私有变量的类,在它下面是如何通过反射获取私有变量的例子:
public class PrivateObject { private String privateString = null; public PrivateObject(String privateString) { this.privateString = privateString; } } PrivateObject privateObject = new PrivateObject("The Private Value"); Field privateStringField = PrivateObject.class. getDeclaredField("privateString"); privateStringField.setAccessible(true); String fieldValue = (String) privateStringField.get(privateObject); System.out.println("fieldValue = " + fieldValue);
这个例子会输出”fieldValue = The Private Value”,The Private Value是PrivateObject实例的privateString私有变量的值,注意调用 PrivateObject.class.getDeclaredField(“privateString”)方法会返回一个私有变量,这个方法返回的变量是定义在PrivateObject类中的而不是在它的父类中定义的变量。 注意privateStringField.setAccessible(true)这行代码,通过调用setAccessible()方法会关闭指定类Field实例的反射访问检查,这行代码执行之后不论是私有的、受保护的以及包访问的作用域,你都可以在任何地方访问,即使你不在他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可以的,在编译的时候就会报错。
访问私有方法
访问一个私有方法你需要调用 Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods() 方法。 Class.getMethod(String name, Class[] parameterTypes)和Class.getMethods()方法,只会返回公有的方法,无法获取私有方法。下面例子定义了一个包含私有方法的类,在它下面是如何通过反射获取私有方法的例子:
public class PrivateObject { private String privateString = null; public PrivateObject(String privateString) { this.privateString = privateString; } private String getPrivateString(){ return this.privateString; } } PrivateObject privateObject = new PrivateObject("The Private Value"); Method privateStringMethod = PrivateObject.class. getDeclaredMethod("getPrivateString", null); privateStringMethod.setAccessible(true); String returnValue = (String) privateStringMethod.invoke(privateObject, null); System.out.println("returnValue = " + returnValue);
这个例子会输出”returnValue = The Private Value”,The Private Value是PrivateObject实例的getPrivateString()方法的返回值。
PrivateObject.class.getDeclaredMethod(“privateString”)方法会返回一个私有方法,这个方法是定义在PrivateObject类中的而不是在它的父类中定义的。
同样的,注意Method.setAcessible(true)这行代码,通过调用setAccessible()方法会关闭指定类的Method实例的反射访问检查,这行代码执行之后不论是私有的、受保护的以及包访问的作用域,你都可以在任何地方访问,即使你不在他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可以的,在编译的时候就会报错。
例如:在重写一个方法的时候,在方法上面加上@Override,在编译阶段就保证编译成功。这就是注解(Anotation)的作用,这是和注释不一样的。
Anotation是一个接口,Override之类的都是它的实现类。
获取注解对象只有一种方法:反射。
使用反射技术创建数组
int[] intArray = (int[]) Array.newInstance(int.class, 3);
这个例子创建一个int类型的数组。Array.newInstance()方法的第一个参数表示了我们要创建一个什么类型的数组。第二个参数表示了这个数组的空间是多大。
使用反射技术访问数组内的内容
具体可以使用Array.get(…)和Array.set(…)方法来访问数组内的内容。下面是一个例子:
int[] intArray = (int[]) Array.newInstance(int.class, 3);
Array.set(intArray, 0, 123);
Array.set(intArray, 1, 456);
Array.set(intArray, 2, 789);
System.out.println("intArray[0] = " + Array.get(intArray, 0));
System.out.println("intArray[1] = " + Array.get(intArray, 1));
System.out.println("intArray[2] = " + Array.get(intArray, 2));
获取数组对象的class属性
如果不通过反射的话你可以这样来获取数组的Class对象:
>
Class stringArrayClass = String[].class;
如果使用Class.forName()方法来获取数组的Class对象则不是那么简单。比如你可以像这样来获得一个原生数据类型(primitive)int数组的Class对象:
>
Class intArray = Class.forName("[I");
在JVM中字母I代表int类型,左边的‘[’代表我想要的是一个int类型的数组,这个规则同样适用于其他的原生数据类型。
对于普通对象类型的数组有一点细微的不同:
Class stringArrayClass = Class.forName("[Ljava.lang.String;");
注意‘[L’的右边是类名,类名的右边是一个‘;’符号。这个的含义是一个指定类型的数组。
获取普通原生数据类型的class属性
需要注意的是,你不能通过Class.forName()方法获取一个原生数据类型的Class对象。下面这两个例子都会报ClassNotFoundException:
Class intClass1 = Class.forName("I");
Class intClass2 = Class.forName("int");
通常会用下面这个方法来获取普通对象以及原生对象的Class对象:
public Class getClass(String className){
if("int" .equals(className)) return int.class;
if("long".equals(className)) return long.class;
...
return Class.forName(className);
}
一旦你获取了类型的Class对象,你就有办法轻松的获取到它的数组的Class对象,你可以通过指定的类型创建一个空的数组,然后通过这个空的数组来获取数组的Class对象。这样做有点讨巧,不过很有效。如下例:
Class theClass = getClass(theClassName); Class stringArrayClass = Array.newInstance(theClass, 0).getClass();
这是一个特别的方式来获取指定类型的指定数组的Class对象。无需使用类名或其他方式来获取这个Class对象。
为了确保Class对象是不是代表一个数组,你可以使用Class.isArray()方法来进行校验:
Class stringArrayClass = Array.newInstance(String.class, 0).getClass(); System.out.println("is array: " + stringArrayClass.isArray());
获取数组的成员的class属性
一旦你获取了一个数组的class属性,你就可以通过class.getComponentType()方法获取这个数组的成员类型。
成员类型就是数组存储的数据类型。例如,数组int[]的成员类型就是一个Class对象int.class。String[]的成员类型就是java.lang.String类的Class对象。
下面是一个访问数组成员的class属性的例子:
String[] strings = new String[3];
Class stringArrayClass = strings.getClass();
Class stringArrayComponentType = stringArrayClass.getComponentType();
System.out.println(stringArrayComponentType);
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64187.html
摘要:反射机制一结合官方通过编写的反射教程,复习一下反射的知识。反射的概念反射是一种在运行时获取以及修改应用行为的一种工具。因为反射需要动态的解析类的信息,相比于非反射使用的方式要慢。反射需要获取一定的运行时权限,在特定的安全环境下不一定存在。 Java反射机制(一) 结合Oracle官方通过JDK8编写的反射教程,复习一下反射的知识。结尾篇补一个小例子。 主要内容 这次博客的主要内容就是简...
摘要:通过反射获取带参无返回值成员方法并使用设置安全检查,访问私有构造函数必须创建实例这种不行,注意和方法需要传递参数测试复制这个功能获取私有方法,同样注意和的区别赋予访问权限调用方法。 反射 目录介绍 1.反射概述 1.1 反射概述 1.2 获取class文件对象的三种方式 1.3 反射常用的方法介绍 1.4 反射的定义 1.5 反射的组成 1.6 反射的作用有哪些 2.反射的...
摘要:反射使用类对象提供的基本元数据,能从类对象中找出方法或字段的名称,然后获取表示方法或字段的对象。常见的反射手段有反射和反射。以之前的反射为例其中指定了方法的返回类型,其实不止如此。 Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在运行时调用任意一个对象的方法 生成动态代理 很多框架...
近期在维护公司项目的时候遇到一个问题,因为实体类中的 set 方法涉及到了业务逻辑,因此在给对象赋值的过程中不能够使用 set 方法,为了实现功能,所以采用了反射的机制给对象属性赋值,借此机会也了解了反射的一些具体用法和使用场景,分以下两点对反射进行分析: 反射的优势和劣势 反射的应用场景 反射的优势和劣势 个人理解,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式...
摘要:一反射机制概念程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言,如,是动态语言显然,,不是动态语言,但是有着一个非常突出的动态相关机制。相关的为二获取源头重点打开权限所有类的对象其实都是的实例。 一、Java反射机制概念 程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言,如Python, Ruby是动态语言;显然C++,Java,C#不是动态语言,但是JAVA有...
阅读 2892·2021-11-17 09:33
阅读 3665·2021-11-16 11:42
阅读 3490·2021-10-26 09:50
阅读 1322·2021-09-22 15:49
阅读 3047·2021-08-10 09:44
阅读 3674·2019-08-29 18:36
阅读 3927·2019-08-29 16:43
阅读 2212·2019-08-29 14:10