java 反射运用

一,获取私有的属性,方法,构造器(俗名:暴力反射)

 现有一个类,属性,方法,构造器均为私有的,如何创建实例对象,更该属性值,调用方法?

public class Student {
    private String name;
    private int age;
    private Student() {
        System.out.println("调用了Student()无参私有构造器");
    }
    private String info() {
        System.out.println("调用了info()无参私有方法");
        return "Student [name=" + name + ", age=" + age + "]";
    }
    private void look(String str) {
        System.out.println("调用了look(String str)参数为String类型的私有方法,参数为:"+str);
    }
}

main方法:

首先要创建Student类的实例对象,即调用该类私有的构造方法

public static void main(String[] args) {
        try {
            //1.首先要创建Student类的实例对象
            //1.1创建Student类的反射对象
            Class<Student> clazz = Student.class;
            //1.2获取私有构造器
            Constructor<Student> c0 = clazz.getDeclaredConstructor();
            //1.3设置访问权限
            c0.setAccessible(true);
            //1.4创建实例对象
            Student student = c0.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行,控制台输出结果:

调用了Student()无参私有构造器

证明调用了Student类的构造器,并创建了该类的一个实例对象

更改,获取该类的私有属性

public static void main(String[] args) {
        try {
            //1.首先要创建Student类的实例对象
            //1.1创建Student类的反射对象
            Class<Student> clazz = Student.class;
            //1.2获取私有构造器
            Constructor<Student> c0 = clazz.getDeclaredConstructor();
            //1.3设置访问权限
            c0.setAccessible(true);
            //1.4创建实例对象
            Student student = c0.newInstance();
            
            //获取私有属性 name
            Field name = clazz.getDeclaredField("name");
            //设置访问权限
            name.setAccessible(true);
            //为该对象的该属性赋值
            name.set(student, "张三");
            
            Field age = clazz.getDeclaredField("age");
            age.setAccessible(true);
            age.set(student, 18);
            //获取该属性的值:get(实例对象)
            Object nameVal = name.get(student);
            System.out.println("name:"+nameVal);
            Object ageVal = age.get(student);
            System.out.println("age:"+ageVal);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行结果:

调用了Student()无参私有构造器
name:张三
age:18

调用私有方法

public static void main(String[] args) {
        try {
            //1.首先要创建Student类的实例对象
            //1.1创建Student类的反射对象
            Class<Student> clazz = Student.class;
            //1.2获取私有构造器
            Constructor<Student> c0 = clazz.getDeclaredConstructor();
            //1.3设置访问权限
            c0.setAccessible(true);
            //1.4创建实例对象
            Student student = c0.newInstance();
            
            //获取私有属性 name
            Field name = clazz.getDeclaredField("name");
            //设置访问权限
            name.setAccessible(true);
            //为该对象的该属性赋值
            name.set(student, "张三");
            
            Field age = clazz.getDeclaredField("age");
            age.setAccessible(true);
            age.set(student, 18);
            //获取该属性的值:get(实例对象)
            Object nameVal = name.get(student);
            System.out.println("name:"+nameVal);
            Object ageVal = age.get(student);
            System.out.println("age:"+ageVal);
            
            //获取info方法
            Method infoMethod = clazz.getDeclaredMethod("info");
            //设置访问权限
            infoMethod.setAccessible(true);
            //执行该方法,获取返回值
            Object infoRet = infoMethod.invoke(student);
            //打印返回值
            System.out.println(infoRet);
            
            //获取look方法
            Method lookMethod = clazz.getDeclaredMethod("look", String.class);
            //设置访问权限
            lookMethod.setAccessible(true);
            //执行该方法,传参
            lookMethod.invoke(student, "Hello World");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行结果:

调用了Student()无参私有构造器
name:张三
age:18
调用了info()无参私有方法
Student [name=张三, age=18]
调用了look(String str)参数为String类型的私有方法,参数为:Hello World

小结:通过java反射机制,可以调用任意类中的任意修饰符的属性,方法,构造器

java的反射机制跟封装冲突吗?

一个房子,没有门,没有窗户,但我为了进去,只能穿墙了...

二,通过反射机制灵活调用(多态)

先上一组简单代码

public interface Father {
    void look();
    String see(String str);
}
public class AAA implements Father{
    @Override
    public void look() {
        System.out.println("this is AAA look()");
    }
    @Override
    public String see(String str) {
        System.out.println("this is AAA see()");
        System.out.println("str is " + str);
        return str;
    }
}
public class BBB implements Father{
    @Override
    public void look() {
        System.out.println("this is BBB look()");
    }
    @Override
    public String see(String str) {
        System.out.println("this is BBB see()");
        System.out.println("str is " + str);
        return str;
    }
}
public class Test01 {
    public static void main(String[] args) {
        Father a = new AAA();
        a.look();
        String seeA = a.see("Hello");
        System.out.println(seeA);
        System.out.println("==================");
        Father b = new BBB();
        b.look();
        String seeB = b.see("Hello");
        System.out.println(seeB);
    }
}

打印结果:

this is AAA look()
this is AAA see()
str is Hello
Hello
==================
this is BBB look()
this is BBB see()
str is Hello

当我们需要在程序运行时,有选择性的调用AAA类或BBB类

即:运行时编译,我们增加一个工厂类

public class Factory {
    public static Father getInstance(String className) {
        if("AAA".equals(className)) {
            return new AAA();
        }else if("BBB".equals(className)) {
            return new BBB();
        }else {
            return null;
        }
    }
}

在main方法中调用:

public static void main(String[] args) {
        //可从外部获取(properties文件,数据库,或其他方法返回值)
        String className = "BBB";
        
        Father a = Factory.getInstance(className);
        a.look();
        String seeA = a.see("Hello");
        System.out.println(seeA);
    }

这样,我们就可以在运行时选择性的创建对象了

这和反射有什么关系呢?

假设,需求变更,要增加一个CCC类,同样实现了Father接口,并也动态调用

需要修改的代码:

//增加一个CCC类,实现Father接口
public class CCC implements Father{
    @Override
    public void look() {
        System.out.println("this is CCC look()");
    }
    @Override
    public String see(String str) {
        System.out.println("this is CCC see()");
        System.out.println("str is " + str);
        return str;
    }
}
//修改Factory工厂类,增加CCC类的创建方法
public class Factory {
    public static Father getInstance(String className) {
        if("AAA".equals(className)) {
            return new AAA();
        }else if("BBB".equals(className)) {
            return new BBB();
        }else if("CCC".equals(className)){
            return new CCC();
        }else {
            return null;
        }
    }
}

注意:在实际项目中,修改一个已经运行成功没问题的方法,是存在风险的,改完万一出错了呢...(程序员都懂...)

所以,在这里如果使用反射机制,就更加灵活,以下代码举例说明

public class Factory {
    public static Father getInstance(String className) {
        Father f = null;
        
//        if("AAA".equals(className)) {
//            return new AAA();
//        }else if("BBB".equals(className)) {
//            return new BBB();
//        }else if("CCC".equals(className)){
//            return new CCC();
//        }else {
//            return null;
//        }
        
        try {
            f = (Father)Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return f;
    }
}
public class Test01 {
    public static void main(String[] args) {
        //可从外部获取(properties文件,数据库,或其他方法返回值)
        String className = "com.dream.springboot.test.clazzTest.BBB";
        
        Father f = Factory.getInstance(className);
        f.look();
        String see = f.see("Hello");
        System.out.println(see);
    }
}

在Factory工厂类中,使用全类名获取实例,这样避免了增加类时就修改Factory类中的方法,可减少错误,也减少工作量

总结:反射就是当詹姆斯·高斯林给你关了一扇门,又给你打开的一扇窗

 代码上传地址:https://download.csdn.net/download/lijian0420/10803158

posted @ 2018-11-23 14:33  删库到跑路  阅读(540)  评论(0编辑  收藏  举报