- 论坛徽章:
- 0
|
在Java开发工具集JDK1.0.2版本中,包含8个包(package),分别包含了语言、输入输出、窗口工具、Applet、常用工具等文献的201个类,到了1.1.1版更增加到21个包,478个类。这些类是Java的开发者们向我们提供的软件资源。根据面向对象程序设计的代码重用原则,我们可以在自己的软件中恰当地使用这些类,从而避免从最底层的、我们不太熟悉的操作做起。
6.1 如何利用类库
当你在机器上安排JDK时,可以看到一个压缩文件class.zip。这个文件缺省置于JDK安装目录的lib子目录下。我们无需对它解压缩,而且解开后浪费空间——但它包含着对我们编程工作的强有力支持,也即Java类库。
让我们用java.lang来做例子,解释对类库中类的运用。
首先必须对程序中出现的任何类注明来源,除非它是在该程序中被定义的。“注明来源”的目的是让编译器和解释器知道此类在哪里,以便引用。注明的方法有两种,其一是在程序文件的开头,用import语句:
import ;
如:import java.lang.Class;
若要引用的类较多,且在同一个package之内,则可以使用‘*’来做通配符。如:
import java.lang.*;
这种注明方法要注意两点。第一,类名之前的package名是必须的,否则类将找不到。例如未例中的java.lang.Class中,java.lang就是package名。对于没有注明package名的类,java将默认它属于一个缺省的package,这样路径就不对了。第二,‘*’通配符与我们常用的通配符含义不完全相同。不允许用
import java.lang.T*;
之类的语句。*只能用来代替完整的类名。
注明来源的方法之二是在用到该类的地方直接把类名连同package名加上去,如
public class MyThread extends java.lang.Thread{
...
}
这里的MyThread类是Thread类的子类,而Thread类是java.lang包中的类。
仅需用以上两种方法之一,你就完成了利用某个类的基础工作。应当提醒的是,即使是程序中出现的异常,也要注明来源。例如:
import java.io.IOException;
注明了输入输出异常的来源,这样程序中才能处理这种异常。
当编译时出现“class xx not found in ...”之类的错误时,请检查这个类是否被注明了来源;如果是,再检查是否拼写类名时拼错了,尤其要注意字母的大小写。这两条简单的检查可以解决大部分这类错误。
其次,正确地引用类的方法。
这个工作更为简单。当你要使用一个方法时,考虑一下它的参数、返回值以及是不是static方法(类方法)。如果它是类方法,那么你可以用.()来引用它,如
Thread.sleep(200);//sleep是类方法
否则,你需先得到一个该类的对象,再用.()来引用它,如
StringBuffer sbuf3=new StringBuffer(10);
subf3.setLength(9);
如果要接收返回值,就用相应类型的变量去接收它。如
StringBuffer sbuf2=new StringBuffer("This is the initial value");
sbuf2=sbuf2.append(" ").insert(0," ");
其中第一行创建了一个对象,第二行引用了它的append()方法。这样得到的返回值仍是一个StringBuffer类的对象,我们又对它运用insert()方法,最后得到的结果用sbuf2来接收。
这里需要解释一下如何得到一个对象。通常的作法是利用构造函数,用new来实现,但有些类是没有构造函数的,需用特殊的途径得到对象,如我们将在本章中介绍的Runtime类;有些类则根本就不能得到其对象,如Math类。因此处理时要具体问题具体分析。
类库中还有相当一部分成员是异常。对它们的处理在“异常”一章中提到过,要么在方法的开头“抛出”(throws),要么用try-catch语句捕捉。这些读者已经熟悉,就不再举例了。
在运用类和方法时,拼写错是最常见的错误之一,尤其是字母的大小写问题。其实类和方法的命名都是有规律的,一般可以望文生义地推理出其功能。类名的第一个字母总是大写,方法的第一个字母总是小写。名字由多个单词组成时,单词间无分隔符,但除第一个单词外每个单词的开头字母总是大写。记住这一点,或许会给你省不少麻烦呢。
java.lang包中的类较多。常用的类继承关系如图6.1(省略了接口和Exception和Error的众多子类)。
┌──────┐
│Object│
└──┬───┘ ┌──────┐
├────────────┤Number│
│ └─┬────┘
│ ├─Number
├─Boolean ├─Byte
├─Class ├─Float
├─Character ├─Integer
├─ClassLoader ├─Long
├─String └─Short
├─Runtime
├─StringBuffer
├─System
├─Void
├─Compiler
├─Math
├─SecurityManger
├─Process
│ ┌─────────┐
├──────────┤Throwable│
│ └───┬─────┘
├─Thread ├Error
└─ThreadGroup └Exception
图6.1 java.lang包中常用类继承关系图
从图6.1可以看出,java.lang包中的常用类可以大致分为几种。有把各个基本类型进行封装而得到的“包装类”,如Byte,Char,Integer,Double,Boolean等;有与线程有关的类,如Thread,ThreadGroup;有与Java环境有关的类,如ClassLoader,SecurityManager,Compiler;有错误类和异常类,除了图上已有的Error和Exception之外,还有这两个类的许多派生类;另外还有运行时系统类Runtime,进程类Process,系统类System等等。
这些类的继承关系较为简单。从图中可见,大多数常用类是Object类的子类,Number类是各种数类的父类,而Error和Exception都是Throwable的子类。Error又派生出许多类,表示各种错误,由Exception则派生出多个异常类(图中未标出)。
本章后面的小节将分别介绍java.lang类库中的类。
6.2 关于字符串
在这一节中我们把注意力集中于两个类:String和StringBuffer。
对字符串我们并不陌生。但Java对字符串的处理有些异乎寻常,使用了两个类。String类封装了有关字符串的操作。这里的字符串是常量,创建后就不可改变。StringBuffer类则是动态可变的字符串缓冲,它提供了一系列方法,把不同类型(如整型、布尔型等)数据插入缓冲或追加在缓冲的末尾。让我们通过一个例子来看二者的区别和使用(例6.1)。
例6.1 StringDemo.java。
1: import java.lang.*;//原文如此,实际上不应该有
2: import java.io.*;
3:
4: public class StringDemo{
5: public static void main(String args[]) throws IOException{
//String对象的哈希值
6: String s1=new String("This is the initial value.");
7: System.out.println("s1's value:" + s1 + " HashCode:" + s1.hashCode());
8: String s2="This is the initial value.";
9: System.out.println("s2's value:" + s2 + " HashCode:" + s2.hashCode());
10: s2=" "+s2+"";
11: System.out.println("s2's value:"+s2+" HashCode:"+s2.hashCode());
//StringBuffer对象的哈希值
12: StringBuffer sbuf1=new StringBuffer(s1);
13: System.out.println("sbuf1's value:"+sbuf1+" HashCode:"
14: +sbuf1.hashCode());
15: StringBuffer sbuf2=new StringBuffer(s1);
16: System.out.println("sbuf2's value:"+sbuf2+" HashCode:"
17: +sbuf2.hashCode());
18: sbuf2=sbuf2.append(" ").insert(0,"");
19: System.out.println("sbuf2's value:"+sbuf2+" HashCode:"
20: +sbuf2.hashCode());
//String 类的方法演示
//改变大小写的方法
21: System.out.println("UpperCase:"+s1.toUpperCase()+"
LowerCase:"
22: +s1.toLowerCase());
//替换字符
23: String s3=s1.replace('i','I');
24: System.out.println("Replace 'i' to 'I',we got s3:"+s3);
//判等
25: if(s3.equals(s1))System.out.println("s3 equals s1");
26: else System.out.println("s3 doesn't equals s1");
//忽略大小写判等
27: if(s3.equalsIgnoreCase(s1))
28: System.out.println("s3 equalsIgnoreCase s1");
29: else System.out.println("s3 doesn't equalsIgnoreCase s1");
//去除首尾空格
30: System.out.println("s2:(after trim)"+s2.trim());
//求特定字符在串中的位置取给定位置的字符
31: System.out.println(s1.charAt(s1.indexOf('h')));
//求子串
32: System.out.println(s1.substring(s1.indexOf("is",0),
33: s1.lastIndexOf("is")));
//转化成字符数组
34: char ch[]=s1.toCharArray();
//从字符数组的一段得到字符串
35: System.out.println(String.valueOf(ch,3,5));
36: double d=123.456;
//从双精度数得字符串
37: System.out.println(String.valueOf(d));
38: boolean b1=true;
39: int i=100;
40: long l=200000;
//StringBuffer的方法演示
41: StringBuffer sbuf3=new StringBuffer(10);
//插入和追加
42: System.out.println("sbuf3:"+sbuf3.append(b1).insert(2,i).insert(5,l));
43: try{
//设置缓冲区长度
44: sbuf3.setLength(9);
45: System.out.println("sbuf3's value(after setLength):"+sbuf3);
46: }catch(StringIndexOutOfBoundsException ex){
47: System.out.println("String index out of bounds.");
48: }
49: }
50:}
这个例子结果运行如下:
C:ookDemoch6>javac StringDemo.java
C:ookDemoch6>java StringDemo
E:Inetpubwwwrootecapitaljava utorialjava01>java StringDemo
s1's value:This is the initial value. HashCode:769637276
s2's value:This is the initial value. HashCode:769637276
s2's value: This is the initial value. HashCode:1476514748
sbuf1's value:This is the initial value. HashCode:2438296
sbuf2's value:This is the initial value. HashCode:2092911
sbuf2's value:This is the initial value. HashCode:2092911
UpperCase:THIS IS THE INITIAL VALUE.
LowerCase:this is the initial value.
Replace 'i' to 'I',we got s3:ThIs Is the InItIal value.
s3 doesn't equals s1
s3 equalsIgnoreCase s1
s2:(after trim)This is the initial value.
h
is
s is
123.456
sbuf3:tr100200000ue
sbuf3's value(after setLength):tr1002000
这个例子可能有些琐碎,但其中包含了关于这两个类的许多要点。先看第一部分(第1至20行)。
便子的第一部分着重显示String与StringBuffer在静态、动态方面的不同。hashcode()方法重写(String类)/继承(StringBuffer类)了Object类中的同名方法,作用是求出对应字符串/字符串缓冲类对象的哈希值。哈希希值与对象一一对应,可以唯一地标识对象。对照执行结果我们发现,两个相同的字符串的哈希值是相同的,而两个StringBuffer对象,尽管用同样的字符串为参数创建,哈希值却不同。原来,Java把String对象当作常量来处理,两个串常量既然值相同,就认作是一个。而StringBuffer则不同,它就像变量一样,尽管两个对象目前值相同却必须分开存储,以备发生改变。
读者看到第10行
s2=""+s2+"";
一句或许会有疑问:既然s2是String对象,它又不可改变,这里值怎么会变了呢?原因在于,提供静态字符串后,若不允许对它做任何操作,这个类本身的意义就不大了。如何解决这个问题呢?编译器做了这样的幕持球工作:它将String对象转化为StringBuffer对象,实施完操作后再变回String对象。比如,上面提到的语句可解释为:
s2=new StringBuffer("").append(s2).append("").toString();
所以,虽然名字相同,此s2却不是原先的s2了,它的哈希值已发性了改变。而我们对StringBuffer对象的悠却没有改变其哈希值。这充分表明了二者“静态”和“动态”的区别。
例子和第一段末尾(行18)用了append()和insert()方法。这两个方法是StringBuffer的主要功能所在。这两个方法有许多重载形式,比如,append()方法可在字符缓冲的末尾追加对象(Object类的对象)、字符串(String类对象)、字符数组(char[])字符数组的一部分(用两个int 值指明始末位置)、布尔值、字符、整数、浮点数、双精度浮点数等。insert()则把许多种对象插入字符缓冲之中。例子第三段第42行中给出了一些例子,演示如何将布尔值、整数、长整数放入缓冲区。
例子第二段中演示了String类的部分方法,我们无法也不必要列出该类的所有方法——读者可以轻易地从网上得到Java类库的文档来查阅——只将例子中用到的作一解释,以期起到指导读者使用的作用。
toUpperCase()和toLowerCase()是进行大/小写转换的方法。replace()方法将字符串中的某字符(第一参数)替换成另一字符(第二个参数)。它将对串中所有该字符的出现都进行替换。
equals()方法是比较两个字符串是否相等的,也即比较每个字符是否对应相等。
equalsIgnoreCase()也是判等方法,但它忽略字符大小写民的不同。trim()方法去年字符串开头和结尾的空白字符。
indexOf()方法求出某个字符在字符串中第一次出现的位置,它的参数是int型的,返值也是整型。它还有一个重载形式是以两上int型变量为参数,这时,第一个int指待查字符,第二个int指起始查找位置。如语句
indexOf('h',3);
指从第3个字符起查找'h'字符第一次出现的位置。如果等查找字符在串中并不存在,将返回-1。此外,indexOf()还可以用来查找某一子串在字符串中的位置,与前面两种重载形式相对应,也是两种形式,不同在于前面的待查找字符换成待查找的String对象即可。
indexOf()方法用来寻找给定字符(串)在字符串中第一次出现的位置,对应地,lastIndexOf()方法则寻找给定字符(串)中字符串中最后一次出现的位置。它也有四种重载形式。
charAt()方法用来取字符串中给定位置的字符,因此,参数为整型,返值为字符型。注意,用于指明位置的int值必须大于等于零,小于字符串长。否则,将出现StringIndexOutOfBoundsException,即字符串下标越界异常(由于它属于运行时异常,程序可以不处理它)。
例子的第三段(41至50行)演示了StringBuffer的方法。除了前面提过的append()和insert()方法之外,还用了一个setLength()方法,该方法强制限定字符缓冲的大小。若设小了,会发生字符的丢失(我们的例子中有意制造了这种情况),设大了,多出来的字符会置为零值。该方法也可能出现StingIndexOutOfBoundsException。
最后附加一点说明。上面的例子对于JDK1.0.2与JDK1.1.1版本中为20527408和20527520。
以上例子覆盖了这两个类的常用方法。我们没有演示的,读者也可举一反三。你将发现使用这两个类将为串操作带来莫大的方便。
6.3 类型包装
进行过程序设计的人们对“类型”并不陌生。在传统的程序设计中,类型的说明和使用都很方便。这似乎已是非常完善的概念了。然而,进入面向对象编程之后,类型却显露出一些弱点。比如,许多方法的操作是针对对象的,而无法操作某个类型的变量;类型不是类,不可以被实例化,也不可被继承。例如,在java.util包中提供了一个Vector类(以后将会讲到),它就像一个多态数组,可以存放任意类的对象。这在某些应用场合中无疑是很有用的,但我们若想在其中放一个整数却不可能。
类型不是类,这使得它在面向对象的世界里颇有些无立足之地的感觉。但我们对它的应用又太熟悉了,绝不能忍痛割爱。Java的设计者们想出了“对类型进行包装的办法。对整型、字符型、数字型、长整型、浮点型、双精度型、布尔型等基本类型分别构筑了相应的类(可参看下文例子中前七个import语句),这些类中,封装了一些有关的常量,以及相关的类转换、判断、求值等方法。也就是说,为类型创建了带有面向对象特点的“包装类”(Wrapped class)。
包装类的名字与相应的类型较为接近。类的名字首字母境外是大写的。这些类有一个共性,都有将对象转化为基本类型的方法。如Boodean类有booleanValue(),Character类有charValue()等。数类Number则有四个方法,分别求出对象所对应的int型值、long型值、float型值、double型值。Integer、Long、Float和Double类作为Number的子类,自然也拥有这四个方法。这些数值转换方法都很好记,名字都是返值类型加上“Value”。
下面让我们一起来看例子6.2,介绍一下用到的变量和方法。
例6.2WrapDemo.java
1:import java.lang.Boolean;
2:import java.lang.Character;
3:import java.lang.Number;
4:import java.lang.Double;
5:import java.lang.Float;
6:import java.lang.Integer;
7:import java.lang.Long;
8:import java.lang.System;
9:
10:public class WrapDemo{
11: public static void main(String[] args){
12: System.out.println("Demo of Boolean...");
//布尔型
13: Boolean b1=new Boolean(true);
//求对应的基本类型值
14: if(b1.booleanValue()) System.out.println("The value is true!");
//字符型
15: System.out.println("Demo of Character...");
//变为大写
16: Character ch=new Character(Character.toUpperCase('a'));
//判等
17: if(b1.equals(ch)) System.out.println("b1 equals ch");
18: else System.out.println("b1 not equals ch");
//进制转换
19: int i=Character.digit('e',16);
20: System.out.println("Character.digit('e',16)="+i);
21: System.out.println(Character.forDigit(i,16));
22: System.out.println("Demo of Integer...");
//整型
23: Integer itg=new Integer(15);
24: System.out.println("flaot value="+itg.floatValue()+" lang value="+itg.longValue()+" double valur="+itg.doubleValue());
25: System.out.println(Integer.parseInt(Integer.toString(22,16),10));
26: System.out.println("Demo of Long...");
27: try{
28: System.out.println("0xabcd="+Long.parseLong("abcd",16));
29: }catch(NumberFormatException ex){
30: System.out.println("Illegal Long");
31: }
//浮点型
32: System.out.println("Demo of Float...");
//常量
33: System.out.println("Now list the Float's static const:");
34: System.out.println(" MAX_VALUE=" + Float.MAX_VALUE );
35: System.out.println(" MIN_VALUE="+Float.MIN_VALUE);
36: System.out.println(" NaN="+Float.NaN);
37: System.out.println(" POSITIVE_INFINITY="+Float.POSITIVE_INFINITY);
38: System.out.println(" NEGATIVE_INFINITY="+Float.NEGATIVE_INFINITY);
39: Float fltnum=new Float(12345678987.98765432);
//值转换
40: System.out.println("12345678987.98765432's float value="+fltnum.floatValue()+";Int value="+fltnum.intValue());
41: System.out.println("long value="+fltnum.longValue()+";double value="+fltnum.doubleValue());
//双精度型
42: System.out.println("Demo of Double...");
43: Double db=new Double(123.4567);
44: System.out.println("123.4567's float value="+db.floatValue()+" double value="+db.doubleValue());
45: }
46:}
执行例6.2,得到 如下的运行结果:
D:archiveMyDeomoch6>java WrapDemo
Demo of Boolean...
The value is true!
Demo of Character...
b1 not equals ch
Character.digit('e',16)=14
e
Demo of Integer...
flaot value=15.0 lang value=15 double valur=15.0
16
Demo of Long...
0xabcd=43981
Demo of Float...
Now list the Float's static const:
MAX_VALUE=3.4028235E38
MIN_VALUE=1.4E-45
NaN=NaN
POSITIVE_INFINITY=Infinity
NEGATIVE_INFINITY=-Infinity
12345678987.98765432's float value=1.23456788E10;Int value=2147483647
long value=12345678848;double value=1.2345678848E10
Demo of Double...
123.4567's float value=123.4567 double value=123.4567
D:archiveMyDeomoch6>
在这个例子中,我们首先创建了一人布尔类的对象b1(13行)构造函数的参数是一个布尔值true。我们还可以用String对象来作参数,但这个字符串应转化成布尔值。比如,我们可以用“true”来做这个参数。然后我们用booleanValue()求b1对应的布尔14行。
Character类的方法较多,先看看创建对象(16行)。其构造函数唯有一个,是用一个char型值作为参数。例子中用Character.toUpperCase('a')来做参数,实际是用'A'作参数。不难看出,toUppercase()是一个类方法(static方法),作用是把字母变为大写。
以下一个语句中用了布尔类的成员方法equals(17行)。为什么能把Boolean对象与Character对象相比较呢?原来equals()的参数是Object类的对象,所有完全可以这样用,当然这样比一定是不会相等的,这样用就是为了提醒读者注意参数类型。
Character类的类方法digit(char ch,int radix)返回在radix进制下ch对应的值。如'e'在16进制中代表14。而
forDigit(int digit,int radix)
则相反,将给定进制下的一个数值转化为字符,如forDigit(14,16)返回值为'e'。
Integer类除求值的方法intValue()之外,还有parseInt()方法也可以返回int值。该方法有两种重载形式,例子中的这一种有两个参数,参数一为字符串,参数二是一个int值,指明进制。其原型为:
public static int parseInt(String s,int radix) throws NumberFormatException
功能是从字符串中分析出指定进制的整数。若串不合法,抛出数格式异常NumberFormatException。另一种重载形式不指明进制,只有一个字符串参数,功能是分析出十进制的整数,原型为
public static int parseInt(String s) throws NumberFormatException。
与parseInt()相对的,Integer类还有从int值转化为字符串的方法,也有两种形式,一种指明进制,一种不指明进制。原型为:
public static String toString(int i,int radix)
public static String toString(int i)
例子25行将parseInt()和toString()一起运用,将十进制的22变为16进制的16。
Long类中有与parseInt()相似的方法parseLong(),也有toString()方法,用法显而易见,参照例子就可以明白(28行)。
Float与Double类中均有判断是否为无穷大的方法isInfinite(),也有判断是否为NaN的方法IsNaN()。其中NaN是“Not a Number”的缩写,意即不是任意一个数。此外,数类Number的各子类中均有一些常量。例子中我们列举了Float类的一些常量作为示意。另外,我们在创建Float对象时特意选了一个较大的值,以例请读者看一下inValue()与longValue()的区别。显然,软化为整形整值后由于数过大而出现了错误。
1.1版的API中增添了Void类,以封装void类型。该类不可被实例化,只含有一个成员域TYPE:
public static final Class TYPE
即表示Void类的Class对象。
乍一接触包装类,可以会觉得它多少有些多此一举。但正如本节开头所提的,这种处理对Java这样一种纯面向对象的语言是必要的。读者可以在将来的编程实践中逐步加深体会。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/18/showart_15417.html |
|