Java反射使用方法总结及代码示例

反射是java提供的一个重要功能,可以在运行时检查类、接口、方法和变量等信息,无需知道类的名字,方法名等。还可以在运行时实例化新对象,调用方法以及设置和获取变量值。


Class对象

检查一个类之前,必须获取到java.lang.Class对象,java中的所有类型,包括long,int,数组等基本数据类型,都和Class对象有关系。

我们很多人去医院参加体检的时候,都做过B超检查,医生只需把一个探头在我们身上滑动就可以将我们体内的肝、胆、肾等器官反射到B超设备上显示。

Class类对象就相当于B超的探头,将一个类的方法、变量、接口、类名、类修饰符等信息告诉运行的程序。

Java提供了两种方式获取Class对象,一种是使用.class,另外一种是使用Class.forName()。
.class方式适用于在编译时已经知道具体的类。

Class nowJavaClass = NowJava.class;

Class.forName()方式适用于运行时动态获取Class对象,只需将类名作为forName方法的参数:

try{
    Class nowJavaClass1 = Class.forName("NowJava");
}catch(ClassNotFoundException e){
    System.out.println("找不到NowJava类");
}

这个方法会出现类找不到的情况,因此使用这个方法获取Class对象时,必须捕获ClassNotFoundException异常。

获取类名

package cn.nowJava;
public class NowJava {
    public static void  main(String arts[]){
        Class nowJavaClass = NowJava.class;
        System.out.println(nowJavaClass.getName());
        System.out.println(nowJavaClass.getSimpleName());
    }
}

上面代码运行结果如下:

cn.nowJava.NowJava
NowJava

getName()方法获取的类名包含包信息。getSimpleName()方法只是获取类名,不包含包信息。

获取类修饰符

public class NowJava {
    public static void  main(String arts[]){
        Class nowJavaClass = NowJava.class;
        System.out.println(nowJavaClass.getModifiers());
        System.out.println(Modifier.isPublic(nowJavaClass.getModifiers()));

        Class birdClass = Bird.class;
        System.out.println(birdClass.getModifiers());
        System.out.println(Modifier.isPublic(birdClass.getModifiers()));

    }

    private class Bird{

    }}

类修饰符有public、private等类型,getModifiers()可以获取一个类的修饰符,但是返回的结果是int,结合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)

获取包信息

package cn.nowJava;


public class NowJava {
    public static void  main(String arts[]){

        Class birdClass = Bird.class;
        System.out.println(birdClass.getPackage());

    }

    private class Bird{

    }
}

getPackage()方法获取包信息。上面代码运行的结果:

package cn.nowJava

获取父类的Class对象

public class NowJava {
    public static void  main(String arts[]){

        Class birdClass = Bird.class;
        Class superclass = birdClass.getSuperclass();
        System.out.println(superclass.getSimpleName());
    }

    private class Bird extends Animal{

    }

    private class Animal{

    }
}

上面代码运行的结果:

Animal

getSuperclass()方法返回的父类的Class对象。

获取接口信息
获取接口信息的方法:

Class[] interfaces = birdClass.getInterfaces();

一个类可以实现多个接口,所以getInterfaces()方法返回的是Class[]数组。 注意:getInterfaces()只返回指定类实现的接口,不会返父类实现的接口。

获取构造函数Constructor
获取构造函数的方法:

Class birdClass = Bird.class;Constructor[] constructors = birdClass.getConstructors();

一个类会有多个构造函数,getConstructors()返回的是Constructor[]数组,包含了所有声明的用public修饰的构造函数。

如果你已经知道了某个构造的参数,可以通过下面的方法获取到回应的构造函数对象:

public class NowJava {
    public static void  main(String arts[]){

        Class birdClass = Bird.class;
        try{
            Constructor constructors = birdClass.getConstructor(new Class[]{String.class});
        }catch(NoSuchMethodException  e){

        }
    }

    private class Bird {
        public Bird(){

        }

        public Bird(String eat){

        }
    }
}

上面获取构造函数的方式有2点需要注意:
1、只能获取到public修饰的构造函数。
2、需要捕获NoSuchMethodException异常。

获取构造函数的参数
获取到构造函数的对象之后,可以通过getParameterTypes()获取到构造函数的参数。

Constructor constructors = birdClass.getConstructor(new Class[]{String.class});
Class[] parameterTypes = constructors.getParameterTypes();

初始化对象

通过反射获取到构造器之后,通过newInstance()方法就可以生成类对象。

public class NowJava {
    public static void  main(String arts[]){

        Class birdClass = Bird.class;
        try{
            Constructor constructors = birdClass.getConstructor(new Class[]{String.class});
            Bird bird = (Bird)constructors.newInstance("eat tea");

        }catch(Exception  e){
            System.out.println("没有对应的构造函数");
        }
    }

     class Bird {
        public Bird(){

        }

        protected Bird(String eat){

        }
    }
}

newinstance()方法接受可选数量的参数,必须为所调用的构造函数提供准确的参数。如果构造函数要求String的参数,在调用newinstance()方法是,必须提供String类型的参数。

获取Methods方法信息
下面代码是通过反射可以获取到该类的声明的成员方法信息:

Method[] metchods = birdClass.getMethods();
Method[] metchods1 = birdClass.getDeclaredMethods();
Method eatMetchod = birdClass.getMethod("eat", new Class[]{int.class});
Method eatMetchod1 = birdClass.getDeclaredMethod("eat", new Class[]{int.class});

无参的getMethods()获取到所有public修饰的方法,返回的是Method[]数组。 无参的getDeclaredMethods()方法到的是所有的成员方法,和修饰符无关。 对于有参的getMethods()方法,必须提供要获取的方法名以及方法名的参数。如果要获取的方法没有参数,则用null替代:

Method eatMetchod = birdClass.getMethod("eat", null);

无参的getMethods()和getDeclaredMethods()都只能获取到类声明的成员方法,不能获取到继承父类的方法。

获取成员方法参数

Class birdClass = Bird.class;
Class[] parameterTypes = eatMetchod1.getParameterTypes();

获取成员方法返回类型

Class birdClass = Bird.class;
Class returnType = eatMetchod1.getReturnType();

invoke()方法
java反射提供invoke()方法,在运行时根据业务需要调用相应的方法,这种情况在运行时非常常见,只要通过反射获取到方法名之后,就可以调用对应的方法:

Class birdClass = Bird.class;
Constructor constructors1 = birdClass.getConstructor();
Method eatMetchod = birdClass.getMethod("eat", new Class[]{int.class});
System.out.println(eatMetchod.invoke(constructors1.newInstance(), 2));

invoke方法有两个参数,第一个参数是要调用方法的对象,上面的代码中就是Bird的对象,第二个参数是调用方法要传入的参数。如果有多个参数,则用数组。

如果调用的是static方法,invoke()方法第一个参数就用null代替:

public class NowJava {
    public static void  main(String arts[]){
        try{
            Class birdClass = Bird.class;
            Constructor constructors1 = birdClass.getConstructor();
            Method eatMetchod = birdClass.getMethod("eat", new Class[]{int.class});
            System.out.println(eatMetchod.invoke(null, 2));
        }catch(Exception  e){
            e.printStackTrace();
            System.out.println("没有对应的构造函数");
        }
    }}class Bird{
    public static int eat(int eat){
        return eat;
    }
    public Bird(){

    }

    public Bird(String eat){

    }

    private void talk(){}
 }

 class Animal{
     public void run(){

     }
 }

使用反射可以在运行时检查和调用类声明的成员方法,可以用来检测某个类是否有getter和setter方法。getter和setter是java bean必须有的方法。 getter和setter方法有下面的一些规律: getter方法以get为前缀,无参,有返回值 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 birdClass = Bird.class;
Field[] fields1 = birdClass.getFields();
Field[] fields2 = birdClass.getDeclaredFields();
Field fields3 = birdClass.getField("age");
Field fields4 = birdClass.getDeclaredField("age");

getFields()方法获取所有public修饰的成员变量,getField()方法需要传入变量名,并且变量必须是public修饰符修饰。

getDeclaredFields方法获取所有生命的成员变量,不管是public还是private。

获取成员变量类型

Field fields4 = birdClass.getDeclaredField("age");
Object fieldType = fields4.getType();

成员变量赋值和取值
一旦获取到成员变量的Field引用,就可以获取通过get()方法获取变量值,通过set()方法给变量赋值:

Class birdClass = Bird.class;
Field fields3 = birdClass.getField("age");
Bird bird = new Bird();
Object value = fields3.get(bird);
fields3.set(bird, value);

访问私有变量
有很多文章讨论禁止通过反射访问一个对象的私有变量,但是到目前为止所有的jdk还是允许通过反射访问私有变量。

使用 Class.getDeclaredField(String name)或者Class.getDeclaredFields()才能获取到私有变量。

package field;
import java.lang.reflect.Field;
public class PrivateField {
    protected  String name;

    public PrivateField(String name){
        this.name = name;
    }
}
public class PrivateFieldTest {
    public static void main(String args[])throws Exception{
        Class privateFieldClass = PrivateField.class;
        Field privateName = privateFieldClass.getDeclaredField("name");
        privateName.setAccessible(false);
        PrivateField privateField = new PrivateField("NowJava");
        String privateFieldValue = (String) privateName.get(privateField);
        System.out.println("私有变量值:" + privateFieldValue);
    }
}

上面的代码有点需要注意:必须调用setAccessible(true)方法,这是针对私有变量而言,public和protected等都不需要。这个方法是允许通过反射访问类的私有变量。

访问私有方法
和私有变量一样,私有方法也是不允许其他的类随意调用的,但是通过反射可以饶过这一限制。 使用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods()方法获取到私有方法。

public class PrivateMethod {
    private String accesPrivateMethod(){
        return "成功访问私有方法";
    }
}
public class PrivateMethodTest {
    public static void main(String args[])throws Exception{
        Class privateMethodClass = PrivateMethod.class;

        Method privateStringMethod = privateMethodClass.getDeclaredMethod("accesPrivateMethod", null);
        privateStringMethod.setAccessible(true);
        String returnValue = (String)privateStringMethod.invoke(new PrivateMethod(), null);

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

和访问私有变量一样,也要调用setAccessible(true)方法,允许通过反射访问类的私有方法。

访问类注解信息
通过反射可以在运行时获取到类、方法、变量和参数的注解信息。

访问类的所有注解信息:

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

访问方法注解信息:

Method method = ... //obtain method 
objectAnnotation[] 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 = ... // obtain method 
objectAnnotation 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());
}

访问参数注解信息:

Method method = ... //obtain method 
objectAnnotation[][] 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()方法返回的是一个二维的Annotation数组,其中包含每个方法参数的注解数组。

访问类所有变量注解信息:

Field field = ... //obtain 
field objectAnnotation[] 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 = ... // obtain method 
objectAnnotation 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());
}

获取泛型信息
很多人认为java类在编译的时候会把泛型信息给擦除掉,所以在运行时是无法获取到泛型信息的。其实在某些情况下,还是可以通过反射在运行时获取到泛型信息的。

获取到java.lang.reflect.Method对象,就有可能获取到某个方法的泛型返回信息。

泛型方法返回类型
下面的类中定义了一个返回值中有泛型的方法:

public class MyClass {

  protected List<String> stringList = ...;

  public List<String> getStringList(){
    return this.stringList;
  }}

下面的代码使用反射检测getStringList()方法返回的是List<String>而不是List

Method method = MyClass.class.getMethod("getStringList", null);
Type returnType = method.getGenericReturnType();
if(returnType instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) returnType;
    Type[] typeArguments = type.getActualTypeArguments();
    for(Type typeArgument : typeArguments){
        Class typeArgClass = (Class) typeArgument;
        System.out.println("typeArgClass = " + typeArgClass);
    }
}

上面这段代码会打印:typeArgClass = java.lang.String

泛型方法参数类型
下面的类定义了一个有泛型参数的方法setStringList():

public class MyClass {
  protected List<String> stringList = ...;

  public void setStringList(List<String> list){
    this.stringList = list;
  }}

Method类提供了getGenericParameterTypes()方法获取方法的泛型参数。

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java