前言:
在我們學(xué)習(xí)Java的過(guò)程中,掌握其中的基本概念對(duì)我們的學(xué)習(xí)無(wú)論是J2SE,J2EE,J2ME都是很重要的,J2SE是Java的基礎(chǔ),所以有必要對(duì)其中的基本概念做以歸納,以便大家在以后的學(xué)習(xí)過(guò)程中更好的理解java的精髓,在此我總結(jié)了30條基本的概念.
Java概述:
目前Java主要應(yīng)用于中間件的開(kāi)發(fā)(middleware)---處理客戶機(jī)于服務(wù)器之間的通信技術(shù),早期的實(shí)踐證明,Java不適合pc應(yīng)用程序的開(kāi)發(fā),其發(fā)展逐漸變成在開(kāi)發(fā)手持設(shè)備,互聯(lián)網(wǎng)信息站,及車(chē)載計(jì)算機(jī)的開(kāi)發(fā).Java于其他語(yǔ)言所不同的是程序運(yùn)行時(shí)提供了平臺(tái)的獨(dú)立性,稱(chēng)許可以在windows,solaris,linux其他操作系統(tǒng)上使用完全相同的代碼.Java的語(yǔ)法與C++語(yǔ)法類(lèi)似,C++/C程序員很容易掌握,而且Java是完全的徹底的面向?qū)ο蟮?其中提出了很好的GC(Garbage Collector)垃圾處理機(jī)制,防止內(nèi)存溢出.
Java的白皮書(shū)為我們提出了Java語(yǔ)言的關(guān)鍵特性.
(1)Easy:Java的語(yǔ)法比C++的相對(duì)簡(jiǎn)單,另一個(gè)方面就是Java能使軟件在很小的機(jī)器上運(yùn)行,基礎(chǔ)解釋其和類(lèi)庫(kù)的支持的大小約為40kb,增加基本的標(biāo)準(zhǔn)庫(kù)和線程支持的內(nèi)存需要增加125kb.
(2)分布式:Java帶有很強(qiáng)大的TCP/IP協(xié)議族的例程庫(kù),Java應(yīng)用程序能夠通過(guò)URL來(lái)穿過(guò)網(wǎng)絡(luò)來(lái)訪問(wèn)遠(yuǎn)程對(duì)象,由于servlet機(jī)制的出現(xiàn),使Java編程非常的高效,現(xiàn)在許多的大的web server都支持servlet.
(3)OO:面向?qū)ο笤O(shè)計(jì)是把重點(diǎn)放在對(duì)象及對(duì)象的接口上的一個(gè)編程技術(shù).其面向?qū)ο蠛虲++有很多不同,在與多重繼承的處理及Java的原類(lèi)模型.
(4)健壯特性:Java采取了一個(gè)安全指針模型,能減小重寫(xiě)內(nèi)存和數(shù)據(jù)崩潰的可能型
(5)安全:Java用來(lái)設(shè)計(jì)網(wǎng)路和分布系統(tǒng),這帶來(lái)了新的安全問(wèn)題,Java可以用來(lái)構(gòu)建防病毒和防攻擊的System.事實(shí)證明Java在防毒這一方面做的比較好.
(6)中立體系結(jié)構(gòu):Java編譯其生成體系結(jié)構(gòu)中立的目標(biāo)文件格式可以在很多處理器上執(zhí)行,編譯器產(chǎn)生的指令字節(jié)碼(Javabytecode)實(shí)現(xiàn)此特性,此字節(jié)碼可以在任何機(jī)器上解釋執(zhí)行.
(7)可移植性:Java中對(duì)基本數(shù)據(jù)結(jié)構(gòu)類(lèi)型的大小和算法都有嚴(yán)格的規(guī)定所以可移植性很好.
(8)多線程:Java處理多線程的過(guò)程很簡(jiǎn)單,Java把多線程實(shí)現(xiàn)交給底下操作系統(tǒng)或線程程序完成.所以多線程是Java作為服務(wù)器端開(kāi)發(fā)語(yǔ)言的流行原因之一
(9)Applet和servlet:能夠在網(wǎng)頁(yè)上執(zhí)行的程序叫Applet,需要支持Java的瀏覽器很多,而applet支持動(dòng)態(tài)的網(wǎng)頁(yè),這是很多其他語(yǔ)言所不能做到的.
基本概念:
1.OOP中惟一關(guān)系的是對(duì)象的接口是什么,就像計(jì)算機(jī)的銷(xiāo)售商她不管電源內(nèi)部結(jié)構(gòu)是怎樣的,他只關(guān)系能否給你提供電就行了,也就是只要知道can or not而不是how and why.所有的程序是由一定的屬性和行為對(duì)象組成的,不同的對(duì)象的訪問(wèn)通過(guò)函數(shù)調(diào)用來(lái)完成,對(duì)象間所有的交流都是通過(guò)方法調(diào)用,通過(guò)對(duì)封裝對(duì)象數(shù)據(jù),很大限度上提高復(fù)用率.
2.OOP中最重要的思想是類(lèi),類(lèi)是模板是藍(lán)圖,從類(lèi)中構(gòu)造一個(gè)對(duì)象,即創(chuàng)建了這個(gè)類(lèi)的一個(gè)實(shí)例(instance)
3.封裝:就是把數(shù)據(jù)和行為結(jié)合起在一個(gè)包中)并對(duì)對(duì)象使用者隱藏?cái)?shù)據(jù)的實(shí)現(xiàn)過(guò)程,一個(gè)對(duì)象中的數(shù)據(jù)叫他的實(shí)例字段(instance field)
4.通過(guò)擴(kuò)展一個(gè)類(lèi)來(lái)獲得一個(gè)新類(lèi)叫繼承(inheritance),而所有的類(lèi)都是由Object根超類(lèi)擴(kuò)展而得,根超類(lèi)下文會(huì)做介紹.
5.對(duì)象的3個(gè)主要特性
behavior---說(shuō)明這個(gè)對(duì)象能做什么.
state---當(dāng)對(duì)象施加方法時(shí)對(duì)象的反映.
identity---與其他相似行為對(duì)象的區(qū)分標(biāo)志.
每個(gè)對(duì)象有惟一的indentity 而這3者之間相互影響.
6.類(lèi)之間的關(guān)系:
use-a :依賴(lài)關(guān)系
has-a :聚合關(guān)系
is-a :繼承關(guān)系--例:A類(lèi)繼承了B類(lèi),此時(shí)A類(lèi)不僅有了B類(lèi)的方法,還有其自己的方法.(個(gè)性存在于共性中)
7.構(gòu)造對(duì)象使用構(gòu)造器:構(gòu)造器的提出,構(gòu)造器是一種特殊的方法,構(gòu)造對(duì)象并對(duì)其初始化.
例:Data類(lèi)的構(gòu)造器叫Data
new Data()---構(gòu)造一個(gè)新對(duì)象,且初始化當(dāng)前時(shí)間.
Data happyday=new Data()---把一個(gè)對(duì)象賦值給一個(gè)變量happyday,從而使該對(duì)象能夠多次使用,此處要聲明的使變量與對(duì)象變量二者是不同的.new返回的值是一個(gè)引用.
構(gòu)造器特點(diǎn):構(gòu)造器可以有0個(gè),一個(gè)或多個(gè)參數(shù)構(gòu)造器和類(lèi)有相同的名字一個(gè)類(lèi)可以有多個(gè)構(gòu)造器,構(gòu)造器沒(méi)有返回值,構(gòu)造器總是和new運(yùn)算符一起使用.
8.重載:當(dāng)多個(gè)方法具有相同的名字而含有不同的參數(shù)時(shí),便發(fā)生重載.編譯器必須挑選出調(diào)用哪個(gè)方法.
9.包(package)Java允許把一個(gè)或多個(gè)類(lèi)收集在一起成為一組,稱(chēng)作包,以便于組織任務(wù),標(biāo)準(zhǔn)Java庫(kù)分為許多包.java.lang java.util java,net等,包是分層次的所有的java包都在java和javax包層次內(nèi).
10.繼承思想:允許在已經(jīng)存在的類(lèi)的基礎(chǔ)上構(gòu)建新的類(lèi),當(dāng)你繼承一個(gè)已經(jīng)存在的類(lèi)時(shí),那么你就復(fù)用了這個(gè)類(lèi)的方法和字段,同時(shí)你可以在新類(lèi)中添加新的方法和字段.
11.擴(kuò)展類(lèi):擴(kuò)展類(lèi)充分體現(xiàn)了is-a的繼承關(guān)系. 形式為:class (子類(lèi)) extends (基類(lèi)).
12.多態(tài):在java中,對(duì)象變量是多態(tài)的.而java中不支持多重繼承.
13.動(dòng)態(tài)綁定:調(diào)用對(duì)象方法的機(jī)制.
(1)編譯器檢查對(duì)象聲明的類(lèi)型和方法名.
(2)編譯器檢查方法調(diào)用的參數(shù)類(lèi)型.
(3)靜態(tài)綁定:若方法類(lèi)型為priavte static final 編譯器會(huì)準(zhǔn)確知道該調(diào)用哪個(gè)方法.
(4)當(dāng)程序運(yùn)行并且使用動(dòng)態(tài)綁定來(lái)調(diào)用一個(gè)方法時(shí),那么虛擬機(jī)必須調(diào)用x所指向的對(duì)象的實(shí)際類(lèi)型相匹配的方法版本.
(5)動(dòng)態(tài)綁定:是很重要的特性,它能使程序變得可擴(kuò)展而不需要重編譯已存代碼.
14.final類(lèi):為防止他人從你的類(lèi)上派生新類(lèi),此類(lèi)是不可擴(kuò)展的.
15.動(dòng)態(tài)調(diào)用比靜態(tài)調(diào)用花費(fèi)的時(shí)間要長(zhǎng),
16.抽象類(lèi):規(guī)定一個(gè)或多個(gè)抽象方法的類(lèi)本身必須定義為abstract
例: public abstract string getDescripition
17.Java中的每一個(gè)類(lèi)都是從Object類(lèi)擴(kuò)展而來(lái)的.
18.object類(lèi)中的equal和toString方法.equal用于測(cè)試一個(gè)對(duì)象是否同另一個(gè)對(duì)象相等.toString返回一個(gè)代表該對(duì)象的字符串,幾乎每一個(gè)類(lèi)都會(huì)重載該方法,以便返回當(dāng)前狀態(tài)的正確表示.(toString 方法是一個(gè)很重要的方法)
19.通用編程:任何類(lèi)類(lèi)型的所有值都可以同object類(lèi)性的變量來(lái)代替.
20.數(shù)組列表:ArrayList動(dòng)態(tài)數(shù)組列表,是一個(gè)類(lèi)庫(kù),定義在java.uitl包中,可自動(dòng)調(diào)節(jié)數(shù)組的大小.
21.class類(lèi) object類(lèi)中的getClass方法返回class類(lèi)型的一個(gè)實(shí)例,程序啟動(dòng)時(shí)包含在main方法的類(lèi)會(huì)被加載,虛擬機(jī)要加載他需要的所有類(lèi),每一個(gè)加載的類(lèi)都要加載它需要的類(lèi).
22.class類(lèi)為編寫(xiě)可動(dòng)態(tài)操縱java代碼的程序提供了強(qiáng)大的功能:反射,這項(xiàng)功能為JavaBeans特別有用,使用反射Java能支持VB程序員習(xí)慣使用的工具.
能夠分析類(lèi)能力的程序叫反射器,Java中提供此功能的包叫Java.lang.reflect反射機(jī)制十分強(qiáng)大.
1.在運(yùn)行時(shí)分析類(lèi)的能力.
2.在運(yùn)行時(shí)探察類(lèi)的對(duì)象.
3.實(shí)現(xiàn)通用數(shù)組操縱代碼.
4.提供方法對(duì)象.
而此機(jī)制主要針對(duì)是工具者而不是應(yīng)用及程序.
反射機(jī)制中的最重要的部分是允許你檢查類(lèi)的結(jié)構(gòu).用到的API有:
java.lang.reflect.Field 返回字段.
java.reflect.Method 返回方法.
java.lang.reflect.Constructor 返回參數(shù).
方法指針:java沒(méi)有方法指針,把一個(gè)方法的地址傳給另一個(gè)方法,可以在后面調(diào)用它,而接口是更好的解決方案.
23.接口(Interface)說(shuō)明類(lèi)該做什么而不指定如何去做,一個(gè)類(lèi)可以實(shí)現(xiàn)一個(gè)或多個(gè)interface.
24.接口不是一個(gè)類(lèi),而是對(duì)符合接口要求的類(lèi)的一套規(guī)范.
若實(shí)現(xiàn)一個(gè)接口需要2個(gè)步驟:
1.聲明類(lèi)需要實(shí)現(xiàn)的指定接口.
2.提供接口中的所有方法的定義.
聲明一個(gè)類(lèi)實(shí)現(xiàn)一個(gè)接口需要使用implements 關(guān)鍵字
class actionB implements Comparable 其actionb需要提供CompareTo方法,接口不是類(lèi),不能用new實(shí)例化一個(gè)接口.
25.一個(gè)類(lèi)只有一個(gè)超類(lèi),但一個(gè)類(lèi)能實(shí)現(xiàn)多個(gè)接口.Java中的一個(gè)重要接口Cloneable
26.接口和回調(diào).編程一個(gè)常用的模式是回調(diào)模式,在這種模式中你可以指定當(dāng)一個(gè)特定時(shí)間發(fā)生時(shí)回調(diào)對(duì)象上的方法.
例:ActionListener 接口監(jiān)聽(tīng).
類(lèi)似的API有:java.swing.JOptionPane
java.swing.Timer
java.awt.Tookit
27.對(duì)象clone:clone方法是object一個(gè)保護(hù)方法,這意味著你的代碼不能簡(jiǎn)單的調(diào)用它.
28.內(nèi)部類(lèi):一個(gè)內(nèi)部類(lèi)的定義是定義在另一個(gè)類(lèi)的內(nèi)部,原因是:1.一個(gè)內(nèi)部類(lèi)的對(duì)象能夠訪問(wèn)創(chuàng)建它的對(duì)象的實(shí)現(xiàn),包括私有數(shù)據(jù)
2.對(duì)于同一個(gè)包中的其他類(lèi)來(lái)說(shuō),內(nèi)部類(lèi)能夠隱藏起來(lái).
3.匿名內(nèi)部類(lèi)可以很方便的定義回調(diào).
4.使用內(nèi)部類(lèi)可以非常方便的編寫(xiě)事件驅(qū)動(dòng)程序.
29.代理類(lèi)(proxy):1.指定接口要求所有代碼
2.object類(lèi)定義的所有的方法(toString equals)
30.數(shù)據(jù)類(lèi)型:Java是強(qiáng)調(diào)類(lèi)型的語(yǔ)言,每個(gè)變量都必須先申明它都類(lèi)型,java中總共有8個(gè)基本類(lèi)型.4種是整型,2種是浮點(diǎn)型,一種是字符型,被用于Unicode編碼中的字符,布爾型.(T111)
posted @
2005-11-17 10:51 安德?tīng)査?閱讀(278) |
評(píng)論 (0) |
編輯 收藏
文件I/O:文件流→序列化
★文件流
文件操作是最簡(jiǎn)單最直接也是最容易想到的一種方式,我們說(shuō)的文件操作不僅僅是通過(guò)FileInputStream/FileOutputStream這么“裸”的方式直接把數(shù)據(jù)寫(xiě)入到本地文件(像我以前寫(xiě)的一個(gè)掃雷的小游戲JavaMine就是這樣保存一局的狀態(tài)的),這樣就比較“底層”了。
主要類(lèi)與方法和描述
FileInputStream.read() //從本地文件讀取二進(jìn)制格式的數(shù)據(jù)
FileReader.read() //從本地文件讀取字符(文本)數(shù)據(jù)
FileOutputStream.write() //保存二進(jìn)制數(shù)據(jù)到本地文件
FileWriter.write() //保存字符數(shù)據(jù)到本地文件
★XML
和上面的單純的I/O方式相比,XML就顯得“高檔”得多,以至于成為一種數(shù)據(jù)交換的標(biāo)準(zhǔn)。以DOM方式為例,它關(guān)心的是首先在內(nèi)存中構(gòu)造文檔樹(shù),數(shù)據(jù)保存在某個(gè)結(jié)點(diǎn)上(可以是葉子結(jié)點(diǎn),也可以是標(biāo)簽結(jié)點(diǎn)的屬性),構(gòu)造好了以后一次性的寫(xiě)入到外部文件,但我們只需要知道文件的位置,并不知道I/O是怎么操作的,XML操作方式可能多數(shù)人也實(shí)踐過(guò),所以這里也只列出相關(guān)的方法,供初學(xué)者預(yù)先了解一下。主要的包是javax.xml.parsers,org.w3c.dom,javax.xml.transform。
主要類(lèi)與方法和描述
DocumentBuilderFactory.newDocumentBuilder().parse() //解析一個(gè)外部的XML文件,得到一個(gè)Document對(duì)象的DOM樹(shù)
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() //初始化一棵DOM樹(shù)
Document.getDocumentElement().appendChild() //為一個(gè)標(biāo)簽結(jié)點(diǎn)添加一個(gè)子結(jié)點(diǎn)
Document.createTextNode() //生成一個(gè)字符串結(jié)點(diǎn)
Node.getChildNodes() //取得某個(gè)結(jié)點(diǎn)的所有下一層子結(jié)點(diǎn)
Node.removeChild() //刪除某個(gè)結(jié)點(diǎn)的子結(jié)點(diǎn)
Document.getElementsByTagName() 查找所有指定名稱(chēng)的標(biāo)簽結(jié)點(diǎn)
Document.getElementById() //查找指定名稱(chēng)的一個(gè)標(biāo)簽結(jié)點(diǎn),如果有多個(gè)符合,則返回某一個(gè),通常是第一個(gè)
Element.getAttribute() //取得一個(gè)標(biāo)簽的某個(gè)屬性的的值
Element.setAttribute() //設(shè)置一個(gè)標(biāo)簽的某個(gè)屬性的的值
Element.removeAttribute() //刪除一個(gè)標(biāo)簽的某個(gè)屬性
TransformerFactory.newInstance().newTransformer().transform() //將一棵DOM樹(shù)寫(xiě)入到外部XML文件
★序列化
使用基本的文件讀寫(xiě)方式存取數(shù)據(jù),如果我們僅僅保存相同類(lèi)型的數(shù)據(jù),則可以用同一種格式保存,譬如在我的JavaMine中保存一個(gè)盤(pán)局時(shí),需要保存每一個(gè)方格的坐標(biāo)、是否有地雷,是否被翻開(kāi)等,這些信息組合成一個(gè)“復(fù)合類(lèi)型”;相反,如果有多種不同類(lèi)型的數(shù)據(jù),那我們要么把它分解成若干部分,以相同類(lèi)型(譬如String)保存,要么我們需要在程序中添加解析不同類(lèi)型數(shù)據(jù)格式的邏輯,這就很不方便。于是我們期望用一種比較“高”的層次上處理數(shù)據(jù),程序員應(yīng)該花盡可能少的時(shí)間和代碼對(duì)數(shù)據(jù)進(jìn)行解析,事實(shí)上,序列化操作為我們提供了這樣一條途徑。
序列化(Serialization)大家可能都有所接觸,它可以把對(duì)象以某種特定的編碼格式寫(xiě)入或從外部字節(jié)流(即ObjectInputStream/ObjectOutputStream)中讀取。序列化一個(gè)對(duì)象非常之簡(jiǎn)單,僅僅實(shí)現(xiàn)一下Serializable接口即可,甚至都不用為它專(zhuān)門(mén)添加任何方法:
public class MySerial implements java.io.Serializable
{
//...
}
但有一個(gè)條件:即你要序列化的類(lèi)當(dāng)中,它的每個(gè)屬性都必須是是“可序列化”的。這句話說(shuō)起來(lái)有點(diǎn)拗口,其實(shí)所有基本類(lèi)型(就是int,char,boolean之類(lèi)的)都是“可序列化”的,而你可以看看JDK文檔,會(huì)發(fā)現(xiàn)很多類(lèi)其實(shí)已經(jīng)實(shí)現(xiàn)了Serializable(即已經(jīng)是“可序列化”的了),于是這些類(lèi)的對(duì)象以及基本數(shù)據(jù)類(lèi)型都可以直接作為你需要序列化的那個(gè)類(lèi)的內(nèi)部屬性。如果碰到了不是“可序列化”的屬性怎么辦?對(duì)不起,那這個(gè)屬性的類(lèi)還需要事先實(shí)現(xiàn)Serializable接口,如此遞歸,直到所有屬性都是“可序列化”的。
主要類(lèi)與方法和描述
ObjectOutputStream.writeObject() //將一個(gè)對(duì)象序列化到外部字節(jié)流
ObjectInputStream.readObject() //從外部字節(jié)流讀取并重新構(gòu)造對(duì)象
從實(shí)際應(yīng)用上看來(lái),“Serializable”這個(gè)接口并沒(méi)有定義任何方法,仿佛它只是一個(gè)標(biāo)記(或者說(shuō)像是Java的關(guān)鍵字)而已,一旦虛擬機(jī)看到這個(gè)“標(biāo)記”,就會(huì)嘗試調(diào)用自身預(yù)定義的序列化機(jī)制,除非你在實(shí)現(xiàn)Serializable接口的同時(shí)還定義了私有的readObject()或writeObject()方法。這一點(diǎn)很奇怪。不過(guò)你要是不愿意讓系統(tǒng)使用缺省的方式進(jìn)行序列化,那就必須定義上面提到的兩個(gè)方法:
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
{
//...
}
//...
}
譬如你可以在上面的writeObject()里調(diào)用默認(rèn)的序列化方法ObjectOutputStream.defaultWriteObject();譬如你不愿意將某些敏感的屬性和信息序列化,你也可以調(diào)用ObjectOutputStream.writeObject()方法明確指定需要序列化那些屬性。關(guān)于用戶可定制的序列化方法,我們將在后面提到。
★Bean
上面的序列化只是一種基本應(yīng)用,你把一個(gè)對(duì)象序列化到外部文件以后,用notepad打開(kāi)那個(gè)文件,只能從為數(shù)不多的一些可讀字符中猜到這是有關(guān)這個(gè)類(lèi)的信息文件,這需要你熟悉序列化文件的字節(jié)編碼方式,那將是比較痛苦的(在《Core Java 2》第一卷里提到了相關(guān)編碼方式,有興趣的話可以查看參考資料),某些情況下我們可能需要被序列化的文件具有更好的可讀性。另一方面,作為Java組件的核心概念“JavaBeans”,從JDK 1.4開(kāi)始,其規(guī)范里也要求支持文本方式的“長(zhǎng)期的持久化”(long-term persistence)。
打開(kāi)JDK文檔,java.beans包里的有一個(gè)名為“Encoder”的類(lèi),這就是一個(gè)可以序列化bean的實(shí)用類(lèi)。和它相關(guān)的兩個(gè)主要類(lèi)有XMLEcoder和XMLDecoder,顯然,這是以XML文件的格式保存和讀取bean的工具。他們的用法也很簡(jiǎn)單,和上面ObjectOutputStream/ObjectInputStream比較類(lèi)似。
主要類(lèi)與方法和描述
XMLEncoder.writeObject() //將一個(gè)對(duì)象序列化到外部字節(jié)流
XMLDecoder.readObject() //從外部字節(jié)流讀取并重新構(gòu)造對(duì)象
如果一個(gè)bean是如下格式:
public class MyBean
{
int i;
char[] c;
String s;
//...(get和set操作省略)...
}
那么通過(guò)XMLEcoder序列化出來(lái)的XML文件具有這樣的形式:
<?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>
像AWT和Swing中很多可視化組件都是bean,當(dāng)然也是可以用這種方式序列化的,下面就是從JDK文檔中摘錄的一個(gè)JFrame序列化以后的XML文件:
<?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>
因此但你想要保存的數(shù)據(jù)是一些不是太復(fù)雜的類(lèi)型的話,把它做成bean再序列化也不失為一種方便的選擇。
★Properties
在以前我總結(jié)的一篇關(guān)于集合框架的小文章里提到過(guò),Properties是歷史集合類(lèi)的一個(gè)典型的例子,這里主要不是介紹它的集合特性。大家可能都經(jīng)常接觸一些配置文件,如Windows的ini文件,Apache的conf文件,還有Java里的properties文件等,這些文件當(dāng)中的數(shù)據(jù)以“關(guān)鍵字-值”對(duì)的方式保存。“環(huán)境變量”這個(gè)概念都知道吧,它也是一種“key-value”對(duì),以前也常常看到版上問(wèn)“如何取得系統(tǒng)某某信息”之類(lèi)的問(wèn)題,其實(shí)很多都保存在環(huán)境變量里,只要用一條
System.getProperties().list(System.out);
就能獲得全部環(huán)境變量的列表:
-- listing properties --
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\項(xiàng)目\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=
java.vm.specification.vendor=Sun Microsystems Inc.
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\項(xiàng)目\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
主要類(lèi)與方法和描述
load() //從一個(gè)外部流讀取屬性
store() //將屬性保存到外部流(特別是文件)
getProperty() //取得一個(gè)指定的屬性
setProperty() //設(shè)置一個(gè)指定的屬性
list() //列出這個(gè)Properties對(duì)象包含的全部“key-value”對(duì)
System.getProperties() //取得系統(tǒng)當(dāng)前的環(huán)境變量
你可以這樣保存一個(gè)properties文件:
Properties prop = new Properties();
prop.setProperty("key1", "value1");
...
FileOutputStream out = new FileOutputStream("config.properties");
prop.store(out, "--這里是文件頭,可以加入注釋--");
★Preferences
如果我說(shuō)Java里面可以不使用JNI的手段操作Windows的注冊(cè)表你信不信?很多軟件的菜單里都有“Setting”或“Preferences”這樣的選項(xiàng)用來(lái)設(shè)定或修改軟件的配置,這些配置信息可以保存到一個(gè)像上面所述的配置文件當(dāng)中,如果是Windows平臺(tái)下,也可能會(huì)保存到系統(tǒng)注冊(cè)表中。從JDK 1.4開(kāi)始,Java在java.util下加入了一個(gè)專(zhuān)門(mén)處理用戶和系統(tǒng)配置信息的java.util.prefs包,其中一個(gè)類(lèi)Preferences是一種比較“高級(jí)”的玩意。從本質(zhì)上講,Preferences本身是一個(gè)與平臺(tái)無(wú)關(guān)的東西,但不同的OS對(duì)它的SPI(Service Provider Interface)的實(shí)現(xiàn)卻是與平臺(tái)相關(guān)的,因此,在不同的系統(tǒng)中你可能看到首選項(xiàng)保存為本地文件、LDAP目錄項(xiàng)、數(shù)據(jù)庫(kù)條目等,像在Windows平臺(tái)下,它就保存到了系統(tǒng)注冊(cè)表中。不僅如此,你還可以把首選項(xiàng)導(dǎo)出為XML文件或從XML文件導(dǎo)入。
主要類(lèi)與方法和描述
systemNodeForPackage() //根據(jù)指定的Class對(duì)象得到一個(gè)Preferences對(duì)象,這個(gè)對(duì)象的注冊(cè)表路徑是從“HKEY_LOCAL_MACHINE\”開(kāi)始的
systemRoot() //得到以注冊(cè)表路徑HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs 為根結(jié)點(diǎn)的Preferences對(duì)象
userNodeForPackage() //根據(jù)指定的Class對(duì)象得到一個(gè)Preferences對(duì)象,這個(gè)對(duì)象的注冊(cè)表路徑是從“HKEY_CURRENT_USER\”開(kāi)始的
userRoot() //得到以注冊(cè)表路徑HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs 為根結(jié)點(diǎn)的Preferences對(duì)象
putXXX() //設(shè)置一個(gè)屬性的值,這里XXX可以為基本數(shù)值型類(lèi)型,如int、long等,但首字母大寫(xiě),表示參數(shù)為相應(yīng)的類(lèi)型,也可以不寫(xiě)而直接用put,參數(shù)則為字符串
getXXX() //得到一個(gè)屬性的值
exportNode() //將全部首選項(xiàng)導(dǎo)出為一個(gè)XML文件
exportSubtree() //將部分首選項(xiàng)導(dǎo)出為一個(gè)XML文件
importPreferences() //從XML文件導(dǎo)入首選項(xiàng)
你可以按如下步驟保存數(shù)據(jù):
Preferences myPrefs1 = Preferences.userNodeForPackage(this);// 這種方法是在“HKEY_CURRENT_USER\”下按當(dāng)前類(lèi)的路徑建立一個(gè)注冊(cè)表項(xiàng)
Preferences myPrefs2 = Preferences.systemNodeForPackage(this);// 這種方法是在“HKEY_LOCAL_MACHINE\”下按當(dāng)前類(lèi)的路徑建立一個(gè)注冊(cè)表項(xiàng)
Preferences myPrefs3 = Preferences.userRoot().node("com.jungleford.demo");// 這種方法是在“HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路徑建立一個(gè)注冊(cè)表項(xiàng)
Preferences myPrefs4 = Preferences.systemRoot().node("com.jungleford.demo");// 這種方法是在“HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路徑建立一個(gè)注冊(cè)表項(xiàng)
myPrefs1.putInt("key1", 10);
myPrefs1.putDouble("key2", -7.15);
myPrefs1.put("key3", "value3");
FileOutputStream out = new FileOutputStream("prefs.xml");
myPrefs1.exportNode(out);
網(wǎng)絡(luò)I/O:Socket→RMI
★Socket
Socket編程可能大家都很熟,所以就不多討論了,只是說(shuō)通過(guò)socket把數(shù)據(jù)保存到遠(yuǎn)端服務(wù)器或從網(wǎng)絡(luò)socket讀取數(shù)據(jù)也不失為一種值得考慮的方式。
★RMI
RMI機(jī)制其實(shí)就是RPC(遠(yuǎn)程過(guò)程調(diào)用)的Java版本,它使用socket作為基本傳輸手段,同時(shí)也是序列化最重要的一個(gè)應(yīng)用。現(xiàn)在網(wǎng)絡(luò)傳輸從編程的角度來(lái)看基本上都是以流的方式操作,socket就是一個(gè)例子,將對(duì)象轉(zhuǎn)換成字節(jié)流的一個(gè)重要目標(biāo)就是為了方便網(wǎng)絡(luò)傳輸。
想象一下傳統(tǒng)的單機(jī)環(huán)境下的程序設(shè)計(jì),對(duì)于Java語(yǔ)言的函數(shù)(方法)調(diào)用(注意與C語(yǔ)言函數(shù)調(diào)用的區(qū)別)的參數(shù)傳遞,會(huì)有兩種情況:如果是基本數(shù)據(jù)類(lèi)型,這種情況下和C語(yǔ)言是一樣的,采用值傳遞方式;如果是對(duì)象,則傳遞的是對(duì)象的引用,包括返回值也是引用,而不是一個(gè)完整的對(duì)象拷貝!試想一下在不同的虛擬機(jī)之間進(jìn)行方法調(diào)用,即使是兩個(gè)完全同名同類(lèi)型的對(duì)象他們也很可能是不同的引用!此外對(duì)于方法調(diào)用過(guò)程,由于被調(diào)用過(guò)程的壓棧,內(nèi)存“現(xiàn)場(chǎng)”完全被被調(diào)用者占有,當(dāng)被調(diào)用方法返回時(shí),才將調(diào)用者的地址寫(xiě)回到程序計(jì)數(shù)器(PC),恢復(fù)調(diào)用者的狀態(tài),如果是兩個(gè)虛擬機(jī),根本不可能用簡(jiǎn)單壓棧的方式來(lái)保存調(diào)用者的狀態(tài)。因?yàn)榉N種原因,我們才需要建立RMI通信實(shí)體之間的“代理”對(duì)象,譬如“存根”就相當(dāng)于遠(yuǎn)程服務(wù)器對(duì)象在客戶機(jī)上的代理,stub就是這么來(lái)的,當(dāng)然這是后話了。
本地對(duì)象與遠(yuǎn)程對(duì)象(未必是物理位置上的不同機(jī)器,只要不是在同一個(gè)虛擬機(jī)內(nèi)皆為“遠(yuǎn)程”)之間傳遞參數(shù)和返回值,可能有這么幾種情形:
值傳遞:這又包括兩種子情形:如果是基本數(shù)據(jù)類(lèi)型,那么都是“可序列化”的,統(tǒng)統(tǒng)序列化成可傳輸?shù)淖止?jié)流;如果是對(duì)象,而且不是“遠(yuǎn)程對(duì)象”(所謂“遠(yuǎn)程對(duì)象”是實(shí)現(xiàn)了java.rmi.Remote接口的對(duì)象),本來(lái)對(duì)象傳遞的應(yīng)該是引用,但由于上述原因,引用是不足以證明對(duì)象身份的,所以傳遞的仍然是一個(gè)序列化的拷貝(當(dāng)然這個(gè)對(duì)象也必須滿足上述“可序列化”的條件)。
引用傳遞:可以引用傳遞的只能是“遠(yuǎn)程對(duì)象”。這里所謂的“引用”不要理解成了真的只是一個(gè)符號(hào),它其實(shí)是一個(gè)留在(客戶機(jī))本地stub中的,和遠(yuǎn)端服務(wù)器上那個(gè)真實(shí)的對(duì)象張得一模一樣的鏡像而已!只是因?yàn)樗悬c(diǎn)“特權(quán)”(不需要經(jīng)過(guò)序列化),在本地內(nèi)存里已經(jīng)有了一個(gè)實(shí)例,真正引用的其實(shí)是這個(gè)“孿生子”。
由此可見(jiàn),序列化在RMI當(dāng)中占有多么重要的地位。
數(shù)據(jù)庫(kù)I/O:CMP、Hibernate
★什么是“Persistence”
用過(guò)VMWare的朋友大概都知道當(dāng)一個(gè)guest OS正在運(yùn)行的時(shí)候點(diǎn)擊“Suspend”將虛擬OS掛起,它會(huì)把整個(gè)虛擬內(nèi)存的內(nèi)容保存到磁盤(pán)上,譬如你為虛擬OS分配了128M的運(yùn)行內(nèi)存,那掛起以后你會(huì)在虛擬OS所在的目錄下找到一個(gè)同樣是128M的文件,這就是虛擬OS內(nèi)存的完整鏡像!這種內(nèi)存的鏡像手段其實(shí)就是“Persistence”(持久化)概念的由來(lái)。
★CMP和Hibernate
因?yàn)槲覍?duì)J2EE的東西不是太熟悉,隨便找了點(diǎn)材料看看,所以擔(dān)心說(shuō)的不到位,這次就不作具體總結(jié)了,人要學(xué)習(xí)……真是一件痛苦的事情
序列化再探討
從以上技術(shù)的討論中我們不難體會(huì)到,序列化是Java之所以能夠出色地實(shí)現(xiàn)其鼓吹的兩大賣(mài)點(diǎn)??分布式(distributed)和跨平臺(tái)(OS independent)的一個(gè)重要基礎(chǔ)。TIJ(即“Thinking in Java”)談到I/O系統(tǒng)時(shí),把序列化稱(chēng)為“l(fā)ightweight persistence”??“輕量級(jí)的持久化”,這確實(shí)很有意思。
★為什么叫做“序列”化?
開(kāi)場(chǎng)白里我說(shuō)更習(xí)慣于把“Serialization”稱(chēng)為“序列化”而不是“串行化”,這是有原因的。介紹這個(gè)原因之前先回顧一些計(jì)算機(jī)基本的知識(shí),我們知道現(xiàn)代計(jì)算機(jī)的內(nèi)存空間都是線性編址的(什么是“線性”知道吧,就是一個(gè)元素只有一個(gè)唯一的“前驅(qū)”和唯一的“后繼”,當(dāng)然頭尾元素是個(gè)例外;對(duì)于地址來(lái)說(shuō),它的下一個(gè)地址當(dāng)然不可能有兩個(gè),否則就亂套了),“地址”這個(gè)概念推廣到數(shù)據(jù)結(jié)構(gòu),就相當(dāng)于“指針”,這個(gè)在本科低年級(jí)大概就知道了。注意了,既然是線性的,那“地址”就可以看作是內(nèi)存空間的“序號(hào)”,說(shuō)明它的組織是有順序的,“序號(hào)”或者說(shuō)“序列號(hào)”正是“Serialization”機(jī)制的一種體現(xiàn)。為什么這么說(shuō)呢?譬如我們有兩個(gè)對(duì)象a和b,分別是類(lèi)A和B的實(shí)例,它們都是可序列化的,而A和B都有一個(gè)類(lèi)型為C的屬性,根據(jù)前面我們說(shuō)過(guò)的原則,C當(dāng)然也必須是可序列化的。
import java.io.*;
...
class A implements Serializable
{
C c;
...
}
class B implements Serializable
{
C c;
...
}
class C implements Serializable
{
...
}
A a;
B b;
C c1;
...
注意,這里我們?cè)趯?shí)例化a和b的時(shí)候,有意讓他們的c屬性使用同一個(gè)C類(lèi)型對(duì)象的引用,譬如c1,那么請(qǐng)?jiān)囅胍幌拢覀冃蛄谢痑和b的時(shí)候,它們的c屬性在外部字節(jié)流(當(dāng)然可以不僅僅是文件)里保存的是一份拷貝還是兩份拷貝呢?序列化在這里使用的是一種類(lèi)似于“指針”的方案:它為每個(gè)被序列化的對(duì)象標(biāo)上一個(gè)“序列號(hào)”(serial number),但序列化一個(gè)對(duì)象的時(shí)候,如果其某個(gè)屬性對(duì)象是已經(jīng)被序列化的,那么這里只向輸出流寫(xiě)入該屬性的序列號(hào);從字節(jié)流恢復(fù)被序列化的對(duì)象時(shí),也根據(jù)序列號(hào)找到對(duì)應(yīng)的流來(lái)恢復(fù)。這就是“序列化”名稱(chēng)的由來(lái)!這里我們看到“序列化”和“指針”是極相似的,只不過(guò)“指針”是內(nèi)存空間的地址鏈,而序列化用的是外部流中的“序列號(hào)鏈”。
使用“序列號(hào)”而不是內(nèi)存地址來(lái)標(biāo)識(shí)一個(gè)被序列化的對(duì)象,是因?yàn)閺牧髦谢謴?fù)對(duì)象到內(nèi)存,其地址可能就未必是原來(lái)的地址了??我們需要的只是這些對(duì)象之間的引用關(guān)系,而不是死板的原始位置,這在RMI中就更是必要,在兩臺(tái)不同的機(jī)器之間傳遞對(duì)象(流),根本就不可能指望它們?cè)趦膳_(tái)機(jī)器上都具有相同的內(nèi)存地址。
★更靈活的“序列化”:transient屬性和Externalizable
Serializable確實(shí)很方便,方便到你幾乎不需要做任何額外的工作就可以輕松將內(nèi)存中的對(duì)象保存到外部。但有兩個(gè)問(wèn)題使得Serializable的威力收到束縛:
一個(gè)是效率問(wèn)題,《Core Java 2》中指出,Serializable使用系統(tǒng)默認(rèn)的序列化機(jī)制會(huì)影響軟件的運(yùn)行速度,因?yàn)樾枰獮槊總€(gè)屬性的引用編號(hào)和查號(hào),再加上I/O操作的時(shí)間(I/O和內(nèi)存讀寫(xiě)差的可是一個(gè)數(shù)量級(jí)的大小),其代價(jià)當(dāng)然是可觀的。
另一個(gè)困擾是“裸”的Serializable不可定制,傻乎乎地什么都給你序列化了,不管你是不是想這么做。其實(shí)你可以有至少三種定制序列化的選擇。其中一種前面已經(jīng)提到了,就是在implements Serializable的類(lèi)里面添加私有的writeObject()和readObject()方法(這種Serializable就不裸了,),在這兩個(gè)方法里,該序列化什么,不該序列化什么,那就由你說(shuō)了算了,你當(dāng)然可以在這兩個(gè)方法體里面分別調(diào)用ObjectOutputStream.defaultWriteObject()和ObjectInputStream.defaultReadObject()仍然執(zhí)行默認(rèn)的序列化動(dòng)作(那你在代碼上不就做無(wú)用功了?呵呵),也可以用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()方法對(duì)你中意的屬性進(jìn)行序列化。但虛擬機(jī)一看到你定義了這兩個(gè)方法,它就不再用默認(rèn)的機(jī)制了。
如果僅僅為了跳過(guò)某些屬性不讓它序列化,上面的動(dòng)作似乎顯得麻煩,更簡(jiǎn)單的方法是對(duì)不想序列化的屬性加上transient關(guān)鍵字,說(shuō)明它是個(gè)“暫態(tài)變量”,默認(rèn)序列化的時(shí)候就不會(huì)把這些屬性也塞到外部流里了。當(dāng)然,你如果定義writeObject()和readObject()方法的化,仍然可以把暫態(tài)變量進(jìn)行序列化。題外話,像transient、violate、finally這樣的關(guān)鍵字初學(xué)者可能會(huì)不太重視,而現(xiàn)在有的公司招聘就偏偏喜歡問(wèn)這樣的問(wèn)題 :(
再一個(gè)方案就是不實(shí)現(xiàn)Serializable而改成實(shí)現(xiàn)Externalizable接口。我們研究一下這兩個(gè)接口的源代碼,發(fā)現(xiàn)它們很類(lèi)似,甚至容易混淆。我們要記住的是:Externalizable默認(rèn)并不保存任何對(duì)象相關(guān)信息!任何保存和恢復(fù)對(duì)象的動(dòng)作都是你自己定義的。Externalizable包含兩個(gè)public的方法:
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
乍一看這和上面的writeObject()和readObject()幾乎差不多,但Serializable和Externalizable走的是兩個(gè)不同的流程:Serializable在對(duì)象不存在的情況下,就可以?xún)H憑外部的字節(jié)序列把整個(gè)對(duì)象重建出來(lái);但Externalizable在重建對(duì)象時(shí),先是調(diào)用該類(lèi)的默認(rèn)構(gòu)造函數(shù)(即不含參數(shù)的那個(gè)構(gòu)造函數(shù))使得內(nèi)存中先有這么一個(gè)實(shí)例,然后再調(diào)用readExternal方法對(duì)實(shí)例中的屬性進(jìn)行恢復(fù),因此,如果默認(rèn)構(gòu)造函數(shù)中和readExternal方法中都沒(méi)有賦值的那些屬性,特別他們是非基本類(lèi)型的話,將會(huì)是空(null)。在這里需要注意的是,transient只能用在對(duì)Serializable而不是Externalizable的實(shí)現(xiàn)里面。
★序列化與克隆
從“可序列化”的遞歸定義來(lái)看,一個(gè)序列化的對(duì)象貌似對(duì)象內(nèi)存映象的外部克隆,如果沒(méi)有共享引用的屬性的化,那么應(yīng)該是一個(gè)深度克隆。關(guān)于克隆的話題有可以談很多,這里就不細(xì)說(shuō)了,有興趣的話可以參考IBM developerWorks上的一篇文章:JAVA中的指針,引用及對(duì)象的clone
一點(diǎn)啟示
作為一個(gè)實(shí)際的應(yīng)用,我在寫(xiě)那個(gè)簡(jiǎn)易的郵件客戶端JExp的時(shí)候曾經(jīng)對(duì)比過(guò)好幾種保存Message對(duì)象(主要是幾個(gè)關(guān)鍵屬性和郵件的內(nèi)容)到本地的方法,譬如XML、Properties等,最后還是選擇了用序列化的方式,因?yàn)檫@種方法最簡(jiǎn)單, 大約可算是“學(xué)以致用”罷。這里“存取程序狀態(tài)”其實(shí)只是一個(gè)引子話題罷了,我想說(shuō)的是??就如同前面我們討論的關(guān)于logging的話題一樣??在Java面前對(duì)同一個(gè)問(wèn)題你可以有很多種solution:熟悉文件操作的,你可能會(huì)覺(jué)得Properties、XML或Bean比較方便,然后又發(fā)現(xiàn)了還有Preferences這么一個(gè)東東,大概又會(huì)感慨“天外有天”了,等到你接觸了很多種新方法以后,結(jié)果又會(huì)“殊途同歸”,重新反省Serialization機(jī)制本身。這不僅是Java,科學(xué)也是同樣的道理。
參考資料
Core Java 2. by Cay S. Horstmann, Gary Cornell
J2SE進(jìn)階. by JavaResearch.org
Thinking in Java. by Bruce Eckel
J2SE 1.4.2 Documentation. by java.sun.com
Java Network Programming. by Elliotte R. Harold
Java分布式對(duì)象:RMI和CORBA. by IBM developerWorks
posted @
2005-11-17 10:37 安德?tīng)査?閱讀(302) |
評(píng)論 (0) |
編輯 收藏
1.document.write(""); 輸出語(yǔ)句
2.JS中的注釋為//
3.傳統(tǒng)的HTML文檔順序是:document->html->(head,body)
4.一個(gè)瀏覽器窗口中的DOM順序是:window->(navigator,screen,history,location,document)
5.得到表單中元素的名稱(chēng)和值:document.getElementById("表單中元素的ID號(hào)").name(或value)
6.一個(gè)小寫(xiě)轉(zhuǎn)大寫(xiě)的JS: document.getElementById("output").value = document.getElementById("input").value.toUpperCase();
7.JS中的值類(lèi)型:String,Number,Boolean,Null,Object,Function
8.JS中的字符型轉(zhuǎn)換成數(shù)值型:parseInt(),parseFloat()
9.JS中的數(shù)字轉(zhuǎn)換成字符型:(""+變量)
10.JS中的取字符串長(zhǎng)度是:(length)
11.JS中的字符與字符相連接使用+號(hào).
12.JS中的比較操作符有:==等于,!=不等于,>,>=,<.<=
13.JS中聲明變量使用:var來(lái)進(jìn)行聲明
14.JS中的判斷語(yǔ)句結(jié)構(gòu):if(condition){}else{}
15.JS中的循環(huán)結(jié)構(gòu):for([initial expression];[condition];[upadte expression]) {inside loop}
16.循環(huán)中止的命令是:break
17.JS中的函數(shù)定義:function functionName([parameter],...){statement[s]}
18.當(dāng)文件中出現(xiàn)多個(gè)form表單時(shí).可以用document.forms[0],document.forms[1]來(lái)代替.
19.窗口:打開(kāi)窗口window.open(), 關(guān)閉一個(gè)窗口:window.close(), 窗口本身:self
20.狀態(tài)欄的設(shè)置:window.status="字符";
21.彈出提示信息:window.alert("字符");
22.彈出確認(rèn)框:window.confirm();
23.彈出輸入提示框:window.prompt();
24.指定當(dāng)前顯示鏈接的位置:window.location.href="URL"
25.取出窗體中的所有表單的數(shù)量:document.forms.length
26.關(guān)閉文檔的輸出流:document.close();
27.字符串追加連接符:+=
28.創(chuàng)建一個(gè)文檔元素:document.createElement(),document.createTextNode()
29.得到元素的方法:document.getElementById()
30.設(shè)置表單中所有文本型的成員的值為空:
var form = window.document.forms[0]
for (var i = 0; i<form.elements.length;i++){
if (form.elements.type == "text"){
form.elements.value = "";
}
}
31.復(fù)選按鈕在JS中判斷是否選中:document.forms[0].checkThis.checked (checked屬性代表為是否選中返回TRUE或FALSE)
32.單選按鈕組(單選按鈕的名稱(chēng)必須相同):取單選按鈕組的長(zhǎng)度document.forms[0].groupName.length
33.單選按鈕組判斷是否被選中也是用checked.
34.下拉列表框的值:document.forms[0].selectName.options[n].value (n有時(shí)用下拉列表框名稱(chēng)加上.selectedIndex來(lái)確定被選中的值)
35.字符串的定義:var myString = new String("This is lightsword");
36.字符串轉(zhuǎn)成大寫(xiě):string.toUpperCase(); 字符串轉(zhuǎn)成小寫(xiě):string.toLowerCase();
37.返回字符串2在字符串1中出現(xiàn)的位置:String1.indexOf("String2")!=-1則說(shuō)明沒(méi)找到.
38.取字符串中指定位置的一個(gè)字符:StringA.charAt(9);
39.取出字符串中指定起點(diǎn)和終點(diǎn)的子字符串:stringA.substring(2,6);
40.數(shù)學(xué)函數(shù):Math.PI(返回圓周率),Math.SQRT2(返回開(kāi)方),Math.max(value1,value2)返回兩個(gè)數(shù)中的最在值,Math.pow(value1,10)返回value1的十次方,Math.round(value1)四舍五入函數(shù),Math.floor(Math.random()*(n+1))返回隨機(jī)數(shù)
41.定義日期型變量:var today = new Date();
42.日期函數(shù)列表:dateObj.getTime()得到時(shí)間,dateObj.getYear()得到年份,dateObj.getFullYear()得到四位的年份,dateObj.getMonth()得到月份,dateObj.getDate()得到日,dateObj.getDay()得到日期幾,dateObj.getHours()得到小時(shí),dateObj.getMinutes()得到分,dateObj.getSeconds()得到秒,dateObj.setTime(value)設(shè)置時(shí)間,dateObj.setYear(val)設(shè)置年,dateObj.setMonth(val)設(shè)置月,dateObj.setDate(val)設(shè)置日,dateObj.setDay(val)設(shè)置星期幾,dateObj.setHours設(shè)置小時(shí),dateObj.setMinutes(val)設(shè)置分,dateObj.setSeconds(val)設(shè)置秒 [注意:此日期時(shí)間從0開(kāi)始計(jì)]
43.FRAME的表示方式: [window.]frames[n].ObjFuncVarName,frames["frameName"].ObjFuncVarName,frameName.ObjFuncVarName
44.parent代表父親對(duì)象,top代表最頂端對(duì)象
45.打開(kāi)子窗口的父窗口為:opener
46.表示當(dāng)前所屬的位置:this
47.當(dāng)在超鏈接中調(diào)用JS函數(shù)時(shí)用:(javascript :)來(lái)開(kāi)頭后面加函數(shù)名
48.在老的瀏覽器中不執(zhí)行此JS:<!-- //-->
49.引用一個(gè)文件式的JS:<script type="text/javascript" src="aaa.js"></script>
50.指定在不支持腳本的瀏覽器顯示的HTML:<noscript></noscript>
51.當(dāng)超鏈和onCLICK事件都有時(shí),則老版本的瀏覽器轉(zhuǎn)向a.html,否則轉(zhuǎn)向b.html.例:<a href="a.html" onclick="location.href='b.html';return false">dfsadf</a>
52.JS的內(nèi)建對(duì)象有:Array,Boolean,Date,Error,EvalError,Function,Math,Number,Object,RangeError,ReferenceError,RegExp,String,SyntaxError,TypeError,URIError
53.JS中的換行:\n
54.窗口全屏大小:<script>function fullScreen(){ this.moveTo(0,0);this.outerWidth=screen.availWidth;this.outerHeight=screen.availHeight;}window.maximize=fullScreen;</script>
55.JS中的all代表其下層的全部元素
56.JS中的焦點(diǎn)順序:document.getElementByid("表單元素").tabIndex = 1
57.innerHTML的值是表單元素的值:如<p id="para">"how are <em>you</em>"</p>,則innerHTML的值就是:how are <em>you</em>
58.innerTEXT的值和上面的一樣,只不過(guò)不會(huì)把<em>這種標(biāo)記顯示出來(lái).
59.contentEditable可設(shè)置元素是否可被修改,isContentEditable返回是否可修改的狀態(tài).
60.isDisabled判斷是否為禁止?fàn)顟B(tài).disabled設(shè)置禁止?fàn)顟B(tài)
61.length取得長(zhǎng)度,返回整型數(shù)值
62.addBehavior()是一種JS調(diào)用的外部函數(shù)文件其擴(kuò)展名為.htc
63.window.focus()使當(dāng)前的窗口在所有窗口之前.
64.blur()指失去焦點(diǎn).與FOCUS()相反.
65.select()指元素為選中狀態(tài).
66.防止用戶對(duì)文本框中輸入文本:onfocus="this.blur()"
67.取出該元素在頁(yè)面中出現(xiàn)的數(shù)量:document.all.tags("div(或其它HTML標(biāo)記符)").length
68.JS中分為兩種窗體輸出:模態(tài)和非模態(tài).window.showModaldialog(),window.showModeless()
69.狀態(tài)欄文字的設(shè)置:window.status='文字',默認(rèn)的狀態(tài)欄文字設(shè)置:window.defaultStatus = '文字.';
70.添加到收藏夾:external.AddFavorite("http://www.dannyg.com";,"jaskdlf");
71.JS中遇到腳本錯(cuò)誤時(shí)不做任何操作:window.onerror = doNothing; 指定錯(cuò)誤句柄的語(yǔ)法為:window.onerror = handleError;
72.JS中指定當(dāng)前打開(kāi)窗口的父窗口:window.opener,支持opener.opener...的多重繼續(xù).
73.JS中的self指的是當(dāng)前的窗口
74.JS中狀態(tài)欄顯示內(nèi)容:window.status="內(nèi)容"
75.JS中的top指的是框架集中最頂層的框架
76.JS中關(guān)閉當(dāng)前的窗口:window.close();
77.JS中提出是否確認(rèn)的框:if(confirm("Are you sure?")){alert("ok");}else{alert("Not Ok");}
78.JS中的窗口重定向:window.navigate("http://www.sina.com.cn";);
79.JS中的打印:window.print()
80.JS中的提示輸入框:window.prompt("message","defaultReply");
81.JS中的窗口滾動(dòng)條:window.scroll(x,y)
82.JS中的窗口滾動(dòng)到位置:window.scrollby
83.JS中設(shè)置時(shí)間間隔:setInterval("expr",msecDelay)或setInterval(funcRef,msecDelay)或setTimeout
84.JS中的模態(tài)顯示在IE4+行,在NN中不行:showModalDialog("URL"[,arguments][,features]);
85.JS中的退出之前使用的句柄:function verifyClose(){event.returnValue="we really like you and hope you will stay longer.";}} window.onbeforeunload=verifyClose;
86.當(dāng)窗體第一次調(diào)用時(shí)使用的文件句柄:onload()
87.當(dāng)窗體關(guān)閉時(shí)調(diào)用的文件句柄:onunload()
88.window.location的屬性: protocol(http:),hostname(www.example.com),port(80),host(www.example.com:80),pathname("/a/a.html"),hash("#giantGizmo",指跳轉(zhuǎn)到相應(yīng)的錨記),href(全部的信息)
89.window.location.reload()刷新當(dāng)前頁(yè)面.
90.window.history.back()返回上一頁(yè),window.history.forward()返回下一頁(yè),window.history.go(返回第幾頁(yè),也可以使用訪問(wèn)過(guò)的URL)
91.document.write()不換行的輸出,document.writeln()換行輸出
92.document.body.noWrap=true;防止鏈接文字折行.
93.變量名.charAt(第幾位),取該變量的第幾位的字符.
94."abc".charCodeAt(第幾個(gè)),返回第幾個(gè)字符的ASCii碼值.
95.字符串連接:string.concat(string2),或用+=進(jìn)行連接
96.變量.indexOf("字符",起始位置),返回第一個(gè)出現(xiàn)的位置(從0開(kāi)始計(jì)算)
97.string.lastIndexOf(searchString[,startIndex])最后一次出現(xiàn)的位置.
98.string.match(regExpression),判斷字符是否匹配.
99.string.replace(regExpression,replaceString)替換現(xiàn)有字符串.
100.string.split(分隔符)返回一個(gè)數(shù)組存儲(chǔ)值.
101.string.substr(start[,length])取從第幾位到指定長(zhǎng)度的字符串.
102.string.toLowerCase()使字符串全部變?yōu)樾?xiě).
103.string.toUpperCase()使全部字符變?yōu)榇髮?xiě).
104.parseInt(string[,radix(代表進(jìn)制)])強(qiáng)制轉(zhuǎn)換成整型.
105.parseFloat(string[,radix])強(qiáng)制轉(zhuǎn)換成浮點(diǎn)型.
106.isNaN(變量):測(cè)試是否為數(shù)值型.
107.定義常量的關(guān)鍵字:const,定義變量的關(guān)鍵字:var
posted @
2005-11-17 09:54 安德?tīng)査?閱讀(475) |
評(píng)論 (0) |
編輯 收藏
Java Reflection (JAVA反射)
Reflection 是 Java 程序開(kāi)發(fā)語(yǔ)言的特征之一,它允許運(yùn)行中的 Java 程序?qū)ψ陨磉M(jìn)行檢查,或者說(shuō)“自審”,并能直接操作程序的內(nèi)部屬性。例如,使用它能獲得 Java 類(lèi)中各成員的名稱(chēng)并顯示出來(lái)。
Java 的這一能力在實(shí)際應(yīng)用中也許用得不是很多,但是在其它的程序設(shè)計(jì)語(yǔ)言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒(méi)有辦法在程序中獲得函數(shù)定義相關(guān)的信息。
JavaBean 是 reflection 的實(shí)際應(yīng)用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過(guò) reflection 動(dòng)態(tài)的載入并取得 Java 組件(類(lèi)) 的屬性。
1. 一個(gè)簡(jiǎn)單的例子
考慮下面這個(gè)簡(jiǎn)單的例子,讓我們看看 reflection 是如何工作的。
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
} catch (Throwable e) {
System.err.println(e);
}
}
}
按如下語(yǔ)句執(zhí)行:
java DumpMethods java.util.Stack
它的結(jié)果輸出為:
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)
這樣就列出了java.util.Stack 類(lèi)的各方法名以及它們的限制符和返回類(lèi)型。
這個(gè)程序使用 Class.forName 載入指定的類(lèi),然后調(diào)用 getDeclaredMethods 來(lái)獲取這個(gè)類(lèi)中定義了的方法列表。java.lang.reflect.Methods 是用來(lái)描述某個(gè)類(lèi)中單個(gè)方法的一個(gè)類(lèi)。
2.開(kāi)始使用 Reflection
用于 reflection 的類(lèi),如 Method,可以在 java.lang.relfect 包中找到。使用這些類(lèi)的時(shí)候必須要遵循三個(gè)步驟:第一步是獲得你想操作的類(lèi)的 java.lang.Class 對(duì)象。在運(yùn)行中的 Java 程序中,用 java.lang.Class 類(lèi)來(lái)描述類(lèi)和接口等。
下面就是獲得一個(gè) Class 對(duì)象的方法之一:
Class c = Class.forName("java.lang.String");
這條語(yǔ)句得到一個(gè) String 類(lèi)的類(lèi)對(duì)象。還有另一種方法,如下面的語(yǔ)句:
Class c = int.class;
或者
Class c = Integer.TYPE;
它們可獲得基本類(lèi)型的類(lèi)信息。其中后一種方法中訪問(wèn)的是基本類(lèi)型的封裝類(lèi) (如 Integer) 中預(yù)先定義好的 TYPE 字段。
第二步是調(diào)用諸如 getDeclaredMethods 的方法,以取得該類(lèi)中定義的所有方法的列表。
一旦取得這個(gè)信息,就可以進(jìn)行第三步了??使用 reflection API 來(lái)操作這些信息,如下面這段代碼:
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
它將以文本方式打印出 String 中定義的第一個(gè)方法的原型。
在下面的例子中,這三個(gè)步驟將為使用 reflection 處理特殊應(yīng)用程序提供例證。
模擬 instanceof 操作符
得到類(lèi)信息之后,通常下一個(gè)步驟就是解決關(guān)于 Class 對(duì)象的一些基本的問(wèn)題。例如,Class.isInstance 方法可以用于模擬 instanceof 操作符:
class A {
}
public class instance1 {
public static void main(String args[]) {
try {
Class cls = Class.forName("A");
boolean b1 = cls.isInstance(new Integer(37));
System.out.println(b1);
boolean b2 = cls.isInstance(new A());
System.out.println(b2);
} catch (Throwable e) {
System.err.println(e);
}
}
}
在這個(gè)例子中創(chuàng)建了一個(gè) A 類(lèi)的 Class 對(duì)象,然后檢查一些對(duì)象是否是 A 的實(shí)例。Integer(37) 不是,但 new A() 是。
3.找出類(lèi)的方法
找出一個(gè)類(lèi)中定義了些什么方法,這是一個(gè)非常有價(jià)值也非常基礎(chǔ)的 reflection 用法。下面的代碼就實(shí)現(xiàn)了這一用法:
import java.lang.reflect.*;
public class method1 {
private int f1(Object p, int x) throws NullPointerException {
if (p == null)
throw new NullPointerException();
return x;
}
public static void main(String args[]) {
try {
Class cls = Class.forName("method1");
Method methlist[] = cls.getDeclaredMethods();
for (int i = 0; i < methlist.length; i++) {
Method m = methlist[i];
System.out.println("name = " + m.getName());
System.out.println("decl class = " + m.getDeclaringClass());
Class pvec[] = m.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #" + j + " " + pvec[j]);
Class evec[] = m.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("return type = " + m.getReturnType());
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
這個(gè)程序首先取得 method1 類(lèi)的描述,然后調(diào)用 getDeclaredMethods 來(lái)獲取一系列的 Method 對(duì)象,它們分別描述了定義在類(lèi)中的每一個(gè)方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 來(lái)代替 getDeclaredMethods,你還能獲得繼承來(lái)的各個(gè)方法的信息。
取得了 Method 對(duì)象列表之后,要顯示這些方法的參數(shù)類(lèi)型、異常類(lèi)型和返回值類(lèi)型等就不難了。這些類(lèi)型是基本類(lèi)型還是類(lèi)類(lèi)型,都可以由描述類(lèi)的對(duì)象按順序給出。
輸出的結(jié)果如下:
name = f1
decl class = class method1
param #0 class java.lang.Object
param #1 int
exc #0 class java.lang.NullPointerException
return type = int
-----
name = main
decl class = class method1
param #0 class [Ljava.lang.String;
return type = void
-----
4.獲取構(gòu)造器信息
獲取類(lèi)構(gòu)造器的用法與上述獲取方法的用法類(lèi)似,如:
import java.lang.reflect.*;
public class constructor1 {
public constructor1() {
}
protected constructor1(int i, double d) {
}
public static void main(String args[]) {
try {
Class cls = Class.forName("constructor1");
Constructor ctorlist[] = cls.getDeclaredConstructors();
for (int i = 0; i < ctorlist.length; i++) {
Constructor ct = ctorlist[i];
System.out.println("name = " + ct.getName());
System.out.println("decl class = " + ct.getDeclaringClass());
Class pvec[] = ct.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #" + j + " " + pvec[j]);
Class evec[] = ct.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
這個(gè)例子中沒(méi)能獲得返回類(lèi)型的相關(guān)信息,那是因?yàn)闃?gòu)造器沒(méi)有返回類(lèi)型。
這個(gè)程序運(yùn)行的結(jié)果是:
name = constructor1
decl class = class constructor1
-----
name = constructor1
decl class = class constructor1
param #0 int
param #1 double
-----
5.獲取類(lèi)的字段(域)
找出一個(gè)類(lèi)中定義了哪些數(shù)據(jù)字段也是可能的,下面的代碼就在干這個(gè)事情:
import java.lang.reflect.*;
public class field1 {
private double d;
public static final int i = 37;
String s = "testing";
public static void main(String args[]) {
try {
Class cls = Class.forName("field1");
Field fieldlist[] = cls.getDeclaredFields();
for (int i = 0; i < fieldlist.length; i++) {
Field fld = fieldlist[i];
System.out.println("name = " + fld.getName());
System.out.println("decl class = " + fld.getDeclaringClass());
System.out.println("type = " + fld.getType());
int mod = fld.getModifiers();
System.out.println("modifiers = " + Modifier.toString(mod));
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
這個(gè)例子和前面那個(gè)例子非常相似。例中使用了一個(gè)新東西 Modifier,它也是一個(gè) reflection 類(lèi),用來(lái)描述字段成員的修飾語(yǔ),如“private int”。這些修飾語(yǔ)自身由整數(shù)描述,而且使用 Modifier.toString 來(lái)返回以“官方”順序排列的字符串描述 (如“static”在“final”之前)。這個(gè)程序的輸出是:
name = d
decl class = class field1
type = double
modifiers = private
-----
name = i
decl class = class field1
type = int
modifiers = public static final
-----
name = s
decl class = class field1
type = class java.lang.String
modifiers =
-----
和獲取方法的情況一下,獲取字段的時(shí)候也可以只取得在當(dāng)前類(lèi)中申明了的字段信息 (getDeclaredFields),或者也可以取得父類(lèi)中定義的字段 (getFields) 。
6.根據(jù)方法的名稱(chēng)來(lái)執(zhí)行方法
文本到這里,所舉的例子無(wú)一例外都與如何獲取類(lèi)的信息有關(guān)。我們也可以用 reflection 來(lái)做一些其它的事情,比如執(zhí)行一個(gè)指定了名稱(chēng)的方法。下面的示例演示了這一操作:
import java.lang.reflect.*;
public class method2 {
public int add(int a, int b) {
return a + b;
}
public static void main(String args[]) {
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod("add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer) retobj;
System.out.println(retval.intvalue());
} catch (Throwable e) {
System.err.println(e);
}
}
}
假如一個(gè)程序在執(zhí)行的某處的時(shí)候才知道需要執(zhí)行某個(gè)方法,這個(gè)方法的名稱(chēng)是在程序的運(yùn)行過(guò)程中指定的 (例如,JavaBean 開(kāi)發(fā)環(huán)境中就會(huì)做這樣的事),那么上面的程序演示了如何做到。
上例中,getMethod 用于查找一個(gè)具有兩個(gè)整型參數(shù)且名為 add 的方法。找到該方法并創(chuàng)建了相應(yīng)的 Method 對(duì)象之后,在正確的對(duì)象實(shí)例中執(zhí)行它。執(zhí)行該方法的時(shí)候,需要提供一個(gè)參數(shù)列表,這在上例中是分別包裝了整數(shù) 37 和 47 的兩個(gè) Integer 對(duì)象。執(zhí)行方法的返回的同樣是一個(gè) Integer 對(duì)象,它封裝了返回值 84。
7.創(chuàng)建新的對(duì)象
對(duì)于構(gòu)造器,則不能像執(zhí)行方法那樣進(jìn)行,因?yàn)閳?zhí)行一個(gè)構(gòu)造器就意味著創(chuàng)建了一個(gè)新的對(duì)象 (準(zhǔn)確的說(shuō),創(chuàng)建一個(gè)對(duì)象的過(guò)程包括分配內(nèi)存和構(gòu)造對(duì)象)。所以,與上例最相似的例子如下:
import java.lang.reflect.*;
public class constructor2 {
public constructor2() {
}
public constructor2(int a, int b) {
System.out.println("a = " + a + " b = " + b);
}
public static void main(String args[]) {
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct = cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
} catch (Throwable e) {
System.err.println(e);
}
}
}
根據(jù)指定的參數(shù)類(lèi)型找到相應(yīng)的構(gòu)造函數(shù)并執(zhí)行它,以創(chuàng)建一個(gè)新的對(duì)象實(shí)例。使用這種方法可以在程序運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建對(duì)象,而不是在編譯的時(shí)候創(chuàng)建對(duì)象,這一點(diǎn)非常有價(jià)值。
8.改變字段(域)的值
reflection 的還有一個(gè)用處就是改變對(duì)象數(shù)據(jù)字段的值。reflection 可以從正在運(yùn)行的程序中根據(jù)名稱(chēng)找到對(duì)象的字段并改變它,下面的例子可以說(shuō)明這一點(diǎn):
import java.lang.reflect.*;
public class field2 {
public double d;
public static void main(String args[]) {
try {
Class cls = Class.forName("field2");
Field fld = cls.getField("d");
field2 f2obj = new field2();
System.out.println("d = " + f2obj.d);
fld.setDouble(f2obj, 12.34);
System.out.println("d = " + f2obj.d);
} catch (Throwable e) {
System.err.println(e);
}
}
}
這個(gè)例子中,字段 d 的值被變?yōu)榱?nbsp;12.34。
9.使用數(shù)組
本文介紹的 reflection 的最后一種用法是創(chuàng)建的操作數(shù)組。數(shù)組在 Java 語(yǔ)言中是一種特殊的類(lèi)類(lèi)型,一個(gè)數(shù)組的引用可以賦給 Object 引用。觀察下面的例子看看數(shù)組是怎么工作的:
import java.lang.reflect.*;
public class array1 {
public static void main(String args[]) {
try {
Class cls = Class.forName("java.lang.String");
Object arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "this is a test");
String s = (String) Array.get(arr, 5);
System.out.println(s);
} catch (Throwable e) {
System.err.println(e);
}
}
}
例中創(chuàng)建了 10 個(gè)單位長(zhǎng)度的 String 數(shù)組,為第 5 個(gè)位置的字符串賦了值,最后將這個(gè)字符串從數(shù)組中取得并打印了出來(lái)。
下面這段代碼提供了一個(gè)更復(fù)雜的例子:
import java.lang.reflect.*;
public class array2 {
public static void main(String args[]) {
int dims[] = new int[]{5, 10, 15};
Object arr = Array.newInstance(Integer.TYPE, dims);
Object arrobj = Array.get(arr, 3);
Class cls = arrobj.getClass().getComponentType();
System.out.println(cls);
arrobj = Array.get(arrobj, 5);
Array.setInt(arrobj, 10, 37);
int arrcast[][][] = (int[][][]) arr;
System.out.println(arrcast[3][5][10]);
}
}
例中創(chuàng)建了一個(gè) 5 x 10 x 15 的整型數(shù)組,并為處于 [3][5][10] 的元素賦了值為 37。注意,多維數(shù)組實(shí)際上就是數(shù)組的數(shù)組,例如,第一個(gè) Array.get 之后,arrobj 是一個(gè) 10 x 15 的數(shù)組。進(jìn)而取得其中的一個(gè)元素,即長(zhǎng)度為 15 的數(shù)組,并使用 Array.setInt 為它的第 10 個(gè)元素賦值。
注意創(chuàng)建數(shù)組時(shí)的類(lèi)型是動(dòng)態(tài)的,在編譯時(shí)并不知道其類(lèi)型。
posted @
2005-11-17 09:53 安德?tīng)査?閱讀(646) |
評(píng)論 (2) |
編輯 收藏
1.對(duì)象的復(fù)制
2.clone()的使用
3.對(duì)象實(shí)例的比較
////////////////////////////
1.對(duì)象的復(fù)制
-
- String str1 = "This is a string!" //這里是 "對(duì)象引用" 的復(fù)制
- String str2 = new String(str1); //這里是 "對(duì)象實(shí)例" 的復(fù)制
淺復(fù)制: 只復(fù)制復(fù)合對(duì)象本身.
深復(fù)制: 除了復(fù)制復(fù)合對(duì)象本身, 還復(fù)制了復(fù)合對(duì)象的引用的對(duì)象實(shí)例.
例如:
-
- class Pupil{
- public Pupil(String sno, String name, int age){
- this.sno = new String(sno);
- this.name = new String(name);
- this.age = age;
- }
-
- public String getSno() {
- return sno;
- }
-
- public String getName() {
- return name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- private String sno;
- private String name;
- private int age;
- }
-
- public class CopyDemo {
- public static Pupil[] shallowCopy(Pupil[] aClass) {
- Pupil[] newClass = new Pupil[aClass.length];
-
- //此時(shí)newClass 與aClass 指向同一塊內(nèi)存
- for(int i=0; i<aClass.length; i++)
- newClass[i] = aClass[i];
- return newClass;
- }
-
- public static Pupil[] deepCopy(Pupil[] aClass) {
- Pupil[] newClass = new Pupil[aClass.length];
-
- //此時(shí)newClass 與aClass 的相應(yīng)sno , name 指向同一塊內(nèi)存
- for(int i=0; i<aClass.length; i++) {
- String sno = aClass[i].getSno();
- String name = aClass[i].getName();
- int age = aClass[i].getAge();
- newClass[i] = new Pupil(sno, name, age);
- }
-
- return newClass;
- }
-
- public static Pupil[] deeperCopy(Pupil[] aClass) {
- Pupil[] newClass = new Pupil[aClass.length];
-
- //完全的復(fù)制
- for(int i=0; i<aClass.length; i++) {
- String sno = new String(aClass[i].getSno());
- String name = new String(aClass[i].getName());
- int age = aClass[i].getAge();
- newClass[i] = new Pupil(sno, name, age);
- }
-
- return newClass;
- }
- }
2.clone()的使用
* Object.clone()
* Cloneable 接口
* CloneNotSupportedException
a. 使用Object.clone 進(jìn)行復(fù)制
兩個(gè)必須條件:
1.一定要將重定義后的clone() 方法定義為公有方法(在Object 類(lèi)中, 它是受保護(hù)的成員, 不能直接使用)
2.該后代類(lèi)聲明實(shí)現(xiàn)接口 Cloneable 接口(當(dāng)類(lèi)實(shí)現(xiàn)該接口, 其任何子類(lèi)也會(huì)繼承該接口), 該接口實(shí)際上沒(méi)有任何
內(nèi)容, 只是一個(gè)標(biāo)識(shí), 標(biāo)志實(shí)現(xiàn)該接口的類(lèi)提供clone() 方法.(這是接口的一種非典型用法)
b.重寫(xiě)Object.clone()
例如對(duì) private char[] cb; character buffer 進(jìn)行復(fù)制
c.復(fù)制數(shù)組
數(shù)組是在方法調(diào)用重以引用的形式傳遞的對(duì)象. 下述情況下非常適合引用來(lái)傳遞數(shù)組:
*正在接收的方法不修改數(shù)組
*正在調(diào)用的方法不必關(guān)心是否修改數(shù)組
*正在調(diào)用的方法想要得到數(shù)組中的修改結(jié)果
否則, 就應(yīng)該在方法調(diào)用中傳遞數(shù)組對(duì)象的副本. 只需調(diào)用 arrObj.clone() 方法即可完成數(shù)組arrObj 的復(fù)制操作. 隨后將該數(shù)組副本強(qiáng)制轉(zhuǎn)換為其正確類(lèi)型:
(type[])arrObj.clone();
System.arraycopy 方法提供一種用于在數(shù)組間復(fù)制多個(gè)元素的有效方式.
System.arraycopy(source, i, target, j, len)
3.對(duì)象實(shí)例的比較
例如:
-
- Pupil p1 = new Pupil("99184001", "zhang3", 18);
- Pupil p2 = new Pupil("99184001", "zhang3", 18);
a. "=="
if(p1 == p2)...
此次測(cè)試的是對(duì)象引用, 其結(jié)果肯定是false, 只要兩個(gè)對(duì)象引用不是互為別名就不會(huì)相等.
b. 淺比較 false
-
- if(p1.getSno() == p2.getSno() && p1.getName() == p2.getName()
- && p1.getAge() == p2.getAge()) ...;
c. 深比較 true[/code]
if(p1.getSno().equals(p2.getSno()) && p1.getName().equals(p2.getName())
&& p1.getAge() == p2.getAge()) ...;[/code]
JAVA API 的跟類(lèi)Object 也提供了equals() 方法, 但它只是比較兩個(gè)對(duì)象引用, 而非比較兩個(gè)對(duì)象實(shí)例.
不管怎樣, 如果需要比較Pupil 類(lèi)的對(duì)象(例如要將它們放入對(duì)象容器), 應(yīng)該為Pupil 類(lèi)重定義equals() 方法:
-
- public boolean equals(Object otherobj) {
- //檢查otherobj 是否為空
- if(otherobj == null) return false;
-
- //檢查otherobj 是否就是當(dāng)前對(duì)象
- if(otherobj == this) return true;
-
- //檢查otherobj 是否具有正確的類(lèi)型, 即檢查是否可與當(dāng)前對(duì)象比較
- if(!(otherobj instanceof Pupil)) return false;
-
- //將otherobj 轉(zhuǎn)換為Pupil 類(lèi)的對(duì)象引用
- Pupil tmpObj = (Pupil)otherobj;
- //關(guān)于學(xué)生是否相等的邏輯檢查
- if(sno.equals(tmpObj.sno) && name.equals(tmpObj.name)
- && age == tmpObj.age) return true;
-
- return false;
- }
JAVA API 所提供的每個(gè)類(lèi)幾乎都提供了采用深比較策略的equals() 方法, 例如String 類(lèi)equals() 方法. 一般來(lái)說(shuō), 用戶自己定義的類(lèi)也應(yīng)當(dāng)提供合適的equals() 方法, 特別是當(dāng)程序要將其對(duì)象放入JAVA API 所提供的對(duì)象容器類(lèi)的時(shí)候.
按照約定, 任何類(lèi)所提供的equals() 方法所實(shí)現(xiàn)的相等比較應(yīng)該是等價(jià)關(guān)系, 即滿足自反性, 對(duì)稱(chēng)性和傳遞性. 另外一個(gè)類(lèi)重定義了equals() 方法, 也應(yīng)該重定義相應(yīng)hashCode() 方法, 否則將這個(gè)類(lèi)的對(duì)象放入映射對(duì)象容器時(shí)也會(huì)發(fā)生以外.
posted @
2005-11-17 09:47 安德?tīng)査?閱讀(966) |
評(píng)論 (1) |
編輯 收藏
??Java反射機(jī)制
侯捷觀點(diǎn)
Java反射機(jī)制
摘要
Reflection 是Java被視為動(dòng)態(tài)(或準(zhǔn)動(dòng)態(tài))語(yǔ)言的一個(gè)關(guān)鍵性質(zhì)。這個(gè)機(jī)制允許程序在運(yùn)行時(shí)
透過(guò)Reflection APIs取得任何一個(gè)已知名稱(chēng)的class的內(nèi)部信息,包括其modifiers(諸如
public, static 等等)、superclass(例如Object)、實(shí)現(xiàn)之interfaces(例如
Cloneable),也包括fields和methods的所有信息,并可于運(yùn)行時(shí)改變fields內(nèi)容或喚起
methods。本文借由實(shí)例,大面積示范Reflection APIs。
關(guān)于本文:
讀者基礎(chǔ):具備Java 語(yǔ)言基礎(chǔ)。
本文適用工具:JDK1.5
關(guān)鍵詞:
Introspection(內(nèi)省、內(nèi)觀)
Reflection(反射)
有時(shí)候我們說(shuō)某個(gè)語(yǔ)言具有很強(qiáng)的動(dòng)態(tài)性,有時(shí)候我們會(huì)區(qū)分動(dòng)態(tài)和靜態(tài)的不同技術(shù)與作法。我們
朗朗上口動(dòng)態(tài)綁定(dynamic binding)、動(dòng)態(tài)鏈接(dynamic linking)、動(dòng)態(tài)加載
(dynamic loading)等。然而“動(dòng)態(tài)”一詞其實(shí)沒(méi)有絕對(duì)而普遍適用的嚴(yán)格定義,有時(shí)候甚至
像對(duì)象導(dǎo)向當(dāng)初被導(dǎo)入編程領(lǐng)域一樣,一人一把號(hào),各吹各的調(diào)。
一般而言,開(kāi)發(fā)者社群說(shuō)到動(dòng)態(tài)語(yǔ)言,大致認(rèn)同的一個(gè)定義是:“程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)
或變量類(lèi)型,這種語(yǔ)言稱(chēng)為動(dòng)態(tài)語(yǔ)言”。從這個(gè)觀點(diǎn)看,Perl,Python,Ruby是動(dòng)態(tài)語(yǔ)言,C+
+,Java,C#不是動(dòng)態(tài)語(yǔ)言。
盡管在這樣的定義與分類(lèi)下Java不是動(dòng)態(tài)語(yǔ)言,它卻有著一個(gè)非常突出的動(dòng)態(tài)相關(guān)機(jī)制:
Reflection。這個(gè)字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以于運(yùn)行時(shí)加
載、探知、使用編譯期間完全未知的classes。換句話說(shuō),Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名
稱(chēng)的class,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對(duì)象實(shí)體、或?qū)ζ鋐ields設(shè)
值、或喚起其methods1。這種“看透class”的能力(the ability of the program to
examine itself)被稱(chēng)為introspection(內(nèi)省、內(nèi)觀、反省)。Reflection和
introspection是常被并提的兩個(gè)術(shù)語(yǔ)。
Java如何能夠做出上述的動(dòng)態(tài)特性呢?這是一個(gè)深遠(yuǎn)話題,本文對(duì)此只簡(jiǎn)單介紹一些概念。整個(gè)篇
幅最主要還是介紹Reflection APIs,也就是讓讀者知道如何探索class的結(jié)構(gòu)、如何對(duì)某
個(gè)“運(yùn)行時(shí)才獲知名稱(chēng)的class”生成一份實(shí)體、為其fields設(shè)值、調(diào)用其methods。本文將談到
file:///H|/download/806.html(第 1/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
java.lang.Class,以及java.lang.reflect中的Method、Field、Constructor等等
classes。
“Class”class
眾所周知Java有個(gè)Object class,是所有Java classes的繼承根源,其內(nèi)聲明了數(shù)個(gè)應(yīng)該在所
有Java class中被改寫(xiě)的methods:hashCode()、equals()、clone()、toString()、
getClass()等。其中g(shù)etClass()返回一個(gè)Class object。
Class class十分特殊。它和一般classes一樣繼承自O(shè)bject,其實(shí)體用以表達(dá)Java程序運(yùn)行時(shí)
的classes和interfaces,也用來(lái)表達(dá)enum、array、primitive Java types
(boolean, byte, char, short, int, long, float, double)以及關(guān)鍵詞void。當(dāng)一
個(gè)class被加載,或當(dāng)加載器(class loader)的defineClass()被JVM調(diào)用,JVM 便自動(dòng)產(chǎn)
生一個(gè)Class object。如果您想借由“修改Java標(biāo)準(zhǔn)庫(kù)源碼”來(lái)觀察Class object的實(shí)際生成
時(shí)機(jī)(例如在Class的constructor內(nèi)添加一個(gè)println()),不能夠!因?yàn)镃lass并沒(méi)有
public constructor(見(jiàn)圖1)。本文最后我會(huì)撥一小塊篇幅順帶談?wù)凧ava標(biāo)準(zhǔn)庫(kù)源碼的改動(dòng)辦
法。
Class是Reflection故事起源。針對(duì)任何您想探勘的class,唯有先為它產(chǎn)生一個(gè)Class
object,接下來(lái)才能經(jīng)由后者喚起為數(shù)十多個(gè)的Reflection APIs。這些APIs將在稍后的探險(xiǎn)
活動(dòng)中一一亮相。
#001 public final
#002 class Class<T> implements java.io.Serializable,
#003 java.lang.reflect.GenericDeclaration,
#004 java.lang.reflect.Type,
#005 java.lang.reflect.AnnotatedElement {
#006 private Class() {}
#007 public String toString() {
#008 return ( isInterface() ? "interface " :
#009 (isPrimitive() ? "" : "class "))
#010 + getName();
#011 }
...
圖1:Class class片段。注意它的private empty ctor,意指不允許任何人經(jīng)由編程方式產(chǎn)生Class object。是的,其object 只能由
JVM 產(chǎn)生。
“Class” object的取得途徑
Java允許我們從多種管道為一個(gè)class生成對(duì)應(yīng)的Class object。圖2是一份整理。
Class object 誕生管道示例
運(yùn)用getClass()
注:每個(gè)class 都有此函數(shù)
String str = "abc";
Class c1 = str.getClass();
file:///H|/download/806.html(第 2/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
運(yùn)用
Class.getSuperclass()2
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
運(yùn)用static method
Class.forName()
(最常被使用)
Class c1 = Class.forName ("java.lang.
String");
Class c2 = Class.forName ("java.awt.Button");
Class c3 = Class.forName ("java.util.
LinkedList$Entry");
Class c4 = Class.forName ("I");
Class c5 = Class.forName ("[I");
運(yùn)用
.class 語(yǔ)法
Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c3 = Main.InnerClass.class;
Class c4 = int.class;
Class c5 = int[].class;
運(yùn)用
primitive wrapper
classes
的TYPE 語(yǔ)法
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;
Class c3 = Character.TYPE;
Class c4 = Short.TYPE;
Class c5 = Integer.TYPE;
Class c6 = Long.TYPE;
Class c7 = Float.TYPE;
Class c8 = Double.TYPE;
Class c9 = Void.TYPE;
圖2:Java 允許多種管道生成Class object。
Java classes 組成分析
首先容我以圖3的java.util.LinkedList為例,將Java class的定義大卸八塊,每一塊分別對(duì)
應(yīng)圖4所示的Reflection API。圖5則是“獲得class各區(qū)塊信息”的程序示例及執(zhí)行結(jié)果,它們
都取自本文示例程序的對(duì)應(yīng)片段。
package java.util; //(1)
import java.lang.*; //(2)
public class LinkedList<E> //(3)(4)(5)
extends AbstractSequentialList<E> //(6)
implements List<E>, Queue<E>,
Cloneable, java.io.Serializable //(7)
{
private static class Entry<E> { … }//(8)
public LinkedList() { … } //(9)
public LinkedList(Collection<? extends E> c) { … }
public E getFirst() { … } //(10)
public E getLast() { … }
private transient Entry<E> header = …; //(11)
private transient int size = 0;
}
file:///H|/download/806.html(第 3/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
圖3:將一個(gè)Java class 大卸八塊,每塊相應(yīng)于一個(gè)或一組Reflection APIs(圖4)。
Java classes 各成份所對(duì)應(yīng)的Reflection APIs
圖3的各個(gè)Java class成份,分別對(duì)應(yīng)于圖4的Reflection API,其中出現(xiàn)的Package、
Method、Constructor、Field等等classes,都定義于java.lang.reflect。
Java class 內(nèi)
部模塊(參見(jiàn)圖
3)
Java class 內(nèi)部模塊說(shuō)明相應(yīng)之Reflection
API,多半為Class
methods。
返回值類(lèi)型
(return type)
(1) package class隸屬哪個(gè)package getPackage() Package
(2) import class導(dǎo)入哪些classes 無(wú)直接對(duì)應(yīng)之API。
解決辦法見(jiàn)圖5-2。
(3) modifier class(或methods,
fields)的屬性
int getModifiers()
Modifier.toString
(int)
Modifier.
isInterface(int)
int
String
bool
(4) class
name or
interface
name
class/interface 名稱(chēng)getName() String
(5) type
parameters
參數(shù)化類(lèi)型的名稱(chēng)getTypeParameters
()
TypeVariable
<Class>[]
(6) base
class
base class(只可能一個(gè)) getSuperClass() Class
(7)
implemented
interfaces
實(shí)現(xiàn)有哪些interfaces getInterfaces() Class[]
(8) inner
classes
內(nèi)部classes getDeclaredClasses
()
Class[]
(8') outer
class
如果我們觀察的class 本身是
inner classes,那么相對(duì)它
就會(huì)有個(gè)outer class。
getDeclaringClass() Class
(9)
constructors
構(gòu)造函數(shù)
getDeclaredConstructors
()
不論 public 或
private 或其它
access level,皆可獲
得。另有功能近似之取得
函數(shù)。
Constructor[]
file:///H|/download/806.html(第 4/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
(10) methods 操作函數(shù)
getDeclaredMethods()
不論 public 或
private 或其它
access level,皆可獲
得。另有功能近似之取得
函數(shù)。
Method[]
(11) fields 字段(成員變量) getDeclaredFields()
不論 public 或
private 或其它
access level,皆可獲
得。另有功能近似之取得
函數(shù)。
Field[]
圖4:Java class大卸八塊后(如圖3),每一塊所對(duì)應(yīng)的Reflection API。本表并非
Reflection APIs 的全部。
Java Reflection API 運(yùn)用示例
圖5示范圖4提過(guò)的每一個(gè)Reflection API,及其執(zhí)行結(jié)果。程序中出現(xiàn)的tName()是個(gè)輔助函
數(shù),可將其第一自變量所代表的“Java class完整路徑字符串”剝除路徑部分,留下class名
稱(chēng),儲(chǔ)存到第二自變量所代表的一個(gè)hashtable去并返回(如果第二自變量為null,就不儲(chǔ)存而只
是返回)。
#001 Class c = null;
#002 c = Class.forName(args[0]);
#003
#004 Package p;
#005 p = c.getPackage();
#006
#007 if (p != null)
#008 System.out.println("package "+p.getName()+";");
執(zhí)行結(jié)果(例):
package java.util;
圖5-1:找出class 隸屬的package。其中的c將繼續(xù)沿用于以下各程序片段。
#001 ff = c.getDeclaredFields();
#002 for (int i = 0; i < ff.length; i++)
#003 x = tName(ff[i].getType().getName(), classRef);
#004
#005 cn = c.getDeclaredConstructors();
#006 for (int i = 0; i < cn.length; i++) {
#007 Class cx[] = cn[i].getParameterTypes();
#008 for (int j = 0; j < cx.length; j++)
#009 x = tName(cx[j].getName(), classRef);
#010 }
#011
#012 mm = c.getDeclaredMethods();
#013 for (int i = 0; i < mm.length; i++) {
#014 x = tName(mm[i].getReturnType().getName(), classRef);
#015 Class cx[] = mm[i].getParameterTypes();
file:///H|/download/806.html(第 5/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
#016 for (int j = 0; j < cx.length; j++)
#017 x = tName(cx[j].getName(), classRef);
#018 }
#019 classRef.remove(c.getName()); //不必記錄自己(不需import 自己)
執(zhí)行結(jié)果(例):
import java.util.ListIterator;
import java.lang.Object;
import java.util.LinkedList$Entry;
import java.util.Collection;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
圖5-2:找出導(dǎo)入的classes,動(dòng)作細(xì)節(jié)詳見(jiàn)內(nèi)文說(shuō)明。
#001 int mod = c.getModifiers();
#002 System.out.print(Modifier.toString(mod)); //整個(gè)modifier
#003
#004 if (Modifier.isInterface(mod))
#005 System.out.print(" "); //關(guān)鍵詞 "interface" 已含于modifier
#006 else
#007 System.out.print(" class "); //關(guān)鍵詞 "class"
#008 System.out.print(tName(c.getName(), null)); //class 名稱(chēng)
執(zhí)行結(jié)果(例):
public class LinkedList
圖5-3:找出class或interface 的名稱(chēng),及其屬性(modifiers)。
#001 TypeVariable<Class>[] tv;
#002 tv = c.getTypeParameters(); //warning: unchecked conversion
#003 for (int i = 0; i < tv.length; i++) {
#004 x = tName(tv[i].getName(), null); //例如 E,K,V...
#005 if (i == 0) //第一個(gè)
#006 System.out.print("<" + x);
#007 else //非第一個(gè)
#008 System.out.print("," + x);
#009 if (i == tv.length-1) //最后一個(gè)
#010 System.out.println(">");
#011 }
執(zhí)行結(jié)果(例):
public abstract interface Map<K,V>
或 public class LinkedList<E>
圖5-4:找出parameterized types 的名稱(chēng)
#001 Class supClass;
#002 supClass = c.getSuperclass();
#003 if (supClass != null) //如果有super class
#004 System.out.print(" extends" +
#005 tName(supClass.getName(),classRef));
執(zhí)行結(jié)果(例):
public class LinkedList<E>
extends AbstractSequentialList,
file:///H|/download/806.html(第 6/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
圖5-5:找出base class。執(zhí)行結(jié)果多出一個(gè)不該有的逗號(hào)于尾端。此非本處重點(diǎn),為簡(jiǎn)化計(jì),不多做處理。
#001 Class cc[];
#002 Class ctmp;
#003 //找出所有被實(shí)現(xiàn)的interfaces
#004 cc = c.getInterfaces();
#005 if (cc.length != 0)
#006 System.out.print(", \r\n" + " implements "); //關(guān)鍵詞
#007 for (Class cite : cc) //JDK1.5 新式循環(huán)寫(xiě)法
#008 System.out.print(tName(cite.getName(), null)+", ");
執(zhí)行結(jié)果(例):
public class LinkedList<E>
extends AbstractSequentialList,
implements List, Queue, Cloneable, Serializable,
圖5-6:找出implemented interfaces。執(zhí)行結(jié)果多出一個(gè)不該有的逗號(hào)于尾端。此非本處重點(diǎn),為簡(jiǎn)化計(jì),不多做處
理。
#001 cc = c.getDeclaredClasses(); //找出inner classes
#002 for (Class cite : cc)
#003 System.out.println(tName(cite.getName(), null));
#004
#005 ctmp = c.getDeclaringClass(); //找出outer classes
#006 if (ctmp != null)
#007 System.out.println(ctmp.getName());
執(zhí)行結(jié)果(例):
LinkedList$Entry
LinkedList$ListItr
圖5-7:找出inner classes 和outer class
#001 Constructor cn[];
#002 cn = c.getDeclaredConstructors();
#003 for (int i = 0; i < cn.length; i++) {
#004 int md = cn[i].getModifiers();
#005 System.out.print(" " + Modifier.toString(md) + " " +
#006 cn[i].getName());
#007 Class cx[] = cn[i].getParameterTypes();
#008 System.out.print("(");
#009 for (int j = 0; j < cx.length; j++) {
#010 System.out.print(tName(cx[j].getName(), null));
#011 if (j < (cx.length - 1)) System.out.print(", ");
#012 }
#013 System.out.print(")");
#014 }
執(zhí)行結(jié)果(例):
public java.util.LinkedList(Collection)
public java.util.LinkedList()
圖5-8a:找出所有constructors
#004 System.out.println(cn[i].toGenericString());
執(zhí)行結(jié)果(例):
file:///H|/download/806.html(第 7/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
public java.util.LinkedList(java.util.Collection<? extends E>)
public java.util.LinkedList()
圖5-8b:找出所有constructors。本例在for 循環(huán)內(nèi)使用toGenericString(),省事。
#001 Method mm[];
#002 mm = c.getDeclaredMethods();
#003 for (int i = 0; i < mm.length; i++) {
#004 int md = mm[i].getModifiers();
#005 System.out.print(" "+Modifier.toString(md)+" "+
#006 tName(mm[i].getReturnType().getName(), null)+" "+
#007 mm[i].getName());
#008 Class cx[] = mm[i].getParameterTypes();
#009 System.out.print("(");
#010 for (int j = 0; j < cx.length; j++) {
#011 System.out.print(tName(cx[j].getName(), null));
#012 if (j < (cx.length - 1)) System.out.print(", ");
#013 }
#014 System.out.print(")");
#015 }
執(zhí)行結(jié)果(例):
public Object get(int)
public int size()
圖5-9a:找出所有methods
#004 System.out.println(mm[i].toGenericString());
public E java.util.LinkedList.get(int)
public int java.util.LinkedList.size()
圖5-9b:找出所有methods。本例在for 循環(huán)內(nèi)使用toGenericString(),省事。
#001 Field ff[];
#002 ff = c.getDeclaredFields();
#003 for (int i = 0; i < ff.length; i++) {
#004 int md = ff[i].getModifiers();
#005 System.out.println(" "+Modifier.toString(md)+" "+
#006 tName(ff[i].getType().getName(), null) +" "+
#007 ff[i].getName()+";");
#008 }
執(zhí)行結(jié)果(例):
private transient LinkedList$Entry header;
private transient int size;
圖5-10a:找出所有fields
#004 System.out.println("G: " + ff[i].toGenericString());
private transient java.util.LinkedList.java.util.LinkedList$Entry<E> ??
java.util.LinkedList.header
private transient int java.util.LinkedList.size
圖5-10b:找出所有fields。本例在for 循環(huán)內(nèi)使用toGenericString(),省事。
找出class參用(導(dǎo)入)的所有classes
file:///H|/download/806.html(第 8/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
沒(méi)有直接可用的Reflection API可以為我們找出某個(gè)class參用的所有其它c(diǎn)lasses。要獲得這
項(xiàng)信息,必須做苦工,一步一腳印逐一記錄。我們必須觀察所有fields的類(lèi)型、所有methods(包
括constructors)的參數(shù)類(lèi)型和回返類(lèi)型,剔除重復(fù),留下唯一。這正是為什么圖5-2程序代碼
要為tName()指定一個(gè)hashtable(而非一個(gè)null)做為第二自變量的緣故:hashtable可為我
們儲(chǔ)存元素(本例為字符串),又保證不重復(fù)。
本文討論至此,幾乎可以還原一個(gè)class的原貌(唯有methods 和ctors的定義無(wú)法取得)。接下
來(lái)討論Reflection 的另三個(gè)動(dòng)態(tài)性質(zhì):(1) 運(yùn)行時(shí)生成instances,(2) 執(zhí)
行期喚起methods,(3) 運(yùn)行時(shí)改動(dòng)fields。
運(yùn)行時(shí)生成instances
欲生成對(duì)象實(shí)體,在Reflection 動(dòng)態(tài)機(jī)制中有兩種作法,一個(gè)針對(duì)“無(wú)自變量ctor”,
一個(gè)針對(duì)“帶參數(shù)ctor”。圖6是面對(duì)“無(wú)自變量ctor”的例子。如果欲調(diào)用的是“帶參數(shù)
ctor“就比較麻煩些,圖7是個(gè)例子,其中不再調(diào)用Class的newInstance(),而是調(diào)用
Constructor 的newInstance()。圖7首先準(zhǔn)備一個(gè)Class[]做為ctor的參數(shù)類(lèi)型(本例指定
為一個(gè)double和一個(gè)int),然后以此為自變量調(diào)用getConstructor(),獲得一個(gè)專(zhuān)屬ctor。
接下來(lái)再準(zhǔn)備一個(gè)Object[] 做為ctor實(shí)參值(本例指定3.14159和125),調(diào)用上述專(zhuān)屬ctor
的newInstance()。
#001 Class c = Class.forName("DynTest");
#002 Object obj = null;
#003 obj = c.newInstance(); //不帶自變量
#004 System.out.println(obj);
圖6:動(dòng)態(tài)生成“Class object 所對(duì)應(yīng)之class”的對(duì)象實(shí)體;無(wú)自變量。
#001 Class c = Class.forName("DynTest");
#002 Class[] pTypes = new Class[] { double.class, int.class };
#003 Constructor ctor = c.getConstructor(pTypes);
#004 //指定parameter list,便可獲得特定之ctor
#005
#006 Object obj = null;
#007 Object[] arg = new Object[] {3.14159, 125}; //自變量
#008 obj = ctor.newInstance(arg);
#009 System.out.println(obj);
圖7:動(dòng)態(tài)生成“Class object 對(duì)應(yīng)之class”的對(duì)象實(shí)體;自變量以O(shè)bject[]表示。
運(yùn)行時(shí)調(diào)用methods
這個(gè)動(dòng)作和上述調(diào)用“帶參數(shù)之ctor”相當(dāng)類(lèi)似。首先準(zhǔn)備一個(gè)Class[]做為ctor的參數(shù)類(lèi)型
(本例指定其中一個(gè)是String,另一個(gè)是Hashtable),然后以此為自變量調(diào)用getMethod(),
獲得特定的Method object。接下來(lái)準(zhǔn)備一個(gè)Object[]放置自變量,然后調(diào)用上述所得之特定
Method object的invoke(),如圖8。知道為什么索取Method object時(shí)不需指定回返類(lèi)型
嗎?因?yàn)閙ethod overloading機(jī)制要求signature(署名式)必須唯一,而回返類(lèi)型并非
signature的一個(gè)成份。換句話說(shuō),只要指定了method名稱(chēng)和參數(shù)列,就一定指出了一個(gè)獨(dú)一無(wú)
二的method。
file:///H|/download/806.html(第 9/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
#001 public String func(String s, Hashtable ht)
#002 {
#003 …System.out.println("func invoked"); return s;
#004 }
#005 public static void main(String args[])
#006 {
#007 Class c = Class.forName("Test");
#008 Class ptypes[] = new Class[2];
#009 ptypes[0] = Class.forName("java.lang.String");
#010 ptypes[1] = Class.forName("java.util.Hashtable");
#011 Method m = c.getMethod("func",ptypes);
#012 Test obj = new Test();
#013 Object args[] = new Object[2];
#014 arg[0] = new String("Hello,world");
#015 arg[1] = null;
#016 Object r = m.invoke(obj, arg);
#017 Integer rval = (String)r;
#018 System.out.println(rval);
#019 }
圖8:動(dòng)態(tài)喚起method
運(yùn)行時(shí)變更fields內(nèi)容
與先前兩個(gè)動(dòng)作相比,“變更field內(nèi)容”輕松多了,因?yàn)樗恍枰獏?shù)和自變量。首先調(diào)用
Class的getField()并指定field名稱(chēng)。獲得特定的Field object之后便可直接調(diào)用Field的
get()和set(),如圖9。
#001 public class Test {
#002 public double d;
#003
#004 public static void main(String args[])
#005 {
#006 Class c = Class.forName("Test");
#007 Field f = c.getField("d"); //指定field 名稱(chēng)
#008 Test obj = new Test();
#009 System.out.println("d= " + (Double)f.get(obj));
#010 f.set(obj, 12.34);
#011 System.out.println("d= " + obj.d);
#012 }
#013 }
圖9:動(dòng)態(tài)變更field 內(nèi)容
Java 源碼改動(dòng)辦法
先前我曾提到,原本想借由“改動(dòng)Java標(biāo)準(zhǔn)庫(kù)源碼”來(lái)測(cè)知Class object的生成,但由于其
ctor原始設(shè)計(jì)為private,也就是說(shuō)不可能透過(guò)這個(gè)管道生成Class object(而是由class
loader負(fù)責(zé)生成),因此“在ctor中打印出某種信息”的企圖也就失去了意義。
這里我要談點(diǎn)題外話:如何修改Java標(biāo)準(zhǔn)庫(kù)源碼并讓它反應(yīng)到我們的應(yīng)用程序來(lái)。假設(shè)我想修改
java.lang.Class,讓它在某些情況下打印某種信息。首先必須找出標(biāo)準(zhǔn)源碼!當(dāng)你下載JDK 套
file:///H|/download/806.html(第 10/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
件并安裝妥當(dāng),你會(huì)發(fā)現(xiàn)jdk150\src\java\lang 目錄(見(jiàn)圖10)之中有Class.java,這就是
我們此次行動(dòng)的標(biāo)準(zhǔn)源碼。備份后加以修改,編譯獲得Class.class。接下來(lái)準(zhǔn)備將.class 搬移
到j(luò)dk150\jre\lib\endorsed(見(jiàn)圖10)。
這是一個(gè)十分特別的目錄,class loader將優(yōu)先從該處讀取內(nèi)含classes的.jar文件??成功的
條件是.jar內(nèi)的classes壓縮路徑必須和Java標(biāo)準(zhǔn)庫(kù)的路徑完全相同。為此,我們可以將剛才做
出的Class.class先搬到一個(gè)為此目的而刻意做出來(lái)的\java\lang目錄中,壓縮為foo.zip(任
意命名,唯需夾帶路徑j(luò)ava\lang),再將這個(gè)foo.zip搬到j(luò)dk150\jre\lib\endorsed并改
名為foo.jar。此后你的應(yīng)用程序便會(huì)優(yōu)先用上這里的java.lang.Class。整個(gè)過(guò)程可寫(xiě)成一個(gè)
批處理文件(batch file),如圖11,在DOS Box中使用。
圖10
圖10:JDK1.5 安裝后的目錄組織。其中的endorsed 是我新建。
del e:\java\lang\*.class //清理干凈
del c:\jdk150\jre\lib\endorsed\foo.jar //清理干凈
c:
cd c:\jdk150\src\java\lang
javac -Xlint:unchecked Class.java //編譯源碼
javac -Xlint:unchecked ClassLoader.java //編譯另一個(gè)源碼(如有必要)
move *.class e:\java\lang //搬移至刻意制造的目錄中
e:
cd e:\java\lang //以下壓縮至適當(dāng)目錄
pkzipc -add -path=root c:\jdk150\jre\lib\endorsed\foo.jar *.class
cd e:\test //進(jìn)入測(cè)試目錄
javac -Xlint:unchecked Test.java //編譯測(cè)試程序
java Test //執(zhí)行測(cè)試程序
圖11:一個(gè)可在DOS Box中使用的批處理文件(batch file),用以自動(dòng)化java.lang.Class
的修改動(dòng)作。Pkzipc(.exe)是個(gè)命令列壓縮工具,add和path都是其命令。
更多信息
以下是視野所及與本文主題相關(guān)的更多討論。這些信息可以彌補(bǔ)因文章篇幅限制而帶來(lái)的不足,或
帶給您更多視野。
l "Take an in-depth look at the Java Reflection API -- Learn about
the new Java 1.1 tools forfinding out information about classes", by
Chuck McManis。此篇文章所附程序代碼是本文示例程序的主要依據(jù)(本文示例程序示范了更
多Reflection APIs,并采用JDK1.5 新式的for-loop 寫(xiě)法)。
l "Take a look inside Java classes -- Learn to deduce properties of
a Java class from inside aJava program", by Chuck McManis。
l "The basics of Java class loaders -- The fundamentals of this key
component of the Javaarchitecture", by Chuck McManis。
l 《The Java Tutorial Continued》, Sun microsystems. Lesson58-61,
"Reflection".
file:///H|/download/806.html(第 11/12 頁(yè))2005-9-8 12:03:22
侯捷觀點(diǎn)??Java反射機(jī)制
注1用過(guò)諸如MFC這類(lèi)所謂 Application Framework的程序員也許知道,MFC有所謂的
dynamic creation。但它并不等同于Java的動(dòng)態(tài)加載或動(dòng)態(tài)辨識(shí);所有能夠在MFC程序中起作用
的classes,都必須先在編譯期被編譯器“看見(jiàn)”。
注2如果操作對(duì)象是Object,Class.getSuperClass()會(huì)返回null。
本文程序源碼可至侯捷網(wǎng)站下載:
http://www.jjhou.com/javatwo-2004-reflection-and-generics-in-jdk15-sample.ZIP發(fā)表于 2004年10月27日 11:30 AM
posted @
2005-11-17 09:45 安德?tīng)査?閱讀(789) |
評(píng)論 (0) |
編輯 收藏
傳值?還是傳引用?
(Wang hailong)
關(guān)于編程的參數(shù)傳遞問(wèn)題,總是存在著這樣的爭(zhēng)論。傳值?還是傳引用?(還是傳指針?還是傳地址?)這些提法,經(jīng)常出現(xiàn)在C++, java, C#的編程技術(shù)文檔里面。這個(gè)問(wèn)題也經(jīng)常引起開(kāi)發(fā)人員的爭(zhēng)論,徒耗人力物力。實(shí)際上,這根本不成為問(wèn)題,只是由于人為加入的概念,混淆了人們的視聽(tīng)。
從程序運(yùn)行的角度來(lái)看,參數(shù)傳遞,只有傳值,從不傳遞其它的東西。只不過(guò),值的內(nèi)容有可能是數(shù)據(jù),也有可能是一個(gè)內(nèi)存地址。
開(kāi)發(fā)人員應(yīng)該了解程序的編譯結(jié)果是怎樣在計(jì)算機(jī)中運(yùn)行的。程序運(yùn)行的時(shí)候,使用的空間可以分為兩個(gè)部分,棧和堆。棧是指運(yùn)行棧,局部變量,參數(shù),都分配在棧上。程序運(yùn)行的時(shí)候,新生成的對(duì)象,都分配在堆里,堆里分配的對(duì)象,棧里的數(shù)據(jù)參數(shù),或局部變量。
下面舉一個(gè)C++的例子。
public class Object{
int i;
public Object(int i){
this.i = i;
}
public int getValue(){
return i;
}
public void setValue(int i){
this.i = i;
}
};
class A {
Void func1(int a, Object b){
Object * c = new Object( a );
b = c;
}
public void main(){
Object * param = new Object( 1 );
func1( 2, param );
// what is value of parram now ?
// it is still 1.
}
};
我們來(lái)看一下,當(dāng)調(diào)用到func1函數(shù)時(shí),運(yùn)行到Object * c = new Object( a ); 棧和堆的狀態(tài)。不同編譯器生成的代碼運(yùn)行的結(jié)果可能會(huì)稍有不同。但參數(shù)和局部變量的大致排放順序都是相同的。
這時(shí)候,我們來(lái)看,param變量被壓入運(yùn)行棧的時(shí)候,只是進(jìn)行了簡(jiǎn)單的復(fù)制。把param里面的內(nèi)容拷貝到b里面。這時(shí)候,b就指向了Object(1)。這里的參數(shù)傳遞,是把param的值傳遞給b。
下面我們來(lái)看,程序執(zhí)行到b = c;時(shí)候的堆棧狀態(tài)。
我們可以看到,b現(xiàn)在指向了Object(2)。但是對(duì)param的值毫無(wú)影響。param的值還是Object(1)。
所以,我們說(shuō),對(duì)參數(shù)的賦值不會(huì)影響到外層函數(shù)的數(shù)據(jù),但是,調(diào)用參數(shù)的操作方法,卻等于直接操作外層函數(shù)的數(shù)據(jù)。比如,如果我們?cè)?/SPAN>func1()函數(shù)中,不調(diào)用b=c;而調(diào)用b.setValue(3),那么Object(1)的數(shù)據(jù)就會(huì)變?yōu)?/SPAN>3,param的數(shù)據(jù)也會(huì)改變?yōu)?/SPAN>3。
在java和C#中的情況,也都是一樣。
C++還有一種變量定義方法,表面上看起來(lái),不符合上面的說(shuō)明,這里進(jìn)行說(shuō)明。
Object * a = new Object(1);
Object & * b = a;
這里的b就等于是a的另外一個(gè)別名,b就是a。對(duì)b賦值就等于對(duì)a賦值。甚至作為參數(shù)傳遞時(shí),也是如此。對(duì)這種類(lèi)型的參數(shù)的賦值,就等于對(duì)外層函數(shù)數(shù)據(jù)的賦值。
public class B{
void func1(Object & * b){
b = new Object(4);
}
public void main(){
Object * a = new Object(1);
func1(a);
// a is changed to Object(4) now.
}
}
當(dāng)運(yùn)行完func1(a);時(shí),a的值變化為Object(4)。這是因?yàn)榫幾g器實(shí)際把參數(shù)Object & * b編譯為Object ** b_addr,b_addr的值是b的地址,也就是a的地址。
當(dāng)調(diào)用func1()的時(shí)候,實(shí)際上是把b_addr作為參數(shù)壓到棧里,b_addr的值是a的地址。
當(dāng)執(zhí)行b = new Object(4); 時(shí),實(shí)際執(zhí)行了 b_addr->b = new Object(4); 也就是執(zhí)行了 b_addr->a = new Object(4); a的值當(dāng)然變化了。
還有一點(diǎn)需要說(shuō)明,當(dāng)使用COM,CORBA等中間件規(guī)范進(jìn)行開(kāi)發(fā)時(shí),我們需要定義IDL語(yǔ)言。參數(shù)的類(lèi)型分為,[in],[out],[in, out],其中的RPC遠(yuǎn)程調(diào)用的參數(shù)打包規(guī)范,就更復(fù)雜了,但原理卻是一樣的。
posted @
2005-11-17 09:43 安德?tīng)査?閱讀(390) |
評(píng)論 (0) |
編輯 收藏
/*
* Created on 2004-8-4
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package myclass.test;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
/**
* @author
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class Image {
public String sRand="";
public Color getRandColor(int fc,int bc){//給定范圍獲得隨機(jī)顏色
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
public BufferedImage creatImage(){
// 在內(nèi)存中創(chuàng)建圖象
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 獲取圖形上下文
Graphics g = image.getGraphics();
//生成隨機(jī)類(lèi)
Random random = new Random();
// 設(shè)定背景色
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
//設(shè)定字體
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
//畫(huà)邊框
//g.setColor(new Color());
//g.drawRect(0,0,width-1,height-1);
// 隨機(jī)產(chǎn)生155條干擾線,使圖象中的認(rèn)證碼不易被其它程序探測(cè)到
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
// 取隨機(jī)產(chǎn)生的認(rèn)證碼(4位數(shù)字)
//String rand = request.getParameter("rand");
//rand = rand.substring(0,rand.indexOf("."));
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
// 將認(rèn)證碼顯示到圖象中
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//調(diào)用函數(shù)出來(lái)的顏色相同,可能是因?yàn)榉N子太接近,所以只能直接生成
g.drawString(rand,13*i+6,16);
}
// 圖象生效
g.dispose();
return image;
}
}
======================================================================
image.jsp(對(duì)bean的引用)
<%@ page contentType="image/jpeg" import="javax.imageio.*" %>
<jsp:useBean id="image" scope="session" class="myclass.test.Image"/>
<%
//設(shè)置頁(yè)面不緩存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
// 將認(rèn)證碼存入SESSION
session.setAttribute("rand",image.sRand);
// 輸出圖象到頁(yè)面
ImageIO.write(image.creatImage(), "JPEG", response.getOutputStream());
%>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
大家經(jīng)常在網(wǎng)上登陸的時(shí)候經(jīng)常會(huì)看到讓你輸入驗(yàn)證碼,有的是文字的,有的呢是圖片,比如chinaren.com校友錄中留言的時(shí)候,我們就會(huì)看到數(shù)字圖片驗(yàn)證碼;網(wǎng)上關(guān)于數(shù)字文字驗(yàn)證碼實(shí)現(xiàn)方法的相關(guān)資料很多,而我們這里介紹的是數(shù)字和字母隨機(jī)組成的并且生成圖片的驗(yàn)證碼的實(shí)現(xiàn)方法。看起來(lái)很復(fù)雜、其實(shí)很簡(jiǎn)單的,大家跟著我往下看:
首先,我們先介紹一下設(shè)計(jì)思路,數(shù)字和字母的隨機(jī)組合生成驗(yàn)證碼,然后將驗(yàn)證碼生成圖片,這里“數(shù)字和字母的組合”應(yīng)該是隨機(jī)取出來(lái)的;如果是專(zhuān)門(mén)的數(shù)字驗(yàn)證碼,我們可以這樣實(shí)現(xiàn):
ycodenum=4 '驗(yàn)證碼的位數(shù),或者說(shuō)成個(gè)數(shù)
for i=1 to ycodenum
Randomize '初始化隨機(jī)數(shù)發(fā)生器
ycode=ycode&Int((9*Rnd)) 'rnd是隨機(jī)數(shù),從0到1之間的任意實(shí)數(shù),這里獲得0到9之間的整數(shù)
next
response.write ycode '就可以輸出數(shù)字驗(yàn)證碼(4位)
然而,我們要讓數(shù)字和字母同樣隨機(jī)生成,這里我們可以用到數(shù)組來(lái)實(shí)現(xiàn)這種效果,如下:
ychar="0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z" '將數(shù)字和大寫(xiě)字母組成一個(gè)字符串
yc=split(char,",") '將字符串生成數(shù)組
ycodenum=4
for i=1 to ycodenum
Randomize
ycode=ycode&yc(Int((35*Rnd))) '數(shù)組一般從0開(kāi)始讀取,所以這里為35*Rnd
next
response.write ycode
現(xiàn)在看看輸出結(jié)果是不是數(shù)字和字母隨機(jī)組合的呢?
下面看看怎樣生成圖片,這個(gè)也許有些朋友知道:asp不能生成圖片,必須使用asp組件。不錯(cuò),我們這里使用的是ASP圖象組件shotgraph。有一點(diǎn)大家注意,服務(wù)器不是自己的不能用哦,因?yàn)槟阊b不了這組件。
組件的下載地址:
Response.BinaryWrite (img)
針對(duì)以上代碼也就是說(shuō)shotgraph普通的畫(huà)圖的原理請(qǐng)參考:http://www.pconline.com.cn/pcedu/empolder/wz/asp/10204/45207.html
posted @
2005-11-17 09:40 安德?tīng)査?閱讀(1759) |
評(píng)論 (3) |
編輯 收藏