1 初识io流
IO,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。
Java 中是通过流处理IO 的,那么什么是流?
流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。
当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
一般来说关于流的特性有下面几点:
- 先进先出:最先写入输出流的数据最先被输入流读取到。
- 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
- 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
2 IO流分类
IO流主要的分类方式有以下3种:
按数据流的方向:输入流、输出流
按功能:字符流,字节流
按名字:字符输入流,字符输出流,字节输入流,字节输出流。
2.1 输入流与输出流
输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流,这点很容易搞反。
2.2 字节流与字符流
字节流和字符流的用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。
为什么要有字符流?Java中字符是采用Unicode标准,Unicode 编码中,一个英文字母或一个中文汉字为两个字节。
而在UTF-8编码中,一个中文字符是3个字节。例如下面图中,“云深不知处”5个中文对应的是15个字节:-28-70-111-26-73-79-28-72-115-25-97-91-27-92-124
那么问题来了,如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。
字节流和字符流的其他区别:
字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。
2.3 File类
File类是用来操作文件的类,但它不能操作文件中的数据。
File类实现了Serializable、 Comparable,它是支持序列化和排序的。
File类的构造方法
File类的常用方法
代码1:
public class FileTest {
public static void main(String[] args) {
File file = new File("D:\\iotest");
boolean state=file.exists();
System.out.println(state);
File file1 = new File("D:\\iotest\\file\\test01");
boolean state1=file1.exists();
if(!state1){
file1.mkdirs();//创建多级文件夹
}
File file2 = new File("D:\\iotest\\file\\test01\\a.txt");
if(!file2.exists()){
try {
file2.createNewFile();//创建一个文件
} catch (IOException e) {
e.printStackTrace();
}
}
File file3=new File("D:\\qy141");
File[] lists=file3.listFiles();//子文件
for(File f1:lists){
System.out.println(f1.getAbsolutePath());
}
}
查找文件:
public class FileTools {
public static void main(String[] args) {
File file3=new File("D:\\qy141");
bianli(file3);
}
public static void bianli(File file){
File[] lists=file.listFiles();//子文件
for(File f1:lists){
System.out.println(f1.getAbsolutePath());
if(f1.isDirectory()){
bianli(f1);//递归:自己调用自己
}
}
}
}
2.4 字节流
InputStream与OutputStream是两个抽象类,是字节流的基类,所有具体的字节流实现类都是分别继承了这两个类。
InputStream的关系继承图
InputStream:InputStream是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。
FileInputSream:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。
PipedInputStream:管道字节输入流,能实现多线程间的管道通信。
ByteArrayInputStream:字节数组输入流,从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。
FilterInputStream:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。
DataInputStream:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。
BufferedInputStream:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。
ObjectInputStream:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream的实例对象。
OutputStream类继承关系图:
字节流方法
字节输入流InputStream主要方法:
public static void main(String[] args) {
/*File file3=new File("D:\\qy141");
bianli(file3);*/
File f1=new File("D:\\qy141\\java01\\src\\java01\\Class07.java");//文本文件
read(f1);
}
//读得必须是一个文件
public static void read(File file){
//流——输入——字节输入流——基础流
try {
InputStream is=new FileInputStream(file);
//读
int a;
int[] ints=new int[1024];
int i=0;
for(;(a=is.read())!=-1;i++){
ints[i]=a;//d读取得内容放到数组
}
System.out.println(new String(ints,0,i));
//只将读取得转换,空得数组不转换
is.close();
/*1.读出来得东西看不懂 什么时候到头不知道 效率低
int a=is.read();//01010101
System.out.println(a);
a=is.read();//01010101
System.out.println(a);*/
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
循环读取:
public static void main(String[] args) {
/*File file3=new File("D:\\qy141");
bianli(file3);*/
File f1=new File("D:\\qy141\\java01\\src\\java01\\Class07.java");//文本文件
read(f1);
}
public static void read2(File file){
try {
InputStream is = new FileInputStream(file);
byte[] bytes=new byte[100];
int length;
while((length=is.read(bytes))!=-1){
System.out.println(new String(bytes,0,length));
//避免空的 避免重复得转换
}
}catch (Exception e){
e.printStackTrace();
}
}
字节输出流OutputStream主要方法:
public static void write1(){
File file = new File("D:\\iotest\\a.txt");
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
//字节流 输出流 基础流
OutputStream os=new FileOutputStream(file,true);//默认:覆盖书写 append:true
//写东西
//os.write(120);8个01对应的数字——一个字节
String str="Hello World!";
byte[] bytes=str.getBytes();
os.write(bytes);
os.flush();//强制将缓存中的东西推送到琉——刷新缓存
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
字节包装流——缓冲流
构造1:BufferedInputStream(InputStream in)
构造2:BufferedInputStream(InputStream in,int size)
第一种形式的构造方法创建带有32个字节缓冲区的缓冲流
第二种形式的构造方法按指定的大小来创建缓冲区
BufferedOutputStream()
构造1:BufferedOutputStream(OutputStream in)
构造2:BufferedOutputStream(OutputStream in,int size)
public static void read3(){
File f1=new File("D:\\qy141\\java01\\src\\java01\\Class07.java");//文本文件
try {
InputStream is = new FileInputStream(f1);
BufferedInputStream bis=new BufferedInputStream(is);//参数:基础流——提供了额外的功能——缓存区
byte[] bytes=new byte[100];//缓存数组
int length;
while((length=bis.read(bytes))!=-1){//length=is.read(bytes)读取然后返回得读取得长度
System.out.println(new String(bytes,0,length));//避免空得 避免得重复得转换
}
bis.close();
}catch (Exception e){
e.printStackTrace();
}
}
public static void write2(){
try {
//字节流 输出流 基础流
OutputStream os=new FileOutputStream("D:\\iotest\\b.txt",true);//默认:覆盖书写 append:true
BufferedOutputStream bos=new BufferedOutputStream(os);
//写东西
String str="Hello World!";
byte[] bytes=str.getBytes();
bos.write(bytes);
bos.flush();//强制将缓存中的东西推送到琉——刷新缓存
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
文件拷贝:
// 对文件做读写——文本文件——其他没有办法直接都城文本或者写文本
// 拷贝——文件转换成输入流——写道输出流中——所以:什么文件都行
//61090
public static void copy1(File oldfile){
File fileNew=new File("D://12号New.zip");
//将原文件转成输入流
try {
long l1=System.currentTimeMillis();
InputStream is=new FileInputStream(oldfile);
//将目的文件转成输出流
OutputStream os=new FileOutputStream(fileNew);
//将输入流读取——写道输出流
int length;
byte[] bytes=new byte[1024];
while((length=is.read(bytes))!=-1){
os.write(bytes,0,length);
}
os.flush();
os.close();
is.close();
long l2=System.currentTimeMillis();
System.out.println(l2-l1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//17002
public static void copy2(File oldfile){
File fileNew=new File("D://12号New.zip");
//将原文件转成输入流
try {
long l1=System.currentTimeMillis();
InputStream is=new FileInputStream(oldfile);
BufferedInputStream bis=new BufferedInputStream(is);
//将目的文件转成输出流
OutputStream os=new FileOutputStream(fileNew);
BufferedOutputStream bos=new BufferedOutputStream(os);
//将输入流读取——写道输出流
int length;
byte[] bytes=new byte[1024];
while((length=bis.read(bytes))!=-1){
bos.write(bytes,0,length);
}
bos.flush();
bos.close();
bis.close();
long l2=System.currentTimeMillis();
System.out.println(l2-l1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
字节包装流:——对象流(序列化)
功能:new Student(“zs”,11)——这个对象存储起来——文件中
对象:new Array Map Studentnew的一个个对象——原本堆区——内存
存储放到其他的地方:
文件中——:首先将对象序列化:对象以特殊形式进行转换,获取一个字符串的东西——序列化——写到文件(对象流:对象进行序列化写道文件)
序列化:对象-》字节
做缓存。
public static void duixiang(){
//将对象写道文件里
try {
OutputStream os=new FileOutputStream("D://iotest//aaa.obj");
ObjectOutputStream oos=new ObjectOutputStream(os);
Student stu=new Student("zs",11);//序列化,必须实现Serializable 接口
oos.writeObject(stu);
oos.flush();
oos.close();
InputStream is=new FileInputStream("D://iotest//aaa.obj");
ObjectInputStream ois=new ObjectInputStream(is);
Student stu1= (Student) ois.readObject();//文件中
System.out.println(stu1.getAge()+"---"+stu1.getName());
System.out.println(stu1);
System.out.println(stu);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
2.5 字符流
与字节流类似,字符流也有两个抽象基类,分别是Reader和Writer。其他的字符流实现类都是继承了这两个类。
以Reader为例,它的主要实现子类如下图:
InputStreamReader:从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。
BufferedReader:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader是对InputStreamReader的封装,前者构造器的入参就是后者的一个实例对象。
FileReader:用于读取字符文件的便利类,new FileReader(File file)等同于new InputStreamReader(new FileInputStream(file, true),“UTF-8”),但FileReader不能指定字符编码和默认字节缓冲区大小。
PipedReader :管道字符输入流。实现多线程间的管道通信。
CharArrayReader:从Char数组中读取数据的介质流。
StringReader :从String中读取数据的介质流。
Writer与Reader结构类似,方向相反,不再赘述。唯一有区别的是,Writer的子类PrintWriter。
字符流方法
字符输入流Reader主要方法:
public static void copy3(File oldFile){
File newFile=new File("D:\\iotest\\aa.txt");
try {
Reader reader=new FileReader(oldFile);
Writer writer=new FileWriter(newFile);
char[] chars=new char[1024];
int length;
while((length=reader.read(chars))!=-1){
writer.write(chars,0,length);
}
writer.write("已经拷贝完毕");
writer.flush();
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
字符输出流Writer主要方法:
另外,字符缓冲流还有两个独特的方法。
BufferedWriter类newLine() :写入一个行分隔符。这个方法会自动适配所在系统的行分隔符。
BufferedReader类readLine() :读取一个文本行。
public static void main(String[] args) {
copy4(new File("D:\\iotest\\a.txt"));
//duixiang();
}
public static void copy4(File oldFile){
File newFile=new File("D:\\iotest\\aa.txt");
try {
Reader reader=new FileReader(oldFile);
BufferedReader bread=new BufferedReader(reader);
Writer writer=new FileWriter(newFile);
BufferedWriter bwriter=new BufferedWriter(writer);
char[] chars=new char[1024];
int length;
while((length=bread.read(chars))!=-1){
bwriter.write(chars,0,length);
}
bwriter.newLine();
bwriter.write("已经拷贝完毕");
bwriter.flush();
bwriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2.6 转换流:
InputStreamReader和OutputStreamWriter是字节流和字符流之间的转换类
InputStreamReader(InputStream in):类是从字节流到字符流的桥接器:它使用指定的字符集读取字节并将它们解码为字符
FileReader:将读取 的字节转换成字符,默认的——发生乱码
OutputStreamWriter(outPutStream os);是字符通向字节流的桥梁:可以指定的charset将要写入流中的字符编码成字节。(编码:能看懂的字符变为看不懂的字节)
InputStreamReader(InputStream in,“gb2312”); //将读取的字节流抓换成字符流 _可以指明编码格式
OutputStreamWriter(outPutStream os”utf-8”); //将书写的字符转换成字节流——可以指明编码格式
public static void main(String[] args) {
//源文件:GBK java环境:utf-8,新建的文件Utf-8
// 读--字节---java环境:utf-8转换成字符
// 写——字符---java的utf-8,转换成字节,然后写的文件
copy5(new File("D:\\iotest\\WebSocketTest.java"));
//duixiang();
}
public static void copy5(File oldFile){
File newFile=new File("D:\\iotest\\WebSocketTestNew.java");
try {
InputStream is=new FileInputStream(oldFile);// 字节
InputStreamReader reader=new InputStreamReader(is,"gbk");//按照GBK将字节转换成字符
OutputStream os=new FileOutputStream(newFile);
// 写的时候 字符 底层 转成字节
OutputStreamWriter writer=new OutputStreamWriter(os,"utf-8");//写的字符按照utf-8取字节
char[] chars=new char[1024];
int length;
while((length=reader.read(chars))!=-1){
writer.write(chars,0,length);
}
writer.write("已经拷贝完毕");
writer.flush();
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}