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

泛型

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

  • 为什么要使用泛型?
    1、在编译时进行更强的类型检查。Java编译器将强类型检查应用于通用代码,如果代码违反类型安
    全,则会发出错误。修复编译时错误比修复运行时错误容易,后者可能很难找到
    2、消除类型转换。以下不带泛型的代码段需要强制转换
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
  • 声明一个"Class"不同的是,注解的声明使用 @interface 关键字
public @interface Demo{ }
  • 元注解
    在定义注解时,注解类也能够使用其他的注解声明。@Documented 与 @Inherited 元注解,前者用于被javadoc工具提取成文档,后者表示允许子类
    继承父类中定义的注解
  • Target:注解标记另一个注解,以限制可以应用注解的 Java 元素类型
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 可以应用于类的任何元素。

  • @Retention:指定标记注解的存储方式
    RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略。
    RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
    RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它。
image.png

APT注解处理器

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

  • 在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用
    框架中都有注解处理器的身影。这些框架中对注解的定义并不是 SOURCE 级别,更多的
    是 CLASS 级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。

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被视为动态语言的关键。

  • Java反射机制主要提供了以下功能:
  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)
  • Class
    反射始于Class,Class是一个类,封装了当前对象所对应的类的信息

获取Class对象的三种方式

  1. 通过类名获取 类名.class
  2. 通过对象获取 对象名.getClass()
  3. 通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)
  • 用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的
    isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法
public native boolean isInstance(Object obj); 

判断是否为某个类的类型

public boolean isAssignableFrom(Class<?> cls)

创建实例

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

  • 使用Class对象的newInstance()方法来创建Class对象对应类的实例。
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);

动态代理原理

  • 静态代理
    代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
    目的:
    (1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
    (2)通过代理对象对访问进行控制;


    image.png
  • 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口

  • 真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。

  • 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!

  • 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。

  • 静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。

动态代理

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个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); } });
  • 实际上, Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来,即没有真正的文件,只是在内存中按照Class格式生成了一个Class。
image.png

还带完善。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270