1.能够读懂别人写的代码,特别是框架相关的代码。
2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程更加简洁,代码更加清晰。
首先我们来明确一种我们经常见的一种注解我们先新建一个接口people,如下:
public interface people {
public String name();
public int age();
public void work();
}
然后再建一个类Child实现类people这个接口,并实现该类的方法:
public class Child implements people {
@Override
public String name(){
return null;
}
@Override public int age() {
return 0;
}
@Override public void work() {
}
看到这里,我们发现这里的所有方法都会加上一个@Override标记,它告诉我们,同时也告诉编译器我们的这些方法肯定覆盖了类people里面的方法的。假如说,我现在把类people里面的某一个方法注释掉:
//public String name();
再看类Child里面的name方法就会报错。这样,以后大家看到@Override的时候就能想到这个方法是覆盖了某个接口的方法的。然后,我们回过头来看类people里面有一个work的方法。这里我们可以理解为人是要工作的,但是并不是所有的人都在工作,那么怎么办呢?如果说这个接口正在用,我们不能删除这个方法,这个时候我们就可以这样:
@Deprecatedpublic void work();
@Deprecated标记就表明这个方法已经过时了
注解的分类还可以按照运行机制划分(当然这是一种较大范围的分类):
【源码注解→编译时注解→运行时注解】
源码注解:只在源码中存在,编译成.class文件就不存在了。
编译时注解:在源码和.class文件中都存在。像前面的@Override、@Deprecated、@SuppressWarnings,他们都属于编译时注解。
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的这样一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。
注解按照按照来源划分:
【来自JDK的注解——元注解——自定义注解】
来自JDK的注解:在JDK中已经定义好的注解
元注解:元注解是给注解进行注解,可以理解为注解的注解就是元注解。
自定义注解我们分四步来解析自定义注解:
自定义注解的语法要求:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documentedpublic
@interface Myannotation{
String desc();
String author();
int age() default 18;
}
首先我们要明确这不是一个接口,它是使用@interface关键字定义的一个注解。
然后我们看下面的几个方法,String desc();虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量(成员以无参无异常的方式声明),int age() default 18;(成员变量可以用default指定一个默认值的)。
最后我们要知道:
①.成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation,Enumeration等。
②.如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)。
③.注解类可以没有成员,没有成员的注解称为标识注解。
元注解:有没有发现上面那段代码有一个没有说呢?没错,它们就是我们所说的元注解:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
我们先看第一行:
@Target是这个注解的作用域,ElementType.METHOD是这个注解的作用域的列表,METHOD是方法声明,除此之外,还有:
作用域 | 作用域声明 |
---|---|
FIELD | 字段声明 |
CONSTRUCTOR | 构造方法声明 |
FIELD | 字段声明 |
LOCAL VARIABLE | 局部变量声明 |
METHOD | 方法声明 |
PACKAGE | 包声明 |
PARAMETER | 参数声明 |
TYPE | 类接口 |
第二行:
@Retention是它的生命周期,前面不是说注解按照运行机制有一个分类嘛,RUNTIME就是在运行时存在,可以通过反射读取。除此之外,还有:
生命周期 | 周期说明 |
---|---|
SOURCE | 只在源码显示,编译时丢弃 |
CLASS | 编译时记录到class中,运行时忽略 |
RUNTIME | 运行时存在,可以通过反射读取 |
第三行:@Inherited是一个标识性的元注解,它允许子注解继承它。
第四行:@Documented,生成javadoc时会包含注解。
使用自定义注解:
使用注解的语法:
@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,…)
案例:
@Myannotation(desc="i am Color",author="boy",age=18)
public String Color() {
return "red";
}
这里的Myannotion是我们刚才在自定义注解语法要求里面定义的注解噢,然后我们可以给它的每一个成员变量赋值,注意数据类型。值得注意的是,因为我们前面定义的作用域是在方法和类接口上,所以这个注解在Color()方法上使用是没问题的。
类的加载概述:
当程序要使用某个类时,如果该类还未被加载到内存中,
则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 : 是否有正确的内部结构,并和其他类协调一致
准备 : 负责为类的静态成员分配内存,并设置默认初始化值
解析: 把类中的符号引用转换为直接引用
类的加载时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类加载器的概述
负责将.class文件加载到内在中,并为之生成对应的Class对象。
类加载器的分类
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器
类加载器的作用
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
获取class文件对象的三种方式
a:Object类的getClass()方法
b:静态属性class
c:Class类中静态方法forName()
反射: 就是在运行状态中的一种动态调用方法或者属性的一种机制.
就是获取字节码文件对象,然后剖析改类中存在哪些构造方法,哪些成员变量,哪些成员方法。
获取所有构造方法
public Constructor>[] getConstructors() 获取所有的构造方法不包含私有的
public Constructor>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
获取单个构造方法
public Constructor getConstructor(Class>… parameterTypes) 获取单个的构造方法 不包含私有的
public Constructor getDeclaredConstructor(Class>… parameterTypes) 获取单个的构造方法包含私有的
解析注解
概念:
通过反射获取类 、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。准备工作: Myannotation类
Student类.png接下来,我们就开始测试了:
public class ParseAnn {
public static void main(String[] args) {
try { // 使用类加载器加载类
Class c = Class.forName("com.test.Child");
// 找到类上面的注解
boolean isExist = c.isAnnotationPresent( Myannotation.class);
// 上面的这个方法是用这个类来判断这个类是否存在 Myannotation这样的一个注解 if (isExist) { // 拿到注解实例,解析类上面的注解
Myannotation d = (Description) c.getAnnotation( Myannotation.class);
System.out.println(d.value());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出的结果:i am class annotation
可以看到,我们成功的解析了Child类上面的注解。接下来,我们继续解析方法上的注解:
//获取所有的方法Method[] ms = c.getMethods();
// 遍历所有的方法
for (Method m : ms) {
boolean isExist1 = m.isAnnotationPresent( Myannotation.class);
if (isExist1) {
Myannotation d1=m.getAnnotation( Myannotation.class);
System.out.println(d1.value());
}
}
输出的结果:
i am class annotation
i am method annotation
可以看到,我们成功的解析了方法上面的注解。
//另一种解析方法for (Method m : ms) {
//拿到方法上的所有的注解
Annotation[] as=m.getAnnotations();
for (Annotation a : as) {
//用二元操作符判断a是否是Description的实例
if (a instanceof Myannotation) {
Myannotation d=( Myannotation) a;
System.out.println(d.value());
}
}
}
也可以得到上面的效果。