JavaSE高级开发之反射(一)

反射指的是对象的反向处理操作。 我们熟知的 “正” 的操作必须要先导入一个包,然后创建于一个类,根据构造方法才能产生类的实例化对象。 而反射是根据对象来取得对象的来源信息

  • 我们先来看看正常的关于对象的处理流程:根据包名.类名找到某个类。
package www.tech.java.com;

import java.util.Date;

public class TestReflect {
	public static void main(String[] args) {
		Date date = new Date() ;
	}
}

既然所谓的 “反” 指的是根据对象来取得对象的来源信息,而这个 “反” 的操作核心的处理就在于Object类的一个方法:取得Class对象。

我们首先来了解一下Class类:

每个类运行时的类型信息就是用Class对象来表示的,它包含了与类有关的信息,其实我们的实例化对象就是通过Class对象来创建的,每一个类都有一个Class对象,每当编译一个新类就会产生一个Class对象。基本类型、数组都有Class对象,就连关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。

1. 认识反射机制

1.1 初识反射

取得Class对象:public final native Class<?> getClass();,该方法返回的是一个Class类对象,这个Class描述的就是类。

package www.tech.java.com;

import java.util.Date;

public class TestReflect {
    public static void main(String[] args) {
        //通过类名,调用构造方法创建对象
        Date date = new Date();
        //通过date对象获取到创建date对象的类的对象
        Class classz = date.getClass();
        System.out.println(classz);
    }
}

在这里插入图片描述

java.util.Date -> Class对象,描述Date这个类
java.lang.String -> Class对象,描述String这个类
  • 此时就通过对象取得了对象的来源,这就是 “反” 的本质。在反射的世界里面,看重的不再是一个对象,而是对象身后的组成(类、构造、普通、成员等)。

1.2 三种实例化Class对象方式

Class类是描述整个类的概念,也是整个反射的操作源头,在使用Class类的时候需要关注的仍然是这个类的对象。

  • 这个Class类的对象的产生方式一共有以下三种方式:
    ① 通过对象.getclass()方法获取,比如:obj.getclass()
    ② 通过类名.class方法
    ③ 通过Class.forName(类的全限定名)获取Class对象
package www.tech.java.com;

import java.util.Date;

public class TestReflect {
    public static void main(String[] args) {
        Date date = new Date();
        //第一种方法
        Class class1 = date.getClass();
        System.out.println(class1);
        //第二种方法
        Class class2 = Date.class;
        System.out.println(class1 == class2);
        //第三种方法
        try {
            Class class3 = Class.forName("java.util.Date");
            System.out.println(class1 == class3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • 结果是true也就说明每个类的Class对象只有一个!!

在以上给出的三个方法中我们可以发现,除了第一种方法会产生Date类的实例化对象之外,其他的两种都不会产生Date类的实例化对象。这样看来取得Class类对象有一个最直接的好处:通过Class对象的newInstance方法可以获取实例化对象,但是要求类中必须要有默认的无参构造。

1.3 通过反射实例化对象

package www.tech.java.com;

import java.util.Date;

public class TestReflect {
    public static void main(String[] args) {
        Class classz = Date.class;

        //Class -> new -> object
        //Class对象创建实例化对象
        try {
        	//实例化对象,等价于new java.util.Date();
            Object obj = classz.newInstance();
            //ClassCastException
            if (obj instanceof Date) {
                Date date = (Date) obj;
                System.out.println(date);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • 现在发现除了关键字new之外,对于对象的实例化模式有了第二种做法,那就是通过Class对象反射获取。

1.4 反射VS工厂模式

工厂设计模式:如果是自己编写的接口,要想取得本接口的实例化对象,最好使用工厂类来设计。但是传统工厂类在实际开发之中根本用不到,因为每增加一个接口的子类就需要修改工厂类,违背了 “开闭原则” 。

  • 要想解决关键字new带来的问题,最好的做法就是通过反射来完成处理,因为Class类可以使用newInstance()实例化对象,同时Class.forName()能够接收类名称。
package www.tech.java.com;

interface Fruit {
    void eat();
}

class Apple implements Fruit {
    public void eat() {
        System.out.println("吃苹果");
    }
}

class Orange implements Fruit {
    public void eat() {
        System.out.println("吃橘子");
    }
}

class FruitFactory {
    public static Fruit getFruitInstance(String fruitName) {
        try {
            Class classz = Class.forName(fruitName);
            return (Fruit) classz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class TestFactory {
    public static void main(String[] args) {
        Fruit fruit = FruitFactory.getFruitInstance("www.tech.java.com.Orange");
            if (fruit != null) {
                fruit.eat();
            }
    }
}

在这里插入图片描述

  • 引入反射后,每当新增接口子类,无需去修改工厂类代码就可以很方便的进行接口子类扩容。以上这种工厂类代码我们称之为简单工厂模式。

2. 反射与类操作

2.1 取得父类信息

在java中任何的程序类都一定会有父类,在Class类中就可以通过如下方法来取得父类或者实现的父接口:

  • 取得父类信息:
    ① 取得类的包名称:getPackage()
    ② 取得父类的Class对象:getSuperclass()
    ③ 取得实现的父接口:getInterfaces()
package www.tech.java.com;

interface IMessage {}

interface SystemEdition {}

class Father {}

class Son extends Father implements IMessage, SystemEdition {}

public class TestBaseInfo {
    public static void main(String[] args) {

        Class sonClass = Son.class;

        //获取包
        Package packages = sonClass.getPackage();
        //包的名称
        System.out.println(packages.getName());

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

        //获取父类
        Class superClass = sonClass.getSuperclass();
        System.out.println(superClass.getName());//类全限定名
        System.out.println(superClass.getSimpleName());//类名

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

        //获取接口
        Class[] interfaces = sonClass.getInterfaces();
        for (Class i : interfaces) {
            System.out.println(i.getName());
        }
    }
}

在这里插入图片描述

5.2 反射调用构造方法

① 取得类中的所有构造:getConstructors()
② 取得指定参数类型的构造:getConstructor(Class<?>... parameterTypes)

package www.tech.java.com;

import java.lang.reflect.Constructor;
import java.util.Arrays;

class Person {

    private String name;
    private Integer age;

    public Person() {

    }

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

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class TestBaseInfo {
    public static void main(String[] args) {

        Class personClass = Person.class;

        //取得所有构造方法
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.getName() + "(" +
                    Arrays.toString(c.getParameterTypes())
                    + ")"

            );

        }

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

        try {
            //取得指定的一个参数的构造方法
            Constructor c = personClass.getConstructor(String.class);
            System.out.println(c.getName() + "(" +
                    Arrays.toString(c.getParameterTypes())
                    + ")"

            );

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • 观察Class实例化对象的问题:
  • 类中没有无参构造的情况下:
package www.tech.java.com;

class Person {

    private String name;
    private Integer age;

    /*
    public Person() {

    }

    public Person(String name) {
        this.name = name;
    }
    */

    //没有无参构造
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class TestBaseInfo {
    public static void main(String[] args) {

        Class personClass = Person.class;
        try {
            System.out.println(personClass.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • 类中有无参构造的情况下:
package www.tech.java.com;

class Person {

    private String name;
    private Integer age;
    
    //有无参构造
    public Person() {

    }

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

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class TestBaseInfo {
    public static void main(String[] args) {

        Class personClass = Person.class;
        try {
            System.out.println(personClass.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • 结论:Class类通过反射实例化类对象的时候,只能够调用类中的无参构造。如果现在类中没有无参构造则无法使用Class类调用,只能够通过明确的构造调用实例化处理。
  • 通过Constructor实例化对象:
package www.tech.java.com;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Person {

    private String name;
    private Integer age;

    public Person() {

    }

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

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class TestBaseInfo {
    public static void main(String[] args) {

        Class personClass = Person.class;
        try {
            //调用有参数的构造方法
            Constructor c = personClass.getConstructor(String.class);

            Object object = c.newInstance("张三");
            System.out.println(object);

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

5.3 反射调用普通方法

① 取得全部普通方法:getMethods()
② 取得指定普通方法:getMethod(String name, Class<?>... parameterTypes)

package www.tech.java.com;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

class Person {

    private String name;
    private Integer age;

    public Person() {

    }

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

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class TestBaseInfo {
    public static void main(String[] args) {

        Person person = new Person("Jack", 20);
        Class personClass = Person.class;

        //取得所有普通方法
        Method[] methods = personClass.getMethods();
        System.out.println("Person类中的所有方法");
        for (Method m : methods) {
            System.out.println(m.getName() + "(" +
                    Arrays.toString(m.getParameterTypes())
                    + ")");
        }

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

        //取得指定的setName方法
        Method setNameMethod = null;
        try {
            setNameMethod = personClass.getMethod("setName", String.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        System.out.println("Person类中的setName方法:");
        System.out.println(setNameMethod.getName() + "(" +
                Arrays.toString(setNameMethod.getParameterTypes()));

    }
}

在这里插入图片描述

5.4 反射调用类中属性

在之前已经成功的实现了类的构造调用、普通方法调用,除了这两种模式之外还有类中属性的调用。

前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)。

  • 在Class类中提供有四组取得属性的方法:
    ① 取得父类中公开属性:getFields()
    ② 取得父类中公开指定名称属性:getField(String name)
    ③ 取得本类中全部属性:getDeclaredFields()
    ④ 取得本中指定名称属性:getDeclaredMethod(String name, Class<?>... parameterTypes)
package www.tech.java.com;

import java.lang.reflect.Field;

class Person {

    private String name;
    private Integer age;

    public Person() {
    }

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

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

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

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

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

class Student extends Person {

    private String school;

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

    @Override
    public String toString() {
        return "Student{" +
                "school='" + school + '\'' +
                "} " + super.toString();
    }
}

public class TestBaseInfo {
    public static void main(String[] args) {

        //先要实例化对象
        Student student = new Student();
        student.setSchool("清华");
        student.setName("Jack");
        student.setAge(22);
        Class studentClass = student.getClass();

        //获取父类中的所有属性
        Field[] fields = studentClass.getFields();
        for(Field f : fields) {
            System.out.println(f.getName());
        }

        //获取父类中的age属性
        try {
            Field ageField = studentClass.getField("age");
            System.out.println(ageField.getName());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

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

        //获取本类中的所有属性
        Field[] fields1 = studentClass.getDeclaredFields();
        for(Field f : fields1) {
            System.out.println(f.getName());
        }

        //获取本类中的school属性
        try {
            Field schoolField = studentClass.getDeclaredField("school");
            System.out.println(schoolField.getName());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
  • 属性的核心描述类java.lang.reflect.Field,在这个类之中有两个重要方法:
    ① 设置属性内容:set(Object obj, Object value)
    ② 取得属性内容:get(Object obj)
    ③ 通过反射获取到Constructor/Method/Filed私有访问:setAccessible(true)
package www.tech.java.com;

import java.lang.reflect.Field;

class Emp {
    private String name;
}

public class BeanOperation {
    public static void main(String[] args) {
        Class classz = Emp.class;
        Object object = null;
        try {
        	// 实例化本类对象
            object = classz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        Field nameField = null;
        try {
        	// 获取name属性
            nameField = classz.getDeclaredField("name");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        // 取消封装
        nameField.setAccessible(true);
        
        try {
        	//设置属性值,相当于object.name = "张三";
            nameField.set(object, "张三");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        
        try {
        	// 取得属性
            System.out.println(nameField.get(object));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
在这里插入图片描述

3. 反射与单级VO操作

假设现在有一个简单Java类,按照原始的做法就是使用getter/setter方法对属性值进行操作,那么加入这个类中存在有几十个属性,要是还按照原始做法,就需要调用几十次getter/setter方法,这样操作就太麻烦了。

  • 现在希望能对程序做简化,输入字符串"属性名称:属性值|属性名称:属性值|属性名称:属性值|....",就可以将类中的属性设置好。
package www.tech.java.com;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Emp {

    private String ename;
    private String job;
    private String skill;

    public String getName() {
        return ename;
    }

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

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "ename='" + ename + '\'' +
                ", job='" + job + '\'' +
                ", skill='" + skill + '\'' +
                '}';
    }
}

public class BeanOperation {

    public static void main(String[] args) {

        String line = "name:Jack|job:SoftDev|skill:Java";
        Emp emp = new Emp();

        String[] attributes = line.split("\\|");
        for(String kv : attributes) {
            setXxx(emp, kv);
        }

        System.out.println(emp);
    }

    //object:进行赋值的对象    kv:属性和值
    public static void setXxx(Object object, String kv) {

        //1.获取指定的Class对象
        Class classz = object.getClass();

        //2.解析kv
        String[] segments = kv.split(":");
        String attribute = segments[0];
        String attributeValue = segments[1];

        //3.setXxx方法名称:set+属性名首字母大写
        String methodName = "set" + attribute.substring(0, 1).toUpperCase()
                + ((attribute.length() > 1) ? attribute.substring(1) : "");



        try {

            //4.通过反射获取setter方法
            Method method = classz.getDeclaredMethod(methodName, String.class);

            //5.通过method对象调用方法
            method.invoke(object, attributeValue);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43508801/article/details/89139829