前言
反射算是Java中很强很重要的技术,尤其是在spring中,它有两个重要的特点依赖注入、反转控制,就是运用反射
反射的理解:通过class获得类对象,然后分解映射class对象中的各种元素(构造方法、成员变量等)为一个个对象
反射
动态语言
动态语言(Dynamic programming Language -动态语言或动态编程语言),是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化
静态类型语言:(Statically Typed Language-静态类型语言)静态类型语言与动态类型语言刚好相反,它的数据类型是在编译期间检查的,也就是说在写程序时要声明所有变量的数据类型
JavaScript 就是动态语言,除此之外 Ruby,Python 等也属于动态语言
而 C、C++则不属于动态语言。从反射角度说 JAVA 属于半动态语言
最明显的就是JavaScript里的var数据类型,在运行时确定数据类型
反射机制
反射机制:在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法
这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制
反射的应用场合
Java 程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。
编译时类型由声明对象时实用的类型来决定
运行时类型是由该变量指向的对象类型决定
两种类型不同,就会出现多态,即子类对象赋值给父类引用变量
Person p=new Student(); 其中编译时类型为Person,运行时类型为Student,Person为Student的父类
编译时根本无法预知对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射
反射的API
反射 API 用来生成 JVM 中的类、接口或则对象的信息。
- Class 类:反射的核心类,可以获取类的属性,方法等信息。
- Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性
值。 - Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或
者执行方法。 - Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。
反射使用步骤(获取 Class 对象、调用对象方法)
- 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方
法。 - 调用 Class 类中的方法,既就是反射的使用阶段。
- 使用反射 API 来操作这些信息。
获得类对象:
创建一个类Student:
package com.company.entity;
public class Student {
private int id;
private String name;
public String myclass;
public Student(int id, String name, String myclass) {
this.id = id;
this.name = name;
this.myclass = myclass;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
有3种方法
- 调用某个对象的 getClass()方法
Student student=new Student();
Class studentClass=student.getClass();
- 调用某个类的 class 属性来获取该类对应的 Class 对象
Class studentClass=Student.class;
- 使用 Class 类中的 forName()静态方法(最安全/性能最好)
Class studentClass=Class.forName("com.company.entity.Student");
Method[] method=studentClass.getDeclaredMethods();
Field[] field=studentClass.getDeclaredFields();
Constructor[] constructors=studentClass.getDeclaredConstructors();
System.out.println("Student类的所有方法:");
for(Method m:method) {
System.out.println(m.toString());
}
System.out.println("Student类的所有成员变量:");
for (Field field1:field){
System.out.println(field1.toString());
}
System.out.println("Student类的所有构造方法:");
for (Constructor constructor:constructors){
System.out.println(constructor.toString());
}
到这大概可以看出反射的作用:把Java类的各个成分(成员变量、构造方法、方法等)分解映射成一个个对象
使用反射机制
通过反射绕开泛型
java中集合的泛型是防止错误输入的;只在编译阶段有效,只要绕过编译就无效,在运行时候,是不区分输入什么
ArrayList list1=new ArrayList();
ArrayList<String> list2=new ArrayList<String>();
Class c1=list1.getClass();
Class c2=list2.getClass();
//在运行期间泛型无效,所以c1和c2应该是一样的。
System.out.println(c1==c2);//true
//由于泛型失效,所以此时list当然可以添加任何对象
Method m=c2.getMethod("add",Object.class);
m.invoke(list2,20);//向list2集合中添加一个int 型的值;绕过编译
System.out.println(list2.toString());
源码实现
大佬的解释:Java基础之—反射(非常重要)
深入分析Java方法反射的实现原理
Class类:获得类对象
class类的加载过程:
最常用的方法forName():
public static Class<?> forName(String className)
throws ClassNotFoundException {
//这个方法获取调用该方法的类,是为了获得该类的类加载器,来加载驱动
Class<?> caller = sun.reflect.Reflection.getCallerClass();
//forName0是一个本地方法,有4个参数
//className表示要加载器的类的全路径名
//initialize 布尔值,true表示加载并初始化该类,false表示加载不初始化该类
//loader:表示使用哪个类加载器,使用调用者的类加载器
//caller:表示调用forname方法的类
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller);
}
看看注释(谷歌翻译):
*返回与该类关联的{@code Class}对象,或者
*具有给定字符串名称的接口。调用此方法是
* 相当于:
*
* <blockquote>
* {@code Class.forName(className,true,currentLoader)}
* </ blockquote>
*
*其中{@code currentLoader}表示的定义类加载器
*当前的类。
*
* <p>例如,以下代码片段返回
*名为类的运行时{@code Class}描述符
* {@code java.lang.Thread}:
*
* <blockquote>
* {@code Class t = Class.forName(“ java.lang.Thread”)}
* </ blockquote>
* <p>
*调用{@code forName(“ X”)}会导致名为
* {@code X}要初始化。
*
* @param className所需类的完全限定名称。
* @返回带有该类的{@code Class}对象
*指定名称。
* @exception LinkageError如果链接失败
* @exception ExceptionInInitializerError如果引发了初始化
*通过此方法失败
* @Exception ClassNotFoundException如果无法找到该类
forName(String className)方法通过输入的类地址找到我们想要的类,获得类加载器获得驱动
forName(String name, boolean initialize,ClassLoader loader)多了自己设定的initialize(初始化设定)、loader(类加载器)
Method类:获得类对象中的方法
源码:
getDeclaredMethods返回的是所有类对象的方法
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyMethods(privateGetDeclaredMethods(false));
}
可以看看getDeclaredMethods方法的注释(谷歌翻译):
*返回一个包含{@code Method}对象的数组,这些对象反映了所有
*此{@code代表的类或接口的声明方法
* Class}对象,包括public,protected,default(包)
*访问和私有方法,但不包括继承的方法。
*
* <p>如果此{@code Class}对象表示具有多个
*声明的方法具有相同的名称和参数类型,但不同
*返回类型,则返回的数组具有一个{@code Method}对象,用于
*每种这样的方法。
*
* <p>如果此{@code Class}对象表示具有类的类型
*初始化方法{@code <clinit>},然后返回的数组
* <em>不是</ em>具有相应的{@code方法}对象。
*
* <p>如果此{@code Class}对象表示不包含的类或接口
*声明的方法,则返回的数组长度为0。
*
* <p>如果此{@code Class}对象表示数组类型,则为原语
*类型或void,则返回的数组长度为0。
*
* <p>返回数组中的元素未排序,也不在任何元素中
*特定顺序。
*
* @返回代表所有对象的{@code方法}对象的数组
*此类的声明方法
* @抛出SecurityException
*如果存在安全管理者<i> s </ i>,并且任何
*满足以下条件:
*
* <ul>
*
* <li>调用者的类加载器与
*此类的类加载器并调用
* {@link SecurityManager#checkPermission
* s.checkPermission}方法,
* {@code RuntimePermission(“ accessDeclaredMembers”)}
*拒绝访问此类中已声明的方法
*
* <li>调用者的类加载器与或
*当前类的类加载器的祖先,
*调用{@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()}拒绝访问包
*此类
checkMemberAccess方法是什么?
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
final SecurityManager s = System.getSecurityManager();
if (s != null) {
/* Default policy allows access to all {@link Member#PUBLIC} members,
* as well as access to classes that have the same class loader as the caller.
* In all other cases, it requires RuntimePermission("accessDeclaredMembers")
* permission.
*/
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
if (which != Member.PUBLIC) {
if (ccl != cl) {
s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
}
this.checkPackageAccess(ccl, checkProxyInterfaces);
}
}
看不懂,看看注释
/*
*检查是否允许客户端访问成员。如果访问被拒绝,引发SecurityException。
*此方法还强制执行包访问。
*默认策略:允许所有客户端使用普通Java访问
*控制。
*/
那copyMethods返回的是什么?
private static Method[] copyMethods(Method[] arg) {
Method[] out = new Method[arg.length];
ReflectionFactory fact = getReflectionFactory();
for (int i = 0; i < arg.length; i++) {
out[i] = fact.copyMethod(arg[i]);
}
return out;
}
大致就是检查class类对象的方法?后面返回 copyMethods返回的数组
Field类:获得类对象的成员变量
源码:
getDeclaredFields方法返回的是所有成员变量
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
跟Method类似,返回copyFields的数组
Constructor类:获得构造方法
源码:
getDeclaredConstructors返回的是所有构造方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(false));
}
看看注释(谷歌翻译):
*返回{@code Constructor}对象的数组,这些对象反映了所有
*由此表示的类声明的构造函数
* {@code Class}对象。这些是公共的,受保护的,默认的
*(包)访问权限和私有构造函数。数组中的元素
*返回的不是排序的,并且没有任何特定的顺序。如果
*类具有默认构造函数,它包含在返回的数组中。
*如果此{@code Class},则此方法返回长度为0的数组
*对象表示接口,原始类型,数组类或
*无效。
*
* <p>参见<em> Java语言规范</ em>,第8.2节。
*
* @返回代表所有对象的{@code Constructor}对象的数组
*声明为此类的构造方法
* @抛出SecurityException
*如果存在安全管理者<i> s </ i>,并且任何
*满足以下条件:
*
* <ul>
*
* <li>调用者的类加载器与
*此类的类加载器并调用
* {@link SecurityManager#checkPermission
* s.checkPermission}方法,
* {@code RuntimePermission(“ accessDeclaredMembers”)}
*拒绝访问此类中已声明的构造方法
*
* <li>调用者的类加载器与或
*当前类的类加载器的祖先,
*调用{@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()}拒绝访问包
*此类
Java反射大致就这样,源码一环套一环,多看看源码、注释了解一下就好
在Spring中反射是个关键技术