1、封面,Java,程序设计,第9章 Java高级编程,9.1 异常处理 9.2 Java多线程机制 9.3 I/O数据流与文件处理 9.4 网络编程9.5 数据库操作,9.3 I/O数据流与文件处理,9.3.1 流概述 9.3.2 文件类(File)的使用, 缓冲区流输出和输入 数据和对象的输入输出流 InputStreamReader类,大多数程序所处理的数据都要从外部输入,即这些数据要从数据源(source)获得,数据源指提供数据的地方;而程序的运行结果又是要送到数据宿(destination),数据宿指接收数据的地方。其中数据源可以是磁盘文件、键盘或网络插口等,数据宿可以是磁盘文什、显示器
2、、网络插口或者打印机等。 数据源和数据宿的这种多样性,常常造成程序处理输入/输出的复杂性。另一方面,就程序而言,不希望将编程的精力过多地消耗在处理输入/输出的具体细节上面,而希望所有的输入/输出操作都能够有一个相对统一的、简单的操作方式,而不管输入/输出所涉及的数据源和数据宿是怎样的不同和多样。,9.3.1 流概述,1. 流的概念,怎样解决由于数据源和数据宿多样性而带来的输入/输出操作的复杂性呢?Jave引入的“流”以及有关的“流类”就是用于解决上述矛盾的有效办法。,“流”可以被理解为一条“管道”。这条“管道”有两个端口:一端与数据源(当输入数据时)或数据宿(当输出数据时)相连,另一端与程序相
3、连。,在与数据源或数据宿相连的端口,“管道”在读写数据时能够应付数据源和数据宿的多样性,消化掉因数据源和数据宿的多样性带来的数据读/写的复杂性;而在与程序相连的端口,“管道”提供了输入/输出的统一操作界面。由于在程序和数据源/数据宿之间建立了“管道”,使得程序输入/输出时原本直接对数据源和数据宿的繁杂操作转化为对“导管”的统一而简单的操作,这样就大大降低了输入/输出的复杂性,减轻了程序员的负担。 有了流,程序和外界的数据交换,都可通过流实现。当程序要从数据源获得数据时,必须在程序和数据源之间建立如图6-5的输入流;当程序要把结果输送到数据宿时,必须在程序和数据宿之间连接建立如图6-6的输出流。
4、 无论涉及输入/输出的数据源和数据宿是什么,只要在程序和数据源/数据宿之间建立了流,用户就不需要再关心数据来自何方或送向何处,程序中输入/输出操作的复杂性就大大降低了。所有输入/输出操作都转换为对流的操作。,2. 输入输出类的结构,根据“管道”里流动的数据的类型, 流被分为字符流(Character Streams) 和字节流 (Byte Streams),字符流以字符为传输单位,而字节流以字节为传输单位。,根据流中数据传输的方向,流被分为输入流和输出流。,虽然Java语言提供了种类繁多的流,但这些流被很好地组织成树状结构,并不显得错综复杂。一旦熟练地掌握I/O流,编程效率就会有显著的提高,下
5、图为java 字节流输入输出接口、类的继承图。,根据流的建立方式和工作原理,流分为节点流 (Node Streams)与过滤流 (Filter Streams),节点流是直接建立在输入、输出媒体之上的,而过滤流必须以某一个节点流作为流的来源,可以在读/写数据的同时对数据进行处理。,例. System.out和System.in对象的使用import java.io.*;class InOut public static void main(Stringargs) byte buffer=new byte20; System.out.println(请在下面输入一行字符: );try Syste
6、m.in.read(buffer,0,20); /使用System.in对象的read方法,read方法会抛出异常 catch(Exception e) /捕获异常并处理 System.out.println(读取输入字符出错,错误信息为:+e.toString(); System.out.println(您刚才输入的一行字符为:); String inputStr=new String(buffer); System.out.println(inputStr); ,3. 标准输入输出流,System类管理标准输入输出流和错误流。 Java定义了System类的三个静态成员对象: System
7、.in 从标准输入(通常是键盘)中读入数据 System.out 将输出送到默认的显示(通常是显示屏) System.err 将错误信息数据输出到默认的显示 每当main方法被执行时就自动生成上述三个对象。,4. InputStream类和OutputStream类,int read() 从流中读取一个字节并将该字节作为整数返回, 若没有数据则返回-1 int read(byte b) 从流中读取多个字节放到b中, 返回实际读取到的字节数 int read(byte b,int off,int len) 从流中读取最多len字节的数据, 放到数组b的下标off开始的单元中,返回读取到的字节数
8、long skip(long n) 跳过流中指定的字节数,InputStream类表示基本输入流,是字节流,它定义了一套所有输入流都需要用的方法:,注:上面给的方法定义并非是完整的,完整定义(如int read()如下: public abstract int read() throws IOException,InputStream类,InputStream类中定义了一套所有输入流都需要用的方法,而它的子类将这些方法定义完整,供程序中使用。如我们常用的read方法就是标准输入对象System.in的read方法。,read()方法使用例: System.out.println( System
9、.in.read() ); System.out.println( (int)System.in.read() ); /不必要的强制转换 System.out.println( (byte)System.in.read() ); System.out.println( (char)System.in.read() );,read(byte b)方法使用例: byte b=new byte3; System.in.read(b); for (int i=0;i3;i+) System.out.print(bi+ ); System.out.println(); String s=new Stri
10、ng(b); System.out.print(s);,注意: InputStream类的read方法接受的是字节数据(8位),而不是字符。 ASCII码中字符和字节数据对应的都是8位数据, 而在Unicode字符集中,字符都被设置为16位数据,这样, 利用InputStream类来接收键盘字符将接收不到字符的高位信息!,InputStream类的其它方法:,public int available() throws IOException 返回当前流中可用的字节数public void close() throws IOException 关闭当前流对象public synchronized
11、 void mark(int readlimit) 在流中标记一个位置public synchronized void reset() throws IOExeeption 返回流中标记过的位置public boolean markSupported() 返回一个流是否支持标记和复位操作的布尔值,与InputStream类相对应的输出流是OutputStream,它具有所有输出流类的基本功能,同InputStream类相似,它具有以下相应的方法:public abstract void write(int b) throws IOException 向流中写一个字节public void wr
12、ite(byte b) throws IOException 向流中写入一个字节数组public void write(byte b,int off,int len) throws IOException 从数组b的第off个位置开始写入len长度的数据 public void flush() throws IOException 清空流并强制将缓冲区中的所有数据写入到流中 public void close() throws IOException 关闭流对象,OutputStream类,9.3.2 文件类(File)的使用,为了将程序的运行结果永久保留,常需要将运行结果输出到文件中,反之又
13、需要从文件中读取数据输入到程序中,这就是文件操作。 Java提供了专门处理文件的类File,还提供了对文件进行输入输出的文件输入输出流类(文件流),以此来实现文件的操作。 文件流是基本输入输出流类的子类。按数据交换的单位不同, 可分为字符流、字节流两种, 其读写的方式都是顺序方式。 本节介绍以字符为单位的文件访问,以字节为单位的方式在下一小节介绍。,1. 文件、目录、路径2. File类 3. FileReader类和FileWriter类,文件(file)是存储在辅助存储器中的一组相关信息的集合,它可以存放程序、文档、图片、声音或视频信息等。为了便于对文件管理, 系统允许用户给文件设置或取消
14、有关的文件属性, 如只读属性、隐藏属性、存档属性、系统属性。 目录(directory)是一种特殊的文件,用以存放普通文件或其他的目录。磁盘格式化时,系统自动地为其创建一个目录(称为根目录)。用户可以根据需要在根目录中创建低一级的目录(称为子目录),子目录中还可以再创建下一级的子目录,从而形成树型目录结构,目录也可以设置相应的属性。 路径(path)是从盘符经过各级子目录到文件的目录序列。由于文件可以在不同的磁盘、不同的目录中,所以在存取文件时,必须指定文件的存放位置。,1.文件、目录、路径,文件的存放位置通过路径来描述,路径的表示格式为:盘符目录名文件名,比如在C盘根目录下的子目录WINDO
15、W下子目录JAVA中的文件myfile.java,应表示为 C:WINDOWJAVAmyfile.java 其中,C:为驱动器名,表示C盘,而WINDOWJAVA表示myfile.java所存储的位置,其中第一个“”表示根目录,其余的“”为各子目录之间、最后一级子目录与文件名之间的分隔符。 在一个树型目录结构中,任一时刻,可以指定一个当前工作或操作的子目录,称其为当前目录(又叫缺省目录)。凡从根目录开始的路径叫做绝对路径,应以“”开头;凡不从根目录开始而从当前目录开始的路径叫相对路径,应从当前目录的下级目录开始写起。,例如,假使上述文件myfile.java完整的文件标识是C:WINDOWJA
16、VAmyfile.java,如果当前磁盘是C盘,可写成WINDOWJAVAmyfile.java;如果当前目录是C:WINDOW,可写成JAVAmyfile.java;如果当前目录是C:WINDOWJAVA,可改写成myfile.java。,java.io包中定义了一个File类用来专门处理文件或获取文件的有关信息。Java语言中通过File类来建立与磁盘文件的联系。File类用来获取或设置文件或目录的属性,但不支持从文件读取数据或者往文件里写数据(读取或写数据由文件流实现)。 文件是许多程序的基本数据源和数据宿,是保存永久数据和共享信息的媒体。在Java中,目录也被当作文件,可以用list方
17、法列出目录中的文件名。,2. File类,File类的功能十分强大,它可以访问指定文件的所有属性,包括文件名称、文件长度、文件的最后修改时间等,同时还可以利用File类来建立文件对象、删除文件和改变文件名称等。,File类具有下面三种构造函数: File (String path) File (String path,String name) File (String dir,String name)其中参数意义如下: String path指定的路径 String name执行的文件名 String dir执行的目录,例如下面的语句是在d盘上建立一个名为myfile.txt的文件: File
18、 f = new File(d:myfiel.txt);,public String getName() 返回文件名public String getPath() 返回文件路径 public String getAbsolutePath() 返回文件绝对路径public String getCanonicalPath() throws IOException 返回文件的规范路径public String getParent() 返回文件的父目录public boolean exists() 判断文件是否存在public boolean canWrite() 判断文件是否可写public boo
19、lean canRead() 判断文件是否可读public boolean isFile() 判断对象是否是文件public boolean isDirectory() 判断对象是否是目录,File类的有关方法:,public native boolean isAbsolute() 如果文件名为绝对名则返回真public long lastModified() 返回文件最后修改日期public long length() 返回文件长度public boolean mkdir() 创建目录public boolean rename To(File dest) 重命名文件public boolea
20、n mkdirs() 创建目录及子目录public String list() 列出目录下的所有文件和目录public String list(FilenameFilter filter) 列出目录下的指定文件public boolean delete() 删除文件对象public int hashCode() 为文件创建散列代码public boolean equals(Object obj) 判断是否同对象obj相等public String toString() 返回文件对象的字符串描述,前面介绍的File类用来创建一个文件,但是如何往文件里写数据或读出文件中的内容 ?这就需要使用下面流
21、类: (1)文件输出流类FileWriter (2)文件输入流类FileReader,3. FileWriter类和FileReader类,(1)字符文件输出流(FileWriter),FileWriter类可以在一指定的文件上建立一个文件输出流,也可以建立一个文件同时实例化为文件输出流。流提供了将字符写到文件中的方法。如果用FileWrite来打开一个只读文件会产生IOExcption异常。 FileWriter类有两种构造方法: 构造方法一: File f=new File(d:t1.txt); /建立文件 FileWriter f1=new FileWriter(f); /建立文件输出流
22、 构造方法二: (建立文件同时实例化为文件输出流) FileWriter f2=new FileWriter(d:t1.txt); 第一种方法是先建立一个文件对象f,然后再建立一个输出流f1,并使输出流f1和文件f相连接。这种情况下,可以通过对象f对该文件作进一步的分析, 比如, 显示文件的属性、大小等。第二种方法就是将第一种方法的二步合成一步,相对来说更方便一些,但不能做文件分析等其它工作。,注意:写完后一定要关闭输出流,数据才真正地写到了文件中!,(1) write(char c) 将一个字符写到文件的末尾 (2) write(char b ) 将一个字符数组写到文件的末尾 (3) clo
23、se() 关闭输出流,FileWriter类的最重要的方法是write()与close():,例. 在d盘上建立一个名为myfile.txt的文件,并写入两串字符。,import java.io.*; public class FileOutputpublic static void main(Stringargs)throws Exception File f = new File(d:myfile.txt); FileWriter f1 = new FileWriter(f); f1.write(abcdefghij); f1.write(xyz); f1.close(); ,(2)字符文
24、件输入流,FileReader类可以在一指定的文件上实例化一个文件输入流, FileReader类流提供了从文件中读取一个字符或者一组字符方法。FileReader类的构造方法都有可能出现FileNotFoundExcption异常。 构造方法一: File f=new File(d:t1.txt); FileReader f1=new FileReader(f); 构造方法二: FileReader f2=new FileReader(d:t1.txt); 这两种建立输入流的方法与前面建立输出流的两种方法意义完全类似。,(2)字符文件输入流(FileReader),FileReader类的最
25、重要的方法是read()与close():,(1) read( ) 读一字符,返回读入的字符的整型表示 (2) read(char b ) 读入字符放到字符数组b中并返回实际读入的字符数。如果所定义的字符数组容量小于获得的字符数,则运行时将产生一个IOException例外情况。 (3) read(char b ,int off,int len) 读入len个字符放到数组b下标off开始的位置中,并返回实际读入的字符数 (4) close() 关闭输入流,例. 将d盘上名为myfile.txt的文件的内容输出到屏幕。,import java.io.*; public class FileInp
26、utpublic static void main(Stringargs)throws Exception File f = new File(d:myfile.txt); FileReader f1 = new FileReader(f); char a=new char(int)f.length(); f1.read(a); System.out.println(a); f1.close(); ,要建立(或打开)一个文件并对它进行写或读,需要如下几步: (1) 用File类建立(或打开)一个文件; (2) 用FileWriter(FileReader)类建立输出(输入)流; (3) 调用w
27、rite(read)方法进行写(读); (4) 关闭输出(输入)流。,字符文件输入/输出方法小结,平常所使用的文件中,有很多是二进制文件,它们以字节作为数据处理单位。对这些文件就要使用字节流来读写了,其实字符文件也可以用字节流来进行读写。 对字节流文件进行读写的流类是: FileInputStream 字节文件输入流 FileOutputStream 字节文件输出流, 字节流文件,1. FileInputStream,FileInputStream类的方法:,int available() 返回可读入的字节数void close() 关闭输入流,并释放任何与该流有关的资源protected v
28、oid finalize() 当读到无用信息时,关闭该流FileDescriptor getFD() 返回与该流有关的文件描述符 (即文件的完整路径)int read() 从输入流中读取一个字节的数据int read(byteb) 将数据读入到一个字节数组中int read(byteb,int off,int len) 读入len个字节数据放到数组b的off开始的位置中long skip(long n) 跳过输入流上的n个字节,void close() 关闭输出流,并释放任何该流有关的资源protected void finalize() 当写到无用信息时,关闭该流File Descripto
29、r getFD () 返回与该流有关的文件描述符 (即文件的完整路径)void write(int b) 将一个字节数据写到输出流中void write(byte b) 将字节数组中的数据写到输出流中long skip(long n) 跳过输出流上的n个字节,2. FileOutputStream,FileOutputStream类方法:, 缓冲区流输入和输出,1. BufferedInputStream,缓冲区流建立在节点流之上, 对节点流中数据进行某些加工,并提供一些友好的方法供用户进行输入、输出操作以及流控制。 例如, BufferedInputStream可以对任何种类的输入流进行带缓
30、冲区的封装以达到性能的改善 ( 可减少程序 I/O 操作次数,提高程序执行效率 )。 Jave利用缓冲区流可以在读/写数据的同时对数据进行处理。 使用缓冲区流时要注意: 必须将缓冲区流和某个节点流(前面介绍的都是节点流)连接 在程序中,连接是通过在缓冲区流的构造方法中指定入口参数节点流来实现的。如: FileInputStream in=new FileInputStream(text); BufferedInputStream bufin=new BufferedInputStream(in);实现了缓冲区流bufin和文件输入流in连接。,1. BufferedInputStream,对I
31、/O进行缓冲是一种常见的性能优化方法。Jave的BufferedInputStream类可以对任何的InputStream流进行带缓冲的封装以达到性能的改善。该类在已定义输入流上再定义一个具有缓冲的输入流,可以从此流中成批地读取字符而不会每次都引起直接对数据源的读操作。数据输入时,首先被放入缓冲区,随后的读操作就是对缓冲区中的内容进行访问。,该类有两个构造方法: 1)public BufferedInputStream(InputStream in); 2)public BufferedInputStream(InputStream in,int size);,两种构造方法都有是为某种输入流
32、in 创建一个缓冲流,创建的缓冲大小为缺省值(32bytes),方法一,方法二,用户指定缓冲区大小,对方法二,在性能优化时,通常都把size的值设定为内存页大小或I/O块大小的整数倍。在I/O量不大时,该类所起作用不是很明显,但当程序I/O量很大,且对程序效率要求很高时,使用该类就能大大提高程序的效率。,2. BufferedOutputStream,BufferedOutputStream类在已定义节点输出流上再定义一具有缓冲功能的输出流。用户可以向流中写字符而不会每次都引起直接对数据宿的写操作,只有在缓冲区已满或清空流(flush)时,数据才输出到数据宿上。在Java中使用输出缓冲流是为了
33、提高性能。,该类有两个构造方法:1)public BufferedOutputStream(OutputStream out);2)public BufferedInOutputStream(OutputStream out, int size);,DataInputStream用来从一种已定义的节点输入流中读取Jave基本数据类型的数据,如布尔型数、整型数、浮点数等,然后再生成一个数据输入流。 DataOutputStream用来将Jave基本数据类型数据写到一个数据输出流中。 这两个类都是在某节点流上再定义一个数据输入(输出)流,通过它们,用户可以更方便地按照Jave原始数据类型来读(写)
34、数据。,1. DataInputStream和DataOutputStream, 数据和对象的输入输出流,1)pubilc DataInputStream(InputStream in); 创建一新的DataInputStream,该流从输入流in读取数据 2)pubilc DataOutputStream(OutputStream out); 在输出流out上创建一个新的DataOutputStream,使DataOutputStream的输出数据能够输出到输出流out中,构造方法:,例如,建立基于标准输入的输入流的语句可如下: DataInputStream In=new DataInpu
35、tStream(System.in);,readBoolean()、writeBoolean() 读写布尔型数据readByte()、writeByte() 读写字节数据readChar()、writeChar() 读写字符数据readInt()、writeInt() 读写整型数据readFloat()、writeFloat() 读写单精度数据readDouble()、writeDouble() 读写双精度数据readLine()、writeLine() 读写整行数据,成员方法:,例如,要从键盘输入一行字符,语句可如下: DataInputStream In=new DataInputStre
36、am(System.in); String stra=In.readLine();,例. 建立基于标准输入的过滤流并进行输入,import java.io.*; public class DataInputpublic static void main(Stringargs)throws Exception int a; double b,c; String str; DataInputStream In=new DataInputStream(System.in); /建立基于标准输入的过滤流 str=In.readLine(); /读入一个字符串 a=Integer.parseInt(st
37、r); /把字符串转化为整数 str=In.readLine(); /读入一个字符串 b=Double.parseDouble(str); /把字符串转化为实数 c=a+b; System.out.println(c); ,利用过滤流输入字符串、整数、实数的方法:,String s1,s2; int a; double b; float c; DataInputStream In=new DataInputStream(System.in); /建立基于标准输入的过滤流 s1=In.readLine(); /读入一个字符串 s2=In.readLine(); /读入一个整数字符串 a=Inte
38、ger.parseInt(s2); /把字符串转化为整数 s2=In.readLine(); /读入一个单精度字符串 b=Double.parseDouble(s2); /把字符串转化为单精度数 c=Float.parseFloat(In.readLine(); /读入一个双精度字符串并转化为双精度数,例. 建立基于文件输出输入的缓冲区流并进行输出输入,import java.io.*; public class DataInputpublic static void main(Stringargs)throws Exception FileOutputStream f=new FileOut
39、putStream(d:a.txt); DataOutputStream outf=new DataOutputStream(f); outf.writeBoolean(true); outf.writeChar(a); outf.writeDouble(1.111); outf.writeInt(2222); outf.close(); DataInputStream fin=new DataInputStream (new FileInputStream(d:a.txt); System.out.println(fin.readBoolean(); System.out.println(f
40、in.readChar(); System.out.println(fin.readDouble()+fin.readInt(); fin.close(); ,packege ObjectIO;import java.io.*;import java.util.Hashtable;public class ObjectIOClass public static void main(String args) throws Exception Hashtable ht=new Hashtable(); /建立一个Hashtable对象,ObjectInputStream和ObjectOutputS
41、tream是针对对象的输入输出。它的使用通过下面的例子来介绍。例. 把Hashtable中存放的信息输出到磁盘文件中,并再从中读进Hashtable.,2. ObjectInputStream和ObjectOutputStream,DataInputStream dis=new DataInputStream(System.in); /建立DataInputStream对象,并且与标准输入对象连接 String st_no=dis.readLine();/从标准输入读一行存入st_no String st_rec=dis.readLine(); /从标准输入读一行存入st_rec ht.put
42、(st_no,st_rec); /st_no作关键字,st_rec为Hashtable保存的对象,存入ht System.out.println(ht); String st_no1=dis.readLine(); /从标准输入读一行存入st_no1 if (ht.containsKey(st_no1) /测ht中是否有关键字st_no1, 有关键字st_no1则输出对应的对象 System.out.println(ht.get(st_no1); else System.out.println(error);,ObjectReadWrite my_object=new ObjectReadWr
43、ite(); /建立文件对象 my_object.write(ht); /将ht对象输出到输出源(文件) my_object.read(); /从输入源(文件)读入对象 public class ObjectReadWrite static File object_file=new File(d:,data);public static void write(Hashtable ht ) try /建FileOutputStream对象,并与文件对象object_file连接 FileOutputStream fos=new FileOutputStream(object_file); /建立
44、ObjectOutputStream对象,并与文件对象fos连接,ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(ht); /Hashtable对象内容写入输出文件中 oos.close(); catch(Exception e) ; public static void read() /从对象输入文件中读对象 try /建ObjectOutputStream对象,并与文件对象object_file连接 ObjectInputStream ois=new ObjectInputStream ( new FileInputStream(object_file ); Hashtable ht1=(Hashtable)ois.readObject(); /从输入源读入对象 System.out.println(ht1); /标准输出ht1 catch(Exception e) e.printStackTrace(); ,