2008年4月17日
#
public class QuenDemo {
public static void main(String[] args){
//public class Queue extends Collection
Queue<Integer> queue=new LinkedList<Integer>();
Random rand = new Random(47);
for(int i=0;i<10;i++){
queue.offer(rand.nextInt(i+10));
}
System.out.println("1111"+queue.toString());
printQ(queue);
//public class LinkedList extends AbstractSequentialList implements List,Queue,Cloneable,Serializable
Queue<Character> qc=new LinkedList<Character>();
for(char c:"guoxzh".toCharArray()){
qc.offer(c);
System.out.println("qc=="+qc.toString());
}
printQ(qc);
}
public static void printQ(Queue queue){
while(queue.peek()!=null){
//peek和element都是在不移除的情况下返回对头,但是peek在队列为空的情况下返回null,element方法会抛出NoSuchElementException异常
System.out.println(queue.remove());
//remove和poll方法都是在移除并返回对头,但是poll在为空时返回null,而remove会抛出NoSucheElementException异常
System.out.println("2222"+queue.toString());
}
}
}
package src;
import java.io.UnsupportedEncodingException;
public class ChangeCharset {
/** 7位ASCII字符,也叫作ISO646-US、Unicode字符集的基本拉丁块 */
public static final String US_ASCII = "US-ASCII";
/** ISO 拉丁字母表 No.1,也叫作 ISO-LATIN-1 */
public static final String ISO_8859_1 = "ISO-8859-1";
/** 8 位 UCS 转换格式 */
public static final String UTF_8 = "UTF-8";
/** 16 位 UCS 转换格式,Big Endian(最低地址存放高位字节)字节顺序 */
public static final String UTF_16BE = "UTF-16BE";
/** 16 位 UCS 转换格式,Little-endian(最高地址存放低位字节)字节顺序 */
public static final String UTF_16LE = "UTF-16LE";
/** 16 位 UCS 转换格式,字节顺序由可选的字节顺序标记来标识 */
public static final String UTF_16 = "UTF-16";
/** 中文超大字符集 */
public static final String GBK = "GBK";
/**
* 将字符编码转换成US-ASCII码
*/
public String toASCII(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, US_ASCII);
}
/**
* 将字符编码转换成ISO-8859-1码
*/
public String toISO_8859_1(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, ISO_8859_1);
}
/**
* 将字符编码转换成UTF-8码
*/
public String toUTF_8(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, UTF_8);
}
/**
* 将字符编码转换成UTF-16BE码
*/
public String toUTF_16BE(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, UTF_16BE);
}
/**
* 将字符编码转换成UTF-16LE码
*/
public String toUTF_16LE(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, UTF_16LE);
}
/**
* 将字符编码转换成UTF-16码
*/
public String toUTF_16(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, UTF_16);
}
/**
* 将字符编码转换成GBK码
*/
public String toGBK(String str) throws UnsupportedEncodingException{
return this.changeCharset(str, GBK);
}
/**
* 字符串编码转换的实现方法
* @param str 待转换编码的字符串
* @param newCharset 目标编码
* @return
* @throws UnsupportedEncodingException
*/
public String changeCharset(String str, String newCharset)
throws UnsupportedEncodingException {
if (str != null) {
//用默认字符编码解码字符串。
byte[] bs = str.getBytes();
//用新的字符编码生成字符串
return new String(bs, newCharset);
}
return null;
}
/**
* 字符串编码转换的实现方法
* @param str 待转换编码的字符串
* @param oldCharset 原编码
* @param newCharset 目标编码
* @return
* @throws UnsupportedEncodingException
*/
public String changeCharset(String str, String oldCharset, String newCharset)
throws UnsupportedEncodingException {
if (str != null) {
//用旧的字符编码解码字符串。解码可能会出现异常。
byte[] bs = str.getBytes(oldCharset);
//用新的字符编码生成字符串
return new String(bs, newCharset);
}
return null;
}
public static void main(String[] args) throws UnsupportedEncodingException {
ChangeCharset test = new ChangeCharset();
String str = "This is a 中文的 String!";
System.out.println("str: " + str);
String gbk = test.toGBK(str);
System.out.println("转换成GBK码: " + gbk);
System.out.println();
String ascii = test.toASCII(str);
System.out.println("转换成US-ASCII码: " + ascii);
gbk = test.changeCharset(ascii,ChangeCharset.US_ASCII, ChangeCharset.GBK);
System.out.println("再把ASCII码的字符串转换成GBK码: " + gbk);
System.out.println();
String iso88591 = test.toISO_8859_1(str);
System.out.println("转换成ISO-8859-1码: " + iso88591);
gbk = test.changeCharset(iso88591,ChangeCharset.ISO_8859_1, ChangeCharset.GBK);
System.out.println("再把ISO-8859-1码的字符串转换成GBK码: " + gbk);
System.out.println();
String utf8 = test.toUTF_8(str);
System.out.println("转换成UTF-8码: " + utf8);
gbk = test.changeCharset(utf8,ChangeCharset.UTF_8, ChangeCharset.GBK);
System.out.println("再把UTF-8码的字符串转换成GBK码: " + gbk);
System.out.println();
String utf16be = test.toUTF_16BE(str);
System.out.println("转换成UTF-16BE码:" + utf16be);
gbk = test.changeCharset(utf16be,ChangeCharset.UTF_16BE, ChangeCharset.GBK);
System.out.println("再把UTF-16BE码的字符串转换成GBK码: " + gbk);
}
}
JAVA里面关于byte数组和String之间的转换问题
把byte转化成string,必须经过编码。
例如下面一个例子:
import java.io.UnsupportedEncodingException;
public class test{
public static void main(String g[]) {
String s = "12345abcd";
byte b[] = s.getBytes();
String t = b.toString();
System.out.println(t);
}
}
输出字符串的结果和字符串s不一样了.
经过以下方式转码就可以正确转换了:
public class test{
public static void main(String g[]) {
String s = "12345abcd";
byte b[] = s.getBytes();
try {
String t = new String(b);
System.out.print(t);
} catch (Exception e) {
e.printStackTrace();
}
}
}
引用:
String str = "String";
byte[] byte1 = str.getBytes();
String str1 = new String(byte1);
byte[] byte2 = str1.getBytes();
String str2 = new String(byte2);
System.out.println("str<<<" + str);
System.out.println("byte1<<<" + byte1);
System.out.println("str1<<<" + str1);
System.out.println("byte2<<<" + byte2);
System.out.println("str2<<<" + str2);
-------------------------------------
输出结果
str<<<String
byte1<<<[B@192d342
str1<<<String
byte2<<<[B@6b97fd
str2<<<String
想请教为什么两个byte输出的不一样呢?
String str = "String";
byte[] byte1 = str.getBytes();
String str1 = new String(byte1);
byte[] byte2 = str1.getBytes();
----------
注意byte1是str得到的byte数组,而byte2是另一个字符串str1得到的数组
他们本身也是两个对象
直接打印实际上调用的是toString()方法,而toString()的默认实现是打印对象类型+hashCode()
[B表示byte数组 @表示之后的是地址 后面跟着的是hashCode,其实就是其虚拟机地址
从大的方面来讲,JVM的内存模型分为两大块:
永久区内存( Permanent space)和堆内存(heap space)。
栈内存(stack space)一般都不归在JVM内存模型中,因为栈内存属于线程级别。
每个线程都有个独立的栈内存空间。
Permanent space里存放加载的Class类级对象如class本身,method,field等等。
heap space主要存放对象实例和数组。
heap space由Old Generation和NewGeneration组成,OldGeneration存放生命周期长久的实例对象,而新的对象实例一般放在NewGeneration。
New Generation还可以再分为Eden区(圣经中的伊甸园)、和Survivor区,新的对象实例总是首先放在Eden区,Survivor区作为Eden区和Old区的缓冲,可以向Old区转移活动的对象实例。
一般,我们常见的OOM(out of memory)内存溢出异常,就是堆内存空间不足以存放新对象实例时导致。
永久区内存溢出相对少见,一般是由于需要加载海量的Class数据,超过了非堆内存的容量导致。通常出现在Web应用刚刚启动时,因此Web应用推荐使用预加载机制,方便在部署时就发现并解决该问题。
栈内存也会溢出,但是更加少见。
堆内存优化:
调整JVM启动参数-Xms -Xmx -XX:newSize -XX:MaxNewSize,如调整初始堆内存和最大对内存 -Xms256M -Xmx512M。 或者调整初始New Generation的初始内存和最大内存-XX:newSize=128M -XX:MaxNewSize=128M。
永久区内存优化:
调整PermSize参数 如 -XX:PermSize=256M-XX:MaxPermSize=512M。
栈内存优化:
调整每个线程的栈内存容量 如 -Xss2048K
最终,一个运行中的JVM所占的内存= 堆内存 + 永久区内存 + 所有线程所占的栈内存总和 。
财务公司的背景:
财务公司有两种模式:
1.美国模式财务公司是以搞活商品流通、促进商品销售为特色的非银行金融机构,它依附于制造商,是一些大型耐用品而设立的受控子公司,这类财务公司主要是为零售商提供融资服务的,主要分布在美国、加拿大和德国。
2.英国模式财务公司基本上依附于商业银行,其组建的目的在于规避政府对商业银行的监管,因为政府规定,商业银行不得从事证券投资业务,而财务公司不属于银行,所以不受此限制,这种模式主要分布在英国、日本和中国香港。
中国财务公司概况
财务公司又称金融公司,是为了企业技术改造,新产品开发及产品销售提供金融服务的,以中长期金融业务为主的非银行机构,各国的名称不同,业务内容也不一样。
财务公司是中国企业体制改革和融资体制改革的产物,国家为了增强国有大中型企业的活力,盘活企业内部资金,增强企业集团的融资能力,支持企业集团的发展,促进产业结构和产品结果的调整,以及探索具有中国特色的产品资本与金融资本相结合的道路,于1987年成立了中国第一家企业集团财务公司,即东风汽车工业集团财务公司。
财务公司---公司主体
财务公司是根据《公司法》和《企业集团财务公司管理办法》设立的,为企业集团成员单位技术改造、新产品开发及产品销售提供金融服务,以中长期金融业务为主的非银行机构。
财务公司一般分企业附属财务公司和非企业附属财务公司,企业附属财务公司由企业设立,为本企业服务,但是服务范围不仅局限于本企业;非企业附属财务公司包括银行附属财务公司、引起合资财务公司和独立财务公司。
1.银行附属财务公司是由银行控股,因规避监管、实现金融创新和弥补银行的不足而设立的,同时也为企业和个人提供金融服务。
2.银行合资财务公司是银行和企业出于金融创新规避监管或促进产融合作的考虑而设立,为企业和个人提供金融服务,
3.独立财务公司一般是没有母公司的财务公司,规模较小,比较灵活,在某一方面提供融资服务。
财务公司的业务范围
1.经中国人民银行批准,中国财务公司可从事下列部分或全部业务:
2.吸收成员单位3个月以上定期存款。
3.发行财务公司债券。
4.同业拆借。
5.对成员单位办理贷款及融资租赁。
6.办理集团成员单位产品的消费信贷、买方信贷及融资租赁。
7.办理成员单位商业汇票的承兑及贴现。
8.办理成员单位的委托贷款及委托投资。
9.有价证券、金融机构股权及成员单位股权投资。
10.承销成员单位的企业债券。
11.对成员单位办理财务顾问、信用鉴证及其他咨询代理业务。
12.对成员单位提供担保。
13.境外外汇借款。
14.经中国人民银行批准的其他业务。
财务公司的主要作用
1.业务范围比较广,但是以企业集团为限。
主要业务有存款、贷款、结算、担保和代理等一般银行业务,还可以经人民银行批准,开展证券、信托投资等业务。
2.资金来源于集团公司,用于集团公司,对集团公司的依附性强,
财务公司的资金主要来源于两个方面:a、集团公司和集团成员公司投入的资本金;b、集团公司成员企业在财务公司的存款。
3.接受企业集团和人民银行的双重监管。
财务公司是企业内部的金融机构,期股东大都是集团公司成员企业,因而其景荣活动必然受到集团公司的监督,同时,财务公司所从事的是金融业务,其金融活动必须接受人民银行监管
4.坚持服务与效益相结合、服务优先的经营原则。虽然财务公司作为独立的法人,但是是企业集团内部的机构,且集团公司成员企业大都是财务公司的股东,所以财务公司在经营中一般都应较好地处理服务与效益的关系,在坚持为集团公司成员企业提供良好金融服务的前提下,努力实现利润的最大化,
财务公司的作用
1.在资金管理方面和使用上,促使企业从粗放型向集约型转变,
没有财务公司之前,集团公司成员企业不能直接发生信贷关系,有些单位资金闲置,有些单位资金紧张,财务公司成立以后,成员单位成为财务公司的股东,在一定程度上集中了各成员单位的资本来进行一体化经营,财务公司应用金融手段将集团公司内企业的闲散资金集中起来,统筹安排使用,这样不仅可以加快集团成员企业间资金结算的速度,而且总整体上降低了集团财务费用,提高就然公司资金的使用效率,加速集团公司资产一体化经营的进程,
2.财务公司以资金为纽带,以服务为手段,增强了集团公司的凝聚力。
股本金将成员单位联接在一起,另一方面财务公司吸纳的资金又成为集团公司企业信贷资金的一个来源,从而集团公司成员企业进一步紧密的联结在一起,形成了一种相互支持,相互促进,共同发展的局面。
3.及时解决了企业集团急需的资金,保证企业生产经营的正常进行。
4.增强了企业集团的融资功能,促进了集团公司的发展壮大,
5.有利于打破现有银行体制资金规模按行政区域分割的局面,促进大集团公司跨地域、跨行业发展,
6.促进了金融业的竞争,有利于金融机构提高服务质量和效益,有利于金融体制改革的深化。
1、“我请客”:觉得我们常用pay这个词,如Let me pay it for you。这里列举三种说法:I am buying;This is on me;This is all my bill。
2、“收买某人”:有个比较正式的词叫bribe,名词时为“贿赂”的意思,动词时就有“收买”之意。既然提到了“买”,那么我们能不能用上buy呢?当然,那就是buy sb off。
3、“向前看!”:我们会说Look forward!而美语里有个更贴切的说法是Eyes front!“眼睛朝前”,是不是很生动?
4、“头等大事”:你会怎么翻译呢?The most important thing吗?看这个吧“It's on the top of my list”。
5、“看在主的份上,你就……”:两种说法,其一是For the love of God,另外For God's sake(sake的意思是缘故、关系)二者之中,后者更常用
6、“我不是傻子!”:I am not a fool?对,语法完全正确。但再看这个I am no fool。比上面的只少两个字母,但是不是感觉不一样?同样的道理,我们常说I have no idea,而不常说I dont have any idea。
7、short hairs:是说“短头发”吗?呵呵,它就是我们说的“小辫子”!
8、one-time thing:帅哥跟一美女过了一夜,回来后室友问帅哥:Do you really love her?帅哥回答:Oh, it was just a one-time thing!那么one-time thing是什么呢?我就不罗嗦喽!
9、She knew red was her color。“她知道红色是她的颜色”?恰当的翻译是:她知道自己和红色很相配。Then, what's your color?
10、“停电”:No electricity?恩,够直白!其实提到“电”,老外更多是用power,停电就可以是Ther is a power failure或Power goes out
有一次编网页的时候,把base 标签给删掉了,超链接怎么也行不通,老是路径不对,原来是base 标签在做怪:
<base>标记定义了文档的基础URL地址,在文档中所有的相对地址形式的URL都是相对于这里定义的URL而言的。一篇文档中的<base>标记不能多于一个,必须放于头部,并且应该在任何包含URL地址的语句之前。
(1)href 属性
href属性指定了文档的基础URL地址,该属性在<base>标记中是必须存在的。
例如:如果希望将文档的基础URL定义为“www.abc.com”,则可以使用如下语句:
<base href = "www.abc.com"> 当定义了基础URL地址之后,文档中所有引用的URL地址都从该基础URL地址开始,例如,对于上面的语句,如果文档中一个超级链接指向gsl/welcome.htm,则它实际上指向的是如下URL地址:www.abc.com/gsl/welcome.htm
(2)target
target属性同框架一起使用,它定义了当文档中的链接被点击后,在哪一个框架中展开页面。如果文档中超级链接没有明确指定展开页面的目标框架集,则就使用这里定义的地址代替。常用的target的属性值有:
_blank,表明在新窗口中打开链接指向的页面。
_self,在当前文档的框架中打开页面。
_parent,在当前文档的父窗口中打开页面。
_top,在链接所在的完整窗口中展开页面。
1、增加一个虚似硬盘
如果是在vmware里面安装的windows系统,添加个硬盘很容易,
(1)先打开要添加硬盘的虚拟系统(不要启动该系统),选择虚拟机菜单---设置-----选添加,
(2)然后在弹出添加硬件向导窗口中------选择硬盘-------一路next就好了,后面的操作和新建一个虚拟机的时候配置硬盘是一样的。
(3)添加了新的硬盘后,启动windows进到系统中,在控制面板中找“管理工具”->“计算机管理”,然后选“磁盘管理”,添加新磁盘就好了。
其实很简单的..如果想继续给VMware增加硬盘的话,重复上述动作。
2、改变原虚拟硬盘的大小
界面中并没有提供增加硬盘容量的方法。
只能在命令行形式下执行。
安装所在盘的c:\Program Files\VMware\VMware Workstation下有一个vmware-vdiskmanager.exe,就是它。
命令参数如下:
------------------------------------------------------------------
Usage: vmware-vdiskmanager.exe OPTIONS <disk-name> | <mount-point>
Offline disk manipulation utility
Options:
-c : create disk; need to specify other create options
-d : defragment the specified virtual disk
-k : shrink the specified virtual disk
-n <source-disk> : rename the specified virtual disk; need to
specify destination disk-name
-p : prepare the mounted virtual disk specified by
the drive-letter for shrinking
-q : do not log messages
-r <source-disk> : convert the specified disk; need to specify
destination disk-type
-x <new-capacity> : expand the disk to the specified capacity
Additional options for create and convert:
-a <adapter> : (for use with -c only) adapter type (ide, buslogic o
r lsilogic)
-s <size> : capacity of the virtual disk
-t <disk-type> : disk type id
Disk types:
0 : single growable virtual disk
1 : growable virtual disk split in 2Gb files
2 : preallocated virtual disk
3 : preallocated virtual disk split in 2Gb files
The capacity can be specified in sectors, Kb, Mb or Gb.
The acceptable ranges:
ide adapter : [100.0Mb, 950.0Gb]
scsi adapter: [100.0Mb, 950.0Gb]
ex 1: vmware-vdiskmanager.exe -c -s 850Mb -a ide -t 0 myIdeDisk.vmdk
ex 2: vmware-vdiskmanager.exe -d myDisk.vmdk
ex 3: vmware-vdiskmanager.exe -r sourceDisk.vmdk -t 0 destinationDisk.vm
dk
ex 4: vmware-vdiskmanager.exe -x 36Gb myDisk.vmdk
ex 5: vmware-vdiskmanager.exe -n sourceName.vmdk destinationName.vmdk
ex 6: vmware-vdiskmanager.exe -k myDisk.vmdk
ex 7: vmware-vdiskmanager.exe -p <mount-point>
(A virtual disk first needs to be mounted at <mount-point>)
-----------------------------------------------------------------
其中的-x参数就是用来扩容的……
如这个:vmware-vdiskmanager.exe -x 36Gb myDisk.vmdk
解析: vmware-vdiskmanager.exe -x 大小 虚拟硬盘文件
-------------------------------------------------------
我的执行过程:
D:\Program files\VMware\VMware Workstation>vmware-vdiskmanager.exe -x 12GB "F:\Windows Server 2003 Enterprise Edition\Windows Server 2003 Enterprise Edition.vmdk"
==================================================================
总结一下:
1。文件名应该用双引号括起来。
2。vmdk文件名要连同路径。
3。GB,MB,别忘了B。
什么是金融债券?金融债券有哪些种类?
金融债券是由银行和非银行金融机构发行的
债券。在英、美等欧美国家,金融机构发行的债券归类于
公司债券。在我国及日本等国家,金融机构发行的债券称为金融债券。 金融债券能够较有效地解决银行等金融机构的资金来源不足和期限不匹配的矛盾。
一般来说,银行等金融机构的资金有三个来源,即吸收存款、向其他机构借款和发行债券。
存款资金的特点之一,是在经济发生动荡的时候,易发生储户争相提款的现象,从而造成资金来源不稳定;
向其他商业银行或中央银行借款所得的资金主要是短期资金,而金融机构往往需要进行一些期限较长的投融资,这样就出现了资金来源和资金运用在期限上的矛盾,发行金融债券比较有效地解决了这个矛盾。债券在到期之前一般不能提前兑换,只能在市场上转让,从而保证了所筹集资金的稳定性。同时,金融机构发行债券时可以灵活规定期限,比如为了一些长期项目投资,可以发行期限较长的债券。因此,发行金融债券可以使金融机构筹措到稳定且期限灵活的资金,从而有利于优化资产结构,扩大长期投资业务。由于银行等金融机构在一国经济中占有较特殊的地位,政府对它们的运营又有严格的监管,因此,金融债券的资信通常高于其他非金融机构债券,违约风险相对较小,具有较高的安全性。所以,金融债券的利率通常低于。般的企业债券,但高于风险更小的国债和银行储蓄存款利率。
按不同标准,金融债券可以划分为很多种类。最常见的分类有以下两种:
(1) 根据利息的支付方式 金融债券可分为附息金融债券和贴现全融债券。如果金融债券上附有多期息票,发行人定期支付利息,则称为附息金融债券;如果金融债券是以低于面值的价格贴现发行,到期按面值还本付息,利息为发行价与面佰的差额,则称为贴现债券。比如票面金额为1000元,期限为1年的贴现金融债券,发行价格为900元,1年到期时支付给投资者1000元,那么利息收入就是100元,而实际年利率就是11.11%(即<1 000-900>/900* 100%〕。按照国外通常的做法,贴现金融债券的利息收入要征税,并且不能在证券交易所上市交易。
(2) 根据发行条件 金融债券可分为普通金融债券和累进利息金融债券。普通金融债券按面值发行,到期一次还本付息,期限一般是1年、2年和3年。普通金融债券类似于银行的定期存款,只是利率高些。累进利息金融债券的利率不固定,在不同的时间段有不同的利率,并且一年比一年高,也就是说,债券的利率随着债券期限的增加累进,比如面值1000无、期限为5年的金融债券,第回年利率为9%,第二年利率为10%,第三年为11%,第四年为12%,第五年为13%。投资者可在第一年至第五年之间随时去银行兑付,并获得规定的利息。
此外,金融债券也可以像
企业债券一样,根据期限的长短划分为短期债券、中期债券和长期债券;根据是否记名划分为记名债券和不记名债券;根据担保情况划分为信用债券和担保债券;根据可否提前赎回划分为可提前赎回债券和不可提前赎回债券;根据债券票面利率是否变动划分为
固定利率债券、
浮动利率债券和
累进利率债券;根据发行人是否给予投资者选择权划分为附有选择权的债券和不附有选择权的侦券等。
学习资源:
http://www.cnblogs.com/jimmyzhang/archive/2007/10/24/936151.html (花上1-2个小时仔细学习,然后反复实践,能够很快上手)
正则表达式工具:
我首推RegexBuddy了。下面这个地址里有RegexBuddy3.2.1完整版的下载地址(如果你仔细浏览这个网站,会发现此人是一个正则表达式狂热分子):
http://iregex.org/blog/regexbuddy321.html
1、<script language="javascript">
window.onbeforeunload = function()
{
if(((event.clientX > document.body.clientWidth - 43) && (event.clientY < 23)) || event.altKey) {
window.event.returnValue = '关闭。';
}
}
</script>
2、<script language="javascript">
window.onbeforeunload = function()
{
var n = window.event.screenX - window.screenLeft;
var b = n > document.documentElement.scrollWidth-20;
if(b && window.event.clientY < 0 || window.event.altKey)
{
alert("是关闭而非刷新");
window.open(this.location);
//return false;
//window.event.returnValue = ""; }
}
</script>
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ajaxchen_615/archive/2009/07/06/4325917.aspx
如果你的页面对IE7兼容没有问题,又不想大量修改现有代码,同时又能在IE8中正常使用,微软声称,开发商仅需要在目前兼容IE7的网站上添加一行代码即可解决问题,此代码如下:
CODE:
<meta http-equiv="x-ua-compatible" content="ie=7" />
从今天开始学习.net开发,java开发工作暂放一放,不过在学习.net的过程中,会结合java对比,在学习新知识的同时也巩固和复习一下java的知识,在学习中提升,在学习中成长,加油!
1.对象和属性
对象是一种复核数据类型,它们将多个数据值几种在一个单元中,而且允许使用名字来存取这些值,即对象是一个无序的属性集合,这个属性都有自己的名字和值,存储在对象中的以命名的值可以是数字和字符串这样的原始值,也可以是对象。
2.对象的创建
对象是由运算符new来创建的,在这个运算符之后必须有用于初始化对象的构造函数名。
创建一个空对象(即没有属性的对象)
var o = new Object();
js还支持内部构造函数,它们以另一种简洁的方式初始化新创建的对象
var now = new Date();
var new_year = new Date(2009,09,19);
3.属性的设置和查询
4.属性的枚举
for/in循环列出的属性并没有特定顺序,而且它只能枚举出所有用户定义的属性,但是却不能枚举出那些预定义的属性或方法,并且它可以
枚举出被设为undefined的属性,但是它不能列出被delete删除的属性。
5.未定义的属性
如果要读取一个不存在属性的值,那么得到的结果是一个特殊的js值,undefined
可以使用delete来删除一个对象的属性,注意:删除一个属性并不仅仅是把该属性设置为undefined,而是真正从对象中移除了该属性。
6.构造函数
它由new运算符调用,传递给它的是一个新创建的空对象引用,将该引用作为关键字this的值,而且它还要对新创建的对象进行适当的初始化。
注意:构造函数如何使用它的参数来初始化this关键字所引用的对象的属性,记住,构造函数只是初始化了特定的对象,但并不返回这个对象。
构造函数通常没有返回值,他们只是初始化由this值传递进来的对象,并且什么也不返回..但是,构造函数可以返回一个对象值,如果这样做,被返回的对象就成了new表达式的值了,在这种情况下,
this值所引用的对象就被丢弃了。
7.方法
方法有一个非常重要的属性,即在方法主体内部,关键字this的值变成了调用该方法的对象。
方法和函数的区别,其实他们没有什么技术上的差别,真正的区别存在于设计和目的上,方法是用来对this对象进行操作的,而函数通常是独立的,并不需要使用this对象。
8.原型对象和继承
js对象都“继承”原型对象的属性,每个对象都有原型对象,原型对象的所有属性是以它为原型的对象的属性,也就是说,每个对象都继承原型对象的所有属性,
一个对象的原型是有创建并初始化该对象的构造函数定义的,js中的所有函数都有prototype属性,它引用一个对象,虽然原型对象初始化时是空的,但是你在其中定义的任何属性都会被构造函数创建
的所有对象继承。
构造函数定义了对象的类,并初始化了类中状态变量的属性,因为原型对象和构造函数关联在一起,所以类的每个成员都从原型对象继承了相同的属性,这说明原型对象是存放方法和其他常量属性的理
想场所。
继承是在查询一个属性值时自动发生的,属性并非从原型对象赋值到新的对象的,他们只不过看起来像是那些对象的属性,有两点重要的含义,一是:使用原型对象可以大量减少每个对象对内存的需求
量,因为对象可以继承许多属性;而且即使属性在对象被创建之后才添加属性到它的原型对象中,对象也能够继承这些属性。
属性的继承只发生在读属性值时,而在写属性值时不会发生。
因为原型对象的属性被一个类的所有对象共享,所以通常只用他们来定义类中所有对象的相同的属性,这使得原型对象适合于方法定义,另外原型对象还适合于具有常量的属性的定义,
a.原型和内部类
不只是用户定义的类有原型对象,像内部类同样具有原型对象,也可以给他们赋值,
e.g String.prototype.endsWith = function(o){
return (e == this,charAt(this.length-1));
}
9.面向对象的js
在面向对象的程序设计中,共有的概念是强类型和支持以类为基础的继承机制,根据这个评判标准,就可以证明js不是面向对象语言。
js对象可以具有大量的属性,而且还可以动态的将这些属性添加到对象中,这是对面对象c++和java做不到的,
虽然js没有类的概念,但是它用构造函数和原型对象模拟了类。
js和以类为基础的面向对象语言中,同一个类可以具有多个对象,对象是它所属的那个类的实力,所以任何类都可以有多个实例,js中的命名延勇了java中的命名约定,即命名类时以大写字母开头,命名对象时以小写字母开头,类帮助我们区分代码中的类和对象。
实例属性
每个对象都有它自己单据的实力属性的副本,为了模拟面向对象的程序设计语言,js中的实例属性是那些在对象中用构造函数创建的或初始化的属性。
实例方法
实例方法和实例数据非常的相似,实例方法是由特定对象或实例调用的,实例方法使用了关键字this来引用他们要操作的对象或实例,但是和实例属性不同额一点是每个实例方法都是由类的所有实例共享的,在js中,给类定义一个实例方法,是通过把构造函数的原型对象中的一个属性设置为函数值类实现的,这样,由那个构造函数创建的所有对象都会共享一个以继承的对函数的引用,而且使用上面素数的方法调用语法就能够调用这个函数。
类属性
类属性是一个与类相关联的变量,而不是和类的每个实例相关联的变量,每个类属性只有一个副本,它是通过类存取的,可以简单的定义了构造函数自身的一个属性来定义类属性
类方法
类方法是一个与类关联在一起的方法,而不是和类的实例关联在一起的方法,要调用类方法,就必须使用类本身,而不是使用类的特定实例。由于类方法不能通过一个特定对象调用,所以使用关键字this对它来说没有意义,和类属性一样,类方法是全局性的,
超类和子类
面向对象语言中有类层次的概念,每个类都有一个超类,他们从超类中继承属性和方法,类还可以被扩展,或者说子类化,这样其他子类就能继承它的行为,js中继承是以原型为基础的,而不是以类基础的继承机制,但是我们仍旧能够总结出累世的类层次图,在js中,类Object是最通用的类,其他所有类都是专用化了的版本,或者说的是Object的子类,另一种解释方法是Object是所有内部类的超类,所有类都继承了Object的基本方法。
举例说明:
类Complex的对象就继承了Complex.prototype对象的属性,而后者又继承了Object.prototype的属性,由此可以推出,对象Complex继承了两个对象的属性,在Complex对象中查询某个属性时,首先查询的是这个对象本身,如果在这个对喜爱那个中没有发现要查询的属性,就查询Complex.prototype对象,最后,如果在那个对象中还没有最后按到要查询的属性,就查询Object.prototype对象,注意类层次关系中的属性隐藏。参考P153
10.作为关联数组的对象
运算符“.”类存取一个对象属性,而数组更常用的存取书香运算赋是[],下面的两行代码是等价的:
obj.property ====== obj["property"],他们的语法区别是,前者的属性名是标识符,后者的属性名却是一个字符串,
在c、c++、java和其他类似的强类型语言中,一个对象的属性数是固定,而且必须预定义这些属性的名字,由于js是一种弱类型语言,它并没有采用这一规则,所以在用js编写的程序,可以为对象创建任意数目的属性,但是当你采用“.”运算符来存取一个对象的属性时,属性名时是用标识符表示的,而js程序性中,标识符必须被逐字的输入,他们不是一种数据类型,因此程序不能对他们进行操作。
constructor属性
每个对象都有constructor属性,它引用的是用来初始化该对象的构造函数。但是并不是所有的对象都具有自己唯一的constructor属性,相反,如果这个属性是从原型对象继承来的。
js会为我们定义的每一个构造函数都创建一个原型对象,并且将那个对象赋给构造函数的prototype属性。但是之前没有说明原型对象初始时是非空的,在原型对象创建之初,它包括一个constructor属性, 用来引用构造函数,也就是说,如果有一个函数f,那么属性f.prototype.constructor就总是等于f的。
由于构造函数定义了一个对象的类,所以属性construtor在确定给定对象的类型时是一个功能强大的工具。
并不能保证constructor属性总是存在的,例如,一个类的创建者可以用一个全新的对象来替换构造函数的原型对象,而新对象可能不具有有效的constructor属性。
toString()方法
toLocaleString()方法
valueOf()方法
js需要将一个对象转化成字符创之外的原型类型时,就调用它,这个函数返回的是能代表关键字this所引用的对象的值的数据。
hasOwnProperty()
如果兑现局部定义了一个非继承的属性,属性名是由一个字符串实际参数指定的,那么该方法就返回true,否则,它将返回false。
propertyIsEnumerable()
如果对象定义了一个属性,属性名是由一个字符串实际参数指定的,而且该属性可以用for/in循环枚举出来,那么该方法返回true,否则返回false。
注意:该方法只考虑对象直接定义的属性,而不考虑继承的属性,因为返回false可能是因为那个属性是不可枚举的,也可能是因为它虽然是可以枚举的,但却是个继承的属性。
怎么判断一个属性是可枚举的?
isPrototypeOf()
如果调用对象是实际参数指定的对象的原型对象,该方法返回true,否则返回false,该方法的用途和对象的constructoe属性相似。
1.函数
注意:定义函数时可以使用个数可变的参数,而且函数既可以有return语句,也可以没有return语句;如果函数不包含return语句,它就只执行函数体中的每条语句,然后返回给调用者undefined。
使用运算符typeof来检测参数的数据类型,使用if(!param)return;来判断是否存在该参数,因为js是一种无类型语言,所以你不能给函数的参数制定一个数据类型,而且js也不会检测传递的数据是不是那个函数所需要的类型,如果参数很重要时,就使用前面介绍的运算符进行检测。
不可变参数js的处理:如果传递的参数比函数需要的个数多,那么多余的几个参数被忽略掉,如果传递的参数比函数需要的个数少,那么多余的几个参数就会被赋予undefined,在大多数情况下,这回使得函数产生错误。
2.嵌套函数
a,函数定义中可以嵌套其他函数的定义,但是只能在在顶层全局代码和顶层函数代码中,不能出现在循环或条件语句中,并且这些限制只应用于由function语句声明的函数,函数直接量可以出现在任何js表达式中。
3.Function()构造函数
可以使用Function()构造函数和new运算符动态地定义函数, var f = new Function("x","y","return x*y;");它等价于:function f(x,y){return x*y;}
Function构造函数可以接受任意多个字符串参数,它的最后一个参数是函数的主体,其中可以包含任何js语句,语句之间用分号分隔。由于传递给构造函数Function()的参数中没有一个用于说明它要创建的函数名,用Function()构造函数创建的未命名函数有时被称作为“匿名函数”。
Function()函数存在的意义:因为Function()构造函数允许我们动态地建立和编译一个函数,它不会将我们限制在function语句预编译的函数体中;另一个原因是它能将函数定义为js表达式的一部分,而不是将其定义为一个语句;缺点是:这样做每次调用一个函数时,Function()构造函数都要对它进行编译,
4.函数直接量
函数直接量是一个表达式,它可以定义匿名函数。
function f(x){return x*x;} //function语句
var f = new Function("x","return x*x;"); //Function()构造函数
var f = function(X){return x*x;}; //函数直接量
虽然函数直接量创建的是未命名函数,但是它的语法也规定它可以指定函数名,这在编写调用自身的递归函数时特别的有用,e.g
var f= function fact(x){if(x<=1)return 1; else return x*fact(x-1);}
总结:function()函数可以任意的使用,具有通用性,Function()函数和函数直接量具有很多的相似性,他们都是未命名函数(函数直接量可以有函数名,尤其是在子调用函数中),函数直接量有个重要的有点,函数直接量只被解析和编译一次,而作为字符串传递给Function()构造函数的js代码则在每次调用构造函数时只需要被解析和编译一次。
函数最重要的特性就是他们能够被定义和调用,但是在js中函数并不只是一种语法,还可以是数据,可以把函数赋给变量、存储在对象的属性中或存储在数组的元素中,传递给函数。其实函数名并没有什么实际意义,它只是保存函数的变量的名字,可以将这个函数赋给其他的变量,它仍然以相同的方式起作用,
e.g function square(x){x*x;}
var a = square(4);
var b = square;//这种情况下b引用的函数和square的作用相同
var c = b(5);
除了赋给全局变量之外,还可以将函数赋给对象的属性,这是称函数为方法;也可以赋给数组元素。
e.g
var a = new Object; var a = new Object();
a.square = new Function("x","return x*x";);
y = o.square(16);
e.g
var a = new Array(3);
a[0] = function(x){return x*x;};
a[1] = 20;
a[2] = a[0](a[1]);
除这些之外,如何将函数作为参数传递给其他函数,
e.g
function add(x,y){return x+y;}
function subtract(x,y){return x-y;}
function multiply(x,y){return x*y;}
function dibide(x,y){return x/y;}
function operate(operator,operand1,operand2){
return operator(operand1,operand2);
}
var i = operate(add,operate(add,2,3),operate(multiply,4,5));
var operators = new Object();
operators["add"] = function(x,y){return x+y;}
operators["multiply"] = function(x,y){return x*y;}
operators["divide"] = function(x,y){return x/y;}
operators["pow"] = Math.pow;
function operate2(op_name,operand1,operand2){
if(operators[op_name] == null)return "unknow operator";
else return operators[op_name](operand1,operand2);
}
var j = operate2('add',"hello",operate2("add","","world"));
var k = operate2('pow',10,2);
5.函数的作用域,调用对象
函数的作用域中除了全局变量、函数内部的局部变量和形式参数外,函数还定义了一个特殊属性,
arguments,这个属性应用了另外一个特殊的对象-----Arguments对象,因为arguments属性是调用对象的一个属性,所以它的状态和局部变量以及函数的形式参数是相同的。
所以arguments标识符被看做是保留字,不能将它作为变量名或形式参数名。
6.Arguments对象
arguments它具有特殊的意义,是调用对象的一个特殊属性,用来引用Arguments对象,Arguments对象就像数组,可以按照数字获取传递给函数的参数值,但是它并非真正的Array对象。
arguments具有length属性,
可以使用arguments来检测调用函数使用了正确数目的实际参数,
注意:arguments并非真正的数组,它是一个Arguments对象,Arguments对象有一个非同寻常的特征,当函数具有命名了的参数时,Arguments对象的数组元素是存放函数参数的局部变量的同义词。
e.g
function(x){
alert(x); //显示参数的初始值
arguments[0] = null;//改变数组预算也会改变x
alert(x); //现在显示为“null”
除了数组元素,Arguments对象还定义了callee属性,用来引用当前正在执行的函数,这对未命名的函数调用自身非常有用。
e.g
function(x){
if(x<-1)return 1;
return x*arguments.callee(x-1);
}
7.函数的属性和方法
由于函数是对象,所以它具有数据和方法。
函数的length属性
函数的属性length和arguments属性的length不同,arguments数组的length属性指定了传递给该函数的实际参数数目,并且arguments属性的length只在函数内部起作用,而函数自身的length属性它是只读的,返回的是函数需要的实际参数的数目,并且函数的属性length函数体的内部和外部都在是有效的。
函数的prototype属性
每个函数都有一个prototype属性,它引用的是预定义的原型对象,原型对象在使用new运算符把函数作为构造函数时起作用。
函数自定义属性
有时候定义全局变量比较乱,可以通过自定义函数属性来解决
函数的apply()和call()方法
他们的第一个参数都是要调用的函数的对象,在函数体内这一参数是关键字this的值,call()的剩余参数是传递给要调用的函数的值,apply()的剩余参数是由数组指定的参数。
写出漂亮代码的七种方法
首先我想说明我本文阐述的是纯粹从美学的角度来写出代码,而非技术、逻辑等。以下为写出漂亮代码的七种方法:
1, 尽快结束 if语句
例如下面这个JavaScript语句,看起来就很恐怖:
- 1 function findShape(flags, point, attribute, list) {
-
- 2 if(!findShapePoints(flags, point, attribute)) {
-
- 3 if(!doFindShapePoints(flags, point, attribute)) {
-
- 4 if(!findInShape(flags, point, attribute)) {
-
- 5 if(!findFromGuide(flags,point) {
-
- 6 if(list.count() > 0 && flags == 1) {
-
- 7 doSomething();
-
- 8 }
-
- 9 }
-
- 10 }
-
- 11 }
-
- 12 }
-
- 13 }
1 function findShape(flags, point, attribute, list) {
2 if(!findShapePoints(flags, point, attribute)) {
3 if(!doFindShapePoints(flags, point, attribute)) {
4 if(!findInShape(flags, point, attribute)) {
5 if(!findFromGuide(flags,point) {
6 if(list.count() > 0 && flags == 1) {
7 doSomething();
8 }
9 }
10 }
11 }
12 }
13 }
但如果这么写就好看得多:
- 1 function findShape(flags, point, attribute, list) {
-
- 2 if(findShapePoints(flags, point, attribute)) {
-
- 3 return;
-
- 4 }
-
- 5
-
- 6 if(doFindShapePoints(flags, point, attribute)) {
-
- 7 return;
-
- 8 }
-
- 9
-
- 10 if(findInShape(flags, point, attribute)) {
-
- 11 return;
-
- 12 }
-
- 13
-
- 14 if(findFromGuide(flags,point) {
-
- 15 return;
-
- 16 }
-
- 17
-
- 18 if (!(list.count() > 0 && flags == 1)) {
-
- 19 return;
-
- 20 }
-
- 21
-
- 22 doSomething();
-
- 23
-
- 24 }
1 function findShape(flags, point, attribute, list) {
2 if(findShapePoints(flags, point, attribute)) {
3 return;
4 }
5
6 if(doFindShapePoints(flags, point, attribute)) {
7 return;
8 }
9
10 if(findInShape(flags, point, attribute)) {
11 return;
12 }
13
14 if(findFromGuide(flags,point) {
15 return;
16 }
17
18 if (!(list.count() > 0 && flags == 1)) {
19 return;
20 }
21
22 doSomething();
23
24 }
你可能会很不喜欢第二种的表述方式,但反映出了迅速返回if值的思想,也可以理解为:避免不必要的else陈述。
2, 如果只是简单的布尔运算(逻辑运算),不要使用if语句
例如:
- 1 function isStringEmpty(str){
-
- 2 if(str === "") {
-
- 3 return true;
-
- 4 }
-
- 5 else {
-
- 6 return false;
-
- 7 }
-
- 8 }
1 function isStringEmpty(str){
2 if(str === "") {
3 return true;
4 }
5 else {
6 return false;
7 }
8 }
可以写为:
- 1 function isStringEmpty(str){
-
- 2 return (str === "");
-
- 3 }
1 function isStringEmpty(str){
2 return (str === "");
3 }
3, 使用空白,这是免费的
例如:
1
- function getSomeAngle() {
-
- 2
-
- 3 radAngle1 = Math.atan(slope(center, point1));
-
- 4 radAngle2 = Math.atan(slope(center, point2));
-
- 5 firstAngle = getStartAngle(radAngle1, point1, center);
-
- 6 secondAngle = getStartAngle(radAngle2, point2, center);
-
- 7 radAngle1 = degreesToRadians(firstAngle);
-
- 8 radAngle2 = degreesToRadians(secondAngle);
-
- 9 baseRadius = distance(point, center);
-
- 10 radius = baseRadius + (lines * y);
-
- 11 p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
-
- 12 p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
-
- 13 pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
-
- 14 pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
-
- 15
-
- 16 }
function getSomeAngle() {
2 // Some code here then
3 radAngle1 = Math.atan(slope(center, point1));
4 radAngle2 = Math.atan(slope(center, point2));
5 firstAngle = getStartAngle(radAngle1, point1, center);
6 secondAngle = getStartAngle(radAngle2, point2, center);
7 radAngle1 = degreesToRadians(firstAngle);
8 radAngle2 = degreesToRadians(secondAngle);
9 baseRadius = distance(point, center);
10 radius = baseRadius + (lines * y);
11 p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
12 p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
13 pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
14 pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
15 // Now some more code
16 }
很多开发者不愿意使用空白,就好像这要收费一样。我在此并非刻意地添加空白,粗鲁地打断代码的连贯性。在实际编写代码的过程中,会很容易地发现在什么地方加入空白,这不但美观而且让读者易懂,如下:
- 1 function getSomeAngle() {
-
- 2
-
- 3 radAngle1 = Math.atan(slope(center, point1));
-
- 4 radAngle2 = Math.atan(slope(center, point2));
-
- 5
-
- 6 firstAngle = getStartAngle(radAngle1, point1, center);
-
- 7 secondAngle = getStartAngle(radAngle2, point2, center);
-
- 8
-
- 9 radAngle1 = degreesToRadians(firstAngle);
-
- 10 radAngle2 = degreesToRadians(secondAngle);
-
- 11
-
- 12 baseRadius = distance(point, center);
-
- 13 radius = baseRadius + (lines * y);
-
- 14
-
- 15 p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
-
- 16 p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
-
- 17
-
- 18 pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
-
- 19 pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
-
- 20
-
- 21 }
-
-
-
- 4, 不要使用无谓的注释
-
- 无谓的注释让人费神,这实在很讨厌。不要标出很明显的注释。在以下的例子中,每个人都知道代码表达的是“students id”,因而没必要标出。
-
- 1 function existsStudent(id, list) {
-
- 2 for(i = 0; i < list.length; i++) {
-
- 3 student = list[i];
-
- 4
-
- 5
-
- 6 thisId = student.getId();
-
- 7
-
- 8 if(thisId === id) {
-
- 9 return true;
-
- 10 }
-
- 11 }
-
- 12 return false;
-
- 13 }
1 function getSomeAngle() {
2 // Some code here then
3 radAngle1 = Math.atan(slope(center, point1));
4 radAngle2 = Math.atan(slope(center, point2));
5
6 firstAngle = getStartAngle(radAngle1, point1, center);
7 secondAngle = getStartAngle(radAngle2, point2, center);
8
9 radAngle1 = degreesToRadians(firstAngle);
10 radAngle2 = degreesToRadians(secondAngle);
11
12 baseRadius = distance(point, center);
13 radius = baseRadius + (lines * y);
14
15 p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
16 p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
17
18 pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
19 pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
20 // Now some more code
21 }
4, 不要使用无谓的注释
无谓的注释让人费神,这实在很讨厌。不要标出很明显的注释。在以下的例子中,每个人都知道代码表达的是“students id”,因而没必要标出。
1 function existsStudent(id, list) {
2 for(i = 0; i < list.length; i++) {
3 student = list[i];
4
5 // Get the student's id
6 thisId = student.getId();
7
8 if(thisId === id) {
9 return true;
10 }
11 }
12 return false;
13 }
5, 不要在源文件中留下已经删除的代码,哪怕你标注了
如果你使用了版本控制,那么你就可以轻松地找回前一个版本的代码。如果别人大费周折地读了你的代码,却发现是要删除的代码,这实在太恨人了。
//function thisReallyHandyFunction() {
// someMagic();
// someMoreMagic();
// magicNumber = evenMoreMagic();
// return magicNumber;
//}
6,不要有太长的代码
看太长的代码实在太费劲,尤其是代码本身的功能又很小。如下:
- 1 public static EnumMap<Category, IntPair> getGroupCategoryDistribution(EnumMap<Category, Integer> sizes, int groups) {
-
- 2 EnumMap<Category, IntPair> categoryGroupCounts = new EnumMap<Category,IntPair>(Category.class);
-
- 3
-
- 4 for(Category cat : Category.values()) {
-
- 5 categoryGroupCounts.put(cat, getCategoryDistribution(sizes.get(cat), groups));
-
- 6 }
1 public static EnumMap<Category, IntPair> getGroupCategoryDistribution(EnumMap<Category, Integer> sizes, int groups) {
2 EnumMap<Category, IntPair> categoryGroupCounts = new EnumMap<Category,IntPair>(Category.class);
3
4 for(Category cat : Category.values()) {
5 categoryGroupCounts.put(cat, getCategoryDistribution(sizes.get(cat), groups));
6 }
#
我并不是说非要坚持70个字符以内,但是一个比较理想的长度是控制在120个字符内。如果你把代码发布在互联网上,用户读起来就很困难。
7,不要在一个功能(或者函数内)有太多代码行
我的一个老同事曾经说Visual C++很臭,因为它不允许你在一个函数内拥有超过10,000行代码。我记不清代码行数的上限,不知道他说的是否正确,但我很不赞成他的观点。如果一个函数超过了50行,看起来有多费劲你知道么,还有没完没了的if循环,而且你还的滚动鼠标前后对照这段代码。对我而言,超过35行的代码理解起来就很困难了。我的建议是超过这个数字就把一个函数代码分割成两个。
本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。
在开始之前,我先定义一个测试类Student,代码如下:
- package chb.test.reflect;
-
- public class Student {
- private int age;
- private String name;
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
-
- public static void hi(int age,String name){
- System.out.println("大家好,我叫"+name+",今年"+age+"岁");
- }
- }<PRE></PRE>
package chb.test.reflect;
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void hi(int age,String name){
System.out.println("大家好,我叫"+name+",今年"+age+"岁");
}
}
一、JAVA反射的常规使用步骤
反射调用一般分为3个步骤:
-
得到要调用类的class
-
得到要调用的类中的方法(Method)
-
方法调用(invoke)
代码示例:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});
- m.invoke(cls.newInstance(),20,"chb");<PRE></PRE>
Class cls = Class.forName("chb.test.reflect.Student");
Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});
m.invoke(cls.newInstance(),20,"chb");
二、方法调用中的参数类型
在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class。
如我要调用Student的setAge方法,下面的调用是正确的:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method setMethod = cls.getDeclaredMethod("setAge",int.class);
- setMethod.invoke(cls.newInstance(), 15);<PRE></PRE>
Class cls = Class.forName("chb.test.reflect.Student");
Method setMethod = cls.getDeclaredMethod("setAge",int.class);
setMethod.invoke(cls.newInstance(), 15);
而如果我们用Integer.class替代int.class就会出错,如:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
- setMethod.invoke(cls.newInstance(), 15);<PRE></PRE>
Class cls = Class.forName("chb.test.reflect.Student");
Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
setMethod.invoke(cls.newInstance(), 15);
jvm会报出如下异常:
- java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)
- at java.lang.Class.getDeclaredMethod(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:23)<PRE></PRE>
java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)
at java.lang.Class.getDeclaredMethod(Unknown Source)
at chb.test.reflect.TestClass.testReflect(TestClass.java:23)
三、static方法的反射调用
static方法调用时,不必得到对象示例,如下:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);
- staticMethod.invoke(cls,20,"chb");
-
Class cls = Class.forName("chb.test.reflect.Student");
Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);
staticMethod.invoke(cls,20,"chb");//这里不需要newInstance
//staticMethod.invoke(cls.newInstance(),20,"chb");
四、private的成员变量赋值
如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。代码示例:
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();
- Field field = cls.getDeclaredField("age");
- field.set(student, 10);
- System.out.println(field.get(student));<PRE></PRE>
Class cls = Class.forName("chb.test.reflect.Student");
Object student = cls.newInstance();//得到一个实例
Field field = cls.getDeclaredField("age");
field.set(student, 10);
System.out.println(field.get(student));
运行如上代码,系统会报出如下异常:
- java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"
- at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
- at java.lang.reflect.Field.doSecurityCheck(Unknown Source)
- at java.lang.reflect.Field.getFieldAccessor(Unknown Source)
- at java.lang.reflect.Field.set(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:20)<PRE></PRE>
java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
at java.lang.reflect.Field.doSecurityCheck(Unknown Source)
at java.lang.reflect.Field.getFieldAccessor(Unknown Source)
at java.lang.reflect.Field.set(Unknown Source)
at chb.test.reflect.TestClass.testReflect(TestClass.java:20)
解决方法:
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();
- Field field = cls.getDeclaredField("age");
- field.setAccessible(true);
- field.set(student, 10);
- System.out.println(field.get(student));<PRE></PRE>
Class cls = Class.forName("chb.test.reflect.Student");
Object student = cls.newInstance();
Field field = cls.getDeclaredField("age");
field.setAccessible(true);//设置允许访问
field.set(student, 10);
System.out.println(field.get(student));
其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例:
- Class cls = Class.forName("chb.test.reflect.Student");
- Object student = cls.newInstance();
-
- Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
- setMethod.invoke(student, 15);
-
- Method getMethod = cls.getDeclaredMethod("getAge");
- System.out.println(getMethod.invoke(student));
|
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。
1. 一个简单的例子
考虑下面这个简单的例子,让我们看看 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);
}
}
}
按如下语句执行:
java DumpMethods java.util.Stack
它的结果输出为:
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 类的各方法名以及它们的限制符和返回类型。
这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。还有就是getDeclaredMethod(para1,para2)来获取这个类中的具体某一个方法,其中para1是一个String类型,具体代表的是方法名,para2是个一个Class类型的数组,其中定义个方法的具体参数类型。
例如:
- Class cls = Class.forName("chb.test.reflect.Student");
- Method m = cls.getDeclaredMethod("方法名",new Class[]{int.class,String.class});
- m.invoke(cls.newInstance(),20,"chb");
总结:
//使用反射类调用某个类中的方法
Class c = Class.forName("com.inspur.reflect.MethodTest");
Method n = c.getDeclaredMethod("show", new Class[]{String.class,int.class});
n.invoke(c.newInstance(), "guoxzh",20);
a.使用Class.forName("类名")来获取类
b.其次使用getDeclaredMethods()方法获取该类所有的方法,也可以使用getDeclaredMethod("方法名",new Class[]{int.class,String.class})方法类获取具体的某一个方法
c.接着可以使用invoke(c.newInstance,param....)来调用具体的方法。
2.详细介绍开始使用 Reflection
用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。
下面就是获得一个 Class 对象的方法之一:
Class c = Class.forName("java.lang.String");
这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:
Class c = int.class;
或者
Class c = Integer.TYPE;
它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。
第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。
一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
它将以文本方式打印出 String 中定义的第一个方法的原型。
在下面的例子中,这三个步骤将为使用 reflection 处理特殊应用程序提供例证。
模拟 instanceof 操作符
得到类信息之后,通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如,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)); //判断Integer(37)该对象是否是A类的对象
System.out.println(b1);
boolean b2 = cls.isInstance(new A());
System.out.println(b2);
} catch (Throwable e) {
System.err.println(e);
}
}
}
在这个例子中创建了一个 A 类的 Class 对象,然后检查一些对象是否是 A 的实例。Integer(37) 不是,但 new A() 是。
3.找出类的方法
找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法:
package com.inspur.reflect;
import java.lang.reflect.Method;
public class Methodtest1 {
private int abc(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("com.inspur.reflect.Methodtest1");
Method methodlist[]= cls.getDeclaredMethods();
for(int i = 0;i<methodlist.length;i++){
Method m = methodlist[i];
System.out.println("name=="+m.getName());//得到方法的名称
System.out.println("decl class=="+m.getDeclaringClass());//得到定义的类名
Class prev[] = m.getParameterTypes(); //取m方法中的所有参数
//遍历所有的参数
for(int j = 0; j<prev.length;j++){
System.out.println("param["+j+"]=="+prev[j]);
}
Class exec[] = m.getExceptionTypes(); //得到所有的异常
//遍历所有的异常
for(int k=0;k<exec.length;k++){
System.out.println("execption["+k+"]=="+exec[k]);
}
Class ret = m.getReturnType(); //得到每个方法的返回值
System.out.println("return leixing=="+ret.toString());
}
}catch(Throwable e){
System.err.println(e.getMessage());
}
}
}
这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。
如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。同时你也可以使用Modifier.toString(m.getModifiers())来获取方法的限制属性。
取得了 Method 对象列表之后,要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型,都可以由描述类的对象按顺序给出。
输出的结果如下:
name==main
decl class==class com.inspur.reflect.Methodtest1
param[0]==class [Ljava.lang.String;
return leixing==void
name==abc
decl class==class com.inspur.reflect.Methodtest1
param[0]==class java.lang.Object
param[1]==int
execption[0]==class java.lang.NullPointerException
return leixing==int 4.获取构造器信息
获取类构造器的用法与上述获取方法的用法类似,如:
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);
}
}
}
这个例子中没能获得返回类型的相关信息,那是因为构造器没有返回类型。
这个程序运行的结果是:
name = constructor1
decl class = class constructor1
-----
name = constructor1
decl class = class constructor1
param #0 int
param #1 double
-----
5.获取类的字段(域)
找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个事情:
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);
}
}
}
这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是:
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 =
-----
和获取方法的情况一下,获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。
6.根据方法的名称来执行方法
文本到这里,所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。下面的示例演示了这一操作:
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);
}
}
}
假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。
上例中,getMethod 用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的 Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。
7.创建新的对象
对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:
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);
}
}
}
根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。
(这里如果使用无参构造器创建对象的话,这可以直接使用Class.forName("...").newInstance();来创建对象)
8.改变字段(域)的值
reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:
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);
}
}
}
这个例子中,字段 d 的值被变为了 12.34。
9.使用数组
本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:
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);
}
}
}
例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。
下面这段代码提供了一个更复杂的例子:
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]);
}
}
例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。注意,多维数组实际上就是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素,即长度为 15 的数组,并使用 Array.setInt 为它的第 10 个元素赋值。
注意创建数组时的类型是动态的,在编译时并不知道其类型。
摘要: 转自其他博客《收藏》
(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效):
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询,...
阅读全文
1,使用Spring 的 ActionSupport
2,使用Spring 的 DelegatingRequestProcessor 类。
3,全权委托。
无论用那种方法来整合第一步就是要为struts来装载spring的应用环境。 就是在 struts 中加入一个插件。
struts-config.xml中
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml"/>
</plug-in>
|
spring 的配置文件被作为参数配置进来。这样可以省略对web.xml 文件中的配置。确保你的applicationContext.xml 在WEB-INF目录下面
1、使用Spring的ActionSupport .
Spring 的ActionSupport 继承至org.apache.struts.action.Action
ActionSupport的子类可以或得 WebApplicationContext类型的全局变量。通过getWebApplicationContext()可以获得这个变量。
这是一个 servlet 的代码:
public class LoginAction extends org.springframework.web.struts.ActionSupport {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
LoginForm loginForm = (LoginForm) form;// TODO Auto-generated method stub
//获得 WebApplicationContext 对象
WebApplicationContext ctx = this.getWebApplicationContext();
LoginDao dao = (LoginDao) ctx.getBean("loginDao");
User u = new User();
u.setName(loginForm.getName());
u.setPwd(loginForm.getPwd());
if(dao.checkLogin(u)){
return mapping.findForward("success");
}else{
return mapping.findForward("error");
}
}
}
applicationContext.xml 中的配置
<beans>
<bean id="loginDao" class="com.cao.dao.LoginDao"/>
</beans>
|
这中配置方式同直接在web.xml文件配置差别不大。
注意:Action继承自 org.springframework.web.struts.ActionSupport 使得struts和spring耦合在一起。
但实现了表示层和业务逻辑层的解耦(LoginDao dao = (LoginDao) ctx.getBean("loginDao"))。
2、使用Spring 的 DelegatingRequestProcessor 类
DelegatingRequestProcessor 继承自 org.apache.struts.action.RequestProcessor 并覆盖了里面的方法。
sturts-config.xml 中
processorClass="org.springframework.web.struts.DelegatingRequestProcessor"/> 通过 来替代
org.apache.struts.action.RequestProcessor 的请求处理。
public class LoginAction extends Action {
//利用spring来注入这个对象。
private LoginDao dao ;
public void setDao(LoginDao dao) {
System.out.println("执行注入");
this.dao = dao;
}
public LoginDao getDao() {
return dao;
}
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
LoginForm loginForm = (LoginForm) form;// TODO Auto-generated method stub
//这样一改这行代码似乎没有必要了。
//WebApplicationContext ctx = this.getWebApplicationContext();
//LoginDao dao = (LoginDao) ctx.getBean("loginDao");
User u = new User();
u.setName(loginForm.getName());
u.setPwd(loginForm.getPwd());
//直接用dao来调用spring会将这个对象实例化。
if(dao.checkLogin(u)){
return mapping.findForward("success");
}else{
return mapping.findForward("error");
}
}
}
这里的。
LoginAction extends Action 说明 struts没有和spring 耦合。
看一下
applicationContext.xml 中的配置。
<beans>
<bean id="loginDao" class="com.cao.dao.LoginDao"/>
<bean name="/login" class="com.cao.struts.action.LoginAction">
<property name="dao">
<ref local="loginDao"/>
</property>
</bean>
</beans>
|
这里 name="/login" 与struts 中的path匹配
class="com.cao.struts.action.LoginAction" 与struts 中的type匹配
还要为 LoginAction 提供必要的setXXX方法。 获得ApplicationCotext和依赖注入的工作都在DelegatingRequestProcessor中完成。
3,全权委托:
Action 的创建和对象的依赖注入全部由IOC容器来完成。使用Spring的DelegatingAcionProxy来帮助实现代理的工作
org.springframework.web.struts.DelegatingActiongProxy继承于org.apache.struts.action.Action .
全权委托的配置方式同 方式 2 类似 (applcationContext.xml文件的配置和 Action类的实现方式相同)。
<struts-config>
<data-sources />
<form-beans >
<form-bean name="loginForm"
type="com.cao.struts.form.LoginForm" />
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings >
<!-- type指向的是spring 的代理类 -->
<action
attribute="loginForm"
input="login.jsp"
name="loginForm"
path="/login"
scope="request"
type="org.springframework.web.struts.DelegatingActionProxy" >
<forward name="success" path="/ok.jsp" />
<forward name="error" path="/error.jsp" />
</action>
</action-mappings>
<message-resources parameter="com.cao.struts.ApplicationResources" />
<plug-in className=
"org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml"/>
</plug-in>
</struts-config>
不同之处
1, <action>中 type指向的是spring 的代理类
2, 去掉struts-config.xml中 <controller >
|
三种整和方式中我们优先选用 全权委托的方式。
理由:
1,第一种使得过多的耦合了Spring和Action .
2,RequestProcessor类已经被代理 如果要再实现自己的实现方式(如:编码处理)怕有点麻烦。
总结一下:
整合工作中的步骤:
1,修改struts-config.xml
2, 配置applicationContext.xml
3, 为Action添加get/set方法 来获得依赖注入的功能。
最近比较忙,但是每天网上还是的坚持学点,不积小流,无以成江河。
今天学jQuery对象访问:
1.each(callback) 该方法以每一个匹配的元素作为上下文来执行一个函数,
在每次执行函数时,都会给函数传递一个表示作为执行环境的元素在匹配的元素集合中所处位置的数字值作为参数(从0开始的int)
返回‘false’将停止循环(相当于普通循环中使用的‘break’)
返回‘true’将跳至下一个循环,(相当于在普通的循环中使用‘continue’)
参数:callback(function)
e.g1
<img/><img/>
jQuery代码:
$('img').each(function(){
this.src="test"+i+".jpg";
});
e.g2
<button>Change colors</button>
<span></span>
<div></div>
<div></div>
<div></div>
<div></div>
<div id="stop">Stop here</div>
<div></div>
<div></div>
<div></div>
jQuery代码:
$('button').click(function(){
$('div').each(function(index,domEle){//domEle ==this
$(domEle).css('backgroundColor',"yellow");
if($(this).is("#stop")){
$("span").text("stopped at div index #"+index);
return false;}
});});
2.size() 和length
都可以勇于得到jQuery对象中元素的个数,
返回: Number
e.g1
<img src="test1.jpg"/><img src="test2.jpg"/>
jQuery代码:
$("img").size();
e.g2
同理:$("img").length;
3.get()取得所有匹配的DOM元素集合
返回:Array<Element>
e.g1
<img src="test1.jpg"/><img src="test2.jpg"/>
jQuery代码:
$("img").get().reverse();
result:
[<img src="test1.jpg"/><img src="test2.jpg"/>]
4.get(index)
取得其中一个匹配元素,index表示取得第几个匹配的元素
返回值:Element
HTML代码:
<img src="test1.jpg"/><img src="test2.jpg"/>
jQuery代码:
$("img").get(0);
result:
[<img src="test1.jpg"/>]
5.index(subject)
搜索与参数表示的对象匹配的元素,并返回相应元素的索引值,
如果哦找到了匹配的元素,从0开始返回;如果没有找到匹配的元素,返回-1
返回值;
Number
参数:
subject(Element)
e.g1返回id值为foobar的元素的索引值
<div id="foobar"><div></div><div id="foo"></div></div>
jQuery代码:
$("div").index($("#foobar")[0]) //0
$("div").index($('#foo')[0]) // 2
$("div").index($('#foo')) // -1
备注:
今天在浏览别人博客的时候看到的,收藏。
有时候,我们页面当中并不需要把要用到的JS全部加载出来,
这会使页面加载时速度变慢~~~如果能按需加载,那能提高不少性能...也能节约不少流量~~~给用户带来好的体验~~
好比说,当某个JS效果是触发事件才显示的...这个效果被封闭在一个JS中,,我想大家经常这样做吧~~这时候,我们能按需加载那就不必在页面载入时去加载JS文件~~~这在jquery插件中很多。
用法:
1 , 当在需要的时候再加载所需的javascript和css文件。
$.include('file/test.js')或$.include('file/test.css')
2, 当然若你一次想加载多个文件你也可以这样写:
$.include(['file/test.js','file/test.css'])。
3, 因为这两个文件的路径相同,所以可以先指定路径再加载所有文件:
$.ImportBasePath = 'file/';
$.include(['test.css','test.js']);
4, 你还可以加载完文件后执行回调函数
$.include("file/test.css",function(){
alert("加载css后执行");
});
插件下载地址:http://www.94this.com.cn/myCode/jqueryIncludefile/jqueryIncludefile.rar
注:jquery 自带了有一个异步请求的方法,$.getScript ,可以异步加到JS并执行
jQuery.getScript(url,[callback])
通过 HTTP GET 请求载入并执行一个 JavaScript 文件。
jQuery 1.2 版本之前,getScript 只能调用同域 JS 文件。 1.2中,您可以跨域调用 JavaScript 文件。注意:Safari 2 或更早的版本不能在全局作用域中同步执行脚本。如果通过 getScript 加入脚本,请加入延时函数。
Loads, and executes, a local JavaScript file using an HTTP GET request.
Before jQuery 1.2, getScript was only able to load scripts from the same domain as the original page. As of 1.2, you can now load JavaScript files from any domain. Warning: Safari 2 and older is unable to evaluate scripts in a global context synchronously. If you load functions via getScript, make sure to call them after a delay.
返回值
XMLHttpRequest
参数
url (String) : 待载入 JS 文件地址。
callback (Function) : (可选) 成功载入后回调函数。
示例
载入 jQuery 官方颜色动画插件 成功后绑定颜色变化动画。
HTML 代码:
<button id="go">» Run</button>
<div class="block"></div>
jQuery 代码:
jQuery.getScript("http://dev.jquery.com/view/trunk/plugins/color/jquery.color.js",
function(){
$("#go").click(function(){
$(".block").animate( { backgroundColor: 'pink' }, 1000)
.animate( { backgroundColor: 'blue' }, 1000);
});
});
加载并执行 test.js。
jQuery 代码:
$.getScript("test.js");
加载并执行 test.js ,成功后显示信息。
jQuery 代码:
$.getScript("test.js", function(){
alert("Script loaded and executed.");
});
jsp文件上传大多采用采用开源项目来简化处理,这里列出常用的两个jar包的实现,并进行比较,说明他们的优缺点和应该注意的问题。
Commons FileUpload,可以在http://jakarta.apache.org/commons/fileupload/下载,这个包需要Commons IO的支持,可以在http://jakarta.apache.org/commons/io/下载
com.oreilly.servlet,可以在http://www.servlets.com/cos/下载
Commons FileUpload提供三种文件上传处理方式,DiskFileUpload、ServletFileUpload和PortletFileUpload三种方式,其中DiskFileUpload已经在javadoc下已经被标记为过期的方法,建议用ServletFileUpload代替,而PortletFileUpload需要配合portlet-api来使用,所以这里我们只介绍ServletFileUpload,并且这个也是最常用的。
com.oreilly.servlet也提供了三种文件上传的处理方式,MultipartWrapper、MultipartRequest和MultipartParser三种方式,其中MultipartWrapper和MultipartRequest的用法基本相同,并且没有MultipartRequest提供的操作多,所以这里介绍MultipartRequest,MultipartParser和前两者有些不同,可以用来处理某些特殊情况,例如表单中有两个同名的文件上传选择框。
我们暂时称三面三种文件上传方式分别为:ServletFileUpload方式(MultipartTestServlet)、MultipartRequest方式(MultipartTestServlet2)、MultipartParser方式(MultipartTestServlet3)
代码如下:
test.html
<%@ page language="java" import="java.util.*" contentType="text/html;charset=gbk" pageEncoding="gbk"%>
<html>
<body>
<form action="MultipartTestServlet" enctype="multipart/form-data" method="post">
<input type="text" name="username" /><br />
<input type="file" name="myfile" /><br/>
<input type="file" name="myfile" /><br/>
<input type="submit" />
</form>
<br/><br/><br/><br/>
<form action="MultipartTestServlet2" enctype="multipart/form-data" method="post">
<input type="text" name="username" /><br />
<input type="file" name="myfile" /><br/>
<input type="file" name="myfile" /><br/>
<input type="submit" />
</form>
<br/><br/><br/><br/>
<form action="MultipartTestServlet3" enctype="multipart/form-data" method="post">
<input type="text" name="username" /><br />
<input type="file" name="myfile" /><br/>
<input type="file" name="myfile" /><br/>
<input type="submit" />
</form>
</body>
</html>
MultipartTestServlet.java
package com.bug.servlet;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
public class MultipartTestServlet extends HttpServlet {
public MultipartTestServlet() {
super();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("gbk");
RequestContext requestContext = new ServletRequestContext(request);
if(FileUpload.isMultipartContent(requestContext)){
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File("c:/tmp/"));
ServletFileUpload upload = new ServletFileUpload(factory);
//upload.setHeaderEncoding("gbk");
upload.setSizeMax(2000000);
List items = new ArrayList();
try {
items = upload.parseRequest(request);
} catch (FileUploadException e1) {
System.out.println("文件上传发生错误" + e1.getMessage());
}
Iterator it = items.iterator();
while(it.hasNext()){
FileItem fileItem = (FileItem) it.next();
if(fileItem.isFormField()){
System.out.println(fileItem.getFieldName() + " " + fileItem.getName() + " " + new String(fileItem.getString().getBytes("iso8859-1"), "gbk"));
}else{
System.out.println(fileItem.getFieldName() + " " +
fileItem.getName() + " " +
fileItem.isInMemory() + " " +
fileItem.getContentType() + " " +
fileItem.getSize());
if(fileItem.getName()!=null && fileItem.getSize()!=0){
File fullFile = new File(fileItem.getName());
File newFile = new File("c:/temp/" + fullFile.getName());
try {
fileItem.write(newFile);
} catch (Exception e) {
e.printStackTrace();
}
}else{
System.out.println("文件没有选择 或 文件内容为空");
}
}
}
}
}
}
MultipartTestServlet2.java
package com.bug.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
public class MultipartTestServlet2 extends HttpServlet {
public MultipartTestServlet2() {
super();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//request.setCharacterEncoding("gbk"); 不起作用
System.out.println("start ");
MultipartRequest multi = new MultipartRequest(request, "c:/tmp/", 2*1024*1024, "gbk", new DefaultFileRenamePolicy());
System.out.println("start ");
Enumeration filesName = multi.getFileNames();
Enumeration paramsName = multi.getParameterNames();
while(paramsName.hasMoreElements()){
String paramName = (String) paramsName.nextElement();
System.out.println(multi.getParameter(paramName));
}
while(filesName.hasMoreElements()){
String fileName = (String) filesName.nextElement();
System.out.println(multi.getFilesystemName(fileName) + " " +
multi.getOriginalFileName(fileName) + " " +
multi.getContentType(fileName) + " ");
if(multi.getFilesystemName(fileName)!=null && !multi.getFilesystemName(fileName).equals(""))
System.out.println(multi.getFile(fileName).toURI());
}
}
}
MultipartTestServlet3.java
package com.bug.servlet;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.oreilly.servlet.multipart.FilePart;
import com.oreilly.servlet.multipart.MultipartParser;
import com.oreilly.servlet.multipart.ParamPart;
import com.oreilly.servlet.multipart.Part;
public class MultipartTestServlet3 extends HttpServlet {
public MultipartTestServlet3() {
super();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MultipartParser mp = new MultipartParser(request, 2*1024*1024, false, false, "gbk");
Part part;
while ((part = mp.readNextPart()) != null) {
String name = part.getName();
if (part.isParam()) {
ParamPart paramPart = (ParamPart) part;
String value = paramPart.getStringValue();
System.out.println("param: name=" + name + "; value=" + value);
}
else if (part.isFile()) {
// it's a file part
FilePart filePart = (FilePart) part;
String fileName = filePart.getFileName();
if (fileName != null) {
long size = filePart.writeTo(new File("c:/tmp/"));
System.out.println("file: name=" + name + "; fileName=" + fileName +
", filePath=" + filePart.getFilePath() +
", contentType=" + filePart.getContentType() +
", size=" + size);
}
else {
System.out.println("file: name=" + name + "; EMPTY");
}
System.out.flush();
}
}
}
}
web.xml中加入
<servlet>
<servlet-name>MultipartTestServlet</servlet-name>
<servlet-class>com.bug.servlet.MultipartTestServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>MultipartTestServlet2</servlet-name>
<servlet-class>com.bug.servlet.MultipartTestServlet2</servlet-class>
</servlet>
<servlet>
<servlet-name>MultipartTestServlet3</servlet-name>
<servlet-class>com.bug.servlet.MultipartTestServlet3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MultipartTestServlet</servlet-name>
<url-pattern>/MultipartTestServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MultipartTestServlet2</servlet-name>
<url-pattern>/MultipartTestServlet2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MultipartTestServlet3</servlet-name>
<url-pattern>/MultipartTestServlet3</url-pattern>
</servlet-mapping>
问题1、中文问题:
三种凡是都可以通过自己的方法来设置encoding为gbk开处理和解决中文问题,包括初始化的时候传入gbk作为参数,或是是初始化后通过setEncoding的方式来实现。
另外ServletFileUpload方式也可以通过request.setCharacterEncoding("gbk");的方式来实现,而其它两种方式不支持这种方式。
问题2、文件大小限制
ServletFileUpload方式可以设置文件大小限制,也可以不用设置,例子中的upload.setSizeMax(2000000)就可以注释掉。如果设置upload.setSizeMax(-1),表明不限制上传的大小。文档中没有指明默认的限制的多少,我在不设置的情况下上传了一个9M的东西,可以上传,估计默认是不限制大小的。
而MultipartRequest方式和MultipartParser方式是必须设置文件的上传文件的大小限制的,如果不设置,默认是1M的大小限制。
问题3、文件上传发生错误
如果文件上传过程中发生任何错误,或者是文件的大小超出了范围,系统都将抛出错误。
ServletFileUpload方式在upload.parseRequest(request)时抛出错误
MultipartRequest方式在new MultipartRequest(。。。)时抛出错误
MultipartParser方式在new MultipartParser(。。。)时抛出错误
问题4、上传同名文件时,他们保存到临时目录是的冲突问题
ServletFileUpload方式,不会有冲突,系统会把上传得文件按照一定的规则重新命名,保证不会冲突
MultipartParser方式,会产生冲突,系统会把文件按照上传时的文件名,保存到临时目录下,如果两个用会同时上传文件名相同的文件,那么就可能会产生冲突,一方把另一方的临时文件给替换了。
MultipartRequest方式,在初始化时如果提供了一个名称转换策略,就不会有冲突,如果不提桶,就会有冲突。在上面的例子中我们提供了一个new DefaultFileRenamePolicy()保证了文件名不会有冲突发生。
问题5:表单中有两个同名的文件上传选择框,就像我们例子中的myfile一样,每个表单中有两个name=“myfile”的上传框
ServletFileUpload方式,可以处理,可以分别得到他们各自的文件,
MultipartRequest方式,不可以处理,会发生冲突,会有一个上传框的文件覆盖了另外一个。
MultipartParser方式,可以处理,可以分别得到他们各自的文件,
备注:
代码比较乱,主要是为了说明他们间的区别。他们的详细地使用说明还是要参考他的javadoc和domo。
参考:
1、http://www.servlets.com/cos/#classes
2、http://jakarta.apache.org/commons/fileupload/apidocs/index.html
3、http://jakarta.apache.org/commons/fileupload/using.html
4、http://www.onjava.com/pub/a/onjava/2003/06/25/commons.html?page=3
//先锁表,然后再判断是否已经存在数据,以防止出现重复行
String lockSql = "update YHZHMX set YHZHMX_DJBH=YHZHMX_DJBH where 1=2";
//上面的语法貌似不好用.故采用下面的语法
lockSql = "SELECT * FROM YHZHMX FOR UPDATE";
一、jquery核心函数的学习
1、
jQuery(exp,[context]),这个函数接受一个包含css选择器的字符串,然后用这个字符串去匹配一组元素,通俗的讲,exp参数是要匹配的表达式,context是匹配的范围,可以是dom元素,文档或者jquery对象。
jQuery的核心功能都是通过这个函数实现的,
例子:
a.找到所有p元素,并且这些元素都是div元素的子元素
HTML代码:<p>guoxzh</p><div><p>guoxiaozheng</p></div><p>guoxzh</p.
jQuery代码:$("div>p")
b.在文档的第一个表单中,查找所有的当选按钮
HTML代码:是带有type值为radio的input元素
JQuery代码:$("input:radio",document.forms[0]);
c.在一个有AJAX返回的xml文档中,查找所有的div元素
$("div",xml.responseXML);
2.
jQuery(html)根据提供的原始的HTMl标记字符串,动态创建有jQuery对象包含的Dom元素,你可以传递一个手写的 HTML 字符串,或者由某些模板引擎或插件创建的字符串,也可以是通过 AJAX 加载过来的字符串。但是在你创建 input 元素的时会有限制,可以参考第二个示例。当然这个字符串可以包含斜杠 (比如一个图像地址),还有反斜杠。当你创建单个元素时,请使用闭合标签或 XHTML 格式。例如,创建一个 span ,可以用 $("<span/>") 或 $("<span></span>") ,但不推荐 $("<span>");
返回值:
JQuery
参数:
用于动态创建dom元素的HTML标签的字符串,
例子:
a.动态创建一个div元素,追加到body里
jQuery代码:
$("<div><input type="text" name="name" value=""></div>").appendTo("body");
b.创建一个<input>元素必须同时设定type属性,
jQuery代码:
IE中无效
$("<input>").attr("type","checkbox");
在IE中有效
$("<input type='checkbox'>");
3.jQuery(element)将一个或多个元素DOM元素转化为jQuery对象
这个函数也可以接收XML文档和Window对象(虽然它们不是DOM元素)作为有效的参数。
返回值:jQuery
例子:
a.设置页面的背景色
jQuery代码:
$("document.body").css("background","black");
b.隐藏一个表单中所有元素
jQuery代码:
$("myForm.elements").hide();
Java栈与堆
----对这两个概念的理解总是忘记,今天从网上搜到一篇比较好的文章收藏
1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
3. Java中的数据类型有两种。
一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
复制内容到剪贴板代码:
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 4. String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。
5. 关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤:
(1)先定义一个名为str的对String类的对象引用变量:String str;
(2)在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。
(3)将str指向对象o的地址。
值得注意的是,一般String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!
为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。
复制内容到剪贴板代码:
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。
结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。
我们再来更进一步,将以上代码改成:
复制内容到剪贴板代码:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。
事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良影响。
再修改原来代码:
复制内容到剪贴板代码:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
String str3 = str1;
System.out.println(str3); //bcd
String str4 = "bcd";
System.out.println(str1 == str4); //true
str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。
我们再接着看以下的代码。
复制内容到剪贴板代码:
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false 创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1==str2); //false
创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。
6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。
7. 结论与建议:
(1)我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。
(2)使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。
(3)当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。
(4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。
摘要: 压缩的
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import...
阅读全文
现在关键是如何读取XML配置文件?有好几种XML解析器:主要有DOM和SAX ,这些区别网上文章介绍很多。
在apache的XML项目组中,目前有Xerces Xalan Cocoon几个开发XML相关技术的project.Tomcat本身使用的是 Sun 的 JAXP,而其XSL Taglib project中使用Xerces解析器。
好了,上面都是比较烦人的理论问题,还是赶快切入XML的配置文件的读取吧。
在我们的程序中,通常要有一些根据主机环境确定的变量。比如数据库访问用户名和密码,不同的主机可能设置不一样。只要更改XML配置文件,就可以正常运行。
localhost
sqlname
username
password
上面这个myenv.xml配置文件一般是放在tomcat的WEB-INF/classes目录下.
我们编制一个Java程序直接读取,将dbhost dbuser dbpassword提取出来供其他程序访问数据库用.
目前使用SAX比较的多,与DOM主要区别是 SAX是一行一行读取XML文件进行分析,适合比较大文件,DOM是一次性读入内存,显然不能对付大文件.这里我们使用SAX解析,由于SAX解析器不断在发展,网上有不少文章是针对老版本的.如果你使用JDK1.4 ,可以参考 使用SAX处理XML文档 一文.这里的程序是根据其改进并且经过实践调试得来的.
对上面myenv.xml读取的Java程序:
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import java.util.Properties;
//使用DefaultHandler的好处 是 不必陈列出所有方法,
public class ConfigParser extends DefaultHandler {
////定义一个Properties 用来存放 dbhost dbuser dbpassword的值
private Properties props;
private String currentSet;
private String currentName;
private StringBuffer currentValue = new StringBuffer();
//构建器初始化props
public ConfigParser() {
this.props = new Properties();
}
public Properties getProps() {
return this.props;
}
//定义开始解析元素的方法. 这里是将 中的名称xxx提取出来.
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
currentValue.delete(0, currentValue.length());
this.currentName =qName;
}
//这里是将 之间的值加入到currentValue
public void characters(char[] ch, int start, int length) throws SAXException {
currentValue.append(ch, start, length);
}
//在遇到 结束后,将之前的名称和值一一对应保存在props中
public void endElement(String uri, String localName, String qName) throws SAXException {
props.put(qName.toLowerCase(), currentValue.toString().trim());
}
}
上面的这个解析程序比较简单吧? 其实解析XML就是这么简单.
现在我们已经将dbhost dbuser dbpassword的值localhost sqlname username password提取了出来.但是这只是在在解析器内部,我们的程序还不能访问.需要再编制一个程序.
import java.util.Properties;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.net.URL;
public class ParseXML{
//定义一个Properties 用来存放 dbhost dbuser dbpassword的值
private Properties props;
//这里的props
public Properties getProps() {
return this.props;
}
public void parse(String filename) throws Exception {
//将我们的解析器对象化
ConfigParser handler = new ConfigParser();
//获取SAX工厂对象
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(false);
//获取SAX解析
SAXParser parser = factory.newSAXParser();
//得到配置文件myenv.xml所在目录. tomcat中是在WEB-INF/classes
//下例中BeansConstants是用来存放xml文件中配置信息的类,可以自己代替或定义
URL confURL = BeansConstants.class.getClassLoader().getResource(filename);
try
{
//将解析器和解析对象myenv.xml联系起来,开始解析
parser.parse(confURL.toString(), handler);
//获取解析成功后的属性 以后 我们其他应用程序只要调用本程序的props就可以提取出属性名称和值了
props = handler.getProps();
}finally{
factory=null;
parser=null;
handler=null;
}
}
}
由于我们的XML文件是使用最简单的形式 ,因此解析器相对简单,但是这已经足够对付我们的配置文件了.
判断一个程序系统的先进性,我们先看看他的配置文件,如果还在使用老套的xxx=123 这样类似.ini的文件,
Web应用程序在浏览器中显示字符串时,由于显示长度的限制,常常需要将字符串截取后再进行显示。但目前很多流行的语言,如
C#、
Java内部采用的都是 Unicode 16(UCS2)编码,在这种编码中所有的字符都是两个字符,因此,如果要截取的字符串是中、英文、数字混合的,就会产生问题,如下面的字符串:
String s = "a加b等于c,如果a等1、b等于2,那么c等3";
上面的字符串既有汉字,又有英文字符和数字。如果要截取前6个字节的字符,应该是”a加b等",但如果用substring方法截取前6个字符就成了"a 加b等于c"。产生这个问题的原因是将substring方法将双字节的汉字当成一个字节的字符(UCS2字符)处理了。要解决这个问题的方法是首先得到该字符串的UCS2编码的字节数组,如下面的代码如下:
byte[] bytes = s.getBytes("Unicode");
由于上面生成的字节数组中前两个字节是标志位,bytes[0] = -2,bytes[1] = -1,因此,要从第三个字节开始扫描,对于一个英文或数字字符,UCS2编码的第二个字节是相应的ASCII,第一个字节是0,如a的UCS2编码是0 97,而汉字两个字节都不为0,因此,可以利于UCS2编码的这个规则来计算实际的字节数,该方法的实现代码如下:
public static String bSubstring(String s, int length) throws Exception
{
byte[] bytes = s.getBytes("Unicode");
int n = 0; // 表示当前的字节数
int i = 2; // 要截取的字节数,从第3个字节开始
for (; i < bytes.length && n < length; i++)
{
// 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
if (i % 2 == 1)
{
n++; // 在UCS2第二个字节时n加1
}
else
{
// 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
if (bytes[i] != 0)
{
n++;
}
}
}
// 如果i为奇数时,处理成偶数
if (i % 2 == 1)
{
// 该UCS2字符是汉字时,去掉这个截一半的汉字
if (bytes[i - 1] != 0)
i = i - 1;
// 该UCS2字符是字母或数字,则保留该字符
else
i = i + 1;
}
return new String(bytes, 0, i, "Unicode");
}
下面代码使用了bSubstring方法:
String s = "a加b等于c,如果a等1、b等于2,那么c等3";
System.out.println(bSubstring(s, 6));
上面的代码截取的字符串是
"a加b等
"。
先来看一张简单的文档树
很明显树的顶层节点是NodeA节点,接下来可以通过指定的合适节点移动到树中的任何点,结合以下的代码你可以更好的了解这棵树节点间的相互关系:
NodeA.firstChild = NodeA1
NodeA.lastChild = NodeA3
NodeA.childNodes.length = 3
NodeA.childNodes[0] = NodeA1
NodeA.childNodes[1] = NodeA2
NodeA.childNodes[2] = NodeA3
NodeA1.parentNode = NodeA
NodeA1.nextSibling = NodeA2
NodeA3.prevSibling = NodeA2
NodeA3.nextSibling = null
NodeA.lastChild.firstChild = NodeA3a
NodeA3b.parentNode.parentNode = NodeA
DOM定义对操作一个文档对象的节点结构提供了实用的方法,它提供了像执行对象插入,更新,删除,克隆等这些常用的方法。
insertBefore()--在参考子节点之前插入一个新的子节点.如果参考的子节点为null,则新的子节点将作为调用节点的最后一个子节点插入。
replaceChild()--在childNodes集合种使用指定的newChild来代替oldChild;如果代替成功,则返回oldChild;如果newChild是null,则只需删除oldChild即可。
removeChild()--从节点的ChildNodes集合中删除removeChild指定的节点,如果删除成功,则返回删除的子节点。
appendChild()--添加一个新节点到childNodes集合的末尾,如果成功,则返回新节点。
cloneNode()--创建一个新的、复制的节点,并且如果传入的参数是true时,还将复制子节点,如果节点是一个元素,那么还将复制相应属性,返回新的节点。
为了在一棵文档树中访问或者建立一个新的节点,可以用下面这些方法:
getElementById()
getElementsByTagName()
createElement()
createAttribute()
createTextNode()
注意:在一个页面中只有一个文档对象,除了getElementsByTagName()外,其它方法均只能通过document.methodName()调用。
再看一下下面这个例子:
<html>
<head>
<title></title>
</head>
<body>
<p>This is a sample paragraph.</p>
<SCRIPT LANGUAGE="JavaScript">
<!--
alert(document.documentElement.lastChild.firstChild.tagName);
//-->
</SCRIPT>
</body>
</html>
结果将会显示"P",下面是一些解释
document.documentElement - gives the page's HTML tag.
lastChild - gives the BODY tag.
firstChild - gives the first element in the BODY.
tagName - gives that element's tag name, "P" in this case.
另一个:
<html>
<head>
<title></title>
</head>
<body>
<p>This is a sample paragraph.</p>
<SCRIPT LANGUAGE="JavaScript">
<!--
alert(document.documentElement.lastChild.firstChild.tagName);
//-->
</SCRIPT>
</body>
</html>
这个例子和上面并没有什么大的区别,仅仅是多了一个空行,但是在NS中,会自动为空行加上一个节点所以返回值是"undefined",而在IE中将跳过空行仍然指向P标签。
更常用的方法:
<p id="myParagraph">This is a sample paragraph.</p>
...
alert(document.getElementById("myParagraph").tagName);
这种方法你不用关心节点在文档树的哪一个地方,而只要保证在页面中它的ID号是唯一的就可以了。
接下来一种访问元素节点的方法是document.getElementsByTagName(),它的返回值是一个数组,例如你可以通过下面的例子改变整个页面的连接:
var nodeList = document.getElementsByTagName("A");
for (var i = 0; i < nodeList.length; i++)
nodeList[i].style.color = "#ff0000";
attribute和attributes
attribute对象和元素相关,但是却没有被认为是文档树的一部分,因此属性不能作为子节点集合的一部分来使用。
有三种方法可以为元素建立新的属性
1.
var attr = document.createAttribute("myAttribute");
attr.value = "myValue";
var el = document.getElementById("myParagraph");
el.setAttributeNode(attr);
2.
var el = document.getElementById("myParagraph");
el.setAttribute("myAttribute", "myValue");
3.
var el = document.getElementById("myParagraph");
el.myAttribute = "myValue";
你可以在html标签种定义自己的属性:
<p id="myParagraph" myAttribute="myValue">This is a sample paragraph.</p>
...
alert(document.getElementById("myParagraph").getAttribute("myAttribute"));
返回值将是"myValue".但是请注意这里必须使用getAttribute,而不是AttributeName,因为有一些浏览器并不支持自定义属性。
attributes也可以被轻易的从一个元素中删除,你可以使用removeAttribute()或者将element.attributeName指向一个null值。
通过attributes我们就可以产生一些动态效果:
<p id="sample1" align="left">Text in a paragraph element.</p>
... code for the links ...
document.getElementById('sample1').setAttribute('align', 'left');
document.getElementById('sample1').setAttribute('align', 'right');
另一种:
<p id="sample2" style="text-align:left;">Text in a paragraph
element.</p>
... code for the links ...
document.getElementById('sample2').style.textAlign = 'left';
document.getElementById('sample2').style.textAlign = 'right';
跟上面的例子一样,展示了可用通过元素修改style中的属性,甚至是class中的.唯一要提到的是textAlign是从style中的text-align中演变而来的,有一条基本规律,就是style中的属性如果出现-则在dom中将会被去掉并且随后的一个字母将改为大写,还有一点就是如果即使元素中没有style属性,上述例子同样可以使用。
text nodes:
先看一下例子:
<p id="sample1">This is the initial text.</p>
... code for the links ...
document.getElementById('sample1').firstChild.nodeValue =
'Once upon a time...';
document.getElementById('sample1').firstChild.nodeValue =
'...in a galaxy far, far away';
首先text nodes并没有像elements那样具有id属性,所有它并不能直接通过document.getElementById()或者document.getElementsByTagName()访问
看一下下面的结构也许会更明白一些:
可以看出通过document.getElementById('sample1').firstChild.nodeValue就可以读取或者设置text nodes的值了。
另一个更加复杂一点的例子:
<p id="sample2">This is the <b>initial</b> text.</p>
它的文档结构
在这里通过document.getElementById('sample1').firstChild.nodeValue讲仅仅改变"This is the"
而initial text.将不会改变.在这里大家应该看到了它和innerHTML的不同了.当然你也可以这样用:
document.getElementById('sample3').firstChild.nodeValue =
'<b>Once</b> upon a time...';
document.getElementById('sample3').firstChild.nodeValue =
'...in a galaxy <i>far, far</i> away';
其中的html代码将不会被解释,浏览器将把他们当成普通的文本来显示。
创建和删除text nodes:
var myTextNode = document.createTextNode("my text");
通过上面的代码你可以创建一个新的text node,但是它并不是文档树的一部分,要让它显示在页面上就必须让它成为文档树中某一个节点的child,因为
text nodes不能有儿子,所以你不能将它加入到一个text nodes中,attribute也不属于文档树的一部分,这条路也不行,现在只剩下elements nodes
了,以下的例子展示了如何添加和删除一个text node:
<p id="sample1">Initial text within a paragraph element.</p>
... code to add a text node ...
var text = document.createTextNode(" new text " + (++counter1));
var el = document.getElementById("sample1");
el.appendChild(text);
... code to remove the last child node ...
var el = document.getElementById("sample1");
if (el.hasChildNodes())
el.removeChild(el.lastChild);
增加文本是很容易的,上面的代码建立了一个新的text node并且通过appendChild()方法将其加入到childNodes数组的末尾,并设置了一个counter1的全局变量,利于观察
hasChildNodes()的返回值是true or false;用来判断当前节点是否还有child,以阻止当其没有child的时候调用removeChild()产生的错误。
创建element nodes
有了上面的基础,应该更容易理解了,先看一下下面的代码
<div id="sample1">This text is in a DIV element.</div>
... code for the link ...
var paraEl, boldEl;
paraEl = document.createElement("p");
boldEl = document.createElement("b");
paraEl.appendChild(document.createTextNode("This is a new paragraph with "));
boldEl.appendChild(document.createTextNode("bold"));
paraEl.appendChild(boldEl);
paraEl.appendChild(document.createTextNode(" text added to the DIV"));
document.getElementById("sample1").appendChild(paraEl);
你还可以直接为新加的element nodes设置attribute,以下两种都可以:
boldEl.style.color = "#ffff00";
paraEl.appendChild(boldEl);
或者:
paraEl.appendChild(boldEl);
boldEl.style.color = "#ffff00";
|
、帧标志
1.<frameset></frameset>
2.<frame>
3.<noframes></noframes>
帧是由英文Frame翻译过来的,它可以用来向浏览器窗口中装载多个Html文件。即每个Html文件占据一个帧,而多个帧可以同时显示在同一个浏览器窗口中,它们组成了一个最大的帧,也即是一个包含多个Html文档的Html文件(我称它为
主文档)。帧通常的使用方法是在一个帧中放置目录(即可供选择的链接),然后将Html文件显示在另一个帧中。
1.<frameset></frameset>
<frameset></frameset>标志对放在帧的
主文档的<body></body>标志对的
外边,也可以嵌在其他帧文档中,并且可以
嵌套使用。此标志对用来定义主文档中有几个帧并且各个帧是如何排列的。它具有
rows和
cols属性,使用<frameset>标志时这两个属性
至少必须选择一个,否则浏览器只显示第一个定义的帧,剩下的一概不管,<frameset></frameset>标志对也就没有起到任何作用了。rows用来规定主文档中各个帧的
行定位,而cols用来规定主文档中各个帧的
列定位。这两个属性的取值可以是百分数、绝对像素值或
星号(“*”),其中星号代表那些未被说明的空间,如果同一个属性中出现多个星号则将
剩下的未被说明的空间平均分配。同时,所有的帧按照rows和cols的值
从左到右,然后
从上到下排列。示例如下:
<frameset rows="*,*,*"> |
总共有三个按列排列的帧,每个帧占整个浏览器窗口的1/3 |
<frameset cols="40%,*,*"> |
总共有三个按行排列的帧,第一个帧占整个浏览器窗口的40%,剩下的空间平均分配给另外两个帧 |
<frameset rows="40%,*" cols="50%,*,200"> |
总共有六个帧,先是在第一行中从左到右排列三个帧,然后在第二行中从左到右再排列三个帧,即两行三列,所占空间依据rows和cols属性的值,其中200的单位是像素 |
2.<frame>
<frame>标志放在<frameset></frameset>之间,用来定义某一个具体的帧。<frame>标志具有src和name属性,这两个属性都是必须赋值的。src是此帧的源Html文件名(包括网络路径,即相对路径或网址),浏览器将会在此帧中显示src指定的Html文件;name是此帧的名字,这个名字是用来供超文本链接标志<a
href=""
target="">中的target属性用来指定链接的Html文件将显示在哪一个帧中。例如定义了一个帧,名字是main,在帧中显示的Html文件名是jc.htm,则代码是<frame
src="jc.htm" name="main">,当您有一个链接,在点击了这个链接后,文件new.htm将要显示在名为main的帧中,则代码为<a
href="new.htm"
target="main">需要链接的文本</a>。这样一来,就可以在一个帧中建立网站的目录,加入一系列链接,当点击链接以后在另一个帧中显示被链接的Html文件。
此外,<frame>标志还有scrolling和noresize属性,scrolling用来指定是否显示滚动轴,取值可以是“yes”(显示)、“no”(不显示)或“auto”(若需要则会自动显示,不需要则自动不显示)。noresize属性直接加入标志中即可使用,不需赋值,它用来禁止用户调整一个帧的大小。
3.<noframes></noframes>
<noframes></noframes>标志对也是放在<frameset></frameset>标志对之间,用来在那些不支持帧的浏览器中显示文本或图像信息。在此标志对之间先紧跟<body></body>标志对,然后才可以使用我们在教程七以前讲过的任何标志。
下边是一个综合示例:
例8 帧标志的综合示例
主文档:
<html>
<head>
<title>帧标志的综合示例</title>
</head>
<frameset cols="25%,*">
<frame src="menu.htm" scrolling="no" name="Left">
<frame src="page1.htm" scrolling="auto" name="Main">
<noframes>
<body>
<p>对不起,您的浏览器不支持“帧”!</p>
</body>
</noframes>
</frameset>
</html>
menu.htm
<html>
<head>
<title>目录</title>
</head>
<body>
<p><font color="#FF0000">目录</font></p>
<p><a href="page1.htm" target="Main">链接到第一页</a></p>
<p><a href="page2.htm" target="Main">链接到第二页</a></p>
</body>
</html>
page1.htm
<html>
<head>
<title>第一页</title>
</head>
<body>
<p align="center"><font color="#8000FF">这是第一页!</font></p>
</body>
</html>
page2.htm
<html>
<head>
<title>第二页</title>
</head>
<body>
<p align="center"><font color="#FF0080">这是第二页!</font></p>
</body>
</html>
现在在Java开发中,使用的开发工具大部分都是Eclipse,并且和Eclipse关系紧密的要数MyEclipse了,但是MyEclipse是一个EXE可执行程序,对于没有安装Eclipse与MyEclilpse的电脑来说,首先得先解压Eclipse,然后再安装MyEclipse,这不光很麻烦,而且还很费时,对于已经安装好的电脑来说,如果哪天电脑出了问题或是Eclipse崩溃了,导致工具不能用,这时又不得不重新安装时,那可真够郁闷滴~~~,因此,大象本着我为人人,人人为我的奉献精神,在此,将Eclipse+MyEclipse的完全绿色版制作方法写出来,和大家一起分享,让大家都能享受到这种方便。
在这里,大象采用Eclipse3.3.1与MyEclipse_6.0.1GA_E3.3.1_Installer版来举例说明,其它的版本做法与此相同。
第1步:下载Eclipse3.3.1和MyEclipse_6.0.1GA
这里我要提醒大家注意一下:下载Eclipse时不要选择3.2的版本,因为MyEclipse6.0需要3.3版本以上的支持,另外就是下载MyEclipse时不要下完全版,而应该只下插件版,我的这个MyEclipse6.0.1的插件版是176M。
第2步:解压Eclipse3.3.1
将Eclipse3.3.1的压缩包解压到D盘根目录下。
做这个绿色版,把它放在根目录下是因为这样做很方便,在这里,大象以D盘为例,来说明制作方法。
第3步:安装MyEclipse6.0.1GA
双击"MyEclipse_6.0.1GA_E3.3.1_Installer.exe"开始安装MyEclipse,在第3步:"Choose Eclipse Folder"时,注意 "Please Choose Existing Eclipse Installation Folder",点击"Choose...",请选择你解压的Eclipse文件夹,选择好之后如下图:
点击"Next",出现"Where Would You Like to Install MyEclipse 6.0.1?",点击"Choose...",选择上面的eclipse文件夹,这时记得在eclipse后面加一个目录名,否则,MyEclipse的安装文件就会全部放在eclipse的根目录下,这可不是我们希望看到滴,设置好之后如下图:
下面的安装没什么好说的,就是一路Next了。安装结束后,可以在eclipse目录下看到有一个"MyEclipse 6.0.1GA"这个文件夹,进去看看,是不是有两个文件夹,两个文件?
OK,到此MyEclipse插件已经安装完成了,下面来进行我们的绿色插件制作。
第4步:插件制作
在eclipse目录下,新建一个文件夹,命名为"ThirdPlugins"(你要取别的名字也可以,不过一定要和links目录里面的配置文件中的路径一致,后面会有说明),将"MyEclipse 6.0.1GA"这个文件夹复制到"ThirdPlugins"目录下,别用剪切喔,这可是刚才安装MyEclipse的目录,等会还要缷载MyEclipse,如果这个目录没有了,到时缷载不了,出了什么问题可不要怪大象喔!
MyEclipse安装好之后,会在eclipse目录下生成一个links文件夹,里面有一个"com.genuitec.eclipse.MyEclipse.link"文件,我们删除它,另外新建一个"MyEclipse 6.0.1GA.ini"文件,内容为:path=ThirdPlugins/MyEclipse 6.0.1GA
保存完之后,我们的插件制作也结束了,然后就是缷载MyEclipse,千万不要直接把那个文件夹删掉,而应该缷载它。
其实所有的插件都可以按这个方式来做,这样做的好处就是,想用就放进去,不想用就删掉,如果放到eclipse的features和plugins里面,会很不好管理。
第5步:配置参数
虽然插件已经安装好了,但是,此时我们还不能启动它,应该对eclipse的启动参数设置一下,提高它的启动速度和运行时的稳定性。在eclipse.exe上点右键,选择"创建快捷方式",在快捷方式上点右键,选择"属性",在"D:\eclipse\eclipse.exe后面加上空格,将这些参数加在后面:
-vmargs -Xverify:none -XX:+UseParallelGC -XX:PermSize=20M -XX:MaxPermSize=128M -Xms256M -Xmx512M
-Xms256M -Xmx512M:这是堆,根据内存大小来设置,比如大象的内存是1G,我就设成256和512,这样一般都够用了。
当然了,你也可以什么都不设置,不过大象还是建议设置这些参数,可以很大程度上提升eclipse的启动速度。在安装完MyEclipse时,还会生成一个eclipse.ini的备份文件,这个不需要,删掉。我们可以修改下eclipse.ini文件,原始的如下:
-showsplash
com.genuitec.myeclipse.product
--launcher.XXMaxPermSize
256m
-vmargs
-Xms128m
-Xmx512m
-Dosgi.splashLocation=D:\eclipse\MyEclipse 6.0.1GA\eclipse\MyEclipseSplash.bmp
-Duser.language=en
-XX:PermSize=128M
-XX:MaxPermSize=256M
其实这个文件为空都没关系,大象试过,全部删除,没有错误,不过我还是建议大家里面至少保留这些东东
-vmargs
-Xms256m
-Xmx512m
我将128改成了256,如果你想在MyEclipse插件中用"MyEclipse 6.0.1"快捷方式来启动的话,可以写成这样
-vmargs
-Xms256m
-Xmx512m
-Dosgi.splashLocation=D:\Eclipse-3.3.1\ThirdPlugins\MyEclipse 6.0.1GA\eclipse\MyEclipseSplash.bmp
最下面一行是启动时,显示MyEclipse的图片,如果没有这句话运行"MyEclipse 6.0.1"快捷方式,则会显示eclipse的启动画面,其实"MyEclipse 6.0.1"快捷方式还是连接着eclipse.exe这个执行程序,在"MyEclipse 6.0.1"上点右键,选择属性,在目标里就可以看到。
第6步:注册MyEclipse
MyEclipse6.0的注册一定要断开网络,否则肯定不成功!
6.0.1GA注册码
Subscriber: administrator
Subscription Code: nLR7ZL-655342-54657656405281154
这里有一点大象要提醒大家注意,如果你电脑上现在正有使用的MyEclipse,就是说已经注册了,那么在C:\Documents and Settings\"自己的用户名" 目录下,会有一个".myeclipse.properties"文件,这时请先备份此文件,然后删除它,断开网络,再来注册MyEclipse6.0.1,成功后如下:
第7步:打包eclipse
到现在所有的工作都已经完成,启动eclipse的速度快不快?好了,该做最后一步操作了,将"configuration"文件夹下的内容除"config.ini"文件外全部删除,另外再把workspace文件夹删除,大象一般会把workspace放在eclipse根目录下,方法是在第一次启动选择路径时把前面的目录都删除,只保留workspace(前面什么都不要保留)。这样方便管理,你要放在其它的地方随便,这个看各人喜好。做完这两步之后,最好还是在eclipse目录下建一个txt文本文件,把上面的注册码放到里面,另外加上一句话:"注册时一定要断开网络,否则肯定注册不成功!"这样以后用时,可以提醒自己一下。里面有注册码,要用时很方便。
在eclipse文件夹上点右键,选择"添加到eclipse.rar",等到压缩完成,至此,终于大功告成!
大家尽管放心按着我的方法试,大象前前后后做了不下十遍,今天又在公司的电脑上做了一遍,图片都是刚刚截取的,嘿嘿,今天老板不在,大象小小的放松一下,写写博客。有了这个压缩包,以后大家在使用时就会方便很多,特别是保存到移动硬盘里,想在哪用就在哪用。哇哈哈哈哈~~~~~~~~祝大家好运,都成功做出来!
此帖为菠萝大象原创,如要转载请注明出处。
存款准备金是指金融机构为保证客户提取存款和资金清算需要而准备的在
中央银行的存款,中央银行要求的存款准备金占其存款总额的比例就是
存款准备金率。
存款准备金,是限制金融机构信贷扩张和保证客户提取存款和资金清算需要而准备的资金。法定存款准备金率,是金融机构按规定向中央银行缴纳的存款准备金占其存款的总额的比率。这一部分是一个风险准备金,是不能够用于发放贷款的。这个比例越高,执行的紧缩政策力度越大。存款准备金率变动对商业银行的作用过程如下:
当中央银行提高法定准备金率时,
商业银行可提供放款及创造信用的能力就下降。因为准备金率提高,货币乘数就变小,从而降低了整个商业银行体系创造信用、扩大信用规模的能力,其结果是社会的
银根偏紧,货币供应量减少,
利息率提高,投资及社会支出都相应缩减。反之,亦然。
打比方说,如果存款准备金率为7%,就意味着金融机构每吸收100万元存款,要向央行缴存7万元的存款准备金,用于发放贷款的资金为93万元。倘若将存款准备金率提高到7.5%,那么金融机构的可贷资金将减少到92.5万元。
在存款准备金制度下,金融机构不能将其吸收的存款全部用于发放贷款,必须保留一定的资金即存款准备金,以备客户提款的需要,因此存款准备金制度有利于保证金融机构对客户的正常支付。随着金融制度的发展,存款准备金逐步演变为重要的
货币政策工具。当中央银行降低存款准备金率时,金融机构可用于贷款的资金增加,社会的贷款总量和
货币供应量也相应增加;反之,社会的贷款总量和货币供应量将相应减少。
中央银行通过调整存款准备金率,可以影响金融机构的
信贷扩张能力,从而间接调控货币供应量。超额存款准备金率是指商业银行超过法定存款准备金而保留的准备金占全部
活期存款的比率。从形态上看,
超额准备金可以是现金,也可以是具有高流动性的
金融资产,如在中央银行账户上的准备存款等。
2006年以来,中国经济快速增长,但经济运行中的矛盾也进一步凸显,投资增长过快的势头不减。而投资增长过快的主要原因之一就是货币信贷增长过快。提高存款准备金率可以相应地减缓货币信贷增长,保持国民经济健康、协调发展。
一般地,存款准备金率上升,利率会有上升压力,这是实行紧缩的货币政策的信号。存款准备金率是针对银行等金融机构的,对最终客户的影响是间接的;利率是针对最终客户的,比如你存款的利息,影响是直接的。
1、因为
流动性过剩造成的
通货膨胀,上调准备金率可以有效降低流动性 。
2、因为现在美国的信贷危机,上调准备金率可以保证金融系统的支付能力,增加银行的抗风险能力,防止金融风险的产生。
7月19日,中国经济半年报发布。2007年上半年,中国经济增长率达11.5%,消费物价指数(CPI)则创下了33个月来的新高。一些投行当天即预测,紧缩政策已近在眼前。
我国货币供应量多、贷款增长快、超额存款准备金率较高、市场利率走低。这一“多”、一“快”、一“高”、一“低”,表明流动性过剩问题确实比较突出。
始自2003年下半年的这一轮宏观调控,一直试图用“点刹”的办法让经济减速,而今看来,这列快车的“刹车”效率似乎有问题。11.9%的增速,偏离8%的预期目标近4个百分点。中国经济不仅没有软着陆,反而有一发难收之势。
次数 时间 调整前 调整后 调整幅度
26 08年06月25日 17% 17.5% 1%
26 08年06月15日 16.5% 17%
25 08年05月20日 16% 16.5% 0.5%
24 08年04月25日 15.5% 16% 0.5%
23 08年03月25日 15% 15.5% 0.5%
22 08年01月25日 14.5% 15% 0.5%
21 07年12月25日 13.5% 14.5% 1%
20 07年11月26日 13% 13.5% 0.5%
19 07年10月13日 12.5% 13% 0.5%
18 07年09月25日 12% 12.5% 0.5%
17 07年08月15日 11.5% 12% 0.5%
16 07年06月5日 11% 11.5% 0.5%
15 07年05月15日 10.5% 11% 0.5%
14 07年04月16日 10% 10.5% 0.5%
13 07年02月25日 9.5% 10% 0.5%
12 07年01月15日 9% 9.5% 0.5%
11 06年11月15日 8.5% 9% 0.5%
10 06年08月15日 8% 8.5% 0.5%
9 06年07月05日 7.5% 8% 0.5%
8 04年04月25日 7% 7.5% 0.5%
7 03年09月21日 6% 7% 1%
6 99年11月21日 8% 6% -2%
5 98年03月21日 13% 8% -5%
4 88年9月 12% 13% 1%
3 87年 10% 12% 2%
2 85年 央行将法定存款准备金率统一调整为10%
1 84年 央行按存款种类规定法定存款准备金率,企业存款20%,农村存款25%,储蓄存款40%
今天在学习的过程中遇到了一篇关于java中使用oracle导入导出的文章,感觉还不错,为了学习和以后工作的需要,我整理如下:
String[] cmds = new String[3];
cmds[0] = "cmd";
cmds[1] = "/C";
cmds[2]=commandBuf.toString();
Process process=Runtime.getRuntime().exec(cmds);
boolean shouldClose=false;
try {
InputStreamReader isr = new InputStreamReader(process.getErrorStream());
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null){
if(line.indexOf("错误")!=-1){
shouldClose=true;
break;
}
}
}
catch (IOException ioe) {
shouldClose=true;
}
if(shouldClose)
process.destroy();
int exitVal = process.waitFor();
下面还有一种形式:
exp和imp的输出是要从ErrorStream中获取,这是我以前写的
Process proc = null;
try
{
proc = Runtime.getRuntime().exec(cmd.toString());
InputStream istr = proc.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(istr));
String str;
while ((str=br.readLine()) != null)
{
errorInfo.append(str + "\n");
}
proc.waitFor();
}
catch (Exception e)
{
...
}
if (proc.exitValue() == 0)
{
proc.destroy();
return true;
}
else
{
if(logger.isDebugEnabled())
logger.debug(errorInfo);
proc.destroy();
return false;
}
两者可以比较的看看
注意:在执行oracle的exp时,出现了一个很怪的现象,就是exp在console输出的信息没有被放入InputStream,反而是放到了ErrorStream中(即使正确的情况也是),这就导致了按照正常的情况去写这段代码的话反而会出问题。---这是在jdk1.4环境下实现的。
还有中建议是在jdk1.5环境下:可以如下实现
1,把对InputStream的处理放到一个单独Thread里面。
2,用ProcessBuilder的redirectErrorStream来合并OutputStream和ErrorStream。注意子进程的InputStream对应父进程的OutStream。如果不合并这两个流的话则必须并行排空它们,顺序的排空会导致思索。
Java数据库连接(JDBC)API是一系列能够让Java编程人员访问数据库的接口,各个开发商的接口并不完全相同。在使用多年的Oracle公司的JDBC后,我积累了许多技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能。
1、在客户端软件开发中使用Thin驱动程序
在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。
2、关闭自动提交功能,提高系统性能
在第一次建立与数据库的连接时,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:
conn.setAutoCommit(false);
值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。
3、在动态SQL或有时间限制的命令中使用Statement对象
在执行SQL命令时,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有时间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。
此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。
4、利用helper函数对动态SQL命令进行格式化
在创建使用Statement对象执行的动态SQL命令时,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。
5、利用PreparedStatement对象提高数据库的总体效率
在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。
6、在成批处理重复的插入或更新操作中使用PreparedStatement对象
如果成批地处理插入和更新操作,就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch():
PreparedStatement pstmt3D null;
try {
((OraclePreparedStatement)
pstmt).setExecuteBatch(30);
...
pstmt.executeUpdate();
}
调用setExecuteBatch()时指定的值是一个上限,当达到该值时,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法随时传输批处理任务。
7、使用Oracle locator方法插入、更新大对象(LOB)
Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。
8、使用SQL92语法调用存储过程
在调用存储过程时,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程时使用SQL92。
9、使用Object SQL将对象模式转移到数据库中
既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。
10、利用SQL完成数据库内的操作
我要向大家介绍的最重要的经验是充分利用SQL的面向集合的方法来解决数据库处理需求,而不是使用Java等过程化的编程语言。
如果编程人员要在一个表中查找许多行,结果中的每个行都会查找其他表中的数据,最后,编程人员创建了独立的UPDATE命令来成批地更新第一个表中的数据。与此类似的任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成。当能够在单一的SQL命令中完成任务,何必要让数据在网上流来流去的?我建议用户认真学习如何最大限度地发挥SQL的功能。
转帖:
最近做的一个项目中运用到了连接池技术,可能我们大家比较熟悉的开源连接池有dbcp,c3p0,proxool。对这三种连接池来说,从性能和出错率来说,proxool稍微比前两种好些。今天我主要简单的讲述一下,我在项目中成功的配置和源码。
第一步:首先去http://proxool.sourceforge.net/下载一个proxool.jar文件了,我用的是proxool-0.8.3.jar,现在最新版本是proxool-0.9.3.jar.除了他们的查看监控台的页面有变化,其他的变化不是太大。
第二步:就是写一个单独的proxool.xml文件放到WEB-INF文件夹下。我用的数据库是MySQL.
peoxool.xml的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<proxool-config>
<proxool>
<alias>db</alias>
<driver-url>jdbc:mysql://×××.×××.××.×××:3303/mydb<driver-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver-properties>
<property name="user" value="root" />
<property name="password" value="*******" />
</driver-properties>
<house-keeping-sleep-time>90000</house-keeping-sleep-time>
<maximum-new-connections>20</maximum-new-connections>
<prototype-count>5</prototype-count>
<maximum-connection-count>1000</maximum-connection-count>
<minimum-connection-count>10</minimum-connection-count>
</proxool>
</proxool-config>
对以上配置简单的解释:
1.<alias> :为该连接池起一个别名,在其他文件中引用。引用是:(proxool.db);
2. <driver-class>com.mysql.jdbc.Driver</driver-class>
<driver-properties>
<property name="user" value="root" />
<property name="password" value="root" />
</driver-properties>
这些配置大家应该比较熟悉吧。配置数据库的驱动和连接。
3.<house-keeping-sleep-time> :proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁。
4.<maximum-new-connections>: 指因未有空闲连接可以分配而在队列中等候的最大请求数,超过这个请求数的用户连接就不会被接受。
5.<prototype-count>:最少保持的空闲连接数。
6.<maximum-connection-count>: 允许最大连接数,超过了这个连接,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定
7.<minimum-connection-count>:最小连接数
第三步:加载并初始化proxool.xml文件。因为它是连接数据库的,其他很多模块都用到数据,所以你必须首先加载它,在web.xml中进行如下配置:如果你以前加载applicationContext.xml用的是:
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
这时你必须换成如下配置:
<servlet>
<servlet-name>contextConfigLocation</servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
要不然你就会遇见这样的错误:
Problem
org.logicalcobwebs.proxool.ProxoolException: Attempt to refer to a unregistered pool by its
alias 'db'
如果用过proxool与spring整合时,不少就遇到过这样的问题,其实这个问题很明显就是你的proxool.xml没有被先加载初始化,我们应该让它先加载,应该这样配置:
<servlet>
<servlet-name>ServletConfigurator</servlet-name>
<servlet-class>
org.logicalcobwebs.proxool.configuration.ServletConfigurator
</servlet-class>
<init-param>
<param-name>xmlFile</param-name>
<param-value>WEB-INF/proxool.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
把<load-on-startup>的值设为1,值越小级别就越高,就先被加载初始化。一定要先于applicationContext.xml的加载。
这一步很关键,一定要注意,不然你就会遇到上面的问题。网上有很大人都遇见过这样的问题,只要你用了你才会体会到那样的错误。如果是第一次,你会很迷茫的,我第一次配置就出现了上面的问题,让我调试了好处时间才找出问题所在。希望你们不要再犯这样的错误。
如果你想监控数据库连接池的连接情况,可以简单的配置一下就可以了,因为大部分功能源码中都已写好,我们用是只需简单的配置。
<servlet>
<servlet-name>Admin</servlet-name>
<servlet-class>
org.logicalcobwebs.proxool.admin.servlet.AdminServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Admin</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
如果访问的话,可能有点问题,会报ISO-8859-1的问题。我们一般用的是utf-8,gbk2312.最好的解决办法就是重写源码中的AdminServlet
.java。我就是重写了这个源码。才解决了这个乱码问题。可能还有其他办法:只需简单的该一个方法就可以了。
private void printDefinitionEntry(ServletOutputStream out, String name, String value) throws IOException {
out.println(" <tr>");
out.print(" <td width=\"200\" valign=\"top\" style=\"" + STYLE_CAPTION + "\">");
out.print(name);
out.println("</td>");
if (value != null) {
out.print(" <td style=\"" + STYLE_DATA + "\">");
out.print(new String(value.getBytes("ISO-8859-1"),"utf-8"));
} else {
out.print(" <td style=\"" + STYLE_NO_DATA + "\">off");
}
out.print("</td>");
out.println(" </tr>");
}
上面红色的部分就是我改的部分,然后把web.xml中的
<servlet-class>
com.jack.servlet.AdminServlet
</servlet-class>
中<servlet-class>换成你改写的那个类。
最后一步:整合spring和proxool。在applicationContext.xml文件中把原来数据源的配置成这样:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.logicalcobwebs.proxool.ProxoolDriver</value>
</property>
<property name="url">
<value>proxool.db</value>
</property>
</bean>
这个 <property name="url">要配置成proxool.xml中的别名。其他地方不用该了。也可以与hibernate整合。与spring整合比较简单。我采用的就是上面的配置方式。
这样就配置完了,很清晰,很简单。如果查看监控情况的话:
http://www.××××.com/admin 就可以查看了,如果你是本地测试:改一下路径就可以了。
上面所讲述的是一个成功的配置,并在项目中成功的运用了。希望对连接池有爱好的朋友有所帮助。
Static变量的总结
1.不可从一个static方法内部发出对非static方法的调用,尽管反过来说是可以的。如果要在static方法中访问所在类的成员变量或方法,就必须首先创建相应的实例变量。
2.static变量可以通过类名直接访问,
3.static方法不能被覆盖,但是在子类中可以声明与父类惊天方法相同的方法,从而将父类的静态方法隐藏,另外自乐不能把父类的非静态方法重写为静态的:并且static方法也不能够重载(因为他们是前期绑定的,所以不能重载;不能重载的方的还有final、private)
而普通方法能被覆盖,覆盖的结果是产生多态;
例子:
package test;
class Test {
public static void main(String[] args) {
A a = new B();
a.f();
a.m();
}
}
class A {
public static void f() {
System.out.println("hello,A");
}
public void m() {
System.out.println("hello,A");
}
}
class B extends A {
public static void f() {
System.out.println("hello,B");
}
public void m() {
System.out.println("hello,B");
}
}
结果是:
hello,A
hello,B
1.根据Java Language Specification (Version 3) 8.4.8 的描述,子类在继承父类时,对于方法而言,存在两种关系:
A. override 即覆盖,这是对实例方法(instance method)而言的;子类与父类中形构相同的方法(原文中是 subsignature,它的范围比“形构相同”要大,请参考原文)会override 父类中的那个方法。
B. hide 即隐藏,这是对类方法(class method)即static 方法而言的。如果子类中定义了静态方法,则它会隐藏父类中形构相同的(原文中是 subsignature,它的范围比“形构相同要”大,请参考原文)所有方法,但如果隐藏了父类中的实例方法,则会编译报错。
2.根据上面的规范:
“override 覆盖”的前提是实例方法,只有实例方法在继承时才会出现override情况。
如果是static方法,在继承时出现的现象根本就不能用“override”这个词描述,如果static方法在父类和子类中形构一致,则被成为 hide(隐藏)。
3.因为static方法是类方法,实现时是静态绑定的(引用“JAVA 核心技术 卷1 第六版”中149页内容“private、static、final”修饰的方法是静态绑定的。其他的方法在运行时动态绑定。“interhanchi”所说的“static和final方法外都是后期绑定”并不精确),只与类相关,不会有多态性。 从编程的角度看,效果好像是“static方法不能被覆盖”;
4.从术语上看,“static方法能否被覆盖”这种说法本身就是错误的,因为“覆盖”定义的前提和基础是实例方法。
5.结论: 子类中的static方法会隐藏父类中形构相同的static方法。
final变量的总结:
Final不想做改变的理由是:设计和效率
在java中除了static方法、final方法、(private方法属于final方法)之外,其他所有的方法都是后期绑定
Final可以修饰基本类型,也可以修饰引用类型,但是当final修饰引用类型的时候,不能够改变引用指向另一个对象, 但这个对象本身的状态是可以改变的...
final String string=“final”;
是开辟了2个内存空间,在栈中的string引用在堆中的final,其中string是始终指向堆中的final这个地址的引用,不能改变。但是堆中的final却可以改变。
可以通过下面的例子来说明static final的作用:
public class Test0
{
private static final String string;
public Test0(String str)
{
string=str;
System.out.println(string);
}
public static void main(String[] args)
{
Test0 t=new Test0("hello world");
Test0 tt=new Test0("world hello");
}
}
解释说明:
1.首先正确的认识一下final, 一个final修饰的叫"终态", 而这种终态很特殊, 它指的是:"当这个变量被赋值之后成为终态". 那么,当一个被赋值之后的final修饰变量, 将不可再被赋新值. (先理解)
2.而static表示静态变量, 说穿了,你需要知道JAVA如何为一个类创建内存空间--我们知道类有空间,类产生的实例(对象)有空间,方法有空间,变量有空间, 当static修饰时, 这个变量会在类分配内存时就被分配内存了, 所以你知道,你可以不用产生对象就使用静态变量.
3.好了,有了以上两点,我们来看看, 你可以根据我的叙述试试. 首先,你只使用final修饰你的string, 你会发现不会报错, 因为这是一个"还没有被赋值的非静态的终态变量(在类分配内存时不会分配内存).好,接下来你改一下,写:private final String string = "sss"; 你会发现报错了,原因自己想.
接下来, 看你发出来的代码, 又被static修饰,又被final修饰,首先它是一个静态变量, 那么在类分配时就会分配内存, 实际上这个String就被初始化了,既然初始化了, 那么也就有值了, 而一个终态被赋值变量将不能再被赋值, 那么自然就报错了
学过Java基础的人都能很容易理解上面的代码和多态的原理,但是仍有一些关键的地方需要注意的,算是自己对多态的一个小结:
1. Java中除了static和final方法外,其他所有的方法都是运行时绑定的。private方法都被隐式指定为final的,因此final的方法不会在运行时绑定。当在派生类中重写基类中static、final、或private方法时,实质上是创建了一个新的方法。
2.在派生类中,对于基类中的private方法,最好采用不同的名字。
3.包含抽象方法的类叫做抽象类。注意定义里面包含这样的意思,只要类中包含一个抽象方法,该类就是抽象类。抽象类在派生中就是作为基类的角色,为不同的子类提供通用的接口。
4.对象清理的顺序和创建的顺序相反,当然前提是自己想手动清理对象,因为大家都知道Java垃圾回收器。
5.在基类的构造方法中小心调用基类中被重写的方法,这里涉及到对象初始化顺序。
6.构造方法是被隐式声明为static方法。
7.用继承表达行为间的差异,用字段表达状态上的变化。
Java把内存划分成两种:一种是栈内存,一种是堆内存。
在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
堆内存用来存放由new创建的对象和数组。
在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。
具体的说:
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等 指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时 动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本 类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器 会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这 种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
String是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。
因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。
另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的 对象。只有通过new()方法才能保证每次都创建一个新的对象。 由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。
java中内存分配策略及堆和栈的比较
2.1 内存分配策略
按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允 许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知 的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知 的栈一样,栈式存储分配按照先进后出的原则进行分配。
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时 模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释 放.
2.2 堆和栈的比较
上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈:
从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:
在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶 向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程 序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时.
堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的 优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面 向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用 new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花 掉更长的时间!这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点(晕~).
2.3 JVM中的堆和栈
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译 原理中的活动纪录的概念是差不多的.
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程 共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也 就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
1.去
http://barcode4j.sourceforge.net/下载文件,(源代码和生成好的都要
下载)
2.解压barcode4j-2.0alpha2-bin.zip这个包,在build目录下有barcode4j.jar,在lib目录下有avalon-framework-4.2.0.jar, 将barcode4j.jar和avalon-framework-4.2.0.jar添加到项目的lib中,eclipse中只要复制到web-inf下面的lib里面就OK了.
3.解压将barcode4j-2.0alpha2-src.zip,将srcjavaorgkrysalisbarcode4jservlet目录下的BarcodeServlet.java类的代码拷出来,修改默认的图片显示方式,找到 if (format == null) format = MimeTypes.MIME_JPEG;这一行,表示默认的格式为JPEG文件
4.将以下这段servlet配置在web.xml中
<servlet>
<servlet-name>BarcodeServlet</servlet-name>
<servlet-class>com.yourname.BarcodeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BarcodeServlet</servlet-name>
<url-pattern>/barcode</url-pattern>
</servlet-mapping>
|
5.在页面中添加<img src="<%=request.getContextPath() %>/barcode?msg=12345678" height="50px" width=130px/>
type是生成条形码的类型:
看例子就明白了
<table border="1">
<tr>
<td>
<h1>code39</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=0123456789&type=code39" height="100px" width=300px/>
</td>
<td>
<h1>code128</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=0123456789&type=code128" height="100px" width=300px/>
</td>
<td>
<h1>Codabar</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=0123456789&type=codabar" height="100px" width=300px/>
</td>
</tr>
<tr>
<td>
<h1>intl2of5</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=01234567890540&type=intl2of5" height="100px" width=300px/>
</td>
<td>
<h1>upc-a</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=012345678912&type=upc-a" height="100px" width=300px/>
</td>
<td>
<h1>ean-13</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=200123457893&type=ean-13" height="100px" width=300px/>
</td>
<td>
<h1>ean-8</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=20123451&type=ean-8" height="100px" width=300px/>
</td>
</tr>
<tr>
<td>
<h1>postnet</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=01234567890540&type=postnet" height="100px" width=300px/>
</td>
<td>
<h1>royal-mail-cbc</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=012345AS678912&type=royal-mail-cbc" height="100px" width=300px/>
</td>
<td>
<h1>pdf417</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=200123457893&type=pdf417" height="100px" width=300px/>
</td>
<td>
<h1>datamatrix</h1>
<img src="<%=request.getContextPath()%>/barcode?msg=20123451&type=datamatrix" height="100px" width=300px/>
</td>
</tr>
</table>
|
运行时类型识别(run-time type identification ,RTTI)的概念上看非常简单:
当只有一个指向对象基类的引用时RTTI机制可以让你找到这个对象的确切概念。
1。Class对象是RTTI的核心,Class的类的类,每个类都有一个class对象。每当编写并且编译一个新类,就会产生一个Class对象(被保存在同名的.class文件当中)
2。Class.forName("classname"),如果对象没有加载就加载对象(这将会触发类的静态初始化)
Class.newInstance()用来产生一个对象。如
Class m = Class.forName("classname");//1
Object o = m.newInstance();//2
java也提供"
类字面常量"的机制生成对象的引用。像这样:
A.class
对于基本类型,boolean.class === Boolean.TYPE , char.class ===Character.TYP
void.class ===Void.TYPE,等等。。。。
那么也可以用Class m = char.class; //或者 Class m = <aclass>
.class
Object o = m.newInstance();
((Char)o).××
3。instanceof 关键字用于检查对象是不是某个特定类型的实例。这用于类型转换前做检测。如:
if ( x instanceof Dog )
((Dog)x).bark();
除了 instanceof 关键字以外,还可以使用 Class.isInstance() 方法,两者功能相同。
4。instanceof的替代方案是: x.getClass == Y.class 或者x.getClass.equals( Y.class)
5。Class对象的getInterfaces()获得接口,getSurperClass 或者获得超类。
6。反射是运行时的类信息。java附带的库java.lang.reflect含有Field,Method,Constructor类(每个类都实现了Memeber接口)。这些类型的对象是有JVM在运行时创建的,用以表示未知类里对象的成员,然后用Constructor创建新的对象,用get ()和set()方法读取和修改Field对象关联的字段,用invoke()方法调用于Method对象关联的方 法,还可以用getFields(),getMethods(),getConstructors()等等方法。
一:无返回值的存储过程
存储过程为:
CREATE OR REPLACE PROCEDURE TESTA(PARA1 IN VARCHAR2,PARA2 IN VARCHAR2) AS
BEGIN
INSERT INTO HYQ.B_ID (I_ID,I_NAME) VALUES (PARA1, PARA2);
END TESTA;
|
然后呢,在java里调用时就用下面的代码:
package com.hyq.src;
import java.sql.*;
import java.sql.ResultSet;
public class TestProcedureOne {
public TestProcedureOne() {
}
public static void main(String[] args ){
String driver = "oracle.jdbc.driver.OracleDriver";
String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521: hyq ";
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
CallableStatement cstmt = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, " hyq ", " hyq ");
CallableStatement proc = null;
proc = conn.prepareCall("{ call HYQ.TESTA(?,?) }");
proc.setString(1, "100");
proc.setString(2, "TestOne");
proc.execute();
}
catch (SQLException ex2) {
ex2.printStackTrace();
}
catch (Exception ex2) {
ex2.printStackTrace();
}
finally{
try {
if(rs != null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch (SQLException ex1) {
}
}
}
}
|
当然了,这就先要求要建张表TESTTB,里面两个字段(I_ID,I_NAME)。
二:有返回值的存储过程(非列表)
存储过程为:
CREATE OR REPLACE PROCEDURE TESTB(PARA1 IN VARCHAR2,PARA2 OUT VARCHAR2) AS
BEGIN
SELECT INTO PARA2 FROM TESTTB WHERE I_ID= PARA1;
END TESTB;
|
在java里调用时就用下面的代码:
package com.hyq.src;
public class TestProcedureTWO {
public TestProcedureTWO() {
}
public static void main(String[] args ){
String driver = "oracle.jdbc.driver.OracleDriver";
String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521:hyq";
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, " hyq ", " hyq ");
CallableStatement proc = null;
proc = conn.prepareCall("{ call HYQ.TESTB(?,?) }");
proc.setString(1, "100");
proc.registerOutParameter(2, Types.VARCHAR);
proc.execute();
String testPrint = proc.getString(2);
System.out.println("=testPrint=is="+testPrint);
}
catch (SQLException ex2) {
ex2.printStackTrace();
}
catch (Exception ex2) {
ex2.printStackTrace();
}
finally{
try {
if(rs != null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch (SQLException ex1) {
}
}
}
}
}
|
注意,这里的proc.getString(2)中的数值2并非任意的,而是和存储过程中的out列对应的,如果out是在第一个位置,那就是proc.getString(1),如果是第三个位置,就是proc.getString(3),当然也可以同时有多个返回值,那就是再多加几个out参数了。
三:返回列表
由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用pagkage了。所以要分两部分,
1, 建一个程序包。如下:
CREATE OR REPLACE PACKAGE TESTPACKAGE AS
TYPE Test_CURSOR IS REF CURSOR;
end TESTPACKAGE;
|
2,建立存储过程,存储过程为:
CREATE OR REPLACE PROCEDURE TESTC(p_CURSOR out TESTPACKAGE.Test_CURSOR) IS
BEGIN
OPEN p_CURSOR FOR SELECT * FROM HYQ.TESTTB;
END TESTC;
|
可以看到,它是把游标(可以理解为一个指针),作为一个out 参数来返回值的。
在java里调用时就用下面的代码:
package com.hyq.src;
import java.sql.*;
import java.io.OutputStream;
import java.io.Writer;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import oracle.jdbc.driver.*;
public class TestProcedureTHREE {
public TestProcedureTHREE() {
}
public static void main(String[] args ){
String driver = "oracle.jdbc.driver.OracleDriver";
String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521:hyq";
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(strUrl, "hyq", "hyq");
CallableStatement proc = null;
proc = conn.prepareCall("{ call hyq.testc(?) }");
proc.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);
proc.execute();
rs = (ResultSet)proc.getObject(1);
while(rs.next())
{
System.out.println("<tr><td>" + rs.getString(1) + "</td><td>"+rs.getString(2)+"</td></tr>");
}
}
catch (SQLException ex2) {
ex2.printStackTrace();
}
catch (Exception ex2) {
ex2.printStackTrace();
}
finally{
try {
if(rs != null){
rs.close();
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
}
catch (SQLException ex1) {
}
}
}
}
|
1、取得存储过程返回的值
CallableStatement cs = conn.prepareCall("{call proc_fbquery(?,?,?)}"); //调用存储过程cs.setString(1,mem);cs.setInt(2,n);cs.registerOutParameter(3,oracle.jdbc.OracleTypes.CURSOR);cs.execute();rs=(ResultSet)cs.getObject(3);
|
2、对存储过程赋值时:
CallableStatement cs= conn.prepareCall("{call proc_fbquery(?)}"); //调用存储过程cs.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);cs.setCursorName(cusorName); //提供result的名称cs.setString(1,rs);rs=cs.executeQuery();rs =(ResultSet)cs.getObject(1);
|
云计算是一种全新的商业模式,其核心部分依然是数据中心,它使用的硬件设备主要是成千上万的工业标准服务器,它们由英特尔或AMD生产的处理器以及其他硬件厂商的产品组成。企业和个人用户通过高速互联网得到计算能力,从而避免了大量的硬件投资。
简而言之,云计算将使未来的互联网变成超级计算的乐土。“云计算的基本原理是,通过使计算分布在大量的分布式计算机上,而非本地计算机或远程服务器中,企业数据中心的运行将更与互联网相似。这使得企业能够将资源切换到需要的应用上,根据需求访问计算机和存储系统。”
import java.security.*;
import java.security.spec.*;
class MD5_Test{
public final static String MD5(String s){
char hexDigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f'};
try {
byte[] strTemp = s.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
catch (Exception e){
return null;
}
}
public static void main(String[] args){
//MD5_Test aa = new MD5_Test();
System.out.print(MD5_Test.MD5("XX"));
}
用三个方法设置
Oracle数据库穿越
防火墙:
方法一:在系统注册表中
hkey_local_machinesoftwareoraclehome0下加入字符串值:
方法二:
1、首先,我们需要将数据库实例改为SHARED SERVER模式
2、以SYSDBA登录SQLPLUS,通过SQLPLUS生成系统当前的参数设置文件pfile:
create pfile='d:init.ora' from spfile;
3、修改d:init.ora文件,在其中增加(用editplus编辑):
*.service_names='your service
name'和*.dispatchers='(address=(protocol=tcp)
(host=localhost)(port=1521) (dispatchers=1)' |
4、生成新的SPFILE:create spfile from pfile='d:init.ora';
5、重启动数据库。
6、在防火墙中开放1521端口。
方法三:
在数据库端(可以是另外的机器,但cman的机器必须和数据库都在防火墙的后面)安装cman的前提下,启动cman,然后开放防火墙端的1630端口(需要查看cman开的是什么端口),最后在客户端的tnsnames.ora文件中添加以下内容:
cmantest = (description = (address_list = (address =
<- first address is to CMAN (protocol=tcp)
(host=hostname or ip of cman) (port=1610) )
(address= <- second address is to Listener
(protocol=tcp) (host=hostname or ip of listener) (port=1521) ) )
(connect_data = (sid = sidname)) (source_route = yes) |
现象:使用plsql/developer工具导出数据时出现错误,具体示例如下:
EXP-00056: 遇到 ORACLE 错误 6550
ORA-06550: line 1, column 41:
PLS-00302: component 'SET_NO_OUTLINES' must be declared
ORA-06550: line 1, column 15:
PL/SQL: Statement ignored
EXP-00000: 导出终止失败
解决方法如下:
exp.exe 改成使用 expdp.exe
类似导入时使用impdp.exe命令
在plsql/dev中方法改成E:\oracle\product\10.2.0\client_2\bin\expdp.exe就可以了。
我想任何一本介绍模式的书在讲到Decorator模式的时候不能不提到它的实际应用——在
Java/IO库里面的应用,<<
Java与模式>>这本书也不例外,有点不一样的是,这本书在介绍的时候有个专题,是从两个模式来看Java/IO库,完这个专题后,个人感觉对Java/IO库有了全新的认识同时也加深了Decorator模式跟Adapter适配器模式的理解,现和大家分享下这个在我看来很伟大的成果,同时说明下,以下大部分文字跟图片是来自<<Java与模式>>这本书。
一。引子(概括地介绍Java的IO)
无论是哪种编程语言,输入跟输出都是重要的一部分,Java也不例外,而且Java将输入/输出的功能和使用范畴做了很大的扩充。它采用了流的机制来实现输入/输出,所谓流,就是数据的有序排列,而流可以是从某个源(称为流源或Source of Stream)出来,到某个目的地(称为流汇或Sink of Stream)去的。由流的方向,可以分成输入流和输出流,一个程序从输入流读取数据向输出流写数据。
如,一个程序可以用FileInputStream类从一个磁盘文件读取数据,如下图所示:
像FileInputStream这样的处理器叫做流处理器,它就像流的管道一样,从一个流源吸入某种类型的数据,并输出某种类型的数据。上面这种示意图叫做流的管道图。
同样道理,也可以用FileOutputStream类向一个磁盘文件写数据,如下图所示:
在实际应用这种机制并不没有太大的用处,程序需要写出地通常是非常结构化的信息,因此这些byte类型的数据实际上是一些数值,文字,源代码等。Java的I/O库提供了一个称做链接(Chaining)的机制,可以将一个流处理器跟另一个流处理器首尾相接,以其中之一的输出为输入,形成一个流管道的链接。
例如,DataInputStream流处理器可以把FileInputStream流对象的输出当作输入,将Byte类型的数据转换成Java的原始类型和String类型的数据。如下图所示:
类似地,向一个文件写入Byte类型的数据不是一个简单的过程。一个程序需要向一个文件里写入的数据往往都是结构化的,而Byte类型则是原始类型。因此在写的时候必须经过转换。DataOutputStream流处理器提供了接收了原始数据类型和String数据类型,而这个流处理器的输出数据则是Byte类型。也就是说DataOutputStream可以将源数据转换成Byte类型的数据,再输出来。
这样一来,就可以将DataOutputStream与FileOutputStream链接起来,这样程序就可以将原始数据类型和String类型的源数据写入这个链接好的双重管道里面,达到将结构化数据写到磁盘文件里面的目的,如下图所示:
这又是链接的所发挥的大作用。
流处理器所处理的流必定都有流源,而如果将流类所处理的流源分类的话,基本可以分成两大类:
第一 数组,String,File等,这一种叫原始流源。
第二 同样类型的流用做链接流类的流源,叫链接流源。
二 Java I/O库的设计原则
Java语言的I/O库是对各种常见的流源,流汇以及处理过程的抽象化。客户端的Java程序不必知道最终的流源,流汇是磁盘上的文件还是数组等;也不必关心数据是否经过缓冲的,可否按照行号读取等处理的细节。
书中提到了,对于第一次见到Java/IO库的人,无不因为这个库的庞杂而感到困惑;而对于熟悉这个库的人,而又常常为这个库的设计是否得当而争论不体。书的作者提出自己的意见,要理解Java I/O这个庞大而复杂的库,关键是要掌握两个对称性跟两个设计模式模式。
Java I/O库具有两个对称性,它们分别是:
1 输入-输出对称性,比如InputStream和OutputStream各自占据Byte流的输入与输出的两个平行的等级结构的根部。而Reader和Writer各自占据Char流的输入与输出的两个平行的等级结构的根部。
2 byte-char对称,InputStream和Reader的子类分别负责Byte和Char流的输入;OutputStream和Writer的子类分别负责Byte和Char流的输出,它们分别形成平行的等级结构。
Java I/O库的两个设计模式:
Java的I/O库总体设计是符合装饰者模式(Decorator)跟适配器模式(Adapter)的。如前所述,这个库中处理流的类叫做流类。引子里所谈到的FileInputStream,FileOutputStream,DataInputStream及DataOutputStream都是流处理器的例子。
1 装饰者模式:在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器可以对另一些流处理器起到装饰作用,形成新的,具有改善了的功能的流处理器。装饰者模式是Java I/O库的整体设计模式。这样的一个原则是符合装饰者模式的,如下图所示:
2 适配器模式:在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器是对其它类型的流源的适配。这就是适配器模式的应用,如下图所示。
适配器模式应用到了原始流处理器的设计上面,构成了I/O库所有流处理器的起点。
JDK为程序员提供了大量的类库,而为了保持类库的可重用性,可扩展性和灵活性,其中使用到了大量的设计模式,本文将介绍JDK的I/O包中使用到的Decorator模式,并运用此模式,实现一个新的输出流类。
Decorator模式简介
Decorator模式又名包装器(Wrapper),它的主要用途在于给一个对象动态的添加一些额外的职责。与生成子类相比,它更具有灵活性。
有时候,我们需要为一个对象而不是整个类添加一些新的功能,比如,给一个文本区添加一个滚动条的功能。我们可以使用继承机制来实现这一功能,但是这种方法不够灵活,我们无法控制文本区加滚动条的方式和时机。而且当文本区需要添加更多的功能时,比如边框等,需要创建新的类,而当需要组合使用这些功能时无疑将会引起类的爆炸。
我们可以使用一种更为灵活的方法,就是把文本区嵌入到滚动条中。而这个滚动条的类就相当于对文本区的一个装饰。这个装饰(滚动条)必须与被装饰的组件(文本区)继承自同一个接口,这样,用户就不必关心装饰的实现,因为这对他们来说是透明的。装饰会将用户的请求转发给相应的组件(即调用相关的方法),并可能在转发的前后做一些额外的动作(如添加滚动条)。通过这种方法,我们可以根据组合对文本区嵌套不同的装饰,从而添加任意多的功能。这种动态的对对象添加功能的方法不会引起类的爆炸,也具有了更多的灵活性。
以上的方法就是Decorator模式,它通过给对象添加装饰来动态的添加新的功能。如下是Decorator模式的UML图:
Component为组件和装饰的公共父类,它定义了子类必须实现的方法。
ConcreteComponent是一个具体的组件类,可以通过给它添加装饰来增加新的功能。
Decorator是所有装饰的公共父类,它定义了所有装饰必须实现的方法,同时,它还保存了一个对于Component的引用,以便将用户的请求转发给Component,并可能在转发请求前后执行一些附加的动作。
ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰,可以使用它们来装饰具体的Component.
JAVA IO包中的Decorator模式
JDK提供的java.io包中使用了Decorator模式来实现对各种输入输出流的封装。以下将以java.io.OutputStream及其子类为例,讨论一下Decorator模式在IO中的使用。
首先来看一段用来创建IO流的代码:
以下是代码片段:
try {
OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
这段代码对于使用过JAVA输入输出流的人来说再熟悉不过了,我们使用DataOutputStream封装了一个FileOutputStream.这是一个典型的Decorator模式的使用,FileOutputStream相当于Component,DataOutputStream就是一个Decorator.将代码改成如下,将会更容易理解:
以下是代码片段:
try {
OutputStream out = new FileOutputStream("test.txt");
out = new DataOutputStream(out);
} catch(FileNotFoundException e) {
e.printStatckTrace();
}
由于FileOutputStream和DataOutputStream有公共的父类OutputStream,因此对对象的装饰对于用户来说几乎是透明的。下面就来看看OutputStream及其子类是如何构成Decorator模式的:
OutputStream是一个抽象类,它是所有输出流的公共父类,其源代码如下:
以下是代码片段:
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException;
……
}
它定义了write(int b)的抽象方法。这相当于Decorator模式中的Component类。
ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三个类都直接从OutputStream继承,以ByteArrayOutputStream为例:
以下是代码片段:
public class ByteArrayOutputStream extends OutputStream {
protected byte buf[];
protected int count;
public ByteArrayOutputStream() {
this(32);
}
public ByteArrayOutputStream(int size) {
if (size 〈 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
public synchronized void write(int b) {
int newcount = count + 1;
if (newcount 〉 buf.length) {
byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
buf[count] = (byte)b;
count = newcount;
}
……
}
它实现了OutputStream中的write(int b)方法,因此我们可以用来创建输出流的对象,并完成特定格式的输出。它相当于Decorator模式中的ConcreteComponent类。
接着来看一下FilterOutputStream,代码如下:
以下是代码片段:
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
this.out = out;
}
public void write(int b) throws IOException {
out.write(b);
}
……
}
同样,它也是从OutputStream继承。但是,它的构造函数很特别,需要传递一个OutputStream的引用给它,并且它将保存对此对象的引用。而如果没有具体的OutputStream对象存在,我们将无法创建FilterOutputStream.由于out既可以是指向FilterOutputStream类型的引用,也可以是指向ByteArrayOutputStream等具体输出流类的引用,因此使用多层嵌套的方式,我们可以为ByteArrayOutputStream添加多种装饰。这个FilterOutputStream类相当于Decorator模式中的Decorator类,它的write(int b)方法只是简单的调用了传入的流的write(int b)方法,而没有做更多的处理,因此它本质上没有对流进行装饰,所以继承它的子类必须覆盖此方法,以达到装饰的目的。
BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类,它们相当于Decorator模式中的ConcreteDecorator,并对传入的输出流做了不同的装饰。以BufferedOutputStream类为例:
以下是代码片段:
public class BufferedOutputStream extends FilterOutputStream {
……
private void flushBuffer() throws IOException {
if (count 〉 0) {
out.write(buf, 0, count);
count = 0;
}
}
public synchronized void write(int b) throws IOException {
if (count 〉= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
……
}
这个类提供了一个缓存机制,等到缓存的容量达到一定的字节数时才写入输出流。首先它继承了FilterOutputStream,并且覆盖了父类的write(int b)方法,在调用输出流写出数据前都会检查缓存是否已满,如果未满,则不写。这样就实现了对输出流对象动态的添加新功能的目的。
下面,将使用Decorator模式,为IO写一个新的输出流。
自己写一个新的输出流
了解了OutputStream及其子类的结构原理后,我们可以写一个新的输出流,来添加新的功能。这部分中将给出一个新的输出流的例子,它将过滤待输出语句中的空格符号。比如需要输出"java io OutputStream",则过滤后的输出为"javaioOutputStream".以下为SkipSpaceOutputStream类的代码:
以下是代码片段:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* A new output stream, which will check the space character
* and won‘t write it to the output stream.
* @author Magic
*
*/
public class SkipSpaceOutputStream extends FilterOutputStream {
public SkipSpaceOutputStream(OutputStream out) {
super(out);
}
/**
* Rewrite the method in the parent class, and
* skip the space character.
*/
public void write(int b) throws IOException{
if(b!=‘ ’){
super.write(b);
}
}
}
它从FilterOutputStream继承,并且重写了它的write(int b)方法。在write(int b)方法中首先对输入字符进行了检查,如果不是空格,则输出。
以下是一个测试程序:
以下是代码片段:
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Test the SkipSpaceOutputStream.
* @author Magic
*
*/
public class Test {
public static void main(String[] args){
byte[] buffer = new byte[1024];
/**
* Create input stream from the standard input.
*/
InputStream in = new BufferedInputStream(new DataInputStream(System.in));
/**
* write to the standard output.
*/
OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out));
try {
System.out.println("Please input your words: ");
int n = in.read(buffer,0,buffer.length);
for(int i=0;i〈n;i++){
out.write(buffer[i]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行以上测试程序,将要求用户在console窗口中输入信息,程序将过滤掉信息中的空格,并将最后的结果输出到console窗口。比如:
以下是引用片段:
Please input your words:
a b c d e f
abcdef
总 结
在java.io包中,不仅OutputStream用到了Decorator设计模式,InputStream,Reader,Writer等都用到了此模式。而作为一个灵活的,可扩展的类库,JDK中使用了大量的设计模式,比如在Swing包中的MVC模式,RMI中的Proxy模式等等。对于JDK中模式的研究不仅能加深对于模式的理解,而且还有利于更透彻的了解类库的结构和组成。
我们都知道,当想要保存一组基本类型数据时,数组是最有效的保存方式,也是推荐使用这种方式的。但是数组是固有大小的,当运行时才知道大小的程序,这种方式使用就受限制了,这就是
Java容器类产生的原因。
Java集合类有几个特点:首先,这种容器是高性能的,对基本数据集合(动态数组、链接表、树和散列表)的实现是高效率的。第二,容器类允许不同类型的类集合以相同的方式和高度互操作方式工作。第三,容器类是容易扩展或修改的。容器类的常用的基本类型有List、Set和Map,这些对象类型也称为集合类,但是在Java中使用了Collection这个名字来指代该类库的一个特殊子集,所以业界使用了范围更广泛的“容器”来称呼。
Collection:是一个接口,它位于集合框架层次结构的顶层,继承自Iterable接口,说明是可以用Iterator迭代器来访问该集合中的元素的。又有List、Set和Queue接口继承Collection接口,直接实现该接口的是一个叫AbstractCollection的抽象类,该抽象类以最大限度地减少了实现此接口所需的工作。
List:继承自Collection接口,表示有序的、可包括重复元素的列表。同时拥有Collection内的方法外,还添加了大量的方法,使得可以在List的中间插入和删除元素。实现该接口的基本类有ArrayList和LinkedList. ArrayList:擅长于对元素的随机访问,但是在插入和删除元素时效率较慢。其实,看看ArrayList类实现的源代码就知道,ArrayList是以线性表的数据结构形式存取数据的,初始化的表大小为10,下面就有几个经常用到的核心方法:add(E e):在当前表的末尾插入元素,如果在前面表不满的情况下,也是很高效的,直接插入到末尾,但是如果在当前表已经满的情况下,就要重新生成一个比当前表大小更大的新表,新表的大小是当前表大小的1.5倍加1,比如当前表长度为20的,新表的大小就为31,还需要把当前表元素复制到新表中去,然后把当前表引用指向新表,最后把数值插入到表末尾,所以这种操作是非常低效的。
add(int index,E element):在指定索引位置插入元素,检查表大小和重新追加表大小和上面的add(E e)方式是一样的。最后是要把index以后的元素都是要依次往后移一个大小,然后把元素插入到index位置上去。涉及到表的复制和表内元素的移动,所以效率也是比add(E e)方法还要低。
remove(int index):在指定索引位置删除元素,就是把index位置后的所有元素都往前移一个大小,也是涉及到表内元素的移动,效率也是很低的。
remove(Object o):删除指定的元素,也就需要查找出该元素在表中出现第一次的位置,查找是用到顺序一个一个进行匹配的方法,找出后就把该元素后面的所有元素往前移一个大小。该方法涉及到顺序查找和表内元素移动,比remove(int index)方法更低效。
set(int index,E element):替换表中索引为index的元素值,返回被替换的值,直接用下标索引访问元素,所以效率非常高。
get(int index):获取索引为index的元素,直接用下标索引访问,所以效率也是非常高。
indexOf(Object o):获取元素的索引号,也就是需要查找,虽然用到了顺序查找法,但效率还是比较高的。
LinkedList:擅长于对元素的插入和删除操作,但对于随机访问元素比较慢。该类的实现是以双向链表的数据结构为基础的,所以是比较消耗内存的,但它的特定集比ArrayList更大。双向链表,每个节点都有三个域,两个域是存放前后节点的内存地址引用的,一个域是存放数据元素的。在LinkedList类中,有一个叫Entry的内部类,是private的,里面三个属性,分别是element、next和previous,分别对应了双向链表中的三个域,在ArrayList类中每实例化一个Entry就生成一个节点。下面看看它的核心方法:add(E e):把元素插入到链表末尾,首先要实例化一个节点,新节点previous域存放链表中最后一个节点地址,next域存放链表中第一个节点地址,element域存放元素值,链表中最后一个节点的next域存放新节点的地址,第一个元素的previous域存放新节点的地址,这样这个元素就插入到该链表中去了,没有涉及到复杂的操作,所以是非常高效的。
add(int index,E element):在index位置插入元素,这就需要先查找到该位置。查到后,这里就把查到的节点的前一个节点叫为A,实例化新的节点为B,查到index的节点为C.B的next域等于A的next值(也就是C的内存地址),B的previous域等于C的previous值(也就是A的内存地址),B的element域存放元素值,然后把A的next域和C的previous域都等于B的内存地址。这样也就把元素插入到链表的index位置中去了,但涉及到了查询,所以效率虽然高,但也没有add(E e)那么高。
remove(int index):删除在index位置的元素,首先也是要找到该位置的节点。然后把该节点的下一个节点(也就是该节点next域的内存地址那个节点)的previous值等于该节点的previous值,该节点的上一个节点(也就是该节点previous域的内存地址那个节点)的next值等于该节点的next值。这样就把该节点从这条链表删除了,过程中虽然涉及到了查找,但没有涉及到像ArrayList类中的remove方法要移动表中元素,所以该方法的效率还是很高的。
remove(Object o):删除在链表中第一个元素为o的节点,也是需要查找到该节点,然后就跟remove(int index)思路一样把元素删除,所以效率也是很高的。
set(int index,E element):把在链表中第index个元素值改为element,这也需要找到该节点来修改元素值,但涉及到了查找节点,ArrayList中的set方法就不用查找就可以修改,所以相对于ArrayList中的set方法,LinkedList方法set方法效率就没那么高了。
get(int index):获取第index位置的元素值,也是要找到该节点,所以就也没ArrayList中的get方法那么高效率了,因为该方法需要查找链表。
indexOf(Object o):获取该链表中第一o元素的位置,也是要查找链表,但ArrayList中的indexOf方法也是需要查找的,所以这两个类的indexOf的效率都差不多。
所以,在编程中,如果要进行大量的随机访问,就使用ArrayList;如果要经常从表中插入或删除元素的就应该使用LinkedList.
Set:继承自Collection接口,表示无序的,无重复元素的集合。Set中最常使用的是测试归属性,可以很容易测试某个对象是否在某个Set中。所以,查找就成为了Set中最重要的操作,因此通常会选择一个HashSet的实现查找,因为有比较复杂的哈希表支持,它专门对快速查找进行了优化。
迭代器:迭代器是一种设计模式,在这里是一个对象,它的作用就是遍历并选择列表和操作列表中的对象。迭代器的创佳的代价小,所以通常被称为轻量级对象。迭代器统一了对各种容器的访问方式,很方便。Java中的迭代器有两种,一种是Iterator,另一种是继承了Iterator只能用于各种List访问的ListIterator. Iterator:只能用于单向移动,方法有:iterator()要求容器返回一个Iterator,Iterator将准备好返回序列的第一元素。next()获得列表中的下一个元素。hasNext()检查列表中是否还有元素。remove()将迭代器新近返回的元素删除。
ListIterator:只能用于各种的List类的访问,但能用于双向的移动,有一个hasPrevious()检查时候有前一个元素的,这种操作很像数据库的游标。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class ListTest ...{
public static void main(String [] args)
...{
Collection<Integer> col = new ArrayList<Integer>(Arrays.asList(10,20,30));
List<Integer> list = new LinkedList<Integer>();
list.addAll(col);
list.add(40);
list.add(50);
list.add(60);
displayIterator(list);
list.remove(3);
displayListIterator(list);
}
public static void displayIterator(Collection<Integer> list)
...{
Iterator<Integer> it = list.iterator();
Integer i;
while(it.hasNext())
...{
i = it.next();
System.out.print(i + " ");
if(i==50)
...{
it.remove();
}
}
System.out.println();
}
public static void displayListIterator(List<Integer> list)
...{
ListIterator<Integer> li = list.listIterator();
/** *//**以下注释代码为死循环,永远输入表中的第一个数据*/
/** *//**while(li.hasNext())
{
System.out.println(li.next());
System.out.println(li.previous());
}*/
while(li.hasNext())
...{
System.out.print(li.next() + " ");
}
System.out.println();
while(li.hasPrevious())
...{
System.out.print(li.previous() + " ");
}
}
}
|
Map:也是一个映射存储键/值对的接口,但跟Collection没有任何关系的,也没有继承任何接口,所以不能用Iterator迭代器来访问该集合中的元素。给定一个关键字和一个值,可以存储这个值到一个Map对象中,存储以后,就可以使用它的关键字来检索它。映射经常使用到的两个基本操作:get()和put()。使用put()方法可以将一个指定了关键字和值的项加入映射。为了得到值,可以通过将关键字作为参数来调用get()方法。
import java.util.HashMap;
import java.util.Map;
public class TestMap ...{
public static void main(String [] args)
...{
Map<String,Integer> hm = new HashMap<String,Integer>();
hm.put("a1", 1);
hm.put("b2", 2);
hm.put("c3", 3);
hm.put("d4", 4);
hm.put("e5", 5);
display(hm);
System.out.println(hm.containsKey("c3"));
hm.remove("c3");
System.out.println(hm.containsValue(3));
System.out.println(hm.size());
}
public static void display(Map<String,Integer> m)
...{
for(String s : m.keySet())
...{
System.out.println(s + " : " + m.get(s));
}
}
}
|
线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的两个接口是List和Set。
List接口
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
LinkedList类
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类
ArrayList实现了可变大小的数组。
它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。
Stack 类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
Set接口
Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
Map接口
请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
Hashtable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));
要取出一个数,比如2,用相应的key:
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);
由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
Hashtable是同步的。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。