java.io包含许多允许发送和接收数据的统一的输入输出(I/O)操作。数据的来源或目的地事实上可以是任何事物——来自于键盘的输入输出到显示器,文件,甚至服务器/客户端架构。Java中I/O背后的魔力是通过流(streams)实现的。流只是或者从源读出,或者写入目的地的字符或字节的序列。
Java中I/O以两个叫做java.io.Reader和java.io.Writer的抽象类开始,所有其他的I/O操作都是在扩展它们功能的子类中实现的。
稍后,我们会研究处理声音,图像,文件和网络通信的流。因为GUI应用程序处理用户输入与控制台应用程序不同,所以我们不必成为写Java控制台输入程序的奇才。但是对于现在,在刚熟悉Java语言的时候来研究它则是个不错的主意,应该继续写那些控制台应用程序。
StringTokenizer类允许把String对象分解成标记(token),标记被分隔符分离。在需要分解具有一定格式的数据项目序列时,使用StringTokenizer类非常方便。
Math.random()方法产生一个在0.0到1.0之间的随机数。第一次调用这个方法时,它用当前系统时间作为种子,创建了一个新的Java.util.Random对象。接下来对Math.random()方法的调用从同一个Random对象返回下一个产生的数字。
使用Math.random()方法在大多数情况下都是好用的。但是,有时候会希望对产生的随机值多些控制,例如,当Random类用同一个值作为种子,就会产生相同的输出。这时可能想指定一个特殊的种子(一个long值),这样就能够控制并监控结果以保证每次获得的是相同的输出。有时可能还想给特定的对象以它们自己的Random对象,以保证每个对象获得它自己的随机值的惟一分配,而不是让它们都来自于同一个Random实例。
另外,使用Random类的优势还在于可以获得不同原始类型的值。
1.java.util.Vector
import java.util.*;
class VectorTest extends Object
{
public static Vector getRandomScores(){
//创建一个随机数产生器
Random rand=new Random();
//随机数的范围设为500-1000
int numElements=500+Math.abs(rand.nextInt())%501;
//创建一个新的Vector并随机规定一个增量
Vector v=new Vector(numElements);
while(numElements>0){
//添加一个在0-100之间的Integer
v.add(new Integer(Math.abs(rand.nextInt(101))));
numElements--;
}
return v;
}
public static void main(String[] args){
int highestScore=0;//目前所找到的最大值
//产生一些随机数
Vector scores=getRandomScores();
//遍历这些数值并找到最大值
for(Enumeration e=scores.elements();e.hasMoreElements();){
Integer score=(Integer)(e.nextElement());
if(score.intValue()>highestScore){
highestScore=score.intValue();
}
}
//打印最大值
System.out.println(highestScore);
}
}//VectorTest
2.java.util.Stack
堆栈是一种后进先出(LIFO)数据结构,这意味着后插入的对象会先被移出。Java中的Stack类定义了5种方法:push,pop,peek,empty,search。
方法push允许放置元素到Stack的顶部;相反地,peek方法返回顶部元素,但不把它移出;要判断Stack是否包含任何元素,使用empty方法,如果Stack中没有剩余的元素,该方法返回true值;search方法返回一个元素在Stack中放置的从1开始计数的位置,如果找到的元素在Stack的顶部,方法会返回1,如果没有这个元素,会返回-1.
作为一个简单例子,我们来把一组Integer对象压入堆栈,然后弹出它们并打印出值:
//创建一个新Stack对象
Stack integerStack=new Stack();
//把10个Integer对象压入堆栈
for(int i=0;i<10;i++){
integerStack.push(new Integer(i));
}
//弹出堆栈中所有的值并打印
while(!integerStack.empty()){
System.out.println(integerStack.pop());
}
堆栈最好的应用之一是处理自定义菜单对象,在第3篇中我们会实际看到如何写一个自定义菜单处理器。
3.java.util.LinkedList
与堆栈不同,链表是先进先出结构。LinkedList类的Java实现包括允许在链表任一端插入和取回的方法,这样的链表通常被称为双向链表,或双端队列。
链表也提供插入,移出以及在集合中查询数据的操作,并且不用写额外的代码。
当有一个需要有规律的查询或更新的数据集合时,应该考虑使用LinkedList类。像往常那样,在使用之前应该多思考问题,在想清楚是使用数组还是链表之后再动手。
4.java.util.Hashtable
Java的Hashtable类的妙处在于它可以用非常少的代码建立一个简单的数据库。
添加到Hashtable的元素由两部分组成:元素的Object值以及与它相关的Object关键字值。添加到表的值在以后可以由它的相关关键字重新获得。虽然关键字值可以是任何类型,但是它们必须能够实现hashCode和equals方法。
假设要为一个overhead-view游戏实现tile引擎。这里想把所有的tiles放到一个容器中,并且通过含有每个tile的图像数据的文件名来访问它们。Hashtable可能是实现这种方案的理想选择。Tiles的图像会作为数据元素被添加进来,它们相关的String表示将作为关键字值。
现在已对如何存储图像有了清晰的认识,下面写一个程序来试验一下。
import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
public class HashTest extends Applet implements ItemListener{
//用来添加平铺图片的Hashtable
private Hashtable imageTable;
//选择不同平铺图像的
private Choice selections;
//假设图片块的宽与高相等,这个值既代表宽又代表高
private int imageSize;
//图片的文件名
private final String[] filenames={"cement.gif","dirt.gif","grass.gif",
"pebbles.gif","stone.gif","water.gif"};
//初始化Applet
public void init(){
int n=filenames.length;
//创建一个有n个成员的Hashtable
imageTable=new Hashtable(n);
//创建Choice
selections=new Choice();
//创建一个Panel来在窗体的底部容纳选择框
Panel p=new Panel();
p.add(selections,BorderLayout.SOUTH);
p.setBackground(Color.RED);
//把Choice添加到applet中并注册ItemListener
setLayout(new BorderLayout());
add(p,BorderLayout.SOUTH);
selections.addItemListener(this);
//为图像分配内存
for(int i=0;i<n;i++){
Image img=getImage(getCodeBase(),filenames[i]);
while(img.getWidth(this)<0);//这句代码不知道有什么用 ,看不明白
//把图像添加到Hashtable和Choice中
imageTable.put(filenames[i],img);
selections.add(filenames[i]);
//设置imageSize属性
if(i==0){
imageSize=img.getWidth(this);
}
}
}//init
//在Applet内平铺当前所选择的图像
public void paint(Graphics g){
//把传入的Graphics容器转化为可用的Graphics2D对象
Graphics2D g2d=(Graphics2D)g;
//把Applet的宽和高存下来
int width=getSize().width;
int height=getSize().height;
//为放置平铺图像创建一个AffineTransform
AffineTransform at=new AffineTransform();
//得到当前所选择的图像
Image currImage=(Image)imageTable.get(selections.getSelectedItem());
//在Applet内部铺满图像
int y=0;
while(y<height){
int x=0;
while(x<width){
at.setToTranslation(x,y);
//绘制图像
g2d.drawImage(currImage,at,this);
x+=imageSize;
}
y+=imageSize;
}
}//paint
//在所选图像改变时调用
public void itemStateChanged(ItemEvent e){
//下拉列表框已经改变,重新绘制画面
repaint();
}
}//HashTest
虽然已经在第1章看到如何编译和运行applet,但因为这是我们研究的第一个applet,所以还要再复习一遍。编译源代码总是如下所示:
javac HashTest.java
一旦编译成功,运行HashTest applet就简单了:
appletviewer HashTest.html
Hashtable applet的代码清单看起来比较复杂,在第2篇中将会讲解每个构成部分。就目前而言,应把注意力集中于如何使用插入和从表中取值时调用的方法上。
第3篇中讨论创建自定义字体类时,我们将研究另外一个Hashtable例子。如果理解了这个例子以及本章练习中出现的表练习,那么就可以认为自己已经掌握了Hashtable。
请不要把本章看作关于Java功能的一个完整的可用的论述,在API的标准版本中有数百个Java类。这里只是想使读者熟悉Java提供的很多常用特性中的一些。在自己的Java学习中,一定要在构思编写工具之前查询Java API——可能已经存在提供读者所期望的功能的类,或者至少有一个可以依赖的基类。
总而言之,Java提供了许多类,接口和工具,比如线程,使用它可以更快,更简单地开发更强大的应用程序,并且比其他大多数语言优美。本书希望在理解了Java语言背后的所有细节和扩展能力之后,读者会欣赏到它帮助程序员摆脱基本操作,而专注于解决问题的好处和能力。
试着做下面的练习,并设想用其他的语言如C++解决它可能有的难度。希望读者会认同Java所提供的类是构建起程序并更快有效地运行的最好方法。
4.5
练习
4.1写出保存一个链表顺序的代码。(提示:Java LinkedList是FIFO结构——哪些类具有LIFO结构?)
4.2练习4.1的代码会不会与保存一个StringBuffer对象的内容有很大的不同?
4.3写一个工具类,封装BufferedReader类,并从键盘读入不同类型的信息,如float值,int值,boolean值等。可以写如readInt,ReadFloat等的方法,确保每个组件只接收有效的输入,并且在输入值不正确时提示用户重新输入数据。
4.4写一个程序(使用BufferedReader类),从名为file.txt的文件读入Strings。Reader应该连续地读遍全文件。文件输入应该立即改变到标准输出。文件不存在或打不开时输出一个错误消息。(提示:使用java.io包中的FileReader类)
4.5重写Fibonacci程序,使用Java.util包提供的Timer和TimerTask类。建议保持Fibonacci类框架,但是用一个匿名内部类来做定时。
4.6创建一个映射假期和它们的日历日期的Hashtable。使用java.util.GregorianCalendar对象作为表值,String对象作为关键字。例如,要添加最喜欢的假期到表,可能会这样写:
Hashtable ht=new Hashtable();
Calendar c=new GregorianCalendar();
c.set(Calendar.MONTH,Calendar.NOVEMBER);
c.set(Calendar.DAY_OF_MONTH,3);
ht.put(“Tom’s birthday”,c);
System.out.println(ht.get(“Tom’s birthday”));
试着添加一些你最喜爱的假期,并确保可以从表中重新得到它们。
4.7这道题更多是关于工程风格的问题,所以请仔细思考。我们还没有研究过如何创建和绘制图形,但是仍然可以写一个使用面向对象概念,简单地使用命令行的游戏。写一个玩扑克牌的二十一点的单人游戏,会需要(至少)5个类:Card类,Deck类,Hand类,Player类和Blackjack类。在第1篇中我们已经见到过Card和Deck类。Blackjack类将完成主程序所要处理的大部分工作。Card只包括一个声明是52张纸牌中某一个的整数型变量。更多关于这个类的信息可以参照第3章。Deck包括52个Card对象,并且应该在每个游戏之前调用它的shuffle方法。一个Deck对象也必须能够把这副纸牌中的下一张纸牌处理到某一玩家。为了灵活起见,Hand类应该能够包含任何数量的Card对象;实际上可以为任何纸牌游戏重用Hand类。Player类应该含有玩游戏者的数量,以及游戏胜利的数量。它还会包含一个Hand变量来表示玩家的纸牌。但是,确保Blackjack类做全部游戏的大部分处理。
记住下面的细节:
q 幺点可以表示1或11,这依赖于哪一个更合适。例如AA25可能总共有19点;AAAQ可能总共有13点;AK则可能是21点。
q 可能需要询问用户是否愿意在游戏开始之前浏览二十一点的规则。
q 如果玩家能够拿到5张牌而没超过21,则获胜。
如果需要这个工程的帮助,可以浏览配书光盘中的源代码。希望读者能做出干净的界面和令人上瘾的游戏局。
对于一个Java新手来说,这个程序最难的地方可能是使每一部分在一起工作。建议在写任何代码之前把所想列到纸上。并且可以使用你觉得有必要而这里没有指出的方法。记住保持数据成员private(或protected),并且使用正确的访问方法访问它们。可以同样使用Card,Deck和Hand类,把Blackjack程序扩展成单人5张牌游戏。
现在的位置和目标
第2篇看起来好像一次需要掌握的东西很多——尤其对于Java或面向对象程序设计的新手来说。把整个Java语言的细节浓缩成几章是很难的,一个关于整个语言的彻底的研究可能会需要差不多1000页的篇幅才能完成。因此,如果现在你对我们所讲述的内容感觉不满意的话,可能就需要再复习一遍。在继续前进之前,努力做好每章后面的练习,或者自己写一些基本的程序。开始编写游戏时,并不需要必须是Java专家,但是要对相关的Java基础有比较牢固的理解,这会帮助你更容易地消化本书余下的部分。Java中充满着奇妙的地方,但是它并不像火箭技术那样高深(除非你用它来设计火箭)
如果你真想深入到Java语言工作机理的核心,可以查阅Java语言详述(Java Language Specification),可以在Java 2文档或在线
http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html查看。
讲到这里,大多数读者差不多已准备好开始编写一些游戏了,接下来我们会关注创建图形化用户界面,以及如何使用输入设备来真正地告诉应用程序谁是“老板”。
本文介绍了Java的IO操作及常用集合框架类,包括java.io包中的输入输出流概念,java.util包内的StringTokenizer、Random类使用方法,以及Vector、Stack、LinkedList和Hashtable等数据结构的特点与应用实例。
204

被折叠的 条评论
为什么被折叠?



