什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射。简单地说:反射就是根据Java类的实例化对象或Java的类名来获取类的信息(类名,类成员,包名,类方法),然后将这些信息映射为一个个对象(因为在Java中,万物皆对象,类名,类成员,包名,类方法 都是对象).
Class类
因为反射机制是要在程序运行时获取类的属性,而Java类在运行时是被编译成字节码文件的,所以要使用反射,必须获取Java类的字节码文件,而Class类就用来获取Java类的字节码文件. Class类是没有公共构造函数的,无法由我们手动地创建,Class类对象是在JVM将class文件(要分清class和Class,class文件是指一个Java类经过编译后产生的文件)读入内存的时候自动创建的需要注意的是每个Java类在编译后有且只有一个class文件,所以每一个Java类的Class对象都是只有一个–无论Class对象是由实例对象(即使不是同一个对象)获取还是由Java类获取,这个Java类的Class对象都是同一个
如果还是不太清楚的话,可以看下面的例子:
public static void main(String[] args) throws Exception {
String str1 = "abc";
String str2 = "aaa";
//通过对象.getClass()获取
Class class1 = str1.getClass();
// 通过类.class获取
Class class2 = String.class;
//通过Class.forName("类名")或
Class class3 = Class.forName("java.lang.String");
Class class4 = str2.getClass();
System.out.println(class1==class2);
System.out.println(class2==class3);
System.out.println(class1==class4);
}
上面三个例子都是true,他们的Class类对象都是同一个.
同时也可以看到获取一个类的Class对象有三种方式:
- Object . getClass();
- Java类.class , (包括基本数据类型)
- forName(String className)
使用反射获取构造方法
创建一个User类来演示如何使用反射来获取类的构造方法public class User {
//类成员属性
private String name;
private int password;
//无参构造函数
public User(){
System.out.println("null");
}
//一个参数的有参构造函数
public User(String name){
this.name = name;
System.out.println(name);
}
private User(int password){
this.password = password;
System.out.println(password);
}
//两个参数的有参构造函数
public User(String name,int password){
this.password = password;
this.name = name;
System.out.println(name+"-"+password);
}
}
使用Class对象获取Java类的所有构造函数
public static void main(String[] args) throws Exception {
//获取User类的Class对象
Class userClass = User.class;
//获取User类的所有构造方法(包括公有和私有的)
Constructor[] constructors = userClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
}
public cn.QEcode.day01.User(java.lang.String,int)
public cn.QEcode.day01.User(java.lang.String)
public cn.QEcode.day01.User()
Class类还提供了其他获取构造函数的方法:
public Constructor[] getConstructors():所有公有构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
public Constructor getConstructor(Class… parameterTypes):获取公有的构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取构造方法包括(私有,受保护、默认、公有)
使用反射调用构造方法
//获取User类中,参数为string型的构造函数
Constructor constructor = userClass.getConstructor(String.class);
//调用构造方法,实例化一个对象
Object user1 = constructor.newInstance("user");
在使用反射调用构造方法的时候要注意:
- 使用反射来调用构造方法,在实例化对象时,如果调用的构造方法参数类型和获取的构造方法的参数类型不一致时,Java是不会报错的,只有运行的时候,才会抛出异常
//获取User类中,参数为string型的构造方法
Constructor constructor = userClass.getConstructor(String.class);
//调用构造方法,但传入的参数类型为double型,可以编译,但运行时报错
Object user2 = constructor.newInstance(1.0);
- 调用私有的构造方法需要暴力访问
Constructor constructor = userClass.getConstructor(int.class);
//暴力访问
constructor.setAccessible(true);
Object user = constructor.newInstance(1);
使用反射获取类属性
和获取构造方法一样,Class类为我们提供四种获取类属性的方法 Field[] getFields():获取所有的 ==公有== 属性 Field[] getDeclaredFields():获取所有属性,包括:私有、受保护、默认、公有 public Field getField(String fieldName):获取某个==公有的==属性; public Field getDeclaredField(String fieldName):获取某个属性(可以是私有的)为了方便演示,将User类中的name改为public.
public String name;
获取所有公有属性
//获取User类的Class对象
Class userClass = User.class;
//获取公有属性
Field[] field = userClass.getFields();
for (Field f : field) {
System.out.println(f);
}
public java.lang.String cn.QEcode.day01.User.name
设置字段的值
Field.public void set(Object obj,Object value):
第一个参数:要赋值的实例化对象
第二个参数:要赋的值
这里要理解为什么传入的是实例化对象?
因为除非是static属性,否则每个实例化对象的属性都不是同一个,所以我们要赋值的是某个实例化对象的属性,而不是一个class类的属性.这个要注意.
要注意:如果要获取的属性是私有的,要设置Field.setAccessible(true).
//获取User类的Class对象
Class userClass = User.class;
//获取公有属性
Field field = userClass.getField("name");
//获取User对象
Object user = userClass.getConstructor().newInstance();
//给name赋值
field.set(user, "user");
User user1 = (User) user;
System.out.println(user1.name);
使用反射获取类方法
public Method[] getMethods():获取所有公有方法;(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法
public Method getMethod(String name,Class<?>… parameterTypes): 获取一个公有成员方法
public Method getDeclaredMethod(String name,Class<?>… parameterTypes): 获取一个成员方法
//获取User类的Class对象
Class userClass = User.class;
//获取User对象
Object user = userClass.getConstructor().newInstance();
//第一个参数是方法的明珠,第二个参数是方法参数的类型的Class对象
Method method = userClass.getMethod("test",int.class);
//调用方法,第一个参数是实例化对象,第二个参数是方法参数
Object i = method.invoke(user,1);
System.out.println(i);
**要注意:如果要获取的属性是私有的,要设置Method.setAccessible(true).还有要理解为什么调用方法时method.invoke(object,object) ,传入的第一个参数是一个实例化对象.但是…static 方法是例外,在调用static方法的时候,第一个参数可以是null,即method.invoke(null,object)
**