本文首发于 http://www.YoungZY.com/
原文网址:Singleton Pattern
意图
- 确保一个类只存在一个实例,并供全局访问
- 适时初始化,或者在第一次使用时初始化
问题
应用程序需要一个,并且只需要一个对象的实例。另外,延迟初始化和全局访问是必要的。
讨论
单例的职责有:创建、初始化,访问和执行。声明一个私有的、静态的成员变量;提供一个公共的、静态的方法来封装初始化代码,并访问该实例。
客户端可以随时调用这个静态方法来获取单例。
本文首发于 http://www.YoungZY.com/
原文网址:Singleton Pattern
应用程序需要一个,并且只需要一个对象的实例。另外,延迟初始化和全局访问是必要的。
单例的职责有:创建、初始化,访问和执行。声明一个私有的、静态的成员变量;提供一个公共的、静态的方法来封装初始化代码,并访问该实例。
客户端可以随时调用这个静态方法来获取单例。
本文首发于 http://www.YoungZY.com/
原文网址:Prototype Pattern
new
操作是危险的应用程序在使用 new
操作时跟对象产生了“硬连接”(耦合)。
定义一个可供“克隆”的接口方法,维护一个包含所有“可克隆”的具体衍生类的“注册表”。每一个类都具有“多态性”:继承自基类或者接口,注册它的原型实例,实现了“克隆”方法。
作为客户端,不用再使用跟跟类名“硬连接”的 new
操作了,而是调基类的 clone
方法,通过字符串或者枚举值类型来指定具体的衍生类。
本文首发于 http://www.YoungZY.com/
原文网址:Object Pool Pattern
对象池对性能有显著的提升。在以下场景中最有效:实例化过程复杂的对象;实例化频率高的对象;同一时刻用到的实例数量较少。
对象池(也叫资源池)是用来管理对象缓存的。一个使用了对象池的客户端可以请求对象池中已经存在的实例,而不用自己创建。一般来说,这个对象池是可以自动增长的,当它是空的时候可以自己创建对象。或者我们可以限定对象池的大小。
将所有可重复使用的、当前未使用的对象放在同一个对象池中以保证其连贯性是有必要的。为了达到这个目的,对象池应该是单例的。
对象池允许其他程序从中获取对象,当用完后在放回池中,以便下次再用。
但是我们不会一直等着某个对象被释放,所以在必要的时候对象池可以创建新的对象,但一定要有定期清理未使用的对象的机制。
本文首发于 http://www.YoungZY.com/
new
是危险的操作一个框架需要对其架构模型有一套标准化的操作规范,但对一个单独的应用来说,可以定义不同的作用域对象及其初始化过程。
工厂方法模式是为了创建对象,就像模板方法模式是为了实现一种算法。接口定义了标准的创建对象的方法(用纯虚拟的标识——如abstract——来修饰创建步骤),然后委托子类去完成创建的具体步骤,并将子类提供给客户端调用。
工厂方法模式使得设计具有更多的可定制化空间,而只是多了一点点复杂性。其他设计模式需要新的类,而它只需要新的操作(方法)。
大家经常使用工厂方法模式作为创建对象的标准方式,但以下情况就没有必要用了:初始化过程不会再改变的类,或者初始化过程被放在很容易被子类覆盖的方法里(如构造函数)。
工厂模式方法和抽象工厂很相似,只是少了那一组相关或类似对象。
工厂模式通常是在基础框架中定义,然后由使用者去实现。
本文首发于 http://www.YoungZY.com/
一个应用需要创建一个有很多元素组成的复杂的集合体。该集合体的具体内容存储在辅助存储器内(如文件),但其中某一种展现形式需要在主存储器(内存)中构建。
将解析(读取和分析)已存储的数据(如RTF文件)的算法与构建并展示众多表现形式(如ASCII,TeX,文本组件)之一的算法分开。重点在于创建一个复杂的集合体。
“主管”在解析底层数据时调用“构建者”。“构建者”每次被调用都构建这个复杂对象的某一部分,并且掌控所有中间状态。构建完成后,客户端可以从“构建者”处获取。
对创建过程有了更好的控制。不像之前的“抽象工厂模式”一步构建(new
)对象,“构造器模式”是在“主管”的控制下一步步地完成构建。
本文首发于 http://www.YoungZY.com/
new
操作是有害的(言下之意就是少用)如果一个应用是便捷式的,那么它就要封装对平台的依赖。这里说的“平台”包括视窗系统、操作系统、数据库等等。通常这些封装不会提前设计,所以当要增加新的平台时 if
的条件语句就会像兔子一样在代码里到处乱串。
采用一种迂回的方式将一组类似或相关的对象的创建与具体的对象类隔离开来。工厂对象负责提供所有这一类对象的创建服务。客户端不用直接创建平台对象,让“工厂”去创建。
这样的机制使得不同“产品”之间的切换变得简单,因为具体的类只在工厂对象类里出现一次——实例化对象的地方。通过在抽象工厂里使用不同的具体实现类的方式,应用程序可以大规模地替换整个产品簇。
由于抽象工厂的方式使用得很普遍,它惯常用单例模式实现。
本文首发于 http://www.YoungZY.com/
@Override
,一定不陌生吧!就是没敲过,也一定见过。看看下面这个例子,请你来找茬儿。
IShape类
1 2 3 |
public interface IShape { String introduceYourself(); } |
Rectangle类
1 2 3 4 5 |
public class Rectangle implements IShape { public String introduceYourself() { return "我是一个长方形。"; } } |
Square类
1 2 3 4 5 |
public class Square extends Rectangle { public String introduceYouself() { return "你可以叫我长方形,但它并不是我的真名。我的真名叫正方形。"; } } |
测试类
1 2 3 4 5 6 7 8 |
public class AnnotationTest { @Test public void introduceYourself() { IShape aShape = new Square(); assertEquals("你可以叫我长方形,但它并不是我的真名。我的真名叫正方形。", aShape.introduceYourself()); } } |
测试结果
这是为什么呢? 明明是一个正方形,它怎么偏说自己是个长方形呢?能看出是哪的问题吗? 如果还是找不到问题,在方法上添加@Override
注解看看。 是不是很神奇?
所以,请善待它吧! 其实之前我也没认识到这一点。是最近听了一个关于 Annotation的课才知道它还有这个用处。
本文首发于 http://www.YoungZY.com/
import
语句里不想/不能带有星号(*):
1
|
import com.youngzy.*;
|
要改成:
1 2 3 |
import com.youngzy.Foo; import com.youngzy.Bar; ... |
如果你熟悉Eclipse的快捷键(不熟悉的话网上很容易查),组合键 Ctrl+Shift+O
(字母O)应该就能达到你的要求。
但你有没有遇到这样的情况:删掉了*
的这一行代码,通过上述快捷键补全的代码仍然是xxx.*
?
时灵时不灵?
你都要怀疑是不是Eclipse有Bug了。(好吧,是我在怀疑^_^)
上网各种搜,噢,原来还有个叫Organize Imports的东西。
Eclipse里打开 Preferences,搜索框里输入 import ,你会看到 Java-Code Style-Organize Imports ,下方有两个数字,代表如果同一个包下需要导入的类太多(大于这里设置的数字),就会用*代替,而不是具体到类。默认值是 99 ,如图:
将其改大点,譬如 999,再回到代码里试试,是不是跟之前不一样了?
惊喜不惊喜?!
意外不意外?!
参考:
https://blog.csdn.net/qq_34068082/article/details/80096646
本文首发于 http://www.YoungZY.com/
举个常用但没太注意过的例子 —— SimpleDateFormat 类。
这个类是JDK里面封装的,用来对日期进行格式化。提供 parse(String dateStr)
, format(Date date)
等方法。
注:上面提到的方法都在其父类 DateFormat 中。
翻开源代码就会发现,它有一个实体变量 calendar
(也是在父类 DateFormat 中)。上面提到的 parse 、format 等方法会对这个变量执行 clear() 、set…(…) 等操作(具体操作请查看源代码)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public abstract class DateFormat extends Format { /** * The {@link Calendar} instance used for calculating the date-time fields * and the instant of time. This field is used for both formatting and * parsing. * * <p>Subclasses should initialize this field to a {@link Calendar} * appropriate for the {@link Locale} associated with this * <code>DateFormat</code>. * @serial */ protected Calendar calendar; ... } |
这样,如果有一个类型为 SimpleDateFormat 的公共变量,就要小心了,这个变量不是线程安全的,多个线程间的数据可能会串了…几乎是一定会串。
例如,有这样一个日期的工具类,就是将日期转换成特定的字符串格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * * * @author by youngz * on 2016年11月27日 * * Package&FileName: org.young.thread.DateUtil */ public class DateUtil { public final static String PATTERN_YMD = "yyyy-MM-dd"; private static SimpleDateFormat formatter = new SimpleDateFormat(); public static String getFormattedDate(Date date) { formatter.applyPattern(PATTERN_YMD); return formatter.format(date); } } |
这是测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/** * 测试类 * * @author by youngz * on 2016年11月27日 * * Package&FileName: org.young.thread.Main */ public class Main { public static void main(String[] args) { // 昨天的日期 Calendar calYesterday = Calendar.getInstance(); calYesterday.setTime(new Date()); calYesterday.add(Calendar.DAY_OF_MONTH, -1); ShowDate yesterday = new ShowDate("YESTERDAY", calYesterday.getTime()); // 今天的日期 ShowDate today = new ShowDate("TODAY", new Date()); Thread t1 = new Thread(yesterday); Thread t2 = new Thread(today); /* * start() * 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。 * run() * 就和普通的成员方法一样,可以被重复调用。单独调用s的话,会在当前线程中执行run(),而并不会启动新线程! */ t1.start(); t2.start(); } } |
还有一个线程类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
/** * 一个简单的线程的实现类 * * @author by youngz * on 2016年11月27日 * * Package&FileName: org.young.thread.ShowDate */ class ShowDate implements Runnable { private String desc; private Date date; public ShowDate(String desc, Date date) { this.desc = desc; this.date = date; } @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 30 == 0) { try { Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(desc + ": " + DateUtil.getFormattedDate(date)); } } } |
按照设想,以今天(2016-11-27)为例,应该是 YESTERDAY: 2016-11-26 和 TODAY: 2016-11-27 这两个字符串交叉着,各自打印 1000 遍。
可事实是这样吗?
运行一下看看。
结果简直惨不忍睹,第一行就出错了(当然,这个可能需要一定的“运气”)。“今天”一会儿 26,一会儿 27。“昨天”也一样,一会儿 26,一会儿 27。
这就是线程不安全导致的问题了。
该怎么解决呢??
比较容易想到的就是这个关键字了:synchronized 。在学校就是这么学的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/** * 进化的 DateUtil * 使用同步块,synchronized * * @author by youngz * on 2016年11月27日 * * Package&FileName: org.young.thread.SyncDateUtil */ public class SyncDateUtil { public final static String PATTERN_YMD = "yyyy-MM-dd"; private static SimpleDateFormat formatter = new SimpleDateFormat(); public static String getFormattedDate(Date date) { synchronized(formatter) { formatter.applyPattern(PATTERN_YMD); return formatter.format(date); } } } |
只要把线程类 —— ShowDate 的第 30 行的工具类替换一下就行,DateUtil 换成 SyncDateUtil ,即:
1 2 |
// System.out.println(desc + ": " + DateUtil.getFormattedDate(date)); System.out.println(desc + ": " + SyncDateUtil.getFormattedDate(date)); |
在运行试试!
不管执行多少遍,都不会出现前面说的串数据的问题了。
除了 synchronized 还有 ThreadLocal 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * 进化的 DateUtil —— ThreadLocal * * @author by youngz * on 2016年11月27日 * * Package&FileName: org.young.thread.ThreadLocalDateUtil */ public class ThreadLocalDateUtil { public final static String PATTERN_YMD = "yyyy-MM-dd"; private static ThreadLocal<DateFormat> formatter = new ThreadLocal<DateFormat>() { protected DateFormat initialValue() { return new SimpleDateFormat(PATTERN_YMD); } }; public static String getFormattedDate(Date date) { return formatter.get().format(date); } } |
ThreadLocal 还有另一种实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/** * 进化的 DateUtil —— ThreadLocal 的另一种实现 * * @author by youngz * on 2016年11月27日 * * Package&FileName: org.young.thread.ThreadLocalDateUtil2 */ public class ThreadLocalDateUtil2 { public final static String PATTERN_YMD = "yyyy-MM-dd"; private static ThreadLocal<DateFormat> formatter = new ThreadLocal<DateFormat>(); public static DateFormat getDateFormat() { DateFormat df = formatter.get(); if (null == df) { df = new SimpleDateFormat(PATTERN_YMD); formatter.set(df); } return df; } public static String getFormattedDate(Date date) { return getDateFormat().format(date); } } |
详细代码可参照 https://github.com/youngzhu/CollectionCode4Java/tree/master/src/org/young/thread
参考:
http://www.cnblogs.com/doit8791/p/4093808.html
本文首发于 http://www.YoungZY.com/
从 Hibernate 3 升级到 Hibernate 4 ,出现了这样的错误:
1 2 3 4 5 6 |
org.hibernate.id.IdentifierGenerationException: Unknown integral data type for ids : java.lang.String at org.hibernate.id.IdentifierGeneratorHelper.getIntegralDataTypeHolder(IdentifierGeneratorHelper.java:215) at org.hibernate.id.SequenceGenerator.buildHolder(SequenceGenerator.java:150) at org.hibernate.id.SequenceGenerator.generateHolder(SequenceGenerator.java:126) at org.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:116) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:118) |
原来,在 Hibernate 4 中如果主键的生成策略是 Sequence ,则要求数据库中对应的字段必须为数字(Number,Long 等)类型。
而当前系统中使用的是字符串类型,所以报错了。
网上参考了几篇文章,就一篇(见文末)还不错。在此基础上,覆写了部分方法,解决了问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
package org.young.hibernate.common; import java.io.Serializable; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.IntegralDataTypeHolder; import org.hibernate.id.SequenceGenerator; /** * 继承 hibernate 里的 SequenceGenerator 类, * 覆写2个方法: * 1. buildHolder() * 2. generate(SessionImplementor session, Object obj) * * @author by youngz * on 2016年7月24日 * * Package&FileName: org.young.hibernate.common.HISequenceGenerator */ public class HISequenceGenerator extends SequenceGenerator { @Override public Serializable generate(SessionImplementor session, Object obj) { /* * 源码 * * return generateHolder(session).makeValue(); */ // Serializable id = generateHolder(session).makeValue(); Number id = generateHolder(session).makeValue(); /* * 笔者曾自作聪明地以为可以省掉这一步, * 继而这个方法就不用覆写,毕竟都是 Serializable , * 不管是数字(Number)还是字符串(String) * * 但返回值却需要确定到String,因为Java类(hbm对象)的属性是String类型的 * 而 makeValue() 返回的 Number (这里的实际类型是 Long) * * 所以 id 的类型直接定义成 Number (更直观), * 而不是 Serializable */ if (getIdentifierType().getReturnedClass() == String.class){ //增加对String的判断 return id.toString(); } return id; } @Override protected IntegralDataTypeHolder buildHolder() { /* * 源码 * * return IdentifierGeneratorHelper.getIntegralDataTypeHolder(this.identifierType.getReturnedClass()); */ return HIIdentifierGeneratorHelper .getIntegralDataTypeHolder(getIdentifierType() .getReturnedClass()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
package org.young.hibernate.common; import java.math.BigDecimal; import java.math.BigInteger; import org.hibernate.id.IdentifierGenerationException; import org.hibernate.id.IdentifierGeneratorHelper; import org.hibernate.id.IntegralDataTypeHolder; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * IdentifierGeneratorHelper ,final 类,不可继承 * * @author by youngz * on 2016年7月24日 * * Package&FileName: org.young.hibernate.common.HIIdentifierGeneratorHelper */ public class HIIdentifierGeneratorHelper { public static IntegralDataTypeHolder getIntegralDataTypeHolder( Class integralType) { if ((integralType == Long.class) || (integralType == Integer.class) || (integralType == Short.class) // 增加对String的判断 || (integralType == String.class)) { return new HIBasicHolder(integralType); } if (integralType == BigInteger.class) return new IdentifierGeneratorHelper.BigIntegerHolder(); if (integralType == BigDecimal.class) { return new IdentifierGeneratorHelper.BigDecimalHolder(); } throw new IdentifierGenerationException( "Unknown integral data type for ids : " + integralType.getName()); } public static class HIBasicHolder implements IntegralDataTypeHolder { private final Class exactType; private long value = 0L; public HIBasicHolder(Class exactType) { this.exactType = exactType; if ((exactType != Long.class) && (exactType != Integer.class) && (exactType != Short.class) // 增加对String的判断 && (exactType != String.class) ) throw new IdentifierGenerationException( "Invalid type for basic integral holder : " + exactType); } public long getActualLongValue() { return this.value; } public IntegralDataTypeHolder initialize(long value) { this.value = value; return this; } public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException { long value = resultSet.getLong(1); if (resultSet.wasNull()) value = defaultValue; return initialize(value); } public void bind(PreparedStatement preparedStatement, int position) throws SQLException { preparedStatement.setLong(position, this.value); } public IntegralDataTypeHolder increment() { checkInitialized(); this.value += 1L; return this; } private void checkInitialized() { if (this.value == 0L) throw new IdentifierGenerationException( "integral holder was not initialized"); } public IntegralDataTypeHolder add(long addend) { checkInitialized(); this.value += addend; return this; } public IntegralDataTypeHolder decrement() { checkInitialized(); this.value -= 1L; return this; } public IntegralDataTypeHolder subtract(long subtrahend) { checkInitialized(); this.value -= subtrahend; return this; } public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) { return multiplyBy(IdentifierGeneratorHelper.extractLong(factor)); } public IntegralDataTypeHolder multiplyBy(long factor) { checkInitialized(); this.value *= factor; return this; } public boolean eq(IntegralDataTypeHolder other) { return eq(IdentifierGeneratorHelper.extractLong(other)); } public boolean eq(long value) { checkInitialized(); return (this.value == value); } public boolean lt(IntegralDataTypeHolder other) { return lt(IdentifierGeneratorHelper.extractLong(other)); } public boolean lt(long value) { checkInitialized(); return (this.value < value); } public boolean gt(IntegralDataTypeHolder other) { return gt(IdentifierGeneratorHelper.extractLong(other)); } public boolean gt(long value) { checkInitialized(); return (this.value > value); } public IntegralDataTypeHolder copy() { HIBasicHolder copy = new HIBasicHolder(this.exactType); copy.value = this.value; return copy; } public Number makeValue() { checkInitialized(); if (this.exactType == Long.class) return Long.valueOf(this.value); // 增加对String的判断 if (this.exactType == String.class) { return Long.valueOf(this.value); } if (this.exactType == Integer.class) { return Integer.valueOf((int) this.value); } return Short.valueOf((short) (int) this.value); } public Number makeValueThenIncrement() { Number result = makeValue(); this.value += 1L; return result; } public Number makeValueThenAdd(long addend) { Number result = makeValue(); this.value += addend; return result; } public String toString() { return "BasicHolder[" + this.exactType.getName() + "[" + this.value + "]]"; } public boolean equals(Object o) { if (this == o) return true; if ((o == null) || (super.getClass() != o.getClass())) { return false; } HIBasicHolder that = (HIBasicHolder) o; return (this.value == that.value); } public int hashCode() { return (int) (this.value ^ this.value >>> 32); } } } |
1 2 3 4 5 6 7 |
<id name="id" type="java.lang.String"> <column name="ID" length="20" /> <!-- 此处的 class 即刚刚新建的 Generator --> <generator class="org.young.hibernate.common.HISequenceGenerator" > <param name="sequence">SEQ_ID</param> </generator> </id> |
本文的实验是在
hibernate-core-4.1.8.Final.jar
基础上完成。可至 GitHub 上查看相关代码。
http://my.oschina.net/liyuj/blog/386582?fromerr=4h5OjIKZ
近期评论