java文件操作主要封装在Java.io.File中,而文件读写一般采用的是流的方式,Java流封装在 java.io 包中。Java中流可以理解为一个有序的字符序列,从一端导向到另一端。建立了一个流就好似在两个容器中建立了一个通道,数据就可以从一个容器流到另一个容器

文件操作

Java文件操作使用 java.io.File 类进行。该类中常见方法和属性有:

  • static String pathSeparator: 多个路径间的分隔符,这个分隔符常用于系统的path环境变量中。Linux中采用 : Windows中采用 ;
  • static String separator: 系统路径中各级目录的分隔符,比如Windows路劲 c:\windows\ 采用的分隔符为 \, 而Linux中 /root 路径下的 分隔符为 /

为了达到跨平台的效果,在写路径时一般不会写死,而是使用上述几个静态变量来进行字符串的拼接

构造方法有:

  • File(String pathname); 传入一个路径的字符串
  • File(String parent, String child); 传入父目录和子目录的路径,系统会自动进行路径拼接为一个完整的路径
  • File(File parent, String child); 传入父目录的File对象和子目录的路径,生成一个新的File对象

常见方法:

  • 以can开头的几个方法,用于判断文件的相关权限,比如可读、可写、可执行
  • String getAbsolutePath() 获取文件绝对路径的字符串
  • String getPath() 获取文件的路径,这个方法会根据构造时传入的路径来决定返回绝对路径或者相对路径
  • String getName() 获取文件或者路径的名称
  • long length() 返回文件的大小,以字节为单位,目录会返回0;
  • boolean exists(); 判断文件或者目录是否存在
  • boolean isDirectory(); 判断对应的File对象是否为目录
  • boolean isFile(); 判断对应的File对象是否为文件
  • boolean delete(); 删除对应的文件或者目录
  • boolean mkdir(); 创建目录
  • boolean mkdirs(); 递归创建目录
  • String[] list(); 遍历目录,将目录中所有文件路径字符串放入到数组中
  • File[] listFiles(); 遍历目录,将目录中所有文件和目录对应的File对象保存到数组中返回

下面是一个遍历目录中文件的例子

public static void ResverFile(String path){
File f = new File(path);
ResverFile_Core(f);
} public static void ResverFile_Core(File f){
//System.out.println("开始遍历目录:" + f.getAbsolutePath());
File[] subFile = f.listFiles(); for(File sub : subFile){
if(sub.isDirectory()){
if(".".equals(sub.getName()) || "..".equals(sub.getName())){
continue;
} ResverFile_Core(sub);
}else{
System.out.println(sub.getAbsolutePath());
}
}
}

上述代码根据传入的路径,递归遍历路径下所有文件。

从 JDK文档中可以看到 list 和listFiles方法都可以传入一个FileFilter 或者FilenameFilter 的过滤器, 查看一下这两个过滤器:

public interface FilenameFilter{
boolean accept(File dir, String name);
} public interface FileFilter{
boolean accept(File pathname);
}

上述接口都是用来进行过滤的,FilenameFilter 会传入一个目录的File对象和对应文件的名称,我们在实现时可以根据这两个值来判断文件是否是需要遍历的,如果返回true则结果会包含在返回的数组中,false则会舍去结果

将上述的代码做一些改变,该成遍历所有.java 的文件

public static void ResverFile(String path){
File f = new File(path);
ResverFile_Core(f);
} public static void ResverFile_Core(File f){
//System.out.println("开始遍历目录:" + f.getAbsolutePath());
File[] subFile = f.listFiles(pathname->pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java")); for(File sub : subFile){
if(sub.isDirectory()){
if(".".equals(sub.getName()) || "..".equals(sub.getName())){
continue;
} ResverFile_Core(sub);
}else{
System.out.println(sub.getAbsolutePath());
}
}
}

IO 流

Java将所有IO操作都封装在了 java.io 包中,java中流分为字符流(Reader、Writer)和字节流(InputStream、OutputStream), 它们的结构如下:

字节流读写文件

在读写任意文件时都可以使用字节流进行,文件字节流是 FileInputStream和FileOutputStream

//可以使用路径作为构造方式
//FileInputStream fi = new FileInputStream("c:/test.dat");
//可以使用File对象进行构造
FileInputStream fi = new FileInputStream(new File("c:/test.dat"));
int i = fi.read();
byte[] buffer = new byte[1024];
while(fi.read(buffer) > 0 ){
//do something
}
fi.close();

下面是一个copy文件的例子

public static void CopyFile() throws IOException{
FileInputStream fis = new FileInputStream("e:\\党的先进性学习.avi");
FileOutputStream fos = new FileOutputStream("党的先进性副本学习.avi"); int len = 0;
byte[] buff = new byte[1024];
long start = System.currentTimeMillis();
while((len = fis.read(buff)) > 0){
fos.write(buff, 0, len);
}
long end = System.currentTimeMillis(); System.out.println("耗时:" + (end - start));
fos.close();
fis.close();
}

字符流读写文件

一般在读写文本文件时,为了读取到字符串,使用的是文件的字符流进行读写。文件字节流是FileReader和FileWriter

FileReader fr = new FileReader(new File("c:/test.dat"));
char[] buffer = new char[]
while(fr.read(buffer) > 0 ){
//do something
}
fr.close();

下面是一个拷贝文本文件的例子

public static void CopyFile() throws IOException{
FileReader fr = new FileInputStream("e:\\党的先进性学习.txt");
FileWriter fw = new FileOutputStream("党的先进性副本学习.txt"); int len = 0;
char[] buff = new char[1024];
long start = System.currentTimeMillis();
while((len = fr.read(buff)) > 0){
fw.write(buff, 0, len);
}
long end = System.currentTimeMillis(); System.out.println("耗时:" + (end - start));
fr.close();
fw.close();
}

读写IO流的其他操作

IO流不仅能够读写磁盘文件,在Linux的哲学中,一切皆文件。根据这点IO流是可以读写任意设备的。比如控制台;

之前在读取控制台输入的时候使用的是Scanner,这里也可以使用InputStream或者InputStreamReader。Java中定义了用于控制台输入输出的InputStream 和 OutputStream 对象: System.in 和 System.out

//多次读取单个字符
char c;
InputStreamReader isr = new InputStreamReader(System.in);
System.out.println("输入字符, 按下 'q' 键退出。");
// 读取字符
do {
c = (char) isr.read();
System.out.println(c);
} while (c != 'q'); isr.close(); //读取字符串
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter lines of text.");
System.out.println("Enter 'end' to quit.");
do {
str = br.readLine();
System.out.println(str);
} while (!str.equals("end"));
br.close();

控制台的写入与读取类似

OutputStreamWriter ow = new OutputStreamWriter(System.out);
char[] buffer = new char{'a', 'b', 'c'};
ow.write(buffer);
ow.flush();
ow.close();

由于write函数的功能有限,所以在打印时经常使用的是 System.out.println 函数。

缓冲流

在操作系统中提到内存的速度是超过磁盘的,在使用流进行读写操作时,CPU向磁盘下达了读写命令后会长时间等待,影响程序效率。而缓冲流在调用write和read方法时并没有真正的进行IO操作。而是将数据缓存在一个缓冲中,当缓冲满后或者显式调用flush 后一次性进行读写操作,从而减少了IO操作的次数,提高了效率。

常用的缓冲流有下面几个

  • BufferedInputStream
  • BufferedOutputStream
  • BufferReader
  • BufferWriter

分别对应字节流和字符流的缓冲流。它们需要传入对应的Stream 或者Reader对象。

下面是一个使用缓冲流进行文件拷贝的例子,与上面不使用缓冲流的拷贝进行对比,当文件越大,效率提升越明显

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\test.avi"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.avi")); int len = 0;
byte[] buff = new byte[1024];
long start = System.currentTimeMillis();
while((len = bis.read(buff)) > 0){
bos.write(buff, 0, len);
}
long end = System.currentTimeMillis(); System.out.println("耗时:" + (end - start));
bos.close();
bis.close();

文件编码转换

在读取文件时经常出现乱码的情况,乱码出现的原因是文件编码与读取时的解码方式不一样,特别是出现中文的情况。

上面说过Java 中主要有字符流和字节流。从底层上来说,在读取文件时都是二进制的数据。然后将二进制数据转化为字符串。也就是先有InputStream/OutputStream 读出二进制数据,然后根据默认的编码规则将二进制数据转化为字符也就是 Reader/Writer。如果读取时的编码方式与文件的编码方式不同,则会出现乱码。

我们在程序中使用 InputStreamReader和 OutputStreamWriter 来设置输入输出流的编码方式。

//以UTF-8方式写文件
FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
osw.write(FileContent);
osw.flush();
//以UTF-8方式读文件
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
FileContent += line;
}

序列化与反序列化

在程序中经常需要保存类的数据,如果直接使用OutputStream 也是可以保存类数据的,但是需要考虑类中有引用的情况,如果里面有引用,需要保存引用所对应的那块内存。每个类都需要额外提供一个方法来处理存在引用成员的情况。针对这种需求,Java提供了序列化与反序列化的功能

Java序列化与反序列化可以使用ObjectOutputStream 和 ObjectInputStream。

public class Student{
public String name;
public int age;
public Date birthday;
}

比如我们要序列化 上述的 Student 类,可以使用下面的代码

ObjectOutputStream oos = ObjectOutputStream(new FileOutputStream("student.dat"));

Student stu = new Student();
stu.name = "Tom";
stu.age = 22;
stu.brithday = new Date();
oos.writeObject(stu);

当然如果要进行序列化和反序列化操作,必须要在类中实现Serializable接口, 这个接口没有任何方法它仅仅作为一个标志,拥有这个标志的方式才能进行序列化。也就是得将上述的Student 类做一个修改

public class Student implements Serializable{
public String name;
public int age;
public Date birthday;
}

类的静态变量在类的对象创建之前就加载到了内存中。它与具体的类对象无关,所以在序列化时不会序列化静态成员。如果有的成员不想被序列化,可以将它变为静态成员;但是从设计上来说,也不是所有的类成员都可以变为静态成员。为了保证非静态成员可以不被序列化,可以使用 transient 关键字

实现了serialiable 接口的类在保存为.class文件 时会增加 一个SerializableID, 序列化时会在对应文件中保存序列号,如果类发生了修改而没有进行序列化操作时,二者不同会抛出一个异常。

例如说上述的Student类中先进行了一次序列化,在文件中保存了一个ID,后来根据需求又增加了一个 id 字段,在编译后又生成了一个ID,如果这个时候用之前的文件来反序列化,此时就会报错。

为了解决上述问题,可以采用以下几种方法:

  1. 改类代码文件后重新序列化。
  2. 增加一个 static final long serialVerssionID = xxxx; 这个ID是之前序列化文件保存的ID。这个操作是为了让新修改的类ID与文件中的ID相同。

调用 writeObject 方法时一个文件只能保存一个对象的内容。为了使一个文件保存多个对象,可以使用集合保存多个对象,在序列化时序列化 这个集合


Java 学习笔记(14)—— 文件操作的更多相关文章

  1. 【java学习笔记】文件操作

    文件操作 java.io.File ①创建删除文件及目录 ②查看文件及目录属性 ③文件过滤器 (PS:不包括文件读写数据) 1.单个文件 创建单个文件,查看属性,删除单个文件. package tmp ...

  2. java学习笔记07--日期操作类

    java学习笔记07--日期操作类   一.Date类 在java.util包中定义了Date类,Date类本身使用非常简单,直接输出其实例化对象即可. public class T { public ...

  3. node 学习笔记 - fs 文件操作

    本文同步自我的个人博客:http://www.52cik.com/2015/12/03/learn-node-fs.html 最近看到群里不少大神都开始玩 node 了,我感觉跟他们步伐越来越大了, ...

  4. node学习笔记3——文件操作fs

    文件操作关键字: http('fs') ——  请求 node 里面的 http 模块 readFile ——  读文件,参数包括 文件名,回调函数 writeFile ——  写文件,参数包括 文件 ...

  5. python学习笔记(三):文件操作和集合

    对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件 3.关闭文件. 文件基本操作: f = open('file.txt','r') #以只读方式打开一个 ...

  6. Java学习之==>IO文件操作体系

    一.概述 在整个 Java.io 中最重要的就是5个类和一个接口.5个类指的是 File.InputStream.OutputStream.Reader.Writer,一个接口指的是Serializa ...

  7. python学习笔记之文件操作(三)

    这篇博客小波主要介绍一下python对文件的操作 对文件的操作主要分为三步: 1.打开文件获取文件的句柄,句柄也是文件描述符 2.通过文件句柄操作文件 3.关闭文件. 现有以下文件,是小波随写的周杰伦 ...

  8. python学习笔记4(文件操作)

    文件操作: 1.f=open(”caidan”,”w”,encoding=”utf8”)      直接打开一个文件,如果文件不存在则创建文件 f.close() 2.with open (”caid ...

  9. python学习笔记三 文件操作(基础篇)

    文件操作 打开文件 open(name[,mode[,buffering]])   open函数使用一个文件名作为强制参数,然后返回一个文件对象.[python 3.5 把file()删除掉]   w ...

随机推荐

  1. IDEA-servlet项目创建web项目

    准备:1. 安装jdk1.82. 安装tomcat9.0(idea只支持4.0  9.0的服务器) 一.创建并设置javaweb工程 1.创建javaweb工程File --> New --&g ...

  2. python 并发之线程

    一.什么是线程 #指的是一条流水线的工作过程,关键的一句话:一个进程内最少自带一个线程,其实进程根本不能执行,进程不是执行单位,是资源的单位,分配资源的单位 #线程才是执行单位 #进程:做手机屏幕的工 ...

  3. java 操作Oracle 批量入库的问题

    java 操作Oracle 批量入库的问题 先说下我运行的环境: Windows7 64位操作系统 (四核)Intel i5-2300 CPU @2.80GHz 内存4G 硬盘1T Jdk1.6+My ...

  4. python 常见包中的不定参数

  5. Mybatis表关联多对多

    创建表 创建表对应的 JavaBean 对象 package com.tanlei.newer.model; import java.util.List; /** * @author:Mr.Tan * ...

  6. 如何用django框架完整的写一个项目

    实现目标及功能,增删改,并且实现搜索,分页,日期插件,删除提示,以及批量导入等功能 软件版本: python3.5 django1.11 一  用pycharm创建一个项目,名字自定义 二 编辑url ...

  7. HZOJ 回家

    这篇博客大部分在写我的错解……明明很简单的一道题,知道正解后10分钟AC,然而几个错解打的想死…… 错解1 WA40: 鬼知道这40分哪来的……这也是考试最后很无奈地交上去的代码,最后剩20分钟时发现 ...

  8. laravel 验证码手机与提交手机的验证?

    假如我用自己的手机号码获得了验证码,然后在点击提交之前,更换了手机号一栏的input,用一个比如18888888888的手机号进行注册,用之前得到的验证码,是不是会出现注册成功的情况?是否应该考虑验证 ...

  9. Open Source GIS and Freeware GIS Applications

    Open Source GIS and Freeware GIS Applications   An open source application by definition is software ...

  10. vue简单总结

    首先  介绍几个常见指令 指令:以属性的形式出现在标签上 v-xxx 1.内置指令 数据绑定指令  v-html v-text    举例 <span v-html="msg" ...