目录
前言
1.io流的概述
2.字节流
3.字符流
4.字节字符转换流
5.缓冲流
6.打印流
8.对象流
9.数据流
10.字节数组流
11.字符串流
12.管道流
13.zip流
14.实现文件的分割----缓冲流的运用
总结
前言
io流在Java的开发中,都是非常核心的一部分内容,今天来比较全面的来梳理,关于Java io流相关的知识。file的操作在上一节的内容已经提及,这里不再赘述。下面我们来开始学习io流吧!
1.io流的概述
io流:1.输入流——input 2.输出流——output
流:是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或者抽象。数据在两台设备间的传输称为流。流的本质是数据传输,根据数据传输特性将流抽象为各种类,可以方便直观的对数据进行操作。
io流的分类:
根据处理数据类型的不同分为: 字符流和字节流
根据流的方向:输入流和输出流
2.字节流
字节输出流:OutputStream
public abstract class OutputStream extends Object implements Closeable,Flushable
这个抽象类是所有输出字节流的超类。输出流接受输出字节并且将这些字节发送到inputStream类某个接收器要向文件输出,使用FileOutputStream类
字节输入流:InputStream
public abstract class IntputStream extends Object implements Closeable
这个抽象类是所有输入字节流的超类,FileInputStream类
补充:read()方法的使用
1、 read()方法,这个方法从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
2、read(byte[] b,int off,int len)方法,将输入流中最多 len 个数据字节读入 byte 数组。尝试读取len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取 的字节数。
3、read(byte[] b)方法,从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。
字节流代码示例 实现数据的写入和读取
public static void out() throws IOException {
//确定目标文件
File file = new File("D:\\io\\lzy.txt");
//构建一个文件输出流的对象
OutputStream out = new FileOutputStream(file);
//输出内容
String info = "我好开心啊啊啊啊";
//把输出内容转换成字节写入到文件中
out.write(info.getBytes(StandardCharsets.UTF_8));
out.close();//关闭流
System.out.println("成功输入");
}
public static void In() throws IOException {
//确定目标文件
File file = new File("D:\\io\\lzy.txt");
//创建输入流对象
InputStream in = new FileInputStream(file);
//创建一个有1024个字节大小的字节数组
byte[] bytes = new byte[1024];
StringBuffer buf = new StringBuffer();
int len = -1;
//in.read(bytes) 向目标文件读取1024byte大小
//in.read(bytes)读取一定数量的字节 并且保存在byte[]数组中,返回实际读取字节数,如果没有返回-1;
while ((len = in.read(bytes))!=-1){
//new String(bytes) 字节变成字符 然后添加到buf
buf.append(new String(bytes));
}
System.out.println(buf); //输出
in.close();
}
3.字符流
字符流:
字符输出流: Writer,对文件的操作使用子类,FileWriter
字符输入流: Reader,对文件的操作使用子类,FileReader
每一次操作的单位都是字符
字符流和字节流的区别:
首先,在所有的操作中,字节永远是最基础的,任何基于字节的操作都是正确的。如果确认流里只有可打印的字符,英文,中文等等,可以考虑字符流。看上去方便一点。如果不确定,使用字节流总是不会出错的。
字符流代码示例: tip:可以和上面的字节对比一下
/**
* 字符io StringBuffer
* */
public static void out() throws IOException {
File file = new File("D:\\io\\lzy.txt");// 目标文件
Writer out = new FileWriter(file,true);//字符输出流对象
out.write(",来了来了来了");//输出内容
out.close();
}
public static void in() throws IOException {
File file = new File("D:\\io\\lzy.txt");
Reader in = new FileReader(file);
char[] cs = new char[1];
int len = -1;
StringBuffer buf = new StringBuffer();
while ((len=in.read(cs))!=-1){ //每次读取一个字符 然后添加到buf
buf.append(new String(cs,0,len));
}
in.close();
System.out.println(buf);
}
4.字节字符转换流
转换流:可以将字节流转换为字符流,也可以将一个字符流转换为字节流
OutputStreamWriter: 字符----->字节
InputStreamWriter:字节----->字符
示意图:
转换流代码示例:
public static void write(OutputStream out) throws IOException {
Writer writer = new OutputStreamWriter(out); //创建一个字符输出流对象 并且字符转换为字节 传参out
writer.write("我最帅\r\n"); //写入数据
writer.close();
}
private static void read(InputStream in) throws IOException {
Reader reader = new InputStreamReader(in, Charset.defaultCharset()); //创建一个字符输入流对象 并且字节转换为字符
char[] cs = new char[1024];
int len = -1;
while ((len=reader.read(cs))!=-1){
System.out.println(new String(cs)); //直接以字符串形式输出cs数组
}
}
5.缓冲流
首先,对文件或者是对目标频繁的进行读写操作,效率低,性能差。
所以引入缓冲流,来使更加高效的读写信息,原理就是将数据先缓存起来,然后一起写入或者读出。
BufferedInputStream:为另一个输入流添加一些功能,在创建BufferedInputStream时,会创建一个内部缓冲区数组,用于缓冲数据。
BufferedOutputStream:设置这种输出流,应用程序可以将字节写入底层输出流中,而不必针对每一次字节写入调用底层系统。
BuffereReader:字符输入流中读取文本,缓冲各个字符,从而实现字符的高效读取。
BuffereWriter:将文本写入字符输出流,缓冲各个字符,从而实现字符的高效写入。
字节缓冲流代码:
/**
* 字节缓存流
* */
private static void byteWriter() throws IOException {
File file = new File("D:\\io\\lzy.txt");
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);//输出流缓存 一次缓存8kb
String info = "小桥流水人家";
bos.write(info.getBytes()); //将字符串转化为字节
bos.close(); //先关闭out再刷新
}
private static void byteRead() throws IOException {
File file = new File("D:\\io\\lzy.txt");
InputStream in = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(in);
byte[] bytes = new byte[1024];
int len = -1;
while ((len=bis.read(bytes))!=-1){
System.out.println(new String(bytes));
}
}
字符缓冲流代码
/**
* 字符流缓存
* */
private static void charReader() throws IOException {
File file = new File("D:\\io\\lzy.txt");
Reader reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
char[] chars = new char[1024];
br.read(chars);
System.out.println(chars);
br.close();
}
private static void charWriter() throws IOException {
File file = new File("D:\\io\\lzy.txt");
Writer writer = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(writer);
bw.write("我爱乐昌");
bw.close();
}
6.打印流
主要功能是用于输出,在整个IO包中打印流分为两种类型
字节打印流:PrintStream 字符打印流:PrintWriter
打印流可以很方便的进行输出
private static void bytePrint() throws FileNotFoundException {
File file = new File("D:\\io\\lzy.txt");
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
//bos.write();
PrintStream ps = new PrintStream(bos); //打印流实例
ps.println("花姑娘,大大滴好");
ps.close();
}
private static void charPrin() throws IOException {
File file = new File("D:\\io\\lzy.txt");
Writer out = new FileWriter(file);
BufferedWriter bos = new BufferedWriter(out);
PrintWriter pw = new PrintWriter(bos);
pw.println("小桥六十水水水水");
pw.close();
}
8.对象流
先要明白一些概念:
对象的序列化和反序列化
对象的序列化:把对象写入到输出流中,用来存储或者传输 保存的是对象的状态 ,即它的成员变量
反序列化: 从输入流中读取对象
对象流的两个类:
ObjectOotputStream :将Java对象基本数据类型和图形写入OutputStream
ObjectInputStream :对以前使用ObjectOotputStream 写入的基本数据和对象进行反序列化
transient关键字:使用这个关键字说明的变量,不再进行序列化存储
要序列化的对象一定要实现Serializable接口,这个接口没有任何定义,只是告诉JVM该类对象可以被序列化。
什么时候需要对象的序列化呢?
1.把对象存储到文件 2.把对象需要在网络上传输
代码示例:
首先:准备要序列化的对象
/**对象序列化
*
* */
public static void writeObect() throws IOException {
Dog dog = new Dog("旺旺",12);
File file = new File("D:\\io\\lzy1.txt");
OutputStream out = new FileOutputStream(file); //字节流对象实例
ObjectOutputStream oos = new ObjectOutputStream(out);//对象流实例
oos.writeObject(dog);
oos.close();
}
/**对象反序列化*/
public static void readObject() throws IOException, ClassNotFoundException {
File file = new File("D:\\io\\lzy1.txt");
InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Dog dog = (Dog) ois.readObject();
ois.close();
System.out.println(dog.toString());
}
9.数据流
DataInputStream : 数据输入流 , 允许程序与机器无关的方式从底层输入流中读取Java的基本数据类型,程序可以使用数据输入流写入,然后用数据输出流对数据进行读取。 线程不安全
DataOutputStream 数据输入流 允许程序将Java基本数据类型写入输出流、
专门用来读写各种数据 int char long 等等 二者的读写顺序要一样
/**数据流*/
private static void write() throws IOException {
File file = new File("D:\\io\\lzy1.txt");
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(10);
dos.writeByte(1);//写入1个字节
dos.writeUTF("我爱中国");
dos.close();
}
private static void read() throws IOException {
File file = new File("D:\\io\\lzy1.txt");
InputStream in = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(in);
DataInputStream dis = new DataInputStream(bis);
int num = dis.readInt(); //一定要按写入的数据顺序进行读取
byte b = dis.readByte();
String s = dis.readUTF();
System.out.println(num+","+b+","+s);
}
10.字节数组流
ByteArrayInputStream: 包含一个内部缓冲区,改缓冲区包含从流中读取的字节,内部计数器跟踪read方法要提供下一个字节,关闭ByteArrayInputStream无效,在关闭此流后任然可以被调用,不会产生io异常
ByteArrayOutputStream:实现一个输出流,其中数据写入一个byte数组,缓冲区会随着数据的增加而自动增长,可以使用toByteArray()和toString()获取数据。也是关闭无效
代码示例:
在一串字符中,输出字母
/**
* 再一串字符中,输出字母
* */
private static void byteArray(){
String s = "asda1231asda213123asd";
ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int curr = -1; //每次读取的字节
while ((curr=bais.read())!=-1) { //curr=bais.read()返回 0 到 255 范围内的 int 字节值
if ((curr>=65&&curr<=90)||(curr>=97&&curr<=122)){ //一个字节一个字节读取
baos.write(curr);
}
}
System.out.println(baos.toString());
}
运行结果:
11.字符串流
用途:操作xml 或者json用到
StringReader StringWriter 可以用在字符串缓冲区的输出来构建字符串,
代码示例:
把一个字符串作为数据源 来构建一个字符流
/**
* @author zhiyu2.lai
* @deprecated
* 字符串流
* */
private static void stringReader() throws IOException {
String info = "good good bbbbbbb lzy";
StringReader sr = new StringReader(info);
//流标记器
StreamTokenizer st = new StreamTokenizer(sr);
int count = 0;
while (st.ttype !=StreamTokenizer.TT_EOF){ //EOF是文件结束符
if (st.nextToken()==StreamTokenizer.TT_WORD){ //nextToken() - 分析下一个
count++;
}
}
sr.close();
System.out.println("count="+count);
}
12.管道流
管道输入流应该链接管道输出流,数据由某个线程从PipedInputStream对象读取,并且由其它线程写入PipedOutputStream.
管道A数据写入 管道B数据读取
代码示例:
不同线程写入读取数据
//写入数据线程
class WriteThread implements Runnable{
private PipedOutputStream pout;
WriteThread(PipedOutputStream pout){
this.pout = pout;
}
@Override
public void run() {
try {
pout.write("一个美女".getBytes()); //管道输出流
pout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读取数据线程
class ReadThread implements Runnable{
private PipedInputStream pin; //输入管道
ReadThread(PipedInputStream pin){
this.pin = pin;
}
@Override
public void run() {
byte[] buf = new byte[1024];
try {
int len = pin.read(buf);
String s = new String(buf,0,len);
System.out.println("读到"+s);
pin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
。。。。。
public static void main(String[] args) throws IOException {
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream();
pin.connect(pout);// 管道相连
ReadThread readThread = new ReadThread(pin);
WriteThread writeThread = new WriteThread(pout);
new Thread(readThread).start();
new Thread(writeThread).start();
}
13.zip流
在java中如何实现文件的压缩和解压?
1)ZipOutputStream :创建新的zip输出流 ,实现文件的压缩
void putNesxtEntry(zipEntry e) 开始写入新的zip文件条目,并且将流定位到条目数据开始处
zipEntry(String name) 使用指定名称创建新的zip条目
2) ZipInputStream :创建新的zip输入流,实现文件的解压
ZipEntry getNextEntry()
读取下一个zip文件条目,并且将流定位到改文件条目数据的开始处
代码示例:
实现文件的压缩和解压
public class ioTest {
public static void main(String[] args) throws IOException {
compression("D:\\test.zip",new File("D:\\io"));
decompression("D:\\test.zip","D:\\aaa\\");
}
private static void zip(ZipOutputStream zOut, File targetFile, String name/*压缩文件名*/, BufferedOutputStream bos) throws IOException {
//如果是目录
if (targetFile.isDirectory()){
File[] files = targetFile.listFiles();
if (files.length==0){
zOut.putNextEntry(new ZipEntry(name+"/"));//处理空目录 putNextEntry 写入zip条目
}
for (File f:files){
zip(zOut,f,name+"/"+f.getName(),bos);
}
}else {
zOut.putNextEntry(new ZipEntry(name));//putNextEntry开始写入新的zip文件 ZipEntry为新的zip文件设置名字
InputStream in = new FileInputStream(targetFile);
BufferedInputStream bis = new BufferedInputStream(in);
byte[] bytes = new byte[1024];
int len = -1;
while ((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
}
}
/**
* @param zipFileName 压缩文件名
* @param targetFile 要压缩的文件
* */
private static void compression(String zipFileName,File targetFile) throws IOException {
System.out.println("正在压缩");
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
BufferedOutputStream bos = new BufferedOutputStream(out);
zip(out,targetFile,targetFile.getName(),bos); //out压缩文件的字节流
bos.close();
out.close();
}
/**
* 解压
* */
private static void decompression(String targetFileName,String parent) throws IOException {
//构建解压输入流
System.out.println("解压成功");
ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
ZipEntry entry = null;
File file =null;
while ((entry = zIn.getNextEntry())!=null&& !entry.isDirectory()){ //zIn.getNextEntry()读取下一个zip文件条目
file = new File(parent,entry.getName()); //创建解压文件
if (!file.exists()){
new File(file.getParent()).mkdirs(); //创建此文件上的上一级目录
}
OutputStream out = new FileOutputStream(file); //输出流
BufferedOutputStream bos = new BufferedOutputStream(out);
byte[] bytes = new byte[1024];
int len = -1;
while ((len=zIn.read(bytes))!=-1){
bos.write(bytes,0,len); //将解压流写入
}
bos.close();
System.out.println(file.getAbsoluteFile()+"解压成功");
}
}
}
14.实现文件的分割----缓冲流的运用
注意分割算法 有详细注释
public class IO {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("D:\\io\\lzy1.txt");
division(file,70);
}
private static void division(File targetFile,long cutSize) throws IOException {
if (targetFile==null){return;}
//计算分割文件数
int num = targetFile.length()%cutSize==0? (int) ((int) (targetFile.length()) / cutSize) :(int)(targetFile.length()/cutSize+1); //如果不够除,那就把它强行转换为int 再加一
//构造文件输入流
BufferedInputStream in = new BufferedInputStream(new FileInputStream(targetFile));
BufferedOutputStream out = null; //缓存输出流
byte[] bytes = null;//每次读取的字节数
int len = -1;//读取的内容
int count = 0;//每一个文件要读取的次数
for (int i=0;i<num;i++){
out = new BufferedOutputStream(new FileOutputStream(new File("D:\\io"+i+targetFile.getName())));
if (cutSize<=1024){ //每一个文件大小 小于1024 1kb
bytes = new byte[(int) cutSize]; //写入原来的
count=1;//写入一次
}else {
bytes = new byte[1024]; //写入1024
count=(int) cutSize/1024;// 写入次数
}
while (count>0&&(len = in.read(bytes))!=-1){ //读入
out.write(bytes,0,len);// 写出
out.flush();//刷新
count--;
}
if (cutSize%1024!=0){ //单个文件大小,不能整除
//bytes = new byte[(int) cutSize%1024]; //读取大小 ==余数
bytes = new byte[1024]; //读取大小 ==余数
len = in.read(bytes);
out.write(bytes,0,len);
out.flush();
out.close();
}
}
in.close();
}
}
总结
对各种的流进行了比较深入的讲解,并且都有代码示例来理解各种流的作用,上面的代码都经过本人亲自的编写和运行,所以因为问题不会很大。有错误的地方欢迎指正。