本章目标
- Java中的文件
- 流
- 字节流
- 字符流
本章内容
一、Java中的文件
1、文件的介绍
Java中文件操作主要通过File类来实现,File类关心的是磁盘上存储的文件
- File类并不是只代表文件
- 可以表示特定文件的名称,这里的名称就是路径
- 可以是某个目录。
2、路径的表示方式:
因为在Java中\
被用作转义字符,
- Windows风格的路径最好用
\\
来分隔,如:c:\\temp\\test.txt
- Linux下此路径就应该这样写:
c:/temp/test.txt
如果要考虑跨平台,则最好这样写:”c:”+File.separator+”temp”+ File.separator+”文件名”
3、File类的构造函数
构造方法 | 简介 |
---|---|
File(File parent,String child) | 建立一个以parent加上child为路径的File组件 |
File(String pathname) | 建立一个以pathname为路径的File组件 |
File(String parent,String child) | 建立一个以parent加上child为路径的File组件 |
4、常用方法
方法 | 说明 |
---|---|
File.exists() | 文件或者目录是否存在 |
File.isFile() | 是否是文件 |
File.isDirectory() | 是否是目录 |
File.getName() | 取文件或者目录的名字 |
File.getPath() | 取文件或者目录的路径 |
File.getAbsolutePath() | 取文件绝对路径 |
File.lastModified() | 最后修改日期 |
File.length() | 文件或者目录的字节大小 |
File.createNewFile() | 创建新文件 |
5、示例
5.1、创建文件
package com.it.file;
import java.io.File;
import java.io.IOException;
public class FileTest {
/**
* @param args
*/
public static void main(String[] args) {
File fi=new File("e:/test/helo");
File file=new File(fi,"aa.txt");
if(!fi.exists()){
fi.mkdirs();
}
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
5.2、文件的操作
获取当前目录下所有文件和文件的大小
import java.io.*;
public class FileSize{
public static void main(String[] args) {
File files=new File(".");
String[] list=files.list();
for(int i=0;i<list.length;i++){ File file=new File(list[i]);
System.out.println(list[i]+file.length()); }
}
}
.代表当前目录
二、流
1、流的概念
流是指程序用运行中的数据通信信道,流类所关心的是文件的内容。流使我们将实际I/O设备中处理数据的细节动作
隐藏起来。
什么是数据流?
- 表示了字符或者字节数据的流动序列
- 数据源和程序之间的数据传输
- 数据流是指所有的数据通信通道
2、输入输出
我们通常提到的输入输出,都是立足于编程者角度上的,
2.1、什么是输入输出
- 所谓输入:就是将编程者所设计的程序看作流的终点
- 所谓输出:是将程序看作流的起点
在java中输入、输出功能是通过有关流的操作来完成的,需要使用java.io包中的类
2.1、基本的输入输出对象
System类提供了标准输入输出流和错误流
用法 | 类型 | 说明 |
---|---|---|
System.out | PrintStream | 把输出送到缺省的显示(通常是显示器) |
System.in | InputStream | 从标准输入获取输入(通常是键盘) |
System.err | PrintStream | 把错误信息送到缺省的显示 |
每当main方法被执行时,就自动生成上述三个对象
3、流的分类
3.1、从读写操作角度划分:
- 输入流:就是把程序看作终点,通过
read
方法把内容读到流管道的过程 - 输出流:就是把程序看作起点,通过
write
方法把内容写到流管道的过程
3.2、从传输内容角度划分:
- 字节流:会以字节的形式来处理数据。
- 字符流:会以字符的形式来处理数据
三、字节流
字节流采用ASCII编码即一个字节(8个位)为单位进行数据的输入和输出。
如果是音频文件、图片、歌曲、等内容用字节流
1、字节流分类
所有的输入和输出在较低层次上都是面向字节的
1.1、InputStream类
InputStream类是所有输入流类的基类,InputStream类的方法有:
- read():从流中读入数据
- skip():跳过流中若干字节数
- available():返回流中可用字节数
- mark():在流中标记一个位置
- reset():返回标记过得位置
- markSupport():是否支持标记和复位操作
- close():关闭流
1.2、OutputStream
OutputStream类是所有输出流类的基类,也是个抽象类,常用方法有:
- Close():关闭输出流
- flush():清空输出流缓冲区
- write(int b):写入单个字节到输出流
- write(byte[] buffer):写入一个字节数组到输出流。
2、FileInputStream
FileInputStream的常用方法:
方法 | 说明 |
---|---|
void close() | 关闭此文件输入流并释放与此流有关的所有系统资源 |
int read() | 从输入流中读取一个数据字节 |
int read(byte[ ] b) | 从输入流中将最多b.length个字节的数据读入到一个字节数组中 |
示例
注意:当创建一个输入流对象时,必须传入一个文件路径。该路径下如果没有此文件,将会抛出FileNotFoundException
public static void main(String[] args) {
File file = new File("test.txt");
byte[] bt = new byte[(byte) file.length()];
try {
FileInputStream fis = new FileInputStream(file);
fis.read(bt);
for (int i = 0; i < bt.length; i++) {
System.out.print((char) bt[i] + "\t");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
3、FileOutputStream
FileOutputStream的常用方法:
方法 | 说明 |
---|---|
void close() | 关闭文件输出流并释放与此流有关的所有系统资源 |
void write(byte[ ] b) | 将b.length个字节从指定字节数组写入到此文件输出流中 |
void write(int b) | 将指定字节写入到此文件输出流 |
示例
注意:当你创建一个输出流对象时,必须传入一个文件路径。
- 该路径下,如果没有这个文件,会创建该文件。
- 如果有这个文件,会清空这个文件中的数据
public static void main(String[] args) {
try {
//输出流创建成功,会往空文件中写内容
FileOutputStream fos = new FileOutputStream(new File("test.txt"));
for (int i = 97; i < 123; i++) {
fos.write((char) i);
}
fos.flush();
System.out.println("写入成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
4、文件拷贝
文件拷贝即要用到输入流,又要使用到输出流操作:
FileInputStream in=new FileInputStream(fp);
FileOutputStream out=new FileOutputStream(fp);
输入流的参数是用于输入的文件名
输出流的参数是用于输出的文件名
public class FileStreamDemo {
public static void main(String[] args) {
try {
File inFile = new File("file1.txt");
File outFile = new File("file2.txt");
FileInputStream fis = new FileInputStream(inFile);
FileOutputStream fos = new FileOutputStream(outFile);
int data = -1;
while ((data = fis.read()) != -1)
fos.write(data);
fis.close();
fos.close();
} catch (IOException e) {
System.out.println("文件不存在");
}
}
}
每次读取一个效率太低,我们可以先把内容读取到一个字节数组中,再从字节数组中读取效果高很多了
改造为字节数组
public class FileStreamDemo {
public static void main(String[] args) throws IOException {
File fileFrom = new File("e:\\aa\\bb\\cc.txt");
File fileTo = new File("e:\\aa\\bb\\dd.txt");
FileInputStream fis = new FileInputStream(fileFrom);// 输入流
FileOutputStream fos = new FileOutputStream(fileTo, true);// 输出流
// byte[] b = new byte[(int)fileFrom.length()];//根据输入流对应的文件构造一个字节数组
byte[] bytes = new byte[10];//用于保存输入流读取到的数据,我们这里定义10长度,说明一次读取10位数据。
int len = -1;//表示已经读取了多少个字节,如果是 -1,表示已经读取到文件的末尾。
while ((len = fis.read(bytes)) != -1) {//-1的话表示文件已经被读完。
fos.write(bytes, 0, len);//将 bytes数组中从 0 开始,长度为 len 的数据输出到dd.txt 文件中
}
fos.close();
fis.close();
}
}
5、缓冲输入流
5.1、简介
BufferedInputStream
类继承自 FilterInputStream
类,它提供了缓冲和流的级联两个功能,可以提高读取操作的效率,减少 I/O 操作次数。
BufferedInputStream 类底层最主要的实现是通过缓冲区来提升读取效率的,通过读取尽可能多的数据到缓冲区中,减少 I/O 操作次数。BufferedInputStream 的源代码实现中,最重要的两个方法就是 fill() 方法和 read() 方法。
- fill()方法:用于将缓冲区中的数据填满,以便能够进行读取操作。
- read()方法:通过读取缓冲区中的数据,来达到提高读取效率的目的。
在读取时,如果缓冲区中的数据已经全部被读取,那么就需要再次调用 fill() 方法来填充缓冲区。这样就达到了高效读取的目的。
5.2、部分源码
public class BufferedInputStream extends FilterInputStream {
protected volatile byte buf[];
protected int count;
protected int pos;
protected int markpos = -1;
protected int marklimit;
protected InputStream in;
//缓存数组默认大小
private static int DEFAULT_BUFFER_SIZE = 8192;
//缓存数组最大值
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; // 构造函数
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
marklimit = size;
this.in = in;
} // 读取数据到缓冲区
private void fill() throws IOException {
byte[] tmpbuf = buf;
if (markpos < 0)
pos = 0;
else if (pos >= tmpbuf.length)
if (markpos > 0)
pos = markpos;
else if (tmpbuf.length < marklimit)
tmpbuf = new byte[Math.min(marklimit, MAX_BUFFER_SIZE)];
else
if (markpos < 0)
pos = 0;
else
throw new IOException("Marked position invalid");
count = pos;
int n = in.read(tmpbuf, pos, tmpbuf.length - pos);
if (n > 0)
count = n + pos;
}
// 从缓冲区读取数据
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
//接上面,直接返回数组中内容
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
public synchronized int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = 0;
for (;;) {
int nread = read1(b, off + n, len - n);
if (nread <= 0)
return (n == 0) ? nread : n;
n += nread;
if (n >= len)
return n;
// 如果没有数据可读取,就退出循环
InputStream input = in;
if (input != null && input.available() <= 0)
return n;
}
}
private synchronized int read1(byte[] b, int off, int len) throws IOException {
int avail = count - pos;
// 如果缓冲区中没有数据可供读取,就从输入流中读取数据到缓冲区
if (avail <= 0) {
if (len >= buf.length && markpos < 0) {
return in.read(b, off, len);
}
fill();
avail = count - pos;
if (avail <= 0)
return -1;
}
int cnt = (avail < len) ? avail : len;
System.arraycopy(buf, pos, b, off, cnt);
pos += cnt;
return cnt;
}
}
buf为缓冲区,count表示缓冲区有效数据长度,pos表示下一次从缓冲区读取的位置,markpos表示标记的位置,marklimit表示标记的上限。 fill()方法用于从输入流中读取数据到缓冲区。
read()方法从缓冲区中读取一个字节的数据。
read(byte b[], int off, int len)方法从缓冲区中读取len个字节的数据到b数组中的off位置。
read1(byte[] b, int off, int len)方法从缓冲区中读取len个字节的数据到b数组中的off位置。如果缓冲区中没有数据可供读取,就从输入流中读取数据到缓冲区。
6、缓冲输出流
6.1、简介
BufferedOutputStream类是一个带缓冲区的输出流,其主要作用是用于提高输出效率和减少IO次数。
在使用BufferedOutputStream类时,可以使用write()方法进行写操作,该方法会将数据写入到缓冲区中,当缓冲区满时,缓冲区中的数据会被一次性写出。此外,还可以使用flush()方法将缓冲区中的数据强制写出。
6.2、构造方法
public BufferedOutputStream(OutputStream out, int size)
其中第一个参数out是输出流对象,第二个参数size是缓冲区的大小。如果不指定缓冲区的大小,默认为8192字节。
6.3、缓冲区流的特点
- 只有缓冲区满时,才会将数据送到输出流.
- Java在输出数据流中,当对方尚未将数据取走时,程序就会被阻塞.
- 有时要人为地将尚未填满的缓冲区中的数据送出,使用
flush()
方法.
7、缓冲流综合案例
介绍参考:https://blog.csdn.net/J169YBZ/article/details/123706407
7.1、文件复制操作
通过缓冲区流,减少访问硬盘的次数,提高效率
7.3、示例
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileStreamDemo {
public static void main(String[] args) throws IOException {
File fileFrom = new File("e:\\aa\\bb\\cc.txt");
File fileTo = new File("e:\\aa\\bb\\dd.txt");
FileInputStream fis = new FileInputStream(fileFrom);// 输入流
FileOutputStream fos = new FileOutputStream(fileTo, true);// 输出流
BufferedInputStream bis = new BufferedInputStream(fis);//输入缓冲流
BufferedOutputStream bos = new BufferedOutputStream(fos);//输出缓冲流 byte[] bytes = new byte[10];//数组值越大效率会越高,但也要考虑计算机内存
int len = 0;//此时len代表读取的个数
while ((len = bis.read(bytes)) != -1){// -1的话表示文件已经被读完。
//利用缓冲流,将数据写入到缓存中,待缓存到达了8129后,再一次性写入磁盘。
bos.write(bytes,0,len);//注意这里使用的是三个参数的方法
}
bos.flush();// 输出字节数组中的内容或调用close操作
bos.close();
bis.close();
fos.close();
fis.close();
}
}
BufferedInputStream 输入缓冲流: 首先bufferedInputStream 会通过底层基础流读取文件,将文件中的8192个字节先读取出来,保存在buf数组中。 在程序中,可以从buf数组中10个字节10个字节得读取。这里得读取就不是从磁盘文件中读取了,而是从内存中的buf数组中读取,性能极佳。
BufferedOutputStream:输出缓冲流
利用缓冲流,将数据写入到缓存中,待缓存到达了8129后,再通过基础流一次性写入磁盘,而不是每10个字节或1个字节就和底层操作系统打交道一次
总之:有了缓冲流都可以很好的少减了和系统硬盘的交互次数,提高效率 参考1 参考2
7.4、内存的读写速度是硬盘的多少倍(扩展)
DDR3内存读写速度大概10G每秒(10000M)
固态硬盘速度是300M每秒,是内存的三十分之一
机械硬盘的速度是100M每秒,是内存的百分之一
DDR4内存读写速度大概50G每秒(50000M)
固态硬盘速度是300M每秒,是内存的二百分之一
机械硬盘的速度是100M每秒,是内存的五百分之一
四、字符流
字符流采用UNICODE编码即2个字节(16个位)为单位进行数据的输入和输出。
如果是关系到中文(文本)的内容读写操作,用字符流比较好
1、字符流分类
由于字符流采用的是UNICODE编码,因此能够进行国际化,这时字符流比字节流更有效,所以它对多国语言支持性比较好。
1.1、Reader
Reader类是所有输入流类的基类,是个抽象类。Reader类对应的子类的所有方法在发生错误时都会抛出IOException异常
Reader的常用方法:
方法 | 说明 |
---|---|
abstract void close() | 关闭输入源 |
int read() | 从输入流读取单个字符的整数表示,如果不存在则返回-1 |
int read(char buffer[ ]) | 从输入流中将字符读入数组,如果遇到文件结尾,返回-1 |
long skip(long n) | 跳过n个字符,返回跳过的字符数 |
1.2、Writer
Writer类是所有输出流类的基类,也是个抽象类。
Writer的常用方法:
方法 | 说明 |
---|---|
abstract void close() | 关闭输出流 |
Writer append(char c) | 将指定字符追加到此 writer |
void write(int ch) | 写入单个字符到调用的输出流 |
void write(String str) | 写入str到调用输出流 |
abstract void close() | 关闭输出流 |
abstract void flush() | 清空输出缓冲区 |
2、InputStreamReader
InputStreamReader继承了Reader,它实现了Reader的抽象方法。
-
InputStreamReader是字节流通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符。
-
每次调用InputStreamReader中的一个read()方法都会导致从基础输入流读取一个或多个字节。
-
为了达到最高效率,可以考虑在BufferedReader内包装InputStreamReader,BufferedReader类在后面小节中介绍
//例如以下语句把一个读取从控制台输入的InputStreamReader转换为了BufferedReader
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
2.1、InputStreamReader的常用方法
方法 | 说明 |
---|---|
void close() | 关闭该流并释放与之关联的所有资源 |
String getEncoding() | 返回此流使用的字符编码的名称 |
int read() | 读取单个字符 |
boolean ready() | 判断此流是否已经准备好用于读取 |
2.2、示例
public static void main(String[] args) {File file = new File("test.txt");InputStream is = null;try {is = new FileInputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();}InputStreamReader reader = new InputStreamReader(is);try {int i = 0;while ((i = reader.read()) != -1) {System.out.print((char) i + "\t");}} catch (IOException e) {e.printStackTrace();}}
3、OutputStreamWriter
OutputStreamWriter继承了Writer,它实现了Writer的抽象方法。
- OutputStreamWriter是字符流通向字节流的桥梁,这刚好与InputStreamReader相反。
- 他使用指定的charset将写入的字符编码转换为字节输出。
- 它使用的字符集可以由名称指定或显示给定,否则可能接受平台默认的字符集。
注意,传递到此write()方法的字符是未经缓冲的。为了达到最高效率,可考虑将OutputStreamWriter包装到BufferedWriter中以避免频繁调用转换器,BufferedWriter类将在后面小节中介绍
//例如以下语句把一个读取从控制台输入的InputStreamReader转换为了BufferedReader
BufferedWriter out= new BufferedWriter(new OutputStreamWriter(System.out));
3.1、OutputStreamWriter的常用方法
方法 | 说明 |
---|---|
void close() | 关闭该流并释放与之关联的所有资源 |
String getEncoding() | 返回此流使用的字符编码的名称 |
void write(char[] cbuf, int off, int len) | 写入字符数组的某一部分 |
void flush() | 刷新该流的缓冲 |
3.2、示例
public static void main(String[] args) {String str = "大家好";try {OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File("test.txt")));osw.write(str, 0, str.length());osw.flush();osw.close();System.out.println("文件写入成功。");} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
4、FileReader
此类继承了InputStreamReader,所以除了有InputStreamReader的功能以外还有自己的一些功能,可以使用它直接读取文件的内容。它也是读取字符文件的便捷类
4.1、FileReader的常用方法
方法 | 说明 |
---|---|
int read() | 读取单个字符,返回值为数据内容 |
4.2、示例
public static void main(String[] args) {FileReader reader = new FileReader("test.txt");try {int i = 0;while ((i = reader.read()) > 0) {System.out.print((char) i + "\t");}} catch (IOException e) {e.printStackTrace();}}
5、FileWriter
此类继承了OutputStreamWriter类,所以它有OutputStreamWriter类的全部功能。另外它还有自己的一些功能,可以使用它创建一个写文件的对象,它也是向文件写入字符的便捷类
5.1、FileWriter的常用方法
方法 | 说明 |
---|---|
void write(char[ ] cbuf, int off, int len) | 写入字符数组的某一部分 |
void write(int c) | 写入单个字符 |
void write(String str, int off, int len) | 写入字符串的某一部分 |
5.2、示例
public static void main(String[] args) {char c[]={'人','之','初','性','本','善','性','相','近','习','相','远'};try{FileWriter fw=new FileWriter("三字经a.txt");fw.write(c);fw.flush();//刷新}catch(IOException e){e.printStackTrace();}}
6、BufferedReader
BufferedReader由Reader类扩展而来,提供通用的缓冲方式文本读取,而且提供了很实用的readLine()方法
//例如以下语句把一个读取从控制台输入的InputStreamReader转换为了BufferedReader
BufferedWriter out= new BufferedWriter(new OutputStreamWriter(System.out));
6.1、BufferedReader有两个构造函数
方法 | 说明 |
---|---|
BufferedReader(Reader inputStream) | 创建使用缺省尺寸输入缓冲区的缓冲字符输入流 |
BufferedReader(Reader inputStream,int bufsize) | 创建一个使用指定大小输入缓冲区的缓冲字符输入流 |
6.2、BufferedReader的常用方法
方法 | 说明 |
---|---|
void close() | 关闭该流并释放与之关联的所有资源 |
int read() | 读取单个字符 |
int read(char[] cbuf, int off, int len) | 将字符读入数组的某一部分 |
String readLine() | 读取一个文本行 |
boolean ready() | 判断此流是否已准备好被读取 |
long skip(long n) | 跳过n个字符,并返回跳过的字符数 |
6.3、示例
public class CharInput {public static void main(String args[]) throws IOException {String s;InputStreamReader ir;BufferedReader in;ir = new InputStreamReader(System.in);in = new BufferedReader(ir);s = in.readLine();int i = Integer.parseInt(s);i = i * 2;System.out.println("the result is" + i);}
}
7、PrintWriter
PrintWriter继承了Writer类,但除此之外此类还实现了PrintStream中的所有print()方法, PrintWriter可以直接输出字符串
7.1、PrintWriter的常用方法
方法 | 说明 |
---|---|
void print(Object obj) | 打印对象 |
void write(String s) | 写入字符串 |
void close() | 关闭该流并释放与之关联的所有系统资源 |
void flush() | 刷新该流的缓冲 |
7.2、示例
public static void main(String args[]) throws IOException {String s="人之初 性本善 性相近 习相远";try{FileWriter fw=new FileWriter("三字经a.txt");PrintWriter pw=new PrintWriter(fw,true);pw.println(s);}catch(IOException e){e.printStackTrace();}}