Spring源码前奏——Java反射

一、引言

在日常的工作中,如果是做业务开发,反射机制这个知识点很少有机会用到,当然也就不会放在心上,但如果想去看一些框架的源码或者深入一点学习,如果对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------------");
}

在这里插入图片描述

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值