版权声明:本文为CSDN博主「赵彦军」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhaoyanjun6/article/details/54292148
————————————————
版权声明:本文为CSDN博主「陆勤」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangloveall/article/details/7992448
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
字节流:一次读入或读出是8位二进制。
字符流:一次读入或读出是16位二进制。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
PipedOutputStream 是向与其它线程共用的管道中写入数据。
ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
总结:
节点流:直接与数据源相连,读入或读出。
直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
1.如果是文本文件使用字符流,如果是非文本文件则应该使用字节流
2.如果仅对于一个文件进行复制,并不读取到内存中进行显示,则可以使用字节流进行复制
*/
public class FileInputAndOutput {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("图片.jpg");
File destFile = new File("图片2.jpg");
//2.创建输入输出流对象
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3.数据的读入和写出
byte[] buffer = new byte[5];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileReaderAndWriter {
public static void main(String[] args){
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File readerFile = new File("text1.txt");
File WriteFile = new File("text2.txt");
//2.创建输入输出流对象
fr = new FileReader(readerFile);
fw = new FileWriter(WriteFile);
//3.数据的读入和写出
char[] chars = new char[5];
int len;
while((len=fr.read(chars)) != -1){
fw.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
if(fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fr != null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
缓冲流:BufferedInputStrean 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。
转换流:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。
数据流: DataInputStream 、DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。
对象流:ObjectInputStream、ObjectOutputStream对象流可以将一个对象写出,或者读取一个对象到程序中,也就是执行了序列化和反序列化的操作。
import java.io.*;
public class BufferedText {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.创建File类的对象,指明输入输出的文件
File srcFile = new File("图片.jpg");
File destFile = new File("图片3.jpg");
//2.创建输入输出流对象
//1.先创建节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//2.在创建处理流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.读入和写出文件
byte[] buffer = new byte[10];
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流的关闭
//说明:关闭外层流的同时,内层流会自动的进行关闭。内层流的关闭我们可以省略
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import java.io.*;
public class BufferedText3 {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建文件和相应的流
br = new BufferedReader(new FileReader(new File("text1.txt")));
bw = new BufferedWriter(new FileWriter(new File("text3.txt")));
//读写操作
//方式一:
// char[] chars = new char[5];
// int len;
// while((len=br.read(chars)) != -1){
// bw.write(chars,0,len);
// }
//方式二:
String data;
while ((data = br.readLine()) != null){
//方法一:
// bw.write(data+"\n"); //data中不包含换行符
//方法二:
bw.write(data); //data中不包含换行符
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
InputStreamReader 、OutputStreamWriter 要InputStream或OutputStream作为参数,实现从字节流到字符流的转换。
1. 转换流属于字符流
InputStreamReader :将一个字节的输入流转换为字符的输出流
OutputStreamWriter :将一个字符的输出流转换为字节的输出流
2. 作用
提供字节流与字符流之间的转换
3.解码与编码
解码:字节,字节数组 —》字符数组,字符串
编码:字符数组,字符串—》字节,字节数组
import java.io.*;
public class TransitionTest {
public static void main(String[] args) {
InputStreamReader isr = null;
OutputStreamWriter osr = null;
try {
//1.//创建文件和相应的流
File file1 = new File("背影.txt");
File file2 = new File("背影2.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
isr = new InputStreamReader(fis,"utf-8");
osr = new OutputStreamWriter(fos,"gbk");
//2.文件的读入和写出
char[] buffer = new char[20];
int len;
while ((len = isr.read(buffer)) != -1){
osr.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
try {
if (osr != null)
osr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (isr != null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
序列化的概念:Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。
为什么需要序列化与反序列化
我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等,而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。
如何实现Java序列化与反序列化
JDK类库中序列化API
java.io.ObjectOutputStream:表示对象输出流
它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream:表示对象输入流
它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。
实现序列化的要求
只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。
实现Java对象序列化与反序列化的方法
假定一个Student类,它的对象需要序列化,可以有如下三种方法:
方法一:若Student类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化
ObjectOutputStream采用默认的序列化方式,对Student对象的非transient的实例变量进行序列化。
ObjcetInputStream采用默认的反序列化方式,对对Student对象的非transient的实例变量进行反序列化。
方法二:若Student类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。
ObjectOutputStream调用Student对象的writeObject(ObjectOutputStream out)的方法进行序列化。
ObjectInputStream会调用Student对象的readObject(ObjectInputStream in)的方法进行反序列化。
方法三:若Student类实现了Externalnalizable接口,且Student类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。
ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。
ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。
关于 serialVersionUID 的描述
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 “serialVersionUID” 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java™ 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 – serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。
import java.io.*;
public class ObjectTest {
//序列化
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new String("Java学习")); //String类实现了Serializable接口,定义了 serialVersionUID
oos.flush(); //刷新操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectTest2 {
//反序列化
public static void main(String[] args) {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object o = ois.readObject();
String str = (String)o;
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
常见的编码表
ASCII:美国标准信息交换码。.
用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。
GB2312:中国的中文编码表。最多两个字节编码所有字符
GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
Unicode: 国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
UTF-8: 变长的编码方式,可用1-4个字节来表示一个字符。