反射
反射是将类抽象为一个Class对象。将类看成对象,分析它的构造方法,成员变量,方法以及内部类。
对类的分析,是将类抽象为Class对象;对构造方法的分析,是将构造方法抽象为Constructor类的对象;对成员变量的分析,是将变量抽象为Feild类的对象;对方法的分析,是将方法抽象为Method类的对象。
举个例子:
public class Student {
public String name;//定义学生姓名
private int age;//定义学生年龄
private String sex;//定义学生性别
//获得学生姓名的方法
public String getName() {
return name;
}
//学生姓名赋值的方法
public void setName(String name) {
this.name = name;
}
//获得学生年龄的方法
public int getAge() {
return age;
}
//注意,这里没有为学生年龄赋值的方法,学生年龄为私有类型。
//获得学生性别的方法
public String getSex() {
return sex;
}
//学生性别赋值的方法
public void setSex(String sex) {
this.sex = sex;
}
}
public class Test {
public static void main(String[] args) {
Class<Student> clazz = Student.class;
try {
//Class Field Method的使用。
//获得Student类的name属性对象。
Field field1 = clazz.getDeclaredField("name");
//返回name字段的修饰符
System.out.println(field1.getModifiers());
//返回name字段的类型
System.out.println(field1.getType());
//返回name字段的名称
System.out.println(field1.getName());
//获得Student类的所有属性对象。
Field[] field2 = clazz.getDeclaredFields();
//遍历所有属性
for (Field field : field2) {
System.out.println(field.getName());
System.out.println(field.getType());
System.out.println(field.getModifiers());
}
//为学生对象zhangsan没有set方法的私有类型age赋值
Student zhangsan = new Student();
//获得age属性的对象
Field age = clazz.getDeclaredField("age");
//取消对age属性的修饰符的检查访问,以便为age属性赋值
age.setAccessible(true);
//为age属性赋值
age.set(zhangsan, 18);
//回复对age属性的修饰符的检查访问
age.setAccessible(false);
System.out.println(zhangsan.getAge());
//给name字段设置内容
field1.set(zhangsan,"zhangsan");
//Method 的使用
Method method1 = clazz.getDeclaredMethod("getName");
System.out.println(method1.getModifiers());
//请求方法
Object invoke = method1.invoke(zhangsan, null);
System.out.println(((String) invoke));
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
注解(Annotation)
注解的定义形式:
@interface 注解名{
<成员类型> <成员名称>() default "<默认值>";
……
}
下面来个例子:
新建一个注解,声明用于字段上
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnno {
int value();
}
使用情况:
public class Test {
@TestAnno(12)
int china;
public static void main(String[] args) throws Exception {
// TestStudent();
testAnno();
}
private static void testAnno() throws NoSuchFieldException {
Class<Test> testClass = Test.class;
Field china = testClass.getDeclaredField("china");
Annotation[] annotations = china.getAnnotations();
TestAnno annotation = china.getAnnotation(TestAnno.class);
int value = annotation.value();
System.out.println(value);
}
}
通过反射可以拿到字段上的注解和值
再测试一个类上的注解:新建一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestAnnoForClass {
int value();
}
使用情况:
@TestAnnoForClass(13)
public class Test {
@TestAnno(12)
int china;
public static void main(String[] args) throws Exception {
Class<Test> testClass = Test.class;
// TestStudent();
// testAnno(testClass);
TestAnnoForClass annotation = testClass.getAnnotation(TestAnnoForClass.class);
int value = annotation.value();
System.out.println(value);
}
}
在类上注解我们可以做一些有意思的事情,比如扫描包下所有带此注解的类,并加载到一个容器里
public static void main(String[] args) throws Exception {
Class<Test> testClass = Test.class;
// TestStudent();
// testAnno(testClass);
// testAnnoForClass(testClass);
Package aPackage = testClass.getPackage();
Map<String, Object> stringObjectMap = new HashMap<>();
List<Class<?>> allClassByPackageName = ClassUtil.getAllClassByPackageName(aPackage);
for (Class<?> aClass : allClassByPackageName) {
TestAnnoForClass annotation = aClass.getAnnotation(TestAnnoForClass.class);
if (annotation != null) {
stringObjectMap.put(aClass.getSimpleName(), aClass.newInstance());
}
}
System.out.println(stringObjectMap.size());
}
在测试包下仅仅有3个类有TestAnnoForClass 注解,所以在测试中的map里仅实例加载了3个类
这样我们在自己写的架构里就可以像Spring里维护Bean一样操作了