注解和反射是所有框架的底层实现机制
注解
注解入门
注解都在java.lang.annotation中
注解作用:
- 对程序做出解释(和注释相同)
- 被其他程序读取
基本格式:@+注释名,还可以添加一些参数值,如@SuppressWarnings(value="unchecked")
内置注解
- @Override,定义在java.lang.Override中,表示重写父类方法
- @Deprecated,定义在java.lang.Deprecated中,表示该方法(或类、属性)不推荐使用,但仍可使用
- @SuppressWarnings("all"),用来抑制编译器的警告信息
元注解
元注解为注解其他注解,即解释其他注解
有四种:
- @Target:描述注解的使用范围
- @Retention:描述注解的生命周期,(SOURCE<CLASS<RUNTIME)
- @Document:说明该注解被包含在JavaDoc中
- @Inherit:子类可继承父类中的该注解
@Target:
源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
可发现,可传递枚举类型的参数value,根据PERCENTILE__博客@Target注解详解
@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) ///包
package com.annotation;
import java.lang.annotation.*;
//测试元注解
public class Demo2 {
@MyAnnotation
public void test () {
}
}
//定义一个注解
@Target({ElementType.TYPE,ElementType.METHOD}) //该注解可用到什么地方
@Retention(RetentionPolicy.RUNTIME) //注解生命周期
@Documented //是否将该注解生成到JavaDoc中
@Inherited //子类可继承父类的注解
@interface MyAnnotation {
}
自定义注解
@interface
package com.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
public class Demo3 {
//@TestAnnotation(name = "AAA")
@TestAnnotation(name = "A", school = "B", id = 1) //注解没有顺序
public void test () {
}
}
@Target({ElementType.TYPE,ElementType.METHOD}) //可以在类或方法上使用
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation {
//注解的参数,格式:参数类型+参数名+()
String name() default "A"; //加default后,提供默认值
int age() default 1;
int id();
String school();
}
反射
概述
动态语言:在运行时可改变自身结构
静态语言:运行时不可改变自身结构,Java不是动态语言
Reflection(反射)是Java动态语言的关键
正常方式和反射方式的区别:
反射优点:
- 可进行动态创建对象和编译,具有灵活性
反射缺点:
- 影响性能,和直接执行相同的操作(new)相比效率较低
Object类的getClass可返回一个Class对象,Class是反射Reflection的根源,想要动态加载的类必须获得相应Class对象
package com.reflect;
//反射
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c1 = Class.forName("com.reflect.Person");//位置
System.out.println(c1);
Class<?> c2 = Class.forName("com.reflect.Person");
Class<?> c3 = Class.forName("com.reflect.Person");
System.out.println(c2 == c3);
System.out.println(c2.hashCode());
}
}
class Person {...}
获得Class的方式
Class常用方法:
Class cl = Person.class; //效率更高
Class cl = person.getClass;
Class cl = Class.getName("com.sgkurisu.Person");
//第四种:基本数据类型包装类中的TYPE属性,可返回Class类型数据
Class<Integer> c1 = Integer.TYPE;
//第五种:使用getSuperClass获得父类的Class
Class<?> c2 = c1.getSuperclass();
package com.reflect;
//Class类的创建方法
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException {
Student student = new Student("AB", 11);
System.out.println(student.hashCode());
Class<? extends Student> c1 = student.getClass();
Class<Student> c2 = Student.class;
Class<?> c3 = Class.forName("com.reflect.Student");
System.out.println(c1.hashCode() + "\n" + c2.hashCode() + "\n" + c3.hashCode());
//基本数据类型的包装类中TYPE属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
//获得父类类型
Class<?> c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Student {...}
所有类型的Class对象
哪些类有Class对象:
- class:外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型的包装类
- void
Class<Object> c1 = Object.class;
Class<Comparable> c2 = Comparable.class;
Class<String[]> c3 = String[].class; //当数组长度不同时,Class对象仍相同,即hashCode相同
Class<Override> c4 = Override.class;
Class<Element> c5 = Element.class;
Class<Integer> c6 = Integer.class;
Class<Void> c7 = void.class;
Class<Class> c8 = Class.class;
类加载内存分析
类的加载过程分为:
- 加载:将class字节码文件加载到内存中,将静态数据转化为方法区运行时的数据结构,并生成该类的java.lang.Class对象
- 链接:将Java的二进制代码合并到JVM的运行状态中
- 准备:是否符合JVM规范,语法问题等
- 验证:为static对象分配内存空间和初始值,在方法区中进行分配
- 解析:常量名替换为地址
- 初始化:
- 执行类构造器clinit()方法,将静态变量、方法合并
- 若父类未初始化,则现将父类初始化
- 保证clint()方法线程安全
package com.reflect;
/**
* 类加载内存分析
* 1、在方法区中产生Demo4类和Test类的基本数据
* 2、在堆中加载产生Demo4和Test的Class对象
* 3、链接,为静态变量m分配内存空间和初始值=0
* 4、初始化,执行clint方法,合并静态变量、方法,即clinit(){
* System.out.println("静态代码块");
* m = 10;
* m = 5; //和先后顺序有关
* }
*/
public class Demo4 {
public static void main(String[] args) {
Test test = new Test();
System.out.println(Test.m);
}
}
class Test {
static {
System.out.println("静态代码块");
m = 10;
}
static int m = 5;
public Test(){
System.out.println("构造方法");
}
}
以上程序类加载内存分析
分析类的初始化
何时类会被初始化:类的主动引用
- 首先初始化main方法所在的类
- new一个对象的类
- 调用类中的静态成员或静态方法
- 反射调用,java.lang.reflect
- 当初始化一个类时,若父类未被初始化,则先初始化父类
类的被动引用,不会发生类的初始化:
- 访问静态域时,只有真正声明这个域的类才会被初始化,例如,当使用子类访问父类的静态变量时,不会子类初始化
- 数组定义类的引用,不会初始化
- 引用常量final也不会初始化(常量在链接阶段已存入类的常量池中)
package com.reflect;
//测试类的初始化
public class Demo5 {
static {
System.out.println("Main");
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用会被初始化
//Son son = new Son();
//Class.forName("com.reflect.Son");
//int c = Son.c;
//被动引用不会初始化
//int a = Son.a;
//Son[] son = new Son[5];
int b = Son.b;
}
}
class Father {
static int a = 1;
static {
System.out.println("父类");
}
}
class Son extends Father {
static final int b = 2;
static int c = 3;
static {
System.out.println("子类");
}
}
类加载器
类加载器作用:将.class文件加载到内存中,并将静态数据转换为方法区的数据结构,然后在堆中生成这个类的Class对象,作为方法区中类数据的访问入口。
类缓存:一旦某个类被加载到类加载器中,将会维持一段时间,JVM垃圾回收机制可回收这些Class对象
JVM定义了以下类型的类加载器:
- 引导类加载器:用C++编写,是JVM自带类加载器,负责Java平台核心库,加载核心类库。该加载器无法直接获取
- 扩展类加载器ExtClassLoader:将jre/lib/ext下的jar包装入工作库
- 系统类加载器AppClassLoader:将已知目录下的类和jar包装入工作库,是最常用的类加载器
引导类加载器是扩展类加载器的父类,扩展类加载器是系统类加载器的父类
package com.reflect;
public class Demo6 {
public static void main(String[] args) throws ClassNotFoundException {
//系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//系统类加载器的父类-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//扩展类加载器的父类-->引导类加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1); //无法获得引导类加载器,因此输出为null
//当前类的类加载器,即当前类是由哪个类加载器加载的
ClassLoader classLoader = Class.forName("com.reflect.Demo6").getClassLoader();
System.out.println(classLoader);
//系统内部的类是由哪个类加载器加载的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
//系统类加载器可加载类的路径
String property = System.getProperty("java.class.path");
System.out.println(property);
/*
输出为:
F:\JDK\jre\lib\charsets.jar;
F:\JDK\jre\lib\deploy.jar;
F:\JDK\jre\lib\ext\access-bridge-64.jar;
F:\JDK\jre\lib\ext\cldrdata.jar;
F:\JDK\jre\lib\ext\dnsns.jar;
F:\JDK\jre\lib\ext\jaccess.jar;
F:\JDK\jre\lib\ext\jfxrt.jar;
F:\JDK\jre\lib\ext\localedata.jar;
F:\JDK\jre\lib\ext\nashorn.jar;
F:\JDK\jre\lib\ext\sunec.jar;
F:\JDK\jre\lib\ext\sunjce_provider.jar;
F:\JDK\jre\lib\ext\sunmscapi.jar;
F:\JDK\jre\lib\ext\sunpkcs11.jar;
F:\JDK\jre\lib\ext\zipfs.jar;
F:\JDK\jre\lib\javaws.jar;
F:\JDK\jre\lib\jce.jar;
F:\JDK\jre\lib\jfr.jar;
F:\JDK\jre\lib\jfxswt.jar;
F:\JDK\jre\lib\jsse.jar;
F:\JDK\jre\lib\management-agent.jar;
F:\JDK\jre\lib\plugin.jar;
F:\JDK\jre\lib\resources.jar;
F:\JDK\jre\lib\rt.jar;
E:\code\basic\out\production\javabasic;
E:\code\basic\javabasic\src\com\lib\commons-io-2.8.0.jar;
F:\IDEA\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar
*/
}
}
双亲委派机制
根据IT烂笔头的博客通俗易懂的双亲委派机制
利用这种机制,可防止用户替换系统级别的类,例如String.java,防止危险代码的植入
获取类运行时的结构
利用反射可获取类运行时的字段(Field),方法(Method),构造器(Constructor),父类(SuperClass),接口(Interface),注解(Annotation)
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获取类的信息
public class Demo7 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("com.reflect.Student"); //Demo2中的Student类
System.out.println(c1.getName()); //包名+类名
System.out.println(c1.getSimpleName()); //类名
Field[] f1 = c1.getFields(); //仅能获得public属性
Field[] f2 = c1.getDeclaredFields(); //可获得所有属性
for (Field field : f1) {
System.out.println(field);
}
for (Field field : f2) {
System.out.println(field);
}
Field name = c1.getDeclaredField("name"); //获得指定属性,使用c1.getField("name")方法,仅能获得指定public属性
System.out.println(name);
Method[] m1 = c1.getMethods(); //获得本类及父类的所有public方法
for (Method method : m1) {
System.out.println(method);
}
Method[] m2 = c1.getDeclaredMethods(); //获得本类所有方法,包括私有方法
for (Method method : m2) {
System.out.println(method);
}
//获得指定方法
Method m3 = c1.getMethod("getName", null);//null表示获取的该方法没有参数
Method m4 = c1.getMethod("setName", String.class);//setName方法的参数为String类型
System.out.println(m3);
System.out.println(m4);
Constructor<?>[] constructor1 = c1.getConstructors();
for (Constructor<?> constructor : constructor1) {
System.out.println(constructor);
}
Constructor<?>[] constructor2 = c1.getDeclaredConstructors();
for (Constructor<?> constructor : constructor2) {
System.out.println(constructor);
}
//获得指定构造器
Constructor<?> constructor3 = c1.getConstructor(String.class, int.class); //括号中为构造器参数类型
System.out.println(constructor3);
}
}
动态创建对象(创建和操作对象)
有了类的Class后,可用Class.newInstance()方法创建类的对象(和new类似),本质是调用无参构造器。需满足
- 该类需要有一个无参构造器
- 需要有类的无参构造器权限
当类中没有无参构造器时,可采用构造器来创建对象
- 通过Class类中的getDeclaredConstructor(Class...)方法获得有参构造器
- 使用构造器的newInstance(...)创建对象,括号中的参数为类有参构造器中的参数,需要传形参
- 使用Constructor实例化对象
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo8 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> c1 = Class.forName("com.reflect.Student");
//Student student = (Student) c1.newInstance(); //本质是调用无参构造器,没有无参构造时,将会出错
//System.out.println(student.toString());
Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
Object s1 = constructor.newInstance("AA", 11); //创建为Object类,因此不可调用Student的方法
System.out.println(s1);
//通过反射调用普通方法
//通过反射获取一个方法
Method method = c1.getDeclaredMethod("setName", String.class);
method.invoke(s1, "ABCD"); //invoke为激活,参数为(对象, 激活方法所传的形参)
System.out.println(s1); //创建是s1是为Object,因此不可调用Student的getName方法,需要强制转换为Student类
//通过反射操作属性
Field name = c1.getDeclaredField("name");
//使用反射无法操作私有属性,但通过name.setAccessible(true)方法,可操作类中的所有属性
name.setAccessible(true);
name.set(s1, "CDA");
System.out.println(s1);
}
}
Field,Method和Constructor都有setAccessible安全监测方法,当参数值为true时,表示取消权限安全检查,参数为false表示开启权限安全检查
性能分析
package com.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//性能分析
public class Demo9 {
//普通方式
public void test1() {
Student student = new Student("A", 11);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
student.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式时间:"+ (endTime - startTime));
}
//反射方式
public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Student student = new Student("A", 11);
Class<? extends Student> studentClass = student.getClass();
Method getName = studentClass.getDeclaredMethod("getName");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(student);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式时间:"+ (endTime - startTime));
}
//关闭检测的反射方式
public void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Student student = new Student("A", 11);
Class<? extends Student> studentClass = student.getClass();
Method getName = studentClass.getDeclaredMethod("getName");
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(student);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式时间:"+ (endTime - startTime));
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
new Demo9().test1();
new Demo9().test2();
new Demo9().test3();
}
}
反射操作泛型
- Parameterized:表示一种参数化类型,例如Collection
- GenericArrayType:表示元素类型是参数化类型或类型变量的数组类型
操作过程如下:
- 通过Class.getMethod方法获得Method
- 通过Method的getGenericParameterTypes方法获得参数类型
- 通过Method的getGenericReturnType方法获得返回值类型
- 用xxxx instanceof ParameterizedType判断是否属于结构化参数类型,若是,则通过getActualTypeArguments返回实际的参数类型
package com.reflect;
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 Demo10 {
public void test(Map<String, Integer> map, List<Student> list) {
}
public Map<String, Student> test1() {
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method test = Demo10.class.getMethod("test", Map.class, List.class);
Type[] genericParameterTypes = test.getGenericParameterTypes(); //getGenericParameterTypes获得参数类型
for (Type genericParameterType : genericParameterTypes) { //对该方法的参数类型进行输出
System.out.println(genericParameterType);
if (genericParameterType instanceof ParameterizedType) { //是否该参数类型为结构化参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); //getActualTypeArguments获取实际的参数类型
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
System.out.println("============================");
Method test1 = Demo10.class.getMethod("test1");
Type genericParameterTypes1 = test1.getGenericReturnType(); //获取返回值类型
if (genericParameterTypes1 instanceof ParameterizedType) { //是否该参数类型为结构化参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterTypes1).getActualTypeArguments(); //getActualTypeArguments获取实际的参数类型
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
反射操作注解
ORM:对象关系映射
数据库中
- 类映射为表
- 属性映射为字段
- 对象映射为记录
操作过程:
- 类注解:Class.getAnnotation(xxx)方法参数值为所需注解的Class,同样也有getAnnotations方法
- 字段注解:先获得所需字段的Field,再通过Field的getAnnotation(xxx)方法,xxx为所需字段注解的Class,然后再通过返回的注解直接可获得注解的值
package com.reflect;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//反射操作注解
public class Demo11 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> c1 = Class.forName("com.reflect.Student2");
//获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取类的注解value的值
test1 annotation = c1.getAnnotation(test1.class);
System.out.println(annotation.value());
//获取字段注解的值
//先获取字段
Field field = c1.getDeclaredField("name");
test2 annotation1 = field.getAnnotation(test2.class);//参数为注解的Class
System.out.println(annotation1.name());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
}
@test1(value = "Student2")
class Student2 {
@test2(name = "name", type = "String", length = 10)
private String name;
@test2(name = "age", type = "int", length = 20)
private int age;
public Student2(String name, int age) {
this.name = name;
this.age = age;
}
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 "Student2{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface test1 {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface test2 {
String name();
String type();
int length();
}