Java注解和反射机制笔记
注解
注解的作用:
-
不是程序本身,但可以对程序作出解释(这一点和注释(comment)没什么区别);
-
可以被其它程序(比如:编译器读取);
注解在哪可以使用?
-
我们可以附在package,class,method,field...上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
元注解
什么是元注解?
-
元注解的作用就是负责注解其它注解,Java提供了3个meta-annotation类型,它们被用来提供对其它annotation类型的说明;
-
这些类型可以在java.lang.annotation包中可以找到(@Target、@Retention、@Document、@Inherited);
元注解的作用:
-
@Target:用来描述注解的使用范围(即:被描述的注解可以使用在什么地方);
-
@Retention:表示什么级别保存该注解信息,用于描述注解的生命周期(SOURCE < CLASS < RUNTIME);
-
@Document:说明该注解将被包含在javadoc中;
-
@Inherited:说明子类可以继承父类中的该注解;
代码示例:
package com.zq.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 测试元注解
*/
@MyAnnotation
public class TestAnnotation {
public static void main(String[] args) {
}
@MyAnnotation
public void test(){}
}
/**
* 自定义一个注解
* Target表示有效的范围,目前设置的是在方法和类上都有效
* Retention表示什么级别时保留该注解,目前设置的是运行级别
*/
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation{
}
如何定义一个注解
用@interface定义即可。
代码示例:
package com.zq.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class TestAnnotation02 {
//注解中的参数如果没有默认值,那么必须手动赋值
@MyAnnotation02(name = "张三", school = {"清华大学", "北京大学"})
public void test(){
}
}
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02{
//定义的参数(注意:不是我们平时中的方法):参数类型 + 参数名 ()
String name() default "";
int age() default 0;
int id() default -1; //-1表示不存在,indexOf() == -1差不多
String[] school();
}
反射机制
Java的反射机制使Java语言变成了准动态性语言。
原理:
加载完类之后,在堆内存中的方法区中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了这个类的完整结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,通过这个镜子看到类的结构,所以,我们形象的称之为:反射。
反射的有点和缺点:
-
优点:可以实现动态创建对象的编译,提现出很大的灵活性;
-
缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么,并且它满足我们的要求。这类操作总是慢于直接执行相同的操作;
Class类
-
Class本身也是一个类;
-
Class对象只能由系统创建;
-
一个加载的类在JVM中只有一个Class实例;
-
一个Class对象对应的是一个加载到JVM的一个.class文件;
-
每个类的实例都会记得自己由哪个Class实例所生成;
-
通过Class可以完整的得到一个类中的所有被加载的结构;
-
Class类是Reflect的根源,针对任何你想动态加载、运行的类,都必须获取相对应的Class对象;
Class对象的创建:
-
通过Class.forName(类的全限定类名)创建;
-
通过类名.class创建;
-
通过对象.getClass()方法创建,注意:getClass()方法是Object类里面定义的一个方法;
-
基本数据类型的包装类通过类名.TYPE获取;
-
父类的Class对象可以通过子类的Class对象.getSuperclass()方法获取;
代码示例:
package com.zq.reflect;
public class TestReflect {
public static void main(String[] args) throws ClassNotFoundException {
//获取Student类的Class对象
//1.通过Class.forName()获取
Class c1 = Class.forName("com.zq.reflect.Student");
System.out.println(c1);
//2.通过(类名.class)获取
Class c2 = Student.class;
//3.用过对象的getClass()方法获取,是Object类的一个方法
Student student = new Student();
Class c3 = student.getClass();
//4.基本数据类型的包装类都有一个TYPE属性,通过这个属性可获取到包装类的Class对象
Class c4 = Integer.TYPE;
System.out.println(c4);
//5.要想获取到父类的Class对象,可以通过“子类的Class对象.getSuperclass()”方法获取
Class c5 = c1.getSuperclass();
System.out.println(c5);
System.out.println("=========================");
//一个类只有一个Class对象,可以看到它们的hashCode都一样
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
//pojo类
class Student{
private Integer id;
private String name;
private int age;
}
哪些类可以有Class对象
-
class:类
-
interface:接口
-
[]:数组
-
enum:枚举
-
annotation:注解@interface
-
primitive type:基本数据类型
-
void
代码示例:
package com.zq.reflect;
import java.lang.annotation.ElementType;
public class TestAllClass {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Runnable.class; //接口
Class c3 = int[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = int.class; //基本数据类型
Class c6 = ElementType.class; //枚举类
Class c7 = Override.class; //注解
Class c8 = void.class; //void
Class c9 = Class.class; //Class本身
System.out.println("类:" + c1);
System.out.println("接口:" + c2);
System.out.println("一维数组:" + c3);
System.out.println("二维数组:" + c4);
System.out.println("基本数据类型:" + c5);
System.out.println("枚举类:" + c6);
System.out.println("注解:" + c7);
System.out.println("void:" + c8);
System.out.println("Class本身:" + c9);
}
}
结果输出:
类:class java.lang.Object
接口:interface java.lang.Runnable
一维数组:class [I
二维数组:class [[I
基本数据类型:int
枚举类:class java.lang.annotation.ElementType
注解:interface java.lang.Override
void:void
Class本身:class java.lang.Class
Java内存分析
类的加载过程:
-
类的加载(Load):将class文件加载到内存,并创建一个java.lang.Class对象。此过程由类加载器完成。
-
类的连接(Link):将类的二进制数据合并到JRE中。
-
类的初始化(initialize):JVM进行对类的一个初始化。
package com.zq.reflect;
//执行顺序
public class Test01 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.a);
}
}
class A{
static {
System.out.println("A类的静态代码块进行初始化!");
a = 200;
}
static int a = 100;
public A(){
System.out.println("A类的构造方法进行初始化!");
}
}
结果输出:
A类的静态代码块进行初始化!
A类的构造方法进行初始化!
100
什么时候类会进行初始化?
-
类的主动引用(一定会发生类的初始化)
-
虚拟机启动时,会先初始化有main方法的类;
-
new一个类的对象;
-
调用类的静态成员(除了final常量和静态方法);
-
使用java.lang.reflect包的方法对类进行反射调用;
-
类的被动引用(不会发生类的初始化)
-
当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致类的初始化;
-
通过数组定义类的引用,不会触发此类的初始化;
-
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中);
Class类的常用方法(获取运行时类的完整结构)
-
获取类的类名、属性、方法、构造器;
package com.zq.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test02 {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
//获取到User类的Class对象
Class c1 = User.class;
//获取User对象的名字
String name = c1.getName(); //包名 + 类名
System.out.println("name1:" + name);
name = c1.getSimpleName(); //类名
System.out.println("name2:" + name);
System.out.println("===========================");
//获取指定的属性
// Field field = c1.getField("name"); //只能获取到public修饰的属性
// System.out.println(field);
Field field = c1.getDeclaredField("name"); //能获取到private修饰的属性
System.out.println(field);
System.out.println("==========================");
//获取类的所有属性
Field[] fields = c1.getFields(); //只能获取public修饰的所有属性
for(Field f : fields)
System.out.println("f1:" + f);
fields = c1.getDeclaredFields(); //能获取任意修饰符修饰的所有属性
for(Field f : fields)
System.out.println("f2:" + f);
System.out.println("==============================");
//获取指定的方法
Method method = c1.getMethod("setName", String.class);//只能获取到public修饰的方法
System.out.println("m1:" + method);
method = c1.getDeclaredMethod("setName", String.class);//能获取到任意修饰的方法
System.out.println("m2:" + method);
System.out.println("==================================");
//获取所有方法
Method[] methods = c1.getMethods(); //能获取到本类以及父类的所有public方法
for (Method m : methods)
System.out.println("ms1:" + m);
methods = c1.getDeclaredMethods(); //能获取到本类的所有方法
for (Method m : methods)
System.out.println("ms2:" + m);
System.out.println("==============================");
//获取指定构造器
// Constructor constructor = c1.getConstructor(null); //只能获取到public修饰的构造器
// System.out.println("cst1:" + constructor);
Constructor constructor = c1.getDeclaredConstructor(null); //能获取到任何修饰符修饰的构造器
System.out.println("cst2:" + constructor);
System.out.println("==================================");
//获取所有构造器
Constructor[] constructors = c1.getConstructors(); //获取到public修饰的所有构造器
for(Constructor cst : constructors)
System.out.println("csts1:" + cst);
constructors = c1.getDeclaredConstructors(); //获取到所有构造器
for (Constructor cst : constructors)
System.out.println("csts2:" + cst);
}
}
class User{
private Integer id;
private String name;
private int age;
User() {
}
public User(Integer id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer 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 "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
private void test(){}
}
-
动态的创建类的对象、并使用方法和属性(注意:使用对象的私有方法和属性时必须要关掉安全检测才能使用,关闭的方法为setAccessible(true))
package com.zq.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test03 {
public static void main(String[] args) throws Exception {
//获取User类的Class对象
Class c1 = User.class;
//通过无参构造器创建对象
User user1 = (User) c1.newInstance();
System.out.println("user1:" + user1);
System.out.println("================================");
//通过有参构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(Integer.class, String.class, int.class);
User user2 = (User) constructor.newInstance(1, "张三", 3);
System.out.println("user2:" + user2);
System.out.println("==================================");
//方法的调用
User user3 = (User) c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke表示激活,与对象进行绑定
setName.invoke(user3, "李四");
System.out.println(user3);
System.out.println("===================================");
//属性的使用(注意:私有属性赋值需要安全检测,要想使用必须关掉检测)
User user4 = new User();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //允许使用
name.set(user4, "王五");
System.out.println(user4);
}
}
不同方式调用方法测试性能(普通调用,反射不关闭安全检测调用,反射关闭安全检测调用)
package com.zq.reflect;
import java.lang.reflect.Method;
public class Test04 {
public static void main(String[] args) throws Exception {
test01();
test02();
test03();
}
//通过普通方法调用getName()10亿次
static void test01(){
User user = new User();
//执行开始时间
long start = System.currentTimeMillis();
for (int i = 0; i < 10_0000_0000; i++) {
user.getName();
}
//执行结束时间
long end = System.currentTimeMillis();
System.out.println("普通方法调用需要:" + (end - start) + "ms");
}
//通过反射不关闭安全检测调用getName()10亿次
static void test02() throws Exception{
Class c1 = User.class;
User user = (User) c1.newInstance();
Method method = c1.getDeclaredMethod("getName", null);
//执行开始时间
long start = System.currentTimeMillis();
for (int i = 0; i < 10_0000_0000; i++) {
method.invoke(user, null);
}
//执行结束时间
long end = System.currentTimeMillis();
System.out.println("反射不关闭安全检测调用需要:" + (end - start) + "ms");
}
//通过反射关闭安全检测调用getName()10亿次
static void test03() throws Exception{
Class c1 = User.class;
User user = (User) c1.newInstance();
Method method = c1.getDeclaredMethod("getName", null);
//关闭安全检测
method.setAccessible(true);
//执行开始时间
long start = System.currentTimeMillis();
for (int i = 0; i < 10_0000_0000; i++) {
method.invoke(user, null);
}
//执行结束时间
long end = System.currentTimeMillis();
System.out.println("反射关闭安全检测调用需要:" + (end - start) + "ms");
}
}
输出结果:
普通方法调用需要:1ms
反射不关闭安全检测调用需要:2677ms
反射关闭安全检测调用需要:1403ms
-
ParameterizedType:表示一种参数化类型,比如:Collection<String>;
-
GenericArrayType:表示一种元素类型是参数化类型或者是类型变量的数组类型;
-
TypeVariable:是各种类型变量的公共父接口;
-
WildcardType:代表一种通配符类型表示;
反射操作注解
package com.zq.reflect;
import java.lang.annotation.*;
import java.lang.reflect.Field;
/**
* 通过反射操作注解
*/
public class Test05 {
public static void main(String[] args) throws Exception {
//获取User的Class对象
Class c1 = Class.forName("com.zq.reflect.UserInfo");
//获取对象的注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations)
System.out.println("annotation:" + annotation);
System.out.println("================================");
//获取对象指定的注解的value值
TableUser tableAnnotation = (TableUser) c1.getAnnotation(TableUser.class);
String value = tableAnnotation.value();
System.out.println("value = " + value);
//获取属性注解的值
System.out.println("===========================================");
Field id = c1.getDeclaredField("id"); //首先通过获取到属性的信息
RowUser rowUser = id.getAnnotation(RowUser.class); //再通过属性获取注解
String columnName = rowUser.columnName();
String type = rowUser.type();
int length = rowUser.length();
System.out.println("columnName = " + columnName);
System.out.println("type = " + type);
System.out.println("length = " + length);
}
}
//定义pojo类
@TableUser("user_info")
class UserInfo {
@RowUser(columnName = "id", type = "int", length = 8)
private Integer id;
@RowUser(columnName = "name", type = "varchar", length = 20)
private String name;
@RowUser(columnName = "age", type = "int", length = 4)
private int age;
public UserInfo() {
}
public UserInfo(Integer id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer 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 "UserInfo{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//表注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableUser {
String value(); //表名
}
//表属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface RowUser {
String columnName(); //列名
String type();//属性类型
int length();//长度
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南