注解&反射
注解
Annotation是从jdk5.0开始引入的新技术
使用注解的时候只需要在代码中添加 @注解名 即可使用。注解和注释不同,注解有检查和约束的作用他是能够影响程序的执行的
内置注解
@Override
:重写方法注解@Deprecated
:表示该方法已经废弃@SuppressWarnings
:忽略警告@FunctionalInterface
:表示该接口是一个函数式接口- ......
元注解
作用:负责解释其他注解的注解,Java定义了四个标准
@Target注解
用于描述注解的作用范围,可以指定注解应用的地方,如类、方法、字段等
常见取值包括:
常量 | 作用 |
---|---|
ElementType.TYPE | 类、接口(包括注解类型)、枚举声明 |
ElementType.FIELD | 字段声明(包括枚举常量) |
ElementType.METHOD | 方法声明 |
ElementType.PARAMETER | 参数声明 |
ElementType.CONSTRUCTOR | 构造方法声明 |
ElementType.LOCAL_VARIABLE | 局部变量声明 |
ElementType.ANNOTATION_TYPE | 注解类型声明 |
ElementType.PACKAGE | 包声明 |
@Retention注解
用于描述注解的生命周期,即在什么级别保存注解信息
有三种策略:
常量 | 作用 |
---|---|
RetentionPolicy.RUNTIME | 注解会被保留到运行时,可以通过反射机制读取 |
RetentionPolicy.SOURCE | 注解仅存在于源代码中,在编译时会丢弃 |
RetentionPolicy.CLASS | 注解会被保留到编译进行时,但不会被加载到 JVM 中 |
@Documented注解
用于说明该注解将被包含在生成的 JavaDoc 文档中,这样可以让开发人员了解该注解的使用方法和含义
@Inherited注解
用于说明该注解是否会被子类继承。如果一个注解被 @Inherited 修饰,那么该注解将被子类继承,否则不会
自定义注解
注解定义的时候需要使用@interface
修饰,然后在内部定义属性内容
import java.lang.annotation.*;
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface InitExample {
String value() default "";
}
以上就是定义了一个初始化的注解,可以看到里面定义了一个value
属性,默认值为""
然后在类中方法上使用该注解
public class TestClazz {
@InitExample("INIT")
public void test1() {
System.out.println("test-1");
}
public void test2() {
System.out.println("test-2");
}
}
这就算是使用了,但是还是需要通过反射机制来读取编写这个注解的逻辑。没有使用反射的话这个注解什么用都没有
一般都是会扫描所有类,然后找找这个类里面有没有使用该注解的地方,然后在这个地方执行该注解的逻辑
以下是一个简单的例子
public static void main(String[] args) throws Exception {
// 这里通过类的路径获取class,然后通过反射获取该类的所有方法
Class<?> aClass = Class.forName("top.xmln.annotation.TestClazz");
Method[] methods = aClass.getMethods();
// 如果不为空遍历所有方法
if (methods != null) {
for (Method method : methods) {
// 判断方法上是否有InitExample注解
boolean present = method.isAnnotationPresent(InitExample.class);
// 如果有就执行以下逻辑
if (present) {
// 下面两行代码是获取注解上的值
InitExample annotation = method.getAnnotation(InitExample.class);
System.out.println(annotation.value());
// 下面这行代码是执行方法
method.invoke(aClass.getConstructor(null).newInstance(null));
}
}
}
}
反射
反射是动态获取某一个class对象
的全部信息
获取反射对象:Class xxx = Class.forName("对象路径")
一个类在内存中只有一个class对象,在加载后,类的整个结构都会被封装在class对象中,每个class对象都对应着一个加载到jvm中的.class文件
常用方法
方法名 | 功能 |
---|---|
forName(String) | 返回指定的类名的class对象 |
newInstance() | 调用缺省构造参数,返回class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组···)的名称 |
getSuperClass() | 返回当前class对象的父类的class对象 |
getInterfaces() | 返回当前class对象的接口 |
getClassLoader() | 返回该类的加载器 |
getConstructors() | 返回一个包含某些Constructors对象的数组 |
getMothed(String,Class·· T) | 返回一个Method对象,此对象的形参为paramType |
getDaeclaredFields() | 返回Field对象的数组 |
获取类的实例
已至具体类:类名.class
已知类的实例:实例名.getClass()
已知类的全类名(包括路径):Class.forName("全类名")
基本数据类型的包装类都有一个TYPE
属性
类加载器
类加载的流程:将字节码文件内容加载到内存中,然后把静态数据转换成方法区的运行时入口,最后在堆中生成一个代表这个类的 java.lang.Class
对象作为方法区数据访问的入口
类缓存:某个类被加载到类加载器中,它将维持加载(缓存)一段时间,JVM垃圾回收机制可以回收这些Class对象
扩展(双亲委派机制):这是一种保护机制,他会在类加载的时候逐层往上查找,如:定义一个类,在加载的时候会一层一层往上寻找直到最顶端,如果找到了这个类,那么就会使用上层的这个类而不会去使用你自己定义的这个类,如果在最顶端没有找到这个类,那么他会自上而下开始寻找,加载找到的第一个类
反射获取运行时类的完整结构
User类:
public class User {
public String id;
private String name;
private String age;
// 有参、无参
// get、set
// toString
}
反射:
Class aClass = Class.forName("com.wang.User");
// 说明:返回类的全部名称
// 结果:com.wang.User
String name = aClass.getName();
// 说明:返回类的简单名称
// 结果:User
String simpleName = aClass.getSimpleName();
// ------------------------------------------------
// 说明:返回全部属性
// 权限:public
// 某个结果:public java.lang.String User.id
Field[] fields = aClass.getFields();
// 说明:返回某个属性
// 权限:public
// 异常:防止没有该属性(NoSuchFieldException)
// 结果:public java.lang.String User.id
Field id = aClass.getField("id");
// ------------------------------------------------
// 说明:返回全部属性
// 权限:所有权限
// 异常:防止没有该属性(NoSuchFieldException)
// 某个结果:private java.lang.String User.name
Field[] declaredFields = aClass.getDeclaredFields();
// 说明:返回某个属性
// 权限:所有权限
// 异常:防止没有该属性(NoSuchFieldException)
// 结果:private java.lang.String User.name
Field declaredField = aClass.getDeclaredField("name");
// ------------------------------------------------
// 说明:返回全部类的方法,包含继承的类
// 权限:public
// 异常:防止没有该方法(NoSuchMethodException)
// 某个结果:public java.lang.String User.getId() ...
Method[] methods = aClass.getMethods();
// 说明:返回类的某个方法,包含继承的类
// 权限:public
// 异常:防止没有该方法(NoSuchMethodException)
// 值的属性:get方法null就行,set方法填入参数的类型String.class
// 结果:public java.lang.String User.getId() ...
Method method = aClass.getMethod("getId", null);
// ------------------------------------------------
// 说明:返回类的全部方法,不包含继承的类
// 权限:所有权限
// 异常:防止没有该方法(NoSuchMethodException)
// 某个结果:public java.lang.String User.getId() ...
Method[] declaredMethods = aClass.getDeclaredMethods();
// 说明:返回类的某个方法,不包含继承的类
// 权限:所有权限
// 异常:防止没有该方法(NoSuchMethodException)
// 值的属性:get方法null就行,set方法填入参数的类型String.class
// 某个结果:public java.lang.String User.getId() ...
Method declaredMethod = aClass.getDeclaredMethod("setId", String.class);
// ------------------------------------------------
// 说明:获取所有构造方法
// 权限:public
// 某个结果:public User()
Constructor[] constructors = aClass.getConstructors();
// 说明:获取指定构造方法
// 权限:public
// 参数:构造方法中传入的类型
// 某个结果:public User(java.lang.String,java.lang.String,java.lang.String)
Constructor constructor = aClass.getConstructor(String.class, String.class, String.class);
// ------------------------------------------------
// 说明:获取所有构造方法
// 权限:所有权限
// 某个结果:public User()
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
// 说明:获取指定构造方法
// 权限:所有权限
// 参数:构造方法中传入的类型
// 某个结果:public User(java.lang.String,java.lang.String,java.lang.String)
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, String.class, String.class);
反射实例化对象
private
修饰的不能直接使用,除非关闭权限检查(xxx.setAccessible(true)
)
无参
User user = (User) Class.forName("com.wang.User").newInstance();
user.setId("1");
System.out.println(user.getId());
可以通过newInstance
来实例化对象,本质上是调用了类的无参构造器
有参
Class aClass = Class.forName("com.wang.User");
Constructor constructor = aClass.getDeclaredConstructor(String.class, String.class, String.class);
User user = (User) constructor.newInstance("1", "张三", "18");
System.out.println(user.getId());
先获取指定构造方法,然后传值,最后实例化
方法
User user = new User();
Class<?> aClass = Class.forName("User");
aClass.getDeclaredMethod("setName", String.class).invoke(user, "张三");
System.out.println(user.getName());
反射获取class对象,然后调用getDeclaredMethod()
并传入数据,调用invoke()
方法激活执行一下