《第十三讲 输入输出流.ppt》由会员分享,可在线阅读,更多相关《第十三讲 输入输出流.ppt(53页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第十三讲第十三讲 输入输出流输入输出流本章要点本章要点1.文件2.文件字节流3.文件字符流4.从键盘读取数据5缓冲流6数组流7字符串流8数据流9管道流10对象流11.序列化与对象克隆12.随机读写流13.文件锁目的地:标准输出设备:如屏幕目的地:标准输出设备:如屏幕 磁盘文件磁盘文件 网络连接网络连接等等什么是流什么是流一、基本概念一、基本概念输入:指的是如何从输入:指的是如何从数据源数据源中获得字节序列中获得字节序列数据源:标准输入设备,如键盘数据源:标准输入设备,如键盘 磁盘文件磁盘文件 网络连接网络连接等等输出:指的是如何将字节序列发送到能接收输出:指的是如何将字节序列发送到能接收 数据
2、的数据的目的地目的地基本概念Java的I/O流库提供大量的流类(在包java.io中)。其中有4个重要的abstract类:1InputStream(字节输入流)2Reader(字符输入流)3OutputStream(字节输出流)4Writer(字符输出流)其中InputStream 和Reader类为其子类提供了重要的读取数据的read方法,OutputStream和Writer类为其子类提供了重要的写入数据的write方法。我们将陆续介绍它们的子类及用法。基本概念在Java 2中,定义了两种类型的流:字节流和字符流。字节流(byte stream)为处理字节的输入和输出提供了方便的方法。例
3、如,使用字节流读取或书写二进制数据。字符流(character stream)为字符的输入和输出处理提供了方便。这两种流采用了统一的编码标准,因而可以国际化。当然,在某些场合字符流比字节流更为有效。在Java的早期版本(Java 1.0)中不包括字符流,因此所有的输入和输出都是以字节为单位的。后来,Java 1.1中加入了字符流,某些字节形式的类和方法不被推荐使用。这也是为什么没用字符流的老代码在适当的地方需要更新的原因。需要说明的是,在最底层,所有的输入/输出都是字节形式的。基于字符的流只是为处理字符提供方便有效的方法。预定义流我们已经知道,所有的Java程序运行时自动导入java.lang
4、包,这个包定义了一个名为System的类,该类封装了运行时环境的多个方面。例如,使用它的某些方法,能获得当前时间和与系统有关的不同属性。System同时还包含有3个预定义的流变量:in、out和err。这些成员在System中是被定义成public和static型的,即意味着它们可以不引用特定的System对象而被用于程序的其他部分。System.out是标准的输出流,在默认情况下它是一个控制台;System.in是标准输入,默认情况下指的是键盘;System.err指的是标准错误流,它默认是控制台。需要指出的是,这些流可以重定向到任何兼容的输入/输出设备。System.in是InputStr
5、eam的对象;System.out和System.err是PrintStream的对象。尽管它们是用来读写外设字符的,它们都是字节流。如果编程人员愿意,可以用基于字符的流来包装它们。在前面章节中多次用到过System.out,我们可以同样的方式使用System.err。System.in的使用则稍微复杂一些。文件文件 Java使用File类创建的对象来获取文件本身的一些信息,例如文件所在的目录、文件的长度、文件读写权限等,文件对象并不涉及对文件的读写操作。创建一个File对象的构造方法有4个:File(String filename);File(String directoryPath,Str
6、ing filename);File(File f,String filename);File(URI uri);其中,filename是文件名字,directoryPath是文件的路径,f是指定成一个目录的文件。使用File(String filename)创建文件时,该文件被认为是与当前应用程序在同一目录中。文件文件1文件的属性经常使用File类的下列方法获取文件本身的一些信息:public String getName()获取文件的名字。public boolean canRead()判断文件是否是可读的。public boolean canWrite()判断文件是否可被写入。publi
7、c boolean exists()判断文件是否存在。public long length()获取文件的长度(单位是字节)。文件文件public String getAbsolutePath()获取文件的绝对路径。public String getParent()获取文件的父目录。public boolean isFile()判断文件是否是一个正常文件,而不是目录。public boolean isDirectroy()判断文件是否是一个目录。public boolean isHidden()判断文件是否是隐藏文件。public long lastModified()获取文件最后修改的时间(时
8、间是从1970年午夜至文件最后修改时刻的毫秒数目录目录 2目录(1)创建目录 mkdir()方法创建了一个目录,创建成功返回true,创建失败返回false。创建失败是指File对象指定的目录已经存在,或者是因为整个路径不存在而不能创建目录。创建路径不存在的目录,用mkdirs()的方法,它创建目录以及该目录所有的父目录。(2)列出目录中的文件 如果File对象是一个目录,那么该对象可以调用下述方法列出该目录下的文件和子目录:public String list()用字符串形式返回目录下的全部文件,public File listFiles()用File对象形式返回目录下的全部文件。目录目录
9、我们有时需要列出目录下指定类型的文件,比如.java、.txt等扩展名的文件。可以使用File类的下述两个方法,列出指定类型的文件,public String list(FilenameFilter obj)该方法用字符串形式返回目录下的指定类型的所有文件。public File listFiles(FilenameFilter obj)该方法用File对象返回目录下的指定类型所有文件。FilenameFilter是一个接口,该接口有一个方法:public boolean accept(File dir,String name);目录目录 3文件的创建与删除 当使用File类创建一个文件对象后
10、,例如 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();在下面的例子1中,列出C:/1000目录下扩展名是.java文件的名字以及它们的大小,并删除了C:/1000中的一个.java文件。4运行可执行文件 当要执行一个本地机上的可执行文件
11、时,可以使用java.lang包中的Runtime类。首先使用Runtime 类声明一个对象,如:Runtime ec;然后使用该类的静态getRuntime()方法创建这个对象:ec=Runtime.getRuntime();ec可以调用exec(String command)方法打开本地机的可执行文件或执行一个操作。下面的例子2中,Runtime对象打开windows平台上的绘图程序和记事本程序。返回文件字节流文件字节流 1FileInputStream类 FileInputStream类是InputStream的子类,称为文件字节输入流。文件字节输入流按字节读取文件中的数据。该类的所有方
12、法都是从InputStream类继承来的。为了创建FileInputStream类的对象,可以使用下列构造方法:FileInputStream(String name)FileInputStream(File file)第一个构造方法使用给定的文件名name创建一个FileInputStream对象。第二个构造器使用File对象创建FileInputStream对象。构造方法参数指定的文件称作输入流的源,输入流通过使用read()方法从输入流读出源中的数据。文件字节流文件字节流 当您使用文件输入流构造器建立通往文件的输入流时,可能会出现异常FileNotFoundException。程序必须使
13、用一个try-catch块检测并处理这个异常。输入流的唯一目的是提供通往数据的通道,程序可以通过这个通道读取数据,read方法给程序提供一个从输入流中读取数据的基本方法。read方法的格式如下:int read();read方法从输入流中顺序读取单个字节的数据。该方法返回字节值(0255之间的一个整数),读取位置到达文件末尾,则返回-1。文件字节流文件字节流 read方法还有其它一些形式。这些形式能使程序把多个字节读到一个字节数组中:int read(byte b);int read(byte b,int off,int len);其中,off参数指定read方法把数据存放在字节数组b中的什么
14、地方样,len参数指定该方法将读取的最大字节数。上面所示的这两个read方法都返回实际读取的字节个数,如果它们到达输入流的末尾,则返回-1。FileInputStream流顺序地读取文件,只要不关闭流,每次调用read方法就顺序地读取文件中其余的内容,直到文件的末尾或流被关闭。文件字节流文件字节流 2FileOutputStream类 与FileInputStream类相对应的类是FileOutputStream类。FileOutputStream提供了基本的文件写入能力。FileOutputStream类是OutputStream的子类,称为文件字节输出流。文件字节输出流按字节将数据写入到文
15、件中。为了创建FileOutputStream类的对象,可以使用下列构造方法:FileOutputStream(String name)FileOutputStream(File file)FileOutputStream(String name,boolean append)FileOutputStream(File file,boolean append)第一个构造方法使用给定的文件名name创建一个FileOutputStream对象。第二个构造方法使用File对象创建FileOutputStream对象。第三个和第四个构造方法的第二个参数指明了是否以追加方式写文件。输出流通过使用wri
16、te()方法把数据写入输出流到达目的地。文件字节流文件字节流 FileOutputStream对象可以使用write方法把字节发送给输出流,write的用法如下:public void write(int n)写1个字节到输出流。public void write(byte b)写b.length个字节到输出流。public void write(byte b,int off,int len)从给定字节数组中起始于偏移量off处写len个字节到输出流,参数b是存放了数据的字节数组。返回文件字节流文件字节流FileOutStream流顺序地写文件,只要不关闭流,每次调用write方法就顺序地向文
17、件写入内容,直到流被关闭。如果输出流要写入数据的文件已经存在,没有以追加方式写文件的话文件中的数据内容就会被清除;如果要写入数据的文件不存在,该文件就会首先试图被建立。注意:使用输出流向文件中写入数据,最后要调用一下flush()方法或close()方法,确保数据会被最终写入到文件。形成良好的编程习惯,输入流和输出流使用完之后,调用close()方法关闭掉输入流或输出流。在下面的例子3中,首先将“欢迎Welcome”写入到“hello.txt”中,然后再读取该文件中的内容。文件字符流文件字符流 1FileReader类 为了创建FileReader类的对象,可以使用下列构造方法:FileRea
18、der(String name)FileReader(File file)第一个构造方法使用给定的文件名name创建一个FileReader 对象。第二个构造方法使用File对象创建FileReader对象。构造方法参数指定的文件称作输入流的源,输入流通过使用read()方法从输入流读出源中的数据。以下是read方法的功能解释:文件字符流文件字符流 int read()输入流调用该方法从源中读取一个字符,该方法返回一个整数(065535之间的一个整数,Unicode字符值),如果未读出字符就返回-1。int read(char b)输入流调用该方法从源中读取b.length个字符到字符数组b中
19、,返回实际读取的字符数目。如果到达文件的末尾,则返回-1。int read(char b,int off,int len)输入流调用该方法从源中读取len个字符并存放到字符数组b中,返回实际读取的字符数目。如果到达文件的末尾,则返回-1。其中,off参数指定read方法从符数组b中的什么地方存放数据 文件字符流文件字符流 2FileWriter类 FileWriter提供了基本的文件写入能力。FileWriter类是Writer的子类,称为文件字符输出流。文件字符输出流按字符将数据写入到文件中。为了创建FileWriter类的对象,可以使用下列构造方法:FileWriter(String na
20、me)FileWriter(File file)FileWriter(String name,boolean append)FileWriter(File file,boolean append)第一个构造方法使用给定的文件名name创建一个FileWriter对象。第二个构造方法使用File对象创建FileWriter对象。第三个和第四个构造方法的第二个参数指明了是否以追加方式写文件。输出流通过使用write()方法把数据写入输出流到达目的地。文件字符流文件字符流 FileWriter对象可以使用write方法把字符数据写入输出流,write的用法如下:public void write(i
21、nt n)写1个字符到输出流。public void write(char b)写b.length个字符到输出流。public void write(char b,int off,int len)从给定字符数组中起始于偏移量off处写len个字符到输出流,参数b是存放了数据的字符数组。void write(String str)把字符串中的全部字符写入到输出流。void write(String str,int off,int len)从字符串str中起始于偏移量off处写len个字符到输出流。在下面的例子4中,首先用字符输出流向一个文件写入若干个字符,然后再用字符输入流读出文件中的内容。返回
22、从键盘读取数据从键盘读取数据 java.lang包中的System类有两个重要的类成员:in和out,分别是输入流和输出流类型的对象。in的源是键盘,in可以读取用户从键盘键入的数据。Scanner是JDK1.5新增的一个类,该类可以使用in初始化一个对象:Scanner reader=new Scanner(System.in);然后reader对象调用方法,读取用户在命令行输入的各种数据类型。out流的目的地是命令行窗口,out调用方法可以将数据送到命令行窗口。返回缓冲流缓冲流 1BufferedReader类 BufferedReader类创建的对象称为缓冲输入流,该输入流的指向必须是一
23、个Reader流,称作BufferedReader流的底层流,底层流负责将数据读入缓冲区,BufferedReader流的源就是这个缓冲区,缓冲输入流再从缓冲区中读取数据。BufferedReader的构造方法是:BufferedReader(Reader in)BufferedReader流能够读取文本行,方法是readLine()。通过向BufferedReader传递一个Reader对象(如FileReader的实例),来创建一个BufferedReader对象,如:FileReader inOne=new FileReader(Student.txt)BufferedReader in
24、Two=new BufferedReader(inOne);然后inTwo调用readLine()顺序读取文件“Student.txt”的一行。2BufferedWriter类 类似地,可以将BufferedWriter流和FileWriter流连接在一起,然后使用BufferedWriter流将数据写到目的地,FileWriter流称作BufferedWriter的底层流,BufferedWriter流将数据写入缓冲区,底层流负责将数据写到最终的目的地。例如:FileWriter tofile=new FileWriter(hello.txt);BufferedWriter out=new
25、BufferedWriter(tofile);BufferedReader流调用方法:write(String str)write(String s,int off,int len)把字符串s或s的一部分写入到目的地。BufferedWriter是一个增加了flush()方法的Writer。flush()方法可以用来确保数据缓冲区确实被写到实际的输出流。用BufferedWriter 可以通过减小数据被实际地写到输出流的次数而提高程序的性能。在下面的例子5中,我们将文件“Student.txt”中的内容按行读出,并写入到另一个文件中,且给每一行加上行号。返回如何读取控制台输入 在Java 1.
26、0中,完成控制台输入的唯一途径是字节流,这种方法现在依旧可用。但是,这种做法不值得推荐。在Java 2中,读取控制台输入的首选方法应该是字符流,它使程序容易符合国际标准,并且易于维护。在Java中,控制台输入由从System.in读取数据来完成。为获得属于控制台的字符流,在BufferedReader对象中包装了System.in。BufferedReader支持缓冲输入流,它最常见的构造函数如下:BufferedReader(ReaderinputReader)其中,inputReader是链接被创建的BufferedReader实例的流。Reader是一个抽象类,它的一个具体的子类是Inp
27、utStreamReader,该子类将字节转换成字符。为获得链接System.in的一个InputStreamReader的对象,使用下面的构造函数:InputStreamReader(InputStreaminputStream)由于System.in引用了InputStream 类型的对象,它可以用于inputStream。综上所述,下面的代码创建了与键盘相连的BufferedReader对象。BufferedReaderbr=newBufferedReader(newInputStreamReader(System.in);当该语句执行后,br是通过System.in生成链接控制台的字符
28、流。读取字符 要从BufferedReader读取字符,用read()。例如:intread()throwsIOException 该方法每次执行都从输入流读取一个字符,然后以整型形式返回。当遇到流的末尾时,它返回-1。可以看到,它要引发一个IOException异常。读取字符串 从键盘读取字符串,使用readLine()方法,它是BufferedReader 类的成员。这个方法的一般使用形式如下。StringreadLine()throwsIOException 该方法返回一个String对象。字节数组流字节数组流 字节输入流:ByteArrayInputStream 和字节输出流:Byte
29、ArrayOutputStream分别使用字节数组作为流的源和目标。使用下列ByteArrayInputStream流的两个构造方法构造字节数组输入流对象:ByteArrayInputStream(byte buf)ByteArrayInputStream(byte buf,int offset,int length)第一个构造方法构造的数组字节流的源是参数buf指定的数组的全部字节单元,第二个构造方法构造的数组字节流的源是参数buf指定的数组从offset处取的length个字节单元。使用下列ByteArrayOutputStream流的两个构造方法构造字节数组输出流对象:ByteArray
30、OutputStream()ByteArrayOutputStream(int size)第一个构造方法构造的数组字节输出流指向一个默认大小为32字节的缓冲区,如果输出流向缓冲区写入的字节个数大于缓冲区时,缓冲区的容量会自动增加。第二个构造方法构造的数组字节输出流指向的缓冲区的初始大小由参数size指定,如果输出流向缓冲区写入的字节个数大于缓冲区时,缓冲区的容量会自动增加。数组字节输出流调用 public byte toByteArray()方法,可以返回输出流写入到缓冲区的全部字节。数组字节流读写操作不会发生IOException异常。在下面的例子6中,我们向内存(输出流的缓冲区)写入ASC
31、II表,然后再读出这些字节和字节对应的字符。字符数组流字符数组流 与数组字节流对应的是数组字符流:CharArrayReader与CharArrayWriter类,数组字符流分别使用字符数组作为流的源和目标。与数组字节流不同的是,数组字符流的读操作可能发生IOException异常。在下面的例子7中,我们将Unicode表中的一些字符写入内存,然后再读出。返回字符串流字符串流 StringReader使用字符串作为流的源。使用下列构造方法构造字符串输入流对象:public StringReader(String s)该构造方法构造的输入流指向参数s指定的字符串,字符串输入流调用 public
32、int read()方法顺序读出源中的一个字符,并返回字符在Unicode表中的位置;调用 public int read(char buf,int off,int len)方法可以顺序地从源中读出参数len指定的字符个数,并将读出的字符存放到参数b指定的数组中,参数off指定数组b存放读出字符的起始位置,该方法返回实际读出的字符个数。字符串流字符串流 字符串输出流StringWriter调用下列方法可以向缓冲区写入字符:public void write(int b)public void write(char b,int off,int len)public void write(Stri
33、ng str)public void write(String str,int off,int len)字符串输出流调用public String toString()方法,可以返回输出流写入到缓冲区的全部字符;调用public void flush()方法可以刷新缓冲区。返回数据流数据流1DataInputStream类和DataOutputStream类 DataInputStream 类和DataOutputStream类创建的对象称为数据输入流和数据输出流。这两个流是很有用的两个流,它们允许程序按着与机器无关的风格读取Java原始数据。也就是说,当我们读取一个数值时,不必再关心这个数值
34、应当是多少个字节。2DataInputStream类和DataOutputStream的构造方法(1)DataInputStream(InputStream in)将创建的数据输入流指向一个由参数in指定的输入流,以便从后者读取数据(按着机器无关的风格读取)。(2)DataOutputStream(OutnputStream out)将创建的数据输出流指向一个由参数out指定的输出流,然后通过这个数据输出流把Java数据类型的数据写到输出流out。数据流数据流 下面的这个例子8应用程序可以完成写几个Java类型的数据到一个文件,并再读出来。返回管道流管道流 管道是不同线程之间直接传输数据的基本
35、手段。一个线程A通过它的输出管道发送数据,另一个线程B从自己的输入管道中读取数据,因为B把自己的输入管道接到了A的输出管道上了。可以使用PipedInputStream类的无参数构造方法创建一个管道输入对象:PipedInputStream in=new PipedInputStream();管道输入流in 处于等待连接状态,要使用in,必须再使用PipedOutputStream类的构造方法创建一个等待连接的管道输出流:PipedOutputStream out=new PipedOutputStream();然后使用connect(PipedOutputStream c)方法把in和out
36、相连接:in.connect(out);也可以使用带参数的构造方法:PipedInputStream(PipedOutputStream a)创建一个管道输入流,它被连接到由参数a指定的管道输出流。可以使用PipedOutputStream 类的无参数构造方法创建一个管道输出流:PipedOutputStream out=new PipedOutputStream();管道输出流out 处于等待连接状态,要使用out,必须再使用PipedInputStream类的构造方法创建一个等待连接的管道输入流:PipedInputStream in=new PipedInputStream();然后使用
37、connect(PipedInputStream c)方法把in和out相连接:out.connect(in);也可以使用带参数的构造方法 PipedOutputStream(PipedInputStream a)创建一个管道输出流,它被连接到由参数a指定的管道输入流。在下面的例子9模拟两个人玩猜数字游戏。第一个线程首先向第二个线程发出一句话:“给你一个1至100之间的数,请您猜”,然后就等待第二个线程的回复。第二个线程将自己的猜测发给第一个线程,第一个线程收到回复后,将根据收到的猜测的情况,再向第二个线程发出:“您猜大了”、“您猜小了”或“您猜对了”这样的信息。第二个线程再根据收到的信息继续
38、将自己的猜测发给第一个线程。返回对象流对象流 1ObjectInputStream类和ObjectOutputStream类 ObjectInputStream类和ObjectOutputStream类分别是InputStream类和OutputStream类的子类。ObjectInputStream类和ObjectOutputStream类创建的对象被称为对象输入流和对象输出流。对象输出流使用writeObject(Object obj)方法将一个对象obj写入输出流送往目的地,对象输入流使用readObject()从源中读取一个对象到程序中。ObjectInputStream类和Objec
39、tOutputStream类的构造方法分别是:ObjectInputStream(InputStream in),ObjectOutputStream(OutputStream out)。当我们使用对象流写入或读入对象时,要保证对象是序列化的。这是为了保证能把对象写入到文件,并能再把对象正确读回到程序中的缘故。Java提供给我们的绝大多数对象都是所谓序列化的,比如组件等。一个类如果实现了Serializable接口,那么这个类创建的对象就是所谓序列化的对象。Serializable接口中的方法对程序是不可见的,因此实现该接口的类不需要实现额外的方法,当把一个序列化的对象写入到对象输出流时,JV
40、M就会实现Serializable接口中的方法,按着一定格式的文本将对象写入到目的地。需要注意的是:使用对象流把一个对象写入到文件时不仅要保证该对象是序列化的,而且该对象的成员对象也必须是序列化的。在下面的例子10中有一个实现接口Serializable的Goods类。返回序列化与对象克隆序列化与对象克隆 对象调用clone方法(已过时)就可以获取对象的“复制品”,称作原对象的克隆对象。对象进行克隆时需要特别注意的是:如果原对象有引用型成员变量,那么克隆对象对应的成员变量的引用就与原对象那个成员变量的引用相同,那么,克隆对象对自己的这个成员变量所引用的实体的操作,将影响原对象引用型成员变量的实
41、体。这样一来就涉及到一个深度克隆问题,因为,原对象的成员变量中可能还会有其它对象。因此,程序必须重写clone方法,增加了编程的难度。使用对象流很容易得获取一个序列化对象的克隆。我们只需将该对象写入到对象输出流,然后用对象输入流读回的对象就是原对象的一个克隆。在下面的例子11中,我们将对象写入到内存,然后读回该对象的一个克隆。返回随机读写流随机读写流 Java还提供了专门用来处理文件输入输出操作的功能更完善的RandomAccessFile流。当用户真正需要严格地处理文件时,就可以使用RandomAccessFile类来创建一个对象,称做随机读写流。RandomAccessFile类的两个构造
42、方法:(1)RandomAccessFile(String name,String mode)参数name 用来确定一个文件名,给出创建的流的源,也是流目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权利。(2)RandomAccessFile(File file,String mode):参数file 是一个File对象,给出创建的流的源,也是流目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权利。例子12中我们把几个int型整数写入到一个名字为tom.dat文件中,然后按相反顺序读出这些数据。返回文件锁文件锁 JDK1.4增加了一个Fil
43、eLock类,该类的对象称做文件锁。RondomAccessFile创建的流在读写文件时可以使用文件锁,那么只要不解除该锁,其它线程无法操作被锁定的文件。使用文件锁的步骤如下:(1)首先使用RondomAccessFile流建立指向文件的流对象,该对象的读写属性必须是“rw”,例如:RandomAccessFile input=new RandomAccessFile(Example.java,rw);(2)流对象input调用方法getChannel()获得一个连接到底层文件的FileChannel 对象(信道),例如 FileChannel channel=input.getChannel();(3)信道调用tryLock()或lock()方法获得一个FileLock(文件锁)对象,这一过程也称做对文件加锁,例如:FileLock lock=channel.tryLock();另外,FileInputStream以及FileOutputStream在读/写文件时都可以获得文件锁。在下面的例子13中有两个线程,一个线程在读取文件的过程中,使用了文件锁。该线程每次读取文件的50字节,然后给文件加锁,该线程休眠1000毫秒后,调用release()释放文件锁。在该线程休眠期间其他线程无法读取该文件。返回