1.流的概念
在计算机中,流是个抽象的概念,是对输入输出设备的抽象。在Java程序中,对于数据的输入/输 出操作,都是以"流"的方式进行
数据以二进制的形式在程序与设备之间流动传输,就像水在管道里流动一样,所以就把这种数据传输的 方式称之为输入流、输出流。这里描述的设备,可以是文件、网络、内存等
流具有方向性,可以分为输入和输出。
以java程序本身作为参照点,如果数据是从程序“流向”文件,那么这个流就是输出流,如果数据是从文 件“流向”程序,那么这个流就是输入流。例如:
2. 流的分类
java中的IO流可以根据很多不同的角度进行划分,常见的是以数据的流向和数据的类型来划分
根据数据的流向分为:输入流和输出流
输入流 :把数据从其他设备上读取到程序中的流 输出流 :把数据从程序中写出到其他设备上的流
输出流 :把数据从程序中写出到其他设备上的流
根据数据的类型分为:字节流和字符流
字节流 :以字节为单位(byte),读写数据的流
字符流 :以字符为单位(char),读写数据的流
字节输入流,在程序中,以字节的方式,将设备(文件、内存、网络等)中的数据读进来
字节输出流,在程序中,以字节的方式,将数据写入到设备(文件、内存、网络等)中
字符输入流,在程序中,以字符的方式,将设备(文件、内存、网络等)中的数据读进来
字符输出流,在程序中,以字符的方式,将数据写入到设备(文件、内存、网络等)中
注意,字节指的是byte,字符指的的是char
3 .流的结构
在Java中,和IO流相关的类,主要是在 java.io 包下的定义的
InputStream ,代表字节输入流类型
OutputStream ,代表字节输出流类型
Reader ,代表字符输入流类型
Writer ,代表字符输出流类型
java中常用的流及其关系结构,如下:
注意,一般情况下,一个流,会具备起码的三个特点:
是输入还是输出
是字节还是字符
流的目的地
其中,流的目的地指定是: 如果是输入流,就表示这个流从什么地方读数据,如果是输出流,就表示这 个流把数据写到什么地方!
例如, InputStream ,是所有字节输入流的抽象父类型。
那么它的子类型一定是以 字节 的方式,从某个地方把数据 读 取到程序中,只是不同的子类型,它们 的 目的地 不一样,并且一般可以通过子类型的名字看出它的的目的地是哪里,例如:
ByteArrayInputStream ,它是以字节的形式,去读取某个地方的数据,通过名字可以看出,它 的目的地是字节数组中,也就是以字节的形式从byte数组中读取数据
FileInputStream ,它是以字节的形式,去读取某个地方的数据,通过名字可以看出,它的目的
4.字节流
4.1 概述
计算机中,存储一切数据(文本、图片、视频等),都是以二进制的形式进行的,终都是一个一个的字 节,所以使用流,进行传输数据的时候,也是一个一个的字节进行的。
java.io.InputStream 是所有字节输入流的抽象父类型
InputStream 中核心的三个read方法
//每次读一个字节,返回值是本次读取的字节值
public abstract int read() throws IOException;
//每次读多个字节,并存放到指定的字节数组中,返回值是本次一共读取了多个字节(字节数)
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//每次读多个字节,并存放到指定的字节数组中,返回值是本次一共读取了多个字节(字节数) //同时可以指定从数组的什么位置开始存放,以及在数组中多存放多个字节 public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
注意, InputStream 中的 skip 方法,可以让我们先跳过指定的字节数,再开始读数据
java.io.OutputStream 是所有字节输出流的抽象父类型:
OutputStream 中核心的三个write方法:
int i = 1;
try {
for (; i < len ; i++)
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
java.io.OutputStream 是所有字节输出流的抽象父类型:
OutputStream 中核心的三个write方法:
//写出去一个字节值
public abstract void write(int b) throws IOException;
//把一个自己数组中的值全部写出去
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
//写出字节数组,指定开始位置,以及写出的字节数量
public void write(byte b[], int off, int len) throws IOException { if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
一般情况,使用字节流来操作数据的时候,往往是使用一对,一个字节输入流,负责读取数据, 一个字节输出流,负责将数据写出去,而这些流都将是 InputStream 和 ==OutputStream ==的子类 型
在代码中,使用流操作数据的的基本步骤是:
- 声明流
- 创建流
- 使用流
- 关闭流
注意,基本所有流的使用,都是这个套路
4.2 控制台
使用字节流,从控制台读取数据,以及向控制台中写数据。
//The "standard" input stream.
public final static InputStream in = null;
//The "standard" output stream.
public final static PrintStream out = null;
PrintStream 是 == OutputStream== 的子类型
public class Test {
public static void main(String[] args) {
//1.声明流
InputStream in = null;
OutputStream out = null;
//2.创建流,这里使用System中已经创建好的流对象
in = System.in;
out = System.out;
//3.使用流
int data = -1;
try {
data = in.read();
out.write(data);
out.flush();
//刷新缓冲区,强制所有数据写出去
} catch (IOException e) {
e.printStackTrace();
}finally {
//4.关闭流
if(in!=null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null) {
try { out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
这里是一次读取用一个字节,然后把字节值输出,也可以一次读取多个字节,然后将本次读取到 的都输出
注意,这个子类中重写的read方法,会让线程阻塞,等待用户在控制台中的输入,用户输入并按 下回车,程序中的read方法就从阻塞状态恢复过来,从而读取到用户输入的内容