Java 基础查漏补缺【IO篇】

Posted by 石福鹏 on 2017-05-04

IO

IO是指Input/Output,即输入和输出。以内存为中心:

  • Input指从外部读入数据到内存,例如,把文件从磁盘读取到内存,从网络读取数据到内存等等。
  • Output指把数据从内存输出到外部,例如,把数据从内存写入到文件,把数据从内存输出到网络等等。

image-20200918173106607

Java.io包中定义了多个流类型(类或者抽象类)来实现输入/输出功能,根据不同的角度对其进行分类

  • 按照数据流的方向不同分为输入流输出流

  • 按照处理数据单位不同分为字节流字符流

  • 按照功能不同分为节点流处理流

    J2 SDK所提供的所有流类型位于包java.io分别继承自以下四种抽象流类型

    字节流 字符流
    输入流 InputStream Reader
    输出流 outputStream Writer

    节点流

    可以从一个特定的数据源(节点)读取数据(如:文件,内存)(通俗的讲就是一根管道直接怼在数据源上)

    处理流

    是”连续“在已经存在的流(节点流或者处理流)之上,通过对数据的处理为程序提供更为强大的读写能力

image-20200921160557404

InputStream的基本方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//读取一个字节并以证书的形式返回(0~255)
//如果返回为-1表示输入流已经到末尾了
int read() throws IoException

//读取一系列字节并存储到一个数据buffer
//返回实际读取的字节数,如果读取前已经到输入流的末尾返回-1
//即将字节数据都先放在内存的缓冲区buffer中,然后统一处理
int read(byte[] buffer) throw IoException

//读取length个字节
//并存储到一个字节数组buffer,从length位置开始
//返回实际读取的字节数,如果读取前已经到输入流的末尾返回-1
int read(byte[] buffer,int offset,int length) throws IOException

//关闭流释放内存资源
void close() throw IOException

//跳过N个字节不读,返回实际跳过的字节数
long skip(long n) throws IOException

OutputStream

这个方法会写入一个字节到输出流

image-20200922171033145

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//向输出流中写入一个字节数据,该字节数据为参数b的低8位
//这个方法会写入一个字节到输出流。要注意的是,虽然传入的是int参数,但只会写入一个字节,即只写入int最低8位表示字节的部分(相当于b & 0xff)。
void write(int b)throws IOException

//将一个字节类型的数组中的数据写入输出流
void write(byte[] b)throws IOException

//将一个字节类型的数组中的从指定位置off开始的len个字节写入到输出流
void write(byte[] b,int off,int len) throws IOException

//关闭流释放内存资源
void close() throws IOException

//将输出流中缓冲的数据全部写出到目的地
void flush()throws IOEXception

如果直接调用了close,相当于直接将水管切断了,所以一般情况下,调用的时候先吊用flush()

Reader

除了数据的单位是是字符(2字节)(16 bi t)之外,其他跟inputStream一样

image-20200922174130311

相关的基本方法操作基本单位都是字符

同理Writer也一样,数据单位位字符(2字节)(16位),基本操作单位都是字符

有一点不同

1
2
//将一个字符串中的字符写入到输出流
void write(String string)throws IOException

节点流

image-20200922175742314

FileInputStreamFileInputStream分别继承自InputStreamOutputStream,用于向文件中输入和输出字节

例子:FileInputStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TestFileInputStream {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
try {
in = new FileInputStream("/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test/TestFileInputStream.java");
} catch (FileNotFoundException e) {
System.out.printf("找不到指定文件");
}

try {
long num = 0;
while ((b = in.read()) != -1) {
System.out.printf((char) b + "");
num++;
}
in.close();
System.out.printf("一共读到了" + num + "个字节");
} catch (IOException e) {
e.printStackTrace();
System.out.printf("文件读取失败");
}
}
}

运行上面的程序,打印出来下面的内容

image-20200924152409479

但是我们发现,文中的汉子变成了乱码了,这是什么原因?

因为FileInputStream是一个一个字节往外拿,而中文是两个字节,所以解析不出来,使用FileReader就可以

例子:FileOutputStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestFileOutputStream {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test/TestFileInputStream.java");
out = new FileOutputStream("/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test/TestFileInputStream_bak.java");
while ((b = in.read()) != -1) {
out.write(b);
}
in.close();
out.close();
} catch (FileNotFoundException e) {
System.out.printf("找不到指定文件");
} catch (IOException e) {

}
System.out.printf("文件赋值成功");
}
}

FileReaderFileWriter同理

处理流

包在其他“管道”上的处理流

image-20200924170106998

缓冲流

要“套接”在响应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写效率,还增加了一些新的方法

常见的四种缓冲流

1
2
3
4
5
6
7
8
BufferedReader(Reader in);
BufferedReader(Reader in,int sz); //sz 为自定义缓冲区的大小
BufferedWriter(Writer out);
BufferedWriter(Writer out,int sz);
BufferedInputStream(InputStream in);
BufferedInputStream(InputStream in,int sz);
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out,int sz)

缓冲输入流支持其父类的markreset方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
try {
FileInputStream in = new FileInputStream("/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test/TestFileInputStream.java");
BufferedInputStream bufferedInputStream = new BufferedInputStream(in);
bufferedInputStream.mark(100);
int c = 0;
for (int i = 0; i <= 10 && (c = bufferedInputStream.read()) != -1; i++) {
System.out.printf((char) c + " ");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {

}
}

转换流

InputStreamReaderOutPutStreamWriter 用于字节流盒字符流之间的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
//写文件
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(""));
//读文件
InputStreamReader isr = new InputStreamReader(new FileInputStream(""));
//键盘的输入
InputStreamReader isr1 = new InputStreamReader(System.in);
//这里再转一下,是因为BufferedReader有个很好用的方法,可以一次读取一行
BufferedReader bw=new BufferedReader(isr1);
bw.readLine();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

数据流

DataInputStreamDataOutputStream分别继承自InputStreamOutputStream,它属于处理流,需要分别"套接"在InputStream和`OutputStream

DataInputStreamDataOutputStream提供了可以存取和机器无关的JJava原始类型数据(如:int,double)的方法

例如:将一个long类型的数据写入文件中

  • 方法一:将一个long类型的数据转成字节,然后写入

  • 方法二:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //在内存中分配了一个字节数组
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    //一根管道怼在内存上,他有个好处,可以直接将double类型的数据8个字节直接写到字节数组
    DataOutputStream dos = new DataOutputStream(baos);
    try {
    dos.writeDouble(Math.random());
    dos.writeBoolean(true);
    //上了两个一共写了9个字节
    //独处数据
    ByteArrayInputStream bait = new ByteArrayInputStream(baos.toByteArray());
    System.out.println(bait.available());
    DataInputStream dataInputStream = new DataInputStream(bait);
    System.out.println(dataInputStream.readDouble()); //先写的先读
    System.out.println(dataInputStream.readBoolean());
    dos.close();
    dataInputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }

    //输出
    // 9
    // 0.0407320091819684
    // true

    Print

    PrintWriterPrintStream都是输出流。分别针对字符和字节。他们均有自动的flush功能。他们的输出操作不会抛出异常

    Object 流

    直接将Object写入或者读出

    将一个对象转化成字节流即序列化。

    Serializable标记性接口,里面没有任何方法,编译看到类实现了Serializable,表示该类可以被序列化

    transient透明的,在序列化的时候不予考虑。

    externalizableSerializable的字接口,实现了它,相当于实现了Serializable接口,自己可以控制自己的序列化过程