﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-呓语的博客-随笔分类-基础知识</title><link>http://www.blogjava.net/xieyunlong/category/5624.html</link><description>不管前面的路有多艰辛多长，只要怀着一颗执着的心。成功就离你不远了!</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 03:57:29 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 03:57:29 GMT</pubDate><ttl>60</ttl><item><title>随机数类Random</title><link>http://www.blogjava.net/xieyunlong/archive/2006/02/13/30527.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Mon, 13 Feb 2006 15:24:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2006/02/13/30527.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/30527.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2006/02/13/30527.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/30527.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/30527.html</trackback:ping><description><![CDATA[<P>Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处，后者只产生double型的随机数。<BR>　　类Random中的方法十分简单，它只有两个构造方法和六个普通方法。<BR>　　构造方法：<BR>　　(1)public Random()<BR>　　(2)public Random(long seed)<BR>　　Java产生随机数需要有一个基值seed，在第一种方法中基值缺省，则将系统时间作为seed。<BR>　　普通方法：<BR>　　(1)public synonronized void setSeed(long seed)<BR>　　该方法是设定基值seed。<BR>　　(2)public int nextInt()<BR>　　该方法是产生一个整型随机数。<BR>　　(3)public long nextLong()<BR>　　该方法是产生一个long型随机数。<BR>　　(4)public float nextFloat()<BR>　　该方法是产生一个Float型随机数。<BR>　　(5)public double nextDouble()<BR>　　该方法是产生一个Double型随机数。<BR>　　(6)public synchronized double nextGoussian()<BR>　　该方法是产生一个double型的Goussian随机数。<BR>　　例2 RandomApp.java。<BR>　　//import java.lang.*;<BR>　　import java.util.Random;</P>
<P>　　public class RandomApp{<BR>　　　public static void main(String args[]){<BR>　　　　Random ran1=new Random();<BR>　　　　Random ran2=new Random(12345);<BR>　　　　//创建了两个类Random的对象。<BR>　　　　System.out.println("The 1st set of random numbers:");<BR>　　　　System.out.println("\t Integer:"+ran1.nextInt());<BR>　　　　System.out.println("\t Long:"+ran1.nextLong());<BR>　　　　System.out.println("\t Float:"+ran1.nextFloat());<BR>　　　　System.out.println("\t Double:"+ran1.nextDouble());<BR>　　　　System.out.println("\t Gaussian:"+ran1.nextGaussian());<BR>　　　　//产生各种类型的随机数<BR>　　　　System.out.print("The 2nd set of random numbers:");<BR>　　　　for(int i=0;i&lt;5;i++){<BR>　　　　　System.out.println(ran2.nextInt()+" ");<BR>　　　　　if(i==2) System.out.println();<BR>　　　　　//产生同种类型的不同的随机数。<BR>　　　　　System.out.println();<BR>　　　　}<BR>　　　}<BR>}</P><img src ="http://www.blogjava.net/xieyunlong/aggbug/30527.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2006-02-13 23:24 <a href="http://www.blogjava.net/xieyunlong/archive/2006/02/13/30527.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实用工具类库java.util</title><link>http://www.blogjava.net/xieyunlong/archive/2006/02/13/30400.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Mon, 13 Feb 2006 01:00:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2006/02/13/30400.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/30400.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2006/02/13/30400.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/30400.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/30400.html</trackback:ping><description><![CDATA[<P>本章介绍Java的实用工具类库java.util包。在这个包中，Java提供了一些实用的方法和数据结构。例如，Java提供日期(Data)类、日历(Calendar)类来产生和获取日期及时间，提供随机数(Random)类产生各种类型的随机数，还提供了堆栈(Stack)、向量(Vector) 、位集合(Bitset)以及哈希表(Hashtable)等类来表示相应的数据结构。<BR>　　图8.1给出了java.util包的基本层次结构图。下面我们将具体介绍其中几个重要的类。<BR>　　　　　　　　　　　┌java.util.BitSet<BR>　　　　　　　　　　　│java.util.Calendar<BR>　　　　　　　　　　　│　　　　　　└java.util.GregorianCalendar<BR>　　　　　　　　　　　│java.util.Date<BR>　　　　　　　　　　　│java.util.Dictionary<BR>　　　　　　　　　　　│　　　　　　└java.util.Hashtable<BR>　　　　　　　　　　　│　　　　　　　　　　　　　└java.util.Properties<BR>　　　　　　　　　　　│java.util.EventObject<BR>　　　　　　　　　　　│java.util.ResourceBundle<BR>　　　　　　　┌普通类┤　　　　　　├java.util.ListResourceBundle<BR>　　　　　　　│　　　│　　　　　　└java.util.PropertyResourceBundle<BR>　　　　　　　│　　　│java.util.Local<BR>　　　　　　　│　　　│java.util.Observable<BR>　　　　　　　│　　　│java.util.Random<BR>　　　　　　　│　　　│java.util.StringTokenizer<BR>　　　　　　　│　　　│java.util.Vector<BR>　　　　　　　│　　　│　　　　　　└java.util.Stack<BR>　　Java.util┤　　　└java.util.TimeZone<BR>　　　　　　　│　　　　　　　　　　└java.util.SimpleTimeZone<BR>　　　　　　　│　　　┌java.util.Enumeration<BR>　　　　　　　├接　口┤java.util.EventListener<BR>　　　　　　　│　　　└java.util.Observer<BR>　　　　　　　│　　　┌java.util.EmptyStackException<BR>　　　　　　　└异常类┤java.util.MissingResourceException<BR>　　　　　　　　　　　│java.util.NoSuchElementException<BR>　　　　　　　　　　　└java.util.TooManyListenersException<BR>　　　　　　　图8.1 java.util包的基本层次结构</P>
<P><BR>8.2 日期类Date</P>
<P>　　Java在日期类中封装了有关日期和时间的信息，用户可以通过调用相应的方法来获取系统时间或设置日期和时间。Date类中有很多方法在JDK1.0公布后已经过时了，在8.3中我们将介绍JDK1.0中新加的用于替代Date的功能的其它类。<BR>　　在日期类中共定义了六种构造函数。<BR>　　(1)public Date()<BR>　　创建的日期类对象的日期时间被设置成创建时刻相对应的日期时间。<BR>　　例 Date today=new Date()；//today被设置成创建时刻相对应的日期时间。<BR>　　(2)public Date (long date)<BR>　　long 型的参数date可以通过调用Date类中的static方法parse(String s)来获得。<BR>　　例 long l=Date.parse("Mon 6 Jan 1997 13:3:00");<BR>　　　　Date day=new Date(l);<BR>　　//day中时间为1997年 1月6号星期一，13:3:00。<BR>　　(3)public Date(String s)<BR>　　按字符串s产生一日期对象。s的格式与方法parse中字符串参数的模式相同。<BR>　　例 Date day=new Date("Mon 6 Jan 1997 13:3:00");<BR>　　//day 中时间为1997年1月6号星期一，13:3:00.<BR>　　(4)public Date(int year,int month,int date)<BR>　　(5)public Date(int year,int month,int date,int hrs,int min)<BR>　　(6)public Date(int year,int month,int date,int hrs,int min,int sec)<BR>　　按给定的参数创建一日期对象。<BR>　　参数说明：<BR>　　year的值为：需设定的年份-1900。例如需设定的年份是1997则year的值应为97，即1997-1900的结果。所以Date中可设定的年份最小为1900；<BR>　　month的值域为0～11，0代表1月，11表代表12月；<BR>　　date的值域在1～31之间；<BR>　　hrs的值域在0～23之间。从午夜到次日凌晨1点间hrs=0，从中午到下午1点间hrs=12；<BR>　　min和sec的值域在0～59之间。<BR>　　例 Date day=new Date(11,3,4);<BR>　　//day中的时间为：04-Apr-11 12:00:00 AM<BR>另外，还可以给出不正确的参数。<BR>　　例　设定时间为1910年2月30日，它将被解释成3月2日。<BR>　　Date day=new Date(10,1,30,10,12,34);<BR>　　System.out.println("Day's date is:"+day);<BR>　　//打印结果为：Day's date is:Web Mar 02 10:13:34 GMT+08:00 1910<BR>　　下面我们给出一些Date类中常用方法。<BR>　　(1)public static long UTC(int year,int month,int date,int hrs. int min,int sec)<BR>　　该方法将利用给定参数计算UTC值。UTC是一种计时体制，与GMT(格林威治时间)的计时体系略有差别。UTC计时体系是基于原子时钟的，而GTMT计时体系是基于天文学观测的。计算中使用的一般为GMT计时体系。<BR>　　(2)public static long parse(String s)<BR>　　该方法将字符串s转换成一个long型的日期。在介绍构造方法Date(long date)时曾使用过这个方法。<BR>　　字符串s有一定的格式，一般为：<BR>　　(星期 日 年 时间GMT+时区)<BR>　　若不注明时区，则为本地时区。<BR>　　(3)public void setMonth(int month)<BR>　　(4)public int getMonth()<BR>　　这两个方法分别为设定和获取月份值。<BR>　　获取的月份的值域为0～11，0代表1月，11代表12月。<BR>　　(5)public String toString()<BR>　　(6)public String toLocalString()<BR>　　(7)public String toGMTString()<BR>　　将给定日期对象转换成不同格式的字符串。它们对应的具体的格式可参看例子8.1。<BR>　　(8)public int getTimezoneOffset()<BR>　　该方法用于获取日期对象的时区偏移量。<BR>　　例8.1中对上面介绍的Date类中的基本方法进行了具体的应用，并打印了相应的结果。由于使用了一些过时的方法，所以编译时会有警告信息。另外，由于本例中的时间表示与平台有关，不同的JDK版本对此处理不完全相同，因此不同版本的JDK执行本例的结果可能有细微差异。<BR>　　例8.1 DateApp.java<BR>　　import java.lang.System;<BR>　　import java.util.Date;<BR>　　public class DateApp{<BR>　　　public static void main(String args[]){<BR>　　　　Date today=new Date();<BR>　　　　//today中的日期被设成创建时刻的日期和时间，假设创建时刻为1997年3月<BR>　　　　//23日17时51分54秒。<BR>　　　　System.out.println("Today's date is "+today);<BR>　　　　//返回一般的时间表示法，本例中结果为<BR>　　　　//Today's date is Fri May 23 17:51:54 1997<BR>　　　　System.out.println("Today's date(Internet GMT)is:"<BR>　　　　　+today.toGMTString());<BR>　　　　//返回结果为GMT时间表示法，本例中结果为<BR>　　　　//Today's date(Internet GMT)is: 23 May 1997 09:51:54:GMT<BR>　　　　System.out.println("Today's date(Locale) is:"<BR>　　　　　+today.toLocaleString());<BR>　　　　//返回结果为本地习惯的时间表示法，结果为<BR>　　　　//Today's date(Locale)is:05/23/97 17:51:54<BR>　　　　System.out.println("Today's year is: "+today.getYear());<BR>　　　　System.out.println("Today's month is: "+(today.getMonth()+1));<BR>　　　　System.out.println("Today's date is: "+today.getDate());<BR>　　　　//调用Date类中方法，获取年月日的值。<BR>　　　　//下面调用了不同的构造方法来创建Date类的对象。<BR>　　　　Date day1=new Date(100,1,23,10,12,34);<BR>　　　　System.out.println("Day1's date is: "+day1);<BR>　　　　Date day2=new Date("Sat 12 Aug 1996 13:3:00");<BR>　　　　System.out.println("Day2's date is: "+day2);<BR>　　　　long l= Date.parse("Sat 5 Aug 1996 13:3:00 GMT+0800");<BR>　　　　Date day3= new Date(l);<BR>　　　　System.out.println("Day3's date(GMT)is: "+day3.toGMTString());<BR>　　　　System.out.println("Day3's date(Locale)is: "<BR>　　　　　+day3.toLocaleString());<BR>　　　　System.out.println("Day3's time zone offset is:"<BR>　　　　　+day3.getTimezoneOffset());<BR>　　　}<BR>　　}</P>
<P>　　运行结果(JDK1.3版，与原文不同，原文是JDK1.0版)：<BR>　　E:\java\tutorial\java01&gt;java DateApp<BR>　　Today's date is Thu Dec 27 17:58:16 CST 2001<BR>　　Today's date(Internet GMT)is:27 Dec 2001 09:58:16 GMT<BR>　　Today's date(Locale) is:2001-12-27 17:58:16<BR>　　Today's year is: 101<BR>　　Today's month is: 12<BR>　　Today's date is: 27<BR>　　Day1's date is: Wed Feb 23 10:12:34 CST 2000<BR>　　Day2's date is: Fri Aug 12 13:03:00 CST 1996<BR>　　Day3's date(GMT)is: 5 Aug 1996 05:03:00 GMT<BR>　　Day3's date(Locale)is: 1996-8-5 13:03:00<BR>　　Day3's time zone offset is:-480</P>
<P>　　E:\java\tutorial\java01&gt;</P>
<P>8.3 日历类Calendar</P>
<P>　　在早期的JDK版本中，日期(Date)类附有两大功能：(1)允许用年、月、日、时、分、秒来解释日期：(2)允许对表示日期的字符串进行格式化和句法分析。在JDK1.1中提供了类Calendar来完成第一种功能，类DateFormat来完成第二项功能。dateFormat是java.text包中的一个类。与Date类有所不同的是，DateFormat类接受用各种语言和不同习惯表示的日期字符串。本节将介绍java.util包中的类Calendar及其它新增加的相关的类。<BR>　　类Calendar是一个抽象类，它完成日期(Date)类和普通日期表示法(即用一组整型域如YEAR，MONTH，DAY，HOUR表示日期)之间的转换。<BR>　　由于所使用的规则不同，不同的日历系统对同一个日期的解释有所不同。在JDK1.1中提供了Calendar类一个子类GregorianCalendar??它实现了世界上普遍使用的公历系统。当然用户也可以通过继承Calendar类，并增加所需规则，以实现不同的日历系统。<BR>　　第GregorianCalendar继承了Calendar类。本节将在介绍类GregorianCalendar的同时顺带介绍Calendar类中的相关方法。<BR>　　类GregorianCalendar提供了七种构造函数：<BR>　　(1)public GregorianCalendar()<BR>　　创建的对象中的相关值被设置成指定时区，缺省地点的当前时间，即程序运行时所处的时区、地点的当前时间。<BR>　　(2)public GregorianCalendar(TimeZone zone)<BR>　　创建的对象中的相关值被设置成指定时区zone，缺省地点的当前时间。<BR>　　(3)public GregorianCalendar(Locale aLocale)<BR>　　创建的对象中的相关值被设置成缺省时区，指定地点aLocale的当前时间。<BR>　　(4)public GregorianCalendar(TimeZone zone,Local aLocale)<BR>　　创建的对象中的相关值被设置成指定时区，指定地点的当前时间。<BR>　　上面使用到的类TimeZone的性质如下：<BR>　　TimeZone是java.util包中的一个类，其中封装了有关时区的信息。每一个时区对应一组ID。类TimeZone提供了一些方法完成时区与对应ID两者之间的转换。<BR>　　(Ⅰ)已知某个特定的ID，可以调用方法<BR>　　public static synchronized TimeZone getTimeZone(String ID)<BR>来获取对应的时区对象。<BR>　　例 太平洋时区的ID为PST，用下面的方法可获取对应于太平洋时区的时区对象：<BR>　　TimeZone tz=TimeZone.getTimeZone("PST");<BR>　　调用方法getDefault()可以获取主机所处时区的对象。<BR>　　TimeZone tz=TimeZone.getDefault();<BR>　　(Ⅱ)调用以下方法可以获取时区的ID<BR>　　■public static synchronized String[] getavailableIDs(int rawOffset)<BR>　　根据给定时区偏移值获取ID数组。同一时区的不同地区的ID可能不同，这是由于不同地区对是否实施夏时制意见不统一而造成的。<BR>　　例String s[]=TimeZone.getAvailableIDs(-7*60*60*1000);<BR>　　打印s，结果为s[0]=PNT，s[1]=MST<BR>　　■public static synchronized String[] getAvailableIDs()<BR>　　获取提供的所有支持的ID。<BR>　　■public String getID()<BR>　　获取特定时区对象的ID。<BR>　　例 TimeZone tz=TimeZone.getDefault();<BR>　　String s=tz.getID();<BR>　　打印s，结果为s=CTT。<BR>　　上面使用类的对象代表了一个特定的地理、政治或文化区域。Locale只是一种机制，它用来标识一类对象，Local本身并不包含此类对象。<BR>　　要获取一个Locale的对象有两种方法：<BR>　　(Ⅰ)调用Locale类的构造方法<BR>　　Locale(String language,String country)<BR>　　Locale(String language,String country,String variant)<BR>　　参数说明：language??在ISO-639中定义的代码，由两个小写字母组成。<BR>　　　　　　　country??在ISO-3166中定义的代码，由两个大写字母组成。<BR>　　　　　　　variant??售货商以及特定浏览器的代码，例如使用WIN代表Windows。<BR>　　(Ⅱ)调用Locale类中定义的常量<BR>　　Local类提供了大量的常量供用户创建Locale对象。<BR>　　例 Locale.CHINA<BR>　　　　为中国创建一个Locale的对象。<BR>　　类TimeZone和类Locale中的其它方法，读者可查阅API。<BR>　　(5)public GregorianCalendar(int year,int month,int date)<BR>　　(6)public GregorianCalendar(int year,int month,int date,int hour,int minute)<BR>　　(7)public GregorianCalendar(int year,int month,int date,int hour,int minute,int second)<BR>　　用给定的日期和时间创建一个GregorianCalendar的对象。<BR>　　参数说明：<BR>　　year-设定日历对象的变量YEAR；month-设定日历对象的变量MONTH；<BR>　　date-设定日历对象的变量DATE；hour-设定日历对象的变量HOUR_OF_DAY；<BR>　　minute-设定日历对象的变量MINUTE；second-设定日历对象的变量SECOND。<BR>　　与Date类中不同的是year的值没有1900这个下限，而且year的值代表实际的年份。month的含义与Date类相同，0代表1月，11代表12月。<BR>　　例 GregorianCalendar cal=new GregorianCalendar(1991,2,4)<BR>　　cal的日期为1991年3月4号。<BR>　　除了与Date中类似的方法外，Calendar类还提供了有关方法对日历进行滚动计算和数学计算。计算规则由给定的日历系统决定。进行日期计算时，有时会遇到信息不足或信息不实等特殊情况。Calendar采取了相应的方法解决这些问题。当信息不足时将采用缺省设置，在GregorianCalendar类中缺省设置一般为YEAR=1970,MONTH=JANUARY,DATE=1。<BR>　　当信息不实时，Calendar将按下面的次序优先选择相应的Calendar的变量组合，并将其它有冲突的信息丢弃。<BR>　　MONTH+DAY_OF_MONTH<BR>　　MONTH+WEEK_OF_MONTH+DAY_OF_WEEK<BR>　　MONTH+DAY_OF_WEEK_OF_MONTH+DAY_OF_WEEK<BR>　　DAY_OF+YEAR<BR>　　DAY_OF_WEEK_WEEK_OF_YEAR<BR>　　HOUR_OF_DAY</P>
<P>8.4 随机数类Random</P>
<P>　　Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处，后者只产生double型的随机数。<BR>　　类Random中的方法十分简单，它只有两个构造方法和六个普通方法。<BR>　　构造方法：<BR>　　(1)public Random()<BR>　　(2)public Random(long seed)<BR>　　Java产生随机数需要有一个基值seed，在第一种方法中基值缺省，则将系统时间作为seed。<BR>　　普通方法：<BR>　　(1)public synonronized void setSeed(long seed)<BR>　　该方法是设定基值seed。<BR>　　(2)public int nextInt()<BR>　　该方法是产生一个整型随机数。<BR>　　(3)public long nextLong()<BR>　　该方法是产生一个long型随机数。<BR>　　(4)public float nextFloat()<BR>　　该方法是产生一个Float型随机数。<BR>　　(5)public double nextDouble()<BR>　　该方法是产生一个Double型随机数。<BR>　　(6)public synchronized double nextGoussian()<BR>　　该方法是产生一个double型的Goussian随机数。<BR>　　例8.2 RandomApp.java。<BR>　　//import java.lang.*;<BR>　　import java.util.Random;</P>
<P>　　public class RandomApp{<BR>　　　public static void main(String args[]){<BR>　　　　Random ran1=new Random();<BR>　　　　Random ran2=new Random(12345);<BR>　　　　//创建了两个类Random的对象。<BR>　　　　System.out.println("The 1st set of random numbers:");<BR>　　　　System.out.println("\t Integer:"+ran1.nextInt());<BR>　　　　System.out.println("\t Long:"+ran1.nextLong());<BR>　　　　System.out.println("\t Float:"+ran1.nextFloat());<BR>　　　　System.out.println("\t Double:"+ran1.nextDouble());<BR>　　　　System.out.println("\t Gaussian:"+ran1.nextGaussian());<BR>　　　　//产生各种类型的随机数<BR>　　　　System.out.print("The 2nd set of random numbers:");<BR>　　　　for(int i=0;i&lt;5;i++){<BR>　　　　　System.out.println(ran2.nextInt()+" ");<BR>　　　　　if(i==2) System.out.println();<BR>　　　　　//产生同种类型的不同的随机数。<BR>　　　　　System.out.println();//原文如此<BR>　　　　}<BR>　　　}<BR>　　}</P>
<P>　　运行结果：<BR>　　E:\java01&gt;java RandomApp<BR>　　The 1st set of random numbers:<BR>　　　　Integer:-173899656<BR>　　　　Long:8056223819738127077<BR>　　　　Float:0.6293638<BR>　　　　Double:0.7888394520265607<BR>　　　　Gaussian:0.5015701094568733<BR>　　The 2nd set of random numbers:1553932502<BR>　　-2090749135<BR>　　-287790814<BR>　　-355989640<BR>　　-716867186<BR>　　E:\java01&gt;</P>
<P>8.5 向量类Vector</P>
<P>　　Java.util.Vector提供了向量(Vector)类以实现类似动态数组的功能。在Java语言中。正如在一开始就提到过，是没有指针概念的，但如果能正确灵活地使用指针又确实可以大大提高程序的质量，比如在C、C++中所谓“动态数组”一般都由指针来实现。为了弥补这点缺陷，Java提供了丰富的类库来方便编程者使用，Vector类便是其中之一。事实上，灵活使用数组也可完成向量类的功能，但向量类中提供的大量方法大大方便了用户的使用。<BR>　　创建了一个向量类的对象后，可以往其中随意地插入不同的类的对象，既不需顾及类型也不需预先选定向量的容量，并可方便地进行查找。对于预先不知或不愿预先定义数组大小，并需频繁进行查找、插入和删除工作的情况，可以考虑使用向量类。<BR>　　向量类提供了三种构造方法：<BR>　　public vector()<BR>　　public vector(int initialcapacity,int capacityIncrement)<BR>　　public vector(int initialcapacity)<BR>　　使用第一种方法，系统会自动对向量对象进行管理。若使用后两种方法，则系统将根据参数initialcapacity设定向量对象的容量(即向量对象可存储数据的大小)，当真正存放的数据个数超过容量时，系统会扩充向量对象的存储容量。参数capacityIncrement给定了每次扩充的扩充值。当capacityIncrement为0时，则每次扩充一倍。利用这个功能可以优化存储。<BR>　　在Vector类中提供了各种方法方便用户使用：<BR>　　■插入功能<BR>　　(1)public final synchronized void addElement(Object obj)<BR>　　将obj插入向量的尾部。obj可以是任何类的对象。对同一个向量对象，可在其中插入不同类的对象。但插入的应是对象而不是数值，所以插入数值时要注意将数值转换成相应的对象。<BR>　　例 要插入一个整数1时，不要直接调用v1.addElement(1)，正确的方法为：<BR>　　Vector v1=new Vector();<BR>　　Integer integer1=new Integer(1);<BR>　　v1.addElement(integer1);<BR>　　(2)public final synchronized void setElementAt(object obj,int index)<BR>　　将index处的对象设成obj，原来的对象将被覆盖。<BR>　　(3)public final synchronized void insertElementAt(Object obj,int index)<BR>　　在index指定的位置插入obj，原来对象以及此后的对象依次往后顺延。<BR>　　■删除功能<BR>　　(1)public final synchronized void removeElement(Object obj)<BR>　　从向量中删除obj。若有多个存在，则从向量头开始试，删除找到的第一个与obj相同的向量成员。<BR>　　(2)public final synchronized void removeAllElement()<BR>　　删除向量中所有的对象。<BR>　　(3)public final synchronized void removeElementlAt(int index)<BR>　　删除index所指的地方的对象。<BR>　　■查询搜索功能<BR>　　(1)public final int indexOf(Object obj)<BR>　　从向量头开始搜索obj ,返回所遇到的第一个obj对应的下标，若不存在此obj，返回-1。<BR>　　(2)public final synchronized int indexOf(Object obj,int index)<BR>　　从index所表示的下标处开始搜索obj。<BR>　　(3)public final int lastIndexOf(Object obj)<BR>　　从向量尾部开始逆向搜索obj。<BR>　　(4)public final synchronized int lastIndexOf(Object obj,int index)<BR>　　从index所表示的下标处由尾至头逆向搜索obj。<BR>　　(5)public final synchronized Object firstElement()<BR>　　获取向量对象中的首个obj。<BR>　　(6)public final synchronized Object lastelement()<BR>　　获取向量对象中的最后一个obj。<BR>　　了解了向量的最基本的方法后，我们来看一下例8.3VectorApp.java。<BR>　　例8.3 VectorApp.java。<BR>　　import java.util.Vector;<BR>　　import java.lang.*;//这一句不应该要，但原文如此<BR>　　import java.util.Enumeration;<BR>　　public class VectorApp{<BR>　　　public static void main(String[] args){<BR>　　　　Vector v1=new Vector();<BR>　　　　Integer integer1=new Integer(1);<BR>　　　　v1.addElement("one");<BR>　　　　//加入的为字符串对象<BR>　　　　v1.addElement(integer1);<BR>　　　　v1.addElement(integer1);<BR>　　　　//加入的为Integer的对象<BR>　　　　v1.addElement("two");<BR>　　　　v1.addElement(new Integer(2));<BR>　　　　v1.addElement(integer1);<BR>　　　　v1.addElement(integer1);<BR>　　　　System.out.println("The vector v1 is:\n\t"+v1);<BR>　　　　//将v1转换成字符串并打印<BR>　　　　v1.insertElementAt("three",2);<BR>　　　　v1.insertElementAt(new Float(3.9),3);<BR>　　　　System.out.println("The vector v1(used method insertElementAt()) is:\n\t "+v1);<BR>　　　　//往指定位置插入新的对象，指定位置后的对象依次往后顺延<BR>　　　　v1.setElementAt("four",2);<BR>　　　　System.out.println("The vector v1(used method setElementAt()) is:\n\t "+v1);<BR>　　　　//将指定位置的对象设置为新的对象<BR>　　　　v1.removeElement(integer1);<BR>　　　　//从向量对象v1中删除对象integer1由于存在多个integer1所以从头开始<BR>　　　　//找，删除找到的第一个integer1<BR>　　　　Enumeration enum=v1.elements();<BR>　　　　System.out.print("The vector v1(used method removeElement())is:");<BR>　　　　while(enum.hasMoreElements())<BR>　　　　System.out.print(enum.nextElement()+" ");<BR>　　　　System.out.println();<BR>　　　　//使用枚举类(Enumeration)的方法来获取向量对象的每个元素<BR>　　　　System.out.println("The position of object 1(top-to-bottom):"<BR>　　　　　+ v1.indexOf(integer1));<BR>　　　　System.out.println("The position of object 1(tottom-to-top):"<BR>　　　　　+v1.lastIndexOf(integer1));<BR>　　　　//按不同的方向查找对象integer1所处的位置<BR>　　　　v1.setSize(4);<BR>　　　　System.out.println("The new vector(resized the vector)is:"+v1);<BR>　　　　//重新设置v1的大小，多余的元素被行弃<BR>　　　}<BR>　　}<BR>　　运行结果：<BR>　　E:\java01&gt;java VectorApp<BR>　　The vector v1 is:<BR>　　　　　[one, 1, 1, two, 2, 1, 1]<BR>　　The vector v1(used method insertElementAt()) is:<BR>　　　　　[one, 1, three, 3.9, 1, two, 2, 1, 1]<BR>　　The vector v1(used method setElementAt()) is:<BR>　　　　　[one, 1, four, 3.9, 1, two, 2, 1, 1]<BR>　　The vector v1(used method removeElement())is:one four 3.9 1 two 2 1 1<BR>　　The position of object 1(top-to-bottom):3<BR>　　The position of object 1(tottom-to-top):7<BR>　　The new vector(resized the vector)is:[one, four, 3.9, 1]<BR>　　E:\java01&gt;<BR>　　从例8.3运行的结果中可以清楚地了解上面各种方法的作用，另外还有几点需解释。<BR>　　(1)类Vector定义了方法<BR>　　public final int size()<BR>　　此方法用于获取向量元素的个数。它的返回值是向是中实际存在的元素个数，而非向量容量。可以调用方法capactly()来获取容量值。<BR>　　方法：<BR>　　public final synchronized void setsize(int newsize)<BR>　　此方法用来定义向量大小。若向量对象现有成员个数已超过了newsize的值，则超过部分的多余元素会丢失。<BR>　　(2)程序中定义了Enumeration类的一个对象<BR>　　Enumeration是java.util中的一个接口类，在Enumeration中封装了有关枚举数据集合的方法。<BR>　　在Enumeration中提供了方法hawMoreElement()来判断集合中是束还有其它元素和方法nextElement()来获取下一个元素。利用这两个方法可以依次获得集合中元素。<BR>　　Vector中提供方法：<BR>　　public final synchronized Enumeration elements()<BR>　　此方法将向量对象对应到一个枚举类型。java.util包中的其它类中也大都有这类方法，以便于用户获取对应的枚举类型。</P>
<P>8.6 栈类Stack</P>
<P>　　Stack类是Vector类的子类。它向用户提供了堆栈这种高级的数据结构。栈的基本特性就是先进后出。即先放入栈中的元素将后被推出。Stack类中提供了相应方法完成栈的有关操作。<BR>　　基本方法：<BR>　　public Object push(Object Hem)<BR>　　将Hem压入栈中，Hem可以是任何类的对象。<BR>　　public Object pop()<BR>　　弹出一个对象。<BR>　　public Object peek()<BR>　　返回栈顶元素，但不弹出此元素。<BR>　　public int search(Object obj)<BR>　　搜索对象obj,返回它所处的位置。<BR>　　public boolean empty()<BR>　　判别栈是否为空。<BR>　　例8.4 StackApp.java使用了上面的各种方法。<BR>　　例8.4 StackApp.java。<BR>　　import java.lang.*;<BR>　　import java.util.*;<BR>　　public class StackApp{<BR>　　　public static void main(String args[]){<BR>　　　　Stack sta=new Stack();<BR>　　　　sta.push("Apple");<BR>　　　　sta.push("banana");<BR>　　　　sta.push("Cherry");<BR>　　　　//压入的为字符串对象<BR>　　　　sta.push(new Integer(2));<BR>　　　　//压入的为Integer的对象，值为2<BR>　　　　sta.push(new Float(3.5));<BR>　　　　//压入的为Float的对象，值为3.5<BR>　　　　System.out.println("The stack is,"+sta);<BR>　　　　//对应栈sta<BR>　　　　System.out.println("The top of stack is:"+sta.peek());<BR>　　　　//对应栈顶元素，但不将此元素弹出<BR>　　　　System.out.println("The position of object Cherry is:"<BR>　　　　+sta.search("cherry"));<BR>　　　　//打印对象Cherry所处的位置<BR>　　　　System.out.print("Pop the element of the stack:");<BR>　　　　while(!sta.empty())<BR>　　　　System.out.print(sta.pop()+" ");<BR>　　　　System.out.println();<BR>　　　　//将栈中的元素依次弹出并打印。与第一次打印的sta的结果比较，可看出栈<BR>　　　　//先进后出的特点<BR>　　　}<BR>　　}<BR>　　运行结果(略)</P>
<P><BR>8.7 哈希表类Hashtable</P>
<P>　　哈希表是一种重要的存储方式，也是一种常见的检索方法。其基本思想是将关系码的值作为自变量，通过一定的函数关系计算出对应的函数值，把这个数值解释为结点的存储地址，将结点存入计算得到存储地址所对应的存储单元。检索时采用检索关键码的方法。现在哈希表有一套完整的算法来进行插入、删除和解决冲突。在Java中哈希表用于存储对象，实现快速检索。<BR>　　Java.util.Hashtable提供了种方法让用户使用哈希表，而不需要考虑其哈希表真正如何工作。<BR>　　哈希表类中提供了三种构造方法，分别是：<BR>　　public Hashtable()<BR>　　public Hashtable(int initialcapacity)<BR>　　public Hashtable(int initialCapacity,float loadFactor)<BR>　　参数initialCapacity是Hashtable的初始容量，它的值应大于0。loadFactor又称装载因子，是一个0.0到0.1之间的float型的浮点数。它是一个百分比，表明了哈希表何时需要扩充，例如，有一哈希表，容量为100，而装载因子为0.9，那么当哈希表90%的容量已被使用时，此哈希表会自动扩充成一个更大的哈希表。如果用户不赋这些参数，系统会自动进行处理，而不需要用户操心。<BR>　　Hashtable提供了基本的插入、检索等方法。<BR>　　■插入<BR>　　public synchronized void put(Object key,Object value)<BR>给对象value设定一关键字key,并将其加到Hashtable中。若此关键字已经存在，则将此关键字对应的旧对象更新为新的对象Value。这表明在哈希表中相同的关键字不可能对应不同的对象(从哈希表的基本思想来看，这也是显而易见的)。<BR>　　■检索<BR>　　public synchronized Object get(Object key)<BR>　　根据给定关键字key获取相对应的对象。<BR>　　public synchronized boolean containsKey(Object key)<BR>　　判断哈希表中是否包含关键字key。<BR>　　public synchronized boolean contains(Object value)<BR>　　判断value是否是哈希表中的一个元素。<BR>　　■删除<BR>　　public synchronized object remove(object key)<BR>　　从哈希表中删除关键字key所对应的对象。<BR>　　public synchronized void clear()<BR>　　清除哈希表<BR>　　另外，Hashtalbe还提供方法获取相对应的枚举集合：<BR>　　public synchronized Enumeration keys()<BR>　　返回关键字对应的枚举对象。<BR>　　public synchronized Enumeration elements()<BR>　　返回元素对应的枚举对象。<BR>　　例8.5 Hashtable.java给出了使用Hashtable的例子。<BR>　　例8.5 Hashtalbe.java。<BR>　　//import java.lang.*;<BR>　　import java.util.Hashtable;<BR>　　import java.util.Enumeration;<BR>　　public class HashApp{<BR>　　　public static void main(String args[]){<BR>　　　　Hashtable hash=new Hashtable(2,(float)0.8);<BR>　　　　//创建了一个哈希表的对象hash，初始容量为2，装载因子为0.8</P>
<P>　　　　hash.put("Jiangsu","Nanjing");<BR>　　　　//将字符串对象“Jiangsu”给定一关键字“Nanjing”,并将它加入hash<BR>　　　　hash.put("Beijing","Beijing");<BR>　　　　hash.put("Zhejiang","Hangzhou");</P>
<P>　　　　System.out.println("The hashtable hash1 is: "+hash);<BR>　　　　System.out.println("The size of this hash table is "+hash.size());<BR>　　　　//打印hash的内容和大小</P>
<P>　　　　Enumeration enum1=hash.elements();<BR>　　　　System.out.print("The element of hash is: ");<BR>　　　　while(enum1.hasMoreElements())<BR>　　　　　System.out.print(enum1.nextElement()+" ");<BR>　　　　System.out.println();<BR>　　　　//依次打印hash中的内容<BR>　　　　if(hash.containsKey("Jiangsu"))<BR>　　　　　System.out.println("The capatial of Jiangsu is "+hash.get("Jiangsu"));<BR>　　　　hash.remove("Beijing");<BR>　　　　//删除关键字Beijing对应对象<BR>　　　　System.out.println("The hashtable hash2 is: "+hash);<BR>　　　　System.out.println("The size of this hash table is "+hash.size());<BR>　　　}<BR>　　}</P>
<P>　　运行结果：<BR>　　The hashtable hash1 is: {Beijing=Beijing, Zhejiang=Hangzhou, Jiangsu=Nanjing}<BR>　　The size of this hash table is 3<BR>　　The element of hash is: Beijing Hangzhou Nanjing<BR>　　The capatial of Jiangsu is Nanjing<BR>　　The hashtable hash2 is: {Zhejiang=Hangzhou, Jiangsu=Nanjing}<BR>　　The size of this hash table is 2</P>
<P>　　Hashtable是Dictionary(字典)类的子类。在字典类中就把关键字对应到数据值。字典类是一个抽象类。在java.util中还有一个类Properties，它是Hashtable的子类。用它可以进行与对象属性相关的操作。</P>
<P>8.8 位集合类BitSet</P>
<P>　　位集合类中封装了有关一组二进制数据的操作。<BR>　　我们先来看一下例8.6 BitSetApp.java。<BR>　　例8.6 BitSetApp.java<BR>　　//import java.lang.*;<BR>　　import java.util.BitSet;<BR>　　public class BitSetApp{<BR>　　　private static int n=5;<BR>　　　public static void main(String[] args){<BR>　　　　BitSet set1=new BitSet(n);<BR>　　　　for(int i=0;i&lt;n;i++) set1.set(i);<BR>　　　　//将set1的各位赋1，即各位均为true<BR>　　　　BitSet set2= new BitSet();<BR>　　　　set2=(BitSet)set1.clone();<BR>　　　　//set2为set1的拷贝<BR>　　　　set1.clear(0);<BR>　　　　set2.clear(2);<BR>　　　　//将set1的第0位set2的第2位清零<BR>　　　　System.out.println("The set1 is: "+set1);<BR>　　　　//直接将set1转换成字符串输出，输出的内容是set1中值true所处的位置<BR>　　　　//打印结果为The set1 is:{1,2,3,4}<BR>　　　　System.out.println("The hash code of set2 is: "+set2.hashCode());<BR>　　　　//打印set2的hashCode<BR>　　　　printbit("set1",set1);<BR>　　　　printbit("set2",set2);<BR>　　　　//调用打印程序printbit(),打印对象中的每一个元素<BR>　　　　//打印set1的结果为The bit set1 is: false true true true true<BR>　　　　set1.and(set2);<BR>　　　　printbit("set1 and set2",set1);<BR>　　　　//完成set1 and set2,并打印结果<BR>　　　　set1.or(set2);<BR>　　　　printbit("set1 or set2",set1);<BR>　　　　//完成set1 or set2,并打印结果<BR>　　　　set1.xor(set2);<BR>　　　　printbit("set1 xor set2",set1);<BR>　　　　//完成set1 xor set2,并打印结果<BR>　　　}<BR>　　　//打印BitSet对象中的内容<BR>　　　public static void printbit(String name,BitSet set){<BR>　　　　System.out.print("The bit "+name+" is: ");<BR>　　　　for(int i=0;i&lt;n;i++)<BR>　　　　　System.out.print(set.get(i)+" ");<BR>　　　　System.out.println();<BR>　　　}<BR>　　}</P>
<P>　　运行结果：<BR>　　The set1 is: {1, 2, 3, 4}<BR>　　The hash code of set2 is: 1225<BR>　　The bit set1 is: false true true true true<BR>　　The bit set2 is: true true false true true<BR>　　The bit set1 and set2 is: false true false true true<BR>　　The bit set1 or set2 is: true true false true true<BR>　　The bit set1 xor set2 is: false false false false false</P>
<P>　　程序中使用了BitSet类提供的两种构造方法：<BR>　　　　public BitSet();<BR>　　　　public BitSet(int n);<BR>　　参数n代表所创建的BitSet类的对象的大小。BitSet类的对象的大小在必要时会由系统自动扩充。<BR>　　其它方法：<BR>　　public void set(int n)<BR>　　将BitSet对象的第n位设置成1。<BR>　　public void clear(int n)<BR>　　将BitSet对象的第n位清零。<BR>　　public boolean get(int n)<BR>　　读取位集合对象的第n位的值，它获取的是一个布尔值。当第n位为1时，返回true；第n位为0时，返回false。<BR>　　另外，如在程序中所示，当把一BitSet类的对象转换成字符串输出时，输出的内容是此对象中true所处的位置。<BR>　　在BitSet中提供了一组位操作，分别是：<BR>　　public void and(BitSet set)<BR>　　public void or(BitSet set)<BR>　　public void xor(BitSet set)<BR>利用它们可以完成两个位集合之间的与、或、异或操作。<BR>　　BitSet类中有一方法public int size()来取得位集合的大小，它的返回值与初始化时设定的位集合大小n不一样，一般为64。</P>
<P>小结</P>
<P>　　本章我们介绍了Java的实用工具类库java.util中一些常用的类。java.util包中还有其它一些类。它们的具体用法用户可以自行查阅API。在下一章，我们将学习利用java.awt包进行窗口程序设计。在下章的例子中我们将使用到本章介绍的各种类。</P><img src ="http://www.blogjava.net/xieyunlong/aggbug/30400.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2006-02-13 09:00 <a href="http://www.blogjava.net/xieyunlong/archive/2006/02/13/30400.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA中的集合类(2) </title><link>http://www.blogjava.net/xieyunlong/archive/2006/01/16/28157.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Mon, 16 Jan 2006 01:47:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2006/01/16/28157.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/28157.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2006/01/16/28157.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/28157.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/28157.html</trackback:ping><description><![CDATA[<UL>
<LI><STRONG>Vector 还是ArrayList，哪一个更好，为什么？</STRONG></LI></UL>
<P>要回答这个问题不能一概而论，有时候使用Vector比较好；有时是ArrayList，有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案，因为这要看你用它们干什么。下面有4个要考虑的因素：</P>
<P>(1)API</P>
<P>(2)同步处理</P>
<P>(3)数据增长性</P>
<P>(4)使用模式</P>
<P>下面针对这4个方面进行一一探讨</P>
<P><STRONG>API </STRONG><BR>在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述，Vector类似于ArrayList.。所有从API的角度来看这两个类非常相似。但他们之间也还是有一些主要的区别的。</P>
<P><STRONG>同步性</STRONG></P>
<P>Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的，因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率，所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择，这样可以避免由于同步带来的不必要的性能开销。</P>
<P><STRONG>数据增长</STRONG></P>
<P>从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候，如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度，Vector缺省情况下自动增长原来一倍的数组长度，ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势，因为你可以通过设置集合的初始化大小来避免不必要的资源开销。</P>
<P><STRONG>使用模式</STRONG></P>
<P>在ArrayList和Vector中，从一个指定的位置（通过索引）查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的，这个时间我们用O(1)表示。但是，如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长：O(n-i)，其中n代表集合中元素的个数，i代表元素增加或移除元素的索引位置。为什么会这样呢？以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢？</P>
<P>这意味着，你只是查找特定位置的元素或只在集合的末端增加、移除元素，那么使用Vector或ArrayList都可以。如果是其他操作，你最好选择其他的集合操作类。比如，LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1)，但它在索引一个元素的使用缺比较慢－O(i),其中i是索引的位置.使用ArrayList也很容易，因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象，所有你要明白它也会带来额外的开销。</P>
<P>最后，在《Practical Java》一书中Peter Haggar建议使用一个简单的数组（Array）来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。<BR></P><img src ="http://www.blogjava.net/xieyunlong/aggbug/28157.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2006-01-16 09:47 <a href="http://www.blogjava.net/xieyunlong/archive/2006/01/16/28157.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java.util包</title><link>http://www.blogjava.net/xieyunlong/archive/2006/01/06/26949.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Fri, 06 Jan 2006 14:03:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2006/01/06/26949.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/26949.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2006/01/06/26949.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/26949.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/26949.html</trackback:ping><description><![CDATA[ArticleContent1_ArticleContent1_lblContent"&gt;ArrayList是List接口的一个可变长数组实现。实现了所有List接口的操作，并允许存储null值。除了没有进行同步，ArrayList基本等同于Vector。在Vector中几乎对所有的方法都进行了同步，但ArrayList仅对writeObject和readObject进行了同步，其它比如add(Object)、remove(int)等都没有同步。<BR><BR>1.存储<BR>ArrayList使用一个Object的数组存储元素。<BR>private&nbsp;transient&nbsp;Object&nbsp;elementData[];<BR>ArrayList实现了java.io.Serializable接口，这儿的transient标示这个属性不需要自动序列化。下面会在writeObject()方法中详细讲解为什么要这样作。<BR><BR>2.add和remove<BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;boolean&nbsp;add(Object&nbsp;o)&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;ensureCapacity(size&nbsp;+&nbsp;1);&nbsp;&nbsp;//&nbsp;Increments&nbsp;modCount!!&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;elementData[size++]&nbsp;=&nbsp;o;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>注意这儿的ensureCapacity()方法，它的作用是保证elementData数组的长度可以容纳一个新元素。在“自动变长机制”中将详细讲解。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Object&nbsp;remove(int&nbsp;index)&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;RangeCheck(index);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;modCount++;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;oldValue&nbsp;=&nbsp;elementData[index];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;numMoved&nbsp;=&nbsp;size&nbsp;-&nbsp;index&nbsp;-&nbsp;1;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(numMoved&nbsp;&gt;&nbsp;0)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.arraycopy(elementData,&nbsp;index+1,&nbsp;elementData,&nbsp;index,&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;numMoved);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;elementData[--size]&nbsp;=&nbsp;null;&nbsp;//&nbsp;Let&nbsp;gc&nbsp;do&nbsp;its&nbsp;work&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;oldValue;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>RangeCheck()的作用是进行边界检查。由于ArrayList采用一个对象数组存储元素，所以在删除一个元素时需要把后面的元素前移。删除一个元素时只是把该元素在elementData数组中的引用置为null，具体的对象的销毁由垃圾收集器负责。<BR>modCount的作用将在下面的“iterator()中的同步”中说明。<BR>注：在前移时使用了System提供的一个实用方法：arraycopy()，在本例中可以看出System.arraycopy()方法可以对同一个数组进行操作，这个方法是一个native方法，如果对同一个数组进行操作时，会首先把从源部分拷贝到一个临时数组，在把临时数组的元素拷贝到目标位置。<BR><BR>3.自动变长机制<BR>在实例化一个ArrayList时，你可以指定一个初始容量。这个容量就是elementData数组的初始长度。如果你使用：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;ArrayList&nbsp;list&nbsp;=&nbsp;new&nbsp;ArrayList();&nbsp;<BR><BR>则使用缺省的容量：10。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;ArrayList()&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;this(10);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>ArrayList提供了四种add()方法，<BR><BR>public&nbsp;boolean&nbsp;add(Object&nbsp;o)<BR><BR>public&nbsp;void&nbsp;add(int&nbsp;index,&nbsp;Object&nbsp;element)<BR><BR>public&nbsp;boolean&nbsp;addAll(Collection&nbsp;c)<BR><BR>public&nbsp;boolean&nbsp;addAll(int&nbsp;index,&nbsp;Collection&nbsp;c)<BR><BR>在每一种add()方法中，都首先调用了一个ensureCapacity(int&nbsp;miniCapacity)方法，这个方法保证elementData数组的长度不小于miniCapacity。ArrayList的自动变长机制就是在这个方法中实现的。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;ensureCapacity(int&nbsp;minCapacity)&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;modCount++;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;oldCapacity&nbsp;=&nbsp;elementData.length;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(minCapacity&nbsp;&gt;&nbsp;oldCapacity)&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;oldData[]&nbsp;=&nbsp;elementData;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;newCapacity&nbsp;=&nbsp;(oldCapacity&nbsp;*&nbsp;3)/2&nbsp;+&nbsp;1;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(newCapacity&nbsp;&lt;&nbsp;minCapacity)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newCapacity&nbsp;=&nbsp;minCapacity;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elementData&nbsp;=&nbsp;new&nbsp;Object[newCapacity];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.arraycopy(oldData,&nbsp;0,&nbsp;elementData,&nbsp;0,&nbsp;size);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>从这个方法实现中可以看出ArrayList每次扩容，都扩大到原来大小的1.5倍。<BR>每种add()方法的实现都大同小异，下面给出add(Object)方法的实现：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;boolean&nbsp;add(Object&nbsp;o)&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;ensureCapacity(size&nbsp;+&nbsp;1);&nbsp;&nbsp;//&nbsp;Increments&nbsp;modCount!!&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;elementData[size++]&nbsp;=&nbsp;o;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR><BR>4.iterator()中的同步<BR>在父类AbstractList中定义了一个int型的属性：modCount，记录了ArrayList结构性变化的次数。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;transient&nbsp;int&nbsp;modCount&nbsp;=&nbsp;0;&nbsp;<BR><BR>在ArrayList的所有涉及结构变化的方法中都增加modCount的值，包括：add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次，modCount的值就加1。<BR>注：add()及addAll()方法的modCount的值是在其中调用的ensureCapacity()方法中增加的。<BR><BR>AbstractList中的iterator()方法（ArrayList直接继承了这个方法）使用了一个私有内部成员类Itr，生成一个Itr对象（Iterator接口）返回：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Iterator&nbsp;iterator()&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;Itr();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>Itr实现了Iterator()接口，其中也定义了一个int型的属性：expectedModCount，这个属性在Itr类初始化时被赋予ArrayList对象的modCount属性的值。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;expectedModCount&nbsp;=&nbsp;modCount;&nbsp;<BR><BR>注：内部成员类Itr也是ArrayList类的一个成员，它可以访问所有的AbstractList的属性和方法。理解了这一点，Itr类的实现就容易理解了。<BR><BR>在Itr.hasNext()方法中：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;boolean&nbsp;hasNext()&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;cursor&nbsp;!=&nbsp;size();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>调用了AbstractList的size()方法，比较当前光标位置是否越界。<BR><BR>在Itr.next()方法中，Itr也调用了定义在AbstractList中的get(int)方法，返回当前光标处的元素：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Object&nbsp;next()&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;next&nbsp;=&nbsp;get(cursor);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkForComodification();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastRet&nbsp;=&nbsp;cursor++;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;next;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch(IndexOutOfBoundsException&nbsp;e)&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkForComodification();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;NoSuchElementException();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>注意，在next()方法中调用了checkForComodification()方法，进行对修改的同步检查：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;void&nbsp;checkForComodification()&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(modCount&nbsp;!=&nbsp;expectedModCount)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;ConcurrentModificationException();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>现在对modCount和expectedModCount的作用应该非常清楚了。在对一个集合对象进行跌代操作的同时，并不限制对集合对象的元素进行操作，这些操作包括一些可能引起跌代错误的add()或remove()等危险操作。在AbstractList中，使用了一个简单的机制来规避这些风险。这就是modCount和expectedModCount的作用所在。<BR><BR>5.序列化支持<BR>ArrayList实现了java.io.Serializable接口，所以ArrayList对象可以序列化到持久存储介质中。ArrayList的主要属性定义如下：<BR><BR>private&nbsp;static&nbsp;final&nbsp;long&nbsp;serialVersionUID&nbsp;=&nbsp;8683452581122892189L;<BR><BR>private&nbsp;transient&nbsp;Object&nbsp;elementData[];<BR><BR>private&nbsp;int&nbsp;size;<BR><BR>可以看出serialVersionUID和size都将自动序列化到介质中，但elementData数组对象却定义为transient了。也就是说ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现？因为elementData数组中存储的“元素”其实仅是对这些元素的一个引用，并不是真正的对象，序列化一个对象的引用是毫无意义的，因为序列化是为了反序列化，当你反序列化时，这些对象的引用已经不可能指向原来的对象了。所以在这儿需要手工的对ArrayList的元素进行序列化操作。这就是writeObject()的作用。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;synchronized&nbsp;void&nbsp;writeObject(java.io.ObjectOutputStream&nbsp;s)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;java.io.IOException{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Write&nbsp;out&nbsp;element&nbsp;count,&nbsp;and&nbsp;any&nbsp;hidden&nbsp;stuff&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;s.defaultWriteObject();&nbsp;<BR>&nbsp;&nbsp;&nbsp;//&nbsp;Write&nbsp;out&nbsp;array&nbsp;length&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;s.writeInt(elementData.length);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Write&nbsp;out&nbsp;all&nbsp;elements&nbsp;in&nbsp;the&nbsp;proper&nbsp;order.&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i=0;&nbsp;i&lt;size;&nbsp;i++)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.writeObject(elementData[i]);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>这样元素数组elementData中的所以元素对象就可以正确地序列化到存储介质了。<BR>对应的readObject()也按照writeObject()方法的顺序从输入流中读取：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;synchronized&nbsp;void&nbsp;readObject(java.io.ObjectInputStream&nbsp;s)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;java.io.IOException,&nbsp;ClassNotFoundException&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Read&nbsp;in&nbsp;size,&nbsp;and&nbsp;any&nbsp;hidden&nbsp;stuff&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;s.defaultReadObject();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Read&nbsp;in&nbsp;array&nbsp;length&nbsp;and&nbsp;allocate&nbsp;array&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;arrayLength&nbsp;=&nbsp;s.readInt();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;elementData&nbsp;=&nbsp;new&nbsp;Object[arrayLength];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Read&nbsp;in&nbsp;all&nbsp;elements&nbsp;in&nbsp;the&nbsp;proper&nbsp;order.&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i=0;&nbsp;i&lt;size;&nbsp;i++)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elementData[i]&nbsp;=&nbsp;s.readObject();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><img src ="http://www.blogjava.net/xieyunlong/aggbug/26949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2006-01-06 22:03 <a href="http://www.blogjava.net/xieyunlong/archive/2006/01/06/26949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java新手入门的30个基本概念</title><link>http://www.blogjava.net/xieyunlong/archive/2005/12/28/25675.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Wed, 28 Dec 2005 01:35:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2005/12/28/25675.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/25675.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2005/12/28/25675.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/25675.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/25675.html</trackback:ping><description><![CDATA[在我们学习Java的过程中,掌握其中的基本概念对我们的学习无论是J2SE,J2EE,J2ME都是很重要的,J2SE是Java的基础,所以有必要对其中的基本概念做以归纳,以便大家在以后的学习过程中更好的理解java的精髓,在此我总结了30条基本的概念。 <BR><BR>Java概述: <BR><BR>　　 目前Java主要应用于中间件的开发(middleware)---处理客户机于服务器之间的通信技术,早期的实践证明,Java不适合pc应用程序的开发,其发展逐渐变成在开发手持设备,互联网信息站,及车载计算机的开发.Java于其他语言所不同的是程序运行时提供了平台的独立性,称许可以在windows,solaris,linux其他操作系统上使用完全相同的代码.Java的语法与C++语法类似,C++/C程序员很容易掌握,而且Java是完全的彻底的面向对象的,其中提出了很好的GC(Garbage Collector)垃圾处理机制,防止内存溢出。 <BR><BR>　　 Java的白皮书为我们提出了Java语言的11个关键特性。 <BR><BR>　　 (1)Easy:Java的语法比C++的相对简单,另一个方面就是Java能使软件在很小的机器上运行,基础解释其和类库的支持的大小约为40kb,增加基本的标准库和线程支持的内存需要增加125kb。 <BR><BR>　　 (2)分布式:Java带有很强大的TCP/IP协议族的例程库,Java应用程序能够通过URL来穿过网络来访问远程对象,由于servlet机制的出现,使Java编程非常的高效,现在许多的大的web server都支持servlet。 <BR><BR>　　 (3)O面向对象设计是把重点放在对象及对象的接口上的一个编程技术.其面向对象和C++有很多不同,在与多重继承的处理及Java的原类模型。 <BR><BR>　　 (4)健壮特性:Java采取了一个安全指针模型,能减小重写内存和数据崩溃的可能型。 <BR><BR>　　 (5)安全:Java用来设计网路和分布系统,这带来了新的安全问题,Java可以用来构建防病毒和防攻击的System.事实证明Java在防毒这一方面做的比较好。 <BR><BR>　　 (6)中立体系结构:Java编译其生成体系结构中立的目标文件格式可以在很多处理器上执行,编译器产生的指令字节码(Javabytecode)实现此特性,此字节码可以在任何机器上解释执行。 <BR><BR>　　 (7)可移植性:Java中对基本数据结构类型的大小和算法都有严格的规定所以可移植性很好。 <BR><BR>　　 (8)多线程:Java处理多线程的过程很简单,Java把多线程实现交给底下操作系统或线程程序完成.所以多线程是Java作为服务器端开发语言的流行原因之一。 <BR><BR>　　 (9)Applet和servlet:能够在网页上执行的程序叫Applet,需要支持Java的浏览器很多,而applet支持动态的网页,这是很多其他语言所不能做到的。 <BR><BR>基本概念: <BR><BR>　　 1.OOP中唯一关系的是对象的接口是什么,就像计算机的销售商她不管电源内部结构是怎样的,他只关系能否给你提供电就行了,也就是只要知道can or not而不是how and why.所有的程序是由一定的属性和行为对象组成的,不同的对象的访问通过函数调用来完成,对象间所有的交流都是通过方法调用,通过对封装对象数据,很大限度上提高复用率。 <BR><BR>　　 2.OOP中最重要的思想是类,类是模板是蓝图,从类中构造一个对象,即创建了这个类的一个实例(instance)。 <BR><BR>　　 3.封装:就是把数据和行为结合起在一个包中)并对对象使用者隐藏数据的实现过程,一个对象中的数据叫他的实例字段(instance field)。 <BR><BR>　　 4.通过扩展一个类来获得一个新类叫继承(inheritance),而所有的类都是由Object根超类扩展而得,根超类下文会做介绍。 <BR><BR>　　 5.对象的3个主要特性 <BR><BR>　　 behavior---说明这个对象能做什么. <BR>　　 state---当对象施加方法时对象的反映. <BR>　　 identity---与其他相似行为对象的区分标志. <BR>　　 每个对象有唯一的indentity 而这3者之间相互影响. <BR><BR>　　 6.类之间的关系: <BR><BR>use-a :依赖关系 <BR>has-a :聚合关系 <BR>is-a :继承关系--例:A类继承了B类,此时A类不仅有了B类的方法,还有其自己的方法.(个性存在于共性中) <BR><BR>　　 7.构造对象使用构造器:构造器的提出,构造器是一种特殊的方法,构造对象并对其初始化。 <BR><BR>　　 例:Data类的构造器叫Data <BR><BR>　　　 new Data()---构造一个新对象,且初始化当前时间. <BR>　　　 Data happyday=new Data()---把一个对象赋值给一个变量happyday,从而使该对象能够多次使用,此处要声明的使变量与对象变量二者是不同的.new返回的值是一个引用。 <BR><BR>　　 构造器特点:构造器可以有0个,一个或多个参数 <BR>　　 构造器和类有相同的名字 <BR>　　 一个类可以有多个构造器 <BR>　　 构造器没有返回值 <BR>　　 构造器总是和new运算符一起使用. <BR><BR>　　 8.重载:当多个方法具有相同的名字而含有不同的参数时,便发生重载.编译器必须挑选出调用哪个方法。 <BR><BR>　　 9.包(package)Java允许把一个或多个类收集在一起成为一组,称作包,以便于组织任务,标准Java库分为许多包.java.lang java.util java,net等,包是分层次的所有的java包都在java和javax包层次内。 <BR><BR>　　 10.继承思想:允许在已经存在的类的基础上构建新的类,当你继承一个已经存在的类时,那么你就复用了这个类的方法和字段,同时你可以在新类中添加新的方法和字段。 <BR><BR>　　 11.扩展类:扩展类充分体现了is-a的继承关系. 形式为:class (子类) extends (基类)。 <BR><BR>　　 12.多态:在java中,对象变量是多态的.而java中不支持多重继承。 <BR><BR>　　 13.动态绑定:调用对象方法的机制。 <BR><BR>　　 (1)编译器检查对象声明的类型和方法名。 <BR><BR>　　 (2)编译器检查方法调用的参数类型。 <BR><BR>　　 (3)静态绑定:若方法类型为priavte static final 编译器会准确知道该调用哪个方法。 <BR><BR>　　 (4)当程序运行并且使用动态绑定来调用一个方法时,那么虚拟机必须调用x所指向的对象的实际类型相匹配的方法版本。 <BR><BR>　　 (5)动态绑定:是很重要的特性,它能使程序变得可扩展而不需要重编译已存代码。 <BR><BR>　　 14.final类:为防止他人从你的类上派生新类,此类是不可扩展的。 <BR><BR>　　 15.动态调用比静态调用花费的时间要长。 <BR><BR>　　 16.抽象类:规定一个或多个抽象方法的类本身必须定义为abstract。 <BR><BR>　　 例: public abstract string getDescripition <BR><BR>　　 17.Java中的每一个类都是从Object类扩展而来的。 <BR><BR>　　 18.object类中的equal和toString方法。 <BR><BR>　　 equal用于测试一个对象是否同另一个对象相等。 <BR><BR>　　 toString返回一个代表该对象的字符串,几乎每一个类都会重载该方法,以便返回当前状态的正确表示. <BR>(toString 方法是一个很重要的方法) <BR><BR>　　 19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。 <BR><BR>　　 20.数组列表:ArrayList动态数组列表,是一个类库,定义在java.uitl包中,可自动调节数组的大小。 <BR><BR>　　 21.class类 object类中的getclass方法返回ckass类型的一个实例,程序启动时包含在main方法的类会被加载,虚拟机要加载他需要的所有类,每一个加载的类都要加载它需要的类。 <BR><BR>　　 22.class类为编写可动态操纵java代码的程序提供了强大的功能反射,这项功能为JavaBeans特别有用,使用反射Java能支持VB程序员习惯使用的工具。 <BR><BR>　　 能够分析类能力的程序叫反射器,Java中提供此功能的包叫Java.lang.reflect反射机制十分强大. <BR><BR>　　 1.在运行时分析类的能力。 <BR>　　 2.在运行时探察类的对象。 <BR>　　 3.实现通用数组操纵代码。 <BR>　　 4.提供方法对象。 <BR><BR>　　 而此机制主要针对是工具者而不是应用及程序。 <BR><BR>　　 反射机制中的最重要的部分是允许你检查类的结构.用到的API有: <BR><BR>java.lang.reflect.Field 返回字段. <BR>java.reflect.Method 返回方法. <BR>java.lang.reflect.Constructor 返回参数. <BR><BR>　　 方法指针:java没有方法指针,把一个方法的地址传给另一个方法,可以在后面调用它,而接口是更好的解决方案。 <BR><BR>　　 23.接口(Interface)说明类该做什么而不指定如何去做,一个类可以实现一个或多个interface。 <BR><BR>　　 24.接口不是一个类,而是对符合接口要求的类的一套规范。 <BR><BR>　　 若实现一个接口需要2个步骤:　 <BR><BR>　　 1.声明类需要实现的指定接口。 <BR>　　 2.提供接口中的所有方法的定义。 <BR><BR>　　 声明一个类实现一个接口需要使用implements 关键字 <BR><BR>　　 class actionB implements Comparable 其actionb需要提供CompareTo方法,接口不是类,不能用new实例化一个接口. <BR><BR>　　 25.一个类只有一个超类,但一个类能实现多个接口。Java中的一个重要接口：Cloneable <BR><BR>　　 26.接口和回调.编程一个常用的模式是回调模式,在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。 <BR><BR>　　 例:ActionListener 接口监听. <BR>　　 类似的API有:java.swing.JOptionPane <BR><BR>　　　 java.swing.Timer <BR>　　　 java.awt.Tookit <BR><BR>　　 27.对象clone:clone方法是object一个保护方法,这意味着你的代码不能简单的调用它。 <BR><BR>　　 28.内部类:一个内部类的定义是定义在另一个内部的类。 <BR><BR>　　 原因是: <BR><BR>　　 1.一个内部类的对象能够访问创建它的对象的实现,包括私有数据。 <BR><BR>　　 2.对于同一个包中的其他类来说,内部类能够隐藏起来。 <BR><BR>　　 3.匿名内部类可以很方便的定义回调。 <BR><BR>　　 4.使用内部类可以非常方便的编写事件驱动程序。 <BR><BR>　　 29.代理类(proxy): <BR><BR>　　 1.指定接口要求所有代码 <BR><BR>　　 2.object类定义的所有的方法(toString equals) <BR><BR>　　 30.数据类型:Java是强调类型的语言,每个变量都必须先申明它都类型,java中总共有8个基本类型.4种是整型,2种是浮点型,一种是字符型,被用于Unicode编码中的字符,布尔型。 <img src ="http://www.blogjava.net/xieyunlong/aggbug/25675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2005-12-28 09:35 <a href="http://www.blogjava.net/xieyunlong/archive/2005/12/28/25675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MANIFEST.MF 文件内容详解</title><link>http://www.blogjava.net/xieyunlong/archive/2005/12/10/23242.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Sat, 10 Dec 2005 02:34:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2005/12/10/23242.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/23242.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2005/12/10/23242.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/23242.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/23242.html</trackback:ping><description><![CDATA[<TABLE class="fixedTable blogpost" cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=ellipse><SPAN class=bvTitle id=subjcns!1pKAyp8dF3BleA1BpVUG7vmQ!111><STRONG>MANIFEST.MF 文件内容详解</STRONG></SPAN></TD></TR>
<TR>
<TD class=bvh8><STRONG></STRONG></TD></TR>
<TR>
<TD id=msgcns!1pKAyp8dF3BleA1BpVUG7vmQ!111>
<P>打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录，这个目录下会有一些文件，其中必有一个MANIFEST.MF，这个文件描述了该Jar文件的很多信息，下面将详细介绍MANIFEST.MF文件的内容，先来看struts.jar中包含的MANIFEST.MF文件内容：</P>
<P><FONT style="BACKGROUND-COLOR: #ffffff" color=#006400>Manifest-Version: 1.0<BR>Created-By: Apache Ant 1.5.1<BR>Extension-Name: Struts Framework<BR>Specification-Title: Struts Framework<BR>Specification-Vendor: Apache Software Foundation<BR>Specification-Version: 1.1<BR>Implementation-Title: Struts Framework<BR>Implementation-Vendor: Apache Software Foundation<BR>Implementation-Vendor-Id: org.apache<BR>Implementation-Version: 1.1<BR>Class-Path:&nbsp; commons-beanutils.jar commons-collections.jar commons-dig<BR>&nbsp;ester.jar commons-logging.jar commons-validator.jar jakarta-oro.jar s<BR>&nbsp;truts-legacy.jar</FONT></P>
<P>如果我们把MANIFEST中的配置信息进行分类，可以归纳出下面几个大类：</P>
<P><STRONG>一. 一般属性</STRONG></P>
<P>1. Manifest-Version<BR>&nbsp;用来定义manifest文件的版本，例如：Manifest-Version: 1.0<BR>2. Created-By<BR>&nbsp;声明该文件的生成者，一般该属性是由jar命令行工具生成的，例如：Created-By: Apache Ant 1.5.1<BR>3. Signature-Version<BR>&nbsp;定义jar文件的签名版本<BR>4. Class-Path<BR>&nbsp;应用程序或者类装载器使用该值来构建内部的类搜索路径</P>
<P><STRONG>二. 应用程序相关属性</STRONG></P>
<P>1. Main-Class<BR>&nbsp;定义jar文件的入口类，该类必须是一个可执行的类，一旦定义了该属性即可通过 java -jar x.jar来运行该jar文件。<BR>&nbsp;<BR><STRONG>三. 小程序(Applet)相关属性</STRONG></P>
<P>1. Extendsion-List<BR>&nbsp;该属性指定了小程序需要的扩展信息列表，列表中的每个名字对应以下的属性<BR>2. &lt;extension&gt;-Extension-Name<BR>3. &lt;extension&gt;-Specification-Version<BR>4. &lt;extension&gt;-Implementation-Version<BR>5. &lt;extension&gt;-Implementation-Vendor-Id<BR>5. &lt;extension&gt;-Implementation-URL</P>
<P><STRONG>四. 扩展标识属性</STRONG></P>
<P>1. Extension-Name<BR>&nbsp;该属性定义了jar文件的标识，例如Extension-Name: Struts Framework<BR>&nbsp;<BR><STRONG>五. 包扩展属性</STRONG><BR>&nbsp;<BR>1. Implementation-Title&nbsp; &nbsp;定义了扩展实现的标题<BR>2. Implementation-Version&nbsp; &nbsp;定义扩展实现的版本<BR>3. Implementation-Vendor &nbsp; 定义扩展实现的组织&nbsp; <BR>4. Implementation-Vendor-Id&nbsp; &nbsp;定义扩展实现的组织的标识<BR>5. Implementation-URL :&nbsp; &nbsp;定义该扩展包的下载地址(URL)<BR>6. Specification-Title&nbsp; &nbsp;定义扩展规范的标题<BR>7. Specification-Version&nbsp; &nbsp;定义扩展规范的版本<BR>8. Specification-Vendor&nbsp; &nbsp;声明了维护该规范的组织<BR>9. Sealed&nbsp; &nbsp;定义jar文件是否封存，值可以是true或者false (这点我还不是很理解)</P>
<P><STRONG>六. 签名相关属性</STRONG></P>
<P>签名方面的属性我们可以来参照JavaMail所提供的mail.jar中的一段</P>
<P>Name: javax/mail/Address.class<BR>Digest-Algorithms: SHA MD5 <BR>SHA-Digest: AjR7RqnN//cdYGouxbd06mSVfI4=<BR>MD5-Digest: ZnTIQ2aQAtSNIOWXI1pQpw==</P>
<P>这段内容定义类签名的类名、计算摘要的算法名以及对应的摘要内容(使用BASE64方法进行编码)</P>
<P><STRONG>七.自定义属性</STRONG></P>
<P>除了前面提到的一些属性外，你也可以在MANIFEST.MF中增加自己的属性以及响应的值，例如J2ME程序jar包中就可能包含着如下信息</P>
<P>MicroEdition-Configuration: CLDC-1.0<BR>MIDlet-Name: J2ME_MOBBER Midlet Suite<BR>MIDlet-Info-URL: <A href="http://www.javayou.com/">http://www.javayou.com</A><BR>MIDlet-Icon: /icon.png<BR>MIDlet-Vendor: Midlet Suite Vendor<BR>MIDlet-1: mobber,/icon.png,mobber<BR>MIDlet-Version: 1.0.0<BR>MicroEdition-Profile: MIDP-1.0<BR>MIDlet-Description: Communicator</P>
<P>关键在于我们怎么来读取这些信息呢？其实很简单，JDK给我们提供了用于处理这些信息的API，详细的信息请见java.util.jar包中，我们可以通过给JarFile传递一个jar文件的路径，然后调用JarFile的getManifest方法来获取Manifest信息。</P>
<P>更详细关于JAR文件的规范请见<BR><A href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html">http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html</A></P>
<P>中文说明<BR><A href="http://www-900.ibm.com/developerWorks/cn/java/j-jar/">http://www-900.ibm.com/developerWorks/cn/java/j-jar/</A></P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/xieyunlong/aggbug/23242.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2005-12-10 10:34 <a href="http://www.blogjava.net/xieyunlong/archive/2005/12/10/23242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小议JAVA数据类型间的相互转换</title><link>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22715.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Tue, 06 Dec 2005 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22715.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/22715.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22715.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/22715.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/22715.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="98%" align=center border=0>
<TBODY>
<TR>
<TD>
<P style="LINE-HEIGHT: 16pt"><SPAN class=bt>一些初学JAVA的朋友可能会遇到JAVA的数据类型之间转换的苦恼，例如，整数和float,double型之间的转换，整数和String类型之间的转换，以及处理、显示时间方面的问题等。下面笔者就开发中的一些体会介绍给大家。 <BR>我们知道，Java的数据类型分为三大类，即布尔型、字符型和数值型，而其中数值型又分为整型和浮点型；相对于数据类型，Java的变量类型为布尔型boolean；字符型char；整型byte、short、int、long；浮点型float、double。其中四种整型变量和两种浮点型变量分别对应于不同的精度和范围。此外，我们还经常用到两种类变量，即String和Date。对于这些变量类型之间的相互转换在我们编程中经常要用到，在下面的论述中，我们将阐述如何实现这些转换。 <BR>1 数据类型转换的种类 <BR>java数据类型的转换一般分三种,分别是: <BR>(1). 简单数据类型之间的转换 <BR>(2). 字符串与其它数据类型的转换 <BR>(3). 其它实用数据类型转换 <BR>下面我们对这三种类型转换分别进行论述。 <BR>2 简单数据类型之间的转换 <BR>在Java中整型、实型、字符型被视为简单数据类型，这些类型由低级到高级分别为 <BR>[center](byte，short，char)--int--long--float--double[/center] <BR>简单数据类型之间的转换又可以分为： <BR>●低级到高级的自动类型转换 <BR>●高级到低级的强制类型转换 <BR>●包装类过渡类型能够转换 <BR>2.1自动类型转换 <BR>低级变量可以直接转换为高级变量，笔者称之为自动类型转换,例如，下面的语句可以在Java中直接通过： <BR><BR>byte b;int i=b;long l=b;float f=b;double d=b; <BR><BR>如果低级类型为char型，向高级类型（整型）转换时，会转换为对应ASCII码值，例如 <BR><BR>char c='c'; int i=c; System.out.println("output:"+i); <BR><BR>输出：output:99; <BR>对于byte,short,char三种类型而言，他们是平级的，因此不能相互自动转换，可以使用下述的强制类型转换。 <BR><BR>short i=99;char c=(char)i;System.out.println("output:"+c); <BR><BR>输出：output:c; <BR>但根据笔者的经验，byte,short,int三种类型都是整型，因此如果操作整型数据时，最好统一使用int型。 <BR>2.2强制类型转换 <BR>将高级变量转换为低级变量时，情况会复杂一些，你可以使用强制类型转换。即你必须采用下面这种语句格式： <BR><BR>int i=99;byte b=(byte)i;char c=(char)i;float f=(float)i; <BR><BR>可以想象，这种转换肯定可能会导致溢出或精度的下降，因此笔者并不推荐使用这种转换。 <BR>2.3包装类过渡类型转换 <BR>在我们讨论其它变量类型之间的相互转换时，我们需要了解一下Java的包装类，所谓包装类，就是可以直接将简单类型的变量表示为一个类，在执行变量类型的相互转换时，我们会大量使用这些包装类。Java共有六个包装类，分别是Boolean、Character、Integer、Long、Float和Double，从字面上我们就可以看出它们分别对应于 boolean、char、int、long、float和double。而String和Date本身就是类。所以也就不存在什么包装类的概念了。 <BR>在进行简单数据类型之间的转换（自动转换或强制转换）时，我们总是可以利用包装类进行中间过渡。 <BR>一般情况下，我们首先声明一个变量，然后生成一个对应的包装类，就可以利用包装类的各种方法进行类型转换了。例如： <BR>例1，当希望把float型转换为double型时： <BR><BR>float f1=100.00f; Float F1=new float(f1); Double d1=F1.doubleValue();//F1.doubleValue()为Float类的返回double值型的方法 <BR><BR>当希望把double型转换为int型时： <BR><BR>double d1=100.00; Double D1=new Double(d1); int i1=D1.intValue(); <BR><BR>当希望把int型转换为double型时，自动转换： <BR><BR>int i1=200; double d1=i1; <BR><BR>简单类型的变量转换为相应的包装类，可以利用包装类的构造函数。即： <BR>Boolean(boolean value)、Character(char value)、Integer(int value)、Long(long value)、Float(float value)、Double(double value) <BR>而在各个包装类中，总有形为××Value()的方法，来得到其对应的简单类型数据。利用这种方法，也可以实现不同数值型变量间的转换，例如，对于一个双精度实型类，intValue()可以得到其对应的整型变量，而doubleValue()可以得到其对应的双精度实型变量。 <BR>3 字符串型与其它数据类型的转换 <BR>通过查阅类库中各个类提供的成员方法可以看到，几乎从java.lang.Object类派生的所有类提供了toString()方法，即将该类转换为字符串。例如：Characrer,Integer,Float,Double,Boolean,Short等类的toString()方法toString()方法用于将字符、整数、浮点数、双精度数、逻辑数、短整型等类转换为字符串。如下所示： <BR><BR>int i1=10;float f1=3.14f;double d1=3.1415926;Integer I1=new Integer(i1);//生成Integer类Float F1=new Float(f1); //生成Float类Double D1=new Double(d1); //生成Double类//分别调用包装类的toString()方法转换为字符串String si1=I1.toString();String sf1=F1.toString();String sd1=D1.toString();Sysytem.out.println("si1"+si1);Sysytem.out.println("sf1"+sf1);Sysytem.out.println("sd1"+sd1); <BR><BR>五、将字符型直接做为数值转换为其它数据类型 <BR>将字符型变量转换为数值型变量实际上有两种对应关系，在我们在第一部分所说的那种转换中，实际上是将其转换成对应的ASCII码，但是我们有时还需要另一种转换关系，例如，'1'就是指的数值1，而不是其ASCII码，对于这种转换，我们可以使用Character的getNumericValue(char ch)方法。 <BR>六、Date类与其它数据类型的相互转换 <BR>整型和Date类之间并不存在直接的对应关系，只是你可以使用int型为分别表示年、月、日、时、分、秒，这样就在两者之间建立了一个对应关系，在作这种转换时，你可以使用Date类构造函数的三种形式： <BR>Date(int year, int month, int date)：以int型表示年、月、日 <BR>Date(int year, int month, int date, int hrs, int min)：以int型表示年、月、日、时、分 <BR>Date(int year, int month, int date, int hrs, int min, int sec)：以int型表示年、月、日、时、分、秒 <BR>在长整型和Date类之间有一个很有趣的对应关系，就是将一个时间表示为距离格林尼治标准时间1970年1月1日0时0分0秒的毫秒数。对于这种对应关系，Date类也有其相应的构造函数：Date(long date) <BR>获取Date类中的年、月、日、时、分、秒以及星期你可以使用Date类的getYear()、getMonth()、getDate()、getHours()、getMinutes()、getSeconds()、getDay()方法，你也可以将其理解为将Date类转换成int。 <BR>而Date类的getTime()方法可以得到我们前面所说的一个时间对应的长整型数，与包装类一样，Date类也有一个toString()方法可以将其转换为String类。 <BR>有时我们希望得到Date的特定格式，例如20020324，我们可以使用以下方法，首先在文件开始引入， <BR><BR>import java.text.SimpleDateFormat;import java.util.*;java.util.Date date = new java.util.Date();//如果希望得到YYYYMMDD的格式SimpleDateFormat sy1=new SimpleDateFormat("yyyyMMDD");String dateFormat=sy1.format(date);//如果希望分开得到年，月，日SimpleDateFormat sy=new SimpleDateFormat("yyyy");SimpleDateFormat sm=new SimpleDateFormat("MM");SimpleDateFormat sd=new SimpleDateFormat("dd");String syear=sy.format(date);String smon=sm.format(date);String sday=sd.format(date); <BR><BR>结束语： <BR>当然，笔者的论述只是一人之见，如果希望更多了解有关JAVA数据类型转换的知识，笔者建议参考JAVA类库java.util.*下面的 <BR>Integer类 <BR>Boolean类 <BR>Character类 <BR>Float类 <BR>Double类 <BR>String类 <BR>Date类 <BR>根据自己的需要，参考不同类的各种成员方法进行数据类型之间的转换。 <BR>大家可以参考JAVA的网上类库或下载一份，来更灵活的利用其中的成员方法进行数据类型之间的转换，IP地址： <BR>http://java.sun.com/j2se/1.3/docs/api/index.html <BR></SPAN></P></TD></TR>
<TR>
<TD>
<DIV align=right></DIV></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/xieyunlong/aggbug/22715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2005-12-06 14:52 <a href="http://www.blogjava.net/xieyunlong/archive/2005/12/06/22715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA之精髓IO流</title><link>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22713.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Tue, 06 Dec 2005 06:43:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22713.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/22713.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22713.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/22713.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/22713.html</trackback:ping><description><![CDATA[<P style="LINE-HEIGHT: 16pt"><SPAN class=bt>一． Input和Output <BR>1. stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out stream）都包括两种类型： <BR>1.1 以字节为导向的stream <BR>以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型： <BR>1) input　stream： <BR>1) ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用 <BR>2) StringBufferInputStream：把一个String对象作为InputStream <BR>3) FileInputStream：把一个文件作为InputStream，实现对文件的读取操作 <BR>4) PipedInputStream：实现了pipe的概念，主要在线程中使用 <BR>5) SequenceInputStream：把多个InputStream合并为一个InputStream <BR>2) Out　stream <BR>1) ByteArrayOutputStream：把信息存入内存中的一个缓冲区中 <BR>2) FileOutputStream：把信息存入文件中 <BR>3) PipedOutputStream：实现了pipe的概念，主要在线程中使用 <BR>4) SequenceOutputStream：把多个OutStream合并为一个OutStream <BR>1.2 以Unicode字符为导向的stream <BR>以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型： <BR>1) Input　Stream <BR>1) CharArrayReader：与ByteArrayInputStream对应 <BR>2) StringReader：与StringBufferInputStream对应 <BR>3) FileReader：与FileInputStream对应 <BR>4) PipedReader：与PipedInputStream对应 <BR>2) Out　Stream <BR>1) CharArrayWrite：与ByteArrayOutputStream对应 <BR>2) StringWrite：无与之对应的以字节为导向的stream <BR>3) FileWrite：与FileOutputStream对应 <BR>4) PipedWrite：与PipedOutputStream对应 <BR>以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，字是在操作时的导向不同。如CharArrayReader：和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的是前者每次从内存中读取一个字节的信息，而后者每次从内存中读取一个字符。 <BR>1.3 两种不现导向的stream之间的转换 <BR>InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。 <BR>2. stream添加属性 <BR>2.1 “为stream添加属性”的作用 <BR>运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。 <BR>如果我们要往一个文件中写入数据，我们可以这样操作： <BR>FileOutStream fs = new FileOutStream(“test.txt”); <BR>然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现“先把要写入文件的数据先缓存到内存中，再把缓存中的数据写入文件中”的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类，为FileOutStream添加我们所需要的功能。 <BR>2.2 FilterInputStream的各种类型 <BR>2.2.1 用于封装以字节为导向的InputStream <BR>1) DataInputStream：从stream中读取基本类型（int、char等）数据。 <BR>2) BufferedInputStream：使用缓冲区 <BR>3) LineNumberInputStream：会记录input stream内的行数，然后可以调用getLineNumber()和setLineNumber(int) <BR>4) PushbackInputStream：很少用到，一般用于编译器开发 <BR>2.2.2 用于封装以字符为导向的InputStream <BR>1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream <BR>2) BufferedReader：与BufferedInputStream对应 <BR>3) LineNumberReader：与LineNumberInputStream对应 <BR>4) PushBackReader：与PushbackInputStream对应 <BR>2.3 FilterOutStream的各种类型 <BR>2.2.3 用于封装以字节为导向的OutputStream <BR>1) DataIOutStream：往stream中输出基本类型（int、char等）数据。 <BR>2) BufferedOutStream：使用缓冲区 <BR>3) PrintStream：产生格式化输出 <BR>2.2.4 用于封装以字符为导向的OutputStream <BR>1) BufferedWrite：与对应 <BR>2) PrintWrite：与对应 <BR>3. RandomAccessFile <BR>1) 可通过RandomAccessFile对象完成对文件的读写操作 <BR>2) 在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写 <BR>3) 可以直接跳到文件中指定的位置 <BR>4. I/O应用的一个例子 <BR>import java.io.*; <BR>public class TestIO{ <BR>public static void main(String[] args) <BR>throws IOException{ <BR>//1.以行为单位从一个文件读取数据 <BR>BufferedReader in = <BR>new BufferedReader( <BR>new FileReader("F:\\nepalon\\TestIO.java")); <BR>String s, s2 = new String(); <BR>while((s = in.readLine()) != null) <BR>s2 += s + "\n"; <BR>in.close(); <BR><BR>//1b. 接收键盘的输入 <BR>BufferedReader stdin = <BR>new BufferedReader( <BR>new InputStreamReader(System.in)); <BR>System.out.println("Enter a line:"); <BR>System.out.println(stdin.readLine()); <BR><BR>//2. 从一个String对象中读取数据 <BR>StringReader in2 = new StringReader(s2); <BR>int c; <BR>while((c = in2.read()) != -1) <BR>System.out.println((char)c); <BR>in2.close(); <BR><BR>//3. 从内存取出格式化输入 <BR>try{ <BR>DataInputStream in3 = <BR>new DataInputStream( <BR>new ByteArrayInputStream(s2.getBytes())); <BR>while(true) <BR>System.out.println((char)in3.readByte()); <BR>} <BR>catch(EOFException e){ <BR>System.out.println("End of stream"); <BR>} <BR><BR>//4. 输出到文件 <BR>try{ <BR>BufferedReader in4 = <BR>new BufferedReader( <BR>new StringReader(s2)); <BR>PrintWriter out1 = <BR>new PrintWriter( <BR>new BufferedWriter( <BR>new FileWriter("F:\\nepalon\\ TestIO.out"))); <BR>int lineCount = 1; <BR>while((s = in4.readLine()) != null) <BR>out1.println(lineCount++ + "：" + s); <BR>out1.close(); <BR>in4.close(); <BR>} <BR>catch(EOFException ex){ <BR>System.out.println("End of stream"); <BR>} <BR><BR>//5. 数据的存储和恢复 <BR>try{ <BR>DataOutputStream out2 = <BR>new DataOutputStream( <BR>new BufferedOutputStream( <BR>new FileOutputStream("F:\\nepalon\\ Data.txt"))); <BR>out2.writeDouble(3.1415926); <BR>out2.writeChars("\nThas was pi:writeChars\n"); <BR>out2.writeBytes("Thas was pi:writeByte\n"); <BR>out2.close(); <BR>DataInputStream in5 = <BR>new DataInputStream( <BR>new BufferedInputStream( <BR>new FileInputStream("F:\\nepalon\\ Data.txt"))); <BR>BufferedReader in5br = <BR>new BufferedReader( <BR>new InputStreamReader(in5)); <BR>System.out.println(in5.readDouble()); <BR>System.out.println(in5br.readLine()); <BR>System.out.println(in5br.readLine()); <BR>} <BR>catch(EOFException e){ <BR>System.out.println("End of stream"); <BR>} <BR><BR>//6. 通过RandomAccessFile操作文件 <BR>RandomAccessFile rf = <BR>new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw"); <BR>for(int i=0; i&lt;10; i++) <BR>rf.writeDouble(i*1.414); <BR>rf.close(); <BR><BR>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r"); <BR>for(int i=0; i&lt;10; i++) <BR>System.out.println("Value " + i + "：" + rf.readDouble()); <BR>rf.close(); <BR><BR>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw"); <BR>rf.seek(5*8); <BR>rf.writeDouble(47.0001); <BR>rf.close(); <BR><BR>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r"); <BR>for(int i=0; i&lt;10; i++) <BR>System.out.println("Value " + i + "：" + rf.readDouble()); <BR>rf.close(); <BR>} <BR>} <BR>关于代码的解释（以区为单位）： <BR>1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称“缓存字节读取方式”）。 <BR>1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行BufferedReader封装。 <BR>2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。 <BR>4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信息输出到缓存中，再把缓存中的信息输出到文件中。 <BR>5区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文件中的数据读取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了正确显示。也要以基本类型的形式进行读取。 <BR>6区是通过RandomAccessFile类对文件进行操作。</SPAN></P><img src ="http://www.blogjava.net/xieyunlong/aggbug/22713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2005-12-06 14:43 <a href="http://www.blogjava.net/xieyunlong/archive/2005/12/06/22713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>每个初学者都应该搞懂的问题</title><link>http://www.blogjava.net/xieyunlong/archive/2005/12/05/22614.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Mon, 05 Dec 2005 09:21:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2005/12/05/22614.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/22614.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2005/12/05/22614.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/22614.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/22614.html</trackback:ping><description><![CDATA[<P>问题1<BR>我声明了什么！</P>
<P>String s = "Hello world!";</P>
<P>许多人都做过这样的事情，但是，我们到底声明了什么？回答通常是：一个String，内容<BR>是“Hello world!”。这样模糊的回答通常是概念不清的根源。如果要准确的回答，一半<BR>的人大概会回答错误。<BR>这个语句声明的是一个指向对象的引用，名为“s”，可以指向类型为String的任何对象，<BR>目前指向"Hello world!"这个String类型的对象。这就是真正发生的事情。我们并没有声<BR>明一个String对象，我们只是声明了一个只能指向String对象的引用变量。所以，如果在<BR>刚才那句语句后面，如果再运行一句：</P>
<P>String string = s;</P>
<P>我们是声明了另外一个只能指向String对象的引用，名为string，并没有第二个对象产生<BR>，string还是指向原来那个对象，也就是，和s指向同一个对象。<BR>String s1 = "This is cool!"; // 生成一个字符串对象[OBJ_1]，将其引用赋值给s1<BR>String s2 = "This is cool!"; // 将同样的对象[OBJ_1]的引用赋值给s2<BR>String s3 = new String("This is cool!"); // 新建一个字符串对象[OBJ_2]，其引用赋<BR>值给s3;<BR>s1 = s2; // 将s2保存的[OBJ_1]的引用赋值给s1，没有实际效果<BR>s3 = "This is cool!"; // 将[OBJ_1]的引用赋值给s3，s3原先引用的[OBJ_2]不再被引用</P>
<P><BR>这样的代码共有产生2个String实例，有1对象可以被GC。</P>
<P>问题2，"=="和equals方法究竟有什么区别？</P>
<P>==操作符专门用来比较变量的值是否相等。比较好理解的一点是：<BR>int a=10;<BR>int b=10;<BR>则a==b将是true。<BR>但不好理解的地方是：<BR>String a=new String("foo");<BR>String b=new String("foo");<BR>则a==b将返回false。</P>
<P>根据前一帖说过，对象变量其实是一个引用，它们的值是指向对象所在的内存地址，而不<BR>是对象本身。a和b都使用了new操作符，意味着将在内存中产生两个内容为"foo"的字符串<BR>，既然是“两个”，它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址<BR>的值，所以使用"=="操作符，结果会是false。诚然，a和b所指的对象，它们的内容都是"<BR>foo"，应该是“相等”，但是==操作符并不涉及到对象内容的比较。<BR>对象内容的比较，正是equals方法做的事。</P>
<P>看一下Object对象的equals方法是如何实现的：<BR>boolean equals(Object o){</P>
<P>return this==o;</P>
<P>}<BR>Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法，那你的类使<BR>用equals和使用==会得到同样的结果。同样也可以看出，Object的equals方法没有达到eq<BR>uals方法应该达到的目标：比较两个对象内容是否相等。因为答案应该由类的创建者决定<BR>，所以Object把这个任务留给了类的创建者。</P>
<P>看一下一个极端的类：<BR>Class Monster{<BR>private String content;<BR>...<BR>boolean equals(Object another){ return true;}</P>
<P>}<BR>我覆盖了equals方法。这个实现会导致无论Monster实例内容如何，它们之间的比较永远返<BR>回true。</P>
<P>所以当你是用equals方法判断对象的内容是否相等，请不要想当然。因为可能你认为相等<BR>，而这个类的作者不这样认为，而类的equals方法的实现是由他掌握的。如果你需要使用<BR>equals方法，或者使用任何基于散列码的集合（HashSet,HashMap,HashTable），请察看一<BR>下 java doc以确认这个类的equals逻辑是如何实现的。</P>
<P>问题3<BR>String到底变了没有</P>
<P>没有。因为String被设计成不可变(immutable)类，所以它的所有对象都是不可变对象。请<BR>看下列代码：</P>
<P>String s = "Hello";<BR>s = s + " world!";</P>
<P>s所指向的对象是否改变了呢？从本系列第一篇的结论很容易导出这个结论。我们来看看发<BR>生了什么事情。在这段代码中，s原先指向一个String对象，内容是"Hello"，然后我们对<BR>s进行了+操作，那么s所指向的那个对象是否发生了改变呢？答案是没有。这时，s不指向<BR>原来那个对象了，而指向了另一个String对象，内容为"Hello world!"，原来那个对象还<BR>存在于内存之中，只是s这个引用变量不再指向它了。<BR>通过上面的说明，我们很容易导出另一个结论，如果经常对字符串进行各种各样的修改，<BR>或者说，不可预见的修改，那么使用String来代表字符串的话会引起很大的内存开销。因<BR>为String对象建立之后不能再改变，所以对于每一个不同的字符串，都需要一个String对<BR>象来表示。这时，应该考虑使用StringBuffer 类，它允许修改，而不是每个不同的字符串<BR>都要生成一个新的对象。并且，这两种类的对象转换十分容易。<BR>同时，我们还可以知道，如果要使用内容相同的字符串，不必每次都new一个String。例如<BR>我们要在构造器中对一个名叫s的String引用变量进行初始化，把它设置为初始值，应当这<BR>样做：<BR>public class Demo {<BR>&nbsp; private String s;<BR>&nbsp; ...<BR>&nbsp; public Demo {<BR>&nbsp;&nbsp;&nbsp; s = "Initial Value";<BR>&nbsp; }<BR>&nbsp; ...<BR>}<BR>而非<BR>s = new String("Initial Value");<BR>后者每次都会调用构造器，生成新对象，性能低下且内存开销大，并且没有意义，因为St<BR>ring对象不可改变，所以对于内容相同的字符串，只要一个String对象来表示就可以了。<BR>也就说，多次调用上面的构造器创建多个对象，他们的String类型属性s都指向同一个对象<BR>。<BR>上面的结论还基于这样一个事实：对于字符串常量，如果内容相同，Java认为它们代表同<BR>一个String对象。而用关键字new调用构造器，总是会创建一个新的对象，无论内容是否相<BR>同。<BR>至于为什么要把String类设计成不可变类，是它的用途决定的。其实不只String，很多Ja<BR>va标准类库中的类都是不可变的。在开发一个系统的时候，我们有时候也需要设计不可变<BR>类，来传递一组相关的值，这也是面向对象思想的体现。不可变类有一些优点，比如因为<BR>它的对象是只读的，所以多线程并发访问也不会有任何问题。当然也有一些缺点，比如每<BR>个不同的状态都要一个对象来代表，可能会造成性能上的问题。所以Java标准类库还提供<BR>了一个可变版本，即 StringBuffer。</P>
<P>问题4，final关键字到底修饰了什么？<BR>final使得被修饰的变量"不变"，但是由于对象型变量的本质是“引用”，使得“不变”也<BR>有了两种含义：引用本身的不变，和引用指向的对象不变。</P>
<P>引用本身的不变：<BR>final StringBuffer a=new StringBuffer("immutable");<BR>final StringBuffer b=new StringBuffer("not immutable");<BR>a=b;//编译期错误</P>
<P>引用指向的对象不变：<BR>final StringBuffer a=new StringBuffer("immutable");<BR>a.append(" broken!"); //编译通过</P>
<P>可见，final只对引用的“值”(也即它所指向的那个对象的内存地址)有效，它迫使引用只<BR>能指向初始指向的那个对象，改变它的指向会导致编译期错误。至于它所指向的对象的变<BR>化，final是不负责的。这很类似==操作符：==操作符只负责引用的“值”相等，至于这个<BR>地址所指向的对象内容是否相等， ==操作符是不管的。</P>
<P>理解final问题有很重要的含义。许多程序漏洞都基于此----final只能保证引用永远指向<BR>固定对象，不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享<BR>或修改，一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错<BR>误的解决方法就是在此对象新建的时候把它声明为final，意图使得它“永远不变”。其实<BR>那是徒劳的。</P>
<P>问题5<BR>到底要怎么样初始化！</P>
<P>本问题讨论变量的初始化，所以先来看一下Java中有哪些种类的变量。<BR>1. 类的属性，或者叫值域<BR>2. 方法里的局部变量<BR>3. 方法的参数</P>
<P>对于第一种变量，Java虚拟机会自动进行初始化。如果给出了初始值，则初始化为该初始<BR>值。如果没有给出，则把它初始化为该类型变量的默认初始值。</P>
<P>int类型变量默认初始值为0<BR>float类型变量默认初始值为0.0f<BR>double类型变量默认初始值为0.0<BR>boolean类型变量默认初始值为false<BR>char类型变量默认初始值为0(ASCII码)<BR>long类型变量默认初始值为0<BR>byte类型变量默认初始值为0<BR>short类型变量默认初始值为0<BR>所有对象引用类型变量默认初始值为null，即不指向任何对象。注意数组本身也是对象，<BR>所以没有初始化的数组引用在自动初始化后其值也是null。</P>
<P>对于两种不同的类属性，static属性与instance属性，初始化的时机是不同的。instance<BR>属性在创建实例的时候初始化， static属性在类加载，也就是第一次用到这个类的时候初<BR>始化，对于后来的实例的创建，不再次进行初始化。这个问题会在以后的系列中进行详细<BR>讨论。</P>
<P>对于第二种变量，必须明确地进行初始化。如果再没有初始化之前就试图使用它，编译器<BR>会抗议。如果初始化的语句在try块中或if块中，也必须要让它在第一次使用前一定能够得<BR>到赋值。也就是说，把初始化语句放在只有if块的条件判断语句中编译器也会抗议，因为<BR>执行的时候可能不符合if后面的判断条件，如此一来初始化语句就不会被执行了，这就违<BR>反了局部变量使用前必须初始化的规定。但如果在else块中也有初始化语句，就可以通过<BR>编译，因为无论如何，总有至少一条初始化语句会被执行，不会发生使用前未被初始化的<BR>事情。对于try-catch也是一样，如果只有在try块里才有初始化语句，编译部通过。如果<BR>在catch或finally里也有，则可以通过编译。总之，要保证局部变量在使用之前一定被初<BR>始化了。所以，一个好的做法是在声明他们的时候就初始化他们，如果不知道要出事化成<BR>什么值好，就用上面的默认值吧！</P>
<P>其实第三种变量和第二种本质上是一样的，都是方法中的局部变量。只不过作为参数，肯<BR>定是被初始化过的，传入的值就是初始值，所以不需要初始化。</P>
<P>问题6<BR>instanceof是什么东东？</P>
<P>instanceof是Java的一个二元操作符，和==，&gt;，&lt;是同一类东东。由于它是由字母组成的<BR>，所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例，<BR>返回boolean类型的数据。举个例子：</P>
<P>String s = "I AM an Object!";<BR>boolean isObject = s instanceof Object;</P>
<P>我们声明了一个String对象引用，指向一个String对象，然后用instancof来测试它所指向<BR>的对象是否是Object类的一个实例，显然，这是真的，所以返回true，也就是isObject的<BR>值为True。<BR>instanceof有一些用处。比如我们写了一个处理账单的系统，其中有这样三个类：</P>
<P>public class Bill {//省略细节}<BR>public class PhoneBill extends Bill {//省略细节}<BR>public class GasBill extends Bill {//省略细节}</P>
<P>在处理程序里有一个方法，接受一个Bill类型的对象，计算金额。假设两种账单计算方法<BR>不同，而传入的Bill对象可能是两种中的任何一种，所以要用instanceof来判断：</P>
<P>public double calculate(Bill bill) {<BR>&nbsp; if (bill instanceof PhoneBill) {<BR>&nbsp;&nbsp;&nbsp; //计算电话账单<BR>&nbsp; }<BR>&nbsp; if (bill instanceof GasBill) {<BR>&nbsp;&nbsp;&nbsp; //计算燃气账单<BR>&nbsp; }<BR>&nbsp; ...<BR>}<BR>这样就可以用一个方法处理两种子类。</P>
<P>然而，这种做法通常被认为是没有好好利用面向对象中的多态性。其实上面的功能要求用<BR>方法重载完全可以实现，这是面向对象变成应有的做法，避免回到结构化编程模式。只要<BR>提供两个名字和返回值都相同，接受参数类型不同的方法就可以了：</P>
<P>public double calculate(PhoneBill bill) {<BR>&nbsp; //计算电话账单<BR>}</P>
<P>public double calculate(GasBill bill) {<BR>&nbsp; //计算燃气账单<BR>}</P>
<P>所以，使用instanceof在绝大多数情况下并不是推荐的做法，应当好好利用多态。</P>
<P>&nbsp;</P><img src ="http://www.blogjava.net/xieyunlong/aggbug/22614.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2005-12-05 17:21 <a href="http://www.blogjava.net/xieyunlong/archive/2005/12/05/22614.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>