Java面向对象编程篇6——注解与反射
2021/9/3 22:06:00
本文主要是介绍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"); } }
这篇关于Java面向对象编程篇6——注解与反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-28MQ底层原理资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:入门与初级用户指南
- 2024-11-28MQ消息队列资料入门教程
- 2024-11-28MQ消息队列资料:新手入门详解
- 2024-11-28MQ消息中间件资料详解与应用教程
- 2024-11-28MQ消息中间件资料入门教程
- 2024-11-28MQ源码资料详解与入门教程
- 2024-11-28MQ源码资料入门教程
- 2024-11-28RocketMQ底层原理资料详解