文件就是保存数据的地方
文件流:文件在程序中是以流的形式来操作的
java程序(内存)------>文件磁盘(输入流)
java程序(内存)<------文件磁盘(输出流)
流:数据在数据源(文件)和程序(内存)之间经历的路径
//演示创建文件
public class FileCreat {
public static void main(String[] args) {
}
//方式1 new File(String pathname) 根据路径构建一个File对象
public void creadte1(){
String filePath = "d:\\file\\file1.txt";
File file = new File(filePath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式2 new File(File parent,String child) 根据父目录文件+子路径构建
//d:\\file\\file2.txt
@Test
public void create2(){
File file = new File("d:\\");
String fileName = "file2.txt";
//这里的file对象,在java程序中,只是一个对象
File file1 = new File(file, fileName);
try {
//把对象写入磁盘
file.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式3 new File(File parent,String child) 根据父目录+子路径构建
public void create3(){
String parentPath = "d:\\";
//String parentPath = "d:/";
String filePath = "file3.txt";
File file = new File(parentPath, filePath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
常用的文件操作
获取文件的相关信息
public class FileInformation {
public static void main(String[] args) {
}
//获取文件信息
@Test
public void info(){
//创建文件对象
File file = new File("d:/file3.txt");
//调用相应的方法,得到对应信息
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("是不是一个文件=" + file.isFile());
System.out.println("是不是一个目录=" + file.isDirectory());
}
}
目录的操作和文件删除
mkdir创建一级目录
mkdirs创建多级目录
delete删除空目录或文件
public class Directory_ {
public static void main(String[] args) {
}
//判断d:file3.txt是否存在,如果存在就删除
public void m1(){
String filePath = "d:/file3.txt";
File file = new File(filePath);
if(file.exists()){
if(file.delete()){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}else {
System.out.println("文件不存在");
}
}
//判断d:\demo2是否存在,存在就删除
//在java中 目录也被当作文件
public void m2(){
String filePath = "d:/demo02";
File file = new File(filePath);
if(file.exists()){
if(file.delete()){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}else {
System.out.println("目录不存在");
}
}
//判断d:/demo/a/b/c目录是否存在,如果存在就提示已存在,否则就创建
@Test
public void m3(){
String filePath = "d:/demo/a/b/c";
File file = new File(filePath);
if(file.exists()){
System.out.println(filePath + "目录存在");
}else {
if(file.mkdirs()) {
System.out.println("目录创建成功");
}else {
System.out.println("目录创建失败");
}
}
}
}
流的分类
按操作数据单位不同分为:字节流(二进制文件),字符流(文本文件)
按数据流的流向分为:输入流,输出流
按流的角色不同分为:节点流,处理流/包装流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输入流 | OutputStream | Writer |
它们都是抽象类,要使用要用实际子类
由这四个类派生出的子类名称都是以其父类名作为子类名的后缀
字节流
FileInputStream
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
//演示FileInPutStream的使用(子节输入流 文件-->程序)
public class FileInPutStream_ {
public static void main(String[] args) {
}
//读取文件 单个子节的读取 效率较低
//@Test
public void readFile01(){
String filePath = "d:/hello.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建FileInputStream对象,用于读取文件
fileInputStream = new FileInputStream(filePath);
//读取一个字节数据,如果没有输入可用,此方法将阻止
//如果返回-1,表示读取完毕
while( (readData = fileInputStream.read())!=-1) {
System.out.println((char)readData);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭文件,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读取文件 字节数组
@Test
public void readFile02(){
String filePath = "d:/hello.txt";
//字节数组
byte[] buf = new byte[8];//一次读取8个字节
int readLength = 0;
FileInputStream fileInputStream = null;
try {
//创建FileInputStream对象,用于读取文件
fileInputStream = new FileInputStream(filePath);
//读取b.length字节数据,此方法将阻塞,指导某些输入可用;
//如果返回-1,表示读取完毕
//如果读取正常,返回实际读取的字节数
while( (readLength = (fileInputStream.read(buf))) !=-1) {
//显示
System.out.println(new String(buf,0,readLength));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭文件,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class FileOutputStream01 {
public static void main(String[] args) {
}
//使用FileOutputStream将数据写到文件中
//若文件不存在则创建该文件(前提是目录必须存在)
@Test
public void writeFile(){
//创建FileOutputStream对象
String filePath = "d:/a.txt";
FileOutputStream fileOutputStream = null;
try {
//得到一个对象
//说明:new FileOutputStream(filePath)创建方式,是覆盖
//new FileOutputStream(filePath,true) 写入内容是追加
fileOutputStream = new FileOutputStream(filePath);
//写入一个字节
//fileOutputStream.write('H');
//写入字符串
String str = "hello,world";
//str.getBytes() 可以把字符串转换成字节数组
// fileOutputStream.write(str.getBytes());
//write(byte[] b,int off,int len)将 len 字节从位于偏移量 off的指定字节数组写入此文件输出流
//off 起始位置 len字节数组长度
fileOutputStream.write(str.getBytes(),1,3);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
拷贝文件
完成文件拷贝
思路分析
1.创建文件输入流 将文件读入到程序
2.创建文件的输出流 将读取到的文件数据输出到指定文件
3.在完成程序时,读取部分数据就写入到文件里面,使用循环操作
文件字符流
FileReader相关方法
1.new FileReader(File/String)
2.read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
3.read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
相关API
1.new String(char[])将char[]转换成String
2.new String(char[],off,ien):将char[]的指定部分转换成String
FileWriter相关方法
1.new FileWriter(File/String):覆盖模式
2.new FileWriter(File/String,true):追加模式
3.write(int):写入单个字符
4.write(char[]):写入指定数组
5.write(char[],off,len):写入指定数组的指定部分
6.write(string):写入整个字符串
7.write(string,off,len):写入字符串的指定部分
相关API
String类:toCharArray:将String转换成char[]
注意
FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件
FileReader
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReader_ {
public static void main(String[] args) {
String filePath = "d:/story.txt";
//1.创建FileReader对象
FileReader fileReader = null;
int data = 0;
int readLength = 0;
char[] buf = new char[8];
try {
fileReader = new FileReader(filePath);
//单个字符读取
// while((data = fileReader.read()) != -1){
// System.out.print((char)data);
// }
//多个字符读取,返回的是实际读取到的字符数
//如果返回-1,说明到文件结束
while((readLength = fileReader.read(buf)) != -1){
System.out.print(new String(buf,0,readLength));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fileReader != null){
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileWriter
import java.io.FileWriter;
import java.io.IOException;
public class FileWriter_ {
public static void main(String[] args) {
String filePath = "d:/not.txt";
//1.创建一个FileWriter对象
FileWriter fileWriter = null;
char[] chars = {'a','b','c'};
try {
fileWriter = new FileWriter(filePath);
//1.写入单个字符
fileWriter.write('H');
//2.写入指定数组write(char[])
fileWriter.write(chars);
//3.write(char[],off,len)
fileWriter.write("你好,北京".toCharArray(),0,2);
//4.wirte(string):写入整个字符串
fileWriter.write("你好北京~");
//5.write(string,off,len)
fileWriter.write("上海深圳",0,2);
} catch (IOException e) {
e.printStackTrace();
} finally {
//对于FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("程序结束");
}
}
节点流和处理流
1.节点流可以从一个特定的数据源读写数据,如FileReader,FileWriter
2.处理流(也叫包装流)是“连接”在已经存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如CufferedReader,BufferedWriter(让节点流更加强大)
节点流和处理流的区别和联系
1.节点流是底层流/低级流,直接跟数据源相接
2.处理流(包装流)包装节点流,既可以消除不同节点流的现实差异,也可以提供更方便的方法来完成输入输出
3.处理流,使用了修饰器设计模式,不会直接与数据源相连接
处理流的功能主要体现在以下两个方面
1.性能的提高:主要以增加缓冲的方式来提高输入输出的效率
2.操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
BufferedReader
1.属于字符流,是按照字符来读取数据的
2.关闭处理流,只需要关闭外层流即可
3.读取文本文件
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferdReader_ {
public static void main(String[] args) throws Exception{
String filePath = "d:/story.txt";
//创建bufferReader
BufferedReader bufferedReader = new BufferedReader(new FileReader((filePath)));
//读取
String line;//按行读取
//说明
//1.bufferedReader.readLine()是按行读取文件
//2.当返回null时,表示文件读取完毕
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
//关闭流,只需要关闭BuffererdReader,因为底层会自动去关闭 节点流
bufferedReader.close();
}
}
BufferedWriter
import java.io.BufferedWriter;
import java.io.FileWriter;
public class BufferedWriter_ {
public static void main(String[] args) throws Exception{
String filePath = "d:/a.txt";
//创建一个BufferedWriter
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
//BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true)); 追加的方式
bufferedWriter.write("hi!");
//插入一个和系统相关的换行符
bufferedWriter.newLine();
bufferedWriter.write("hi!");
//关闭外层流即可
bufferedWriter.close();
}
}
BufferedInputStream,BufferedOutputStream
对象流-ObjectInputStream and ObjectOutputStream
序列化和反序列化
1.序列化就是在保存数据时,保存数据的值和数据类型(例如保存int 100)
2.反序列化就是在恢复数据时,恢复数据的值和数据类型
3.需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
Serializable//这是一个标记接口
Externalizable//该接口有方法需要实现
ObjectOutputStream
提供了序列化功能
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Obj_ {
public static void main(String[] args) throws Exception{
//序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
String filePath = "d:/data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
//序列化数据到 d:\\data.dat
oos.write(100);//int -> Interger(实现了 Serializable)
oos.writeBoolean(true);//boolean -> Boolean
oos.writeChar('a');//char -> Character
oos.writeDouble(8.8);//double -> Double
oos.writeUTF("你好北京");//String
//保存一个dog对象
oos.writeObject(new Dog("旺财",10));
oos.close();
System.out.println("数据保存完毕(序列化形式)");
}
}
//如果需要序列化某个对象的类,实现Serializable
class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
ObjectInputStream
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException,ClassNotFoundException{
//指定反序列化的文件
String filePath = "d:/data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
//读取
//读取(反序列化)的顺序需要和保存数据(序列化)的顺序一致,这个包里也需要有这个类
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Object dog = ois.readObject();
//dog的编译类型是Object
System.out.println("运行类型=" + dog.getClass());
System.out.println(dog); // 底层Obj -> Dog
//细节
//1.如果我们希望调用Dog的方法,需要向下转型
//2.还需要将Dog类,拷贝到可以引用的地方
Dog doge2 = (Dog)dog;
System.out.println(doge2.getName());
//关闭外层流即可
ois.close();
}
}
注意事项
1.读写顺序一致
2.要求实现序列化或反序列化,需要 实现Serializable
3.序列化的类中建议添加SerialVersionUID,为了提高版本兼容性
4.序列化对象时,默认将里面所有的属性都进行序列化,但除了static或transient修饰的成员
5.序列化对象时,要求里面属性的类型也需要实现序列化接口
6.序列化具备可继承性