基础知识
1、流的分类 所有的流都位于java.io包
根据流的方向分:输入流和输出流
根据处理数据的单位分:字节流和字符流
根据功能不同分:节点流和处理流
各种流的抽象类,所有流都继承于这四个流
字节流 | 字符流 | |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
注:写是往程序外写,用的是输出流;读是往程序里读,用输入流;输入流、输出流都是站在程序的角度来说
字节流:按照最原始的形式读,读出来的就是0、1串,但是是一个字节一个字节(即8位8位的)的读。
字符流是一个字符一个字符(一个字符是2个字节16位)的读。
节点流 :直接连接到数据源,从数据源上直接读写数据(如:文件、内存)
处理流:用于处理节点流,连接在已有的流上(包括节点流和处理流),用于处理流的数据,为程序提供更强大的读写功能,一个流的外面可以套多层处理流。
2、InputStream的基本方法
继承自InputStream的流都是用于向程序中输入数据,且数据的单位为字节(8位),下图中深色为字节流,浅色为处理流。
(1)int read() throws IOException:读取一个字节并以整数的形式返回(0-255),方法的返回值即为督导如果返回-1表示已经读到了输入流的末尾。
(2)int read(byte[] buffer) throws IOException:读取一个字节并存储到一个数组buffer,把字节比作水滴,流为水流,buffer 相当于水桶, 该方法就相当于把一滴一滴的水滴接收后存放到水桶里,读满buffer字节处理后再对读到的数据进行处理(装满水桶),int返回的是实际读取的字节数,返回的是实际读了多少个字节,如果读取前已经到了输入流的末尾返回-1.
(3)int read(byte[] buffer, int offset, int length) throws IOException, 读取length个字节并存到字节数组buffer里,从buffer的offset位置开始存,返回的是实际读了多少个字节,返回的int可以比length小。
(4)void close() throws IOException 关闭流释放内存资源。
(5)long skip(long n) throws IOException 跳过多少了字节不读。
3、outputStream的基本方法
继承自outputStream的流是用于程序中输出数据,且数据的单位为字节;下图中深色为字节流,浅色为处理流
(1)void write(int b) throws IOException; 向输出流写入一个字节数据,该字节数据为参数b的低8位。
(2)void write(byte[] b) throws IOException; 将一个字节类型的数组中的数据写入输出流。
(3)void write(byte[] b, int off, int len) throws IOException; 将一个字节类型的数组中的从指定位置off开始的len个字节写入到输出流。
(4)void close() throws IOException; 关闭流释放内存资源
(5)void flush() throws IOException; 重要方法,当用close()方法关闭输出流时,有可能数据还没有完全写入到输出流,输出流就被粗暴地关闭了,因此在IO编程时要养成良好的习惯,关闭输出流之前务必调用flush()方法,将输出流缓冲中的数据全部写出到目的地。根据帮助文档,对于outputStream来说虽然有flush方法,且编程时通常会写flush方法,但对于outputStream及其子类该方法并未起作用。
4、Reader的基本方法
继承自Reader的流都是用于向程序中输入数据,与InputStream的不同之处在于数据的单位为字符(16 bit),比如在处理中文字符的时候,中文字符占两个字节,如果采用字节流就有可能出现读了半个汉字的情况,因此字符流也很有必要。下图中深色为节点流,浅色为处理流。
5、Writer的基本方法
继承自writer的流都是用于向程序中输入数据,与outputStream的不同之处在于数据的单位为字符(16 bit),比如在处理中文字符的时候,中文字符占两个字节,如果采用字节流就有可能出现写了半个汉字的情况,因此字符流也很有必要。下图中深色为节点流,浅色为处理流。
这里void write(String string)方法是将一个字符串中的字符写入到输出流,其中的实现过程是调用了java.lang.String的toCharArray()方法将字符串转换成了字符数组写入到输出流中。
void write(String string, int offset, int length) throws IOException 是从String字符串的offset位置开始,将length个字符写入到输出流。
5、流的各子类以及它们的基本方法
(1)文件处理
FileInputStream和FileOutputStream:
分别继承自InputStream和OutputStream,用于向文件中输入和输出字节。
常用构造方法:
FileInputStream用于从文件中读取数据的原始字节,比如读取图片,如果要读取字符类型的数据,需要用FileReader
FileInputStream(String name) throws FileNotFoundException 创建一个文件输入流,同时连接都系统中的名为name的文件上。
FileInputStream(File file) throws FileNotFoundException 创建一个文件输入流,同时连接到指定文件上。
FileOutputStream(String name) throws FileNotFoundException 创建一个文件输出流,并且将要输出的数据写入到名称指定的文件上。
FileOutputStream(File file) throws FileNotFoundException 创建一个文件输出流,并且将要输出的数据写入到指定的文件上。FileOutputStream当文件不存在时会自动创建文件,但不会自动创建不存在的路径。如果路径存不存在 、文件不存在且不能够被创建,文件存在但打不开,会抛出FileNotFoundException异常。
FileOutputStream(String name, boolean append) throws FileNotFoundException
FileOutputStream( File file, boolean append) throws FileNotFoundException
FileInputStream和FileOutputStream类支持其父类InputStream和OutputStream提供的所有读写方法。
注意:在实例化FileInputStream和FileOutputStream流时一定要用try...catch语句包起来用以处理可能抛出的FileNotFoundException,在调用读写方法时也一定要用try...catch语句包起来用以处理可能抛出的IOException,FileNotFoundException是IOException的子类。
练习小程序I:用字节输入流读文件中的内容
package test.io.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest {
public static void main(String args[]){
FileInputStream fin = null;
int b = 0;
try{
//路径的“\”如果直接用反斜杠“”会提示错误,这里要用两个反斜杠“\”或者一个正斜杠“/”
fin = new FileInputStream("E:\技术学习\java\test\Socket\test.txt");
}catch(FileNotFoundException e){
e.printStackTrace(); //将错误信息打印到控制台,不写就不会打印错误信息在控制台。
System.out.println("文件不存在");
}
try {
while((b= fin.read()) != -1){
//System.out.print(b);
System.out.print((char)b);
}
fin.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件读取错误");
}
}
}
如果用fileInputStream.read()读取中文汉字会乱码,因为fileInputStream为字节流,只能一个字节一个字节的读取内容,每个汉字占两个字节,读取出的都是半个汉字,识别不出,所以乱码。
注意:在练习的过程中出现一个奇怪的错误:练习的时候程序中int b = 0;这一行没有,下面try程序块中的代码为:
while(fin.read() != -1){
System.out.print((char)fin.read() );
}
会得到一个奇怪的结果:控制台上只打印出了位置为双数的内容。比如输入abcdefghijklmnopqrstuvwxyz这26个英文字母,打印结果为:
bdfhjlnprtvxz
这是因为每执行一次fileInputStream.read()方法就会去读取一个字节的内容,在while的条件里执行了一次fin.read(),读出的结果只是判断没有打印,而{}中的System.out.print((char)fin.read() );又执行了一次read方法,读出的是下一个字节的内容,因此输出的是下一个字节的内容,单数位置的字节就被空出来了。
练习小程序2:文件内容复制
package test.io.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest {
public static void main(String args[]){
FileOutputStream fos = null;
FileInputStream fis = null;
int b = 0;
try{
fis = new FileInputStream("E:/技术学习/java/test/Socket/TalkClient.java");
fos = new FileOutputStream("E:/技术学习/java/test/Socket/testtest.txt");
}catch(FileNotFoundException e){
e.printStackTrace();
System.out.println("文件不存在或文件打不开");
}
try {
while((b=fis.read()) != -1){
fos.write(b);
}
fos.flush();
fis.close();
fos.close();
System.out.println("文件已复制");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件读写错误");
}
}
}
练习小程序3:字符流读文件
package test.io.file;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] agrs){
FileReader fr = null;
int b = 0;
try {
fr = new FileReader("E:/技术学习/java/test/Socket/TalkClient.java");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件不存在");
}
try {
while((b = fr.read()) != -1){
System.out.print((char)b);
}
fr.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("读文件错误");
}
}
}
练习小程序4:字符流写文件
package test.io.file;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String args[]){
FileWriter fw = null;
try {
fw = new FileWriter("E:/技术学习/java/test/Socket/test1.txt");
} catch (IOException e) {
e.printStackTrace();
}
try {
for(int i=0; i<5000; i++){
fw.write(i);
}
fw.flush();
fw.close();
System.out.println("写入文件内容成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}