标签: Java与设计模式
static关键字的作用是把类的成员变成类相关,而不是实例相关.
普通初始化块
当Java创建一个对象时, 系统先为对象的所有实例变量分配内存(前提是该类已经被加载过了), 然后开始对这些实例变量进行初始化, 顺序是: 先执行初始化块或声明实例变量时指定的初始值(这两处执行的顺序与他们在源代码中排列顺序相同), 再执行构造器里指定的初始值.
静态初始化块
又名类初始化块(普通初始化块负责对象初始化, 类初始化块负责对类进行初始化). 静态初始化块是类相关的, 系统将在类初始化阶段静态初始化, 而不是在创建对象时才执行. 因此静态初始化块总是先于普通初始化块执行.
执行顺序
系统在类初始化以及对象初始化时, 不仅会执行本类的初始化块
[static/non-static]
, 而且还会一直上溯到java.lang.Object
类, 先执行Object类中的初始化块[static/non-static]
, 然后执行其父类的, 最后是自己.
顶层类(初始化块, 构造器) -> ... -> 父类(初始化块, 构造器)
-> 本类(初始化块, 构造器)
小结:
static{} 静态初始化块会在类加载过程中执行;
{} 则只是在对象初始化过程中执行, 但先于构造器;
枚举类与普通类的区别
1. 枚举类继承了java.lang.Enum
, 而不是Object
, 因此枚举不能显示继承其他类; 其中Enum实现了Serializable和Comparable接口(implements Comparable<E>, Serializable
);
2. 非抽象的枚举类默认使用final
修饰,因此枚举类不能派生子类;
3. 枚举类的所有实例必须在枚举类的第一行显示列出(枚举类不能通过new来创建对象); 并且这些实例默认/且只能是public static final
的;
4. 枚举类的构造器默认/且只能是private
;
5. 枚举类通常应该设计成不可变类, 因此建议成员变量都用private final修饰;
6. 枚举类不能使用abstract关键字将枚举类声明成抽象类(因为枚举类不允许有子类), 但如果枚举类里面有抽象方法, 或者枚举类实现了某个接口, 则定义每个枚举值
时必须为抽象方法提供实现, 如:
/** * Created by jifang on 15/12/9. */
public interface GenderDesc {
void info();
}
public enum Gender implements GenderDesc {
MALE("男"), FEMALE("女");
private final String name;
Gender(String name) {
this.name = name;
}
@Override
public void info() {
System.out.println("该枚举代表 " + name);
}
}
或者:
public enum Gender implements GenderDesc {
MALE("男") {
@Override
public void info() {
System.out.println("该枚举代表 男");
}
}, FEMALE("女") {
@Override
public void info() {
System.out.println("该枚举代表 女");
}
};
private final String name;
Gender(String name) {
this.name = name;
}
}
this
, 外部类名.this
加以区分.private
权限对外部类也是不起作用的:public class OuterClass {
@Test
public void test() {
visitInner();
}
public void visitInner() {
InnerClass clazz = new InnerClass();
clazz.value = 10;
System.out.println(clazz.value);
}
private class InnerClass {
private int value;
}
}
创建型模式
社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:
1. 单例模式
2. 工厂模式(分为简单工厂模式,工厂方法模式,抽象工厂模式)
3. 建造者模式
4. 原型模式.
今天, 我但就单例模式进行研究:
单例模式:
保证一个类只有一个实例, 并提供一个访问他的全局访问点.
文件系统
, 一个操作系统只能有一个文件系统;由于单例模式只生成一个实例, 减少了系统性能开销(如: 当一个对象的产生需要比较多的资源时, 如读取配置, 产生其他依赖对象, 则可以通过在应用启动时直接产生一个单例对象, 然后永久驻留内存的方式来解决)
常见的单例模式实现方式有五种: 饿汉式, 懒汉式, 双重锁定式, 静态内部类, enum枚举.
/** * 饿汉式 * 问题: 如果只是加载本类, 而没有调用getInstance方法, 会造成资源浪费 * Created by jifang on 15/12/4. */
public class HungerSingleton {
/** * 类初始化时理解初始化该实例 * 类加载时, 天然的线程安全时刻 */
private static final HungerSingleton instance = new HungerSingleton();
private HungerSingleton() {
}
/** * 方法没有同步(synchronized), 调用效率高 */
public static HungerSingleton getInstance() {
return instance;
}
@Override
public String toString() {
return "HungerSingleton{}";
}
}
/** * 懒汉式 * 问题: 每次调用getInstance都要同步(synchronized), 效率降低 * Created by jifang on 15/12/4. */
public class LazySingleton {
/** * 类加载时并没初始化, 延迟加载 */
private static LazySingleton instance;
private LazySingleton() {
}
/** * 注意synchronized, 线程安全 */
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
@Override
public String toString() {
return "LazySingleton{}";
}
}
由于同步只在第一次实例化Instance时才需要,也就是单例类实例创建的时候, 因此我们使用双重锁定式(double checked locking pattern)
注: 有的文章中提到
由于JVM底层内部模型原因, 双重锁定偶尔会出问题, 不建议使用
, 但是自从JDK1.5开始, 该问题已经被解决了, 因此可放心使用, 详见如何在Java中使用双重检查锁实现单例
/** * 双重锁定实现 * 问题: 适用于JDK1.5之后的版本 * Created by jifang on 15/12/4. */
public class DoubleCheckSingleton {
/** * 需要使用volatile * 保证所有的写(write)都将先行发生于读(read) */
private static volatile DoubleCheckSingleton instance;
private DoubleCheckSingleton() {
}
public static DoubleCheckSingleton getInstance() {
if (instance == null) { //Single Checked
synchronized (DoubleCheckSingleton.class) {
if (instance == null) { // Double Checked
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
@Override
public String toString() {
return "DoubleCheckSingleton{}";
}
}
/** * 静态内部类实现Singleton * Created by jifang on 15/12/4. */
public class StaticInnerSingleton {
/** * 外部类没有static属性, 因此加载本类时不会立即初始化对象 */
private static class InnerClassInstance {
private static final StaticInnerSingleton instance = new StaticInnerSingleton();
}
private StaticInnerSingleton() {
}
/** * 只有真正调用getInstance方法时, 才会加载静态内部类(延迟加载), 而且加载类是天然的线程安全的(线程安全), 没有synchronized(调用效率高) * * @return */
public static StaticInnerSingleton getInstance() {
return InnerClassInstance.instance;
}
@Override
public String toString() {
return "StaticInnerSingleton{}";
}
}
/** * 枚举实现单例 * 基于JVM底层实现, Enum天然的单例以及线程安全 * Created by jifang on 15/12/5. */
public enum EnumSingleton {
/** * 构造方法默认为private */
INSTANCE;
/** * 可以添加其他操作 * other operation */
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "EnumSingleton{" +
"name='" + name + '\'' +
'}';
}
}
方式 | 优点 | 缺点 |
---|---|---|
饿汉式 | 线程安全, 调用效率高 | 不能延迟加载 |
懒汉式 | 线程安全, 可以延迟加载 | 调用效率不高 |
双重检测锁式 | 线程安全, 调用效率高, 可以延迟加载 | - |
静态内部类式 | 线程安全, 调用效率高, 可以延迟加载 | - |
枚举单例 | 线程安全, 调用效率高 | 不能延迟加载 |
双重锁定单例
, 其他类同, 不再赘述./** * 单例破解 * Created by jifang on 15/12/4. */
public class TestCase {
@Test
public void testBreakDoubleCheck() {
try {
Class<DoubleCheckSingleton> clazz = (Class<DoubleCheckSingleton>) Class.forName("com.feiqing.singleton.DoubleCheckSingleton");
Constructor<DoubleCheckSingleton> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
DoubleCheckSingleton instance1 = constructor.newInstance();
DoubleCheckSingleton instance2 = constructor.newInstance();
System.out.println("singleton? " + (instance1 == instance2));
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
LazySingleton
进行改造, 使其实现Serializable
接口, 以支持序列化/反序列化.public class LazySingleton implements Serializable{
private static final long serialVersionUID = 8511876423469188139L;
/** * 类加载时并没初始化, 延迟加载 */
private static LazySingleton instance;
private LazySingleton() {
if (instance != null){
throw new RuntimeException();
}
}
/** * 注意synchronized, 线程安全 */
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
@Override
public String toString() {
return "LazySingleton{}";
}
}
破解:
public class TestCase {
private static final String SYSTEM_FILE = "/tmp/save.txt";
@Test
public void testBreakLazy() {
LazySingleton instance1 = LazySingleton.getInstance();
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SYSTEM_FILE));
oos.writeObject(instance1);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SYSTEM_FILE));
LazySingleton instance2 = (LazySingleton) ois.readObject();
System.out.println("singleton? " + (instance1 == instance2));
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
LazySingleton
中添加下面一个方法: /** * 反序列化时, 如果定义了readResolve方法, 则直接返回此方法制定的对象. * * @return */
private Object readResolve() {
return instance;
}
详细信息请参考深入理解Java对象序列化
/** * 单例性能测试 * Created by jifang on 15/12/4. */
public class TestCase {
private static final String SYSTEM_FILE = "/tmp/save.txt";
private static final int THREAD_COUNT = 10;
private static final int CIRCLE_COUNT = 100000;
@Test
public void testSingletonPerformance() throws IOException, InterruptedException {
final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
FileWriter writer = new FileWriter(new File(SYSTEM_FILE), true);
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_COUNT; ++i) {
new Thread(
new Runnable() {
@Override
public void run() {
for (int i = 0; i < CIRCLE_COUNT; ++i) {
Object instance = HungerSingleton.getInstance();
}
latch.countDown();
}
}
).start();
}
latch.await();
long end = System.currentTimeMillis();
writer.append("HungerSingleton 共耗时: " + (end - start) + " 毫秒\n");
writer.close();
}
}