分布式-网络通信-IO-基础(2)

IS 与 OS
1. 基本 IO 操作
1.1. InputStream 与 OutputStream
1.1.1. 输入与输出
我们编写的程序除了自身会定义一些数据信息外,经常还会引用外界的数据,或是将自身的数据发送到
外界。比如,我们编写的程序想读取一个文本文件,又或者我们想将程序中的某些数据写入到一个文件
中。这时我们就要使用输入与输出。
什么是输入:输入是一个从外界进入到程序的方向,通常我们需要 读取 外界的数据时,使用输入。所以 “ ”
输入是用来读取数据的。
什么是输出:输出是一个从程序发送到外界的方向,通常我们需要 写出 数据到外界时,使用输出。所以 ” ”
输出是用来写出数据的。
流是一套 API(灰常多),提供了各种类型数据的读写 API!流是一套可扩展的 API,通过扩展的 API 支
持了各种类型数据的读写。
流是从两个抽象类扩展的:
• InputStream 输入流
• read()
• read(byte[] buf)
• OutputStream 输出流
• wirte(int b)
• write(byte[] buf)
1.1.2. 节点流与处理流
按照流是否直接与特定的地方 (如磁盘、内存、设备等) 相连,分为节点流和处理流两类。
节点流:可以从或向一个特定的地方(节点)读写数据。(功能简单 byte 读写)
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
(必须依赖低级流 不可单独工作 功能复杂 使用方便)
处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链
接。
1.1.3. InputStream 与 OutputStream 常用方法
InputStream 是所有字节输入流的父类,其定义了基础的读取方法,常用的方法如下:
int read()
读取一个字节,以 int 形式返回,该 int 值的 低八位 有效,若返回值为 ” ” -1 则表示 EOF。
int read(byte[] d)
尝试最多读取给定数组的 length 个字节并存入该数组,返回值为实际读取到的字节量。
总结:输入流是抽象类,所有的输入扩展流都扩展于 InputStream。
InputStream 定义了基本的数据读取方法 read 其读取规则与 RAF 中的 read 规则一致。
InputStream 是抽象类,不能直接使用,使用的都是其子类。
OutputStream 是所有字节输出流的父类,其定义了基础的写出方法,常用的方法如下:
void write(int d)
写出一个字节,写的是给定的 int 的 低八位 ” ”
void write(byte[] d)
将给定的字节数组中的所有字节全部写出
总结:
文件输出流是 OutputStream 的子类,实现了其约定的抽象方法:write
使用文件流可以向文件写出数据。
案例:
public class Demo05 {
public static void main (String[] args) throws Exception{
//打开被复制的文件(被读取的文件)
FileInputStream in =new FileInputStream("demo.txt");
//打开被写入的文件(复制目标文件)
FileOutputStream out =new FileOutputStream(
"newOne.txt");
int b;//b 代表文件中的每个 byte
while((b = in.read())!=-1){
out.write(b);
}
in.close();
out.close();
}
}
1.2. 文件流
1.2.1. 创建 FOS 对象(重写模式)
FileOutputStream 是文件的字节输出流,我们使用该流可以以字节为单位将数据写入文件。
1.构造方法:
FileOutputStream(File file)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
例如:
FIle file = new File("demo.dat");
FileOutputStream fos = new FileOutputStream(file);
2.构造方法:
FileOutputStream(String filename):
创建一个向具有指定名称的文件中写入数据的输出文 件流。
例如:
FileOutputStream fos = new FileOutputStream("demo.dat");
这里需要注意,若指定的文件已经包含内容,那么当使用 FOS 对其写入数据时,会将该文件中原有数据
全部清除。
总结:构造器
文件流的构造器必须给定被写出的文件名
• FileOutputStream(Stirng filename)
• FileOutputStream(File file)
1.2.2. 创建 FOS 对象(追加模式)
通过上一节的构造方法创建的 FOS 对文件进行写操作时会覆盖文件中原有数据。若想在文件的原有数据
之后追加新数据则需要以下构造方法创建 FOS
1.构造方法:
FileOutputStream(File file,boolean append)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
例如:
File file = new File("demo.dat");
FileOutputStream fos = new FileOutputStream(file,true);
2.构造方法:
FileOutputStream(String filename,boolean append):
创建一个向具有指定名称的文件中写入数据的输出文 件流。
例如:
FileOutputStream fos = new FileOutputStream("demo.dat",true);
以上两个构造方法中,第二个参数若为 true,那么通过该 FOS 写出的数据都是在文件末尾追加的。
总结:
重载的构造器可以按照追加的方式打开文件,打开以后文件的写入位置在文件的末尾,写出数据追加到
文件的末尾。
• FileOutputStream(Stirng filename, boolean append )
• FileOutputStream(File file, boolean append )
文件输出流在打开文件时候,如果文件不存在就创建文件,如果不是追加方式时候就将文件的
内容清空,重写写文件。如果文件名是一个目录,这时候抛出异常!
1.2.3. 创建 FIS 对象
FileInputStream 是文件的字节输入流,我们使用该流可以以字节为单位读取文件内容。
FileInputStream 有两个常用的构造方法:
1. FileInputStream(File file):
创建用于读取给定的 File 对象所表示的文件 FIS
例如:
File file = new File("demo.dat");
FileInputStream fis = new FileInputStream(file);//创建一个用于读取 demo.dat 文件的输
入流
2.另一个构造方法:
FileInputStream(String name):
创建用于读取给定的文件系统中的路径名 name 所指定的文件的 FIS
例如
FileInputStream fis = new FileInputStream("demo"); //创建一个用于读取 demo.dat 文件的
输入流
总结:
• FileInputStream(File file)
• FileInputStream(String filename)
FileInputStream 构造器用于以读取的方式打开文件,如果文件不存在(或者是目录)则抛
出文件没有找到的异常!
1.2.4. read()和 write(int d)方法
FileInputStream 继承自 InputStream,其提供了以字节为单位读取文件数据的方法 read。
int read()
从此输入流中读取一个数据字节,若返回-1 则表示 EOF(End Of File)
FileOutputStream 继承自 OutputStream,其提供了以字节为单位向文件写数据的方法 write。
void write(int d)
将指定字节写入此文件输出流。,这里只写给定的 int 值的 低八位 ” ”
例如
FileOutputStream fos = new FileOutputStream("demo.dat");
fos.write('A');//这里要注意,char 占用 2 个字节,但这里只写入了 1 个字节。
1.2.5. read(byte[] d)和 write(byte[] d)方法
FileInputStream 也支持批量读取字节数据的方法:
int read(byte[] b)
从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中 。
FileOutputStream 也支持批量写出字节数据的方法:
void write(byte[] d)
将 b.length 个字节从指定 byte 数组写入此文件输出流中。
例如:
FileOutputStream fos = new FileOutputStream("demo.txt");
byte[] data = "HelloWorld".getBytes();
fos.write(data);//会将 HelloWorld 的所有字节写入文件。
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流的方法:
void write(byte[] d,int offset,int len)
例如:
FileOutputStream fos = new FileOutputStream("demo.txt");
byte[] data = "HelloWorld".getBytes();
fos.write(data,5,5);//只会将 world 这 5 个字节写入文件。
三.缓冲流
缓冲流是一种扩展流,其在基本的 byte 节点流的基础上扩展了自动化缓冲区管理功能。使用缓冲流可以
简化缓冲区的管理,提高流的 IO 性能。工作中使用的非常广泛。
缓冲流有两个类 BufferedInputStream 和 BufferedOutputStream 分别是输入缓冲流和输出
缓冲流。输入缓冲流为输入流扩展了缓冲区功能,输出缓冲流为输入流扩展了缓冲区功能。
1.BufferedOutputStream
1. BOS 基本工作原理
与输入流相似,在向硬件设备做写出操作时,增大写出次数无疑也会降低写出效率,为此我们可以使用
缓冲输出流来一次性批量写出若干数据减少写出次数来提高写 出效率。
BufferedOutputStream 缓冲输出流内部也维护着一个缓冲区,每当我们向该流写数据时,都会先将数据
存入缓冲区,当缓冲区已满时,缓冲流会将数据一次性全部写出。
2. BOS 的 flush 方法
使用缓冲输出流可以提高写出效率,但是这也存在着一个问题,就是写出数据缺乏即时性。有时我们需
要在执行完某些写出操作后,就希望将这些数据确实写出,而非在缓冲区中保存直到缓冲区满后才写出。
这时我们可以使用缓冲流的一个方法 flush。
void flush()
清空缓冲区,将缓冲区中的数据强制写出。
案例:
public class Demo07 {
public static void main(String[] args)
throws Exception{
FileOutputStream fos = new FileOutputStream("test.txt");
//创建缓冲输出流 bos,必须依赖于节点流 fos
BufferedOutputStream bos =new BufferedOutputStream(fos);
//bos 的写出方法先将数据写到缓冲区
//当缓冲区满了再写到文件中。
bos.write("HelloWorld".getBytes());
//flush 方法作用:检查缓冲区中是否有数据,
//如果有数据就将数据强行写文件。
bos.flush();
//bos 关闭方法先检查缓冲区是否有数据,如果有数据
//将数据写到文件中,再关闭文件。
bos.close();
}
}
2. BufferedInputStream
1. BIS 基本工作原理
在读取数据时若以字节为单位读取数据,会导致读取次数过于频繁从而大大的降低读取效率。为此我们
可以通过提高一次读取的字节数量减少读写次数来提高读取的效率。
BufferedInputStream 是缓冲字节输入流。其内部维护着一个缓冲区(字节数组),使用该流在读取一个字
节时,该流会尽 可能多的一次性读取若干字节并存入缓冲区,然后逐一的将字节返回,直到缓冲区中的
数据被全部读取完毕,会再次读取若干字节从而反复。这样就减少了读取的次 数,从而提高了读取效率。
BIS 是一个处理流,该流为我们提供了缓冲功能。
2. BIS 实现输入缓冲
使用缓冲流来实现文件复制:
FileInputStream fis = new FileInputStream("java.zip");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("copy_java.zip");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d = -1;
while((d = bis.read())!=-1){
bos.write(d);
}
bis.close();//读写完毕后要关闭流,只需要关闭最外层的流即可
bos.close();
四. 对象流
ObjectOutputStream
ObjectInpputStream
对象流是高级流,不能单独工作,必须依赖低级流(低级流做为构造器参数),对象流提供了对象的序
列化和反序列化功能,可以将对象序列化为 byte 数据,或将 byte 数据反序列化为对象。
对象流要求被序列化的对象必须实现序列化接口,如果不实现序列化接口,在序列化时候会出现运
行异常。
对象序列化概念
对象是存在于内存中的。有时候我们需要将对象保存到硬盘上,又有时我们需要将对象传输到另一台计
算机上等等这样的操作。这时我们需要将 对象转换为一个字节序列,而这个过程就称为对象序列化。相
反,我们有这样一个字节序列需要将其转换为对应的对象,这个过程就称为对象的反序列化。
1. 使用 OOS 实现对象序列化
ObjectOutputStream 是用来对对象进行序列化的输出流。
其实现对象序列化的方法为:
void writeObject(Object o)
该方法可以将给定的对象转换为一个字节序列后写出。
例如:
Emp emp = new Emp("张三",12,"男");
FileOutputStream fos = new FileOutputStream("Emp.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(emp);//将 emp 对象序列化后写入文件
oos.close();
2. 使用 OIS 实现对象反序列化
ObjectInputStream 是用来对对象进行反序列化的输入流。
其实现对象反序列化的方法为:
Object readObject()
该方法可以从流中读取字节并转换为对应的对象。
例如:
FileInputStream fis = new FileInputStream("Emp.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Emp emp = (Emp)ois.readObject();//将 Emp 对象从文件中读取并反序列....
ois.close();
3. Serializable 接口
ObjectOutputStream 在对对象进行序列化时有一个要求,就是需要序列化的对象所属的类必须实现
Serializable 接口。
实现该接口不需要重写任何方法。其只是作为可序列化的标志。
通常实现该接口的类需要提供一个常量 serialVersionUID,表明该类的版本。若不显示的声明,在对象
序列化时也会根据当前类的各个方面计算该类的默认 serialVersionUID,但不同平台编译器实现有所不
同,所以若向跨平台,都应显示的声明版本号。
如果声明的类序列化存到硬盘上面,之后随着需求的变化更改了类别的属性(增加或减少或改名),那么当
反序列化时,就会出现 InvalidClassException,这样就会造成不兼容性的问题。 但当
serialVersionUID 相同时,它就会将不一样的 field 以 type 的预设值反序列化,可避开不兼容性问题。
例如:
public class Emp implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String gender;
//getter and setter and other...}
4. transient 关键字
对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要
的属性,从而对序列化后得到的字节序列 瘦” ” 身 。
关键字 transient 的作用:
避免数据的暴涨,忽略其不重要的数据。
例如:
public class Emp implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private transient int age;//该属性在序列化时会被忽略
private String gender;//getter and setter and other...
}
五.字符流 Reader 和 Writer
1. 字符流原理
Reader 是所有字符输入流的父类,而 Writer 是所有字符输出流的父类。字符流是以字符(char)为单位读
写数据的。一次处理一个 unicode。字符流都是高级流,其底层都是依靠字节流进行读写数据的,所以底
层仍然是基于字节读写数据的。
2. 常用方法
Reader 的常用方法:
int read()
读取一个字符,返回的 int”值低 16”位有效。
int read(char[] chs)
从该流中读取一个字符数组 length 个字符并存入该数组,返回值为实际读取到的字符量。
Writer 的常用方法:
void write(int c)
写出一个字符,写出给定 int 值 低” 16”位表示的字符。
void write(char[] chs)
将给定字符数组中所有字符写出。
void write(String str)
将给定的字符串写出
void write(char[] chs,int offset,int len):
将给定的字符数组中从 offset 处开始连续的 len 个字符写出
六. 转换流
1. 字符转换流原理
InputStreamReader:字符输入流, 使用该流可以设置字符集,并按照指定的字符集从流中按照该编码
将字节数据转换为字符并读取。
OutputStreamWriter:字符输出流,使用该流可以设置字符集,并按照指定的字符集将字符转换为对应字
节后通过该流写出。
2. 指定字符编码
InputStreamReader 的构造方法允许我们设置字符集:
InputStreamReader(InputStream in,String charsetName)
基于给定的字节输入流以及字符编码创建 ISR
InputStreamReader(InputStream in)
该构造方法会根据系统默认字符集创建 ISR
OutputStreamWriter:的构造方法:
OutputStreamWriter(OutputStream out,String charsetName)
基于给定的字节输出流以及字符编码创建 OSW
OutputStreamWriter(OutputStream out)
该构造方法会根据系统默认字符集创建 OSW
3. 使用 OutputStreamWriter
public void testOutput() throws IOException{
FileOutputStream fos = new FileOutputStream("demo.txt");
OutputStreamWriter writer= new OutputStreamWriter(fos,"UTF-8");//这里使用的字符编码
为 UTF-8
String str = "大家好!";//UTF-8 中文为 3 个字节,英文符号占 1 个字节
writer.write(str);//写出后该文件大小应该为 10 字节
writer.close();
}
4. 使用 InputStreamReader
public void testInput() throws IOException{
FileInputStream fis = new FileInputStream("demo.txt");
/** 这里设置了字符编码为 GBK* 之后再通过 ISR 读取 demo.txt 文件时* 就使用 GBK 编码读取
字符了*/
InputStreamReader reader= new InputStreamReader(fis,"GBK");
int c = -1;
while((c = reader.read()) != -1){
System.out.print((char)c);
}
reader.close();
}

猜你喜欢

转载自www.cnblogs.com/caoyingjielxq/p/9280533.html