8-注解与反射【狂神】
注解-注解是给程序看的,注释是给人看的

//什么是注解
public class Test01 extends Object{
//重写的注解
@Override
public String toString() {
return "Hello World";
}
}
内置注解

package annotation;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("all")//压制警告,黄色的提示
//什么是注解
public class Test01 extends Object{
//重写的注解
@Override
public String toString() {
return "Hello World";
}
//不推荐程序员使用,但是可以使用 或者存在更好的方式
@Deprecated
public static void test(){
System.out.println("Hello World");
}
public void test2(){
List arrayList = new ArrayList();
}
public static void main(String[] args) {
test();
}
}
元注解

package annotation;
import java.lang.annotation.*;
//测试元注解
//一个类中只有一个public类
public class Test02 {
@MyAnnotation
public void test(){
}
}
//表示下面的注解只能用在哪些地方
@Target(value= ElementType.METHOD)//表示下面的注解只能用于方法
@Retention(value= RetentionPolicy.RUNTIME)//表示注解的生命周期,在运行时有效,runtime>class>source
@Documented//表示注解应该生成在javadoc中
@Inherited//表示子类可以继承父类的注解
@interface MyAnnotation {//自定义一个注解
}
自定义注解,承接上面元注解

package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
public class Test03 {
//注解可以显示赋值,如果定义了参数但是没有默认值,就必须给注解赋值
@MyAnnotation(name="haha")
public void test() {
}
@MyAnnotation2("hello")
public void test2() {
}
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
//注解的参数:参数类型、参数名()
//有了参数要不就在下面定义默认值,又不在上面赋值,也可以在上面重新赋值
String name();//String name() default '';这样的话就是默认值为空,所以上面的注解就不用加参数
int age() default 18;
int id() default -1;//如果默认值 ,代表不存在,indexof,如果找不到,就返回-1
String[] schools() default {"清华大学","北京大学"};
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
String value();//用了value,上面不可以不用加参数,直接赋值就行
}
反射-JAVA通过反射变成了动态语言
案例补充
当new一个对象时,是先生成class对象,然后才生成new对象吗?
| 步骤 | 行为 | 关键说明 |
|---|---|---|
| 首次访问类 | 当首次 new 对象、访问静态成员或调用静态方法时 | 如果类尚未加载,JVM 会触发完整的类加载流程。如果已加载,则跳过此过程。 |
| 类加载阶段 | JVM 加载、验证、准备、解析(Linking),并初始化类。 | - 加载(Loading): 加载字节码生成对应的 Class 对象。 - 初始化(Initialization): 执行静态代码块和显式赋值。 |
| 创建实例 | 为对象分配内存,调用构造函数 (new) | 此时类的 Class 对象已完成初始化,据此创建实例。 |
代码示例
java
public class Cat {
static {
System.out.println("1. 静态代码块执行 → 类被初始化");
}
public Cat() {
System.out.println("3. 构造函数执行 → 实例被创建");
}
}
public class Test {
public static void main(String[] args) {
System.out.println("0. 开始创建实例");
new Cat(); // 首次访问Cat类,触发类加载和初始化
}
}
输出结果
0. 开始创建实例
1. 静态代码块执行 → 类被初始化
3. 构造函数执行 → 实例被创建
关键结论
当首次 new 对象时:
类的加载(生成 Class 对象)和初始化必须完成,然后才能创建实例。因此 Class 对象的生成早于实例的创建。
如果类已加载过:
直接复用已有的 Class 对象,不再重复加载,直接创建实例。
触发类加载的典型场景:
new 实例。
访问类的静态字段或方法(如 Cat.staticMethod())。
反射(如 Class.forName("Cat"))。
流程图解
new Cat()
│
▼
类 Cat 是否已加载? 否 ➔ 执行类加载(生成Class对象) ➔ 初始化(静态块、静态变量赋值)
│ 是
▼
调用构造函数创建实例
总结
在 Java 中,首次通过 new 创建对象时,一定会先确保该类的 Class 对象存在并完成初始化。后续的 new 操作则无需重复加载类,直接基于已有的 Class 对象创建新实例。这是保证运行时一致性和安全性的关键机制。
以上的案例:输出结果中1. 静态代码块执行 → 类被初始化,是类加载的结果(生成class对象,然后执行static语句,初始化,然后才执行new对象中的构造方法)
总结:当你new一个对象时,会先生成class对象,然后再执行static代码块,然后才执行构造器,
new出来的对象是正射,


获得反射对象-class类型的对象,包含了这个类所有的信息,存在方法区


package reflection;
//什么叫反射
public class Test02 {
public static void main(String[] args)throws Exception {
//通过反射获取类的Class对象
Class<?> c1 = Class.forName("reflection.User");
System.out.println(c1);
Class<?> c2 = Class.forName("reflection.User");
Class<?> c3 = Class.forName("reflection.User");
//一个类在内存中哈哈有一个class对象
//一个类被加载后,类的整个结构都会被封装在这个class对象中
System.out.println(c1.hashCode());//189568618
System.out.println(c2.hashCode());//189568618
System.out.println(c3.hashCode());//189568618
}
}
//实体类
class User {
private String name;
private int age;
private int id;
public User() {
}
public User(String name, int age, int id) {
this.name = name;
this.age = age;
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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}

得到class类的几种方式



package reflection;
//测试class类创建方式有哪些
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException {
Person person =new Student();
System.out.println("这个人是:"+person.name);
//方式一:通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());//793589513
//方式二:forname获得
Class<?> c2 = Class.forName("reflection.Student");
System.out.println(c2.hashCode());//793589513
//方式三:类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());//793589513
//方式四:基本内置类型的包装类都有一个TYPE属性,可以获得对应的Class对象
Class c4 = Integer.TYPE;
System.out.println(c4);//int
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);//class reflection.Person
}
}
class Person {
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
}
class Student extends Person {
public Student() {
this.name = "Student";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "Teacher";
}
}
哪些类型可以有Class对象
package reflection;
import java.lang.annotation.ElementType;
//所有类型的class
public class Test04 {
public static void main(String[] args) throws Exception {
Class<Object> c1 = Object.class;//类
Class<Comparable> c2 = Comparable.class;//接口也有class对象
Class<String[]> c3 = String[].class;//数组也有class对象
Class<int[][]> c4 = int[][].class;//二维
Class<Override> c5 = Override.class;//注解
Class c6 = ElementType.class;//枚举
Class<Integer> c7 = Integer.class;//基本数据类型
Class<Void> c8 = void.class;//void
Class<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(c8);
System.out.println(c9);
//class java.lang.Object
//interface java.lang.Comparable
//class [Ljava.lang.String;
//class [[I
//interface java.lang.Override
//class java.lang.annotation.ElementType
//class java.lang.Integer
//void
//class java.lang.Class
int[]a=new int[10];
int[]b=new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());//都是189568618,证明一个类只有一个class对象
}
}
类加载的内存分析




将class文件加载到内存中
第一步:将类的信息加载到方法区中-加载
类的数据信息1,静态变量。2,静态方法3,常量池(类名)4,代码
第二步:生成一个class对象到堆中-加载
第三步:开始执行main方法(在栈中),给静态变量初始值static m=0
第四步:new A(),A类的对象,这个对象指向Class A对象,能拿到信息- 在堆中
第五步:将静态方法和静态变量合并起来,形成《clinit》-初始化
ackage reflection;
public class Test05 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
//A类静态代码块初始化
//A类无参构造函数初始化
//2
/**
1、加载到内存,会产生一个类对应的class对象
2、链接,链接结束后m=0
3,初始化,
<clinit>(){
System.out.println("A类静态代码块初始化");
m=3;
m=2;
}
m=2;
*/
}
}
class A {
static {//会先加载静态代码块
System.out.println("A类静态代码块初始化");
m=3;
}
static int m=2;
public A() {
System.out.println("A类无参构造函数初始化");
}
}
案例补充!!
创建 Class 对象
当 JVM 首次使用一个类时(比如 new、调用静态方法),会触发以下流程:
加载(Loading):
找到 .class 文件(本地、网络、动态生成等),将字节码加载到内存。
此时生成此类的 Class 对象(每个类对应一个唯一的 Class 实例)。
连接(Linking)
验证(Verification):确保字节码符合 JVM 规范。
准备(Preparation):为静态变量分配内存并初始默认值。
解析(Resolution):将符号引用转换为直接引用(地址)。
初始化(Initialization)
执行类的
至此,Class 对象包含完整的类元数据(方法、字段、注解等),并可通过反射访问。
代码示例
步骤 1:定义测试类
用一个包含 静态变量 和 静态代码块 的类 MyClass,观察各阶段变化:
java
public class MyClass {
// 初始化阶段的显式赋值,初始值为 "JVM默认值" (准备阶段时会被改为默认值0)
public static int year = 2024;
public static String author = "Unknown";
static {
// 静态代码块将在初始化阶段执行
System.out.println("### 初始化阶段:执行静态代码块 ###");
author = "程序员";
}
}
步骤 2:分阶段观察 Class 对象
通过 Class.forName() 控制类加载流程,观察 year 和 author 的默认值变化:
java
public class ClassLoadingDemo {
public static void main(String[] args) throws Exception {
// 1. 强制触发类加载(加载、验证、准备),但不初始化
Class<?> clazz = Class.forName("MyClass", false, ClassLoader.getSystemClassLoader());
System.out.println("[已加载类] Class对象创建完成");
// 2. 准备阶段:查看静态变量默认值(int=0, String=null)
Field yearField = clazz.getField("year");
Field authorField = clazz.getField("author");
System.out.println("\n[准备阶段] 静态变量默认值:");
System.out.println("year = " + yearField.getInt(null)); // 输出:0
System.out.println("author = " + authorField.get(null)); // 输出:null
// 3. 强制触发初始化阶段(此时执行静态代码块和显式赋值)
Class.forName("MyClass", true, ClassLoader.getSystemClassLoader());
System.out.println("\n[初始化阶段] 静态变量最终值:");
System.out.println("year = " + yearField.getInt(null)); // 输出:2024
System.out.println("author = " + authorField.get(null)); // 输出:程序员
}
}
输出结果
[已加载类] Class对象创建完成
[准备阶段] 静态变量默认值:
year = 0
author = null
### 初始化阶段:执行静态代码块 ###
[初始化阶段] 静态变量最终值:
year = 2024
author = 程序员
关键机制总结
Class 对象诞生于加载阶段:即使类未初始化,Class 对象已生成,可通过反射查看字段和方法等的元数据。
准备阶段的默认值:静态变量在此时被赋予类型默认值(如 int → 0),而非代码中的显式初始值。
初始化阶段的动态性:通过
通过这个案例,你会发现 Class 对象从诞生到完成的整个过程,其实就是 JVM 一步步将“静态的字节码”转化为“可执行的动态类”的旅程。
分析类的初始化

package reflection;
//测试类什么时候会初始化
public class Test06 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
//Son son = new Son();
//Main类被加载
//父类被加载
//子类被加载
//反射也会产生主动引用
//Class.forName("reflection.Son");//和主支引用的结果一样
//2.不会产生类的引用的方法
//System.out.println(Son.b);
//Main类被加载
//父类被加载
//2
/**
* 在Java中,静态变量(使用static修饰的变量)是属于类的,而不是属于类的实例。因此,子类不会继承父类的静态变量。子类可以访问父类的静态变量,但这并不意味着继承。实际上,子类是通过父类名来访问这个静态变量的。
* 在你的代码中,Son.b实际上是访问了Father类中的静态变量b,因为b是在Father类中定义的静态变量。这就是为什么在执行System.out.println(Son.b);时,会加载Father类并执行其静态代码块的原因。Java规范规定,访问一个类的静态成员会触发该类的初始化,如果该类有直接或间接的父类,父类也会被初始化。
*/
//Son[] array = new Son[10];//Main类被加载`
System.out.println(Son.M);//Main类被加载\1
}
}
class Father{
static int b=2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m=300;
}
static int m = 100;
static final int M = 1;
}
类加载器


package reflection;
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
//获得系统类的加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@4617c264
//获取系统类加载器的父类加载器
ClassLoader parent = classLoader.getParent();
System.out.println(parent);//jdk.internal.loader.ClassLoaders$PlatformClassLoader@b4c966a
//获取扩展类加载器的父类加载器--根加载器(C/C++)
ClassLoader ext = parent.getParent();
System.out.println(ext);//null
//测试当前类是哪个加载器加载的
ClassLoader classLoader1 = Class.forName("reflection.Test07").getClassLoader();
System.out.println(classLoader1);//jdk.internal.loader.ClassLoaders$AppClassLoader@4617c264
//测试JDK内置类是哪个加载器加载的
ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader2);//null
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));//C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_
//双亲委派机制,如果你手写了跟内置类同名的类,根加载器会优先加载内置类,而不会加载你手写的类。
}
}
获取类的运行时结构
package reflection;
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 ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("reflection.User");
//获得类的名字
System.out.println(c1.getName());//reflection.User包名+类名
System.out.println(c1.getSimpleName());//User类名
//通过对象获得类的信息
User user = new User();
Class<?> c2 = user.getClass();
System.out.println(c2.getName());
System.out.println(c2.getSimpleName());
//获得类的属性
Field[] fields = c1.getFields();//只能找到public的属性
fields = c1.getDeclaredFields();//找到全部的属性
for (Field field : fields) {
System.out.println(field);
}
//private java.lang.String reflection.User.name
//private int reflection.User.age
//private int reflection.User.id
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
Method[] methods = c1.getMethods();
methods = c1.getDeclaredMethods();
for (Method method : methods) {//获得本类及其父类的全部public方法
System.out.println(method);
}
methods = c1.getDeclaredMethods();//获得本类全部的方法
for (Method method : methods) {
System.out.println(method);
}
//获得指定方法
Method getName = c1.getMethod("getName",null);
System.out.println(getName);//public java.lang.String reflection.User.getName()
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);//public void reflection.User.setName(java.lang.String)
//获得指定的构造器
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
}
}
动态创建对象执行方法




package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//动态的创建对象,通过反射
public class Test09 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得class对象
Class<?> c1 = Class.forName("reflection.User");
//构造一个对象
User user = (User) c1.newInstance();//本质是调用了类的的无参构造方法,适合无参构造器New对象的方法,如果构造器是有参构造器就得用下面了
System.out.println(user);//User{name='null', age=0, id=0}
//通过构造器创建对象
Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("Tom", 20, 100);
System.out.println(user2);//User{name='Tom', age=20, id=100}
//通过反射调用普通方法
User user3 = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke;激活的意思
//(对象,方法的值 )
setName.invoke(user3, "jeff");
System.out.println(user3.getName());//jeff
//通过反射操作属性
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要设置访问权限。关闭程序的安全检查,才能操作私有属性
name.setAccessible(true);
name.set(user4, "haha");
System.out.println(user4.getName());//haha
}
}
性能对比分析
package reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//分析性能问题
public class Test10 {
//普通方式调用
public static void test1() {
User user = new User();
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endtime = System.currentTimeMillis();
System.out.println(endtime - starttime);
}
//反射方式调用
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<? extends User> c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName");
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user);
}
var endtime = System.currentTimeMillis();
System.out.println(endtime - starttime);
}
//反射方式调用 关闭检测
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<? extends User> c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName");
getName.setAccessible(true);
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user);
}
var endtime = System.currentTimeMillis();
System.out.println(endtime - starttime);
}
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
test1();//5
test2();//5134
test3();//4290
}
}
获取泛型信息


浙公网安备 33010602011771号