单例(Singleton)模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类
1、单例模式的要点:
1) 某个类只能有一个实例
2) 它必须自行创建这个实例
3) 它必须自行向整个系统提供这个实例
2、单例模式的例子:
1) 打印机
2) 传真机使用的传真卡,只应该由一个软件管理
3) Windows回收站
3、单例模式有以下特点:
1) 单例类只能有一个实例
2) 单例类必须自己创建自己的唯一的实例
3) 单例类必须给所有其他对象提供这一实例
*饿汉式单例类:
public class EagerSingleton{
private static final EagerSingleton m_instance = new EagerSingleton();
private EagerSingleton(){}
public static EagerSingleton getInstance(){
return m_instance;
}
}
java语言中单例类的一个最重要的特点是类的构造子是私有的,从而避免外界利用构造子直接创建出任意多的实例。
因为构造子是私有的,因此此类不能被继承
*懒汉式单例类:
与饿汉式单例类相同的是,类的构造子是私有的,不同的是在第一次被引用时将自己实例化,如果加载器是静态的,那么在懒汉
式单例类被加载时不会将自己实例化。
public class LazySingleton{
private static LazySingleton m_instance = null;
private LazySingleton(){}
synchronized public static LazySingleton getInstance(){
if(m_instance == null){
m_instance = new LazySingleton();
}
return m_instance;
}
}
*登记式单例类
public class RegSingleton{
private static HashMap m_registry = new HashMap();
static{
RegSingleton x = new RegSingleton();
m_registry.put(x.getClass().getName(),x);
}
protected RegSingleton(){}
public static RegSingleton getInstance(String name){
if(name == null){
name = "RegSingleton";
}
if(m_registry.get(name) == null){
try{
m_registry.put(name,Class.forName(name).newInstance());
}catch(Exception e){
System.out.println("Error happened.");
}
}
return (RegSingleton)(m_registry.get(name));
}
//一个示意性的商业方法
public String about(){
return "Hello, I am RegSingleton";
}
}
//登记式单例类的子类
public class RegSingletonChild extends RegSingleton{
public RegSingletonChild(){}
//静态工厂方法
public static RegSingletonChild getInstance(){
return (RegSingletonChild)RegSingleton.getInstance("RegSingleton")
}
public String about(){
return "Hello, I am RegSingletonChild";
}
}
4、使用单例模式的条件:
使用单例模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式,反过来说,如果一个类可以有几个实例共
存,那么就没有必要使用单例模式类。
//属性管理器
import java.util.Properties;
import java.io.FileInputStream;
import java.io.File;
public class ConfigManager{
//属性文件全名
private static final String PFILE = System.getProperty("user.dir")
+ File.Separator + "Singleton.properties";
//对应于属性文件的文件对象变量
private File m_file = null;
//属性文件的最后修改日期
private long m_lastModifiedTime = 0;
//属性文件所对应的属性对象变量
private Properties m_props = null;
//本类可能存在的唯一的一个实例
private static ConfigManager m_instance = new ConfigManager();
//私有的构造子,用以保证外界无法直接实例化
private ConfigManager(){
m_file = new File(PFILE);
m_lastModifiedTime = m_file.lastModified();
if(m_lastModifiedTime == 0){
System.out.println(PFILE + "file does not exits!");
}
m_props = new Properties();
try{
m_props.load(new FileInputStream(PFILE));
}catch(Exception e){
e.printStackTrace();
}
}
//静态工厂方法
public synchronized static ConfigManager getInstance(){
return m_instance;
}
//读取一个属性项
//@name_名称,defaultVal_属性项的默认值,
public final Object getConfigItem(String name, Object defaultVal){
long newTime = m_file.lastModified();
if(newTime == 0){
if(m_lastModifiedTime == 0){
System.err.println(PFILE + "file does not exits!");
}else{
System.err.println(PFILE + "file was deleted!");
}
return defaultVal;
}else if(newTime > m_lastModifiedTime){
m_props.clear();
try{
m_props.load(new FileInputStream(PFILE));
}catch(Exception e){
e.printStackTrace();
}
}
m_lastModifiedTime = newTime;
Object val = m_props.getProperty(name);
if(cal == null){
return defaultVal;
}else{
return val;
}
}
}
5、不完全单例类
public class LazySingleton{
private static LazySingleton m_instance = null;
//公开的构造子
public LazySingleton(){}
public static synchronized LazySingleton getInstance(){
if(m_instance == null){
m_instance = new LazySingleton();
}
return m_instance;
}
}
6、相关模式:
1) 简单工厂模式:单例模式使用了简单工厂模式来提供自己的实例
最常见的目录服务包括LDAP和DNS————(命名—服务)将一个对象与一个名字联系起来,使得客户可以通过对象的名字找到这个对象
目录服务允许对象有属性,这样客户端通过查询找到对象后,可以进一步得到对象的属性,或者反过来根据属性查询对象
DNS————即域名服务,电脑用户在网络上找到其他的电脑用户必须通过域名服务。在国际网络以及任何一个建立在TCP/IP基础上的
网络上的,每一台电脑都有一个唯一的IP地址。
MX记录——就是邮件交换记录。电子邮件服务器记录指定一台服务器接受某个域名服务器的邮件,也就是说,发往jeffcorp.com 的邮
件将发往 mail.jeffcorp.com 的服务器,完成此任务的MX纪录应当像下面这样:
jeffcorp.com.IN MX 10 mail.jeffcorp.com
在上面的这个记录的最左边是国际网络上使用的电子邮件域名,第三列是一个数字10,它代表此服务器的优先权是10。通常来说,一个
大型的系统会有数台电子邮件服务器,这些服务器可以依照优先权作为候补服务器使用,优先权不需是一个正整数,这个数字越低,表明
优先权越高。
JNDI的全称是java命名和地址界面,其目的是为java系统提供支持各种目录类型的一个一般性的访问界面
JNDI架构由JNDI API 和 JNDI SPI组成。JNDI API使得一个java应用程序可以使用一系列的命名和目录服务。JNDI SPI是为服务提
供商,包括目录服务提供商准备的,它可以让各种命名和目录服务能够以对应用程序透明的方式插入到系统里。
JNDI API由以下四个库组成:
1) javax.naming: 包括了使用命名服务的类和接口。
2) javax.naming.directory: 扩展了核心库javax.naming的功能,提供目录访问功能。
3) javax.naming.event: 包括了命名和目录服务中所需要的时间同志机制的类和接口
4) javax.naming.ldap: 包括了命名和目录服务中支持LDAP(v3)所需要的类和接口
JNDI SPI的库为:
javax.naming.spi: 包括的类和接口允许各种的命名和目录服务提供商能将自己的软件服务构建加入到JNDI架构中去
在JNDI里,所有的命名和目录操作都是相对于某一个context(环境)而言,JNDI并没有任何的绝对根目录。JNDI定义一个初始环境对象
称为InitialContext,来提供命名和目录操作的起始点。一旦得到了初始环境,就可以使用初始环境查询其他环境和对象。
//MailServer
public class MailServer{
private int priority;
private String server;
//优先权的赋值方法
public void setPriority(int priority){
this.priority = priority;
}
//服务器名的赋值方法
public void setServer(String server){
this.server = server;
}
public int getPriority(){
return priority;
}
public String getServer(){
return server;
}
}
//系统核心类(MXList)
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.String.Tokenizer;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.Attributes;
import javax.naming.directory.Attribute;
public class MXList{
private static MXList mxl = null;
private Vector list = null;
private static final String FACTORY_ENTRY = "java.naming.factory.initial";
private static final String FACTORY_CLASS = "com.sun.jndi.dns.DnsContextFactory";
private static final String PROVIDER_ENTRY = "java.naming,provider.url";
private static final String MX_TYPE = "MX";
private String dnsUrl = null;
private String domainName = null;
private MXList(){}
private MXList(String dnsUrl, String domainName) throws Exception{
this.dnsUrl = dnsUrl;
this.domainName = domainName;
this.list = getMXRecords(dnsUrk, domainName);
}
//静态工厂方法
public static synchronized MXList getInstance(String dnsUrl,String domainName) throws Exception{
if(mxl == null){
mxl = new MXList(dnsUrl, domainName);
}
return mxl;
}
//聚集方法,提供聚集元素
public MailServer elementAt(int index) throws Exception{
return (MailServer)list.elementAt(index);
}
//聚集方法,提供聚集大小
public int size(){
return list.size();
}
//辅助方法,向DNS服务器查询MX记录
private Vector getMXRecords(String providerUrl, String domainName) throws Exception{
//设置环境性质
Hashtable env= new Hashtable();
env.put(FACTORY_ENTRY, FACTORY_CLASS);
env.put(PROVIDER_ENTRY, providerUrl);
//创建环境对象
DirContext dirContext = new InitialDirContext(env);
Vector records = new Vector(10);
//读取环境对象的属性
Attributes attrs = dirContext.getAttributes(domainName,new String[]{MX_TYPE});
for(NamingEnumeration ne = attrs.getAll(); ne != null && ne.hasMoreElements();){
Attribute attr = (Attribute)ne.next();
NamingEnumeration e = attr.getAll();
while(e.hasMoreElements()){
String element = e.nextElement().toString();
StringTokenizer tokenizer = new StringTokenizer(element, "");
MailServer mailServer = new MailServer();
String token1 = tokenizer.nextToken();
String token2 = tokenizer.nextToken();
if(token1 != null && token2 != null){
mailServer.setPriority(Integer.valueOf(token1).inValue());
mailServer.setServer(token2);
records.addElement(mailServer);
}
}
}
System.out.println("List created.");
return records;
}
}
//客户端类
public class Client{
private static MXList mxl;
public static void main(String args[]) throws Exception{
mxl = MXList.getInstance("dns://dns01390.ny.jeffcorp.com", "jeffcorp.com");
for(int i = 0; i < mxl.size(); i ++){
System.out.println((i + 1) + ") priority = " + ((MailServer)mxl.elementAt(i)).
getPriority() + ", Name = " + ((MailServer)mxl.elementAt(i)).getServer());
}
}
}