Java学习笔记之反射(Reflection)

什么是反射

能够分析类能力的程序称为反射(reflection)

反射可以用来干什么

Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。

- 在运行中分析类的能力

- 在运行中查看对象

- 实现通用的数组操作代码

- 利用Method对象

Class类

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着这个对象所属的类。可以通过专门的Java类访问这些信息,保存这些信息的类被称为Class类

获取Class对象的三种方法:

1. 通过调用Object类中的getClass()方法返回一个Class对象

Object myObject = new Object();
Class myObjectClass = myObject.getClass();
  1. 通过调用静态方法forName获得类名对应的Class对象
String className = "java.util.Date";
Class cl = Class.forName(className);

注意:在使用Class.forName()方法时,必须提供一个类的全名,这个全名包括类所在的包的名字。

3. 如果在调用该方法时,没有找到该路径的类,将会抛出ClassNotFoundException。

获得Class对象的第三种方法非常简单,如果T是任意的Java类型,T.class将代表匹配的类对象

Class cl1 = Date.class;
Class cl2 = int.class;

补充:可通过下面方式访问类的父类

Object myObject = new Object();
Class myObjectClass = myObject.getSuperclass();

获取类名

通过Class对象可以获取两个版本的类名:

1. 通过getName()方法返回类的全限定类名(包含包名):

Class aClass = ... //获取Class对象
String className = aClass.getName();
  1. 通过getSimpleName()方法返回类名(不包含包名)
Class aClass = ... //获取Class对象
String className = aClass.getSimpleName();

获取修饰符

可以通过Class对象的getModifiers()方法来访问一个类的修饰符,该方法通过返回一个整型数值,用不同的位开关描述public/private/static等修饰符的使用状况。

Class aClass = ... //获取Class对象
int modifiers = aClass.getModifiers();

还可以使用Modifier类中的isPublic、isPrivate或isFinal判断方法或构造器是否是public、private或final。

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);

获取包信息

可通过以下方式获取包信息:

...
Object object = new Object();
Class cl = object.getClass();
System.out.println(cl.getPackage());
...
/*output
package java.lang, Java Platform API Specification, version 1.7
*/

获取实现的接口集合

可通过调用Class对象的getInterfaces()方法获取一个类实现的接口集合

Class aClass = ... //获取Class对象
Class[] interfaces = aClass.getInterfaces();

注意:getInterfaces()方法仅仅只返回当前类所实现的接口,不包括当前类的父类所实现的接口。

获取构造器

可通过调用Class对象的getConstructors()方法获取一个类的构造函数

Class aClass = ... //获取Class对象
Constructor[] constructors = aClass.getConstructors();

返回的Constructor数组包含每一个声明为public的构造方法。



还可以通过给定的构造方法的参数类型获取指定的构造方法,如下:返回的构造方法的方法参数为String类型

Class aClass = ... //获取Class对象
Constructor constructor = aClass.getConstructor(new Class[]{String.class});

注意:如果没有指定的构造方法能匹配给定的方法参数,则会抛出NoSuchMethodException异常。



还可以获取指定构造方法的方法参数信息

Constructor constructor = ... //获取Constructor对象
Class[] parameterTypes = constructor.getParameterTypes();


利用Constructor对象实例化一个类

Constructor constructor = ... //获取Constructor对象
Class[] parameterTypes = constructor.getParameterTypes();

获取方法

可通过调用Class对象的getMethods()方法获取一个类的所有方法

Class aClass = ...//获取Class对象
Method[] methods = aClass.getMethods();

返回的Method对象数组包含了指定类中声明为public的所有变量集合。



还可以通过具体的参数类型来获取指定的方法

Class  aClass = ...//获取Class对象
Method method = aClass.getMethod("doSomething", new Class[]{String.class});

注意:如果根据给定的方法名称以及参数类型无法匹配到相应的方法,则会抛出NoSuchMethodException



获取指定方法的方法参数以及返回类型

Method method = ... //获取Class对象
Class[] parameterTypes = method.getParameterTypes();
Class returnType = method.getReturnType();


通过Method对象调用方法

//获取一个方法名为doSomesthing,参数类型为String的方法
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

获取变量

可通过调用Class对象的getFields()方法获取一个类的成员变量

Class aClass = ... //获取Class对象
Field[] method = aClass.getFields();

返回的Field对象数组包含了指定类中声明为public的所有变量集合



还可以通过具体的变量名称获取指定的变量

Class aClass = ... //获取Class对象
Field[] method = aClass.getFields("someField");

注意:在调用getField()方法时,如果根据给定的方法参数没有找到对应的变量,那么就会抛出NoSuchFieldException



通过调用Field对象的getName()方法获取它的变量名称

Field field = ... //获取Field对象
String fieldName = field.getName();


通过调用Field对象getType()方法来获取一个变量的类型(如String, int等等)

Class aClass = ... //获取Class对象
Field field = aClass.getField("someField");
Object fieldType = field.getType();


通过调用Field.get()Field.set()方法获取或设置(get/set)变量值

Class  aClass = MyObject.class
Field field = aClass.getField("someField");

MyObject objectInstance = new MyObject();

Object value = field.get(objectInstance);

field.set(objetInstance, value);

获取指定类的getters和setters

  • Getter:Getter方法的名字以get开头,没有方法参数,返回一个值
  • Setter:Setter方法的名字以set开头,有一个方法参数

一个获取getter方法和setter方法的例子:

public static void printGettersSetters(Class aClass){
  Method[] methods = aClass.getMethods();

  for(Method method : methods){
    if(isGetter(method)) System.out.println("getter: " + method);
    if(isSetter(method)) System.out.println("setter: " + method);
  }
}

public static boolean isGetter(Method method){
  if(!method.getName().startsWith("get"))      return false;
  if(method.getParameterTypes().length != 0)   return false;
  if(void.class.equals(method.getReturnType()) return false;
  return true;
}

public static boolean isSetter(Method method){
  if(!method.getName().startsWith("set")) return false;
  if(method.getParameterTypes().length != 1) return false;
  return true;
}

获取私有变量

可以通过调用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法获取私有变量和受保护变量,不包括超类的成员;Class.getField(String name)Class.getFields()只会返回公有变量,其中包括超类的公有变量,而无法获取私有变量。

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }
}
/**********************************************************************/
PrivateObject privateObject = new PrivateObject("The Private Value");

Field privateStringField = PrivateObject.class.
            getDeclaredField("privateString");

privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue);

这个例子会输出”fieldValue = The Private Value”,The Private ValuePrivateObject实例的privateString私有变量的值,注意调用PrivateObject.class.getDeclaredField("privateString")方法会返回一个私有变量,这个方法返回的变量是定义在PrivateObject类中的而不是在它的父类中定义的变量。

注意:privateStringField.setAccessible(true)这行代码,通过调用setAccessible()方法会关闭指定类Field实例的反射访问检查,这行代码执行之后不论是私有的、受保护的以及包访问的作用域,你都可以在任何地方访问,即使你不在他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可以的,在编译的时候就会报错。

获取私有方法

可以通过调用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods()方法获取私有方法,不包括超类的成员;Class.getMethod(String name, Class[] parameterTypes)Class.getMethods()方法,只会返回公有的方法,其中包括超类的公有方法,而无法获取私有方法。

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }

  private String getPrivateString(){
    return this.privateString;
  }
}
/*******************************************************************/
PrivateObject privateObject = new PrivateObject("The Private Value");

Method privateStringMethod = PrivateObject.class.
        getDeclaredMethod("getPrivateString", null);

privateStringMethod.setAccessible(true);

String returnValue = (String)
        privateStringMethod.invoke(privateObject, null);

System.out.println("returnValue = " + returnValue);

这个例子会输出"returnValue = The Private Value"The Private ValuePrivateObject实例的getPrivateString()方法的返回值。PrivateObject.class.getDeclaredMethod("privateString")方法会返回一个私有方法,这个方法是定义在PrivateObject类中的而不是在它的父类中定义的。

注意:Method.setAcessible(true)这行代码,通过调用setAccessible()方法会关闭指定类的Method实例的反射访问检查,这行代码执行之后不论是私有的、受保护的以及包访问的作用域,你都可以在任何地方访问,即使你不在他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可以的,在编译的时候就会报错。

获取注解

什么是注解?

注解是Java 5的一个新特性。注解是插入你代码中的一种注释或者说是一种元数据(meta data)。这些注解信息可以在编译期使用预编译工具进行处理(pre-compiler tools),也可以在运行期使用Java反射机制进行处理。

下面是一个类注解的例子:

@MyAnnotation(name="someName",  value = "Hello World")
public class TheClass {
}

在TheClass类定义的上面有一个@MyAnnotation的注解。注解的定义与接口的定义相似,下面是MyAnnotation注解的定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)

public @interface MyAnnotation {
  public String name();
  public String value();
}

在interface前面的@符号表名这是一个注解,一旦你定义了一个注解之后你就可以将其应用到你的代码中。

说明:在注解定义中的两个指示@Retention(RetentionPolicy.RUNTIME)和@Target(ElementType.TYPE),说明了这个注解该如何使用。

1. @Retention(RetentionPolicy.RUNTIME)表示这个注解可以在运行期通过反射访问。如果你没有在注解定义的时候使用这个指示那么这个注解的信息不会保留到运行期,这样反射就无法获取它的信息。

2. @Target(ElementType.TYPE) 表示这个注解只能用在类型上面(比如类跟接口)。你同样可以把Type改为Field或者Method,或者你可以不用这个指示,这样的话你的注解在类,方法和变量上就都可以使用了。



类注解

下是一个访问类注解的例子

Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你还可以像下面这样指定访问一个类的注解

Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}


方法注解

下面是一个方法注解的例子

public class TheClass {
  @MyAnnotation(name="someName",  value = "Hello World")
  public void doSomething(){}
}

你可以像这样访问方法注解:

Method method = ... //获取方法对象
Annotation[] annotations = method.getDeclaredAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你可以像这样访问指定的方法注解

Method method = ... // 获取方法对象
Annotation annotation = method.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}


参数注解

方法参数也可以添加注解,就像下面这样

public class TheClass {
  public static void doSomethingElse(
        @MyAnnotation(name="aName", value="aValue") String parameter){
  }
}

你可以通过Method对象来访问方法参数注解

Method method = ... //获取方法对象
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();

int i=0;
for(Annotation[] annotations : parameterAnnotations){
  Class parameterType = parameterTypes[i++];

  for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("param: " + parameterType.getName());
        System.out.println("name : " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
  }
}

需要注意的是Method.getParameterAnnotations()方法返回一个注解类型的二维数组,每一个方法的参数包含一个注解数组。



变量注解

下面是一个变量注解的例子

public class TheClass {

  @MyAnnotation(name="someName",  value = "Hello World")
  public String myField = null;
}

你可以像这样来访问变量的注解

Field field = ... //获取方法对象</pre>
<pre>Annotation[] annotations = field.getDeclaredAnnotations();

for(Annotation annotation : annotations){
 if(annotation instanceof MyAnnotation){
 MyAnnotation myAnnotation = (MyAnnotation) annotation;
 System.out.println("name: " + myAnnotation.name());
 System.out.println("value: " + myAnnotation.value());
 }
}

你可以像这样访问指定的变量注解

Field field = ...//获取方法对象</pre>
<pre>
Annotation annotation = field.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
 MyAnnotation myAnnotation = (MyAnnotation) annotation;
 System.out.println("name: " + myAnnotation.name());
 System.out.println("value: " + myAnnotation.value());
}

参考资料

  1. Java核心技术(卷Ⅰ)
  2. Java Reflection教程
时间: 2024-10-31 01:22:13

Java学习笔记之反射(Reflection)的相关文章

Java学习笔记之反射

鲁春利的工作笔记,谁说程序员不能有文艺范? Java程序在运行期间可以动态加载.解析和使用一些在编译阶段并不确定的类型,这一机制被称为反射(Reflection)或自省(Introspection).Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.这种"看透class"的能力(the ability of the program toexamine itself)被称为

【Todo】Java学习笔记 100==100 &amp; Reflection API &amp; Optional类详解 &amp; DIP、IoC、DI &amp; token/cookie/session管理会话方式

为什么1000 == 1000返回为False,而100 == 100会返回为True?   Link Java Reflection API:Link Java8 Optional 类深度解析: Link 深入理解DIP.DI及IoC容器: Link 3种会话管理的方式: Link

JAVA学习笔记(六十一)- 反射Reflection

反射Reflection import java.lang.reflect.Modifier; /* * 反射Reflection * java.lang.Class类 */ public class Test01 { public static void main(String[] args) { String name = "tom"; // 方式一:通过对象getClass()方法 // 任意一个类的对象,都有一个getClass()方法 // 可以通过此获取获取当前对象的类型所

java学习笔记 第二篇 核心技术(二)

第十四章 集合类 集合类用来存放对象的引用.继承关系如下图: 14.1 Collection 接口 是层次结构中的根接口,构成Collection的单位称为元素.Collection接口不能直接使用,但该接口提供了添加元素.删除元素.管理数据的方法. Collection接口常用方法: 14.2 List 集合 包括List接口以及List集合的所有实现类.List集合中的元素允许重复,各元素循序就是对象插入的顺序 1.List接口,两个重要方法: get(int index): 获取指定索引位

java学习笔记 5

随手 看到了这篇关于Java到底哪里出了问题的文章,笑傻了23333 “Java developers just can’t help themselves it seems - give em an inch, and next thing you know you’re looking at a OO hierarchy 15 layers deep and instantiating a hammer hammer factory.” 继承 Java中的继承用extends,所有的继承都是

Java学习笔记——动态代理

所谓动态,也就是说这个东西是可变的,或者说不是一生下来就有的.提到动态就不得不说静态,静态代理,个人觉得是指一个代理在程序中是事先写好的,不能变的,就像上一篇"Java学习笔记--RMI"中的远程代理,其中客户端服务对象就是一个远程服务对象的代理,这个代理可以使得客户在操作时感觉像在操作本地对象一样,远程对象对于客户是透明的.我们可以看出这里的远程代理,是在程序中事先写好的,而本节我们要讨论的远程代理,是由JVM根据反射机制,在程序运行时动态生成的.(以上是本人的理解,如果有不正确的地

Java学习笔记3-操作符

Java基本操作符:+.-.*./.%.=.==.!=.+=.-=. 优先级:先乘除后加减,如果是连接符+号会优先往前匹配,比如 a+++++b,会被解释称 a++ ++ +b,所以会报错,需要自行使用括号隔离为 (a++) + (++b). 对象的引用如果赋值给了对象的引用后,2 个对象将指向同一个引用,有一个对象的引用重新赋值后将同时影响到另一个对象,比如 ClassName classA = new ClassName(); ClassName classB = new ClassName

java学习笔记10--泛型总结

java学习笔记系列: java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对象的初始化与回收 java学习笔记3--类与对象的基础 java学习笔记2--数据类型.数组 java学习笔记1--开发环境平台总结 本文地址:http://www.cnblogs.com/archimedes/p/java-study-note10.html,转载

Java学习笔记_25_Collections类

25.Collections类: Collections类是一个工具类,用来对集合进行操作,它主要是提供一些排序算法,包括随机排序.反相排序等. Collections类提供了一些静态方法,实现了基于List容器的一些常用算法. Collections的一些方法列表: · void sort(List): 对List内的元素进行排序. · void shuffle(List): 对List内的元素随机排序. · void reverse(List): 对List内的元素进行逆序排列. · voi