Java面向对象编程6——注解与反射
Java面向对象编程6——注解与反射
1、注解概述
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制
Java 语言中的类、方法、变量、参数和包等都可以被标注。Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标
可简单理解为标签
2、元注解
元注解顾名思义我们可以理解为注解的注解
@Retention(注解保留时期)
-
@Retention(RetentionPolicy.SOURCE),注解仅存在于源码中,在class字节码文件中不包含
-
@Retention(RetentionPolicy.CLASS), 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
-
@Retention(RetentionPolicy.RUNTIME), 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
@Target(注解作用的范围)
- @Target(ElementType.TYPE) 作用接口、类、枚举、注解
- @Target(ElementType.FIELD) 作用属性字段、枚举的常量
- @Target(ElementType.METHOD) 作用方法
- @Target(ElementType.PARAMETER) 作用方法参数
- @Target(ElementType.CONSTRUCTOR) 作用构造函数
- @Target(ElementType.LOCAL_VARIABLE)作用局部变量
- @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
- @Target(ElementType.PACKAGE) 作用于包
- @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
- @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
}
@Documented(文档)
- Document的英文意思是文档。它的作用是能够将注解中的元素包含到 Javadoc 中去
@Documented
public @interface MyAnnotation {
}
@Inherited(继承)
- Inherited的英文意思是继承
- 并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Inherited
public @interface MyAnnotation {
}
@MyAnnotation
public class Test {
}
class Test1 extends Test{
}
注解 MyAnnotation被 @Inherited 修饰,之后类 Test 被 MyAnnotation 注解,类 Test1 继承 Test,那么类 Test1 也拥有 MyAnnotation 这个注解。
@Repeatable(可重复)
- Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义
//相当于一堆注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
Value[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
String id() default "value";
}
public class Test {
@Value("hello")
@Value("world")
public static void test(String var1, String var2) {
System.out.println(var1 + " " + var2);
}
public static void main(String[] args) {
Method[] methods = Test.class.getMethods();
for (Method method : methods){
if (method.getName().equals("test")) {
Annotation[] annotations = method.getDeclaredAnnotations();
System.out.println(annotations.length);
System.out.println(method.getName() + " = " + Arrays.toString(annotations));
}
}
}
}
-
在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组
-
注解中属性可以有默认值,默认值需要用 default 关键值指定
-
如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内
3、注解的提取(反射)
这里我们暂时先使用以下反射,稍后详细讲述反射
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
Value[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
String id() default "id";
String name() default "name";
}
@Value(id = "1",name = "zhangsan")
@Value(id = "2",name = "lisi")
public class Test {
public static void main(String[] args) {
if (Test.class.isAnnotationPresent(Values.class)){
Values annotation = Test.class.getAnnotation(Values.class);
Value[] values = annotation.value();
for (Value value : values) {
System.out.println("id="+value.id());
System.out.println("name="+value.name());
}
}
}
}
id=1
name=zhangsan
id=2
name=lisi
属性、方法上的注解照样是可以的,同样还是要假手于反射
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
Value[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
String id() default "id";
String name() default "name";
}
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@Value(id = "-1",name = "yoya")
@Value(id = "0",name = "ruoye")
public class Test {
@Value(id = "1",name = "zhangsan")
public int a;
@Value(id = "2",name = "lisi")
public void todo(){
}
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(Values.class);
if ( hasAnnotation ) {
Values values = Test.class.getAnnotation(Values.class);
//获取类的注解
System.out.println("类注解======================");
System.out.println(values);
}
try {
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
//获取一个成员变量上的注解
Value value = a.getAnnotation(Value.class);
if ( value != null ) {
System.out.println("属性注解======================");
System.out.println(value.id());
System.out.println(value.name());
}
Method testMethod = Test.class.getDeclaredMethod("todo");
if (testMethod!=null) {
// 获取方法中的注解
System.out.println("方法注解======================");
Value declaredAnnotation = testMethod.getDeclaredAnnotation(Value.class);
if ( value != null ){
System.out.println(declaredAnnotation.id());
System.out.println(declaredAnnotation.name());
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
4、反射(框架设计的灵魂)
4.1、反射是什么
-
将类的各个组成部分封装为其他对象,这就是反射机制
-
可以在程序运行过程中,操作这些对象
-
可以解耦,提高程序的可扩展性
-
过多的反射会影响性能
4.2、获取Class对象的三种方式
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个
4.2.1、通过该类的对象去获取到对应的Class对象
一般不使用这种方式
public class Student {
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getClass());
System.out.println(student.getClass().getName());
}
}
class com.ruoye.Student
com.ruoye.Student
4.2.2、通过类名.class静态属性获取
需要导包
public class Student {
}
public class Test {
public static void main(String[] args) {
System.out.println(Student.class);
}
}
class com.ruoye.Student
4.2.3、通过Class类中的静态方法 forName()方法获取
public class Student {
}
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class<Student> studentClass = (Class<Student>) Class.forName("com.ruoye.Student");
System.out.println(studentClass.getName());
}
}
com.ruoye.Student
4.3、通过Class对象获取到该类的构造器
public class Student {
private int num;
private String name;
public Student() {
}
private Student(int num) {
this.num = num;
}
public Student(String name) {
this.name = name;
}
private Student(int num, String name) {
this.num = num;
this.name = name;
}
}
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
Class<Student> studentClass = Student.class;
System.out.println("所有构造函数================");
Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("公有构造函数=================");
Constructor<?>[] constructors = studentClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("指定参数的所有构造器==============");
Constructor<Student> constructor = studentClass.getDeclaredConstructor(int.class);
System.out.println(constructor);
System.out.println("指定参数的公开构造器==============");
Constructor<Student> constructor1 = studentClass.getConstructor(String.class);
System.out.println(constructor1);
}
}
所有构造函数================
private com.ruoye.Student(int,java.lang.String)
public com.ruoye.Student(java.lang.String)
private com.ruoye.Student(int)
public com.ruoye.Student()
公有构造函数=================
public com.ruoye.Student(java.lang.String)
public com.ruoye.Student()
指定参数的所有构造器==============
private com.ruoye.Student(int)
指定参数的公开构造器==============
public com.ruoye.Student(java.lang.String)
4.4、通过获取到的构造器创建对象
public class Student {
private int num;
private String name;
public Student() {
}
private Student(int num) {
this.num = num;
}
public Student(String name) {
this.name = name;
}
private Student(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Student> studentClass = Student.class;
Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class, String.class);
//暴力反射
declaredConstructor.setAccessible(true);
Student zhangsan = declaredConstructor.newInstance(1, "zhangsan");
System.out.println(zhangsan);
}
}
4.5、通过Class对象获取成员变量
public class Student {
public boolean flag;
private int num;
private String name;
public Student() {
}
private Student(int num) {
this.num = num;
}
public Student(String name) {
this.name = name;
}
private Student(int num, String name) {
this.num = num;
this.name = name;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Student> studentClass = Student.class;
System.out.println("获取所有字段==============");
Field[] fields = studentClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
System.out.println("获取公有字段==============");
Field[] fields1 = studentClass.getFields();
for (Field field : fields1) {
System.out.println(field.getName());
}
System.out.println("获取特定字段===========");
Field num = studentClass.getDeclaredField("num");
System.out.println(num.getName());
System.out.println("为字段设置值============");
Student student = studentClass.getDeclaredConstructor(null).newInstance();
student.setName("ruoye");
System.out.println(student.toString());
}
}
4.6、通过Class对象获取到该类的方法
public class Student {
private void sleep(int a){
System.out.println("sleep");
}
public void study(String name){
System.out.println("study");
}
}
public class Test {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Student> studentClass = Student.class;
System.out.println("获取所有方法================");
Method[] declaredMethods = studentClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("获取公有方法(包含父类的方法)================");
Method[] declaredMethods1 = studentClass.getMethods();
for (Method declaredMethod : declaredMethods1) {
System.out.println(declaredMethod);
}
System.out.println("获取指定公共方法================");
Method study = studentClass.getMethod("study", String.class);
System.out.println(study);
System.out.println("获取指定方法================");
Method sleep = studentClass.getDeclaredMethod("sleep", int.class);
Student student = studentClass.getDeclaredConstructor(null).newInstance();
sleep.setAccessible(true);
System.out.println(sleep);
//唤醒方法
sleep.invoke(student,1);
}
}
4.7、通过Method对象调用指定方法
public class Student {
private void sleep(int a){
System.out.println("sleep:"+a);
}
public void study(String name){
System.out.println(name+":study");
}
public static void run(String name){
System.out.println(name+":run");
}
}
public class Test {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Student> studentClass = Student.class;
Method sleep = studentClass.getDeclaredMethod("sleep", int.class);
Student student = studentClass.getDeclaredConstructor(null).newInstance();
sleep.setAccessible(true);
System.out.println(sleep);
sleep.invoke(student,1);
Method run = studentClass.getDeclaredMethod("run", String.class);
System.out.println(run);
//方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null
//方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null
//方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化
run.invoke(null,"zhangsan");
}
}