理解和用好反射


练习源码

理解和用好反射

一、反射概念导引

1 Java核心思想–面向对象

  万事万物皆对象,即所有的事物都具备一些特征,根据这些特征抽象成Java类,再由Java类创建具体的对象进行操作。
  Java类创建的具体的对象称为“类的对象”。
  Java类由属性、方法和构造器组成,Java类、属性、方法和构造器作为事物也有其特征。

2 Java类、属性、方法和构造器特征分析

(1)Java类特征

  构造器 属性 方法

(2)属性特征

  修饰符 类型 属性名 属性值

(3)方法特征

  修饰符 返回值类型 方法名 形参 方法体

(4)构造器特征

  修饰符 形参 构造器名 方法体

  因此,Java类、属性、方法和构造器这四种事物都可以抽象成Java类。

3 四个特殊的Java类(反射类)

(1)Java类的类——class类

  作用:可以完整的描述一个Java类。
  由class创建的对象称为“class类的对象(或类对象)”,是一种特殊的“类的对象”。
  每个Java类都对应一个类对象,这个类对象可以完整的描述这个Java类。
  要理解好 普通Java类、普通Java类的对象、class类和class类的对象这四个概念之间的关系。

(2)属性的类——Field类

  作用:可以完整的描述Java类的一个属性。
  Field类创建的对象是属性对象,属性对象(或属性对象数组,因为一个Java类可能有多个属性)是类对象的一个属性。

(3)方法的类——Method类

  作用:可以完整的描述Java类的一个方法。
  Method类创建的对象是方法对象,方法对象(或方法对象数组)是类对象的一个属性。

(4)构造器的类——Constructor类

  作用:可以完整的描述Java类的一个构造器。
  Constructor类创建的对象是构造器对象,构造器对象(或构造器对象数组)是类对象的一个属性。

4 传统方法操作对象

(1)操作步骤

  ① 导入类
  ② new对象
  ③ 操作对象

(2)问题分析

  ① 有时候程序运行时提前不知道操作的对象属于哪个类,由用户请求来决定,如何操作对象?——动态问题
  ② 有时候想要更改使用的类,代码中需要修改的地方很多,如何能很方便的更换使用的类?——耦合问题

  反射可以解决以上问题,可以在程序运行时动态获取类的信息和操作对象,可以降低代码的耦合性,方便更换使用的类。

二、反射概念解读

1 什么是反射?

(1)宏观来讲:

  Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
  这种动态获取类的信息和操作对象属性和方法的功能称为Java语言的反射机制。
  “动态”是因为编写程序时不确定要操作的是哪一个类的对象,运行时根据反射机制可以操作任何一个类的对象。

(2)具体来讲:

  反射将Java类、属性、方法、构造器都抽象成Java类,其中Java类的类(class)创建的对象叫类对象,属性类(Field)、
  方法类(Method)和构造器类(Constructor)的对象都是类对象的属性。
  反射的过程就是① 获取类对象,② 获取属性对象(或方法对象、构造器对象),③ 操作属性对象(或方法对象、构造器对象)的过程。
  注:实际上使用传统方法new对象时,JVM也是通过加载类对象来创建实例对象的,这里不做深究。

2 反射有什么缺点?

  (1)性能差,效率低,是一种解释操作,操作属性和方法时要远慢于传统方式
  (2)代码复杂,不易阅读

  因此,实际开发中根据需求才使用反射。

三、获取类对象

1 反射获取类对象的三种方式

// 第一种方式--->根据全限定路径获取
Class cla01 = Class.forName("com.cty.pojo.Person");
// 第二种方式---->调用底层使用反射封装的方法,根据类名获取
Class cla02 = Person.class;
// 第三种方式---->调用底层使用反射封装的方法,根据对象获取
Class cla03 = new Person().getClass();
// 一个Java类只有一个类对象,上述三种方式获取的类对象都是同一个,可通过==来判断

2 class类的常用方法

// class类的常用方法
System.out.println("获取类对象的包名---->"+cla01.getPackage());
System.out.println("获取类的修饰符----->"+cla01.getModifiers());
System.out.println("获取类的名称(全限定)----->"+cla01.getName());
System.out.println("获取类的名称(类名)----->"+cla01.getSimpleName());
System.out.println("获取类的父类的类对象----->"+cla01.getSuperclass());

/* res:
获取类对象的包名---->package com.cty.pojo
获取类的修饰符----->1
获取类的名称(全限定)----->com.cty.pojo.Person
获取类的名称(类名)----->Person
获取类的父类的类对象----->class java.lang.Object
*/

四、操作属性

1 获取类对象

  见章节三。

2 获取属性对象

(1)class类获取属性对象的方法
方法 说明 返回值类型
类对象.getFields() 获取 “类及其父类” 的 “所有” 公共属性 Field[]
类对象.getField(String name) 获取 “类及其父类” 的 “指定” 公共属性 Field
类对象.getDeclaredFields() 获取 “类” 的 “所有” 属性 (不包括父类) Field[]
类对象.getDeclaredField(String name) 获取 “类” 的 “指定” 属性(不包括父类) Field
(2)Field类的常用方法
// 打印属性对象的修饰符、类型、属性名(fd是一个属性对象)
System.out.println("获取修饰符-->"+fd.getModifiers());  // 每个修饰符对应一个数值,若有多个修饰符则返回数值之和,数值之和不会与修饰符的数值重复
System.out.println("获取类型-->"+fd.getType());  // 返回一个 class对象或基本数据类型
System.out.println("获取属性名-->"+fd.getName());  // 返回一个字符串

/* res:
获取修饰符-->1
获取类型-->class java.lang.String
获取属性名-->sno
*/

3 操作属性对象(即设置和获取属性值)

方法 说明
属性对象.set(null,“值”) 设置静态属性值
属性对象.get(null) 获取静态属性值
属性对象.set(Object obj,“值”) 设置非静态属性值
属性对象.get(Object obj,“值”) 获取非静态属性值

五、操作方法

1 获取类对象

  见章节三。

2 获取方法对象

(1)class类获取方法对象的方法
方法 说明 返回值类型
类对象.getMethods() 获取 “类及其父类” 的 “所有” 公共方法 Method[]
类对象.getMethod(“方法名”, 参数数据类型.class,…) 获取 “类及其父类” 的 “指定” 公共方法 Method
类对象.getDeclaredMethods() 获取 “类” 的 “所有” 方法 Method[]
类对象.getDeclaredMethod(“方法名”, Class…cla) 获取指定的声明方法 Method
(2)Method类的常用方法
// 打印方法对象的修饰符、返回值类型、方法名、形参(md是一个方法对象)
System.out.println("获取修饰符-->"+md.getModifiers());  // 每个修饰符对应一个数值,若有多个修饰符则返回数值之和
System.out.println("获取返回值类型-->"+md.getReturnType());  // 返回一个 class对象
System.out.println("获取方法名-->"+md.getName());  // 返回一个字符串
System.out.println("获取形参类型:");  // 返回一个class对象数组
for(int i=0; i<md.getParameterTypes().length; i++){
    System.out.println(md.getParameterTypes()[i]);
}

/* res:
获取修饰符-->1
获取返回值类型-->int
获取方法名-->doExercises
获取形参类型:
class java.lang.String
int
int
*/

3 操作方法(即执行方法体)

方法 说明
方法对象.invoke(null, 参数值1,参数值2,…) 执行静态含参方法
方法对象.invoke(null, null) 执行静态无参方法
方法对象.invoke(obj, 参数值1,参数值2,…) 执行静态含参方法
方法对象.invoke(obj, null) 执行静态无参方法
其中Object obj=cla.newInstance();

六、操作构造器

1 获取类对象

  见章节三。

2 获取构造器对象

(1)class类获取构造器对象的方法
方法 说明 返回值类型
类对象.getConstructors() 获取 “类” 的 “所有” 构造器 Constructor[]
类对象.getConstructor(数据类型.class,…) 获取 “类” 的 “指定” 构造器 Constructor
(2)Constructor类的常用方法
// 打印方法对象的修饰符、构造器名、形参(cs是一个构造器对象)
System.out.println("获取修饰符-->"+cs.getModifiers());  // 每个修饰符对应一个数值,若有多个修饰符则返回数值之和,数值之和不会与修饰符的数值重复
System.out.println("获取构造器名-->"+cs.getName());  // 返回一个字符串
System.out.println("获取形参类型:");  // 返回一个class对象数组
for(int i=0; i<cs.getParameterTypes().length; i++){
    System.out.println(cs.getParameterTypes()[i]);
}

/* res:
获取修饰符-->1
获取构造器名-->com.cty.pojo.Student
获取形参类型:
class java.lang.String
class java.lang.String
class java.lang.String
int
boolean
*/

3 操作构造器对象(即创建对象)

方法 说明
构造器对象.newInstance() 无参构造器创建实例化对象
构造器对象.newInstance(参数值1,参数值2,…) 有参构造器创建实例化对象

附录

访问权限修饰符

在这里插入图片描述

参考

尚学堂视频教程
反射
5.4.2 封装的实现—使用访问控制符

猜你喜欢

转载自blog.csdn.net/ChenTianyu666/article/details/104400480