AwkingのBlog
行己所爱 爱己所行
2025 年 9 月
17
Wednesday
白日梦想出版社
AwkingのBlog
行己所爱 爱己所行
白日梦想出版社
利用反射机制获取到每个类对应的Class对象,一共有三种方法获取
类名.class
Class.forName()
使用Class类静态方法forName(),通过包名.类名,注意返回值是Class<?>getClass()
方法使用反射机制判断类型
public static void main(String[] args) {
String str = "";
System.out.println(str.getClass() == String.class); //直接判断是否为这个类型
}
判断是否为子类或者是接口/抽象类的实现,使用asSubClass()
方法:
public static void main(String[] args) {
Integer i = 10;
i.getClass().asSubclass(Number.class); //当Integer不是Number的子类时,会产生异常
}
使用getSuperclass()
方法获取当前父类的Class对象
public static void main(String[] args) {
System.out.println(Integer.class.getSuperclass());
}
使用getGenericSuperclass()
获取父类的原始类型的Type
使用类似的方法去获取父类的接口以及接口类型
public static void main(String[] args) {
for (Class<?> anInterface : String.class.getInterfaces()) {
System.out.println(anInterface.getName());
}
System.out.println("-----------------------------------");
for (Type genericInterface : String.class.getGenericInterfaces()) {
System.out.println(genericInterface.getTypeName());
}
}
获取到当前类的Class对象后,可以使用newInstance()
方法创建该类的对象
public class test01 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Class<Student> clazz = Student.class;
Student student = clazz.newInstance();
student.test();
}
}
class Student{
public void test(){
System.out.println("hello world");
}
}
但是newInstance()
只适用于无参构造,否则会出现InstantiationException异常
当默认无参构造的权限不是public
时,会出现IllegalAccessException异常,表示我们无权去调用默认构造方法。
在JDK9之后,不再推荐使用newInstance()
方法了,而是使用getConstructor()
方法来获取类的构造方法,使用的格式为
Class对象.getConstructor(构造方法形参1的class对象,构造方法形参2的class对象,...).newInstance(实参1,实参2,...)
示例:
public class test01 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<Student> clazz = Student.class; Student student = clazz.getConstructor(String.class,int.class).newInstance("123",123); }
} class Student{ Student(){
} public Student(String str,int x){ System.out.println("构造方法"); } public void test(){ System.out.println("hello world"); }
}
需要注意的是,该方法的前提是该有参构造函数的权限是public
,否则就需要使用getDeclaredConstructor()
方法,并使用setAccessible()
方法修改访问权限,然后再进行构造
public class test01 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<Student> clazz = Student.class; Constructor<Student> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class); declaredConstructor.setAccessible(true); Student student = declaredConstructor.newInstance("123",123); student.test(); }
} class Student{ Student(){
} Student(String str,int x){ System.out.println("构造方法"); } public void test(){ System.out.println("hello world"); }
}
使用getMethod()
方法可以获取到类中声明为public
的方法,得到一个Method
对象,使用该对象中的invoke()
方法(返回值为获取到的方法的返回值)调用已获取到方法,格式为
Method method = class对象.getMethod("对象中的public成员方法名",方法中形参的class对象,...);
method.invoke(该类的实例化对象,实参1,实参2,...);
示例:
public class test01 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<Student> studentClass = Student.class; Student student = studentClass.newInstance(); Method test = studentClass.getMethod("test", String.class); test.invoke(student,"fafafafd"); }
} class Student{ Student(){ } Student(String str,int x){ System.out.println("构造方法"); } public void test(String a){ System.out.println(a + "hello world"); } }
同构造方法一样,出现非public
方法时,使用getDeclaredMethod()
方法,然后使用Method
对象中的setAccessible(true)
无视权限修饰符调取方法
如果参数的类型是可变参数,实际就是该参数类型的数组,只需要传入该数组的Class对象即可
使用getField()
方法可以获取一个类定义的指定字段,得到一个Field
对象后使用该对象的set()
方法即可设定属性值
ublic class test01 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class<Student> studentClass = Student.class; Student student = studentClass.newInstance(); Field age = studentClass.getField("age"); age.set(student,100); System.out.println(student.age); }
} class Student{ public int age; Student(){ } Student(String str,int x){ System.out.println("构造方法"); } public void test(String a){ System.out.println(a + "hello world"); } }
如果该属性是private属性,也可以按照之前的操作使用getDeclaredField()
获取Field
对象,实现越权访问并设置属性值
public class test01 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class<Student> studentClass = Student.class; Student student = studentClass.newInstance(); Field age = studentClass.getDeclaredField("age"); age.setAccessible(true); age.set(student,100); System.out.println(student.age); }
} class Student{ int age; Student(){ } Student(String str,int x){ System.out.println("构造方法"); } public void test(String a){ System.out.println(a + "hello world"); } }
注解可以被标注在任意地方,包括方法上、类名上、参数上、成员属性上、注解定义上等,就像注释一样,它相当于我们对某样东西的一个标记。而与注释不同的是,注解可以通过反射在运行时获取,注解也可以选择是否保留到运行时
JDK预设了以下注解,作用于代码:
元注解是作用于注解上的注解,用于我们编写自定义的注解:
@Retention表示此注解的保留策略,使用时可以传入参数RetentionPolicy.*
,‘‘ * ’’有三种选择:CLASS
、SOURCE
、RUNTIME
@Target 限定注解的使用范围,使用时传入参数ElementType.*
,例如该注解只能标记方法,则使用@Target(ElementType.METHOD)
注解中还可以定义一些属性,也叫成员变量。注解只能有成员变量,不能有成员方法,注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String value();
}
默认只有一个属性,且该属性名称为value
时,赋值时不需要手动指定注解的属性名称
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String value();
}
public class Main { @Test("hello world") //括号内传入属性值 public static void main(String[] args) {
}
}
如果不是value
则需要指定属性名称后赋值
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String test();
}
public class Main { @Test(test = "hello world") public static void main(String[] args) {
}
}
使用default
关键字可以为属性指定默认值
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String value() defualt "hello world";
}
当属性存在默认值,使用注解不用传入属性,如果是数组,数组只有一个元素可以和普通属性一样传入一个值即可,如果是多个值则需要罗列出来用逗号隔开并套上花括号
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String[] test();
String[] test1();
}
@Test(test={"1231","123131"},test1 = {"123131","12313131"}) public class test01 {
public static void main(String[] args){ }
}
@Test(test={"1231","123131"},test1 = {"123131","12313131"}) public class test01 { public static void main(String[] args) { for (Annotation annotation : test01.class.getAnnotations()) { System.out.println(annotation.annotationType()); //注解类型 System.out.println(annotation instanceof Test); //判断是否为Test Test test = (Test) annotation; System.out.println(Arrays.toString(test.test())); //获取注解中的属性值 System.out.println(Arrays.toString(test.test1())); } }
}