一、反射
1、是什么
Oracle官方对反射的解释是:
- Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
- The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.
人话:通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
2、神马用
Java 反射主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用 private 方法);
- 在运行时调用任意一个对象的方法
重点:是运行时而不是编译时
3、怎么用
如果要进一步掌握反射,还需要对反射的常用 API 有更深入的理解。
在 JDK 中,反射相关的 API 可以分为下面几个方面:
1:成员变量 - Field
2:成员方法 - Constructor
3:构造方法 - Method
获取 class 文件对象的方式:
1:Object类的getClass()方法
2:数据类型的静态属性class
3:Class类中的静态方法:public static Class ForName(String className)
获取成员变量并使用
1: 获取Class对象
2:通过Class对象获取Constructor对象
3:Object obj = Constructor.newInstance()创建对象
4:Field field = Class.getField(“指定变量名”)获取单个成员变量对象
5:field.set(obj,"") 为obj对象的field字段赋值
如果需要访问私有或者默认修饰的成员变量
1:Class.getDeclaredField()获取该成员变量对象
2:setAccessible() 暴力访问
通过反射调用成员方法
1:获取Class对象
2:通过Class对象获取Constructor对象
3:Constructor.newInstance()创建对象
4:通过Class对象获取Method对象 ------getMethod(“方法名”);
5: Method对象调用invoke方法实现功能
如果调用的是私有方法那么需要暴力访问
1: getDeclaredMethod()
2: setAccessiable();
但是笔者觉得如果真的想深入的、清楚的了解反射这一部分内容,还应对Java语言特性、JVM的内存细节、类加载机制深入学习之后,再回过头来再看反射会有更深刻的了解与体会。
动态代理说实话…没有看懂…所以不敢轻易瞎比比…
二、注解
1、是神马
说到注解,似乎我们还有一个名词与之相似:注释。但二者不是一个东西,注解是说明程序的,是给计算机看得。注释则是用文字描述程序,是给程序猿看的。
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.
人话:注解(Annotation)是插入代码中的元数据,一种代码级别的说明。它是在JDK5.0及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。它既能被编译器解析,也能在运行时被解析
(1)JDK 1.5 之后的新特性
(2)用来说明程序
(3)使用注解:@注解名称
2、神马用
作用主要分为三类:
- 编写文档:通过代码里标识的注解生成文档(生成 Java doc 文档( api ));
- 代码分析:通过代码里标识的注解对代码进行分析(使用反射);
- 编译检查:通过代码里标识的注解让编译器能实现基本的编译检查;
本质上,Annotion 是一种特殊的接口,程序可以通过反射来获取指定程序元素的 Annotion 对象,然后通过 Annotion 对象来获取注解里面的元数据。(元数据 metadata :关于数据的数据)
3、常见注解
JDK内置注解:
1.@Override
用于检测被该注解标注的方法是否时继承自父类(接口)的。
@Override 是一个标记注解类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种 Annotation 在一个没有覆盖父类方法的方法时,Java 编译器将以一个编译错误来警示:Method does not override method from its superclass
2.@Deprecated
用于标注已经过时的方法;
Deprecated 也是一个标记注解。当一个类型或者类型成员使用 @Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。
3.@SuppressWarnnings
用于通知 Java 编译器禁止特定的编译警告;
@SuppressWarnings 其注解目标为类、字段、函数、函数入参、构造函数和函数的局部变量。在 Java5.0,Sun 提供的 javac 编译器为我们提供了-Xlint 选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。有时我们无法避免这种警告,在调用的方法前增加 @SuppressWarnings 修饰,告诉编译器停止对此方法的警告。
一般传递参数 all @SuppressWarnnings(“all”)
4、注解分类
(4.1)按照运行机制分类
- 源码注解:注解只在源码中存在,编译成 .class 文件就不存在了
- 编译时注解:注解只在源码中与 .class 文件都存在了,包括 @Override、@Deprecated、@SuppressWarnnings
- 运行时注解:在运行阶段仍起作用,甚至影响运行逻辑的注解,如 @Autowired
(3.2)根据注解功能与用途
- 系统内置注解:系统自带的注解类型,如 @Override
- 元注解:注解的注解,负责注解其他注解,如 @Target
- 自定义注解:用户根据自己的需求自定义的注解类型
(3.3)根据成员个数分类
- 标记注解:没有定义成员的 Annotation 类型,自身代表某类信息,如:@Override
- 单成员注解:只定义了一个成员,比如 @SuppressWarnings 定义了一个成员String[] value,使用value={…}大括号来声明数组值,一般也可以省略“value=”
- 多成员注解:定义了多个成员,使用时以name=value对分别提供数据
5. 元注解相关
元注解的作用就是负责注解其他注解。Java5.0 定义了4个标准的 meta-annotation 类型,它们被用来提供对其它 annotation 类型作说明。
- @Target
作用:
描述注解能够作用的位置
取值( ElementType ): 8种
- @Target(ElementType.TYPE) //接口、类、枚举
- @Target(ElementType.FIELD) //字段、枚举的常量
- @Target(ElementType.METHOD) //方法
- @Target(ElementType.PARAMETER) //方法参数
- @Target(ElementType.CONSTRUCTOR) //构造函数
- @Target(ElementType.LOCAL_VARIABLE)//局部变量
- @Target(ElementType.ANNOTATION_TYPE)//注解
- @Target(ElementType.PACKAGE) ///包
-
@Retention
作用:
描述该注解类型会被保留到什么时候。
取值(RetentionPoicy):
1.SOURCE:当前被描述的注解只在源文件中有效,不会保留到class字节码文件中,也不会被JVM读取到。
2.CLASS:当前被描述的注解,会保留到class字节码文件中,不会被JVM读取到。
3.RUNTIME:当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。 -
@Documented
@Documented 用来描述注解是否被抽取到api文档中。在生成javadoc文档的时候将该Annotation也写入到文档中。 -
@Inherited
@Inherited 元注解是一个标记注解,@Inherited用来描述注解是否被子类继承。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
6.自定义注解
元注解
public @interface 注解名称{
... 属性列表
}
- 注解类型是用@interface关键字定义的。
- 所有的方法均没有方法体,且只允许public和abstract这两种修饰符号,默认为public。
- 注解方法只能返回:原始数据类型,String,Class,枚举类型,注解,它们的一维数组。
- 定义了属性,在使用时需要给属性赋值,如果使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
- 如果只有一个属性需要赋值,且属性的名称是value,则value可以省略,直接定义值即可(例如@SuppressWarnings(“all”))
- 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}省略
目前较多框架使用注解,用来自动生成代码或替代配置文件的功能。注解可以使编码更简洁,学习注解可以理解使用开源框架,甚至自定义框架来解决问题。
同时,注解和反射配合使用,可以使用简洁的代码实现复杂的功能,但可能对于初学者来说,这方面的内容需要更加贴近代码区分析、学习从而以便取得更好的效果。
相关资源来源于网络,仅供学习交流使用,如有侵权烦请站内联系。