java反射

本文详细介绍了Java反射的概念,强调了其动态加载类的优势,并探讨了类加载过程和类加载器。接着,文章阐述了Class类的特点,展示了获取Class对象的三种方法。通过反射可以获取类的成员,包括属性、方法、构造器以及注解。此外,文中还讲解了如何通过反射创建对象和调用属性及方法。最后,通过示例说明了反射下泛型的本质,揭示了泛型在编译后失去约束的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java反射

1.反射的理解和好处
2.Class类的理解和特点
3.通过反射获取类中的成员
4.通过反射创建对象
5.通过反射调用类中的成员
6.通过反射观察泛型的本质

1.反射的理解和好处

反射的理解

在这里插入图片描述

反射的好处

案例一

张三和李四共同完成一个项目

张三:设计类:Music类(√)、Word类(×)

李四:创建对象,并调用里面的方法

public void method(){
	Music m = new Music();
	m.work();
	Word w = new Word();//编译错误
	w.work();
}


//使用反射
public void method(Object o){
	Object obj = o.getClass().newInstance();
	o.getClass().getMethod("work").invoke();
}

静态加载:编译期加载,也就是说编译时加载所有类,如果该类不存在,则直接报编译错误,所以依赖性太强。

动态加载:(反射)运行期加载。运行时加载用到的类,编译时如果该类不存在,不报编译错误,降低了依赖性

类的加载过程
1、装载:通过类加载器将字节码文件读入到方法区中,并为之开辟Class类的对象
2、链接:将读入到方法区的字节码文件中的二进制数据合并到JRE
3、初始化:将类中的静态成员进行初始化
类加载器

一)类加载器分类

启动类加载器
	引导类加载器
	扩展类加载器
	系统类加载器
用户自定义加载器

二)类加载器加载的机制

先从下往上 ,检测该类是否被加载器加载过。
然后再从上往下,依次判断类加载器是否可以加载该类!
获取类加载器
1、获取系统类加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();

2、获取扩展类加载器
ClassLoader extLoader = loader.getParent();

3、获取引导类加载器
ClassLoader bootLoader = extLoader.getParent();

4、获取某个类所使用的类加载器
ClassLoader cl = 对应类的Class类对象.getClassLoader();

2.Class类的理解和特点 ★

  • 问:自定义的类,是一个对象吗?
    答:是

  • 问:自定义的类,是哪个类型的对象?
    答:java.lang.Class类

  • 问:Class类的对象如何创建
    答:不是new出来的,而是系统自动创建的,我们可以通过一定的方式获取对象的引用

特点
1.Class本身也是一个类
2.Class 对象只能由系统建立对象
3.一个类在 JVM 中只会有一个Class实例 
4.一个Class对象对应的是一个加载到JVM中的一个.class文件
5.每个类的实例都会记得自己是由哪个 Class 实例所生成
6.通过Class可以完整地得到一个类中的完整结构 
获取Class类对象的三种方式
1、Class.forName(全类名);
2、对象名.getClass()
3、类名.class

3.通过反射获取类中的成员

Class c = String.class;

一、获取类中的属性

Field[] fields = c.getFields();//获取public修饰的所有属性,包含从父类继承来的,不限于直接父类
Field[] fields = c.getDeclaredFields();//获取本类中定义的所有属性,不问修饰符

二、获取类中的方法

Method[] methods = c.getMethods();//获取public修饰的所有方法,包含从父类继承来的,不限于直接父类
Method[] methods = c.getDeclaredMethods();//获取本类中定义的所有方法,不问修饰符

三、获取类中的构造器

Constructor[] cons = c.getDeclaredConstructors();//获取本类中定义的所有构造器,不问修饰符

四、获取类所在的包

Package package = c.getPackage();

五、获取当前类的父类

Class parent = c.getSuperClass();

六、获取当前类的接口

Class[] interfaces = c.getInterfaces();

七、获取当前类带有泛型的父类

Type t = c.getGenericSuperClass();//Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
ParameterizedType pt = (ParameterizedType)t;
Class c = (Class)(pt.getActualArguments()[0]);//获取泛型列表中的第一个泛型参数
String name = c.getSimpleName();//第一个泛型参数的类型

八、获取当前类带泛型的接口

Type[] ts = c.getGenericInterfaces();
for(Type t:ts){
	if(!(t instanceof ParameterizedType))
		continue;
	ParameterizedType pt = (ParameterizedTypse)t;
	Class c = (Class)(pt.getActualArguments()[0]);
	String name = c.getSimpleName();
}

九、获取当前类的注解

Annotation[] anns = c.getAnnotations();
for(Annotation a : anns){
	System.out.pringln(a.annotationType().getsimpleName())
}

4.通过反射创建对象

方式一:调用无参构造器【推荐使用】

Class c = Class.forName("Student全类名");
Object o = c.newInstance();

方式二:调用有参构造器

Class c = Class.forName("Student全类名");
Constructor con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object o = con.newInstance("john");

5.通过反射调用类中的属性和方法

一、属性
1.获取指定的Field对象
Field field =clazz.getDeclaredField("属性名");
2.创建对象
Object obj = clazz.newInstance();
3.暴力破解
field.setAccessible(true);
4.为属性赋值
field.set(obj,value);
5.读取属性
Object value = field.get(obj);

二、方法
1.获取指定的Method对象
Method method = clazz.getDeclaredMethod(方法名,Class...cs);
2.创建对象
Object obj = clazz.newInstance();
3.暴破
method.setAccessible(true);
4.调用方法
method.invoke(obj,Object...obj);

6.通过反射观察泛型的本质

public class MethodDemo2 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        ArrayList<String> list2 = new ArrayList<String>();
        Class c1 = list1.getClass();
        Class c2 = list2.getClass();
        System.out.println(c1 == c2);
    }
}

因为list1和list2属于两个不同的对象,由此我们推断c1和c2也是不相等的两个类类型,但实际上结果输出true;因为反射将类的字节码文件加载内存,相当于字节码的执行阶段,那么c1和c2肯定属于执行阶段的比较,所以,编译之后集合的泛型是去泛型化的,所有的集合类的类类型都相等,泛型就不存在了,泛型只是在编译的时候约束元素的类型,只在编译阶段有效,所以我们可以利用反射的原理,绕过编译,让list1也可以存放不同类型的元素:

try {
	System.out.println(list1.size());
	Method method = c1.getMethod("add", Object.class);
	method.invoke(list1, "12234");
	System.out.println(list1.size());
} catch (NoSuchMethodException | SecurityException e) {
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
} catch (IllegalArgumentException e) {
	e.printStackTrace();
} catch (InvocationTargetException e) {
	e.printStackTrace();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值