简析反射
反射
1 什么是反射
(1)Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
(2)反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
通俗的说:
(1)反射可以从类里面拿东西。
(2)类里面有字段(成员变量),构造方法,成员方法。
(3)IDEA中的提示就是反射。
2 反射的作用
1 获取任意一个类中的所有信息。
2 一个类中都有哪些具体的信息呢?
(1)成员变量(修饰符、名字、类型、值)
(2)构造方法(修饰符、名字、形参、创建对象)
(3)成员方法(修饰符、名字、形参、返回值、抛出的异常、注解、运行方法)
3 结合配置文件动态创建对象。
3 获得Class 字节码文件对象的三种方式
(1)Class.forName("完整类名");
(2)类名.Class
(3)对象.getClass()
public static void main(String[] args) throws Exception {
Class cla = Class.forName("day10.Dog");
System.out.println(cla);
Class cla2 = Dog.class;
System.out.println(cla2);
Dog dog = new Dog();
Class cla3 = dog.getClass();
System.out.println(cla3);
System.out.println(cla == cla2);
System.out.println(cla2 == cla3);
System.out.println(cla == cla3);
}
4 如何获取构造方法、成员变量、成员方法
4.1 获取构造方法
getConstructors():返回所有公共构造方法对象的数组
getDeclaredConstructors(): 返回所有构造方法对象的数组
getConstructor(Class<?>... parameterTypes):返回单个公共构造方法对象
getDeclaredConstructor(lass<?>... parameterTypes):返回单个私有构造方法对象
Constructor类中用于创建对象的方法
newInstance(Objet... initargs): 根据指定的构造方法创建对象
setAccessible(boolean flag):设置为true,表示取消访问检查
实例
public static void main(String[] args) throws Exception {
Class cla = Class.forName("day10.Dog");
System.out.println("得到公共的构造方法***********");
Constructor[] con = cla.getConstructors();
for (Constructor constructor : con) {
System.out.println(constructor);
}
System.out.println("得到包括私有的所有构造方法*********");
Constructor[] con1 = cla.getDeclaredConstructors();
for (Constructor constructor : con1) {
System.out.println(constructor);
}
System.out.println("得到单个构造方法的修饰符********");
Constructor con2 = cla.getDeclaredConstructor(String.class);
int modifiers = con2.getModifiers();
System.out.println(modifiers);
Constructor con3 = cla.getDeclaredConstructor(String.class, String.class);
con3.setAccessible(true);
int modifiers2 = con3.getModifiers();
System.out.println(modifiers2);
System.out.println("获取了类里面的所有参数***********");
Parameter[] p1 = con3.getParameters();
for (Parameter parameter : p1) {
System.out.println(parameter);
}
}
分析
结果
API帮助文档 -> 常量字段值
使用反射创建对象
Dog dog = (Dog) con3.newInstance("旺财","金毛");
System.out.println(dog);
结果
4.2 获取成员变量
Field[] getFields():返回所有公共成员变量对象的数组
Field[] getDeclaredFields():返回所有成员变量对象的数组
Field getField(String name):返回单个公共成员变量对象
Field getDeclaredField(String name):返回单个成员变量对象
Field类中用于创建对象的方法
void set(Object obj, Object value):赋值
Object get(Object obj)获取值。
实例
public class MemberVariables {
public static void main(String[] args) throws Exception{
Class cla = Class.forName("day10.Dog");
System.out.println("获取公共的成员变量*********");
Field[] fields = cla.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("获取包含私有的所有的成员变量*********");
Field[] fields1 = cla.getDeclaredFields();
for (Field field : fields1) {
System.out.println(field);
}
System.out.println("获取单个的成员变量*********");
Field field = cla.getField("name");
System.out.println(field);
System.out.println("获取单个的成员变量*********");
Field field1 = cla.getDeclaredField("age");
System.out.println(field1);
System.out.println("获取成员变量的权限修饰符*********");
int mod = field1.getModifiers();
System.out.println(mod);
Class type = field1.getType();
System.out.println(type);
System.out.println("获取成员变量的值***********");
Constructor con = cla.getDeclaredConstructor(String.class,String.class,int.class,double.class);
Dog dog = (Dog)con.newInstance("旺财","金毛",8,20.5);
field1.setAccessible(true);
Object value = field1.get(dog);
System.out.println(value);
System.out.println("修改成员变量的值***********");
field1.setAccessible(true);
field1.set(dog,9);
System.out.println(dog);
}
分析
结果
4.3 获取成员方法
Field[] getFields():返回所有公共成员变量对象的数组
Field[] getDeclaredFields():返回所有成员变量对象的数组
Field getField(String name):返回单个公共成员变量对象
Field getDeclaredField(String name):返回单个成员变量对象
Field类中用于创建对象的方法
void set(Object obj, Object value):赋值
Object get(Object obj)获取值。
实例
代码
public static void main(String[] args) throws Exception {
Class cla = Class.forName("day10.Dog");
System.out.println("获取指定的方法*********************");
Method methods = cla.getMethod("show");
System.out.println(methods);
System.out.println("获取异常*********************");
Class[] excep = methods.getExceptionTypes();
for(Class exception : excep){
System.out.println(exception);
}
System.out.println("运行方法************");
Constructor con = cla.getConstructor(String.class,String.class,int.class,double.class);
Dog dog = (Dog) con.newInstance("小黑","德牧",5,30.5);
methods.invoke(dog);
}
结果
5 综合案例
做一个简易的配置文件:创建一个Dog对象,用反射将对象的成员变量及其对应值保存到本地文件中。
public static void main(String[] args) throws Exception {
Class cla = Class.forName("day10.Dog");
Constructor con = cla.getConstructor(String.class,String.class,int.class,double.class);
Dog dog = (Dog) con.newInstance("招财","中华田园犬",6,30.5);
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("dog.txt"));
Field[] fields = cla.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
Object value = field.get(dog);
bufferedWriter.write(name + "=" + value);
bufferedWriter.newLine();
}
bufferedWriter.close();
}
结果
反之,当你有一个类的配置文件时,你可以利用Properties(Properties 类是 Hashtable 的子类,该对象用于处理属性文件)和反射来对这个类进行操作。
6 反射的优点和缺点
优点
(1)反射机制可以动态地创建和使用对象,这在一些框架的开发中非常重要。它提供了灵活性,使得代码可以在运行时根据需要创建对象,并且可以对对象执行各种操作。
(2)反射机制是一些框架的核心组成部分,使得框架可以更加动态和可扩展。
缺点
(1)使用反射机制进行对象操作基本上是解释执行的,而不是直接的原生代码执行。这导致了执行速度较慢,相对于直接调用对象的方法或访问对象的属性,使用反射会带来一定的性能损失。因此,在对性能要求较高的情况下,使用反射可能会影响程序的执行速度,需要权衡利弊来选择是否使用反射机制。
(2)使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。
public static void main(String[] args) throws Exception {
Class cla = Class.forName("day10.Dog");
Constructor con = cla.getConstructor();
long start = System.currentTimeMillis();
for(int i = 0 ; i < 100000 ; i ++) {
Dog dog = (Dog) con.newInstance();
}
long end = System.currentTimeMillis();
System.out.println("反射执行10万次所用时间 = "+ (end - start));
long start1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
Dog dog = new Dog();
}
long end1 = System.currentTimeMillis();
System.out.println("new执行10万次所用时间 = "+ (end1 - start1));
}
结果
7 new对象和反射区别
使用new关键字创建对象和使用反射创建对象的主要区别如下:
(1)创建方式:使用new关键字创建对象时,直接调用类的构造方法进行实例化。而使用反射创建对象时,需要通过Class对象获取构造方法,然后调用构造方法进行实例化。
(2)可见性:使用new关键字创建对象时,需要在代码中显式引用类名来调用构造方法,因此构造方法必须具有相应的可见性(public、protected或默认访问修饰符)。而使用反射创建对象时,可以通过Class对象访问和调用非公共构造方法(即使是私有构造方法)。
(3)运行时确定:使用new关键字创建对象时,对象的创建在编译时就已经确定,并且编译器可以对其进行类型检查和优化。而使用反射创建对象时,对象的创建是在运行时动态确定的,无法在编译时进行类型检查和优化。
(4)异常处理:在使用new关键字创建对象时,如果构造方法抛出异常,可以在代码中使用try-catch块来捕获并处理异常。而使用反射创建对象时,由于构造方法调用是通过反射调用的,因此需要使用try-catch块来捕获InvocationTargetException异常,并从中获取原始的构造方法抛出的异常。
(5)性能:使用new关键字创建对象的性能通常比使用反射创建对象的性能更好。因为反射需要通过类的元数据进行额外的操作,如查找构造方法、参数匹配等,这些操作相对于直接调用构造方法来说更加耗时。
总体而言,使用new关键字创建对象是最常见和推荐的方式,因为它更简单、直观,并且具有更好的性能。而使用反射创建对象主要用于一些特殊需求,如动态加载类、调用私有构造方法等,在一般情况下并不常用。
Dog dog = new Dog()
Class cla = Class.forName("day10.Dog");
通过JVM的ClassLoader将class加载到内存,反射获取类信息等信息进行对成员变量和方法进行操作。