Java 核心基础(二)反射 API 详解

35 篇文章 11 订阅


本文涉及反射的 API 类有:

  • Class
  • Method
  • Field
  • Contructor
  • Type
  • ParameterizedType


Java 反射是一个非常重要的技术,很多框架都是通过反射来完成的。

Java反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

在 JDK 中,主要由以下类来实现 Java 反射机制,这些类都位于 java.lang.reflect 包中:

  • Class类:代表一个类。
  • Field类:代表类的成员变量(成员变量也称为类的属性)。
  • Method类:代表类的方法。
  • Constructor类:代表类的构造方法。
  • Array类:提供了动态创建数组,以及访问数组元素的静态方法。

当真正需要使用一个类时系统才会装入它 java.lang.Class 是 Java 应用程序在运行时装入类和接口的实例 Class 只能由系统建立对象我们可以通过 Object 的 getClass() 方法來取得某个对象对应 Class 对象
 
我们知道每一个类被加载之后,系统就会为该类生成一个对应的 Class 对象,通过该 Class 对象我们就可以访问到 JVM 中的这个类了。

Java 程序获取 Class 对象有三种方式:

  1. 使用 Class 类的 forName() 静态方法,该方法传入字符串参数,该字符串是某个类的全限定类名!
  2. 调用该类的 class 属性来获取该类对应的 Class 对象,例如 Dog.class
  3. 调用某个对象的 getClass 方法,该方法是 Object 类的一个方法,所以 Java 对象都可以调用这个方法.


下面通过代码实例的方式给大家介绍反射的使用。

1 通过反射来获取类的基本信息

下面我们就通过一个简单的程序来详细说 Class 对象来获取对应的基本信息:

  • 构造方法
  • 方法
  • 注解
  • 内部类
  • 外部类
//使用2个注解修饰该类
@SuppressWarnings(value="unchecked")
@Deprecated
public class ClassTest{
    //为该类定义一个私有的构造器
    private ClassTest(){}
    //定义一个有参数的构造器
    public ClassTest(String name) {
        System.out.println("执行有参数的构造器");
    }
    //定义一个无参数的info方法
    public void info() {
        System.out.println("执行无参数的info方法");
    }
    //定义一个有参数的info方法
    public void info(String str) {
        System.out.println("执行有参数的info方法"
            + ",其实str参数值:" + str);
    }
    //定义一个测试用的内部类
    class Inner {
    }
    public static void main(String[] args) 
        throws Exception {
        //下面代码可以获取ClassTest对应的Class
        Class<ClassTest> clazz = ClassTest.class;
        //获取该Class对象所对应类的全部构造器
        Constructor[] ctors = clazz.getDeclaredConstructors();
        System.out.println("ClassTest的全部构造器如下:");
        for (Constructor c : ctors) {
            System.out.println(c);
        }
        //获取该Class对象所对应类的全部public构造器
        Constructor[] publicCtors = clazz.getConstructors();
        System.out.println("ClassTest的全部public构造器如下:");
        for (Constructor c : publicCtors) {
            System.out.println(c);
        }
        //获取该Class对象所对应类的全部public方法
        Method[] mtds = clazz.getMethods();
        System.out.println("ClassTest的全部public方法如下:");
        for (Method md : mtds) {
            System.out.println(md);
        }
        //获取该Class对象所对应类的指定方法
        System.out.println("ClassTest里带一个字符串参数的info方法为:"
            + clazz.getMethod("info" , String.class));
        //获取该Class对象所对应类的上的全部注释
        Annotation[] anns = clazz.getAnnotations();
        System.out.println("ClassTest的全部Annotattion如下:");
        for (Annotation an : anns) {
            System.out.println(an);
        }
        System.out.println("该Class元素上的@SuppressWarnings注释为:"
            + clazz.getAnnotation(SuppressWarnings.class));
        //获取该Class对象所对应类的全部内部类
        Class<?>[] inners = clazz.getDeclaredClasses();
        System.out.println("ClassTest的全部内部类如下:");
        for (Class c : inners) {
            System.out.println(c);
        }
        //使用Class.forName方法加载ClassTest的Inner内部类
        Class inClazz = Class.forName("com.zx.ClassTest$Inner");
        //通过getDeclaringClass()访问该类所在的外部类
        System.out.println("inClazz对应类的外部类为:" + 
            inClazz.getDeclaringClass());
        System.out.println("ClassTest的包为:" + clazz.getPackage());
        System.out.println("ClassTest的父类为:" + clazz.getSuperclass());
    }
}

2 使用反射生成并操作对象

通过反射来生成对象有两种方式:

  • 使用 Class 对象 newInstance()方法来创建,该中方式需要该类有默认的构造器。实际上 newInstance()方法它是同过默认的构造器创建实例的
  • 先使用 Class 对象获取指定的 Constructor 对象,在调用 Contructor 对象的 newInstance() 方法来创建实例的

下面程序就实现一个简单的对象池该对象池通过配置文件读取 name 和 value 对,然后创建这些对象,这有点像 Spring 容器,然后把这些创建的对象放进 HashMap 中。

配置文件如下 obj.txt:

a=java.util.Date
b.javax.swing.JFrame

Java 代码如下:


public class ObjectPoolFactory
{
    //定义一个对象池,前面是对象名,后面是实际对象
    private Map<String ,Object> objectPool =  new HashMap<String , Object>();
    //定义一个创建对象的方法,
    //该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象
    private Object createObject(String clazzName)
        throws InstantiationException , IllegalAccessException
        ,ClassNotFoundException
    {
        //根据字符串来获取对应的Class对象
        Class<?> clazz =Class.forName(clazzName);
        //使用clazz对应类的默认构造器创建实例
        return clazz.newInstance();        
    }
    //该方法根据指定文件来初始化对象池,
    //它会根据配置文件来创建对象
    public void initPool(String fileName)
        throws InstantiationException , IllegalAccessException
        ,ClassNotFoundException
    {
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(fileName);
            Properties props = new Properties();
            props.load(fis);
            for (String name : props.stringPropertyNames())
            {
                //每取出一对属性名-属性值对,就根据属性值创建一个对象
                //调用createObject创建对象,并将对象添加到对象池中
                objectPool.put(name , 
                    createObject(props.getProperty(name))); 
            }
 
        }
        catch (IOException ex)
        {
            System.out.println("读取" + fileName + "异常");
        }
        finally
        {
            try
            {
                if (fis != null)
                {
                    fis.close();
                }
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
        }
    }
    public Object getObject(String name)
    {
        //从objectPool中取出指定name对应的对象。
        return objectPool.get(name);
    }
    
    public static void main(String[] args)
        throws Exception
    {
        ObjectPoolFactory pf = new ObjectPoolFactory();
        pf.initPool("obj.txt");
        System.out.println(pf.getObject("a"));
    }
}


 

3 通过反射调用方法

通过 invoke(Object obj,Object...args) 来实现方法的调用:

public class TestRef {
    public static void main(String args[]) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        Foo foo = new Foo("这个一个Foo对象!");
        Class clazz = foo.getClass();
        Method m1 = clazz.getDeclaredMethod("outInfo");
        Method m2 = clazz.getDeclaredMethod("setMsg", String.class);
        Method m3 = clazz.getDeclaredMethod("getMsg");
        m1.invoke(foo);
        m2.invoke(foo, "重新设置msg信息!");
        String msg = (String) m3.invoke(foo);
        System.out.println(msg);
    }
}

4 通过反射访问属性

通过 Class 对象的 getFields() 和 getField() 方法可以获取该类所包括的全部属性,如:

class Person
{
    private String name;
    private int age;
    public String toString()
    {
        return "Person [ name:" + name + 
            " , age:" + age + " ]";
    }
}
public class FieldTest
{
    public static void main(String[] args) 
        throws Exception
    {
        //创建一个Person对象
        Person p = new Person();
        //获取Person类对应的Class对象
        Class<Person> personClazz = Person.class;
        //获取Person类名为name的属性
        //使用getDeclaredField,表明可获取各种访问控制符的field
        Field nameField = personClazz.getDeclaredField("name");
        //设置通过反射访问该Field时取消访问权限检查
        nameField.setAccessible(true);
        //调用set方法为p对象的指定Field设置值
        nameField.set(p , "Yeeku.H.Lee");
        //获取Person类名为age的属性
        Field ageField = personClazz.getDeclaredField("age");
        //设置通过反射访问该Field时取消访问权限检查
        ageField.setAccessible(true);
        //调用setInt方法为p对象的指定Field设置值
        ageField.setInt(p , 30);
        System.out.println(p);
    }
}

5 通过反射操作数组

下面程序测试了,如果使用 Array 来生成数组,为指定数组元素复制,并获取指定数组元素的方式:

public class ArrayTest1 
{
    public static void main(String args[])
    {
        try
        {
            //创建一个元素类型为String ,长度为10的数组
            Object arr = Array.newInstance(String.class, 10);
            //依次为arr数组中index为5、6的元素赋值
            Array.set(arr, 5, "Struts2权威指南");
            Array.set(arr, 6, "ROR敏捷开发最佳实践");
            //依次取出arr数组中index为5、6的元素的值
            Object book1 = Array.get(arr , 5);
            Object book2 = Array.get(arr , 6);
            //输出arr数组中index为5、6的元素
            System.out.println(book1);
            System.out.println(book2);
        }
        catch (Throwable e)
        {
            System.err.println(e);
        }
    }
}

在上面的程序基础上,我们想生成一个三维数组怎么办呢?

public class ArrayTest2
{
    public static void main(String args[])
    {
        /*创建一个三维数组。
          根据前面介绍数组时讲的:三维数组也是一维数组,是数组元素是
          二维数组的一维数组,因此可以认为arr是长度为3的一维数组
        */
        Object arr = Array.newInstance(String.class, 3, 4, 10);
        //获取arr数组中index为2的元素,应该是二维数组
        Object arrObj = Array.get(arr, 2);
        //使用Array为二维数组的数组元素赋值。
        //二维数组的数组元素是一维数组,所以传入Array set方法的第三个参数是
        //第三个参数是一维数组。
        Array.set(arrObj , 2 , new String[]
        {
            "Struts2权威指南",
            "轻量级J2EE企业应用实战"
        });
        //获取arrObj数组中index为3的元素,应该是一维数组。
        Object anArr  = Array.get(arrObj, 3);
        Array.set(anArr , 8  , "ROR敏捷开发最佳实践");
        //将arr强制类型转换为三维数组
        String[][][] cast = (String[][][])arr;
        //获取cast三维数组中指定元素的值
        System.out.println(cast[2][3][8]);
        System.out.println(cast[2][2][0]);
        System.out.println(cast[2][2][1]);
    }
}

 

6 通过反射来获取泛型的信息

下面是一个完整的实例程序,详细介绍了怎么获取泛型的信息:
 

public class GenericTest
{
    private Map<String , Integer> score;
    public static void main(String[] args)
        throws Exception
    {
        Class<GenericTest> clazz = GenericTest.class;
        Field f = clazz.getDeclaredField("score");
        //直接使用getType()取出Field类型只对普通类型的Field有效
        Class<?> a = f.getType();
        System.out.println("score的类型是:" + a);
        //获得Field实例f的泛型类型
        Type gType = f.getGenericType();
        //如果gType类型是ParameterizedType对象
        if(gType instanceof ParameterizedType)
        {
            //强制类型转换
            ParameterizedType pType = (ParameterizedType)gType;
            //获取原来类型
            Type rType = pType.getRawType();
            System.out.println("原始类型是:" + rType);
            //取得泛型类型的泛型参数
            Type[] tArgs = pType.getActualTypeArguments();
            System.out.println("泛型类型是:");
            for (int i = 0; i < tArgs.length; i++) 
            {
                System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
            }
        }
        else
        {
            System.out.println("获取泛型类型出错!");
        }
    }
}

关于 Java 反射相关的知识就介绍到这里,希望能帮到大家。

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiclaim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值