天天看点

尚硅谷_初级_反射(636-661)动态代理(662-663)

https://www.bilibili.com/video/BV1Kb411W75N?p=636

一:java 反射 机制概述

    1.反射(Reflecttion):

        反射 被视为 动态语言 的关键,反射机制 允许程序 在执行期,借助于 Reflection API,

        取得任何 类的 内部信息,并能直接操作 任意对象的 内部属性及方法。

    2.反射:

        加载完类之后,在堆内存的方法区中,就产生一个 class 类型的对象,这个 class 对象,

        包含了 类的完整信息,可以通过这个 class 对象,看到类的结构。

    3.反射相关主要 API:

        Method:方法

        Field:代表类的成员变量

        Constructor:代表类的构造器

    面试:

        1)通过new方式 或者 反射方式都可以调用公共类结构,开发中到底用哪个?

            建议:用 new

            动态的创建,会用到反射

        2)反射机制 与 面向对象中的封装性,是不是矛盾的?如何看待两个技术?

            不矛盾,封装性:不建议,也不能够,调用私有的属性和方法。

                    反射:虽然不建议,但是可以调用的。

二:理解 Class 类,并 获取 Class 实例

    1.理解 Class 类

        1)类加载过程:

            程序经过 javac 命令后,会生成一个字节码文件(.class),结尾,

                接着使用 java 对字节码文件,进行解释运行。

                就相当于 把字节码对象加载到内存中。此过程,称为类的加载。

                加载到内存中的类,我们就称为运行时类,此运行时类,就称为 一个 Class 实例。

    2.Class 实例的获取:

        注意:

            Class<? extends Person> b

            Class 类,加上泛型限制,避免了 之后在使用(newInstance),再去强转。

        1)方式一:

            Class<Person> a = Person.class;

        2)方式二:

            Person p1 = new Person();

            Class<? extends Person> b = p1.getClass();

        3)方式三:

            Class<?> c = forName("com.classloader.Son");

        4)方式四:(了解,使用类加载器)

            //获取 ClassLoader 类加载器

            ClassLoader classLoader = Test1.class.getClassLoader();

            Class<?> d = classLoader.loadClass("com.classloader.Son");

        注意:a,b,c,d 指向同一个对象。

    3.哪些类型 可以有 Class 对象

        1)class:类(外部类,内部类)

        2)interface:接口

        3)[]:数组

        4)enum:枚举

        5)annotation:注解@interface

        6)基本数据类型

        7)void

    4.只要 数组的 元素类型 与 维度 一样,就是同一个 class

        元素类型:int,String,Byte。

        维度:一维数组,二维数组。

三:类的加载 与 ClassLoader 的理解

    1.类的加载过程:

        1)类的加载(Load):

            java 命令,将 class 文件,加载到内存,并将这些静态数据,

            转换成方法区的,运行时数据结构。并为之创建一个 Class 实例。

            此过程由类加载器完成。

        2)类的链接(Link):

            将 Java 类的二进制代码,合并到 JVM 的运行状态之中的过程。

                验证:确保加载类信息符合 JVM 规范

                准备:为 类变量(static),分配内存,并设置类变量默认初始值,在方法去中分配。

                解析:虚拟机常量池内 的符号引用(常量名),替换为直接引用(地址)的过程。

        3)类的初始化(Intialize):

    2.类加载顺序:

        1)先为 父类 类变量(static),分配空间,然后赋值。

        2)再为 子类 类变量(static),分配空间,然后赋值。

        3)开始创建对象,子类构造方法,为 子类 和 父类 本地变量分配内存地址,

        4)父类本地变量先赋值,在执行父类构造方法。

        5)子类本地变量先赋值,在执行子类构造方法。

        6)创建子类对象完毕。

        7)虚拟机会保证,类的初始化,在多线程环境下,被正确 加锁 和 同步

    3.类加载器(ClassLoader)的理解    

        1)ClassLoader:类加载器的作用,是把类(class文件),加载到内存中, 

        2)读取配置文件,可以使用流,也可以使用 类加载器 读取。

            示例:

                ClassLoader classLoader = ClassLoader_01.class.getClassLoader();

                InputStream ras = classLoader.getResourceAsStream("a.properties");

                            --目录默认为 :src 下

            相当于:

                FileInputStream fileInputStream1 = new FileInputStream(new File(""));

四:创建 运行时 类的对象

    1.根据字节码对象,创建对象

        Person person = Person.class.newInstance();

    2.本质:

        本质还是调用 Person 类的无参构造 方法。

        如需要创建成功:

            1)对象有无参构造方法

            2)无参构造方法,权限够。

    3.练习:写一个方法,传入字节码对象,创建对应的对象实例并返回。

五:获取 运行时 类的完整结构(获取私有的:Declared)

    1.获取类 结构信息

        1).获取所有属性:(包含父类)

            Field[] fields = Person.class.getFields();

        2).获取所有方法:(包含父类)

            Method[] methods = Person.class.getMethods();

        3).获取所有构造方法:(包含父类)

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

    2.所获得都是数组,可以使用 增强循环 遍历结果。

        每遍历一次,获得一个实例对象,在其中也可以获取信息。

    3.获取 Person 类,父类字节码对象

        Class<? super Person> superclass = Person.class.getSuperclass();

        Type genericSuperclass = Person.class.getGenericSuperclass();

    4.获取 类 的 接口,所在包,注解等。

六:调用 运行时 类的指定结构

    1.调用属性:

        Person person = Person.class.newInstance();

        Field age = Person.class.getDeclaredField("age");

        //为属性赋值

        age.set(person, 12);

        //获取属性值

        Object o = age.get(person);

    2.调用方法:

        1.普通方法:

            Class<Person> p = Person.class;

            Person person = p.newInstance();

            //获取指定方法                    -- 方法名    参数类型

            Method method = p.getDeclaredMethod( "setName" , String.class );

            //调用指定方法:    --person 调用对象。传入参数

            //invoke() 返回值就是,调用方法返回值。

            Object invoke = method.invoke( person , "123" );

            System.out.println( person.toString() );

        2.静态方法:

            Object o = method.invoke( Person.class , String.class );

    3.调用构造方法:

        Constructor<Person> dc = 

                Person.class.getDeclaredConstructor( String.class , Integer.class );

        Person zhangsan = dc.newInstance( "zhangsan" , 12 );

    4.调用时,碰见私有方法 

        clazz.setAccessible(true);

七:反射的应用:动态代理

    1. 静态代理:

        1)实现接口的静态代理,在调用方法前后,加上增强功能。

        2)特点:代理类 和 被代理类,在编译期间,就被确定下来。

    2.动态代理举例:

        1)用 反射 实现动态代理。

        2)

    3.反射的应用:AOP

    4.