1、常用的文件操作
1.1 基本概念
文件:存储数据的地方,比如图片、pdf、word文档等等。
输入流:文件存储于磁盘,从磁盘读入内存的路径(把内存想做是自己的大脑,磁盘相当于课本,本子的内容记录到脑子里就是输入)。
输出流:从内存到磁盘的路径。
1.2 创建文件对象
相关方法:
new File(String pathname) //根据路径构建一个File对象
new File(File parent,String child) //根据父目录文件+子路径构建
new File(String parent,String child) //根据父目录+子路径构建
//创建文件需要使用createNewFile方法
file.createNewFile();
题目:在磁盘下,以三种不同方式创建新文件
- 方式一:
new File(String pathname) //根据路径构建一个File对象
@Test
public void create01(){
String filePath = "D:\\笔记和课程\\code review\\javase-io\\news1.txt";
File file = new File(filePath);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
- 方式二:
new File(File parent,String child) //根据父目录文件+子路径构建
@Test
public void create02(){
//这里是个目录路径
File file = new File("D:\\笔记和课程\\code review\\javase-io\\");
String filename = "new2.txt";
File file1 = new File(file, filename);
try {
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
- 方式三:
new File(String parent,String child) //根据父目录+子路径构建
@Test
public void create03(){
String pathname = "D:\\笔记和课程\\code review\\javase-io\\";
String filename = "new3.txt";
File file = new File(pathname,filename);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
书中的记录
1、File类不仅可以表示一个特定的文件名,还可以表示一个目录下一组文件名的集合,可以通过file.list()获取集合:
File file = new File("D:\\笔记和课程\\code review\\javase-io\\news");
String[] list = file.list();
for(String fileName:list){
System.out.println(fileName);
}
在这个文件下创建一个目录news,并在它下面创建一些文件,然后按照上面方法就能拿到这些文件名称。
1.3 获取文件相关的信息
File的方法结构:
比较常用的方法是:getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
@Test
public void info(){
//创建文件对象
File file = new File("D:\\笔记和课程\\code review\\javase-io\\news1.txt");
File file1 = new File("D:\\笔记和课程\\code review\\javase-io\\");
System.out.println("文件的名字是" + file.getName());
System.out.println("文件的绝对路径是" + file.getAbsolutePath());
System.out.println("文件的父路径是" + file.getParent());
System.out.println("文件的字节长度是" + file.length());
System.out.println("是否存在该文件" + file.exists());
System.out.println("是否是文件" + file1.isFile());
System.out.println("是否是目录" + file1.isDirectory());
}
1.4 目录的操作和文件删除
判断:D:\笔记和课程\code review\javase-io\news下是否有news1.txt,如果有就删除
@Test
public void m1(){
String filePath = "D:\\笔记和课程\\code review\\javase-io\\news\\news1.txt";
File file = new File(filePath);
if (file.exists()){
file.delete();
}else{
System.out.println("文件不存在");
}
}
判断:D:\笔记和课程\code review\javase-io\news目录是否存在,如果存在就提示已经存在,否则就创建
@Test
public void m2(){
String filePath = "D:\\笔记和课程\\code review\\javase-io\\news";
File file = new File(filePath);
if(file.exists()) {
if(file.delete()){
System.out.println("文件删除成功");
}else{
System.out.println("文件删除失败");
}
}else {
System.out.println("文件不存在");
}
}
判断:D:\笔记和课程\code review\javase-io\new目录是否存在,如果存在就提示存在,否则就创建
@Test
public void m3() throws IOException {
String filePath = "D:\\笔记和课程\\code review\\javase-io\\new";
File file = new File(filePath);
if (file.exists()){
System.out.println(filePath + "已存在");
}else{
//mkdirs()是创建多级目录,注意不要用mkdir(),这个只能创建一级目录
if (file.mkdirs()){
System.out.println("创建成功");
}else{
System.out.println("创建失败");
}
}
}
2、IO流的分类
2.1 IO流的分类
数据的输入和输出是以Stream流的方式进行的。
java.io包下提供了很多接口和类。
- 字节流(二进制文件:可以传输声音文件、视频文件)和字符流(文本文件)
- 输入流和输出流
- 节点流和处理流(包装流)
具体的结构:

2.2 字节输入流和字节输出流
InputStream字节输入流,它是一个抽象类。
public abstract class InputStream implements Closeable {
三个比较重要的子类:
- FileInputStream:文件输入流
- BufferedInputStream:缓冲字节输入流
- ObjectInputStream:对象字节输入流
(1)FileInputStream类
构造方法:
- FileInputStream(File file)
- FileInputStream(String name)
相当于通过获取到的流对象去操作磁盘上实际的文件,上面创建的就是流对象。
常用方法:
- close():关闭流
- read():从该输入流中读取一个数据字节
- read(byte[] b):从该输入流中最多读取b.length个字节数据到byte[]数组中
- read(byte[] b,int off,int len):从该输入流中将最多len个字节的数据读取到byte数组中
@Test
public void readFile01(){
String filePath = "D:\\笔记和课程\\code review\\javase-io\\hello.txt";
int content = 0;
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath);
//如果read()得到的结果返回为-1,说明全部读取完毕
while ((content = fis.read()) != -1){
//这里读取到的值是int类型的,要转换成char类型
System.out.print((char)content);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
还可以改进,可以读取多个字节一起输出,提高效率,这就相当于先读取到一个数组中去,然后把这个数组传给String:
@Test
public void readFile02(){
String filePath = "D:\\笔记和课程\\code review\\javase-io\\hello.txt";
FileInputStream fis = null;
int content = 0;
byte[] bytes = new byte[10];
try {
fis = new FileInputStream(filePath);
while ((content = fis.read(bytes)) != -1) {
System.out.print(new String(bytes,0,content));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)FileOutputStream类
构造方法:
- FileOutputStream(File file)
- FileOutputStream(File file,boolean append):创建一个向file对象表示的文件中写入数据的文件输出流,以追加的方式写入
- FileOutputStream(String name):创建一个向具有指定名称的文件写入数据的输出文件流
- FileOutputStream(String name,boolean append):创建一个向具有指定name文件中写入数据的输出文件流
常用方法:
- close()
- write(byte[] b):将b.length个字节从指定byte数组写入此文件输出流中
- write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入此文件输出流
- write(int b):将指定字节写入此文件输出流
把hello world写入电脑指定位置的文件中,并且要创建该文件
@Test
public void write01(){
String filePath = "d:\\a.txt";
FileOutputStream fos = null;
String message = "hello world";
try {
fos = new FileOutputStream(filePath);
fos.write(message.getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果不想覆盖原来的文件可以采用这种方式FileOutputStream(File file,boolean append),append为true表示在原文件追加
@Test
public void write01(){
String filePath = "d:\\a.txt";
FileOutputStream fos = null;
String message = "hello world!!!";
try {
fos = new FileOutputStream(filePath,true);
fos.write(message.getBytes());
fos.write(message.getBytes(),0,4);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(3)综合-文件的拷贝
@Test
public static void copyFile(){
//原文件路径
String srcFilePath = "C:\\Users\\16140\\Pictures\\Saved Pictures\\godfather.jpg";
String descFilePath = "D:\\godfather.jpg";
FileInputStream fis = null;
FileOutputStream fos = null;
int content = 0;
byte[] bytes = new byte[1024];
try {
fis = new FileInputStream(srcFilePath);
fos = new FileOutputStream(descFilePath);
while ((content = fis.read(bytes)) != -1){
fos.write(bytes,0,content);
}
System.out.println("copy finished");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.3 字符输入流和字符输出流
(1)FileReader类
构造方法:
- new FileReader(File/String)
常用方法:
- read():每次读取单个字符返回该字符,读到文件末尾返回-1
- read(char[] char):批量读取多个字符到数组,返回读取到的字符数
- new String(char[] char):将char[]转换成String
- new String(char[],off,len):将char的指定部分转换成字符串
读取story.txt中的内容,并显示
@Test
public void test01(){
String filePath= "D:\\笔记和课程\\code review\\javase-io\\story.txt";
FileReader fr = null;
int data = 0;
try {
fr = new FileReader(filePath);
while ((data = fr.read()) != -1){
System.out.print((char)data);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fr != null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
方式二:
@Test
public void test02(){
String filePath= "D:\\笔记和课程\\code review\\javase-io\\story.txt";
FileReader fr = null;
int readLength = 0;
char[] chars = new char[10];
try {
fr = new FileReader(filePath);
while ((readLength = fr.read(chars)) != -1){
System.out.print(new String(chars,0,readLength);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fr != null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)FileWriter类
构造方法:
- new FileWriter(File/String):覆盖的方式,相当于流的指针在前端
- new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
常用方法:
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off,len):写入指定数组的指定部分
- write(string):写入整个字符串
- write(string,off,len):写入字符串的指定部分
注意:FileWriter使用完毕需要关闭或者刷新流,否则不能写入指定文件
将“填平水泊擒晁盖,踏破梁山捉宋江”写入到note.txt文件中
@Test
public void write1(){
String filePath = "d:\\note.txt";
FileWriter fw = null;
String message = "填平水泊擒晁盖,踏破梁山捉宋江\r";
try {
fw = new FileWriter(filePath,true);
//fw.write(message);
fw.write(message,0,message.length());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fw != null) {
try {
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
看源码会发现close和flush方法最终走向是一样的。
2.4 节点流和处理流
(1)基本概念
节点流:就是从特定的数据源读写数据的,比如FileReader和FileWriter等。
- 比如针对带中文的文件:可以使用Reader或Writer
- 针对二进制文件:可以使用InputStream或OutputStream
- 针对数组:可以使用ByteArrayInputStream或ByteArrayOutputStream
处理流:也叫包装流,它是在已经存在的流之上,给其提供更加强大的读写功能
- 比如BufferedReader或BufferedWriter
public class BufferedReader extends Reader {
private Reader in;
......
}
可见BufferedReader缓冲字符输入流包含了Reader,这也就是说,可以使用这个来包含不同类型的Reader和Writer,以达到自己需要的目的。
看下面:BufferedWriter(Writer):这里就是说可以传一个Writer对象,也就包含了任意类型的Writer子类的对象。
这也叫做装饰器模式。
节点流和处理流:节点流偏底层,直接操作数据源;而处理流可以包装节点流,提供更加强大的功能。
装饰器代码
创建一个抽象类当作父类
public abstract class Reader_ {
public void readFile(){};
public void readString(){};
}
分别写两个不同类型的子类
public class FileReader extends Reader_{
public void readFile(){
System.out.println("读取文件...");
}
}
public class StringReader extends Reader_{
public void readString(){
System.out.println("读取字符串...");
}
}
写一个Buffered类
public class BufferedReader_ extends Reader_{
private Reader_ reader;//Reader_属性
public BufferedReader_(Reader_ reader) {
this.reader = reader;
}
public void readFiles(int nums){
while (nums > 0){
reader.readFile();
nums--;
}
}
public void readString(int nums){
while (nums > 0){
reader.readString();
nums--;
}
}
}
写一个测试类
public class Test01 {
public static void main(String[] args) {
BufferedReader_ bufferedReader_1 = new BufferedReader_(new FileReader());
bufferedReader_1.readFiles(5);
BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader());
bufferedReader_2.readString(10);
}
}
//输出
读取文件...
读取文件...
读取文件...
读取文件...
读取文件...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
(2)BufferedReader类
BufferedReader和BufferedWriter都是属于字符流,按照字符读取,因为是外层包内层,所以只需要关闭外层流,就可以关闭内层。
使用BufferedReader读取文本文件,并显示在控制台
@Test
public void testBufferedReader(){
String filePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
BufferedReader bufferedReader = null;
String line;
try {
bufferedReader = new BufferedReader(new FileReader(filePath));
//readLine就相当于是增加的功能,读取的为null的时候,说明读取完了
while ((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
debug进bufferedReader.close()方法,可以看到调用了fileReader.close();也就是把内层的流给关闭了
(3)BufferedWriter类
写入文件
@Test
public void testBufferedWriter() throws IOException {
String filePath = "d:\\test.txt";
BufferedWriter bufferedWriter = null;
bufferedWriter = new BufferedWriter(new FileWriter(filePath));
bufferedWriter.write("他时若遂凌云志");
//添加一个换行符
bufferedWriter.newLine();
bufferedWriter.write("敢笑黄巢不丈夫");
bufferedWriter.newLine();
bufferedWriter.close();
}
(4)综合-文件的拷贝
使用BufferedReader和BufferedWriter完成文本文件拷贝
public static void main(String[] args){
String srcFilePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
String descFilePath = "D:\\笔记和课程\\code review\\javase-io\\story1.txt";
BufferedReader br = null;
BufferedWriter bw = null;
String line;
try {
br = new BufferedReader(new FileReader(srcFilePath));
bw = new BufferedWriter(new FileWriter(descFilePath));
while ((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意:BufferedReader和BufferedWriter不要去操作二进制文件。
(5)BufferedInputStream
和BufferedReader相似。
(6)BufferedOutputStream
和BufferedWriter相似。
完成图片或音乐的拷贝
public static void main(String[] args){
String srcFilePath = "D:\\笔记和课程\\code review\\javase-io\\云彩.jpg";
String descFilePath = "D:\\笔记和课程\\code review\\javase-io\\云彩1.jpg";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(descFilePath));
byte[] buffer = new byte[1024];
int readLine = 0;
while ((readLine = bis.read(buffer)) != -1){
bos.write(buffer,0,readLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意,这个可以使用于文本文件。
(7)ObjectInputStream类
如果我们想把一个对象保存到文件中时,直接保存它的值是不行的,需要保存它的值和数据类型,这种操作就是序列化,反之,把文件中的数据值和数据类型恢复到程序中叫做反序列化。
这里可以实现两个接口实现:
- Serializable:这个很方便
- Externalizable:这个需要实现方法,不建议使用
使用ObjectInputStream来从文件中反序列化数据
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String filePath = "D:\\笔记和课程\\code review\\javase-io\\dog.txt";
ObjectInputStream ois = null;
ois = new ObjectInputStream(new FileInputStream(filePath));
//一定要安装顺序读取
System.out.println(ois.read());
System.out.println(ois.readBoolean());
System.out.println(ois.readDouble());
//注意这里取出的是Object类型,如果想用需要向下转型
Object dog = ois.readObject();
//向下转型如果Dog类不是公共的,会找不到,所以建议把Dog类提出来变为public
Dog dog1 = (Dog) dog;
((Dog) dog).eat();
ois.close();
}
}
(8)ObjectOutputStream类
使用ObjectOutputStream保存基本数据类型和应用数据类型对象到文件中
public class ObjectOutputStream_ {
public static void main(String[] args) throws IOException {
String filePath = "D:\\笔记和课程\\code review\\javase-io\\dog.txt";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
oos.write(100);
oos.writeBoolean(true);
oos.writeDouble(10.5);
oos.writeObject(new Dog("小花",5));
oos.close();
}
}
Dog类公开
//注意这里要实现Serializable接口
public class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void eat(){
System.out.println("eating...");
}
}
注意事项:
序列号:serialVersionUIO=1L,如果对需要序列化的类又添加新的属性,加了序列号的类就不会认为这是一个新的类。
static和transient修饰的测试:
public class Dog implements Serializable {
private String name;
private int age;
//一个static修饰
private static String type;
private transient int price;
public Dog(String name, int age, String type,int price) {
this.name = name;
this.age = age;
this.type = type;
this.price = price;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", price=" + price +
", type=" + type +
'}';
}
public void eat(){
System.out.println("eating...");
}
}
然后修改另外两个的代码,测试结果为:
100
true
10.5
//price和type就没有值,因为没有被序列化过来
Dog{name='小花', age=5, price=0, type=null}
eating...
2.5 标准输入输出流
| 名称 | 类型 | 默认设备 |
|---|---|---|
| System.in 标准输入 | InputStream | 键盘 |
| System.out 标准输出 | PrintStream | 显示器 |
看代码:
System.in其实就等于
编译类型就是InputStream
public final static InputStream in = null;
运行类型就是BufferedInputSstream
System.out其实就等于
编译类型就是PrintStream
public final static PrintStream out = null;
运行时类型也是PrintStream
2.6 转换流
(1)InputStreamReader类
主要用于解决文件字符集使用造成乱码。
先模拟编码字符集使用非utf-8造成读取文件出现的问题
@Test
public void test01() throws IOException {
//读取文件
String filePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
BufferedReader br = new BufferedReader(new FileReader(filePath));
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
注意把文件的编码改成别的试一下。
如何改进
把字符流改成字节流,这就用到了转换流。
@Test
public void test02() throws IOException {
//读取文件
String filePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"GBK");
BufferedReader bf = new BufferedReader(isr);
String line;
while ((line = bf.readLine()) != null){
System.out.println(line);
}
}
(2)OutputStreamWriter类
写入一些文本到文件,编码不要utf-8,看看是否会出现乱码
@Test
public void test03() throws IOException {
String filePath = "D:\\笔记和课程\\code review\\javase-io\\test.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
osw.write("转换流可以用来转换数据,it's nice");
osw.close();
}
2.7 打印流
只有输出流,没有输入流。
(1)PrintStream类
public class PrintStream_ {
public static void main(String[] args) {
//默认情况下,PrintStream输出的位置是标准输出(显示器)
PrintStream out = System.out;
out.print("2 * 5 = 10");
out.close();
}
}
这里看一下源码:
public void print(String s) {
if (s == null) {
s = "null";
}
//调用了write方法
write(s);
}
既然调用了write()方法,那么也可以直接使用write()方法:
public class PrintStream_ {
public static void main(String[] args) throws IOException {
//默认情况下,PrintStream输出的位置是标准输出(显示器)
PrintStream out = System.out;
out.print("2 * 5 = 10");
//等价于print()方法
out.write("2 * 5 = 10".getBytes());
out.close();
}
}
当然也可以设置输出的位置:
System.setOut(new PrintStream("d:\\test.txt"));
System.out.print("aaaaaaa");
(2)PrintWriter类
public class PrintWriter_ {
public static void main(String[] args) throws FileNotFoundException {
//输出到显示器上
//PrintWriter printWriter = new PrintWriter(System.out);
//输出到指定位置
PrintWriter printWriter = new PrintWriter(new PrintWriter("D:\\笔记和课程\\code review\\javase-io\\test.txt"));
printWriter.println("hi bob");
//不close()是不会输出的
printWriter.close();
}
}
2.8 Properties类
mysql的配置文件mysql.properties,如何读取值:
ip = 192.168.200.130
user = root
password = 123456
第一种方式:
@Test
public void getProperties01() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
String line = "";
while ((line = br.readLine()) != null){
String[] strings = line.split("=");
System.out.println(strings[0] + "的值是" + strings[1]);
}
br.close();
}
第二种方式:
@Test
public void getProperties02() throws IOException {
Properties props = new Properties();
props.load(new FileReader("src\\mysql.properties"));
System.out.println(props.getProperty("username"));
System.out.println(props.getProperty("password"));
System.out.println(props.getProperty("address"));
}