Java – 反射学习笔记

1、Java内存分析。

2、类的加载过程。

3、类的加载与ClassLoader的理解。

4、类在什么会初始化?

测试会用到的类

    static class A {
        static int m = 1000;
        static {
            System.out.println("A类的静态代码块执行了");
            m = 100;
        }
        public A() {
            System.out.println("A类的无参构造执行了");
        }

        static void test() {
            System.out.println("A类中的静态方法");
        }
    }

    static class B extends A {

        public static final String M = "10000";

        static {
            System.out.println("B类的静态代码块执行了");
        }

        public B() {
            System.out.println("B类的无参构造执行了");
        }

        static int mm = 100;
    }

4.1 类的主动引用(一定会发生类的初始化)

  • 当虚拟机启动,先初始化main方法所在的类

效果

  • new了一个类对象

效果

  • 调用类的静态成员(除了final常量)和静态方法

效果

  • 调用 java.lang.reflect包的方法对类进行反射调用

效果

  • 当初始化一个类,如果其父类没有被初始化,则会先初始化它的父类

效果

4.2 类的被动引用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化。

效果

可见,B类的静态代码块并没有被初始化

  • 通过数组室外父类引用,不会触发此类的初始化。
  • 引用常量不会触发眩类的初始化(常量在连接阶段就存入调用类的常量池中了)

效果

5、类加载器的作用

  • 类加载的作用:将class文件字节码内容加载到内容中,并将这些静态数据转换成方法区的运行时数据 结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中数据的访问入口。
  • 类缓存:标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象。

5.1 类加载器的分类:

  • Bootstrap classLoader(启动类加载器):用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取。
  • PlatformClassLoader(扩展类加载器):Bootstrap classLoader的子类,负责jre/lib/ext目录下的jar包或 -D java.ext.dirs指定目录下的jar包装入工作库。
  • AppClassLoader(应用程序类加载器):PlatformClassLoader的子类,负责java -classpath 或 -D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。我们自定义的类就是使用该加载器来加载的。

5.2 获取应用程序类加载器可以加载的路径

        String property = System.getProperty("java.class.path");
        System.out.println(property);

5.3 双亲委派机制

当一个类收到了类的加载请求,它首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所以的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器来会尝试自己去加载。

优势:

  • 沙箱安全机制:比如自己写的String.class不会被加载,这个可以防止系统类被随意篡改。
  • 避免类的重复加载:当父类ClassLoad已经加载的时候,就不需要子类加载器再加载一次。

6、获取类运行时的完整结构

Field、Method、Constructor、Superclass、Interface、Annotation

测试类:

package com.company;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class User {
    public int version = 1;
    private int id;
    private String userNAME = "用户";
    private String password;


    private User() {

    }

    public User(int id, String userNAME, String password) {
        this.id = id;
        this.userNAME = userNAME;
        this.password = password;
    }

    public int getId() {
        return id;
    }

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

    public String getUserNAME() {
        return userNAME;
    }

    public void setUserNAME(String userNAME) {
        this.userNAME = userNAME;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userNAME='" + userNAME + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

6.1 Field

  • 获得本类所有public,不包含父类
        // 获得本类的所有public属性,不包含父类的属性。
        Field[] fields = userClass.getFields();
 
        for (Field field : fields) {
            System.out.println(field);
        }
       /*
        public int com.company.User.version
         */
  • 获得本类所有属性,不包含父类
        // 获得本类的所有属性,不包含父类的属性。
        Field[] declaredFields = userClass.getDeclaredFields();

        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        /*
            public int com.company.User.version
            private int com.company.User.id
            private java.lang.String com.company.User.userNAME
            private java.lang.String com.company.User.password
         */

6.2 Method

  • 获得本类及父类的public方法
        // 获取本类及其父类的全部public方法
        Method[] methods = userClass.getMethods();
      
        for (Method method : methods) {
            System.out.println(method);
        }
  /*
            public static void com.company.User.main(java.lang.String[])
            public java.lang.String com.company.User.toString()
            public int com.company.User.getId()
            public void com.company.User.setId(int)
            public java.lang.String com.company.User.getPassword()
            public void com.company.User.setPassword(java.lang.String)
            public void com.company.User.setUserNAME(java.lang.String)
            public java.lang.String com.company.User.getUserNAME()
            public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            public final void java.lang.Object.wait() throws java.lang.InterruptedException
            public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
            public boolean java.lang.Object.equals(java.lang.Object)
            public native int java.lang.Object.hashCode()
            public final native java.lang.Class java.lang.Object.getClass()
            public final native void java.lang.Object.notify()
            public final native void java.lang.Object.notifyAll()
         */
  • 获得本类的所有方法,不包含父类的方法
 Method[] declaredMethods = userClass.getDeclaredMethods();

        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        /*
            public static void com.company.User.main(java.lang.String[])
            public java.lang.String com.company.User.toString()
            public int com.company.User.getId()
            public void com.company.User.setPassword(java.lang.String)
            public void com.company.User.setUserNAME(java.lang.String)
            public void com.company.User.setId(int)
            public java.lang.String com.company.User.getPassword()
            public java.lang.String com.company.User.getUserNAME()
         */

6.3 Constructor

  • 获得本类中public的构造器,不包含父类的构造器

        Constructor<?>[] constructors = userClass.getConstructors();

        for (Constructor<?> constructor : constructors) {
           System.out.println(constructor);
        }
        /*
            public com.company.User(int,java.lang.String,java.lang.String)
         */
  • 获得本类中所有的构造器,但不包含父类的构造器
       // 获得本类中所有的构造器,不包含父类的构造器
        Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();

        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);

        }
        /*
            public com.company.User(int,java.lang.String,java.lang.String)
            private com.company.User()
         */

7、有了Class对象,能做什么?

接下来会用到的User类:

public class User {
    public int version = 1;
    private int id;
    private String userNAME = "用户";
    private String password;


    private User() {

    }

    public User(int id, String userNAME, String password) {
        this.id = id;
        this.userNAME = userNAME;
        this.password = password;
    }

    public int getId() {
        return id;
    }

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

    public String getUserNAME() {
        return userNAME;
    }

    public void setUserNAME(String userNAME) {
        this.userNAME = userNAME;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userNAME='" + userNAME + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

Object invoke(Object obj, Objecty… args)

  • Object对象原方法的返回值,若原方法无法返回值,此时返回null
  • 若原方法若为静态方法,此时形参Object obj可以为null
  • 若原方法声明为private,则需要在调用此invoke()方法前,显式调用对象的setAccessible(true)方法,将可访问pivate的方法。

void setAccessible(boolean flag)

  • Method和Field、Constructor对象都有setAccessible()方法。
  • setAccessible作用是启动和禁用访问安全检查的开关。
  • 参数为true则指示反射的对象在使用时应该取消Java语言访问检查。(提高反射的效果:如果代码中必须用到反射,而该名代码需要频繁的被调用,那么请设置为true。关闭了安全检查之后,使得原本无法访问的私有成员也可以访问)
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。

7.1 创建类的对象:调用Class对象的newInstance()方法

1,类必须有一个无参数的构造器
2,类的构造器的访问权限需要足够

7.2 获取指定构造器并创建对象

1,通过Class类的getDeclaredConstructor(Class... parameterTypes)取得本类的指定形参类型的构造器,并将参数传递进去之后,才可以实例化操作。
2,向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3,通过Constructor实例化对象
package com.company;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class User {

    public static void main(String[] args) throws Exception {

        // 无参构造
        Class<User> userClass = User.class;
        User user = userClass.newInstance();
        System.out.println(user);

        // 有参构造
        Constructor<User> constructor = userClass.getConstructor(int.class, String.class, String.class);
        User user1 = constructor.newInstance(1, "年少有为", "root");
        System.out.println("user1 = " + user1);
    }
 ........


}

7.3 通过反射操作属性

7.4 通过反射调用方法

通过反射,调用类中的方法,通过Method类完成。

1,通过Class类的getMethod(String name, Class… parameterTRypes)方法取得一个Method对象,并设置此方法操作时所需要的类型参数。

2,之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

7.5 调用方法性能分析

7.5.1 普通方式调用

  User user = new User();


        // 普通方式调用
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getPassword();
        }
        System.out.println("普通方式调用:" + (System.currentTimeMillis() - t1 + "ms"));

7.5.2 反射方式调用


        User user = new User();
        // 反射方式调用
        Class<User> userClass = (Class<User>) user.getClass();
        Method getPassword = userClass.getDeclaredMethod("getPassword");
        long t2 = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getPassword.invoke(user, null);

        }
        System.out.println("反射方式调用:" + (System.currentTimeMillis() - t2 + "ms"));

7.5.3 反射方式调用,关闭安全检查

         User user = new User();
        // 反射方式调用,关闭安全检查
        Class<User> userClass2 = (Class<User>) user.getClass();
        Method getPassword2 = userClass.getDeclaredMethod("getPassword");
        getPassword2.setAccessible(true);
        long t3 = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getPassword2.invoke(user, null);

        }
        System.out.println("反射方式调用:" + (System.currentTimeMillis() - t3 + "ms"));

7.5.4 测试结果,仅供参考

发表回复

相关

浙ICP备2021031744号-3