自己对反射的理解和应用还处于比较浅显的阶段,写这篇文章更多在于整理总结,也就是帮助自己进一步的理解和学习反射机制。

反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力

java中类反射

反射是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法
简单总结这些定义,那就是反射可以让我们获得一个类的所有信息,包括私有属性和私有方法,对于我们这种小白,先知道这点就可以啦,那在java中如何使用发射呢。这里我们随便创建一个类来演示。比如说创建一个Book类:

public class Book implements Parcelable
{
    private int id=1;
    private String name="android";

    private String author="wf";

    private String getName()
    {
        return name;
    }
}

Book类中属性和方法都是私有的,现在我们通过反射来访问这些属性和方法。

String s = null;
try
{
    Class<?> bookClass = Class.forName("cc.abto.demo.Book");//完整类名
    Object book = bookClass.newInstance();//获得实例
    Method getAuthor = bookClass.getDeclaredMethod("getName");//获得私有方法
    getAuthor.setAccessible(true);//调用方法前,设置访问标志
    s = (String) getAuthor.invoke(book);//使用方法
}
catch (Exception e)
{
    e.printStackTrace();
}

可以看到上面代码中我们用Class和Method这两个类完成了反射,这两个类分别对应了类和方法,也就是包装了类和方法的信息,下面对反射的部分API做一下简单介绍:

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

在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。 java中的Class三种获取方式:

//使用Class类的静态方法forName(),用类的名字获取一个Class实例
Class<?> bookClass = Class.forName("cc.abto.demo.Book");

//利用对象调用getClass()方法获取该对象的Class实例
Book book = new Book();
Class<? extends Book> bookClass = book.getClass();

//运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例
Class<Book> bookClass = Book.class;
Class<Integer> type = Integer.TYPE;

然后再贴一些常用的方法

    public Annotation[] getAnnotations () //获取这个类中所有注解

    getClassLoader() //获取加载这个类的类加载器

    getDeclaredMethods() //获取这个类中的所有方法

    getReturnType() //获取方法的返回类型

    getParameterTypes() //获取方法的传入参数类型

    isAnnotation() //测试这类是否是一个注解类

    getDeclaredConstructors() //获取所有的构造方法

    getDeclaredMethod(String name, Class… parameterTypes)// 获取指定的构造方法(参数:参数类型.class)

    getSuperclass() //获取这个类的父类

    getInterfaces()// 获取这个类实现的所有接口

    getFields() //获取这个类中所有被public修饰的成员变量

    getField(String name) //获取指定名字的被public修饰的成员变量

    newInstance() //返回此Class所表示的类,通过调用默认的(即无参数)构造函数创建的一个新实例

更多的方法和方法的注解大家可以查看文档。

Android中的简单应用

查看Android SDK的源码时候。你会发现很多类或方法中经常加上了“@hide”注释标记,这些API是不允许在程序中调用的。Hidden API之所以被隐藏,是想阻止开发者使用SDK中那些未完成或不稳定的部分(接口或架构)。如图所示

所以在开发中,我们不仅可以通过反射获取私有属性和方法,也可以利用反射获取一些SDK对外部隐藏的API,比如说前阵子在做蓝牙开发的时候,自动配对的一些方法在API 19以后才对外开放的,这边我们就可以使用反射来实现配对功能了

try
{
    Class<BluetoothDevice> bluetoothDeviceClass = BluetoothDevice.class;
    bluetoothDeviceClass.getMethod("setPin", byte[].class).invoke(device, "1234".getBytes());
    bluetoothDeviceClass.getMethod("createBond").invoke(device);
    bluetoothDeviceClass.getMethod("setPairingConfirmation", boolean.class).invoke(device, true);
    bluetoothDeviceClass.getMethod("cancelPairingUserInput").invoke(device);

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

反射的好处

反射不仅可以让我们获得隐藏的方法和属性,还可以让对象的实例化从编译时转化为运行时,因为我们可以通过Class.forName(“cc.abto.demo.Book”).newInstance()的方法来生成新的实例,而这边的”cc.abto.demo.Book”是一个字符串,完全可以用变量来代替,再结合抽象工厂模式什么的,我们就可以很大程度上对程序应用中的功能模块进行解耦合。可能这边简单几句没能解释清楚,大家可以看看《大话设计模式》之类的书,里面就介绍的比较清楚明白了。
##反射的弊端
反射带来的两大弊端可能就是安全和性能问题了吧,这方面我知之甚少,有待进一步的了解和学习。

最后

因为自己水平有限,如果有些错误的地方还请大家见谅。下面贴出几篇写得比较好和详细的博客。