Java代码的生命周期
Java代码在计算机中经历的阶段:Source源代码阶段、Class类对象阶段、RunTime运行时阶段。
Source源代码阶段: 这个阶段是由程序员编写生成源代码,再由Javac编译器生成class文件。
Class类对象阶段:由类加载器将class文件加载到JVM内存中,并对class文件进行解析生成Class对象。
Runtime运行时阶段:由class对象创建对应的类对象。
反射的概念
正射与反射
正射:一般情况下,我们创建对象时就知道需要创建什么类,所以直接new一个对应类型的对象。
Car car = new Car();
反射:在创建对象时,并不知道对象类型。需要一定的手段(下文中获取class对象的方式)去找到其类型,再将该类型分装成class对象,class对象根据找到的类型再去创建对应类型的对象。
分析Java代码的阶段时,ClassLoader将class文件加载到内存,并且创建class对象,将原本class文件的各个部分都封装成对象。后面在运行时通过class对象及其内部的其他对象去生成最后程序真正所需要的对象。这个过程就是反射。
总结:
正射是先创建对象,再访问类信息。
反射是获取信息,再创建对象。
反射的优点
1.可以在程序运行的过程中,动态的获取类信息,并进行操作。比较灵活。
比如Idea的提示功能,Idea之所以能够提示类中的各种信息,就是通过反射获取到了类信息。
2.可实现解耦,增强程序的可扩展性。
通过反射机制,可以动态的获取类信息,并进行操作。在庞大的项目中,面对复杂的需求,利用反射机制可以做到不修改源代码的情况下,就可以实现不同的功能。
比如启动Tomcat服务器时,用户修改了配置文件中的端口号,那么服务器就可以使用用户指定的端口号。这就是将配置文件与Tomcat程序进行解耦。
反射的使用过程
1.查找类信息。
类信息可以是字节码文件、加载到内存中的类信息(解析了字节码文件之后的信息)、对象(对象中包含着类的详细信息)。
2.根据类信息创建Class对象。
3.通过Class对象动态的获取类信息,甚至操作类信息。
Class对象的获取方式
使用反射的核心就是获取到Class对象。Java是面向对象的编程语言,反射根据类信息创建Class对象,而想要动态的将类信息获取出来就属于Class对象的职能了。
1.Class.forName(String className)
这种方式是通过Class类获取的。
这种方式是在Source源代码阶段,根据className找到对应的class文件,在文件中获取到类信息,将其信息的各个组成部分封装成对象。
类的各个组成部分:成员变量、成员方法、构造器、静态资源等。
使用场景:多用于读取配置文件来加载类信息,可以将类名在文件中进行配置。
2.类名.class
这种方式是通过类名的属性来获取的。
这种方式是在Class类对象阶段,根据已经记载到内存中的类信息就能知道类名,再根据类名的class属性获取Class对象。
使用场景:多用于参数传递。
3.对象名.getClass()
这种方式是根据对象的getClass()方法获取的,getClass()方法是Object提供的。
这种方式是在Runtime运行时阶段,根据已经存在的对象实例来获取其Class对象。
使用场景:多用于通过对象来获取字节码的方式。
扩展:不管使用哪一种方式获取class对象,都是同一个对象。
因为字节码文件(*.class)在程序运行过程中只会被加载一次,只会创建一个class对象。
结论验证:
//获取class对象的方法
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName(String className)
Class clazz1 = Class.forName("reflectdemo1.Car");
//2.类名.class
Class clazz2 = Car.class;
//3.对象名.getClass()
Class clazz3 = new Car().getClass();
System.out.println(clazz1 == clazz2);
System.out.println(clazz3 == clazz2);
System.out.println(clazz1 == clazz3);
}
}
输出:
true
true
true
Class对象的功能
Class对象可以让我们获取类的所有信息,包括但不限于类中定义的成员方法、成员变量、静态资源、所属包等等。接下来介绍一下Class的API(主要的)。
1.获取成员变们
Field[] getFields()
Field getField(String name)
Field[] getDeclaredFields()
Field getDeclaredField(String name)
2.获取构造方法们
Constkuctor<?>[] getConstructors()
Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Constructor<?>[] getDeclaredconstructors()
3.获取成员方法们
Method[] getMethods()
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(string name, 类<?>... parameterTypes)
4.获取类名
string getName()
获取到对应的信息可以在根据其API进行操作。
反射使用案例
需求:实现一个框架,使其能够动态的创建指定类型的对象并执行指定类型的方法。
代码实现:
//框架:动态的创建对象,并执行相应的方法;
public class Test {
public static void main(String[] args) throws Exception {
//1.读取配置文件
//1.1创建Properties对象
Properties properties = new Properties();
//1.2读取class目录下的配置文件
ClassLoader classLoader = Test.class.getClassLoader();
//1.3获取配置文件的输入流
InputStream is = classLoader.getResourceAsStream("application.properties");
//1.4将配置文件加载到properties中
properties.load(is);
//1.5读取配置文件中的内容
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//2.获取需加载类的Class对象
Class<?> cls = Class.forName(className);
//3.创建对象
Object obj = cls.newInstance();
//4.执行方法
//4.1获取需要执行的方法
Method method = cls.getDeclaredMethod(methodName);
method.invoke(obj);
}
}
框架中读取的application.properties配置文件如下:
className = model.Student
methodName = sleep
运行结果:
sleep....