反射

反射

在了解反射之前需要先了解一下什么是类加载器。

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,也没有通过反序列化。

什么是反射?

  反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

原文地址:https://www.cnblogs.com/xinzong/p/14479090.html