java.lang.Boolean源码研究

作者研究JDK 1.5 b2源码,以Boolean类为例,剖析其为immutable class,有两种状态实例可共享,提供静态变量和静态工厂方法节省内存与开销。还提到Integer类缓存部分状态。同时分析了hashCode和equals方法,并给出类设计的总结建议。

http://blog.youkuaiyun.com/asklxf/archive/2004/06/23/23589.aspx

 

闲来无事,开始研究JDK源码(jdk 1.5 b2),先找了一个最简单的java.lang.Boolean开始解剖。

由于水平有限,难免有不少错误,还请大家指正!

首先我们剔除所有的方法和静态变量,Boolean的核心代码如下:

public final class Boolean implements java.io.Serializable,Comparable
{
    private final boolean value;
}

很明显,凡是成员变量都是final类型的,一定是immutable class,这个Boolean和String一样,一旦构造函数执行完毕,实例的状态就不能再改变了。

Boolean的构造函数有两个:

public Boolean(boolean value) {
    this.value = value;
}
public Boolean(String s) {
    this(toBoolean(s));
}

都很简单就不多说了。

另外注意到Boolean类实际上只有两种不同状态的实例:一个包装true,一个包装false,Boolean又是immutable class,所以在内存中相同状态的Boolean实例完全可以共享,不必用new创建很多实例。因此Boolean class还提供两个静态变量:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

这两个变量在Class Loader装载时就被实例化,并且申明为final,不能再指向其他实例。

提供这两个静态变量是为了让开发者直接使用这两个变量而不是每次都new一个Boolean,这样既节省内存又避免了创建一个新实例的时间开销。

因此,用
    Boolean b = Boolean.TRUE;

    Boolean b = new Boolean(true);
要好得多。

如果遇到下面的情况:
    Boolean b = new Boolean(var);
一定要根据一个boolean变量来创建Boolean实例怎么办?

推荐你使用Boolean提供的静态工厂方法:
    Boolean b = Boolean.valueOf(var);
这样就可以避免创建新的实例,不信看看valueOf()静态方法:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

这个静态工厂方法返回的仍然是两个静态变量TRUE和FALSE之一,而不是new一个Boolean出来。虽然Boolean非常简单,占用的内存也很少,但是一个复杂的类用new创建实例的开销可能非常大,而且,使用工厂方法可以方便的实现缓存实例,这对客户端是透明的。所以,能用工厂方法就不要用new。

和Boolean只有两种状态不同,Integer也是immutable class,但是状态上亿种,不可能用静态实例缓存所有状态。不过,SUN的工程师还是作了一点优化,Integer类缓存了-128到127这256个状态的Integer,如果使用Integer.valueOf(int i),传入的int范围正好在此内,就返回静态实例。

hashCode()方法很奇怪,两种Boolean的hash code分别是1231和1237。估计写Boolean.java的人对这两个数字有特别偏好:

public int hashCode() {
    return value ? 1231 : 1237;
}

equals()方法也很简单,只有Boolean类型的Object并且value相等才返true:

public boolean equals(Object obj) {
    if (obj instanceof Boolean) {
        return value == ((Boolean)obj).booleanValue();
    }
    return false;
}

顺便提一句:很多人写equals()总是在第一行写:
   if (obj==null) return false;
其实完全没有必要,因为如果obj==null,下一行的
    if (obj instanceof Type)
就肯定返回false,因为(null instanceof AnyType) = false。
详细内容请参考《Effective Java》第7条:Obey the general contract when overriding equals。

其他的方法如toString()就更简单了,只要稍微熟悉java的程序员相信都能写出来,我就不多说了。

总结

1.如果一个类只有有限的几种状态,考虑用几个final的静态变量来表示不同状态的实例。
例如编写一个Weekday类,状态只有7个,就不要让用户写new Weekday(1),直接提供Weekday.MONDAY即可。

2.要防止用户使用new生成实例,就取消public构造函数,用户要获得静态实例的引用有两个方法:如果申明public static var就可以直接访问,比如Boolean.TRUE,
第二个方法是通过静态工厂方法:Boolean.valueOf(?)

3.如果不提供public构造函数,让用户只能通过上面的方法获得静态变量的引用,还可以大大简化equals()方法:
    public boolean equals(Object obj) {
        return this==obj;
    }
可以直接用==比较引用,绝对没有问题,而且效率最高。

4.为什么JDK的Boolean没有实现上面第3点?因为那两个static变量TRUE和FALSE是在jdk 1.2以后才有的,由于前面的版本已经把构造函数申明为public,所以为了保持客户端代码能够不修改也在后面的版本中运行,只好继续提供public构造函数。

源码地址: https://pan.quark.cn/s/44d07910d9c3 标题“PCBM_LP_Viewer_V2009”标识了一种专门服务于PCB焊盘构建的软件应用,其版本号记录为2009。 该软件的核心用途在于辅助用户依照IPC7351国际标准来构建精密的焊盘构造,从而保障电子设备的稳定运行与生产品质。 PCB焊盘在印制电路板(Printed Circuit Board)的设计环节中占据核心地位,它承担着将电子元件的引脚与电路板进行电气连接的任务,确保两者间的连接性能。 焊盘的构造规划对焊接成效及整个电路板的运作效能具有直接影响。 在PCBM LP Viewer V2009软件中,使用者能够借助前沿的工具与参数设定,对焊盘的形态、尺寸、间距等要素进行个性化定制,以适应不同元器件与实际应用场景的要求。 IPC7351是由国际电子互连技术协会颁布的一套规范体系,它为电子产品的焊盘构建提供了统一的准则。 该规范体系涉及焊盘的尺寸规格、形态样式、热管理效能、机械耐力等多个维度,致力于提升焊盘的工作表现,减少生产过程中的瑕疵,增强电子产品在高温条件下的运行稳定性。 PCBM LP Viewer V2009软件严格遵循这一规范体系,使得设计人员能够便捷地构建出符合要求的焊盘,进而降低设计过程中的潜在风险,提高产品的整体质量。 操作PCBM LP Viewer V2009软件时,用户可以体验到以下特性:1. **图形化界面**:设计出直观且操作简便的界面,让焊盘设计流程更为顺畅。 2. **自定义参数**:能够根据元器件的具体规格对焊盘的尺寸、形态及孔径等参数进行灵活调整。 3. **自动计算**:软件能够自动核算出最优的焊盘尺寸,并按照IPC7351标准进行优化处理。 4. **...
代码下载地址: https://pan.quark.cn/s/5c2ff67041b8 在Android应用构建过程中,ListView被广泛采用作为一个展示大量数据条目的控件。 该控件具备出色的数据滚动处理能力,能够依据可用屏幕空间进行视图的动态加载与回收,以此实现内存的有效节省。 本文将详细阐述在Android平台如何构建一个ListView,并展示如何使其包含多种不同规格的条目。 1. **ListView的构成框架** ListView由一系列视图(条目)构成,每个视图对应数据集中的单个数据项。 这些条目通过适配器进行连接,适配器负责建立数据与视图的联系,并决定数据在ListView中的呈现方式。 2. **适配器的设计** 在Android系统里,通常选用BaseAdapter或者诸如ArrayAdapter、CursorAdapter等预设适配器来为ListView提供适配服务。 适配器需要重新设计以下功能: - `getCount()`: 返回ListView中包含的条目总数。 - `getItem(int position)`: 根据指定位置提取数据对象。 - `getItemId(int position)`: 获取指定位置条目的唯一标识符,一般返回位置索引值。 - `getView(int position, View convertView, ViewGroup parent)`: 为ListView生成或复用条目视图。 这是适配器的关键部分,依据数据对象和可复用的视图来填充条目内容。 3. **定制化适配器** 当ListView需要展示多种格式条目时,可以设计专属的适配器。 在`getView()`方法中,依据数据类型返回不同的视图样式。 可以通过检查数据对...
源码地址: https://pan.quark.cn/s/db95d26ecc9b “图书管理(借阅)”小程序作为一个入门级项目,主要运用C#编程语言进行开发,通过整合tabControl控件,致力于构建一个基础的图书管理平台,其核心功能在于图书信息的维护以及借阅流程的处理。 对于希望掌握C#及Windows应用程序开发的初学者而言,该项目提供了一个理想的实践环境。 在C#开发环境中,tabControl被视作一种常见的界面组件,它赋予了用户在不同标签页间切换以组织与展示信息的能力。 在此图书管理应用中,tabControl或许被用于划分不同的功能模块,诸如“图书目录”、“借阅详情”和“用户档案”等,使用者借此能够便捷地在各项功能间切换操作。 图书管理系统的数据管理通常依赖于数据库技术。 依据描述,该应用内含数据库文件,但作者指出“运行时需自行添加”,暗示数据库可能并未与项目原生集成,使用者必须手动进行配置或导入数据库方可正常运行。 数据库的构建可能包含书籍资料表(涵盖书名、作者、出版社、存量等字段)、借阅记录表(涉及借阅者、借阅时段、归还时段等字段)以及用户资料表(包含用户名、密码、联络方式等字段)。 在现实中的图书管理平台,为了完成借阅操作,开发者需设计逻辑以处理以下任务:1. **新增/修改书籍信息**:赋予管理员录入新书籍资料或更新现有书籍详情的权限。 2. **检索书籍**:使用者可依据书名、作者等条件查询书籍。 3. **执行借阅**:用户选择借书时,系统将核查库存,并修正借阅记录,同步减少相应书籍的存量。 4. **处理还书**:书籍归还时,系统将更新借阅记录并补充库存。 5. **用户账户管理**:涵盖注册、登录、调整个人资料等操作。 6. **权限管理**:区分普通...
Java 中,`java.lang.reflect.Proxy` 是一个非常重要的类,用于实现动态代理机制。它允许通过反射创建接口的代理实例,并将方法调用转发到指定的 `InvocationHandler`。以下是该类的部分核心源代码(以 OpenJDK 8 为例): ```java package java.lang.reflect; import java.security.ProtectionDomain; public class Proxy implements java.io.Serializable { private static final long serialVersionUID = -2222568059341876393L; protected InvocationHandler h; protected Proxy(InvocationHandler h) { this.h = h; } public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { // 返回为指定接口生成的代理类 return ProxyFactory.getProxyClass(loader, interfaces); } public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } final Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass(); return ProxyFactory.getProxyInstance(loader, interfaces, h, caller); } public static boolean isProxyClass(Class<?> cl) { return Proxy.class.isAssignableFrom(cl) && ProxyFactory.isProxyClass(cl); } public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { if (!isProxyClass(proxy.getClass())) { throw new IllegalArgumentException("Not a proxy instance"); } return ((Proxy) proxy).h; } } ``` 此外,`Proxy` 类依赖于内部的 `ProxyFactory` 来生成代理类。这部分逻辑涉及 JVM 的底层机制,并不完全在 Java 层面可见。生成的代理类会继承自 `Proxy` 并实现传入的接口,在调用接口方法时会委托给 `InvocationHandler` 处理。 ### 使用示例 以下是一个使用 `Proxy` 创建动态代理的简单示例: ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface HelloService { String sayHello(String name); } class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "Hello, " + name; } } class LoggingInvocationHandler implements InvocationHandler { private Object target; public LoggingInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method call: " + method.getName()); Object result = method.invoke(target, args); System.out.println("After method call: " + method.getName()); return result; } } public class ProxyExample { public static void main(String[] args) { HelloService realService = new HelloServiceImpl(); HelloService proxyService = (HelloService) Proxy.newProxyInstance( realService.getClass().getClassLoader(), realService.getClass().getInterfaces(), new LoggingInvocationHandler(realService) ); String response = proxyService.sayHello("World"); System.out.println(response); } } ``` 在这个例子中,`Proxy.newProxyInstance` 方法创建了一个实现了 `HelloService` 接口的代理对象。所有对 `proxyService` 的方法调用都会被转发到 `LoggingInvocationHandler` 的 `invoke` 方法中,从而实现日志记录功能[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值