6、I/O

主要是解决设备与设备之间的数据传输问题 (内存 <----> 硬盘)
应用场景:导出报表、上传数据、下载、解释XML文件
数据一般以文件形式保存在硬盘上,使用了一个File类用于描述文件或者文件夹的(文件和目录路径名的抽象表示形式),读取文件或者文件夹的属性数据
要读到文件中的内容数据,需要用到流技术
 

一、File

1、File类的构造方法:

File(File parent, String child);  // 根据父抽象路径和子路径创建文件对象
File(String pathname);  // 指定文件路径创建文件对象 File file = new File("E:\1.txt"); 并不是创建文件
File(String parent, String child) // 两部分构建

目录分隔符:Windows“”(都可以使用,/只需要写一个),Linux是“/”,可以通过separator获取

绝对路径和相对路径
绝对:完整路径
相对:资源文件相对于当前程序所在的路径
 
.代表当前路径
..上一级路径
注意:程序当前所在的路径与资源文件不是在一个盘下,不能写当前路径
 

2、常用方法:

1、创建

(1)指定位置创建一个空文件,成功就返回true,存在就不创建返回false(要抛出IOExecption异常)
createNewFile();

(2)创建文件夹,只会创建最后一级文件夹,上级目录不存在就抛出异常

mkdir();

(3)创建多级文件夹,创建所有不存在的目录

mkdirs();

(4)目标文件与源文件是在同一目录下,就是重命名(文件和文件夹都行);不在则是剪切,而且不能操作文件夹

renameTo(File dest);

2、删除

(1)删除文件或文件夹

delete();

(2)JVM退出的时候删除文件,一般用于删除临时文件

deleteOnExit();

3、判断

(1)

exists();

(2)

isFile();

(3)

isDirectory();

(4)是否是隐藏文件或者隐藏目录

isHidden();

(5)

isAbsolute();

4、获取

(1)获取文件或文件夹名,不含上级路径

getName();

(2)返回绝对路径,也可以是相对路径,但是目录要指定

getPath();

(3)获取绝对路径,与文件存在没有关系

getAbsolutePath();

(4)获取文件大小,字节数

length();

(5)获取文件的父路径

getParent();

(6)最后一次修改时间

lastModified();

5、文件夹相关

(1)列出所有的根目录

listRoots();

(2)把当前文件夹下面的所有子文件名与子文件夹名(不包括孙)存储到一个String数组中返回

list();

(3)把当前文件夹下的所有子文件和子文件夹都使用一个File对象描述,然后把File对象存储到File数组中

listFiles();

(4)

list(FilenameFilter filter);

(5)自定义文件名过滤器

listFiles(FilenameFilter filter);

输入输出划分:

--------输入流:相对本程序
--------输出流
 
处理单位划分:
--------字节流:读取文件中二进制数据,不会结果任何处理(FileInputStream )
--------字符流:以字符为单位,读取文件中二进制数据,将其转换为我们能识别的字符,字符流 = 字节流+解码,(FileReader )
 

二、字节流

1、输入字节流:

---------------| InputStream 所有输入字节流的基类
--------------------| FileInputStream:读取文件数据的输入字节流
 
FileInputStream读取文件的步骤
1、找到目标文件
2、建立数据的输入通道(管道)
3、建立缓冲数组,读取文件数据
4、关闭资源(释放资源)

注意:用缓冲数组读取效率更高

要是缓冲数组只有4时,每次读完数据第二次是覆盖到前面的数组

2、输出字节流

-----------| OutputStream 所有输出字节流的基类
---------------| FileInputStream 向文件输出数据的字节流

 

1、使用FileInputStream的时候,如果目标文件不存在,会自动创建目标文件对象
2、写数据的时候,目标文件存在会先清空目标文件的数据,再写入数据
3、已经存在,需要在原来的后面追加数据,应该使用FileOutputStream(file, true);
4、write方法写数据的时候,虽然接收的是int型的数据,但是真正写出的只是一个字节(8位)的数据,把低八位二级制数据写出,其他二十四位数据全部丢弃

输出[-1, 0, 0, 0];255(11111111)以补码形式存在

拷贝图片

注意:每新创建一个FileOutputStream的时候,默认情况下FileOutputStream的指针指向文件开始位置,每写出一次指向都会相应移动(写的时候加不加true问题)

3、异常处理 

 

将IOException传递给RuntimeException包装一层,然后再抛出,让调用者使用更加灵活,可以处理也可以不处理
将异常throw的时候,阻止后面代码执行(要是return的话,能阻止代码执行,但是不会通知外界出了问题)

 

4、缓冲输入字节流BufferedInputStream(其实跟自定义内存数组byte[] buf的原理一样,不常用

代替自定义的缓冲数组buf
-------------| InputStream
---------------------| FileInputStream
---------------------| BufferedInputStream 缓冲输入字节流,提高读取文件数据效率,内部维护了一个8K 的字节数组,效果和自定义缓冲数组一样
 
使用步骤
1、找到目标文件
2、建立数据输入通道
3、建立缓冲输入字节流
4、关闭资源
 
缓冲流都不具备读写的能力,BufferedInputStream借助FileInputStream实现读写; 调用BufferedInputStream的close方法实际关闭FileInputStream
 
BufferedInputStream读取效率高的原因:每次从内存中BufferedInputStream维护的数组读取8K的数据(FileInputStream 则是从硬盘中读取)

 

5、缓冲输出字节流BufferedOutputStream

内部维护了一个8K的缓冲字节数组
注意:
1、写数据时,write是写到在内部维护的字节数组中
2、要想写到硬盘中,需要调用flush或者close方法或者内部字节数组已经填满的时候
 
用缓冲输入输出流拷贝图片

bufferedInputStream.read()方法中,要是传入了buf缓冲数组返回值是存储到缓冲数组字节个数;要是没有传入,则返回的是读取到的内容

 三、字符流

字符流 = 字节流+解码(字节流读取文件中二进制数据,一次读取一个字节),可以读取中英文,一次读取一个字符
 

输入字符流(默认使用GBK编码表)

---------------| Reader 输入字符流的基类
---------------------| FileReader 读取文件的输入字符流,每次读一个字符(char),不论中英文都可以读(byte字节,char字符)

 

1、FileWriter输出字符流

写数据的时候,FileWriter内部维护了1024字节的数组,写数据的时候会先写到数组中,要写到硬盘上时,需要调用flush或者是close方法或者是填满内部字符数组

 

使用字符流拷贝图片时,会造成图片变小且打不开
FileReader默认使用GBK编码表,每次读取数据时会在码表里面找,找得到就存储中文两位(将二进制数据转换为字符),找不到就返回一个未知字符对应的数字,未知字符占一个字节,导致有些数据丢失
 
字符流的应用场景
如果是读写字符数据的时候就是用字符流(看得到的1、我......)
 
字节流的应用场景
读写的数据不需要转换为字符的时候使用
用字节流配合缓冲数组读取字符的时候,要是缓冲数组比较小(byte[4]),而字符为中英文混合(我de妈呀),会造成读取乱码;用字符流就每次读取一个字符,不会出错
 

2、缓冲输入字符流(常用,比缓冲数组好,常用)BufferedReader 

----------------| Reader 所有输入字符流的基类,抽象
----------------------| FileReader 输入字符流
----------------------| BufferedReader 字符缓冲输入流,提高文件读取效率和扩展了FileReader的功能,其内部维护了一个4K的数组(缓冲流都不具备读写文件的能力,每次读取的是一个字符)
 
readline()方法可以读取一行,文件末尾返回null,读取的一行不包含/r/n(以/r/n 为结束符)
 1 public static void testRead() throws IOException {
 2     // 找到目标文件
 3     File file = new File("E:\eclipseProject\Demo4\src\cn\guojie\file\Demo7.java");
 4     // 建立数据传输通道
 5     FileReader fileReader = new FileReader(file);
 6     // 建立缓冲输入字符流
 7     BufferedReader reader = new BufferedReader(fileReader);
 8     // 读取数据
 9     String line = null;
10     while((line = reader.readLine())!=null) {
11         System.out.println(line);
12     }
13     // 关闭资源
14     reader.close();
15 }
 1 // 自定义readLine方法,读一行
 2 public static String myReadLine(FileReader fileReader) throws IOException {
 3     StringBuilder sb = new StringBuilder();
 4     int content = 0;
 5     while((content = fileReader.read())!=-1) { // 每次读一个字符,存在int中
 6         if(content =='
') {  // 
回车忽略
 7             continue;
 8         } else if(content =='
') {  // 
跳出循环
 9             break;
10         } else {
11             sb.append((char)content);  // 整型强转为char
12         }
13     }
14     
15     if(content==-1) { // 代表读完了
16         return null;  
17     }
18     
19     return sb.toString();  // StringBuilder转化为String
20 }
21 
22 public static void testReader1() throws IOException {
23     // 找到目标文件
24     File file = new File("E:\eclipseProject\Demo4\src\cn\guojie\file\Demo7.java");
25     // 建立数据传输通道
26     FileReader fileReader = new FileReader(file);
27     // 读取数据
28     String line = null;
29 //        !(line = myReadLine(fileReader)).equals("")  // 用StringBuilder最少会返回空串""
30     while((line = myReadLine(fileReader))!=null) {
31         System.out.println(line);
32     }
33     // 关闭通道
34     fileReader.close();
35 }

3、缓冲输出字符流(提高FileWriter的读写效率和扩展功能)(常用) BufferedWriter

----------------| Writer 所有输入字符流的基类,抽象
----------------------| FileWriter 输入字符流
----------------------| BufferedWriter:内部提供了8K的字符数组作为缓冲区
提供了newLine()方法,换行
 1 boolean isLogin = false;  // 判断是否登录成功标识
 2 while((line = bufferReader.readLine())!=null) {
 3     if(info.equals(line)) {  // 读一行,如果第一行不行接着往下读,如果用if else判断的成功话,会造成
 4                             // 读完一行之后不对会直接判断登录失败,应该用一个isLogin的变量判断是否登录成功
 5         isLogin = true;
 6         break;
 7     }
 8 }
 9 
10 if(isLogin) {  // 此时通过标识判断是否登录成功
11     System.out.println("恭喜,登陆成功!");
12 } else {
13     System.out.println("对不起,登录失败,请重新输入");
14 }

四、装饰者设计模式

增强一个类的功能,还可以让这些类互相装饰
通过继承增强一个类的功能:
优点:代码结构清晰易懂
缺点:使用不灵活,会导致继承的体系过于庞大
 
步骤:
1、在装饰类内部维护一个被装饰类的引用
2、让装饰类有共同的父类或者父接口
 
父类有无参的构造方法时默认调用,要是没有无参的构造方法则需要指定有参构造方法
修饰模式实战增强类:
     优点:内部可以通过多态对多个需要增强的类进行增强,让这些修饰类达到互相装饰的效果,使用灵活
     缺点:需要内部通过多态维护需要被增强类的实力,使代码复杂
 1 // 加;缓冲输入字符流
 2 class BufferedSepetrator extends BufferedReader { // 继承是为了让装饰类对象可以作为参数传递,达到互相装饰的效果
 3     // 在内部维护维护一个被装饰类的引用
 4     BufferedReader bufferedReader;
 5     
 6     public BufferedSepetrator(BufferedReader bufferedReader) { // new BufferdLineNum()
 7         super(bufferedReader);
 8         this.bufferedReader = bufferedReader;
 9     }
10     
11     public String readerLine() throws IOException {
12         String line = bufferedReader.readLine();  // 传入bufferedLineNum,使bufferedReader指
13                                                   // 向子类BufferdLineNum,调用子类的readLine方法
14         if(line == null) {
15             return null;
16         }
17         line = line+";";
18         
19         return line;
20     }
21 }
 1 public class Demo {
 2     
 3     public static void main(String[] args) throws IOException {
 4         File file = new File("E:\eclipseProject\Demo4\src\cn\guojie\file\Demo7.java");
 5         FileReader fileReader = new FileReader(file);
 6         BufferedReader bufferedReader = new BufferedReader(fileReader);
 7         // 建立带行号的缓冲输入字符流
 8         BufferdLineNum bufferedLineNum = new BufferdLineNum(bufferedReader);
 9         // 建立带分号的缓冲输入字符流,传入bufferedLineNum,使父类引用类型指向子类对象从而调用子类的方法
10         BufferedSepetrator bufferedSeperator = new BufferedSepetrator(bufferedLineNum);
11         // 读缓冲流
12         String line = null;
13         while((line = bufferedSeperator.readerLine())!=null) {
14             System.out.println(line);
15         }
16         // 关闭资源
17         bufferedSeperator.close();
18     }
19 
20 }

五、序列流 SequenceInputStream

合并两个或多个流

常用方法

(1)合并两个输入流

SequenceInputStream(InputStream s1, InputStream s2);

(2)合并多个输入流,借助vector的element方法获得Enumation枚举对象传入

SequenceInputStream(Enumeration<? extends InputStream> e);
 1 public static void merge1() throws IOException {
 2     // 找到目标文件
 3     File file1 = new File("E:\a.txt");
 4     File file2 = new File("E:\b.txt");
 5     File file3 = new File("E:\c.txt");
 6     // 建立输入输出字节流
 7     FileInputStream fileInputStream1 = new FileInputStream(file1);
 8     FileInputStream fileInputStream2 = new FileInputStream(file2);
 9     FileOutputStream fileOutputStream = new FileOutputStream(file3);
10     // 创建序列化流
11     SequenceInputStream sequenceInputStream = new SequenceInputStream(fileInputStream1, fileInputStream2);
12     // 读写数据
13     byte[] buf = new byte[1024];
14     int length = 0;
15     while((length = sequenceInputStream.read(buf))!=-1) {
16         fileOutputStream.write(buf, 0, length);
17     }
18     // 关闭资源
19     sequenceInputStream.close(); // 调用FileInputStreamdeclose()
20     fileInputStream2.close();
21     fileOutputStream.close();
22 }
 1 public static void merge2() throws IOException {
 2     // 找到目标文件
 3     File file1 = new File("E:\a.txt");
 4     File file2 = new File("E:\b.txt");
 5     File file3 = new File("E:\c.txt");
 6     File file4 = new File("E:\d.txt");
 7     // 建立输入输出字节流
 8     FileInputStream fileInputStream1 = new FileInputStream(file1);
 9     FileInputStream fileInputStream2 = new FileInputStream(file2);
10     FileInputStream fileInputStream3 = new FileInputStream(file3);
11     FileOutputStream fileOutputStream = new FileOutputStream(file4);
12     // 创建序列化流
13     Vector<FileInputStream> vector = new Vector<FileInputStream>();
14     vector.add(fileInputStream1);
15     vector.add(fileInputStream2);
16     vector.add(fileInputStream3);
17     // 通过Vector获取迭代器
18     Enumeration<FileInputStream> e = vector.elements();
19     
20     SequenceInputStream sequenceInputStream = new SequenceInputStream(e);
21     byte[] buf = new byte[1024];
22     int length = -1;
23     while((length = sequenceInputStream.read(buf))!=-1) {
24         fileOutputStream.write(buf, 0, length);
25     }
26     //关闭资源
27     sequenceInputStream.close();
28     fileOutputStream.close();
29 }

切割和合并mp3

// 切割mp3
public static void cutFile() throws IOException {
    // 找到目标文件
    File file = new File("D:\CloudMusic\走在冷风中.mp3");
    // 建立目标文件夹
    File dir = new File("E:\cutMusic");
    // 建立输入输出流
    FileInputStream fileInputStream = new FileInputStream(file);
    // 读写文件,以1M为单位切割
    byte[] buf = new byte[1024 * 1024];
    int length = 0;

    for (int i = 0; (length = fileInputStream.read(buf)) != -1; i++) {
        int num = i + 1;
        FileOutputStream fileOutputStream = new FileOutputStream(new File(dir, "part" + num + ".mp3"));
        fileOutputStream.write(buf, 0, length);
        // 关闭缓冲输出流
        fileOutputStream.close();
    }
    // 关闭资源
    fileInputStream.close();
}

// 合并mp3
public static void merge() throws IOException {
    // 找到目标文件
    File dir = new File("E:\cutMusic");
    // 通过目标文件夹找到所有的mp3文件,然后把所有的mp3文件添加到Vector
    Vector<FileInputStream> vector = new Vector<FileInputStream>();
    File[] files = dir.listFiles(); // 列出目标文件夹中所有的目标文件
    
    for (File file : files) {
        if (file.getName().endsWith(".mp3")) { // 文件以.mp3结尾
            vector.add(new FileInputStream(file)); // vector中需要传入FileInputStream
        }
    }
    // 通过Vector获取迭代器
    Enumeration<FileInputStream> e = vector.elements();
    // 创建序列流
    SequenceInputStream sequenceInputStream = new SequenceInputStream(e);
    // 创建文件输出字节流
    FileOutputStream fileOutputStream = new FileOutputStream("F:\合并.mp3");
    // 创建缓冲数组读写文件
    byte[] buf = new byte[1024];
    int length = 0;
    while ((length = sequenceInputStream.read(buf)) != -1) {
        fileOutputStream.write(buf, 0, length);
    }
    // 关闭资源
    fileOutputStream.close();
    sequenceInputStream.close();
}

 六、序列化

对象的输入输出流:用于写对象的信息和读对象的信息,对象的信息一旦写到文件就实现了对象的持久化
序列化:把对象信息写入到硬盘(ObjectOutputStream)
反序列化:把文件中的对象信息读取出来(ObjectInputStream)
 

注意细节:

1、对象需要写出到文件上,那么对象所属的类必须实现Serializable接口(没有任何方法,只是一个标志接口),不实现的话会报错java.io.NotSerializableException
2、对象的反序列化创建对象时,并不会调用到构造方法 
3、serialVersionUID用于记录class文件的版本信息,serialVersionUID通过一个类名、成员、包名、工程名计算出的
4、使用ObjectInputStream反序列化时,ObjectInputStream先读取文件中的serialVersionUID,然后与本地的class文件中对比,相同则成功,不同则失败
5、序列化和反序列化的时候可能会修改类的成员,最好一开始就给该类指定serialVersionUID,后面序列化和反序列化的时候jvm都不会再算这个class的serialVersionUID
6、如果一个对象的某项数据不想写到硬盘上,用transient修饰
7、如果一个类维护了另外一个类的引用,那么另外一个类也需要实现Serialable接口
 
 1 // 序列化
 2 public static void writeObj() throws IOException {
 3     // 把User对象信息持久化存储
 4     User user = new User("xiaoming", "123456");
 5     // 找到目标文件
 6     File file = new File("E:\Obj.txt");
 7     // 建立输出流对象
 8     FileOutputStream fileOutputStream = new FileOutputStream(file); 
 9     // 建立对象的输出流对象
10     ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
11     // 写出对象
12     objectOutputStream.writeObject(user);
13     // 关闭资源
14     objectOutputStream.close();
15     
16 }
 1 // 反序列化
 2 public static void readObj() throws IOException, ClassNotFoundException {
 3     // 找到目标文件
 4     File file = new File("E:\Obj.txt");
 5     // 建立输入流对象
 6     FileInputStream fileInputStream = new FileInputStream(file);
 7     // 建立对象的输入流对象
 8     ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
 9     // 读取文件信息
10     User readObject = (User) inputStream.readObject(); // 强转时可能产生ClassNotFoundException
11     System.out.println(readObject);
12     // 关闭文件
13     inputStream.close();
14 }

七、配置文件类Properties(常用)

用于生成和读取配置文件的信息,属于Java.util的集合体系Map的类---> Hashtable

常用方法:

(1)添加Properties项,不用put(配置文件的键值都要求是字符串,put却可以任意添加类型数据)

setProperty(String key, String value);

(2)产生配置文件

store(Writer writer, String comments);

(3)加载配置文件

load(Reader reader);
 
注意细节:
1、配置文件信息使用中文,那么在使用store方法生成配置文件的时候只能使用字符流解决(要是使用字节流默认使用IOS_8859-1码表,出现乱码)
2、Properties中的内容发生了变化,一定要重新存储配置文件
 1 // 保存配置文件的信息
 2 public static void saveProperties() throws IOException {
 3     // 创建Properties对象
 4     Properties properties = new Properties();
 5     // 添加,不用put
 6     properties.setProperty("张三", "123");
 7     properties.setProperty("李四", "456");
 8     properties.setProperty("王五", "789");
 9     // 遍历Properties
10     Set<Entry<Object,Object>> entrys = properties.entrySet();
11     for(Entry<Object,Object> entry : entrys) {
12         System.out.println("键:"+entry.getKey()+"值:"+entry);
13     }
14     // 使用Properties产生配置文件,字符传FileWriter,字节传FileInputStream
15     properties.store(new FileWriter("E:\a.properties"), "nihao");  // comments
16 }
 1 // 读取配置文件
 2 public static void readProperties() throws IOException {
 3     // 创建Properties对象
 4     Properties properties = new Properties();
 5     // 加载配置文件
 6     properties.load(new FileReader("E:\a.properties"));
 7     // 遍历Properties
 8     Set<Entry<Object,Object>> entrys = properties.entrySet();
 9     for(Entry<Object,Object> entry : entrys) {
10         System.out.println("键:"+entry.getKey()+"值:"+entry);
11     }
12     // 修改
13     properties.setProperty("张三", "aaa");
14     // 修改完成后再保存配置文件
15     properties.store(new FileWriter("E:\b.properties"), "修改之后");
16 }
 1 // 软件运行次数配置文件
 2 public static void count() throws IOException {
 3     // 找到目标文件
 4     File file = new File("E:\c.properties");
 5     if(!file.exists()) {  // 目标文件不存在就创建
 6         file.createNewFile();
 7     }
 8     
 9     // 创建Properties对象
10     Properties properties = new Properties();
11     // 加载配置文件到Properties中
12     properties.load(new FileReader(file));
13     
14     int count = 0;  // 计数
15     String value = properties.getProperty("count");  // 从properties取出count
16     if(value!=null) {
17         count = Integer.parseInt(value);  // String转换为整数
18     }
19     // 超过三次退出jvm
20     if(count==3) {
21         System.out.println("您已使用超过试用次数,请购买软件");
22         System.exit(0);  // 退出java虚拟机
23     }
24     
25     count++;
26     
27     System.out.println("已使用"+count+"次");
28     // 添加properties项
29     properties.setProperty("count", count+"");  
30     // 产生配资文件
31     properties.store(new FileOutputStream(file), "运行次数");  // new FileOutputStream(file)要放到load后,不然FileOutputStream每次会清空
32 }

 八、打印流PrintStream

system.setOut();重新设置标准输出流对象

作用:

1、可以打印任意类型的数据,打印数据之前都会把数据转换成字符串
2、收集异常日志信息
1 Exception e;
2 e.printStackTrace   // 打印异常信息到控制台,会丢失一部分异常信息
 1 // 收集异常日志信息
 2 public static void exceptionLog() throws IOException {
 3      // 创建打印流对象
 4     PrintStream logPrintStream = new PrintStream(new FileOutputStream(new File("E:\2015年11月29日.log")));
 5     try {
 6         int[] a = null;
 7         System.out.println(a.length);
 8         
 9         int c = 4/0;
10         System.out.println(c);
11         
12     } catch (Exception e) {
13         e.printStackTrace(logPrintStream);
14     }
15 }    

九、编码和解码

ASCII码表:一个字节7位(256)表示,26个英文字母对应各自码值,很多空出,2(8)
ISO8859-1:填上了ASCII的空位
GBK2312:英文1个字节,中文2字节,GBK升级版,2(16)=65536
Unicode:国际标准码,规范,融合多种文字,2个字节
UTF-8:世界通用,英文1个字节,中文3个字节
UTF-16:不管中英文都是2个字节,指定的是Unicode其实是UTF-16,开始有个标志-2 -1
 
编码和解码都使用统一的码表,英文在各个码表都一样,中文不同

十、转换流(常用)

输入字节转换流 InputStreamReader 
输出字节转换流OutputStreamWriter

作用:

1、如果目前所获取到的是字节流,需要转换成字符流使用,就可以使用转换流(字节流 ---> 字符流),不能反过来(可以使用BufferedReader缓冲输入字符流的readline()方法)
2、使用转换流可以指定编码表进行读写文件
1 // 使用输出字节转换流指定码表写数据
2 public static void writeTest() throws IOException {
3     // 把输出字节流转换成字符流并指定码表(读写的码表要一样)
4     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(new File("E:\abc.txt")), "utf-8");
5     // 使用输出字节转换流写数据
6     outputStreamWriter.write("你好");
7     //关闭资源
8     outputStreamWriter.close();
9 }
 1 // 使用输入字节转换流指定码表读数据
 2 public static void readTest() throws IOException {
 3     // 把输入字节流转换成字符流并指定码表(读写的码表要一样)
 4     InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(new File("E:\abc.txt")), "utf-8");
 5     // 使用输入字节转换流读数据
 6     char[] buf = new char[1024];
 7     int length = 0;
 8     while((length = inputStreamReader.read(buf))!=-1) {
 9         System.out.println(new String(buf, 0, length));
10     }
11     // 关闭资源
12     inputStreamReader.close();    
13 }

十一、递归

函数调用自身
前提:必须要有条件下的调用
先易后难
 1 // 递归文件夹的目录,列出子文件名和子文件夹名以及子文件夹下的孙文件名,先浅后深
 2 public static void listFiles(File dir, String space) {
 3     File[] files = dir.listFiles();  // 列出目录下所有文件名
 4     for(File file : files) {
 5         if(file.isFile()) {  // 文件
 6             System.out.println(space+file.getName());  // 列出文件名
 7         } else if(file.isDirectory()) {  // 目录
 8             System.out.println(space+file.getName());  // 列出目录名
 9             // 递归
10             listFiles(file, "|  "+space);  
11         }
12     }
13 }
 1 // 递归删除文件夹及其下面的文件夹和文件
 2 public static void deleteFiles(File dir) {
 3     File[] files = dir.listFiles(); // 列出目录下所有文件名
 4     for(File file : files) {
 5         if(file.isFile()) {  // 文件
 6             file.delete();  // 删除文件
 7         } else if(file.isDirectory()) {  // 文件夹
 8             deleteFiles(file);  // 文件夹
 9         }
10     }
11     dir.delete();  // 删除根目录
12 }
 
有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
原文地址:https://www.cnblogs.com/1989guojie/p/6107791.html