Java学习-12-韩顺平老师

Java-反射机制01

目录:

01-反射机制02-Class类
03-类加载

反射机制

基本介绍:

1.反射机制允许程序在执行期间借助ReflectionAPI取得任何类的内部信息
(比如成员变量,构造器,成员方法等),并能操作对象属性及方法。反射在设 计模式和框架底层都有用。
2.加载类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class
对象
),这个对象包含类的完整信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过镜子可以看到类的结构,所以形成的称之为反射

反射相关的主要类:

1.java.lang.Class:代表一个类,Class对象表示某个类加载在堆中的一个对象。
2.java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法。
3.java.lang.reflect.Field:代表类的成员变量,Filed对象表示某个类的成员变量。
4.java.lang.reflect.Constructor:代表类的构造方法。

反射的优缺点:

1.优点:可以动态创建和使用对象(也是框架底层的核心),使用灵活,没有反射机制,框架技术就失去了底层支撑。
2.缺点:使用反射机制,解释执行,对执行速度有影响。

反射机制调优:

1.Method和Filed、Constructor对象都setAccessible方法。
2.setAccessible作用是启动和禁止访问安全检查开关
3.参数ture表示反射对象在使用时取消访问检查,提高反射效率。

public class SetAccessible_ {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //使用普通方法调用对象方法
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            new Car();
        }
        long end = System.currentTimeMillis();
        System.out.println("普通方式使用时长:"+ (end-start)); // 10

        // 使用反射机制调用方法
        Class<?> cls = Class.forName("com.zcc.class_.Car");
        Object o = cls.newInstance();
        Method cry = cls.getMethod("cry");
        start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            cry.invoke(o);
        }
        end = System.currentTimeMillis();
        System.out.println("普通方式使用时长:"+ (end-start)); // 219

        // 使用setAccessible() 可以提升一点速度
        start = System.currentTimeMillis();
        cry.setAccessible(true);
        for (int i = 0; i < 90000000; i++) {
            cry.invoke(o);
        }
        end = System.currentTimeMillis();
        System.out.println("普通方式使用时长:"+ (end-start)); // 93

        // 上述运行数据根据电脑不同也不一定相同,但是setAccessible来提升
    }
}
class Car{
    public void cry(){}
}

Class类

基本介绍:

1.Class也是类,因此也继承了Object类。
2.Class类对象不是new出来的,而是系统创建的。
3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次。
4.每个类的实例都会记得自己来自哪一个Class实例所生成。
5.通过Class对象可以得到一个类的完整结构,通过一系列API。
6.Class对象是存放在堆中的。
7.类的二进制数据,是存放在方法区的,有的地方成为类的数据元。

以下类型有Class对象:

1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类。
2.interface:接口
3.数组
4.enum:枚举
5.annotation:注解
6.基本数据类型
7.void

类加载

基本介绍:

反射机制是java实现动态语言的关键,也就是通过反射实现类的动态加载。
1.静态加载:编译时加载相关的类,如果没有则 报错,依赖性太强。
2.动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。

类加载时机:

1.当创建对象时(new) 静态加载
2.当子类被加载 静态加载
3.调用类的静态成员时 静态加载
4.通过反射 动态加载

静态加载和动态加载演示:

public class DynamicLoad {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Scanner scanner = new Scanner(System.in);
        String next = scanner.next();
        switch (next){
            case "1":
                // 如果没有Dog类,在编译的时候就会报错,没有找类
                // 因为这个是静态加载,在new的时候就要加载类,
                // 发现找不到类,就会报错。
                Dog dog = new Dog();
                break;
            case "2":
                // 使用静态方法,获取类,在编译的时候可以通过
                // 只有执行到这里的代码才会报错,因为在编译的时候不需要加载类
                Class<?> dog1 = Class.forName("Dog");
                Object o = dog1.newInstance();
                break;
        }
    }
}

Java程序执行的三个阶段示意图:

安卓如何给classJava的反射动态添加一个对象_System

类加载的各个阶段:

安卓如何给classJava的反射动态添加一个对象_反射机制_02

加载(Loading)阶段:

JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至是网络)转换为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象

连接(Linking)阶段
  验证(Verification):

1.目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
2.包括:文件格式验证(是否oxcafebabe开头)、元数据验证,字节码验证和符号引用验证。
3.可以考虑使用-Xverify:none参数来关闭大部分类验证措施,缩短虚拟机类加载的时间。

  准备(Preparetion):

JVM会在该阶段对静态变量,分配内存默认初始化(对应数据类型的默认初始值,
,如0,0L,null,false)等.这些变量所使用的内存在方法区中进行分配。

  解析(Resolution):

虚拟机将常量池内的符号引用替换为直接引用的过程。

初始化(Initialization)阶段:

1.到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程。
2.()方法是由编译器按语句在文件中的出现书匈奴,依次自动收集类的中所有静态变量的赋值动作和静态代码块中的语句,并进行整合。
3.虚拟机会保证一个类的()方法在线程中被正确的加锁,同步,如果多个线程同时初始化一个类,那么只会有一个线程执行这个类的()方法,其他线程堵塞等待。

演示初始化内容的顺序

public class Initialization {
    public static void main(String[] args) {
        new Person();
    }
}
class Person{
    static int num = 10;
    static final int num1 = 100;
    static {
        num = 100;
    }
    // 对于上诉代码的初始化
    // 在Preparation准备阶段默认初始化,会初始化num为 0
    // 但是final修饰的 num1 = 100,因为final修饰,一旦初始化就不会在改变
    // 直接给num1赋值,进行初始化。
    // 到达Initialization初始化阶段,会将上述代码按顺序整合,所以num = 10
    // 直接被整合掉,上述代码变成
    /*
    static final int num1 = 100;
    static {
       num = 100;
    }
    */
}