JAVA14-File类、递归、字节流、字符流、缓冲流、转换流、序列化流、Files
1.File类
java.lang.Object java.io.File: 文件和目录路径名的抽象表示形式,也就是说java将电脑中的文件和文件夹(目录)封装为了一个类,我们可以使用File类对文件和文件夹进行操作。所有已实现的接口: Serializable, Comparable<File> 。
1.1.作用:我们可以使用File类的方法
1.2.注意:File类与操作系统无关,任何系统都可以使用这个类的方法
1.3学习File的4个静态的成员变量,既然是静态的,类名可以直接访问
其实 pathSeparator 和pathSeparatorChar 一样,只是返回加了一个字符串
public static final String pathSeparator = "" + pathSeparatorChar;
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 String s= File.pathSeparator;//static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。 4 char s1= File.pathSeparatorChar;//static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。 5 String s2= File.separator;//static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。 6 char s3= File.separatorChar;//static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。 7 System.out.println(s); 8 System.out.println(s1); 9 System.out.println(s2); 10 System.out.println(s3); 11 } 12 } 13 结果 14 ; 15 ; 16 \ 17 \
注意:separator:分离器
1)windows系统是\ linux是/
2) 未来开发,路径不建议写死,用“+File.separator+”链接名称如下:
E:\02 软件开发\02-Java语进阶\day08_File类、递归
E:“+File.separator+”02 软件开发“+File.separator+”-Java语进阶”+File.separator+”day08_File类、递归
1.4.文件路径知识
绝对路径:是一个完整的路径,一般以盘符开始,就是绝对路径
E:\02 软件开发\02-Java语进阶\day08_File类、递归\123.txt
相对路径:是一个简化路径,一般相对于当前项目的根目录,简化书写
123.txt
注意:路径不区分大小写,路径中文件分隔符使用反斜杠,两个反斜杠是转义符
1.5.File类的构造方法
静态变量通过类名直接访问,而通过构造方法之后,就可以创建对象,也就可以使用成员方法。
参数 File parent, String child,也是父子类路径,不同的是父类路径是类对象,也就是说可以File的方法对路径进行操作,再使用路径创建对象。
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 File f = new File("c:\\");// 4 File file = new File(f,"123.txt");// f可以调用File类的方法 5 System.out.println(file); 6 } 7 结果 8 c:\123.txt
注意:参数String pathname是字符串的路径名称,而路径可以是文字杰尔威,也可以是文件夹结尾,路径可以是相对路径,也可以是绝对路径,可以可以存在,也可以不存在,创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况,且idea自动编译2个反斜杠
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 show01(); 4 } 5 6 private static void show01() { 7 File f = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归\\123.txt"); 8 // public String toString() { 9 // return getPath(); 10 // } 11 System.out.println(f);// 打印对象,出不是地址值,而是路径,说明重写了toString()方法 12 } 13 }
结果:
E:\02 软件开发\02-Java语进阶\day08_File类、递归\123.txt
参数:把路径分为两部分,前者是父路径,后者为子路径。父路径和子路径可以单独书写,使用起来非灵活,父子路径均可变化。
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 show02("c:", "123.txt"); 4 } 5 private static void show02(String parent, String child){ 6 File f2 = new File(parent, child); 7 System.out.println(f2); 8 } 9 } 10 结果 11 c:123.txt
1.6 File的方法 获取功能方法4个,判断功能方法3个
获取功能
1 private static void show02(){ 2 File f = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归"); 3 String abPath = f.getAbsolutePath(); 4 System.out.println(abPath); 5 System.out.println("================="); 6 File f2 = new File("123.txt"); 7 String ab = f2.getAbsolutePath(); 8 System.out.println(ab); 9 }
public String getPath() {
return path;
}
1 private static void show02(){ 2 File f = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归"); 3 String abPath = f.getPath(); 4 System.out.println(abPath); 5 System.out.println("================="); 6 File f2 = new File("123.txt"); 7 String ab = f2.getPath(); 8 System.out.println(ab); 9 }
1 private static void show02(){ 2 File f = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归"); 3 String abPath = f.getName(); 4 System.out.println(abPath); 5 System.out.println("================="); 6 File f2 = new File("123.txt"); 7 String ab = f2.getName(); 8 System.out.println(ab); 9 } 10 结果 11 day08_File类、递归 12 ================= 13 123.txt
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 show02(); 4 } 5 private static void show02(){ 6 File file = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归\\day08【File类、递归】.md"); 7 long l = file.length(); 8 System.out.println(l); 9 } 10 } 11 结果 12 15576
判断功能:返回的都是布尔值
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 show02(); 4 } 5 private static void show02(){ 6 File file = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归\\day08【File类、递归】.md"); 7 File file2 = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归"); 8 System.out.println(file.exists()); 9 System.out.println(file.isFile()); 10 System.out.println(file2.isDirectory()); 11 } 12 } 13 结果 14 true 15 true 16 false
由于考虑文件可能路径不对,可以用if语句:
1 private static void show02(){ 2 File file = new File("day08-code.iml"); 3 File file2 = new File("E:\\02 软件开发\\02-Java语进阶\\day08_File类、递归"); 4 if (file2.exists()){ 5 System.out.println(file.exists()); 6 System.out.println(file.isFile()); 7 System.out.println(file2.isDirectory()); 8 } 9 10 }
创建和删除的功能
注意:此方法只能创建文件而不是文件夹,如果路径不存在则报错。
此方法本身抛出io异常,我们得处理这个异常,我们要么throws 要么try catch
public boolean createNewFile() throws IOException {
SecurityManager security = System.getSecurityManager();
if (security != null) security.checkWrite(path);
if (isInvalid()) {
throw new IOException("Invalid file path");
}
return fs.createFileExclusively(path);
}
注意:1.文件夹内有内容,不删除返回false
2.delete方法是直接在硬盘中删除文件或文件夹,不走回收站,删除要谨慎
3.delete方法,如果此File表示目录,则目录必须为空才能删除。
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 File file = new File("aaa"); 4 File file2 = new File("D:\\IdeaProjects\\day08-code\\111\\222"); 5 boolean delete = file.delete(); 6 boolean delete1 = file2.delete(); 7 8 } 9 }
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 File file = new File("aaa"); 4 boolean mkdir = file.mkdir(); 5 System.out.println(mkdir); 6 File file2 = new File("D:\\IdeaProjects\\day08-code\\111\\222\\333"); 7 boolean mkdir2 = file2.mkdirs(); 8 System.out.println(mkdir2); 9 } 10 } 11
遍历方法
注意:
1.两个方法遍历的是构造方法给出的目录,如果构造方法中给出的目录的路径不存在或是文件,则会抛出空指针异常。
2.两个方法遍历的是构造方法给出的目录,如果构造方法中给出的路径不是一个目录,也会抛出空指针异常。
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 File file2 = new File("D:\\IdeaProjects\\day08-code"); 4 String[] list = file2.list(); 5 for (String s : list) { 6 System.out.println(s); 7 } 8 } 9 } 10
2.递归:分为直接递归和间接递归
递归就是指当前方法内调用自己的现象,递归分为直接递归和间接递归
直接递归:自己调用自己
main() { a();}
a(){ a();}
间接递归:A方法调用B方法,B方法调用C方法
main() { a();}
a(){ b();}
b(){ c();}
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 show(); 4 } 5 6 private static void show() { 7 for (int i = 0; i < 5 ; i++) { 8 System.out.println(10); 9 } 10 show(); 11 } 12 }
注意事项:java.lang.StackOverflowError 栈内存溢出
递归的使用前提:
当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归。
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 show(1); 4 } 5 6 private static void show(int i) { 7 System.out.println(i); 8 if (i == 10){ 9 return; 10 } 11 show(++i); 12 } 13 }
练习:计算1-n的合
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 int su = show(4); 4 System.out.println(su); 5 } 6 7 private static int show(int s) { 8 if (s == 1){ 9 return 1; 10 } 11 int sum = s + show(s-1);// 获取下一个被加的数字 12 return sum; 13 } 14 }
原理:内存中出现4个方法
练习:计算阶乘 即3! = 3*2*1
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 int su = show(3); 4 System.out.println(su); 5 } 6 7 private static int show(int s) { 8 if (s == 1){ 9 return 1; 10 } 11 int sum = s * show(s-1);// 获取下一个被加的数字 12 return sum; 13 } 14 }
练习:递归打印多级目录
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 File f1 = new File("E:\\02 软件开发\\02-Java语进阶"); 4 getFiles(f1); 5 } 6 private static void getFiles(File dir ) { 7 File[] files = dir.listFiles(); 8 for (File f : files) {// 增强for循环 9 if (f.isDirectory()){// 如果是文件夹,调用getFiles()方法,这属于递归 10 getFiles(f); 11 } 12 System.out.println(f); 13 } 14 } 15 }
综合案例:搜索`D:\aaa` 目录中的`.java` 文件。如果担心文件夹结尾有大小写,可以转小写 s = s.tolowerCase();
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 File f1 = new File("E:\\02 软件开发\\02-Java语进阶"); 4 getFiles(f1); 5 } 6 private static void getFiles(File dir ) { 7 File[] files = dir.listFiles(); 8 for (File f : files) {// 增强for循环 9 if (f.isDirectory()){// 如果是文件夹,调用getFiles()方法,这属于递归 10 getFiles(f); 11 }else { 12 // 把f转换为字符串方法, 13 //f.getName(); 可以 14 //f.getPath();可以 15 String string = f.toString();// 可以 16 boolean b = string.endsWith(".java"); 17 if (b){// true代表是.java结尾的路径 18 System.out.println(string); 19 } 20 } 21 } 22 } 23 }
综合案例的优化,可以使用文件过滤器优惠
2.3.File类的过滤器
在File类中,有两个和ListFiles重载的方法,方法的参数传递就是过滤器
1)File[] listFiles(FileFilter filter) 传递的是过滤器, 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。其中java.io.FileFilter接口(用于抽象路径名(就是file对象)的过滤器。 )它有一个方法是
boolean accept(File pathname) 这个参数传递的就是过滤器(过滤文件 file对象),测试指定抽象路径名是否应该包含在某个路径名列表中。参数File pathname就是使用ListFiles方法遍历目录,得到的一个对象。
2)File[] listFiles(FilenameFilter filter) 传递的是过滤器, 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。java.io.FilenameFilter(实现此接口的类实例可用于过滤器文件名)作用用于过滤文件名称。他的方法:
boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
参数 File dir 构造方法中传递的被遍历的目录
参数 String name 使用ListFiles方法遍历目录,获得每一个文件或文件夹名称。
因此1和2两过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤方法accept,在方法中自己定义过滤的原则。
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 File f1 = new File("E:\\02 软件开发\\02-Java语进阶"); 4 getFiles(f1); 5 } 6 private static void getFiles(File dir ) { 7 File[] files = dir.listFiles(new FileFilterImpl());// 传递了过滤器 8 for (File file : files) { 9 if (file.isDirectory()){ 10 getFiles(file); 11 }else { 12 System.out.println(file); 13 } 14 } 15 } 16 }
1 package cn.itcast.demo01.demo01; 2 3 import java.io.File; 4 import java.io.FileFilter; 5 6 // 创建 7 public class FileFilterImpl implements FileFilter { 8 9 10 @Override 11 public boolean accept(File pathname) { 12 if (pathname.isDirectory()){ 13 return true; 14 }else{ 15 return pathname.getName().toLowerCase().endsWith(".java"); 16 17 } 18 } 19 }
如果是匿名内部类
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 File f1 = new File("E:\\02 软件开发\\02-Java语进阶"); 4 getFiles(f1); 5 } 6 private static void getFiles(File dir ) { 7 File[] files = dir.listFiles(new FileFilter() { 8 @Override 9 public boolean accept(File pathname) {// 过滤规则 pathname是文件夹或者是.java结尾的文件返回true 10 return pathname.isDirectory()|| pathname.getName().toLowerCase().endsWith(".java"); 11 } 12 });// 传递了过滤器 13 for (File file : files) { 14 if (file.isDirectory()){// 如果是文件,则继续遍历 15 getFiles(file); 16 }else { 17 System.out.println(file); 18 } 19 } 20 } 21 }
匿名内部类玩法
1 public class Demo05StaticField { 2 public static void main(String[] args) { 3 File f1 = new File("E:\\02 软件开发\\02-Java语进阶"); 4 getFiles(f1); 5 } 6 private static void getFiles(File dir ) { 7 File[] files = dir.listFiles(new FileFilter() { 8 @Override 9 public boolean accept(File pathname) {// 过滤规则 pathname是文件夹或者是.java结尾的文件返回true 10 return pathname.isDirectory()|| pathname.getName().toLowerCase().endsWith(".java"); 11 } 12 });// 传递了过滤器 13 File[] files2 = dir.listFiles(new FilenameFilter() { 14 @Override 15 public boolean accept(File dir, String name) { 16 return new File(dir,name).isDirectory()|| name.toLowerCase().endsWith(".java"); 17 } 18 }); 19 20 for (File file : files) { 21 if (file.isDirectory()){// 如果是文件,则继续遍历 22 getFiles(file); 23 }else { 24 System.out.println(file); 25 } 26 } 27 } 28 }
3.IO
生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了`ctrl+s` ,可能文件就白白编辑了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。
我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为`输入input` 和`输出output` ,即流向内存是输入流,流出内存的输出流。
Java中I/O操作主要是指使用`java.io`包下的内容,进行输入、输出操作。**输入**也叫做**读取**数据,**输出**也叫做作**写出**数据。
3.1.字节流
根据数据的类型分为:**字节流**和**字符流**。
* **字节流** :以字节为单位,读写数据的流。
* **字符流** :以字符为单位,读写数据的流。
3.2 顶级父类们
| | **输入流** | 输出流 |
| :-----: | :------------------------: | :-------------------------: |
| **字节流** | 字节输入流<br />**InputStream** | 字节输出流<br />**OutputStream** |
| **字符流** | 字符输入流<br />**Reader** | 字符输出流<br />**Writer**
3.1.字节输出流
public abstract class OutputStream extends Object implements Closeable, Flushable
实现的子类
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。 需要定义 OutputStream
子类的应用程序必须始终提供至少一种可写入一个输出字节的方法。
OutputStream
方法摘要 ,供所有子类使用在此我们先学习下FileOutputStream实现类的构造方法和成员方法
1)FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
参数:File file 目的是一个路径
String name 目的地是一个文件
2)FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。
3)FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。
5)FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。
FileOutputStream构造方法的作用:
1)创建一个FileOutputStream对象
2)会根据构造方法中传递的文件/文件路径,创建一个空的文件
3)会把FileOutputStream对象指向创建好的文件
写入数据的原理(内存→硬盘)
java程序→JVM(Java虚拟机)→OS(操作系统)→OS调用写数据的方法→把数据写入文件中
字节输出流的使用步骤
1)创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
2)调用FileOutputStream对象中的方法write,把数据写入文件中
3)释放资源。(流会占用一定的内存)
//FileOutputStream对象会抛出FileNotFoundException异常,含义是可能没有路径
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 FileOutputStream fos = new FileOutputStream("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\223.txt"); 4 fos.write(97);//写入成功,但是是a 5 fos.close(); 6 } 7 }
>注意:
> 1. 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
> 2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。
练习:一次写多个数据
注意:如果写的第一个字节是正数(0-127),那么显示的是时候会查询ASCII表
如果写的第一个字节是负数,那么第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(比方说gbk)
byte[] b,数组b
int off, 从字符串b中第几个索引开始写
int len 写几个进去
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 //用匿名对象来传参,实现创建对象的构造方法中绑定要写入数据的目的地 4 FileOutputStream fos = new FileOutputStream(new File("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt")); 5 byte[] bytes = {65,66,67,68,69}; 6 fos.write(bytes); 7 fos.close(); 8 } 9 }
utf-8中,3个字节一个中文,你好是[-28, -67, -96, -27, -91, -67],两个字。
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 //用匿名对象来传参,实现创建对象的构造方法中绑定要写入数据的目的地 4 FileOutputStream fos = new FileOutputStream(new File("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt")); 5 byte[] bytes1 = {-65,-66,67,68,69}; 6 byte[] bytes = "你好".getBytes(); 7 System.out.println(Arrays.toString(bytes));// [-28, -67, -96, -27, -91, -67] 8 fos.write(bytes,0,6); 9 } 10 }
3.1.1数据追加续写和换行(window 、\r\n Linux /n 苹果 /r)
经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?
这两个构造方法,参数中都需要传入一个boolean类型的值,`true` 表示追加数据,`false` 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 //用匿名对象来传参,实现创建对象的构造方法中绑定要写入数据的目的地 4 FileOutputStream fos = new FileOutputStream(new File("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt"),true); 5 byte[] bytes1 = {-65,-66,67,68,69}; 6 byte[] bytes = "你好".getBytes(); 7 System.out.println(Arrays.toString(bytes));// [-28, -67, -96, -27, -91, -67] 8 fos.write(bytes,0,6); 9 } 10 }
1 public class Demo05StaticField { 2 public static void main(String[] args) throws IOException { 3 //用匿名对象来传参,实现创建对象的构造方法中绑定要写入数据的目的地 4 FileOutputStream fos = new FileOutputStream(new File("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt"),true); 5 for (int i = 0; i <10 ; i++) { 6 fos.write("你好".getBytes()); 7 fos.write("\r\n".getBytes()); 8 } 9 } 10 }
> * 回车符`\r`和换行符`\n` :
> * 回车符:回到一行的开头(return)。
> * 换行符:下一行(newline)。
> * 系统中的换行:
> * Windows系统里,每行结尾是 `回车+换行` ,即`\r\n`;
> * Unix系统里,每行结尾只有 `换行` ,即`\n`;
> * Mac系统里,每行结尾是 `回车` ,即`\r`。从 Mac OS X开始与Linux统一。
3.2.字节输入流 inputStream 把硬盘中的文件读取到内存中使用
java.io.InputStream `抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。既然是抽象接口,无法实现对象,得用子类。
实现的子类有:重点研究FileInputStream(文件字节输入流)可以把硬盘文件中的数据,读取到内存中使用
( java.io.FileInputStream extends InputStream )
inputStream 定义了所有子类共有的方法:
1)public void close()` :关闭此输入流并释放与此流相关联的任何系统资源。
2)public abstract int read()`:从输入流读取数据的下一个字节并返回,读到文件末尾会返回-1。
3)public int read(byte[] b)`:从输入流中读取一些字节数,并将它们存储到字节数组 b中。
4) public int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。
3.2.1.FileInputStream(文件字节输入流)构造方法
- FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
- FileInputStream(FileDescriptor fdObj) 通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。
- FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
参数解释
String name:文件的路径
File file 文件
构造方法的作用:
1)创建一个FileInputStream对象
2)把FileInputStream对象指定构造方法中药读取的文件
读取数据的原理:硬盘→内容
java程序 →Jvm→OS→OS读取数据的方法→读取文件
字节输入流的使用步骤(重点)
1)创建FileInputStream对象,构造方法中绑定要读取的数据源
2)使用FileInputStream对象将的方法read,读取文件中
3)释放资源
3.2.1.1字节输入流一次读取一个字节的方法
练习:从文件3txt读取 abcde,读到末尾返回-1
1 public class Demo06 { 2 public static void main(String[] args) throws IOException { 3 FileInputStream fis = new FileInputStream("D:\\IdeaProjects\\day08-code\\3.txt"); 4 int read = fis.read(); 5 System.out.println(read); 6 int read1 = fis.read(); 7 System.out.println(read1); 8 int read2 = fis.read(); 9 System.out.println(read2); 10 int read3 = fis.read(); 11 System.out.println(read3); 12 int read4 = fis.read(); 13 System.out.println(read4); 14 int read5 = fis.read(); 15 System.out.println(read5); 16 fis.close(); 17 } 18 } 19 结果 20 97 21 98 22 99 23 100 24 101 25 -1
while循环遍历文件中的数值,并进行char转换。read读完当前数据后,指针会指向下一个字节。不可以写成
while(fis.read()!=-1){
System.out.print(fis.read());//因为判断和输出都调用一次,值变了。
}
1 package cn.itcast.demo01.demo01; 2 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.IOException; 6 7 public class Demo06 { 8 public static void main(String[] args) throws IOException { 9 FileInputStream fis = new FileInputStream("D:\\IdeaProjects\\day08-code\\3.txt"); 10 int len = 0; 11 while((len = fis.read())!=-1){ 12 System.out.print(len+" ");// 97,98,99,100,101 13 System.out.print((char)len+" ");// abcde char类型转换 14 } 15 fis.close(); 16 } 17 } 18
read()读取数据的原理“:
1)第一步对象指定,而且指定的位置是第一个字节a
2)第二步read读取,但read不是直接去找a,而是找JVM,jvm找OS,OS再用read方法去读取数据,read比较特殊,读完一个自动往后指一位。
3)当读取到最后一位后,指针后再无数据,结束标记返回给os,os再返回结束标记给jvm,jvm返回结束标记给我们,当然linux和windows一样,结束标记最后都是-1.
3.2.1.2字节输入流一次读取多个字节
int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
明确两件事:
1)方法的参数byte[]的作用?
起到缓冲作用,存储每次取得多个字节,一般而言数组填1024(1kb)或1024整数倍。覆盖的时候是从索引0开始覆盖。
2)方法的返回值int是什么?
每次读取的有效字节个数。
1 public class Demo06 { 2 public static void main(String[] args) throws IOException { 3 FileInputStream fis = new FileInputStream("D:\\IdeaProjects\\day08-code\\3.txt"); 4 byte[] bytes = new byte[1024]; 5 int len = 0; 6 while ((len = fis.read(bytes))!= -1){ 7 System.out.println(new String(bytes,0,len)); 8 } 9 fis.close(); 10 } 11 }
练习:如何属于文件输出流复制文件:
注意:关闭输入流和输出流时,先关闭输出流,再关闭输入流。
1 public class Demo06 { 2 public static void main(String[] args) throws IOException { 3 FileInputStream fis = new FileInputStream("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt"); 4 FileOutputStream fos = new FileOutputStream("E:\\02 软件开发\\b.txt"); 5 int len = 0; 6 while ((len = fis.read())!= -1){ 7 fos.write(len); 8 } 9 fos.close(); 10 fis.close(); 11 } 12 }
3.3.字符流reader
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
3.3.1.字符输入流reader流
`java.io.Reader`抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。它是抽象类。
1)public void close()` :关闭此流并释放与此流相关联的任何系统资源。
2)public int read()`: 从输入流读取一个字符。
3)public int read(char[] cbuf)`: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。
它的实现子类有多个
BufferedReader 带缓冲区的字符流
CharArrayReader 数组字符流
FilterReader 过滤器字符流
InputStreamReader 转换字符流 重点学习
PipedReader 管道流
StringReader 读取字符串字符流
注意:InputStreamReader,它的直接子类是FileReader,也就是说
java.io.FileReader extends InputStreamReader extends Reader:把字符从硬盘文件中的数据,以字符的方式读取到内存中。
3.3.1.InputStreamReader,它的直接子类是FileReader的构造方法:
- FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。
- FileReader(FileDescriptor fd) 在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。
- FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
参数:fileName 文件路径
File file 读取的文件
作用:FileReader构造方法的作用两个:
1)创建一个FileReader对象。
2)会把FileReader对象指向要读取的文件。
练习:从文件中读取一些数据
1 public class Demo06 { 2 public static void main(String[] args) throws IOException { 3 FileReader fr = new FileReader("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt"); 4 int len = 0; 5 while ((len = fr.read())!= -1){ 6 System.out.print((char) len); 7 } 8 fr.close(); 9 } 10 }
1 public class Demo06 { 2 public static void main(String[] args) throws IOException { 3 FileReader fr = new FileReader("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\b.txt"); 4 char[] chars = new char[1024]; 5 int len = 0; 6 while ((len = fr.read(chars))!= -1){ 7 System.out.println(new String(chars,0,len)); 8 } 9 fr.close(); 10 } 11 }
3.4.字符输出流Writer
`java.io.Writer `抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
3.4.1.字节输出流的基本共性功能方法
- 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()` 关闭此流,但要先刷新它。
3.4.2.实现的子类
BufferedWriter
CharArrayWriter
FilterWriter
OutputStreamWriter 重点学习 输出转换流
PipedWriter
PrintWriter
StringWriter
java.io.FileWriter extends OutputStreamWriter extends Writer
用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
文件是否可用或是否可以被创建取决于底层平台。特别是某些平台一次只允许一个 FileWriter(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FileWriter 用于写入字符流。要写入原始字节流,请考虑使用 FileOutputStream。
3.4.3.FileWriter 的构造方法
- FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。
- FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
参数:File file 是一个文件
String fileName 文件路径
作用:1)创建一个FileWriter对象
2)
3)
4)
3.5.字符输出流中异常的处理办法
在jdk1.7之前,异常处理格式:
4)注意格式:
try{可能出现的异常代码}
catch(异常类变量 变量名){异常处理的逻辑:抓取处理异常的代码}
finally{必须执行的代码,如资源释放}
1 public class Demo06 { 2 public static void main(String[] args) { 3 FileWriter fw = null;// 必须先赋值,否则try catch的finally。 4 try { 5 // 可能产生异常的代码 6 System.out.println("============="); 7 fw = new FileWriter("F:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\e.txt",true); 8 System.out.println("---------------------"); 9 for (int i = 0; i <10; i++) { 10 fw.write("hewllworld" + i + "\r\n"); 11 12 } 13 14 // fw.close(); 思考一下,无论这个异常是否执行完,但资源释放都要进行,因此Close得放在finally里面 15 }catch(IOException e){// 由于try中的异常都是一样的io异常,因此这里异常的类型和变量名可以用一个,此处代码块我们打印出来 16 System.out.println("++ ++ "); 17 System.out.println(e); 18 System.out.println("((((((()))))))"); 19 20 }finally { 21 if (fw != null){ 22 // fw.close();由于此处fw这个定义是在try内部,需要挪出去定义fw对象,且赋值,否则try若执行不成功,则finally将无法成功执行,因为变量使用时未赋值 23 try { 24 fw.close(); 25 } catch (IOException e) { 26 System.out.println("== === === "); 27 e.printStackTrace(); 28 } 29 } 30 } 31 } 32 }
注意:1)fw.close();由于此处fw这个定义是在try内部,需要挪出去定义fw对象,且赋值,否则try若执行不成功,则finally将无法成功执行,因为变量使用时未赋值。
2)fw.close(); 思考一下,无论这个异常是否执行完,但资源释放都要进行,因此Close得放在finally里面,而且要判断是否fw为空,可用if(fw != null)
3)于try中的异常都是一样的io异常,因此这里异常的类型和变量名可以用一个,此处代码块我们打印出来。
=========JDK升级1.7后,异常处理新特性的分隔符===============
在JDK1.7 中,try后面可用增加一个(),括号内可以定义流对象。那么这个流对象的作用域就在try中有效。当try中的代码执行完毕,会自动把流对象释放,也就不需要用finally来释放。
JDK1.7异常处理格式:
try(流对象,可以定义多个,逗号分隔){可能出现的异常代码}
catch(异常类变量 变量名){异常处理的逻辑:抓取处理异常的代码}
1 public class Demo06 { 2 public static void main(String[] args) { 3 // 必须先赋值,否则try catch的finally。 4 try (FileWriter fw = new FileWriter("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\h.txt",true);){ 5 // 可能产生异常的代码 6 System.out.println("============="); 7 8 System.out.println("---------------------"); 9 for (int i = 0; i <10; i++) { 10 fw.write("hewllworld" + i + "\r\n"); 11 12 } 13 14 // fw.close(); 思考一下,无论这个异常是否执行完,但资源释放都要进行,因此Close得放在finally里面 15 }catch(IOException e){// 由于try中的异常都是一样的io异常,因此这里异常的类型和变量名可以用一个,此处代码块我们打印出来 16 System.out.println("++ ++ "); 17 System.out.println(e); 18 System.out.println("((((((()))))))"); 19 20 } 21 } 22 }
=========JDK升级1.9后,异常处理新特性的分隔符===============
在try的前面定义流对象,然后在try后面的括号内引入流对象的名称(变量名,如果多个要用分号;可以理解为多个定义需要语句结束),在try代码执行完毕后,流对象也可以释放掉。不用写finally
格式:
A a = new A();
try(a ;b){可能出现的异常代码}
catch(异常类变量 变量名){异常处理的逻辑:抓取处理异常的代码}
对比1.7和1.9发现,1.9定义流对象在外侧,还是要抛出异常,又有try{} catch(){}。相对麻烦,因此1.7好用些。
1 public class Demo06 { 2 public static void main(String[] args) throws IOException { 3 // 必须先赋值,否则try catch的finally。 4 FileWriter fw = new FileWriter("E:\\02 软件开发\\02-Java语进阶\\day09_字节流、字符流\\h.txt",true); 5 try (fw){ 6 // 可能产生异常的代码 7 System.out.println("============="); 8 9 System.out.println("---------------------"); 10 for (int i = 0; i <10; i++) { 11 fw.write("hewllworld" + i + "\r\n"); 12 13 } 14 15 // fw.close(); 思考一下,无论这个异常是否执行完,但资源释放都要进行,因此Close得放在finally里面 16 }catch(IOException e){// 由于try中的异常都是一样的io异常,因此这里异常的类型和变量名可以用一个,此处代码块我们打印出来 17 System.out.println("++ ++ "); 18 System.out.println(e); 19 System.out.println("((((((()))))))"); 20 21 } 22 } 23 }
4.属性集,说的是一个集合-每个键及其对应值都是一个字符串
我们之前学习的map集合中,有一个实现类HashTable,是最早期的双列集合。但是它是一个单线程的,被淘汰了,被HashMap取代了,但是HashTable有个子类Properties,现在依然很活跃。
java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
Properties 继承了Hashtable<k,v>,而Hashtable<k,v>又实现了Map<k,v>
解释: `java.util.Properties ` 继承于` Hashtable` ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,`System.getProperties` 方法就是返回一个`Properties`对象。
含义:Properties 集合是唯一一个和IO流相结合的集合,它有两个方法与流输入输出结合,可以使用store方法,将集合中的临时数据,持久化写入到硬盘中存储。又可以使用load方法,将硬盘中的数据,临时写到集合中。由于Properties 是双列的且键值都为字符串,所以使用它时,泛型不用定义。
- void store(OutputStream out, String comments) 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
- void store(Writer writer, String comments) 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。
- void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
- void load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
由于Propertis(所有物; 财产; 财物; 不动产; 房地产)的键值是字符串,所以它有一些处理字符串的特有方法:
- Object setProperty(String key, String value) 调用 Hashtable 的方法 put。底层也就是put,只是直接用字符串了。
- String getProperty(String key) 用指定的键在此属性列表中搜索属性。通过key找到value,此方法相当于map集合中的get(Key)方法
- String getProperty(String key, tring defaultValue) 用指定的键在属性列表中搜索属性。
- Set<String> stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。该方法常用于遍历。相当于map中的keyset()方法
1 public class Demo06 {
2 public static void main(String[] args) throws IOException {
3 show01();
4 }
5 // 学集合最终要的是保存数据和取出数据
6 private static void show01() {
7 Properties p = new Properties();//因为集合就是string,泛型不需要注意。ArrayList<String> arrayList2=new ArrayList<String>();
8 p.setProperty("张三","11"); 9 p.setProperty("李四","11"); 10 p.setProperty("王五","11"); 11 p.setProperty("赵六","11"); 12 Set<String> str = p.stringPropertyNames(); 13 for (String s : str) { 14 System.out.println("key" + s); 15 } 16 } 17 }