08 反射与注解
目录
1.Java类加载机制
类加载器
双亲委派模型
工作流程
优点
2.反射
基本概念
常见用法
1. 获取 Class 对象
2.获取构造方法
3.获取成员方法
4.获取成员变量
3.注解
注解的基本概念
定义和使用注解
定义注解
使用注解
解释
元注解详解
常见内置注解
总结
1.Java类加载机制
ava 类加载机制是 Java 运行时环境的核心组成部分之一,它负责将字节码文件加载到内存中,并对其进行解析和初始化。Java 类加载器采用了一种称为“双亲委派模型”的机制,这种机制确保了类的加载过程是安全和高效的。
Java 类加载器主要负责以下几个任务:
-
加载:将字节码文件从文件系统或网络中读取到内存中。
-
验证:确保加载的字节码文件符合 JVM 规范,没有安全问题。
-
准备:为类的静态变量分配内存,并设置默认初始值。
-
解析:将符号引用替换为直接引用。
-
初始化:执行类的初始化代码,即静态初始化块和静态变量的赋值操作。
类加载器
Java 类加载器主要有以下几种:
-
Bootstrap ClassLoader(引导类加载器)
-
负责加载核心的 Java 类库,如
java.lang.*
、java.util.*
等。 -
通常由 C++ 编写,是 JVM 的一部分。
-
不可以通过 Java 代码直接访问。
-
-
Extension ClassLoader(扩展类加载器)
-
负责加载 Java 的扩展库,如
javax.*
包下的类。 -
通常加载位于
JRE/lib/ext
目录下的 JAR 文件。
-
-
Application ClassLoader(应用类加载器)
-
负责加载应用程序的类路径(classpath)上的类。
-
通常加载用户自定义的类。
-
-
自定义类加载器
-
用户可以根据需要编写自定义的类加载器,继承
java.lang.ClassLoader
类。
-
双亲委派模型
双亲委派模型是 Java 类加载器的一种层次关系,其主要目的是为了保证类的唯一性和安全性。在双亲委派模型中,当一个类加载器收到类加载请求时,它会先委托其父类加载器去加载该类,只有当父类加载器无法加载时,才会尝试自己加载。
工作流程
-
检查是否已经加载:
-
类加载器首先检查该类是否已经被加载过,如果已经加载,则直接返回已加载的类。
-
-
委托给父类加载器:
-
如果类未被加载,则委托给父类加载器去加载。
-
-
父类加载器递归委托:
-
父类加载器重复上述步骤,直到到达最顶层的 Bootstrap ClassLoader。
-
-
加载类:
-
如果所有父类加载器都无法加载该类,则由当前类加载器尝试加载。
-
优点
-
避免类的重复加载:
-
通过双亲委派模型,确保了类的唯一性,避免了不同类加载器加载同一个类导致的问题。
-
-
安全性:
-
核心类库由 Bootstrap ClassLoader 加载,确保了核心类库的安全性,防止被恶意代码篡改。
-
2.反射
Java 反射(Reflection)是一种强大的机制,允许程序在运行时动态地获取类的信息并操作对象。通过反射,你可以获取类的构造器、方法、字段等信息,甚至可以在运行时调用私有方法和修改私有字段。以下是关于 Java 反射的一些基本概念和常见用法。
基本概念
-
Class 类:
-
Class
是 Java 反射的核心类,代表类的类型信息。 -
可以通过
Class.forName
、对象的.getClass
方法或直接使用类的.class
字段来获取Class
对象。
-
-
Constructor 类:
-
表示类的构造器。
-
可以通过
Class
对象的getConstructors
、getDeclaredConstructors
、getConstructor
和getDeclaredConstructor
方法获取。
-
-
Method 类:
-
表示类的方法。
-
可以通过
Class
对象的getMethods
、getDeclaredMethods
、getMethod
和getDeclaredMethod
方法获取。
-
-
Field 类:
-
表示类的字段。
-
可以通过
Class
对象的getFields
、getDeclaredFields
、getField
和getDeclaredField
方法获取。
-
常见用法
1. 获取 Class
对象
-
通过类名获取对象
-
通过对象getClass()方法获取
-
通过.class属性获取
// 通过类名获取 Class<?> clazz1 = Class.forName("com.example.MyClass"); // 通过对象获取 MyClass obj = new MyClass(); Class<?> clazz2 = obj.getClass(); // 通过类的 .class 属性获取 Class<?> clazz3 = MyClass.class;
2.获取构造方法
-
获取所有构造方法
-
获取指定参数类型构造方法
// 获取所有public的构造器 Constructor<?>[] constructors = MyClass.class.getConstructors(); // 获取指定类型参数构造器 Constructor<MyClass> constructor = MyClass.class.getConstructor(int.class, String.class);
3.获取成员方法
-
获取public方法
-
获取所有方法,包含private的
// 获取指定方法 Method method = MyClass.class.getMethod("myMethod", String.class); // 调用方法 String result = (String) method.invoke(obj, "parameter"); // 获取所有public方法 Method[] methods = MyClass.class.getMethods(); // 获取所有方法,包含private Method[] privateMethods = MyClass.class.getDeclaredMethods();
4.获取成员变量
-
获取public变量
-
获取所有变量,包括private的
// 获取所有public的成员变量 Field[] fields = MyClass.class.getFields(); // 获取所有属性,包括private的成员变量 Field[] privateFields = MyClass.class.getDeclaredFields(); // 获取指定字段 Field field = MyClass.class.getDeclaredField("myField"); // 设置字段可访问(即使它是私有的) field.setAccessible(true); // 设置字段值 field.set(obj, "new value"); // 获取字段值 String value = (String) field.get(obj);
定义一个Person类
public class Person {private String name;private Integer age; public Person() {} //私有构造private Person(String name){this.name = name;} public Person(String name, Integer age) {this.name = name;this.age = age;} public String getName() {return name;} public void setName(String name) {this.name = name;} public Integer getAge() {return age;} public void setAge(Integer age) {this.age = age;} @Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';} }
简单练习:
public class ReflectTest {public static void main(String[] args) {try {System.out.println("=========反射获取对象:通过全限定类名=========");// 反射:通过类名获取类对象,类名必须是全限定类名:包名+类名Class<?> clazz = Class.forName("com.haha.test.Person");System.out.println(clazz); System.out.println("=========反射获取对象:通过对象getClass()方法=========");// 反射:通过类名获取类对象Person person = new Person("xiaoming", 18, "male");Class<?> clazz2 = person.getClass();System.out.println(clazz2); System.out.println("=========反射获取对象:通过.class属性=========");// 反射:通过类名获取类对象Class<?> clazz3 = Person.class;System.out.println(clazz3); System.out.println("=========反射获取所有public的构造方法=========");Constructor<?>[] constructors = clazz.getConstructors();for (Constructor<?> constructor : constructors) {System.out.println(constructor);} System.out.println("=========反射获取指定参数类型的构造方法=========");Constructor<?> constructor = clazz.getConstructor(String.class, int.class, String.class);System.out.println(constructor); System.out.println("=========反射获取所有public的成员方法=========");Method[] methods = clazz.getMethods();for (Method method : methods) {System.out.println(method);} System.out.println("=========反射获取私有的成员方法=========");Method[] privateMethods = clazz.getDeclaredMethods();for (Method method : privateMethods) {System.out.println(method);} System.out.println("=========反射获取所有public的成员变量=========");Field[] fields = clazz.getFields();for (Field field : fields) {System.out.println(field);} System.out.println("=========反射获取所有属性,包括private的成员变量=========");Field[] privateFields = clazz.getDeclaredFields();for (Field field : privateFields) {System.out.println(field);// 设置字段可访问(即使它是私有的)field.setAccessible(true); // 获取字段名称和类型String fieldName = field.getName();Class<?> fieldType = field.getType(); // 根据字段类型设置新值if (fieldType.equals(String.class)) {field.set(person, "New " + fieldName);} else {field.set(person, 35);} // 打印字段值System.out.println(field.get(person));}} catch (Exception e) {System.out.println(e.getMessage());}} }
3.注解
注解(Annotations)是 Java 语言的一个重要特性,用于提供元数据信息,这些信息可以被编译器、运行时环境或其他工具使用。注解本身并不会直接影响程序的行为,但可以通过反射机制在运行时读取这些注解,并根据注解的信息执行相应的逻辑。
注解的基本概念
-
元注解:
-
元注解是用于注解其他注解的注解。常见的元注解包括:
-
@Retention
:指定注解的保留策略。 -
@Target
:指定注解可以应用的目标。 -
@Documented
:指定注解是否会被包含在 JavaDoc 中。 -
@Inherited
:指定注解是否可以被子类继承。
-
-
-
内置注解:
-
@Override
:表示方法重写父类或实现接口中的方法。 -
@Deprecated
:表示方法或类已过时。 -
@SuppressWarnings
:抑制编译器警告。
-
-
自定义注解:
-
开发者可以自定义注解,以满足特定的需求。
-
定义和使用注解
定义注解
定义一个注解需要使用 @interface
关键字。以下是一个简单的自定义注解示例:
java深色版本
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 指定注解的保留策略为运行时 @Retention(RetentionPolicy.RUNTIME) // 指定注解可以应用于方法和字段 @Target({ ElementType.METHOD, ElementType.FIELD }) public @interface MyAnnotation {// 注解的属性String value() default "";int id() default -1; }
使用注解
在类、方法或字段上使用自定义注解:
java深色版本
public class MyClass { @MyAnnotation(value = "Hello", id = 1)private String myField; @MyAnnotation(value = "World", id = 2)public void myMethod() {System.out.println("This is my method.");} public static void main(String[] args) {MyClass obj = new MyClass();obj.myMethod(); // 通过反射读取注解信息readAnnotations(obj);} private static void readAnnotations(Object obj) {// 获取类的 Class 对象Class<?> clazz = obj.getClass(); // 获取类的所有字段Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 检查字段是否有 MyAnnotation 注解if (field.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);System.out.println("Field: " + field.getName());System.out.println("Value: " + annotation.value());System.out.println("ID: " + annotation.id());}} // 获取类的所有方法Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {// 检查方法是否有 MyAnnotation 注解if (method.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);System.out.println("Method: " + method.getName());System.out.println("Value: " + annotation.value());System.out.println("ID: " + annotation.id());}}} }
解释
-
定义注解:
-
使用
@interface
关键字定义注解MyAnnotation
。 -
使用
@Retention(RetentionPolicy.RUNTIME)
指定注解的保留策略为运行时,这意味着注解信息会在运行时可用。 -
使用
@Target({ ElementType.METHOD, ElementType.FIELD })
指定注解可以应用于方法和字段。 -
注解包含两个属性
value
和id
,并分别设置了默认值。
-
-
使用注解:
-
在
MyClass
类中,使用@MyAnnotation
注解标注字段myField
和方法myMethod
。 -
在
main
方法中,创建MyClass
的实例并调用myMethod
。
-
-
通过反射读取注解信息:
-
使用
Class
对象获取类的所有字段和方法。 -
使用
isAnnotationPresent
方法检查字段或方法是否有MyAnnotation
注解。 -
使用
getAnnotation
方法获取注解实例,并读取注解的属性值。
-
元注解详解
-
@Retention:
-
RetentionPolicy.SOURCE
:注解仅保留在源代码中,编译时会被忽略。 -
RetentionPolicy.CLASS
:注解保留在字节码文件中,但运行时不可用。 -
RetentionPolicy.RUNTIME
:注解保留在字节码文件中,运行时可通过反射读取。
-
-
@Target:
-
ElementType.TYPE
:类、接口、枚举。 -
ElementType.FIELD
:字段。 -
ElementType.METHOD
:方法。 -
ElementType.PARAMETER
:方法参数。 -
ElementType.CONSTRUCTOR
:构造器。 -
ElementType.LOCAL_VARIABLE
:局部变量。 -
ElementType.ANNOTATION_TYPE
:注解类型。 -
ElementType.PACKAGE
:包。
-
-
@Documented:
-
指定注解是否会被包含在 JavaDoc 中。
-
-
@Inherited:
-
指定注解是否可以被子类继承。
-
常见内置注解
-
@Override:
-
表示方法重写父类或实现接口中的方法。
-
-
@Deprecated:
-
表示方法或类已过时,不推荐使用。
-
-
@SuppressWarnings:
-
抑制编译器警告。
-
总结
注解是 Java 中一个非常强大的特性,可以用于提供元数据信息,帮助编译器、运行时环境或其他工具更好地理解和处理代码。通过自定义注解和反射机制,可以实现灵活的代码管理和功能扩展。希望这些示例和解释对你有所帮助!