
介绍:
I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流,流的本质是数据传输java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
IO流的使用场景
如果 *** 作的是纯文本文件,优先使用字符流如果 *** 作的是图片、视频、音频等二进制文件,优先使用字节流如果不确定文件类型,优先使用字节流.字节流是万能的流
流的分类
按 *** 作数据单位不同分为:字节流(8 bit),字符流(16 bit)按数据流的流向不同分为:输入流,输出流按流的角色的不同分为:节点流,处理流
1. Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
字节流抽象基类
InputStream:这个抽象类是表示字节输入流的所有类的超类OutputStream:这个抽象类是表示字节输出流的所有类的超类子类名特点:子类名称都是以其父类名作为子类名的后缀
字节流输入常用方法
字节流输出常用方法
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
// 读取某文件内容读入程序中,并输出到控制台
// 说明点:
//1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
//2. 异常的处理:为了保证流资源一定可以执行关闭 *** 作。需要使用try-catch-finally处理
// 3. 读入的文件一定要存在,否则就会报FileNotFoundException
@Test
public void test04() throws Exception{
FileInputStream fis = null;
try {
fis = new FileInputStream("E:\a.txt");
int len;
while ((len = fis.read()) != -1) {
System.out.print((char) len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
fis.close();
}
}
//read有参构造
@Test
public void test05(){
FileInputStream fis = null;
try {
fis = new FileInputStream("E:\a.txt");
byte[] bytes = new byte[10];//和字符的区别只需把byte换成char,数组长度一般使用1024
int len;
while ((len=fis.read(bytes))!=-1) {
//方式1
// for (int i = 0; i < len; i++) {
// System.out.print(chars[i]);
// }
//方式2
// String str = new String(chars);//和for循环chars[]的错误是一样的
String str = new String(bytes,0,len);
System.out.print(str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.2字节输出流
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
@Test
public void test01() throws IOException {
//创建字节输出流对象
//FileOutputStream(String name):创建文件输出流以指定的名称写入文件
FileOutputStream fos = new FileOutputStream("E:\a.txt");
//void write(int b):将指定的字节写入此文件输出流
fos.write(97);
// fos.write(57);
// fos.write(55);
//最后都要释放资源
//void close():关闭此文件输出流并释放与此流相关联的任何系统资源。
fos.close();
}
字节输出流换行和追加
追加
默认输出流的write方法是覆盖以前的数据
public FileOutputStream(String name,boolean append)创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
换行 *** 作
windows:rn
linux:n
mac:r
@Test
public void test02() throws IOException {//通过异常 finally块关闭流资源
//创建字节输出流对象
FileOutputStream fos = null;
try {
new FileOutputStream("E:\a.txt",true);
//写数据
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
fos.write("rn".getBytes());
}
}catch (Exception e){
e.fillInStackTrace();
}finally {
if (fos!=null){
//释放资源
fos.close();
}
}
}
复制一个文件到指定目录
@Test
public void test4() {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("E:\a.txt");
fw = new FileWriter("E:\new.txt");
char[] chars = new char[10];
int len;
while ((len=fr.read(chars))!=-1){//读入内存中
//写到文件中
// String str = new String(chars,0,len);
// System.out.print(str);
// fw.write(str);
fw.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fr.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3字符流
由于字节流 *** 作中文不是特别的方便,所以Java就提供字符流
字符流的基类是Reader和Writer
字符流 = 字节流 + 编码表
常用方法
刷新和关闭的方法
其他方法参考字节流
3.1编码表是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
常见的字符集
ASCII字符集:
lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
字符串中的编码解码
相关方法
@Test
public void test01() throws UnsupportedEncodingException {
String s = "中国";
//byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67]
//byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
System.out.println(Arrays.toString(bys));
//String ss = new String(bys);
//String ss = new String(bys,"UTF-8");
String ss = new String(bys,"GBK");
System.out.println(ss);
}
4缓冲流
开发中字节流和字符流使用的较少,一般使用缓冲流,提高读取效率
在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
复制一个文件到指定目录
//实现文件复制的方法
public void copyFileWithBuffered(String srcPath,String destPath){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2.造流
//2.1 造节点流
FileInputStream fis = new FileInputStream((srcFile));
FileOutputStream fos = new FileOutputStream(destFile);
//2.2 造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节:读取、写入
byte[] buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,再关闭内层的流
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
// fos.close();
// fis.close();
}
}
@Test
public void testCopyFileWithBuffered(){
long start = System.currentTimeMillis();
String srcPath = "C:\Users\Administrator\Desktop-视频.avi";
String destPath = "C:\Users\Administrator\Desktop-视频.avi";
copyFileWithBuffered(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制 *** 作花费的时间为:" + (end - start));//618 - 176
}
在使用缓存流之后明显节省了很多的时间
5其他流 5.1转换流InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
@Test
//转换流
public void test02() throws Exception{
FileReader fr = new FileReader("E:\a.txt");
int len;
char[] buff = new char[10];
while ((len=fr.read(buff))!=-1){
System.out.print(new String(buff,0,len));//出现乱码问题
}
fr.close();//生产应该用finally关闭流
}
@Test
//InputStreamReader
public void test08() throws Exception{
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\a.txt"),"utf-8");
int len;
char[] buff = new char[10];
while ((len=isr.read(buff))!=-1){
System.out.print(new String(buff,0,len));//出现乱码问题
}
isr.close();//生产应该用finally关闭流
}
//OutputStreamWriter没有演示参考其它输出流
5.2对象流
ObjectInputStream和OjbectOutputSteam
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
对象的序列化(****)对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是JavaEE 平台的基础
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSerializableException异常
SerializableExternalizable
public class Student implements Serializable {
private String name;
// private int age;//修改该类信息后会报InvalidClassException
public int age;
//get set方法
}
@Test
//序列化
public void test1() throws Exception{//没有try catch异常
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\a.txt"));
Student student = new Student("张飞",13);
oos.writeObject(student);
oos.close();
}
@Test
//反序列化
public void test2() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\a.txt"));
Object o = ois.readObject();//修改该类信息后会报InvalidClassException
System.out.println(o);
}
用对象序列化流序列化了一个对象后,修改了对象所属的类文件,读取数据会出问题,抛出InvalidClassException异常
解决方法
重新序列化
给对象所属的类加一个serialVersionUID(private static final long serialVersionUID = 42L;)
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
如果一个对象中的某个成员变量的值不想被序列化,使用transient关键字修饰
public class Student implements Serializable {
private static final long serialVersionUID = -7619734160029632002L;
private String name;
private int age;//修改该类信息后会报InvalidClassException
// public int age;
private transient String passWord;//密码不想被序列化
}
@Test
//序列化
public void test1() throws Exception{//没有try catch异常
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\a.txt"));
Student student = new Student("张飞",13,"sodifd");
oos.writeObject(student);
oos.close();
}
@Test
//反序列化
public void test2() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\a.txt"));
Object o = ois.readObject();//
System.out.println(o);
}
测试结果:passWord的值是为空的
谈谈你对 java.io.Serializable 接口的理解,我们知道它用于序列化, 是空方法接口,还有其它认识吗? 实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿 *** 作系统间的差异。换句话说,可以先在Windows机器上创建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。 由于大部分作为参数的类如String、Integer等都实现了java.io.Serializable的接口,也可以利用多态的性质,作为参数使接口更灵活。欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)