IO流

IO流

为了能够将内存里面的东西变为永久性的,我们需要引入流的概念,将内存的东西"流入"到文件里去,从而达到永久性储存的目的

IO流的概述

在程序中所有的数据都是以流的方式进行传输或保存的,程序通过输入流读取数据;当程序需要将一些数据长期保存起来的时候使用输出流完成。

例如:本地文件拷贝,上传文件和下载文件等等。

这里介绍一下输入流和输出流的概念:

输入流:将文件读入内存当中(文件----->内存)

输出流:将内存写入到文件当中(内存------->文件)

注意:

  1. 但凡是对数据的操作,Java都是通过流的方式来操作的。
  2. 程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。
  3. IO流可以做到数据的持久化,但是IO流本质上是用来处理本地文件系统以及不同设备之间的数据传输。
  4. FileNotFountException抛出异常的情况: a.父路径不存在 b.盘符不存在
  5. 输出流如果文件不存在会自动创建
  6. 写数据的时候会抛出 IOException
  7. Stream Closed 表示 流对象已经关闭,不能够再次使用

IO流分类

按照数据流向

​ 输入流:从外界(键盘、网络、文件…)读取数据到内存
​ 输出流:用于将程序中的数据写出到外界(显示器、文件…)
​ 数据源 目的地 交通工具

按照数据类型

​ 字节流:主要用来处理字节或二进制对象。
​ 字节输入流(InputStream)
​ 字节输出流 (OutputStream)
​ 字符流:主要用来处理字符、字符数组或字符串。
​ 字符输入流(Reader)
​ 字符输出流(Writer)

字节流和字符流的区别

读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

选取:只要是处理纯文本数据,就优先考虑使用字符流,除此之外都使用字节流。

字节流

下面对于字节流的相关类进行介绍:

FileOutputStream:字节输出流

FileInputStream:字节输入流

BufferedOutputStream:高效字节缓冲输出流

BufferedInputStream:高效字节缓冲输入流

FileOutputStream

构造方法
FileOutputStream(String name) 创建一个向name文件中写数据的输出文件流
FileOutputStream(File file) 创建一个向file文件中写数据的输出文件流
FileOutputStream(File file, boolean append) 创建一个向file文件中追加数据的输出文件流
成员方法
public void write(int b) 写一个字节
public void write(byte[] b) 写一个字节数组
public void write(byte[] b,int off,int len) 写一个字节数组的一部分
void close() 关闭此文件输出流并释放与此流有关的所有系统资源

关于字节流的一些注意事项:

1、数据写入完成后记得调用close()方法关闭流对象,如果没有关闭流对象并且还在继续使用的话,会抛出异常,显示Stream Closed
2、数据追加写入要使用如下构造方法
​ FileOutputStream(File file, boolean append)
3、不同的系统针对不同的换行符号识别是不一样的

​ windows \r\n
​ Linux \n
​ max \r
常见的一些高级记事本,是可以识别任意换行符号的。

4、数据写入中存在两个异常需要处理FileNotFoundException,IOException。

FileInputStream

构造方法

FileInputStream(File file) 创建一个从file读取数据的输入流
FileInputStream(String name) 创建一个从name文件读取数据的输入流

成员方法
int read() 一次读取一个字节
​ 1.读取一个字节并且返回给调用者,返回的是读取的内容
​ 2.将指针移动到下一个位置
​ 3.读取到文件末尾返回-1

int b = 0;
while((b = is.read()) != -1) {
	System.out.print((char)b);
}

int read(byte[] b) 一次读取一个字节数组
​ 1.读取一个字节数组到定义好的数组中,返回的是实际读取的长度
​ 2.将指针移动到下一个位置
​ 3.读取到文件末尾返回-1

int len = 0;
byte[] bys = new byte[5];
while((len = is.read(bys)) != -1) {
	System.out.print(new String(bys, 0, len));
}

int read(byte[] b, int off, int len) 一次读取一个字节数组的一部分
void close() 关闭此文件输入流并释放与此流有关的所有系统资源

注意:

关于输入流的一些注意事项
​ 1.输出流文件不一定要存在,会自动创建 ,输入流文件一定要存在,否则会抛出异常 抛出FileNotFindException
​ 2.计算机如何识别中文? 中文在GBK编码表中使用两个字节表示,两个字节的第一个字节是负数,
​ 计算机它首先读取一个字节,发现该字节是负数,它会自动等待下一个字节来组合

BufferedOutputStream

为了提升读写效率,Java考虑到流读写效率的提升所以为我们提供了字节缓冲流。

格式:

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
bos.write("HelloWorld".getBytes());
bos.flush(); 
bos.close();

注意:

​ 1.使用带有缓冲区的输出流一定要注意使用flush方法或者close方法刷新缓冲区
​ 2.高效流本质也是使用了字节数组作为缓冲区

BufferedInputStream

格式:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
	byte[] bys = new byte[1024];
	int len = 0;
	while((len = bis.read(bys)) != -1) {
    	代码段;
	}

字符流

字符流产生的原因:

  1. 每次只能够读取一个字节或者一个字节数组,每次在需要转换成字符或者字符串的时候不是很方便
  2. 不同的操作系统针对换行符的处理不方便
  3. 有的时候会出现中文乱码(中文占两个字节,如果针对中文中某个字节做了转换或者显示,就会出现乱码)
  4. 如果需要读取某一行数据,非常不方便

Java就设计了字符流

字符流就是一次性读取一个字符或者一个字符数组(字符串)

乱码问题

为什么会出现乱码?

  • 服务器,客户端,数据库,文件系统之间的编码和解码使用的编码表不一致 (沟通)

  • 人为在使用字节流读取的过程中做了转换 (使用字符流解决)

下面对字符流的相关类进行介绍:

OutputStreamWriter:转换流(写入)

InputStreamReader:转换流(读取)

FileWriter:用来写入字符文件的文件字符流

FileReader:用来读取字符文件的文件字符流

BufferedWriter:用来写入字符文件的高效字符流

BufferedReader:用来读取字符文件的高效字符流

DataInputStream:基本数据类型输入流

DataOutputStream:基本数据类型输出流

OutputStreamWriter & InputStreamReader

字符流

Writer(抽象类,不能直接使用,选择子类)

OutputStreamWriter(转换流,是字节流通向字符流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认字符集编码。)
构造方法
OutputStreamWriter(OutputStream out) 创建使用默认字符编码的字符输出转换流
OutputStreamWriter(OutputStream out, Charset cs) 创建使用cs字符集的字符输出转换流
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建使用enc字符集编码器的
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的

转换流的核心功能在于将字节流转换成字符流,这样就能够使用更多好的方法来处理文本文件

格式:

//Writer w = new OutputStreamWriter(new FileOutputStream("osw.txt"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw2.txt"),"UTF-8");
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw2.txt"), "gbk");
//编码格式不同会造成乱码问题
//下面代码完成文件的拷贝
	char[] chs = char[1024];
	int len = 0;
	while((len = isr.read(chs)) != -1){
      	//w.write("中国你好");
      	osw.write(chs, 0, len);
    	w.flush();
	}
	osw.closed();
	isr.closed();
	
FileWrite & FileReader

构造方法类似FileInputStream和FileOutputStream
成员方法完全继承自父类OutputStreamWriter和InputStreamReader

//以下方法用来完成文件的拷贝功能
	FileReader fr = new FileReader("a.txt");
	FileWriter fw = new FileWriter("b.txt");
	//方式一:一次性读取一个字符
	int ch = 0;
	while ((ch = fr.read())!=-1) {
		fw.write(ch);
		fw.flush();
	}
	//方式二:一次性读取一个字节数组
    int len = 0;
		char[] chs = new char[1024];
		while ((len = fr.read(chs))!=-1) {
			fw.write(chs, 0, len);
			fw.flush();
		}
	fr.close();
	fw.close();	
BufferedWriter & BufferedReader

类似于字节流的BufferedOutputStream和BufferedInputStream,字符流同样存在字符缓冲流
同理,字符缓冲流也是为了提高字符流的效率。
BufferedWriter和BufferedReader继承自Writer和Reader,所以具备原有的读取方法,但是还存在自己特有的方法。
特有的方法:
BufferedWriter:void newLine() //写一行
BufferedReader:String readLine() //读取一行

//实现文件的拷贝
	BufferedReader br = new BufferedReader(new FileReader("a.java"));
	BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
	String line = null;
	while((line = br.readLine())!=null) {
		bw.write(line);
		//bw.newLine();
		bw.flush();
	}
	
	br.close();
	bw.close();

其他IO流

DataInputStream & DataOutputStream

可以读写基本数据类型

数据输入流:DataInputStream
​ DataInputStream(InputStream in)
数据输出流:DataOutputStream
​ DataOutputStream(OutputStream out)

注意:
​ 1.该流不仅可以读写字节和字符,还可读写基本数据类型
​ 2.该流写入文本后的数据是看不懂的
​ 3.读取该流写入文本的数据一定要使用DataInputStream来读,并且要按照写入的顺序来读

示例代码如下:

public class IODemo01 {
public static void main(String[] args) throws Exception {	
	write();	
	read();		
}
public static void read() throws FileNotFoundException, IOException{
	DataInputStream dis = new DataInputStream(new FileInputStream("dos.txt"));
	byte by = dis.readByte();
	short s = dis.readShort();
	int i = dis.readInt();
	long l = dis.readLong();
	float f = dis.readFloat();
	double d = dis.readDouble();
	boolean bool = dis.readBoolean();
	char ch = dis.readChar();
	
	System.out.println(by);
	System.out.println(s);
	System.out.println(i);
	System.out.println(l);
	System.out.println(f);
	System.out.println(d);
	System.out.println(bool);
	System.out.println(ch);
	
	byte[] bys = new byte[5];
	int len = dis.read(bys);
	System.out.println(new String(bys, 0, len));
	
	String utfStr = dis.readUTF();
	System.out.println(utfStr);	
	dis.close();
}
public static void write() throws FileNotFoundException, IOException {
	DataOutputStream dos = new DataOutputStream(new FileOutputStream("dos.txt"));
	dos.writeByte(25);
	dos.writeShort(250);
	dos.writeInt(300);
	dos.writeLong(10000L);
	dos.writeFloat(2.5f);
	dos.writeDouble(2.8);
	dos.writeBoolean(false);
	dos.writeChar(97);
	dos.write("hello".getBytes());
	dos.writeUTF("好的");
	dos.close();
}
}
PrintWriter & PrintStream

概述:
向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。

PrintStream(System.out)

特点:

只有输出流,没有输入流
具备将字节流转换成字符流
只能写数据,不能读取数据。
可以操作任意类型的数据。
如果启动了自动刷新,能够自动刷新。
如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作

格式:

PrintWriter pw = new PrintWriter(new FileWriter("pw.txt"), true);
	pw.write("abc");
	pw.print(2.5);
	pw.println("我换行了");
	pw.print("efg");
InputStream in & PrintStream out

System类中的静态变量:in,out 。

“标准”输入流:static InputStream in (System.in)。
“标准”输出流:static PrintStream out (System.out) 。
它们各代表了系统标准的输入和输出设备。
默认输入设备是键盘,输出设备是显示器。

例如:

InputStream is = System.in;
PrintStream ps = System.out;
Scanner input = new Scanner(is);
System.out.print(" 哈哈 ");
ps.print(" 嘿嘿 ");
RandomAccessFile

概述:此类的实例支持对随机访问文件的读取和写入

long getFilePointer()
void seek(long pos)
void setLength(long newLength)
long length()
int skipBytes(int n)

特点:

RandomAccessFile类不属于流,是Object类的子类。
包含了InputStream和OutputStream的功能支持对随机访问文件的读取和写入
读写基本数据类型
具备字节流的功能
能够支持随机访问文件的读取,在任意位置读取和写入数据
可设置读写的能力,mode模式下:"r"表示只读,"rw"表示可读写

Properties属性集

属性集:键值对的集合

如果文件中有这样的数据:

userName=admin

password=123456

Properties概述:

Properties 类表示了一个持久的属性集;Properties 可保存在流中或从流中加载;属性列表中每个键及其对应值都是一个字符串。
Properties可以当做Map集合类使用
Properties的特殊遍历功能
public Object setProperty(String key,String value)
public String getProperty(String key)
public Set < String > stringPropertyNames()
Properties和IO流结合使用
public void load(Reader reader)
public void store(Writer writer,String comments)

实例代码如下:

// Properties和IO流结合使用
	prop.load(new FileReader("config/xxx.properties"));	
	//此config/xxx.properties文件下是属性的配置文件
	System.out.println(prop);
	prop.store(new FileWriter("yyy.properties"), "This is a comment!!!");
ObjectInputStream & ObjectOutputStream

读写引用数据类型的流 (序列化流和反序列化流)

通俗来说:就比如给你基因,让你来基因序列化重组,当你需要读取文件,如果不进行反序列化读取的话,文件信息会乱码让你看不懂,此时就要利用到反序列化流来读取文件,而且序列化数据后,再次修改类文件,读取数据会出问题,此时也需要重新进行序列化数据

特点:

1.能够具备读写字节的能力

2.能够读写基本数据类型

3.能够读写引用数据类型

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。

如何实现序列化?
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化,该接口没有任何方法,是一个标机接口。
序列化数据后,再次修改类文件,读取数据会出问题,如何处理?
使用transient关键字声明不需要序列化的成员变量。

transient: 在将成员写入到文件的时候确保数据的安全,在反序列化的时候不能够获取到数据

简而言之:就是加上transient之后,你反序列化是获取不到这项数据的,但是可以让整个读取的文件能够正常的读取,不会发生读取信息的错误。

java.io.NotSerializableException

异常名称: 未序列化异常

产生原因: 需要序列化的对象没有实现Serializable接口

解决办法: 实现Serializable接口

java.io.InvalidClassException

异常名称: 无效类异常

产生原因: 流中的序列化id和本地文件中的序列化id不匹配,每当你改变一次需要序列化的数据,id就会发生变化

解决办法: 创建或者随机生成一个新的固定的序列化ID即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值