|
2006年1月13日
Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处,后者只产生double型的随机数。 类Random中的方法十分简单,它只有两个构造方法和六个普通方法。 构造方法: (1)public Random() (2)public Random(long seed) Java产生随机数需要有一个基值seed,在第一种方法中基值缺省,则将系统时间作为seed。 普通方法: (1)public synonronized void setSeed(long seed) 该方法是设定基值seed。 (2)public int nextInt() 该方法是产生一个整型随机数。 (3)public long nextLong() 该方法是产生一个long型随机数。 (4)public float nextFloat() 该方法是产生一个Float型随机数。 (5)public double nextDouble() 该方法是产生一个Double型随机数。 (6)public synchronized double nextGoussian() 该方法是产生一个double型的Goussian随机数。 例2 RandomApp.java。 //import java.lang.*; import java.util.Random;
public class RandomApp{ public static void main(String args[]){ Random ran1=new Random(); Random ran2=new Random(12345); //创建了两个类Random的对象。 System.out.println("The 1st set of random numbers:"); System.out.println("\t Integer:"+ran1.nextInt()); System.out.println("\t Long:"+ran1.nextLong()); System.out.println("\t Float:"+ran1.nextFloat()); System.out.println("\t Double:"+ran1.nextDouble()); System.out.println("\t Gaussian:"+ran1.nextGaussian()); //产生各种类型的随机数 System.out.print("The 2nd set of random numbers:"); for(int i=0;i<5;i++){ System.out.println(ran2.nextInt()+" "); if(i==2) System.out.println(); //产生同种类型的不同的随机数。 System.out.println(); } } }
本章介绍Java的实用工具类库java.util包。在这个包中,Java提供了一些实用的方法和数据结构。例如,Java提供日期(Data)类、日历(Calendar)类来产生和获取日期及时间,提供随机数(Random)类产生各种类型的随机数,还提供了堆栈(Stack)、向量(Vector) 、位集合(Bitset)以及哈希表(Hashtable)等类来表示相应的数据结构。 图8.1给出了java.util包的基本层次结构图。下面我们将具体介绍其中几个重要的类。 ┌java.util.BitSet │java.util.Calendar │ └java.util.GregorianCalendar │java.util.Date │java.util.Dictionary │ └java.util.Hashtable │ └java.util.Properties │java.util.EventObject │java.util.ResourceBundle ┌普通类┤ ├java.util.ListResourceBundle │ │ └java.util.PropertyResourceBundle │ │java.util.Local │ │java.util.Observable │ │java.util.Random │ │java.util.StringTokenizer │ │java.util.Vector │ │ └java.util.Stack Java.util┤ └java.util.TimeZone │ └java.util.SimpleTimeZone │ ┌java.util.Enumeration ├接 口┤java.util.EventListener │ └java.util.Observer │ ┌java.util.EmptyStackException └异常类┤java.util.MissingResourceException │java.util.NoSuchElementException └java.util.TooManyListenersException 图8.1 java.util包的基本层次结构
8.2 日期类Date
Java在日期类中封装了有关日期和时间的信息,用户可以通过调用相应的方法来获取系统时间或设置日期和时间。Date类中有很多方法在JDK1.0公布后已经过时了,在8.3中我们将介绍JDK1.0中新加的用于替代Date的功能的其它类。 在日期类中共定义了六种构造函数。 (1)public Date() 创建的日期类对象的日期时间被设置成创建时刻相对应的日期时间。 例 Date today=new Date();//today被设置成创建时刻相对应的日期时间。 (2)public Date (long date) long 型的参数date可以通过调用Date类中的static方法parse(String s)来获得。 例 long l=Date.parse("Mon 6 Jan 1997 13:3:00"); Date day=new Date(l); //day中时间为1997年 1月6号星期一,13:3:00。 (3)public Date(String s) 按字符串s产生一日期对象。s的格式与方法parse中字符串参数的模式相同。 例 Date day=new Date("Mon 6 Jan 1997 13:3:00"); //day 中时间为1997年1月6号星期一,13:3:00. (4)public Date(int year,int month,int date) (5)public Date(int year,int month,int date,int hrs,int min) (6)public Date(int year,int month,int date,int hrs,int min,int sec) 按给定的参数创建一日期对象。 参数说明: year的值为:需设定的年份-1900。例如需设定的年份是1997则year的值应为97,即1997-1900的结果。所以Date中可设定的年份最小为1900; month的值域为0~11,0代表1月,11表代表12月; date的值域在1~31之间; hrs的值域在0~23之间。从午夜到次日凌晨1点间hrs=0,从中午到下午1点间hrs=12; min和sec的值域在0~59之间。 例 Date day=new Date(11,3,4); //day中的时间为:04-Apr-11 12:00:00 AM 另外,还可以给出不正确的参数。 例 设定时间为1910年2月30日,它将被解释成3月2日。 Date day=new Date(10,1,30,10,12,34); System.out.println("Day's date is:"+day); //打印结果为:Day's date is:Web Mar 02 10:13:34 GMT+08:00 1910 下面我们给出一些Date类中常用方法。 (1)public static long UTC(int year,int month,int date,int hrs. int min,int sec) 该方法将利用给定参数计算UTC值。UTC是一种计时体制,与GMT(格林威治时间)的计时体系略有差别。UTC计时体系是基于原子时钟的,而GTMT计时体系是基于天文学观测的。计算中使用的一般为GMT计时体系。 (2)public static long parse(String s) 该方法将字符串s转换成一个long型的日期。在介绍构造方法Date(long date)时曾使用过这个方法。 字符串s有一定的格式,一般为: (星期 日 年 时间GMT+时区) 若不注明时区,则为本地时区。 (3)public void setMonth(int month) (4)public int getMonth() 这两个方法分别为设定和获取月份值。 获取的月份的值域为0~11,0代表1月,11代表12月。 (5)public String toString() (6)public String toLocalString() (7)public String toGMTString() 将给定日期对象转换成不同格式的字符串。它们对应的具体的格式可参看例子8.1。 (8)public int getTimezoneOffset() 该方法用于获取日期对象的时区偏移量。 例8.1中对上面介绍的Date类中的基本方法进行了具体的应用,并打印了相应的结果。由于使用了一些过时的方法,所以编译时会有警告信息。另外,由于本例中的时间表示与平台有关,不同的JDK版本对此处理不完全相同,因此不同版本的JDK执行本例的结果可能有细微差异。 例8.1 DateApp.java import java.lang.System; import java.util.Date; public class DateApp{ public static void main(String args[]){ Date today=new Date(); //today中的日期被设成创建时刻的日期和时间,假设创建时刻为1997年3月 //23日17时51分54秒。 System.out.println("Today's date is "+today); //返回一般的时间表示法,本例中结果为 //Today's date is Fri May 23 17:51:54 1997 System.out.println("Today's date(Internet GMT)is:" +today.toGMTString()); //返回结果为GMT时间表示法,本例中结果为 //Today's date(Internet GMT)is: 23 May 1997 09:51:54:GMT System.out.println("Today's date(Locale) is:" +today.toLocaleString()); //返回结果为本地习惯的时间表示法,结果为 //Today's date(Locale)is:05/23/97 17:51:54 System.out.println("Today's year is: "+today.getYear()); System.out.println("Today's month is: "+(today.getMonth()+1)); System.out.println("Today's date is: "+today.getDate()); //调用Date类中方法,获取年月日的值。 //下面调用了不同的构造方法来创建Date类的对象。 Date day1=new Date(100,1,23,10,12,34); System.out.println("Day1's date is: "+day1); Date day2=new Date("Sat 12 Aug 1996 13:3:00"); System.out.println("Day2's date is: "+day2); long l= Date.parse("Sat 5 Aug 1996 13:3:00 GMT+0800"); Date day3= new Date(l); System.out.println("Day3's date(GMT)is: "+day3.toGMTString()); System.out.println("Day3's date(Locale)is: " +day3.toLocaleString()); System.out.println("Day3's time zone offset is:" +day3.getTimezoneOffset()); } }
运行结果(JDK1.3版,与原文不同,原文是JDK1.0版): E:\java\tutorial\java01>java DateApp Today's date is Thu Dec 27 17:58:16 CST 2001 Today's date(Internet GMT)is:27 Dec 2001 09:58:16 GMT Today's date(Locale) is:2001-12-27 17:58:16 Today's year is: 101 Today's month is: 12 Today's date is: 27 Day1's date is: Wed Feb 23 10:12:34 CST 2000 Day2's date is: Fri Aug 12 13:03:00 CST 1996 Day3's date(GMT)is: 5 Aug 1996 05:03:00 GMT Day3's date(Locale)is: 1996-8-5 13:03:00 Day3's time zone offset is:-480
E:\java\tutorial\java01>
8.3 日历类Calendar
在早期的JDK版本中,日期(Date)类附有两大功能:(1)允许用年、月、日、时、分、秒来解释日期:(2)允许对表示日期的字符串进行格式化和句法分析。在JDK1.1中提供了类Calendar来完成第一种功能,类DateFormat来完成第二项功能。dateFormat是java.text包中的一个类。与Date类有所不同的是,DateFormat类接受用各种语言和不同习惯表示的日期字符串。本节将介绍java.util包中的类Calendar及其它新增加的相关的类。 类Calendar是一个抽象类,它完成日期(Date)类和普通日期表示法(即用一组整型域如YEAR,MONTH,DAY,HOUR表示日期)之间的转换。 由于所使用的规则不同,不同的日历系统对同一个日期的解释有所不同。在JDK1.1中提供了Calendar类一个子类GregorianCalendar??它实现了世界上普遍使用的公历系统。当然用户也可以通过继承Calendar类,并增加所需规则,以实现不同的日历系统。 第GregorianCalendar继承了Calendar类。本节将在介绍类GregorianCalendar的同时顺带介绍Calendar类中的相关方法。 类GregorianCalendar提供了七种构造函数: (1)public GregorianCalendar() 创建的对象中的相关值被设置成指定时区,缺省地点的当前时间,即程序运行时所处的时区、地点的当前时间。 (2)public GregorianCalendar(TimeZone zone) 创建的对象中的相关值被设置成指定时区zone,缺省地点的当前时间。 (3)public GregorianCalendar(Locale aLocale) 创建的对象中的相关值被设置成缺省时区,指定地点aLocale的当前时间。 (4)public GregorianCalendar(TimeZone zone,Local aLocale) 创建的对象中的相关值被设置成指定时区,指定地点的当前时间。 上面使用到的类TimeZone的性质如下: TimeZone是java.util包中的一个类,其中封装了有关时区的信息。每一个时区对应一组ID。类TimeZone提供了一些方法完成时区与对应ID两者之间的转换。 (Ⅰ)已知某个特定的ID,可以调用方法 public static synchronized TimeZone getTimeZone(String ID) 来获取对应的时区对象。 例 太平洋时区的ID为PST,用下面的方法可获取对应于太平洋时区的时区对象: TimeZone tz=TimeZone.getTimeZone("PST"); 调用方法getDefault()可以获取主机所处时区的对象。 TimeZone tz=TimeZone.getDefault(); (Ⅱ)调用以下方法可以获取时区的ID ■public static synchronized String[] getavailableIDs(int rawOffset) 根据给定时区偏移值获取ID数组。同一时区的不同地区的ID可能不同,这是由于不同地区对是否实施夏时制意见不统一而造成的。 例String s[]=TimeZone.getAvailableIDs(-7*60*60*1000); 打印s,结果为s[0]=PNT,s[1]=MST ■public static synchronized String[] getAvailableIDs() 获取提供的所有支持的ID。 ■public String getID() 获取特定时区对象的ID。 例 TimeZone tz=TimeZone.getDefault(); String s=tz.getID(); 打印s,结果为s=CTT。 上面使用类的对象代表了一个特定的地理、政治或文化区域。Locale只是一种机制,它用来标识一类对象,Local本身并不包含此类对象。 要获取一个Locale的对象有两种方法: (Ⅰ)调用Locale类的构造方法 Locale(String language,String country) Locale(String language,String country,String variant) 参数说明:language??在ISO-639中定义的代码,由两个小写字母组成。 country??在ISO-3166中定义的代码,由两个大写字母组成。 variant??售货商以及特定浏览器的代码,例如使用WIN代表Windows。 (Ⅱ)调用Locale类中定义的常量 Local类提供了大量的常量供用户创建Locale对象。 例 Locale.CHINA 为中国创建一个Locale的对象。 类TimeZone和类Locale中的其它方法,读者可查阅API。 (5)public GregorianCalendar(int year,int month,int date) (6)public GregorianCalendar(int year,int month,int date,int hour,int minute) (7)public GregorianCalendar(int year,int month,int date,int hour,int minute,int second) 用给定的日期和时间创建一个GregorianCalendar的对象。 参数说明: year-设定日历对象的变量YEAR;month-设定日历对象的变量MONTH; date-设定日历对象的变量DATE;hour-设定日历对象的变量HOUR_OF_DAY; minute-设定日历对象的变量MINUTE;second-设定日历对象的变量SECOND。 与Date类中不同的是year的值没有1900这个下限,而且year的值代表实际的年份。month的含义与Date类相同,0代表1月,11代表12月。 例 GregorianCalendar cal=new GregorianCalendar(1991,2,4) cal的日期为1991年3月4号。 除了与Date中类似的方法外,Calendar类还提供了有关方法对日历进行滚动计算和数学计算。计算规则由给定的日历系统决定。进行日期计算时,有时会遇到信息不足或信息不实等特殊情况。Calendar采取了相应的方法解决这些问题。当信息不足时将采用缺省设置,在GregorianCalendar类中缺省设置一般为YEAR=1970,MONTH=JANUARY,DATE=1。 当信息不实时,Calendar将按下面的次序优先选择相应的Calendar的变量组合,并将其它有冲突的信息丢弃。 MONTH+DAY_OF_MONTH MONTH+WEEK_OF_MONTH+DAY_OF_WEEK MONTH+DAY_OF_WEEK_OF_MONTH+DAY_OF_WEEK DAY_OF+YEAR DAY_OF_WEEK_WEEK_OF_YEAR HOUR_OF_DAY
8.4 随机数类Random
Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处,后者只产生double型的随机数。 类Random中的方法十分简单,它只有两个构造方法和六个普通方法。 构造方法: (1)public Random() (2)public Random(long seed) Java产生随机数需要有一个基值seed,在第一种方法中基值缺省,则将系统时间作为seed。 普通方法: (1)public synonronized void setSeed(long seed) 该方法是设定基值seed。 (2)public int nextInt() 该方法是产生一个整型随机数。 (3)public long nextLong() 该方法是产生一个long型随机数。 (4)public float nextFloat() 该方法是产生一个Float型随机数。 (5)public double nextDouble() 该方法是产生一个Double型随机数。 (6)public synchronized double nextGoussian() 该方法是产生一个double型的Goussian随机数。 例8.2 RandomApp.java。 //import java.lang.*; import java.util.Random;
public class RandomApp{ public static void main(String args[]){ Random ran1=new Random(); Random ran2=new Random(12345); //创建了两个类Random的对象。 System.out.println("The 1st set of random numbers:"); System.out.println("\t Integer:"+ran1.nextInt()); System.out.println("\t Long:"+ran1.nextLong()); System.out.println("\t Float:"+ran1.nextFloat()); System.out.println("\t Double:"+ran1.nextDouble()); System.out.println("\t Gaussian:"+ran1.nextGaussian()); //产生各种类型的随机数 System.out.print("The 2nd set of random numbers:"); for(int i=0;i<5;i++){ System.out.println(ran2.nextInt()+" "); if(i==2) System.out.println(); //产生同种类型的不同的随机数。 System.out.println();//原文如此 } } }
运行结果: E:\java01>java RandomApp The 1st set of random numbers: Integer:-173899656 Long:8056223819738127077 Float:0.6293638 Double:0.7888394520265607 Gaussian:0.5015701094568733 The 2nd set of random numbers:1553932502 -2090749135 -287790814 -355989640 -716867186 E:\java01>
8.5 向量类Vector
Java.util.Vector提供了向量(Vector)类以实现类似动态数组的功能。在Java语言中。正如在一开始就提到过,是没有指针概念的,但如果能正确灵活地使用指针又确实可以大大提高程序的质量,比如在C、C++中所谓“动态数组”一般都由指针来实现。为了弥补这点缺陷,Java提供了丰富的类库来方便编程者使用,Vector类便是其中之一。事实上,灵活使用数组也可完成向量类的功能,但向量类中提供的大量方法大大方便了用户的使用。 创建了一个向量类的对象后,可以往其中随意地插入不同的类的对象,既不需顾及类型也不需预先选定向量的容量,并可方便地进行查找。对于预先不知或不愿预先定义数组大小,并需频繁进行查找、插入和删除工作的情况,可以考虑使用向量类。 向量类提供了三种构造方法: public vector() public vector(int initialcapacity,int capacityIncrement) public vector(int initialcapacity) 使用第一种方法,系统会自动对向量对象进行管理。若使用后两种方法,则系统将根据参数initialcapacity设定向量对象的容量(即向量对象可存储数据的大小),当真正存放的数据个数超过容量时,系统会扩充向量对象的存储容量。参数capacityIncrement给定了每次扩充的扩充值。当capacityIncrement为0时,则每次扩充一倍。利用这个功能可以优化存储。 在Vector类中提供了各种方法方便用户使用: ■插入功能 (1)public final synchronized void addElement(Object obj) 将obj插入向量的尾部。obj可以是任何类的对象。对同一个向量对象,可在其中插入不同类的对象。但插入的应是对象而不是数值,所以插入数值时要注意将数值转换成相应的对象。 例 要插入一个整数1时,不要直接调用v1.addElement(1),正确的方法为: Vector v1=new Vector(); Integer integer1=new Integer(1); v1.addElement(integer1); (2)public final synchronized void setElementAt(object obj,int index) 将index处的对象设成obj,原来的对象将被覆盖。 (3)public final synchronized void insertElementAt(Object obj,int index) 在index指定的位置插入obj,原来对象以及此后的对象依次往后顺延。 ■删除功能 (1)public final synchronized void removeElement(Object obj) 从向量中删除obj。若有多个存在,则从向量头开始试,删除找到的第一个与obj相同的向量成员。 (2)public final synchronized void removeAllElement() 删除向量中所有的对象。 (3)public final synchronized void removeElementlAt(int index) 删除index所指的地方的对象。 ■查询搜索功能 (1)public final int indexOf(Object obj) 从向量头开始搜索obj ,返回所遇到的第一个obj对应的下标,若不存在此obj,返回-1。 (2)public final synchronized int indexOf(Object obj,int index) 从index所表示的下标处开始搜索obj。 (3)public final int lastIndexOf(Object obj) 从向量尾部开始逆向搜索obj。 (4)public final synchronized int lastIndexOf(Object obj,int index) 从index所表示的下标处由尾至头逆向搜索obj。 (5)public final synchronized Object firstElement() 获取向量对象中的首个obj。 (6)public final synchronized Object lastelement() 获取向量对象中的最后一个obj。 了解了向量的最基本的方法后,我们来看一下例8.3VectorApp.java。 例8.3 VectorApp.java。 import java.util.Vector; import java.lang.*;//这一句不应该要,但原文如此 import java.util.Enumeration; public class VectorApp{ public static void main(String[] args){ Vector v1=new Vector(); Integer integer1=new Integer(1); v1.addElement("one"); //加入的为字符串对象 v1.addElement(integer1); v1.addElement(integer1); //加入的为Integer的对象 v1.addElement("two"); v1.addElement(new Integer(2)); v1.addElement(integer1); v1.addElement(integer1); System.out.println("The vector v1 is:\n\t"+v1); //将v1转换成字符串并打印 v1.insertElementAt("three",2); v1.insertElementAt(new Float(3.9),3); System.out.println("The vector v1(used method insertElementAt()) is:\n\t "+v1); //往指定位置插入新的对象,指定位置后的对象依次往后顺延 v1.setElementAt("four",2); System.out.println("The vector v1(used method setElementAt()) is:\n\t "+v1); //将指定位置的对象设置为新的对象 v1.removeElement(integer1); //从向量对象v1中删除对象integer1由于存在多个integer1所以从头开始 //找,删除找到的第一个integer1 Enumeration enum=v1.elements(); System.out.print("The vector v1(used method removeElement())is:"); while(enum.hasMoreElements()) System.out.print(enum.nextElement()+" "); System.out.println(); //使用枚举类(Enumeration)的方法来获取向量对象的每个元素 System.out.println("The position of object 1(top-to-bottom):" + v1.indexOf(integer1)); System.out.println("The position of object 1(tottom-to-top):" +v1.lastIndexOf(integer1)); //按不同的方向查找对象integer1所处的位置 v1.setSize(4); System.out.println("The new vector(resized the vector)is:"+v1); //重新设置v1的大小,多余的元素被行弃 } } 运行结果: E:\java01>java VectorApp The vector v1 is: [one, 1, 1, two, 2, 1, 1] The vector v1(used method insertElementAt()) is: [one, 1, three, 3.9, 1, two, 2, 1, 1] The vector v1(used method setElementAt()) is: [one, 1, four, 3.9, 1, two, 2, 1, 1] The vector v1(used method removeElement())is:one four 3.9 1 two 2 1 1 The position of object 1(top-to-bottom):3 The position of object 1(tottom-to-top):7 The new vector(resized the vector)is:[one, four, 3.9, 1] E:\java01> 从例8.3运行的结果中可以清楚地了解上面各种方法的作用,另外还有几点需解释。 (1)类Vector定义了方法 public final int size() 此方法用于获取向量元素的个数。它的返回值是向是中实际存在的元素个数,而非向量容量。可以调用方法capactly()来获取容量值。 方法: public final synchronized void setsize(int newsize) 此方法用来定义向量大小。若向量对象现有成员个数已超过了newsize的值,则超过部分的多余元素会丢失。 (2)程序中定义了Enumeration类的一个对象 Enumeration是java.util中的一个接口类,在Enumeration中封装了有关枚举数据集合的方法。 在Enumeration中提供了方法hawMoreElement()来判断集合中是束还有其它元素和方法nextElement()来获取下一个元素。利用这两个方法可以依次获得集合中元素。 Vector中提供方法: public final synchronized Enumeration elements() 此方法将向量对象对应到一个枚举类型。java.util包中的其它类中也大都有这类方法,以便于用户获取对应的枚举类型。
8.6 栈类Stack
Stack类是Vector类的子类。它向用户提供了堆栈这种高级的数据结构。栈的基本特性就是先进后出。即先放入栈中的元素将后被推出。Stack类中提供了相应方法完成栈的有关操作。 基本方法: public Object push(Object Hem) 将Hem压入栈中,Hem可以是任何类的对象。 public Object pop() 弹出一个对象。 public Object peek() 返回栈顶元素,但不弹出此元素。 public int search(Object obj) 搜索对象obj,返回它所处的位置。 public boolean empty() 判别栈是否为空。 例8.4 StackApp.java使用了上面的各种方法。 例8.4 StackApp.java。 import java.lang.*; import java.util.*; public class StackApp{ public static void main(String args[]){ Stack sta=new Stack(); sta.push("Apple"); sta.push("banana"); sta.push("Cherry"); //压入的为字符串对象 sta.push(new Integer(2)); //压入的为Integer的对象,值为2 sta.push(new Float(3.5)); //压入的为Float的对象,值为3.5 System.out.println("The stack is,"+sta); //对应栈sta System.out.println("The top of stack is:"+sta.peek()); //对应栈顶元素,但不将此元素弹出 System.out.println("The position of object Cherry is:" +sta.search("cherry")); //打印对象Cherry所处的位置 System.out.print("Pop the element of the stack:"); while(!sta.empty()) System.out.print(sta.pop()+" "); System.out.println(); //将栈中的元素依次弹出并打印。与第一次打印的sta的结果比较,可看出栈 //先进后出的特点 } } 运行结果(略)
8.7 哈希表类Hashtable
哈希表是一种重要的存储方式,也是一种常见的检索方法。其基本思想是将关系码的值作为自变量,通过一定的函数关系计算出对应的函数值,把这个数值解释为结点的存储地址,将结点存入计算得到存储地址所对应的存储单元。检索时采用检索关键码的方法。现在哈希表有一套完整的算法来进行插入、删除和解决冲突。在Java中哈希表用于存储对象,实现快速检索。 Java.util.Hashtable提供了种方法让用户使用哈希表,而不需要考虑其哈希表真正如何工作。 哈希表类中提供了三种构造方法,分别是: public Hashtable() public Hashtable(int initialcapacity) public Hashtable(int initialCapacity,float loadFactor) 参数initialCapacity是Hashtable的初始容量,它的值应大于0。loadFactor又称装载因子,是一个0.0到0.1之间的float型的浮点数。它是一个百分比,表明了哈希表何时需要扩充,例如,有一哈希表,容量为100,而装载因子为0.9,那么当哈希表90%的容量已被使用时,此哈希表会自动扩充成一个更大的哈希表。如果用户不赋这些参数,系统会自动进行处理,而不需要用户操心。 Hashtable提供了基本的插入、检索等方法。 ■插入 public synchronized void put(Object key,Object value) 给对象value设定一关键字key,并将其加到Hashtable中。若此关键字已经存在,则将此关键字对应的旧对象更新为新的对象Value。这表明在哈希表中相同的关键字不可能对应不同的对象(从哈希表的基本思想来看,这也是显而易见的)。 ■检索 public synchronized Object get(Object key) 根据给定关键字key获取相对应的对象。 public synchronized boolean containsKey(Object key) 判断哈希表中是否包含关键字key。 public synchronized boolean contains(Object value) 判断value是否是哈希表中的一个元素。 ■删除 public synchronized object remove(object key) 从哈希表中删除关键字key所对应的对象。 public synchronized void clear() 清除哈希表 另外,Hashtalbe还提供方法获取相对应的枚举集合: public synchronized Enumeration keys() 返回关键字对应的枚举对象。 public synchronized Enumeration elements() 返回元素对应的枚举对象。 例8.5 Hashtable.java给出了使用Hashtable的例子。 例8.5 Hashtalbe.java。 //import java.lang.*; import java.util.Hashtable; import java.util.Enumeration; public class HashApp{ public static void main(String args[]){ Hashtable hash=new Hashtable(2,(float)0.8); //创建了一个哈希表的对象hash,初始容量为2,装载因子为0.8
hash.put("Jiangsu","Nanjing"); //将字符串对象“Jiangsu”给定一关键字“Nanjing”,并将它加入hash hash.put("Beijing","Beijing"); hash.put("Zhejiang","Hangzhou");
System.out.println("The hashtable hash1 is: "+hash); System.out.println("The size of this hash table is "+hash.size()); //打印hash的内容和大小
Enumeration enum1=hash.elements(); System.out.print("The element of hash is: "); while(enum1.hasMoreElements()) System.out.print(enum1.nextElement()+" "); System.out.println(); //依次打印hash中的内容 if(hash.containsKey("Jiangsu")) System.out.println("The capatial of Jiangsu is "+hash.get("Jiangsu")); hash.remove("Beijing"); //删除关键字Beijing对应对象 System.out.println("The hashtable hash2 is: "+hash); System.out.println("The size of this hash table is "+hash.size()); } }
运行结果: The hashtable hash1 is: {Beijing=Beijing, Zhejiang=Hangzhou, Jiangsu=Nanjing} The size of this hash table is 3 The element of hash is: Beijing Hangzhou Nanjing The capatial of Jiangsu is Nanjing The hashtable hash2 is: {Zhejiang=Hangzhou, Jiangsu=Nanjing} The size of this hash table is 2
Hashtable是Dictionary(字典)类的子类。在字典类中就把关键字对应到数据值。字典类是一个抽象类。在java.util中还有一个类Properties,它是Hashtable的子类。用它可以进行与对象属性相关的操作。
8.8 位集合类BitSet
位集合类中封装了有关一组二进制数据的操作。 我们先来看一下例8.6 BitSetApp.java。 例8.6 BitSetApp.java //import java.lang.*; import java.util.BitSet; public class BitSetApp{ private static int n=5; public static void main(String[] args){ BitSet set1=new BitSet(n); for(int i=0;i<n;i++) set1.set(i); //将set1的各位赋1,即各位均为true BitSet set2= new BitSet(); set2=(BitSet)set1.clone(); //set2为set1的拷贝 set1.clear(0); set2.clear(2); //将set1的第0位set2的第2位清零 System.out.println("The set1 is: "+set1); //直接将set1转换成字符串输出,输出的内容是set1中值true所处的位置 //打印结果为The set1 is:{1,2,3,4} System.out.println("The hash code of set2 is: "+set2.hashCode()); //打印set2的hashCode printbit("set1",set1); printbit("set2",set2); //调用打印程序printbit(),打印对象中的每一个元素 //打印set1的结果为The bit set1 is: false true true true true set1.and(set2); printbit("set1 and set2",set1); //完成set1 and set2,并打印结果 set1.or(set2); printbit("set1 or set2",set1); //完成set1 or set2,并打印结果 set1.xor(set2); printbit("set1 xor set2",set1); //完成set1 xor set2,并打印结果 } //打印BitSet对象中的内容 public static void printbit(String name,BitSet set){ System.out.print("The bit "+name+" is: "); for(int i=0;i<n;i++) System.out.print(set.get(i)+" "); System.out.println(); } }
运行结果: The set1 is: {1, 2, 3, 4} The hash code of set2 is: 1225 The bit set1 is: false true true true true The bit set2 is: true true false true true The bit set1 and set2 is: false true false true true The bit set1 or set2 is: true true false true true The bit set1 xor set2 is: false false false false false
程序中使用了BitSet类提供的两种构造方法: public BitSet(); public BitSet(int n); 参数n代表所创建的BitSet类的对象的大小。BitSet类的对象的大小在必要时会由系统自动扩充。 其它方法: public void set(int n) 将BitSet对象的第n位设置成1。 public void clear(int n) 将BitSet对象的第n位清零。 public boolean get(int n) 读取位集合对象的第n位的值,它获取的是一个布尔值。当第n位为1时,返回true;第n位为0时,返回false。 另外,如在程序中所示,当把一BitSet类的对象转换成字符串输出时,输出的内容是此对象中true所处的位置。 在BitSet中提供了一组位操作,分别是: public void and(BitSet set) public void or(BitSet set) public void xor(BitSet set) 利用它们可以完成两个位集合之间的与、或、异或操作。 BitSet类中有一方法public int size()来取得位集合的大小,它的返回值与初始化时设定的位集合大小n不一样,一般为64。
小结
本章我们介绍了Java的实用工具类库java.util中一些常用的类。java.util包中还有其它一些类。它们的具体用法用户可以自行查阅API。在下一章,我们将学习利用java.awt包进行窗口程序设计。在下章的例子中我们将使用到本章介绍的各种类。
(前言) 根据上一讲,你或许大概已经了解了Eclipse的组成,以及大致的运行机理. 这篇文章, 将 开始带您使用eclipse. (正文) [Eclipse的工作台使用指南] 这部分要写的话,其实要写很多,而且最好方式则是图文并茂,最好再有演示. 我这里只是 给一些总体的介绍,并给予一些使用上的指导. 个人感觉,如果你从来 没碰过eclipse, 启动之后,最好先看一下help, 这样会比较好. 具体的操作步骤是这样的, 启动eclipse后, 选 菜单里的help-> help contents, 此时 会弹出一个新的窗口,就是eclipse的帮助窗口, 这个窗口的左边是一个导航 条,选择Workbench User Guide, 里面分为Getting Started, Concepts, Tasks 和Refer ence. 可以先看一下 Getting Started里面的Basic Tutorial. 这份 tutorial可以在最短的时间内,让你熟悉eclipse的工作台. 其实,eclipse平台ui方面有这几个组件: 菜单, 工具栏, 视图, 透视图, 编辑器 菜单和工具栏不用说了, 地球人都知道的, 视图就是view, 比如 Navigator, Outline, Tasks 等等都是视图, 每一个视图都有自己相应的功能,你可以参看 workbench user guide来了解这些视图. 编辑器,就是editor, 比如有开发java的编辑器 , 编写文本的编辑器,等等, 最后还有一个叫透视图, 英文是perspective, 这个东东其实是不同的view,menu,toolbar,editor的排列组合. 比如你开发java, 你经 常会用到package explorer, tasks, outline等view和编写java的editor, 以及适合开发java的菜单(Source和Refactor), 那它就会布局一个适合开发java的透视 图, 以此类推. 下面我介绍一下常用菜单项: + File - New: 新建文件,项目,或者其他都是从这里进入 - Import: 这个也是很有用的,比如别人开发的eclipse项目,你copy到 你机器上,可以通过import把这个项目导入工作区 - Export: 这个是导出功能, 比如你开发了一个项目,最后想导出一个 运行库,jar包之类的都可以用这个,这样你就不用自己手动的去把那些class 文件打包了 + Window - New Window: 你如果觉得在一个window下开发东西太挤的话,可以再开一 个,等于冒出两个workbench,其实操作的resource都是一样的. - Open Perspective: eclipse有很多透视图如resource, java, java browsing, cvs, debug等等,你可以根据当前开发的需要,选择你要的透视图进行开发 - Open View: 透视图毕竟有限,不可能把所有的view都帮你开好, 所以你 如果发觉你要用某一个view,但是它没有开,就用这个选项 - Customize Perspective: 毕竟每个人都有自己的习惯, 你觉得这个透视图用 的不爽, 可以自己定制的 - Reset Perspective: 给你定制的一塌糊涂, 唉, 没办法, 还原成老样子把, 就用这个 - Preference: 这个是非常重要的选项, 偶是没事就进去改的, 它保存了 很多配置方面的东西, 比如字体, 快捷键设置, 很多很多方面都要用到这个 的, 这个东西的详细介绍, 会稍后介绍. + Help - Welcome: eclipse很多插件都做了welcome page,这个page对很多初学 者来说,很有用的,否则很多情况下,新的东西是无从下手的. - Help Content: 前面讲过了,用来启动帮助系统 - Update: 这个以后会具体介绍 - About Platform: 你可以从这里了解你装了哪些features和plugins [利用 eclipse 开发简单的 java 程序] 好了,我们一起step by step来学一下把, 很easy的 1. 菜单 new -> project, 然后在new project 对话框里选 java ->java project, 按n ext 按钮 2. 输入 project 的名字, 按 next 按钮, 当然如果你不想把项目的根目录建在默认的 地方,也可以取消掉use default ,然后自己设定目录 3. 之后就 finish 把, 都用默认配置 4. 如果你没有在java透视图下面,它会提示你是否跳到java透视图,选择是 5. ok 一个项目就建好了 6. 之后,你就可以 new class 开始写java程序了 7. 比如你的new一个class,如下 public class A{
public static void main(String args[]){
System.out.println("Hello World");
}
}
8. 编辑好保存, 然后跳到菜单 -> run -> run as -> java application 9. 你可以看到console view中就冒出Hello World了 很easy把, 当然, 你会在开发中会遇到很多问题, 这是必然的, 那这些问题只能在实践 中积累才能得到解决, 所以不用急,多用用,多玩玩,遇到问题经常到版上来问 问. 还有如果你发现问题的话, 找问题解决方案的第一个地方,应该是eclipse的帮助系统里 的Java Development User Guide, 几乎绝大多数问题,上面都有答案. 所以 有空的话还是要多读一读. 不过,很多人都说用了eclipse之后,就抛弃了其他的java ide, 说明它必有爽的地方, 那 我来介绍一下用eclipse 开发java,有哪些爽的地方 [Java Development Tool (JDT) 特色] 其实特色有很多,我也只是凭我的开发经验, 介绍一下jdt的突出功能. 1. 自动修饰代码功能 这个功能很大程度上, 把平时一些开发代码中的琐碎的工作给自动化了 打开菜单 + Source - Comment: 这个比较有用,比如你写java代码,发觉你有一段代码要注 释掉,那就选中那一块代码,然后选这项,他就会自动把这段代码注释掉. 快捷 健是 "Ctrl+/" - Uncomment: 反注释, 操作方法和Comment差不多, 快捷键是 "Ctrl+\" - Format: 这个操作项是我一直推荐的, 非常方便, 比如你写了段格 式很烂的代码, 乱七八糟的, 那你就选择这项,你会发觉, 哇~~, 我的代码怎 么一下子变漂亮了, 不信你可以试试, 快捷键是"Ctrl+Shift+F", 所以我现在都养成习 惯了,写一会儿代码,就c+s+f一下, 呵呵,很方便的. - Sort Member: 这个不是很常用,但是如果你觉得代码太长,老是找不到函 数,,也可以试试. 他会帮你把你写的函数,变量重新排序 - Organize Import: 这个功能也是一个不用不爽的功能, 比如你编一个项目, 发现有很多import都没有用到,或者说你引用了一个类,但是你没有import, 结果编译不通过, 那都没关系, 一用这个,所有的问题迎刃而解. 至少我用这个之后, 就 从来没有写过import这类语句了. 快捷键是"Ctrl+Shift+O(是字母O,不是数 字0)", 我经常把这个和c+s+f一起用, 呵呵,人也变懒了不少 - Override/Implement Method: 这个比如你写一个类, 实现了某一个接口,但是 你还没有实现那个接口的函数, 那就用这个,它会自动搜索父类和接口的方 法,你可以选择要覆盖还是实现哪些函数 - Generate Getter and Setter: 如果你加了一个类变量, 要为它写getter和se tter,不用那么麻烦, 用这个把,都是自动的 - Generate Delegating Method: 如果某一个field要生成代理函数,用这个把, 选一下就ok - Add Javadoc Comment: 点中某一个你想要加javadoc的函数或类或变量,然后 选这项,它会自动帮你加好javadoc的头,包括你用的那些param或return - surround with try/catch block: 比如你有一段代码要处理某些exception, 可你又忘了用try/catch来写,别急,选中那段代码,然后选这项,你会发觉它会 自动针测你这段代码里要抛出哪些exception,并且自动生成好所有代码 - externalize string: 这个是在做国际化的时候用的,简单的说,就是把string 包在resource bundle里, 这也是i18n的一个解决方案,我想我以后会详细介 绍这方面的东西. 2. 重构功能: 重构这两年很热, 那让我们看看jdt里面的重构到底有多强. 呵呵, 这也是eclipse最吸 引我的一个地方. 打开菜单 + Refactor - Rename: 如果你写了一个类,你发觉这个类某个类变量的名字起的不太好听 , 你觉得不爽, 于是你就想改名字, 那怎么改呢, 就把那个变量名改了 ? 呵呵,没那么简单,因为你这个变量如果已经在某些函数里引用到,那编译要出错了, 而 且你根本就不知道你哪里引用了, 写了那么多代码,脑子都晕了, 那不是死 菜了吗? 别紧张, 用这个rename可以帮你解决一切问题, 它不仅可以帮你把变量的名字 改了,而且它还会自动搜寻所有这个变量被引用到的地方,然后把那些地方也 一起改了, 爽不爽啊, 给你省了很大的劳动力不是. - Move: 同样, 要移动一个实现了的静态函数或变量到别的类的话, 用这个移动, 保证不出错 - Modify Method Signature: 你设计函数不可能一下子就定型的,比如你一开始这 个函数有一个参数,后来发觉不对,要用两个, 那你就要用这个来改, 这 样它还会搜寻所有已经引用这个函数的地方, 并且把这些应用的地方也改掉,否则编译也 要出错的 - Extract Interface: 它可以帮你把一个类抽象成一个接口, 规范你的代码 - Extract Method: 如果你写了一段很长的函数, 但是这个函数有些代码有 重复利用性, 你就可以把给分割出来, 选中那段代码,然后选这项,系统会问 你抽出来的函数的定义, 然后它就会生成这段函数,把实现从原来函数那里抽出来,并在 原来函数那里写一个对这个新函数的引用,以保证程序不变性. - Extract Local Variable: 如果你发觉你函数里有些值都是通过一样的表达 式得到的,你就可以通过这项把这段表达式变成一个变量,并且把这个变量替 换到引用到表达式的地方 - Extract Constant: 抽取常量, 比如一个string= "eclipse", 你觉得很多 地方要用到, 那就抽出来变成一个常量 ECLIPSE, 就这么简单 - convert local variable to field : 这个看名字就知道, 不多说了 - encapsulate field: 这个和生成getter,setter有点类似, 但是不同的在于, 如果你有一个public的常量 var,并且已经在别处引用到了, 那你用生成 getter,setter肯定有问题, 因为引用的地方没改过来, 用这个的话,不仅会生成getter, setter,而且还会改掉所有引用的地方, 比如把var = ...;的地方改成 setvar(...), 把 ... = var地方改成 ... = getvar(); , 呵呵,够强把... 3. 敏感帮助: 这个jb之类的ide也有, 启动方式为"alt+/" , 你如果觉得这个不爽,可以选preference- >workbench->keys->edit->content assist 修改键值 而且,这个敏感帮助还有一个强的地方在于: 你如果想写一个for语句, 呵呵, 就打 for, 然后alt+/, 选一个for的生成方式, 一个完 成的for语句就出来了, 呵呵,eclipse多用用, 人都会变懒的 这个功能其实是jdt的模板功能,你也可以加自己用的模板, 具体在 preference-> java -> editor ->templates 加 4. Quick Fix功能: 比如我们来编一个类A, 如下: public class A {
public static void main(String args[]){
System.out.prin("Hello World");
}
}
编译是不通过的,print方法打错了, 打成prin了 所有你会发觉那一行的左边有个红差差,说明这行有错误,你把鼠标移到那个红差差上,它 会有一个提示出来,告诉你出了什么错了 这还不止,你还发觉在红差差左边有个电灯泡, 你点那个电灯泡,它会弹出框问你是chang e to print 还是change to println, 选择change to print, 它就自动帮 你改好了,呵呵,连改错都这么方便,太爽了. 当然,如果没有电灯泡的话,你就只能手动改 了,毕竟这东西没这么智能,可以帮你自动解决所有的问题
(前言) 今天这篇侧重于eclipse的内部结构剖析,对于想开发插件的同志们,这些都是基础知识, 可以好好看看. 由于我写这个东西,也是随性发挥,想到什么就写什么. 而关于eclipse的 如何使用,如何用eclipse来开发一个java项目或其他项目之类的文章, 我想我会在以后 的文章中写到. 但我想对于要在开发eclipse上开发web项目,c项目或其他的话,你也要会 自己能安装相应的插件,才能开发,所以,这些基础知识的对于这些人来说还是很有必要的 . (正文) 上一回我们且说到eclipse的下载,安装,启动. 其中讲到eclipse目录结构时,你会发现有 两个目录,一个叫plugins,一个叫features,而且你会发觉就这两个目录就占了整个eclip se项目的9x%的空间,如果少了这两个目录,呵呵,eclipse根本就是空架子. 那这里面到底 存放了些什么东西呢, 让我们来研究一下. [什么是 plug-in] 我们来做一个比喻, 你买了一套新房子,买过来是毛坯房,然后你稍微装修一下,铺了地板 ,上了墙纸, 当然现在大家都要用家用电器, 没电咋行, 所以我们就要布好电线,装好电 源插座. 这个时候,你可以把这个房子想像成eclipse这个平台. 之后, 我们或许就要添 置家用电器了,比如电视, 音响等等, 等我们买好回家, 然后把电源往插座上一插, 那我 们就抱着孩子, 搂着老婆, 看电视, 听音乐, 舒舒服服的过上幸福美满的小生活了~~~ 同样的, eclipse的plug-in 也是同样的工作原理, plug-in 只要放到 /p lugins目录下, eclipse启动后就会自动给所有在这个目录下的plug-ins, 通上电, 那这 些plug-ins就会自动的运行起来, 美妙的eclipse界面也随之呈现在你眼前. 那接下来,让我们看看eclipse这个由插件组成的平台,到底是个什么样的架构 [Eclipse 平台架构] --------------------------------------------- | Eclipse Platform | -------- | | ---\ | | | ----------------------- ------------ |==| _ |______| JDT | | |Workbench | | | |==| | | | | | | | | | ---/ -------- | | | | | | | | ---------------| | Help | | | | | jface | | | | | |----------- | | | | | | SWT | | | | | | | | | ------------ | | ----------------------- | | ------------ | -------- | ------------------ | | | ---\ | | | |Workspace | | | |==| _ |______| PDE | | | | | Team | |==| | | | | ------------------ | | | ---/ -------- | | | | | ------------ | ---------------------------------------------
Eclipse Platform 就是一个房子, workbench,jface, swt, workspace, help, team, jdt, pde都是基于这个平台的插件.
下面我介绍一下这些基础插件的基本功能:
*: workbench用来控制工作台, 负责控制工作台上包括菜单,视图,透视图等等的控制和 操作 *: SWT是一个类似AWT,SWING的java组件,是一个轻量级的组件,而且和awt,swing不同的 是,它底层实现不是基于jre,而是根据不同操作系统,有相应的动态链接库实现,所以作出 来的效果很专业, SWT主要用于workbench的ui绘制 *: jface是基于SWT的一个插件, 对SWT进行了封装, 封装实现了对话框, 视图等东东 *: workspace是用来控制工作区的,(有别于工作台), 包括对工作区内的项目的控制,删 除,添加,编译项目资源等等都由它来控制 *: help是一个eclipse帮助系统, eclipse的菜单->Help-> Help Content,就可以打开这 个帮助系统, 这个系统不是封闭的, 可以进行扩展(以后会介绍做eclipse帮助的插件) *: team是一个cvs系统,可以和CVS server协调使用,进行版本控制 *: jdt 是 Java Development Tools, 开发java的插件 *: pde 是 plug-in development environment, 开发插件的平台
[plug-in 的基本结构]
每一个plug-in都用一个目录包起来, 而且起目录名也是有讲究的,比如plug-in的名字叫 edu.sjtu.bbs.eclipse,版本是1.0.0, 那这个目录名就是edu.sjtu.bbs.eclipse_1.0.0.
而且随便打开一个plugin目录,可以发现总有一个文件叫一个叫plugin.xml,这个文件对 于plugin来说十分重要, 它相当于定义了plugin的运行参数,没有这个,plugin无法启动, 就像你家的电冰箱如果不知道是用110V还是220V的,你也不敢乱往插座上插,所以总要有 个说明,这个说明就是plugin.xml. 至于这个文件有些什么具体结构,我想在以后介绍编 写插件的时候,我会详细介绍.
[什么是 feature]
feature是功能部件,它里面没有实际的运行的库,它只是eclipse用来管理plugins的一种 途径. 比如你家装了电灯,总要有开关控制把,比如大堂的灯有一个开关控制, 卧室的灯 也有一个开关控制, 它们分别用来控制灯的亮与灭. 同样,功能部件就是用来控制插件的启动与否. eclipse的update透视图可以设定各个功 能部件的启用或禁用状态, 所以你可以通过禁用功能部件,来禁止插件的启动. 这样有一 个好处,比如你装了很多插件在eclipse上,但是装的越多,加载就越多,启动也会变慢, 你 不信的话,可以玩玩wsad, 就知道我说的话不是假的了. 所以,我们可以把功能部件看作是插件或插件集合的开关, 用来控制插件的状态. 如果pl ugins目录有插件没有被任何一个功能部件包络的话, 我称之为"野插件", 就是eclipse 启动,它也一定会启动, 就相当于没有开关, 电源一直连通一样.
当然,功能部件还有很多其他方面的用处,以后会有详细介绍.
(前言)
Eclipse这个新的东东,大家都很感兴趣,为了帮助初学者揭开Eclipse的神秘面纱,也为了 总结一下自己的使用开发经验,所以打算写下来共享给大家,当然我也是在不断的摸索中, 文中如有不对之处,还望大家指正.
(正文)
Eclipse项目是IBM在2001年捐献的一个开发平台,当时此项目评估价值为40million USD. 此东东如此值钱,可见自是有过人之处. 接下来直接转入正题, 哪里可以下到Eclip se呢.
[Eclipse 下载]
下载Eclipse的官方网站:
http://www.eclipse.org/downloads/
这个网站上有很多mirror,你可以根据网路,选择自己最方便的下载mirror 如果你在交大的校园的话,呵呵,那就去这里
ftp://ftp.sjtu.edu.cn/mirror/sites/download.eclipse.org/
这个目录是也是一个Eclipse网站的镜像,但是为什么没有公布在Eclipse.org网站上,这 我就不清楚了 你可以从上述网站上找到Eclipse的下载包 上了网站,你会发现有许多Eclipse版本,有1.x, 2.x, 3.x,那到底该用什么版本呢?
[Eclipse 版本 (以及WSAD版本)]
IBM有个深受大家欢迎的产品,叫做Webshpere Studio Application Developer (WSAD), 当时WSAD4.0发布时用的Eclipse是1.0版本的,后来IBM把Eclipse捐献出来后,就陆续开发 了2.x版本,而且也应用到了后来的WSAD产品,现在WSAD5.0用的是Eclipse 2.0, WSAD 5.1 用的是Eclipse 2.1.1. 现在Eclipse.org Release的最近Release的版本是Eclipse 2.1.3, 相信大家平时要用Ec lipse做开发的话,用2.1.3比较合适. 同时,如果你如果想尝尝鲜的话,就试试Eclipse 3.0版本,现在3.0还没有Release, Eclipse 3.0的开发过程是分成10个Milestone来分的,现在已经开发到第8个Milestone, 就是Eclipse 3.0M8. 大家可以试试. 预计3.0的正式版本要到今年6月发布, 相信到时候 发布不久后, WSAD6.0就会发布了. 呵呵,期待啊 @_@
另外对于Eclipse的开发,还有一种版本标记方式,是分别已 N, I, R 打头的, 比如 N20040101之类的, 大家会觉得纳闷,这到底代表什么意思呢. 其实, N 是 Nightly Build , I 是 Integration Build, R 是 Release Build, 特别是对于大型软件, 有时 编译一个项目要花很多时间, 所以很多时候编译的工作就放在了夜里, 所以就有了 Nightly Build 这一说, 而Nightly版本在经过集成测试,就生成了 Integration 版本. Integration版本再经过严格的测试,最后就发布Release版本. 所以 稳定性程度: R > I > N.
相信大家了解了这个版本后,就可以当自己想要的版本了. 等当好了之后,我们就可以安装了
[Eclipse 安装] (以 R2.1.3 版本为例)
当下来的东东是一个压缩包, 然后你要做的就是把这个压缩包解压都某一个目录,为了方 便介绍,我们叫这个目录为.
让我们展开一下这个目录看看:
| --- /plugins 存放插件的目录 (稍后介绍) --- /features 存放功能部件的目录 (稍后介绍) --- /links 其他plugins和features的连接地址的存放目录 (稍后介绍) --- /readme --- eclipse.exe 启动Eclipse程序 --- ...
[启动 Eclipse]
但是如果你机器还没有装jre的话,那eclipse还是不能启动的 如果没有的话,去java.sun.com当一个jre,安装好jre之后才能启动 如果你同时有几个jre, 那eclipse会自动搜索注册表,并找到版本高的jre使用.
提示: 如果想指定eclipse使用你想它使用的jre的话,可以设置eclipse.exe的启动参数:
eclipse.exe -vm
然后你就可以启动eclipse了, 慢慢等, 还是挺慢的, 你可以顺便喝口茶, 欣赏欣赏eclipse的splash画面, 呵呵 启动好后, eclipse的开发平台就展现你的眼前了.
在这个启动的时候, eclipse自动创建了一个workspace, 你可以在的目 录下看到一个workspace的目录,这个目录下还有一个.metadata的目录,这个目录存着你 这个启动的工作区的所有配置.
当然,如果你不想把workspace这个工作区目录放到别的目录下,也没有问题,设置一下eclipse的启动参数: eclipse.exe -data
以后,你如果同时要开发好几个项目, 当时有不想放一个工作区里, 学会启动的不同的工作区是必要的
- Vector 还是ArrayList,哪一个更好,为什么?
要回答这个问题不能一概而论,有时候使用Vector比较好;有时是ArrayList,有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案,因为这要看你用它们干什么。下面有4个要考虑的因素:
(1)API
(2)同步处理
(3)数据增长性
(4)使用模式
下面针对这4个方面进行一一探讨
API 在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述,Vector类似于ArrayList.。所有从API的角度来看这两个类非常相似。但他们之间也还是有一些主要的区别的。
同步性
Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销。
数据增长
从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。
使用模式
在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?
这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选择其他的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的使用缺比较慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,所有你要明白它也会带来额外的开销。
最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。
http://forum.javaeye.com/viewtopic.php?p=17108&highlight=Serializable+generate+SessionImplementor+sessionImplementor%2C#17108
看了看。真是万分高兴,可能别人用了一个星期才解决的问题。我现在就可以不劳而获。惭愧啊。。
由于担心Hibernate自身提供的increment id generator有性能影响,我对increment进行了扩展,改为InMemoryIncrement:
InMemoryIncrement.java
代码: //Declare Classes Import here,Do not use .* to import all the classes in package. import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties;
import java.util.Map; import java.util.HashMap;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;
import net.sf.hibernate.HibernateException; import net.sf.hibernate.MappingException; import net.sf.hibernate.dialect.Dialect; import net.sf.hibernate.engine.SessionImplementor; import net.sf.hibernate.type.Type; import net.sf.hibernate.id.IdentifierGenerator; import net.sf.hibernate.id.PersistentIdentifierGenerator; import net.sf.hibernate.id.Configurable;
/** * Hibernate high performence id generator extend IncrementGenerator.<br> * This generator use ’select max(column) from table ’ to get id’s init value * in first time.Then generate increment id in memory . * <p> * 原理: * <li>使用long[]数组在内存中存储每个表的最大值,使用Map存储表名在数组中对应的索引值。 * <li>第一次访问某个表的id的最大值时会从数据库中取出并存放到long数组中, * 同时在Map中保存索引值 * <li>第二次以后访问会首先从Map中读出索引值,根据索引值把long数组对应的值 * 加上递增步长并返回 * </p> * @author fenghm * @version $Revision$ */ public final class InMemoryIncrement implements IdentifierGenerator, Configurable {
private static final Log log = LogFactory.getLog(InMemoryIncrement.class); //存储最大值的数组的容量 private static final int MAX_CAPACITY = 200; /**同步锁*/ private static final Object lock = new Object();
//存储表存储在数组中的索引值 private static Map map = new HashMap(); //递增步长,默认加1 private int step = 1; //最大值数组 private static long[] seqs = new long[MAX_CAPACITY]; //最大值数组已经使用的容量 private static int lastIndex; private String key; private String sql; private Connection connection; private Class returnClass; /** * (none java doc) * @see net.sf.hibernate.id.IdentifierGenerator# * generate(net.sf.hibernate.engine.SessionImplementor, java.lang.Object) */ public Serializable generate(SessionImplementor session, Object object) throws SQLException, HibernateException { connection = session.connection(); long seq = -1; //找到索引值 int index = findIndex(); //把最大值加1 seqs[index] = seqs[index] + step; seq = seqs[index]; return new Long(seq); }
/** * 找到表中自动增长字段存储在数组中的索引值 * @return 索引值 */ private int findIndex(){ int index = 0; //首先中缓存中取出索引值 Integer integer = (Integer)map.get(key); //如果没有找到就从数据库中读出最大值并进行cache if(null == integer){ //double check lock synchronized(lock){ integer = (Integer)map.get(key); if(null == integer){ long maxvalue = 1; try{ maxvalue = getMaxvalue(); }catch(SQLException e){ log.error(e); } integer = new Integer(lastIndex++); seqs[integer.intvalue()] = maxvalue; map.put(key,integer); } } } index = integer.intvalue(); return index; } /** * (none java doc) * @see net.sf.hibernate.id.Configurable#configure(net.sf.hibernate.type.Type, * java.util.Properties, net.sf.hibernate.dialect.Dialect) */ public void configure(Type type, Properties params, Dialect d) throws MappingException { //取出table参数 String table = params.getProperty("table"); if (table == null){ table = params.getProperty(PersistentIdentifierGenerator.TABLE); } //取出column参数 String column = params.getProperty("column"); if (column == null){ column = params.getProperty(PersistentIdentifierGenerator.PK); } //表的sehcma参数 String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA); returnClass = type.getReturnedClass(); //取出step参数 String stepvalue = params.getProperty("step"); if(null != stepvalue && !"".equals(stepvalue.trim())){ try{ step = Integer.parseInt(stepvalue); }catch(Exception e){ log.error(e); } }
//构造存储在Map中的索引值的key name key = table + "_$_" + column; //根据参数构造取最大值的SQL sql = "select max(" + column + ") from "; if(null != schema){ sql += schema + "."; } sql += table; } /** * 取指定表中id字段的最大值,不存在记录返回0 * @return 最大值 * @throws SQLException if sql error occurs. */ private long getMaxvalue() throws SQLException { long maxvalue = 0; PreparedStatement st = connection.prepareStatement(sql); ResultSet rs = null; try { rs = st.executeQuery(); if(rs.next()) { maxvalue = rs.getLong(1); } sql = null; }finally { if (rs != null){ rs.close(); } st.close(); } return maxvalue; }
测试代码: Data.java 代码: public class Data {
private long id; private String name; private String email;
/** * @return */ public String getEmail() { return email; }
/** * @return */ public long getId() { return id; }
/** * @return */ public String getName() { return name; }
/** * @param string */ public void setEmail(String string) { email = string; }
/** * @param l */ public void setId(long l) { id = l; }
/** * @param string */ public void setName(String string) { name = string; } }
Data.hbm.xml 代码: <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> <hibernate-mapping package="com.kmview.test.hibernate.model"> <class name="Data" table="data"> <id name="id" type="long" column="id"> <generator class="com.kmview.test.hibernate.Generator"/> </id> <property name="name" type="java.lang.String" length="20" column="name"/> <property name="email" type="java.lang.String" column="email"/> </class> </hibernate-mapping>
ThreadMain.java 代码:
public class ThreadMain extends Thread {
public static void main(String[] args) { try{ Configuration cfg = new Configuration().configure(); SchemaExport schema = new SchemaExport(cfg); schema.create(false,true);
for(int i=0;i<100;i++){ new ThreadMain().start(); } }catch(Exception e){ e.printStackTrace(); } } public void run(){ try{ for(int i=0;i<20;i++){ Session session = HibernateSession.openSession();
Data data = new Data(); data.setName("sdfsfsdf"); data.setEmail("sdflkas;lfdalsfjasljf@d.d");
session.save(data); session.flush(); HibernateSession.closeSession(); } }catch(Exception e){ e.printStackTrace(); } } }
HibernateSession.java 代码: public final class HibernateSession {
private HibernateSession(){} //synchronized lock for getSessionFactory private static Object lock = new Object(); //common log object private static Log log = LogFactory.getLog(HibernateSession.class); //Hibernate SessionFactory private static SessionFactory sessionFactory; //Implement Hibatenate ThreadLocal pattern private static final ThreadLocal session = new ThreadLocal(); /** * 自动在ClassPath的根路径寻找hibernate.properties与hibernate.cfg.xml文件<br> * 并初始化SessionFactory * @return SessionFactory */ private static SessionFactory buildSessionFactory(){ SessionFactory factory = null; try{ Configuration config = new Configuration(); config = config.configure(); factory = config.buildSessionFactory(); log.info("ok,SessionFactory builded!"); }catch(HibernateException e){ log.error(e); } return factory; } /** * 取得当前会话的Hibernae Session对象,并打开数据库连接 * @return Hibernate Session Object for Data Access * @exception HibernateException throw HibernateException */ public static Session openSession() throws HibernateException{ Session s = (Session)session.get(); if(null == s){ getSessionFactory(); if(null != sessionFactory){ s = sessionFactory.openSession(); session.set(s); }else{ log.error("Error,SessionFactory object is not inited!"); } }else if(!s.isConnected()){ s.reconnect(); } return s; } /** * 关闭数据库连接,在每次Session用完的时候,应该马上调用closeSession,<br> * 以把数据库连接归还到数据库连接池中,提高并发性能。 */ public static void closeSession(){ try{ Session s = (Session)session.get(); if(null != s && s.isConnected()){ s.disconnect(); } }catch(HibernateException e ){ log.error(e); } } /** * 释放Hibernate Session对象,此方法应该在当前线程消亡之前调用。<br> * 例如:在Web应用中可以使用filter统一在最后进行调用<br> * <code><pre> * public class HibernateFilter extends HttpServlet implements Filter{ * private FilterConfig filterConfig; * public void init(FilterConfig filterConfig){ * this.filterConfig = filterConfig; * } * * public void doFilter(ServletRequest request,ServletResponse response, * FilterChain filterChain){ * try{ * filterChain.doFilter(request,response); * } catch(ServletException sx){ * filterConfig.getServletContext().log(sx.getMessage()); * } catch(IOException iox){ * filterConfig.getServletContext().log(iox.getMessage()); * }finally{ * HibernateSession.releaseSession(); * } * } * } * </pre></code> * 在Hibernate的API文档提到,Session的close()方法不是必须要被调用的,<br> * 但disconnect()方法是必须的;所以此方法也不是必须要被调用的,但是<br> * closeSession()方法是必须的。 * @exception HibernateException throw HibernateException */ public static void releaseSession() throws HibernateException{ Session s = (Session)session.get(); session.set(null); if(null != s){ s.close(); } } /** * 设置SessionFactory,提供此方法是为了可以在外部初始化SessionFactory. * @param factory SessionFactory */ public static void setSessionFactory(SessionFactory factory){ if(null == sessionFactory){ sessionFactory = factory; } } /** * 获取SessionFactory,第一次访问时会自动在ClassPath的根路径寻找<br> * hibernate.properties与hibernate.cfg.xml文件并初始化。 * @return SessionFactory */ public static SessionFactory getSessionFactory(){ //Double check lock. if(null == sessionFactory){ synchronized(lock){ if(null == sessionFactory){ sessionFactory = buildSessionFactory(); } } } return sessionFactory; } }
从网上当了一个例子,发觉上传图片文件时图片文件中内容发生了变化,最大的变化时所有的FF变成了3F
也就是说输出时把字符当成有符号传送了?(是这样把?)
使用代码如下:
PrintWriter pw = new PrintWriter(new BufferedWriter(new
FileWriter((savePath==null? "" : savePath) + filename)));
while (i != -1 && !newLine.startsWith(boundary)) {
// 文件内容的最后一行包含换行字符
// 因此我们必须检查当前行是否是最
// 后一行
i = in.readLine(line, 0, 1280);
if ((i==boundaryLength+2 || i==boundaryLength+4)
&& (new String(line, 0, i).startsWith(boundary)))
pw.print(newLine.substring(0, newLine.length()-2));
else
pw.print(newLine);
newLine = new String(line, 0, i);
}
pw.close();
但是总是不行,后来我把PrintWriter用FileOutputStream替代,代码如下:
FileOutputStream pw=new FileOutputStream((savePath==null? "" : savePath) + filename);
//PrintWriter pw = new PrintWriter(new BufferedWriter(new
//FileWriter((savePath==null? "" : savePath) + filename)));
while (i != -1 && !newLine.startsWith(boundary)) {
// 文件内容的最后一行包含换行字符
// 因此我们必须检查当前行是否是最
// 后一行
i = in.readLine(line, 0, 1280);
if ((i==boundaryLength+2 || i==boundaryLength+4)
&& (new String(line, 0, i).startsWith(boundary)))
pw.write(newLine.substring(0, newLine.length()-2).getBytes("ISO8859_1"));
else
pw.write(newLine.getBytes("ISO8859_1"));
newLine = new String(line, 0, i,"ISO8859_1");
}
pw.close();
竟然就可以了,那个转换的编码还一定要上才行,不然也不干活的,真是郁闷,这跨平台就不能让写CODE的人解放解放,一个输出都N多类,看着心寒,想着简直想死啊
哎 忙了我一个下午,原来就是这回事
下面把原代码附上,是把别人的代码改吧改吧放上来的,还请原作者不要见意:
Java bean 文件:
package cr.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletInputStream;
import java.util.Dictionary;
import java.util.Hashtable;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.*;
public class FileUploadBean {
private String savePath, filepath, filename, contentType;
private byte[] b;
byte t;
private Dictionary fields;
public String getFilename() {
return filename;
}
public String getFilepath() {
return filepath;
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
public String getContentType() {
return contentType;
}
public String getFieldValue(String fieldName) {
if (fields == null || fieldName == null)
return null;
return (String) fields.get(fieldName);
}
private void setFilename(String s) {
if (s==null)
return;
int pos = s.indexOf("filename=\"");
if (pos != -1) {
filepath = s.substring(pos+10, s.length()-1);
// Windows浏览器发送完整的文件路径和名字
// 但Linux/Unix和Mac浏览器只发送文件名字
pos = filepath.lastIndexOf("\\");
if (pos != -1)
filename = filepath.substring(pos + 1);
else
filename = filepath;
}
}
private void setContentType(String s) {
if (s==null)
return;
int pos = s.indexOf(": ");
if (pos != -1)
contentType = s.substring(pos+2, s.length());
}
public void getByte(HttpServletRequest request)
{
DataInputStream is;
int i=0;
try
{
is=new DataInputStream(request.getInputStream());
b=new byte[request.getContentLength()];
while (true)
{
try
{
t=is.readByte();
b[i]=t;
i++;
}
catch(EOFException e)
{ break;}
}
is.close();}
catch(IOException e)
{}
}
public void doUpload1(HttpServletRequest request) throws
IOException {
byte[] line=new byte[128];
FileOutputStream os=new FileOutputStream("c:\\Demo.out");
ServletInputStream in = request.getInputStream();
getByte(request);
String temp="";
temp=new String(b,"ISO8859_1");
byte[] img=temp.getBytes("ISO8859_1");
for (int i=0;i<img.length;i++)
{ os.write(img[i]); }
os.close();
}
public void doUpload(HttpServletRequest request) throws IOException {
request.setCharacterEncoding("GB2312");
ServletInputStream in = request.getInputStream();
byte[] line = new byte[1280];
int i = in.readLine(line, 0, 1280);
if (i < 3)
return;
int boundaryLength = i - 2;
String boundary = new String(line, 0, boundaryLength); //-2丢弃换行字符
fields = new Hashtable();
while (i != -1) {
String newLine = new String(line, 0, i);
if (newLine.startsWith("Content-Disposition: form-data; name=\"")) {
if (newLine.indexOf("filename=\"") != -1) {
setFilename(new String(line, 0, i-2));
if (filename==null)
return;
//文件内容
i = in.readLine(line, 0, 1280);
setContentType(new String(line, 0, i-2));
i = in.readLine(line, 0, 1280);
//空行
i = in.readLine(line, 0, 1280);
newLine = new String(line, 0, i,"ISO8859_1");
FileOutputStream pw=new FileOutputStream((savePath==null? "" : savePath) + filename);
//PrintWriter pw = new PrintWriter(new BufferedWriter(new
//FileWriter((savePath==null? "" : savePath) + filename)));
while (i != -1 && !newLine.startsWith(boundary)) {
// 文件内容的最后一行包含换行字符
// 因此我们必须检查当前行是否是最
// 后一行
i = in.readLine(line, 0, 1280);
if ((i==boundaryLength+2 || i==boundaryLength+4)
&& (new String(line, 0, i).startsWith(boundary)))
pw.write(newLine.substring(0, newLine.length()-2).getBytes("ISO8859_1"));
else
pw.write(newLine.getBytes("ISO8859_1"));
newLine = new String(line, 0, i,"ISO8859_1");
}
pw.close();
}
else {
// 普通表单输入元素
// 获取输入元素名字
int pos = newLine.indexOf("name=\"");
String fieldName = newLine.substring(pos+6, newLine.length()-3);
i = in.readLine(line, 0, 1280);
i = in.readLine(line, 0, 1280);
newLine = new String(line, 0, i);
StringBuffer fieldValue = new StringBuffer(1280);
while (i != -1 && !newLine.startsWith(boundary)) {
// 最后一行包含换行字符
// 因此我们必须检查当前行是否是最后一行
i = in.readLine(line, 0, 1280);
if ((i==boundaryLength+2 || i==boundaryLength+4)
&& (new String(line, 0, i).startsWith(boundary)))
fieldValue.append(newLine.substring(0, newLine.length()-2));
else
fieldValue.append(newLine);
newLine = new String(line, 0, i);
}
fields.put(fieldName, fieldValue.toString());
}
}
i = in.readLine(line, 0, 1280);
}
}
}
HTML页面:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>文件上载</title>
</head>
<body>
<form action=jsp1.jsp enctype="MULTIPART/FORM-DATA" method=post>
作者: <input type=text name=author>
<br>
公司: <input type=text name=company>
<br>
说明: <textarea name=comment></textarea>
<br>
选择要上载的文件<input type=file name=filename>
<br>
文件描述: <input type=text name=description>
<br>
<input type=submit value="Upload">
</form>
</body>
</html>
JSP页面:
<%@ page contentType="text/html;charset=gb2312"%>
<jsp:useBean id="TheBean" scope="page" class="cr.web.FileUploadBean" />
<%
TheBean.setSavePath("d:\\");
//TheBean.doUpload1(request);
TheBean.doUpload(request);
out.println("Filename:" + TheBean.getFilename());
out.println("<BR>内容类型:" + TheBean.getContentType());
out.println("<BR>作者:" + TheBean.getFieldValue("Author"));
out.println("<BR>公司:" + TheBean.getFieldValue("Company"));
out.println("<BR>说明:" + TheBean.getFieldValue("Comment"));
%>
|