1.什么是IO流?
答:文件的输入和输出
将文件放入内存的过程叫输入(Input)也称为读(Read),
输入过程产生的数据的流动叫输入流(InputStream)。
将文件从内存 存放到硬盘的过程叫输出(Output)也称为写(Write),
输出过程产生的数据的流动叫输出流(OutputStream)。

通过IO可以完成硬盘文件的读和写。
2.IO流的分类
2.1按照流的方向进行分类:
以内存作为参照物
往内存中去,叫做输入(Input),或者叫做读(Read)
从内存出来,叫做输出(Output),或者叫做写(Write)
2.2按照读取数据方式不同:
按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型都可以读取。包括:文本文件、图片、音频文件、视频文件......
这种流叫字节流。
假设文件filel.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到‘a’
第二次读:一个字节,正好读到‘中’字符的一半
第三次读:一个字节,正好读到‘中’字符的另一半
按照字符的方式读取数据,一次读取一个字符。这种流是为了方便读取普通的文本文件而存在的,不能读取:图片、音频、视频等文件。只能读取纯文本文件。(work文件有格式,不是普通文件.txt,故word文件也无法读取。注意能用记事本编辑的都是文本文件)
这种流叫字符流。
假设文件filel.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字符,正好读到‘a’
第二次读:一个字符,正好读到‘中’字符
第三次读:一个字符,正好读到‘国’字符
‘a’英文字母,在Windows操作系统中占1个字节;但是‘a’字符在java中占用2个字节。
‘中’字符在Windows系统中占用2个字节。
综上所述
流的分类:
输入流、输出流
字节流、字符流
3.Java IO流
java中所有流都是在java.io.*下
主要研究如何new流对象,调用流对象的哪个方法是读,哪个方法是写。
Java IO四大家族(都是抽象类)
java.io.InputStream:字节输入流
java.io.OutputStream:字节输出流
java.io.Reader:字符输入流
java.io.Writer:字符输出流
在java中只要类名以Stream结尾都是字节流,以Reader/Writer结尾的都是字符流。
所有流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法。
养成好习惯:流是内存和硬盘之间的通道,用完后一定要关闭,不然会占用很多内存。
所有输出流都实现了:java.io.Flushable接口,都是可刷新的,都有flush()方法。
养成好习惯:输出流在最终输出之后,一定能更要flush()刷新一下。(表示将通道/管道当中剩余未输出的数据强行输出完,即清空管道。)
4.Java 需要掌握的16个IO流
文件专属
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter
转换流(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流
java.io.BufferedInputStream
java.io.BufferedOutputStream
java.io.BufferedReader
java.io.BufferedWriter
数据流
java.io.DataInputStream
java.io.DataOutputStream
标准输出流
java.io.PrinfWriter
java.io.PrintStream(掌握)
对象专属流
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
文件专属流
4.1 java.io.FileInputStream(重点掌握)
1.文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
2.字节的方式,完成输入的操作,完成读的操作(硬盘-->内存)

4.1.1 read()
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
try {
//创建文件字节流输入流对象
//文件路径:G:\大4上学期\实习\JavaSE\temp(IDEA会自动把\变成\\,因为java中\表示转译,把\\替换成/也可以。)
fileInputStream=new FileInputStream("G:\\大4上学期\\实习\\JavaSE\\temp");
//开始读
int read = fileInputStream.read();//这个方法的返回值是:读取到的“字节”本身
System.out.println(read);//97
read = fileInputStream.read();//98
System.out.println(read);
read = fileInputStream.read();//99
System.out.println(read);
read = fileInputStream.read();//100
System.out.println(read);
read = fileInputStream.read();//101
System.out.println(read);
read = fileInputStream.read();//102
System.out.println(read);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭字节数入流对象
if (fileInputStream != null) {
//关闭流的前提是:流不是空,流是null没必要关闭
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
问题:该例中使用read(),存在代码冗余,可采用循环的方式读取:
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
try {
//创建字节输出流对象
fileInputStream=new FileInputStream("G:\\大4上学期\\实习\\JavaSE\\temp");
//读
/*while (true){
int read=fileInputStream.read();
if (read == -1) {
break;
}
System.out.println(read);
}*/
//改造while循环
int read=0;
while((read=fileInputStream.read()) != -1){
System.out.println(read);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放字节输出流对象
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.1.2 read(byte[] b)
问题:read()使用循环读取后,存在效率低的问题。一次才读一个字节,硬盘与内存交互太频繁。改进方案:使用read(byte[] b)


注意:
开始读,采用byte数组,一次读取多个字节。最多读取“数组.length”个字节
这个方法的返回值:读到的字节数量,不是字节本身。
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
int read(byte[] b)
一次最多读取b.length个字节。
减少硬盘和内存的交互,提高程序的执行效率。
*/
public class FileInputStreamTest03 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
try {
//创建字节输出流对象
//使用相对路径,一定是当前所在的位置作为起点开始找!
//IDEA默认的当前路径:工程Project的根
fileInputStream=new FileInputStream("temp");
//开始读,采用byte数组,一次读取多个字节。最多读取“数组.length”个字节
byte[] bytes=new byte[4];
//这个方法的返回值:读到的字节数量,不是字节本身。
int read = fileInputStream.read(bytes);//第一次读到了4个字节
System.out.println(new String(bytes,0,read));//abcd
read = fileInputStream.read(bytes);//第二次读到了2个字节
System.out.println(new String(bytes,0,read));//ef
read = fileInputStream.read(bytes);//第三次读到了0个字节
System.out.println(read);//返回-1。
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意:
使用相对路径,一定是当前所在的位置作为起点开始找!
IDEA默认的当前路径:工程Project的根
问题:该例中使用String(byte[] bytes, int offset, int length),既减少了硬盘和内存的交互,也可以读多少就转多少,但未采用循环的方式读取。改进后:
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest04 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
try {
//声明字节输出流对象
fileInputStream=new FileInputStream("temp");
//准备一个byte数组
byte[] bytes=new byte[4];
int read=0;
while ((read = fileInputStream.read(bytes)) != -1){
System.out.print(new String(bytes,0,read));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileInputStream类的其他常用方法:
4.1.3 int available():
返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
即返回流当中剩余的没有读到的字节数量。
作用:创建数组时使用,不再需要循环。但不适合大文件,因为byte数组不能太大。
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest05 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
try {
fileInputStream=new FileInputStream("temp");
System.out.println("剩余未读字节:"+fileInputStream.available());
//直接获得剩余未读字节数,不再需要循环了
byte[] bytes=new byte[fileInputStream.available()];
int read = fileInputStream.read(bytes);
System.out.println(new String(bytes,0,read));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

4.1.4 long skip(long n):
从输入流中跳过并丢弃 n个字节的数据
即跳过几个字节不读。
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest05 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
try {
fileInputStream=new FileInputStream("temp");
fileInputStream.skip(42);
System.out.println("剩余未读字节:"+fileInputStream.available());
//直接获得剩余未读字节数,不再需要循环了
byte[] bytes=new byte[fileInputStream.available()];
int read = fileInputStream.read(bytes);
System.out.println(new String(bytes,0,read));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

4.2 java.io.FileOutputStream(重点掌握
1.文件字节输出流。
2.字节的方式,完成输出的操作,完成写的操作(内存-->硬盘)

4.2.1 write(byte[] b)
将 b.length 个字节从指定 byte 数组写入此文件输出流中。 |
注意:文件不存在会自动新建,存在则把原文件清空再写。
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream("myfile");
//开始写
byte[] bytes=new byte[]{97,98,99,100};
fileOutputStream.write(bytes);
//写完要刷新
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

4.2.2 write(byte[] b,int off,int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
注意:文件不存在会自动新建,存在则把原文件清空再写。
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream("myfile");
//开始写
byte[] bytes=new byte[]{97,98,99,100};
fileOutputStream.write(bytes,0,2);
//写完要刷新
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}


4.2.3 FileOutputStream(String name, boolean append)
创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。创建一个新 FileDescriptor 对象来表示此文件连接。
注意:这个方法可以以追加的方式在文件末尾写入,不会清空原文件内容。
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fileOutputStream=null;
try {
//以追加的方式写,不会清空源文件内容
fileOutputStream=new FileOutputStream("myfile",true);
//开始写
byte[] bytes=new byte[]{97,98,99,100};
fileOutputStream.write(bytes);
//写完要刷新
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

写字符串
要点:
1.定义一个字符串。String s= “内容”;
2.将字符串转成byte数组。s.getBytes()
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream("myfile",true);
String s="今天天气真不错!";
//将字符串转换成byte数组
byte[] bytes = s.getBytes();
//开始写
fileOutputStream.write(bytes);
//写完要刷新
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

文件拷贝
使用FileInputStream+FileOutputStream完成文件的拷贝。
拷贝的过程是一边读一边写。
使用以上字节流可拷贝任意类型的文件。

package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
使用FileInputStream+FileOutputStream完成文件的拷贝。
拷贝的过程是一边读一边写。
使用以上字节流可拷贝任意类型的文件。
*/
public class Copy01 {
public static void main(String[] args) {
FileInputStream fileInputStream=null;
FileOutputStream fileOutputStream=null;
try {
//创建一个字节输入流对象
fileInputStream=new FileInputStream("temp");
//创建一个字节输出流对象
fileOutputStream=new FileOutputStream("tempCopy");
//一边读一边写
byte[] bytes=new byte[1024 * 1024];// 一次最多拷贝1MB
int read=0;
//while条件里开始读
while ((read=fileInputStream.read(bytes)) != -1){
//while方法体内写
fileOutputStream.write(bytes,0,read);
}
//输出流最后要刷新
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//分开try,一起try时,其中一个出异常会影响另一个流的关闭。
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

4.3 java.io.FileReader
文件字符输入流,只能读取普通文本。
读取文本内容时,比较方便快捷。
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fileReader=null;
try {
//创建字符输入流
fileReader=new FileReader("temp");
//开始读
char[] chars=new char[4];//一次读取4个字符
int readCount=0;
while ((readCount=fileReader.read(chars))!= -1){
System.out.print(new String(chars,0,readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.4 java.io.FileOutputStream
文件字符输出流,写。
只能输出普通文本。

package com.bjpowernode.java.io;
import java.io.FileWriter;
import java.io.IOException;
/*
文件字符输出流,写
只能输出普通文本。
*/
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fileWriter=null;
try {
//创建字符输出流对象
fileWriter=new FileWriter("temp",true);
//开始写
char[] chars={'我','爱','你','中','国'};
fileWriter.write(chars);//可写数组、字符、字符串
fileWriter.write(chars,2,3);
fileWriter.write("\n");//换行符
fileWriter.write('善');//可写数组、字符、字符串
fileWriter.write("我是一个java软件工程师");//可写数组、字符、字符串
//刷新
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
复制文件
使用FileReader FileWriter进行拷贝的话,只能拷贝“普通文本”文件。
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
使用FileReader FileWriter进行拷贝的话,只能拷贝“普通文本”文件。
*/
public class Copy02 {
public static void main(String[] args) {
FileReader fileReader=null;
FileWriter fileWriter=null;
try {
//创建字符输入流对象,负责读的
fileReader=new FileReader("temp");
//创建字符输出流对象,负责写的
fileWriter=new FileWriter("tempCopy2");
//一边读一边写
char[] chars=new char[1024 * 512];//每次最多读1MB
int readCount=0;
while ((readCount=fileReader.read(chars))!= -1){
fileWriter.write(chars);
}
//刷新
fileWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

缓冲流
4.5 java.io.BufferedReader:
1.带有缓冲区的字符输入流
2.使用这个流的时候不需要自定义char数组或byte数组,自带缓冲。

注意:
1. 当一个流的构造方法中需要传一个流时,这个被传进来的流被称为“节点流”
2. 外部负责包装的流,叫做“包装流”或“处理流”
3. 对于包装流来说,只需要关闭最外层包装流就行,里面的节点流会自动关闭。

package com.bjpowernode.java.io;
import java.io.BufferedReader;
import java.io.FileReader;
/*
BufferedReader
带有缓冲区的字符输入流
使用这个流的时候不需要自定义char数组或byte数组,自带缓冲。
*/
public class BufferedReaderTest01 {
public static void main(String[] args) throws Exception{
FileReader fr=new FileReader("temp");
//当一个流的构造方法中需要传一个流时,这个被传进来的流被称为“节点流”
//外部负责包装的流,叫做“包装流”或“处理流”
//像当前这个程序来说:节点流是FileReader,包装流是BufferedReader
BufferedReader br=new BufferedReader(fr);
//利用readLine循环读
String firstLine2=null;
while ( (firstLine2=br.readLine()) != null ){
System.out.println(firstLine2);
}
//对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。
br.close();
}
}
4.6 java.io.BufferedWriter:
1.带有缓冲区的字符输出流
2.使用这个流的时候不需要自定义char数组或byte数组,自带缓冲。

package com.bjpowernode.java.io;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterTest {
public static void main(String[] args) {
try {
//创建带有缓冲区的字符输出流对象
BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter("temp"));
//写
bufferedWriter.write("hello world!");
bufferedWriter.write("\n");
bufferedWriter.write("hello kitty!");
//刷新
bufferedWriter.flush();
//关闭
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

转换流
4.7 java.io.InputStreamReader

package com.bjpowernode.java.io;
import java.io.*;
public class BufferedReaderTest02 {
public static void main(String[] args) {
try {
//创建字节输入流对象
FileInputStream in=new FileInputStream("temp");
//使用转换流将字节流转换成字符流再传入BufferedReader中。
//这里in是“节点流”,reader是“包装流”
InputStreamReader reader=new InputStreamReader(in);
//因为BufferedReader只能传字符流对象,不能传字节流对象,故需要用转换流先转换一下。
//这里reader是“节点流”,br是“包装流”
BufferedReader br=new BufferedReader(reader);
//开始读
String line=null;
while( (line = br.readLine()) != null){
System.out.println(line);
}
//关闭最外层
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
简化代码后
package com.bjpowernode.java.io;
import java.io.*;
public class BufferedReaderTest02 {
public static void main(String[] args) {
try {
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("temp")));
//开始读
String line=null;
while( (line = br.readLine()) != null){
System.out.println(line);
}
//关闭最外层
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.8 java.io.OutputStreamWriter

package com.bjpowernode.java.io;
import java.io.*;
public class BufferedWriterTest {
public static void main(String[] args) {
try {
//先创建字节输出流,再使用转换流创建带有缓冲区的字符输出流对象
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("temp",true)));
//写
bufferedWriter.write("大家好!");
bufferedWriter.write("\n");
bufferedWriter.write("新年快乐!");
//刷新
bufferedWriter.flush();
//关闭
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

数据流
4.9 java.io.DataInputStream
DataOutputStream写的文件,只能使用DataInputStream去读。
注意:读顺序需要和写的顺序一致,才能正常取出数据。

package com.bjpowernode.java.io;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
DataInputStream:数据字节输入流
DataOutputStream写的文件,只能使用DataInputStream去读。并且读顺序需要和写的顺序一致,才能正常取出数据。
*/
public class DataInputStreamTest01 {
public static void main(String[] args) {
DataInputStream dataInputStream=null;
try {
//创建数据专属的字节输入流对象
dataInputStream=new DataInputStream(new FileInputStream("temp"));
//读
byte b=dataInputStream.readByte();
short s=dataInputStream.readShort();
int i =dataInputStream.readInt();
long l=dataInputStream.readLong();
float f=dataInputStream.readFloat();
double d =dataInputStream.readDouble();
boolean sex =dataInputStream.readBoolean();
char c=dataInputStream.readChar();
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(sex);
System.out.println(c);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.10 java.io.DataOutputStream
这个流可以将数据连同数据的类型一并写入文件
注意:这个文件不是普通文本文档。(用记事本打不开,要去数据得用DataInputStream)

package com.bjpowernode.java.io;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
java.io.DataInputStream:数据专属的流
这个流可以将数据连同数据的类型一并写入文件
注意:这个文件不是普通文本文档。(用记事本打不开)
*/
public class DataOutputStreamTest {
public static void main(String[] args) {
DataOutputStream dos=null;
try {
//创建数据专属的字节输出流
dos=new DataOutputStream(new FileOutputStream("temp"));
//写数据
byte b=100;
short s=200;
int i =300;
long l=400L;
float f=3.0F;
double d =3.14;
boolean sex =false;
char c='a';
//把数据以及数据的类型一并写入到文件当中
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(sex);
dos.writeChar(c);
//刷新
dos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭最外层
if (dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
标准输出流
4.11 java.io.PrinfWriter
4.12 java.io.PrintStream(重点掌握)
标准的字节输出流,默认输出到控制台。
标准输出流不需要手动close()关闭。
package com.bjpowernode.java.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) throws Exception {
//联合起来写
System.out.println("hello world!");
//分开写
PrintStream ps=System.out;
ps.print("hello zhangsan");
//标准输出流不再指向控制台,指向temp文件
PrintStream printStream=new PrintStream(new FileOutputStream("tempCopy"));
//修改输出方向,将输出方向修改到“temp”文件
System.setOut(printStream);
//再输出
System.out.println("hello world!");
System.out.println("hello kitty");
System.out.println("hello zhangsan");
}
}
日志工具
package com.bjpowernode.java.io;
import javax.xml.crypto.Data;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
日志工具
*/
public class LogUtil {
/*
记录日志的方法
*/
public static void log(String msg){
try {
//指向一个日志文件
PrintStream out = new PrintStream(new FileOutputStream("log.txt",true));
//改变输出方向
System.setOut(out);
//日期当前时间
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();
}
}
}
package com.bjpowernode.java.io;
public class logTest {
public static void main(String[] args) {
//测试工具类是否好用
LogUtil.log("调用了System类的gc()方法,建议启动垃圾回收");
LogUtil.log("调用了UserService的doSome()方法");
LogUtil.log("用户尝试进行登录,验证失败");
LogUtil.log("我非常喜欢这个记录日志的工具哦!");
}
}

对象专属流

4.13java.io.ObjectInputStream(重点掌握)
序列化:
package com.bjpowernode.java.bean;
public class Student {
private int no;
private String name;
//省略构造方法、setter和getter方法、toString方法
package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws Exception{
//创建java对象
Student s=new Student(1111,"zhangsan");
//序列化
ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("student"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}

分析:直接创建一个Student类,不支持序列化。Student类需要继承Serializable可序列化接口。
package com.bjpowernode.java.bean;
public class Student implements Serializable {
private int no;
private String name;
//省略构造方法、setter和getter方法、toString方法

总结(重要):参与序列化和反序列化的对象需要实现Serializable可序列化接口。
(Serializable接口什么代码都没有,但它起标识的作用。java虚拟机看到这个类实现了这个接口,会为该类自动生成一个序列化版本号。)
序列化版本号:
Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。
Java语言中通过什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类;
第二:如果类名一样,靠序列化版本号进行区分。
好处:
因为类只要实现了Serializable接口,就能自动生成序列化版本号,Java虚拟机可以根据序列化版本号的不同,区分类。
缺陷:
一个类在序列化后,会生成一个序列化版本号。若未来这个类有修改,会生成一个新的序列化版本号。虽然从业务逻辑角度上还是同一个类,但前后序列化版本号不一致,Java虚拟机会认为是两个类。
一旦代码确定之后,不能进行后续的修改。因为只要修改必然会重新编译,此时会生成全新的序列化版本号,Java虚拟机会认为这是一个全新的类。
总结:
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号(即不使用自动生成序列化版本号)。
例如:private static final long seralVersionUID =11;
IDEA快捷生成序列化版本号:




一次性序列化多个对象:
将对象放到集合中,序列化集合
直接存储多个对象,第二个就会报错
package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.User;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
//参与序列化的ArrayList集合以及集合中的元素User都需要实现java.io.Serializable接口
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws Exception{
//准备一个List集合
List<User> userList=new ArrayList<>();
userList.add(new User(1,"zhangsan"));
userList.add(new User(2,"lisi"));
userList.add(new User(3,"wangwu"));
//序列化
ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("users"));
//一次序列化一个集合对象,这个集合对象中存放了很多其他对象
oos.writeObject(userList);
//关闭
oos.close();
}
}
package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.User;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
/*
反序列化集合
*/
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws Exception{
//反序列化
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("users"));
//反序列化对象
List<User> userList = (List<User>) ois.readObject();
for (User user:userList
) {
System.out.println(user);
}
//关闭
ois.close();
}
}

若想指定某个属性不参与序列化,使用transient关键字修饰指定属性。👇
package com.bjpowernode.java.bean;
import java.io.Serializable;
public class User implements Serializable {
private int no;
//transient关键字标识游离的,不参与序列化
private transient String name;
//省略setter和getter、toString、构造方法。
}

4.14 java.io.ObjectOutputStream(重点掌握)
反序列化
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws Exception{
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("student"));
//开始反序列化,读
Object o = objectInputStream.readObject();
//反序列化回来是一个学生对象,所以会调用学生对象的toString方法
System.out.println(o);
//关闭
objectInputStream.close();
}
}

4.15 java.io.File类

1.File类和四大家族没有关系,所以FIle类不能完成文件的读和写
2.File对象代表什么?
:文件和目录路径名的抽象表示形式
C:\fitnesse-src-20161106 是一个File对象
C:\fitnesse-src-20161106\gradlew.bat 也是File对象
一个File对象有可能对应的是目录,也可能是文件。
3.需要掌握File类的常用方法:
exists():测试此抽象路径名表示的文件或目录是否存在。
createNewFile():当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
mkdir():创建此抽象路径名指定的目录。
mkdirs():创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
getParent():返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。(返回String类型)
getParentFile():返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。(返回File类型)
getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
getName():返回由此抽象路径名表示的文件或目录的名称。
isDirectory():测试此抽象路径名表示的文件是否是一个目录。
isFile():测试此抽象路径名表示的文件是否是一个标准文件。
lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。(返回值是long类型,表示从1970年到现在的总毫秒数。)
length():返回由此抽象路径名表示的文件的长度(即文件的大小)。
length():返回由此抽象路径名表示的文件的长度(即文件的大小)。
listFiles():返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
package com.bjpowernode.java.io;
import java.io.File;
import java.io.IOException;
public class FileTest01 {
public static void main(String[] args) throws Exception {
/*File file=new File(("G:\\大4上学期\\实习\\JavaSE\\chapter23\\src\\com\\bjpowernode\\java\\io\\file"));
//判断文件是否存在
System.out.println(file.exists());*/
/*//如果G:\大4上学期\实习\JavaSE\chapter23\src\com\bjpowernode\java\io\file不存在,则以文件的形式创建出来
if (!file.exists()) {
file.createNewFile();
}*/
/* //如果G:\大4上学期\实习\JavaSE\chapter23\src\com\bjpowernode\java\io\file不存在,则以目录的形式创建出来
if (!file.exists()) {
file.mkdir();
}*/
/*File file2=new File(("G:\\大4上学期\\实习\\JavaSE\\chapter23\\src\\com\\bjpowernode\\java\\io\\file\\a\\b\\c\\d"));
if (!file2.exists()) {
//以多重目录的形式新建
file2.mkdirs();
}*/
File file3=new File(("G:\\大4上学期\\实习\\JavaSE\\chapter23\\src\\com\\bjpowernode\\java\\io\\file"));
//获取文件的父路径
String parentPath=file3.getPath();
System.out.println(parentPath);
File parentFile=file3.getParentFile();
System.out.println("获取绝对路劲:"+parentFile.getAbsolutePath());
File file4=new File("temp");
System.out.println("获取绝对路径:"+file4.getAbsolutePath());
}
}
package com.bjpowernode.java.io;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
File类的常用方法
*/
public class FileTest02 {
public static void main(String[] args) {
File f1=new File("G:\\大4上学期\\实习\\JavaSE\\chapter23\\src\\com\\bjpowernode\\java\\io\\BufferedReaderTest01.java");
//获取文件名
System.out.println("文件名:"+f1.getName());
//判断是是否是一个目录
System.out.println(f1.isDirectory());//false
//判断是否是一个文件
System.out.println(f1.isFile());//true
//获取文件最后一次修改时间
long haoMiao=f1.lastModified();//这个毫秒是从1970年到现在的总毫秒数。
Date time=new Date(haoMiao);
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime=sdf.format(time);
System.out.println(strTime);
//获取文件大小
System.out.println(f1.length());//1054
}
}
package com.bjpowernode.java.io;
import java.io.File;
/*
File中的listFIles方法
*/
public class FileTest03 {
public static void main(String[] args) {
//File[] listFiles()
//获取当前目录下所有的子文件
File f=new File("G:\\大4上学期\\实习\\JavaSE\\chapter23\\src\\com\\bjpowernode\\java\\io");
File[] files = f.listFiles();
//foreach
for (File file :files) {
System.out.println(file.getAbsolutePath());
System.out.println(file.getName());
}
}
}
目录拷贝(非常具有代表性)
题目:拷贝一个目录到一个地方
解题思路:
递归
FileInputStream
FileOutputStream
字符串拼接
boolean endsWith(String suffix):
测试此字符串是否以指定的后缀结束。
package com.bjpowernode.java.io;
import java.io.File;
/*
测试:拷贝一个目录到一个地方
*/
public class work {
public static void main(String[] args) {
CopyUtil copyUtil=new CopyUtil();
//指定拷贝源
File srcFile=new File("G:\\大4上学期\\实习\\JavaSE\\chapter23\\src\\com");
//拷贝目标
File destFile=new File("G:\\a\\");
//调用方法拷贝
copyUtil.copy(srcFile,destFile);
}
}
package com.bjpowernode.java.io;
import java.io.*;
/*
inPath:拷贝源
outPatch: 拷贝目标
*/
public class CopyUtil {
private static FileInputStream fi=null;
private static FileOutputStream fo=null;
public void copy(File inPath, File outPath) {
if (inPath.isFile()){
//是文件的时候需要拷贝,一边读一边写
try {
//读文件
fi=new FileInputStream(inPath);
//写文件
String path=(outPath.getAbsolutePath().endsWith("\\") ? outPath.getAbsolutePath(): outPath.getAbsolutePath()+"\\") +inPath.getAbsolutePath().substring(12);
fo=new FileOutputStream(path);
//一边读一边写
byte[] bytes=new byte[1024*1024];
int readCount=0;
while ( (readCount=fi.read()) !=-1){
fo.write(bytes,0,readCount);
}
//刷新
fo.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fi != null) {
try {
fi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fo != null) {
try {
fo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//inPath如果是一个文件的话,递归结束
return;
}
//获取源下面的子目录
File[] files=inPath.listFiles();
for (File file:files
) {
//是目录的时候创建目录
if (file.isDirectory()) {
String srcDir=file.getAbsolutePath();
/*System.out.println(srcDir.substring(12));*/
String desDir= (outPath.getAbsolutePath().endsWith("\\") ? outPath.getAbsolutePath(): outPath.getAbsolutePath()+"\\") +srcDir.substring(12);
/*System.out.println(desDir);*/
File newFile=new File(desDir);
if (!newFile.exists()) {
newFile.mkdirs();
}
}
//递归调用
copy(file,outPath);
}
}
}
5. IO流+Properties集合的联合使用
IO流:文件的读和写。
Properties:是一个Map集合,key和value都是String类型。

package com.bjpowernode.java.io;
import java.io.FileReader;
import java.util.Properties;
/*
IO+Properties的联合应用
非常好的设计理念:
以后要是经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
将来只要修改这个文件的内容,java代码不需要改动,不需要重新编译。
服务器也不需要重启,就可以拿到动态的信息。
*/
public class IoPropertiesTest {
public static void main(String[] args) throws Exception{
/*
Properties是一个Map集合,key和value都是String类型。
想将userinfo文件中的数据加载到Properties对象当中。
*/
//新建输入流对象
FileReader reader=new FileReader("userinfo");
//新建一个Map集合
Properties pro=new Properties();
//调用Properties对象的load方法将文件的数据加载到Map集合中。
pro.load(reader);//文件中的数据顺着管道加载到Map集合中
//通过key来获取value呢?
String username = pro.getProperty("username");
System.out.println(username);
String password = pro.getProperty("password");
System.out.println(password);
}
}

