Java笔记整理六(File类,递归,字节流IO,字符流IO,流中的异常处理,属性集Properties,缓冲流,转换流,序列化,打印流)
1.File类
java.io.File
类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
文件和目录路径名的抽象表示
java把文件和文件夹封装位为一个File类,我们可以用File类的对文件和文件夹进行操作
使用File类我们可以
- 创建一个文件夹/文件
- 删除文件/文件夹
- 获取文件/文件夹
- 判断文件/文件夹是否存在
- 获取文件大小
File是一个与系统无关的类,任何系统都可以使用这个类中的方法。
重点:
file:文件
directory:目录
path:路径
static String pathSeparator
:路径分隔符
static char pathSeparator
:路径分隔符
Srting pathSeparator=File.pathSeparator;
windows:分号
linux:冒号
static String separator
:默认名称分隔符
static char separator
:默认名称分隔符
Srting separator=File.separator;
windows:\
linux:/
路径不能写死了
“c:“+File.separator+”develop“+File.separator+a.txt”
绝对路径,相对路径
绝对路径:完整路径c:\a.txt
相对路径:简化路径,相对当前项目的根目录
注意:
- 路径不区分大小写
- 路径中文件名称分隔符windows使用双反斜杠
File中的构造方法
public File(String pathname)
:通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
可以是以文件/文件夹结尾。可以相对路径也可以是绝对路径,可以是存在的也可以不存在。创建File对象,只把字符串路径封装给File对象,不考虑路径对象 它重写了File的toString方法。public File(String parent, String child)
:从父路径名字符串和子路径名字符串创建新的 File实例。
父路径和子路径,可以单独书写,使用灵活public File(File parent, String child)
:从父抽象路径名和子路径名字符串创建新的 File实例。
父路径和子路径,可以单独书写,使用灵活,父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象
获取功能的方法
public String getAbsolutePath()
:返回此File的绝对路径名字符串。public String getPath()
:将此File转换为路径名字符串。public String getName()
:返回由此File表示的文件或目录的名称。public long length()
:返回由此File表示的文件的长度。方法演示,代码如下:
public class FileGet {public static void main(String[] args) {File f = new File("d:/aaa/bbb.java"); System.out.println("文件绝对路径:"+f.getAbsolutePath());System.out.println("文件构造路径:"+f.getPath());System.out.println("文件名称:"+f.getName());System.out.println("文件长度:"+f.length()+"字节");File f2 = new File("d:/aaa"); System.out.println("目录绝对路径:"+f2.getAbsolutePath());System.out.println("目录构造路径:"+f2.getPath());System.out.println("目录名称:"+f2.getName());System.out.println("目录长度:"+f2.length());}}输出结果:文件绝对路径:d:\aaa\bbb.java文件构造路径:d:\aaa\bbb.java文件名称:bbb.java文件长度:636字节目录绝对路径:d:\aaa目录构造路径:d:\aaa目录名称:aaa目录长度:4096
判断功能的方法
public boolean exists()
:此File表示的文件或目录是否实际存在。public boolean isDirectory()
:此File表示的是否为目录。public boolean isFile()
:此File表示的是否为文件。
方法演示,代码如下:
public class FileIs {public static void main(String[] args) {File f = new File("d:\\aaa\\bbb.java");File f2 = new File("d:\\aaa");// 判断是否存在System.out.println("d:\\aaa\\bbb.java 是否存在:"+f.exists());System.out.println("d:\\aaa 是否存在:"+f2.exists());// 判断是文件还是目录System.out.println("d:\\aaa 文件?:"+f2.isFile());System.out.println("d:\\aaa 目录?:"+f2.isDirectory());}
}
输出结果:
d:\aaa\bbb.java 是否存在:true
d:\aaa 是否存在:true
d:\aaa 文件?:false
d:\aaa 目录?:true
创建删除功能的方法
public boolean createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。 只能创建文件。不能创建文件夹。需要处理异常,必须路径存在,否则会抛出io异常。public boolean delete()
:删除由此File表示的文件或目录。 不走回收站文件/文件夹删除成功,返回true
文件夹有内容不回删除,返回false,构造方法中不存在也返回false
public boolean mkdir()
:创建由此File表示的目录。只能创建单集空文件夹public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录。可以创建单级文件夹也可以创建多级空文件夹。- 返回值:true:文件夹不存在,创建文件夹,返回true
返回false:文件夹存在,不会创建,构造方法中给出的路径不存在也返回false
只能创建文件夹不能创建文件
创建文件的路径必须存在,否则会抛出异常
方法演示,代码如下:
public class FileCreateDelete {public static void main(String[] args) throws IOException {// 文件的创建File f = new File("aaa.txt");System.out.println("是否存在:"+f.exists()); // falseSystem.out.println("是否创建:"+f.createNewFile()); // trueSystem.out.println("是否存在:"+f.exists()); // true// 目录的创建File f2= new File("newDir"); System.out.println("是否存在:"+f2.exists());// falseSystem.out.println("是否创建:"+f2.mkdir()); // trueSystem.out.println("是否存在:"+f2.exists());// true// 创建多级目录File f3= new File("newDira\\newDirb");System.out.println(f3.mkdir());// falseFile f4= new File("newDira\\newDirb");System.out.println(f4.mkdirs());// true// 文件的删除System.out.println(f.delete());// true// 目录的删除System.out.println(f2.delete());// trueSystem.out.println(f4.delete());// false}
}
API中说明:delete方法,如果此File表示目录,则目录必须为空才能删除。
目录的遍历
public String[] list()
:返回一个String数组,表示该File目录中的所有子文件或目录。把获取到的多个名称存储到一个String类型的数组中。
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。遍历构造方法中给出的目录,获取所有文件或文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
list和listFiles方法遍历的是构造方法中给出的目录
如果走早方法中给出路径不存在,会抛出空指针异常
如果构造方法中给出的路径不是一个目录,会抛出空指针异常
public class FileFor {public static void main(String[] args) {File dir = new File("d:\\java_code");//获取当前目录下的文件以及文件夹的名称。String[] names = dir.list();for(String name : names){System.out.println(name);}//获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就可以获取更多信息File[] files = dir.listFiles();for (File file : files) {System.out.println(file);}}
}
小贴士:
调用listFiles方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历。
递归
递归:指再当前方法体调用自己
分为直接递归和间接递归
注意事项:
- 递归一定要有条件限制,保证递归能够停止
- 递归次数不能太多,否则可能会发生栈内存溢出
- 构造方法禁止递归
递归方法使用前提:
当调用方法的时候,方法主体不变,每次调用方法的参数不同,可以使用递归
使用递归计算1–n的和
使用递归必须明确:
- 递归的结束条件:获取到一结束
- 递归的目的:获取下一个被加的数字
实现代码:
public class DiGuiDemo {public static void main(String[] args) {//计算1~num的和,使用递归完成int num = 5;// 调用求和的方法int sum = getSum(num);// 输出结果System.out.println(sum);}/*通过递归算法实现.参数列表:int 返回值类型: int */public static int getSum(int num) {/* num为1时,方法返回1,相当于是方法的出口,num总有是1的情况*/if(num == 1){return 1;}/*num不为1时,方法返回 num +(num-1)的累和递归调用getSum方法*/return num + getSum(num-1);}
}
递归求阶乘
阶乘:所有小于及等于该数的正整数的积。
n的阶乘:n! = n * (n-1) *...* 3 * 2 * 1
推理得出:n! = n * (n-1)!
代码实现:
public class DiGuiDemo {//计算n的阶乘,使用递归完成public static void main(String[] args) {int n = 3;// 调用求阶乘的方法int value = getValue(n);// 输出结果System.out.println("阶乘为:"+ value);}/*通过递归算法实现.参数列表:int 返回值类型: int */public static int getValue(int n) {// 1的阶乘为1if (n == 1) {return 1;}/*n不为1时,方法返回 n! = n*(n-1)!递归调用getValue方法*/return n * getValue(n - 1);}
}
递归打印多级目录
分析:多级目录的打印,就是当目录的嵌套。遍历之前,无从知道到底有多少级目录,所以我们还是要使用递归实现。
代码实现:
public class DiGuiDemo2 {public static void main(String[] args) {// 创建File对象File dir = new File("D:\\aaa");// 调用打印目录方法printDir(dir);}public static void printDir(File dir) {// 获取子文件和目录File[] files = dir.listFiles();// 循环打印/*判断:当是文件时,打印绝对路径.当是目录时,继续调用打印目录的方法,形成递归调用.*/for (File file : files) {// 判断if (file.isFile()) {// 是文件,输出文件绝对路径System.out.println("文件名:"+ file.getAbsolutePath());} else {// 是目录,输出目录绝对路径System.out.println("目录:"+file.getAbsolutePath());// 继续遍历,调用printDir,形成递归printDir(file);}}}
}
文件搜索
搜索D:\aaa
目录中的.java
文件。
分析:
- 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。
- 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件。
代码实现:
public class DiGuiDemo3 {public static void main(String[] args) {// 创建File对象File dir = new File("D:\\aaa");// 调用打印目录方法printDir(dir);}public static void printDir(File dir) {// 获取子文件和目录File[] files = dir.listFiles();// 循环打印for (File file : files) {if (file.isFile()) {// 是文件,判断文件名并输出文件绝对路径if (file.getName().endsWith(".java")) {System.out.println("文件名:" + file.getAbsolutePath());}} else {// 是目录,继续遍历,形成递归printDir(file);}}}
}
文件过滤器优化
listFiles(FileFilter fileter)
File类中有两个和ListFiles重载的方法,方法参数就是过滤器
java.io.FileFilter
是一个接口,是File的过滤器。 该接口的对象可以传递给File类的
用于抽象路径名(File对象)的过滤器,用来过滤文件
boolean accept(File pathname)
:测试pathname是否应该包含在当前File目录中,符合则返回true。
listFiles(FilenameFilter fileter)
:
java.io.FilenameFilter
接口:实现此接口的类实例可用于过滤器文件名
用于过滤文件名称
boolean accept(File dir ,String name)
:测试文件是否包含在某一文件夹中
File dir:构造方法中传递的被遍历的目录
String name::使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤方法accep,在方法中自己定义过滤器规则。
分析:
- 接口作为参数,需要传递子类对象,重写其中方法。我们选择匿名内部类方式,比较简单。
accept
方法,参数为File,表示当前File下所有的子文件和子目录。保留住则返回true,过滤掉则返回false。保留规则:- 要么是.java文件。
- 要么是目录,用于继续遍历。
- 通过过滤器的作用,
listFiles(FileFilter)
返回的数组元素中,子文件对象都是符合条件的,可以直接打印。
代码实现:
public class DiGuiDemo4 {public static void main(String[] args) {File dir = new File("D:\\aaa");printDir2(dir);}public static void printDir2(File dir) {// 匿名内部类方式,创建过滤器子类对象File[] files = dir.listFiles(new FileFilter() {@Overridepublic boolean accept(File pathname) {return pathname.getName().endsWith(".java")||pathname.isDirectory();}});// 循环打印for (File file : files) {if (file.isFile()) {System.out.println("文件名:" + file.getAbsolutePath());} else {printDir2(file);}}}
}
FileFilter过滤器的原理和使用
必须明确两件事情:
- 过滤器中accept方法是谁调用的,
- accept方法的参数pathname是什么。
ListFiles方法一共做了三件事情:- ListFiles方法对构造中传递的目录进行遍历,获取目录中的每一个文件夹/文件----封装成File对象
- ListFiles方法会调用参数传递的过滤器中的方法accept
- listFiles方法会把遍历的到的File对象,传递过accept方法的参数pathname
accept方法返回的是一个boolean值
true: 会把传递过去的File对象保存到File数组中
false:就不会把传递过去的File对象保存到File数组中
因此,过滤的规则:
在accept方法中,判断File对象
Lambda优化
分析:FileFilter
是只有一个方法的接口,因此可以用lambda表达式简写。
lambda格式:
()->{ }
代码实现:
public static void printDir3(File dir) {// lambda的改写File[] files = dir.listFiles(f ->{ return f.getName().endsWith(".java") || f.isDirectory(); });// 循环打印for (File file : files) {if (file.isFile()) {System.out.println("文件名:" + file.getAbsolutePath());} else {printDir3(file);}}
}
IO
IO的分类
根据数据的流向分为:输入流和输出流。
- 输入流 :把数据从
其他设备
上读取到内存
中的流。 - 输出流 :把数据从
内存
中写出到其他设备
上的流。
格局数据的类型分为:字节流和字符流。
- 字节流 :以字节为单位,读写数据的流。
- 字符流 :以字符为单位,读写数据的流。
流:数据(字符,字节)一个字符=两个字节
一个字节=八个二进制(八位)
:: | 输入流 | 输出流 |
---|---|---|
字节流 |
字节输入流 InputStream |
字节输出流 OutputStream |
字符流 |
字符输入流 Reader |
字符输出流 Writer |
IO字节流
以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此
字节输出流OutputStream
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b)
:将指定的字节输出流。
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
FileOutputStream类:文件输出流
java.io.FileOutputStream
类是文件输出流,用于将内存数据写出到硬盘的文件中。
构造方法
public FileOutputStream(File file)
:创建文件输出流以写入由指定的 File对象表示的文件。public FileOutputStream(String name)
: 创建文件输出流以指定的名称写入文件。
参数:写入数据的目的地。
String name文件路径
File file:目的地是一个文件
构造方法地作用:
1.创建一个 FileOutputStream对象
2.会根据构造方法中传递的文件/文件路径,创建一个空的文件
3.会把 FileOutputStream对象指向创建好的问文件
字节流写入数据到文件
写出字节
写入数据的原理:(内存–>硬盘)
java程序—>JVM(Java虚拟机)—>Os(操作系统)—>OS调用写数据的方法—>把数据写入到文件中
字节输出流的使用步骤:
- 创建一个 FileOutputStream对象,构造方法中传递写入的数据的目的地
- 调用 FileOutputStream对象中的方法write,把数据写入到文件中
- 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
需要抛出异常
public class FOSWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileOutputStream fos = new FileOutputStream("fos.txt"); // 写出数据fos.write(97); // 写出第1个字节fos.write(98); // 写出第2个字节fos.write(99); // 写出第3个字节// 关闭资源fos.close();}
}
输出结果:
abc
写数据的时候,会把10进制的整数转换为二进制整数97
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
写出字节数组:write(byte[] b)
,
一次写多个字节
- 创建 FileOutputStream对象, 构造方法中绑定要写入的数据的目的地
- 调用 FileOutputStream对象中的方法
write(byte[] b)
(如果第一个字节是正数(0–127 ),那么显示的时候会查询ascii表 ,如果第一个字节是负数,那么第一个字节会和第二个字节,两个字节组成 一个中文显示,查询系统默认码表(GBK)),把数据写入到文本中. - 释放资源
public class FOSWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileOutputStream fos = new FileOutputStream("fos.txt"); // 字符串转换为字节数组byte[] b = "黑马程序员".getBytes();// 写出字节数组数据fos.write(b);// 关闭资源fos.close();}
}
写出指定长度字节数组:write(byte[] b, int off, int len)
,每次写出从off索引开始,len个字节。把字节数组的一部写入到文件中
.getBytes()把字符串转换为字节数组
arrays.toString(字节数组)转换为十进制数组
public class FOSWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileOutputStream fos = new FileOutputStream("fos.txt"); // 字符串转换为字节数组byte[] b = "abcde".getBytes();// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。fos.write(b,2,2);// 关闭资源fos.close();}
}
输出结果:
cd
数据追加续写
** 追加写**:使用两个参数的构造方法
public FileOutputStream(File file, boolean append)
: 创建文件输出流以写入由指定的 File对象表示的文件。
-创建一个向指定File对象表示的文件中写入数据的文件输出流public FileOutputStream(String name, boolean append)
: 创建文件输出流以指定的名称写入文件创建一个向具有指定name的文件中写入数据的输出文件流
String 那么,File file:写入数据的目的地
boolean append:追加写开关true:创建对象不回覆盖原文件,继续在文件的末尾追加写数据
false:创建一个新文件,覆盖原文件
写换行:写换行符号
windows:\r\n
linux: /n
mac:/r
public class FOSWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileOutputStream fos = new FileOutputStream("fos.txt"); // 定义字节数组byte[] words = {97,98,99,100,101};// 遍历数组for (int i = 0; i < words.length; i++) {// 写出一个字节fos.write(words[i]);// 写出一个换行, 换行符号转成数组写出fos.write("\r\n".getBytes());}// 关闭资源fos.close();}
}输出结果:
a
b
c
d
e
字节输入流InputStream
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
所有子类共性的方法:
public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read()
: 从输入流读取数据的下一个字节。public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
FileInputStream类
作用:把硬盘中的文件数据,读取到内存中使用
构造方法:
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException
。
参数:读取文件的数据源
String name:文件路径
File file:文件
构造方法的作用:
1.会创建一个FileInputStream对象
2.会把FileInputStream对象指定给构造方法中读取的文件
读取数据的原理(硬盘–>内存)
java程序 -->JVM -->OS–>OS读取数据的方法–>读取文件
自己输入流的使用步骤(重点):
- 创建FileInputStream对象,构造方法中绑定要读取的数据源。
- 使用FileInputStream对象中的方法read,读取文件
- 释放文件
读取字节数据
- 读取字节:
read
方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1
,代码使用演示:
每次读取一个字节
public class FISRead {public static void main(String[] args) throws IOException{// 使用文件名称创建流对象FileInputStream fis = new FileInputStream("read.txt");// 读取数据,返回一个字节int read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);// 读取到末尾,返回-1read = fis.read();System.out.println( read);// 关闭资源fis.close();}
}
输出结果:
a
b
c
d
e
-1
循环改进读取方式,代码使用演示:
不知道文件有多少字节,使用while循环
结束条件:读取到-1的时候
public class FISRead {public static void main(String[] args) throws IOException{// 使用文件名称创建流对象FileInputStream fis = new FileInputStream("read.txt");// 定义变量,保存数据int b ;// 循环读取,必须用变量接收while ((b = fis.read())!=-1) {System.out.println((char)b);}// 关闭资源fis.close();}
}
输出结果:
a
b
c
d
e
. 使用字节数组读取:read(byte[] b)
,每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
,:
public class FISRead {public static void main(String[] args) throws IOException{// 使用文件名称创建流对象.FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde// 定义变量,作为有效个数int len ;// 定义字节数组,作为装字节数据的容器 byte[] b = new byte[2];// 循环读取while (( len= fis.read(b))!=-1) {// 每次读取后,把数组的有效字节部分,变成字符串打印System.out.println(new String(b,0,len));// len 每次读取的有效字节个数}// 关闭资源fis.close();}
}输出结果:
ab
cd
e
一次读取多个字节
原理:
1.创建一个流对象,并把它指向要读取的文件
2.创建一个byte数组
3.读取数据
- 读取数组长度大小的数据
- 返回有效读取字节个数
数组起到缓冲作用,存储读取到的多个字节 一般定义为1024的整数倍
字节流练习:图片复制
文件复制:一读一写
明确:
数据源
数据的目的地:
- 创建一个字节输入流对象,在构造方法中绑定要读取的数据源
- 创建一个
- 流对象,构造方法中绑定要写入的目的地
- 使用字节输入流对象中的当打中read读取文件
- 使用字节输出流中的方法write,把读取到的字节写入到目的地文件中
- 释放资源
public class Copy {public static void main(String[] args) throws IOException {// 1.创建流对象// 1.1 指定数据源FileInputStream fis = new FileInputStream("D:\\test.jpg");// 1.2 指定目的地FileOutputStream fos = new FileOutputStream("test_copy.jpg");// 2.读写数据// 2.1 定义数组byte[] b = new byte[1024];// 2.2 定义长度int len;// 2.3 循环读取while ((len = fis.read(b))!=-1) {// 2.4 写出数据fos.write(b, 0 , len);}// 3.关闭资源fos.close();fis.close();}
}
字符流
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
GBK:占用两个字节
utf-8:占用三个字节
字符输入流【Reader】
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
共有成员方法
public int read()
: 从输入流读取一个字符。public int read(char[] cbuf)
: 从输入流中读取多个字符,并将它们存储到字符数组 cbuf中 。public void close()
:关闭此流并释放与此流相关联的任何系统资源。
FileReader类 文件字符输入流
java.io.FileReader
extend InputStreamReader··
extendReader
类
是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
作用:把硬盘中的数据以字符的方式读取到内存中
构造方法:
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的名称。
参数:读取的数据源
String fileName:文件的路径
File file:文件
FileReader构造方法中的作用:
1.创建一个FileReader的对象
2.会把FileReader对象指向要读取的文件
当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。
字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。
idea中UTF-8
字节缓冲区:一个字节数组,用来临时存储字节数据。
字符输入流的使用步骤?
1.创建FileReader对象,构造方法中要绑定要读取的数据源
2.使用FileReader对象中的方法Read读取文件
3.释放资源
读取字符数据
- 读取字符:
read
方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1
,循环读取,代码使用演示:
public class FRRead {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileReader fr = new FileReader("read.txt");// 定义变量,保存数据int b ;// 循环读取while ((b = fr.read())!=-1) {System.out.println((char)b);}// 关闭资源fr.close();}
}
输出结果:
黑
马
程
序
员
使用字符数组读取:read(char[] cbuf)
,每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1
,代码使用演示:
String类构造方法:
String(char[] value):把字符数组转换为字符串
String(char[] value,int offset ,int count):把字符数组一部分转换为字符串
public class FRRead {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileReader fr = new FileReader("read.txt");// 定义变量,保存有效字符个数int len ;// 定义字符数组,作为装字符数据的容器char[] cbuf = new char[2];// 循环读取while ((len = fr.read(cbuf))!=-1) {System.out.println(new String(cbuf));}// 关闭资源fr.close();}
}
输出结果:
黑马
程序
员序
获取有效的字符改进,代码使用演示:
public class FISRead {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileReader fr = new FileReader("read.txt");// 定义变量,保存有效字符个数int len ;// 定义字符数组,作为装字符数据的容器char[] cbuf = new char[2];// 循环读取while ((len = fr.read(cbuf))!=-1) {System.out.println(new String(cbuf,0,len));}// 关闭资源fr.close();}
}输出结果:
黑马
程序
员
字符输出流【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()
关闭此流,但要先刷新它。
FileWriter类 文件字符输出流
作用: 把内存中的字符数数剧写入到文件中
java.io.FileWriter
extends OutputStreamWrite
extends··Write
java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
构造方法:
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。
当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。
参数:写入文件的目的地
String fileName:文件的路径
File file:文件
构造方法中的作用:
- 会创建一个FileWriter对象
- 会根据构造方法中传递的文件/文件路径创建文件
- 会把FileWriter对象指向创建好的文件
字符输出流的使用步骤:
- 创建FileWriter对象。构造方法中绑定要写入的数据的目的地
- 使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
- 使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
- 释放资源(会先把内存缓冲区中的数据,刷新到文件中)
基本写出数据
写出单个字符:write(int b)
方法,每次可以写出一个字符数据,代码使用演示:
public class FWWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileWriter fw = new FileWriter("fw.txt"); // 写出数据fw.write(97); // 写出第1个字符fw.write('b'); // 写出第2个字符fw.write('C'); // 写出第3个字符fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字。/*【注意】关闭资源时,与FileOutputStream不同。如果不关闭,数据只是保存到缓冲区,并未保存到文件。*/// fw.close();}
}
输出结果:
abC田
- 虽然参数为int类型四个字节,但是只会保留一个字符的信息写出。
- 未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。
关闭和刷新
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
写出其他数据
- 写出字符数组 :
write(char[] cbuf)
和write(char[] cbuf, int off, int len)
,每次可以写出字符数组中的数据,用法类似FileOutputStream,代码使用演示:
public class FWWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileWriter fw = new FileWriter("fw.txt"); // 字符串转换为字节数组char[] chars = "黑马程序员".toCharArray();// 写出字符数组fw.write(chars); // 黑马程序员// 写出从索引2开始,2个字节。索引2是'程',两个字节,也就是'程序'。fw.write(b,2,2); // 程序// 关闭资源fos.close();}
}
- 写出字符串:
write(String str)
和write(String str, int off, int len)
,每次可以写出字符串中的数据,更为方便,代码使用演示:
public class FWWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象FileWriter fw = new FileWriter("fw.txt"); // 字符串String msg = "黑马程序员";// 写出字符数组fw.write(msg); //黑马程序员// 写出从索引2开始,2个字节。索引2是'程',两个字节,也就是'程序'。fw.write(msg,2,2); // 程序// 关闭资源fos.close();}
}
- 续写和换行:操作类似于FileOutputStream。
使用两个参数的构造方法:
FileWriter(String fileName,Boolean append)
FileWriter(File file,Boolean append)
Boolean append:
true:可以续写,不回创建新的文件覆盖源文件
false:创建新的文件覆盖源文件
public class FWWrite {public static void main(String[] args) throws IOException {// 使用文件名称创建流对象,可以续写数据FileWriter fw = new FileWriter("fw.txt",true); // 写出字符串fw.write("黑马");// 写出换行fw.write("\r\n");// 写出字符串fw.write("程序员");// 关闭资源fw.close();}
}
输出结果:
黑马
程序员
流中的异常处理
jdk1.7之前使用try...catch...finally
处理流中的异常
public class HandleException1 {public static void main(String[] args) {// 声明变量,提高变量作用域,声明时可以没有值,使用时必须有值FileWriter fw = null;try {//可能会出现异常的代码创建流对象fw = new FileWriter("fw.txt");// 写出数据fw.write("黑马程序员"); //黑马程序员} catch (IOException e) {//异常的处理逻辑e.printStackTrace();} finally {try {//若创建对象失败,fw默认值时null,null不能调用方法,会抛出nullponitException,需要增加一个判断不是null,则释放资源if (fw != null) {fw.close();}} catch (IOException e) {e.printStackTrace();}}}
}
JDK7的新特性:
在try之后增加一个();
在括号中可以定义流对象,那么这个流对象的作用域就在try中有效,try中代码执行完毕,会自动把流对象释放掉,不用写finally,
格式如下:
try (创建流对象语句,如果多个,使用';'隔开) {// 读写数据
} catch (IOException e) {e.printStackTrace();
}
代码使用演示:
public class HandleException2 {public static void main(String[] args) {// 创建流对象try ( FileWriter fw = new FileWriter("fw.txt"); ) {// 写出数据fw.write("黑马程序员"); //黑马程序员} catch (IOException e) {e.printStackTrace();}}
}
JDK9的改进(扩展知识点了解内容)
try的前边可以定义流对象
在try后边()中可以直接引入流对象的名称(变量名)
在try代码执行完毕之后流对象也可以释放掉,不用写finally
JDK9中try-with-resource
的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,无需手动close,我们来了解一下格式。
A a =new A();
B b=new B();
try(a,b){
可能会产出异常的代码,
} catch(异常类变量 变量名){
异常的处理逻辑
}
public class TryDemo {public static void main(String[] args) throws IOException {// 创建流对象** final ** FileReader fr = new FileReader("in.txt");FileWriter fw = new FileWriter("out.txt");// 引入到try中try (fr; fw) {// 定义变量int b;// 读取数据while ((b = fr.read())!=-1) {// 写出数据fw.write(b);}} catch (IOException e) {e.printStackTrace();}}
}
属性集
概述
java.util.Properties
继承于Hashtable
,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties
方法就是返回一个Properties
对象。
java.util.Properties
extends hashtable<K,v>implements Map<K,v>
Properties类
表示了一个持久的属性集Properties可保存在流中或从流中加载。
Properties集合是唯一一个和IO流相结合的集合
可以使用Properties集合中的临时数据,持久化写入到硬盘中存储、
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
Properties属性列表中每个键及其对对应的值都是一个字符串
Properties是一个双列集合,key和value默认都是字符串
构造方法
public Properties()
:创建一个空的属性列表。
基本的存储方法
使用Properties结合存储数据,遍历取出Properties集合中的数据
是一个双列集合,Key和value都是字符串
public Object setProperty(String key, String value)
: 保存一对属性。 调用hashtable的方法putpublic String getProperty(String key)
:使用此属性列表中指定的键搜索属性值。通过key找到value值,此方法相当于map集合中的get(key)方法public Set<String> stringPropertyNames()
:所有键的名称的集合。返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的KeySet方法
public class ProDemo {public static void main(String[] args) throws FileNotFoundException {// 创建属性集对象Properties properties = new Properties();// 添加键值对元素properties.setProperty("filename", "a.txt");properties.setProperty("length", "209385038");properties.setProperty("location", "D:\\a.txt");// 打印属性集对象System.out.println(properties);// 通过键,获取属性值System.out.println(properties.getProperty("filename"));System.out.println(properties.getProperty("length"));System.out.println(properties.getProperty("location"));// 遍历属性集,获取所有键的集合Set<String> strings = properties.stringPropertyNames();// 打印键值对for (String key : strings ) {System.out.println(key+" -- "+properties.getProperty(key));}}
}输出结果:
{filename=a.txt, length=209385038, location=D:\a.txt}
a.txt
209385038
D:\a.txt
filename -- a.txt
length -- 209385038
location -- D:\a.txt
与流相关的方法
可以使用properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
void store (OutputStream out,String comments)
void store(Write write,String comments)
参数:
OutputStream out:不可以写入中文
Write write:字符输出流,可以写入中文
String comments:注释,用来解释说明保存的文件是做什么用的,不能使用中文,会产生乱码默认unicide编码,一般使用“空字符串”
使用步骤:
- 创建Properties 集合对象,添加数据
- 创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
- 使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
- 释放资源
Properties pro = new Properties();创建Properties集合
pro.setProperty(“11”,“11 ”);
FileWrite fw=new FileWrite("Filename");
pro.store(fw,"savedate");
fw.close();
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
public void load(InputStream inStream)
: 从字节输入流中读取键值对。public void load(Reader reader)
:
参数:
(InputStream inStream):字节输入流,不能读取含有中文的键值对,
Reader reader:字符输入流,可以读取含有中文的键值对
通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本数据格式:
使用步骤:
- 创建Properties集合对象
- 使用Properties集合对象中的方法load读取保存键值对文件
- 遍历Properties集合
注意:
- 存储键值对的文件中,键与值默认的连接符号使用=, 空格(其他符号)
- 存储键值对的文件中,可使用#进行注释,被注释的键值对不会再被读取
- 键与值默认都是字符串,不用再加引号
filename=a.txt
length=209385038
location=D:\a.txt
加载代码演示:
一般使用字符流
public class ProDemo2 {public static void main(String[] args) throws FileNotFoundException {// 创建属性集对象Properties pro = new Properties();// 加载文本中信息到属性集pro.load(new FileInputStream("read.txt"));// 遍历集合并打印Set<String> strings = pro.stringPropertyNames();for (String key : strings ) {System.out.println(key+" -- "+pro.getProperty(key));}}
}
输出结果:
filename -- a.txt
length -- 209385038
location -- D:\a.txt
小贴士:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。
缓冲流,也叫高效流,是对4个基本的FileXxx
流的增强,所以也是4个流,按照数据类型分类:
缓冲流
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
字节缓冲流
BufferedInputStream
,字节缓冲输入流
所有子类共性的方法:
public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read()
: 从输入流读取数据的下一个字节。public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
BufferedOutputStream
字节缓冲输出流
共性方法:
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b)
:将指定的字节输出流。
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
字节缓冲输入流
构造方法
public BufferedInputStream(InputStream in)
:创建一个 新的缓冲输入流。 ,保存其参数,即输入流in,以便将来使用
public BufferedInputStream(InputStream in,int size )
:创建一个 具有指定缓冲区大小的缓冲输入流,并保存其参数
参数:
InputStream in字节输入流:我们可以传递FileInputStream,增加一个缓冲区,提高FileInputStream的读取效率
int size:指定缓冲流内部缓冲区大小,不指定默认
使用步骤
1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象效率
3.使用BufferedInputStream对象中的方法read ,读取文件
4.释放资源(会调用Flue方法刷新数据)
字节缓冲输出流
public BufferedOutputStream(OutputStream out)
: 创建一个新的缓冲输出流。以将数据写入指定的底层输出流public BufferedOutputStream(OutputStream out,int size)
: 创建一个新的缓冲输出流。以将数据写入指定的底层输出流
参数:
OutputStream out:字节输出流
我们可以传递FileOutputStream,缓冲流会给FileOutoutStream增加一个缓冲区,提高FileOutputStream 的写入效率
使用步骤:
1.创建FileOutputStream对象,构造方法中绑定要输出目的地
2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象 对象,提高FileOutputStream对象效率
3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区,
4.使用BufferedOutputStream对象中的方法flush把内部缓冲区中的数据,刷新到文件中
5.释放资源(会调用Flue方法刷新数据)
构造举例,代码如下:
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
关闭缓冲流,会自动关闭基本流
效率测试
- 基本流,代码如下:
public class BufferedDemo {public static void main(String[] args) throws FileNotFoundException {// 记录开始时间long start = System.currentTimeMillis();// 创建流对象try (FileInputStream fis = new FileInputStream("jdk9.exe");FileOutputStream fos = new FileOutputStream("copy.exe")){// 读写数据int b;while ((b = fis.read()) != -1) {fos.write(b);}} catch (IOException e) {e.printStackTrace();}// 记录结束时间long end = System.currentTimeMillis();System.out.println("普通流复制时间:"+(end - start)+" 毫秒");}
}十几分钟过去了...
- 缓冲流,代码如下:
public class BufferedDemo {public static void main(String[] args) throws FileNotFoundException {// 记录开始时间long start = System.currentTimeMillis();// 创建流对象try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));){// 读写数据int b;while ((b = bis.read()) != -1) {bos.write(b);}} catch (IOException e) {e.printStackTrace();}// 记录结束时间long end = System.currentTimeMillis();System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");}
}缓冲流复制时间:8016 毫秒
如何更快呢?
使用数组的方式,代码如下:
public class BufferedDemo {public static void main(String[] args) throws FileNotFoundException {// 记录开始时间long start = System.currentTimeMillis();// 创建流对象try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));){// 读写数据int len;byte[] bytes = new byte[8*1024];while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0 , len);}} catch (IOException e) {e.printStackTrace();}// 记录结束时间long end = System.currentTimeMillis();System.out.println("缓冲流使用数组复制时间:"+(end - start)+" 毫秒");}
}
缓冲流使用数组复制时间:666 毫秒
字符缓冲流
共性方法
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()
关闭此流,但要先刷新它。
BufferedWriter
字符缓冲输出流
构造方法
BufferedWriter(writer out):创建一个使用默认大小输出缓冲区的缓冲输出流
BufferedWriter(writer out,int sz):创建一个使用给定大小输出缓冲区的新缓冲字符输出流
参数:
writer out:字符输出流
我们可以传递一个FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWrite的写入效率
int sz:指定缓冲区的大小,不写默认大小
特有成员方法:
Void newLine():写入一个分隔符,会根据不同的操作系统,获取不同的行分隔符
换行符号:
windows:\r\n
linux:/n
mac:/r
使用步骤:
1.创建字符缓冲输出流对象,构造方法中传递字符输出流
2.调用字符缓冲流中的方法,write,把数据写入到内存缓冲区中
3.调用字符缓冲流中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源
BufferedReader
,extends Reader 字符缓冲输入流
- 共有成员方法
public int read()
: 从输入流读取一个字符。public int read(char[] cbuf)
: 从输入流中读取多个字符,并将它们存储到字符数组 cbuf中 。public void close()
:关闭此流并释放与此流相关联的任何系统资源。
构造方法
BufferedReader(Reader in):创建一个使用默认大小的输出缓冲的缓冲字符输入流
BufferedReader(Reader in,int sz):创建一个使用指定大小的缓冲区的缓冲字符输入流
参数
Reader in:字符输入流我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率
int sz:
特有成员方法
String readLine():读取一个文本行 。读一行数据,行的终止符号:通过列字符之一即可认为某行已终止,换行\n 回车\r,回车后跟着换行(\r\n)
返回值:包含该行内容字符串,不包含任何行终止符,如果已到达流末尾,则返回null
使用步骤
- 创建字符缓冲输入流对象,构造方法中传递字符输入流
- 使用字符缓冲输入流对象中的方法read/readline读取文本
- 释放资源
readLine
方法演示,代码如下:
public class BufferedReaderDemo {public static void main(String[] args) throws IOException {// 创建流对象BufferedReader br = new BufferedReader(new FileReader("in.txt"));// 定义字符串,保存读取的一行文字String line = null;// 循环读取,读取到最后返回nullwhile ((line = br.readLine())!=null) {System.out.print(line);System.out.println("------");}// 释放资源br.close();}
}
1.4 练习:文本排序
练习:
对文本的内容进行排序
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
案例分析
1.创建一个HashMap集合对象,可以存储每行文本的序号,value存储每行的文本
2.创建字符缓冲输入流对象,构造方法绑定字符输入流
3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
4.使用字符缓冲输入流的方法readLine,逐行读取文本
5.对读取到的文本 进行切割,获取行中的序号和文本内容
6.把切割好的序号和文本的内容存储到Hashmap集合中(key序号是有序的,会自动排序)
7.遍历HashMap集合,获取每一个键值对
8.把每一个键值对拼接为一个文本行
9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
10.释放资源
案例实现
public class BufferedTest {public static void main(String[] args) throws IOException {//1. 创建map集合,保存文本数据,键为序号,值为文字HashMap<String, String> lineMap = new HashMap<>();// 2.创建字符缓冲输入流对象,构造方法中绑定字符输入流BufferedReader br = new BufferedReader(new FileReader("in.txt"));//3.创建一个字符缓冲输出流对象,构造方法中绑定字符串输出流BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
//4.使用字符缓冲输出流中的方法readLine逐行读取文本// 读取数据String line = null;while ((line = br.readLine())!=null) {// 解析文本String[] split = line.split("\\.");// 保存到集合lineMap.put(split[0],split[1]);}// 释放资源br.close();// 遍历map集合for (int i = 1; i <= lineMap.size(); i++) {String key = String.valueOf(i);// 获取map中文本String value = lineMap.get(key);// 写出拼接文本bw.write(key+"."+value);// 写出换行bw.newLine();}// 释放资源bw.close();}
}
转换流
字符编码和字符集
按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。
字符编码:就是一套自然语音字符与二进制数之间的对应规则
字符集:也叫编码表,是一个系统支持所有字符的集合
Idea默认编码utf-8
FileReader可以读取IO默认编码格式UTF-8的文件
FileReader读取系统默认编码GBK中文 会产生乱码
InputStreamReader类
转换流java.io.InputStreamReader
,是Reader的子类,是从字节流到字符流的桥梁。
它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
参数:
InputSrteam in:字节输入流,用来读取文件中保存的字节
String charsetName:指定的编码表名称,不区分大小写,可以实Utf-8,Gbk。。。。不指定默认使用UTF-8
使用步骤:
- 创建InputStreamReader对象,构造方法重视传递字节输出流和指定的编码表名称。
- 使用InputStreamReader对象中的方法read读取文件
- 释放资源
注意事项:
构造方法中指定的编码表民晨光要和文件的编码相同,否则会发生乱码
OutputStreamWriter类
转换流java.io.OutputStreamWriter
,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。
参数
OutputStream out:字节输出流,用来写转换之后的字节到文件中
String charsetName:指定的编码表名称,不区分大小写,可以是Utf-8,GBK…,不指定默认使用Utf-8
使用步骤
- 创建OutputStreamWrite对象,构造方法中传递字节输出流和指定编码表名称
- 使用OutputStreamWrite,把字符转换为字节存储在缓冲区中(编码)
- 使用OutputStreamWrite对象中的方法flush,把内存缓冲区的字节刷新到文件中(使用字节流写字节的过程)
- 释放资源
指定编码写出
public class OutputDemo {public static void main(String[] args) throws IOException {// 定义文件路径String FileName = "E:\\out.txt";// 创建流对象,默认UTF8编码OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));// 写出数据osw.write("你好"); // 保存为6个字节osw.close();// 定义文件路径String FileName2 = "E:\\out2.txt";// 创建流对象,指定GBK编码OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");// 写出数据osw2.write("你好");// 保存为4个字节osw2.close();}
}
练习:转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
案例分析
1.创建InputStreanReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
2.创建OutputStreamWrite对象,构造方法中传递字节输出流和指定的编码表utf-8
3.使用InputSteamReader对象中的方法read读取文件
4.使用OutputStreamWrite方法write,把读取的数据写入到文件中
5.释放资源
案例实现
public class TransDemo {public static void main(String[] args) { // 1.定义文件路径String srcFile = "file_gbk.txt";String destFile = "file_utf8.txt";// 2.创建流对象// 2.1 转换输入流,指定GBK编码InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");// 2.2 转换输出流,默认utf8编码OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));// 3.读写数据// 3.1 定义数组char[] cbuf = new char[1024];// 3.2 定义长度int len;// 3.3 循环读取while ((len = isr.read(cbuf))!=-1) {// 循环写出osw.write(cbuf,0,len);}// 4.释放资源osw.close();isr.close();}
}
序列化
概述
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据
、对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。
把对象以流的方式,写入到文件中保存,叫写对象,也可以叫对象的序列化
对象中包含的不仅仅是字符使用字节流
ObjectOutputStream:对象的序列化writeObject(p)
把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫对象的反序列化
读取的文件保存的都是字节使用字节流
ObjectInputStream:对象的反序列化流
readObject()
ObjectOutputStream类
作用:把对象以流的方式写入到文件中保存
java.io.ObjectOutputStream
extends OutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定OutputStream的ObjectOutputStream。
参数:
OutputStream out:字节输出流
构造举例,代码如下:
特有的成员方法:
public final void writeObject (Object obj)
: 将指定的对象写出。
使用步骤,需要序列化对象实现类
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
3.释放资源
序列化操作
1.一个对象要想序列化,必须满足两个条件:
该类必须 实现
java.io.Serializable
接口来启用序列化功能,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。实现Serializable
接口,会给类添加一个标记,当序列化和反序列化的时候,就会检测类上是否有这个标记该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。static关键字:静态关键字
静态优先于非静态加载到内存中(静态优先于对象进入到内存中)被static修饰的成员变量不能被序列化
transient
瞬态关键字: 被其修饰,不能被序列化
public class Employee implements java.io.Serializable {public String name;public String address;public transient int age; // transient瞬态修饰成员,不会被序列化public void addressCheck() {System.out.println("Address check : " + name + " -- " + address);}
}
2.写出对象方法
public final void writeObject (Object obj)
: 将指定的对象写出。
public class SerializeDemo{public static void main(String [] args) {Employee e = new Employee();e.name = "zhangsan";e.address = "beiqinglu";e.age = 20; try {// 创建序列化流对象ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));// 写出对象out.writeObject(e);// 释放资源out.close();fileOut.close();System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。} catch(IOException i) {i.printStackTrace();}}
}
输出结果:
Serialized data is saved
ObjectInputStream类
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
作用:把文件中保存的对象,以流的方式读取出来使用
构造方法
public ObjectInputStream(InputStream in)
: 创建一个指定InputStream的ObjectInputStream。
参数InputStream in:字节输入流
特有成员方法:
public final Object readObject ()
: 从ObjectInputStream读取一个对象。
使用步骤:
1.创建一个ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
3.释放资源
4.使用读取出来的对象
反序列化操作1
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream
读取对象的方法:
public final Object readObject ()
: 读取一个对象。
readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)当不存在对象的class文件时抛出异常
反序列化前提:
- 类必须实现
Serializable
- 必须存在类对应的class文件
public class DeserializeDemo {public static void main(String [] args) {Employee e = null;try { // 创建反序列化流FileInputStream fileIn = new FileInputStream("employee.txt");ObjectInputStream in = new ObjectInputStream(fileIn);// 读取一个对象e = (Employee) in.readObject();// 释放资源in.close();fileIn.close();}catch(IOException i) {// 捕获其他异常i.printStackTrace();return;}catch(ClassNotFoundException c) {// 捕获类找不到异常System.out.println("Employee class not found");c.printStackTrace();return;}// 无异常,直接打印输出System.out.println("Name: " + e.name); // zhangsanSystem.out.println("Address: " + e.address); // beiqingluSystem.out.println("age: " + e.age); // 0}
}
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException
异常。
反序列化操作2
**另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。**发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
问题:
每次修改类的定义,都会给class文件生成一个新得序列号
解决方法:
无论是否对类的定义进行修改,都不生成新的序列号
可以手动给类添加一个序列号
格式在Serializable
接口规定:
可序列化类可以通过声明为serialVersionUID的字段(该字段必须是static)最终final的long星字段,显示声明其自己的serialVersionUID
static final long serialVersionUID=42L
public class Employee implements java.io.Serializable {// 加入序列版本号private static final long serialVersionUID = 1L;public String name;public String address;// 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.public int eid; public void addressCheck() {System.out.println("Address check : " + name + " -- " + address);}
}
练习:序列化集合
- 将存有多个自定义对象的集合序列化操作,保存到
list.txt
文件中。 - 反序列化
list.txt
,并遍历集合,打印对象信息。
练习:
当我们想在文件中保存多个对象的时候
可以把多个对象存储到一个集合中
对象进行序列化和反序列化
分析:
定义一个存储Person对象的Array List集合
往ArrayList集合中存储Person对象
创建一个序列化流ObjectOutputStream
使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
创建一个反序列化Object InputStream对象
使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
把Object类型的集合转换为ArraysList类型
遍历ArrayList集合
释放资源
案例分析
- 把若干学生对象 ,保存到集合中。
- 把集合序列化。
- 反序列化读取时,只需要读取一次,转换为集合类型。
- 遍历集合,可以打印所有的学生信息
案例实现
public class SerTest {public static void main(String[] args) throws Exception {// 创建 学生对象Student student = new Student("老王", "laow");Student student2 = new Student("老张", "laoz");Student student3 = new Student("老李", "laol");ArrayList<Student> arrayList = new ArrayList<>();arrayList.add(student);arrayList.add(student2);arrayList.add(student3);// 序列化操作// serializ(arrayList);// 反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));// 读取对象,强转为ArrayList类型ArrayList<Student> list = (ArrayList<Student>)ois.readObject();for (int i = 0; i < list.size(); i++ ){Student s = list.get(i);System.out.println(s.getName()+"--"+ s.getPwd());}}private static void serializ(ArrayList<Student> arrayList) throws Exception {// 创建 序列化流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));// 写出对象oos.writeObject(arrayList);// 释放资源oos.close();}
}
打印流
概述
平时我们在控制台打印输出,是调用print
方法和println
方法完成的,这两个方法都来自于java.io.PrintStream
类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
PrintStream类extends OutputStream
为其他输出流添加了功能,是他们能够方便的打印各种数据值表示形式
特点:
- 只负责数据的输出,不负责数据的读取
- 与其他输出流不同,其永远不回抛出IOexception
- 特有方法:print,println可以输出任意类型的值
构造方法
public PrintStream(String fileName)
: 使用指定的文件名创建一个新的打印流。输出目的地是一个文件路径public PrintStream(File file)
: 使用指定的文件创建一个新的打印流。输出目的地是一个文件public PrintStream(Output Stream)
: 使用指定的输出流创建一个新的打印流。输出目的地是一个字节输出流
构造举例,代码如下:
PrintStream ps = new PrintStream("ps.txt");
继承自父类的成员方法
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b)
:将指定的字节输出流。
注意事项
如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表
如果使用自己特有的print/println方法写数据,写的数据会原样输出
使用步骤
- 创建打印流PrintStream对象,构造方法中绑定要输出的目的地(会抛出文件找不到异常,不回抛出IO异常)
- 使用父类方法或自己特有方法
- 释放资源
改变打印流向
System.out
就是PrintStream
类型的,只不过它的流向是系统规定的,打印在控制台上。
可以改变输出语句的目的地(打印流的流向)
输出语句,默认在控制台输出
使用System.setOut方法改变输出语句的目的地改为参数传递的打印流的目的地
static void setOut(PrintStream out )
重新分配“标准”输出流
public class PrintDemo {public static void main(String[] args) throws IOException {// 调用系统的打印流,控制台直接输出97System.out.println(97);// 创建打印流,指定文件的名称PrintStream ps = new PrintStream("ps.txt");// 设置系统的打印流流向,输出到ps.txtSystem.setOut(ps);// 调用系统的打印流,ps.txt中输出97System.out.println(97);
ps.close();}
}
Java笔记整理六(File类,递归,字节流IO,字符流IO,流中的异常处理,属性集Properties,缓冲流,转换流,序列化,打印流)相关推荐
- 一文读懂Java中File类、字节流、字符流、转换流
一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...
- 字节流转化为文件流_JAVA IO分析一:File类、字节流、字符流、字节字符转换流...
因为工作事宜,又有一段时间没有写博客了,趁着今天不是很忙开始IO之路:IO往往是我们忽略但是却又非常重要的部分,在这个讲究人机交互体验的年代,IO问题渐渐成了核心问题. 一.File类 在讲解File ...
- 【Java基础】Java中的持久属性集Properties
Properties 类的介绍 Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串.一个属性列表可包含另一个属性列 ...
- java基础练习复习二:递归字节流字符流二
本篇是基于java基础练习复习一:递归&字节流&字符流一, 如果对您有帮助 ,请多多支持.多少都是您的心意与支持,一分也是爱,再次感谢!!!打开支付宝首页搜"55672346 ...
- JAVA笔记整理(1):进制转换
JAVA笔记整理(1):进制转换 16.2.28 深圳坪山 多云 18℃ 爱吃螺蛳粉的巴赫 摘要:工具类转化函数:关于进制--2/10/16进制转化(Util_transform_scale)- ps ...
- Java笔记整理五(Iterator接口,泛型,常见数据结构(栈,队列,数组,链表,红黑树,集合),jdk新特性,异常,多线程,Lambda表达式)
Java笔记整理五 1.1Iterator接口 Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象 ...
- 【Java10】lambda表达式(函数式编程),Stream流,File类,字节/字符流,乱码,缓冲/转换/序列化/打印流,Properties
文章目录 1.lambda表达式标准语法:()->{} 2.lambda表达式简略语法:可推导即可省略 3.lambda表达式原理:lambda效率比匿名内部类高 4.两个函数式接口:Consu ...
- 《Java SE实战指南》22-04:字节流和字符流
<Java SE实战指南> 22-04:字节流和字符流 内容导航: 前言 1.分类定义 2.字符流常用类 2.1.FileWriter 2.2.FileReader 前言 我们每天都在使用 ...
- java(十)【属性集,缓冲流、转换流、序列化流】
day10[缓冲流.转换流.序列化流] 今日目标 IO资源的处理. finnally释放资源 jdk 1.7开始的新技术 try-with-resources 缓冲流 提高字节流和字符流读写数据的性能 ...
最新文章
- H.264NLU和RTSP协议理解
- 什么是优秀管理者的第一课?【如何搞垮一个企业】
- 回车键兼容多个浏览器
- Cannot initialize a parameter of type ‘NSArray<id<RCTBridgeModule>> *‘ with an rvalue of type ‘NSArr
- opencv图片全景拼接详解
- SpringBoot对于标注@ResponseBody注解返回JSON数据的处理
- 图论--SCC缩点--Tarjan
- 谷歌正在移除 Chrome 的“关闭其他选项卡”选项
- 如何设置.net控件SplitContainer平均分配
- Git 修改历史提交中的用户名和邮箱
- 邮箱格式怎么写?电子邮箱格式怎么写才是正确的?
- 前端培训课随感,传智、千锋、尚硅谷、渡一
- 学习Python前景怎么样?
- 【python第3课】顺序、循环、分支
- bayes-opt安装
- Excel如何快速评定考核成绩等级
- php 标点符号,php 过滤英文标点符号及过滤中文标点符号代码_PHP教程
- 如何下载知网上的论文?
- CCSP201902纸牌计数——解题报告
- SSM框架下对信息执行修改操作时的信息弹窗回显以及对信息修改后对数据库的更新问题