2008年12月13日
#
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个以上的表连接查询,...
阅读全文