javaweb-day-52

2021.12.6

反射

程序中的反射指程序运行状态中,

1、对于给定的一个类(Class)对象,可以获得这个类(Class)对象的所有属性和方法;

2、对于给定的一个对象(new XXXClassName<? extends Object>),都能够调用它的任意一个属性和方法。

这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,就叫做Java反射机制

反射初识

public class User {

    public void eat(){

        System.out.println("---eat---");

    }

}

public class TestReflect{

    public static void main(String[] args) throws Exception {

        Class<User> clazz = User.class;

        //获取类对象对应的属性和方法

        System.out.println(clazz.getName());

        System.out.println(clazz.getPackage());

        System.out.println(clazz.getSuperclass());

        System.out.println(clazz.getClassLoader());

        //获取一个对象实例

        User user = clazz.newInstance();

        Method method = clazz.getDeclaredMethod("eat");

        method.invoke(user);

    }

 为何用反射

直接new User()不就可以了,为什么要用这种反射写法?

实例化一个User()对象,不使用反射,如果想再实例化其他对象比如new Person(),那么

就需要修改源代码,并重新编译。使用反射后就不需要修改源代码只需要灵活改动类描述就

可以了,不需要重新再编译。 如下代码所示

 //类描述,可根据需要实例化的描述而改动
    String className = "com.tledu.pojo.User";
    //使用反射中API创建对象
    Class.forName(className).newInstance();

 反射的优缺点

优点:

增加程序的灵活性,避免固有逻辑写死到程序中

代码相对简洁,可以提高程序的复用性

缺点:

相比于直接调用反射有比较大的性能销毁

内部暴露和安全隐患

灵活性测试 

public interface Ball {
        void playBall();
    }
    public class BasketBall implements Ball{
        @Override
        public void playBall() {
            System.out.println("打篮球");
        }
    }
    public class FootBall implements Ball{
        @Override
        public void playBall() {
            System.out.println("踢足球");
        }
    }
    //----------------------------
    public static void main(String[] args) {
       //System.out.println(getInstanceByKey("basket"));
        System.out.println(getInstanceReflectByKey("FootBall"));
    }
    public static Ball getInstanceByKey(String key) {
        if("foot".equals(key)){
            return new FootBall();
        }
        if("basket".equals(key)){
            return new BasketBall();
        }
        return null;
    }

 如果我们想在添加一个乒乓球那么除了创建一个乒乓球类,还需要在方法中加入获取乒乓球对应的判断和乒乓球创建对象的返回,很显然不够灵活出现了写死代码的情况。

public static Ball getInstanceReflectByKey(String key) {
        String basePackage = "com.tledu.mjw";
        Ball ball = null;
        try {
            Class clazz = Class.forName(basePackage + "." + key);
            ball = (Ball)clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ball;
    }

 使用反射的方式获取对象,可以看出代码灵活了很多而且不需要改动创建返回对象的源码。


性能测试 

public static void main(String[] args) {
       //System.out.println(getInstanceByKey("basket"));
       //System.out.println(getInstanceReflectByKey("FootBall"));
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++){
            getInstanceByKey("basket");
            //getInstanceReflectByKey("FootBall");
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start));
    }

为什么这么慢呢?(分别查看forName()方法和newIntance()方法源码分析)

1、调用了native方法

2、每次调用newInstance()方法都会做安全检查,比较耗时

反射的操作 

 

获取类对象的四种方式

Class<User> clazz1 = User.class;

Class<?> clazz2 = Class.forName("com.tledu.mjw.User");

Class<? extends User> clazz3 = new User().getClass();

Class<?> clazz4 = Demo3.class.getClassLoader().loadClass("com.tledu.mjw.User");

类中基本信息的获取 

System.out.println(clazz1.getClassLoader());

System.out.println(clazz1.getSuperclass());

System.out.println(clazz1.getPackage());

System.out.println(clazz1.getModifiers()); //获取类的修饰符

System.out.println(clazz1.getName());

System.out.println(clazz1.getSimpleName());

System.out.println(clazz1.getInterfaces().length);//获取类实现的所有接口

System.out.println(clazz1.getAnnotations().length);

类中字段的操作 

创建User类继承Person类添加相应属性

public class Person {
    public String idCard;
    private String userName;
}
public class User extends Person{
    private String name;
    public String sex;
    public static String address;
    public void eat(){
        System.out.println("---eat---");
    }
}

 测试方法调用

public static void main(String[] args) throws Exception {
    Class<User> userClass = User.class;
    User user = userClass.newInstance();
    //getFields()获取的是本类及父类中的公共属性
    Field[] fields = userClass.getFields();
    for (Field field : fields) {
        System.out.println(field.getModifiers() + " " + field.getName());
    }
    System.out.println("---------------");
    //getDeclaredFields()获取本类中的所有属性
    Field[] fields2 = userClass.getDeclaredFields();
    for (Field field : fields2) {
        System.out.println(field.getModifiers() + " " + field.getName());
    }
    System.out.println("---------------");
    //获取name字段对应的field
    Field nameField = userClass.getDeclaredField("name");
    //如果需要修改私有属性信息那么我们要放开权限
    nameField.setAccessible(true);
    nameField.set(user,"天亮教育");
    System.out.println(nameField.get(user));
    //System.out.println(user.getName());
    System.out.println("---------------");
    //如何对静态的属性赋值
    Field addressField = userClass.getDeclaredField("address");
    addressField.set(null,"Jack");
    System.out.println(addressField.get(null));
    //System.out.println(User.address);
}

类中方法的操作

在User类中将eat方法改为私有的,并添加静态方法say 

private void eat(){
    System.out.println("---eat---");
}
public static void say(String msg){
    System.out.println(msg);
}

在Person类中添加共有的fn1()方法和私有的fn2()方法

public void fn1(){}
private void fn2(){}

 测试方法调用

public static void main(String[] args) throws Exception {
    Class<User> userClass = User.class;
    User user = userClass.newInstance();

    //获取本类及父类中的公共方法
    Method[] methods1 = userClass.getMethods();
    for (Method method : methods1) {
        System.out.println(method.getModifiers() + " " + method.getName());
    }
    System.out.println("--------------");
    //获取本类中所有的方法包括私有
    Method[] methods2 = userClass.getDeclaredMethods();
    for (Method method : methods2) {
        System.out.println(method.getModifiers() + " " + method.getName());
    }
    System.out.println("--------------");
    //调用方法执行
    Method eatMethod = userClass.getDeclaredMethod("eat");
    //调用私有方法时要先开放权限
    eatMethod.setAccessible(true);
    eatMethod.invoke(user);
    System.out.println("--------------");
    //调用静态的方法
    Method sayMethod = userClass.getDeclaredMethod("say",String.class);
    sayMethod.invoke(null,"你好");
}

 类中构造器的操作

在User中添加如下构造方法 

public User() {
}
public User(String name) {
    this.name = name;
}
private User(String name,String sex) {
    this.name = name;
    this.sex = sex;
}

 通过反射操作User中的构造方

public static void main(String[] args) throws Exception {
    Class<User> userClass = User.class;
    //获取公共的构造器
    Constructor<?>[] constructors = userClass.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor.getModifiers() + " " + constructor.getName());
    }
    System.out.println("------------------");
    //获取所有的构造器
    Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
    for (Constructor<?> declaredConstructor : declaredConstructors) {
        System.out.println(declaredConstructor.getModifiers() + "  " + declaredConstructor.getName());
    }
    //1、直接通过newInstance创建对象
    User user = userClass.newInstance();
    //2、获取对应的Constructor对象获取实例
    Constructor<User> constructor = userClass.getDeclaredConstructor(String.class, String.class);
    //操作私有的构造器要先打开权限
    constructor.setAccessible(true);
    User user1 = constructor.newInstance("乔森", "男");
}

 反射破局单例

单例模式 

public class PersonSingle {
    private static PersonSingle instance = null;
    private PersonSingle(){}
    public static PersonSingle getInstance(){
        if(instance == null){
            instance = new PersonSingle();
        }
        return instance;
    }
}

 测试单例、通过反射可以调用私有构造

public static void main(String[] args) throws Exception {
    PersonSingle instance1 = PersonSingle.getInstance();
    PersonSingle instance2 = PersonSingle.getInstance();
    PersonSingle instance3 = PersonSingle.getInstance();
    System.out.println(instance1);
    System.out.println(instance2);
    System.out.println(instance3);
    System.out.println("---------------------");
    Class<PersonSingle> personSingleClass = PersonSingle.class;
    Constructor<PersonSingle> declaredConstructor = personSingleClass.getDeclaredConstructor();
    declaredConstructor.setAccessible(true);
    System.out.println(declaredConstructor.newInstance());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值