反射
在了解反射之前需要先了解一下什么是类加载器。
1.1 类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象,同时在堆中也会创建一个文件对象(也叫字节码文件对象,可以访问文件中所有信息),具有唯一性。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
1.2 类初始化时机
就是被加载到内存
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 这是要研究的
5. 初始化某个类的子类
6. 直接使用java.exe命令来运行某个主类
验证:
public class Person {
static int a=1;
static{
//静态代码块验证,有且第一次使用该类进内存的时候,只执行一次
System.out.println("Person类进内存了");
}
}
public class Demo01 {
public static void main(String[] args) {
//创建类的实例
//new Person();
//类的静态变量或者赋值
//System.out.println(Person.a);
//初始化某个子类,父类进内存 因为子类进内存,父类会被初始化
new Zi();
}
}
public class Zi extends Person {
}
1.3 类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象。
1.4 类加载器的组成
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类(系统内部类)的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包(引入类)的加载。
在JDK中JRE的lib目录下ext目录
System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
通过这些描述就可以知道我们常用的类,都是由谁来加载完成的。
到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?
这就是我们反射要研究的内容。
研究如何获得class文件对象,并获取里面的内容
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
获取Class对象的三种方法
public class Person {
private int age;
public String name;
public Person(){
System.out.println("public Person()");
}
private Person(String name){
System.out.println("Person(String name)");
}
public void eat(){
System.out.println("public void eat()");
}
private void sleep(String name){
System.out.println("private void sleep(String name)");
}
}
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.通过Object中的getClass方法获取
Person p=new Person();
Class c=p.getClass();
System.out.println(c);
//2.通过class属性获取
Class c2=Person.class;
System.out.println(c2);
//因为是同一个class文件对象,所以地址相同
System.out.println(c==c2);
//因为是同一个class文件对象,所以地址相同,内容也相同
System.out.println(c.equals(c2));
//3.通过forName获取
//JDBC加载链接数据库也用此方法
Class c3=Class.forName("com.oracle.demo02.Person");//输入完整的包名+类名,不能只写类名
System.out.println(c3);
}
}
注意:第三种和前两种的区别
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
返回多个构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//获取字节码文件对象
Class c=Class.forName("com.oracle.demo02.Person");
//获取公共构造方法
Constructor con=c.getConstructor();
System.out.println(con);
//创建对象 向下转型
//通过class对象调用Instance()只能使用无参构造方法
Person p=(Person)con.newInstance();
p.eat();
}
}
这个过程通过反射获取对象,没有通过new,也没有通过反序列化。
什么是反射?
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。