1.反射的定义
允许java程序获取自身的信息,并且可以操作类或对象的内部属性。通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要在写代码的时候或编译期知道运行对象是谁
反射的主要功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
在运行时调用任意一个对象的方法
2.反射的主要用途
- 当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法
- 很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候用到反射,运行时动态加载需要加载的对象
3.反射的基本运用
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。Class 和 java.lang.reflect 一起对反射提供了支持
3.1 获得class对象
(1) 使用 Class 类的 forName 静态方法:
public static Class<?> forName(String className)
Class.forName("com.mysql.jdbc.Driver")
(2)直接获取某一个对象的 class,比如:
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
(3)调用某个对象的 getClass() 方法,比如:
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
3.2 判断是否为某个类的实例
用 instanceof 关键字来判断是否为某个类的实例。也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:public native boolean isInstance(Object obj)
3.3 创建对象实例
通过反射来生成对象主要有两种方式。
1.使用Class对象的newInstance()方法来创建Class对象对应类的实例。
1 Class<?> C = String.class;
2 Object str = c.newInstance();
2.先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。
//Constructor<T> getConstructor (类<?>... parameterTypes)
//Constructor<?>[] getConstructors()返回包含一个数组Constructor对象反射由此表示的类的所有公共构造类对象。
Class person2=Person.class;
Constructor construc tor=person2.getConstructor(String.class, int.class) ;
Object person=constructor.newInstance("马小跳”, 25);
//如果使用私有的构造方法也需要注意进行暴力反射 构造器.setAccessible( );
constructor.setAccessible(true) ;
3.4 获取方法
获取某个Class对象的方法集合,主要有以下几个方法:
●getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
●getMethods 方法返回某个类的所有公用(public) 方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException
●getMethod 方法返回一个特定的方法,其中第一 个参数为方法名称,后面的参数为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
3.5 获取构造器信息
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:public T newInstance(Object ... initargs )
此方法可以根据传入的参数来调用对应的Constructor创建对象实例。
3.6 获取类的成员变量信息
getFiled:访问公有的成员变量
getDeclaredField :所有已声明的成员变量,但不能得到其父类的成员变量
setaccessible(true):忽略访问权限修饰符的安全检查
3.7 调用方法
当我们从类中获取了一个方法后,我们就可以用invoke() 方法来调用这个方法。invoke 方法的原型为:
public object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,InvocationTargetException
//getMethod(String name,类<?>... parameterTypes)| 传递的参 数是方法的名称以及方法的参数列表
//使用的是空参方法
Class person4=Person. class;
Method method1=person4. getMethod( name:" eat" ) ;
//Method. invoke调用 该方法.nvoke这 个函数的参数是对象以及方法的参数列表|
Person pl=new Person();
method1.invoke(p1) ;
3.8 利用反射创建数组
Array类为java.lang.reflect.Array类,通过Array.newInstance()创建数组对象
public static void testArray() throws ClassNotFoundException {
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,25);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
//获取某一项的内容
System.out.println(Array.get(array,3));
}
4.优点缺点
优点:
可扩展性 :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类
类浏览器和可视化开发环境 :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
调试器和测试工具 : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
缺点:
性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。
内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。