16 File,IO流
File
构造方法
public File(String pathname)
public File(String parent,String child)
public File(File parent,String child)
创建删除重命名
public boolean createNewFile() //创建文件
public boolean mkdir() //创建单层文件夹
public boolean mkdirs() //创建多层文件夹
public boolean delete() //删除文件或文件夹(文件夹中有内容无法删除)
public boolean renameTo(File dest) //文件的重命名(文件的移动)
import java.io.File;
import java.io.IOException;
public class FileDemo1 {
public static void main(String[] args) throws IOException {
File file = new File("F:/a.java");
file.mkdirs();
//file.delete();
//file.createNewFile();
//file.renameTo(new File("b.java"));
}
}
判断功能
public boolean isDirectory() //是否是文件夹
public boolean isFile() //是否是文件
public boolean exits() //是否存在
public boolean canRead() //是否可读
public boolean canWrite() //是否可写
public boolean canExecute() //是否可执行
public boolean isHidden() //是否隐藏
import java.io.File;
public class FileDemo2 {
public static void main(String[] args) {
File file = new File("F:/a.txt");
System.out.println(file.isDirectory());
System.out.println(file.isFile());
System.out.println(file.exists());
System.out.println(file.canRead());
System.out.println(file.canWrite());
System.out.println(file.canExecute());
System.out.println(file.isHidden());
}
}
基本获取功能
public String getAbsolutePath() //获取绝对路径
public String getPath() //获取相对路径
public String getName() //获取文件名
public long length() //获取文件字节数
public long lastModified() //获取最后修改时间
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileDemo3 {
public static void main(String[] args) {
File f = new File("b.java");
System.out.println(f.getAbsolutePath());
System.out.println(f.getName());
System.out.println(f.getPath());
System.out.println(f.length());
System.out.println(f.lastModified());
Date d = new Date(f.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(d));
}
}
高级获取功能
public String[] list() //获取所有子文件名称
public File[] listFiles() //获取所有子文件对象
/**
* 判断单级目录下是否有后缀名为.jpg的文件,如果有,就输出次文件的名称
*
*/
import java.io.File;
public class FileDemo4 {
public static void main(String[] args) {
File file = new File("F://a");
File[] lf = file.listFiles();
for (int i = 0; i < lf.length; i++) {
if (lf[i].isFile()&&lf[i].getName().toLowerCase().endsWith(".jpg")) {
System.out.println(lf[i].getName());
}
}
}
}
过滤器的使用
public String[] list(FilenameFilter filter) //获取所有子文件名称
public File[] listFiles(FilenameFilter filter) //获取所有子文件对象
import java.io.File;
import java.io.FilenameFilter;
public class FileDemo5 {
public static void main(String[] args) {
File file = new File("F://a");
File[] lfs = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File f = new File(dir, name);
if (f.isFile()&&name.toLowerCase().endsWith(".jpg")) {
return true;
}
return false;
}
});
for (int i = 0; i < lfs.length; i++) {
System.out.println(lfs[i]);
}
}
}
递归
- 方法定义中调用方法本身的现象
注意事项 :
- 要有出口 , 否则就是死递归
- 次数不能太多 , 否则就内存溢出
- 构造方法不能递归使用
//递归遍历目录下指定后缀名结尾的文件名称
import java.io.File;
public class FileDemo6 {
public static void main(String[] args) {
File f = new File("D://develop");
getFile(f, ".java");
}
public static void getFile(File f, String name) {
if (f.isFile() && f.getName().toLowerCase().endsWith(".java")) {
System.out.println(f);
} else {
File[] lf = f.listFiles();
if (lf != null && lf.length > 0) {
for (int i = 0; i < lf.length; i++) {
getFile(lf[i], name);
}
}
}
}
}
//递归删除一个带内容的文件夹
import java.io.File;
public class FileDemo7 {
public static void main(String[] args) {
File f = new File("F:\\英雄时刻");
deleteFile(f);
}
public static void deleteFile(File f) {
if (f.isFile()) {
f.delete();
} else {
File[] lf = f.listFiles();
if (lf != null && lf.length>0) {
for (int i = 0; i < lf.length; i++) {
deleteFile(lf[i]);
}
}
System.out.println(f);
f.delete();
}
}
}
IO流
- I : Input 输入
- O : Output 输出
文件的本质
- 存储都是二进制的数据 . 我们能够看到数据的具体形态 , 都是因为各自的软件帮我们做了解码的工作
字节和字符的区别
- 字节是存储容量的基本单位 , 1字节 = 8个二进制 .
- 字符是指字母 , 数字 , 汉字和各种字符 .
- 一个字符在计算机中用若干个字节的二进制表示 .
- GBK : 中文 : 2个字节 英文/数字 : 1个字节
- UTF-8 : 中文 : 3个字节 英文/数字 : 1个字节
- 计算机中所有的数据可以使用字节表示
- 但是只有纯文本文件才能使用字符表示
读写和输入输出的关系
- 输入 : 读 read
- 输出 : 写 write
读进去 , 写出来
IO流的分类
-
从流向上划分 :
- 输入流 Input
- 输出流 Output
-
从处理单位上 :
- 字节流 以字节为单位进行处理 (任何文件)
- 字符流 以字符为单位进行处理 (纯文本文件)
-
两两组合得到四个基类
- 字节输入流 : InputStream
- 字节输出流 : OutputStream
- 字符输入流 : Reader
- 字符输出流 : Writer
- 凡是以InputStream结尾的 , 都是属于字节输入流
- 凡是以OutputStream结尾的 , 都是属于字节输出流
- 凡是以Reader结尾的 , 都是属于字符输入流
- 凡是以Writer结尾的 , 都是属于字符输出流
文件字节流
文件字节输出流
- FileOutputStream : 文件字节输出流 , 以字节位单位向文件中写数据
- 构造方法 :
FileOutputStream(String path) //路径代表写出到哪个文件
FileOutputStream(File file)
FileOutputStream(String path,boolean append) //append:true:如果源文件有内容,在内容后追加
FileOutputStream(File file,boolean append)
- 成员方法 :
public void write(int b) //写一个字节
public void write(byte[] b) //写一个byte数组
public void write(byte[] b,int off,int len) //写一个数组,从off开始,len个长度
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo1 {
public static void main(String[] args) throws IOException{
FileOutputStream fos1 = new FileOutputStream(new File("F:\\hello.txt"));
FileOutputStream fos2 = new FileOutputStream("F:\\java.txt");
fos1.write(97);
fos1.write("你这不是欺负老实人么".getBytes());
fos1.write("你这不是欺负老实人么".getBytes(), 0, 3);
}
}
文件字节输入流
- FileInputStream : 文件字节输入流 , 以字节为单位从文件中读取数据
- 构造方法 :
FileInputStream(String path) //代表从哪个文件中读取数据
FileInputStream(File file)
- 成员方法 :
public int read() //每次读取一个字节,并返回
public int read(byte[] b) //每次读取一个数组长度的字节,数组存到了数组中,返回真正读取回来的长度
import java.io.FileInputStream;
import java.io.IOException;
public class IODemo1 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("b.txt");
int i1 = fis.read();
System.out.println(i1);
int i2 = fis.read();
System.out.println(i2);
int i3 = fis.read();
System.out.println(i3);
String s = new String(new byte[] {(byte)i1,(byte)i2,(byte)i3});
System.out.println(s);
byte[] by = new byte[1024];
int len = fis.read(by);
System.out.println(len);
fis.close();
}
}
补:
-
JDK1.7以后自动关流
-
把流的定义语句放到try()中 , 这样当程序结束后 , 就可以帮我们关流了
-
在try()只能定义流的定义语句(实现AutoCloseable接口)
-
FileOutputStream(String path,boolean append)
- append : 是否追加
- true : 如果源文件已存在 , 且里面有内容 , 则写入的数据会在原来文件的内容后面继续写
- false : 如果源文件已存在 , 且里面有内容 , 会先将源文件中的内容清空 , 再写
- append : 是否追加
/**
* IO流的异常处理
*
*/
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo2 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("F://world.java");
fos.write("你这不是欺负老实人么".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (fos != null) {
fos.close();
}
fos = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* IO流的异常处理
*
*/
import java.io.FileOutputStream;
public class IODemo3 {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("F:\\world.java");){
fos.write("你这不是欺负老实人么".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
}
拷贝文件
- 使用字节流可以拷贝任意文件
流程 : 从一个文件中一个一个的读取数组 , 数组在一个一个的写到文件中
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("F:\\多态.png");
FileOutputStream fos = new FileOutputStream("D:\\1.png");
byte[] by =new byte[1024];
int len = 0;
while((len = fis.read(by)) != -1) {
fos.write(by , 0 , len);
}
fis.close();
fos.close();
}
}
缓冲字节流
-
为什么要是用缓冲字节流?
使用字节流每次从文件中进行读写时 , 都需要和文件进行大量的IO交互 , 与磁盘做交互的效率其实是比较低的 , 所以为了降低与磁盘交互的次数 , 可以使用缓冲字节流 . 缓冲字节流现将数据放到缓存区 , 我们直接和缓冲区做交互 . 可以提升效率 .
缓冲字节输出流
BufferedOutputStream(OutputStream)
- flush : 缓冲输出流中特有的方法 , 将缓冲区中的内容写到文件中
- close : 会先调用flush方法 , 再关流
缓冲字节输入流
BufferedInputStream(InputStream)
BufferedInputStream(InputStream,int size) //size:缓冲区大小,默认8K
拷贝文件
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo3 {
public static void main(String[] args) throws IOException {
String path = "D:\\文件\\doit-javaSE\\doit06-java编程思维模式训练.zip.fiq";
copyFile(path);
bufferedCopyFile(path);
}
private static void copyFile(String path) throws IOException {
long t1 = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream("F://c.fip");
byte[] by = new byte[1024*8];
int len = 0;
while((len = fis.read()) != -1) {
fos.write(by, 0, len);
}
fis.close();
fos.close();
long t2 = System.currentTimeMillis();
System.out.println("普通字节流复制:"+(t2-t1));
}
private static void bufferedCopyFile(String path) throws IOException {
long t1 = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F://b.fip"));
byte[] by = new byte[1024];
int len = 0;
while ((len = bis.read(by)) != -1) {
bos.write(by, 0, len);
}
bis.close();
bos.close();
long t2 = System.currentTimeMillis();
System.out.println("高效流的运行时间:" + (t2 - t1));
}
}
转换流(字符流)
- 字符流 : 字节流 + 编码
-
- 编码 : 把文字转成二进制
-
- 解码 : 把二进制转成文字
-
字符流只能处理纯文本文件(用记事本打开能看懂)
-
场景 : 也可以指定编码 ; 也可以把字节流转成字符流
字符输出流
- 构造方法 :
public OutputStreamWriter(OutputStream out)
public OutputStreamWriter(OutputStream out,String charsetName)
- 成员方法 :
public void write(int c)
public void write(char[] cbuf)
public void write(char[] cbuf,int off,int len)
public void write(String str)
public void write(String str,int off,int len)
字符输入流
- 构造方法 :
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in,String charsetName)
- 成员方法 :
public int read()
public int read(char[] cbuf)
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class IODemo4 {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("b.txt"));
char[] ch = new char[1024];
System.out.println(isr.read());
int len = isr.read(ch);
System.out.println(new String(ch,0,len));
isr.close();
}
}
拷贝文件
- 只能拷贝纯文本文件
public static void copyFileByInputStreamReaderAndOutputStreamWriter(String srcPath,String destPath) {
long start = System.currentTimeMillis();
try (
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcPath));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destPath));
){
char[] chs = new char[1024];
int len;
while((len=isr.read(chs))!=-1) {
osw.write(chs,0,len);
}
long end = System.currentTimeMillis();
System.out.println("转换字符流拷贝成功,耗时:"+(end - start)+"毫秒");
} catch (Exception e) {
e.printStackTrace();
}
}
简化流(字符流)
- 不能指定编码 , 也不能把字节流转成字符流
FileReader/FileWriter
public class FileWriterDemo {
public static void main(String[] args) {
try (
FileWriter fw = new FileWriter("d:/ccc.txt");//默认编码,无法指定编码.可以设置是否追加
){
fw.write("轻轻地我走的正如我轻轻的来,我挥一挥衣袖,和你说声goobye");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class FileReaderDemo {
public static void main(String[] args) {
try(
FileReader fr = new FileReader("d:/ccc.txt");
) {
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
拷贝文件
public static void copyFileByFileReaderAndFileWriter(String srcPath,String destPath) {
long start = System.currentTimeMillis();
try(
FileReader fr = new FileReader(srcPath);
FileWriter fw = new FileWriter(destPath);
) {
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!=-1) {
fw.write(chs,0,len);
}
long end = System.currentTimeMillis();
System.out.println("简化流拷贝成功,耗时:"+(end - start)+"毫秒");
} catch (Exception e) {
e.printStackTrace();
}
}
- 一个程序中先写后读的注意事项 :
- 如果在一个程序中 , 向同一个文件中写数据 , 并读回来
- 写完数据后 , 要关流 , 否则会导致输出流继续占用文件 , 这样输入流就无法读出数据
public class NoticeDemo {
public static void main(String[] args) {
try (
FileWriter fw = new FileWriter("d:/fff.txt");//代码执行到这里,会检查文件是否存在,不会再会自动创建
FileReader fr = new FileReader("d:/fff.txt");//代码执行到这里,会检查文件是否存在,不存在就报错 d:\fff.txt (系统找不到指定的文件。)
){
fw.write("燕雀安知鸿鹄之志哉");
fw.close();//一定要关流,否则继续占用文件,下面读取不出来
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
缓冲字符流
读和写
- BufferedWriter
void newLine()
- BufferedReader
String readerLine()
拷贝文件
public static void copyFileByBufferedReaderAndBufferedWriter(String srcPath,String destPath) {
long start = System.currentTimeMillis();
try(
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(srcPath)));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destPath)));
) {
char[] chs = new char[1024];
int len;
while((len=br.read(chs))!=-1) {
bw.write(chs,0,len);
}
long end = System.currentTimeMillis();
System.out.println("缓冲字符流拷贝成功,耗时:"+(end - start)+"毫秒");
} catch (Exception e) {
e.printStackTrace();
}
}
序列化和对象流
-
序列化 : 把对象转成二进制
-
反序列化 : 把二进制转成对象
-
持久化 : 把内存数据存储到磁盘上 ( 一般数据库 )
-
实现序列化步骤 :
- 让类实现Serialiazable接口
- 使用ObjectOutputStream 写数据 : 调用writerObject
- 使用ObjectInputStream 读数据 : 调用readObject
-
如果报错 :
- java.io.NotSerializableException : 检查是否实现Serializable接口
- local class incompatible : 是由于模板改变导致的 , 解决办法
- 重写一遍 , 再读
- 生成序列化ID
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class IODemo1 {
public static void main(String[] args) throws Exception {
Students st = new Students("小孔",25);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\a.txt"));
//开始序列化
oos.writeObject(st);
oos.close();
}
}
class Students implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Students() {
super();
// TODO Auto-generated constructor stub
}
public Students(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class IODemo2 {
public static void main(String[] args) throws Exception {
//创建反序列化对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\a.txt"));
//开始从文件上反序列化数据
Object obj = ois.readObject();
System.out.println(obj);
ois.close();
}
}
练习
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 复制单级文件夹中指定文件并修改文件名称
* @author Apase
*
*/
public class IODemo3 {
public static void main(String[] args) throws IOException {
//第一步:先去将a文件夹封装成数组
File f = new File("E://照片");
File[] ff = f.listFiles();
//目标路径
String dest = "D:/b/";
File file = new File(dest);
if (!file.exists()) {
file.mkdir();
}
//第二步:将每一个文件对象放到IO流里面进行读写操作
for (int i = 0; i < ff.length; i++) {
copyFile(dest,ff[i]);
}
}
/**
* 复制文件的方法
*
* @param dest
* @param file
* @throws IOException
*/
private static void copyFile(String dest, File file) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
File f = new File(dest,file.getName());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f));
byte[] by = new byte[1024];
int len = 0;
while((len = bis.read(by)) != -1) {
bos.write(by,0,len);
bos.flush();
}
bos.close();
bis.close();
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Scanner;
/**
* 将学生对象存储到ArrayList里面,然后序列化集合到文件上
* 以后在存储学生对象时,需要到集合中判断该学生对象是否存在
* 如果不在就重新存进去
*
*/
public class IODemo4 {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) throws Exception {
System.out.println("请输入功能:1.添加新同学,2.注销学生信息");
String s = sc.next();
// 添加新同学
if (s.equals("1")) {
addStudent();
System.out.println();
}
// 注销学生信息
if (s.equals("2")) {
cancelStudent();
System.out.println();
}
}
/**
* 添加新同学的方法
* @return
* @throws Exception
*/
private static ArrayList<Student> addStudent() throws Exception {
System.out.println("请输入新同学的姓名:");
String name = sc.next();
System.out.println("请输入新同学的年龄:");
int age = sc.nextInt();
// 对新同学进行赋值操作
Student st = new Student(name, age);
File file = new File("StudentList.txt");
ArrayList<Student> rs = new ArrayList<Student>();
if (file.exists()) {
//获取序列化到文件上的集合
rs = reverseSerial();
if (!rs.contains(st)) {
rs.add(st);
}
}else {
file.createNewFile();
rs.add(st);
}
//将添加以后的集合重新序列化到文件里面
serializableFile(rs);
return rs;
}
/**
* 注销学生信息的方法
* @return
* @throws Exception
* @throws FileNotFoundException
*/
private static ArrayList<Student> cancelStudent() throws FileNotFoundException, Exception {
System.out.println("请输入要注销的学生姓名:");
String name = sc.next();
System.out.println("请输入要注销的学生年龄:");
int age = sc.nextInt();
ArrayList<Student> rs = reverseSerial();
for (int i = 0; i < rs.size(); i++) {
Student stu = rs.get(i);
if (stu.getName().endsWith(name)&&stu.getAge()==age) {
rs.remove(i);
break;
}
}
serializableFile(rs);
return rs;
}
/**
* 序列化集合的方法
* @param rs
* @throws Exception
* @throws FileNotFoundException
*/
private static void serializableFile(ArrayList<Student> a) throws FileNotFoundException, Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("StudentList.txt"));
//序列化集合
oos.writeObject(a);
oos.close();
}
/**
* 反序列化集合的方法
* @return
* @throws Exception
* @throws FileNotFoundException
*/
private static ArrayList<Student> reverseSerial() throws FileNotFoundException, Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("StudentList.txt"));
ArrayList<Student> a = (ArrayList<Student>)ois.readObject();
ois.close();
return a;
}
}
class Student implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}