一丶枚举
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 注解的作用
-
能够读懂别人写的代码(尤其是框架相关的代码);
-
实现替代配置文件的功能。比如可能原本需要很多配置文件以及很多逻辑才能实现的内容,如果使用合理的注解,就可以使用一个或多个注解来实现相同的功能。这样就使得代码更加清晰和整洁;
-
编译时进行格式检查
-
如 @Override 注解放在方法前,如果该方法不是覆盖了某个超类方法,编译的时候编译器就能检查出来。
-
-
装逼。
-
做技术的怎么可以没有一点用技术吹牛逼的心理呢?如果会在合适的地方恰好的使用注解或者自定义注解的话,老板肯定会双手送你 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 优点
灵活性高。因为反射属于动态编译,即只有到运行时才动态创建 &获取对象实例。
编译方式说明:
静态编译:在编译时确定类型 & 绑定对象。如常见的使用
new关键字创建对象动态编译:运行时确定类型 & 绑定对象。动态编译体现了
Java的灵活性、多态特性 & 降低类之间的藕合性
3.2.2 缺点
-
执行效率低 因为反射的操作 主要通过
JVM执行,所以时间成本会 高于 直接执行相同操作
因为接口的通用性,Java的invoke方法是传object和object[]数组的。基本类型参数需要装箱和拆箱,产生大量额外的对象和内存开销,频繁促发GC。
编译器难以对动态调用的代码提前做优化,比如方法内联。
反射需要按名检索类和方法,有一定的时间开销。
-
容易破坏类结构 因为反射操作饶过了源码,容易干扰类原有的内部逻辑
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类是反射机制的基础 -
作用:存放着对应类型对象的 运行时信息
在
Java程序运行时,Java虚拟机为所有类型维护一个java.lang.Class对象该
Class对象存放着所有关于该对象的 运行时信息泛型形式为
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反射机制时,主要步骤包括:
-
获取 目标类型的
Class对象 -
通过
Class对象分别获取Constructor类对象、Method类对象 &Field类对象 -
通过
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.Type是Java中所有类型的父接口 -
这些类型包括:

示意图
-
之间的关系如下

示意图
步骤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:工厂模式优化
-
背景 采用简单工厂模式
-
冲突
-
操作成本高:每增加一个接口的子类,必须修改工厂类的逻辑
-
系统复杂性提高:每增加一个接口的子类,都必须向工厂类添加逻辑
-
关于 简单工厂模式的介绍 & 使用 请看文章:简单工厂模式(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 }