学习内容
反射
将类的各个组成部分封装为其他对象的过程叫反射。
好处
- 可以在程序运行中操作这些对象
- 可以解耦,提高程序的可扩展性
Class 对象
获取 Class 对象的三种方式
-
类名.class 多用于参数的传递。 -
对象名.getClass() 多用于对象的获取字节码。 -
Class.forName(类的全名) 多用于配置文件
Class 对象的功能
- 获取成员变量
Field[] getFields() :获取所有 public 修饰的成员变量。Field getField(String fieldName) :获取指定的,public 修饰的成员变量。Field getDeclaredFields() :获取所有的成员变量,不考虑修饰符。Field getDeckaredField(String fieldName) :获取指定的成员变量,不考虑修饰符。setAccessible(true) :将访问权限打开,打开之后可以忽略修饰符的安全检查,从而访问所有修饰符修饰的字段,也称暴力反射。 - 获取构造方法
Constructor<?>[] getConstructors() :获取所有的 public 构造器。Constructor<?>[] getDeclaredConstructors() :获取所有的构造器。Constructor<T> getConstructor(类<?>... parameterTypes) :获取指定的,public 修饰的构造器。Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :获取指定的构造器,不考虑修饰符。 - 获取成员方法
Method[] getMehods() :获取所有的 public 方法。Method[] getMehod(String name, 类<?>... parameterTypses) : 获取指定的的 public 方法。Method[] getDeclaredehods() :获取所有方法。Method[] getDeclaredMehod(String name, 类<?>... parameterTypses) : - 执行获取的成员方法
(Method 对象).invoke(实例对象) ;
Field:成员变量
-
操作:
- 设置值
void set(Object obj, Object value)
- 获取值
- 忽略访问权限修饰符的安全检查
setAccessible(true) :暴力反射
Constructor:构造方法
- 创建对象:
T newInstance(Object... initargs) - 如果使用空参数构造方法创建对象,操作可以简化:
Class 对象的 newInstance 方法
Method :方法对象
-
执行方法:
Object invoke(Object obj, Object... args) -
获取方法名称:
注解
概念
注解(Annotation),也叫元数据。一种代码级别的说明。可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。但这种注释不是给人看的,是给计算机看的。
使用方式
@注解名称
作用
- 编写文档:通过代码里标识的注解生成文档
- 代码分析:通过代码里标识的注解对代码进行分析
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查
JDK 中一些预定义注解
@Override
检测被标注的方法是否是继承自父类的。
@Deprecated
该注解标注的内容已过时(就不推荐你使用这个内容,但是用了也不会出错)。
调用@Deprecated 标注的方法,方法会有一道横线划掉。例如 deprecatedMethod
@SuppressWarinings
压制警告:使编译器对于被标注内容的警告不再提示。一般会传递参数 (“all”)。
注解的本质
本质是一个接口,默认继承了 Annotation 接口。
public interface 注解名 extends java.lang.annotation.Annotation{}
属性
属性:接口中的抽象方法
- 要求:
- 属性的返回值类型有下列取值
- 在使用属性时,需要给属性赋值。
- 在定义属性时,可以后接
default 以设置默认初始化值,这样使用注解时就可以不用赋值。 - 如果只有一个属性需要赋值,并且属性的名称是 value ,则 value 可以省略,直接定义值即可。
- 数组赋值时,值使用 {} 包裹。如果数组中只有一个值,则 {} 可以省略
元注解
用于描述注解的注解
常用元注解
1.@Target:描述注解能够作用的位置
- ElementType取值:
- TYPE:可以作用于类上
- METHOD:可以作用于方法上
- FIELD:可以作用于成员变量上
- @Retention:描述注解被保留的阶段
@Retention(RetentionPolicy.RUNTIME) :当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。- SOURCE:
- CLASS:类对象阶段
- @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否被子类继承
自定义注解
格式
- 元注解
- public @interface 注解名称{}
工作内容
反射任务
任务1
使用反射解析某个你创建的类,把类中所有的属性、方法、接口都提取出来。要求:
通过三种不同的方式获取 Class 对象;
修改解析出来的类的属性值,然后再给类添加新的属性;
调用类方法(包括私有方法、静态方法和构造方法),执行后打印结果;
解析出该类的父类对象,并同样修改其父类的属性值并调用父类的方法。
答:
-
先定义类,及其方法、属性。
public class Animal {
int weight ;
public void sleep(){
System.out.println("动物在睡觉");
}
}
class Cat extends Animal {
public String name;
public int age;
public int count;
@Override
public void sleep() {
System.out.println("猫在睡觉");
}
public static void stop(){
System.out.println("静态方法,发呆,什么都不做");
}
private void catchMouse() {
System.out.println("抓老鼠");
}
public void Cat(String name) {
this.name = name;
}
public void Cat(int age) {
this.age = age;
}
public void Cat(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
-
用三种方法获取 Class 对象
Class cls1 = Cat.class;
Cat cat2 = new Cat();
Class cls2 = cat2.getClass();
Class cls3 = Class.forName("com.xxm.Mission8.Cat");
-
修改解析出来的类的属性值,然后再给类添加新的属性;
Class cls4 = Cat.class;
Field field = cls4.getDeclaredField("name");
Cat cat4 = new Cat();
Object value = field.get(cat4);
System.out.println(cat4.name);
System.out.println(value);
field.set(cat4, "field猫");
System.out.println(cat4.name);
-
调用类方法(包括私有方法、静态方法和构造方法),执行后打印结果;
Class cls5 = Cat.class;
Cat cat5 = new Cat();
Method eatMethod = cls5.getMethod("sleep");
eatMethod.invoke(cat5);
Method stopMethod = cls5.getDeclaredMethod("stop");
stopMethod.invoke(cat5);
Method catMouseMethod = cls5.getDeclaredMethod("catchMouse");
catMouseMethod.setAccessible(true);
catMouseMethod.invoke(cat5);
Constructor constructor = cls5.getConstructor(lass);
System.out.println(constructor);
-
解析出该类的父类对象,并同样修改其父类的属性值并调用父类的方法。
Class cls6 = cls5.getSuperclass();
System.out.println(cls6);
Method animalSleepMethod = cls6.getDeclaredMethod("sleep");
animalSleepMethod.invoke(cat5);
Animal animal1 = new Animal();
animalSleepMethod.invoke(animal1);
这里有一个很有意思的现象,在我使用 invoke 指令时,由于忘记重新创建一个 Animal 类的实例对象,我的invoke 方法传入的参数为前面创建的 cat5 ,结果调用的是在 Cat 类中被覆写过的 sleep 方法。可以得出 Method 对象的获取方法的方法,确实是根据传入的字符串来判断的,而实际调用的时候,会根据传入参数的类来决定调用哪个方法。
注解任务
任务1
-
自定义一个 ElementType 类别为 METHOD,RetentionPolicy 类别为 RUNTIME 的注解,任务要求:
答: 创建自定义注解 MyAnno1 : package com.xxm.mission9;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
public @interface MyAnno1 {
String value() default "这是MyAnno1的默认注解内容";
}
创建类 Cat :
package com.xxm.mission9;
public class Cat {
@MyAnno1
public void eat(){
System.out.println("猫猫吃猫粮");
}
}
- 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。
答:
Class class1 = Cat.class;
Method m = class1.getMethod("eat");
MyAnno1 anno1 = m.getAnnotation(MyAnno1.class);
System.out.println(anno1.value());
?
-
自定义一个 ElementType 类别为 FIELD,RetentionPolicy 类别为 CLASS 的注解,任务要求:
答: 创建自定义注解 MyAnno2 : package com.xxm.mission9;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno2 {
String className();
String fieldName();
String value() default "这是 MyAnno2 的默认内容";
}
? 将上一题中的 Cat 类添加字段,并添加自定义注解: ?
package com.xxm.mission9;
public class Cat {
@MyAnno1(className = "com.xxm.mission9.Cat", methodName = "eat")
public void eat() {
System.out.println("猫猫吃猫粮");
}
@MyAnno2(className = "com.xxm.mission9.Cat", fieldName = "name")
public int name;
}
-
模仿上面的嵌套注解,除了可以字段类型的注解外,再加上字段长度和是否可以为空的注解,并加入到@TableColumn 注解中去。 答: 字符串长度: @Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnLength {
int value() default 0;
}
字符串是否为空: public @interface ColumnEmptyOrNot {
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnLength {
boolean value() default true;
}
}
添加后的 @TableColumn : @Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {
ColumnType columntype() default @ColumnType;
ColumnLength columnlength() default @ColumnLength;
ColumnEmptyOrNot columnEmptyOrNot() default @ColumnEmptyOrNot;
}
? -
自定义嵌套注解,用来描述用户类中的「岗位」字段。
- 第一层注解定义岗位所在的部门;
- 第二层注解定义岗位名称和描述。
答:
岗位名称注解:
package com.xxm.advanced_camp.mission9.Task4;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PostName {
String postName() default "";
}
岗位描述注解:
package com.xxm.advanced_camp.mission9.Task4;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PostDescription {
String postDescription() default "";
}
部门注解:
package com.xxm.advanced_camp.mission9.Task4;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Department {
String departmentName() default "";
PostName postName() default @PostName;
PostDescription postDescription() default @PostDescription;
}
应用嵌套注解:
package com.xxm.advanced_camp.mission9.Task4;
public class Position {
@Department(postName = @PostName, postDescription = @PostDescription)
private String postName;
private String postDescription;
}
|