JDK源码分析 反射

说明

对于JDK源码分析的文章,仅仅记录我认为重要的地方。源码的细节实在太多,不可能面面俱到地写清每个逻辑。所以我的JDK源码分析,着重在JDK的体系架构层面,具体源码可以参考:http://www.cnblogs.com/skywang12345/category/455711.html

反射简介

在运行状态中,我们可以根据“类的部分已知的信息”来还原“类的全部的信息”。

类的部分已知的信息:

  • 类名
  • 类的对象

类的全部信息

  • 属性
  • 方法
  • 继承关系
  • Annotation注解

根据类名构造类

代码示例

User类

public class User implements Serializable{
    private static final long serialVersionUID = 1510634274152200118L;
    
    private int id;
    private String passWord;
    
    public User() {
        System.out.println("Create user... ");
    }
    
    public User(int id, String passWord) {
        this.id = id;
        this.passWord = passWord;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getPassWord() {
        return passWord;
    }
    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", passWord=" + passWord + "]";
    }
}

根据类名,构造类的代码:

@Test
public void testReflection() throws Exception {
    Class<?> clazz = Class.forName("test.User");
    User user = (User) clazz.newInstance();
    System.out.println("user = " + user);
}

输出:

Create user... 
user = User [id=0, passWord=null]

获取 class 对象

@Test
public void testClazz() throws Exception {
    Class<?> clazz1 = Class.forName("test.User");
    Class clazz2 = User.class;
    Class clazz3 = new User().getClass();
    System.out.println("clazz1: " + clazz1);
    System.out.println("clazz2: " + clazz2);
    System.out.println("clazz3: " + clazz3);
}

输出:

Create user... 
clazz1: class test.User
clazz2: class test.User
clazz3: class test.User

class 的 API

  • 构造函数
  • 成员方法
  • 成员变量
  • 类的其它信息(如注解、包名、类名、继承关系等等)

构造函数

// 获取“参数是parameterTypes”的public的构造函数
public Constructor    getConstructor(Class[] parameterTypes)
// 获取全部的public的构造函数
public Constructor[]    getConstructors()
// 获取“参数是parameterTypes”的,并且是类自身声明的构造函数,包含public、protected和private方法。
public Constructor    getDeclaredConstructor(Class[] parameterTypes)
// 获取类自身声明的全部的构造函数,包含public、protected和private方法。
public Constructor[]    getDeclaredConstructors()
// 如果这个类是“其它类的构造函数中的内部类”,调用getEnclosingConstructor()就是这个类所在的构造函数;若不存在,返回null。
public Constructor    getEnclosingConstructor()

测试:

@Test
public void testConstructor() throws Exception {
    Class<?> clazz = Class.forName("test.User");
    Constructor<?> constructor = clazz.getDeclaredConstructor(null);
    Object object1 = constructor.newInstance();
    System.out.println(object1);
    
    Constructor<?> constructor2 = clazz.getDeclaredConstructor(new Class[] {int.class, String.class});
    Object object2 = constructor2.newInstance(1, "123456");
    System.out.println(object2);
}

输出:

Create user... 
User [id=0, passWord=null]
User [id=1, passWord=123456]

可以调用默认的构造函数,也可以通过

clazz.getDeclaredConstructor(new Class[] {int.class, String.class})

来调用User的含参构造函数

public User(int id, String passWord) {
    this.id = id;
    this.passWord = passWord;
}

成员方法

// 获取“名称是name,参数是parameterTypes”的public的函数(包括从基类继承的、从接口实现的所有public函数)
public Method    getMethod(String name, Class[] parameterTypes)
// 获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)
public Method[]    getMethods()
// 获取“名称是name,参数是parameterTypes”,并且是类自身声明的函数,包含public、protected和private方法。
public Method    getDeclaredMethod(String name, Class[] parameterTypes)
// 获取全部的类自身声明的函数,包含public、protected和private方法。
public Method[]    getDeclaredMethods()
// 如果这个类是“其它类中某个方法的内部类”,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。
public Method    getEnclosingMethod()

可以判断类中是否含有某个方法,也可以调用类中的任何一个方法(包括私有方法)。

@Test
public void testMethod() throws Exception {
    Class<?> clazz = Class.forName("test.User");
    
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method method : declaredMethods) {
        System.out.println(method);
    }
    System.out.println();
    
    Method printInfo = clazz.getDeclaredMethod("printInfo", new Class[]{});
    User user = (User) clazz.newInstance();
    printInfo.invoke(user, null);
}

输出:

Create user... 
public java.lang.String test.User.toString()
public int test.User.getId()
public void test.User.printInfo()
public void test.User.setId(int)
public void test.User.setPassWord(java.lang.String)
public java.lang.String test.User.getPassWord()
User [id=0, passWord=null]

成员变量

// 获取“名称是name”的public的成员变量(包括从基类继承的、从接口实现的所有public成员变量)
public Field    getField(String name)
// 获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量)
public Field[]    getFields()
// 获取“名称是name”,并且是类自身声明的成员变量,包含public、protected和private成员变量。
public Field    getDeclaredField(String name)
// 获取全部的类自身声明的成员变量,包含public、protected和private成员变量。
public Field[]    getDeclaredFields()
@Test
public void testField() throws Exception {
    Class<?> clazz = Class.forName("test.User");
    
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        System.out.println(field);
    }
    
    // 创建并通过反射,修改一个 private 变量 id
    User user = (User) clazz.newInstance();
    Field field = clazz.getDeclaredField("id");
    field.setAccessible(true);
    field.set(user, 123);
    user.printInfo();
}

输出:

private static final long test.User.serialVersionUID
private int test.User.id
private java.lang.String test.User.passWord
Create user... 
User [id=123, passWord=null]

我们可以看到,User中对于id的定义:

private int id;

而在测试用例中,可以获取对象中私有的id变量,并直接修改内容,最终输出的id=123。

类的其它信息

注解

// 获取类的"annotationClass"类型的注解 (包括从基类继承的、从接口实现的所有public成员变量)
public Annotation<A>    getAnnotation(Class annotationClass)
// 获取类的全部注解 (包括从基类继承的、从接口实现的所有public成员变量)
public Annotation[]    getAnnotations()
// 获取类自身声明的全部注解 (包含public、protected和private成员变量)
public Annotation[]    getDeclaredAnnotations()

“父类”和“接口”相关的API

// 获取实现的全部接口
public Type[]    getGenericInterfaces()
// 获取父类
public Type    getGenericSuperclass()

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微信公众号:Java团长

Java反射初探 ——“当类也学会照镜子”

镜子(反射机制)照出(反射)了人的全貌(类的全方位的信息,例如方法,成员变量和构造器等的相关信息)

8230
来自专栏开发之途

重拾Java(9)-Lambda表达式

12920
来自专栏代码拾遗

反射基础之Class

Java中每个类型要么是引用类型,要么是原生类型。类,枚举,数组(他们都继承于java.lang.Object)和接口都是引用类型。例如:java.lang.S...

11540
来自专栏coding for love

JS入门难点解析

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

11120
来自专栏C/C++基础

C++11 变参模板

版权声明:感谢您对博文的关注!校招与社招,有需要内推腾讯的可以QQ(1589276509)or 微信(louislvlv)联系我哈,期待您的加入。 ...

42920
来自专栏C++

python笔记:#005#算数运算符

17920
来自专栏java达人

java枚举类型enum的使用

最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public final static 类型来定义呢?以...

244100
来自专栏编程坑太多

JS实现运算符重载

50120
来自专栏coding for love

JS常用方法整理-遍历对象

JS中经常需要对对象的属性进行遍历,下面我们来总结一下JS遍历对象属性的几种方法。

16720
来自专栏DT乱“码”

Java 枚举类型enum 的使用

Java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public fin...

30790

扫码关注云+社区

领取腾讯云代金券