Java 反射机制的深入应用

反射除了可以取得一个类的完整结构外,还可以调用类中的指定方法或指定属性,并且可以通过反射完成对数组的操作

一、通过反射调用类中的方法

如果要使用反射调用类中的方法可以通过 Method 类完成:
(1)通过 Class 类的 getMethod(String name,Class...parameterTypes) 方法取得一个 Method 的对象,并设置此方法操作时所需要的参数类型
(2)之后可以使用 invoke 进行调用,并向方法中传递要设置的参数

  1. 调用 Person 类中的 sayChina() 方法:
import java.lang.reflect.Method;

public class Test{
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("Person");
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        try{
            Method met = c1.getMethod("sayChina");//此方法没有参数
            met.invoke(c1.newInstance());//调用方法,必须传递对象实例
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

程序中通过 Class 类的 getMethod() 方法根据一个类中的方法取得 Method 对象,并通过 invoke 调用指定的方法。但是在使用 invoke() 方法时必须传入一个类的实例化对象,因为在 sayChina() 方法上没有任何的参数,所以此处没有设置参数类型和参数内容,程序操作如下:
在这里插入图片描述

  1. 调用Person 类中的 sayHello() 方法
import java.lang.reflect.Method;

public class Test{
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("Person");
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        try{
            Method met = c1.getMethod("sayHello",
                    String.class,int.class);//此方法需要两个参数
            String rv = null;//接收方法的返回值
            rv = (String)met.invoke(c1.newInstance(),"Java",20);
            System.out.println(rv);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

程序中在使用 getMethod() 方法调用时除了要指定调用的方法名称,也同样指定了参数的类型,因为 sayHello() 方法调用完后存在返回值,所以用来一个字符串接收返回的内容。

二、调用 setter 及 getter 方法

面向对象部分已经知道,类中的属性必须封装,封装之后的属性要通过 setter及getter 方法设置和取得,同样在使用反射的调用方法操作中,最重要的也是调用类中的 setter 及 getter 方法。

import java.lang.reflect.Method;

public class Test{
    public static void main(String[] args) {
        Class<?> c1 = null;//声明 Class 对象
        Object obj = null;//声明一个对象
        try {
            c1 = Class.forName("Person");
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        try{
            obj = c1.newInstance();//实例化操作对象
        }catch (InstantiationException e){
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
        setter(obj,"name","Java",String.class);
        setter(obj,"age",30,int.class);
        System.out.print("姓名:");
        getter(obj,"name");
        System.out.print("年龄:");
        getter(obj,"age");
    }
    /*
    @param obj 操作的对象
    @param att 操作的属性
    @param obj value 设置的值
    @param obj type 参数的类型
     */
    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){//调用 getter 方法
        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;
    }
}

三、通过反射操作属性

反射操作中虽然可以使用 Method 调用类中的setter 及 getter 方法设置和取得属性,但是这样操作很麻烦,所以在反射机制中也可以直接通过 Field 类操作类中的属性,通过 Field 类提供的 set() 和get() 方法就可以完成设置和取得属性内容的操作。
但是操作之前,类中的属性已经都设置成私有的访问权限,所以在使用 set() 或 get() 方法时首先要使用 Field 类中的 setAccessible(true) 方法将需要操作的属性设置成可以被外部访问

import java.lang.reflect.Field;

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

使用反射操作属性时最好通过 setter 及 getter 方法
在这里插入图片描述

四、通过反射操作数组

反射机制不仅只能用在类上,还可以用在任意的引用数据类型的数据上,当然这本身就包含了数组,既可以使用反射操作数组。可以通过 Class 类的以下方法取得一个数组的 Class 对象:

public Class<?> getComponentType()

在反射操作包 java.lang.reflect 中使用 Array 类表示一个数组,可以通过此类取得数组长度,取得数组内容的操作
在这里插入图片描述

  1. 取得数组信息并修改数组内容
import java.lang.reflect.Array;

public class Test{
    public static void main(String[] args)throws Exception {
        int temp[] = {1,2,3};
        Class<?> c = temp.getClass().getComponentType();//取得数组的 Class 对象
        System.out.println("类型:" + c.getName());//得到数组类型名称
        System.out.println("长度:" + Array.getLength(temp));//得到数组长度
        System.out.println("第一个内容:" + Array.get(temp,0));//得到第一个内容
        Array.set(temp,0,6);//修改第一个内容
        System.out.println("第一个内容:" + Array.get(temp,0));//得到第一个内容
    }
}
  1. 修改数组的大小
import java.lang.reflect.Array;

public class Test{
    public static void main(String[] args)throws Exception {
        int temp[] = {1,2,3};
        int newTemp[] = (int[])arrayInc(temp,5);//扩大数组长度
        print(newTemp);
        System.out.println("\n-------------------------");
        String t[] = {"J1","J2","J3"};//声明一个字符串数组
        String nt[] = (String[])arrayInc(t,8);//扩大数组长度
        print(nt);
    }
    public static Object arrayInc(Object obj,int len){//修改数组大小
        Class<?> c = obj.getClass();//通过数组得到 Class 对象
        Class<?> arr = c.getComponentType();//得到数组的 Class 对象
        Object newO = Array.newInstance(arr,len);//重新开辟新的数组大小
        int co = Array.getLength(obj);//取得数组长度
        System.arraycopy(obj,0,newO,0,co);//复制数组内容
        return newO;
    }
    public static void print(Object obj){
        Class<?> c = obj.getClass();//通过数组得到 Class 对象
        if (!c.isArray()){//判断是否是数组,不是则返回
            return;
        }
        Class<?> arr = c.getComponentType();//取得数组的 Class
        System.out.println(arr.getName() + 
                "数组的长度是:" + Array.getLength(obj));
        for (int i=0;i<Array.getLength(obj);i++){
            System.out.print(Array.get(obj,i) + "、");
        }
    }
}

五、程序中用到的Person 类

interface China{
    public static final String NATIONAL = "China";
    public static final String AUTHOR = "Java";
    public void sayChina();//定义无参的抽象方法
    public String sayHello(String name,int age);//定义有参的抽象方法
}
class Person implements China{
    private String name;
    private int age;
    public Person(){
    }
    public Person(String name){//声明有一个参数的构造方法
        this.name = name;
    }
    public Person(String name,int age){
        this(name);
        this.setAge(age);
    }

    public String getName() {
        return name;
    }

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

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

    public int getAge() {
        return age;
    }

    @Override
    public void sayChina() {
        System.out.println("作者:" + AUTHOR + ",国籍:" + NATIONAL);
    }

    @Override
    public String sayHello(String name, int age) {
        return name + "你好!我今年" + age + "岁了!";
    }
}
发布了613 篇原创文章 · 获赞 276 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/nanhuaibeian/article/details/104433629