反射

反射

先简简单单的说下类的加载

类的加载

在程序运行后,首次使用某个类,会把这个类的字节码文件读取到内存,并且会将这个类的所有信息存到一个Class对象中。

简单步骤:

​ 1:jvm执行代码,例如读到 new Student()

​ 2 : 读取Student的字节码文件

​ 3:把字节码文件的内容(读取成字节码对象)存储到方法区(以前的说法),1.8以后叫做元空间(貌似)

​ 4:最后类加载完成以后,在堆空间创建内存空间。

类的加载时机(下面六种)

  1. 创建类的实例。(new 一个类)
  2. 类的静态变量,或者为静态变量赋值。
  3. 类的静态方法。
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
  5. 初始化某个类的子类。
  6. 直接使用java.exe命令来运行某个主类。(编译 javac 类名.java, 运行 java.类名)

今天的反射也属于类加载时机的一种

先上代码:

public class Student {
    public static  int thinging;
    private int id;
    private String name;

    public  static void  get(){

    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    static {
        System.out.println("使用了静态代码块");
    }
}

类加载时机代码演示


  /**
     * //        1. 创建类的实例。 静态代码块随着类的加载而加载,并且只执行一次。
     */
    @Test
    public void checkNew(){
     
        new Student();
    }

    /**
     *
     *  2. 类的静态变量,或者为静态变量赋值。
     */
    @Test
    public void checkStaticFiled(){
     
        Student.thinging=1;
    }

    /**
     * 3. 类的静态方法。
     */
    @Test
    public void checkStaticMethod(){
     
        Student.get();
    }

    /**
     * 4 反射  通过 Class.forName("全类名") 进行反射
     */
    @Test
    public void checkReflect(){
     
        try {
     
            Class.forName("Student");
        } catch (ClassNotFoundException e) {
     
            e.printStackTrace();
        }
    }

    /**
     *  5.通过子类实例
     */
    @Test
    public void checkExtends(){
     
       new Reflect(); //这个类继承了Student
   }

运行结果

反射_第1张图片

都证明了这个stu类被加载了。

类加载器(暂时先了解了解)

下面说说类加载器,类加载器是负责将实际空间中的某个class文件读取到内存中并且生成Class的对象。

Java中有三种类加载器,它们分别用于加载不同种类的class:

Java中有三种类加载器,它们分别用于加载不同种类的class:

  • 启动类加载器(Bootstrap ClassLoader):用于加载系统类库\bin目录下的class,例如:rt.jar。

  • 扩展类加载器(Extension ClassLoader):用于加载扩展类库\lib\ext目录下的class。

  • 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。

    进去代码看看

    我们是如何获得类加载器的

反射_第2张图片

在点进getClassLoader()方法看看jdk是怎么描述的

反射_第3张图片

图中看到,一些实现可能使用null来表示引导类加载器,可能在此类中的实现返回null.

举个例子。

反射_第4张图片

好了,接下来该回到反射的主题

反射的概念

它一种机制,利用这个机制就可以在程序中对类进行解析,并且可以操作类中所有成员(变量,方法,构造方法)。

使用反射需要一个前提,获取需要反射的那个类的字节码对象。

Class对象的获取方式

  • 通过类名.class获得
  • 通过对象名.getClass()方法获得
  • 通过Class类的静态方法获得: static Class forName(“类全名”)

上代码

/**
 * 通过类名.class来获取。
 */
@Test
public void getClassByDotClass(){
     
    Class<Student> studentClass = Student.class;
    try {
     
        studentClass.newInstance();
    } catch (InstantiationException e) {
     
        e.printStackTrace();
    } catch (IllegalAccessException e) {
     
        e.printStackTrace();
    }
}

/**
 *
 * 通过对象名.getClass()来获取
 */
@Test
public void getClassByGetClass(){
     
    Student student = new Student();
    student.getClass();
}

/**
 *
 * 通过Class的静态方法ClassForName("全类名")
 */
@Test
public void getClassByForName(){
     
    try {
     
        Class.forName("Student");
    } catch (ClassNotFoundException e) {
     
        e.printStackTrace();
    }
}

Tips:每个类的Class对象都只有一个

@Test
public void isTheSame(){
     
    try {
     
        System.out.println(Student.class==Class.forName("Student"));
    } catch (ClassNotFoundException e) {
     
        e.printStackTrace();
    }
}

反射_第5张图片

Class类常用的方法

String getSimpleName(); 获取类名字符串
String getName(); 获取全类名字符串


    /**
     * 
     * 获取类名:getSimpleName()
     * 获取全类名:getName()
     */
    @Test
    public void ClassApi(){
     
        Class<Student> studentClass = Student.class;
        System.out.println("Class的类名是:"+studentClass.getSimpleName());
        System.out.println("Class的全类名是:"+studentClass.getName());
    }

反射_第6张图片

创建类的对象

1.T newInstance(): 创建Class对象关联类的对象。
/**
 *
 * T newInstance() ;  创建Class对象关联类的对象
 */
@Test
public void getStudentByInstance(){
     
    try {
     
        Student student = Student.class.newInstance();
    } catch (InstantiationException e) {
     
        e.printStackTrace();
    } catch (IllegalAccessException e) {
     
        e.printStackTrace();
    }
}
2.通过Constructor类来创建对象

首先要获取构造器类

Class类中与Constructor相关的方法

  1. Constructor getConstructor(Class… parameterTypes)
    * 根据参数类型获得对应的Constructor对象。

    反射_第7张图片

            getConstructor()的源码描述
            ```Java
         
            @CallerSensitive
            public Constructor getConstructor(Class... parameterTypes)
                throws NoSuchMethodException, SecurityException {
                checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
                return getConstructor0(parameterTypes, Member.PUBLIC);
            }
            
            ```
    
            上个使用的代码
    
            ```Java
            /**
             * 先用getConstructor() 获取Constructor类对象
             * 再通过这个对象实例T。 没有参数的用法
             * @throws Exception
             */
            @Test
            public void getPublicConstructorWithoutParameter() throws Exception {
                Class studentClass = Student.class;
                //先获取无参构造类对象。
                Constructor constructor=studentClass.getConstructor();
                //再通过这个实例方法,实例这个类
                Student student = constructor.newInstance();
               
            
            }
            /**
             * 先用getConstructor() 获取Constructor类对象
             * 再通过这个对象实例T。 有参数的用法
             * @throws Exception
             */
            @Test
            public void getPublicConstructor() throws Exception {
                Class studentClass = Student.class;
                //获取有参构造类
                Constructor constructor=studentClass.getConstructor(int.class);
                //输入参数实例化Student类
                Student student = constructor.newInstance(1);
               
            }
            
        
    
    
            如图所述,如果传入了没有相关参数重载的构造器,会报NoSuchMethodException异常
    
  2. Constructor getDeclaredConstructor(Class… parameterTypes)

    * 根据参数类型获得对应的Constructor对象
    * 可以是public、protected、(默认)、private修饰符的构造方法。
    * 如果是被protected、(默认)、private修饰符修饰的构造器,需要确定本身能否访问到,如果不能访问到就要设置权限,用setAccessible(true)。
    
    /**
     * 暴力获取构造器,能获取包括public protected  default private
     * 通过getDeclaredConstructor()暴力获取Student类中除了public其他修饰符的构造器,但这个构造类对象需要设置权限,setAccessible(true)
     * 具体要不要设置,得看这个修饰符本身能否访问到相关资源。
     */
    @Test
    public void getAnotherAccessConstructor() throws Exception {
           
            Class<Student> studentClass = Student.class;
            Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class);
            constructor.setAccessible(true);
            Student student = constructor.newInstance("PETER");
            System.out.println(student);
        }
    
    

    反射_第8张图片

    图上是个反例。

  3. Constructor[] getConstructors()
    获得类中的所有构造方法对象,只能获得public的

    上代码:

    /**
     *
     * 通过getConstructors()获取所有public修饰的构造器类对象。
     *
     */
    @Test
    public void getAllPublicConstructor(){
           
        Class<Student> studentClass = Student.class;
        Constructor<Student>[] constructors = (Constructor<Student>[]) studentClass.getConstructors();
        for (Constructor<Student> constructor : constructors) {
           
            System.out.println(constructor);
        }
    }
    

    遍历打印的结果:

在这里插入图片描述

  1. Constructor[] getDeclaredConstructors()
    获得类中的所有构造方法对象

    可以是public、protected、(默认)、private修饰符的构造方法。
    
    /**
     *
     * 通过getDeclaredConstructors()获取所有p修饰的构造器类对象。
     *
     */
    @Test
    public void getAllAccessConstructor(){
           
        Class<Student> studentClass = Student.class;
        Constructor<Student>[] constructors = (Constructor<Student>[]) studentClass.getDeclaredConstructors();
        for (Constructor<Student> constructor : constructors) {
           
            System.out.println(constructor);
        }
    }
    

    遍历打印的结果:

反射_第9张图片

 ####   获取成员方法

 接下来的部分就不放代码演示了,就提供下api。

 1 **Method getMethod(String name,Class...args);**

 ​	方法名和参数类型获得对应的成员方法对象,只能获得public的

 2 **Method getDeclaredMethod(String name,Class...args);**    

 ​	根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的

 3 **Method[] getMethods();**

 ​	获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的

 4**Method[] getDeclaredMethods();**    

 ​	获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的

 #### 通过反射访问成员变量

 Method对象常用方法
 *  **Object invoke(Object obj, Object... args)**
     * 调用指定对象obj的该方法
     * args:调用方法时传递的参数
 *  void setAccessible(true)
     设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消

 #### 通过反射获取类的成员变量

 Class类中与Field相关的方法
 * **Field getField(String name);**
     *  根据成员变量名获得对应Field对象,只能获得public修饰
 * **Field getDeclaredField(String name);**
     *  根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
 * **Field[] getFields();**
     * 获得所有的成员变量对应的Field对象,只能获得public的
 * **Field[] getDeclaredFields();**
     * 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的



 #### 通过反射访问成员变量

 Field对象常用方法
 **void  set(Object obj, Object value)** 
 **void setInt(Object obj, int i)** 	
 **void setLong(Object obj, long l)**
 **void setBoolean(Object obj, boolean z)** 
 **void setDouble(Object obj, double d)** 

 **Object get(Object obj)  
 int	getInt(Object obj)** 
 **long getLong(Object obj)** 
 **boolean getBoolean(Object ob)**
 **double getDouble(Object obj)** 

 **void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。**
 **Class getType(); 获取属性的类型,返回Class对象。**



 今天的学习就到这里了,如果有理解的错误地方,请各位大佬提醒我哦。

你可能感兴趣的:(JAVA基础)