Java基础入门--第九章--反射机制

9.1 反射概述

在日常生活中,反射是一种物理现象。例如,通过照镜子可以反射出自己的容貌,水面可以反射出景物,等等,这些都是反射。通过反射,可以将一个虚像映射到实物,这样就可以获取实物的某些形态特征。
Java程序中也有反射,Java程序中的反射也是同样的道理,常规情况下程序通过类创建对象,反射就是将这一过程反转,通过实例化对象来获取所属类的信息。

Java的反射机制可以动态获取程序信息以及动态调用对象的功能,它主要有以下4个作用:
(1)在程序运行状态中,构造任意一个类的对象。
(2)在程序运行状态中,获取任意一个对象所属的类的信息。
(3)在程序运行状态中,调用任意一个类的成员变量和方法。
(4)在程序运行状态中,获取任意一个对象的属性和方法。

反射机制的优点是可以实现动态创建对象和动态编译,特别是在JavaEE的开发中,反射的灵活性表现得十分明显。例如,一个大型软件,不可能一次就把程序设计得很完美,当这个程序编译、发布上线后,需要更新某些功能时,如果采用静态编译,就需要把整个程序重新编译一次,同时也需要用户把以前的软件卸载,再重新安装新的版本。而采用反射机制,程序可以在运行时动态地创建和编译对象,不需要用户重新安装软件,即可实现功能的更新。

9.2 认识Class类

Class类的常用方法

方法声明功能描述
forName(String className)获取与给定字符串名称的类或接口相关联的Class对象
getConstructors()获取类中所有public修饰的构造方法对象
getDeclaredFields()获取所有成员变量对应的字段类对象,包括public、protected、default和private修饰的字段,但不包括从父类继承的字段
getFields()获取所有public修饰的成员变量对应的字段类对象,包括从父类继承的字段
getMethods()获取所有public修饰的成员方法对应的方法类对象,包括从父类继承的方法
getMethod(String name,Class…parameter Type)根据方法名和参数类型获得对应的方法类对象,并且只能获得public修饰的方法类对象
getInterfaces()获取当前类实现的全部接口
getClass()获取调用该方法的Class对象
getName()获取类的完整名称,名称中包含包的名称
getPackage()获取类所属的包名称
getSuperclass()获取类的父类
newInstance()创建Class对象关联类的对象
getComponentType()获取数组的对应Class对象
isArray()判断此Class对象是否是一个数组

因为Class类本身并没有定义任何构造方法,所以Class类不能直接使用构造方法进行对象的实例化,使用Class类进行对象的实例化可以使用以下3种方式:
(1)根据全限定类名获取:Class.forName(“全限定类名”)。
(2)根据对象获取:对象名.getClass()。
(3)根据类名获取:类名.class。
下面通过一个案例演示Class类的3种实例化方式

class A{
}

class Example01 {
    public static void main(String[] args){
        Class<?> c1 = null;			//声明Class对象c1
        Class<?> c2 = null;			//声明Class对象c2
        Class<?> c3 = null;			//声明Class对象c3
        try{
            c1 = Class.forName("com.test.A");//通过第(1)种方式实例化c1对象
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        c2 = new A().getClass();			//通过第(2)种方式实例化c2对象
        c3 = A.class;						//通过第(3)种方式实例化c3对象
        System.out.println("类名称:"+c1.getName());
        System.out.println("类名称:"+c2.getName());
        System.out.println("类名称:"+c3.getName());
    }
}

在这里插入图片描述

9.3 Class类的使用

9.3.1 通过无参构造方法实例化对象

如果想通过Class类实例化其他类的对象,则可以调用newInstance()方法,在调用newInstance()方法实例化其他类的对象时,必须保证被实例化的类中存在无参构造方法。下面通过一个案例演示Class类通过无参构造方法实例化对象

class Person {
    private String name;
    private int age;

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

class Example02 {
    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.test.Person"); //调用forName()方法实例化c
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Person person = null;    //声明Person类对象person
        try {
            person = (Person) c.newInstance();   //
        } catch (Exception e) {
            e.printStackTrace();
        }
        person.setName("张三");
        person.setAge(20);
        System.out.println(person);
    }

}

然而,newInstance()方法已经被弃用

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

class Person {
    private String name;
    private int age;

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

class Example02 {
    public static void main(String[] args) {
        try {
            // 获取Person类的Class对象
            Class<?> c = Class.forName("com.test.Person");

            // 获取无参构造函数
            Constructor<?> constructor = c.getDeclaredConstructor();

            // 确保构造函数可访问
            constructor.setAccessible(true);

            // 使用构造函数创建Person类的实例
            Person person = (Person) constructor.newInstance();

            // 设置属性并打印对象
            person.setName("张三");
            person.setAge(20);
            System.out.println(person);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

9.3.2 通过有参构造方法实例化对象

如果类中没有无参构造方法,则可以通过有参构造方法实例化对象。通过有参构造方法实例化对象时,需要明确调用的构造方法,并传递相应的参数。通过有参构造方法实例化对象的操作步骤如下:
(1)通过调用Class类中的getConstructors()方法获取要实例化的类中的全部构造方法。
(2)获取实例化使用的有参构造方法对应的Constructor对象。
(3)通过Constructor类实例化对象。

方法声明功能描述
getModifiers()获取构造方法的修饰符
getName()获取构造方法的名称
getParameterTypes()获取构造方法中参数的类型
toString()返回此构造方法的信息
newInstance(Object…initargs)通过该构造方法的指定参数列表创建一个该类的对象,如果未设置参数则表示采用默认无参的构造方法。Object…initargs代表Object类型的可变参数
import java.lang.reflect.Constructor;

class Person {
    private String name;
    private int age;

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

    // getters and setters...

    @Override
    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

class Example01 {
    public static void main(String[] args) {
        Class<?> c = null;    // 声明Class对象c
        try {
            c = Class.forName("com.test.Person");     // 实例化对象c
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return; // 如果找不到类,则退出程序
        }

        Constructor<?> cons[] = null;      // 声明Constructor类对象数组cons
        try {
            cons = c.getDeclaredConstructors(); // 使用getDeclaredConstructors来获取所有构造方法,包括非公共的
            // 如果需要公共构造方法,使用c.getConstructors();
        } catch (Exception e) {
            e.printStackTrace();
            return; // 如果找不到构造方法,则退出程序
        }

        Person person1 = null;
        Person person2 = null;

        try {
            // 假设Person类有一个接受String和int参数的构造方法
            Constructor<?> constructor = cons[0]; // 获取第一个构造方法,这里假设它是正确的构造方法
            constructor.setAccessible(true); // 如果构造方法不是public的,设置为可访问
            person1 = (Person) constructor.newInstance("张三", 30); // 实例化第一个Person对象

            // 通常情况下,你不会同时有两个接受相同参数的构造方法,因此这里仅创建一个人
            // 如果Person类确实有多个构造方法,并且你想使用另一个,你需要确保选择正确的构造方法

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

        // 如果需要创建第二个对象,确保使用不同的变量,并且确保构造方法可用

        System.out.println(person1); // 打印第一个人的信息
        // System.out.println(person2); // 如果创建了第二个人,可以打印第二个人的信息
    }
}

在这里插入图片描述
对象数组cons中存储了Person类的全部构造方法,因为在Person类中只有一个构造方法,所以对象数组cons中其实只有一个构造方法,cons[0]表示Person类的第一个构造方法。\

9.4 通过反射获取类结构

在实际开发中,通过反射可以得到一个类的完整结构,包括类的构造方法、类的属性和类的方法。通过反射获取类结构需要使用java.lang.reflect包中的以下3个类:
(1)Constructor:用于获取类中的构造方法。
(2)Field:用于获取类中的属性
(3)Method:用于获取类中的方法Constructor类、Field类和Method类都是AccessibleObject类的子类,AccessibleObject类的继承关系如图所示
在这里插入图片描述

9.4.1 获取类实现的全部接口

要获取一个类实现的全部接口,可以调用Class类中的getInterfaces()方法getInterfaces()方法声明格式如下:

public Class[] getInterfaces();

getInterfaces()方法返回一个Class类的对象数组,其中存储的是类实现的接口。使用对象数组中的元素(接口)调用Class类中的getName()方法可以获取接口的名称。下面通过一个案例讲解如何通过getInterfaces()方法获取一个类所实现的全部接口

interface China{
    public static final String NATION = "China";
    public static final String AUTHOR = "张三";
}
class Person implements China{
    private String name;
    private int age;
    public Person(String name, int age){
        this.setName(name);
        this.setAge(age);
    }

    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;
    }

    @Override
    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

public class Example01 {
    public static void main(String[] args) {
        Class<?> c=null;
        try{
            c=Class.forName("com.test.Person");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        Class<?> cons[]=c.getInterfaces();
        for(int i=0; i<cons.length; i++){
            System.out.println("实现的接口名称:" + cons[i].getName());
        }
    }
}

第1~4行代码定义了China接口,并在China接口中定义了两个常量—NATION和AUTHOR。第5~27行代码定义了Person类并实现了China接口。因为一个类可能实现了多个接口,所以第36~39行代码以Class数组的形式将全部接口对象返回,并利用循环的方式将数组的内容依次输出。
在这里插入图片描述

9.4.2 获取父类

如果要获取一个类的父类,可以调用Class类中的getSuperclass()方法。getSuperclass()方法声明格式如下:

public Class<? Super T> getSuperclass();

getSuperclass()方法返回一个Class类的实例,通过该实例调用Class类中的getName()方法可以获取类的名称


class Person {
    private String name;
    private int age;

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

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

public class Example01 {
    public static void main(String[] args) {
        Class<?> c1 = null;         //声明Class类的对象
        try {
            c1 = Class.forName("com.test.Person");      //实例化Class类的对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Class<?> c2 = c1.getSuperclass();      //获取Person类的父类
        System.out.println("父类名称:" + c2.getName());
    }
}


第1~23行代码定义了Person类,在其中定义了属性和方法。第26行代码声明了Class对象c1。第28行代码将Person类的全限定类名作为参数传人forName()方法中,通过调用forName()方法进行Class对象的实例化,并将实例化后的值返回给Class对象c1。第32行代码通过cl对象调用getSuperclass()方法获取Person类的父类,并赋值给Class类的对象c2。第33行代码通过c2对象调用getName()方法获取Psrson类的父类名称并输出。
在这里插入图片描述
由图可知,Person类在编写时没有显式地继承一个父类,所以会默认继承Object类。

9.4.3 获取全部构造方法

类的构造方法的获取在9.3.2节已经讲解,获取类的构造方法需要调用Class类的getConstructors()方法。调用getConstructors()方法获取的构造方法需要存储到Constructor类的数组中。通过调用Constructor类的方法可以获取构造方法的详细信息,如构造方法的权限、名称、参数信息等。下面通过一个案例讲解如何使用Constructor类的常用方法获取一个类中的全部构造方法。

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

class Person {
    private String name;
    private int age;

    public Person() {
    }                        //无参构造方法

    public Person(String name) {            //有一个参数的构造方法
        this.name = name;
    }

    public Person(String name, int age) {    //有两个参数的构造方法
        this.setName(name);
        this.setAge(age);
    }

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

public class Example01 {
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("com.test.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //获取全部构造方法,存储到Constructors类数组con中
        Constructor<?> con[] = c1.getConstructors();
        for (int i = 0; i < con.length; i++) {   //循环打印构造方法信息
            //获取构造方法详细信息并输出
            Class<?> p[] = con[i].getParameterTypes();
            System.out.print("构造方法:");
            System.out.print(con[i].getModifiers()+" ");  //获取构造方法权限
            int mo = con[i].getModifiers();
            System.out.print(Modifier.toString(mo) + " ");
            System.out.print(con[i].getName() + " ");   //获取构造方法名称
            System.out.print(" (");
            for (int j = 0; j < p.length; j++) {
                //打印构造方法参数信息
                System.out.print(p[j].getName() + " arg" + i);
                if (j < p.length - 1){
                    System.out.print(",");
                }
            }
            System.out.println("){}");
        }
    }
}



第2~28行代码定义了Person类,在类中定义了3个构造方法和其他成员。第31行代码声明了一个Class对象c1。第33行代码将Person类的全限定类名作为参数传人forName()方法中,通过调用forName()方法进行Class对象的实例化,并将实例化后的值返回给Class对象c1。第38行代码调用Class类的getConstructors()方法获取Person类的全部构造方法,并将获取的构造方法存储到Constructor类的数组con中。第39~54行代码遍历con数组,并调用Constructor类的方法获取构造方法的详细信息。
在这里插入图片描述

9.4.4 获取全部方法

要获取类中的所有public修饰的成员方法对象,可以使用Class类中的getMethods()方法,该方法返回一个Method类的对象数组。如果想要进一步获取方法的具体信息,如方法的参数、抛出的异常声明等,可以调用Method类提供的一系列方法。Method类的常用方法如表所示。

方法声明功能描述
getModifiers()获取方法的权限修饰符
getName()获取方法的名称
getParameterTypes()获取方法的全部参数的类型
getReturnType()获取方法的返回值类型
getExceptionType()获取方法抛出的全部异常类型
newInstance(Object…initargs)通过反射调用类中的方法
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

class Person {
    private String name;
    private int age;

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

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

public class Example02 {
    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.test.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //获取全部方法,存储到Method类数组对象 m 中
        Method m[] = c.getMethods();
        for (int i = 0; i < m.length; i++) {        //遍历数组m循环输出方法信息
            Class<?> r = m[i].getReturnType();    //获取方法的返回值类型
            Class<?> p[] = m[i].getParameterTypes();   //获取全部的参数类型
            int xx = m[i].getModifiers();  //获取方法的权限修饰符
            System.out.print(Modifier.toString(xx) + " ");  //还原修饰符
            System.out.print(r.getName() + " ");      //获取方法名称
            System.out.print(m[i].getName());
            System.out.print("(");
            for(int x=0; x< p.length;x++){
                System.out.print(p[x].getName() + " "+"args"+" ");
                if(x<p.length-1){
                    System.out.print(",");
                }
            }
            //获取方法抛出的全部异常
            Class<?> ex[] =m[i].getExceptionTypes();
            if(ex.length>0){
                System.out.print(") throws ");
            }else{
                System.out.print(")");
            }
            for(int j=0;i<ex.length;j++){
                System.out.print(ex[j].getName());    //输出异常信息
                if(j<ex.length-1){
                    System.out.print(",");
                }
            }
            System.out.println();
        }
    }
}

第3~25行代码定义了Person类,并在Person类中声明了name和age属性以及其getter和setter方法。第28~33行代码声明了Class对象c并在try…catch语句中对其进行了实例化。第35行代码通过Class对象c调用getMethods()方法获取了Person类的所有方法,并将获取的所有方法存储到Method类的数组m当中。第36~64行代码输出Person类中的所有方法信息,包括方法的返回值类型、权限参数类型、修饰符、方法名称、参数、抛出的全部异常等。
在这里插入图片描述

9.4.5 获取全部属性

通过反射也可以获取一个类中的全部属性,类中的属性包括两种:从父类继承的属性和本类定义的属性。针对这两种属性,Java提供了两种获取方式,分别如下:
(1)获取本类中,以及实现的接口或继承的父类中的公共属性,需要调用getFields()方法。getFields()方法声明格式如下:

public Field[] getFields() throws SecurityException;

(2)获取本类中的全部属性,需要调用getDeclaredFields()方法。getDeclareFields()方法声明格式如下:

public Field[] getDeclaredFields throws SecurityException;

上述两个方法返回的都是Field数组,Field数组中的每一个Field对象表示类中的一个属性。如果要获取属性的详细信息,就需要调用Field类提供的一系列方法。Field类的常用方法如表所示。

方法声明功能描述
getModifiers()获取属性的权限修饰符
getName()获取属性的名称
isAccessible()判断属性是否可被外部访问
setAccessible(Boolean flag)设置属性是否可被外部访问
toString()返回Field类的信息
get(Object obj)获取指定对象中属性的具体内容
set(Object obj,Object value)设置指定对象中属性的具体内容
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

class Person {
    private String name;
    private int age;

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

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

public class Example02 {
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("com.test.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        {
            //获取本类属性,存储到Field类数组f当中
            Field f[] = c1.getDeclaredFields();
            for (int i = 0; i < f.length; i++) {            //循环输出属性信息
                Class<?> r = f[i].getType();           //获取属性的类型
                int mo = f[i].getModifiers();          //获取属性权限修饰符
                String priv = Modifier.toString(mo);  //转换属性的权限修饰符
                System.out.print("本类属性:");
                System.out.print(priv + " ");             //输出属性权限修饰符
                System.out.print(r.getName() + " ");     //输出属性类型
                System.out.print(f[i].getName());      //输出属性名称
                System.out.println(";");
            }
        }
    }
}

第3~25行代码定义了Person类,并在Person类中定义了name和age属性以及相应的getter和setter方法。第30行代码实例化了Class对象c1。第36行代码通过调用Class类的getDeclaredFields()方法获取Person类的所有属性,并存人Field数组f中。第37~46行代码通过for循环输出Field数组中Person类的属性信息。
在这里插入图片描述

9.4.6 通过反射调用类中的方法

通过反射调用类中的方法时,需要使用Method类完成,具体操作步骤如下:
(1)通过调用Class类的getMethod()方法获取一个Method类的对象。调用getMethod()方法时需要传人方法名称作为参数。
(2)通过获取的Method对象调用invoke()方法,执行目标方法。调用invoke()方法时,需要传递Class对象的实例作为参数。

import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

    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 sayHello() {
        System.out.println("Hello World!");
    }

    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

public class Example02 {
    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.test.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            Method met = c.getMethod("sayHello");
            met.invoke(c.newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

第2~31行代码定义了Person类,并声明了Person类的属性以及方法。第34行代码声明了Class类的对象c。第36行代码将Person类的全限定类名作为参数传人forName()方法中,通过forName()方法进行Class类的对象的实例化,并将实例化后的值返回给Class类的对象c。第41行代码通过Class类的getMethod()方法根据类中的方法名称sayHello获取Method对象met,对象met中保存的是sayHello()方法的信息。第42行代码通过对象met调用invoke()方法执行sayHello()方法。需要注意的是,在调用invoke()方法时,必须传人一个类的实例化对象作为参数。
在这里插入图片描述

9.5 反射的应用

9.5.1 通过反射调用类中的getter/setter方法

在第3章读者就了解到类中的属性必须封装,封装之后的属性要通过setter方法设置属性值,通过getter方法获取属性值。在使用反射调用类中方法的操作中,最重要的是调用类中的getter/setter方法。getter/setter方法的调用过程与9.4.6节中类方法的调用过程类似,但因为getter/setter方法需要访问属性,所以稍显复杂。下面通过一个案例讲解如何使用反射调用类中的getter/setter方法。

import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

    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 String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}
public class Example02 {
    public static void main(String[] args){
        Class<?> c = null;
        Object obj = null;
        try{
            c = Class.forName("com.test.Person");	//对象c为Class类型
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        try{
            obj = c.newInstance();					//实例化Class对象
        }catch(InstantiationException e){
            e.printStackTrace();
        }catch(IllegalAccessException e){
            e.printStackTrace();
        }
        setter(obj,"name","张三",String.class);
        setter(obj,"age",18,int.class);
        System.out.print("姓名:");
        getter(obj,"name");
        System.out.print("年龄:");
        getter(obj,"age");
    }
    public static void setter(Object obj,String att,Object value,Class<?>
            type){
        try {
            Method met= obj.getClass().getMethod("set"+initStr(att),type);
            met.invoke(obj,value);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void getter(Object obj,String att){
        try {
            Method met= obj.getClass().getMethod("get"+initStr(att));
            System.out.println(met.invoke(obj));
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static String initStr(String old){
        String str= old.substring(0,1).toUpperCase()+old.substring(1);
        return str;
    }
}

(1)设置方法名称。第45、46行代码在设置方法名称时直接使用了属性名称,例如name和age。但实际需要的方法名称是setName()、getName()、setAge()、getAge(),所有属性名称的首字母必须大写。为了解决这个问题,程序中单独定义了initStr()方法,通过此方法将字符串中的首字母转换成大写,随后将字符串连接到set字符串及get字符串之后,找到对应的方法。
(2)调用setter()方法时,传人了实例化对象、要操作的属性名称、要设置的参数内容以及具体的参数类型,这样做是为了满足getMethod()方法和invoke()方法的调用要求。
(3)在调用getter()方法时,同样传人了一个实例化对象,因为其getter方法本身不需要接收任何参数,所以只传人了属性名称。对文件中的程序,读者只需要了解基本操作原理即可。在实际的开发中,很多框架会为开发者预告实现以上功能。

在这里插入图片描述

9.5.2 通过反射操作属性

在反射操作中,虽然可以通过调用类中的getter/setter方法访问类的属性,但是这样操作起来太烦琐。除了调用getter/setter方法访问类的属性之外,反射机制也可以直接通过Field类操作类中的属性,通过Field类提供的set()方法和get()方法可以直接设置、获取类的属性值。通过调用Field类的getter/setter方法访问类的属性时,首先要调用Field类中的setAccessible()方法将需要操作的属性权限设置成可以被外部访问。

import java.lang.reflect.Field;

class Person {
    private String name;
    private int age;
    public Person(){}
    public Person(String name){
        this.name = name;
    }
    public Person(String name,int age){
        this.setName(name);
        this.setAge(age);
    }
    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 sayHello(){
        System.out.println("Hello World!");
    }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}

public class Example02 {
    public static void main(String[] args) throws Exception{
        Class<?> c = null;                             //声明一个Class对象
        Object obj = null;                             //声明一个Object对象
        c = Class.forName("com.test.Person");   //实例化Class对象
        obj = c.newInstance();                         //实例化Object对象
        Field nameField = null;                        //表示name属性
        Field ageField = null;                         //表示age属性
        nameField = c.getDeclaredField("name");     //获取name属性
        ageField = c.getDeclaredField("age");        //获取age属性
        nameField.setAccessible(true);       //将name属性设置为可被外部访问
        nameField.set(obj,"张三");             //设置name属性值
        ageField.setAccessible(true);         //将age属性设置为可被外部访问
        ageField.set(obj,30);                   //设置age属性值
        System.out.println("姓名:"+nameField.get(obj));
        System.out.println("年龄:"+ageField.get(obj));
    }
}

第2~31行代码定义了Person类,并声明了Person类的属性以及方法。第34、35行代码声明了Class类的对象c和Object类的对象obj。第36行代码将Person类的全限定类名作为参数传人forName()方法中,调用forName()方法完成Class类的对象的实例化,并将实例化后的值返回给Class类的对象c。第37行代码实例化Object类的对象obj。第40、41行代码通过调用Class类的getDeclaredField()方法获取Person类的name属性和age属性。第42~45行代码将name属性和age属性设置为可被外部访问,并设置name属性和age属性的值。需要注意的是,以上程序在扩大类属性的访问权限后直接进行了属性的操作,所以在Person类中并不需要编写getter方法和setter方法,但是在实际开发中,这种直接操作属性的方式是不安全的,读者在以后的开发中,不要直接操作属性,而要通过getter方法和setter方法操作属性。文件只是演示通过反射可以直接访问类中的属性。
在这里插入图片描述

本章内容如下:反射机制原理;
Class类;Class类的使用,包括通过无参构造方法实例化对象和通过有参构造方法实例化对象;
通过反射获取类结构,包括获取接口、获取父类、获取构造方法、获取全部方法、获取全部属性和通过反射调用类中的方法;
反射的应用,包括通过反射调用类中的getter和setter方法和通过反射操作类中的属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值