Java 反射机制

作者: 未见哥哥 | 来源:发表于2019-04-03 17:37 被阅读260次
Java 反射机制

反射

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法或者属性。

反射机制主要提供以下几种功能

  • 在运行时构造任意类的对象。
  • 在运行时获取一个类的成员变量和方法。
  • 在运行时调用某个类对象的方法和成员变量。

在 Java 这个面向对象的语言中,万事万物皆为对象,使用什么来用于表示每一个 Java 类呢?在 Java 中使用 Class 这个类来表示所有的类。

Class 类

Class 类用于描述一个类的所有信息,例如构造方法,成员变量,成员方法等,类中每一个元素都有一个对应的类来表示。

  • 成员变量使用 Field 表示
  • 成员方法使用 Method 表示
  • 构造函数使用 Constructor 表示
  • ...

Class 类常用的方法:

Class 类

反射的入口的第一步是首先获取类的 Class 对象

获取一个类的 Class 对象有三种方式

  • 通过类名.class 获取

  • 通过对象.getClass() 获取

  • 通过 Class.forName() 获取

Class<Person> personClass = Person.class;
Class<? extends Person> personClass2 = new Person().getClass();
//forName 还不知道具体的类
Class<?> personClass3 = Class.forName("com.example.reflect.Person");

Constructor

Constructor 代表某个类的构造方法。

获取构造类的 Construtor 对象

  • 获取类中所有的构造方法
Constructor<String>[] constructors = (Constructor<String>[]) Class.forName("java.lang.String").getConstructors();
System.out.println("---------------获取所有的构造器start---------------");
for (Constructor<?> constructor : constructors) {
    System.out.println(constructor);
}
System.out.println("---------------获取所有的构造器end---------------");
  • 获取类的某个构造方法
Constructor<byte[]> constructor = (Constructor<byte[]>) Class.forName("java.lang.String").getConstructor(byte[].class);

创建对象

  • 正常创建实例对象
String str = new String(new StringBuffer("Hello World"));
  • 反射方式创建对象的两种方式

方式一:

//拿到指定构造器Constructor对象
Constructor<?> constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//通过 newInstance 创建对象
String str = (String) constructor.newInstance( new StringBuffer("Hello World"));

方式二:

该方法Class.forName内部会得到无参构造方法 Constroctor 实例,然后调用 Constructor.newInstance(),它内部会使用缓存机制来保存默认构造方法的示例对象。

Class.forName("java.lang.String").newInstance();

通过源码分析 Class.forName("java.lang.String").newInstance()内部是如何实现缓存的?

在 ① 处判断缓存的 cachedConstructor是否为空,它表示的是当前对象的一个无参构造,第一次使用时肯定是为空,那么通过 ② getConstructor0 得到一个 Constructor 无参实例。如果缓存的 cachedConstructor不为空,那么直接通过③直接创建对象 Constructor.newInstance()。

如果一个类没有无参构造,而直接去调用 Class.forName(...).newInstance() 则会报错。

//Class.java
public T newInstance()
    throws InstantiationException, IllegalAccessException
{
    //①
    if (cachedConstructor == null) {
        //代码省略...
        try {
            Class<?>[] empty = {};
            //② 
            final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
            cachedConstructor = c;
        } catch (NoSuchMethodException e) {
            throw (InstantiationException)
                new InstantiationException(getName()).initCause(e);
        }
    }
    Constructor<T> tmpConstructor = cachedConstructor;
    //代码省略...
    // Run constructor
    try {
        //②创建对象
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
}

下面是具体 Class.newInstance()内部调用逻辑

Class.newInstance()内部调用逻辑

Method

Method 代表某个类的一个方法。

获取 Method 的方式有两种

获取当前类和父类中所有的 public 方法。

public Method getMethod(String name, Class<?>... parameterTypes)

获取本类中所有方法,不包括父类方法。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

注意:通过 invoke 方法调用私有方法时,需要设置 method.setAccessible(true);

实践:通过反射调用 DoSomething 的 main 方法。

//DoSomething.java
package com.example.reflect;
public class DoSomething {
    public static void main(String[] args) {
        System.out.println(DoSomething.class.getSimpleName());
        for (String arg : args) {
            System.out.println(arg);
        }
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }
}
  • 获取表示 main 方法的 Method 对象
Class<?> clazz = Class.forName("com.example.reflect.DoSomething");
//public static void main(String[] args)
Method mainMethod = clazz.getMethod("main", String[].class);
  • 反射调用 main 方法
//调用 main 方法。
mainMethod.invoke(null, new Object[]{new String[]{"Hello"}});

这里需要注意一点 JDK1.4 和 JDk1.5 关于 method#invoke 方法的区别:

JDK1.5 public Object invoke(Object obj,Object...args)
JDK1.4 public Object invoke(Object obj,Object[] args)

如果是以一个字符串数组传入给 invoke 方法,那么 javac 会按照哪种语法给处理呢?
因为 JDK1.5 需要兼容 JDk1.4 因此会按照 JDK1.4 的语法来执行。
表示传入的 method 的参数 new Object[]{},这个数组存放的元素才是真正我们要传入的参数,那么这个数组需要存放的是 String[]{"","",""}数组
所以最终表示为:method.invoke(null,new Object[]{new String[]{"Hello"}})

Field

Field 表示一个类的某一个成员变量。

public class Person {

    private int age;
    private String name;


    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

实践:反射获取一个对象的成员变量和给该成员变量赋值。

//Field 类:代表某个类的一个成员变量
Class<Person> clazz = (Class<Person>) Class.forName("com.example.reflect.Person");
Field ageField = clazz.getDeclaredField("age");
//私有属性需要设置ageField.setAccessible(true)
ageField.setAccessible(true);
Person person = clazz.newInstance();
person.setAge(11);
//获取 age 属性的值
int age = (int) ageField.get(person);
System.out.println(age);
//给 age 属性赋值
ageField.set(person,12);
int ageValue = (int) ageField.get(person);
System.out.println(ageValue);

记录于2019年4月3日

相关文章

  • Java反射机制入门

    Java反射机制入门 一、什么是反射 JAVA反射机制(The JAVA reflection mechanism...

  • 反射之一

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • 反射之二

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • Java基础之反射

    Java基础之—反射(非常重要)Java中反射机制详解Java进阶之reflection(反射机制)——反射概念与...

  • 反射之三

    总结内容源自以下文章 粗浅看java反射机制 反射机制应用实践 谈谈java反射机制 Java Reflectio...

  • java反射机制

    java的反射机制 1 JAVA的反射机制是什么?反射机制能做什么?反射机制的优点与缺点2 认识 Class...

  • Java中反射的用途

    Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对大家以后学习框...

  • Chapter 13 . 反射机制

    阅读原文 Chapter 13 . 反射机制 13.1 Java反射机制研究及应用 Java Reflection...

  • 详解Java反射机制(Reflection)

    详解Java反射机制(Reflection) 反射机制的作用 JAVA反射机制是在运行状态中,对于任意一个类,都能...

  • Java 反射机制

    [1]. java反射详解[2]. Java Reflection(反射机制)详解[3]. 深入理解Java类型...

网友评论

    本文标题:Java 反射机制

    本文链接:https://www.haomeiwen.com/subject/pymzbqtx.html