6.2.2 方案二
步骤:
(1) 先获取这个类的CLass对象
(2) 先获取有参构造器对象
(3) 调用构造器对象的newInstance()方法来创建实例对象
代码演示如下:
public class testDemo { private String name; private int id; public testDemo(String name, int id) { this.name = name; this.id = id; } @Override public String toString() { return "testDemo{" + "name='" + name + '\'' + ", id=" + id + '}'; } }
Class c3=Class.forName("testP1.testDemo"); Constructor construct = c3.getDeclaredConstructor(String.class,int.class); Object o1 = construct.newInstance("jack",12); System.out.println(o1);
🔔如果构造器是非公共的,那么需要调用 构造器对象的setAccessible(true)
🚩结论:
为了后期很多的框架可以为你的类自动创建的对象更方便,请保留你这个类公共的无参构造。
6.3 动态的操作任意对象的任意属性
步骤:
(1) 获取类的CLass对象
(2) 通过CLass对象newInstance()
🔔前提条件是这个类有公共的无参构造
(3) 先获取id属性的Field对象
(4) 可选的,如果属性的权限修饰符允许,这一步可以不要
如果属性的权限修饰符不允许,可以加这步通过id属性对应的Field对象.setAccessible(true)
(5)操作id居性的值通过id属性对应的Field对象.get(实例对象)获取id属性的值通过id属性对应的Field对象.set(实例对象,值)设置id属性的值
代码演示如下:
package testP1; public class Student { private String name; private int id; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", id=" + id + '}'; } }
@Test public void test02() throws Exception{ //通过反射创建Student类的对象,通过反射给student类的对象的id和name属性赋值,并通过反射获取它们的值 Class<?> aClass = Class.forName("testP1.Student");//获取模块Testmodule下的testP1.Student类的class对象 Object stud = aClass.newInstance();//获取Student类的实例对象 Field idField = aClass.getDeclaredField("id");//获取id属性的Field对象 idField.setAccessible(true);//将Student类id属性的权限修饰符设为允许访问,之前是private,不允许访问 System.out.println(idField.get(stud));//获取id属性的值 ==> stud.id idField.set(stud,2);//给stud的id属性赋值 ==> stud.id=2; System.out.println(idField.get(stud)); System.out.println("----------------------------------"); //给stud的name属性赋值 Field nameField = aClass.getDeclaredField("name");//获取name属性的Field对象 nameField.setAccessible(true); System.out.println(nameField.get(stud)); nameField.set(stud,"张三"); System.out.println(nameField.get(stud)); System.out.println(stud); }
6.4 在运行时动态的调用任意类的任意方法
步骤:
(1) 获取CLass对象
(2) 创建这个类的实例对象
🔔前提条件是这个类有公共的无参构造
(3) 先获取你要调用的方法的Method对象
(4) 调用方法
通过Method对象.invoke(实例对象,实参)
代码演示如下:
public class testMain { public int doubleValue(int a){ return a*2; } public long doubleValue(long a){ return a*2; } }
@Test public void test03() throws Exception{ Class<?> aClass = Class.forName("testP1.testMain");//获取class对象 Object testMain = aClass.newInstance();//获取testP1.testMain类的实例对象 Method doubleValueMethod = aClass.getDeclaredMethod("doubleValue", int.class); /* 如何唯一的确定某个类的方法? (1) 类: 通过cLazz对象确定是testMain类 (2) 方法名: doubleValue (3) 形参列表:因为有可能doubleValue会有重载 */ Object returnValue = doubleValueMethod.invoke(testMain, 10);//获取返回值 System.out.println(returnValue); } @Test public void test04() throws Exception{ Class<?> aClass = Class.forName("testP1.testMain");//获取class对象 Object testMain = aClass.newInstance();//获取testP1.testMain类的实例对象 Method doubleValueMethod = aClass.getDeclaredMethod("doubleValue", long.class); System.out.println(doubleValueMethod.invoke(testMain,20)); }
6.5 通过反射操作某个类的静态变量和静态方法
🤮反射操作某个类的静态方法
步骤:
(1) 获取CLass对象
(2) 先获取你要调用的方法的Method对象
(3) 调用方法
通过Method对象.invoke(实例对象,实参)
🤮反射操作静态变量的步骥
(1) 获取CLass对象
(2)获取要换作/访问的静态变量的Field对象
(3)某个静态变量对应的FieLd对象.setAccessible(true);
(4)可以访问静态变量的值,或者给静态变量赋值某个静态变量对应的Field对象.get(null)就是获取静态变量的值某个静态变量对应的Field对象.set(null,值)就是设置静态变量的值
代码演示如下:
public class testMain { static int b=1; public static void getInfo(String info){ System.out.println("testMain.method"); System.out.println("info:"+info); } public int doubleValue(int a){ return a*2; } public long doubleValue(long a){ return a*2; } }
@Test public void test05() throws Exception{ //通过反射调用testP1.testMain类的静态方法 Class<?> aClass = Class.forName("testP1.testMain");//获取class对象 Method getInfo = aClass.getDeclaredMethod("getInfo", String.class); getInfo.invoke(null,"java");//null 表示这里不需要testP1.testMain类的对象,它是静态方法;Java相当于getInfo方法的实参 System.out.println("---------------------------------"); //通过反射调用testP1.testMain类的静态变量 Field bField = aClass.getDeclaredField("b"); bField.setAccessible(true); System.out.println(bField.get(null));//null 表示这里不需要testP1.testMain类的对象,它是静态变量 bField.set(null,10); System.out.println(bField.get(null));//null 表示这里不需要testP1.testMain类的对象,它是静态变量 }
七. 自定义注解与反射
7.1 注解
@Override: 标记某个重写的方法
@Deprecated: 标记某个类、方法已过时
@SuppressWarnings: 抑制警告
@Test: JUnit测试的标记
7.2 自定义注解
语法格式:
[修饰符] @interface 注解名 {
}
7.3 如何使用自定义注解
可以在类、方法、成员变量等上面加注解
7.4 自定义注解包含三个部分
7.4.1 声明
public @interface MyAnnotation { }
7.4.2 使用
@MyAnnotation public class MyClass { }
7.4.3 读取
如果没有 读取部分,前面的代码就完全没用。
就好比说,
@Override
声明: public @interface Override {
}
使用: class Son extends Father{
@Override //注解的使用
public void method() {
System.out.println(“son .method”) ;
}
读取:
编译器会读取这个注解,会对方法进行格式检查
7.5 元注解
给注解加注释的注解
@Target : 用来解释/注释某个注解可以用在哪里可以用
注解的位置一共有10个。
ELementType是一个枚举类型,每一个常量对象,代表一个注解可以使用的位置
ELementType.TYPE: 表示是类型上面
ELementType.FIELD: 表示是属性/字段/成员变量上面
@Retention: 用来解释/注释某个注解的生命周期
每一个注解的生命周期有3个阶段:
SOURCE: 源代码
CLASS: 字节码
RUNTIME: 运行时,内存中
@Documented: 用来解释/注释某个注解是不是可以被javadoc工具读取到API
@Inherited: 用来解释/注释某个注解是不是可以被子类继承
代码演示如下:
import java.lang.annotation.*; @Inherited //该元注解表明子定义注解可被子类继承 @Target(ElementType.TYPE)//a该自定过注解只能放在类类型上 @Retention(RetentionPolicy.RUNTIME)//代表它的生命周期是在运行时,只有这样,才能被反射读到 public @interface MyAnnotation { String getValue(); //自定义的注解中只能写无参的抽象方法 String info(); //可以使用 default 关键字为抽象方法指定默认返回值 /* 抽象方法的返回值类型有限制: 只能是八种基本数据类型、String类型、CLass类型、enum类型、Annotation类型、以上所有类型的数组 */ }
@YourAnnotation @MyAnnotation(getValue ="jack",info = "你好") /* @Override public String getInfo(String str){ return str } */ public class MyClass { }
public class MySub extends MyClass { }
import java.lang.annotation.Annotation; public class TestAnnotation { public static void main(String[] args) { Class aClass=MyClass.class; //获取Annotation对象 Annotation annotation = aClass.getAnnotation(MyAnnotation.class); System.out.println(annotation); MyAnnotation my=(MyAnnotation)annotation; //需要向下转型,才能调用MyAnnotation对象的抽象方法 System.out.println(my.getValue()); System.out.println(my.info()); System.out.println("---------------------"); Class c1=MySub.class; //获取Annotation对象 Annotation annotation1 = c1.getAnnotation(MyAnnotation.class); System.out.println(annotation1); } }
public @interface YourAnnotation { }
🔔注意:
1.抽象方法的返回值类型有限制:
只能是八种基本数据类型、String类型、CLass类型、enum类型、Annotation类型、以上所有类型的数组
2.自定义的注解中只能写无参的抽象方法,在无参的抽象方法中可以使用 default 关键字为抽象方法指定默认返回值