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);
}
}