注解&反射
(学习B站“狂神说Java视频笔记”),链接
框架的底层使用最多
注解 Annotation
一、什么是注解
Annotation从JDK1.5开始
注解作用:不是程序本身,可以对程序作出解释(和注释作用相同);可以被其他程序读取;
注解是以“@注释名”在代码中存在,可以添加参数值,如@SuppressWarings(value = “unchecked”)
使用范围:package、class、method、field,给他们添加了额外的辅助信息,可以使用反射机制编程实现对这些元数据的访问
二、内置注解
Override、Deprecated、SuppressWarings警告镇压
三、元注解(meta-annotation)
负责注解其他注解
**@Target:**用于描述注解的使用范围(被描述的注解可以用在什么地方)
**@Retention:**表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
**@Documented:**说明该注解将被包含在javadoc中
**@Inherited:**说明子类可以继承父类中的该注解
四、自定义注解
@interface关键字,自动继承了java.lang.annotation.Annotation接口
- 使用public @ interface 注解名 { 定义内容 }
- 每个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型,class,string,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般命名为value,使用的时候不用写参数名
- 注解元素必须要有值,定义注解元素时,经常使用空字符串、0作为默认值
// 例子:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @iinterface MyAnnotation{
// 注解的参数: 参数类型 + 参数名()
// 如果没有默认值,就必须给注解赋值
String name() default "";
int id() default -1; // 如果默认值为-1代表不存在
String [] schools();
}
反射 Reflection
一、反射概念
是Java被视为动态的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class c = Class.forName(java.lang.String)
加载完类以后(类的整个结构都会被封装在class对象中),在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,通过这个对象看到类的结构
二、反射相关API
java.lang.Class // 代表一个类
java.lang.Method // 代表类的方法
java.lang.Field // 代表类的成员变量
java.lang.Constructor // 代表类的构造器
三、Class
对于每个类而言,JRE都为其保留一个不便的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type(基本数据类型)/void/[])的有关信息
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- Class类是Reflection的根源,针对任何想动态加载、运行的类,唯有先获得相应的Class对象
得到Class类的几种形式
Class clazz = Person.class;
Class clazz = person.getClass();
Class clazz = Class.forName("package.classname");
内置基本数据类型可以直接用类名.Type
还可以用ClassLoader
常用方法
static ClassforName(String name) // 但会指定类名name的Class对象
Object new Instance() // 调用缺省构造函数,返回Class对象的一个实例
getName() // 返回此Class对象所表示的实体(类、接口、数组类或void)的名称
四、Java内存分析
类的加载与ClassLoader的理解
1、加载
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
2、连接
将Java类的二进制代码合并到JVM的运行状态之中的过程
验证、准备(正式为类变量static分配内存并设置类变量默认初始值的阶段,在方法区中分配)、解析(虚拟机常量池内的符号引用[常量名]替换为直接引用的过程)
3、初始化
执行类构造器()方法的过程,是由编译期自动收集类中所有类变量的赋值动作和静态代码块肿的语句合并产生的
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步
什么时候会发生初始化?
1、类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
2、类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化(例如通过子类调用父类的静态变量,不会导致子类的初始化)
- 通过数组定义类引用
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
类加载器的作用?
分类:引导类加载器(C++编写,JVM自带的类加载器负责Java平台核心库,用来装载核心类库)、扩展类加载器(负责jre/lib/ext目录下的jar包或- D java.ext.dirs指定目录下的jar包装入工作库)、系统类加载器(负责java -classpath 或 -D java.class.path所指定的目录下的类与jar包装入工作,是最常用的加载器)
类缓存:按照要求查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
五、获得类的运行时结构
getName() // 获得报名+类名
getSimpleName() // 获得类名
getFields() // 找到public属性
getDeclaredFields() // 找到全部属性
getField(String name) // 获得指定的属性
getMethods() // 获取本类和父类的所有的public方法
getDeclaredMethods() // 获取本类所有的方法
getMethod(String name, String... parameterTypes ) // 获取指定的方法
getConstructors() // public
getDeclaredConstructors() // 本类的全部方法
getConstructor // 获取指定的构造器
六、使用Class对象能做什么?
Class class =Class.forName("com.user");
User user = (User) class.newInstance(); // 调用无参构造方法,没有则创建不成功,并且类的构造器的访问权限需要足够
// 其他方法,使用getDeclaredConstructor(参数)得到构造器,然后传入参数
// 反射调用方法invoke(对象,“方法的值”)
// getMethod->invoke
method = user.getgetDeclaredMethod("name",Srting.class);
method.invoke(user,"姓名");
// 反射操作属性set(对象,属性值)
Field name = user.getDeclaredField("name");
name.set(user,"123");
// 不能直接操作私有属性,需要关闭程序的安全检测
name.setAccessible(true);
// 反射操作泛型
Java采用泛型擦除的机制来引入泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,一旦编译完成,所有和泛型有关的类型全部擦除
为通过反射操作类型,Java新增了ParameterizedType、GenericArrayType、TypeVariable、WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
ParameterizedType:表示一种参数化类型,如Collection<String>
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式
// 反射获取注解信息
getAnnotation()
getAnnotations()
注解和反射结合例子
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class cls = Class.forName("reflection_annotation.Student");
// 通过反射获得注解
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType());
}
// 获得value的值
Table table =(Table) cls.getAnnotation(Table.class);
System.out.println(table.value());
// 获得类指定的注解,打印出具体值
Field field = cls.getDeclaredField("name");
FieldLin fieldLin =(FieldLin) field.getAnnotation(FieldLin.class);
System.out.println(fieldLin.column());
System.out.println(fieldLin.type());
System.out.println(fieldLin.length());
}
}
// 实体类
@Table("db_student")
class Student {
@FieldLin(column = "db_id", type = "int", length = 10)
int id;
@FieldLin(column = "db_name", type = "varchar", length = 10)
String name;
@FieldLin(column = "db_age", type = "int", length = 3)
int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 作用在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
// 作用在属性上
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldLin{
String column();
String type();
int length();
}