Class
除了基本数据类型外,Java的其他类型全部都是class
。例如:String
、Object
。
class
是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class
类型时,将其加载进内存。每加载一种class
,JVM就为其创建一个Class
类型的对象,并关联起来。
如:当JVM加载String
类时,它首先读取String.class
文件到内存,然后,为String
类创建一个Class
对象并关联起来。这个Class
对象是JVM内部创建的,如果我们查看JDK源码,可以发现Class
类的构造方法是private
,只有JVM能创建Class
对象,我们自己的Java程序是无法创建Class
对象的。
一个Class
对象包含了该class
的所有完整信息:
由于JVM为每个加载的class
创建了对应的Class
对象,并在对象中保存了该class
的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class
对象,我们就可以通过这个Class
对象获取到该对象对应的class
的所有信息。
这种通过Class
对象获取class
信息的方法称为反射(Reflection)。
反射是指程序在运行期可以获取一个对象的所有信息。
反射的作用
- 在运行期获取一个类的全部成分,可读可改。
- 解除封装性。即使被private修饰的成员也可以进行使用。
- 解除泛型的约束。如一个集合被约束为
<String>
,可以利用反射强制填入其他类型的数据。 - 做Java高级框架
反射的第一步就是获取Class类对象,Class类不能直接通过构造获取对象:
// 名为 Class 的 class
public final class Class<T> {
private Class(...) { ... }
}
那么应该如何获取Class对象呢?
1、类名.class
通过一个class
的静态变量class
获取:
Class cl = String.class;
2、对象名.getClass
final native Class<?> getClass
() //通过类对象获取Class对象。方法来自Object
String s = "aaa";
Class c2 = s.getClass();
3、Class.forName
static Class<?> forName
(String className) //获取指定全限定名的类的Class对象
Class c3 = Class.forName("java.lang.String");
访问构造方法
获取构造器对象
Class API | 说明 |
---|---|
Constructor<?>[] getConstructors () | 返回全部公共权限构造器对象的数组 |
Constructor<?>[] getDeclaredConstructors () | 返回全部任意权限构造器对象的数组 |
Constructor getConstructor (Class<?>… parameterTypes) | 返回单个公共权限构造器对象 |
Constructor getDeclaredConstructor (Class<?>… parameterTypes) | 返回单个任意权限构造器对象 |
Student类
public class Student {
private String name;
private int age;
//私有的无参构造
private Student() {
}
//公共的带参构造
public Student(String name, int age) {
this.name = name;
this.age = age;
}
...
}
//返回全部公共权限构造器对象的数组
@Test
public void testGetConstructors() {
Class cl = Student.class;
Constructor[] constructors = cl.getConstructors();
for (Constructor c : constructors) {
// Constructor.getName :获取构造器名;
// Constructor.getParameterCount :获取构造器中参数个数
System.out.println(c.getName() + " -> " + c.getParameterCount());
// Reflection.Student -> 2
}
}
//返回全部任意权限构造器对象的数组
@Test
public void testGetDeclaredConstructors() {
Class cl = Student.class;
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c.getName() + " -> " + c.getParameterCount());
// Reflection.Student -> 0
// Reflection.Student -> 2
}
}
//获取公共权限的带参构造器对象
@Test
public void testGetConstructor() throws NoSuchMethodException {
Class cl = Student.class;
Constructor c = cl.getConstructor(String.class, int.class);
System.out.println(c.getName() + " -> " + c.getParameterCount());
// Reflection.Student -> 2
}
//获取私有权限的无参构造器对象
@Test
public void testGetDeclaredConstructor() throws NoSuchMethodException {
Class cl = Student.class;
Constructor c = cl.getDeclaredConstructor();
System.out.println(c.getName() + " -> " + c.getParameterCount());
// Reflection.Student -> 0
}
使用构造器对象
使用反射获取构造器对象的目的是创建对象。
Constructor API | 说明 |
---|---|
T newInstance (Object… initargs) | 根据指定的构造器创建对象 |
public void setAccessible (boolean flag) | true表示解除访问权限(暴力反射) |
将
setAccessible
置为true
后,即使是私有构造器也能构造对象。但该操作可能会失败。
还是上述Student类
public static void main(String[] args) throws Exception {
Class cl = Student.class;
Constructor con = cl.getDeclaredConstructor();
Student s = (Student) con.newInstance();
System.out.println(s); // IllegalAccessException 非法访问异常。
}
Student 类的无参构造是私有的,不能访问,但是可以通过 setAccessible 解除权限。
public static void main(String[] args) throws Exception {
Class cl = Student.class;
Constructor con = cl.getDeclaredConstructor();
//解除权限
con.setAccessible(true);
Student s = (Student) con.newInstance();
System.out.println(s); // Student{name = null, age = 0}
}
访问字段
字段(Field),是 Java 编程语言中类的一个成员,主要用来存储对象的状态,所以有时也可称为成员字段或成员变量。
获取字段对象
Class AIP | 说明 |
---|---|
Field[] getFields () | 返回全部公共权限成员变量对象的数组 |
Field[] getDeclaredFields () | 返回全部任意权限成员变量对象的数组 |
Field getField (String name) | 返回单个公共权限成员变量对象 |
Field `getDeclaredField(String name) | 返回单个任意权限成员变量对象 |
Student类
public class Student {
private String name;
private int age;
private static String TeacherName = "zhangSan";
public final String SchoolName = "laJi";
...
}
public static void main(String[] args) throws NoSuchFieldException {
//反射获取Student类的Class对象
Class cl = Student.class;
//获取全部任意权限字段对象的数组
Field[] fields = cl.getDeclaredFields();
for (Field field : fields) {
// Field.getName :获取变量名;Field.getType :获取变量类型
System.out.println(field.getName() + " - " + field.getType());
/* name - class java.lang.String
age - int
TeacherName - class java.lang.String
SchoolName - class java.lang.String */
}
//获取单一任意权限字段对象
Field field = cl.getDeclaredField("TeacherName");
System.out.println(field.getName() + " - " + field.getType());// TeacherName - class java.lang.String
}
使用字段对象
使用反射获取字段对象的目的是赋值和取值。
Field API | 说明 |
---|---|
void set (Object obj, Object value) | 给指定对象注入指定值 |
Object get (Object obj) | 获取指定对象的成员变量值 |
void setAccessible (boolean flag) | true表示解除访问权限(暴力反射) |
public static void main(String[] args) throws Exception {
//获取指定字段对象
Class cl = Student.class;
Field name = cl.getDeclaredField("name");
//创建Student对象
Student s = new Student("liSi", 23);
//解除字段权限
name.setAccessible(true);
//修改字段值
name.set(s, "wangWu");
System.out.println(s); // Student{name = wangWu, age = 23}
//获取字段值
String str = (String) name.get(s);
System.out.println(str); // wangWu
}
访问成员方法
获取方法对象
Method AIP | 说明 |
---|---|
Method[] getMethods () | 返回全部公共权限成员方法对象的数组 |
Method[] getDeclaredMethods () | 返回全部任意权限成员方法对象的数组 |
Method getMethod (String name, Class<?>… parameterTypes) | 返回单个公共权限成员方法对象 |
Method getDeclaredMethod (String name, Class<?>… parameterTypes) | 返回单个任意权限成员方法对象 |
Play类
public class Play {
private void play() {
System.out.println("玩...");
}
private void play(String name) {
System.out.println("玩" + name);
}
}
public static void main(String[] args) throws NoSuchMethodException {
Class cl = Student.class;
//返回全部任意权限成员方法对象的数组
Method[] methods = cl.getDeclaredMethods();
for (Method method : methods) {
// getName:获取方法名; getParameterTypes:获取参数类型; getAnnotatedReturnType:获取返回值类型;
System.out.println(method.getName() + " - " + Arrays.toString(method.getParameterTypes()) + " - " + method.getAnnotatedReturnType());
/* 运行结果
play - [] - void
play - [class java.lang.String] - void
*/
}
//返回单个任意权限成员方法对象
Method method = cl.getDeclaredMethod("setName", String.class);
System.out.println(method.getName() + " - " + Arrays.toString(method.getParameterTypes()) + " - " + method.getAnnotatedReturnType());
// play - [class java.lang.String] - void
}
使用方法对象
使用反射获取方法对象的目的是执行。
Method API | 说明 |
---|---|
Object invoke (Object obj, Object… args) | 触发方法对象对应的方法 |
void setAccessible (boolean flag) | true表示解除访问权限(暴力反射) |
参数说明
Object obj
: 用于调取方法的对象
Object... args
: 调用方法时传递的参数(无参不写)
Object
(返回值类型) : 方法的返回值类型,没有返回值返回 null
public static void main(String[] args) throws Exception {
Class cl = Play.class;
//获取两种方法
Method m1 = cl.getDeclaredMethod("play");
Method m2 = cl.getDeclaredMethod("play", String.class);
//解除权限
m1.setAccessible(true);
m2.setAccessible(true);
//触发方法
Play p = new Play();
Object ret1 = m1.invoke(p); // 玩...
Object ret2 = m2.invoke(p, "游戏"); // 玩游戏
System.out.println(ret1); // null
System.out.println(ret2); // null
}
通用框架
反射的最大作用就是做通用框架。
需求: 在不清楚对象字段的情况下,可以将对象的字段名称和对应值储存到文件中。
学生类:
public class Student {
private String name;
private int age;
private char sex;
private String className;
...
}
教师类:
public class Teacher {
private String name;
private int age;
private double wages;
...
}
框架:
public class Universal {
/**
* 可以将不同类型的对象写入文件
*
* @param obj 任意对象
*/
public static void save(Object obj) {
try (PrintStream ps = new PrintStream(new FileOutputStream("D:\\test\\info.txt", true));) {
//1.关联对象的class文件
Class cl = obj.getClass();
ps.println("-------------" + cl.getSimpleName() + "-------------");
//2.获取对象的全部字段(成员变量)
Field[] fields = cl.getDeclaredFields();
for (Field field : fields) {
try {
//3.获取字段在对象中对应的信息
String fieldName = field.getName(); //字段名
field.setAccessible(true);
String fieldValue = field.get(obj) + ""; //字段值
//4.写入文件
ps.println(fieldName + " = " + fieldValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试:
public class Demo {
public static void main(String[] args) {
Student s = new Student("小明", 19, '男', "一班");
Teacher t = new Teacher("张三", 30, 8000);
Universal.save(s);
Universal.save(t);
}
}
运行结果 info.txt文件中:
-------------Student-------------
name = 小明
age = 19
sex = 男
className = 一班
-------------Teacher-------------
name = 张三
age = 30
wages = 8000.0