文档章节

OpenJDK源码研究笔记(十三):Javac编译过程中的上下文容器(Context)、单例(Singleton)和延迟创建(LazyCreation)3种模式

FansUnion
 FansUnion
发布于 2015/05/03 01:29
字数 1039
阅读 2
收藏 0

在阅读Javac源码的过程中,发现一个上下文对象Context。

这个对象用来确保一次编译过程中的用到的类都只有一个实例,即实现我们经常提到的“单例模式”。

今天,特意对这个上下文容器进行解析,主要是讲解上下文容器、单例模式和延迟创建。

通过对OpenJDK和Javac源码的一点点解析,希望自己能够搞懂JDK和Javac的实现。

1.OpenJDK源码示例

a.上下文容器

public class com.sun.tools.javac.util.Context{

    //构造函数

    public Context() {
    }


    /**
     * 客户端创建这个类的实例,用作key。
     */
    public static class Key<T> {
        // 注意: 我们从Object继承身份相等性。we inherit identity equality from Object.
    }

    /**
     * 客户端注册这个工厂,实现实例的延迟创建。
     */
    public static interface Factory<T> {
        T make(Context c);
    };
    public <T> void put(Key<T> key, Factory<T> fac) {
        ft.put(key, fac); // cannot be duplicate if unique in ht
    }
    public <T> T get(Key<T> key) {
        Object o = ht.get(key);
        //如果通过key获取的是一个工厂类
        if (o instanceof Factory<?>) {
            Factory<?> fac = (Factory<?>) o;
            //根据工厂创建对象
            o = fac.make(this);
        }

        //类型转换
        return Context.<T> uncheckedCast(o);
    }

}


b.单例模式

public class com.sun.tools.javac.util.Options{

//如果上下文容器中存在,直接返回;否则,新创建一个对象,并且放到Context中。

public static Options instance(Context context) {
        Options instance = context.get(optionsKey);
        if (instance == null){
            instance = new Options(context);
        }
        return instance;
    }

}


c.延迟创建

 

public static final Context.Key<Log> logKey = new Context.Key<Log>();

 context.put(logKey, new Context.Factory<Log>();


这个时候,put放进去的不是一个对象,而是一个工厂。

当我们通过context.get(logKey)去获得对象的时候,先是获得了工厂对象,然后再根据工厂去创建相应的对象。

2.代码示例

a.上下文容器

package sample.context;

import java.util.HashMap;
import java.util.Map;

public class Context {

    private Map<Key<?>, Object> map = new HashMap<Key<?>, Object>();

    public Context() {
    }

    /**
     * 客户端创建这个类的实例,用作key。
     */
    public static class Key<T> {
        // 注意: 我们从Object继承身份相等性。
    }

    /**
     * 客户端注册这个工厂,实现实例的延迟创建。
     */
    public static interface Factory<T> {
        T make(Context c);
    };

    public <T> void put(Key<T> key, Factory<T> fac) {
        map.put(key, fac);
    }

    public <T> void put(Key<T> key, T data) {
        map.put(key, data);
    }

    public <T> T get(Key<T> key) {
        Object o = map.get(key);
        // 如果通过key获取的是一个工厂类
        if (o instanceof Factory<?>) {
            Factory<?> fac = (Factory<?>) o;
            // 根据工厂创建对象
            o = fac.make(this);
            System.out.println("根据工厂延迟创建了一个对象,工厂类型:" + o.getClass());
        } else {
            if (o != null) {
                System.out.println("直接获得了一个对象,对象类型:" + o.getClass());
            }
        }

        return (T) o;
    }

}


b.用户

package sample.context;

import sample.context.Context.Key;

public class User {

    protected static final Key<User> userKey = new Key<User>();

    private String name;

    public User(Context context) {
        context.put(userKey, this);
    }

    public static User instanceOf(Context context) {
        User user = context.get(userKey);
        if (user == null) {
            user = new User(context);
        }
        return user;

    }

    public String getName() {
        return name;
    }

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

    public String toString() {
        return "User";
    }

}


c.博客

package sample.context;

import sample.context.Context.Key;

public class Blog {
    protected static final Key<Blog> blogKey = new Key<Blog>();

    private String title;

    public Blog(Context context) {
        context.put(blogKey, this);
    }

    public static Blog instanceOf(Context context) {
        Blog blog = context.get(blogKey);
        if (blog == null) {
            return new Blog(context);
        }
        return blog;

    }

    public static void preRegister(Context context) {
        context.put(blogKey, new Context.Factory<Blog>() {
            public Blog make(Context c) {
                return new Blog(c);
            }
        });
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String toString() {
        return "Blog";
    }

}


d.应用例子

package sample.context;

/**
 * OpenJDK源码研究笔记(十三):Javac编译过程中的上下文容器(Context)、单例(Singleton)和延迟创建(LazyCreation)3种模式
 *
 * @author LeiWen@FansUnion.cn
 *
 */
public class ContextExample {

    public static void main(String[] args) {
        // 构造上下文容器
        Context context = new Context();
        // 预注册一个Blog工厂
        Blog.preRegister(context);
        // 获得一个User对象
        User user = User.instanceOf(context);
        user.setName("FansUnion");

        User user2 = User.instanceOf(context);
        //验证是否为单例
        if (user == user2) {
            System.out.println("user和user2是同一个对象,name:" + user2.getName());
        }
        // 获得一个Blog对象
        Blog blog = Blog.instanceOf(context);

    }

}


e.程序输出

直接获得了一个对象,对象类型:class sample.context.User
user和user2是同一个对象,name:FansUnion
根据工厂延迟创建了一个对象,工厂类型:class sample.context.Blog

相关阅读

我的CSDN博客专栏  OpenJDK源码研究笔记

OpenJDK源码研究过程中整理的学习笔记。 OpenJDK是GPL许可(GPL-licensed)的Java平台的开源实现。

原文参见http://FansUnion.cn/articles/3099(小雷网-FansUnion.cn)

© 著作权归作者所有

FansUnion
粉丝 60
博文 858
码字总数 825464
作品 0
丰台
高级程序员
私信 提问
CentOS为OpenJDK设置系统变量

转载著名出处:http://www.crazyphper.com/?p=2328 今天在安装phpstorm时,提示无法找到JAVA_HOME,才想起还没有安装JDK呢!那就来试试linux上的openJDK吧! Centos自带OpenJDK的Runtime(JRE...

konakona
2012/09/22
10.4K
2
还没用上 JDK 11吧,JDK 12 早期访问构建版使用

JDK 更新速度快的飞起,JDK 12 早期访问构建版已发布,你现在用到了第几版本? 本周Java的最大新闻可能是JDK 11的正式发布。不过在 6 月底,JDK 11 就已经进入了 Rampdown Phase One 阶段,这...

关注公众号_搜云库_每天更新
2018/09/27
0
1
openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss
2018/06/18
0
0
ubuntu编译并调试OpenJDK8源码实践

最近打算研究一下 jvm 相关的知识,所以想编译 OpenJDK 的源码,一直在Mac上面编译,但是总是出现莫名其妙的error,还找不到有效的解决方式,所以还是进入 Ubuntu 下面进行编译,虽然中间也出...

ostracod
2018/06/29
0
0
centos7 yum安装java运行环境,初识hadoop

安装java运行环境 1.实验机相关信息: [root@node2 ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) [root@node2 ~]# uname -r 3.10.0-327.el7.x86_6 2.配置epel源,以y......

smile68
2018/04/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊中国的通信行业:从“七国八制”到“中华”脊梁

本期文章和大家一起来聊一聊我曾经从事过的通信行业吧。最近各方面信息的泛滥,包括和华为的同学聊天,自己确实也感慨颇多。想想我自己本科主修通信工程,研究生再修信息与通信工程,从本科开...

CodeSheep
56分钟前
4
0
MDK:ARM M451M:exceed the range of code meory, continue to erase or not?

问题: 代码空间超限 几天前就遇到:exceed the range of code meory, continue to erase or not? 如下所示: 解决过程 开始以为中MDK软件的128KB限制,如是就不能生成HEX文件,应该链接时有提...

SamXIAO
今天
1
1
OSChina 周六乱弹 —— 因违反《中华人民共和国治安管理处罚法》第四十四条之规定

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :#今日歌曲推荐# 惊艳分享谷微的单曲《安守本份》(@网易云音乐) 《安守本份》- 谷微 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
322
8
Angular 英雄编辑器

应用程序现在有了基本的标题。 接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。 创建英雄组件 使用 Angular CLI 创建一个名为 heroes 的新组件。 ng gener...

honeymoose
今天
8
0
Kernel DMA

为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设...

yepanl
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部