反射机制

认识反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
Class类对象实例化的三种方式
第一种:调用Object类中的getClass方法,此操作必须要先new实例化对象
Date date = new Date(); //产生实例化
Class<?cla = date.getClass(); //通过实例化对象取得Class对象
//Class类中定义有pubilc String getName()方法可以取得类的完整名称
System.out.println(cla.getName()); //直接对象所在类的名称
输出内容:java.util.Date
第二种:使用“类.class”获得,此时不需要new对象取得
Class<?cla = java.util.Date.class; //通过类名称取得Class类对象
System.out.println(cla.getName());
第三种:调用Class类提供的方法:public static Class<?forName(String className) throws ClassNotFoundExecption
//此处直接传递了一个要进行反射操作类的完整名称,是利用字符串定义的
//前提是此类是存在的,不然就会抛出异常
Class<?cla = Class.forName("java.util.Date");//直接对象所在类的名称
//这是自己建的类
Class<?cla1 = Class.forName("Class类对象实例化.第二种_利用类点class取得.TestDemo");//直接对象所在类的名称
反射实例化对象
package 利用反射实例化对象;
class Book {
    public Book() {
        System.out.println("***********这是一个无参构造************");
    }
    public String toString() {
    return "《第一行代码》";
    }
}
public class TestDemo {
     public static void main(String[] argsthrows Exception {
     
Class<?cla = Class.forName("利用反射实例化对象.Book");//设置要操作对象的类
         @SuppressWarnings("deprecation")//取消警告
         //反射实例化后的对象返回的结果都是Object类型
        Object obj = cla.newInstance();//相当于使用new调用无参构造 new Book()
         Book book = (Bookobj; //向下转型
       System.out.println(book);
    }
}
但这有什么用呢,相比之下直接new不是更方便?
看一下这一段代码就知道了

package 利用反射实现工厂设计模式; //这是我的包

interface Fruit {
    public void eat();
}
class Apple implements Fruit {
    @Override
    public void eat() {
        System.out.println("***吃苹果!");
    }
}
class Orange implements Fruit {
    @Override
    public void eat() {
        System.out.println("***吃橘子!");
    }
}
class Factory{
/* //这是之前的工厂设计模式,主程序不需要用new关键字来创建水果类,而是根据需要,传递一个字符串来创建水果类
* //使用Factory.getInstance()方法根据指定子类的标记取得接口实例化对象,这就是工厂设计模式
* //不过这种简单的工厂设计模式有个缺点,就是它只有苹果和橘子类,而我想要香蕉,西瓜类,
* //那就又得写一个香蕉类和西瓜类,不过这不重要,只要将它们实现Fruit接口就行了,
* //重要的是Factory类的getFruitName()要修改,又要进行判断是否是香蕉类来创建对象,
* //如果程序已经运行了,那就不可能再去修改程序了,所以这是一个弊端,这种弊端可以用反射解决
* public static Fruit getFruitName(String str) {
* if("apple".equals(str)) { //是否是苹果类
* return new Apple();
* } else if("orange".equals(str)) { //是否是橘子类
* return new Orange();
* }
* }
*/
/*
* 用new创建的对象这种方式耦合度太高,所以我们要解耦合
* 下面的方法使用了反射机制,不仅可以根据主方法传递的完整类名创建方法,也解决了上面所说的弊端,
* 这时我想要创建香蕉类,只需要写一个香蕉类实现Fruit接口,工厂类完全不需要发生改动
* 而且就算工厂类在运行时,也可以创建其他类,这就是反射机制的好处,可以动态创建对象
*/
    @SuppressWarnings("deprecation"//取消警告
    public static Fruit getFruitName(String str) {
    Fruit f = null;
    try {
        f = (FruitClass.forName(str).newInstance();
    } catch (Exception e) {
        System.out.println("没有这个水果!");
    }
    return f;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Fruit f1 = Factory.getFruitName("利用反射实现工厂设计模式.Orange");
        Fruit f2 = Factory.getFruitName("利用反射实现工厂设计模式.Apple");
        f1.eat();
        f2.eat();
    }
}

使用反射调用构造
利用Class类的newInstance()方法可以实现反射实例化对象的操作,但是这样操作有一个限制,就是类中必须要有无参构造方法,
所以当类中只提供有参构造方法,就必须通过java.lang.reflect.Constructor类实例化对象的反射实例化操作
package 使用反射调用构造方法;
import java.lang.reflect.Constructor;
class Book {
    private String title;
    private double price;
    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }
    public String toString() {
        return "书名:"+this.title+",价格:"+this.price;
    }
}
public class TestDemo {

public static void main(String[] argsthrows Exception {
    Class<?cla = Class.forName("使用反射调用构造方法.Book");//锁定Book类
    //根据参数数量,顺序,类型,锁定Book类中的构造方法
    // 取消安全性检查,设置后才可以使用private修饰的构造函数,也可以单独对某个构造函数进行设置
    //Constructor.setAccessible(constructors, true);
    Constructor<?con = cla.getConstructor(String.class,double.class);
    //实例化对象,传递参数
    Object obj = con.newInstance("java实战开发",79.9);//相当于new Book("java实战开发",79.9);
    System.out.println(obj);
    }
}

反射调用方法
public void methodTest() throws Exception {
    User user = userClass.newInstance();
    // 获取当前类的所有方法
    Method[] methods = userClass.getDeclaredMethods();
    // 获取公有方法(包括父类)
    // Method[] methods = userClass.getMethods();
    // 取消安全性检查,设置后才可以调用private修饰的方法,也可以单独对某个方法进行设置
    Method.setAccessible(methods, true);
    for (Method method : methods) {
        // 获取方法名和返回类型 获取参数类型:getParameterTypes
        logger.info("方法名:" + method.getName() + " \t返回类型:" + method.getReturnType().getName());
    }
    // 获取无参方法
    Method getMethod = userClass.getDeclaredMethod("getName");
    // 取消安全性检查,设置后才可以调用private修饰的方法,也可以批量对所有方法进行设置
    getMethod.setAccessible(true);
    // 调用无参方法
    logger.info("调用getName方法:" + getMethod.invoke(user));
    // 获取有参方法
    Method setMethod = userClass.getDeclaredMethod("setName", String.class);
    // 取消安全性检查,设置后才可以调用private修饰的方法,也可以批量对所有方法进行设置
    setMethod.setAccessible(true);
    // 调用有参方法
    logger.info("调用setName方法:" + setMethod.invoke(user, "韦德"));
    logger.info("通过set方法修改属性后对象:\t" + user);
}
反射调用属性
public void fieldTest() throws Exception {
    User user = userClass.newInstance();
    // 获取当前类所有属性
    Field fields[] = userClass.getDeclaredFields();
    // 获取公有属性(包括父类)
    // Field fields[] = cl.getFields();
    // 取消安全性检查,设置后才可以获取或者修改private修饰的属性,也可以单独对某个属性进行设置
    Field.setAccessible(fields, true);
    for (Field field : fields) {
        // 获取属性名 属性值 属性类型
        logger.info("属性名:" + field.getName() + "\t属性值:" + field.get(user) + " \t属性类型:" + field.getType());
    }
    Field fieldUserName = userClass.getDeclaredField("name");
    // 取消安全性检查,设置后才可以获取或者修改private修饰的属性,也可以批量对所有属性进行设置
    fieldUserName.setAccessible(true);
    fieldUserName.set(user, "韦德");
    // 可以直接对 private 的属性赋值
    logger.info("修改属性后对象:\t" + user);
 }
点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注