文件通常是由一连串的字节或字符构成,组成文件的字节序列称为字节流,组成文件的字符序列称为字符流。Java中根据流的方向可以分为输入流和输出流。输入流是将文件或其他输入设备的数据加载到内存的过程;输出流恰恰相反,是将内存中的数据保存到文件或其他输出设备。
IO流的分类:
- 按照流的方向进行分类:以内存作为参照物,往内存中去,叫做输入(Input)。或者叫做读(Read)。从内存中出来,叫做输出(Output)。或者叫做写(Write)。
- 按照读取数据方式不同进行分类:有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等…有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
综上所述:流的分类 - 输入流、输出流
- 字节流、字符流
java.io
包下需要掌握的流有16个:
文件专属:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)
对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
02 IO流四大家族
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类。(abstract class)
-
所有的流都实现了:
java.io.Closeable
接口,都是可关闭的,都有close()
方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,
不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。 -
所有的输出流都实现了:
java.io.Flushable
接口,都是可刷新的,都有flush()
方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()
刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据
强行输出完(清空管道!)刷新的作用就是清空管道。
注意:如果没有flush()
可能会导致丢失数据。
注意:在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
2.1 InputStream(字节输入流)InputStream
是字节输入流,InputStream
是一个抽象类,所有继承了该类的都是字节输入流。
主要方法介绍:
void | close() 关闭此输入流并释放与该流关联的所有系统资源 |
---|---|
abstract int | read() 从输入流读取下一个数据字节 |
int | read(byte[] b) 从输入流中读取一定数量的字节并将其存储在缓冲区数组b中 |
int | read(byte[] b,int off,int len) 将输入流中最多len个数据字节读入字节数组 |
所有继承OutputStream
的都是字节输出流
主要方法介绍:
void | close() 关闭此输出流并释放与此流有关的所有系统资源 |
---|---|
void | flush() 刷新此输出流并强制写出所有缓冲的输出字节 |
void | write(byte[] b) 将b.length个字节从指定的字节数组写入此输出流 |
void | write(byte[] b,int off,int len) 将指定字节数组中从偏移量off开始的len个字节写入到输出流 |
abstract void | write(int b) 将指定的字节写入输出流 |
所欲继承了Reader
的都是字符输入流
主要方法介绍:
abstract void | close() 关闭该流 |
---|---|
int | read() 读取单个字符 |
int | read(char[] cbuf) 将字符读入数组 |
abstract int | read(char cbuf,int off,int len) 将字符读入数组的某一部分 |
所有继承了Writer
的都是字符输出流
主要方法介绍:
Writer | append(char c) 将指定字符追加到此Writer |
---|---|
abstract void | close() 关闭此流,但要先刷新它 |
abstract void | flush() 刷新此流 |
void | writer(char cbuf) 写入字符数组 |
abstract void | writer(char cbuf,int off,int len) 写入字符数组的某一部分 |
void | writer(int c) 写入单个字符 |
void | writer(String str) 写入字符串 |
void | writer(String str,int off,int len) 写入字符串的某一部分 |
文件流主要分为:文件字节输入流、文件字节输出流、文件字符输入流、文件字符输出流
3.1 FileInputStream(文件字节输入流)public class FileInputStreamTest {
public static void main(String[] args) {
InputStream is=null;
try {
is=new FileInputStream("test.txt");
int b=0;
while((b=is.read())!=-1){
System.out.print((char)b);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件可以正确读取,但输出为这是一首简å•çš„å°æƒæŒï¼Œå”±ç€äººä»¬å¿ƒè‚ 的曲折
乱码,原因在于字节输入流是一个字节一个字节读取的,而汉字是两个字节,因此读出一个字节就打印的输出是不完整的。
FileOutputStream
主要按照字节的方式写文件
public class FileOutputStreamTest {
public static void main(String[] args) {
InputStream is=null;
OutputStream os=null;
try {
is=new FileInputStream("test.txt");
os=new FileOutputStream("F:\JavaTest\JavaSE\src\test.txt.bak");
int b=0;
while((b=is.read())!=-1){
os.write(b);
}
System.out.println("文件复制完毕!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if(is!=null){
is.close();
}
if(os!=null){
os.close();
}
}catch (IOException e){}
}
}
}
3.3 FileReader(文件字符输入流)
FileReader
是以字符为单位读取文件,即一次读取两个字节
public class FileReaderTest {
public static void main(String[] args) {
Reader r=null;
try {
r=new FileReader("test.txt");
int b=0;
while((b=r.read())!=-1){
System.out.print((char)b);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(r!=null){
try {
r.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
可以正确读取文本文件,汉字无乱码
3.4 FileWriter(文件字符输出流)public class FileWriterTest {
public static void main(String[] args) {
Writer w=null;
try {
// append参数为true则表示在文件后面追加,默认为flase即覆盖原始文件内容
w=new FileWriter("test.txt",true);
w.write("我想我很快乐");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(w!=null){
try {
w.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
04 缓冲流
缓冲流主要是为了提高效率而存在的,减少物理读取次数,缓冲流主要有:BufferedInputStream
、BufferedOutputStream
、BufferedReader
、BufferedWriter
,并且 BufferedReader
提供了实用方法readLine()
,可以直接读取一行,BufferWriter
提供了 newLine()
可以写换行符。
public class BufferedStreamTest {
public static void main(String[] args) {
InputStream is=null;
OutputStream os=null;
try {
is=new BufferedInputStream(new FileInputStream("test.txt"));
os=new BufferedOutputStream(new FileOutputStream("src\test.txt.bak"));
int b=0;
while((b=is.read())!=-1){
os.write(b);
}
System.out.println("文件复制完毕!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(is!=null){
is.close(); // 在close前会调用flush,将缓冲区的内容写入到磁盘
}
if(os!=null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
可以显示的调用flush
,flush
的含义是刷新缓冲区,即将缓冲区中的数据写到磁盘上,不再放到内存中。在执行os.close()
时,默认执行了os.flush()
,因此可以不显示的调用。
public class BufferedStreamTest {
public static void main(String[] args) {
BufferedReader br=null;
BufferedWriter bw=null;
try {
br=new BufferedReader(new FileReader("test.txt"));
bw=new BufferedWriter(new FileWriter("src\test.txt.bak"));
String s=null;
while((s=br.readLine())!=null){
bw.write(s);
}
System.out.println("文件复制完毕!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(br!=null){
br.close(); // 在close前会调用flush,将缓冲区的内容写入到磁盘
}
if(bw!=null){
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
05 转换流
转换流主要有两个 InputStreamReader
和 OutputStreamWriter
InputStreamReader
主要是将字节流输入流转换成字符输入流OutputStreamWriter
主要是将字节流输出流转换成字符输出流
对 FileInputStreamTest.java
进行改造,使用字符流
public class InputStreamReaderTest {
public static void main(String[] args) {
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(new FileInputStream("test.txt")));
String s=null;
while((s=br.readLine())!=null){
System.out.print(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
5.2 OutputStreamWriter
对 FileOutputStreamTest.java
进行改造,使用字符流
public class OutputStreamWriterTest {
public static void main(String[] args) {
BufferedWriter bw=null;
try {
bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("test.txt")));
bw.write("这是一首简单的小情歌");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if(bw!=null){
bw.close();
}
}catch (IOException e){}
}
}
}
06 对象流
对象流可以将 Java 对象转换成二进制写入磁盘,这个过程通常叫做序列化,并且还可以从磁盘读出完整的 Java 对象,而这个过程叫做反序列化。
对象流主要包括:ObjectInputStream
和 ObjectOutputStream
如果实现序列化该类必须实现序列化接口 java.io.Serializable
,该接口没有任何方法,该接口只是一种标记接口,标记这个类是可以序列化的
- 序列化
public class ObjectStreamTest {
public static void main(String[] args) {
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("src\test.txt.bak"));
Person p=new Person();
p.name="张三";
oos.writeObject(p);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 实现序列化接口,否则不能序列化
class Person implements Serializable {
String name;
int age;
}
- 反序列化
public class ObjectStreamTest02 {
public static void main(String[] args) {
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(new FileInputStream("src\test.txt.bak"));
// 反序列化
Person p=(Person)ois.readObject();
System.out.println(p.name);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 实现序列化接口,否则不能序列化
class Person implements Serializable {
String name;
int age;
}
6.2 关于transient关键字
采用transient
关键字修饰的属性,在序列化时会忽略
class Person implements Serializable {
String name;
transient int age;
}
6.3 关于serialVersionUID属性
在序列化存储某类时,会为该类生成一个serialVersionUID
,而在该类进行改动后,使用它时程序会为该类生成一个新的serialVersionUID
,两个serialVersionUID
不用,Java认为是不兼容的两个类,因此会出现错误。
如果在序列化类中定义了成员域serialVersionUID
,系统会把当前的serialVersionUID
成员域的值作为类的序列号,这样不管如何升级该类,它的序列号都不变。
class Person implements Serializable {
private static final long serialVersionUID=-123455678754L;
String name;
int age;
}
重点:
参与序列化的类型必须实现java.io.Serializable接口。
并且建议将序列化版本号手动的写出来。private static final long serialVersionUID = 1L;
File 提供了大量的文件 *** 作:删除文件,修改文件,得到文件修改日期,建立目录、列表文件等等。
- 如何递归读取目录及子目录下的文件
public class FileTest {
public static void main(String[] args) {
listFile(new File("F:\JavaTest"),0);
}
// 递归读取某个目录及子目录下所有文件
private static void listFile(File f, int level){
String s="";
for (int i = 0; i < level; i++) {
s+="--";
}
File[] files=f.listFiles();
for (int i = 0; i < files.length; i++) {
System.out.println(s+files[i].getName());
if(files[i].isDirectory()){
listFile(files[i],level+1);
}
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)