《Java大学实用教程.ppt》由会员分享,可在线阅读,更多相关《Java大学实用教程.ppt(31页珍藏版)》请在三一办公上搜索。
1、第九章 输入、输出流,本章导读 文件 文件字节流和文件字符流 缓冲流 数组流和字符串流 数据流 对象流 序列化和对象克隆 随机读写流 使用Scanner类解析文件 文件锁,1,输入流、输出流概述,I/O流提供一条通道程序,可以使用这条通道读取“源”中的数据,或把数据送到“目的地”。I/O流中的输入流的指向称为源,程序从指向源的输入流中读取源中的数据(如图9.1所示意);输出流的指向称为目的地,程序通过向输出流中写入数据把信息传递到目的地(如图9.2所示意)。程序的源和目的地也可以是键盘、鼠标、内存或显示器窗口。,2,I/O流库提供的4个重要的abstract类,Java的I/O流库提供大量的流
2、类(在包java.io中),其中有4个重要的abstract类:InputStream字节输入流 Reader 字符输入流 OutputStream 字节输出流 Writer字符输出流 InputStream和Reader类为其子类提供了重要的读取数据的read()方法.OutputStream和Writer类为其子类提供了重要的写入数据的write()方法。,3,9.1 文件,多流的读写与文件有关。Java使用File类创建的对象来获取文件本身的一些信息,如文件所在的目录、文件的长度、文件读写权限等,文件对象并不涉及对文件的读写操作。创建一个File对象的构造方法有3个:File(Strin
3、g filename);File(String directoryPath,String filename);File(File f,String filename);其中filename是文件名字,directoryPath是文件的路径,f是一个目录。使用File(String filename)创建文件时,该文件被认为是与当前应用程序在同一目录中。,4,1文件的属性,使用File类的下列方法可以获取文件本身的一些信息:public String getName()获取文件的名字。public boolean canRead()判断文件是否是可读的。public boolean canWri
4、te()判断文件是否可被写入。public boolean exits()判断文件是否存在。public long length()获取文件的长度(单位是字节)。public String getAbsolutePath()获取文件的绝对路径。public String getParent()获取文件的父目录。public boolean isFile()判断文件是否是一个正常文件,而不是目录。public boolean isDirectroy()判断文件是否是一个目录。public boolean isHidden()判断文件是否是隐藏文件。public long lastModified
5、()获取文件最后修改的时间(时间是从1970年午夜至文件最后修改时刻的毫秒数。,5,2目录,1)创建目录 public boolean mkdir()创建一个目录,创建成功返回true,否则返回false。2)列出目录中的文件(如果File对象是一个目录)public String list()用字符串形式返回目录下的全部文件。public File listFiles()用File对象形式返回目录下的全部文件。File类的下述两个方法可以列出指定类型的文件:public String list(FilenameFilter obj)该方法用字符串形式返回目录下的指定类型的所有文件。publi
6、c File listFiles(FilenameFilter obj)该方法用File对象返回目录下的指定类型所有文件。FilenameFilter是一个接口,该接口有一个方法:public boolean accept(File dir,String name);使用list()方法时,需向该方法传递一个实现FilenameFilter接口的对象。list()方法执行时,参数不断回调接口方法accept(File dir,String name),参数name被实例化目录中的一个文件名,参数dir为调用list的当前对象,当接口方法返回true时,list()方法就将目录dir中的文件存放
7、到返回的数组中。,6,3文件的创建与删除,对于 File f=new File(C:myletter,letter.txt);如果C:myletter目录中没有名字为letter.txt的文件,文件对象f调用方法 public boolean createNewFile()可以在C:myletter目录中建立一个名字为letter.txt的文件。文件对象调用方法 public boolean delete()可以删除当前文件,如 f.delete();例9-1列出了D:ch9目录下java源文件的名字及其大小,并删除了D:ch9中的一个java源文件。,7,4运行可执行文件,使用java.la
8、ng包中的Runtime类,执行一个本地机上的可执行文件。首先使用Runtime类声明一个对象,如 Runtime ec;然后使用该类的静态getRuntime()方法创建这个对象:ec=Runtime.getRuntime();ec可以调用exec(String command)方法打开本地的可执行文件或执行一个操作。例9-2中,Runtime对象打开Windows平台上的绘图程序和记事本程序。,8,9.2 文件字节流,1FileInputStream类 FileInputStream类是InputStream的子类,称为文件字节输入流,按字节读取文件中的数据。该类的所有方法都是从Input
9、Stream类继承来的。构造方法:FileInputStream(String name)FileInputStream(File file)构造方法参数指定的文件称为输入流的源,输入流通过使用read()方法从输入流读出源中的数据。建立一个文件输入流对象,代码如下:try FileInputStream ins=new FileInputStream(myfile.dat);catch(IOException e)System.out.println(e);read()方法的形式:int read();从输入流中顺序读取单个字节的数据 int read(byte b)和int read(by
10、te b,int off,int len)把多个字节读到一个字节数组中,返回实际读取的字节个数。参数off指定read()方法把数据存放在字节数组b中的位置,参数len指定该方法将读取的最大字节数。FileInputStream流顺序地读取文件,只要不关闭流,每次调用read()方法就顺序地读取文件中其余的内容,直到文件的末尾或流被关闭。,9,2FileOutputStream类,FileOutputStream提供了基本的文件写入能力,是OutputStream的子类,称为文件字节输出流。文件字节输出流按字节将数据写入到文件中.构造方法:FileOutputStream(String nam
11、e)FileOutputStream(File file)FileOutputStream(String name,boolean append)FileOutputStream(File file,boolean append)构造方法参数指定的文件称为输出流的目的地。输出流使用write()方法把数据写入输出流到达目的地。public void write(byte b)写b.length个字节到输出流。public void.write(byte b,int off,int len)从给定字节数组中起始于偏移量off处写len个字节到输出流,参数b是存放了数据的字节数组。只要不关闭流,每
12、次调用writer()方法就顺序地向文件写入内容,直到流被关闭 例9-3中,首先将“欢迎welcom”写入到文件“hello.txt”中,然后再读取该文件中的内容。,10,9.3 文件字符流,1FileReader类 FileReader类是Reader的子类,称为文件字符输入流。文件字符输入流按字符读取文件中的数据。构造方法:FileReader(String name)FileReader(File file)构造方法参数指定的文件称为输入流的源,输入流通过使用read()方法从输入流读出源中的数据。int read()输入流调用该方法从源中读取一个字符。该方法返回一个整数(065535之
13、间的一个整数,Unicode字符值),如果未读出字符就返回1。int read(char b)输入流调用该方法从源中读取b.length个字符到字符数组b中,返回实际读取的字符数目。如果到达文件的末尾,则返回-1。int read(char b,int off,int len)输入流调用该方法从源中读取len个字符并存放到字符数组b中,返回实际读取的字符数目。如果到达文件的末尾,则返回-1。其中,参数off指定该方法从字符数组b中的什么地方存放数据。,11,2FileWriter类,FileWriter提供了基本的文件写入能力。FileWriter类是Writer的子类,称为文件字符输出流。文
14、件字符输出流按字符将数据写入到文件中.构造方法:FileWriter(String name);FileWriter(File file);FileWriter(String name,boolean append);FileWriter(File file,boolean append);构造方法参数指定的文件称为输出流的目的地。输出流使用write()方法把数据写入输出流到达目的地。public void write(char b)写b.length个字符到输出流 public void.write(char b,int off,int len)从给定字符数组中起始于偏移量off处写len
15、个字符到输出流,参数b是存放了数据的字符数组 void write(String str)把字符串中的全部字符写入到输出流 void write(String str,int off,int len)从字符串str中起始于偏移量off处写len个字符到输出流 只要不关闭流,每次调用writer()方法就顺序地向文件写入内容,直到流被关闭 例9-4首先用字符输出流向一个已经存在的文件尾加若干个字符,然后再用字符输入流读出文件中的内容。,12,9.4 缓冲流(教材印刷错误9.5),1BufferedReader类 BufferedReader类创建的对象称为缓冲输入流,该输入流的指向必须是一个Re
16、ader流,称为BufferedReader流的底层流,底层流负责将数据读入缓冲区。BufferedReader流的源就是这个缓冲区,缓冲输入流再从缓冲区中读取数据。可以将BufferedReader与FileReader连接,然后BufferedReader就可以按行读FileReader指向的文件。BufferedReader的构造方法如下:BufferedReader(Reader in)BufferedReader流能够读取文本行,方法是 readLine()可以向BufferedReader传递一个Reader对象(如FileReader的实例)来创建一个BufferedReader
17、对象:FileReader inOne=new FileReader(Student.txt)BufferedReader inTwo=new BufferedReader(inOne);然后inTwo调用readLine()顺序读取文件“Student.txt”的一行。,13,2BufferedWriter类,可以将BufferedWriter流和FileWriter流连接在一起,然后使用BufferedWriter流将数据写到目的地。FileWriter流称为BufferedWriter的底层流,BufferedWriter流将数据写入缓冲区,底层流负责将数据写到最终的目的地。例如,Fil
18、eWriter tofile=new FileWriter(hello.txt);BufferedWriter out=new BufferedWriter(tofile);BufferedReader流调用方法:write(String str)write(String s,int off,int len)把字符串s或s的一部分写入到目的地。BufferedWriter调用newLine()方法,可以向文件写入一个回行,调用flush()可以刷新缓冲区。例9-5将文件“Student.txt”中的内容按行读出,并写入到另一个文件中,且给每一行加上行号,14,3标准化考试,标准化试题文件的格式
19、要求如下:每道题目之间用一个或多个星号(*)字符分隔(最后一个题目的最后一行也是*)。每道题目提供A、B、C、D四个选择(单项选择)。test.txt是一套标准化考试的试题文件。例9-6使用输入流读取试题文件,每次显示试题文件中的一道题目。当读取到字符*时,暂停读取,等待用户从键盘输入答案。用户做完全部题目后,程序给出用户的得分。程序运行效果如图9.3。,15,9.5 数组流(教材印刷错误9.6),流的源和目标除了可以是文件外,还可以是计算机内存。ByteArrayInputStream和字节输出流ByteArrayOutputStream分别使用字节数组作为流的源和目标。数组字节输入流 By
20、teArrayInputStream构造方法如下 ByteArrayInputStream(byte buf)ByteArrayInputStream(byte buf,int offset,int length)数组字节输入流调用 public int read()顺序地从源中读出一个字节,该方法返回读出的字节值 public int read(byte b,int off,int len)顺序地从源中读出参数len指定的字节数,并将读出的字节存放到参数b指定的数组中,参数off指定数组b存放读出字节的起始位置,该方法返回实际读出的字节个数。如果未读出字节read方法返回-1。数组字节输出流
21、 ByteArrayOutputStream的构造方法如下:ByteArrayOutputStream()ByteArrayOutputStream(int size)字节数组字节输出流调用 public void write(int b)顺序地向缓冲区写入一个字节 public void write(byte b,int off,int len)将参数b中指定的len个字节顺序地写入缓冲区,参数off指定从b中写出的字节的起始位置public byte toByteArray()返回输出流写入到缓冲区的全部字节 例9-7向内存(输出流的缓冲区)写入ASCII表,然后再读出这些字节和字节对应的
22、字符。,16,数组字符流,与数组字节流对应的是数组字符流CharArrayReader和CharArrayWriter类,数组字符流分别使用字符数组作为流的源和目标。与数组字节流不同的是,数组字符流的读操作可能发生IOException异常。例9-8将Unicode表中的一些字符写入内存,然后再读出。,17,9.6 字符串流(教材印刷错误9.7),StringReader使用字符串作为流的源。构造方法:public StringReader(String s)构造的输入流指向参数s指定的字符串 字符串输入流调用 public int read()顺序读出源中的一个字符,并返回字符在Unicod
23、e表中的位置 public int read(char buf,int off,int len)顺序地从源中读出参数len指定的字符个数,并将读出的字符存放到参数b指定的数组中,参数off指定数组b存放读出字符的起始位置,该方法返回实际读出的字符个数。StringWriter将内存作为流的目的地,构造方法:StringWriter()和 StringWriter(int size)可以构造字符串输出流对象 字符串输出流调用:public void write(int b)向缓冲区写入字符 public void write(char b,int off,int len)向缓冲区写入字符 pub
24、lic void write(String str)向缓冲区写入字符 public void write(String str,int off,int len)向缓冲区写入字符 public String toString()返回输出流写入到缓冲区的全部字符 public void flush()可以刷新缓冲区,18,9.7 数据流(教材印刷错误9.8),1DataInputStream类和DataOutputStream类 DataInputStream 类和DataOutputStream类创建的对象称为数据输入流和数据输出流。它们允许程序按照与机器无关的风格读取Java原始数据。2Dat
25、aInputStream类和DataOutputStream的构造方法 DataInputStream(InputStream in)创建的数据输入流指向一个由参数in指定的输入流,以便从后者读取数据(按着机器无关的风格读取)。DataOutputStream(OutnputStream out)创建的数据输出流指向一个由参数out指定的输出流,然后通过这个数据输出流把Java数据类型的数据写到输出流out。表9.1 DataInputStream类和DataOutputSteam的部分方法 例9-9可以完成写几个Java类型的数据到一个文件,并再读出来。,19,表9.1 DataInputS
26、tream类和DataOutputSteam的部分方法,20,9.8 对象流(教材印刷错误9.9),ObjectInputStream类和ObjectOutputStream类创建的对象被称为对象输入流和对象输出流。构造方法:ObjectInputStream(InputStream in)ObjectOutputStream(OutputStream out)对象输出流使用writeObject(Object obj)方法将一个对象obj写入输出流送往目的地,对象输入流使用readObject()从源中读取一个对象到程序中。将一个对象写入到文件时,首先用FileOutputStream创建一
27、个文件输出流,如下所示:FileOutputStream file_out=new FileOutputStream(tom.txt);ObjectOutputStream object_out=new ObjectOutputStream(file_out);准备从文件中读入一个对象到程序中时,首先用FileInputStream创建一个文件输入流,如下所示:FileInputStream file_in=new FileInputStream(tom.txt);ObjectInputStream object_in=new ObjectInputStream(file_in);注:当我们使
28、用对象流写入或读入对象时,要保证对象是序列化的。一个类如果实现了Serializable接口,那么这个类创建的对象就是序列化的对象。使用对象流把一个对象写入到文件时不仅保证该对象是序列化的,而且该对象的成员对象也必须是序列化的。例9-10中有一个实现Serializable接口的Goods类。,21,9.9 序列化与对象克隆(教材印刷错误9.10),有时我们想得到对象的一个“复制品”,该复制品的实体是原对象实体的拷贝。复制品实体的变化不会引起原对象实体发生变化。对象调用clone()方法就可以获取对象的“复制品”,称为原对象的克隆对象。对象进行克隆时需要特别注意的是:如果原对象有引用型成员变量
29、,那么克隆对象对应的成员变量的引用就与原对象那个成员变量的引用相同,克隆对象对自己的这个成员变量所引用的实体的操作,将影响原对象引用型成员变量的实体。这样一来就涉及到深度克隆的问题,因为原对象的成员变量中可能还会有其他对象。因此,程序必须重写clone()方法,增加了编程的难度。使用对象流很容易获取一个序列化对象的克隆。只需将该对象写入到对象输出流,然后用对象输入流读回的对象就是原对象的一个克隆。例9-11将对象写入到内存,然后读回该对象的一个克隆。,22,9.10 随机读写流(教材印刷错误9.11),当用户需要严格地处理文件时,就可以使用RandomAccessFile类来创建一个对象(称为
30、随机读写流)。RandomAccessFile类创建的流的指向既可以作为源,也可以作为目的地。RandomAccessFile类有两个构造方法:RandomAccessFile(String name,String mode)参数name用来确定一个文件名,给出创建的流的源,也是流目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权力。RandomAccessFile(File file,String mode)参数file是一个File对象,给出创建的流的源,也是流目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权力。流还可以调用 seek(
31、long a)用来移动RandomAccessFile流的读写位置,其中参数a确定读写位置距离文件开头的字节位置。getFilePointer()获取当前流在文件中的读写的位置。表9.2给出了RandomAccessFile的常用方法。,23,表9.2 RandomAccessFile的常用方法,24,例题9-12,例题9-12,例9-12 把5个int类型整数写入到一个名字为tom.dat文件中,然后按相反顺序读出这些数据。一个int类型数据占4个字节,首先将读写位置移动到文件的第16个字节位置,读取tom.dat文件中最后一个整数,然后将读写位置再移动到文件的第12个字节,读取tom.da
32、t文件中倒数第二个整数,依次类推将tom.dat文件中的整数按相反顺序读出。注:RondomAccessFile流的readLine()方法在读取含有非ASCII字符的文件时(比如含有汉字的文件)会出现乱码问题,因此,需要把readLine()读取的字符串用“iso-8859-1”重新编码存放到 byte数组中,然后再用当前机器的默认编码将该数组转化为字符串,操作如下:(1)读取 String str=in.readLine();(2)用“iso-8859-1”重新编码 byte b=str.getBytes(iso-8859-1);(3)使用当前机器的默认编码将字节数组转化为字符串 Stri
33、ng content=new String(b);如果机器的默认编码是“GB2312”,那么 String content=new String(b);等同于:String content=new String(b,GB2312);例9-13中RondomAccessFile流使用readLine()读取一个文件。,25,9.11使用Scanner解析文件(教材印刷错误9.12),应用程序可能需要解析文件中的特殊数据,此时,应用程序可以把文件的内容全部读入内存后,再使用第6章的有关知识解析所需要的内容,其优点是处理速度快,但如果读入的内容较大将消耗较多的内存,即以空间换取时间。本节介绍怎样借助
34、Scanner类和正则表达式来解析文件,比如,要解析出文件中的特殊单词,数字等信息。使用Scanner类和正则表达式来解析文件的特点是以时间换取空间,即解析的速度相对较慢,但节省内存。,26,1使用默认分隔标记解析文件,创建Scanner对象,并指向要解析的文件,例如:File file=new File(hello.java);Scanner sc=new Scanner(file);那么sc将空格作为分隔标记、调用next()方法依次返回file中的单词,如果file最后一个单词已被next()方法返回,sc调用hasNext()将返回false,否则返回true。对于数字型的单词,比如1
35、08,167.92等可以用nextInt()或nextDouble()方法来代替next()方法,即sc可以调用nextInt()或nextDouble()方法将数字型单词转化为int或double数据返回,但需要特别注意的是,如果单词不是数字型单词,调用nextInt()或nextDouble()方法将发生InputMismatchException异常,在处理异常时可以调用next()方法返回该非数字化单词。假设cost.txt的内容如下:TV cost 876 dollar,Computer cost 2398 dollar.The milk cost 98 dollar.The app
36、le cost 198 dollar.例9-14中使用Scanner对象解析文件cost.txt中的全部消费并计算出总消费。程序运行效果如图9.4,27,2使用正则表达式作为分隔标记解析文件,创建Scanner对象,指向要解析的文件,并使用useDelimiter方法指定正则表达式作为分隔标记,例如:File file=new File(hello.java);Scanner sc=new Scanner(file);sc.useDelimiter(正则表达式);那么sc将正则表达式作为分隔标记,调用next()方法依次返回file中的单词,如果file最后一个单词已被next()方法返回,s
37、c调用hasNext()将返回false,否则返回true。对于数字型的单词,比如1979,0.618等可以用nextInt()或nextDouble()方法来代替next()方法,即sc可以调用nextInt()或nextDouble()方法将数字型单词转化为int或double数据返回,但需要特别注意的是,如果单词不是数字型单词,调用nextInt()或nextDouble()方法将发生InputMismatchException异常,那么在处理异常时可以调用next()方法返回该非数字化单词。例9-15使用正则表达式(匹配所有非数字字符串):String regex=0123456789
38、.+作为分隔标记解析communicate.txt文件中的通信费用,程序运行效果如图9.5。communicate.txt的内容:市话费:176.89元,长途费:187.98元,网络费:928.66元,28,3单词记忆训练,例9-16是基于文本文件的英文单词训练程序,运行效果如图9.6,具体内容如下:文本文件word.txt的内容由英文单词所构成,单词之间用空格或回行分隔,例如:first boy girl hello well。使用Scanner流解析word.txt中的单词,并显示在屏幕上,然后要求用户输入该单词。当用户输入单词时,程序将从屏幕上隐藏掉刚刚显示的单词,以便考核用户是否清晰地
39、记住了这个单词。程序读取了word.txt的全部内容后,将统计出用户背单词的正确率。,29,9.12 文件锁(教材印刷错误9.13),JDK 1.4增加了一个FileLock类,该类的对象称为文件锁。RondomAccessFile创建的流在读写文件时可以使用文件锁,那么只要不解除该锁,线程无法操作被锁定的文件。使用文件锁的步骤如下:(1)用RondomAccessFile流建立指向文件的流对象,该对象的读写属性必须是“rw”,如 RandomAccessFile input=new RandomAccessFile(Example.java,rw);(2)流对象input调用方法getCha
40、nnel()获得一个连接到底层文件的FileChannel对象(信道),如 FileChannel channel=input.getChannel();(3)信道调用tryLock()或lock()方法获得一个FileLock(文件锁)对象,这一过程也称为对文件加锁,如FileLock lock=channel.tryLock();文件锁对象产生后,将禁止任何程序对文件进行操作,或再进行加锁。对一个文件加锁之后,如果想读、写文件必须让FileLock对象调用release()释放文件锁,如 lock.release();FileInputStream以及FileOutputStream在读/写文件时都可以获得文件锁。例9-17中,Java程序在读取文件Example9_1.java时,使用了文件锁,这时你无法用其它程序来操作文件Example9_1.java,比如在Java程序结束前,你用Windows下的“记事本”(Notepad.exe)也无法修改、保存Example9_1.java。,30,31,