JavaSE 学习笔记07丨IO流
Chapter 13. IO流
13.1 File类
java.io.File
类是文件(file)和目录(文件夹)(directory)路径名(path)的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
一个
File
对象,代表硬盘中实际存在的一个文件或者目录(文件夹)。
- 绝对路径:从盘符开始的路径,是完整的路径。eg:
D:\\aaa\\233.txt
- 相对路径:相对于项目目录的路径,是便携的路径(开发常用)。eg:
233.txt
13.1.1 构造方法
无论该路径下是否存在文件或者目录,都不影响File
对象的创建。
public File(String pathname)
:通过将给定的路径名字符串pathname
转换为抽象路径名,以创建新的File
实例。String pathname = "D:\\aaa\\233.txt";
File myfile = new File(pathname);
public File(String parent, String child)
:从父路径名字字符串和子路径名字符串,创建新的File
实例。String parent = "D:\\aaa";
String name = "233.txt";
File myfile = new File(parent, child);
public File(File parent, String child)
:从父抽象路径名和子路径名字符串,创建新的File
实例。File parentDir = new File("D:\\aaa");
String child = "233.txt";
File myfile = new File(parentDir, child);
13.1.2 关于获取信息的方法
public String getAbsolutePath()
:返回此File
的绝对路径名的字符串。File f = new File("E:\\素材\\灵感\\GVLOGO2333332.png");
System.out.println("文件的绝对路径为:" + f.getAbsolutePath());
//输出结果为:文件的绝对路径为:E:\素材\灵感\GVLOGO2333332.png File f_2 = new File("灵感\\GVLOGO2333332.png"); //传入相对路径
System.out.println("文件的绝对路径为:" + f_2.getAbsolutePath()); //自动找到绝对路径,但不一定与实际相符。
//输出结果为:E:\IDEA-Projects\basic-codes\灵感\GVLOGO2333332.png
public String getPath()
:将此File
转换为路径名的字符串。File f = new File("E:\\素材\\灵感\\GVLOGO2333332.png");
System.out.println("文件的构造路径为:" + f.getPath());\
//输出结果为:文件的构造路径为:E:\素材\灵感\GVLOGO2333332.png File f_2 = new File("灵感\\GVLOGO2333332.png");
System.out.println("文件的构造路径为:" + f_2.getPath());
//输出结果为:文件的构造路径为:灵感\GVLOGO2333332.png
public String getName()
:返回由此File
表示的文件或目录的名称字符串。public long length()
:返回由此File
表示的文件的大小(以字节为单位)。
13.1.3 关于判断的方法
public boolean exists()
:此File
表示的文件或目录是否实际存在。public boolean isDirectory()
:此File
表示的是否为目录。public boolean isFile()
:此File
表示的是否为文件。
13.1.4 关于创建或删除的方法
public boolean createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。当该文件在实际当中并不存在时,返回true
(表示创建文件);否则,返回false
(不会再创建)。File f = new File("233.txt");
System.out.println("是否存在:" + f.exists()); //false
System.out.println("是否创建:" + f.createNewFile()); //注意,该方法需要对IO异常进行处理
System.out.println("是否存在:" + f.exists()); //true
注意,此方法只能创建文件,不能创建文件夹。且要保证创建文件的路径必须存在,否则抛出异常。
public boolean mkdir()
:创建由此File
表示的单级空目录。File f = new File("newDir2333");
System.out.println("是否存在:" + f.exists());
System.out.println("是否创建:" + f.mkdir());
System.out.println("是否存在:" + f.exists());
public boolean mkdirs()
:创建由此File
表示的目录,包括任何必需但不存在的父目录。也就说,既可创建单级空文件夹,也可创建多级文件夹。其中创建文件夹的路径和名称在构造方法中给出(即构造方法的参数)。当文件夹不存在时,方法会创建文件夹并返回
true
;当文件夹在实际中已经存在,则不会创建并返回false
;当构造方法中给出的路径不存在,返回false
。File f = new File("newDir666\\newDir668");
System.out.println("是否创建:" + f.mkdirs()); //true
File f2 = new File("newDir666\\newDir668");
System.out.println("是否存在:" + f2.mkdirs()); //false, 无需创建
public boolean delete()
:删除由此File
表示的文件或目录。当文件或目录删除成功则返回true
;当文件夹中有内容,则不会删除,且返回false
;当构造方法中路径在实际中并不存在,返回false
。System.out.println(f.delete()); //true
System.out.println(f2.delete()); //false
13.1.5 目录的遍历
public String[] list()
:返回一个String
数组,表示该File
目录中所有子文件或目录的名称字符串。public File[] listFiles()
:返回一个File
数组,表示该File
目录中所有子文件或目录的File
对象(具有更多信息)。File dir = new File("E:\\素材\\灵感");
//获取当前目录下的文件/文件夹名称
String[] names = dir.list();
for(String name : names) System.out.println(name);
//获取当前目录下的文件/文件夹对象
File[] files = dir.listFiles();
for(File file : files) System.out.println(file);
注意,调用上面两种方法的
File
对象,表示的必须是实际存在的目录,否则会抛出空指针异常。
文件搜索的案例:
搜索
D:\aaa
目录中的.java
文件。
public class JustTest {
public static void main(String[] args) {
String pathName = new Scanner(System.in).next();
File fir = new File(pathName);
findDir(fir);
}
public static void findDir(File dir){
File[] files = dir.listFiles();
for(File file : files){
if(file.isFile()){ //是文件,到达递归基
if(file.getName().toLowerCase().endsWith(".java")){
System.out.println("文件:" + file.getAbsolutePath());
}
}
else{ //是目录,继续深搜
findDir(file);
}
}
}
}
13.1.6 文件过滤器的优化
java.io.FileFilter
,是一个接口,是File
的过滤器。该接口的对象,只有一个方法,可以传递给File
类的listFiles(FileFilter)
作为参数:
boolean accept(File pathname)
测试
pathname
是否应该包含在当前File
目录中,保留住则返回true
,过滤掉则返回false
。其保留规则:要么是.java
文件;要么是目录,用于继续遍历。接口作为参数,则需传递子类对象,并覆写其中方法,我们选择匿名内部类方式较为方便。
通过过滤器作业,
listFiles(FileFilter)
返回的数组元素中,子文件对象都是符合条件的,可直接打印。
public class JustTest {
public static void main(String[] args) {
String pathName = new Scanner(System.in).next();
File fir = new File(pathName);
findDir(fir);
}
public static void findDir(File dir){
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java") || pathname.isDirectory();
}
});
for(File file : files){
if(file.isFile()){ //是文件,到达递归基
if(file.getName().endsWith(".java")){
System.out.println("文件:" + file.getAbsolutePath());
}
}
else{ //是目录,继续深搜
findDir(file);
}
}
}
}
13.2 I/O流概述
Java中I/O操作主要指java.io
包下的内容,进行输入、输出操作。
13.2.1 IO的分类
根据数据的流向分为:输入流和输出流。
- 输入流:将数据从其他设备上读取到内存中的流。
- 输出流:将数据从内存中写出到其他设备上的流。
根据数据的类型分为:字节流和字符流。
- 字节流:以字节为单位,读写数据的流。
- 字符流:以字符为单位,读写数据的流。
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流(InputStream) | 字节输出流(OutputStream) |
字符流 | 字符输入流(Reader) | 字符输出流(Writer) |
由于简单演示,在介绍字节流、字符流时,我们暂且将IO异常抛出。实际开发中,针对IO异常,应使用try...catch
代码块进行处理。
13.3 字节流
13.3.1 字节输出流(OutputStream)
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的几个基本方法:
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制写出所有缓冲的输出字节。public void write(byte[] b)
:将b.length
个字节从指定的b
字节数组写出此输出流。public void write(byte[] b, int off, int len)
:将指定的b
字节数组中从偏移量off
开始的len
个字节写出此输出流。public abstract void write(int b)
:写出一个字节的数据
13.3.2 FileOutputStream 类
java.io.FileOutputStream
类 是 文件输出流,它作为OutputStream
中的一个子类,用于将数据写出到文件。
一、构造方法
public FileOutputStream(File file)
:创建文件输出流以写入由指定的File
对象表示的文件。public FileOutputStream(String name)
:创建文件输出流写入以指定名称name
的文件。
当你创建一个流对象时,必须传入一个文件路径。若该路径下没有这个文件,则会自动创建;若该文件已存在,则会清空该文件中数据。举例如下:
public class JustTest {
public static void main(String[] args) throws FileNotFoundException {
File f = new File("233.txt"); //使用File对象
FileOutputStream fos = new FileOutputStream(f);
FileOutputStream fos_2 = new FileOutputStream("b.txt"); //使用文件名称创建流对象
}
}
二、写出字节数据
写出字节:调用
write(int b)
方法,每次可以写出一个字节数据。注意,尽管参数为int
类型的四个字节,但只会保留一个字节的信息写出。public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt");
fos.write(97); //写入第一个字节,转换为字符即为'a'
fos.write(98); //写入第二个字节,转换为字符即为'b'
fos.write(99);
fos.close(); //流操作完毕后必须调用close方法以释放系统资源
}
}
// 233.txt文件中打印为:abc
写出字节数组:调用
write(byte[] b)
方法,每次可以写出数组中的数据。public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt");
byte[] b = "国奖我来了".getBytes(); //字符串转化为字节数组
fos.write(b); //写出字节数组数据
fos.close(); //关闭资源
}
}
// 233.txt文件中打印为:国奖我来了
写出指定长度的字节数组:调用
write(byte[] b, int off, int len)
,每次写出时从off
索引开始,以len
个字节。public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt");
byte[] b = "abcdefgh".getBytes(); //字符串转化为字节数组
fos.write(b, 3, 4); //写出字节数组数据
fos.close(); //关闭资源
}
}
// 233.txt文件中打印为:defg
三、数据的追加续写
对于第二点中的方法,每次程序运行,创建输出流对象时,都会清空目标文件中的数据。为了保留目标文件中的数据,可以使用下面的构造方法(相比于第一点的构造方法,需要多传入一个append
参数):
public FileOutputStream(File file, boolean append)
public FileOutputStream(File file, boolean append)
当传入的append
参数为true
时表示追加数据,false
表示清空原有的数据。
public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt", true);
byte[] b = "abcde".getBytes(); //字符串转化为字节数组
fos.write(b);
fos.close(); //关闭资源
}
}
/* 对于 233.txt 文件
操作前:ddd
操作后:dddabcde
*/
【附】写出换行
- 回车符
\r
:回到一行的开头(return) - 换行符
\n
:下一行(newline)
对于系统中的换行:
- \(Windows\)系统,每行结尾为:
\r\n
(回车+换行) - \(Unix\)系统,每行结尾为:
\n
(换行) - \(Mac\)系统,每行结尾为:
\r
(回车),从\(Mac\ OS\ X\)开始与\(Linux\)统一。
13.3.3 字节输入流(InputStream)
java.io.InputStream
抽象类 是表示字节输入流所有类的超类,可以读取字节信息至内存中。它定义了字节输入流的几个基本方法:
public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read()
:从数据中读入一个字节,并返回该字节,该方法在碰到流的结尾时返回-1
public int read(byte[] b)
:读入一个字节数组,并返回实际读入的字节数,或者在碰到流的结尾时返回-1
。注意,该方法最多读入b.length
个字节。public int read(byte[] b, int off, int len)
13.3.4 FileInputStream 类
java.io.FileInpurStream
类 是文件输入流,从文件中读入字节。
一、构造方法
FileInputStream(File file)
:通过打开与实际文件的连接来创建一个FileInputStream
,该文件由文件系统中的File
对象file
命名。FileInputStream(String name)
:通过打开与实际文件的连接来创建一个FileInputStream
,该文件由文件系统中的路径名name
命名。
注意,在创建一个流对象时,必须传入一个文件路径,该路径下如果没有该文件,会抛出FileNotFoundException
。
public class JustTest {
public static void main(String[] args) throws IOException {
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file); //使用File对象创建流对象
FileInputStream fos_2 = new FileInputStream("233.txt"); //使用文件名称创建该流对象。
}
}
二、读取字节数据
读取字节:调用
read()
方法,每次可读取一个字节数据,并提升为int
类型,读取到文件末尾时,返回-1
。(注意,文件中含有中文时,读入后会变成乱码!)public class JustTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("read.txt");
int b; //临时变量,保存读入一个字节数据转化为的int数据
while( (b = fis.read()) != -1)
System.out.println((char)b);
fis.close();
}
}
使用字节数组读取:调用
read(byte[] b)
,每次读入b.length
个字节数至数组中,并返回读取到的有效字节个数。同样地,当读取到末尾时,返回-1
。使用数组读入,使得每次读入多个字节,从而减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。
public class JustTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("read.txt");
int len;
byte[] b = new byte[2]; //定义2个字节长度的字节数组
while((len = fis.read(b)) != -1){ //每次读取后,将数组的有效字节部分,转换为字符串并打印
System.out.println(new String(b, 0, len)); //len表示每次读取的有效字节个数!(想想边界条件)
}
fis.close();//关闭资源
}
}
/*
输出结果为
ab
cd
e
*/
13.3.5 字节流应用:图片复制
复制原理:从已有文件中读入字节,将该字节写出到另一个文件中。
public class JustTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:\\素材\\灵感\\GVLOGO23.png"); //字节输入流对象,构造方法绑定要读入的数据域
FileOutputStream fos = new FileOutputStream("test_copy.jpg"); //字节输出流对象,绑定要写入的目的地
byte[] b = new byte[1024];
int cur;
while ((cur = fis.read(b)) != -1){
fos.write(b, 0, cur);
}
fos.close(); //先关闭输出流
fis.close(); //后关闭输入流
} //先开后关,后开先关。
}
13.4 字符流
在存储文本字符串时,需要考虑字符编码(character encoding)方式,Java提供字符流类,以字符为单位读写数据,专门用于处理文本文件(如中文字符)。
字符流,只能操作文本文件,不能操作图片、视频等非文本文件。当我们只希望 读或写 文本文件时,使用字符流;其他情况使用字节流。
13.4.1 字符输入类(Reader)
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可读入字符信息至内存中。它定义了字符输入流的几个基本方法:
与字节输入类
InputStream
的基本方法一致,只不过传入的是字符数组而不是字节数组
public void close()
:关闭此流并释放与此流相关联的任何系统资源。public int read()
:从输入流读入一个字符。public int read(char[] cbuf)
:从输入流中读取一些字符,并将他们存储到字符数组cbuf
中。
13.4.2 FileReader 类
java.io.FileReader
类是读取字符文件的便利类,构造时使用系统默认的字符编码和默认字节缓冲区。其构造方法的实现就是使用了 FileInputStream
(文件字节输入流)来实现
字符编码:字节与字符的对应规则,\(Windows\)系统的中文编码默认为\(GBK\)编码表(一个汉字占用2个字节,简体中文),而IDEA中为\(UTF-8\)(一个汉字占用3个字节,8位UniCode转换格式)
字节缓冲区:一个字节数组,用于临时存储字节数据。
一、构造方法:
FileReader(File file)
:给定要读取的File
对象以创建一个新的FileReader
。FileReader(String fileName)
:给定要读取的文件的名称fileName
以创建一个新的FileReader
。public class JustTest {
public static void main(String[] args) throws IOException {
File file = new File("233.txt");
FileReader fr = new FileReader(file); //使用File对象创建流对象
FileReader fr_2 = new FileReader("666.txt"); //使用文件名称创建流对象
}
}
二、读取字符数据
读取字符:调用
read
方法,每次可读入一个字符的数据,并提升为int
类型,读取到文件末尾时返回-1
。public class JustTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("233.txt");
int b;
while((b = fr.read()) != -1){
System.out.println((char)b);
}
fr.close();
}
}
使用字符数组读入:调用
read(char[] cbuf)
方法,每次读入cbuf.length
个字符数至数组中,并返回读取到的有效字符个数。同样地,当读取到末尾时,返回-1
。public class JustTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("233.txt");
int len;
char[] cbuf = new char[2];
while((len = fr.read(cbuf)) != -1){
System.out.println(new String(cbuf, 0, len)); //要获取有效字符
}
fr.close();
}
}
13.4.3 字符输出流(Writer)
java.io.Writer
抽象类 是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地,它定义了字符输出流的几个基本方法:
void write(int c)
:写入单个字符。void write(char[] cbuf)
:写入字符数组。abstract void write(char[] cbuf, int off, int len)
:写入字符数组的某一部分,以off
作为开始索引,len
个需写入的字符个数。void write(String str)
:写入字符串。void write(String str, int off, int len)
:写入字符串的某一部分,以off
作为开始索引,len
个需写入的字符个数。void flush()
:刷新该流的缓冲。void close()
:关闭此输出流但需要先刷新它。
13.4.4 FileWriter 类
java.io.FileWriter
类是写出字符至文件的便利类,构造时使用默认的字符编码和默认字节缓冲区。
一、构造方法
FileWriter(File file)
:给定要读入的File
对象,创建一个新的FileWriter
。FileWriter(String fileName)
:给定要读入的文件名称,创建一个新的FileWriter
。public class JustTest {
public static void main(String[] args) throws IOException {
File file = new File("233.txt");
FileWriter fw = new FileWriter(file); //使用File对象创建流对象
FileWriter fw_2 = new FileWriter("666.txt"); //使用文件名称创建流对象
}
}
二、写出字符数据
写出字符:调用
write(int b)
方法,每次可写出一个字符数据(至内存缓冲区,是字符转换为字节的过程)。关于关闭和刷新:关闭一个输出流的同时,也会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以使用更大的包的形式传递的字符在关闭输出流时都将被送出。特别地,如果不关闭文件,那么写出字节的最后一个包可能将永远得不到传递。
如果既想写出数据,又想继续使用流,则需要
flush
方法。flush
:刷新缓冲区,但流对象仍可继续使用。close
:先刷新缓冲区,然后通知系统释放资源,此时流对象不可再被使用。
public class JustTest {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("233.txt");
fw.write('刷');
fw.flush();
fw.write('新');
fw.write("\r\n"); //写出换行!
fw.flush();
fw.write('关');
fw.close();
/*输出结果为:
刷新
关
*/
//fw.write('闭'); //关闭后再写入会抛出异常
}
}
写出字符数组:调用
write(char[] cbuf)
或write(char[] cbuf, int off, int len)
,每次可以写出字符数组中的数据。public class JustTest {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("233.txt");
char[] arr = "你好打工人".toCharArray();
fw.write(arr); //你好打工人
fw.write(arr, 2, 3); //打工人
fw.close();
}
}
写出字符串:调用
write(String str)
或write(String str, int off, int len)
,每次可写出字符串中的数据。public class JustTest {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("233.txt");
String msg = "你好打工人";
fw.write(msg); //你好打工人
fw.write(msg, 2, 3); //打工人
fw.close();
}
}
13.5 IO 异常的处理
13.5.1 JDK7前的处理
public class JustTest {
public static void main(String[] args) {
FileWriter fw = null; //注意,应在外面定义这个file对象(别忘了赋初值)
try {
fw = new FileWriter("233.txt");
fw.write("早安打工人!");
} catch (IOException e){
e.printStackTrace();
} finally { //关闭流资源
try{
if(fw != null) //fw有可能为空,为空则不能再调用其方法。
fw.close(); //然而,close也有可能抛出异常
} catch (IOException e){
e.printStackTrace();
}
}
}
}
13.5.2 JDK7的处理
可以使用JDK7优化后的try-with-resource
语句,该语句确保了每个资源在语句结束时关闭。所谓的资源(resource),指在程序完成后必须关闭的对象。
格式为:
try(创建流对象语句){ //可以使用多个,若有多个则使用';'隔开
//读写数据
} catch(IOException e){
e.printStackTrace();
}
使用举例:
public class JustTest {
public static void main(String[] args) {
try (FileWriter fw = new FileWriter("233.txt")){
fw.write("早安打工人!");
} catch (IOException e){
e.printStackTrace();
}
}
}
13.6 属性集
java.util.Properties
继承于Hashtable
,以表示一个持久的属性集,它使用键-值结果存储数据,每个键及其对应值默认是字符串.
Properties
类 被许多Java类使用,如获取系统属性时,System.getProperties
方法即为返回一个Properties
对象。
一、构造方法
public Properties()
:创建一个空的属性列表。
二、基本的存储方法
public Object setProperty(String key, String value)
:保存一对属性。public String getProperty(String key)
:使用此属性列表中指定的键key
搜索属性值。public Set<String> stringPropertyNames()
:获取由所有键的名称组成的集合public class JustTest {
public static void main(String[] args) {
Properties mypro = new Properties();
//添加键值对元素
mypro.setProperty("filename", "233.txt");
mypro.setProperty("length", "998244353");
mypro.setProperty("location", "D:\\233.txt");
System.out.println(mypro); //打印属性集对象
//通过键获取属性值
System.out.println(mypro.getProperty("filename"));
System.out.println(mypro.getProperty("length"));
//遍历属性值,获取由所有键组成的集合
Set<String> mystr = mypro.stringPropertyNames();
for(String key : mystr){
System.out.println(key + " : " + mypro.getProperty(key));
}
}
}
三、与流相关的方法
public void load(InputStream inStream)
:从字节输入流inStream
中读入键值对。通过流对象,可关联至硬盘中保存的某文件中,读取到集合中使用。public void load(Reader reader)
:与上同理,传入的是字符输入流。注意:
- 文本中的数据,必须是键值对形式,可以使用空格、等号、冒号(英文)等符号作为分隔符。
- 可以使用
#
进行注释,被注释的键值对不会被读入。
/* 233.txt文件中 */
#save data
#Sun Nov 15 11:26:58 CST 2020
姓名:打工人
年龄=19岁
居住地 广州
#学校:TSH /* JustTest.java文件中 */
public class JustTest {
public static void main(String[] args) throws IOException {
Properties mypro = new Properties();
mypro.load(new FileReader("233.txt")); //字符输入流
Set<String> myset = mypro.stringPropertyNames();
for(String key : myset){
System.out.println(key + "----" + mypro.getProperty(key));
}
}
} /* 输出结果 */
姓名----打工人
居住地----广州
年龄----20岁
13.7 缓冲流
缓冲流,也叫高效流,是对4个基本的FileXxx
流的增强。其基本原理即是,在创建流对象时,创建一个内置的默认大小的缓冲区数组,通过缓冲区的读写,减少系统IO次数,从而提高读写效率。
按数据类型分类有:
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
- 字符缓冲流:
BufferedReader
,BufferedWriter
13.7.1 字节缓冲流
构造方法:
public BufferedInputStream(InputStream in)
:创建一个新的缓冲输入流。public BufferedOutputStream(OutputStream out)
:创建一个新的缓冲输出流。
使用举例:
public class JustTest {
public static void main(String[] args) throws IOException {
long st = System.currentTimeMillis();
try( //创建流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\素材\\灵感\\GVLOGO233.png"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\素材\\copy.png"));
){ //读写数据
int b;
while((b = bis.read()) != -1){
bos.write(b);
}
} catch (IOException e){
e.printStackTrace();
}
long ed = System.currentTimeMillis();
System.out.println("缓冲流复制时间:" + (ed - st) + "ms!");
}
}
/*输出结果:
缓冲流复制时间:82ms!
*/
13.7.2 字符缓冲流
构造方法:
public BufferedReader(Reader in)
:创建一个新的缓冲输入流。public BufferedWriter(Writer out)
:创建一个新的缓冲输出流。
字符缓冲流的基本方法与普通字符流调用方式一致,但也具备特有的方法:
对于
BufferedReader
,public String readLine()
:读一行文字。public class JustTest {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:\\IDEA-Projects\\basic-codes\\233.txt"));
String myline = null; //保存读入的一行字符
while((myline = br.readLine()) != null){ //读取【每】一行的字符
System.out.println(myline);
System.out.println("------------");
}
br.close();
}
}
对于
BufferedWriter
,public void newLine()
:写出一行 行分隔符,由系统属性定义符号。public class JustTest {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("666.txt"));
bw.write("早安");
bw.newLine(); //换行
bw.write("打工人");
bw.write("好起来了!");
bw.close();
}
}
/*输出结果:
早安
打工人好起来了!
*/
13.7.3 缓冲流应用:文本排序
将下列文本信息按段编号恢复顺序:
public class JustTest {
public static void main(String[] args) throws IOException {
HashMap<String, String> lineMap = new HashMap<>(); //Map集合,序号作为key,段落文字内容作为value
BufferedReader br = new BufferedReader(new FileReader("in.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
String myline = null;
while((myline = br.readLine()) != null){
String[] twoStrs = myline.split("\\."); //将一行文字以.作为分隔符,分隔为两个字符串。
lineMap.put(twoStrs[0], twoStrs[1]);
}
br.close();
for(int i = 1; i <= lineMap.size(); i++){
String keyStr = String.valueOf(i); //将数字key转换为字符串类型
String valStr = lineMap.get(keyStr); //查找对应的value字符串
bw.write(keyStr + "." + valStr); //写出段落
bw.newLine(); //换行!
}
bw.close();
}
}
13.8 转换流
13.8.1 字符编码和字符集
字符编码(Character Encoding):一套自然语言的字符与二进制数之间的对应规则。按其相应规则,将字符存储到计算机中,称为编码;将存储在计算机中的二进制数按照相应规则解析显示出来,称为解码。
字符集(Charset):也称编码表,是一个系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等。当指定其常见的字符集有:ASCII字符集、GBK字符集、Unicode字符集等。
当指定了编码,它所对应的字符集也即指定下来。
ASCII 字符集 :用于显示现代英文,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字、西文符号)。
- 基本ASCII字符集使用7位表示一个字符(共128个)
- 拓展字符集使用8位表示一个字符(共256个)。
ISO-8859-1 字符集:用于显示欧洲使用的语言。
GBxxx 字符集:用于显示中文而设计的一套字符集
- GBK:最常用的中文码表。
- GB18030:最新的中文码表。每个字可由1个、2个或4个字节组成。
Unicode 字符集:也称统一码、标准万国码,为表达任意语言的任意字符而设计。最多使用4个字节的数字来表达每个字母、符号或者文字。
- UTF-8编码:最常用,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。其编码规则有:128个US-ASCII字符,只需一个字节编码;拉丁文等字符,需要二个字节编码;大部分常用字(含中文),使用三个字节编码;其他极少使用的Unicode辅助字符,使用四字节编码。
- UTF-16
- UTF-32
转换流即是字节与字符间的桥梁。
13.8.2 InputStreamReader 类
转换流java.io.InputStreamReader
,是Reader
子类,是从字节流到字符流的桥梁,利用它读入字节,并使用指定的字符集(由名称指定,可接受平台默认字符集),将其字节解码为字符。
构造方法:
InputStreamReader(InputStream in, String charsetName)
:创建一个指定字符集的字符流。
使用举例:
public class JustTest {
public static void main(String[] args) throws IOException {
String myfile = "E:\\IDEA-Projects\\666.txt";
InputStreamReader isr = new InputStreamReader(new FileInputStream(myfile)); //创建流对象,默认UTF-8编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(myfile)); //创建流对象,指定GBK编码
int b;
while((b = isr.read()) != -1){ //使用默认编码字符流读入,乱码
System.out.println((char)b); //有可能出现乱码
}
isr.close();
while((b = isr2.read()) != -1){ //使用指定编码字符流读入,正常解析
System.out.println((char)b); //正常显示
}
isr.close();
}
}
13.8.3 OutputStreamWriter 类
转换流java.io.OutputStreamWriter
,是Writer
子类,是从字符流至字节流的桥梁,使用指定的字符集,将字符编码为字节。
构造方法:
OutputStreamWriter(OutputStream in, String charsetName)
:创建一个指定字符集的字节流。
使用举例:
public class JustTest {
public static void main(String[] args) throws IOException {
String myfile = "E:\\IDEA-Projects\\666.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(myfile)); //默认UTF-8编码
osw.write("早安"); //保存6个字节
osw.close();
String myfile_2 = "E:\\IDEA-Projects\\123.txt";
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(myfile_2), "GBK"); //指定GBK编码
osw2.write("打工人"); //保存为4个字节
osw2.close();
}
}
13.8.4 转换流应用:转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
public class JustTest {
public static void main(String[] args) throws IOException {
String srcFile = "E:\\IDEA-Projects\\666.txt";
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile), "GBK"); //转换输入流,指定GBK编码
String destFile = "E:\\IDEA-Projects\\233.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));//转换输出流,默认UTF-8编码
char[] cbuf = new char[1024];
int len;
while((len = isr.read(cbuf)) != -1){ //读入GBK编码的文本文件
osw.write(cbuf, 0, len); //写出UTF-8编码的文本文件
}
osw.close();
isr.close();
}
}
13.9 序列化
Java 提供了一种对象序列化的机制,用一个字节序列表示一个对象,该字节序列包含该「对象的数据」、「对象的类型」和「对象中存储的数据」信息。当字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
该字节序列还可从文件中读入,重构对象,对其进行反序列化。
13.9.1 ObjectOutputStream 类
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出至文件中,实现对象的持久存储。
构造方法:
public ObjectOutputStream(OutputStream out)
:创建一个指定OutputStream
的ObjectOutputStream
。
序列化操作:
一个对象若想要序列化,必须同时满足两个条件:
该类必须实现
java.io.Serializable
接口(该接口是一个标记接口)。若不实现此接口的类,则不会使任何状态序列化或反序列化,抛出NotSerializableException
。该类所有属性必须是可序列化的,若有一个属性不需要可序列化,则该属性必须注明是瞬态的,使用
transient
关键字修饰。public class Student implements java.io.Serializable{ //实现Serializable的接口
public String name;
public String No;
public transient String address; //transient瞬态修饰成员,不会被序列化
public void nameCheck(){
System.out.println("学号为" + No + "的学生姓名为:" + name);
}
} //为演示方便,部分方法暂忽略
写出对象的方法
public final void writeObject(Object obj)
:将指定对象写出。
public class JustTest {
public static void main(String[] args) {
Student a = new Student();
a.name = "Luffy"; a.No = "23333"; a.address = "Brazil";
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IDEA-Projects\\666.txt"));
oos.writeObject(a); //写出对象,但注意该方法会抛出IO异常
oos.close();
System.out.println("Serialized data is saved successfully!");
} catch (IOException i){ //捕获IO异常
i.printStackTrace();
}
}
}
13.9.2 ObjectInputStream 类
ObjectInputStream
反序列流,就之前使用的ObjectOutputStream
序列化的原始数据恢复为对象。
构造方法:
public ObjectInputStream(InputStream in)
:创建一个指定InputStream
的ObjectInputStream
。
反序列化操作:
若能找到一个对象的.class
文件,即可进行反序列化操作,调用ObjectInputStream
读入对象的方法:
public final Object readObject()
:读入一个对象。注意,JVM反序列化对象时,必须能够找到传入的.class
文件的类,否则抛出ClassNotFoundException
异常。public class JustTest {
public static void main(String[] args) {
Student a = null;
try{
FileInputStream fileIn = new FileInputStream("E:\\IDEA-Projects\\666.txt");
ObjectInputStream ois = new ObjectInputStream(fileIn); //传入字节输入流参数
a = (Student) ois.readObject(); //读入对象,返回Object类需强转一下
ois.close();
fileIn.close();
} catch (IOException i){
i.printStackTrace();
return;
} catch (ClassNotFoundException c){ //readObject会抛出这类异常
System.out.println("Student class can't be found!");
c.printStackTrace();
return;
}
System.out.println("Name:" + a.name + " Address:" + a.address); //若无异常则直接打印输出。
}
}
尽管JVM反序列化对象时能够找到
.class
,但注意.class
文件在序列化对象之后发生了修改,则反序列化操作有可能失败,抛出个InvalidClassException
异常。发生该异常的原因如下:- 该类的序列版本号与从流中读入的类描述符的版本号不匹配。
- 该类包含未知数据类型。
- 该类没有可访问的无参构造方法。
Serializable
接口给需要序列化的类提供一个默认的序列版本号serialVersionUID
,该版本号目的在于验证序列化的对象和对应类是否版本匹配。当然,也可以自定义版本号,从而避免序列化后再修改.class
文件所发生的冲突异常。public class Student implements java.io.Serializable{ //实现Serializable的接口
public String name;
public String No;
public transient String address; //transient瞬态修饰成员,不会被序列化
private static final long serialVersionUID = 1L; //加入序列版本号
public int eid; //假定我序列化之后再添加该属性再编译,是能够反序列化的(前提是你不能使用默认的序列版本号),该属性赋为默认值。
public void nameCheck(){
System.out.println("学号为" + No + "的学生姓名为:" + name);
}
}
13.9.3 序列化集合
练习:将存有多个自定义对象的集合进行序列化,并保存至文件中。然后再反序列化该文件,遍历集合打印对象信息。
public class JustTest {
public static void main(String[] args) throws Exception {
Student a = new Student("Luffy", "001");
Student b = new Student("Ben", "002");
Student c = new Student("Nami", "003");
ArrayList<Student> arr = new ArrayList<>();
arr.add(a); arr.add(b); arr.add(c);
serialise(arr); //序列化
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\IDEA-Projects\\666.txt"));
ArrayList<Student> mylist = (ArrayList<Student>) ois.readObject();
for(int i = 0; i < mylist.size(); i++){
Student cur = mylist.get(i);
System.out.println(cur.getName() + "---" + cur.getNo());
}
}
private static void serialise(ArrayList<Student> arr) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IDEA-Projects\\666.txt"));
oos.writeObject(arr);
oos.close();
}
}
执行结果:
13.10 打印流
控制台打印输出即调用了print()
/println()
方法等,这些方法均来自于java.io.PrintStream
类,该类能够方便地打印各种数据类型的值。
13.10.1 PrintStream 类
java.io.PrintStream
继承自父类OutputStream
,故也有close()
、write()
、flush()
等共性方法。
特点:
- 只负责数据的输出,不负责数据的读取。
- 与其他输出流不同,它永远不会抛出
IOException
。(但还是能够抛出FileNotFoundException
异常)
构造方法:
创建打印流PrintStream
对象,其构造方法需传入 要输出的目的地(必须存在) 作为参数。
public PrintStream(File file)
:输出目的地为一个文件。public PrintStream(OutputStream out)
:输出目的地为一个字节输出流。
特有方法:
void print(任意类型)
void println(任意类型)
:输出包括换行若使用继承自父类的
write
方法写出数据,它会查询对应的编码表(如97->'a')并将其解码输出;若使用自己特有的方法print()
或println()
,写出的数据原样输出(如97->97)
另外,通过调用System.setOut()
方法,可以将输出语句的目的地,改变为参数中传递的打印流的目的地(默认情况下,是控制台输出的)
static void setOut(PrintStream out)
:重新分配“标准”输出流。public class Hello {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("我在控制台输出!");
PrintStream ps = new PrintStream("C:\\Users\\admin\\IdeaProjects\\MyIdeaProjects\\666.txt");
System.out.println(ps);
System.out.println("我在打印流的目的地中输出!");
ps.close();
}
}
JavaSE 学习笔记07丨IO流的更多相关文章
- 【知了堂学习笔记】java IO流归纳总结
皮皮潇最近学到了IO流但是感觉这一块要记的东西太多了,所以重API上查阅并总结了以下几点关于IO的知识. 1.File(文件类): File类是文件以及文件夹进行封装的对象,用对象的思想来操作文件和文 ...
- JavaSE 学习笔记02丨对象与类
Chapter 4. 对象与类 4.1 面向对象程序设计概述 面向对象程序设计(简称OOP),是当今主流程序设计范型.面向对象的程序是由对象组成的,每个对象(来自于标准库或自定义的)包含对用户公开的特 ...
- Java基础学习笔记二十 IO流
转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStre ...
- JavaSE 学习笔记08丨网络编程
Chapter 14. 网络编程 14.1 计算机网络入门 当前节的知识点只是一个概述,更具体.详细的内容放在 计算机网络 中. 14.1.1 软件结构 C/S结构(Client/Server结构): ...
- JavaSE 学习笔记01丨开发前言与环境搭建、基础语法
本蒟蒻学习过C/C++的语法,故在学习Java的过程中,会关注于C++与Java的区别.开发前言部分,看了苏星河教程中的操作步骤.而后,主要阅读了<Java核心技术 卷1 基础知识>(第8 ...
- JavaSE 学习笔记06丨并发
Chapter 12. 并发 12.1 并发与并行 并发:指两个或多个事件在同一个时间段内发生. 并行:指两个或多个事件在同一时刻发生(同时发生). 在操作系统中,并发指的是在一段时间内宏观上有多个程 ...
- JavaSE 学习笔记05丨泛型、集合
Chapter. 10 泛型 10.1 泛型程序设计 泛型,指可以在类或方法中预支地使用未知的类型.泛型程序设计(Generic programming),意味着编写的代码可被很多不同类型的对象所重用 ...
- JavaSE 学习笔记04丨异常
Chapter 9 异常 异常:指程序在执行过程中,出现的非正常的情况,最终导致JVM非正常停止. 在Java等面向对象的编程语言中,异常是一个类,所有异常都是发生在运行阶段的(因为也只有程序运行阶段 ...
- JavaSE 学习笔记03丨继承、接口、多态、内部类
Chapter. 5 继承 继承作为面向对象的三大特征之一,它是多态的前提.它主要解决的问题是共性抽取. Java中的继承,是单继承.多级继承的. 已存在的类,被称为超类.基类.父类(parent c ...
随机推荐
- Redis集群环境搭建实践
0 Redis集群简介 Redis集群(Redis Cluster)是Redis提供的分布式数据库方案,通过分片(sharding)来进行数据共享,并提供复制和故障转移功能.相比于主从复制.哨兵模式, ...
- F2. Same Sum Blocks (Hard) 解析(思維、前綴和、貪心)
Codeforce 1141 F2. Same Sum Blocks (Hard) 解析(思維.前綴和.貪心) 今天我們來看看CF1141F2(Hard) 題目連結 題目 給你一個數列\(a\),要你 ...
- Java8新特性探索之Lambda表达式
为什么引入Lambda表达式? Lambda 表达式产生函数,而不是类. 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 lamb ...
- 【转】Event Driven Programming
FROM: http://lazyfoo.net/tutorials/SDL/03_event_driven_programming/index.php Event Driven Programmin ...
- NB-IOT关键技术分析
NB-IOT(NarrowBand Internet of Things,窄带IoT)是一种基于蜂窝的窄带物联网技术,支持低功耗设备在广域网的蜂窝数据连接.NB-IOT在物联网应用广泛,许多领域都充分 ...
- scrapyd部署、使用Gerapy 分布式爬虫管理框架
Scrapyd部署爬虫项目 GitHub:https://github.com/scrapy/scrapyd API 文档:http://scrapyd.readthedocs.io/en/stabl ...
- linux 安装部署
[smb] service smb restart vim /etc/selinux/config systemctl stop firewalld.service(centos7 64) seten ...
- 【Android Studio】安卓开发初体验3.1——UI设计之常用控件
常用控件 首先对xml文件的编辑有三种模式 Code为纯代码 Split是一边代码,一边预览效果图 Designer就是有UI设计界面 TextView 用于在界面上显示一段文本信息 所有控件都可以在 ...
- MVC中Cookie的用法(一)
1创建Cookie 1.1直接创建 Response.Cookies["Account"].Value = "test1"; Response.Cookies[ ...
- C# OpenFileDialog和SaveFileDialog的常见用法
#region 示例1 SaveFileDialog sfd = new SaveFileDialog(); //设置文件类型 sfd.Filter = "备份文件(*.bak)|*.bak ...