java I/O 学习笔记
一、文件对象
File类,就是文件对象
-
基础操作,文件删除,是否存在等
import java.io.File; public class javaDemo{ public static void main(String[] args) throws Exception { File file = new File("file.txt"); if (file.exists()){ // 文件是否存在 file.delete(); // 删除文件 } else{ file.createNewFile(); // 创建文件 } } }
-
父目录和创建目录
import java.io.File; public class javaDemo{ public static void main(String[] args) throws Exception { File file = new File("d:"+ File.separator+"file.txt"); //文件绝对路径,/可以用separator表示 if (!file.getParentFile().exists()){ // 获取file.txt的父目录 file.getParentFile().mkdirs(); // 创建父目录 } } }
-
获取文件信息
import java.io.File; public class javaDemo{ public static void main(String[] args) throws Exception { File file = new File("d:"+ File.separator+"file.txt"); System.out.println(file.canRead()); // 文件是否可读 System.out.println(file.canWrite()); System.out.println(file.canExecute()); System.out.println(file.isDirectory()); // 文件是目录吗 System.out.println(file.isFile()); System.out.println(file.length()); // 文件的大小,输出为字节,需要转化 } }
案例1:列出目录下的所有文件
import java.io.File; public class javaDemo{ public static void listDir(File file){ if (file.isDirectory()){ File result[] = file.listFiles(); // 列出该目录下所有的文件 if (result != null) { result = file.listFiles(); for (int i = 0; i < result.length; i++) { listDir(result[i]); // 递归调用,读取所有的文件 } } } System.out.println(file); } public static void main(String[] args) throws Exception { File file = new File("C:"+ File.separator+"Intel"); listDir(file); } }
案例2:文件批量更名
import java.io.File; public class javaDemo{ public static void renameFile(File file){ if (file.isDirectory()){ File result[] = file.listFiles(); if (result != null) { result = file.listFiles(); for (int i = 0; i < result.length; i++) { renameFile(result[i]); } } } else{ if (file.isFile()){ String newName = null; String lastName = ".java"; String newLastName = ".txt"; if (file.getName().endsWith(lastName)){ newName=file.getName().substring(0,file.getName().lastIndexOf("."))+newLastName; File newfile = new File(file.getParentFile(), newName); file.renameTo(newfile); } } } System.out.println(file); } public static void main(String[] args) throws Exception { // 给定目录,将后缀为.java的文件转为后缀为.txt的文件 File file = new File("C:"+ File.separator+"Intel"); renameFile(file); } }
二、文件读写
1. 字节流
1.1 OutputStream 和 InputStream
OutputStream 和 InputStream 定义了字节的输入和输入流,java的输入和输出都是这两个类的衍生,由于这两个类是abstract类,所以需要其子类的实例化对象,实现对字节的操作
1.2 FileOutputStream 和FileInputStream
这两个继承OutputStream and InputStream 从而实现对字节的操作
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
OutputStream out = new FileOutputStream(file); // 实例化OutputStream对象
String str = "content";
String newStr = "new content";
out.write(str.getBytes()); // 字符串需要转换为字节,才能写入
out.write(newStr.getBytes()); // 文件内容追加
out.close();
InputStream in = new FileInputStream(file);
byte[] data = new byte[1024]; // 生成一个位于内存中的字节数组缓冲区,存储读取的内容
// byte[] data = in.readAllBytes(); // 直接读取所有的数据,存入缓冲区,字节不能过大
int len = in.read(data); // 将in的内容读取到data数组中,并返回这个数组的大小
System.out.println(new String(data, 0, len)) // 将缓冲区中的字节转化为字符串输出到屏幕
in.close();
}
}
2. 字符流
2.1 Writer 和 Reader
这两个类用来实现对字符流进行操作,但是为抽象类,因此需要子类才能实例化对象
2.2 FileWriter 和 FileReader
FileWriter and FileReader分别是OutputStreamWriter 和 InputStreamReader的子类,这就表示,字符流其实是字节流转换之后的东西
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
Writer out = new FileWriter(file);
out.write("content");
out.append("new content");
out.close();
Reader in = new FileReader(file);
char[] data = new char[1024]; // 创建存储字符的数组
in.skip(9); // 跳过九个字符再进行读取
int len = in.read(data);
System.out.println(new String(data, 0, len));
in.close();
}
}
3. 字符流和字节流的区别
字节是程序的底层体现,字符则是字节的整合
字节流可以直接进行操作,但是字符流需要先读取到缓冲区,再进行转换成字符输出。因此字符流如果不适用out.close()
方法关闭,必须使用out.flush()
方法刷新缓冲区,才能将字符内容输出。
3.1 字符流和字节流的转换
OutputStreamWriter 和 InputStreamReader 可以实现字符流和字节流的转换,这两个类分别是Writer和Reader的子类
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
OutputStream output = new FileOutputStream(file); // 写入字节流
Writer out = new OutputStreamWriter(output); // 字节流转换为字符流
out.write("content"); // 实现字符流的写入
out.append("new content");
out.flush();
InputStream input = new FileInputStream(file); // 文件读取为节流
Reader in = new InputStreamReader(input); // 转换
char[] data = new char[1024];
int len = in.read(data);
System.out.println(new String(data, 0, len));
in.close();
}
}
三、字符编码
1. 获取系统默认编码
编码就是字符的语言,不同的编码方式就是英语和中文的区别,写入和读取的编码方式不同,就会出现乱码
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("file.encoding"));
OutputStream out = new FileOutputStream("file.txt");
out.write("content".getBytes("ISO8859-1")); // 字符转为ISO编码,并写入到文件中
out.close();
}
}
输出:UTF-8
文件内容为乱码显示
四、内存操作流
内存操作流直接在内存中进行操作,而不是磁盘数据操作
1. 字节内存操作流
1. ByteArrayOutputStream 和ByteArrayInputStream
2. 字符内存操作流
1. CharArrayOutputStream 和CharArrayInputStream
现在被Scanner类取代
五、管道流(未学习
进行两个线程间的通信
六、RandomAccessFile
大文件的读取和小文件的读取逻辑不同,这个类主要用来实现较大文件的读取
通过对文件内部读取位置的自由定义,以实现部分数据的读取操作,所以写入时就必须保证写入数据格式与长度的统一
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
String name[] = new String[] {"zhangsan","lisi ", "wangwu "}; // 保证名字的8个字节必须相同
int age[] = new int[]{30,20,14};
for (int x= 0;x<name.length;x++){
raf.write(name[x].getBytes());
raf.writeInt(age[x]); // 写入整数
}
raf.close();
}
}
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
{
raf.skipBytes(24); // 跳过24个字节
byte[] data = new byte[8];
int len = raf.read(data);
// trim方法去除空格
System.out.println("姓名: "+new String(data, 0, len).trim()+ "、年龄: "+ raf.readInt());
}
{
raf.seek(12);// 回跳12字节
byte[] data = new byte[8];
int len = raf.read(data);
System.out.println("姓名: "+new String(data, 0, len).trim()+ "、年龄: "+ raf.readInt());
}
{
raf.seek(0);
byte[] data = new byte[8];
int len = raf.read(data);
System.out.println("姓名: "+new String(data, 0, len).trim()+ "、年龄: "+ raf.readInt());
}
raf.close();
}
}
七、打印流
打印流是对OutputStream和Writer的装饰设计类,用来简化输入输出操作
1.字节打印流(PrintStream)和 字符打印流(PrintWriter)
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception{
File file = new File("file.txt");
PrintWriter pu = new PrintWriter(new FileOutputStream(file));
pu.println("姓名: 张三");
pu.print("年龄: 13");
pu.close();
}
}
2. 格式化输出
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception{
File file = new File("file.txt");
PrintWriter pu = new PrintWriter(new FileOutputStream(file));
String name = "张三";
int age = 18;
double salary = 16243.243;
pu.printf("姓名:%s、年龄:%d、收入:%9.2f", name, age, salary); // 格式化输出
pu.close();
}
}
%s
:字符串、%d
整数、%m.nf
浮点数、%c
字符
八、System类对I/O的支持
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception{
InputStream in = System.in;
byte[] data = new byte[1024];
int len = in.read(data);
System.out.println(new String(data, 0, len));
in.close();
}
}
System.in 实现键盘数据的输入,但缺陷如下
- 数据的接受需要一个字节数组完成,如果超出数组长度,会出现数据丢失
- System.in为输入字节流,所以对中文的支持不够好,其相当于一个InputStream的对象
九、BufferedReader缓冲输入流
该类与System.in结合实现合理的键盘输入数据操作
该类提供了一种字符流的缓冲区数据读取,利用此类可以将读取到的数据保存在缓冲区内,而后利用内部提供的方法将读取的内容一次性读取
这个类只能接受Reader in的对象,所以要将System.in通过InputStreamReader转化为Reader对象
import java.io.*;
public class javaDemo{
public static void main(String[] args) throws Exception{
// 将字节流System.in转换为字符流对象
BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入您的年龄:");
String msg = buffer.readLine(); // 一次性读取缓冲区全部的内容
if (msg.matches("\\d{1,3}")){
int age = Integer.parseInt(msg);
System.out.println("年龄为:"+age);
} else{
System.out.println("输入的内容不是数次,错误!");
}
}
}
十、Scanner类输入流工具
案例1:判断输入是否为日期
public class javaDemo{
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in); // 可以接收Readable、File、InputStream类对象
if (scan.hasNext("\\d{4}-\\d{2}-\\d{2}")){ //判断读取的内容是否为指定格式
String str = scan.next("\\d{4}-\\d{2}-\\d{2}"); //读取输入
System.out.println("出生日期: "+ (new SimpleDateFormat("yyyy-MM-dd").parse(str))); //转换
} else{
System.out.println("ERROR!");
}
scan.close();
}
}
案例2:读取文件—文件分割符
import java.io.*;
import java.util.Scanner;
public class javaDemo{
public static void main(String[] args) throws Exception{
Scanner scan = new Scanner(new File("file.txt")); // 接收FIle类对象
scan.useDelimiter("\n"); // 将换行符作为默认文件分割符
while (scan.hasNext()){
System.out.println(scan.next());
}
scan.close();
}
}
十一、对象序列化
把对象变为二进制数据流的一种方法,从而实现对象的传输和存储
1.Serializable接口
class Member implements Serializable{}
允许使用该接口的Member类的实例化对象进行序列化处理
2.ObjectOutputStream 与 ObjectInputStream
import java.io.*;
class Member implements Serializable{ // 生成一个可以序列化的类,这个类的实例化对象可以被序列化处理
private String name;
private int age;
public Member(String name, int age){
this.name = name;
this.age = age;
}
public String toString(){
return "name: "+this.name + "\n"+ "age: "+ this.age;
}
}
public class javaDemo{
private static final File SAVE_FILE = new File("object.member");
public static void saveObject(Object obj) throws Exception{
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(SAVE_FILE));
out.writeObject(obj); // 将对象变为二进制数据写入文件中
out.close();
}
public static Object loadObject() throws Exception{
ObjectInputStream in = new ObjectInputStream(new FileInputStream(SAVE_FILE));
Object obj = in.readObject(); //读取存储对象的文件,返回这个对象
in.close();
return obj;
}
public static void main(String[] args) throws Exception {
saveObject(new Member("bob", 12));
System.out.println(loadObject());
}
}
3. transient关键字
private transient String name
加上这个关键字后,对象序列化处理时,将不会将name属性进行序列化处理,也就是不会保存到对象序列化文件中,在进行反序列化操作时,name属性就是其对应数据类型的默认值。
总结
- java中使用File表示文件本身,从而实现各种文件操作
- 输入流和输出流,主要分为字符流和字节流,字节流是数据传输的基本形式,字符流需要用到缓冲区进行处理,而字节流不用缓冲区,字符流更适合中文操作
- 字节流和字符流都是以抽象类的形式定义的,根据其子类的不同,输入或输出的位置也不同,如果使用文件流进行对象化,则输入输出的终端位置就是文件;如果使用内存流实例化,则终端是内存。体现了java面向对象多态性的设计特点
- 程序将数据写入文件是,可以使用PrintStream, PrintWriter简化输出操作
- System提供3个支持I/O操作的常亮
- out: 对应显示器的标准输出
- err:对应错误信息打印,一般此消息不希望用户看到
- in:对应着标准键盘输入
- 使用Scanner类可以方便的进行输入流操作,读取前先使用
hasNextXxx()
判断是否有指定类型的数据,再使用nextXxx()
方法获取数据内容 - 对象序列化就是将对象转为二进制数据,但对象所在的类必须有Serializable接口,类中的属性如果有transient声明,则此属性内容不会被序列化