Java基础系列之(一) - Reflection

前段时间与一个新手谈论Java基础的时候提到反射,然后对反射的一些问题在这里基本介绍下。在介绍之前,这里了解几个反射的几个概念。

Class - class是对一个类进行描述的,你可以认为它是一个类的模型。

Constructor - constructor是一个Class的构造函数,一个Class可以允许重载多个构造函数。

Field - Filed是用来描述Class的成员变量的。

Method - method是描述一个Class的方法的。

Annotation - Annotation是描述Class的注解的,注解可以作用于Class,Method,Filed,Argument之上。

Generic - Generic指的是泛型,限制参数,返回类型的描述。

当然其他的还有参数,访问域等一些概念。

1、Class(类)

有多种方式如下:

Class clz = myobject.class;

动态加载也可以:

Class clz = Class.forName("类的全名");

(1) 下面描述的是可以通过Class对象获取构造函数、方法、成员变量、注解等,这里注意的是,这里获取得都是访问范围是public。

        Class clz = Reflection1Test.class;

        //获取所有public的构造函数
        Constructor[] constructors = clz.getConstructors();
        //获取参数是String类型的public构造函数
        Constructor constructor = clz.getConstructor(new Class[]{String.class});

        //获取所有public方法
        Method[] methods = clz.getMethods();
        //获取方法名称是"test",参数类型分别是List和String,而且是public的方法
        Method method = clz.getMethod("test", new Class[]{java.util.List.class,String.class});

        //获取指定的枚举类型的枚举
        Annotation annotation = clz.getAnnotation(Reflection1Test.class);
        //获取该类的所有枚举
        Annotation[] annotations = clz.getAnnotations();

(2) 类的修饰范围

Class clz = Reflection1Test.class;
int modifiers = clz .getModifiers();

这里返回的是一个int值,int里面存储的信息包含public,private,protected,final,abstract,interface,native,static,synchronized,Transient,Volatile。这里稍稍提下存储原理,基本原理采用的是二进制的占位思想,之前我的一篇文章bitmap里面也是用的是二进制占位的算法。public,private,protected,final,abstract,interface,native,static,synchronized,Transient,Volatile每个定义一个二进制的位置。例如:

    public static final int PUBLIC           = 0x00000001;    public static final int PRIVATE          = 0x00000002;
    public static final int PROTECTED        = 0x00000004;
    public static final int STATIC           = 0x00000008;
    public static final int FINAL            = 0x00000010;
    public static final int SYNCHRONIZED     = 0x00000020;
    public static final int VOLATILE         = 0x00000040;
    public static final int TRANSIENT        = 0x00000080;
    public static final int NATIVE           = 0x00000100;
    public static final int INTERFACE        = 0x00000200;
    public static final int ABSTRACT         = 0x00000400;
    public static final int STRICT           = 0x00000800;

这样的话,假如一个Class既是public,又是final,这个时候她的modifier值就是public+final。

当然你想判断它是否是public还是final,JDK提供一个modifier类专门处理,如下:

Modifier.isAbstract(int modifiers);
Modifier.isFinal(int modifiers);
Modifier.isInterface(int modifiers);
Modifier.isNative(int modifiers);
Modifier.isPrivate(int modifiers);
Modifier.isProtected(int modifiers);
Modifier.isPublic(int modifiers);
Modifier.isStatic(int modifiers);
Modifier.isStrict(int modifiers);
Modifier.isSynchronized(int modifiers);
Modifier.isTransient(int modifiers);
Modifier.isVolatile(int modifiers);

2、Constructor(构造函数)

(1) 获取构造函数

上面已经提到过了。怎么获取构造函数:

//获取所有public的构造函数
Constructor[] constructors = clz.getConstructors();

上面返回的是一个public的构造函数数组。

如果你像返回指定的构造函数,需要提供构造函数的参数类型。

Constructor constructor = clz.getConstructor(new Class[]{String.class});

返回的也是public的构造函数,如果没有找到指定的构造函数,就会抛NoSuchMethodException。

(2) 构造函数的参数类型

Class[] cType = constructor.getParameterTypes();

(3) 构造函数实例化

我们都知道构造函数是一个对象实例化的入口,或者说是new的时候要首先调用的特殊方法。那么Constructor肯定能够实例化一个对象的。

try {
            MyObject obj = (MyObject)constructor.newInstance(new Object[]{"123",new ArrayList<String>()});
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

constructor.newInstance()需要提供一个可变的参数列表,提供的参数必须和声明的构造函数的值和类型都一一对应。

3、Field(变量)

(1)获取Field对象

        //获取所有public成员变量
        Field[] fields = clz.getFields();
        //获取名称是name的public的变量
        Field field = clz.getField("name");

这里也一样,返回的都是public修饰符的变量,如果找不到指定匹配的变量名称,会抛NoSuchFieldException异常。

(2)获取Field的名称和类型

        //获取变量的名称
        String name = field.getName();

        //获取变量的类型
        Class fieldType = field.getType();

(3)get/set Field的值

        MyObject obj = new MyObject();
        field = clz.getField("name");
        //获取obj对象的name变量的值
        Object object = field.get(obj);
        //设置obj对象的变量name的值为123
        field.set(obj, "123");

这里通过变量的对象来获取它的值,并且能修改它的值。当然这里依然遵照的是public的原则。

(4)访问私有变量

上面已经说过了,上面访问都是public的变量,那么,私有变量怎么访问呢?如下:

        //获取所有声明的变量
        Field[] declaredFields = clz.getDeclaredFields();
        Field declaredField = clz.getDeclaredField("privateString");
        //私有变量必须先将访问权限设置为true
        declaredField.setAccessible(true);
        Object o = declaredField.get(test2);

declaredField.setAccessible(true)代码是关闭字段的访问检查。

4、Method(方法)

(1)获取Method对象

        //获取所有public方法
        Method[] methods = clz.getMethods();
        //获取方法名称是"test",参数类型分别是List和String,而且是public的方法
        Method method = clz.getMethod("test", new Class[]{java.util.List.class,String.class});

如果没有找到指定匹配的Method,会抛NoSuchMethodException异常。

(2)获取Method的参数

        //获取Method的参数类型
        Class[] parameterTypes = method.getParameterTypes();

因为一个方法的参数可以是多个,所以返回的是一个数组。

(3)获取Method的返回值

        //返回Method返回值的类型
        Class returnType = method.getReturnType();

这里有人就问,如果没有返回值呢,如果是void的呢,其实void也可以看成一个返回值,空返回值的。所以void的方法返回的直接是void。

(4)Method调用

定义一个test方法,有两个参数,如下:

    public void test(List s,String s1){
        System.out.println("调用了Test");
    }
    Reflection1Test test = new Reflection1Test("");
        //调用Reflection1Test对象的test方法
        Object returnValue = method.invoke(test, new Object[]{new ArrayList(),"123"});

这里有一个小细节,如果test是null的话,调用的是方法必须是static。

(5)访问私有方法

        Reflection1Test test3 = new Reflection1Test("");
        //获取所有声明的方法
        Method[] declaredMethods = clz.getDeclaredMethods();
        Method declaredMethod = clz.getDeclaredMethod("test", new Class[]{java.util.List.class,String.class});
        declaredMethod.setAccessible(true);
        Object returnValue2 = declaredMethod.invoke(test3, new Object[]{new ArrayList(),"123"});

与Field一样setAccessible(true)关闭反射的权限检查。

5、Annotation(注解)

注解的简单理解就是在类、方法、变量上打一个标签,这个标签为了具体说明它的实际意义,对类、方法、变量没有实际应用意义。

(1)定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Documented
public @interface AnnotationTest {
    String value() default "";
}

注解定义是在interface前面带一个@符号,这就说明是一个注解,一般有几个定义规则。

@Retention - 定义注解存在的周期,RetentionPolicy有三个值,Source,Class,RUNTIME。
  RetentionPolicy.Source 表示只是在源码中存在,编译的时候就被丢弃了。
  RetentionPolicy.Class 表示编译的时候注入到Class中,但是VM加载的时候会丢弃。
  RetentionPolicy.RUNTIME 表示注解会被VM加载进去,在运行期能够调用。

@Target - 定义注解的作用范围,一般作用于类、方法、变量、构造函数。
  • ElementType.ANNOTATION_TYPE
  • ElementType.CONSTRUCTOR
  • ElementType.FIELD
  • ElementType.LOCAL_VARIABLE
  • ElementType.METHOD
  • ElementType.PACKAGE
  • ElementType.PARAMETER
  • ElementType.TYPE

其中,ANNOTATION_TYPE是针对注解用的,在注解定义上被应用的

@Inherited - 能够被子类复用。

@Inherited
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }

public class MySubClass extends MySuperClass { ... }
@Documented - 能够在文档显示的。
@Documented
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }
(2)使用注解

1、类注解
@AnnotationTest(value="chs")
public class Reflection1Test {...}
下面是访问类的注解的例子:
        Class clz = Reflection1Test.class;

        //获取类的所有注解
        Annotation[] annotations = clz.getAnnotations();
        //获取注解类型是AnnotationTest
        Annotation annotation = clz.getAnnotation(AnnotationTest.class);
        if(annotation instanceof AnnotationTest){
            AnnotationTest annotationTest = (AnnotationTest)annotation;
            System.out.println(annotationTest.value());
        }
2、方法注解
    @AnnotationTest(value="chs2")
    public Reflection1Test(String s){

    }
下面是访问方法注解的例子:
        Method method = clz.getMethod("test", new Class[]{java.util.List.class,String.class});

        Annotation[] annotations2 = method.getAnnotations();

        Annotation annotation2 = method.getAnnotation(AnnotationTest.class);
        if(annotation instanceof AnnotationTest){
            AnnotationTest annotationTest = (AnnotationTest)annotation;
            System.out.println(annotationTest.value());
        }
3、变量注解
    @AnnotationTest(value="chs2")
    private String privateString;

下面是变量访问注解的例子:

        Field field = clz.getField("name");

        Annotation[] annotations3 = field.getAnnotations();

        Annotation annotation3 = field.getAnnotation(AnnotationTest.class);
        if(annotation instanceof AnnotationTest){
            AnnotationTest annotationTest = (AnnotationTest)annotation;
            System.out.println(annotationTest.value());
        }

4、参数注解

    public void test(List s,@AnnotationTest String s1){
        System.out.println("调用了Test");
    }

下面是参数访问注解的例子:

        Annotation[][] Annotations5 = method.getParameterAnnotations();
        Class[] parameterTypes = method.getParameterTypes();

        int i=0;
        for(Annotation[] annotations21 : Annotations5){
          Class parameterType = parameterTypes[i++];
          for(Annotation annotation12 : annotations21){
            if(annotation12 instanceof AnnotationTest){
                AnnotationTest myAnnotation = (AnnotationTest) annotation12;
                System.out.println("param: " + parameterType.getName());
                System.out.println("value: " + myAnnotation.value());
            }
          }

5、Generic(泛型)

泛型的应用非常广,一般使用的时候,是在定义一个参数化(可变的)的类或者接口,说白了,你根本不知道运行的时候应该具体哪一种类型,但是你知道是一个可变参数。你不能再运行的时候知道被参数化的类的类型是什么,当你具体使用这个类的时候,你可以显示指定它。

总之是,定义的时候不知道具体类型,使用的时候需要指定具体的类型。

(1)返回值是一个泛型类型

    public List<String> getList(){
        return null;
    }

下面是访问返回值的泛型类型的例子:

Method method = Reflection1Test.class.getMethod("getList", null);

Type return = method.getGenericReturnType();

if(return instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) return ;
    Type[] arguments = type.getActualTypeArguments();
    for(Type argument : arguments ){
        Class argClz = (Class) argument ;
        System.out.println("argClz = " + argClz );
    }
}

(2)方法的参数是一个泛型类型

    public void setList(List<String> list){
    }

下面是一个方法参数的泛型类型访问的例子:

Method method = Reflection1Test.class.getMethod("setList", List.class);

Type[] genericParameterTypes = method.getGenericParameterTypes();

for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        ParameterizedType pType = (ParameterizedType) genericParameterType;
        Type[] argTypes = pType.getActualTypeArguments();
        for(Type parameterType : argTypes){
            Class parameterArgClz= (Class) parameterType;
            System.out.println("parameterArgClass = " + parameterArgClz);
        }
    }
}

(3)变量的类型是一个泛型类型

    public List<String> list;

下面是一个变量的泛型类型访问的例子:

Field field = Reflection1Test.class.getField("list");

Type genericType = field.getGenericType();

if(genericType instanceof ParameterizedType){
    ParameterizedType pType = (ParameterizedType) genericType;
    Type[] argTypes = pType.getActualTypeArguments();
    for(Type argType : argTypes){
        Class argClz = (Class) argType;
        System.out.println("Class = " + argClz);
    }
}
至此,java反射基本介绍如此,下一张准备说下ArrayList、LinkedList、HashMap等数据结构的源码。 
 
 
时间: 2024-08-05 05:12:37

Java基础系列之(一) - Reflection的相关文章

夯实Java基础系列9:深入理解Class类和Object类

目录 Java中Class类及用法 Class类原理 如何获得一个Class类对象 使用Class类的对象来生成目标类的实例 Object类 类构造器public Object(); registerNatives()方法; Clone()方法实现浅拷贝 getClass()方法 equals()方法 hashCode()方法; toString()方法 wait() notify() notifAll() finalize()方法 CLass类和Object类的关系 参考文章 微信公众号 Ja

《Java 基础系列》初步整理

<Java 基础系列>初步整理大概有 12 篇,主要内容为.: 抽象类和接口内部类修饰符装箱拆箱注解反射泛型异常集合IO字符串其他第一篇我们来聊聊抽象类和接口. "抽象类和接口"听起来是非常普遍的东西,有些朋友会觉得:这个太基础了吧,有啥好说的,你又来糊弄我. 这里写图片描述 事实上我在面试中不仅一次被问到相关的问题: 抽象类和接口之间的区别?什么时候创建抽象类?什么时候创建接口?设计框架时该如何选择?我比较喜欢这样的问题,答案可深可浅,体现了我们对日常工作的思考. 我们什

夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理

目录 final使用 final变量 final修饰基本数据类型变量和引用 final类 final关键字的知识点 final关键字的最佳实践 final的用法 关于空白final final内存分配 使用final修饰方法会提高速度和效率吗 使用final修饰变量会让变量的值不能被改变吗: 如何保证数组内部不被修改 final方法的三条规则 final 和 jvm的关系 写 final 域的重排序规则 读 final 域的重排序规则 如果 final 域是引用类型 参考文章 微信公众号 Jav

夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!

目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接口与抽象类的本质区别是什么? 基本语法区别 设计思想区别 如何回答面试题:接口和抽象类的区别? 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl

夯实Java基础系列7:一文读懂Java 代码块和执行顺序

目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块简介 Java代码块使用 局部代码块 构造代码块 静态代码块 Java代码块.构造方法(包含继承关系)的执行顺序 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github

夯实Java基础系列10:深入理解Java中的异常体系

目录 为什么要使用异常 异常基本定义 异常体系 初识异常 异常和错误 异常的处理方式 "不负责任"的throws 纠结的finally throw : JRE也使用的关键字 异常调用链 自定义异常 异常的注意事项 当finally遇上return JAVA异常常见面试题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 - Java异常 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.c

夯实Java基础系列13:深入理解Java中的泛型

目录 泛型概述 一个栗子 特性 泛型的使用方式 泛型类 泛型接口 泛型通配符 泛型方法 泛型方法的基本用法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型方法总结 泛型上下边界 泛型常见面试题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下Star.Fork.Watch三连哈,感谢你的

Java基础系列1:深入理解Java数据类型

Java基础系列1:深入理解Java数据类型 当初学习计算机的时候,教科书中对程序的定义是:程序=数据结构+算法,Java基础系列第一篇就聊聊Java中的数据类型. 本篇聊Java数据类型主要包括四个内容: Java基本类型 Java封装类型 自动装箱和拆箱 封装类型缓存机制 Java基本类型 Java基本类型分类.大小及表示范围 Java的基本数据类型总共有8种,包括三类:数值型,字符型,布尔型,其中 数值型: 整数类型:byte.short.int.long 浮点类型:float.doubl

Java 基础系列合集

Java特性 Java三大特性:封装,继承,多态 Java 抽象类与接口 Java 浅拷贝和深拷贝 Java static和final Java 内部类.静态内部类.匿名内部类 Java 强制类型转换 java 实现多重继承 Java - equals方法 Java 字符串 Java - 异常解析基础 Java集合系列 Java - 数组解析 Java - 集合 Java - ArrayList源码分析 Java - LinkedList源码分析 Java - HashMap源码解析 Java