Java的三魂七魄 —— 反射

1,155 阅读4分钟

目录:

反射

学Java的时候,老师都会告诉我们一句很经典的话:万事万物皆对象。

在Java的世界里每一个对象都是一个类的实例。同样的,类也是某一个类的对象,换句话说,是某一个类的实例。所有的类,都是Class类的实例。

明确上一段话的内容,就可以开始试着理解反射的概念了。

概念:

JAVA反射(Reflection): 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

在没有利用反射机制的程序中,对于类的属性和方法我们需要通过创建对象调用;有了反射,我们就可以利用Field、Method、Constructor等类来调用类中的属性、方法、构造器。(小白浅见,期待指正)

明确了反射的概念,就可一开始着手学习反射的一些操作了。

对象的静态与动态创建:

反射和之前学习的new对象有什么差别呢?让我们先看一个实例: 先创建一个Person类:

/**
 * @author 龙子贞
 * @create 2020/3/9 12:20
 */
public class Person {

    private  String name;
    public int age;
    public int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
    }

    public Person() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void show(){
        System.out.println("你好,我是一个人。");
    }

    public String showName(String name){
        System.out.println("我的名字是:" + name);
        return name;
    }

    private static void showDesc(){
        System.out.println("我是一个可爱的人");
    }
}

然后分别动态和静态的创建对象:

/**
 * @author 龙子贞
 * @create 2020/3/9 12:28
 */
public class ReflectionTest {

    /**
	 * 静态创建
     */
    public void test1(){

        //创建Person类的对象
        Person p1 = new Person("Tom",12);
    }

    /**
	 * 动态创建
     */
    public void test2() throws Exception{
        Class clazz = Person.class;
        //通过反,创建Person类的对象
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person) obj;
    }

值得注意的是,在静态调用时,我们只需要new类的构造器就可以了。当我们想静态调用Person类时,需要先获取Person类的Class类对象(“类”的类),然后用getConstructor()方法获取Person类的构造器,最后用获取到的构造器对象调用newInstance()方法完成了对象的创建。他的基本原理就是通过反射获取构造器来创建对象

获取Class实例

如何获取运行时类的实例?==方法有四==。

	/**
     * 获取Class的实例对象
     */
    public void test3() throws ClassNotFoundException {
	
        //方法一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
		
        //方法二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(p1);
		
        //方法三:调用Class的静态方法:forName(String classPath)
        Class clazz3 = Class.forName("com.langsin.java.Person");
        System.out.println(clazz3);
		
        //方法四:使用类加载器:ClassLoader
        ClassLoader classLoader = ReentrantLock.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.langsin.java.Person");
        System.out.println(clazz4);

        //结果为true,证明都是同一个对象
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);
        System.out.println(clazz1 == clazz4);
    }

通过上述四种方法,我们就可以获取到运行时类的Class的对象。对于这四种方法,最能体现“动态”的方法是方法三。因为在运行前并不确定是有这个类,只是根据指定的类路径寻找类,所以最为动态。

属性、方法、构造器!

通过Class的实例,我们可以利用Field、Method、Constructor来调用类中的属性、方法和构造器。

/**
 * @author 龙子贞
 * @create 2020/3/9 12:28
 */
public class ReflectionTest {

    /**
     * 调用属性、方法、构造器
     */
    public void test2() throws Exception{
        Class clazz = Person.class;
        //调用构造器,创建Person实例
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person) obj;
        System.out.println(obj.toString());

        //通过反射,调用兑现指定的属性、方法
        //调用属性
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(obj.toString());

        System.out.println("***************");

        //调用方法
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);

        //通过反射,可以调用Person类的私有构造的。比如:私有的构造器、方法、属性
        //调用私有的构造器
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person) cons1.newInstance("jerry");
        System.out.println(p1);

        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"HanMeimei");
        System.out.println(p1);

        //调用私有的方法
        Method showNation = clazz.getDeclaredMethod("showName",String.class);
        showNation.setAccessible(true);
        showNation.invoke(p1,"中国");
    }

反射配置文件

反射机制不仅仅能利用在获取运行时类,还能获取Java的配置文件。

配置文件:

user=老吴
password=123

反射获取配置文件:

/**
     * Properties:用来读取配置文件
     */
    public void propertiesTest() throws IOException {

        Properties pros = new Properties();

        //读取配置文件方式一:IO+Properties
//        FileInputStream fis = new FileInputStream("ReflectTest/jdbc.properties");
//        pros.load(fis);

        //读取文件方式二:使用ClassLoader(配置文件默认识别为 当前module的src下)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user=" + user + ",password=" + password);

    }