什么是IO流?
I :input
O :output
通过io就可以完成硬盘文件的读写。
IO 流的分类?
-
一种方式是按照流的方向进行分类。
以内存作为参照物,
往内存中去,叫做输入(input),或者叫做读(Read)。
从内存中出来,叫做输出(Output),或者叫做写(Write)。
-
另一种方式是按照读取数据的方式不同进行分类。
(1)按照字节读取数据
一次性读取一个字节 byte ,等同于一次性读取8个二进制。这种流是万能的,什么类型的文件都能读 取,包括:文本文件、图片、声音文件、视屏。
举例; 文件 filel.txt,采取字节流的方式读取。
a我爱bc中d国
第一次读:一个字节,到‘ a’
第二次读:一个字节,正好读到 ‘我’ 字符的一半
第三次读:一个字节,正好读到 ‘我’ 字符的另一半
(2)按照字符读取数据
一次读取一个字符,这种流是为了读取普通文本文件而存在的,不能读取图片、视频、音乐等文件。只能读取纯文本文件,连word都不行。
举例; 文件 filel.txt,采取字节流的方式读取。
a我爱bc中d国
第一次读:’ a’ 字符
第二次读:'我’字符。
综上所述,流的分类;
输入流、输出流
字节流、字符流
java中的流都已经写好了,java所有的流都在 java.io.* ;下
java IO 流四大家族
java.io.InputStream ; 字节输入流
java.io.OutputStream ; 字节输出流
java.io.Reader ; 字符输入流
java.io.Writer ; 字符输出流
都是抽象类 。
所有的流都实现了java.io.Closeable
接口,都有close()
方法,都是可关闭的,流相当于内存和硬盘之间的管道用完后要及时关闭,否则会占用很多资源。
所有的输出流都实现了java.io.Flushable
接口,都是可刷新的,都有flush()
方法表示将通道当中剩余未输出的数据强行输出完(清空管道),输出流在最终输出之后一定要记得刷新一下清空管道。(如果没有flush()
可能会导致丢失数据)
注意:在 java 中只要类名以stream
结尾的都是字节流;以 Reader/Writer 结尾的都是字符流。
java.io.* 中需要掌握的流
-
文件专属
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter
-
转换流(将字节流转换为字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
-
数据流专属
java.io.DataInputStream
java.io.DataOutputStream
-
标准输出流
java.io.PrintStream
java.io.PrintWriter(掌握)
-
对象专属流
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
字节流读取文件
(一个一个的读)
代码演示:
/*
java.io.FileInputStream
文件字节输入流,万能的。任何类型的文件都可以用这个读。
*/
public static void main(String[] args) {
FileInputStream q1 = null;
try {
q1 = new FileInputStream("test.txt"); //存储内容是; a
int readDate = q1.read();
System.out.println(readDate);//97 ,输出为ascii值
} catch (FileNotFoundException e) {
// FileNotFoundException 是 IOException 的子类。所以只写catch IOException 也可以。
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流。前提是 流不为空。当流是null,没有必要关闭,也是避免空指针异常。
if (q1 != null) {
try {
q1.close();
} catch (IOException e) {
e.printStackTrace();}}}}}
-
使用数组读
public static void main(String[] args) { FileInputStream fls = null; try { //idea 默认都当前路径在哪里? (默认读文件的文件位置) fls = new FileInputStream("test.txt"); //文件内容;abcdef //采用byte读取字节,一次读取多个字节,最多读取 "数组.length" 个字符 byte [] bytes = new byte[4]; //准别一个 4 长度的a数组,一次最多读取4字符 int readTxt = fls.read(bytes); //这个方法返回的是 读取的字节数量。 System.out.println(readTxt); //第一次读到了四个字节,输出4 //System.out.println(new String(bytes)); //abcd 。别忘了引用包,java.lang.String; System.out.println(new String(bytes,0,readTxt)); //(idea此时不能自动引包). //表示从数组bytes的第0个下标开始,读取长度为readTxt的字节数组 readTxt = fls.read(bytes); //第二次只能读取两个字节,输出2, 后两个字节覆盖前两个数组。 System.out.println(readTxt); //System.out.println(new String(bytes)); //efcd System.out.println(new String(bytes,0,readTxt)); //readTxt = fls.read(); //第三次一个字节都没有读到,返回-1。 因为后面没有数据了。 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fls != null){ try { fls.close(); } catch (IOException e) { e.printStackTrace(); } }}}}
此时可以按照设定好的byte数组长度进行读取文件, 一次读取四个字节。
-
如何读取文件中所有的字符?
public static void main(String[] args) { FileInputStream fls = null; try { fls = new FileInputStream("/Users/caoxingxing/Desktop/test.txt"); /*byte [] bytes = new byte[4]; while (true){ int readCount = fls.read(bytes); if (readCount == -1){ break; } System.out.print(new String(bytes,0,readCount)); //如果连续输出字符,去ln。 }*/ byte [] bytes = new byte[4]; int readCount = 0; while ((readCount = fls.read(bytes)) != -1){ //改进后的while循环。 System.out.print(new String(bytes,0,readCount)); //一次读取4个字节,读取长度为readCount的数组 } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fls != null){ try { fls.close(); } catch (IOException e) { e.printStackTrace(); }}} }}
-
available ();方法
int read = fis.read(); System.out.println("剩余" + fis.available() + "字节"); //剩余多少字节
byte [] bytes = new byte[fis.available()]; //数组长度为字符床总长度 int read = fis.read(bytes); System.out.println(new String(bytes)); //不适用太大的文件,因为byte 有一定限制。
获取数组长度的字节。=获取全部字节并输出。
-
skip(); 跳过读取。
//skip跳过几个字节不读取。 public static void main(String[] args) { FileInputStream fls = null; try { fls = new FileInputStream("test.txt"); //文件内容:abcd fls.skip(2); System.out.println(fls.read()); //输出:cd } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (fls != null){ try { fls.close(); } catch (IOException e) { e.printStackTrace(); }}}}}
-
使用FileOutputStream写入文件
//文件字节输出流,负责写
//从内存到硬盘
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//以追加的方式在文件末尾写入,不会晴空源文件内容。文件末尾加true
fos = new FileOutputStream("myfile",true); //文件不存在时会自动重建,
// 后加“true”,每次写入后,不格式化文件。
//不加的话,每次写入文件后会清空文件 重新写入。
//例如;不加true,每一次程序运行,文件都是显示:123。
//加true后,程序运行两次,文件:123123
//开始写
//byte [] bytes = {97,98,99,100};
//fos.write(bytes); //abcd
//fos.write(bytes,0,2); //在写出ab,输出:abcdab
//写完之后记得刷新一下,不然看不到新写的信息、
fos.flush();
String s = "123";
byte [] bytes2 = s.getBytes();
fos.write(bytes2);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//打印添加 "s" 后的文件
FileInputStream fls = null;
try {
fls = new FileInputStream("myfile");
byte [] bytess = new byte[fls.available()];
int read = fls.read(bytess);
System.out.println(new String(bytess));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fls != null){
try {
fls.close();
} catch (IOException e) {
e.printStackTrace();
} } } }}
- 使用FileOutputStream 和 FileInputStream 拷贝文件
public static void main(String[] args) {
FileInputStream fls = null;
FileOutputStream fos = null;
//使用字节流可以拷贝 文本、视频、音频等。(目前只会拷贝单个文件,文件夹还不行)
try {
fls = new FileInputStream("我自己的io作业01");
fos = new FileOutputStream("/Users/caoxingxing/Desktop/test.txt",true);
//若不加true,每一次拷贝文件都会覆盖原文件内容。
byte [] bytes = new byte[4]; //一次四个字节
int readCount = 0;
while ((readCount = fls.read(bytes)) != -1){
fos.write(bytes,0,readCount);
}
//写入文件记得刷新。
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//连个close要分开处理异常(try、catch)。 不然可能会影响另一个流的关闭。
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (fls != null){
try {
fls.close();
} catch (IOException e) {
e.printStackTrace();
} }} }
FileReader
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("我自己的io作业01");
/*char [] chars = new char[4];
int readCount = 0;
while ((readCount = reader.read(chars)) != -1){
System.out.println(new String(chars,0,readCount));
}*/
//按字符的方式读取
char [] chars = new char[4]; //一次读取四个字节,也可以增加。例如:1024*512。
reader.read(chars);
for (char c : chars){ //遍历 chars,保存到c中。
System.out.println(c);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
} }} }}
FileWriter文件字符输出流
//文件字符输出流,负责写的
//只能输出普通文本。world文件不是普通文本。
public class FileWriterrr01 {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("我自己的io作业01",true); //append :追加。
char [] chars = {'豆','雷','楼','姆','˜'};
fw.write(chars); //文件增加chars的内容。
fw.write(chars,2,3); //写入数组下标为2-3的数据。
fw.write("🐒好猴"); //可写入String 类型。!!!!!!!所以FileWriter非常好用。
//一个emoji两个字符位。
fw.write("\n"); //写入的文件换行
fw.write("helloWorld");
fw.flush(); //写入文件一定记得刷新。
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}} } }}
使用FileWriter和FileReader复制文件
public static void main(String[] args) {
FileWriter out = null;
FileReader in = null;
try {
in = new FileReader("/Users/caoxingxing/Desktop/demo/idea的所有java/src/IOo/copy02.java");
out = new FileWriter("我自己的io作业01",true); //加true,写入文件后格式化文件。
char [] chars = new char[1024 * 512]; // 1mb
int readCount = 0;
while ((readCount = in.read(chars)) != -1){
out.write(chars,0,readCount);
System.out.print(new String(chars,0,readCount)); //打印更改后的文件
}
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) { //此处两个try、catch。
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}}} }}
- 只可以对普通文本读写,例如,txt、java等。
BufferedReader字符输入流
/*
BufferedReader是带有缓冲区的字符输入流。
使用这个流不需要自定义char数组,或者不用定义byte数组,自带缓冲。
*/
public class BufferReader01 {
public static void main(String[] args) throws Exception{
FileReader reader = new FileReader("我自己的io作业01");
//当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做,节点流
//外部负责包装这个流,叫做,包装流,也有另一个名子,叫处理流
//对于这个程序,FileReader是一个节点流,BufferedReader是一个包装流/处理流。
BufferedReader br = new BufferedReader(reader);
/* //读一行
String firstLine = br.readLine();
System.out.println(firstLine);
//读取文件第二行。
String secondLine = br.readLine();
System.out.println(secondLine);*/
String s = null;
while ((s = br.readLine()) != null){
System.out.println(s); //读取全部文件。
}
//关闭流
//对于包装流来数,只需要关闭最外层流即可,里面的节点流会自动关闭。
br.close();
}
}
转换流(将字节转换为字符)
InputStreamReader
/*
字节流转换为字符流。
*/
public static void main(String[] args) throws Exception{ //此处将异常统一处理,不需要try、catch。
//完整写法
//创建字节流
FileInputStream q1 = new FileInputStream("我自己的io作业01");
//字符流转换,InputStreamReader将字节流转换为字符流。
InputStreamReader q2 = new InputStreamReader(q1); //q1是节点流,q2是包装流。
//这个构造方法只能传一个字符流,不能传字节流
BufferedReader q3 = new BufferedReader(q2); //q2是节点流,q3是包装流。
//简便写法
//BufferedReader q = new BufferedReader(new InputStreamReader(new FileInputStream("我自己的io作业01")));
String s = null;
while ((s = q3.readLine()) != null) {
System.out.println(s);
}}}
“读” 的转换流
OutputStreamWriter
/*
BufferedWriter:带有缓冲的字符输出流
OutputStreamWriter:转换流
*/
public class BufferWriter01 {
public static void main(String[] args) throws Exception{
//BufferedWriter q1 = new BufferedWriter(new FileWriter("myfile")); //字符流
//
BufferedWriter q1 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("myfile")));
q1.write("fffff");
q1.write("啵啵鸡2");
q1.flush();
q1.close();
}}
”写“ 的转换流。
数据流DataInputStream & DataOutputStream
- DataOutputStream文件
/*
java.io.DataOutputStream:数据专属的流
这个流可以将数据连同数据的类型一起写入文件
这个文件不是普通文本文档
*/
public class DataOutputStreamTest01 {
public static void main(String[] args) throws Exception{
//创建数据专属的字节输出流
DataOutputStream dos = new DataOutputStream(new FileOutputStream("myfile2"));
byte a = 100;
short b = 200;
int c = 300;
long d = 400L;
float e = 3.0F;
double f = 3.14;
boolean sex = false;
char g = 'a';
dos.writeByte(a); //数据及数据类型一并写进去。
dos.writeShort(b);
dos.writeInt(c);
dos.writeLong(d);
dos.writeFloat(e);
dos.writeDouble(f);
dos.writeBoolean(sex);
dos.writeChar(g);
dos.flush();
dos.close();
}
}
-
DataInputStream文件
/* java.io.DataOutputStream:数据专属的流 这个流可以将数据连同数据的类型一起写入文件 这个文件不是普通文本文档.(可以理解为打不开、看不了) */ public class DataOutputStreamTest01 { public static void main(String[] args) throws Exception{ //创建数据专属的字节输出流 DataOutputStream dos = new DataOutputStream(new FileOutputStream("myfile2")); byte a = 100; short b = 200; int c = 300; long d = 400L; float e = 3.0F; double f = 3.14; boolean sex = false; char g = 'a'; dos.writeByte(a); //数据及数据类型一并写进去。 dos.writeShort(b); dos.writeInt(c); dos.writeLong(d); dos.writeFloat(e); dos.writeDouble(f); dos.writeBoolean(sex); dos.writeChar(g); dos.flush(); dos.close(); }} //输出: 100 200 1300 400 3.0 3.14 false a
PrintStream标准输出流
public class PrintStreamTest01 {
public static void main(String[] args) throws Exception{
//输出到控制台。标准输出流不需要手动 close()关闭。
//联合起来写
System.out.println("hahahah");
//分开写
PrintStream ps = System.out;
ps.println("ha");
ps.println("ha");
//改变输出方向
//输出流不再控制台输出,输出到文件"myfile"
PrintStream psw = new PrintStream(new FileOutputStream(" myfile3"));
//修改文件输出方向,到 文件" myfile"。
System.setOut(psw);
System.out.println("dddd");
}
}
- 记录日志的方法
public class Logger {
public static void log(String msg) {
try {
//指向一个日志文件
PrintStream ps = new PrintStream(new FileOutputStream("/Users/caoxingxing/Desktop/demo/idea的所有java/myfile4",true));
//改变输出方向
System.setOut(ps);
//日期当前时间
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(nowTime);
System.out.println(strTime + " :" + msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Logger.log("dddd");
Logger.log("avav");
Logger.log("dddd");
Logger.log("avav");
Logger.log("dddd");
Logger.log("avav");
Logger.log("dddd");
Logger.log("avav");
}
}
File类
File是文件和目录路径名的抽象表示形式。包含许多方法。
public static void main(String[] args) throws Exception{
File file = new File("/Users/caoxingxing/Desktop/test.txt");
//.getName() .获取文件名
System.out.println(file.getName());
//.isDirectory() .判断是否是一个目录
System.out.println(file.isDirectory());
//.isFile() .判断是否是一个文件
System.out.println(file.isFile());
//.lastModified() .获取最后一次修改时间
long time = file.lastModified(); //这个毫秒是从1970年到现在到总毫秒数
//将毫秒数转换成日期
Date time2 = new Date(time);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss SSS");
String time3 = sdf.format(time2);
System.out.println("最后一次更改时间:" + time3);
//获取文件大小
System.out.println(file.length()); //字节
}
}
//输出:
test.txt
false
true
最后一次更改时间:2020-27-07 22:27:31 249
45
.exists() 判断文件是否存在
public static void main(String[] args) {
File file = new File("/Users/caoxingxing/Desktop/test.txt");
System.out.println(file.exists());
}
//若当前文件存在,则返回true。若不存在返回false。
.getParent() 获取文件父类路径
public static void main(String[] args) throws Exception{
File file = new File("/Users/caoxingxing/Desktop/test.txt");
System.out.println(file.getParent()); //输出文件父路径:/Users/caoxingxing/Desktop
}
.getAbsolutePath() 获取绝对路径(完整路径)
public static void main(String[] args) throws Exception{
File file1 = file.getAbsoluteFile();
System.out.println(file1.getAbsolutePath()); //获取绝对路径,/Users/caoxingxing/Desktop/test04
}
.mkdirs()
创建此抽象路径名指定的目录。
//如果不存在,则已文件夹的形式创建
if (!file.exists()){
file.mkdirs();
}
.mkdir()
创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
//如果不存在,则已文件夹的形式创建
if (!file.exists()){
file.mkdir();
}
区别取决于
File file = new File("/Users/caoxingxing/Desktop/test.txt");
若路径下没有此文件,则创建“test.txt”文件夹,若包含多重目录,例如test.txt后又有文件名,则需要用.mkdirs。
既然不清楚要创建的是单个文件目录还是多重目录,直接用.mkdirs较合适。
.createNewFile();
//如果不存在,则已文件的形式创建
if (!file.exists()){
file.createNewFile();
}