一、Java 的反射机制
1.1 Java 反射的概念及作用
如何使用一个 Java 类?
- 已知一个类的类名,以及类中的方法,属性,构造方法等
- 调用构造方法创建对象
- 使用对象调用方法 或 属性
问:
如果仅仅知道一个类的类名,能否动态得到类的定义信息,包括哪些方法,属性等?
答:
可以使用反射做到
1.1.1 反射的概念
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能成为 Java 语言的 反射机制
**
1.1.2 反射的作用
- 动态获取类信息,进一步实现需要的功能
- 例如:Spring框架通过 XML文件描述类的基本信息,使用反射机制动态装配对象
1.2 Java 反射相关 API
1.2.1 Java 反射相关的类主要包括
- Class 类型
- Constructor 构造方法
- Method 方法
Field 属性
除了 Class外,其他类都位于 java.lang.reflect 包中
- 可见,反射 API 将类的类型、方法、属性都封装成了类
- 其中最重要的类是 Class,反射的使用都是从 Class 开始
1.3 Class 类
1.3.1 Class 类的作用
- Class 类是 Java 反射机制的基础,通过 Class 类,可以得到一个类的基本信息
- Class 类中的主要方法
- getMethods:返回类中某一个方法的实例
- getMethods:返回类中所有方法的实例
- getField:返回类中某一个属性的实例
- getConstructor:范湖类中的一个构造方法的实例
- Class 类中还有若干 getXXX 方法,可以获得类 的基本信息
1.3.2 获得 Class 实例
获得 Class 类实例的常用方法
- Object 类中的 getClass 方法,适用于通过对象获得 Class 实例的情况
- 任何类都继承到了 getClass 方法,任意对象可以调用 getClass 方法获得 Class 实例
- 类名.class 方式,适用于通过类名获得 Class 实例的情况
- 任何类名加.class 即返回 Class 实例,例如 Class cls = String.class;
- Class 类的静态方法 forName(String name); 适用于通过类型获得 Class 实例的情况,尤其类名是变量
- 例如 Class.forName(className);
public class TestClas {
public static void main(String[] args) {
// 使用三种方式获得 java.lang.String 类的 Class 实例
String s = "Hello World";
// 使用对象名获得 Class 实例
Class cls1 = s.getClass();
System.out.println("第一种方式:"+cls1);
// 使用对象名获得 Class 实例,类名必须是变量
Class cls2 = String.class;
System.out.println("第二种方式:"+cls2);
// 使用类名获得 Class 实例,类名可以是变量
try {
Class cls3 = Class.forName("java.lang.String"); // 这种方法常常用来加入我们自定义的类
System.out.println("第三种方式:"+cls3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
1.3.3 使用 Class 实例获得类的信息
接上,将获得的 Class 类的实例,打印出 String类的所有方法名字
System.out.println("====== 使用 Class 实例获得类的信息 =======");
Method[] methods = cls1.getMethods();
for (Method m:methods) {
System.out.println(m.getName());
}
所以获得了 Class 实例后,就可以使用其 getXXX 方法得到该类的基本信息,包括方法,属性构造方法等
1.4 Constructor 类
1.4.1 Constructor 基本特点
- Constructor 类可以通过 getXXX 方法获得构造方法的基本信息,例如:
- getName:返回构造方法的名字
- getParameterTypes:返回构造方法的参数类型
- 处理获取构造方法的基本信息,还可以创建实例
- newInstance(Object….initargs):创建实例
1.4.2 获取 Constructor 类实例
- Constructor实例通过Class实例获得,Class类中定义了如下方法
- Constructor
getConstructor(Cla… parameterTypes):通过指定参数类型,返回构造方法实例。
- Constructor
- Constructor[] getConstructors():返回该类的所有构造方法实例。
1.4.3 demo实现
- 写一个 学生类
package day11;
public class Student {
private String name;
private int age;
private double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
// getter 和 setter 省略
}
- 测试 学生类 ```java import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException;
public class TestStudent { public static void main(String[] args) { // 获取 Student 类的Class 实例 Class cls1 = Student.class; try { // 获取 Student 中有参构造方法的三种参数 Constructor con = cls1.getConstructor(String.class, int.class, double.class); // 参数为构造方法的类型
// 得到构造方法就可以使用 newInstance
Student stu = (Student) con.newInstance("小红",12,88.5);
System.out.println(stu);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
3. 运行结果
![image.png](https://cdn.nlark.com/yuque/0/2020/png/473179/1586144399836-ce964a97-bf69-471e-b344-ae7b32c564ed.png#align=left&display=inline&height=82&margin=%5Bobject%20Object%5D&name=image.png&originHeight=82&originWidth=387&size=22830&status=done&style=none&width=387)
这种方式使用的很少,一般在和 xml 配置文件中使用的比较多,这里只是演示基本的使用方式
<a name="9k3vY"></a>
### 1.5 Method 方法
<a name="rWylw"></a>
#### 1.5.1 Method类的作用
- Method类将类中的方法进行封装,可以动态获得方法的信息,例如
- getReturnType :获得方法返回值类型
- getName:获得方法名字
- getParameterTypes: 获得方法参数类型
- 除了动态获得方法信息外,Method还能动态调用某一个对象的具体方法
- invoke(Object obj, Object... args) :使用obj调用该方法,参数为args
<a name="pyRX9"></a>
#### 1.5.2 获取 Method 实例
Method实例都是通过CIass类的方法获得
- Method getMethod(String name, Class... .<br />parameterTypes):通过指定方法名,参数类型,<br />返回一个Method实例
- Method[] getMethods():返回该类中所有方法<br />的Method实例
<a name="EAuDt"></a>
#### 1.5.3 demo2
使用 TestMethod 类做如下操作<br />TestMethod类中:
- 获得Student类的Class实例
- 通过Class实例获得无参构造方法实例
- 通过无参构造方法实例,创建对象
- 通过Class实例获得方法实例
- 调用setXXX方法为Student实例的属性赋值
```java
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethod {
public static void main(String[] args) {
Class cls1 = null;
try {
// 获得 Student 类的 Class 实例
cls1 = Class.forName("day11.Student");
// 获取 Student 类的无参构造方法实例
Constructor con = cls1.getConstructor(null);
// 使用无参构造方法创建对象
Student stu = (Student) con.newInstance(null);
// 返回 Student 类的所有方法实例
Method[] methods = cls1.getMethods();
// 迭代数组,调用 setName 和 setAge,setScore 方法,为 stu 对象的属性赋值
for (Method m:methods) {
if (m.getName().startsWith("setName")) {
m.invoke(stu,"王三");
}
if (m.getName().startsWith("setAge")) {
m.invoke(stu,18);
}
if (m.getName().startsWith("setScore")) {
m.invoke(stu,95.5);
}
System.out.println(stu);
}
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
1.6 Field 属性
1.6.1 Field 类的作用
Field 类将类的属性进行封装,可以获得属性的基本信息、属性的值,也可以对属性进行赋值
- getName:返回属性的名字
- getXXX:例如,getFloat 返回该属性 float 类型的值
- setXXX:例如,setFloat为属性赋值float类型的值
1.6.2 获得 Field 实例
- 通过 Class 中的方法实现
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class TestMethod {
public static void main(String[] args) {
Class cls1 = null;
try {
// 获得 Student 类的 Class 实例
cls1 = Class.forName("day11.Student");
// 获取 Student 类的无参构造方法实例
Constructor con = cls1.getConstructor(null);
// 使用无参构造方法创建对象
Student stu = (Student) con.newInstance(null);
// 通过名字返回为 name, age 的属性实例,注意name,age的访问权限必须是 public
Field name = cls1.getField("name");
Field age = cls1.getField("age");
Field score = cls1.getField("score");
// 调用 Field 类中的 set 方法,为对象 stu 属性赋值
name.set(stu, "张三");
name.setInt(stu,18);
name.setDouble(stu,99.5);
System.out.println(name.get(stu)+" "+age.getInt(stu)+" "+score.getDouble(stu));
} catch (NoSuchFieldException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
1.7 使用场景
1.7.1 JDBC 中加载驱动
Class.forName("com.mysql.jdbc.Driver");
二、枚举
2.1 定义枚举类
- 枚举的作用是定义有限个对象的一种结构,枚举属于多例设计
- 我们使用 enum 关键字,利用此关键字可以实现枚举的定义
- 使用枚举就是把有限个结果一一列举出来
// 定义一个枚举类
public enum Color {
RED,GREEN,BLUE; // 实例化对象
}
public class TestColor {
public static void main(String[] args) {
Color c = Color.RED;
System.out.println(c); // 获取枚举中的内容
for (Color c1:Color.values()) {
System.out.println(c1); // 循环获取枚举中的每一条数据
}
// 枚举和 switch 结合
switch (c) {
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
default:
System.out.println("没有你要的颜色");
}
}
}
2.2 Enum 类
- 枚举的本质相对一个类,它会默认继承 Enum 类
2.2.1 Enum 类的结构
- 现在定义的枚举类的类型就是 Enum 中所使用的的 E 类型
2.2.2 Enum 类的方法
对象序号是根据枚举对象的定义顺序决定的。
for (Color c1:Color.values()) {
System.out.println(c1.ordinal()+"-"+c1); // 循环获取枚举中的每一条数据
}
2.3 定义枚举结构
枚举本身属于一种多例的设计模式,那么既然是多例设计模式,多例设计模式可以有 构造方法,普通方法,属性等,这些内容在枚举中依然可以定义,在枚举类中不能使用 private!!!
2.3.1 枚举使用构造方法
// 定义一个枚举类
public enum Color {
RED("红色"),GREEN("绿色"),BLUE("蓝色"); // 实例化对象
private String title;
Color(String title) {
this.title = title;
}
@Override
public String toString() {
return "Color{" +
"title='" + title + '\'' +
'}';
}
}
2.3.2 枚举使用接口
2.3.3 枚举的使用
枚举的功能,就是把有限个集合一一列举出来,这才是它本身的功能
enum Sex {
MALE("男"), FEAMLE("女") ;
private String title;
private Sex(String title) {
this.title = title;
}
@Override
public String toString() {
return "Sex{" +
"title='" + title + '\'' +
'}';
}
}
class Person {
private String name;
private int age;
private Sex sex;
public Person(String name, int age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(new Person("张三",20,Sex.MALE));
}
}
2.4 补充
2.4.1 面试题一:请解释 enum 和 Enum 的区别
- enum:是 JDK 1.5 之后提供的一个关键字,用于定义枚举类
- Enum:是一个抽象类,所以使用 enum关键字定义的类就默认继承了此类