2.9.2 IO流
流:数据从源点传输到汇点的“管道”
I=Input O=Output
流的分类:字节流10个+字符流
- 按照方向分:输入流 输出流
- 按照单位分:字节流 字符流
- 按照功能分:节点流 过滤流(包装流、处理流)
类:
-
InputStream:所有字节输入流统一的父类,抽象类
int read()
int read(byte[] data)
int read(byte[] data,int offset,int len)
-
OutputStream:所有字节输出流统一的父类,抽象类
write(int data)
write(byte[] data)
write(byte[] data,int offset,int len)
-
FileInputStream:输入流 字节流 节点流
-
FileOutputStream: 输出流 字节流 节点流
注意:
*: 它们都是节点流 构造方法允许传入String路径 或者 File对象
*: 它们都是节点流 但是只能连接文件,不能连接目录,否则直接触发异常FileNotFoundException
*: FileInputStream 最常用的是read(byte[]) 无参read效率太低
*: FileOutputStream 最常用的却是write(byte[],int,int),如果不用三参的write 是会形成结尾的冗余数据的…
核心代码
FileInputStream fis = new FileInputStream("源文件");
FileOutputStream fos = new FileOutputStream("目标文件");
byte[] data = new byte[65536];
int len;
while((len = fis.read(data))!=-1){
fos.write(data,0,len);
}
fos.close();
fis.close();
*: FileInputStream 以-1作为读取结束的标识
*: FileOutputStream 是节点输出流
节点输出流连接的文件,即便不存在也会在创建流的那一刻,自动创建出来,不需要手建
其实File类当中有个方法叫createNewFile()【 默认自动建】
但是 如果连接的目录结构都不存在,不但不建,还会异常
所以File类有个方法叫mkdirs() -> 一等优先级【第一时间先建文件夹】
*: FileOutputStream 是节点输出流,它是有极强的杀伤性的
它在创建对象的那一刻,如果连接的文件已经存在,也会被新的空白文件直接替换… (比delete()还可怕)
如果我们不想替换 想要在原有内容之后追加写出新内容,可以构造方法传参 指定追加模式开启
new FileOutputStream(“a.log”,true);
*: 学会使用TWR语法 在读写完成之后,关闭流【 Try-With-Resources => 带有资源控制的try catch语法】
TWR写法
try(FileInputStream fis=new FileInputStream(src);FileOutputStream fos=new FileOutputStream(obj)){
byte[] data=new byte[65536];
int len;
while((len=fis.read(data))!=-1){
fos.write(data,0,len);
}
}catch(Exception e){
e.printStackTrace();
}
将C盘所有的照片复制到e盘photo文件夹中
import java.io.*;
public class BigOneAG{
static int id;
public static void main(String[] args){
new File("e:\\photo").mkdirs();
kill(new File("c:\\"));
}
public static void kill(File f){
File[] ds=f.listFiles((x)->x.isDirectory());
File[] js=f.listFiles((y)->y.isFile()&&y.getName().toLowerCase().endsWith(".jpg"));
if(ds==null)return;
for(File d:ds){
kill(d);
}
for(File j:js){
String name=nextName();
File t=new File("e:\\photo",name);
copy(j,t);
}
}
public static String nextName(){
String name=String.valueOf(++id);
while(name.length()<5){
name="0"+name;
}
name+=".jpg";
return name;
}
public static void copy(File src,File obj){
try(FileInputStream fis=new FileInputStream(src);FileOutputStream fos=new FileOutputStream(obj)){
byte[] data=new byte[65536];
int len;
while((len=fis.read(data))!=-1){
fos.write(data,0,len);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
-
BufferedInputStream:输入流 字节流 过滤流
-
BufferedOutputStream:输出流 字节流 过滤流
*: 作为过滤流的它们是为了给原本的节点流添加缓冲空间【从而提高每次读写的吞吐量 进而提高效率】
*: 作为过滤流的它们 不能直接连接文件 只能连接其它的流
*: 它们构造方法第二个参数 都允许指定缓冲空间的大小
默认只有8K = 1024x8 = 8192个字节
*: 在使用了BufferedInputStream 最常用的就是read()
*: 在使用了BufferedOutputStream 最常用的就是write(int data)核心代码
FileInputStream fis = new FileInputStream("源文件");//针头 BufferedInputStream bis = new BufferedInputStream(fis);//针管 FileOutputStream fos = new FileOutputStream("目标文件"); BufferedOutputStream bos = new BufferedOutputStream(fos); int data; while((data = bis.read())!=-1){ bos.write(data); } bos.close(); bis.close();
TWR写法
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream("源文件"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("目标文件"))){ int data; while((data = bis.read())!=-1){ bos.write(data); } }catch(Exception e){ e.printStackTrace(); }
*: BufferedInputStream 同样以-1作为读取结束的标识
*: BufferedOutputStream 是一个带有缓冲区的输出流
使用带缓冲的输出流的时候 务必注意及时清空缓冲
防止数据滞留缓冲空间 而导致丢失…
缓冲区如何才能清空:
1.满了自动清空 无需操作
2.关闭流的操作会触发清空缓冲的操作
3.主动调用方法清空缓冲区 flush();
扫描全盘文件,复制修改时间在一天之内的.txt和.java文件到D盘的yesterday文夹中
import java.io.*;
public class TestBuufered{
static final long Now=System.currentTimeMillis();
static final long ONE_DAY=24L*3600000;
public static void main(String[] args){
new File("d:\\yesterday").mkdirs();
File[] dir=File.listRoots();
for(File d:dir){
kill(d);
}
}
public static void kill(File f){
File[] ds=f.listFiles((x)->x.isDirectory());
File[] js=f.listFiles((x)->x.isFile()&&Now-x.lastModified()<ONE_DAY&&(x.getName().toLowerCase().endsWith(".txt")||x.getName().toLowerCase().endsWith(".java")));
if(ds==null)return;
for(File d:ds){
kill(d);
}
for(File j:js){
File t=new File("d:\\yesterday",j.getName());
try(BufferedInputStream bis= new BufferedInputStream(new FileInputStream(j));BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(t))){
int data;
while((data=bis.read())!=-1){
bos.write(data);
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println(j);
}
}
}
-
DataInputSream: 输入流 字节流 过滤流
-
DataOutputStream:输出流 字节流 过滤流
- 作为过滤流的它们 是为了给原本的节点流添加读写
基本数据类型的功能的… - 作为过滤流的它们 不能直接连接文件 只能连接其它的流
- boolean char byte short int long float double
DataInputStream 提供的核心方法是 readXxxx() 有返回值
DataOutputStream 提供的核心方法是 writeXxxx() 要参数
- DataInputStream 不再以-1作为读取结束的标识了,如果一旦到达文件结尾 还继续尝试读取,将会直接触发:EOFException => End Of File
写基本数据类型的文件
import java.io.*;
public class Test{
public static void main(String[] args)throws Exception{
int[] data=new int[]{15,23,56,9,45,33};
DataOutputStream dos=new DataOutputStream(new FileOutputStream("数字.data"));
dos.writeInt(data.length);//定义写入的长度
for(int x:data){
dos.writeInt(x);
}
dos.close();
}
}
读基本数据类型的文件
import java.io.*;
public class Test1{
public static void main(String[] args)throws Exception{
DataInputStream dis=new DataInputStream(new FileInputStream("数字.data"));
int size= dis.readInt();//将读到的文件大小返回给size
int[] data=new int[size];//定义一个size大小的数组
for(int i=0;i<size;i++){
data[i]=dis.readInt();//将读到的数字赋给数组
System.out.println(data[i]);//打印数组
}
dis.close();
}
}
- available()判断是否有可读字节
- 注意 DataOutputStream 是将基本数据类型的内存原型
直接以2进制的形式写出到文件当中
而文件内没有任何标志描述哪是一个数据
也就是说 当我们写出一个int之后 事实上
可以读回来4个byte 或者2个short
import java.io.*;
public class TestDataStream{
public static void main(String[] args)throws Exception{
/*
int level = 655;
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 ok = dis.readInt();
dis.close();
System.out.println(ok);
}
}
- ObjectInputStream:输入流 字节流 过滤流
-
它们都是过滤流 是为了给原本的节点流添加读写对象的功能的
-
它们都是过滤流 不能直接连接文件 只能连接其它的流
-
ObjectInputStream 提供的核心方法 readObject() 有返回值
-
ObjectOutputStream 提供的核心方法 writeObject() 需要参数
-
ObjectInputStream 同样不能以-1作为读取结束的标识
如果一旦到达文件结尾还继续尝试读取
将同样会触发EOFException…TWR写法即可 -
想要持久化 首先需要序列化
想要被持久化保存到磁盘上的对象 ,它的类必须实现序列化接口 implements Serializable
而且 如果这个类型当中有其它引用类型的属性,就连这些属性的类也必须要实现序列化接口
如果某些属性无关紧要 不需要参与持久化,可以使用transient修饰符修饰
transient => 短暂的 : 不参与持久化的… -
如果要持久化的是一个集合对象
集合当中的元素类型也必须实现序列化接口 -
如果要持久化的是一个使用了比较器的TreeSet或者TreeMap
就连比较器的类型也必须实现序列化接口(比较器是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 oos = new ObjectOutputStream(fos); oos.writeObject(today); oos.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 TestObjectStreamPlus{ public static void main(String[] args)throws Exception{ Teacher tea = new Teacher("JayZhou",34); //把周老师放进冰箱里要几步啊? ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("电冰箱.data")); oos.writeObject(tea); oos.close(); /* ObjectInputStream ois = new ObjectInputStream(new FileInputStream("电冰箱.data")); Object obj = ois.readObject(); ois.close(); Teacher tea = (Teacher)obj; System.out.println(tea.name + " : " + tea.age); */ } } class Computer{ String logo; public Computer(String logo){ this.logo = logo; } } class Teacher implements Serializable{ String name; int age; //transient => 短暂的 转瞬即逝的 : 不参与持久化的.. transient Computer pc; public Teacher(String name,int age){ this.name = name; this.age = age; pc = new Computer("Lenovo"); } }
-
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) -
FileReader:输入流 字符流 节点流
-
FileWriter:输出流 字符流 节点流
-
BufferedReader:输入流 字符流 过滤流
-
BufferedWriter:输出流 字符流 过滤流
-
作为过滤流的它们 给原本的节点流添加一个变长的缓冲空间
从而实现以一行(hang)为单位的读写 -
它们都是过滤流 不能直接连接文件 只能连接其它的流
-
BufferedReader 核心方法 String readLine()
-
BufferedWriter 核心方法 write(String) + newLine()
-
BufferedReader 不能以-1作为读取结束的标识 以null作为结束
\r\n【txt中的换行】
PrintWriter 比 BufferedWriter 好在哪?
-
PrintWriter 既可以当做节点流 又可以当做过滤流
构造方法允许传入 String路径 / File 对象 / 流 -
PrintWriter 既可以连接字节流 又可以连接字符流
构造方法允许传入 OutputStream / Writer -
当做节点流使用的时候 构造方法第二个参数可以指定字符集
new PrintWriter(“abc.txt”,“utf-8”); -
当做过滤流使用的时候 构造方法第二个参数可以指定自动清空缓冲
new PrintWriter(new FileWriter(“abc.txt”,true),true);
第二个true 代表 autoFlush -
println() = write() + newLine()
-
所有的Java程序员都对它非常熟悉 因为HelloWorld就在用它弟弟
System.out的这个out属性 是PrintStream类型的
PrintStream和PrintWriter用法完全相同…
综上所述: 在以行为单位的写出文本文件的时候,我们程序员不会使用BufferedWriter 而会使用更强大的PrintWriter
例题:请用程序统计 src目录当中,总共有多少个.java文件所有的.java文件总共有多少行,其中Josh Bloch开发的类有多少个,其中 关键字:public / void / return / class 出现的次数*: 请问所有的.java当中 有效代码多少行…将所有的统计结果 输出到:报告.txt 文件当中
import java.io.*;
import java.util.*;
public class BigThree{
static int javacount;//java文件数量
static int joshcount;//josh Bloch开发的类数量
static int validcount;//有效代码行数(除了注释)
static int linecount;//代码总行数
static Map<String,Integer> map=new HashMap<>();//集合里面放关键词以及出现的数量
static{
map.put("public",0);//默认值都赋值为0
map.put("void",0);
map.put("class",0);
map.put("return",0);
}
public static void main(String[] args)throws Exception{
File dir=new File("D:\\src");
search(dir);
try(PrintWriter pw=new PrintWriter("报告.txt")){
pw.println("Java文件的数量:"+javacount);
pw.println("代码总行数:"+linecount);
pw.println("有效代码总行数:"+validcount);
pw.println("Josh Blosh开发的类有:"+joshcount);
pw.println("你关心的关键字出现的次数如下:");
map.forEach((k,v)->pw.println(k+":"+v));
}catch(Exception e){
e.printStackTrace();
}
}
//定义一个扫描文件的的方法
public static void search(File f){
File[] ds=f.listFiles((x)->x.isDirectory());
File[] js=f.listFiles((x)->x.isFile()&&x.getName().toLowerCase().endsWith(".java"));
if(ds==null)return;
for(File d:ds){
search(d);
}
//Java文件的数量就等价于js数组的大小
javacount+=js.length;
for(File j:js){
boolean isvalid=true;//定义一个布尔变量用来统计/*和*/
try(BufferedReader br=new BufferedReader(new FileReader(j))){
String str;
while((str=br.readLine())!=null){
//每读一行代码行数+1
linecount++;
if(str.contains("Josh Bloch")){
//每一行包含Josh Boch 计数器+1
joshcount++;
}
//排除空行和单行注释
if(str.trim().startsWith("//")||str.trim().length()==0){
continue;
}
if(str.trim().startsWith("/*")){
isvalid=false;
}
//当布尔值为true时,即多行注释结束时,有效代码数量+1
if(isvalid){
validcount++;
}
if(str.trim().endsWith("*/")){
isvalid=true;
}
//遍历map集合里面的主键,每当str包含主键,则改变他对应处出现的次数
Set<String> set=map.keySet();
for(String s:set){
while(str.contains(s)){
map.put(s,map.get(s)+1);
str = str.replaceFirst(s,"!");.//改变值之后将str中的关键字替换掉,避免一行出现多个关键词,漏记现象
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
import java.io.*;
/*
2nd.你存所有的课件的目录知道在哪里hang
请找出当中所有的focus.txt
将其内容汇总写出到一个叫all.txt的文件中
*/
import java.io.*;
import java.util.*;
public class BigOne{
public static void main(String[] args)throws Exception{
File dir = new File("C:\\Users\\5v\\Desktop\\2106");
File[] fs = dir.listFiles((x) -> x.isDirectory());
List<File> list = new ArrayList<>();
Collections.addAll(list,fs);
Collections.sort(list,(a,b) -> {
String name1 = a.getName();
Integer i1 = getNumber(name1);
String name2 = b.getName();
Integer i2 = getNumber(name2);
return i1.compareTo(i2);
});
PrintWriter pw = new PrintWriter("../all.txt");
for(File d : list){
pw.println("============= "+d.getName()+" ==============");
File focus = new File(d,"focus.txt");
if(focus.exists()){
BufferedReader br = new BufferedReader(new FileReader(focus));
String str;
while((str = br.readLine())!=null){
if(str.trim().length()!=0){
pw.println(str);
}
}
br.close();
}
pw.println("========================");
pw.println();
pw.println();
}
pw.close();
}
//我们想要这个方法传入
//day22 [内部类] 返回22
//day1 返回1
public static Integer getNumber(String str){
int end = str.length();
if(str.contains(" "))
end = str.indexOf(" ");
String ok = str.substring(3,end);
return Integer.parseInt(ok);
}
}
URL:统一资源定位符
InputStreamReader:桥转换器
OutputStreamWriter:桥转换器
RandomAccessFile:随机访问文件
从网站上下载照片并重命名
import java.net.*;
import java.io.*;
public class TestURL2{
public static void main(String[] args)throws Exception{
URL url=new URL("http://www.etoak.com/assets/images/wlcs.png");
URLConnection uc=url.openConnection();
InputStream in=uc.getInputStream();//得到数据流
FileOutputStream fos=new FileOutputStream("wang.png");
byte[] data =new byte[65536];
int len;
while((len=in.read(data))!=-1){
fos.write(data,0,len);
}
in.close();
fos.close();
}
}
从网站上拔取数据,统计员工的平均工资
import java.net.*;
import java.io.*;
import java.util.*;
public class TestURL{
public static void main(String[] args)throws Exception{
URL url=new URL("http://www.etoak.com/assets/etoak.json");
URLConnection uc=url.openConnection();
int total=uc.getContentLength();
InputStream in=uc.getInputStream();//得到数据流
//桥转换器 = 字节流转换成字符流的工具
InputStreamReader isr=new InputStreamReader(in,"utf-8");
BufferedReader br=new BufferedReader(isr);
String str;
List<Integer> list=new ArrayList<>();
while((str=br.readLine())!=null){
if(str.trim().length()>5){
String nohead=str.substring(str.indexOf("¥")+"¥".length());
String ok=nohead.substring(0,nohead.indexOf("\""));
//System.out.println(ok);
if(ok.replaceAll("[0-9]","").length()==0){
int ok1=Integer.parseInt(ok);
list.add(ok1);
}else{
ok=ok.substring(0,ok.indexOf("/"));
int salary=Integer.parseInt(ok)*30;
list.add(salary);
}
}
}
int sum=0;
for(Integer i:list){
sum+=i;
}
System.out.println(sum/list.size());
in.close();
}
}
带进度条的下载
import java.net.*;
import java.io.*;
public class TestURL2{
public static void main(String[] args)throws Exception{
URL url = new URL("https://vue.etoak.com/static/video/video/four.mp4");
URLConnection uc = url.openConnection();
InputStream is = uc.getInputStream();
FileOutputStream fos = new FileOutputStream("etoak.mp4");
int total = uc.getContentLength();//资源的总共大小
int current = 0;
int last = -1;
byte[] data = new byte[2<<20];
int len;
while((len = is.read(data))!=-1){
fos.write(data,0,len);
current += len;
//current 占 total 的%
int percent = (int)(current * 100L / total) ;
if(last != percent){
System.out.print("\r已经完成: "+percent+" %");
last = percent;
}
}
fos.close();
is.close();
}
}
把磁盘空间占满·
import java.io.*;
public class TestRandomAccessFile{
public static void main(String[] args)throws Exception{
File dir = new File("d:\\");
long free = dir.getFreeSpace();
RandomAccessFile raf = new RandomAccessFile("d:\\jay.mp4","rw");
raf.setLength(free);
raf.close();
}
}