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;}@Overridepublic 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;}@Overridepublic 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");}
}

Java面向对象编程篇6——注解与反射相关推荐

  1. Java面向对象编程篇5——枚举

    Java面向对象编程篇5--枚举 1.枚举的概念 在日常生活中这些事物的取值只有明确的几个固定值,此时描述这些事 物的所有值都可以一一列举出来,而这个列举出来的类型就叫做枚举类型 2.枚举的定义 使用 ...

  2. Java面向对象编程篇4——内部类

    Java面向对象编程篇4--内部类 1.内部类的概念 当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类 (Inner),而这个内部类所在的类叫做外部类(Outer). 类中的内容:成员变 ...

  3. Java面向对象编程篇3——接口与抽象类

    Java面向对象编程篇3--接口与抽象类 1.接口(interface) 接口中可以含有变量和方法.但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是pub ...

  4. Java面向对象编程篇2——面向对象三大特点

    Java面向对象编程篇2--面向对象三大特点 1.封装 1.1.封装的概念 通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无 论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生 ...

  5. Java面向对象编程篇1——类与对象

    Java面向对象编程篇1--类与对象 1.面向过程 1.1.概念 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了 1.2.优缺点 优点:性 ...

  6. java面向对象编程精讲

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.包 1.1导入包中的类 1.2静态导入 1.3将类放到包中 1.4包的访问权限控制 1.5常见的系统包 二.继承 ...

  7. Java面向对象编程(第2版)_学习记录

    <Java面向对象编程(第2版)> 孙卫琴 编著 文章目录 一.介绍 (一)平台与开发环境 (二)一些知识 (三)数组 二.类的生命周期 (一)类的加载 1. 加载 2. 连接 3. 初始 ...

  8. java面向对象编程知识点总结

    一:今天完成 上午详细了解了java面向对象编程的一些细节,记录如下. 1)类 是一种引用类型,包含一个签名和一个主体,主体是放在花括号里面的成员,成员包括字段和方法,还有构造方法.初始化程序和嵌套类 ...

  9. 【Java】《Java面向对象编程的三大特性》阅读笔记

    前言 偶然读到这篇文章(<Java面向对象编程的三大特性>),想来这也算论文?这种还不满网络都是?读罢觉得写得还真不错,这里以我愚见,简单点评一二,不足之处还望指教. 阅读笔记 笔记1 文 ...

最新文章

  1. 不再重复造轮子,AI 给你推荐更好的代码,还没bug
  2. 数据库数据用Excel导出的3种方法
  3. Opengl-立方体贴图(天空盒子)
  4. 大兴计算机职业学校,北京大兴区第一职业学校
  5. linux卸载gd,linux下 安装GD
  6. 今晚课题:2019-3-20
  7. 【HTML+CSS网页设计与布局 从入门到精通】第10章-CSS
  8. ftp服务器web里有个文件,web实现ftp服务器文件
  9. 功能测试分析和测试用例编写模板
  10. 生物竞赛初赛报名已截止!各省往届真题超全汇总,建议收藏
  11. 关于H5页面在iPhoneX适配(转)
  12. 为什么不要把鸡蛋放在同一个篮子里?
  13. 虚拟机十步安装VMware_workstation
  14. telnet如何开启?
  15. 使用哪种关机方式后再启动计算机时间最长,使用下面哪种关机方式后再启动计算机时间最长?(  )。...
  16. Python算法-穷举法和二分法
  17. android课程设计计步器,数字计步器课程设计.doc
  18. plc 梯形图 转c语言,电路与PLC之间的梯形图转化,电气人必备技能!
  19. 第一次动手构建 Linux 内核
  20. WDM PON(无源光网络技术)

热门文章

  1. [转载] Java中Scanner用法总结
  2. [转载] 列表、元组及通用序列操作
  3. java中的starts_Java Math类静态double nextAfter(double starts,double direction)示例
  4. jsonp请求html页面,JavaScript中的JSON和JSONP
  5. git 怎么查看合并过来哪些代码_git整理纷乱的历史合并记录
  6. 适配接口 java_【Java 设计模式】接口型模式--Adapter(适配器)模式
  7. stl vector 函数_vector :: front()函数以及C ++ STL中的示例
  8. 循环语句与条件语句_在PHP中混合条件语句和循环
  9. MySQL 性能优化 分布式
  10. 英语笔记:词组句子:1112