输入输出可以说是计算机的基本功能。作为一种语言体系,java中主要按照流(stream)的模式来实现。其中数据的流向是按照计算机的方向确定的,流入计算机的数据流叫做输入流(inputStream),由计算机发出的数据流叫做输出流(outputStream)。
Java语言体系中,对数据流的主要操作都封装在java.io包中,通过java.io包中的类可以实现计算机对数据的输入、输出操作。在编写输入、输出操作代码时,需要用import语句将java.io包导入到应用程序所在的类中,才可以使用java.io中的类和接口。
在java.io包中提供了外部设备和计算机之间进行数据传输的输入、输出操作类。主要的操作类及其之间的继承关系如下图所示。从上往下依次是父类和子类的关系。
java.io包中针对输入、输出流目前支持针对字节(或者ASCII编码的字符)和Unicode编码的字符数据流传输。其中支持字节流传输的类包括InputStream和OutputStream类,支持Unicode编码的字符流传输的类有Reader和Writer类,这些类都是抽象类,是所有操作数据流的类的父类,它们提供了操作数据流的标准的基础方法,由子类具体实现数据流的输入、输出操作。
需要注意的是,如果传输的数据流的类型是其他类型,需要先将这些数据对象进行序列化(serializable),即串行化。如此可以将数据对象转换为顺序的字节流,然后就可以使用java.io中提供的各种输入输出操作方法进行数据传输。(实际数据的序列化通过实现java.io中提供的Serializable接口来完成)
控制台输入、输出操作都是静态的。它的原理是在java虚拟机启动后,提供输入、输出操作的类对象会始终驻留在内存中。这里提供输入、输出操作的类对象被定义在java.lang包中的system类中,类对象的名称是in、out和err,而java.lang包是嵌入在java虚拟机中的,并且在java虚拟机启动以后将该包中的类创建为对象驻留在了内存中。因此说system类所提供的输入输出操作为静态的。
实际上,在基本的控制台输入输出操作中,主要有两个方法read()和print()。其中read()负责输入,print负责将其输入的内容显示到屏幕。关于read()方法,在system类中共给出了三种参数类型:
System.in.read(); |
System.in.read(byte[] b); |
System.in.read(byte[] b, int off, int len) |
Read()方法的作用就是从终端命令行中读取字符到java程序中,读取的数据的范围在0~255之间。Read()方法一次只能从终端读取一个字符,这个字符取值范围在byte范围内,但是返回值类型是int型。
不带参数的read()方法在读取字符时,读到用户按下回车键或者数组被填满为止。两种带参数的read()方法中是把读取到的字符存入byte数组中,最后的带有3个参数的read()方方法的解释:后面两个参数指明了读到的字符放入byte数组中的起始位置和最多读取的字符个数。
(1)System.in
作为标输入输出,system.in是InputStream类的对象,当程序中需要从键盘读入数据时,只需要调用system.in中的read()方法即可。不过,在调用read()方式时必须捕获System.in.read()抛出的异常。System.in.read()是从键盘缓冲区读入一个字节的数据,返回16位的数据,其低位是真正输入的数据,而高位为0;当输入数据时,如果键盘缓冲区没有数据,系统将会进入阻塞状态,因此,常常可以利用System.in.read()的这个特点实现程序的暂停,用户输入后继续往下执行。
(2)System.out
System.out是标准输出流,打印输出流printStream类的对象,它定义了能够向显示屏输出各种数据类型的pingt()和println()方法。包括boolean,double,float,int,long类型的变量以及Object类的对象等等。当输出类型为对象时,它将自动调用对象的toString()方法,因此在对象所在的类中需要重写toString()方法并输出特定的文字。
(3)标准输入输出的重定向
重定向也就是改变标准输入输出的方向,具体的实现方法可以通过调用system类中提供的3个static方法实现:setln(InputStream in)、setOut(PrintStream out)、setErr(PrintStream out)
Java在操作磁盘文件时,将磁盘文件当做对象,把文件中存储的内容看做数据流,当通过文件来创建数据流时(将数据流对象和文件对象绑定),对数据流对象的输入、输出操作实际上都看做是对文件的读写操作。此处,文件中的数据流也看做是有顺序的数据流,特别的一点是文件中数据流最后一个流结束符数据是一个文件结束符(EOF)数据。
实际上,关于文件/目录读写操作相关的类、接口以及方法常用的主要有:FileInputStream/FileOutputStream、PipedInputStream/PipedOutputStream、随机文件读写、字符流Reader/Writer、DataInput/DataOutput接口、DataInputStream 和DataOutputStream过滤流、标准输入输出流等。
(1)文件操作
在java语言体系而中,将要处理的文件类型分为两种:文本文件和非文本文件(图片、视屏等都属于非文本文件)。其中文本文件的内容是以16bit(2字节)表示的,而非文本文件则是以8bit(1字节)表示的。两种文件读写操作上有比较大的区别,主要体现在:对文本文件而言,其读写处理的单位是字符(两个字节一组,可以解析为字符);而对非文本文件(流)而言,其读写是以单个字节为单位的,单独的字节在java中不能解析为字符。因此说两种文件的处理有较大不同。
(2)File类
与文件读写相关的类都包含在java.io库中,读入一般使用Input或者reader,而写出一般使用out或者writer。而流式一种利用缓冲机制实现将数据从生产者(如键盘、磁盘文件、内存或者其他设备)传送到接收该数据的消费者(比如屏幕、文件或者内存等)的过程的抽象表达。
文本文件读入 |
Reader |
文本文件写出 |
Writer |
流文件读入 |
inputStream |
流文件写出 |
outputStream |
而文件类File则主要用于创建文件对象的类,可以处理和文件名以及目录名有关的操作。File类独立于系统平台之外,利用其构造函数创建file对象,然后通过对象引用其成员函数实现对文件的各个属性的操作,实现对文件和目录的管理。即,通过File类所提供的方法可以得到文件或者目录的描述信息,比如文件名称、所在路径、可读性、可写性、文件长度等等;还能生成新的目录、改变文件名、删除文件、列出某个指定目录下的所有文件等。
(3)字节流文件操作的一般方法
输入输出操作的抽象基类有InputStream、OutputStream,实现对文件内容基本操作的常用方法有read()、write()、close()等。通常在实际的使用中,都是通过为InputStream、OutputStream创建派生类对象来实现对具体文件的读写。在这个过程中需要注意对异常的处理。
在java.io中定义的字节输入输出操作类都适合于对磁盘文件的读写操作。比如,直接用于磁盘文件读写操作的类有FileInputStream、FileOutputStream等,这些类可以通过绑定文件对象来创建输入、输出流对象,也可以直接指定磁盘文件名创建输入、输出流对象,通过对输入、输出流对象执行读写操作实现对指定文件的读写操作。
对文件操作的一般步骤是:
1)根据具体的操作类型,生成一个输入输出文件类的对象;
2)调用此类的成员函数实现对文件内容的读写操作;
3)关闭文件。
在对文件操作需要注意的是:
1)异常处理:因为在java.io包中,几乎对所有的类都声明了I/O异常,因此用户在编写程序时应该对这些异常加以处理;
2)流结束标识:当read()方法返回值为-1时,readline()方法返回值为NULL时。
(4)字符流文件的操作reader/writer
Reader/writer被称作字符流处理类,提供对字符的处理。不过Reader/writer也都是抽象类,属于它们的子类有InputStreamReader(InputStream in) / OutputStreamWriter(OutputStream out),BufferedReader(InputStreamReader isr, int size) / BufferedWrite(OutputStreamWriter osr, int size)等。
概括来说,java.io中定义的FileReader/FileWriter、BufferedReader/BufferedWrite、PrintWriter等类都是针对字符数据流的输入输出操作而言的。
1)InputStreamReader/OutputStreamWriter可以使用指定的编码规范并基于字节流生成对应的字符流。比如:
FileInputStream fis=new FileInputStream("example.txt");
InputStreamReader isr=new InputStreamReader(fis," iso-8859-1");
说明:为了能够在读取不同的字符时不出现乱码,可以才用 iso-8859-1编码规范,它是一种映射到ASCII码的编码方式,可以实现不同平台系统之间的字符的正确转换。
2)BufferedReader/BufferedWrite是为了提高字符流处理效率而引入的缓冲机制流处理类,实现了对字符流的批量处理。其中的readline()方法一次读取一整行字符,newLine()则是一次写入一整行字符。缓冲机制的引入使得可以把任意输入流/输出流“绑定”到缓流上获得性能的提升。缓冲区的大小可以再创建缓冲流对象时设置。
值得注意的是,在java语言中,处理命令行方式的键盘输入时,系统把输入的内容当做字符串看待,但是java并没有提供自动将输入串转换为其他类型数据的方法,因此,当需要从键盘接收数据时,必须由应用程序自己实现类型转换操作。
从本文前面的数据处理相关的类图关系可以看出,FileReader和FileWriter是文件字符流操作类。因为InputStreamReader(功能是将字节流转换为字符流)和OutputStreamWriter(功能是将字符流转换为字节流)字符流类在创建流类对象时必须以单个字节流作为原始数据流来打开一个文件,为了能够直接将一个具体文件名的文件直接转换为字符流类的对象,在java.io中提供了FileReader和FileWriter,它们分别是InputStreamReader和OutputStreamWriter的子类,适合于字符流数据的输入输出操作。