2008年3月19日
#
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回收。
在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。
Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:
class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();
其中ExampleInterface是Example的接口,可以写成如下形式:
String className = "Example";
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();
进一步可以写成如下形式:
String className = readfromXMlConfig;//从xml 配置文件中获得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();
上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface就可以。
从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:1、这个类已经加载;2、这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载java API的那个加载器。
现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。
最后用最简单的描述来区分new关键字和newInstance()方法的区别:
newInstance: 弱类型。低效率。只能调用无参构造。
new: 强类型。相对高效。能调用任何public构造。
引言
期待以久的EJB3.0规范在最近发布了它的初稿。在本文中将对新的规范进行一个概要性的介绍,包括新增的元数据支持,EJBQL的修改,实体Bean模型访问bean上下文的新方法和运行时环境等等。作者还讨论了EJB在未来要作出的调整以及EJB3.0与其他开发规范之间的关系。
开始
无论如何由于EJB的复杂性使之在J2EE架构中的表现一直不是很好。EJB大概是J2EE架构中唯一一个没有兑现其能够简单开发并提高生产力的组建。EJB3.0规范正尝试在这方面作出努力以减轻其开发的复杂性。EJB3.0减轻了开发人员进行底层开发的工作量,它取消或最小化了很多(以前这些是必须实现)回调方法的实现,并且降低了实体Bean及O/R映射模型的复杂性。
在本文中,我首先会介绍EJB3.0中几个主要的改变。它对进一步深入了解EJB3.0是非常重要的。随后,我会从更高的层面来描述已经被提交到EJB3.0规范中的细节,并一个个的讲解新的规范中的改变:实体Bean,O/R映射模型,实体关系模型和EJB QL(EJB查询语言)等等。
背景
EJB3.0中两个重要的变更分别是:使用了Java5中的程序注释工具和基于Hibernate的O/R映射模型。
Java5中的元数据工具
Java5(以前叫J2SE1.5或Tiger)中加入了一种新的程序注释工具。通过这个工具你可以自定义注释标记,通过这些自定义标记来注释字段、方法、类等等。这些注释并不会影响程序的语义,但是可以通过工具(编译时或运行时)来解释这些标记并产生附加的内容(比如部署描述文件),或者强制某些必须的运行时行为(比如EJB组件的状态特性)。注释的解析可以通过源文件的解析(比如编译器或这IDE工具)或者使用Java5中的APIs反射机制。注释只能被定义在源代码层。由于所有被提交到EJB3.0草案中的注释标记都有一个运行时的RetentionPolicy,因此会增加类文件占用的存储空间,但这却给容器制造商和工具制造商带来了方便。
Hibernate
目前Hibernate非常受欢迎,它是开发源代码的Java O/R映射框架,目的是把开发人员从繁琐的数据持久化编程中解脱出来。它也有一个标准的HQL(Hibernate 查询语言)语言,你可以在新的EJB QL中看到它的影子。Hibernate在处理如数据查询、更新、连接池、事务处理、实体关系处理等方面非常简单。
概览
在已经提交的EJB3.0规范中主要涉及两个方面的改变:
1. 一套以注释为基础的EJB编程模型,再加上EJB2.1中定义的通过部署描述符和几个接口定义的应用程序行为。
2. 新的实体Bean持久化模型,EJBQL也有许多重要的改变。
还有一些有关上述的提议,比如:一个新的客户端编程模型,业务接口的使用以及实体Bean的生命周期。请注意EJB2.1编程模型(包括部署描述符和home/remote接口)仍然是有效的。新的简化模型并没有完全取代EJB2.1模型。
EJB注释
EJB规范组织一个重要的目标是减轻原始代码的数量,并且他们为此给出了一个完美而简介的办法。在EJB3.0的里,任何类型的企业级Bean只是一个加了适当注释的简单Java对象(POJO)。注释可以用于定义bean的业务接口、O/R映射信息、资源引用信息,效果与在EJB2.1中定义部署描述符和接口是一样的。在EJB3.0中部署描述符不再是必须的了;home接口也没有了,你也不必实现业务接口(容器可以为你完成这些事情)。
比如,你可以使用@Stateless注释标记类把Java类声明为一个无状态会话bean。对于有状态会话bean来说,@Remove注释可以用来标记一个特定的方法,通过这个注释来说明在调用这个方法之后bean的实例将被清除掉。
为了减少描述组件的说明信息,规范组织还采纳了由异常进行配置(configuration-by-exception)的手段,意思是你可以为所有的注释提供一个明确的缺省值,这样多数常规信息就可以据此推断得出。
新的持久化模型
新的实体bean也是一个加了注释的简单Java对象(POJO)。一旦它被EntityManager访问它就成为了一个持久化对象,并且成为了持久化上下文(context)的一部分。一个持久化上下文与一个事务上下文是松耦合的;严格的讲,它隐含的与一个事务会话共存。
实体关系也是通过注释来定义的,O/R映射也是,并提供几种不同的数据库规范操作,在EJB2.1中这些要通过开发人员自己的设计模式或者其它技术来完成的(比如,自增长主键策略)。
深入研究
现在是时候详细了解EJB3.0草案了。让我们开始探讨所有EJB中四种企业级bean,并看看他们在新的规范中是什么样子。
无状态会话bean
在EJB3.0规范中,写一个无状态会话bean(SLSB)只需要一个简单的Java文件并在类层加上@Stateless注释就可以了。这个bean可以扩展javax.ejb.SessionBean接口,但这些不是必须的。
一个SLSB不再需要home接口,没有哪类EJB再需要它了。Bean类可以实现业务接口也可以不实现它。如果没有实现任何业务接口,业务接口会由任意public的方法产生。如果只有几个业务方法会被暴露在业务接口中,这些方法可以使用@BusinessMethod注释。缺省情况下所有产生的接口都是local(本地)接口,你也可以使用@Remote注释来声明这个接口为remote(远程)接口。
下面的几行代码就可以定义一个HelloWorldbean了。而在EJB2.1中同样的bean至少需要两个接口,一个实现类和几个空的实现方法,再加上部署描述符。
mport javax.ejb.*;
/**
* A stateless session bean requesting that a remote business
* interface be generated for it.
*/
@Stateless
@Remote
public class HelloWorldBean {
public String sayHello() {
return "Hello World!!!";
}
}
有状态会话bean
除了几个SFSB的特别说明之外,有状态会话bean(SFSB)和SLSB一样精简:
1) 一个SFSB应该有一个方法来初始化自己(在EJB2.1中是通过ejbCreate()来实现的)。在EJB3.0的规范中建议这些初始化操作可以通过自定义方法完成,并把他们暴露在业务接口中。在使用这个bean之前由客户端来调用相应的初始化方法。目前规范组织就是否提供一个注释来标记某个方法用于初始化还存在争议。
2) Bean的提供者可以用@Remove注释来标记任何SFSB的方法,以说明这个方法被调用之后bean的实例将被移除。同样,规范组织仍然在讨论是否要有一种机制来处理这种特殊的情况,即当这个方法出现异常的情况下bean的实例是否被移除。
下面是对以上问题我个人的观点:
1) 是否应该有一个注释来标明一个方法进行初始化呢?我的观点是――应该有,这样容器就可以在调用其他方法之前至少调用一个方法来进行初始化。这不仅可以避免不必要的错误(由于没有调用初始化方法)而且可以使容器更明确的判断是否可以重用SFSB实例。我暂且把这个问题放一放,规范组织只考虑为一个方法提供一个注释来声明它是一个初始化方法。
2) 对于第二个问题我的观点也是肯定的。这有利于Bean的提供者合客户端程序对其进行控制。只有一个遗留的问题:那就是一旦调用这个方法失败,是否能移除这个bean 的实例?答案是不能,但是它将会在会话结束的时候被移除。
消息驱动Bean
消息驱动Bean是唯一一种必须实现一个业务接口的Bean。这个接口指出bean支持的是哪一种消息系统。对于以JMS为基础的MDB来说,这个接口是javax.jms.MessageListener。注意MDB业务接口不是一个真正意义上的业务接口,它只是一个消息接口。
实体Bean
1) 实体Bean使用@Entity注释来标记,所有实体bean中的属性/字段不必使用@Transient注释来标记。实体bean的持久化字段可以通过JavaBean-style机制或者声明为public/protected字段来实现。
2) 实体bean可以使用助手类来描述其状态,但是这些类的实例并没有持久化唯一性(persistent identity)的特性(即,唯一标识这个bean的字段等),实际上这些助手类与他们的实体bean实例是紧密结合的;并且这些对象还是以非共享方式来访问实体对象的。
实体关联
EJB3.0同时支持Bean之间双向的合单向的关联,它们可以是一对一、一对多、多对一或者是多对多的关联。然而双向关联的两端还要分为自身端(owning side)和对方端(inverse side)不同的端。自身端负责向数据库通告关联的变更。对于多对多的关联自身端必须明确的声明。实际上对方端通过isInverse=true进行注释(由此自身端就不必说明了而是由另一段推断出)。看来上面的描述,规范组织还能说让EJB变的简单了吗?
O/R映射
EJB3.0中的O/R映射模型也有了重要的改变,它从原来的abstract-persistence-schema-based变成了现在的Hibernate-inspired模式。尽管目前规范组织还在就此进行讨论但是一个明确的模型将会出现在下一个版本的草案中。
举例来说,O/R映射模型将通过bean类中的注释来声明。而且此方法还会指出对应的具体表和字段。O/R映射模型提供了一套自有的SQL;而且除了提供一些基本的SQL外还支持某些高层开发的功能。比如,有一个通过@Column注释声明的字段columnDefinition,那么可以写这样的SQL:columnDefinition="BLOB NOT NULL"
客户端程序模型
一个EJB客户端可以通过@Inject注释以一种“注入”的方式获得一个bean的业务接口引用。你也可以使用另一个注释@javax.ejb.EJBContext.lookup()来完成上面的操作,但是规范中没有告诉我们一个普通的Java客户端怎样获得一个Bean的实例,因为这个普通的Java客户端是运行在一个客户端容器中,它无法访问@javax.ejb.EJBContex对象。现在还有另外一种机制来完成上面的工作那就是使用一个超级上下文环境对象:@javax.ejb.Context()。但是规范中没有指出该如何在客户端中使用这个对象。
EJB QL
EJB QL可以通过@NamedQuery来注释。这个注释有两个成员属性分别是name和queryString.一旦定义了这些属性,就可以通过EntityManager.createNamedQuery(name)来指向这个查询。你也可以创建一个标准的JDBC风格的查询并使用EntityManager.createQuery(ejbqlString)或EntityManager.createNativeQuery(nativeSqlString)(这个方法用于执行一个本地查询)来执行查询。
EJB QL有两个地方可以定义其参数。javax.ejb.Query接口提供了定义参数、指向查询、更新数据等等方法。下面是一个EJBQL指向查询的例子:
.. ..
@NamedQuery(
name="findAllCustomersWithName",
queryString="SELECT c FROM Customer c WHERE c.name LIKE :custName"
)
.. ..
@Inject public EntityManager em;
customers = em.createNamedQuery("findAllCustomersWithName")
.setParameter("custName", "Smith")
.listResults();
下面列出了一些EJB QL的增强特性:
1) 支持批量更新和删除。
2) 直接支持内连接和外连接。FETCH JOIN运行你指出关联的实体,Order可以指定只查询某个字段。
3) 查询语句可以返回一个以上的结果值。实际上,你可以返回一个依赖的类比如下面这样:
SELECT new CustomerDetails(c.id, c.status, o.count)
FROM Customer c JOIN c.orders o
WHERE o.count > 100
4) 支持group by 和having。
5) 支持where子句的嵌套子查询。
在提交的EJB3.0草案中,EJB QL与标准SQL非常的接近。实际上规范中甚至直接支持本地的SQL(就像我们上面提到的那样)。这一点对某些程序员来说也许有些不是很清楚,我们将在下面进行更详细的讲解。
多样性
方法许可(Method permissions)可以通过@MethodPermissions或@Unchecked注释来声明;同样的,事务属性也可以通过@TransactionAttribute注释来声明。规范中仍然保留资源引用和资源环境引用。这些一样可以通过注释来声明,但是有一些细微的差别。比如,上下文(context)环境要通过注入工具控制。容器根据bean对外部环境引用自动初始化一个适当的已经声明的实例变量。比如,你可以象下面这样获得一个数据源(DataSource):
@Resource(name="myDataSource") //Type is inferred from variable
public DataSource customerDB;
在上面的例子中如果你不指定引用资源的名称(name)那么其中的customerDB会被认为是默认值。当所有的引用属性都可得到时,@Injec注释就可以这样写:
@Inject public DataSource customerDB;
容器负责在运行时初始化customerDB数据源实例。部署人员必须在此之前在容器中定义好这些资源属性。
更好的消息是:那些以前必须检测的异常将一去不复返。你可以声明任意的应用程序异常,而不必在再抛出或捕获其他类似CreateException和FinderException这样的异常。容器会抛出封装在javax.ejb.EJBException中的系统级异常或者只在必要时候抛出IllegalArgumentException或IllegalStateException异常。
EJB文件处理模式
在我们结束本节之前,让我的快速的浏览一下容器提供商在EJB处理模式方面可能的变更。规范中对此并没有明确的表态,但我可以想到至少两种模式。
1) 一种办法是首先利用EJB文件生成类似于EJB2.1部署模式的文件(包括必要的接口和部署描述符)然后再用类似于EJB2.1的方式来部署这个EJB组件。当然,这样产生的部署描述符可能并不标准但是它可以解决同一个容器对EJB2.1和EJB3.0兼容的问题。
2) 另一种方法是一种类似于JSP托放的部署模式。你可以把一个EJB文件放到一个预先定义的目录下,然后容器会识别这个EJB并处理它,然后部署并使之可以使用。这种方法可以建立于上面那种方法之上,在支持反复部署时有很大的帮助。考虑到部署的简单性也是EJB3.0规范的目的之一,我真诚的希望在下一个草案出来时能够确定一个模式(至少能有一个非正式的)。
你有什么想法?
EJB3.0规范的制定正在有序的进行,为了使EJB的开发变得更加容易,EJB规范组织作出的努力是有目共睹的。就像他们说的那样,一切对会变得简单,但做到这一点并不容易。目前已经定义了50个注释标记(还有几个将在下一个草案中发布),每一个都有自己的缺省规则和其他的操作。当然,我真的不希望EJB3.0变成EJB2.1的一个翻版"EJB 3.0 = EJB 2.1 for dummies"(希望这个等式不要成立)。最后,我还是忍不住要提一些我自己的观点:
1) 首先,规范确实使反复部署变得容易了,并且有一个简单的模式来访问运行时环境。我还是觉得home接口应该放弃。
2) 在早期的EJB规范中,实体bean用于映射一个持久化存储。理论上(也许只是理论上)可能需要把实体bean映射到一个遗留的EIS(enterprise information system)系统中。出于将来扩展的考虑这样作是有好处的,并且可以使更多的业务数据模型采用实体bean。也因此其伴随的复杂性使得实体bean不被看好。在本次提交的草案中,一个实体bean只是一个数据库的映射。并且是基于非抽象持久化模式和简单的数据访问模式的更加简单开发。
3) 我对模型变更持保留态度,我认为在EJB中包含SQL脚本片断并不是个好注意。一些开发人员完全反对包含某些“SQL片段(SQLness)”(比如@Table 和 @Column注释)。我的观点是这些SQLness是好的,据此我们可以清楚的知道我们到底要数据库作些什么。但是某些SQL段我看来并不是很好,比如columnDefinition="BLOB NOT NULL",这使得EJB代码和SQL之间的耦合太过紧密了。
4) 尽管对于本地SQL的支持看似很诱人,其实在EJB代码中嵌入SQL是一个非常糟糕的主意。当然,有些办法可以避免在EJB中硬编码SQL,但是这应该在规范中说明,而不能是某些开发人员自己定义的模式。
5) 假设@Table注释只用于类。在运行时通过@Table注释的name属性定义的表名称将必须对应一个实际的数据库表。规范对此应该给予清楚的说明和一致的模式。
6) 规范还需要更清楚的说明客户端编程模型,尤其是普通java客户端。规范中所有的参考都假设或者隐含的使用EJB客户端。而且规范中对客户端的向后兼容方面也没有给出明确的说法。
7) Transient注释应该重新命名以避免和已有的transient关键字发生冲突。事实上,在这一点上我们更乐于稍微的背离一下configuration-by-exception原则并且定义一个@Persistent注释来明确的定义持久化字段。@Persistent注释可以仅仅是一个标记注释或者它可以有几个属性来关联O/R映射注释。
与其他规范的关联
目前可能影响到EJB3.0的JSR有JSR175(java语言元数据工具)和JSR181(Java Web服务元数据)
JSR175已经初步完成并且不会和EJB3.0有太大的冲突;但是JSR181与EJB3.0有两个关联的地方:
1) Web service接口:EJB规范将采用一种机制适应JSR181以便可以把一个bean实现为一个Web service并告诉Web service如何被客户端调用。
2) JSR 181计划采用不同的机制来处理安全问题。在早期的规范中EJB建议使用一个一致的机制(MethodPermissions),但是JSR 181计划使用一个稍微不同的方式(SecurityRoles和SecurityIdentity注释)。同样的RunAs注释的定义也存在这些许差别。这一问题还在解决中最终会在J2EE层的规范中维持其一致性。
在J2EE 1.5中的一些开发规范可能与EJB3.0有关联。除了上面说到的几个关联之外现在没有其他的开发规范与EJB3.0有冲突。
结束语
在使EJB的开发变得简单高效之前,我们还有很长一段路要走。规范组织在降低EJB的开发难度方面起了个好头。O/R映射模型的提议还处在早期阶段,规范组织正在完善它。我希望它不要太复杂也不要与SQL过分的耦合。让我们不要只是停留在期望、希望、思考和请求中:提出你的想法并把你的建议发送给规范组织ejb3-feedback@sun.com。JCP并不是很民主的组织,但是你的建议一定是有价值的。
摘要:
Design Patterns: Elements of Reusable Object-Oriented Software(即后述《设计模式》一书),由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著(Addison-Wesley,1995)。这几位...
阅读全文
单一职责原则SRP:Single Responsibility Principle
开放封闭原则OCP:Open-Close Principle
Liskov替换原则LSP:Liskov Substitution Principle
依赖倒置原则DIP:Dependency Invertion Principle
接口隔离原则ISP:Interface Separate Principle
在面向对象设计中,如何通过很小的设计改变就可以应对设计需求的变化,这是令设计者极为关注的问题。为此不少OO先驱提出了很多有关面向对象的设计原则用于指导OO的设计和开发。下面是几条与类设计相关的设计原则。
1.开闭原则(the Open Closed Principle OCP)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。因此在进行面向对象设计时要尽量考虑接口封装机制、抽象机制和多态技术。该原则同样适合 于非面向对象设计的方法,是软件工程设计方法的重要原则之一。我们以收音机的例子为例,讲述面向对象的开闭原则。我们收听节目时需要打开收音机电源,对准 电台频率和进行音量调节。但是对于不同的收音机,实现这三个步骤的细节往往有所不同。比如自动收缩电台的收音机和按钮式收缩在操作细节上并不相同。因此, 我们不太可能针对每种不同类型的收音机通过一个收音机类来实现(通过重载)这些不同的操作方式。但是我们可以定义一个收音机接口,提供开机、关机、增加频 率、降低频率、增加音量、降低音量六个抽象方法。不同的收音机继承并实现这六个抽象方法。这样新增收音机类型不会影响其它原有的收音机类型,收音机类型扩 展极为方便。此外,已存在的收音机类型在修改其操作方法时也不会影响到其它类型的收音机。
2.替换原则 (the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。这个原则是Liskov于1987年提出的设计原则。它同样可以从Bertrand Meyer 的DBC (Design by Contract) 的概念推出。
我们以学生为例,夜校生为学生的子类,因此在任何学生可以出现的地方,夜校生均可出现。这个例子有些牵强,一个能够反映这个原则的例子时圆和椭圆,圆是椭圆的一个特殊子类。因此任何出现椭圆的地方,圆均可以出现。但反过来就可能行不通。
运用替换原则时,我们尽量把类B设计为抽象类或者接口,让C类继承类B(接口B)并实现操作A和操作B,运行时,类C实例替换B,这样我们即可进行新类的扩展(继承类B或接口B),同时无须对类A进行修改。
3.依赖原则 (the Dependency Inversion Principle DIP)
在进行业务设计时,与特定业务有关的依赖关系应该尽量依赖接口和抽象类,而不是依赖于具体类。具体类只负责相关业务的实现,修改具体类不影响与特定业务有关的依赖关系。
在结构化设计中,我们可以看到底层的模块是对高层抽象模块的实现(高层抽象模块通过调用底层模块),这说明,抽象的模块要依赖具体实现相关的模块,底层模块的具体实现发生变动时将会严重影响高层抽象的模块,显然这是结构化方法的一个"硬伤"。
面向对象方法的依赖关系刚好相反,具体实现类依赖于抽象类和接口。
为此,我们在进行业务设计时,应尽量在接口或抽象类中定义业务方法的原型,并通过具体的实现类(子类)来实现该业务方法,业务方法内容的修改将不会影响到运行时业务方法的调用。
4.接口分离原则(the Interface Segregation Principle ISP)
采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。
ISP原则是另外一个支持诸如COM等组件化的使能技术。缺少ISP,组件、类的可用性和移植性将大打折扣。
这个原则的本质相当简单。如果你拥有一个针对多个客户的类,为每一个客户创建特定业务接口,然后使该客户类继承多个特定业务接口将比直接加载客户所需所有方法有效。
以上四个原则是面向对象中常常用到的原则。此外,除上述四原则外,还有一些常用的经验诸如类结构层次以三到四层为宜、类的职责明确化(一个类对应一个具体职 责)等可供我们在进行面向对象设计参考。但就上面的几个原则看来,我们看到这些类在几何分布上呈现树型拓扑的关系,这是一种良好、开放式的线性关系、具有 较低的设计复杂度。一般说来,在软件设计中我们应当尽量避免出现带有闭包、循环的设计关系,它们反映的是较大的耦合度和设计复杂化。
oracle 存储过程的基本语法
1.基本结构
CREATE OR REPLACE PROCEDURE 存储过程名字
(
参数1 IN NUMBER,
参数2 IN NUMBER
) IS
变量1 INTEGER :=0;
变量2 DATE;
BEGIN
END 存储过程名字
2.SELECT INTO STATEMENT
将select查询的结果存入到变量中,可以同时将多个列存储多个变量中,必须有一条
记录,否则抛出异常(如果没有记录抛出NO_DATA_FOUND)
例子:
BEGIN
SELECT col1,col2 into 变量1,变量2 FROM typestruct where xxx;
EXCEPTION
WHEN NO_DATA_FOUND THEN
xxxx;
END;
...
3.IF 判断
IF V_TEST=1 THEN
BEGIN
do something
END;
END IF;
4.while 循环
WHILE V_TEST=1 LOOP
BEGIN
XXXX
END;
END LOOP;
5.变量赋值
V_TEST := 123;
6.用for in 使用cursor
...
IS
CURSOR cur IS SELECT * FROM xxx;
BEGIN
FOR cur_result in cur LOOP
BEGIN
V_SUM :=cur_result.列名1+cur_result.列名2
END;
END LOOP;
END;
7.带参数的cursor
CURSOR C_USER(C_ID NUMBER) IS SELECT NAME FROM USER WHERE TYPEID=C_ID;
OPEN C_USER(变量值);
LOOP
FETCH C_USER INTO V_NAME;
EXIT FETCH C_USER%NOTFOUND;
do something
END LOOP;
CLOSE C_USER;
8.用pl/sql developer debug
连接数据库后建立一个Test WINDOW
在窗口输入调用SP的代码,F9开始debug,CTRL+N单步调试
9、注意事项
1.在oracle中,数据表别名不能加as,如:
select a.appname from appinfo a;-- 正确
select a.appname from appinfo as a;-- 错误
也许,是怕和oracle中的存储过程中的关键字as冲突的问题吧
2.在存储过程中,select某一字段时,后面必须紧跟into,如果select整个记录,利用游标的话就另当别论了。
select af.keynode into kn from APPFOUNDATION af where af.appid=aid and af.foundationid=fid;-- 有into,正确编译
select af.keynode from APPFOUNDATION af where af.appid=aid and af.foundationid=fid;-- 没有into,编译报错,提示:Compilation
Error: PLS-00428: an INTO clause is expected in this SELECT statement
3.在利用select...into...语法时,必须先确保数据库中有该条记录,否则会报出"no data found"异常。
可以在该语法之前,先利用select count(*) from 查看数据库中是否存在该记录,如果存在,再利用select...into...
4.在存储过程中,别名不能和字段名称相同,否则虽然编译可以通过,但在运行阶段会报错
select keynode into kn from APPFOUNDATION where appid=aid and foundationid=fid;-- 正确运行
select af.keynode into kn from APPFOUNDATION af where af.appid=appid and af.foundationid=foundationid;-- 运行阶段报错,提示
ORA-01422:exact fetch returns more than requested number of rows
5.在存储过程中,关于出现null的问题
6.Hibernate调用oracle存储过程
this.pnumberManager.getHibernateTemplate().execute(
new HibernateCallback() ...{
public Object doInHibernate(Session session)
throws HibernateException, SQLException ...{
CallableStatement cs = session
.connection()
.prepareCall("{call modifyapppnumber_remain(?)}");
cs.setString(1, foundationid);
cs.execute();
return null;
}
});
删除重复记录:
办法一:
DELETE FROM TAB1 A
WHERE A.ROWID > ( SELECT MIN(B.ROWID) FROM TAB1 B WHERE A.字段=B.字段 );
其中子查询中的“WHERE A.字段=B.字段”用来写明重复条件。
——这一办法在数据记录超过10万时一般都会变得很慢。
办法二:
--建立临时表,--清空原表,--插回原表,如下例:
create table temp_emp as (select distinct * from employee) ;
truncate table employee;
insert into employee select * from temp_emp;
——这一办法适用于较大的表的情况。因为是块操作,对应于大表效率会好很多。
一.数据控制语句 (DML) 部分
1.INSERT (往数据表里插入记录的语句)
INSERT INTO 表名(字段名1, 字段名2, ……) VALUES ( 值1, 值2, ……);
INSERT INTO 表名(字段名1, 字段名2, ……) SELECT (字段名1, 字段名2, ……) FROM 另外的表名;
字符串类型的字段值必须用单引号括起来, 例如: ’GOOD DAY’
如果字段值里包含单引号’ 需要进行字符串转换, 我们把它替换成两个单引号''. 字符串类型的字段值超过定义的长度会出错, 最好在插入前进行长度校验.
日期字段的字段值可以用当前数据库的系统时间SYSDATE, 精确到秒或者用字符串转换成日期型函数TO_DATE(‘2001-08-01’,’YYYY-MM-DD’) TO_DATE()还有很多种日期格式, 可以参看ORACLE DOC. 年-月-日 小时:分钟:秒 的格式YYYY-MM-DD HH24:MI:SS
INSERT时最大可操作的字符串长度小于等于4000个单字节, 如果要插入更长的字符串, 请考虑字段用CLOB类型,方法借用ORACLE里自带的DBMS_LOB程序包.
INSERT时如果要用到从1开始自动增长的序列号, 应该先建立一个序列号CREATE SEQUENCE 序列号的名称 (最好是表名+序列号标记) INCREMENT BY 1 START WITH 1
MAXVALUE 99999 CYCLE NOCACHE;
其中最大的值按字段的长度来定, 如果定义的自动增长的序列号 NUMBER(6) , 最大值为999999
INSERT 语句插入这个字段值为: 序列号的名称.NEXTVAL
2.DELETE (删除数据表里记录的语句)
DELETE FROM表名 WHERE 条件;
注意:删除记录并不能释放ORACLE里被占用的数据块表空间. 它只把那些被删除的数据块标成unused.
如果确实要删除一个大表里的全部记录, 可以用 TRUNCATE 命令, 它可以释放占用的数据块表空间
TRUNCATE TABLE 表名;
此操作不可回退.
3.UPDATE (修改数据表里记录的语句)
UPDATE表名 SET 字段名1=值1, 字段名2=值2, …… WHERE 条件;
如果修改的值N没有赋值或定义时, 将把原来的记录内容清为NULL, 最好在修改前进行非空校验; 值N超过定义的长度会出错, 最好在插入前进行长度校验..
注意事项:
A. 以上SQL语句对表都加上了行级锁,确认完成后, 必须加上事物处理结束的命令 COMMIT 才能正式生效, 否则改变不一定写入数据库里. 如果想撤回这些操作, 可以用命令 ROLLBACK 复原.
B. 在运行INSERT, DELETE 和 UPDATE 语句前最好估算一下可能操作的记录范围, 应该把它限定在较小 (一万条记录) 范围内,. 否则ORACLE处理这个事物用到很大的回退段. 程序响应慢甚至失去响应. 如果记录数上十万以上这些操作, 可以把这些SQL语句分段分次完成,
其间加上COMMIT 确认事物处理.
二.数据定义 (DDL) 部分
1.CREATE (创建表, 索引, 视图, 同义词, 过程, 函数, 数据库链接等)
ORACLE常用的字段类型有
CHAR 固定长度的字符串
VARCHAR2 可变长度的字符串
NUMBER(M,N) 数字型M是位数总长度, N是小数的长度
DATE 日期类型
创建表时要把较小的不为空的字段放在前面, 可能为空的字段放在后面创建表时可以用中文的字段名, 但最好还是用英文的字段名
创建表时可以给字段加上默认值, 例如 DEFAULT SYSDATE这样每次插入和修改时, 不用程序操作这个字段都能得到动作的时间
创建表时可以给字段加上约束条件
例如 不允许重复 UNIQUE, 关键字 PRIMARY KEY
2.ALTER (改变表, 索引, 视图等)
改变表的名称
ALTER TABLE 表名1 TO 表名2;
在表的后面增加一个字段
ALTER TABLE表名 ADD 字段名 字段名描述;
修改表里字段的定义描述
ALTER TABLE表名 MODIFY字段名 字段名描述;
给表里的字段加上约束条件
ALTER TABLE 表名 ADD CONSTRAINT 约束名 PRIMARY KEY (字段名);
ALTER TABLE 表名 ADD CONSTRAINT 约束名 UNIQUE (字段名);
把表放在或取出数据库的内存区
ALTER TABLE 表名 CACHE;
ALTER TABLE 表名 NOCACHE;
3.DROP (删除表, 索引, 视图, 同义词, 过程, 函数, 数据库链接等)
删除表和它所有的约束条件
DROP TABLE 表名 CASCADE CONSTRAINTS;
4.TRUNCATE (清空表里的所有记录, 保留表的结构)
TRUNCATE 表名;
ORACLE 常用的SQL语法和数据对象
作者:网络 佚名 更新时间:2005-08-20 收藏此页
三.查询语句 (SELECT) 部分
SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] WHERE 条件;
字段名可以带入函数
例如: COUNT(*), MIN(字段名), MAX(字段名), AVG(字段名), DISTINCT(字段名), TO_CHAR(DATE字段名,'YYYY-MM-DD HH24:MI:SS')
NVL(EXPR1, EXPR2)函数
解释:
IF EXPR1=NULL
RETURN EXPR2
ELSE
RETURN EXPR1
DECODE(AA﹐V1﹐R1﹐V2﹐R2....)函数
解释:
IF AA=V1 THEN RETURN R1
IF AA=V2 THEN RETURN R2
..…
ELSE
RETURN NULL
LPAD(char1,n,char2)函数
解释:
字符char1按制定的位数n显示,不足的位数用char2字符串替换左边的空位
字段名之间可以进行算术运算
例如: (字段名1*字段名1)/3
查询语句可以嵌套
例如: SELECT …… FROM
(SELECT …… FROM表名1, [表名2, ……] WHERE 条件) WHERE 条件2;
两个查询语句的结果可以做集合操作
例如: 并集UNION(去掉重复记录), 并集UNION ALL(不去掉重复记录), 差集MINUS, 交集INTERSECT
分组查询
SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] GROUP BY字段名1
[HAVING 条件] ;
两个以上表之间的连接查询
SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] WHERE
表名1.字段名 = 表名2. 字段名 [ AND ……] ;
SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] WHERE
表名1.字段名 = 表名2. 字段名(+) [ AND ……] ;
有(+)号的字段位置自动补空值
查询结果集的排序操作, 默认的排序是升序ASC, 降序是DESC
SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……]
ORDER BY字段名1, 字段名2 DESC;
字符串模糊比较的方法
INSTR(字段名, ‘字符串’)>0
字段名 LIKE ‘字符串%’ [‘%字符串%’]
每个表都有一个隐含的字段ROWID, 它标记着记录的唯一性.
四.ORACLE里常用的数据对象 (SCHEMA)
1.索引 (INDEX)
CREATE INDEX 索引名ON 表名 ( 字段1, [字段2, ……] );
ALTER INDEX 索引名 REBUILD;
一个表的索引最好不要超过三个 (特殊的大表除外), 最好用单字段索引, 结合SQL语句的分析执行情况, 也可以建立多字段的组合索引和基于函数的索引
ORACLE8.1.7字符串可以索引的最大长度为1578 单字节
ORACLE8.0.6字符串可以索引的最大长度为758 单字节
2.视图 (VIEW)
CREATE VIEW 视图名AS SELECT …. FROM …..;
ALTER VIEW视图名 COMPILE;
视图仅是一个SQL查询语句, 它可以把表之间复杂的关系简洁化.
3.同义词 (SYNONMY)
CREATE SYNONYM同义词名FOR 表名;
CREATE SYNONYM同义词名FOR 表名@数据库链接名;
4.数据库链接 (DATABASE LINK)
CREATE DATABASE LINK数据库链接名CONNECT TO 用户名 IDENTIFIED BY 密码 USING ‘数据库连接字符串’; 数据库连接字符串可以用NET8 EASY CONFIG或者直接修改TNSNAMES.ORA里定义.
数据库参数global_name=true时要求数据库链接名称跟远端数据库名称一样
数据库全局名称可以用以下命令查出
SELECT * FROM GLOBAL_NAME;
查询远端数据库里的表
SELECT …… FROM 表名@数据库链接名;
五.权限管理 (DCL) 语句
1.GRANT 赋于权限
常用的系统权限集合有以下三个:
CONNECT(基本的连接), RESOURCE(程序开发), DBA(数据库管理)
常用的数据对象权限有以下五个:
ALL ON 数据对象名, SELECT ON 数据对象名, UPDATE ON 数据对象名DELETE ON 数据对象名, INSERT ON 数据对象名, ALTER ON 数据对象名
GRANT CONNECT, RESOURCE TO 用户名;
GRANT SELECT ON 表名 TO 用户名;
GRANT SELECT, INSERT, DELETE ON表名 TO 用户名1, 用户名2;
2.REVOKE 回收权限
REVOKE CONNECT, RESOURCE FROM 用户名;
REVOKE SELECT ON 表名 FROM 用户名;
REVOKE SELECT, INSERT, DELETE ON表名 FROM 用户名1, 用户名2;
在java中的java.applet包中,包含三个接口和一个类。
三个接口分别是:
AppletContext:该接口对应Applet的运行环境,包含Applet的文档以及和它在同一个文档中的其他Applet,具体就是装入Applet的浏览器和appliviewer环境。此接口中的方法可以使applet得到关于它环境方面的信息。
Appletstub:该接口当applet第一次被创建的时候,使用Applet的setStub方法(public final void
setStub(
AppletStub stub))把applet stub连接到它,此stub充当了Applet和浏览器或appletviewer环境之间的接口,应用程序在此环境中运行。
AudioClip:该接口是用于播放音频剪辑的简单抽象。多个AudioClip项能够同时播放,得到的声音混合在一起可产生合成声音。它有三个方法loop、play、stop
Applet是一种不适合单独运行但可嵌入到其他程序中运行的小应用程序。它的直接父类是java.awt.panel类。
第一章 设计模式概述
设计模式能够使解决方案既优雅简单,又可复用。设计模式仅仅是一些项目之间和程序员之间面向对象代码的简便方法,设计模式背后的思想很简单:对通用的对象间的相互作用方式进行记录和编目(程序员经常发现这些对象见的相互终于哦用方式很有用)。换言之,设计模式描述了对象如何进行通信才能不牵扯相互的数据模式和方法,保持这种独立性一直是一个好的面向对象程序设计的目标。从而可以实现代码的可复用性。
1. 设计模式
设计模式是对读者经常遇到的设计问题的可再现的解决方案。
设计模式建立了一系列描述如何完成软件开发领域中特定任务的规则。
设计模式更关注于复用和重复出现的结构设计方案,而框架注重于具体的设计和实现。
模式提出了一个发生在特定设计环境中的可重复出现的设计问题,并提供了解决方案
模式识别并确定类和实例层次上或组件层次上的抽象关系
设计模式有三种类型
创建型模式是创建对象而不是直接实例化对象,这会使程序在判断给定情况下创建那一个对象时更灵活。
结构型模式可以将一组对象组合成更大的结构,例如复杂的用户界面和报表数据
行为型模式定义系统内对象间的通信,以及复杂程序中的流程控制
2. 关于面向对象方法
封装和继承能让程序员达到分离类的目的,一个继承父类的类能访问父类所有方法和所有非私有变量,但是如果一个功能完整的类开始继承层次结构,可能过多的限制住了自己,也会给特定的实现带去累赘,设计模式建议遵循下列原则:针对接口编程,而不是针对实现编程。即在任何类层次结构的顶端,定义的是一个抽象类或一个接口,他没有实现方法,但是定义了该类需要支持的方法,这样在所有的派生类中,就会有更大的自由度去实现这些方法,能最大限度地满足你的要求。
对象组合,这是一种可以包含其他对象的对象结构,即把几个对象封装在另一个对象中,当编写比较复杂的程序,对象组合具有一些优势,新的对象拥有一个最适合于要完成的目标的接口,而不是拥有父类中的所有方法,说明了编程的第二个原则:优先使用对象组合,而不是继承。
C#基础知识
C#、java和vb的区别
C#和Java都是区别大小写的;C#中的每一条语句都已分号结束;而vb是不区分大小写的,
C#中的修饰符const的含义是:被命名的值是一个常数,不能更改。
数据类型
基本数据类型的长度与计算机或操作系统的类型无关。
宽度窄的数据类型可以直接赋给较宽的数据类型,并能自动转化为新类型。
可以通过强制转化把较宽的类型缩减成较窄的类型,这种转化需要把数据类型的名称放在圆括号内。
Java和c#的布尔变量只能接受保留字true和false所表示的值,与c和c++不同,不能将数值型数据赋给布尔型变量,也不能在布尔类型和其他数据类型之间转化。
数值与字符串间的转化,可以使用Convert类的方法实现数值转化成字符串或将字符串转化成数值。例如:
String s=Convert.ToString(x);
Float y=Convert.ToSingle(s); //single代表一个单精度浮点数
数值型对象也提供了各种格式话方法类指定小数位数;
Float x=12.3453234f;
String s=x.ToString(“###.###”); //gives 12.345
注意最后一位进行四舍五入
C#和java允许在一条语句里声明同一类型的多个变量
数值型常量,任何数如果没有小数部分的话,就自动为整数类型,如果有小数部分 ,就自动为double类型,如果想指定成不同的类型,可以使用各种前缀和后缀字符。
C#中有三个保留字常量,true、false和null,null表示一个对象变量还没有 指定任何对象。
字符常量
用但引号把字符括起来表示字符常量,转意字符和c、java中约定的一样。
C#和java的区别
1. 许多系统对象方法都有相同的方法名,只是在大小写形式上有区别
2. C#不提供throws关键字,该关键字使编译器检查你是否铺货了一个方法抛出的异常。
3. C#对于布局管理器有更多的限制,因为他是以windows系统为中心的,大多数时候采用的图形元素的绝对位置。
4. C#允许运算符重载
5. C#引进了代理和索引器
6. C#有枚举类型
7. C#有不安全模式,在这种模式下可以使用指针
8. 必须专门声明一个方法能被覆盖及一个方法能覆盖另一个方法
9. 不能通过声明来区别继承和接口实现,他们的声明方式是一样的
10. Switch语句允许使用字符串变量,如果变量没有被匹配,必须有一个默认情况,否则会出现错误,break语句是必须的。
11. 布尔值变量类型在C#中拼为“bool”,而java中拼为“boolean”
C#和C的区别
1. C#通常不能使用指针
2. 可以在一个方法里的任何位置声明变量,不必把声明语句放在方法的顶端
3. 使用一个对象前,不一定要声明它,可以在用到的时候在定义
4. C#对结构体类型偶那个的定义有些不同,他根本不支持联合类型
5. C#有枚举类型,允许一系列被命名的量(如颜色或一周里的每一天)赋值为连续的数值,但是语法有些不同。
6. C#没有位域,也就是说,变量至少要占用一个字节的存储空间
7. C#不支持变长参数列表,必须针对参数值和类型定义一个方法,然而C#语句允许函数的最后一个参数为可变参数数组
8. C#引入了代理和索引器的思想,这些在其他流行的语言中是没有的。
用C#编写windows程序
1. C#中的对象
C#中的任何食物都被看做是对象,对象包含数据并具有操作数据的方法,
整型变量、浮点型变量和双精度型变量也是对象,他们也具有方法
注意:
数值类型的转化是用方法而不是外部函数完成的。在把一个数值格式化成一个特定的字符串时,每种数值类型都提供了一个格式化方法。
2. 受管语言和垃圾自动收集
C#和VB.NET都是受管语言,有两个含义:一个是两者被编译成中间的底层语言,都使用通用语言运行时(Common Language Runtime,CLR)执行编译后的代码,或者进一步编译这些代码。另一方面,受管语言都是垃圾自动收集的,垃圾自动收集语言负责师傅拿个不用的内存,垃圾收集系统一旦检测到变量、数组或对象不在被引用,就把相应的内存空间释放回系统,在大多数情况下,根本不用考虑内存的分配与释放问题。
我们在java程序中可以产生其他的应用程序的进程,在java程序中启动的进程称为子进程,启动了进程的java程序称为父进程。子进程没有键盘和显示器,子进程的标准输入和输出不在连接到键盘和显示器,而是以管道流的形式连接到父进程的一个输出流和输入流对象上,调用Process类的getOutputStream和getInputStream方法可以达到这个输出流和输入流对象。子进程从标准输入读取到的内容是父进程通过输出流对象写入管道的数据,子进程写入标准输出的数据通过管道传送到父进程的输入流对象中,父进程从这个输入流对象中读取到的内容就是子进程写入到标准输出的数据。
从本质上来说他们之间没有什么区别,因为他们在硬盘上都有一种的存放方式--二进制,但是如果要对他们有些区分的话,那可以这样理解。我们现在的每个字符由一个或多个字节组成,每个字节都是用的-128—127之间的部分数值来表示的,也就是说,-128——127之间还有一些数据没有对应任何字符的任何字节。如果一个文件中的每个字节的内容都是可以表示成字符的数据,我们就可以称这个文件为文本文件,可见,文本文件只是二进制文件中的一种特例,为了与文本文件想区别,人们又把除了文本文件以外的文件称为二进制文件,由于很难严格区分文本文件和二进制文件的概念,所以我们可以简单地认为,
如果一个文件专门用于存储文本字符的数据,没有包含字符以外的其他数据,我们就称之为文本文件,除此之外的文件就是二进制文件。
java语言定义了许多类专门用于负责各种方式的输入输出,这些类都被放在java.io包中。
File类是IO包只能够唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操作文件,通过调用File类提供的各种方法,我们能够创建、删除文件
重命名文件、判断文件的读写权限及是否存在、设置和查询文件的最近修改时间。
并且在java中。目录也被当作file使用,只是多了一些目录特有的功能——可以用list方法理出目录中的文件名,在unix下路径分隔符为(/),在Dos下的路径名分隔符为(\),java可以正确的处理Unix和Dos下的路径分隔符。
注意在使用delete方法删除有File对象的路径所表示的磁盘文件或目录,如果删除的对象是目录,该目录下的内容必须为空。
File类不能访问文件的内容,即不能够从文件中读取数据或往文件里写数据,它只能对文件本身的属性进行操作。
RandomAccessFile类可以说是java语言中功能最为丰富的文件访问类,它提供了很多的文件访问方法。RandomAccessFile类支持“随机访问”方式,我们可以跳到文件的任意位置处读写数据。RandomAccessFile对象类又个位置指示器,指向当前读写处的位置,当读写n个字节后,文件指示器将指向这n个字节后的下一个字节处,RandomAccessFile对象在等长记录格式文件的随机读取时又很大的优势,但是
该类仅限于操作文件,不能访问其他的IO设备,如网络、内存映像等。
[转]甘谷方言词汇一、称谓
男人谓之男人家,妇人谓之婆娘家;年老则称老汉,老婆子。小伙子叫少年家,姑娘叫女子娃。
孩子叫碎娃娃,表示亲昵就叫命命。(磐安以上接近武山之地叫曼干。)婴儿叫月娃儿或毂转儿(gu [zu] anz).
父亲或伯父叫大大,父母以上通谓之老家。
夫妻互不称名,谓夫为掌柜的,妻为屋内(念lai)的。
岳父母谓之丈们大,丈们娘(念nia);当面称姨父、姨娘。
母之姊称姨娘,妹称阿姨。
舅舅也叫阿舅,舅母叫妗子。
连襟谓之挑担。
夫之父谓之阿公,母谓之阿家;妯娌谓之先后(念如“宪
继父谓之后大,虐待前妻儿女的后娘谓之妖婆。
儿子谓之后人,第一个孩子谓之头授子,最后一个谓之老生胎。
外孙戏称磨镰水。
大官叫驮贷,驮念te,大也,讽刺驮雷雷(re lui lui )。
地主富户叫财东,也叫富汉!穷人叫穷汉家。
长工叫人手,也谓之做活人;短工叫搭主儿的。雇用来做饭的妇女叫饭倌,也叫锅上家。
交易场所中的说合人叫牙子;买卖房屋土地的介绍人叫地媒(qi mai)。
强不知以为知叫假在行(hang),也叫假机骨,半瓶水,半吊子。
脾性古怪,不可捉摸的人叫“难言也”。
流氓或不务正业的者叫光(念上声)人(guang reng)。
精通赌博的谓之方客,也叫孤碌子(gu iu zi )。
小偷叫贼娃子,扒手叫绺娃子,诈骗都叫骗子手。
工匠,在“匠”上冠以各自行业,如木匠,铁匠等;当面泛称“师”,如张师、王师等。
瓦工叫泥水匠,烧制砖瓦的和瓦匠。
糊裱、捏塑、彩画、做纸贷的,通称画匠。
武艺高强或精通某种技艺的称把戏。
演员叫戏子,杂技演员叫耍把戏的。
外地人称外天人,落户的谓之客邦,贬称外来户儿。
二、身体、疾病、医疗
头叫多脑(de lao ),人老顶秃叫歇顶。
眉毛叫眼眉眉(mi),睫毛叫眼眨毛。
耳朵也叫耳瓜子。(耳念[ru])。
嘴唇叫口皮子。
男人老年不出胡须员粉皮嘴。
门牙长而向外倾斜的叫茄牙。
儿童七、八岁换齿叫序(本作龀)牙齿。
颧骨叫胭脂骨。
肩叫胛骨。
臂叫胳膊(bai);臂长(短)叫梢膀长(短)。
胳肢(逗笑)叫胳驴娃儿。
拳叫槌,也叫槌头子。
胸膛叫腔(念kang)子。
肚脐叫婆脐眼。(脐有时念为pi)
臂叫屁眼,也叫尻(gou)子.
膝叫磕膝盖。
小腿叫干腿儿。
踝骨叫脚骨拽([zu] ai)子。
辫子叫髦角(man ge )子。
旧日姑娘都梳长辫,结婚之日,将头发在脑后结成圆饼形,外套“网子”。名曰“缵缵”(zuan zuan)。
个子高谓之汉子大,低叫矬;矬而粗壮叫短壮儿。
高而胖谓之魁伟;身段瘦小谓之糟碎。
腰下直叫弓腰儿;驼背叫背瓜(锅)儿。
小儿胖叫憨(han);瘦叫阙(que)。
小儿有病叫不乖;大人有病谓之不清生,也说不受霍。
老有有病叫不轻快,也说不攒(zan)劲;老病治疗不愈叫老灾。
拉肚子叫跑肚。
发疟疾叫颤摆子(zhan bai zi)。
肺病旧称痨症,也叫细病。
跌绞叫挨绊(ngai ban )。
脚拐了叫踒(wo)。
呻吟叫声唤。
种牛痘叫点花儿。
会治婴儿风症之类病的老婆子叫风婆婆。
癔病叫毛鬼病。
邪祟叫踳气,禳解踳气叫。会念咒语踳气的叫送匠。
念咒吹气治刀伤之类叫禁,会禁的叫茅山,也叫禁匠。
依托神鬼治病的女巫叫神婆儿。
三、动作、行为
看叫照(城区念(rao),不住地看叫唏(xi);专注地看叫瞅(cou)。
拿东西叫扞(han)。
以肩扛重物叫劫(念qie)。
跑也叫蹑,追赶或赶走叫撵(nian)。
蹲叫蹴(念jiu,本就六切)。
大便叫巴屎,小便叫浇尿。
使用叫使唤,收拾叫拾掇(duo),多方凑钱或东西叫攒掇(cuan duo)也叫斗凑。
乱抓乱摸叫挖抓,也叫搓([ru]aca)。
谈话叫搞话;聊天叫搞闲话。
大声喊叫谓之河(he)。夸耀叫泫,也叫款,今多叫卖拍。
说大话的叫夸耀泫子,也叫牛皮客。
说话诙谐、溜七溜八的叫溜光锤儿。
说假话骗人叫编谎。
爱“编谎”的人谓之谎张三。
无根据,无意义的话叫诳场山(kuang chang san)。
老人唠叨谓之碾碎米子。
丢人叫伤脸,自愧叫难搭整。
吝啬叫啬毛,也叫撕不展或细磨石。
不讲究卫生叫脏稀,脏得使人厌烦叫暮襄。
放东西凌乱不整齐叫烂场。
拿东西或处理事情贪多而无条理,谓之累(念luai)堆。
做事干活很快,能迅速完成,谓之磕勤骂叉(ke qing ma ca )。
做事干活,干净利落谓之干散(gan san ) 。
做事干活有心眼儿叫简亮。
难对付谓之难缠,也谓为牢道。
无能,没本事,勒细。
因无能而贫穷可怜叫裸怜。
大人干小事,巧人干粗活,谓之委捼(wo)了。
整人叫捂整,以财物刁难人,苛索人,叫勒掯(lai keng)。
用尖刻语言挖苦人或讥笑叫欺枉。
故意搅乱,捣乱不让人把活干好叫搁搅,也叫薅搅,又叫哨掏(tao)。
玩儿叫耍子;游戏叫浪。干活儿做活计(念jie)。劳动叫动弹(念tung tan )。
身体强壮,干活力气大,叫拶(zan)劲,办事能干得力也叫拶劲,与此相反叫弱攘(re rang)。
干活卖力,能吃苦,叫泼,吃不了苦,干活不努力,叫消卖消。
干活中途去干别的或托故休息叫打逛。
干活不专心,东张西望,叫(ci)眼子。
干活累了叫吃力,“吃力”了休息叫缓,也叫歇。
不顾一切地蛮干,可能铸成大错,叫胡氽(teng)。
赌气离家出走叫掂山。
小儿犯了错误,大人加以警戒或斥责,叫掂恨。
小儿调皮好动叫(ci);纠缠大人要东西叫盘人。
撒娇撒野叫撒泼(sa pe)。
小呆傻不懂事叫瓜;怯生叫诧(ca)。
小儿漂亮叫心疼,丑陋叫撑眼(ceng nian)。
男人漂亮叫肌俊,也谓为精干,机灵叫机挝([za]a)。
四、情态、意识
舒服(舒适)叫蒇(chan),也叫受霍。
坏,不好叫瞎(念ha),难着丑陋谓之冲心。
寂寞叫绽(can,当是“岑”之转)。
善于巴结人谓之鬼面殷勤。
故意做作叫扭捏,也说扭扭捏捏。
伸开手脚仰卧谓之仰浪八跐(niang lang ba ci)。
东西放得不端正叫侧稜马跨(zai leng ma kua)。
根底端正,不怕人挑毛病叫钢邦硬直。
轻易低头服软,爱哭,叫稀流滴(念die)水。
遇事慌乱,无所措手足,叫毛里毛躁,或身轻毛躁( sheng qing mao cao)。
遇事有主意而不动声色叫四平八稳。
能经受风浪叫背(bai)镇。
胡乱喊叫谓之叽里喳啦;大家乱说谓之七嘴八舌。
不懂装懂,瞎指挥操心假盖盖儿。
年老健忘,说东扯到西。谓之颠懂(读如旭平声)失三。
形容年老体衰谓之老天塌地。
形容气极了谓之咬牙咯登(niao nia ge deng)。
得理不让人,得寸进尺,叫踏住脖子割尾(念yi)杆。
厉害叫威,软弱叫嚷。
羡慕叫脸喜(nian xi)。思考叫上心。
挣扎叫跌绊(die ban)。
关怀叫心上有。
几乎或差点儿叫险吼(xian hou)。
受上级信任或重用叫吃的开,也叫吃香。
不大方,寒酸,谓之猷咩咩(que mia mia )。亦谓其人为猷猷。
在某种场合,自己显得比同身份的人差,感到不光彩,谓之瘪趣(bie qu)。
横竖(或反正)叫横顺。横也念如岤(xue),动不动发脾气,出方生硬叫岤(xue)。
做事十分无有,效果很差。人斥责之谓死已尽(qing)矣。做事不合情理而强做,谓之“捩(lie)天行事。”
在java语言中,String和StringBuffer类的使用非常的普遍,今天有时间认真的复习、整理一下。
String 类和StringBuffer类来分装了对字符串的各种操作,他们属于java.lang包。
String类用于比较两个字符串、查找和抽取串中的字符或字串、字符串与其他类型之间的相互转化等,注意String类对象的内容一旦被初始化就不能在改变了。
StringBuffer类用于内容可以改变的字符串,可以将其他各种类型的数据增加、插入到字符串中,也可以翻转字符串中猿类的内容,并且可以把StringBuffer类生成的字符串通过StringBuffer类的tostring()方法转化成String类,然后就可以通过String类的各种方法操作该字符串了。
java中提供了“+”来连接两个String类型的字符串连接成一个新的字符串,在StringBuffer类中可以通过append方法实现,
例如:
String x="abc"+4+"def";
编译时等效于
String x=new StringBuffer().append("abc").append(4).append("def").toString();
注意:
String s1="hello";
String s2="hello"; 说明s1和s2是同一个对象,而下面的两句代码是创建了两个对象,但是他们虽然内容相同,但确实不同的两个对象;
String s1=new String("hello");
String s2=new String("hello")
在String类中特别要注意“==”和“equals”,他们有着本质的区别,“==”比较的是两个字符串的对象引用,而“equals”比较的是两个字符串的内容。
String类和StringBuffer类具有好多有用的方法,请查看jdk帮助学习和理解。