??xml version="1.0" encoding="utf-8" standalone="yes"?> jungleford如是?/STRONG> 已经有一个多月没有搭理blog了,原因很多Q譬如实验室的项目正在收工,巨忙Q譬如找工作及其相关的事情;而且二月份大部分旉是陪老爹老妈Q家里拨L速度可想而知……但主要q是没有扑ֈ一个合适的topicQ或者说q段旉懒了Q毕业前期l合症)Q净在看《汉武大帝》和历史斚w的书Q还有其它ؕ七八p的闲书Q就是没有认真地玩JavaQ哈哈!现在工作差不多落实了Q好在不太烂,资青年jungleford的生zd开始步入正轨了Q以上是新年里的一些废话?BR> 今天E微聊一点关于“程序状态保存”方面的问题Q我们很Ҏ׃惛_?STRONG>序列?/STRONG>”(SerializationQ有的书上又译为“顺序化”或者“串行化”,但“串行”一词L让我联想到通信和硬件接口,所以我更习惯于“序列化”的叫法Q何况这U叫法是有来头的Q后面我会谈?A href="http://www.tkk7.com/licheng700/jungleford/archive/2005/04/02/2763.html#1">q个名称的由?/FONT>Q,当然Q序列化是一U方便有效的数据存取方式Q但它还有更加广泛的应用。广义上Ԍ是讨论一下I/O的一些应用? 文gI/OQ文件流→序列化 ?STRONG>文g?/STRONG> ?STRONG>XML ?STRONG>序列?/STRONG> 但有一个条Ӟ?STRONG>你要序列化的cd中,它的每个属性都必须是是“可序列化”的。这句话说v来有Ҏ口,其实所?STRONG>基本cdQ就是intQcharQboolean之类的)都是“可序列化”的Q而你可以看看JDK文档Q会发现很多cd实已l实CSerializableQ即已经是“可序列化”的了)Q于是这些类的对象以及基本数据类型都可以直接作ؓ你需要序列化的那个类的内部属性。如果碰C不是“可序列化”的属性怎么办?对不P那这个属性的c还需要事先实现Serializable接口Q如此递归Q?STRONG>直到所有属性都是“可序列化”的? 从实际应用上看来Q“Serializable”这个接口ƈ没有定义MҎQ仿佛它只是一?STRONG>标记Q或者说像是Java的关键字Q而已Q一旦虚拟机看到q个“标记”,׃试调用自n预定义的序列化机?/STRONG>Q除非你在实现Serializable接口的同时还定义了私有的readObject()或writeObject()Ҏ。这一点很奇怪。不q你要是不愿意让pȝ使用~省的方式进行序列化Q那必d义上面提到的两个ҎQ? 譬如你可以在上面的writeObject()里调用默认的序列化方法ObjectOutputStream.defaultWriteObject();譬如你不愿意某些敏感的属性和信息序列化,你也可以调用ObjectOutputStream.writeObject()Ҏ明确指定需要序列化那些属性。关于用?A href="http://www.tkk7.com/licheng700/jungleford/archive/2005/04/02/2763.html#4">可定制的序列化方?/FONT>Q我们将在后面提到? ?STRONG>Bean 如果一个bean是如下格式: 那么通过XMLEcoder序列化出来的XML文ghq样的Ş式: ?A target=_blank>AWT?A target=_blank>Swing中很多可视化lg都是beanQ当然也是可以用q种方式序列化的Q下面就是从JDK文档中摘录的一?A target=_blank>JFrame序列化以后的XML文gQ? 因此但你惌保存的数据是一些不是太复杂的类型的话,把它做成bean再序列化也不׃ؓ一U方便的选择? ?STRONG>Properties p获得全部环境变量的列表: -- listing properties -- java.vm.specification.vendor=Sun Microsystems Inc. 你可以这样保存一个properties文gQ? ?STRONG>Preferences 你可以按如下步骤保存数据Q? Preferences myPrefs1 = Preferences.userNodeForPackage(this);// q种Ҏ是在“HKEY_CURRENT_USER\”下按当前类的\径徏立一个注册表?/SPAN> |络I/OQSocket→RMI ?STRONG>Socket ?STRONG>RMI 数据库I/OQCMP、Hibernate ?STRONG>什么是“Persistence?/STRONG> ?STRONG>CMP和Hibernate 序列化再探讨 从以上技术的讨论中我们不难体会到Q序列化是Java之所以能够出色地实现光吹的两大卖点——分布式QdistributedQ和跨^収ͼOS independentQ的一个重要基。TIJQ即?A target=_blank>Thinking in Java”)谈到I/OpȝӞ把序列化UCؓ“lightweight persistence”——“轻量的持久化”,q确实很有意思?/P>
?STRONG>Z么叫做“序列”化Q?/STRONG> import java.io.*; class B implements Serializable class C implements Serializable A a; 注意Q这里我们在实例化a和b的时候,有意让他们的c属性用同一个Ccd对象的引用,譬如c1Q那么请试想一下,但我们序列化a和b的时候,它们的c属性在外部字节(当然可以不仅仅是文gQ里保存的是一份拷贝还是两份拷贝呢Q序列化在这里用的是一U?STRONG>cM于“指针”的ҎQ它为每个被序列化的对象标上一个?STRONG>序列?/STRONG>”(serial numberQ,但序列化一个对象的时候,如果其某个属性对象是已经被序列化的,那么q里只向输出写入该属性的序列P从字节流恢复被序列化的对象时Q也Ҏ序列h到对应的来恢复。这是“序列化”名U的由来Q这里我们看到“序列化”和“指针”是极相似的Q只不过“指针”是内存I间的地址链,而序列化用的?STRONG>外部中的“序列号䏀?/STRONG>?BR> 使用“序列号”而不是内存地址来标识一个被序列化的对象Q是因ؓ从流中恢复对象到内存Q其地址可能未必是原来的地址了——我们需要的只是q些对象之间的引用关p,而不是死板的原始位置Q这在RMI中就更是必要Q在两台不同的机器之间传递对象()Q根本就不可能指望它们在两台机器上都h相同的内存地址? ?STRONG>更灵zȝ“序列化”:transient属性和Externalizable 乍一看这和上面的writeObject()和readObject()几乎差不多,但Serializable和Externalizable走的是两个不同的程QSerializable在对象不存在的情况下Q就可以仅凭外部的字节序列把整个对象重徏出来Q但Externalizable在重建对象时Q先是调用该cȝ默认构造函敎ͼ即不含参数的那个构造函敎ͼ使得内存中先有这么一个实例,然后再调用readExternalҎ对实例中的属性进行恢复,因此Q如果默认构造函C和readExternalҎ中都没有赋值的那些属性,特别他们是非基本cd的话Q将会是I(nullQ。在q里需要注意的是,transient只能用在对Serializable而不是Externalizable的实现里?/STRONG>? ?STRONG>序列化与克隆 一点启C?/FONT> 作ؓ一个实际的应用Q我在写那个易的邮g客户端JExp的时候曾l对比过好几U保存Message对象Q主要是几个关键属性和邮g的内容)到本地的ҎQ譬如XML、Properties{,最后还是选择了用序列化的方式Q因U方法最单, 大约可算是“学以致用”Ş。这里“存取程序状态”其实只是一个引子话题Ş了,我想说的是——就如同前面我们讨论的关于logging的话题一样——在Java面前对同一个问题你可以有很多种solutionQ熟悉文件操作的Q你可能会觉得Properties、XML或Bean比较方便Q然后又发现了还有Preferencesq么一个东东,大概又会感慨“天外有天”了Q等C接触了很多种新方法以后,l果又会“殊途同归”,重新反省Serialization机制本n。这不仅是JavaQ科学也是同L道理?
文g操作是最单最直接也是最Ҏ惛_的一U方式,我们说的文g操作不仅仅是通过FileInputStream/FileOutputStreamq么“裸”的方式直接把数据写入到本地文gQ像我以前写的一个扫L游戏JavaMine是q样保存一局的状态的Q,q样比较“底层”了?
主要cMҎ
描述
FileInputStream.read()
从本地文件读?STRONG>二进制格?/STRONG>的数?/FONT>
FileReader.read()
从本地文件读?STRONG>字符Q文本)数据
FileOutputStream.write()
保存二进制数据到本地文g
FileWriter.write()
保存字符数据到本地文?/FONT>
和上面的单纯的I/O方式相比QXML显得“高档”得多,以至于成ZU数据交换的标准。以DOM方式ZQ它兛_的是首先在内存中构造文档树Q数据保存在某个l点上(可以是叶子结点,也可以是标签l点的属性)Q构造好了以后一ơ性的写入到外部文Ӟ但我们只需要知道文件的位置Qƈ不知道I/O是怎么操作的,XML操作方式可能多数Z实践q,所以这里也只列出相关的ҎQ供初学者预先了解一下。主要的包是javax.xml.parsersQ?A target=_blank>org.w3c.domQ?A target=_blank>javax.xml.transform?
主要cMҎ
描述
DocumentBuilderFactory.newDocumentBuilder().parse()
解析一个外部的XML文gQ得C个Document对象的DOM?/FONT>
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
初始化一DOM?/FONT>
Document.getDocumentElement(). appendChild()
Z个标{Ҏ加一个子l点
Document.createTextNode()
生成一个字W串l点
Node.getChildNodes()
取得某个l点的所有下一层子l点
Node.removeChild()
删除某个l点的子l点
Document. getElementsByTagName()
查找所有指定名U的标签l点
Document.getElementById()
查找指定名称的一个标{点,如果有多个符合,则返回某一个,通常是第一?/FONT>
Element.getAttribute()
取得一个标{某个属性的的?/FONT>
Element.setAttribute()
讄一个标{某个属性的的?/FONT>
Element.removeAttribute()
删除一个标{某个属?/FONT>
TransformerFactory.newInstance().newTransformer().transform()
一DOM树写入到外部XML文g
使用基本的文件读写方式存取数据,如果我们仅仅保存相同cd的数据,则可以用同一U格式保存,譬如在我的JavaMine中保存一个盘局Ӟ需要保存每一个方格的坐标、是否有地雷Q是否被d{,q些信息l合成一个“复合类型”;相反Q如果有多种不同cd的数据,那我们要么把它分解成若干部分Q以相同cdQ譬如StringQ保存,要么我们需要在E序中添加解析不同类型数据格式的逻辑Q这很不方ѝ于是我们期望用一U比较“高”的层次上处理数据,E序员应?STRONG>花尽可能的旉和代码对数据q行解析Q事实上Q序列化操作为我们提供了q样一条途径?BR> 序列化(SerializationQ大家可能都有所接触Q它可以把对象以某种特定的编码格式写入或从外部字节流Q即ObjectInputStream/ObjectOutputStreamQ中d。序列化一个对象非怹单,仅仅实现一?A target=_blank>Serializable接口卛_Q甚至都不用为它专门dMҎQ?
public class MySerial implements java.io.Serializable
{
...
}
主要cMҎ
描述
ObjectOutputStream.writeObject()
一个对象序列化到外部字节流
ObjectInputStream.readObject()
从外部字节流dq新构造对?/FONT>
public class MySerial implements java.io.Serializable
{
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
...
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
...
}
...
}
上面的序列化只是一U基本应用,你把一个对象序列化到外部文件以后,用notepad打开那个文gQ只能从为数不多的一些可dW中猜到q是有关q个cȝ信息文gQ这需要你熟悉序列化文件的字节~码方式Q那是比较痛苦的(?A target=_blank>《Core Java 2》第一?/FONT>里提C相关~码方式Q有兴趣的话可以查看参考资?/FONT>Q,某些情况下我们可能需要被序列化的文gh更好的可L。另一斚wQ作为Javalg的核心概念?A target=_blank>JavaBeans”,从JDK 1.4开始,其规范里也要求支持文本方式的?STRONG>长期的持久化”(long-term persistenceQ?BR> 打开JDK文档Q?A target=_blank>java.beans?/FONT>里的有一个名为?A target=_blank>Encoder”的c,q就是一个可以序列化bean的实用类。和它相关的两个主要cLXMLEcoder?A target=_blank>XMLDecoderQ显Ӟq是以XML文g的格式保存和dbean的工兗他们的用法也很单,和上面ObjectOutputStream/ObjectInputStream比较cM?
主要cMҎ
描述
XMLEncoder.writeObject()
一个对象序列化到外部字节流
XMLDecoder.readObject()
从外部字节流dq新构造对?/FONT>
public class MyBean
{
int i;
char[] c;
String s;
...(get和set操作省略)...
}
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.4.0" class="java.beans.XMLDecoder">
<object class="MyBean">
<void property="i">
<int>1</int>
</void>
<void property="c">
<array class="char" length="3">
<void index="0">
<int>a</int>
</void>
<void index="1">
<int>b</int>
</void>
<void index="2">
<int>c</int>
</void>
</array>
</void>
<void property="s">
<string>fox jump!</string>
</void>
</object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.0" class="java.beans.XMLDecoder">
<object class="javax.swing.JFrame">
<void property="name">
<string>frame1</string>
</void>
<void property="bounds">
<object class="java.awt.Rectangle">
<int>0</int>
<int>0</int>
<int>200</int>
<int>200</int>
</object>
</void>
<void property="contentPane">
<void method="add">
<object class="javax.swing.JButton">
<void property="label">
<string>Hello</string>
</void>
</object>
</void>
</void>
<void property="visible">
<boolean>true</boolean>
</void>
</object>
</java>
在以前我ȝ的一关于集合框架的文章里提到q,Properties是历史集合类的一个典型的例子Q这里主要不是介l它的集合特性。大家可能都l常接触一?STRONG>配置文gQ如Windows的ini文gQApache的conf文gQ还有Java里的properties文g{,q些文g当中的数据以“关键字-值”对的方式保存。?STRONG>环境变量”这个概念都知道吧,它也是一U“key-value”对Q以前也常常看到版上问“如何取得系l某某信息”之cȝ问题Q其实很多都保存在环境变量里Q只要用一?
System.getProperties().list(System.out);
java.runtime.name=Java(TM) 2 Runtime Environment, Stand...
sun.boot.library.path=C:\Program Files\Java\j2re1.4.2_05\bin
java.vm.version=1.4.2_05-b04
java.vm.vendor=Sun Microsystems Inc.
java.vendor.url=http://java.sun.com/
path.separator=;
java.vm.name=Java HotSpot(TM) Client VM
file.encoding.pkg=sun.io
user.country=CN
sun.os.patch.level=Service Pack 1
java.vm.specification.name=Java Virtual Machine Specification
user.dir=d:\my documents\目\eclipse\SWTDemo
java.runtime.version=1.4.2_05-b04
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs=C:\Program Files\Java\j2re1.4.2_05\li...
os.arch=x86
java.io.tmpdir=C:\DOCUME~1\cn2lx0q0\LOCALS~1\Temp\
line.separator=
user.variant=
os.name=Windows XP
sun.java2d.fontpath=
java.library.path=C:\Program Files\Java\j2re1.4.2_05\bi...
java.specification.name=Java Platform API Specification
java.class.version=48.0
java.util.prefs.PreferencesFactory=java.util.prefs.WindowsPreferencesFac...
os.version=5.1
user.home=D:\Users\cn2lx0q0
user.timezone=
java.awt.printerjob=sun.awt.windows.WPrinterJob
file.encoding=GBK
java.specification.version=1.4
user.name=cn2lx0q0
java.class.path=d:\my documents\目\eclipse\SWTDemo\bi...
java.vm.specification.version=1.0
sun.arch.data.model=32
java.home=C:\Program Files\Java\j2re1.4.2_05
java.specification.vendor=Sun Microsystems Inc.
user.language=zh
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.4.2_05
java.ext.dirs=C:\Program Files\Java\j2re1.4.2_05\li...
sun.boot.class.path=C:\Program Files\Java\j2re1.4.2_05\li...
java.vendor=Sun Microsystems Inc.
file.separator=\
java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
sun.cpu.isalist=pentium i486 i386
主要cMҎ
描述
load()
从一个外部流d属?/FONT>
store()
属性保存到外部(特别是文Ӟ
getProperty()
取得一个指定的属?/FONT>
setProperty()
讄一个指定的属?/FONT>
list()
列出q个Properties对象包含的全部“key-value”对
System.getProperties()
取得pȝ当前的环境变?/FONT>
Properties prop = new Properties();
prop.setProperty("key1", "value1");
...
FileOutputStream out = new FileOutputStream("config.properties");
prop.store(out, "--q里是文件头Q可以加入注?-");
如果我说Java里面可以不用JNI的手D|作Windows的注册表你信不信Q很多Y件的菜单里都有“Setting”或“Preferences”这L选项用来讑֮或修改Y件的配置Q这些配|信息可以保存到一个像上面所q的配置文g当中Q如果是Windowsq_下,也可能会保存到系l注册表中。从JDK 1.4开始,Java?A target=_blank>java.util下加入了一个专门处理用户和pȝ配置信息?A target=_blank>java.util.prefs包,其中一个类Preferences是一U比较“高U”的玩意。从本质上讲QPreferences本n是一个与q_无关的东西,但不同的OS对它的SPIQService Provider InterfaceQ的实现却是与^台相关的Q因此,在不同的pȝ中你可能看到首选项保存为本地文件、LDAP目录V数据库条目{,像在Windowsq_下,它就保存Cpȝ注册表中。不仅如此,你还可以把首选项导出为XML文g或从XML文g导入?
主要cMҎ
描述
systemNodeForPackage()
Ҏ指定的Class对象得到一个Preferences对象Q这个对象的注册表\径是从“HKEY_LOCAL_MACHINE\”开始的
systemRoot()
得到以注册表路径HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs 为根l点的Preferences对象
userNodeForPackage()
Ҏ指定的Class对象得到一个Preferences对象Q这个对象的注册表\径是从“HKEY_CURRENT_USER\”开始的
userRoot()
得到以注册表路径HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs 为根l点的Preferences对象
putXXX()
讄一个属性的|q里XXX可以为基本数值型cdQ如int、long{,但首字母大写Q表C参Cؓ相应的类型,也可以不写而直接用putQ参数则为字W串
getXXX()
得到一个属性的?/FONT>
exportNode()
全部首选项导出Z个XML文g
exportSubtree()
部分首选项导出Z个XML文g
importPreferences()
从XML文g导入首选项
Preferences myPrefs2 = Preferences.systemNodeForPackage(this);// q种Ҏ是在“HKEY_LOCAL_MACHINE\”下按当前类的\径徏立一个注册表?/SPAN>
Preferences myPrefs3 = Preferences.userRoot().node("com.jungleford.demo");// q种Ҏ是在“HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表?/SPAN>
Preferences myPrefs4 = Preferences.systemRoot().node("com.jungleford.demo");// q种Ҏ是在“HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表?/SPAN>
myPrefs1.putInt("key1", 10);
myPrefs1.putDouble("key2", -7.15);
myPrefs1.put("key3", "value3");
FileOutputStream out = new FileOutputStream("prefs.xml");
myPrefs1.exportNode(out);
Socket~程可能大家都很熟,所以就不多讨论了,只是说通过socket把数据保存到q端服务器或从网lsocketd数据也不׃ؓ一U值得考虑的方式?/P>
RMI机制其实是RPCQ远E过E调用)的Java版本Q它使用socket作ؓ基本传输手段Q同时也是序列化最重要的一个应用。现在网l传输从~程的角度来看基本上都是以流的方式操?/STRONG>Qsocket是一个例子,对象{换成字节的一个重要目标就是ؓ了方便网l传输?BR> 惌一下传l的单机环境下的E序设计Q对于Java语言的函敎ͼҎQ调用(注意与C语言函数调用的区别)的参C递,会有两种情况Q如果是基本数据cdQ这U情况下和C语言是一LQ采?STRONG>g?/STRONG>方式Q如果是对象Q则传递的是对象的引用Q包括返回g是引用,而不是一个完整的对象拯Q试想一下在不同的虚拟机之间q行Ҏ调用Q即使是两个完全同名同类型的对象他们也很可能是不同的引用Q此外对于方法调用过E,׃被调用过E的压栈Q内存“现场”完全被被调用者占有,当被调用Ҏq回Ӟ才将调用者的地址写回到程序计数器QPCQ,恢复调用者的状态,如果是两个虚拟机Q根本不可能用简单压栈的方式来保存调用者的状态。因为种U原因,我们才需要徏立RMI通信实体之间的“代理”对象,譬如“存根”就相当于远E服务器对象在客h上的代理Qstub是q么来的Q当然这是后话了?BR> 本地对象与远E对象(未必是物理位|上的不同机器,只要不是在同一个虚拟机内皆为“远E”)之间传递参数和q回|可能有这么几U情形:
由此可见Q序列化在RMI当中占有多么重要的地位?
用过VMWare的朋友大概都知道当一个guest OS正在q行的时候点几ZSuspend”将虚拟OS挂vQ它会把整个虚拟内存的内容保存到盘上,譬如你ؓ虚拟OS分配?28M的运行内存,那挂起以后你会在虚拟OS所在的目录下找C个同h128M的文Ӟq就是虚拟OS内存的完整镜像!q种内存的镜像手D其实就是“Persistence”(持久化)概念的由来?/P>
因ؓ我对J2EE的东西不是太熟悉Q随便找了点材料看看Q所以担心说的不CQ这ơ就不作具体ȝ了,学习……真是一件痛苦的事情
开场白里我说更习惯于把“Serialization”称为“序列化”而不是“串行化”,q是有原因的。介l这个原因之前先回顾一些计机基本的知识,我们知道C计算机的内存I间都是U性编址的(什么是“线性”知道吧Q就是一个元素只有一个唯一的“前驱”和唯一的“后l”,当然头尾元素是个例外Q对于地址来说Q它的下一个地址当然不可能有两个Q否则就乱套了)Q“地址”这个概忉|q到数据l构Q就相当于“指针”,q个在本U低q大概q道了。注意了Q既然是U性的Q那“地址”就可以看作是内存空间的?STRONG>序号”,说明它的l织是有序的,“序号”或者说“序列号”正是“Serialization”机制的一U体现。ؓ什么这么说呢?譬如我们有两个对象a和b,分别是类A和B的实例,它们都是可序列化的,而A和B都有一个类型ؓC的属性,Ҏ前面我们说过?A href="http://www.tkk7.com/licheng700/jungleford/archive/2005/04/02/2763.html#2">原则QC当然也必L可序列化的?
...
class A implements Serializable
{
C c;
...
}
{
C c;
...
}
{
...
}
B b;
C c1;
...
Serializable实很方便,方便C几乎不需要做M额外的工作就可以L内存中的对象保存到外部。但有两个问题得Serializable的威力收到束~:
一个是效率问题Q《Core Java 2》中指出QSerializable使用pȝ默认的序列化机制会媄响Y件的q行速度Q因为需要ؓ每个属性的引用~号和查P再加上I/O操作的时_I/O和内存读写差的可是一个数量的大)Q其代h当然是可观的?BR> 另一个困扰是“裸”的Serializable不可定制Q傻乎乎C么都l你序列化了Q不你是不是想q么做。其实你可以有至三U定制序列化的选择。其中一U前面已l提CQ就是在implements Serializable的类里面dU有的writeObject()和readObject()ҎQ这USerializable׃怺Q?IMG height=19 src="http://www.tkk7.com/Emoticons/teeth_smile.gif" width=19 border=0>Q,在这两个Ҏ里,该序列化什么,不该序列化什么,那就׃说了了Q你当然可以在这两个Ҏ体里面分别调用ObjectOutputStream.defaultWriteObject()和ObjectInputStream.defaultReadObject()仍然执行默认的序列化动作Q那你在代码上不做无用功了Q呵呵)Q也可以用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()Ҏ对你中意的属性进行序列化。但虚拟Z看到你定义了q两个方法,它就不再用默认的机制了?BR> 如果仅仅Z跌某些属性不让它序列化,上面的动作似乎显得麻烦,更简单的Ҏ是对不想序列化的属性加?SPAN class=style3>transient关键字,说明它是个“暂态变量”,默认序列化的时候就不会把这些属性也塞到外部里了。当Ӟ你如果定义writeObject()和readObject()Ҏ的化Q仍然可以把暂态变量进行序列化。题外话Q像transient?SPAN class=style3>violate?SPAN class=style3>finally?/FONT>assertq样的关键字初学者可能会不太重视Q而现在有的公司招聘就偏偏喜欢问这L问题
再一个方案就是不实现Serializable而改成实?A target=_blank>Externalizable接口。我们研I一下这两个接口的源代码Q发现它们很cMQ甚臛_易淆。我们要C的是QExternalizable默认q?STRONG>不保存Q何对象相关信?/STRONG>QQ何保存和恢复对象的动作都是你自己定义的。Externalizable包含两个public的方法:
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
从“可序列化”的递归定义来看Q一个序列化的对象貌似对象内存映象的外部克隆Q如果没有共享引用的属性的化,那么应该是一?STRONG>深度克隆。关于克隆的话题有可以谈很多Q这里就不细说了Q有兴趣的话可以参?A target=_blank>IBM developerWorks上的一文章:JAVA中的指针,引用及对象的clone
]]>
Constructs a FileWriter object given a File object.
append
- if true
, then bytes will be written to the end of the file rather than the beginning
2.cjava.io.RandomAccessFile extends Object implements DataOutput, DataInput 中方?BR> seek(long pos)当前操作指针移到文件末?
2.关于隔行写入文gjava.io.BufferedWritercM的方?/STRONG>newLine()
]]>
ccjsmile (http://ijsp.net)
q是我在学习Log4j时做的一点笔讎ͼ希望对各位朋友有一点帮助。我?/SPAN>mail:ccjsmile@sohu.comQ希望能与您q行讨论^_*
Log4j 是一个开放源码项目,它是一个日志管理程序?/SPAN>
Log4j的优点:
1. 方便的调试信?/SPAN>;
2. 日志以各U丰富的Q主要是文gQŞ式保留,用于以后分析;
~点Q减慢程序运行速度.
(A) 其中Q?/SPAN>level 是日志记录的优先U,分ؓOFF?/SPAN>FATAL?/SPAN>ERROR?/SPAN>WARN?/SPAN>INFO?/SPAN>DEBUG?/SPAN>ALL或者您定义的别?/SPAN>Log4j只用四个别,优先U从高到低分别是ERROR?/SPAN>WARN?/SPAN>INFO?/SPAN>DEBUG。通过在这里定义的U别Q您可以控制到应用程序中相应U别的日志信息的开兟뀂比如在q里定义?/SPAN>INFOU别Q则应用E序中所?/SPAN>DEBUGU别的日志信息将不被打印出来?/SPAN>
appenderName是指定日志信息输出到哪个地斏V您可以同时指定多个输出目的地?/SPAN>
(B) 其中Q?/SPAN>Log4j提供?/SPAN>appender有以下几U:
org.apache.log4j.ConsoleAppenderQ控制台Q,
org.apache.log4j.FileAppenderQ文ӞQ?/SPAN>
org.apache.log4j.DailyRollingFileAppenderQ每天生一个日志文ӞQ?/SPAN>org.apache.log4j.RollingFileAppenderQ文件大到达指定尺寸的时候生一个新的文ӞQ?/SPAN>
org.apache.log4j.WriterAppenderQ将日志信息以流格式发送到L指定的地方)
(C) 其中Q?/SPAN>Log4j提供?/SPAN>layout有以下几U:
org.apache.log4j.HTMLLayoutQ以HTML表格形式布局Q,
org.apache.log4j.PatternLayoutQ可以灵zd指定布局模式Q,
org.apache.log4j.SimpleLayoutQ包含日志信息的U别和信息字W串Q,
org.apache.log4j.TTCCLayoutQ包含日志生的旉、线E、类别等{信息)
下面介绍一?/SPAN>log4j?/SPAN>web中应用的例子Q?/SPAN>
q是一个用?/SPAN>log4j初始化的servlet
package net.ijsp.log4j;
import org.apache.log4j.PropertyConfigurator;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletException;
public class InitLog4j extends HttpServlet {
public void init() throws ServletException {
PropertyConfigurator.configure("D:/resin/webapps/log4j/web-inf/classes/log4j.properties");
System.out.println("ok");
}
}
在上q文件中我们发现需要一?/SPAN>log4j.properties的文Ӟ他的存放路径为:D:/resin/webapps/log4j/web-inf/classes/log4j.properties
q个properties的文件内容如下:
#log4j.properties
#Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO,A1
#A1 is set to be a ConsoleAppender.
#log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1=org.apache.log4j.RollingFileAppender
log4j.appender.A1.File=example11.log
#A1 uses PatternLayout
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c \n- %m%n\n"
log4j.logger.ltestlog4j=INFO,A2
log4j.appender.A2=org.apache.log4j.ConsoleAppender
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%d [%t] %-5p %c \n- %m%n\n"
#log4j.appender.A1.MaxFileSize=1000KB
# Keep one backup file
#log4j.appender.A1.MaxBackupIndex=1
因ؓq是一?/SPAN>servlet文gQ同时我们还要修?/SPAN>web.xml文g
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>log4jinit</servlet-name>
<servlet-class>net.ijsp.log4j.InitLog4j</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
下面q两个ؓ试文gQ?/SPAN>
package net.ijsp.log4j;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletException;
public class Test {
public Test() {}
static Logger logger =Logger.getRootLogger();
static Logger logger1 = Logger.getLogger("ltestlog4j");
public void t() {
logger.error("sssssssssss");
System.out.println(logger);
logger1.error("kjdlfkj");
System.out.println("ddddddddddddddd");
}
}
<%@page import ="net.ijsp.log4j.*"%>
<%
Test t = new Test();
t.t();
%>
SunOS 5.9
login: oracle
Password:
Last login: Mon Sep 26 06:39:46 from 10.211.12.233
Sun Microsystems Inc. SunOS 5.9 Generic May 2002
You have mail.
[oracle@db oracle]$ sqlplus cpf/cpf
SQL*Plus: Release 9.2.0.5.0 - Production on Mon Sep 26 09:24:44 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.5.0 - Production
SQL> drop table bs_banksetting
2 ;
Table dropped.
SQL> drop table bs_bankaccountinfo;
Table dropped.
SQL> drop table bs_clientsetting;
Table dropped.
SQL> drop table bs_countrysetting;
Table dropped.
SQL> drop table bs_currencysetting;
Table dropped.
SQL> exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.5.0 - Production
[oracle@db oracle]$ ftp 211.155.247.197
Connected to 211.155.247.197.
220 fsdev FTP server ready.
Name (211.155.247.197:oracle): root
331 Password required for root.
Password:
230 User root logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd bankportal
250 CWD command successful.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for file list.
AcctDataSourceType.class
AcctDataSourceType.java
CurrencyMappingDAO_oracle.class
SettingData.dmp
account
addp.jsp
bankportal.properties
create_table.sql
datamaintain
example.xls
expbp0926.dmp
expbp1420.dmp
iTreasury-bankportal.ear
itreasury.properties
menubp.dmp
model.xls
query
v003.jsp
v005.jsp
226 Transfer complete.
316 bytes received in 0.016 seconds (19.33 Kbytes/s)
ftp> get expbp0926.dmp
200 PORT command successful.
150 Opening BINARY mode data connection for expbp0926.dmp (47104 bytes).
226 Transfer complete.
local: expbp0926.dmp remote: expbp0926.dmp
47104 bytes received in 0.16 seconds (292.70 Kbytes/s)
ftp> bye
221-You have transferred 47104 bytes in 1 files.
221-Total traffic for this session was 48051 bytes in 2 transfers.
221-Thank you for using the FTP service on fsdev.
221 Goodbye.
[oracle@db oracle]$ imp system/manager1 file=expbp0926.dmp fromuser=bp_cpf touser=cpf
Import: Release 9.2.0.5.0 - Production on Mon Sep 26 09:30:09 2005
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to: Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.5.0 - Production
Export file created by EXPORT:V09.02.00 via conventional path
Warning: the objects were exported by BP_CPF, not by you
import done in ZHS16GBK character set and AL16UTF16 NCHAR character set
. importing BP_CPF's objects into CPF
. . importing table "BS_BANKACCOUNTINFO" 150 rows imported
. . importing table "BS_BANKSETTING" 107 rows imported
. . importing table "BS_CLIENTSETTING" 27 rows imported
. . importing table "BS_COUNTRYSETTING" 21 rows imported
. . importing table "BS_CURRENCYSETTING" 53 rows imported
Import terminated successfully without warnings.
[oracle@db oracle]$
import java.io.*;
import java.util.*;
public class TemplateId {
private static Properties p;
private static final TemplateId pi = new TemplateId();
public TemplateId() {
// 从templateId.properties属性文件获得数?BR> InputStream is = getClass()
.getResourceAsStream("templateId.properties");
p = new Properties();
try {
p.load(is);
} catch (IOException ex) {
ex.printStackTrace();
}
}
// 此处的templateId是templateId.properties属性文件中的templateId?BR> public static String getTemplateId() {
return pi.p.getProperty("templateId");
}
public static void main(String args[]) {
System.out.println("templateId=" + getTemplateId()); // 试调用
}
}
templateId.properties文g内容:templateId=FFD4156506-3-2F8CAC7
接下来便是利用空余时间通过JDKQtutorialq行研究了。希望不是半途而废Q也谨以此文CQ以表决心?/P>(注明:q是一个网友的文章,记此是希望和他一起学?共勉 )
说到Java的基本数据类型必谈到的两个cLDataInputStream和DataOutputStream。它们提供了对Java基本数据cd的操作,但是q些Ҏ事实上是在两个重要的接口中定义的DataInput和DataOutputQ它们的功能是把二q制的字节流转换成Java的基本数据类型,同时q提供了从数据中使用UTF-8~码构徏String的功能。有一个重要的cRandomAccessFile实现了DataInput和DataOutput两个接口使得他能够对文g同时q行写和ȝ操作?/P>
在DataInputStream和DataOutputStream两个cM的方法都很简单,基本l构为readXXXX()和writeXXXX()其中XXXX代表基本数据cd或者String。在q里不多讲述Q不q值得一提的是我们有必要读读java中unicode的编码规则,在API doc中有比较详细的介l。通常我们的对象有很多都是由java的基本数据类型构成的Q比如一个h的信息包括姓名,电子信箱Q电话号码和性别{。其实我们可以用DataInputStream中的Ҏ和DataOutputStream中的Ҏ按照一定的序列把数据写入流中再按照相同的序列把他们d出来Q这是我们自己实现的序列化Q这可以用在数据传输中,比如在J2ME联网E序中用序列化机制传输数据。下面我们看看如何自己实现序列化Q首先我们要有两个构造函数其中一个参CؓI?BR>public Account()
{
}
public Account(String userName, String email, int age, boolean gender)
{
this.userName = userName;
this.email = email;
this.age = age;
this.gender = gender;
}
当我们进行序列化的时候也很简单,我们只是往DataOutputStream中按照顺序写入对象的成员变量。例?BR>public void serialize(DataOutputStream dos) throws IOException
{
dos.writeUTF(userName);
dos.writeUTF(email);
dos.writeInt(age);
dos.writeBoolean(gender);
}
当我们进行反序列化的时候则按照相同的顺序从DataInputStream里面d数据q赋值给成员变量。例?BR> public static Account deserialize(DataInputStream dis) throws IOException
{
Account account = new Account();
account.userName = dis.readUTF();
account.email = dis.readUTF();
account.age = dis.readInt();
account.gender = dis.readBoolean();
return account;
}
Z便于调试我们q提供一个toString()的方法打印出对象的实际信息。这是个好的习惯?BR> public String toString()
{
return "UserName = " + userName + " Email = " + email + " age = " + age
+ " gender = " + (gender ? "male" : "female");
}
Z试序列化我们编写下面的E序q行试Q代码比较简单?BR>
package com.j2medev.mingjava;
import java.io.*;
public class TestDataIO
{
public static void main(String[] args) throws IOException
{
Account account = new Account("mingjava","eric.zhan@263.net",25,true);
System.out.println("before serialization.........");
System.out.println(account.toString());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
account.serialize(dos);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()));
Account sAccount = Account.deserialize(dis);
System.out.println("after serialization..........");
System.out.println(sAccount.toString());
dos.close();
dis.close();
}
}
package com.j2medev.mingjava;
import java.io.*;
public class Account
{
private String userName = "";
private String email = "";
private int age = 0;
private boolean gender = false;
public Account()
{
}
public Account(String userName, String email, int age, boolean gender)
{
this.userName = userName;
this.email = email;
this.age = age;
this.gender = gender;
}
public void serialize(DataOutputStream dos) throws IOException
{
dos.writeUTF(userName);
dos.writeUTF(email);
dos.writeInt(age);
dos.writeBoolean(gender);
}
public static Account deserialize(DataInputStream dis) throws IOException
{
Account account = new Account();
account.userName = dis.readUTF();
account.email = dis.readUTF();
account.age = dis.readInt();
account.gender = dis.readBoolean();
return account;
}
public String toString()
{
return "UserName = " + userName + " Email = " + email + " age = " + age
+ " gender = " + (gender ? "male" : "female");
}
}
~译q行E序在控制台输出Q?BR>before serialization.........
UserName = mingjava Email = eric.zhan@263.net age = 25 gender = male
after serialization..........
UserName = mingjava Email = eric.zhan@263.net age = 25 gender = male
序列化成功,后面我将讲述如何在J2ME联网中用序列化机制?/P>