编程习惯

1、用工厂方法替代构造函数

  • Boolean.valueOf()

通过一个boolean简单类型,构造Boolean对象引用。

优点:无需每次被调用时都创建一个新对象。同时使得类可以严格控制在哪个时刻有哪些实例存在  >>实例受控的类

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

静态工厂方法Boolean.valueOf(String)几乎总是比构造函数Boolean(String)更可取。构造函数每次被调用时都会创建一个新对象,而静态工厂方法则从来不要求这样做,实际上也不会这么做。

  • BigInteger.probablePrime()

构造方法BigInteger(int, int, Random)返回一个可能为素数的BigInteger,而用一个名为BigInteger.probablePrime()的静态工厂方法就更好。(JDK1.4最终增加了这个方法。)

优点:方法名对客户端更友好

public class BigInteger extends Number implements Comparable<BigInteger> {  

    public static BigInteger probablePrime(int bitLength, Random rnd) {
if (bitLength < 2)
throw new ArithmeticException("bitLength < 2"); // The cutoff of 95 was chosen empirically for best performance
return (bitLength < SMALL_PRIME_THRESHOLD ?
smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
}
  • EnumSet

JDK1.5引入的java.util.EnumSet类没有public构造函数,只有静态工厂方法。根据底层枚举类型的大小,这些工厂方法可以返回两种实现:

如果拥有64个或更少的元素(大多数枚举类型都是这样),静态工厂方法返回一个RegularEnumSet实例,用单个long来支持;

如果枚举类型拥有65个或更多的元素,静态工厂方法则返回JumboEnumSet实例,用long数组来支持。

优点:静态工厂方法能返回任意子类型的对象可以根据参数的不同,而返回不同的类型

    /**
* Creates an empty enum set with the specified element type.
*
* @param elementType the class object of the element type for this enum set
* @throws NullPointerException if <tt>elementType</tt> is null
*/
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64)
return new RegularEnumSet<E>(elementType, universe);
else
return new JumboEnumSet<E>(elementType, universe);
}
} class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
}
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
}
  • Collections.unmodifiableMap(Map)

Java集合框架中有32个集合接口的便利实现,提供不可修改的集合、同步集合等等。几乎所有的实现都通过一个不可实例化类(java.util.Collections)中的静态工厂方法导出,返回对象的类都是非public的。

优点:静态工厂方法能返回任意子类型的对象可以返回一个对象而无需使相应的类public。用这种方式隐藏实现类能够产生一个非常紧凑的API

    public class Collections {
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new UnmodifiableMap<K,V>(m);
} private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
}
}

2、私有化构造函数,使类不能被子类化

  • Arrays

这种工具类设计出来并不是为了实例化它。然而,如果不显式地编写构造函数,编译器则会提供一个公共的无参数的默认构造方法。

所以将构造函数私有化:

public class Arrays {
// Suppresses default constructor, ensuring non-instantiability.
private Arrays() {
} }

当然,还可以在这个私有构造器内部加上 throw new AssertionError(),可以确保该方法不会再类内部被意外调用

这种习惯用法的副作用是类不能被子类化了。子类的所有构造函数必须首先隐式或显式地调用父类构造函数,而在这种用法下,子类就没有可访问的父类构造函数可调用了。

3、避免创建不必要的对象

  • Map.keySet()

Map接口的keySet()方法返回Map对象的一个Set视图,包含该Map的所有key。

看起来好像每次调用keySet()都需要创建一个新的Set实例。而实际上,虽然返回的Set通常是可变的,但返回的对象在功能上是等同的:如果其中一个返回对象改变,其他对象也会改变,因为他们的底层都是同一个Map实例。虽然创建多个KeySet视图对象并没有害处,但也没有必要。

public abstract class AbstractMap<K,V> implements Map<K,V>   

        transient volatile Set<K> keySet = null; // volatile!!  

        public Set<K> keySet() {
if (keySet == null) {
keySet = new AbstractSet<K>() {
。。。
};
}
return keySet;
}
}

4、消除无用的对象引用

  • LinkedHashMap.removeEldestEntry()

缓存实体的生命周期不容易确定,随着时间推移,实体的价值越来越低。在这种情况下,缓存应该不定期地清理无用的实体。可以通过一个后台线程来清理(可能是Timer或ScheduledThreadPoolExecutor),也可以在给缓存添加新实体时进行清理。

LikedHashMap可利用其removeEldestEntry,删除较老的实体:

public class LinkedHashMap...{
void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
}

——可以继承LinkedHashMap,覆盖其removeEldestEntry方法。

注:如果想要缓存中的对象只要不被引用,就自动清理;则可以用WeakHashMap

5、如果指定了toString返回值的格式,则应该提供一个对应的静态工厂方法或构造函数

  • BigInteger.toString()

    /**
* Returns the String representation of this BigInteger in the
* given radix. If the radix is outside the range from {@link
* Character#MIN_RADIX} to {@link Character#MAX_RADIX} inclusive,
* it will default to 10 (as is the case for
* {@code Integer.toString}). The digit-to-character mapping
* provided by {@code Character.forDigit} is used, and a minus
* sign is prepended if appropriate. (This representation is
* compatible with the {@link #BigInteger(String, int) (String,
* int)} constructor.)
*
* @param radix radix of the String representation.
* @return String representation of this BigInteger in the given radix.
* @see Integer#toString
* @see Character#forDigit
* @see #BigInteger(java.lang.String, int)
*/
public String toString(int radix) { } /**
* Returns the decimal String representation of this BigInteger.
* The digit-to-character mapping provided by
* {@code Character.forDigit} is used, and a minus sign is
* prepended if appropriate. (This representation is compatible
* with the {@link #BigInteger(String) (String)} constructor, and
* allows for String concatenation with Java's + operator.)
*
* @return decimal String representation of this BigInteger.
* @see Character#forDigit
* @see #BigInteger(java.lang.String)
*/
public String toString() {
return toString(10);
}

对应的构造函数如下。这样程序员能容易地在对象及其字符串表示之间来回转换

    /**
* Translates the decimal String representation of a BigInteger into a
* BigInteger. The String representation consists of an optional minus
* sign followed by a sequence of one or more decimal digits. The
* character-to-digit mapping is provided by {@code Character.digit}.
* The String may not contain any extraneous characters (whitespace, for
* example).
*
* @param val decimal String representation of BigInteger.
* @throws NumberFormatException {@code val} is not a valid representation
* of a BigInteger.
* @see Character#digit
*/
public BigInteger(String val) {
this(val, 10);
}
   /**
* Translates the String representation of a BigInteger in the specified
* radix into a BigInteger. The String representation consists of an
* optional minus sign followed by a sequence of one or more digits in the
* specified radix. The character-to-digit mapping is provided by
* {@code Character.digit}. The String may not contain any extraneous
* characters (whitespace, for example).
*
* @param val String representation of BigInteger.
* @param radix radix to be used in interpreting {@code val}.
* @throws NumberFormatException {@code val} is not a valid representation
* of a BigInteger in the specified radix, or {@code radix} is
* outside the range from {@link Character#MIN_RADIX} to
* {@link Character#MAX_RADIX}, inclusive.
* @see Character#digit
*/
public BigInteger(String val, int radix) {}

迭代器模式

1、Collection.iterator()

未完待续。。

[置顶] 学习JDK源码:编程习惯和设计模式的更多相关文章

  1. [置顶] 学习JDK源码:可进一步优化的代码

    1.参数化类型的构造函数比较啰嗦 new HashMap<String, List<String>>() 如果你调用参数化类的构造函数,那么很不幸,你必须要指定类型参数,即便上 ...

  2. 深入学习JDK源码系列之、ArrayList

    前言 JDK源码解析系列文章,都是基于JDK8分析的,虽然JDK15马上要出来了,但是JDK8我还不会,我... 类图 实现了RandomAccess接口,可以随机访问 实现了Cloneable接口, ...

  3. 学习JDK源码(二):Integer

    最近没有好好保持学习的好习惯,该打. 天天忙,感觉都不知道在干嘛.真的厌倦了普通的Java代码,还是想学点新技术. 用了这么久的Java,最常用的数据类型肯定是Int了,而他的包装类Integer用的 ...

  4. 学习JDK源码(一):String

    用了好久的Java了,从来没有看过jdk的源码,趁着今天有点时间,拿出了jdk的源码看了下,今天先看了关于String的,毕竟开发中String类型使用最广泛.在我们下载安装jdk的时候,部分源码也已 ...

  5. [置顶] Cocos2d-x 实例源码分析之二 小实例的主框架

    这篇文章是分析第一个小实例ActionTest的源码.其实所有实例程序的结构都是一样的,只有特定方法里的代码不同,大的框架都是一样的.也就是说看完这篇文章你就可以自己开始分析其他源码了. 废话不多说, ...

  6. 【设计模式】JDK源码中用到的设计模式

    https://blog.csdn.net/angjunqiang/article/details/42061453 https://blog.csdn.net/baiye_xing/article/ ...

  7. JDK源码中使用的设计模式

    结构型模式: 适配器模式: 用来把一个接口转化成另一个接口. java.util.Arrays#asList() javax.swing.JTable(TableModel) java.io.Inpu ...

  8. JDK源码学习笔记——String

    1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ...

  9. JDK源码系列总索引

    一 目标 记录学习jdk源码的一些笔记和心得,jdk版本使用11.0.1,工具idea Class后面序号为优先级1-4,优先级递减 目录转载自博客: https://blog.csdn.net/qq ...

随机推荐

  1. 后台自动启动appium

    首先说明,本人用的exe方式安装的appium. 新建一个.vbs文件,写入以下脚本,记得把D盘换成你自己的盘符. 1.后面taskkill好像没有实际作用..加就加了吧. Set ws = Crea ...

  2. hdu3689(kmp+dp)

    题意:问随机生成一个长度为m(m<=1000)长度的字符串,出现某个子串s的概率是多少. 解法:dp+kmp优化.ans[i][j]表示i长度,走到了s的j位置的概率,当然这是在i之前没有出现s ...

  3. JavaEE(1) - Weblogic 服务器管理的数据源

    JBoss下载: http://jbossas.jboss.org/downloads http://www.cnblogs.com/xw-cnblogs/articles/2439969.html ...

  4. 【Java编码准则】の #12不要使用不安全或者强度弱的加密算法

    安全性要求高的应用程序必须避免使用不安全的或者强度弱的加密算法,现代计算机的计算能力使得攻击者通过暴力破解能够攻破强度弱的算法.比如,数据加密标准算法DES是极度不安全的,使用类似EFF(Electr ...

  5. FindBugs:Compiler output path for module can not be null. check your module/project settings问题原因

    这可能是很多人在使用Android studio 该插件会发现此错误信息:Compiler output path for module can not be null. check your mod ...

  6. 使用微软 URL Rewrite Module 开启IIS伪静态

    原文 使用微软 URL Rewrite Module 开启IIS伪静态 在IIS5和IIS6时代,我们使用URL REWRITING可实现URL重写,使得WEB程序实现伪静态,但默认情况下只能实现.A ...

  7. sql优化的50中方法

    查询速度慢的原因很多,常见如下几种:    1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)    2.I/O吞吐量小,形成了瓶颈效应.    3.没有创建计算列导致查询不优化 ...

  8. 使用C语言编写windows服务一般框架

    原文:使用C语言编写windows服务一般框架 编写windows服务和编写windows应用程序一样,有一些回调函数必须填写且向windows 服务管理器(service manager)进行注册, ...

  9. c语言发挥帕斯卡三角

    我们已经确定了帕斯卡三角的规则,下面是我的代码,非常实用哦! !! #include<stdio.h>  void main()  {      int i,j,n,k;      sca ...

  10. 一张地图,告诉你NodeJS命令行调试器语句

    NodeJS提供脚本调试. 进入node debug xx.js您可以进入调试模式. 版权声明:本文博客原创文章,博客,未经同意,不得转载.