在阅读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)

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

  1. OpenJDK源码研究笔记(十五):吐槽JDK中的10个富有争议的设计

    前14篇文章,分享了JDK中值得学习和借鉴的编码和设计方法. 每个硬币都是有两面的.Every coin has two sides. 当然,JDK中也有很多值得改进或者说富有争议的设计. 本篇,就来 ...

  2. OpenJDK源码研究笔记(十六):在Java中使用JavaScript脚本语言

    友情提示 本文主要参考了51CTO上的一篇文章,代码经过自己的模仿和整理,还算凑合. 本文中的代码注释比较多,不再过多解释. 更多用法,还是得看JDK的API或者看原文http://developer ...

  3. OpenJDK源码研究笔记(七)–Java字节码文件(.class)的结构

    最近在看OpenJDK源码的过程中,顺便看了Java编译器(javac)的源码. 为了理解javac的源码,需要先搞懂Java字节码文件(.class)的结构. 于是,我就认真看了下OpenJDK中J ...

  4. OpenJDK源码研究笔记(十):枚举的高级用法,枚举实现接口,竟是别有洞天

    在研究OpenJDK,Java编译器javac源码的过程中,发现以下代码. 顿时发现枚举类竟然也有如此"高端大气上档次"的用法. 沙场点兵(用法源码) com.sun.tools. ...

  5. [置顶] OpenJDK源码研究笔记(九)-可恨却又可亲的的异常(NullPointerException)

    可恨的异常 程序开发过程中,最讨厌异常了. 异常代表着程序出了问题,一旦出现,控制台会出现一屏又一屏的堆栈错误信息. 看着就让人心烦. 对于一个新人来讲,遇到异常经常会压力大,手忙脚乱,心生畏惧. 可 ...

  6. OpenJDK源码研究笔记(一)-参数检查&抛出带关键错误提示信息的异常

    OpenJDK源码研究笔记系列文章,是我在阅读OpenJDK7源码的过程中的一些体会.收获.看法. 把研究过程中的成长和收获一点点地整理出来,是对自己研究学习的一个小结,也有可能给学习Java的一些同 ...

  7. OpenJDK源码研究笔记(九)-可恨却又可亲的的异常(NullPointerException)

    可恨的异常 程序开发过程中,最讨厌异常了. 异常代表着程序出了问题,一旦出现,控制台会出现一屏又一屏的堆栈错误信息. 看着就让人心烦. 对于一个新人来讲,遇到异常经常会压力大,手忙脚乱,心生畏惧. 可 ...

  8. OpenJDK源码研究笔记(八)-详细解析如何读取Java字节码文件(.class)

    在上一篇OpenJDK源码研究笔记(七)–Java字节码文件(.class)的结构中,我们大致了解了Java字节码文件的结构. 本篇详细地介绍了如何读取.class文件的大部分细节. 1.构造文件  ...

  9. OpenJDK源码研究笔记(十四):三种经典的设计方法,接口,接口-抽象类-具体实现类,接口-具体实现类

    在研究OpenJDK源码过程中,我发现常用的设计方法就是2种:接口,接口-抽象类-具体实现类 . 在一些其它开源框架和业务开发中,经常存在着第3种设计,接口-具体实现类. 1.只有接口,没有实现类. ...

随机推荐

  1. openSessionInView的使用原理及性能分析

    看到好多项目中用到了openSessionInView,这种做法无非是开发方便,能够在JSP页面中操作数据库层方面的业务. 下边说下openSessionInView的使用方法及性能问题. 使用: 1 ...

  2. SQL Server 运行计划操作符具体解释(3)——计算标量(Compute Scalar)

    接上文:SQL Server 运行计划操作符详细解释(2)--串联(Concatenation ) 前言: 前面两篇文章介绍了关于串联(Concatenation)和断言(Assert)操作符,本文介 ...

  3. bzoj3158&3275: 千钧一发(最小割)

    3158: 千钧一发 题目:传送门 题解: 这是一道很好的题啊...极力推荐 细看题目:要求一个最大价值,那么我们可以转换成求损失的价值最小 那很明显就是最小割的经典题目啊?! 但是这里两个子集的分化 ...

  4. CSRF的原理

    CSRF是什么? (Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click ...

  5. 区间dp学习笔记

    怎么办,膜你赛要挂惨了,下午我还在学区间\(dp\)! 不管怎么样,计划不能打乱\(4\)不\(4\).. 区间dp 模板 为啥我一开始就先弄模板呢?因为这东西看模板就能看懂... for(int i ...

  6. 为Activity生成桌面快捷方式

    有时候如果想让我们的应用在桌面上创建多个快捷方式,我们可以在Manifest.xml文件中对相应的activity进行声明. <application android:icon="@d ...

  7. 使用ShareSDK分享-图片的链接

    微信中使用ShareSDK分享,需要申请微信开放平台账号,并且以微信中的声明的应用签名打包程序. private void showShare(String url, String title, St ...

  8. mysql读写分离的解决方案

    来源于网上整理 http://yanwt.iteye.com/blog/1460780 现有三种解决方式实现mysql读写分离 1 程序修改mysql操作类 优点:直接和数据库通信,简单快捷的读写分离 ...

  9. PostgreSQL 系统表

    postgres=# \d pg_class      Table "pg_catalog.pg_class"     Column     |   Type    | Modif ...

  10. 手把手教你进行R语言的安装及安装过程中相关问题解决方案

    这篇文章旨在为R语言的新手铺砖引路,行文相对基础,希望对在R语言安装上有问题的小伙伴提供帮助和指引.一.什么是 R 语言R 编程语言被广泛应用在统计科学和商业领域. 在各种编程语言排名中 R 语言的排 ...