[Java 09] 注解与反射 2021.11.14 天啊一个坑一个坑的
2021/11/14 9:10:04
本文主要是介绍[Java 09] 注解与反射 2021.11.14 天啊一个坑一个坑的,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
JAVA注解与反射
1. Java. Annotation
-
入门Java. Annotation
annotation的作用:1. 对程序做出解释;2. 可以被其他程序读取
Annotation格式:@注解名,也可以带参数:@SuppressWarnings(value = "unchecked")
Annotation 在package, class, method, field上附加,相当于添加辅助信息,可以通过反射机制对元数据访问
-
内置注解(@Override;@Deprecated; @SuppressWarnings 镇压警告:eg:@SuppressWarnings("all"),镇压所有警告,不报任何警告提示。
-
元注解
注解其他注解的注解,有四个标准的meta-annotation类型:
@Target:用于描述注解的使用范围
@Retention: 表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)最常用的就是RUNTIME,表示在运行时注解依然可用
@Documented: 说明该注解将被包含在javadoc中
@Inherited: 说明子类可以继承父类中的该注解
-
自定义注解(通常Target和Retention是必须的)
格式:@interface 注解名{定义内容}
定义内容:
参数类型 参数名()
, eg: String value(); 可以通过default声明参数默认值。(这里可以理解为:注解时一个接口,参数是由函数返回的,所以以函数的形式声明参数)
注解元素必须要有值,定义注解元素时,长使用空字符串,0,作为默认值
@Target(value = ElementType.METHOD)//可以自定义一些元注解,注解使用范围,不会就看源码 @interface MyAnnotation{ int age() default 0; String name() default ""; }
2. Java. Reflection
-
语言类型:
动态语言:运行时可以改变自身结构的语言,eg:python,JS。。。
静态语言: 运行时结构不变,Java是准动态,反射机制可以使Java从静态变为准动态
-
反射机制允许程序在执行期间借助reflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。(接口,字段,方法,属性等)
-
正常:引入需要的“包类”名称----> new实例化 ----> 得到实例化对象
反射:实例化对象 ---> getClass()----> 得到完整的“包类”名称
-
Java反射机制提供的功能:
运行时,判断任意一个对象所属的类,构造任意一个类的对象,获取泛型信息,处理注解,生成动态代理
-
优缺点:1. 实现动态创建对象和编译,很灵活 2. 对性能有影响,是一种解释操作
-
相关的API:
java.lang.Class: 代表一个类 (大写!C)
java.lang.reflect.Method/Field/Constructor代表类的方法,成员变量,构造器
3. 获取Class类
-
创建一个实体类(只有属性的类,其中的方法都是继承父类Object的)
class User{ int age; int String; public User() { } //打印Class类的hashcode():是一个static方法 sout(c1.hashCode());
获得Class类的方法:
//1. 已知类,通过类的class属性获得 Class c1 = User.class; //2. 实例对象方法getClass() Class c2 = user.getClass(); //3. 已知某个类的路径和全名(参数是包路径.类名, 为字符串类型) Class c3 = Class.forName("path.User"); //4. 基本内置类(JDK定义的类,都有一个TYPE属性)(自定义的没有)(primitive type:基本数据类型) Class c4 = Integer.TYPE;
4. 所有类型的Class类(有Class类的类型)
Class c1 = Object.class;//类 Class c2 = Comparable.class;//接口 Class c3 = String[].class;//数组 Class c4 = Override.class;//注解 Class c5 = ElementType.class;//枚举类 Class c6 = Integer.class;//基本数据类型 Class c7 = void.class;//void类型
5. 类加载内存分析
1. 类加载过程:
-
类的加载Load:将class文件加载到内存,把这些静态数据转换成方法区的运行时数据结构中,堆中创建Class对象
-
类的连接Link:将类的二进制数据合并到JRE中,(验证,准备,解析)
验证:检查是否符合JVM规范
准备:正式为类变量(static)分配内存,设置默认初始值(0,null), 这些内存都在方法区中分配
解析:JVM常量池内的符号引用(常量名)替换为直接引用(地址)的过程。(自己定义的类名是引用,去找类地址
-
类的初始化Initiate:
JVM调用
()方法:JVM执行类构造器,把类变量和静态代码块(都是static的)合并的过程 如果初始化一个类,发现父类没有初始化,则先出发父类初始化
JVM会保证此方法在多线程中正确加锁和同步
Eg:
public class Test02 { public static void main(String[] args) { A a = new A(); System.out.println(A.m); } } class A{ static { System.out.println("A类静态代码块初始化"); m = 300; } static int m = 100; public A() { System.out.println("A类的无参构造初始化"); } }
2. 内存分析:(栈,堆,方法区)
-
Load:
@方法区: 在方法区中加载 Test02类的数据: //静态变量 //静态方法 //常量池 //代码 A类的数据: //同上
@堆: 生成Class类对象 java.lang.Class 代表Test02类 java.lang.Class 代表A类
-
Link
-
JVM检查
-
准备:
@栈 开始执行Main() m = 0//初始化值都是0
-
解析:
@堆: new A(),找到A类的Class对象,然后链接到方法区中的数据
-
-
Initiate
调用
方法 @栈: m =100//这里合并A类中的静态变量和静态方法(哪个在前先执行哪个),所以m 先=300, 后又=100
总结:
- 加载到内存,产生对于的Class类
- 链接,静态变量初始值为0,m = 0
- 初始化:
(){静态代码块执行}
6. 类初始化
-
主动引用:(一定会发生初始化)
虚拟机启动时,初始化main方法所在的类;
new一个类时
调用类的static成员或方法时
使用java.lang.reflect包方法对类进行反射调用时
加载类发现父类未初始化时,去初始化父类
(验证时,写sout在静态代码块中)
-
类的被动引用(不会发生类的初始化)
访问static域时,只有真正声明这个域的类才会被初始化。通过子类引用父类的静态变量,只有父类初始化
通过数组定义类引用,不会触发此类的初始化(son[] array = new Son[5];只对空间命名,不初始化类)即内存分析中,只调用了Load和Link,不调用
引用常量不会出发此类的初始化(常量在链接时就存入类的常量池中了)(同上)
7. 类加载器
1. 类加载器
Java文件
java编译器编译为.class
文件,通过类装载器、字节码校验器、解释器,给操作系统
类加载器:把class文件字节码加载到内存中,方法区产生运行时数据结构,堆中产生Class类对象。
类缓存:类加载器可以按照要求查找类,一旦某个类被加载到类加载器中,将维持加载一段时间,然后被gc回收。
类加载器的作用是把类装载进内存,缓存是为了提高效率
2. Java的类加载器有
引导类加载器 Bootstrap Classloader:JVM自带加载器,负责Java平台核心库,用来装载核心类库(rt.jar)
扩展类加载器 Extension Classloader :负责 jre/lib/ext 目录下的jar包加载
系统类加载器 System Classloader :负责项目中的类包加载工作,是最常用的加载器
//获取系统类加载器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println(classLoader); //获取系统类加载器的父类加载器,扩展类加载器 ClassLoader extendLoader = classLoader.getParent(); System.out.println(extendLoader); //获取扩展类加载器的父类加载器,引导类加载器(返回值为null,因为是c++写的,) ClassLoader bootstrapLoader = extendLoader.getParent(); System.out.println(bootstrapLoader); //sout: sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@14ae5a5 null //获取自己写的类的类加载器:返回依然是系统类加载器,也叫做APPLoader/BootstrapLoader ClassLoader myClassLoader = Class.forName("AnnotationReflection.Test02").getClassLoader(); System.out.println(myClassLoader); //返回AppLoader
3. ClassLoader的方法
获取运行时类的完整结构, 包括:
Field, Method, Constructor, SuperClass, Interface,Annotation
class Test03{ private int age; private String name; public Test03(){} } Class c1 = Class.forName("packetname.Test03"); c1.getName();//返回 包名.类名 c1.getSimpleName();//返回 类名 c1.getDeclaredField();//返回全部属性,没有declared,只返回public的属性 //方法中,打印出的时方法的声明,: 修饰符 返回类型 方法名(参数) 抛出的异常, 都有 c1.getMethod();//如果带参数,则为:String.class,参数也必须时Class类 c1.取得构造器、修饰符等等。。。。。 Constructor constructor = c2.getConstructor(String.class, int.class, int.class); sout(constructor);
8. 动态创建对象 执行方法
package AnnotationReflection; class User{ private int age; private String name; //constructors public User() { } public User(int age, String name) { this.age = age; this.name = name; } @Override public String toString() { return "User{" + "age=" + age + ", name='" + name + '\'' + '}'; } //set and get public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
1. 通过反射创建对象
package AnnotationReflection; public class Test03 { public static void main(String[] args) throws Exception { Class c1 = Class.forName("AnnotationReflection.User"); Constructor constructor = c1.getConstructor(int.class, String.class); User user = (User) constructor.newInstance(18, "Roy"); System.out.println(user); } }
2. 通过反射调用方法,设置属性
invoke():激活 使用方法: XXMethod.invoke(对象名, 传入参数);
package AnnotationReflection; public class Test03 { public static void main(String[] args) throws Exception { //创建对象 Class c1 = Class.forName("AnnotationReflection.User"); Constructor constructor = c1.getConstructor();//调用无参构造 User user = (User) constructor.newInstance(); System.out.println(user); //反射调用方法 Method setName = c1.getDeclaredMethod("setName", String.class); setName.invoke(user, "Roy");//方法.激活(给对象,传递的值) System.out.println(user.getName()); //反射设置属性 Field age = c1.getDeclaredField("age"); age.setAccessible(true);//允许访问私有属性 age.set(user, 18);//和invoke一样 System.out.println(user.getAge()); } }
9. 反射操作泛型
Java采用泛型擦除机制引入泛型,泛型只是给编译器javac使用的,确保安全和免去强制类型转换的问题,一旦编译后,和泛型有关的类型全部擦除。
为了通过反射操作这些类型,java用集中类型来代表不能被归一到Class类中,但又和原始类型齐名的类型:
ParameterizedType: 表示一种参数化的类型:Collection
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。比如限制了key和value类型的字典
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式
! ParameterizedType 是 GenericArrayType的父类,
public class Test04 { public void method01(Map<String, Integer> map, List<String> list){}//方法一 public List<String> method02(){ return null; } public static void main(String[] args) throws Exception{ Class c1 = Test04.class; Method method01 = c1.getMethod("method01", Map.class, List.class); Type[] genericParameterTypes = method01.getGenericParameterTypes(); //打印出genericParamater的Type数组 for (Type genericParameterType : genericParameterTypes) { System.out.println(genericParameterType); } //打印出每一个限定参数化类型的参数 for (Type genericParameterType : genericParameterTypes) { if(genericParameterType instanceof ParameterizedType){//如果ge是P的子类,强制转换并用getATA方法获取数组 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } } } //打印结果: java.util.Map<java.lang.String, java.lang.Integer> java.util.List<java.lang.String> class java.lang.String class java.lang.Integer class java.lang.String
10. 反射操作注解
ORM: Object relationship Mapping 对象关系映射:一个类正好是数据库的一张表
即: 类和表结构对应, 属性和字段对应,对象和记录对应(对象和行对应)
反射操作注解例子:利用注解和反射完成类和表结构的映射关系
获取类的注解:
public class Test05 { public static void main(String[] args) throws Exception{ //1. 通过反射,获得类注解(总体的) Class c1 = Class.forName("AnnotationReflection.Student"); Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //2. 强制转换为自定义注解类型(从Annotation接口转变为TableStudent接口),获得value值 TableStudent tableStudent = (TableStudent) annotations[0]; System.out.println(tableStudent.value()); //or TableStudent tableStudent1 = (TableStudent)c1.getAnnotation(TableStudent.class); System.out.println(tableStudent1.value()); //3. 获得属性注解的值 FieldStudent fieldStudent = (FieldStudent)c1.getDeclaredField("age").getAnnotation(FieldStudent.class); System.out.println(fieldStudent.columnName()); System.out.println(fieldStudent.type()); System.out.println(fieldStudent.length()); } } @TableStudent("db_Student") class Student{ @FieldStudent(columnName = "SID",type = "int", length = 10) private int ID; @FieldStudent(columnName = "AGE", type = "int", length = 10) private int age; @FieldStudent(columnName = "NAME", type = "varchar", length = 3) private String name; public Student() { } public Student(int ID, int age, String name) { this.ID = ID; this.age = age; this.name = name; } // set and get @Override public String toString() {return "Student{" +"ID=" + ID +", age=" + age +", name='" + name + '\'' +'}';} } //类名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableStudent{ String value(); } //属性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldStudent{ String columnName(); String type(); int length(); }
这篇关于[Java 09] 注解与反射 2021.11.14 天啊一个坑一个坑的的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南