01_测试开发

泛型、注解与反射、动态代理原理

2020-05-17  本文已影响0人  仕明同学

泛型

泛型(Generic)是Java编程语言的强大功能。它们提高了代码的类型安全性,使你在编译时可以检测
到更多错误。

List list = new ArrayList(); 
list.add("hello"); 
String s = (String) list.get(0);

当使用泛型重写时,代码不需要强制转换

List<String> list = new ArrayList<String>(); 
list.add("hello"); 
String s = list.get(0); // no cast

3、使程序员能够实现通用算法。通过使用泛型,程序员可以实现对不同类型的集合进行工作,可以自
定义并且类型安全且易于阅读的泛型算法。

注解与反射

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关
于程序但不属于程序本身的数据
Java中所有的注解,默认实现 Annotation 接口:


image.png
public @interface Demo{ }
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。

image.png

APT注解处理器

APT全称为:"Anotation Processor Tools",意为注解处理器。顾名思义,其用于处理注解。编写好的Java源文件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是 javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理。注解处理器是对注解应用最为广泛的场景。

CLASS

定义为 CLASS 的注解,会保留在class文件中,但是会被虚拟机忽略(即无法在运行期反射获取注解)。此时完全符合此种注解的应用场景为字节码操作。如:AspectJ、热修复Roubust中应用此场景。所谓字节码操作即为,直接修改字节码Class文件以达到修改代码执行逻辑的目的。


image.png

我们使用普通的编程方式,需要在代码中进行 if-else 的判断,也许存在十个判断点,则需要在每个判断点加入此项判断。此时,可以借助AOP(面向切面)编程思想,将程序中所有功能点划分为: 需要登录 与 无需登录两种类型,即两个切面。对于切面的区分即可采用注解。
[图片上传中...(image.png-29b91a-1589702240867-0)]

//Java源码
 @Target(ElementType.METHOD) 
@Retention(RetentionPolicy.CLASS) 
public @interface Login { }
@Login
 public void jumpA()
{ startActivity(new Intent(this,AActivity.class)); }
public void jumpB()
{ startActivity(new Intent(this,BActivity.class)); }

RUNTIME

注解保留至运行期,意味着我们能够在运行期间结合反射技术获取注解中的所有信息。

反射

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。

获取Class对象的三种方式

  1. 通过类名获取 类名.class
  2. 通过对象获取 对象名.getClass()
  3. 通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)
public native boolean isInstance(Object obj); 

判断是否为某个类的类型

public boolean isAssignableFrom(Class<?> cls)

创建实例

通过反射来生成对象主要有两种方式。

Class<?> c = String.class; 
Object str = c.newInstance();

先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例

//获取String所对应的Class对象
 Class<?> c = String.class;
 //获取String类带一个String参数的构造器 
Constructor constructor = c.getConstructor(String.class);
 //根据构造器创建实例 
Object obj = constructor.newInstance("23333"); 
System.out.println(obj);

动态代理原理

动态代理

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了 Proxy 来完成这件事情。基本使用如下:

//抽象角色
 interface Api { void test(String a); }
//真实角色
class ApiImpl{ @Override public void test(String a) 
{ System.out.println("真实实现:" + a); } }
//创建真实角色实例
 ApiImpl api = new ApiImpl(); 
//JDK动态代理:
 Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Api.class}, 
//JDK实现只能代理接口 
new InvocationHandler() 
{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
//执行真实对象方法
 return method.invoke(api, args); } });
image.png

还带完善。。。

上一篇 下一篇

猜你喜欢

热点阅读