java 反射机制
反射机制概念
- 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。反射被视为动态语言的关键。简单来说反射就是java的各种成分映射成对应的java类。
- 通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。包括构造方法,属性,方法。
java反射提供的功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
java不是动态语言,但是通过反射机制实现动态机制。
反射的基本使用
java反射的主要组成
- Class:任何运行在内存中的所有类都是改Class类的实例对象,每个Class类对象内存都包含了本来的所有信息。通过反射干任何事情,都要先去找Class
- 类内部主要信息:
- Field:所有属性
- Constructor:所有构造方法
- Method:所有方法
- 类内部主要信息:
- Field:描述一个类的属性,内部包含了该属性的所有信息,例如数据类型,属性名,访问修饰符
- 类内部主要信息
- 标注在属性上的注解
- 属性名
- 属性的数据类型(boolean/double/int/String …)
- 属性访问修饰符(public/private/protected)
- 类内部主要信息
- Constructor:描述一个类的构造方法,内部包含了构造方法的所有信息,例如参数类型、参数名称、访问修饰符
- 类内部主要信息
- 构造方法的访问修饰符
- 构造方法的参数
- 参数的数据类型
- 参数的名称
- 标注在参数上的注解
- 类内部主要信息
- Method:描述一个类的所有方法(包括抽象方法),内部包含了该方法的所有信息,与Constructor类似,不同之处就是Method拥有返回值类型信息,因为构造方法是没有返回值的
- 类内部主要信息
- Constructor类内部主要信息
- 方法返回值类型(Int/double0
- 类内部主要信息
反射使用步骤(获取Class对象、调用对象方法)
- 获取想要操作的类的Class对象,它是反射的核心,通过Class对象我们可以任意调用类的方法
- 调用Class类中的方法,即就是反射的使用阶段
- 使用反射API来操作这些信息
反射使用
例子People
public class People {
public String name;
public int age;
private double weight;
public People(){
}
public People(String name,int age){
this.name=name;
this.age=age;
}
public void getInfo(){
System.out.println(name+"的年龄是:"+age);
}
}
获取Class的三种方法(获取一个类的字节码对象)
当使用javac编译后,就会产生一个.class文件,当字节码文件被装载进虚拟机执行的时候,会在内存中生成Class对象,包含了该类内部的所有信息,在程序运行阶段可以获取这些信息。
-
使用对象获取,使用对象的getClass获取
People people = new People(); Class clazz=people.getClass();
-
使用静态属性class
只有在编译前就已经声明了该类的类型才能获取到Class对象
Class clazz =People.class;
-
使用Class类的静态方法forName(通过类的全限定名获取该类的 Class 对象)
-
类要写全名
Class clazz = Class.forName("")
当我们拿到class对象后就可以,获取类信息、调用其方法、获取其属性。
-
获取一个类的所有信息
Class对象中国包含了该类的所有信息,在编译期间我们能看到的信息及时该类的变量、方法、构造器,在运行时最常被获取的也是这些信息
每种功能内部以Declared细分为两类:
- 有Declared修饰的方法:可以获取该类内部包含的所有变量、方法和构造器,但是无法获取继承下来的信息
- 无Declared修饰的方法:可以获取该类中public修饰的变量、方法和构造器,以及获取继承下来的信息
如果要想获取类中所有的(包含继承)的变量、方法和构造器,则需要同时调用,并且用set集合存储他们获得的遍历,一方获取到相同的东西。
获取类中的变量(Field)
- Field[] getField():获取类中所有被public修饰的所有变量
- Field getField(String name):根据变量名获取类中的一个变量,改变了必须是被public修饰
- Field[] getDeclaredFields):获取类中所有的变量,但无法获取继承下来的变量
- Field getDeclaredField(String name):根据名称获取类中的变量,也无法获取继承下来的变量
获取类中的方法(Method)
- Method[] getMethods():获取类中被
public
修饰的所有方法 - Method getMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,该方法必须被
public
修饰 - Method[] getDeclaredMethods():获取
所有
方法,但无法获取继承下来的方法 - Method getDeclaredMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法
获取类的构造器(Constructor)
- Constuctor[] getConstructors():获取类中所有被
public
修饰的构造器 - Constructor getConstructor(Class…<?> paramTypes):根据
参数类型
获取类中某个构造器,该构造器必须被public
修饰 - Constructor[] getDeclaredConstructors():获取类中所有构造器
- Constructor getDeclaredConstructor(class…<?> paramTypes):根据
参数类型
获取对应的构造器
如果父类的属性用protected修饰,利用反射是无法获取到的
获取注解
- Annotation[] getAnnotations():获取该对象上的所有注解
- Annotation getAnnotation(Class annotaionClass):传入
注解类型
,获取该对象上的特定一个注解 - Annotation[] getDeclaredAnnotations():获取该对象上的显式标注的所有注解,无法获取
继承
下来的注解 - Annotation getDeclaredAnnotation(Class annotationClass):根据
注解类型
,获取该对象上的特定一个注解,无法获取继承
下来的注解
只有注解的@Retension标注为RUNTIME时,才能通过反射获取到该注解
详情请点击:注解详情
通过反射调用方法
通过反射获取到某个Method类对象后,可以通过调用invoke方法执行
invoke(Oject obj, Object... args)
:参数``1指定调用该方法的**对象**,参数
2`是方法的参数列表值。- 如果调用的方法是静态方法,参数1只需要传入
null
,因为静态方法不与某个对象有关,只与某个类有关。
反射的应用场景
常见场景有三种:
- Spring实例化对象:当程序启动时,Spring会读取配置文件applicationContext.xml并解析出里面所有的标签实例化到IOC容器中
- 反射+工程模式:通过反射消除工厂中的多个分支,如果需要生产新的类,无序关注工厂类,工厂类可以应对各种新增类,反射可以使得程序更加健壮
- JDBC连接数据库:使用JDBC连接数据库时,指定连接数据库的驱动类时用到反射加载启动类
Spring的IOC容器
在Spring中,经常会用到一个上下文配置文件applicationContext.xml里面就是关于bean的配置,查询启动时会读取该xml文件,解析出所有的标签,并实例化对象放入IOC容器中,IOC容器本质上就是一个工厂,通过工厂传入标签的id属性获取对应的实例。
Spring在实例化对象的过程经过简化后,可以理解为反射实例化对象的步骤
- 获取Class对象的构造器
- 通过构造器调用newInstance()实例化对象
当然Spring在实例化对象时,做了非常多额外的操作,才能让现在的开发便捷且稳定
反射+抽象工程模式
传统的工程模式,如果需要生产新的子类,就需要修改工厂类,在工厂类中增加新的分支。
利用反射和工厂模式相结合,在生产新子类时,工厂类不需要修改任何东西,可以专注于子类的实现,当子类确定下来时,工厂也就可以生成该子类了、
反射+抽象工厂的核心思想是:
- 在运行时通过参数传入不同子类的全限定名获取到不同的Class对象,调用newInstance()方法返回不同的子类。
JDBC加载数据库驱动类
在导入第三方库时,JVM不会主动去加载外部导入的类,而是等到真正使用时,才会去加载需要的类,正因为如此,我们可以在获取数据库连接时传入驱动类的全限定名,交给JVM加载该类
反射的优缺点
反射的优点:
- 增加程序灵活性:面对需求变更时,可以灵活地实例化不同的对象
反射的缺点:
- 破坏类的封装性:可以强制访问private修饰的信息
- 性能损耗:反射相比直接实例化对象、调用方法、访问变量、中间需要很多检查步骤以及解析步骤,JVM无法对其进行优化
反射总结
- 反射的思想:反射就想一面镜子,只有在运行时才能看到自己是谁,可以获取到自己的信息,甚至实例化对象
- 反射的作用:在运行时才确定实例化对象,使得程序更加的健壮,面对需求变更时,可以最大程度地做到不修改程序源码的情况下,应对不同的场景,实例化不同类型的对象。
- 反射的应用场景:Spring的IOC容器;反射+工厂模式,使得工厂类更加稳定;JDBC连接数据库时加载驱动类
- 反射的三个特点:增加程序的灵活性、破坏类的封装性以及性能损耗
最后
- 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持
- 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
- 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。
转载:https://blog.csdn.net/issunmingzhi/article/details/108342487