1.反射的概念:运行时动态查询类的能力。
反射机制可以用来:
- 在运行时分析类的能力
- 在运行时检查对象,例如,编写一个适用于所有类的方法。
- 实现泛型数组操作代码。
2.Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个运行时候类型标识。这个信息会跟踪每个对象所属的类。
3.获取Class对象的三种方式:
- 通过静态方法:Class.forName(className),其中className为类的全限定名:包名+类名。例如:“java.util.Random”
public static void main(String[] args) throws ClassNotFoundException {
//定义要加载的类全限定名
String className = "java.util.Random";
//获取Class对象
Class cl = Class.forName(className);
}
2.通过类.class来创建Class对象
public static void main(String[] args){
//调用Random类的class来创建class对象
Class cl = Random.class;
}
3.通过类对象.getClass()来创建Class对象
public static void main(String[] args){
//创建random对象
Random random = new Random();
//从对象中获取class
Class cl = random.getClass();
}
常用反射还是第一种,利用全限定名进行加载对象。
4.定义用于说明反射的两个自定义类
Employee类,描述员工的基本信息
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee() {
}
public Employee(String name, double salary, int year, int month, int day) {
this.name = name;
this.salary = salary;
this.hireDay = LocalDate.of(year, month, day);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public LocalDate getHireDay() {
return hireDay;
}
@Override
public String toString() {
return "Employee [name=" + name + ", salary=" + this.getSalary() + ", hireDay=" + hireDay + "]";
}
}
Manager类,继承自Employee类
public class Manager extends Employee {
private double bonus;
public Manager() {
}
public Manager(String name, double salary, int year, int month, int day) {
super(name, salary, year, month, day);
this.bonus = 0;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public double getSalary() {
return this.bonus + super.getSalary();
}
}
5.反射操作:
通过forName创建Class对象,后调用构造器实例化对象,然后在强转为指定类型的对象。
public static void main(String[] args) throws Exception{
//要使用反射加载的类名
String className = "com.shi.refelect.Manager";
//使用Class.forName()加载类
Class cl = Class.forName(className);
//调用构造器后生成实例对象,返回类型为Object。(此时的构造器是无参构造)
Object obj = cl.getConstructor().newInstance();
//为构造器传递指定类型的参数,
Object obj2 = cl.getConstructor(String.class, double.class, int.class, int.class, int.class).newInstance("张三", 5000, 2019, 12, 25);
//对返回的Object类型进行强转
Manager manager = (Manager)obj;
Manager manager2 = (Manager)obj2;
//输出反射生成的Manager对象
System.out.println(manager);
System.out.println(manager2);
}
6.从Class类对象中获取Fields,Methods, Constractors.
public static void main(String[] args) throws Exception{
//要使用反射加载的类名
String className = "com.shi.refelect.Manager";
//使用Class.forName()加载类
Class cl = Class.forName(className);
//返回类中的公共字段,包括超类中的公共字段。返回类型为Field[]。 此时类中没有定义公共字段,fields为空
Field[] fields = cl.getFields();
System.out.println("包含超类的公共字段: " + Arrays.toString(fields));
//返回类中你定义的字段,不限于公共字段。不包括超类
fields = cl.getDeclaredFields();
System.out.println("不包括超类的字段: "+Arrays.toString(fields));
//返回类中的公共方法,包括超类中的公共方法。返回类型为Method[]。
Method[] method = cl.getMethods();
System.out.println("包含超类的公共方法: "+Arrays.toString(method));
//返回类中你定义的方法,不限于公共方法。不包括超类
method = cl.getDeclaredMethods();
System.out.println("不包含超类的方法: "+Arrays.toString(method));
//返回类中的公共构造器,包括超类中的构造器
Constructor[] constructor = cl.getConstructors();
System.out.println("包含超类的公共构造方法: "+Arrays.toString(constructor));
//返回类中的构造器,不包括超类的构造器
constructor = cl.getDeclaredConstructors();
System.out.println("不包含超类的构造方法: "+Arrays.toString(constructor));
}
7.Field在反射中的应用
public static void main(String[] args) throws Exception {
//要获取类的全限定名
String className = "com.shi.refelect.Manager";
//利用反射加载类
Class cl = Class.forName(className);
//获取Manager类的字段,(可以是私有的,不包括其超类的字段)
Field field = cl.getDeclaredField("bonus");
//设置字段是可访问的,不然会报错
field.setAccessible(true);
//创建一个Manager类对象
Manager manager = new Manager("张三", 5000, 2018, 12, 23);
//通过field字段获取manager的bonus字段值,其返回类型为Object
Object obj = field.get(manager);
//把字段值转换为double后输出字段值 结果是0,因为bonus字段没有显式的赋值
System.out.println((double) obj);
//通过field字段设置manager的bonus字段值
field.set(manager, 500);
//此时的bonus字段值为500
System.out.println(manager.getBonus());
}
上面Field.getDeclaredField()能获取 private 和 public 字段 ,其中获取 private字段需要设置field.setAccessible(true); 公共字段可以不用设置field.setAccessible(true);当要获取超类的字段值时,使用:
Field field = cl.getField(字段名);
但是只能获取超类的公共字段,不能获取其其他类型的字段。
8.Method在反射中的应用
public static void main(String[] args){
//要获取类的全限定名
String className = "com.shi.refelect.Manager";
//利用反射加载类
Class cl = Class.forName(className);
//获取有指定参数的方法
//获取公共的带参数的method,指定方法名和参数类型
Method method = cl.getMethod("setBonus", double.class);
//若setBonus()为私用的则用下面这种形式调用。
// Method method = cl.getDeclaredMethod("setBonus", double.class);
// method.setAccessible(true);
//为方法传递对象和参数。
method.invoke(manager, 2000);
//输出结果 bonus = 2000
System.out.println(manager.getBonus());
//获取没有参数的方法
//获取公共无参的method,指定方法名,参数类型为null
Method m = cl.getMethod("getBonus", null);
//在指定对象上执行方法,返回类型为Object
Object target = m.invoke(manager, null);
//进行强转后输出其内容,target = 2000
System.out.println((double) target);
}
Method.getMethod() 可以获取对象包括超类的公共方法,Method.getDeclaredMethod() 可以获取对象的任何方法(不包括超类对象的任何方法)。
- 获取带参数的方法时,需要指定方法名和参数所属的类对象:Method method = cl.getMethod(“setBonus”, double.class); 指定 setBonus(double bonus) 方法。调用时需要传递指定对象和参数:method.invoke(manager, 2000); 意思为:执行 manager.setBonus(2000) 操作;
- 获取不带参数而有返回值的方法时,指定方法名和null参数。Method m = cl.getMethod(“getBonus”, null); 说明此方法没有参数。执行时需要:Object target = m.invoke(manager, null); 在指定对象上执行,且有返回值的话要使用Object对象来进行接收。
9.Constrator在反射中的应用
public static void main(String[] args){
//要获取类的全限定名
String className = "com.shi.refelect.Manager";
//利用反射加载类
Class cl = Class.forName(className);
//获取反射类对象的无参构造
Constructor con = cl.getConstructor();
//使用无参构造来生成实例对象
Object obj2 = con.newInstance();
//判断对象是否为Manager类
if (obj2 instanceof Manager) {
System.out.println("反射成功");
}
//获取反射类对象的有参构造
Constructor con2 = cl.getConstructor(String.class, double.class, int.class, int.class, int.class);
//在实例化对象时进行传参,返回值为Object类型
Object obj3 = con2.newInstance("李四", 8000, 2017, 12, 25);
//若属于Manager类对象则进行强转
if (obj3 instanceof Manager) {
obj3 = (Manager) obj3;
}
//输出反射生成的类对象
System.out.println(obj3);
}
- 无参构造,Constructor con = cl.getConstructor(); 不指定任何参数的构造器,实例化时也不需要进行传参: Object obj2 = con.newInstance(); 反射生成的类都是Object类型的。
- 有参构造:Constructor con2 = cl.getConstructor(String.class, double.class, int.class, int.class, int.class); 指定参数类型的构造器,实例化时需要传递相对应类的参数值:Object obj3 = con2.newInstance(“李四”, 8000, 2017, 12, 25); 构造成功时,通过 instanceof 来进行判断,若属于manager类则可以进行强转。