Java反射详解

Java反射详解

转载说明出处:http://blog.csdn.net/a15286856575/article/details/53330821

反射简介

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

  • Java反射机制是Java语言被视为“准动态”语言的关键性质。Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的内部信息(包括其modifiers(诸如public, static等等)、superclass(例如Object)、实现interfaces(例如Serializable),也包括fields和methods的所有信息),动态地生成此类,并调用其方法或修改其域(甚至是本身声明为private的域或方法)。

  • Java反射机制提供如下功能:

    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判段任意一个类所具有的成员变量和方法
    • 在运行时调用任一个对象的方法
    • 在运行时创建新类对象
    • 在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。

Class对象

  • Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
  • Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
  • 虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
  • 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
  • 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
  • 一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象
  • 获取Class对象有三种方式:
    1. 通过实例变量的getClass()方法。
People people = new People();
people.setName("sdff");
Class<? extends People> aClass = people.getClass();
  1. 通过Class类的静态方法——forName()来实现
Class aclass =Class.forName(“com.wenjutian.injectleaning.refect.People”);

注意:当使用Class.forName()方法时,你必须提供完全限定类名。即类名要包括所有包
名。如果在运行时类路径上找不到类,Class.forName()方法会抛出一个ClassNotFoundExcepton。

  1. 使用类字面常量或TYPE字段
Class aclass= People.class

类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型

这种方式不仅更简单,而且更安全,因为它在编译时就会受到检查,并且根除了对forName方法的调用,所以也更高效,建议使用“.class”的形式。
Class c = Integer.TYPE;(TYPE是基本数据类型的包装类型的一个标准字段,它是一
个引用,指向对应的基本数据类型的Class对象),附表如下,两边等价:

class | TYPE
---|---
boolean.class| Boolean.TYPE
char.class |Character.TYPE
short.class | Short.TYPE
int.class | Integer.TYPE
long.class | Long.TYPE
float.classs| Float.TYPE
double.class |Double.TYPE
void.class | Void.TYPE

拿到Class对象我们能拿到什么?

通过Class对象我们能拿到:类名,
类修饰符 (public, private, synchronized等)
,包信息
,父类
,实现的接口
,构造函数
,方法
,字段
,注解。

1. 类名

aClass.getName();//带包名
aClass.getSimpleName();//不带包名

输入结果:com.wenjutian.injectleaning.refect.People, people

2. 修饰符

使用Class对象可以获取一个类的修饰符.类的修饰符即关键字”public”,”private”, “static”等. 如下:

int modifiers = aClass.getModifiers();

修饰符被包装进一个int内,每一个修饰符都是一个标志位(置位或清零)。可以使用java.lang.reflect.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)

3. 包信息

Package package = aClass.getPackage();
String packageName = package.getname();

从Package对象中你可以访问诸如名字等包信息。您还可以访问类路径上这个包位于JAR文件中Manifest这个文件中指定的信息。例如,你可以在Manifest文件中指定包的版本号。可以在java.lang.Package中了解更多包类信息。

4. 父类

Class superclass = aClass.getSuperclass();

父类的Class对象和其它Class对象一样是一个Class对象,可以继续使用反射.

5. 实现的接口

通过给定的类可以获取这个类所实现的接口列表,如下:

Class[] interfaces = aClass.getInterfaces();

一个类可以实现多个接口。因此返回一个Class数组。在Java反射机制中,接口也由Class对象表示。即使,子类重写了父类的接口,这时候拿不到父类的接口。

6. 构造函数

获取构造方法的api

public Constructor<T> getConstructor(Class<?>... parameterTypes)

因为参数可能有多个所以传入的时候可以是个动态数组。

 public T newInstance(Object... args)

初始化对象的方法,里面传入的是构造方法的可变参数。
* 无参数的构造方法

/**
             * 无参数的构造方法
             */
            Constructor<? extends People> constructor = aClass.getConstructor(null);
            People people1 = constructor.newInstance(null);
            people1.setName("dsf");
            System.out.println(people1.getName());

如果参数为空传入null;
* 有参数的构造方法

Constructor<? extends People> constructor1 = aClass.getConstructor(String.class, int.class);
 //可变参数最后在编译后,会变成数组,所以也可以传数组
//Constructor<? extends People> constructor2 = aClass.getConstructor(new Class[]{String.class, int.class});
constructor1.newInstance("王五",100);
 System.out.println("有参数的构造方法"+constructor1);

7. 方法

  • public方法

    • 获取所有公共方法
       Method[] methods = aClass.getMethods();
              for (Method method : methods) {
                  System.out.println("公共方法"+method.getName());
              }

    此种方法会获取子类和父类所有的公共方法
    相关api

    “`
    public Method getMethod(String name, Class

Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                declaredMethod.setAccessible(true);
                System.out.println("在此类中所有的方法"+declaredMethod.getName());
            }
 Method buyMethod = aClass.getDeclaredMethod("buy", String.class);
            buyMethod.setAccessible(true);
            buyMethod.invoke(people,"bananer");

总结:此方法只能调用当前类的所有方法,如果是私有方法,要设置setAccessible(true).如果是想要拿父类中的方法可以用getSuperClass();
eg:

 Class<?> superclass = aClass.getSuperclass();
            Method eat = superclass.getDeclaredMethod("eat", null);
            eat.setAccessible(true);
            eat.invoke(people,null);

8. 字段

  • public字段
    • 获取

      Field[] fields = aClass.getFields();

      这个方法能够或得该类以及父类的所有公共字段

      Field nameField = aClass.getField("name");

      这个方法只能获取该类以及父类的制定的公共字段
    • 获取字段的名字

      nameField.getName()
    • 字段赋值

      nameField.set(people,"liming");
    • 获取字段的值

      nameField.get(people);
  • 访问非公共字段
    • 获取

      Field[] declaredFields = aClass.getDeclaredFields();
      for (Field declaredField : declaredFields) {
      System.out.println("所有字段"+declaredField.getName());
      }

      此方法注意此方法不能获取父类的字段。
    • 获取制定字段

      Field idField = aClass.getDeclaredField("id");

      *字段赋值和获取

      idField.set(people,33);
      idField.get(people);

      但是对于私有属性要写:

      idField.setAccessible(true);

      对于静态变量,因为没有对象,所以在赋值或者获取的时候,传入null即可。

9. 注解

对于注解不熟悉的同学请参考:
注解
* 类上的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CAnnotation {
    String name() default "张三";

    int age() default 10;
}
@CAnnotation(name = "王五",age = 12)
public class People{
}

拿该类上的注解

 CAnnotation annotation = aClass.getAnnotation(CAnnotation.class);
            if (annotation != null) {
                System.out.println("类上的注解"+annotation.age()+annotation.name());
            }

该类上所有的注解

Annotation[] annotations = aClass.getAnnotations();
  • 字段上的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FAnnotaion {
    String name() default "张三";

}
  @FAnnotaion(name = "xiaoming")
   public String name;

拿注解并赋值

 Field field = aClass.getField("name");
            FAnnotaion fannotation = field.getAnnotation(FAnnotaion.class);
            if (fannotation != null) {
                System.out.println(fannotation.name());
                field.set(people,fannotation.name());
            }
  • 方法的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MAnnotaion {

    String name() default "张三";
}
 @MAnnotaion(name = "sdfsdf")
    public void setName(String name) {
        this.name = name;
    }

拿注解并调用方法

 Method msetName = aClass.getMethod("setName", String.class);
            MAnnotaion mannotion = msetName.getAnnotation(MAnnotaion.class);
            if (mannotion != null) {
                msetName.invoke(people,mannotion.name());
            }

方法参数的注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PAnnotion {
    String name() default "张三";
}

用法

 public void setName(@PAnnotion(name = "hahah") String name) {
        this.name = name;
    }

调用

 /**
             * 第一个坐标代表有几个参数,第二个坐标代表一个参数有几个注解
             */
            Annotation[][] parameterAnnotations = msetName.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                Annotation[] parameterAnnotation = parameterAnnotations[i];
                for (int j = 0; j < parameterAnnotation.length; j++) {
                    if (parameterAnnotation[j] != null) {

                        if (parameterAnnotation[j] instanceof PAnnotion) {
                            pAnnotion = ((PAnnotion) parameterAnnotation[j]);
                            msetName.invoke(people, pAnnotion.name());
                            System.out.println("参数注解"+people.getName());
                        }
                    }
                }
            }

源码传送门

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值