[笔记]跟着狂神学Java——注解与反射
什么是注解
Annotation
- 注解和注释一样可以对程序做出解释
- 注解可以被其他程序读取,注释不行
- 注解的格式:@+注释名
- 可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息。
- 可以通过反射机制返程实现对这些元数据的访问。
内置注解
- @Override:定义在java.lang.Override中,只用于修饰方法,表示一个方法声明打算重写父类中的一个方法声明。
- @Deprecated:定义在java.lang.Deprecated中,可以用于修饰方法,属性,类,表示不建议程序员使用这样的元素,已经过时的或有更好的选择。
- @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。此注解需要添加一个参数才能正确使用。
- @SuppressWarnings(“all”)
- @SuppressWarnings(“unchecked”)
- @SuppressWarnings(value={“unchecked”,“deprecation”})
- 等等…
元注解
- Java定义的四个标准的meta-annotation类型,负责注解其他注解,白用来提供对其他annotation类型作说明
- 这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target、@Retention、@Documented、@Inherited)
- @Target:描述注解的使用范围
- @Retention:表示需要在说明级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
- @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注释
自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation. Annotation接口。
public class Test{
//name 有默认值,可以不用写
@MyAnnotation(id = 10, age = 18)
void test() {
}
}
@Target(value = {ElementType.CONSTRUCTOR, ElementType.FIELD,ElementType.METHOD,ElementType.PACKAGE})
//参数:SOURCE<CLASS<RUNTIME
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation {
//声明配置参数,default设置默认值
String name() default "";
int id();
int age();
}
反射
Reflection(反射):是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API 取得仍和类的内部信息,并能直接操作任意对象的内部属性及方法。
反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意与各类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员便利和方法
- 在运行时处理注解
- 生成动态代理
- …
Java反射的优点与缺点
- 优点: 可以实现动态创建对象和编译,体现出很大的灵活性
- 缺点:对性能有影响。
反射相关的主要API
- java.lang.Class :代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- …
获得反射对象
Class c1 = Class.forName("类路径");
Class c2 = 类名.class;
截取自:狂神说Java
我的理解:只要有一个对象名,就可以通过反射找到对应的类。一个类被加载后,类的整个结构都会被封装在Class对象中。
得到Class类的实例
- Class c1 = 类名.class;
- Class c2 = 类名.getClass();
- Class c3 = Class.forName(“类路径”);
所有类型的Class对象
这里只放了部分~
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = Override.class;
Class c6 = ElementType.class;
Class c7 = Integer.class;
Class c8 = void.class;
Class c9 = Class.class;
获取运行时类的完整结构
通过反射获取运行时类的完整结构
Field、Method、constructor、Superclass、Interface、Annotation…
我的User类(就是普通的一个类,放出来给下面的反射获取完整结构做个参考):
package reflection;
import java.lang.annotation.*;
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException{
/*
//通过反射获取类的Class对象
Class c1 = Class.forName("reflection.User");
System.out.println(c1);
Class c2 = Class.forName("reflection.User");
Class c3 = Class.forName("reflection.User");
Class c4 = Class.forName("reflection.User");
//一个类再内存中只有一个Class对象
//一个类被加载后,类的整个结构都会被封装在Class对象中
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
*/
User u1 = new Student();
Class c1 = u1.getClass();
System.out.println(c1.hashCode());
Class c2 = Class.forName("reflection.User");
System.out.println(c2.hashCode());
Class c3 = Student.class;
System.out.println(c3.hashCode());
Class c4 = Integer.TYPE;
System.out.println(c4.getInterfaces());
System.out.println(c4);
Class c5 = c2.getSuperclass();
System.out.println(c5);
}
}
//实体类:pojo / entity
class User implements Runnable {
private String name;
private int age;
private int id;
public int num;
public User() {
}
public User(String name, int age, int id, int num) {
this.name = name;
this.age = age;
this.id = id;
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
private void test () {}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
", num=" + num +
'}';
}
@Override
public void run() {
}
}
class Student extends User {
public String name;
public Student() {
this.name = "学生";
}
}
class Teacher extends User {
public String name;
public Teacher() {
this.name = "老师";
}
}
@Target(value = {ElementType.CONSTRUCTOR, ElementType.FIELD,ElementType.METHOD,ElementType.PACKAGE})
//参数:SOURCE<CLASS<RUNTIME
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation {
//声明配置参数,default设置默认值
String name() default "";
int id();
int age();
}
获取运行时类的完整结构(部分):
package reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test08 {
public static void main(String[] args) throws Exception {
//通过反射来获取运行时类的完整结构
Class c1 = Class.forName("reflection.User");
//获取公共字段
Field[] fields = c1.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("\t");
//获取全部字段
fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("\t");
//获取指定字段
Field name = c1.getDeclaredField("name");
System.out.println(name);
System.out.println("-------------------------------------");
//获取本类及父类的所有公共方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("\t");
//获取本类的所有方法
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("\t");
//获取指定的方法
Method method = c1.getMethod("getName",null);
System.out.println(method);
method = c1.getMethod("setName", String.class);
System.out.println(method);
System.out.println("-------------------------------------");
//获取公共构造器
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("\t");
//获取指定构造器
Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class,int.class);
System.out.println(constructor);
System.out.println("-------------------------------------");
//获取类的接口
Class[] classes = c1.getInterfaces();
for (Class aClass : classes) {
System.out.println(aClass);
}
}
}
小结:
- 熟悉java.lang.reflect包的作用,反射机制。
- 如何获取属性、方法、构造器的名称、修饰符等等。
动态创建对象
- 通过反射获得对象的构造器来实例化。
- 类必须要有一个无参构造器(也可以使用有参构造器来实例化)
- 类的构造器的访问权限需要足够
- 通过反射来调用一个方法
- 实例化一个类 Class.forName()
- 获取指定方法 getDeclaredMethod()
- 调用方法 invoke() 若原方法被声明为private,须在调用方法前显式调用方法对象的setAccessible()方法,参数设为true,即可访问private方法。
- 通过反射修改属性值
还有一些注意的点写在了注释里:
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test09 {
public static void main(String[] args) throws Exception {
//获取Class 对象
Class c1 = Class.forName("reflection.User");
//利用获取的对象创建一个User类的实例,这个方法从Java9开始已经过时。
/*User user = (User) c1.newInstance();//调用的是User类中的无参构造器
System.out.println(user);*/
//当然也可以不用无参构造器创建实例,通过反射后的构造器来实例化创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class, int.class);
User user1 = (User) constructor.newInstance("xzc", 20, 17, 1145);
System.out.println(user1);
//通过反射调用普通方法
Method method = c1.getDeclaredMethod("setName", String.class);
//invoke激活(对象,参数)
method.invoke(user1,"WF");
System.out.println(user1.getName());
//通过反射访问私有字段
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要调用setAccessible方法,关闭安全检测,会提高反射效率
name.setAccessible(true);
name.set(user1, "XZC LOVE WF");
System.out.println(user1.getName());
}
}
反射获取泛型
为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType, TypeVariable 和 WildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
- ParameterizedType:表示一种参数化类型,比如Collection< String >
- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable:是各种类型变的公共父接口
- WildcardType:代表一种通配符类型表达式
获取泛型:
package reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/*
* 通过反射获取方法的泛型类型
* */
public class Test10 {
public void test (Map<String, User> map, List<User> list) {
}
public Map<String,User> test01 () {
return null;
}
public static void main(String[] args) throws Exception {
//通过反射获取Class类方法对象
Method method = Test10.class.getMethod("test", Map.class, List.class);
//getGenericParameterTypes()
//按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
//遍历数组genericParameterType
for (Type genericParameterType : genericParameterTypes) {
System.out.println(genericParameterType);
//判断数组 genericParameterType 是否是参数化类型 ParameterizedType
if (genericParameterType instanceof ParameterizedType) {
//getActualTypeArguments()
//返回表示此类型实际类型参数的 Type 对象的数组。
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
//遍历数组 actualTypeArguments
for (Type actualTypeArgument : actualTypeArguments) {
//输出 genericParameterTypes 中的实际类型参数 actualTypeArgument
System.out.println("泛型参数:" + actualTypeArgument);
}
}
}
System.out.println("----------------------------------------");
method = Test10.class.getMethod("test01",null);
Type genericReturnType = method.getGenericReturnType();
//判断数组 genericParameterType 是否是参数化类型 ParameterizedType
if (genericReturnType instanceof ParameterizedType) {
//getActualTypeArguments()
//返回表示此类型实际类型参数的 Type 对象的数组。
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
//遍历数组 actualTypeArguments
for (Type actualTypeArgument : actualTypeArguments) {
//输出 genericParameterTypes 中的实际类型参数 actualTypeArgument
System.out.println("泛型参数:" + actualTypeArgument);
}
}
}
}
反射获取注解信息
- 获取注解
- 获取注解中的参数值
package reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test11 {
public static void main(String[] args) throws NoSuchFieldException {
//获取类的对象
Class c1 = Student2.class;
//通过反射获取Class类对象的注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的值
Field id = c1.getDeclaredField("id");
FiledAnnotation annotation = id.getAnnotation(FiledAnnotation.class);
System.out.println(annotation.modify());
System.out.println(annotation.type());
System.out.println(annotation.val());
TypeAnnotation annotation1 = (TypeAnnotation) c1.getAnnotation(TypeAnnotation.class);
System.out.println(annotation1.value());
}
}
@TypeAnnotation("public")
class Student2 {
@FiledAnnotation(modify = "private", type = "int", val = "id")
private int id;
@FiledAnnotation(modify = "private", type = "int", val = "age")
private int age;
@FiledAnnotation(modify = "private", type = "varchar", val = "name")
private String name;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnotation {
String modify();
String type();
String val();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeAnnotation {
String value();
}
什么时候会发生类初始化?
- 类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会初始化他的父类
- 类的被动调用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化。
- 通过数组定义类引用,不会触发此类的初始化。
- 引用常量不会触发此类初始化(常量在链接阶段就存入调用类的常量池中了)
下面是我自己跟着敲得代码试了一下
//类何时会被初始化
public class Test06 {
static {
System.out.println("Test06类被初始化");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
//Son son = new Son();
//Class c1 = Class.forName("reflection.Son");
//System.out.println(Son.m);
//2.被动引用
//System.out.println(Son.b);
//Son[] sons = new Son[10];
//System.out.println(Son.M);
}
}
class Father {
public static int b = 2;
static {
System.out.println("Father类被初始化");
}
}
class Son extends Father {
static {
System.out.println("Son类被初始化");
m = 300;
}
static int m =100;
static final int M = 1;
}
类加载器
(该部分只是浅尝辄止,未深入了解)
- 作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class 对象,作为方法区中类数据的访问入口。
- 类缓存:标准的JavaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,他将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
- 分类
- Bootstrap Class Loader(引导类加载器):用c++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
- Extension Class Loader(扩展类加载器):负责jre/lib/ext目录下的jar包或者-D java.ext.dirs 指定目录下的jar包装入工作库。
- Application Class Loader (System Class Loader)(系统类加载器):负责java -classpath 或 -D java.class.path所指的目录下的类与jar包装入工作库,是最常用的加载器。
- 从3至1检查类是否已经装载,从1至3尝试加载类。
- 双亲委派机制:自己定义一个类,要去加载,会先去各个类加载器里找有没有这个类,如果有了还是会加载已经存在的类。
下面是跟着狂神一块敲得代码,仅仅只是了解了一下类加载器,具体作何作用还未深入:
package reflection;
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();
System.out.println(loader);
//获取系统类加载器的父类加载器---> 扩展类加载器
ClassLoader parent = loader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器---> 根加载器(c/c++)Java获取不到显示null
ClassLoader root = parent.getParent();
System.out.println(root);//null
//测试当前类的哪个类加载器加载的
ClassLoader classLoader = Class.forName("reflection.Test07").getClassLoader();
System.out.println(classLoader);//Application Class Loader
//测试JDK内置的类是谁加载的 (根加载器)
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);//null
//获取系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
了解类加载的过程
- 加载:将Class加载到内存,产生一个Class类型的对象
- 链接 一,验证(验证安全) 二,准备(正式为类变量static在方法区分配默认值) 三,解析(常量名直接替换成地址)
- 初始化---->类构造器
以上都是我跟着狂神学Java注解和反射时的一些代码和感悟,纯属当作笔记来分享,如果大家发现了我笔记中的错误,可以多多指正,我会认真修改,虚心求教的!