IO流
在设备与设备之间的一种数据传输。
分类
按流的方向分:输入流:读文件 从硬盘上读取出来文件,输入到文件中。
输出流:写文件 将文件的内容读出来,写到盘中新的文件中。
按处理数据类型分为:字节流:字节输入流InputStream 字节输出流:OutputStream
字符流: 字符输入流Reader 字符输出流:Writer
以上4个类都是抽象类,不能直接创建对象,我们只能用他们的子类创建对象。
一些常用的子实现类:FileInputStream,FileOutputStream,FileReader,FileWriter
开发步骤:
①创建字节输出流对象 ②写数据 ③关闭资源
字节输出流
例子:
import java.io.FileOutputStream;
public class OutputStream {
public static void main(String[] args) throws Exception {
//创建字节输出流对象
/**
* 1)构造了输出流对象 (系统创建对象)
* 2)指向了当前项目下输出fos.txt
*/
FileOutputStream out = new FileOutputStream("fos.txt");
//写字节
out.write("hello".getBytes());
//关闭资源
/**
* 1)将文件和流对象不建立关系了 (fos对象不指向fos.txt)
* 2)及时释放掉流对象所占用的内存空间
*/
out.close();
}
}
关于字节输出流写数据的方法
public void write(int b):一次写一个字节
public void write(byte[] b) :一次写一个字节数组
public void write(byte[] b, int off,int len):一次写一部分字节数组
我们在写字节流的时候可以一次写一个字节、可以一次写一个字节数组、可以一次写一部分字节数组。
例子:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class Test2 {
public static void main(String[] args) throws Exception {
FileOutputStream out = new FileOutputStream("fos2.txt");
for(int x = 0 ;x < 10 ;x++) {
// out.write(("hello"+x+"\n").getBytes());//这样写的兼容性太差了,不同操作系统换行不一定都是\n
//一般高级记事本都换行都有效果写入换行符/r/n
out.write(("hello"+x).getBytes());
out.write(("\r\n").getBytes());
}
out.close();
}
}
我们还可以使用这种构造方法指定末尾追加:当append为true时
FileOutputStream(File file, boolean append)
创建一个向指定 File
对象表示的文件中写入数据的文件输出流。这种构造方式当append定义为true时自动添加到末尾。(但是当我把append改成false后怎么还是自动添加到末尾?!)
字节输入流
开发步骤:
1)创建字节文件输入流对象
2)读数据
3)释放资源
public abstract int read():一次读取一个字节
public int read(byte[] b):一次读取一个字节数组 (读取实际的字节数)
代码(一次读取一个字节):
//使用输入流将文件中的adcsupport读出并在控制台打印
FileInputStream fis = new FileInputStream("down.txt") ;
int by = 0 ;//必须要给by初始化为0,否则输出会缺少
while((by=fis.read()) != -1) {
System.out.print((char)by);
}
fis.close();
为什么不能直接用fis.read()作为循环的和判断的值而是要先给by初始化为0,然后把fis.read()的值赋给by,遍历输出by的值???
构造方法摘要
构造方法摘要 | |
---|---|
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的 File 对象 file 指定。 | |
FileInputStream(FileDescriptor fdObj) 通过使用文件描述符 fdObj 创建一个 FileInputStream ,该文件描述符表示到文件系统中某个实际文件的现有连接。 | |
FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的路径名 name 指定。 |
方法摘要
int | read() 从此输入流中读取一个数据字节。 |
int | read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 |
int | read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 |
void | close() 关闭此文件输入流并释放与此流有关的所有系统资源。 |
代码(一次读取一个字节数组) :
package IO.file.inputstream;
import java.io.FileInputStream;
public class InputStreamDemo2 {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("down.txt") ;
//一次读取一个字节数组
//指定字节数组的长度是:1024或者1024的倍数
byte[] by =new byte[1024] ;
int length = 0 ;
while((length = fis.read(by)) != -1) {
System.out.println(new String(by , 0 ,length));
}
fis.close();
}
}
复制内容
即是从一个文件中读取内容写到另一个文件中。
思路:①封装目的文件和源文件 ②进行边读边写 ③释放资源
代码:
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception {
//创建src和dest的对象
FileOutputStream fileOutputStream = new FileOutputStream("dest.txt");
FileInputStream fileInputStream = new FileInputStream("src.txt");
//遍历fis文件内容并写入fos中
int by = 0 ;
while((by=fileInputStream.read()) != -1) {
fileOutputStream.write(by);
}
if(fileOutputStream != null) {
fileOutputStream.write(97);
}
//关闭资源
fileOutputStream.close();
fileInputStream.close();
}
}
如果源文件和目的文件不在同一目录中,那么创建流对象时需要加绝对路径。当然,这种复制不仅仅针对的是文本文件,图片也可以,甚至我猜测视频也可以。下面是复制图面的例子:
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test1 {
public static void main(String[] args) throws Exception {
//创建复制的源文件对象保护伞.jpg和目的文件对象umbrella.jpg
FileInputStream fileInputStream = new FileInputStream("保护伞.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("umbrella.jpg");
byte[] byt = new byte[1024] ;
int len = 0 ;
while((len=fileInputStream.read(byt)) != -1 ) {//注意这里的read方法是有参的,作用是读取byt长度的字节
fileOutputStream.write(byt , 0 , len);
}
fileOutputStream.close();
fileInputStream.close();
}
}
字节流的效率
不同的字节读写方法有不同的效率,基本规律是:
①一次读取一个字节的效率小于一次读取一个字节数组。
②基本字节流的效率小于缓冲流
所以分为:基本的字节流一次读取一个字节、基本的字节流一次读取一个字节数组、高效的字节流一次读取一个字节 、高效的字节流一次读取一个字节数组。
下面以一个代码的例子说明:
package day17TestCharStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 四种字节流效率的比较
* @author malaganguo
*
*/
public class TestCharStream {
public static void main(String[] args) throws Exception {
baseCharPerByte();
baseCharPerByteArray();
bufferedCharStreamPerByte();
bufferedCharStreamPerByteArray();
}
private static void bufferedCharStreamPerByteArray() throws FileNotFoundException, IOException {
long start = System.currentTimeMillis();
//创建流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe")) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy3.exe")) ;
//读一个缓冲流数组,写一个缓冲流数组
int len = 0 ;
byte[] bufferbys = new byte[1024] ;
while((len = bis.read(bufferbys)) != -1 ) {
bos.write(bufferbys, 0, len);
}
bos.close();
bis.close();
long end = System.currentTimeMillis();
System.out.println("运行"+(end-start)+"ms");
}
private static void bufferedCharStreamPerByte() throws FileNotFoundException, IOException {
long start = System.currentTimeMillis();
//创建流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy3.txt")) ;
//读一个缓冲区字节写一个缓冲区字节
int len = 0 ;
while((len = bis.read()) != -1) {
bos.write(len);
}
//关闭流
bis.close();
bos.close();
long end = System.currentTimeMillis();
System.out.println("运行"+(end-start)+"ms");
}
private static void baseCharPerByteArray() throws FileNotFoundException, IOException {
long start;
long end;
start = System.currentTimeMillis();
//创建复制对象
FileOutputStream fos = new FileOutputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe");
FileInputStream fis = new FileInputStream("copy2.txt") ;
//读一个数组,写一个数组
int len = 0 ;
byte[] bys = new byte[1024] ;
while((len = fis.read(bys)) != -1 ) {
fos.write(bys, 0, len);
}
//关闭流
fos.close();
fis.close();
end = System.currentTimeMillis();
System.out.println("运行"+(end-start)+"ms");
}
private static void baseCharPerByte() throws FileNotFoundException, IOException {
long start = System.currentTimeMillis();
//创建流对象
FileOutputStream fos = new FileOutputStream("copy1.exe");
FileInputStream fis = new FileInputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe");
//读一个字节写一个字节
int len = 0 ;
while((len = fis.read()) != -1) {
fos.write(len);
}
//关闭资源
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println("运行"+(end-start)+"ms");
}
}
这里利用了四种方法复制一个较大的exe文件,我们得出运行时间结果:
可以验证我们前面提出的运行速率规律。
虽然说字节缓冲流对文件的读写速率得到了大大提高,可是,数据库的速率更快!
使用字节流一次读取一个字节的方式,会造成中文乱码--->Java提供了一个字符流(专门用来解决中文乱码问题)
如何解决中文写出乱码的问题?
使用相同的编码解码方式。 有些方法没有明确编码方式,则默认同平台(eclipse中为GBK)。有些方法提供了使用指定编码格式解码的有参构造,如:
编码: 将字符串变成一个字节数组
public byte[] getBytes() :平台默认编码集(默认的是Gbk)
public byte[] getBytes(Charset charset) ;"指定编码格式
解码:将字节数组--->字符串
public String(byte[] bytes) :使用平台默认编码集(gbk)
public String(byte[] bytes,Charset charset):用指定的编码格式来解码
Charset charset这个形式参数即时我们自定义的编解码方式。书写如"utf-8"。utf-8是一种针对Unicode的可变长度字符编码,又称万国码。一个中文字符是3个字节。
Read类及其子类
直接已知子类1:InputStreamReader
构造方法:
InputStreamReader(InputStream in) :构造一个字符转换输入流,默认编码
public InputStreamReader(InputStream in,Charset cs) 构造一个字符转换输入流,指定编码
是字节流通向字符流的桥梁。它使用指定的
读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。charset
FileReader(便捷类)是InputStreamReader类的子类,是一个读取字符文件的便捷类。FileReader
用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream
。
例子:
package streamReader_Writer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Test {
public static void main(String[] args) throws Exception {
//创建字符流对象
InputStreamReader isr = new InputStreamReader(
new FileInputStream("CopyDemo.java"));
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("copyedDemo.java")) ;
//读一个字符数组,写一个字符数组
int len = 0 ;
char[] chs = new char[1024] ;
while((len = isr.read(chs)) != -1) {
osw.write(chs, 0, len);
}
//关闭流
osw.close();
isr.close();
}
}
便捷类的例子:
package streamReader_Writer;
import java.io.FileReader;
import java.io.FileWriter;
public class Convenient {
public static void main(String[] args) throws Exception {
//创建字符的便捷类
FileWriter fw = new FileWriter("copyedDemo2.java") ;
FileReader fr = new FileReader("copyDemo.java") ;
//读一个字符数组,写一个字符数组
int len = 0 ;
char[] chs = new char[1024] ;
while((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
//关闭流
fw.close();
fr.close();
}
}
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
Writer类(完全可以类比Reader类)
写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
直接已知子类1:OnputStreamReader
public OutputStreamWriter(OutputStream out, Charset cs):使用指定编码格式构造一个字符转换输出流对象
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
字符输出流写数据的功能:
public void write(int c):写单个字符public void write(char[] cbuf):写字符数组
public abstract void write(char[] cbuf, int off, int len):写字符数组的一部分
public void write(String str):写字符串
public void write(String str,int off, int len):写字符串的某一部分
字符缓冲IO流
BufferedReader:字符缓冲输入流
构造方法public BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流。
public BufferedReader(Reader in, int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流。
BufferedWriter:文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入
构造方法
public BufferedWriter(Writer out) :默认缓冲区大小构造字符缓冲输出流对象
public BufferedWriter(Writer out,int size):指定缓冲区大小
字符缓冲输出流:
特有功能:public void newLine():写入一个行的分隔符号
字符缓冲输入流:
特有功能:public String readLine():一次读取一行
举个栗子:
package bufferedwr;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class Test {
public static void main(String[] args) throws Exception {
//创建字符缓冲输出输入流对象
BufferedReader br = new BufferedReader(new FileReader("CopyDemo.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bufferedwr.java"));
//读一个字符数组,写一个字符数组
String line = null ;
String[] bys = new String[1024] ;
while((line=br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭流
bw.close();
br.close();
}
}
注意:
在字符流缓冲输出中,结束输出后在我们关闭资源前,我们需要对缓冲区进行刷新,及调用方法:
flush();
为什么要字符流缓冲区输出结束一定要调用flush方法呢?
因为输出流在进行输出时,比如像某个文件中写入内容,其实是先将输出流写入到缓冲区,当缓冲区写满后才将缓冲区的内容输出到文件中。但是当主机完成输出流的输出后,有可能缓冲区这个时候还没有被填满,这样的话,就会一直等待主机发送内容,这时候,就可以使用flush将缓冲区的内容强制输出到文件中,清空缓冲区。
所以,一般在关闭输出流之前,要先调用flush方法强制缓冲区中的内容输出,并清空缓冲区。
Demo
例子1——将集合中的元素存储的文件中:
package ArrayList_Buffered;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
/**
* 需求:把ArrayList集合中的字符串数据存储到文本文件
* @author malaganguo
*
*/
public class Demo {
public static void main(String[] args) throws Exception {
//创建集合对象
ArrayList<String> al = new ArrayList<String>() ;
//往集合中添加元素
al.add("aloha") ;
al.add("nihao") ;
al.add("hello") ;
al.add("你好") ;
//创建字符输出缓冲区对象
BufferedWriter bf = new BufferedWriter(new FileWriter("demo.txt")) ;
for(String s : al) {
bf.write(s);
bf.newLine();
bf.flush();
}
//关闭资源
bf.close();
}
}
例子2——将文件中的内容读到集合中
package ArrayList_Buffered;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
/**
* 有一个文本文本,需要将文本文件中的内容放到ArrayList集合中,遍历集合获取元素
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//那我们就把我们创建的文本文件demo.txt中的内容放进ArrayList中
//创建字符缓冲输入对象
BufferedReader br = new BufferedReader(new FileReader("demo.txt")) ;
//创建容器:集合对象
ArrayList<String> al = new ArrayList<String>() ;
String line = null ;
byte[] bys = new byte[1024] ;
while((line=br.readLine()) != null ) {
al.add(line);
}
//关闭资源
br.close();
System.out.println(al);
}
}
其他流
一个不需要关闭的临时流——内存操作流。
内存操作流:一个程序结束后,那么这个程序的变量就会从内存中消失。所以不需要关闭流对象。
输入流:byteArrayInputStream
有参构造:byteArrayInputStream (byte[] bys)
输出流:byteArrayOutputStream
有参构造:byteArrayOutputStream(byte[] bys)
针对java基本类型的数据进行读写操作——数据流
输入流:dataInputStream
输出流:dataOutputStream
打印流
字符打印流:printWriter
字节打印流:printStream
特点:
1、打印流只有输出流,没有输入流。只能写数据(只能针对目的地文件进行操作),不能读数据(不能针对源文件进行操作)
2、可以针对文件直接进行操作
3、自动刷新功能::PrintWriter(OutputStream out/Writer out,boolean autoflush);第二个参数如果是true 表示启动自动刷新功能
4、打印的方法:print(XXX x)/println(XXX xx)
合并流:
构造方法:
public sequenceInputStream(file1,file2) ;
public sequenceInputStream(Enumeration e) ;
例子1:
package test.dat17;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.SequenceInputStream;
public class Test1 {
public static void main(String[] args) throws Exception {
//创建两个待合并的流对象
FileInputStream fis = new FileInputStream("a.txt");
FileInputStream fos = new FileInputStream("b.txt");
//创建合并流对象
SequenceInputStream sis = new SequenceInputStream(fis,fos);
//创建字符缓冲输入流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a&b.txt")) ;
//遍历
int len = 0 ;
byte[] bys = new byte[1024] ;
while((len = sis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
//关闭流
fos.close();
fis.close();
bos.close();
sis.close();
}
}
例子2:
package test.dat17;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;
public class Test2 {
public static void main(String[] args) throws IOException {
//定义一个InputStream类型的Vector集合
Vector<InputStream> v = new Vector<InputStream>() ;
//创建流对象
FileInputStream fis1 = new FileInputStream("a.txt");
FileInputStream fis2 = new FileInputStream("b.txt");
FileInputStream fis3 = new FileInputStream("c.txt");
//往集合中添加元素
v.add(fis1) ;
v.add(fis2) ;
v.add(fis3) ;
//枚举对象
Enumeration<InputStream> e = v.elements() ;
//创建合并流对象
SequenceInputStream sis = new SequenceInputStream(e);
//创建输出缓冲区
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("a&b&c.txt"));
//遍历输出
int len = 0 ;
byte[] bys = new byte[1024] ;
while((len = sis.read(bys)) != -1 ) {
bos.write(bys, 0, len);
bos.flush() ;
}
bos.close();
sis.close();
}
}
标准输入输出流
InputStream in = System.in ;PrintStream out = System.out ;
java装修者模式:创建键盘输入对象。创建键盘输出对象
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)) ;
序列化流和反序列化流
序列化:将对象按照流的方式存储到文本文件中或者再网络中传输 对象---->流数据 序列化流 (ObjectOutputStream)
反序列化:将文本文件中的流对象或者网络传输中的流对象还原成对象 流数据--->对象 反序列化流(ObjectInputStream)
持久属性集
Properties:表示了一个持久的属性集(简称:属性集合类) extends Hashtable<K,V> Map集合的。
可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。