Java反射机制总结

导读:本篇文章讲解 Java反射机制总结,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

前言

有两种方法获取Java运行时的类型信息,分别是:

  • 传统的RTTI(Run-Time Type Identification)
  • Java反射机制

了解学习RTTI:浅谈 RTTI

Java反射机制的定义

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

Java类的三个阶段:
在这里插入图片描述
Java文件编译成字节码文件后,由AppClassLoader(JVM内置的面向用户的类加载器)加载类的类型信息并保存到JVM管理内存的方法区,并创建一个Class对象保存到堆中。当我们需要new一个对象时,就是根据Class对象并调用其构造方法来创建实例。

Class类

Class类是Java反射机制中的最重要的类,每装载一个新类时,JVM就会在Java堆中,创建一个Class的实例,这个实例就代表了这个新类的类型,通过这个实例可以获取这个类型的信息:包括属性,方法,注解等。

获取Class对象的方式

下面是获取Class对象的三种方式:

  1. Class.forName(“全限定名”):将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
  2. 类名.class:通过类名的属性class获取。多用于参数的传递。
  3. 对象.getClass():getClass()方法在Object类中定义着。多用于对象的获取字节码的方式。

下面使用分别使用这三种方式来获取Class对象:
创建一个Person类用作测试:

public class Person {
  private int id;
  private String name;
  //省略getter,setter,toString,构造方法

测试三种方式获取的Class对象是否是同一个

/*
获取Class对象的方式
 */
public class reflectDemo01 {
  public static void main(String[] args) throws ClassNotFoundException {
    //1.Class.forName(“全限定名”),将字节码文件加载到内存
    Class c1 = Class.forName("learn.bean.Person");
    System.out.println(c1);
    //2.类名.class,通过类名的属性class获取
    Class c2 = Person.class;
    System.out.println(c2);
    //3.对象.getClass(),getClass方法在Object类中定义
    Class c3 = new Person().getClass();
    System.out.println(c3);
    //验证这三个Class对象是否相同
    System.out.println(c1==c2?(c1==c3?"yes":"no"):"no");//结果为yes
  }
}

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

Class对象的功能

一些常用的作用为获取的方法:

  1. 获取成员变量
- Field[] getFields() //获取所有public修饰的成员变量
- Field getField(String name)//获取指定名称的 public修饰的成员变量
- Field[] getDeclaredFields()//获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name) //获取指定名称的成员变量 

下面的命名规则都相似,带有“Declared”的方法才是获取所有声明的。

  1. 获取构造方法们
- Constructor<?>[] getConstructors()  
- Constructor<T> getConstructor(<?>... parameterTypes)  
- Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  
- Constructor<?>[] getDeclaredConstructors()  
  1. 获取成员方法们:
- Method[] getMethods()  
- Method getMethod(String name,<?>... parameterTypes)  
- Method[] getDeclaredMethods()  
- Method getDeclaredMethod(String name,<?>... parameterTypes)  
  1. 获取全类名
- String getName()  
  1. 获取类加载器
- ClassLoader getClassLoader()
  1. 获取资源
- URL getResource()
  1. 获取父类
- Class getSuperclass()
  1. 获取修饰符
    字段,方法,构造器同样能使用这个方法
getModifiers()
//返回修饰该方法对象修饰符的整数形式,使用 Modifier 类对其进行解码
String Modifier.toString(clazz.getModifiers())

利用Class类来创建实例

Object newInstance()//利用Class类来创建实例
    Class cls = Person.class;
    Object o = cls.newInstance();

下面结合上面的Class类来学习Field类,Method类,Constructor类。

Field类

Field用于存放类的成员变量,下面是Field类的一些相关的操作:

  1. 设置值: void set(Object obj, Object value)
  2. 获取值: get(Object obj)
  3. 忽略访问权限修饰符的安全检查,用于访问被访问权限符限制的成员变量:setAccessible(true)。Constructor类,Method类同样能使用 setAccessible(true)
  4. 明确属性可通过以下方法获取,那个基本类型都有对应的方法
xxx.getDouble(Object o);
xxx.getFloat(Object o);
xxx.getBoolean(Object o);
xxx.getChar(Object o);

下面来使用上面的方法:

public class reflectDemo02 {
  public static void main(String[] args) throws Exception {
    Class cls = Person.class;
    Field[] fields = cls.getFields();
    //因为getFields()修饰的成员变量所以无输出
    for (Field field : fields) {
      System.out.println(field);
    }
    System.out.println("获取private修饰的变量");
    Field[] declaredFields = cls.getDeclaredFields();
    for (Field declaredField : declaredFields) {
      System.out.println(declaredField);
    }
    System.out.println("修改private修饰的变量");
    Person person = new Person(1, "张三");
    //获取指定的属性
    Field nameField = cls.getDeclaredField("name");
    //忽略访问权限
    nameField.setAccessible(true);
    //更改private修饰的变量
    nameField.set(person,"法外狂徒");
    System.out.println(nameField.get(person));
  }
}

程序运行结果:

获取所有的变量
private int learn.bean.Person.id
private java.lang.String learn.bean.Person.name
修改private修饰的变量
张三
法外狂徒

Constructor类

Constructor用于存放类的构造方法

  • 创建对象: T newInstance(Object... initargs)
    如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
public class reflectDemo03 {
  public static void main(String[] args) throws Exception {
    Class personClass = Person.class;
    //获取无参构造函数来创建对象
    Constructor c1 = personClass.getConstructor();
    Person p1 = (Person) c1.newInstance();
    System.out.println(p1);
    //获取有参构造函数
    Constructor c2 = personClass.getConstructor(int.class, String.class);
    Object p2 = c2.newInstance(1, "李四");
    System.out.println(p2);
  }
}

程序运行结果:

Person{id=0, name='null'}
Person{id=1, name='李四'}

Method类

Method用于存放类的方法对象,下面是Method的一些方法:

  • 执行方法: Object invoke(Object obj, Object... args) ,args为参数
  • 获取方法名称:String getName(String name)
  • 获取某个方法:getMethod(String name,Object... args),参数分别是方法名和参数类型
  • 获取方法的参数类型数组:Class<?>[] getParameterTypes()
  • 获取方法的返回值类型:Class<?> getReturnType()

在Person类中添加几个方法:

  public void add(){
    System.out.println("nothing");
  }
  public void add(String s){
    System.out.println(s);
  }
  public void add(String s,String to){
    System.out.println("add "+s+"to "+to);
  }

分别调用这几个方法:

public class reflectDemo04 {
  public static void main(String[] args) throws Exception {
    Class personClass = Person.class;
    Person person = new Person();
    Method m1 = personClass.getMethod("add");
    m1.invoke(person);
    Method m2 = personClass.getMethod("add", String.class);
    m2.invoke(person,"had add");
    Method m3 = personClass.getMethod("add",new Class[]{String.class,String.class} );
    m3.invoke(person,"something","wallet");
  }
}

程序运行结果:

nothing
had add
add somethingto wallet

注解

无论是自定义注解还是预定义的注解,注解的元注解中的@Retention的值必须是RetentionPolicy.RUNTIME才能获取到。

@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。

getAnnotation(Class<T> annotationClass)
//类上
Annotation annotation = clazz.getAnnotation(Bean.class);
Annotation[] annotations = clazz.getAnnotations();

//字段上
Annotation annotation = field.getAnnotation(Bean.class);
Annotation[] annotations = field.getAnnotations();

//方法上
Annotation annotation = method.getAnnotation(Bean.class);
Annotation[] annotations = method.getAnnotations();

一个应用Java反射机制的案例:
应用Java反射模拟实现Spring的IoC容器

参考:
B站的视频教程:【黑马程序员-Java语言高级部分9.2】Java 反射

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/44324.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!