反射

反射是在运行时获取类型的信息,再根据这些信息进行操作。

一、Class类

每个已加载的类在内存中都有一份类信息,每个对象都有指向它的类信息的引用。

在Java中,类信息对应的类就是java.lang.Class(注意不是小写的class),Object方法:

  1. public final native Class<?> getClass()

Class是泛型类,还有一种获取Class方法:

  1. Class<Date> cls = Date.class

接口也有Class对象:

  1. Class<Comparable> cls = Comparable.class;

基本类型没有getClass方法,但也都有对应的Class对象,类型参数为相应的包装类型:

  1. Class<Integer> intCls = int.class;
  2. Class<Byte> byteCls = byte.class;
  3. Class<Character> charCls = char.class;
  4. Class<Double> doubleCls = double.class;

void也有:

  1. Class<Void> voidCls = void.class;

对于数组每个维度都有一个:

  1. String[] strArr = new String[10];
  2. int[][] twoDimArr = new int[3][2];
  3. int[] oneDimArr = new int[10];
  4. Class<? extends String[]> strArrCls = strArr.getClass();
  5. Class<? extends int[][]> twoDimArrCls = twoDimArr.getClass();
  6. Class<? extends int[]> oneDimArrCls = oneDimArr.getClass();

根据类名加载Class:

  1. Class<?> cls = Class.forName("java.util.HashMap");

下面介绍Class的一些方法。

1.名称信息

  1. public String getName()
  2. public String getSimpleName()
  3. public String getCanonicalName()
  4. public Package getPackage()

2.字段信息

类中定义的静态和实例变量被称为字段,在Java中用Field表示,位于包java.lang.reflect。Class中获取字段信息的方法:

  1. //返回所有的public字段,包括其父类的,如果没有字段,返回空数组
  2. public Field[] getFields()
  3. //返回本类声明的所有字段,包括非public的,但不包括父类的
  4. public Field[] getDeclaredFields()
  5. //返回本类或者父类中指定名字的public字段,找不到抛异常
  6. public Field getField(String name)
  7. //根据名字找本类的字段
  8. public Field getDeclaredField(String name)

Field也有很多方法获取字段信息:

  1. //获取字段名称
  2. public String getName()
  3. //判断当前程序是否有该字段的访问权限
  4. public boolean isAccessible()
  5. //flag设为true表示允许读写非public的字段
  6. public void setAccessible(boolean flag)
  7. //获取指定对象obj中该字段的值
  8. public Object get(Object obj)
  9. //将指定对象obj中该字段的值设为value
  10. public void set(Object obj, Object value)

在上面的set/get方法中,对于静态变量,obj被忽略,设置为null。

其他方法:

  1. public int getModifiers()
  2. public Class<?> getType()
  3. public void setBoolean(Object obj, boolean z)
  4. public boolean getBoolean(Object obj)
  5. public void setDouble(Object obj, double d)
  6. public double getDouble(Object obj)
  7. public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
  8. public Annotation[] getDeclaredAnnotations()
  1. try {
  2. Field f = Test.class.getDeclaredField("MAX_COUNT");
  3. int mod = f.getModifiers();
  4. System.out.println(Modifier.toString(mod)); //private static final
  5. System.out.println(Modifier.isPublic(mod));//false
  6. } catch (NoSuchFieldException e) {
  7. e.printStackTrace();
  8. }

3.方法信息

用类Method表示,Class有如下方法:

  1. public Method[] getMethods()
  2. public Method[] getDeclaredMethods()
  3. public Method getMethod(String name, Class<?>... parameterTypes)
  4. public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

Method类中的方法有:

  1. public String getName()
  2. public void setAccessible(boolean flag)
  3. public Object invoke(Object obj, Object... args) throws
  4. IllegalAccessException, Illegal-ArgumentException, InvocationTargetException

对于invoke方法,如果Method为静态方法,obj被忽略,传入null。args可以为null,

或者为空数组。方法的返回值被包装为Object返回,如果实际方法调用抛出异常,异常

被包装为InvocationTargetException重新抛出,可以通过getCause方法得到原异常。

4.创建对象和构造方法

Class有一个可以创建对象的方法:

  1. public T newInstance() throws InstantiationException, IllegalAccessException

它会调用默认的构造方法,如果没有,抛出异常。

newInstance()方法只能使用默认的构造方法。Class还有一些获取其他构造方法的方法:

  1. public Constructor<?>[] getConstructors()
  2. public Constructor<?>[] getDeclaredConstructors()
  3. public Constructor<T> getConstructor(Class<?>... parameterTypes)
  4. public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

类Constructor表示构造方法,通过它可以创建对象,方法为:

  1. public T newInstance(Object ... initargs) throws InstantiationException,
  2. IllegalAccessException, IllegalArgumentException, InvocationTargetException

例子:

  1. Constructor<StringBuilder> contructor= StringBuilder.class
  2. .getConstructor(new Class[]{int.class});
  3. StringBuilder sb = contructor.newInstance(100);

5.类型检查和转换

如果检查的类型是动态的,可以使用Class类的如下方法:

  1. public native boolean isInstance(Object obj);
  1. Class cls = Class.forName("java.util.ArrayList");
  2. if(cls.isInstance(list)){
  3. System.out.println("array list");
  4. }

动态的强制类型转换,可以使用Class方法:

  1. public T cast(Object obj)

判断Class之间的关系

  1. //检查参数类型cls能否赋值给当前class类型的变量
  2. public native boolean isAssignableFrom(Class<?> cls);

6.Class的类型信息

  1. public native boolean isArray()
  2. public native boolean isPrimitive() //是否是基础类型
  3. public native boolean isInterface()
  4. public boolean isEnum()
  5. public boolean isAnnotation()
  6. public boolean isAnonymousClass() //是否是匿名类
  7. public boolean isMemberClass() //是否是成员类,成员类定义在方法外,不是匿名类
  8. public boolean isLocalClass() //是否是本地类,本地类定义在方法内,不是匿名类

7.类的声明信息

Class的其他方法:

  1. public native int getModifiers()
  2. public native Class<? super T> getSuperclass()
  3. //对于类,为自己声明实现的所有接口,对于接口为直接扩展的接口,不包括父类继承的
  4. public native Class<?>[] getInterfaces();
  5. //自己声明的注解
  6. public Annotation[] getDeclaredAnnotations()
  7. //所有注解包括继承得到的
  8. public Annotation[] getAnnotations()
  9. //获取或者检查指定类型的注解
  10. public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
  11. public boolean isAnnotationPresent(
  12. Class<? extends Annotation> annotationClass)

8.类的加载

Class有两个静态方法,可以根据类名加载类:

  1. public static Class<?> forName(String className) //这里的className与Class.getName()返回的值一致
  2. public static Class<?> forName(String name, boolean initialize, ClassLoader loader) //inntialize表示加载后,是否执行类的初始化代码(如static语句块)

第一个方法相当于调用:

  1. Class.forName(className, true, currentLoader)

9.反射与数组

对于数组类型,有一个专门的方法,可以获取它的元素类型:

  1. public native Class<?> getComponentType()

例如:

  1. String[] arr = new String[]{};
  2. System.out.println(arr.getClass().getComponentType());//class java.lang.String

java.lang.reflect包中有一个针对数组的专门类Array,提供了对于数组的一些反射支持,主要方法有:

  1. //创建指定元素类型、长度的数组
  2. public static Object newInstance(Class<?> componentType, int length)
  3. //创建多维数组
  4. public static Object newInstance(Class<?> componentType, int... dimensions)
  5. //获取数组array指定索引位置index处的值
  6. public static native Object get(Object array, int index)
  7. //修改数组array指定的索引位置的index处的值为value
  8. public static native void set(Object array, int index, Object value)
  9. //返回数组的长度
  10. public static native int getLength(Object array)

Array也支持各种基本类型操作数组元素

  1. public static native double getDouble(Object array, int index)
  2. public static native void setDouble(Object array, int index, double d)
  3. public static native void setLong(Object array, int index, long l)
  4. public static native long getLong(Object array, int index)

10.反射与枚举

枚举类型也有一个专门的方法,可以获取所有的枚举常量:

  1. public T[] getEnumConstants()

二、反射与泛型

我们曾经说过,泛型参数在运行时会被擦除,其实在类信息

Class中仍然有关于泛型的一些信息,可以通过反射得到。

获取类的泛型参数的Class实例方法:

  1. public TypeVariable<Class<T>>[] getTypeParameters()

Field有如下方法:

  1. public Type getGenericType()

Method有如下方法:

  1. public Type getGenericReturnType()
  2. public Type[] getGenericParameterTypes()
  3. public Type[] getGenericExceptionTypes()

Constructor有如下方法:

  1. public Type[] getGenericParameterTypes()

其中Type是一个接口,Class实现了Type,Type的其他子接口还有:

TypeVariable:类型参数,可以有上界,比如T extends Number

ParameterizedType :参数化类型,有原始类型和具体的类型参数比如,List

WildcardType :通配符类型,比如?、?extends Number、 ? super Integer

三、总结

不建议使用反射,理由如下:

1)没有编译器检测,容易出错

2)性能相对低下

所以,如果能用接口实现同样的灵活性,就不要使用反射。

Java笔记(十九) 反射的更多相关文章

  1. Java笔记(十九)……多线程

    概述 进程: 是一个正在执行中的程序 每一个进程执行都有一个执行顺序,该执行顺序是一个执行路径,或者叫一个控制单元 线程: 就是进程中的一个独立的控制单元,线程在控制着进程的执行 一个进程中至少有一个 ...

  2. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  3. “全栈2019”Java第九十九章:局部内部类与继承详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. “全栈2019”Java第二十九章:数组详解(中篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. “全栈2019”Java第十九章:关系运算符、条件运算符和三元运算符

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  6. (C/C++学习笔记) 十九. 模板

    十九. 模板 ● 模板的基本概念 模板(template) 函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计. 语法: template <<模 ...

  7. java笔记十:java中的反射

    Java中,反射是一种强大的工具.它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代表链接.反射允许我们在编写与执行时,使我们的程序代码能够接入装载到JVM中的类的内部信息,而 ...

  8. Java基础学习笔记十九 IO

    File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...

  9. Java基础学习笔记十九 File

    IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...

  10. Java学习笔记十九:Java中的访问控制修饰符

    Java中的访问控制修饰符 一:Java修饰符的种类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: public class Hello ...

随机推荐

  1. zoj3471 状态压缩dp基础

    /* dp[S]表示状态S下的最大收益,0表示没有了,1表示还在 */ #include<bits/stdc++.h> using namespace std; <<],mp[ ...

  2. 第一周学习总结-Java

    2018年7月15日 暑假第一周,我从网上找了一些讲Java的视频,学到了一些Java的基础,同时也弥补了一些之前学c/c++的知识漏洞.例如,了解到了原码反码补码和按位取反运算符(~)的运算原理. ...

  3. Eclipse中java文件生成jar文件的方法

    在eclpse中找到你要导出的java程序 选中它   单击文件 -->export   在弹出的export对话框中找到 jar File 单击选中-->next   按图示顺序依次 选 ...

  4. JavaBean toString() - 将bean对象打印成字符串

    JavaBean toString方式 https://www.cnblogs.com/thiaoqueen/p/7086195.html //方法一:自动生成 @Override public St ...

  5. C#学习-属性是对字段的扩展

    属性是对字段的扩展. 根据面向对象语言的封装思想,字段最好设为private,因为这样可以防止客户端直接对字段进行篡改,从而保证了内部成员的完整性. 于是为了访问类中的私有字段,C#提供了属性这种机制 ...

  6. [转] webpack3最新版本配置研究(五) devtool,webpack-dev-server,CommonsChunkPlugin

    devtool devtool是webpack中config自带的属性只要使用就可以了不用安装 webpack官网的解释如下 当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原 ...

  7. [HNOI2007]梦幻岛宝珠

    题解: 一道比较好的题目 首先比较显然的就是我们要按照a*2^b的b的顺序来枚举 那么状态f[i][j]表示当前在b,用了a*2^b 刚开始没想到怎么不同层之间搞 看了题解发现非常简单 由于每一层到最 ...

  8. C# 之 向服务器上传资源

    首先写客服端,winform 应用 或者 WPF 应用 模拟一个post提交: /// <summary> /// 将本地文件上传到指定的服务器(HttpWebRequest方法) /// ...

  9. python全栈开发day98-DRF

    1.CBV源码流程 2.restful协议 1 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性 2 面向资源架构(RO ...

  10. 1000. A+B Problem

    Description Calculate a+b Input Two integer a,b (0<=a,b<=10) Output Output a+b Sample Input 1 ...