一丶枚举
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 }