流
流的本质是一组有顺序,有起点有终点的数据的集合,是对数据传输的抽象 。JavaIO流根据数据传输特性将流抽象成为各种类,组成了JAVAIO类框架。
io流分类
根据传输数据的类型,分为字节流和字符流,根据流向的不同分为输入流和输出流。
以下是JAVAIO流框架:
InputStream、OutSteam和 Reader 和Writer是两组抽象接口,分别代表了输入字节流、输出字节流和输入字符流和输出字符流。
字节流和字符流的区别:
- **读写单位不同。**字节流读写数据按字节为单位读取,字符流读写数据按字符为单位读取,根据码表映射字符,一次可以多去多个字节。
- **读写对象不同。**字节流可以读取任意数据包含视频、图片。字符流只能读取文本数据。
- 字节流在读取时本身不会使用缓冲区,但字符流是会用到缓冲区的,通过缓冲区来操作文件。
常见的流的使用
- FileReader / FileWriter 用于文件的读写
- BufferedInputstream/BufferedOutputStream BufferedReader/BufferedWriter 内置缓存,可以一次性读取写入多个数据,提高读取写入效率
- PrintWriter 打印流,用于输出文本信息到控制台或文件
- 可使用System.setIn () System.setOut() System,setErr()替换系统标准输入输出为其他的流
流的基础类:
- InputStream
- OutputStream
InputStream
字节层面的读
- read()
- int read() 读完会返回-1
- read(byte b[])
- read(byte b[])
- skip(long n)
- int available() 流里还有多少字节可以读
- mark()
- reset() 和mark一起使用
public static void main(String[] args) {
byte[] buffer = new byte[1024];
try {
int len = System.in.read(buffer);
String s = new String(buffer, 0, len);
System.out.println("读到了" + len + "字节");
System.out.println(s);
System.out.println("s的长度是:" + s.length());
} catch (IOException e) {
e.printStackTrace();
}
}
输入:123汉字abc
读到了13字节
s的长度是:9
汉字是两个字节,一个字符
文件流
- FileInputStream
- FileOutPutStream
- 对文件进行读写操作
- 实际工程中已经较少使用
- 更常用的是以在内存数据或通信数据上建立的流,如数据库的二进制数据读写或网络端口通信
- 具体的文件读写往往有更专业的类,比如配置文件和日志文件
public static void main(String[] args) {
byte[] buf = new byte[10];
for(int i = 0; i < buf.length; i++){
buf[i] = (byte)i;
}
try {
//在当前目录建立,如果已经存在,会覆盖掉,如果没有,新建一个文件
FileOutputStream out = new FileOutputStream("a.dat");
out.write(buf);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
流过滤器
以上的流都只能处理单个字节,单个字节读,单个字节写
- 以一个介质流对象为基础层层构建过滤器流,最终形成的流对象能在数据的输入输出过程中,逐层使用过滤器流的方法来读写数据
public static void main(String[] args) {
try {
//输入字节以外的数据,即用二进制写
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("a.dat")));
//在当前目录建立,如果已经存在,会覆盖掉,如果没有,新建一个文件
int i = 123456;
out.write(i);
out.close();
//用二进制读
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("a.dat")));
int j = in.readInt();
System.out.println(j);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
文本输入输出
InputStream和OutPutStream只能处理字节
DataInputStream和DataOutputStrea只能处理基本数据类型
Reader/Writer
- 二进制数据采用InputStream/OutputStream
- 文本数据采用Reader/Writer
public static void main(String[] args) {
try {
PrintWriter out = new PrintWriter(
new BufferedWriter(//字符缓冲输出流
new OutputStreamWriter(//构建Stream与Writer之间的桥梁,即字符流与字节流
new FileOutputStream("abc.txt"))));//字节流
int i = 123;
out.println(i);
out.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(
new FileInputStream("src/main.java")));
String line;
while((line = in.readLine()) != null){
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Reader
-
常用的是BufferReader
-
readLine() 读行
LineNumberReader
- 可以得到行号
- getLineNumber()
FileReader
-
InputStreamReader类的子类,所有方法都从父类中继承而来
-
FileReader(File file)
在给定从中读取数据的File的情况下创建一个新的FileReader
-
FileReader(String fileName)
在给定从中读取文件名的情况下创造一个新的FileReader
-
FileReader不能指定编码转换方式
汉字编码
BufferedReader in = new BufferedReader(
new InputStreamReader(
new FileInputStream("src/main.java"),"utf8"));
为inputStreamReader添加编码参数
格式化输入输出
PrintWriter
- format(“格式”,……)
- printf(“格式”,……)
- print(各种基本类型)
- println(各种基本类型)
Scanner
在InputStream或Reader上建立一个Scanner对象可以从流中的文本中解析出以文本表达的各种基本类型
- next…()
什么情况下使用
流的应用
public static void main(String[] args) {
try {
Socket socket = new Socket(InetAddress.getByName("localhost"), 12345);
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
//这里的socket主要指建立起来的连接
socket.getOutputStream())));
out.println("Hello");
out.close();
socket.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String line;
line = in.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}
阻塞/非阻塞
-
read()函数是阻塞的,在读到所需的内容之前就会停下来等
-
使用read()的更“高级”函数,如nextInt(),readLine()都是这样的
-
所以常用单独的线程来做socket读的等待,或使用nio的channel选择机制
-
对于socket,可以设置SO时间
- setSoTimeOut(int timeOut)
对象串行化
用来将对象转换成流再传递数据
- ObjectInputStream类
- readObject()
- ObjectOutputStream类
- writerObject()
- Serializable接口
public class PublicClientDemo {
public static void main(String[] args) {
try {
Student s1 = new Student("John",18,5);
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("obj.dat"));
out.writeObject(s1);
out.close();
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("obj.dat"));
Student s2 = (Student) in.readObject();
System.out.println(s2);
System.out.println(s1 == s2);// false
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}