枚举注解反射整合详解

梦想的初衷 提交于 2019-12-02 04:54:53

一丶枚举

  1 **
  2  * 一、枚举类的使用
  3  * 1.枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类
  4  * 2.当需要定义一组常量时,强烈建议使用枚举类
  5  * 3.如果枚举类中只有一个对象,则可以作为单例模式的实现方式。
  6  *
  7  * 二、如何定义枚举类
  8  * 方式一:jdk5.0之前,自定义枚举类
  9  * 方式二:jdk5.0,可以使用enum关键字定义枚举类
 10  *
 11  * 三、Enum类中的常用方法:
 12  *    values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
 13  *    valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
 14  *    toString():返回当前枚举类对象常量的名称
 15  *
 16  * 四、使用enum关键字定义的枚举类实现接口的情况
 17  *   情况一:实现接口,在enum类中实现抽象方法
 18  *   情况二:让枚举类的对象分别实现接口中的抽象方法
 19  *
 20  * @author shkstart
 21  * @create 2019 上午 10:17
 22  */
 23 public class SeasonTest {
 24 ​
 25     public static void main(String[] args) {
 26         Season spring = Season.SPRING;
 27         System.out.println(spring);
 28 ​
 29     }
 30 ​
 31 }
 32 //自定义枚举类
 33 class Season{
 34     //1.声明Season对象的属性:private final修饰
 35     private final String seasonName;
 36     private final String seasonDesc;
 37 ​
 38     //2.私有化类的构造器,并给对象属性赋值
 39     private Season(String seasonName,String seasonDesc){
 40         this.seasonName = seasonName;
 41         this.seasonDesc = seasonDesc;
 42     }
 43 ​
 44     //3.提供当前枚举类的多个对象:public static final的
 45     public static final Season SPRING = new Season("春天","春暖花开");
 46     public static final Season SUMMER = new Season("夏天","夏日炎炎");
 47     public static final Season AUTUMN = new Season("秋天","秋高气爽");
 48     public static final Season WINTER = new Season("冬天","冰天雪地");
 49 ​
 50     //4.其他诉求1:获取枚举类对象的属性
 51     public String getSeasonName() {
 52         return seasonName;
 53     }
 54 ​
 55     public String getSeasonDesc() {
 56         return seasonDesc;
 57     }
 58     //4.其他诉求1:提供toString()
 59     @Override
 60     public String toString() {
 61         return "Season{" +
 62                 "seasonName='" + seasonName + '\'' +
 63                 ", seasonDesc='" + seasonDesc + '\'' +
 64                 '}';
 65     }
 66 }
 67 package com.atguigu.java;
 68 ​
 69 /**
 70  * 使用enum关键字定义枚举类
 71  * 说明:定义的枚举类默认继承于java.lang.Enum类
 72  *
 73  * @author shkstart
 74  * @create 2019 上午 10:35
 75  */
 76 public class SeasonTest1 {
 77     public static void main(String[] args) {
 78         Season1 summer = Season1.SUMMER;
 79         //toString():返回枚举类对象的名称
 80         System.out.println(summer.toString());
 81 ​
 82 //        System.out.println(Season1.class.getSuperclass());
 83         System.out.println("****************");
 84         //values():返回所有的枚举类对象构成的数组
 85         Season1[] values = Season1.values();
 86         for(int i = 0;i < values.length;i++){
 87             System.out.println(values[i]);
 88             values[i].show();
 89         }
 90         System.out.println("****************");
 91         Thread.State[] values1 = Thread.State.values();
 92         for (int i = 0; i < values1.length; i++) {
 93             System.out.println(values1[i]);
 94         }
 95 ​
 96         //valueOf(String objName):返回枚举类中对象名是objName的对象。
 97         Season1 winter = Season1.valueOf("WINTER");
 98         //如果没有objName的枚举类对象,则抛异常:IllegalArgumentException
 99 //        Season1 winter = Season1.valueOf("WINTER1");
100         String seasonDesc = winter.getSeasonDesc();
101         String seasonName = winter.getSeasonName();
102         System.out.println(seasonDesc +"----->" + seasonName);
103         System.out.println(winter);
104         winter.show();
105     }
106 }
107 ​
108 interface Info{
109     void show();
110 }
111 ​
112 //使用enum关键字枚举类
113 enum Season1 implements Info{
114     //1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
115     SPRING("春天","春暖花开"){
116         @Override
117         public void show() {
118             System.out.println("春天在哪里?");
119         }
120     },
121     SUMMER("夏天","夏日炎炎"){
122         @Override
123         public void show() {
124             System.out.println("宁夏");
125         }
126     },
127     AUTUMN("秋天","秋高气爽"){
128         @Override
129         public void show() {
130             System.out.println("秋天不回来");
131         }
132     },
133     WINTER("冬天","冰天雪地"){
134         @Override
135         public void show() {
136             System.out.println("大约在冬季");
137         }
138     };
139 ​
140     //2.声明Season对象的属性:private final修饰
141     private final String seasonName;
142     private final String seasonDesc;
143 ​
144     //2.私有化类的构造器,并给对象属性赋值
145 ​
146     private Season1(String seasonName,String seasonDesc){
147         this.seasonName = seasonName;
148         this.seasonDesc = seasonDesc;
149     }
150 ​
151     //4.其他诉求1:获取枚举类对象的属性
152     public String getSeasonName() {
153         return seasonName;
154     }
155 ​
156     public String getSeasonDesc() {
157         return seasonDesc;
158     }
159 //    //4.其他诉求1:提供toString()
160 //
161 //    @Override
162 //    public String toString() {
163 //        return "Season1{" +
164 //                "seasonName='" + seasonName + '\'' +
165 //                ", seasonDesc='" + seasonDesc + '\'' +
166 //                '}';
167 //    }
168 ​
169 ​
170 //    @Override
171 //    public void show() {
172 //        System.out.println("这是一个季节");
173 //    }
174 }

 

二丶注解

2.1 什么是注解?

什么是注解?严谨的来说,注解提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。 Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。注解包含在 java.lang.annotation 包中。

2.2 注解的作用

  1. 能够读懂别人写的代码(尤其是框架相关的代码);

  2. 实现替代配置文件的功能。比如可能原本需要很多配置文件以及很多逻辑才能实现的内容,如果使用合理的注解,就可以使用一个或多个注解来实现相同的功能。这样就使得代码更加清晰和整洁;

  3. 编译时进行格式检查

    • 如 @Override 注解放在方法前,如果该方法不是覆盖了某个超类方法,编译的时候编译器就能检查出来。

  4. 装逼。

    • 做技术的怎么可以没有一点用技术吹牛逼的心理呢?如果会在合适的地方恰好的使用注解或者自定义注解的话,老板肯定会双手送你 666 的。当然笔者现在只是初学而已,距离用技术吹牛逼的道路还远。

2.3 元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。或者可以理解为:元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的

基本的元标签有 @Retention, @Documented, @Target, @Inherited 四种(后来到了 Java 8 又加入了 @Repeatable)。

2.3.1 @Retention

@Retention 定义了该注解的生命周期。当 @Retention 应用到一个注解上的时候,作用就是说明这个注解的存活时间。

  • RetentionPolicy.SOURCE

    : 注解只在源码阶段保留,在编译器完整编译之后,它将被丢弃忽视;

    • 例:@Override, @SuppressWarnings

  • RetentionPolicy.CLASS: 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中;

  • RetentionPolicy.RUNTIME: 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们;笔者接触到大部分的注解都是 RUNTIME 的生命周期。

以 SpringMVC 中的 @Service 的源码为例:

 1 package org.springframework.stereotype;
 2 ​
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.ElementType;
 5 import java.lang.annotation.Retention;
 6 import java.lang.annotation.RetentionPolicy;
 7 import java.lang.annotation.Target;
 8 ​
 9 @Target({ElementType.TYPE})
10 @Retention(RetentionPolicy.RUNTIME)
11 @Documented
12 @Component
13 public @interface Service {
14     String value() default "";
15 }

 

这里 @Service 拥有 @Retention(RetentionPolicy.RUNTIME) 注解,所以在程序运行时可以捕获到它们。

2.3.2 @Target

@Target 表示该注解用于什么地方,可以理解为:当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。可以使用的 ElementType 参数:

  • ElementType.CONSTRUCTOR: 对构造方法进行注解;

  • ElementType.ANNOTATION_TYPE: 对注解进行注解;

  • ElementType.FIELD: 对属性、成员变量、成员对象(包括 enum 实例)进行注解;

  • ElementType.LOCAL_VARIABLE: 对局部变量进行注解;

  • ElementType.METHOD: 对方法进行注解;

  • ElementType.PACKAGE: 对包进行注解;

  • ElementType.PARAMETER: 对描述参数进行注解;

  • ElementType.TYPE: 对类、接口、枚举进行注解;

如上面的 @Service 所示,@Service 的 @Target 注解值为 ElementType.TYPE,即 @Service 只能用于修饰类。

2.3.3 @Documented

@Documented 是一个简单的标记注解,表示是否将注解信息添加在 Java 文档,即 Javadoc 中。

2.3.4 @Inherited

Inherited 是指继承,@Inherited 定义了一个注释与子类的关系。如果一个超类带有 @Inherited 注解,那么对于该超类,它的子类如果没有被任何注解应用的话,那么这个子类就继承了超类的注解。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
​
@Test
public class A {}
public class B extends A {}

 

注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。可以这样理解: 老子非常有钱,所以人们给他贴了一张标签叫做富豪。 老子的儿子长大后,只要没有和老子断绝父子关系,虽然别人没有给他贴标签,但是他自然也是富豪。 老子的孙子长大了,自然也是富豪。 这就是人们口中戏称的富一代,富二代,富三代。虽然叫法不同,好像好多个标签,但其实事情的本质也就是他们有一张共同的标签,也就是老子身上的那张富豪的标签。

2.3.5 @Repeatable

@Repeatable 是 Java 8 中加入的,是指可重复的意思。通常使用 @Repeatable 的时候指注解的值可以同时取多个

 1 @interface Persons {
 2     Person[] value();
 3 }
 4 ​
 5 @Repeatable(Persons.class)
 6 @interface Person {
 7     String role default "";
 8 }
 9 ​
10 @Person(role="artist")
11 @Person(role="coder")
12 @Person(role="PM")
13 public class SuperMan {
14     ...
15 }

 

上面的代码通过 @Repeatable 定义了 Person,而 @Repeatable 后面括号的类相当于一个容器注解。容器注解就是用来存放其它注解的地方,它本身也是一个注解。

1 @Documented
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(ElementType.ANNOTATION_TYPE)
4 public @interface Repeatable {
5     Class<? extends Annotation> value();
6 }

 

上面是 @Repeatable 的源码。按照规定,如果使前面的 Persons 里面可以重复调用某个注解,则 Persons 必须有一个 value 的属性,且属性类型必须为被 @Repeatable 注解的 Person

2.4 注解的属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以无形参的方法形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。以下面的例程为例:

1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface Coder {
4     int id();
5     String name();
6     String language();
7     String company();
8 }

 

上面假设定义了一个名为 @Coder 的注解,该注解有 id, name, language, company 三个属性。使用的时候,我们应该对其赋值。赋值的方式类似于 key="value" 的方式进行,属性之间用 "," 隔开:

1 @Coder(id = 10086, name = "GRQ", language = "JAVA", company = "cetc")
2 public class coderGRQ() {
3 ​
4 }

 

此外,注解可以有默认值,需要用 default 关键字指定。例如上例:

1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface Coder {
4     public int id() default -1;
5     public String name() default "GRQ";
6     public String language() default "C++";
7     public String company() default "China_Company";
8 }

 

如果:

@Coder
public class coderXX {}

由于在 @Coder 注解中设置了默认值,所以就不需要再 @Coder 后面的括号里进行赋值了。

此外,如果注解内只有一个名为 value 的属性时,应用该属性时可以将值直接写到括号内,不用写 value = "..."。例如:

public @interface language {
    String value();
}

那么下面两种声明是相同的:

// 第一种声明
@language("JAVA")
int coderA;
// 第二种声明
@language(value = "JAVA")
int coderA;

 

2.5 常用注解

Java 中自带且常用的几种注解有 @Override, @Deprecated, @SuppresWarninngs, @SafeVarargs。

@Override 是一个标记类型注解,用于提示子类要复写父类中被 @Override 修饰的方法,它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。

@Deprecated 也是一个标记类型注解,用于标记过时的元素。比如如果开发人员正在调用一个过时的方法、类或成员变量时,可以用该注解进行标注。

@SuppressWarnings 并不是一个标记类型注解,它可以阻止警告的提示。它有一个类型为 String[] 的成员,其值为被禁止的警告名。

@SafeVarargs 是一个参数安全类型注解。它的目的是提醒开发人员,不要用参数做一些不安全的操作。它的存在会阻止编译器产生 unchecked 的警告。例如对于可变长度参数,如果和泛型一起使用,会产生比较多的编译器警告。如下面的方法:

public static <T> T useVarargs(T... args) {  
    return args.length > 0 ? args[0] : null;  
} 

如果参数传递的是不可具体化的类型(类似于 List<String> 的泛型类型),每调用一次该方法,都会产生警告信息。如果希望禁止这个警告信息,可以使用 @SuppressWarnings("unchecked") 注解进行声明。同时在 Java 7 版本之后的 @SafeVarargs 注解针对 "unchecked" 警告进行了屏蔽,我们也可以用 @SafeVarargs 获得 @SuppressWarnings("unchecked") 同样的效果。

2.6 自定义注解

自定义注解类编写的规则:

1. 注解类型定义为 @interface,所有的注解会自动继承 java.lang.Annotation 这一接口,而且不能再去继承其他的类或接口;
2. 参数成员只能用 public 或 default 两个关键字修饰;
3. 参数成员只能用基本类型:byte, short, char, int, long, float, double, boolean,以及 String, Enum, Class, Annotations 等数据类型,以及这些类型的数组;
4. 要获取类方法和字段的注解信息,必须通过 Java 的反射技术;
5. 注解也可以不定义成员变量,但这样的注解没有什么卵用;
6. 自定义注解需要使用元注解进行编写;

水果名称注解 FruitName.java:

 1 package com.grq.FruitAnnotation;
 2 ​
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.Target;
 6 ​
 7 import static java.lang.annotation.ElementType.FIELD;
 8 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 9 ​
10 /**
11  * 水果名称注解
12  */
13 @Target(FIELD)
14 @Retention(RUNTIME)
15 @Documented
16 public @interface FruitName {
17     String value() default "";
18 }

 

水果颜色注解 FruitColor.java:

 1 package com.grq.FruitAnnotation;
 2 ​
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.Target;
 6 ​
 7 import static java.lang.annotation.ElementType.FIELD;
 8 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 9 ​
10 /**
11  * 水果颜色注解
12  */
13 @Target(FIELD)
14 @Retention(RUNTIME)
15 @Documented
16 public @interface FruitColor {
17     /**
18      * 颜色枚举
19      */
20     public enum Color{ BLUE,RED,GREEN};
21 ​
22     /**
23      * 颜色属性
24      */
25     Color fruitColor() default Color.GREEN;
26 ​
27 }

 

水果供应者注解 FruitProvider.java:

 1 package com.grq.FruitAnnotation;
 2 
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.Target;
 6 
 7 import static java.lang.annotation.ElementType.FIELD;
 8 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 9 
10 /**
11  * 水果供应者注解
12  */
13 @Target(FIELD)
14 @Retention(RUNTIME)
15 @Documented
16 public @interface FruitProvider {
17     /**
18      * 供应商编号
19      */
20     public int id() default -1;
21 
22     /**
23      * 供应商名称
24      */
25     public String name() default "";
26 
27     /**
28      * 供应商地址
29      */
30     public String address() default "";
31 }

 

注解处理器 FruitInfoUtil.java:

 1 package com.grq.FruitAnnotation;
 2 
 3 import java.lang.reflect.Field;
 4 
 5 /**
 6  * 注解处理器
 7  */
 8 public class FruitInfoUtil {
 9     public static void getFruitInfo(Class<?> clazz){
10 
11         String strFruitName=" 水果名称:";
12         String strFruitColor=" 水果颜色:";
13         String strFruitProvicer="供应商信息:";
14 
15         Field[] fields = clazz.getDeclaredFields();
16 
17         for(Field field :fields){
18             if(field.isAnnotationPresent(FruitName.class)){
19                 FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
20                 strFruitName=strFruitName+fruitName.value();
21                 System.out.println(strFruitName);
22             }
23             else if(field.isAnnotationPresent(FruitColor.class)){
24                 FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
25                 strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
26                 System.out.println(strFruitColor);
27             }
28             else if(field.isAnnotationPresent(FruitProvider.class)){
29                 FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
30                 strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
31                 System.out.println(strFruitProvicer);
32             }
33         }
34     }
35 }

 

苹果 Apple.java:

 1 package com.grq.FruitAnnotation;
 2 
 3 /**
 4  * 注解使用
 5  */
 6 public class Apple {
 7 
 8     @FruitName("Apple")
 9     private String appleName;
10 
11     @FruitColor(fruitColor = FruitColor.Color.RED)
12     private String appleColor;
13 
14     @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
15     private String appleProvider;
16 
17     public void setAppleColor(String appleColor) {
18         this.appleColor = appleColor;
19     }
20     public String getAppleColor() {
21         return appleColor;
22     }
23 
24     public void setAppleName(String appleName) {
25         this.appleName = appleName;
26     }
27     public String getAppleName() {
28         return appleName;
29     }
30 
31     public void setAppleProvider(String appleProvider) {
32         this.appleProvider = appleProvider;
33     }
34     public String getAppleProvider() {
35         return appleProvider;
36     }
37 
38     public void displayName(){
39         System.out.println("水果的名字是:苹果");
40     }
41 }

 

测试输出水果信息 FruitTestAnnotation:

1 package com.grq.FruitAnnotation;
2 
3 public class TestFruitAnnotation {
4     public static void main(String[] args) {
5         FruitInfoUtil.getFruitInfo(Apple.class);
6     }
7 }

 

运行后的测试结果为:

水果名称:Apple 水果颜色:RED 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦


三丶反射

 


3.1. 简介

  • 定义:Java语言中 一种 动态(运行时)访问、检测 & 修改它本身的能力

  • 作用:动态(运行时)获取类的完整结构信息 & 调用对象的方法

1.类的加载过程: 程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件 加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此 运行时类,就作为Class的一个实例。 2.换句话说,Class的实例就对应着一个运行时类。 3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式 来获取此运行时类。


3.2. 特点

3.2.1 优点

灵活性高。因为反射属于动态编译,即只有到运行时才动态创建 &获取对象实例。

编译方式说明:

  1. 静态编译:在编译时确定类型 & 绑定对象。如常见的使用new关键字创建对象

  2. 动态编译:运行时确定类型 & 绑定对象。动态编译体现了Java的灵活性、多态特性 & 降低类之间的藕合性

3.2.2 缺点
  • 执行效率低 因为反射的操作 主要通过JVM执行,所以时间成本会 高于 直接执行相同操作

  1. 因为接口的通用性,Java的invoke方法是传object和object[]数组的。基本类型参数需要装箱和拆箱,产生大量额外的对象和内存开销,频繁促发GC。

  2. 编译器难以对动态调用的代码提前做优化,比如方法内联。

  3. 反射需要按名检索类和方法,有一定的时间开销。

  • 容易破坏类结构 因为反射操作饶过了源码,容易干扰类原有的内部逻辑


3.3. 应用场景

  • 动态获取 类文件结构信息(如变量、方法等) & 调用对象的方法

  • 常用的需求场景有:动态代理、工厂模式优化、Java JDBC数据库操作等

下文会用实际例子详细讲解


3.4. 具体使用

3.4.1 Java反射机制提供的功能

 

3.4.2 实现手段

  • 反射机制的实现 主要通过 操作java.lang.Class类

  • 下面将主要讲解 java.lang.Class

3.4.2.1 java.lang.Class 类
  • 定义:java.lang.Class类是反射机制的基础

  • 作用:存放着对应类型对象的 运行时信息

  1. Java程序运行时,Java虚拟机为所有类型维护一个java.lang.Class对象

  2. Class对象存放着所有关于该对象的 运行时信息

  3. 泛型形式为Class<T>

  • 每种类型的Class对象只有1个 = 地址只有1个

// 对于2个String类型对象,它们的Class对象相同
Class c1 = "Carson".getClass();
Class c2 =  Class.forName("java.lang.String");
// 用==运算符实现两个类对象地址的比较
System.out.println(c1 ==c2);
// 输出结果:true
  • Java反射机制的实现除了依靠Java.lang.Class类,还需要依靠:Constructor类、Field类、Method类,分别作用于类的各个组成部分:

 

示意图

3.4.3 使用步骤

在使用Java反射机制时,主要步骤包括:

  1. 获取 目标类型的Class对象

  2. 通过 Class 对象分别获取Constructor类对象、Method类对象 & Field 类对象

  3. 通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作

下面,我将详细讲解每个步骤中的使用方法。

步骤1:获取 目标类型的Class对象

// 获取 目标类型的`Class`对象的方式主要有4种
 1 <-- 方式1:Object.getClass() -->
 2     // Object类中的getClass()返回一个Class类型的实例 
 3     Boolean carson = true; 
 4     Class<?> classType = carson.getClass(); 
 5     System.out.println(classType);
 6     // 输出结果:class java.lang.Boolean  
 7 
 8 <-- 方式2:T.class 语法    -->
 9     // T = 任意Java类型
10     Class<?> classType = Boolean.class; 
11     System.out.println(classType);
12     // 输出结果:class java.lang.Boolean  
13     // 注:Class对象表示的是一个类型,而这个类型未必一定是类
14     // 如,int不是类,但int.class是一个Class类型的对象
15 
16 <-- 方式3:static method Class.forName   -->
17     Class<?> classType = Class.forName("java.lang.Boolean"); 
18     // 使用时应提供异常处理器
19     System.out.println(classType);
20     // 输出结果:class java.lang.Boolean  
21 
22 <-- 方式4:TYPE语法  -->
23 
24     Class<?> classType = Boolean.TYPE; 
25     System.out.println(classType);
26     // 输出结果:boolean  

 

此处额外讲一下java.lang.reflect.Type

  • java.lang.reflect.TypeJava中所有类型的父接口

  • 这些类型包括:

 

示意图

  • 之间的关系如下

 

示意图

步骤2:通过 Class 对象分别获取Constructor类对象、Method类对象 & Field 类对象

 1 // 即以下方法都属于`Class` 类的方法。
 2 ​
 3 <-- 1. 获取类的构造函数(传入构造函数的参数类型)->>
 4   // a. 获取指定的构造函数 (公共 / 继承)
 5   Constructor<T> getConstructor(Class<?>... parameterTypes)
 6   // b. 获取所有的构造函数(公共 / 继承) 
 7   Constructor<?>[] getConstructors(); 
 8   // c. 获取指定的构造函数 ( 不包括继承)
 9   Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
10   // d. 获取所有的构造函数( 不包括继承)
11   Constructor<?>[] getDeclaredConstructors(); 
12 // 最终都是获得一个Constructor类对象
13 ​
14 // 特别注意:
15   // 1. 不带 "Declared"的方法支持取出包括继承、公有(Public) & 不包括有(Private)的构造函数
16   // 2. 带 "Declared"的方法是支持取出包括公共(Public)、保护(Protected)、默认(包)访问和私有(Private)的构造方法,但不包括继承的构造函数
17   // 下面同理
18 ​
19 <--  2. 获取类的属性(传入属性名) -->
20   // a. 获取指定的属性(公共 / 继承)
21    Field getField(String name) ;
22   // b. 获取所有的属性(公共 / 继承)
23    Field[] getFields() ;
24   // c. 获取指定的所有属性 (不包括继承)
25    Field getDeclaredField(String name) ;
26   // d. 获取所有的所有属性 (不包括继承)
27    Field[] getDeclaredFields() ;
28 // 最终都是获得一个Field类对象
29 ​
30 <-- 3. 获取类的方法(传入方法名 & 参数类型)-->
31   // a. 获取指定的方法(公共 / 继承)
32     Method getMethod(String name, Class<?>... parameterTypes) ;
33   // b. 获取所有的方法(公共 / 继承)
34    Method[] getMethods() ;
35   // c. 获取指定的方法 ( 不包括继承)
36    Method getDeclaredMethod(String name, Class<?>... parameterTypes) ;
37   // d. 获取所有的方法( 不包括继承)
38    Method[] getDeclaredMethods() ;
39 // 最终都是获得一个Method类对象
40 ​
41 <-- 4. Class类的其他常用方法 -->
42 getSuperclass(); 
43 // 返回父类
44 ​
45 String getName(); 
46 // 作用:返回完整的类名(含包名,如java.lang.String ) 
47  
48 Object newInstance(); 
49 // 作用:快速地创建一个类的实例
50 // 具体过程:调用默认构造器(若该类无默认构造器,则抛出异常 
51 // 注:若需要为构造器提供参数需使用java.lang.reflect.Constructor中的newInstance()

 

步骤3:通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法 & 属性的具体信息 & 进行操作

 1 // 即以下方法都分别属于`Constructor`类、`Method`类 & `Field`类的方法。
 2 
 3 <-- 1. 通过Constructor 类对象获取类构造函数信息 -->
 4   String getName();// 获取构造器名
 5   Class getDeclaringClass();// 获取一个用于描述类中定义的构造器的Class对象
 6   int getModifiers();// 返回整型数值,用不同的位开关描述访问修饰符的使用状况
 7   Class[] getExceptionTypes();// 获取描述方法抛出的异常类型的Class对象数组
 8   Class[] getParameterTypes();// 获取一个用于描述参数类型的Class对象数组
 9 
10 <-- 2. 通过Field类对象获取类属性信息 -->
11   String getName();// 返回属性的名称
12   Class getDeclaringClass(); // 获取属性类型的Class类型对象
13   Class getType();// 获取属性类型的Class类型对象
14   int getModifiers(); // 返回整型数值,用不同的位开关描述访问修饰符的使用状况
15   Object get(Object obj) ;// 返回指定对象上 此属性的值
16   void set(Object obj, Object value) // 设置 指定对象上此属性的值为value
17  
18 <-- 3. 通过Method 类对象获取类方法信息 -->
19   String getName();// 获取方法名
20   Class getDeclaringClass();// 获取方法的Class对象 
21   int getModifiers();// 返回整型数值,用不同的位开关描述访问修饰符的使用状况
22   Class[] getExceptionTypes();// 获取用于描述方法抛出的异常类型的Class对象数组
23   Class[] getParameterTypes();// 获取一个用于描述参数类型的Class对象数组
24 
25 <--额外:java.lang.reflect.Modifier类 -->
26 // 作用:获取访问修饰符
27 
28 static String toString(int modifiers)   
29 // 获取对应modifiers位设置的修饰符的字符串表示
30 
31 static boolean isXXX(int modifiers) 
32 // 检测方法名中对应的修饰符在modifiers中的值

 

至此,关于Java反射机制的步骤说明已经讲解完毕。


3.4.4 特别注意:访问权限问题

  • 背景 反射机制的默认行为受限于Java的访问控制

如,无法访问( private )私有的方法、字段

  • 冲突 Java安全机制只允许查看任意对象有哪些域,而不允许读它们的值

若强制读取,将抛出异常

  • 解决方案 脱离Java程序中安全管理器的控制、屏蔽Java语言的访问检查,从而脱离访问控制

  • 具体实现手段:使用Field类Method类 & Constructor类对象的setAccessible()

void setAccessible(boolean flag)    
// 作用:为反射对象设置可访问标志
// 规则:flag = true时 ,表示已屏蔽Java语言的访问检查,使得可以访问 & 修改对象的私有属性

boolean isAccessible()  
// 返回反射对象的可访问标志的值

static void setAccessible(AccessibleObject[] array, boolean flag)   
// 设置对象数组可访问标志

3.5. 实例应用讲解

3.5.1 基础应用讲解
实例1:利用反射获取类的属性 & 赋值
 1 <-- 测试类定义-->
 2 public class Student {
 3 
 4     public Student() {
 5         System.out.println("创建了一个Student实例");
 6     }
 7     private String name;
 8 }
 9 
10 <-- 利用反射获取属性 & 赋值 -->
11         // 1. 获取Student类的Class对象
12         Class studentClass = Student.class;
13 
14         // 2. 通过Class对象创建Student类的对象
15         Object mStudent = studentClass.newInstance();
16    
17         // 3. 通过Class对象获取Student类的name属性
18         Field f = studentClass.getDeclaredField("name");
19  
20         // 4. 设置私有访问权限
21         f.setAccessible(true);
22 
23         // 5. 对新创建的Student对象设置name值
24         f.set(mStudent, "Carson_Ho");
25 
26         // 6. 获取新创建Student对象的的name属性 & 输出
27         System.out.println(f.get(mStudent));

 

  • 测试结果

 

实例2:利用反射调用类的构造函数
 1 <-- 测试类定义-->
 2 
 3 public class Student {
 4 
 5     // 无参构造函数
 6     public Student() {
 7         System.out.println("调用了无参构造函数");
 8     }
 9 
10     // 有参构造函数
11     public Student(String str) {
12         System.out.println("调用了有参构造函数");
13     }
14 
15     private String name;
16 }
17 
18 <-- 利用反射调用构造函数 -->
19         // 1. 获取Student类的Class对象
20         Class studentClass studentClass = Student.class;
21 
22         // 2.1 通过Class对象获取Constructor类对象,从而调用无参构造方法
23         // 注:构造函数的调用实际上是在newInstance(),而不是在getConstructor()中调用
24         Object mObj1 = studentClass.getConstructor().newInstance();
25       
26         // 2.2 通过Class对象获取Constructor类对象(传入参数类型),从而调用有参构造方法
27         Object mObj2 = studentClass.getConstructor(String.class).newInstance("Carson");

 

  • 测试结果

 

实例3:利用反射调用类对象的方法
 1 <-- 测试类定义-->
 2 public class Student {
 3 ​
 4     public Student() {
 5         System.out.println("创建了一个Student实例");
 6     }
 7 ​
 8     // 无参数方法
 9     public void setName1 (){
10         System.out.println("调用了无参方法:setName1()");
11     }
12 ​
13     // 有参数方法
14     public void setName2 (String str){
15         System.out.println("调用了有参方法setName2(String str):" + str);
16     }
17 }
18 ​
19 <-- 利用反射调用方法 -->
20         // 1. 获取Student类的Class对象
21         Class studentClass = Student.class;
22 ​
23         // 2. 通过Class对象创建Student类的对象
24         Object  mStudent = studentClass.newInstance();
25 ​
26         // 3.1 通过Class对象获取方法setName1()的Method对象:需传入方法名
27         // 因为该方法 = 无参,所以不需要传入参数
28         Method  msetName1 = studentClass.getMethod("setName1");
29 ​
30         // 通过Method对象调用setName1():需传入创建的实例
31         msetName1.invoke(mStudent);
32 ​
33         // 3.2 通过Class对象获取方法setName2()的Method对象:需传入方法名 & 参数类型
34         Method msetName2 = studentClass.getMethod("setName2",String.class);
35 ​
36        // 通过Method对象调用setName2():需传入创建的实例 & 参数值
37         msetName2.invoke(mStudent,"Carson_Ho");

 

    
  • 测试结果

 


3.5.2 常见需求场景讲解
实例1:工厂模式优化
  • 背景 采用简单工厂模式

  • 冲突

    1. 操作成本高:每增加一个接口的子类,必须修改工厂类的逻辑

    2. 系统复杂性提高:每增加一个接口的子类,都必须向工厂类添加逻辑

关于 简单工厂模式的介绍 & 使用 请看文章:简单工厂模式(SimpleFactoryPattern)- 最易懂的设计模式解析

  • 解决方案 采用反射机制: 通过 传入子类名称 & 动态创建子类实例,从而使得在增加产品接口子类的情况下,也不需要修改工厂类的逻辑

  • 实例演示

步骤1. 创建抽象产品类的公共接口

Product.java

abstract class Product{
    public abstract void show();
}

 

步骤2. 创建具体产品类(继承抽象产品类),定义生产的具体产品

 1 <-- 具体产品类A:ProductA.java -->
 2 public class  ProductA extends  Product{
 3 
 4     @Override
 5     public void show() {
 6         System.out.println("生产出了产品A");
 7     }
 8 }
 9 
10 <-- 具体产品类B:ProductB.java -->
11 public class  ProductB extends  Product{
12 
13     @Override
14     public void show() {
15         System.out.println("生产出了产品B");
16     }
17 }

 

步骤3. 创建工厂类

Factory.java

 1 public class Factory {
 2 
 3     // 定义方法:通过反射动态创建产品类实例
 4     public static Product getInstance(String ClassName) {
 5 
 6         Product concreteProduct = null;
 7 
 8         try {
 9             
10             // 1. 根据 传入的产品类名 获取 产品类类型的Class对象
11             Class product_Class = Class.forName(ClassName);
12             // 2. 通过Class对象动态创建该产品类的实例
13             concreteProduct = (Product) product_Class.newInstance();
14 
15         } catch (Exception e) {
16             e.printStackTrace();
17         }
18         
19         // 3. 返回该产品类实例
20         return concreteProduct;
21     }
22     
23 }

 

步骤4:外界通过调用工厂类的静态方法(反射原理),传入不同参数从而创建不同具体产品类的实例

TestReflect.java

 1 public class TestReflect {
 2     public static void main(String[] args) throws Exception {
 3 
 4        // 1. 通过调用工厂类的静态方法(反射原理),从而动态创建产品类实例
 5         // 需传入完整的类名 & 包名
 6         Product concreteProduct = Factory.getInstance("scut.carson_ho.reflection_factory.ProductA");
 7 
 8         // 2. 调用该产品类对象的方法,从而生产产品
 9         concreteProduct.show();
10     }
11 }

 

  • 展示结果

 

如此一来,通过采用反射机制(通过 传入子类名称 & 动态创建子类实例),从而使得在增加产品接口子类的情况下,也不需要修改工厂类的逻辑 & 增加系统复杂度

实例2:应用了反射机制的工厂模式再次优化
  • 背景 在上述方案中,通过调用工厂类的静态方法(反射原理),从而动态创建产品类实例(该过程中:需传入完整的类名 & 包名)

  • 冲突 开发者 无法提前预知 接口中的子类类型 & 完整类名

  • 解决方案 通过 属性文件的形式( Properties) 配置所要的子类信息,在使用时直接读取属性配置文件从而获取子类信息(完整类名)

  • 具体实现

步骤1:创建抽象产品类的公共接口

Product.java

abstract class Product{
    public abstract void show();
}

步骤2. 创建具体产品类(继承抽象产品类),定义生产的具体产品

 1 <-- 具体产品类A:ProductA.java -->
 2 public class  ProductA extends  Product{
 3 
 4     @Override
 5     public void show() {
 6         System.out.println("生产出了产品A");
 7     }
 8 }
 9 
10 <-- 具体产品类B:ProductB.java -->
11 public class  ProductB extends  Product{
12 
13     @Override
14     public void show() {
15         System.out.println("生产出了产品B");
16     }
17 }

 

步骤3. 创建工厂类

Factory.java

 1 public class Factory {
 2 ​
 3     // 定义方法:通过反射动态创建产品类实例
 4     public static Product getInstance(String ClassName) {
 5 ​
 6         Product concreteProduct = null;
 7 ​
 8         try {
 9             
10             // 1. 根据 传入的产品类名 获取 产品类类型的Class对象
11             Class product_Class = Class.forName(ClassName);
12             // 2. 通过Class对象动态创建该产品类的实例
13             concreteProduct = (Product) product_Class.newInstance();
14 ​
15         } catch (Exception e) {
16             e.printStackTrace();
17         }
18         
19         // 3. 返回该产品类实例
20         return concreteProduct;
21     }
22     
23 }

 

步骤4:创建属性配置文件 Product.properties

// 写入抽象产品接口类的子类信息(完整类名)ProductA = scut.carson_ho.reflection_factory.ProductAProductB = scut.carson_ho.reflection_factory.ProductB

步骤5:将属性配置文件 放到src/main/assets文件夹中

若没assets文件夹,则自行创建

步骤6:在动态创建产品类对象时,动态读取属性配置文件从而获取子类完整类名 TestReflect.java

 1 public class TestReflect {
 2     public static void main(String[] args) throws Exception {
 3 ​
 4         // 1. 读取属性配置文件
 5         Properties pro = new Properties() ;
 6         pro.load(this.getAssets().open("Product.properties"));
 7             
 8         // 2. 获取属性配置文件中的产品类名
 9         String Classname = pro.getProperty("ProductA");
10         
11         // 3. 动态生成产品类实例
12         Product concreteProduct = Factory.getInstance(Classname);
13 ​
14         // 4. 调用该产品类对象的方法,从而生产产品
15         concreteProduct.show();
16 ​
17 }

 

  • 测试结果

 

3.6类加载器

 1 /**
 2  * 了解类的加载器
 3  * @author shkstart
 4  * @create 2019 下午 2:16
 5  */
 6 public class ClassLoaderTest {
 7 ​
 8     @Test
 9     public void test1(){
10         //对于自定义类,使用系统类加载器进行加载
11         ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
12         System.out.println(classLoader);
13         //调用系统类加载器的getParent():获取扩展类加载器
14         ClassLoader classLoader1 = classLoader.getParent();
15         System.out.println(classLoader1);
16         //调用扩展类加载器的getParent():无法获取引导类加载器
17         //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
18         ClassLoader classLoader2 = classLoader1.getParent();
19         System.out.println(classLoader2);
20 ​
21         ClassLoader classLoader3 = String.class.getClassLoader();
22         System.out.println(classLoader3);
23 ​
24     }
25     /*
26     Properties:用来读取配置文件。
27 ​
28      */
29     @Test
30     public void test2() throws Exception {
31 ​
32         Properties pros =  new Properties();
33         //此时的文件默认在当前的module下。
34         //读取配置文件的方式一:
35 //        FileInputStream fis = new FileInputStream("jdbc.properties");
36 //        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
37 //        pros.load(fis);
38 ​
39         //读取配置文件的方式二:使用ClassLoader
40         //配置文件默认识别为:当前module的src下
41         ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
42         InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
43         pros.load(is);
44 ​
45 ​
46         String user = pros.getProperty("user");
47         String password = pros.getProperty("password");
48         System.out.println("user = " + user + ",password = " + password);
49 ​
50 ​
51 ​
52     }
53 ​
54 }

 

四、整合

4.1Field级别

 1 public class FieldTest {
 2 ​
 3     @Test
 4     public void test1(){
 5 ​
 6         Class clazz = Person.class;
 7 ​
 8         //获取属性结构
 9         //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
10         Field[] fields = clazz.getFields();
11         for(Field f : fields){
12             System.out.println(f);
13         }
14         System.out.println();
15 ​
16         //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
17         Field[] declaredFields = clazz.getDeclaredFields();
18         for(Field f : declaredFields){
19             System.out.println(f);
20         }
21     }
22 ​
23     //权限修饰符  数据类型 变量名
24     @Test
25     public void test2(){
26         Class clazz = Person.class;
27         Field[] declaredFields = clazz.getDeclaredFields();
28         for(Field f : declaredFields){
29             //1.权限修饰符
30             int modifier = f.getModifiers();
31             System.out.print(Modifier.toString(modifier) + "\t");
32 ​
33             //2.数据类型
34             Class type = f.getType();
35             System.out.print(type.getName() + "\t");
36 ​
37             //3.变量名
38             String fName = f.getName();
39             System.out.print(fName);
40 ​
41             System.out.println();
42         }
43     }
44 ​
45 }

 

4.2Method级别

 1 public class MethodTest {
 2 ​
 3     @Test
 4     public void test1(){
 5 ​
 6         Class clazz = Person.class;
 7 ​
 8         //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
 9         Method[] methods = clazz.getMethods();
10         for(Method m : methods){
11             System.out.println(m);
12         }
13         System.out.println();
14         //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
15         Method[] declaredMethods = clazz.getDeclaredMethods();
16         for(Method m : declaredMethods){
17             MyAnnotation a =  m.getAnnotation(MyAnnotation.class);
18             System.out.println(m);
19         }
20     }
21 ​
22     /*
23     @Xxxx
24     权限修饰符  返回值类型  方法名(参数类型1 形参名1,...) throws XxxException{}
25      */
26     @Test
27     public void test2(){
28         Class clazz = Person.class;
29         Method[] declaredMethods = clazz.getDeclaredMethods();
30         for(Method m : declaredMethods){
31             //1.获取方法声明的注解
32             Annotation[] annos = m.getAnnotations();
33             for(Annotation a : annos){
34                 System.out.println(a);
35             }
36 ​
37             //2.权限修饰符
38             System.out.print(Modifier.toString(m.getModifiers()) + "\t");
39 ​
40             //3.返回值类型
41             System.out.print(m.getReturnType().getName() + "\t");
42 ​
43             //4.方法名
44             System.out.print(m.getName());
45             System.out.print("(");
46             //5.形参列表
47             Class[] parameterTypes = m.getParameterTypes();
48             if(!(parameterTypes == null && parameterTypes.length == 0)){
49                 for(int i = 0;i < parameterTypes.length;i++){
50 ​
51                     if(i == parameterTypes.length - 1){
52                         System.out.print(parameterTypes[i].getName() + " args_" + i);
53                         break;
54                     }
55 ​
56                     System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
57                 }
58             }
59 ​
60             System.out.print(")");
61 ​
62             //6.抛出的异常
63             Class[] exceptionTypes = m.getExceptionTypes();
64             if(exceptionTypes.length > 0){
65                 System.out.print("throws ");
66                 for(int i = 0;i < exceptionTypes.length;i++){
67                     if(i == exceptionTypes.length - 1){
68                         System.out.print(exceptionTypes[i].getName());
69                         break;
70                     }
71 ​
72                     System.out.print(exceptionTypes[i].getName() + ",");
73                 }
74             }
75 ​
76             System.out.println();
77         }
78     }
79 }

 

4.3Other级别

  1 /**
  2  * @author shkstart
  3  * @create 2019 下午 4:19
  4  */
  5 public class OtherTest {
  6 ​
  7     /*
  8     获取构造器结构
  9 ​
 10      */
 11     @Test
 12     public void test1(){
 13 ​
 14         Class clazz = Person.class;
 15         //getConstructors():获取当前运行时类中声明为public的构造器
 16         Constructor[] constructors = clazz.getConstructors();
 17         for(Constructor c : constructors){
 18             System.out.println(c);
 19         }
 20 ​
 21         System.out.println();
 22         //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
 23         Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
 24         for(Constructor c : declaredConstructors){
 25             System.out.println(c);
 26         }
 27 ​
 28     }
 29 ​
 30     /*
 31     获取运行时类的父类
 32 ​
 33      */
 34     @Test
 35     public void test2(){
 36         Class clazz = Person.class;
 37 ​
 38         Class superclass = clazz.getSuperclass();
 39         System.out.println(superclass);
 40     }
 41 ​
 42     /*
 43     获取运行时类的带泛型的父类
 44 ​
 45      */
 46     @Test
 47     public void test3(){
 48         Class clazz = Person.class;
 49 ​
 50         Type genericSuperclass = clazz.getGenericSuperclass();
 51         System.out.println(genericSuperclass);
 52     }
 53 ​
 54     /*
 55     获取运行时类的带泛型的父类的泛型
 56 ​
 57 ​
 58     代码:逻辑性代码  vs 功能性代码
 59      */
 60     @Test
 61     public void test4(){
 62         Class clazz = Person.class;
 63 ​
 64         Type genericSuperclass = clazz.getGenericSuperclass();
 65         ParameterizedType paramType = (ParameterizedType) genericSuperclass;
 66         //获取泛型类型
 67         Type[] actualTypeArguments = paramType.getActualTypeArguments();
 68 //        System.out.println(actualTypeArguments[0].getTypeName());
 69         System.out.println(((Class)actualTypeArguments[0]).getName());
 70     }
 71 ​
 72     /*
 73     获取运行时类实现的接口
 74      */
 75     @Test
 76     public void test5(){
 77         Class clazz = Person.class;
 78 ​
 79         Class[] interfaces = clazz.getInterfaces();
 80         for(Class c : interfaces){
 81             System.out.println(c);
 82         }
 83 ​
 84         System.out.println();
 85         //获取运行时类的父类实现的接口
 86         Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
 87         for(Class c : interfaces1){
 88             System.out.println(c);
 89         }
 90 ​
 91     }
 92     /*
 93         获取运行时类所在的包
 94 ​
 95      */
 96     @Test
 97     public void test6(){
 98         Class clazz = Person.class;
 99 ​
100         Package pack = clazz.getPackage();
101         System.out.println(pack);
102     }
103 ​
104     /*
105         获取运行时类声明的注解
106 ​
107      */
108     @Test
109     public void test7(){
110         Class clazz = Person.class;
111 ​
112         Annotation[] annotations = clazz.getAnnotations();
113         for(Annotation annos : annotations){
114 ​
115             System.out.println( annos.toString());
116         }
117     }
118 ​
119 }

 

4.4Reflection级别

  1 /**
  2  * 调用运行时类中指定的结构:属性、方法、构造器
  3  *
  4  * @author shkstart
  5  * @create 2019 下午 4:46
  6  */
  7 public class ReflectionTest {
  8 ​
  9     /*
 10 ​
 11         不需要掌握
 12      */
 13     @Test
 14     public void testField() throws Exception {
 15         Class clazz = Person.class;
 16 ​
 17         //创建运行时类的对象
 18         Person p = (Person) clazz.newInstance();
 19 ​
 20 ​
 21         //获取指定的属性:要求运行时类中属性声明为public
 22         //通常不采用此方法
 23         Field id = clazz.getField("id");
 24 ​
 25         /*
 26         设置当前属性的值
 27 ​
 28         set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
 29          */
 30 ​
 31         id.set(p,1001);
 32 ​
 33         /*
 34         获取当前属性的值
 35         get():参数1:获取哪个对象的当前属性值
 36          */
 37         int pId = (int) id.get(p);
 38         System.out.println(pId);
 39 ​
 40 ​
 41     }
 42     /*
 43     如何操作运行时类中的指定的属性 -- 需要掌握
 44      */
 45     @Test
 46     public void testField1() throws Exception {
 47         Class clazz = Person.class;
 48 ​
 49         //创建运行时类的对象
 50         Person p = (Person) clazz.newInstance();
 51 ​
 52         //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
 53         Field name = clazz.getDeclaredField("name");
 54 ​
 55         //2.保证当前属性是可访问的
 56         name.setAccessible(true);
 57         //3.获取、设置指定对象的此属性值
 58         name.set(p,"Tom");
 59 ​
 60         System.out.println(name.get(p));
 61     }
 62 ​
 63     /*
 64     如何操作运行时类中的指定的方法 -- 需要掌握
 65      */
 66     @Test
 67     public void testMethod() throws Exception {
 68 ​
 69         Class clazz = Person.class;
 70 ​
 71         //创建运行时类的对象
 72         Person p = (Person) clazz.newInstance();
 73 ​
 74         /*
 75         1.获取指定的某个方法
 76         getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
 77          */
 78         Method show = clazz.getDeclaredMethod("show", String.class);
 79         //2.保证当前方法是可访问的
 80         show.setAccessible(true);
 81 ​
 82         /*
 83         3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
 84         invoke()的返回值即为对应类中调用的方法的返回值。
 85          */
 86         Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
 87         System.out.println(returnValue);
 88 ​
 89         System.out.println("*************如何调用静态方法*****************");
 90 ​
 91         // private static void showDesc()
 92 ​
 93         Method showDesc = clazz.getDeclaredMethod("showDesc" ,String.class);
 94         showDesc.setAccessible(true);
 95         //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
 96 //        Object returnVal = showDesc.invoke(null);
 97         Object returnVal = showDesc.invoke(Person.class,"ER");
 98         System.out.println(returnVal);//null
 99 ​
100     }
101 ​
102     /*
103     如何调用运行时类中的指定的构造器
104      */
105     @Test
106     public void testConstructor() throws Exception {
107         Class clazz = Person.class;
108 ​
109         //private Person(String name)
110         /*
111         1.获取指定的构造器
112         getDeclaredConstructor():参数:指明构造器的参数列表
113          */
114         Constructor constructor = clazz.getDeclaredConstructor(String.class);
115 ​
116         //2.保证此构造器是可访问的
117         constructor.setAccessible(true);
118 ​
119         //3.调用此构造器创建运行时类的对象
120         Person per = (Person) constructor.newInstance("Tom");
121         System.out.println(per);
122 ​
123     }
124 ​
125 }

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!