• java基础巩固第四天


    访问字段

    1.对于任意一个object实例,只要我们获取了它的Class,就可以获取它的一切信息。
    2.利用反射拿到字段的一个Field实例只是第一步,我们还可以拿到一个实例对应的该字段的值。
    3.如果使用反射可以获取private字段的值,那么类的封装还有什么意义?
    正常情况下,我们总是通过p.name来访问Person的name字段,编译器会根据public、protected和private决定是否允许访问字段,这样就达到了数据封装的目的。而反射始终非常规的用法,使用反射,首先代码十分繁琐,其次,它更多地是给工具或者底层框架来使用,目的是在不知道目标实例任何信息的情况下,获取特定字段的值。此外,setAccessible(true)可能会失败。如果JVM运行期间存在SercurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全。
    4.通过Filed实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。设置字段值是通过Filed.set(Object, Object)实现的,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值。

    调用方法

    (1)java的反射API提供的Method对象封装了方法的所有信息。
    (2)通过Class实例的方法可以获取Method实例:getMethod(),getMethods(), getDeclaredMethod(), getDeclaredMethods();
    (3)通过Method实例可以获取方法信息:getName(), getReturnType(), getParamterTypes(), getModifiers();
    (4)通过反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法。

    调用构造方法

    (1)我们通常使用new操作符创建新的实例,如果使用反射来创建新的实例,可以调用Class提供的newInstance()方法。调用Class.newInstance()的局限是,它只能调用该类的public无参构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstacn()来调用。
    (2)为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且调用结果总是返回实例。

    动态代理

    (1)class和interface的区别:
    一、可以实例化class(非abstract)
    二、不能实例化interface。
    所有interface类型的变量总是通过某个实例向上转型并赋值给接口类型变量的。为了实现不编写实现类,直接在运行期创建某个interface的实例,java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以再运行期间动态创建某个interface的实例。这种没有实现类但是在运行期间创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,叫作动态代理。

    (2)在运行期动态创建一个interface实例的方法如下:
    1.定义一个InvocationHandler实例,它负责实现接口的方法调用。
    2.通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    使用的ClassLoader,通常就是接口类的ClassLoader;
    需要实现的接口数组,至少需要传一个接口进去;
    用来处理接口调用方法的InvocationHandler
    3.将返回的Object强制转型为接口

    (3)动态代理实际上是JVM在运行期动态创建class字节码并加载的过程。

    使用注解

    (1)注解(Annotation)是放在java源码的类、方法、字段、参数前的一种特殊注释。
    (2)注释会被编译器直接忽略,注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。
    (3)从JVM的角度看,注解本身对代码逻辑没有任何影响,如果使用注解完全由工具决定。
    (4)java的注解可以分为三类:第一类是由编译器使用的注解,例如:@Override:让编译器检查该方法是否正确地实现了覆写。@SupperessWarning:告诉编译器忽略此处代码产生的警告。这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。
    第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。这类注解会被编译进入.class文件,但加载结束后并不会存于内存中。这类注解只被一些底层库使用,一般我们不用自己处理。
    (5)定义一个注解,还可以定义配置参数。配置参数可以包括:所有基本类型;String;枚举类型;基本类型、String、Class以及枚举的数组。因为配置参数必须是常量,所以上述限制保证了注解在定义时就已经确定了每个参数的值。注解的配置参数可以有默认值,缺少某个配置参数时将使用默认值。此外,大部分注解会有一个名为value的配置参数,对此参数赋值,可以只写常量,相当于省略了value参数。如果只写注解,相当于全部使用默认值。

    定义注解

    (1)Java使用@interface语法来定义注解(Annotation)它的格式如下:
    public @interface Report {
    int type() default 0;
    String level() default “info”:
    String value() default “”;
    }
    (2)元注解:有些注解可以修饰其他注解,这些注解就被称为元注解(meta annotation)。
    @Taget
    最常用的元注解就是@Target。使用@Target可以定义Anootation能够被应用于源码的哪些位置:
    类或接口:ElementType.TYPE;
    字段:ElementType.FIELD;
    方法:ElementType.METHOD;
    构造方法:ElementType.CONSTRUCTOR;
    方法参数:ElementType.PARAMETER

    @Retention
    另一个重要的元注解@Retention定义了Annotataion的声明周期:
    仅编译器:RetentionPolicy.SOURCE;
    仅class文件:RetentionPolicy.CLASS
    运行期:RetentionPolicy.RUNTIME

    如果@Retention不存在,则该Annotation默认为CLASS。因为通常我们自定的Annotataion都是RUNTIME,所以,务必加上@Retention(RetentionPolicy.RUNTIME)这个元注解。

    @Inherited
    使用@Inherited定义子类是否可以继承父类定义的Annotation。@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效。

    处理注解

    JAVA的注解本身对于代码逻辑没有任何影响。根据@Retention的配置:
    SOURCE类型的注解在编译器就被丢掉了;
    CLASS类型的注解仅保存在class文件中,它们不会被加载进JVM
    RUNTIME类型的注解会被加载进JVM,并且在运行期可以被程序读取

    (1)因为注解定义后也是一种class,所有的注解都继承自java.lang.annotation.Annotation,因此,读取注解,需要使用反射API。Java提供的使用反射API读取Annotation的方法包括:判断某个注解是否存在于Class、Field、Method、或者Contructor:
    Class.isAnnotationPresent(Class)
    Field.isAnnotationPresent(Class)
    Method.isAnnotationPresent(Class)
    Constructor.isAnnotationPresent(Class)
    (2)使用反射API读取Annotation有两种方法。方法一是判断Annotation是否存在,如果存在,则直接读取:
    Class cls = Person.class;
    if (cls.isAnnotationPresent(Report.class)) {
    Report report = cls.getAnnotation(Report.class);}
    第二种方法是直接读取Annotation,如果Annotation不存在,则返回null。
    Class cls = Person.class;
    Report report= cls.getAnnotation(Report.class);
    if (report != null) {
    …}

    读取方法、字段和构造方法的Annotation和Class类似,单数读取方法参数的Annotation就比较麻烦一点,因为方法参数本身可以看成一个数组,而每个参数又可以定义多个注解,所以,一次获取方法参数的所有注解就必须用一个二维数组来表示。要获取方法参数的注解,我们先用反射获取Method实例,然后读取方法参数的所有注解。

  • 相关阅读:
    Visual Studio App Center 中的 Bug 跟踪服务
    Could not find org.jetbrains.kotlin:kotlin-stdlib-jre7:1.5.21.
    【TES720D】青翼科技基于复旦微的FMQL20S400全国产化ARM核心模块
    Java项目:SSM实现的在线农产品商城
    c++二叉树的进阶--二叉搜索树
    【白盒测试】单元测试的理论基础及用例设计技术(6种)详解
    Type-C协议Ver2.0(学习笔记)
    Intel汇编-Linux系统调用
    Apache Paimon 文件管理
    “逆境中的经济悖论:衰退与通胀之争,解读未来的经济迷局!“
  • 原文地址:https://blog.csdn.net/weixin_49131718/article/details/126042311