java基础_反射

2020-03-01  本文已影响0人  dashingqi

什么是反射

反射是一个很牛的功能,能够在程序运行时修改程序的行为。但是反射是非常规手段,反射有风险,应用需谨慎。

把程序代码比作一辆车,因为Java是面向对象的语言,车子有自己的型号、车牌号、发动机,有倒车、泊车等功能。

正常情况下,我们需要为车子配备一个司机,然后按照行为准则规范驾驶。

反射是非常规手段,正常行驶的时候,车子需要司机的驾驶。但是反射不需要,因为它就是车子的自动驾驶。(非常规嘛)

Class

Java是面向对象的语言,基本上是以类为基础构造了整个程序。反射中要求提供的规格说明书就是类的规格说明书。

Class的获取

反射的入口是Class,但是反射中Class是没有公开的构造函数,不能通过关键字new来获取Class的对象,所以就有如下3种获取Class的方式

Class的内容清单

Class的名字获取

Class对象也有名字,涉及到API如下

因为Class是一个入口,它代表引用、基本数据类型以及数组对象,所以获取它们的方式也是不同的。

getName

当Class代表一个引用时

当Class代表是一个基本数据类型的时候

当Class代表是基本数据类型的数组时(int [] [] [] 这样的3维数组)

元素类型 编码
boolean Z
byte B
char C
Double D
Float F
int I
Long L
short S
类或者接口 L类名;(是有;的哟)
getSimpleName()
getCanonicalName

Canonial 是官方、标准的意思,那么该方法就是获取Class对象的官方名字。

这个CanonicalName是官方规定的,如果Class对象没有canonicalName的话就返回一个null

getCanonicalName()是getName()与getSimpleName()的结合

Class获取修饰符

Java开发中定义一个类,往往要通过很多修饰符来配合使用的。大致分为如下四类

Java反射提供了API去获取这些修饰符

获取Class的成员

获取Filed

获取到指定名字的属性API

获取到所有属性

获取Method

类获取接口中的方法对应到Class就是Method

Constructor

Java把构造器从Method中拎出来,用Constructor来表示

Constructor不能从父类中继承,所以就没有办法通过getConstructor()来获取到父类的Constructor

Field的操作

我们在类中定义字段时,通常是这样的

其中 c、d、f、g、demo这些变量都是属性,在反射机制中映射到Class对象中都是Field

它们要么是8中基本数据类型,或者是引用,所有的引用都是Object的子类。

public class Son extends Father {
    int c;

    private String d;

    protected float f;

    public int g;

    Demo demo;
}
Field类型的获取
Field修饰符的获取
Field内容的读取与赋值

这也是反射机制中对Filed主要的目的

Filed定义了一些列set和get方法来设置和获取不同类型的值

Method操控

Method对应普通类的方法;

一般普通类的构成

public int add(int a, int b){

​ return a+b;

}

主要包括如下几个方面

  1. 方法名
  2. 方法的返回值类型
  3. 方法的参数
  4. 方法的修饰符
  5. 方法抛出的异常

Java的反射中针对Method 有相关操作API的来提取相关元素

获取Method方法名
Method获取方法参数
获取Method的返回值类型
Method获取修饰符
Method获取异常类型
Method方法的执行

这应该是整个反射机制的核心内容,我们很多时候运用反射目的就是为了以非常规手段执行Method

Constructor的操控

构造器也叫做构造方法,在Java的反射机制中把Constructor与Method分离开来,单独用Constructor这个类来表示,Constructor同Method差不多,但是它的特别的地方在于它能够创建一个对象。

Java的反射机制中有两种方法可以用来创建类的对象实例:Class.newInstance()和Constructor.newInstance() 官方文档建议开发者使用第二种方法 原因如下

反射中的数组

数组本质上是一个Class,而在Class中存在一个方法用来识别它是否为一个数组

public class MainTest {

    public static void main(String[] args) {
        Class<Arrays> arraysClass = Arrays.class;

        Field[] declaredFields = arraysClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Class<?> type = field.getType();
            if (type.isArray()) {
                System.out.println("Type is " + type.getName());
                System.out.println("ComponentType type is " + type.getComponentType());
            }
        }
    }
}
//运行结果如下
Type is [I
ComponentType type is int
Type is [Loperate_array.Car;
ComponentType type is class operate_array.Car
反射中动态创建数组
Array的读取与赋值

对于Array整体的读取赋值,把它作为一个普通的Field,调用Field中对应的方法即可。

也就是 Field中提供的 set() 和get

ublic void set(Object obj,
                Object value)
         throws IllegalArgumentException,
                IllegalAccessException;


public Object get(Object obj)
           throws IllegalArgumentException,
                  IllegalAccessException;

当数组中指定位置的元素进行读取与赋值,这要涉及到Array提供的一些列 setXXX()和getXXX()方法

public static native boolean getBoolean(Object array, int index)
        throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

    public static native byte getByte(Object array, int index)
        throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

反射中的枚举类Enum

同数组一样,枚举本质上也是一个Class而已,但是反射中还是把它单独提出来了。

枚举的获取与设定

应为等同于Class,所以枚举的获取与设定都可以通过Field中的get()和set()方法。

如果枚举要获取里面的Field、Method、Constructor可以调用Class通用的API

总结

反射是非常规手段,它会抛弃Java虚拟机的很多优化,所以同样功能代码,反射要比正常方式要慢,所以考虑到采用反射时,要考虑它的时间成本。
参考文章:细说反射,Java 和 Android 开发者必须跨越的坎

知识点总结
反射机制.png
上一篇下一篇

猜你喜欢

热点阅读