目录
3.4 PrintWriter(比BufferedWriter更好用)
1.IO流分类
按照方向:输入流(比如你复制的那个文件) 输出流(比如你复制完的新文件)
按照单位:字节流(以字节为单位进行数据传输byte)
字符流(以字符为单位进行数据传输char)
按照功能:节点流(相当于针头) 过滤流(相当于针管)
2.字节流
2.1 In/OutputStream
InputStream 输入流统一的父类,是抽象类
三种方法
int read()
int read(byte[] data) 最常用(里面参数后面解释)
int read(byte[] data,int off,int len)
OutputStream 输出流统一的父类,是抽象类
对应的也是三种方法
write(int data)
write(byte[] data)
write(byte[] data,int off,int len)这个最常用
=========================================================================
FileInputStream 读取文件的,是节点流(打针的针头,它得插文件里,不能插目录里(会报异常))
FileOutputStream 输出文件的,也是节点流(打针的针头,它得插文件里,不能插目录里(会报异常))
*: FileInputStream 最常用的是read(byte[] data)参数代表一次传输的数据大小,默认(啥都不写)一个字节(慢的感人)
FileInputStream 以-1作为读取结束的标识
import java.io.*;
public class TestFileInputStream{
public static void main(String[] args)throws Exception{
FileInputStream fis = new FileInputStream("abc.txt");
int data = 0;
while((data = fis.read())!=-1//等于-1时结束读取){
System.out.print((char)data);
}
fis.close();
}
}
//abc.txt里是helloworld
//不推荐使用这种方法,更推荐
/*public static void main(String[] args)throws Exception{
FileInputStream fis = new FileInputStream("abc.txt");
int len;
byte[] data = new byte[3];//byte数组大小代表一次运输的数据量大小
while((len = fis.read(data))!=-1){
for(int i = 0;i<len;i++){
System.out.print((char)data[i]);
}
}
fis.close();//用完要及时关闭流
}
*/
*: FileOutputStream 最常用的却是write(byte[],int,int)
将指定字节数组byte中从偏移量(第一个int)开始的(第二个int)个字节写入此文件输出流
这种方法(节点输出流的)很危险,当你连接的文件不存在时它会自动帮你创建那个文件。
但是,当你连接一个已经存在的文件,它会在创建流的一瞬间新建一个空白文件替换那个文件,因此使用前需要注意。
如果你需要的是在原本的文件后要继续追加其它内容(比如在一个txt文件a里想让它后面加上txt文件b的内容),可以在构造方法中指定追加模式开启
FileOutputStream fos = new FileOutputStream("a.txt",true)
=========================================================================
2.2TWR语法
正常情况下所有新建流的语句都需要加上trycatch来处理异常。因此就会
import java.io.*;
public class TestFileCopyWithTryCatch{
public static void main(String[] args){
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream("1.mp3");
fos = new FileOutputStream("3.mp3");
byte[] data = new byte[5<<20];//5*1024*1024
int len;
while((len = fis.read(data))!=-1){
fos.write(data,0,len);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
fis.close();//要确保fis.close()一定能实现,因此放在finally里
}catch(Exception e){
e.printStackTrace();
}finally{
try{
fos.close();//不放上面那个try里是防止上面那个关闭失败导致fos也关闭失败
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
长的感人。。
因此,JDK7.0后推出了一个新的语法TWR:try - with - resources --->带有资源控制的trycatch
于是,代码变成了
import java.io.*;
public class TestFileCopyWithTryCatch{
public static void main(String[] args){
FileInputStream fis = null;
FileOutputStream fos = null;
try(fis = new FileInputStream("1.mp3");//给流赋值的语句放到try()中,{}里写方法主体
fos = new FileOutputStream("3.mp3");//最后的;可写可不写)
{
byte[] data = new byte[5<<20];//5*1024*1024
int len;
while((len = fis.read(data))!=-1){
fos.write(data,0,len);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
它会自动帮你关闭流,不需要你操心,就很棒棒
2.3 BufferedIn/OutputStream
BufferedInputSteam 和上面(FileInputStream)基本一样但它不是针头而是管道(过滤流)。
BufferedOutputStream 和上面(FileOutStream)基本一样但它不是针头而是管道(过滤流)。
1.作为过滤流的它们 是为了给原本的节点流添加缓冲空间,从而提高每次读写的吞吐量,进而提高效率(就和StringBuffer一样)
2.它们不能直接连接文件,而是要连接其它流(节点流)
3.它们构造方法第二个参数 都允许指定缓冲空间的大小 默认8192个字节 就是8k
4.BufferedInputStream 最常用的方法是read()
5. Output 最常用的方法是write(int data) 因为缓冲区大小在构造方法里设置
6.BufferedInputStream 同样以-1作为读取结束的标识
7.BufferedOutputStream 是带缓冲的输出流
使用带缓冲的输出流 务必注意及时清空缓冲
以防止数据滞留缓冲空间 导致丢失
缓冲区什么条件下会清空:
1.满了自动清空 不需要操作
2.关闭流的操作会触发清空缓冲
3.主动清空缓冲 flush();
8.文件复制后变小了是因为什么,文件复制后变大了是因为什么
变小了:使用了带缓冲区的输出流 但是没有及时清空缓冲
所以数据滞留缓冲空间导致丢失...
变大了:使用了只传数组的write(byte[] data) 写出了多余的冗余数据
import java.io.*;
public class TestBufferedStream{
public static void main(String[] args)throws Exception{
FileInputStream fis = new FileInputStream("1.mp3");//针头
BufferedInputStream bis = new BufferedInputStream(fis,5<<20);//针管(针头)
FileOutputStream fos = new FileOutputStream("3.mp3");//针头
BufferedOutputStream bos = new BufferedOutputStream(fos,5<<20);
int data;
while((data = bis.read())!=-1){
bos.write(data);
}
bis.close();
//bos.flush();不需要手动flush刷新,因为.close()会自动刷新
bos.close();
}
}
2.4 DataIn/OutputStream
DataInputStream 输入流 字节流 过滤流
DataOutputStream 输出流 字节流 过滤流
作用:作为过滤流的它们 是为了给原本节点流添加读写基本数据类型的功能的
它们不能直接连接文件,而是要连接其它流(节点流)
DataInputStream 核心方法 readXxxx(); 有返回值
DataOutputStream 核心方法 writeXxxx(); 要参数
在判断结束上:
DataInputStream 不能再以-1作为读取结束的标识了
而是一旦到达文件结尾 还继续尝试读取
将会直接触发EOFException => End Of File
因此trycatch手动修改报错就行
或者使用集合,数组等将对象存起来,当read时一个一个的从集合或数组里取出来就行了
import java.io.*;
public class TestDataStream{
public static void main(String[] args)throws Exception{
/*
int level = 835;
FileOutputStream fos = new FileOutputStream("save.data");
DataOutputStream dos = new DataOutputStream(fos);
dos.writeInt(level);//注意核心方法
dos.close();
*/
FileInputStream fis = new FileInputStream("save.data");
DataInputStream dis = new DataInputStream(fis);
int data = dis.readInt();//注意核心方法
dis.close();
System.out.println(data);
}
}
2.5 ObjectIn/OutputStream
有了基本数据类型,那么引用数据类型也是有输入输出流方法的。
ObjectInputStream 输入流 字节流 过滤流
ObjectOutputStream 输出流 字节流 过滤流
1.作为过滤流的它们 是为了给原本的节点流添加读写对象的功能的
2.作为过滤流的它们 不能直接连接文件 只能连接其它的流!
3.ObjectInputStream 提供的核心方法 名叫readObject()
4.ObjectOutputStream 提供的核心方法 writeObject()
5.ObjectInputStream 同样不能以-1作为读取结束的标识
因为Integer Byte Short 都有可能读取到-1
所以它同样是到达文件结尾 再继续读取
就直接触发EOFException...
6.想要持久化 首先需要序列化 implements Serializable
想要持久化的对象的类型必须要实现序列化接口
而如果其中还有其它引用类型的属性,就连属性的类型也必须要实现序列化接口
如果某些属性无关紧要 不需要参与持久化,可以使用transient修饰
transient => 短暂的 => 不参与持久化的
如果要持久化的是一个集合对象,则必须保证集合当中的元素类型必须实现过序列化接口
如果要持久化的是一个使用了比较器的TreeSet或者TreeMap
就连比较器的类也要实现序列化接口,因为比较器是它们的一个属性
import java.io.*;
import java.util.*;
public class TestObjectStream{
public static void main(String[] args)throws Exception{
/*
Date today = new Date();
FileOutputStream fos = new FileOutputStream("月光宝盒.data");
ObjectOutputStream dos = new ObjectOutputStream(fos);
dos.writeObject(today);
dos.close();
*/
FileInputStream fis = new FileInputStream("月光宝盒.data");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
ois.close();
Date theDate = (Date)obj;
System.out.println(theDate);
}
}
import java.io.*;
public class TestObjectStream2{
public static void main(String[] args)throws Exception{
/*Elephent el = new Elephent("大象");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("电冰箱.data"));
oos.writeObject(el);
oos.close();
*/
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("电冰箱.data"));
Object obj = ois.readObject();
ois.close();
Elephent ele = Elephent(obj);
System.out.println(ele.name);
//没有pc那个属性
}
}
class Computer{
String logo;
public Computer(String logo){
this.logo = logo;
}
}
class Elephent implements Serializable{
String name;
//transient = 短暂的 转瞬即逝的 = 不参与持久化的~
transient Computer pc;
public Elephent(String name){
this.name = name;
pc = new Computer("Lenovo");
}
}
3.字符流
3.1Read/Writer
Reader 所有字符输入流统一的父类 抽象类
int read()
int read(char[] data)
int read(char[] data,int off,int len)
Writer 所有字符输出流统一的父类 抽象类
write(int data)
write(char[] data)
write(char[] data,int off,int len)
3.2FileReader/Writer
FileReader 输入流 字符流 节点流
FileWriter 输出流 字符流 节点流
它们都是节点流,构造方法允许传入String路径/File对象
它们都只能连接文件,不能连接文件夹,否则会抛出异常
FileReader 最常用read(char[] data)
FileWriter 最常用write(char[],int,int)
FileWriter 的其它同FileOutputStream一样(危险性,追加模式)
FileReader 也是以-1为结束标识
用完后要第一时间关闭流,解除文件占用状态
同样可以使用TWR语法来处理异常(JDK7.0)
import java.io.*;
public class TestFileReader{
public static void main(String[] args)throws Exception{
FileReader fis = new FileReader("abc.txt");
int data;
while((data = fis.read())!=-1){
System.out.print((char)data);
}
fis.close();
}
}
3.3 BufferedReader/Writer
BufferedReader 字符流 输入流 过滤流
BufferedWriter 字符流 输出流 过滤流
1.作为过滤流的它们 是为了给原本的节点流添加变长的缓冲空间,从而实现以一行为单位
读写
2.它们都是过滤流 只能连接其它的流 不能直接连接文件
3.BufferedReader 核心方法 String readLine()
4.BufferedReader 不以-1作为读取结束的标识 而是null
5.BufferedWriter 核心方法 write(String) + newLine()
import java.io.*;
public class TestBufferedReader{
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new FileReader("focus.txt"));
String str;
while((str = br.readLine())!=null){
if(str.contains("流")){
System.out.println(str);
}
}
br.close();
}
}
import java.io.*;
public class TestBufferedWriter{
public static void main(String[] args)throws Exception{
BufferedWriter bw = new BufferedWriter(new FileWriter("鹅鹅鹅.txt"));
bw.write("鹅鹅鹅");
bw.newLine();//换行
bw.write("曲项向天歌");
bw.newLine();//换行
bw.write("白毛浮绿水");
bw.newLine();//换行
bw.write("鹅掌拨清波");
//bw.flush();close自动调flush
bw.close();
}
}
3.4 PrintWriter(比BufferedWriter更好用)
1. 它既可以当做节点流 又可以当做过滤流
构造方法允许传入 输出流 或者 文件对象 或者 文件路径
2. 它既可以连接字节流 又可以连接字符流
构造方法允许传入 OutputStream / Writer
3. 当做节点流使用的时候 其构造方法第二个参数
可以指定字符编码..
new PrintWriter("abc.txt","utf-8");
4. 当做过滤流使用的时候 其构造方法第二个参数
可以指定自动清空缓冲~
new PrintWriter(new FileWriter("abc.txt",true),true);
这行代码当中两个true 分别代表 appendMode / autoFlush
5. println() = write() + newLine()
6. 你对它的孪生兄弟太熟悉了
System.out.println(); out是PrintStream
综上所述 以行为单位的写出文本文件
我们根本不会选择 BufferedWriter 而会选择 PrintWriter
import java.io.*;
public class TestPrintWriter{
public static void main(String[] args)throws Exception{
PrintWriter pw = new PrintWriter("春晓.txt");
pw.println("春眠不觉晓");
pw.println("处处闻啼鸟");
pw.println("夜来风雨声");
pw.print("花落知多少");
pw.close();
}
}
3.5 InputStreamReader/OutputStreamWriter
InputStreamReader
OutputStreamWriter
*: 字节流 通向字符流的桥梁 字节流转换成字符流的工具
import java.io.*;
public class TestUTF8{
public static void main(String[] args)throws Exception{
/*
PrintWriter pw = new PrintWriter("测试.txt","utf8");
pw.println("醉里挑灯看剑");
pw.println("梦回吹角连营");
pw.close();
*/
//FileReader fr = new FileReader("测试.txt");
FileInputStream fis = new FileInputStream("测试.txt");
//桥转换器 => 字节流通向字符流的桥梁 字节流转换成字符流的工具
InputStreamReader r = new InputStreamReader(fis,"utf8");//可以指定编码格式
BufferedReader br = new BufferedReader(r);
String str;
while((str = br.readLine())!=null){
System.out.println(str);
}
br.close();
}
}
4.网络下载
4.1联网下载
import java.net.*;
import java.io.*;
//URL => Uniform Resources Locator => 统一资源定位符 => 网址
public class TestURL{
public static void main(String[] args)throws Exception{
URL url = new URL("https://tu.duoduocdn.com/uploads/day_190323/5c954198cc3a5.jpg");
URLConnection uc = url.openConnection();
InputStream is = uc.getInputStream();
FileOutputStream fos = new FileOutputStream("Maldini.jpg");
byte[] data = new byte[65536];
int len;
while((len = is.read(data))!=-1){
fos.write(data,0,len);
}
fos.close();
is.close();
}
}
4.2显示百分比进度
import java.net.*;
import java.io.*;
//URL => Uniform Resources Locator => 统一资源定位符 => 网址
public class TestURLPlus{
public static void main(String[] args)throws Exception{
URL url = new URL("https://mp4.vjshi.com/2022-02-24/5dbea0cbffba416c93a532c16b01d8a9.mp4");
URLConnection uc = url.openConnection();
InputStream is = uc.getInputStream();
int total = uc.getContentLength();//文件总共大小
FileOutputStream fos = new FileOutputStream("test.mp4");
byte[] data = new byte[1<<20];
int len;
int current = 0;
int lastPercent = -1;//之前显示的%
while((len = is.read(data))!=-1){
fos.write(data,0,len);
current += len;
int percent = (int)(current * 100L / total);
if(lastPercent != percent){
System.out.print("\r已经完成:"+percent+"%");
lastPercent = percent;
}
}
fos.close();
is.close();
}
}