Java 基础 - 反射详解

Java反射概念

反射机制:指在程序运行过程中,对任意一个类都能获取其所有属性和方法,并且对任意一个对象都能调用其任意一个方法。这种动态获取类和对象的信息,以及动态调用对象的方法的功能被称为 Java 语言的反射机制

为什么需要反射

在 Java 程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型。 编译时的类型由声明对象时使用的类型来决定,运行时的类型由运行时实际赋值给对象的类型决定。为了解决这些问题,程序需要在运行时发现对象和类的真实信息,此时就必须使用到反射,在运行时来获取对象和类的相关信息。

Java反射使用

反射使用步骤

1、获取想要操作的类的 Class 对象,该 Class 对象是反射的核心,通过它可以调用类的任意方法。

2、调用 Class 对象所对应的类中定义的方法,这是反射的使用阶段。

3、使用反射 API 来获取并调用类的属性和方法等信息。

获取Class对象的3种方式

  • 调用某个对象的 getClass()方法以获取该类对应的 Class 对象:
Person p = new Person();
Class clazz = p.getClass();
复制代码
  • 调用某个类的 class 属性以获取该类对应的 Class 对象:
Class clazz = Person.class;
复制代码
  • 调用 Class 类中的 forName() 静态方法以获取该类对应的 Class 对象,这是最安全、性能也最好的方法:
Class clazz=Class.forName("fullClassPath");
//fullClassPath 为类的包路径及名称(最常用)
复制代码

当我们获得了想要操作的类的 Class 对象后,可以通过 Class 类中的方法获取并查看该类中的方法和属性。

//获取 Person 类的 Class 对象
Class class=Class.forName("reflection.Person");

//获取 Programmer 类的所有方法信息
Method[] method=class.getDeclaredMethods();


//获取 Programmer类的所有成员属性信息
Field[] field=class.getDeclaredFields();


//获取 Programmer 类的所有构造方法信息
Constructor[] constructor=class.getDeclaredConstructors();

复制代码

通过反射创建对象的两种方式

  • Class 对象调用newInstance()方法

使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该Class对象对应的类有默认的空构造器。

  • Constructor 构造器调用newInstance()方法

先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。

//获取 Programmer 类的 Class 对象

Class class=Class.forName("reflection.Person");

//使用newInstane 方法创建对象

Programmer p=(Person) clazz.newInstance();

//获取构造方法并创建对象
Constructor c=class.getDeclaredConstructor(String.class,String.class,Integer.class);

//创建对象并设置属性
Programmer p1=(Person) c.newInstance("Akiang","男",20);

复制代码

Java反射API

  1. Class 类:反射的核心类,由 JVM 生成的,通过它能够获悉整个类的结构,如可以获取类的属性,方法等信息。

    Class类的常用方法:

方法名说明
forName()(1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
(2)为了产生Class引用,forName()立即就进行了初始化。
Object-getClass()获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName()取全限定的类名(包括包名),即类的完整名字。
getSimpleName()获取类名(不包括包名)
isInterface()判断Class对象是否是表示一个接口
getInterfaces()返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSuperclass()返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance()返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
getFields()获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
getDeclaredFields()获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。
  1. Constructor类:存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。

    Class类与Constructor相关的主要方法如下:

方法返回值方法名称方法说明
static Class<?>forName(String className)返回与带有给定字符串名的类或接口相关联的 Class 对象。
ConstructorgetConstructor(Class<?>... parameterTypes)返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[]getConstructors()返回所有具有public访问权限的构造函数的Constructor对象数组
ConstructorgetDeclaredConstructor(Class<?>... parameterTypes)返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[]getDeclaredConstructor()返回所有声明的(包括private)构造函数对象
TnewInstance()调用无参构造器创建此 Class 对象所表示的类的一个新实例。
  1. Field 类:Java.lang.reflec 包中的类,描述一个类中属性,包含属性的名字、数据类型、访问修饰符等,可以用来获取和设置类中的属性值。

    Class类与Field对象相关方法如下:

方法返回值方法名称方法说明
FieldgetDeclaredField(String name)获取指定name名称的(包含private修饰的)字段,不包括继承的字段
Field[]getDeclaredField()获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
FieldgetField(String name)获取指定name名称、具有public修饰的字段,包含继承字段
Field[]getField()获取修饰符为public的字段,包含继承字段
voidset(Object obj, Object value)将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
Objectget(Object obj)返回指定对象上此 Field 表示的字段的值
Class<?>getType()返回一个 Class 对象,它标识了此Field 对象所表示字段的声明类型。
booleanisEnumConstant()如果此字段表示枚举类型的元素则返回 true;否则返回 false
StringtoGenericString()返回一个描述此 Field(包括其一般类型)的字符串
StringgetName()返回此 Field 对象表示的字段的名称
Class<?>getDeclaringClass()返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段
voidsetAccessible(boolean flag)将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性
  1. Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息(包括返回值类型、方法参数等)或者执行方法(通过Method对象的invoke()方法就可以调用类中的方法)。

    Class类获取Method对象相关的方法:

方法返回值方法名称方法说明
MethodgetDeclaredMethod(String name, Class<?>... parameterTypes)返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[]getDeclaredMethod()返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
MethodgetMethod(String name, Class<?>... parameterTypes)返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[]getMethods()返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

Java反射调用流程

  • 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;

  • 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method()反射方法,并作用到其他实例身上;

  • 反射也是考虑了线程安全的,放心使用;

  • 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;

  • 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;

  • 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;

  • 调度反射方法,最终是由jvm执行invoke0()执行;


作者:清粥为伴
链接:https://juejin.cn/post/7007300314541850631
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值