编译期和运行期
首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行,
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
使用反射
正常调用:
java
Apple apple = new Apple();
apple.setPrice(5);
反射调用
从这个简单的例子可以看出,一般情况下我们使用反射获取一个对象的步骤:
1、获取类的 Class 对象实例
java
Class clz = Class.forName("com.zhenai.api.Apple");
2、根据 Class 对象实例获取 Constructor 对象
java
Constructor appleConstructor = clz.getConstructor();
3、使用 Constructor 对象的 newInstance 方法获取反射类对象
java
Object appleObj = appleConstructor.newInstance();
而如果要调用某一个方法,则需要经过下面的步骤:
1、获取方法的 Method 对象
java
Method setPriceMethod = clz.getMethod("setPrice", int.class);
2、利用 invoke 方法调用方法
java
setPriceMethod.invoke(appleObj, 14);
class 类对象的三种实例化模式
Class类对象的三种实例化模式
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
Class类对象的三种实例化模式:
反射原理
1.java文件被编译成class文件
当我们编写完一个Java项目之后,每个java文件都会被编译成一个.class文件。
2.class文件加载到JVM
Java程序在运行时,首先需要将需要的类加载到内存中。
这些class文件在程序运行时,会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中自动产生一个Class对象,用于描述该类的信息。
3.获取类的Class对象
Java反射的第一步是获取需要操作的类的Class对象,如下所示:
java
// 获取Class对象
Class<?> clazz = Class.forName("com.mikechen.MyClass");
Class对象是Java反射的核心,它包含了该类的所有信息,包括构造函数、方法、属性等。
4.获取类的构造函数、方法和属性
在获取到Class对象之后,可以通过反射获取类的构造函数、方法和属性等信息。
java
// 获取Person类的Class对象
Class<?> personClass = Class.forName("Person");
// 创建一个Person对象
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
Object person = constructor.newInstance("Alice", 25);
// 获取name属性并且输出它的值
Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true);
String name = (String) nameField.get(person);
System.out.println(name);
// 设置name属性的值
nameField.set(person, "Bob");
System.out.println(((Person) person).getName());
// 获取sayHello方法并且调用它
Method sayHelloMethod = personClass.getDeclaredMethod("sayHello");
sayHelloMethod.invoke(person);
这样我们就可以通过Java反射获取到类的所有信息了。
5.Java反射方法
Java反射方法常见的有:
getConstructors():获取类的所有public构造函数;
getDeclaredConstructors():获取类的所有构造函数,包括public和非public;
getMethods():获取类的所有public方法,包括从父类继承而来的方法;
getDeclaredMethods():获取类的所有方法,包括public和非public;
getFields():获取类的所有public属性,包括从父类继承而来的属性;
getDeclaredFields():获取类的所有属性,包括public和非public;
反射的优缺点
1、优点:
在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2、缺点:
(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
反射的用途
1、反编译:.class-->.java
2、通过反射机制访问java对象的属性,方法,构造方法等
3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
5、例如,在使用Strut2框架的开发过程中,我们一般会在struts.xml里去配置Action,比如
java
<action name = "login" class="com.xiazi.test.LoginAction">
<result>login.jsp</result>
</action>
比如我们请求login.action时,那么StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,从action中查找出name为login的Action,并根据class属性创建SimpleLoginAction实例,并用Invoke方法来调用execute方法,这个过程离不开反射。配置文件与Action建立了一种映射关系,当View层发出请求时,请求会被StrutsPrepareAndExecuteFilter拦截,然后StrutsPrepareAndExecuteFilter会去动态地创建Action实例。
比如,加载数据库驱动的,用到的也是反射。
java
Class.forName("com.mysql.jdbc.Driver");
类的加载时机
1、静态加载
当新创建一个对象时(new),该类会被加载;
当调用类中的静态成员时,该类会被加载;
当子类被加载时,其超类也会被加载;
2、动态加载
通过反射的方式,在程序运行时使用到哪个类,该类才会被加载;
反射应用场景
1.动态代理
使用反射可以在运行时动态地创建代理类,这在实现AOP(面向切面编程)时非常有用。
2.注解处理器
使用反射可以在运行时动态地解析注解,并执行注解所定义的操作,例如生成代码或加载配置文件。
3.依赖注入
使用反射可以在运行时动态地实例化对象,并将其注入到其他对象中,从而实现依赖注入的功能。
4.反射工厂
使用反射可以在运行时动态地创建对象,并执行对象的方法,这对于编写通用代码非常有用。