一、引言
在日常的工作中,如果是做业务开发,反射机制这个知识点很少有机会用到,当然也就不会放在心上,但如果想去看一些框架的源码或者深入一点学习,如果对Java反射机制不是很了解,那就会一头雾水。
最近又想去再看一下Spring的源码,想起自己第一次看Spring源码时的那种痛苦,我觉得很有必要还是先学习一下反射。希望对你有所帮助~~
恩,书读百遍其义自见。
二、反射基础介绍
1、反射的原理
Java反射机制就是程序在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意方法和属性,这种动态获取信息以及动态调用对象的功能称为Java语言的反射机制。
粗略的理解:反射就是在程序运行时,能够动态的操作类的成员。
2、反射的相关类
类名 | 用途 |
---|---|
Class类 | 代表类和接口 |
Field类 | 代表类的属性 |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
3、Class类
Class类是描述类的类。能理解吗?
(1)、Class这个类中提供类很多的方法。
方法 | 用途 |
---|---|
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共(public)类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有(public、private、default)类和接口类的对象 |
forName(String className) | 根据类名(包含包名)返回类的对象 |
getName() | 获得类的完整路径名字(包含包名) |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
(2)、获取类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有(public)属性对象 |
getFields() | 获得所有公有(public)属性对象 |
getDeclaredField(String name) | 获得某个属性(public、private、default)对象 |
getDeclaredFields() | 获得所有属性(public、private、default)对象 |
(3)、获取类中注解的相关方法
方法 | 用途 |
---|---|
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有(public)注解对象 |
getAnnotations() | 返回该类所有的公有(public)注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有(public、private、default)注解对象 |
getDeclaredAnnotations() | 返回该类所有(public、private、default)的注解对象 |
(4)、获取类中构造器相关的方法
方法 | 用途 |
---|---|
getConstructor(Class<?> parameterTypes) | 获得该类中与参数类型匹配的公有(public)构造方法 |
getConstructors() | 获得该类的所有公有(public)构造方法 |
getDeclaredConstructor(Class<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法,包含私有 |
getDeclaredConstructors() | 获得该类所有(public、private、default)构造方法 |
(5)、获取类中方法的相关方法
方法 | 用途 |
---|---|
getMethod(String name, Class<?> parameterTypes) | 获得该类某个公有(public)的方法 |
getMethods() | 获得该类所有公有(public)的方法 |
getDeclaredMethod(String name, Class<?> parameterTypes) | 获得该类某个方法(public、private、default) |
getDeclaredMethods() | 获得该类所有方法(public、private、default) |
(6)、类中其他重要的方法
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
4、Field类
Field代表类的成员变量对象(类的属性)
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
getName() | 获取类中属性的名字 |
5、Method类
Method代表类的方法
方法 | 用途 |
---|---|
invoke(Object obj, Object… args) | 传递object对象及参数调用该对象对应的方法 |
6、Constructor类
Constructor代表类的构造方法
方法 | 用途 |
---|---|
newInstance(Object… initargs) | 根据传递的参数创建类的对象 |
三、反射示列
上面介绍了与反射有关的类,我们知道反射机制是在运行状态中通过字节码得到类的具体信息,然后去操作类,所以,反射的前提是通过字节码.class来获取Class对象。
我们再来回忆一下Java程序的运行过程:
源文件经过编译(javac.exe)以后,得到一个或多个.class文件。.class文件经过运行(java.exe)以后,就需要进行类的加载(通过JVM的类的加载器),加载到内存中的缓存中,每一个放入缓存中的.class文件就是一个Class的实例。
反射需要的代码类:
public class Creature<T>{
public double weight;
public void breath(){
System.out.println("呼吸!");
}
}
// 注解类
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
// 接口
public interface MyInterface extends Serializable{
}
// Person类
@MyAnnotation(value = "scorpios")
public class Person extends Creature<String>
implements Comparable,MyInterface{
public String name;
private int age;
int id;
// 创建类时,尽量保留一个空参的构造器。
// 因为反射调用的newInstance()方法就是调用空参构造函数
public Person() {
super();
System.out.println("今天天气很闷热");
}
public Person(String name) {
super();
this.name = name;
}
private Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// 方法
@MyAnnotation(value = "abc123")
public void show(){
System.out.println("我是一个人!");
}
private Integer display(String nation,Integer i) throws Exception{
System.out.println("我的国籍是:" + nation);
return i;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Object o) {
return 0;
}
public static void info(){
System.out.println("中国人!");
}
class Bird{
}
}
1、获取类的Class对象的方式
(1)、调用Class的静态方法forName(String className)
Class clazz = Class.forName("com.scorpios.java.Person")
(2)、调用运行时类的.class属性
Class clazz = Person.class
(3)、通过运行时类的对象,调用其getClass()方法
Class clazz = new Person().getClass();
2、有了Class实例以后,可以做什么?
应用一:可以创建对应的运行时类的对象
@Test
public void test1() throws Exception{
System.out.println("------------test1() begin------------");
String className = "com.scorpios.java.Person";
Class clazz = Class.forName(className);
// 创建对应的运行时类的对象。使用newInstance(),
// 实际上就是调用了运行时类的空参的构造器。
// 要想能够创建成功:
// ① 要求对应的运行时类要有空参的构造器。
// ② 构造器的权限修饰符的权限要足够。
Object obj = clazz.newInstance();
Person p = (Person)obj;
System.out.println(p);
System.out.println("------------test1() end------------");
}
@Test
public void test2() throws ClassNotFoundException{
System.out.println("------------test2() end------------");
String className = "com.scorpios.java.Person";
Class clazz = Class.forName(className);
// 获取所有的构造函数
Constructor[] cons = clazz.getDeclaredConstructors();
for(Constructor c : cons){
System.out.println(c);
}
System.out.println("------------test2() end------------");
}
//调用指定的构造器,创建运行时类的对象
@Test
public void test3() throws Exception{
System.out.println("------------test3() end------------");
String className = "com.scorpios.java.Person";
Class clazz = Class.forName(className);
// 获取两个参数的构造函数,该构造函数是private私有的
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("罗伟",20);
System.out.println(p);
System.out.println("------------test3() end------------");
}
应用二:获取对应的运行时类的完整的类的结构:属性
// 获取对应的运行时类的属性
@Test
public void test1(){
System.out.println("------------test1() start------------");
Class clazz = Person.class;
// getFields():只能获取到运行时类中及其父类中声明为public的属性
Field[] fields = clazz.getFields();
for(int i = 0;i < fields.length;i++){
System.out.println(fields[i]);
}
System.out.println();
// getDeclaredFields():获取运行时类本身声明的所有的属性
Field[] fields1 = clazz.getDeclaredFields();
for(Field f : fields1){
System.out.println(f.getName());
}
System.out.println("------------test1() end------------");
}
getFields():只能获取到运行时类中及其父类中声明为public的属性
getDeclaredFields():获取运行时类本身声明的所有的属性
// 权限修饰符 变量类型 变量名
// 获取属性的各个部分的内容
@Test
public void test2(){
System.out.println("------------test2() start------------");
Class clazz = Person.class;
Field[] fields1 = clazz.getDeclaredFields();
for(Field f : fields1){
// 1.获取每个属性的权限修饰符
int i = f.getModifiers();
String str1 = Modifier.toString(i);
System.out.print(str1 + " ");
// 2.获取属性的类型
Class type = f.getType();
System.out.print(type.getName() + " ");
// 3.获取属性名
System.out.print(f.getName());
System.out.println();
}
System.out.println("------------test2() end------------");
}
// 调用运行时类中指定的属性
@Test
public void test3() throws Exception{
System.out.println("------------test3() start------------");
Class clazz = Person.class;
// 1.获取指定的属性
//getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
Field name = clazz.getField("name");
// 2.创建运行时类的对象
Person p = (Person)clazz.newInstance();
System.out.println(p);
// 3.将运行时类的指定的属性赋值
name.set(p,"Jerry");
System.out.println(p);
System.out.println("%"+name.get(p));
System.out.println();
// getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
Field age = clazz.getDeclaredField("age");
// 由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
age.setAccessible(true);
age.set(p,10);
System.out.println(p);
System.out.println("------------test3() end------------");
}
应用三:调用对应的运行时类中指定的结构:方法
Method[] m1 = clazz.getMethods() :获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
Method[] m2 = clazz.getDeclaredMethods():获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)
// 1.获取运行时类的方法
@Test
public void test1(){
System.out.println("------------test1() start------------");
Class clazz = Person.class;
// 1.getMethods():获取运行时类及其父类中所有的声明为public的方法
Method[] m1 = clazz.getMethods();
for(Method m : m1){
System.out.println(m);
}
System.out.println();
// 2.getDeclaredMethods():获取运行时类本身声明的所有的方法
Method[] m2 = clazz.getDeclaredMethods();
for(Method m : m2){
System.out.println(m);
}
System.out.println("------------test1() end------------");
}
Method[] m1 = clazz.getMethods():获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
Method[] m2 = clazz.getDeclaredMethods():获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)
// 注解 权限修饰符 返回值类型 方法名 形参列表 异常
@Test
public void test2(){
System.out.println("------------test2() start------------");
Class clazz = Person.class;
Method[] m2 = clazz.getDeclaredMethods();
for(Method m : m2){
// 1.注解
Annotation[] ann = m.getAnnotations();
for(Annotation a : ann){
System.out.println(a);
}
// 2.权限修饰符
String str = Modifier.toString(m.getModifiers());
System.out.print(str + " ");
// 3.返回值类型
Class returnType = m.getReturnType();
System.out.print(returnType.getName() + " ");
// 4.方法名
System.out.print(m.getName() + " ");
// 5.形参列表
System.out.print("(");
Class[] params = m.getParameterTypes();
for(int i = 0;i < params.length;i++){
System.out.print(params[i].getName() + " args-" + i + " ");
}
System.out.print(")");
// 6.异常类型
Class[] exps = m.getExceptionTypes();
if(exps.length != 0){
System.out.print("throws ");
}
for(int i = 0;i < exps.length;i++){
System.out.print(exps[i].getName() + " ");
}
System.out.println();
}
System.out.println("------------test2() end------------");
}
// 调用运行时类中指定的方法
@Test
public void test3() throws Exception{
System.out.println("------------test3() start------------");
Class clazz = Person.class;
// getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
Method m1 = clazz.getMethod("show");
Person p = (Person)clazz.newInstance();
// 调用指定的方法:Object invoke(Object obj,Object ... obj)
Object returnVal = m1.invoke(p); //我是一个人
System.out.println(returnVal); //null
Method m2 = clazz.getMethod("toString");
Object returnVal1 = m2.invoke(p);
System.out.println(returnVal1); //Person [name=null, age=0]
// 对于运行时类中静态方法的调用
Method m3 = clazz.getMethod("info");
m3.invoke(Person.class);
//getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
m4.setAccessible(true);
Object value = m4.invoke(p,"CHN",10);//我的国籍是:CHN
System.out.println(value);//10
System.out.println("------------test3() end------------");
}
// 1.获取运行时类的父类
@Test
public void test1(){
System.out.println("------------test1() start------------");
Class clazz = Person.class;
Class superClass = clazz.getSuperclass();
System.out.println(superClass);
System.out.println("------------test1() end------------");
}
// 2.获取带泛型的父类
@Test
public void test2(){
System.out.println("------------test2() start------------");
Class clazz = Person.class;
Type type1 = clazz.getGenericSuperclass();
System.out.println(type1);
System.out.println("------------test2() end------------");
}
// 3.获取父类的泛型
@Test
public void test3(){
System.out.println("------------test3() start------------");
Class clazz = Person.class;
Type type1 = clazz.getGenericSuperclass();
ParameterizedType param = (ParameterizedType)type1;
Type[] ars = param.getActualTypeArguments();
System.out.println(((Class)ars[0]).getName());
System.out.println("------------test3() end------------");
}
// 4.获取实现的接口
@Test
public void test4(){
System.out.println("------------test4() start------------");
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class i : interfaces){
System.out.println(i);
}
System.out.println("------------test4() end------------");
}
// 5.获取所在的包
@Test
public void test5(){
System.out.println("------------test5() start------------");
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
System.out.println("------------test5() end------------");
}
//6.获取注解
@Test
public void test6(){
System.out.println("------------test6() start------------");
Class clazz = Person.class;
Annotation[] anns = clazz.getAnnotations();
for(Annotation a : anns){
System.out.println(a);
}
System.out.println("------------test6() end------------");
}