JAVA学习之-I/O流

Posted ff52531314

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA学习之-I/O流相关的知识,希望对你有一定的参考价值。

一,流的概念

   内存与存储设备之间传输数据的通道。

二,流的分类

2.1,按方向,以内存为基准。

     输入流:将存储设备中的数据读取到内存中。

     输出流:将内存中的数据写入到存储设备中。

2.2,按单位,

     字节流:以字节为单位,可以读写任何数据。

     字符流:以字符为单位,只能读写文本数据。

2.3,按功能,

     节点流:具有实际传输数据的读写功能。

     过滤流:节点流的基础上增强功能。

三,字节流

3.1,字节流抽象类

  3.1.1 InputStream:字节输入流

             public int read(){}

             public int read(byte[]b){}

             public int read(byte[]b,int off,int len){}

        OutputStream:字节输出流

             public void write(int n){}

             public void write(byte[]b){}

             public void write(byte[]b,int off,int len){}

3.2,字节节点流

   3.2.1 FileInputStream: 

           public int read(){}:单个字节读取,返回读取的字节。

           public int read(byte[]b):从流中读取多个字节存入数组,返回读取的实际字节数,如果到文件末尾处,返回-1.

         创建FileInputStream对象 传入,路径名或File对象。调用方法。

public class TestFileInputStream {
    public static void main(String[] args) throws Exception{
        //1创建FileInputStream对象
        FileInputStream fis=new FileInputStream("d:\\bbb.txt");
        //2读取(输入)
        //2.1单个字节读取
//        int data=0;
//        while((data=fis.read())!=-1) {
//            System.out.print((char)data);
//        }
        //2.2一次读取多个字节(效率高)
        byte[] buf=new byte[1024];
        //fis.read(buf);//读取5个字节
        int len=0;//保存读取的字节个数
        while((len=fis.read(buf))!=-1) {
            System.out.println("读取字节个数:"+len);
            System.out.println(new String(buf));
        }
        
        //3关闭
        fis.close();
        
    }
}

 

   3.2.2 FileOutputStream:

          public void write(byte[]b){} :将字节数组中的字节写入流

          public void write(byte[]b,int off,int len){}。写出有效长度的字节

 1 FileOutputStream fos=new FileOutputStream("d:\\aaa.txt");
 2         //2输出(写入)数据
 3         fos.write(97);//97是a的ASCII吗
 4         fos.write(98);
 5         fos.write(99);
 6         fos.write(57);
 7         fos.write(55);
 8         String say="ma hua teng li hai";
 9         String say="马化腾制裁苹果";
10         fos.write(say.getBytes());
11         //3刷新(如果没有缓冲区不需要刷新)
12         fos.flush();
13         //4关毕
14         fos.close();
15         System.out.println("执行完毕");

3.3,字节过滤流(缓冲流):提高I/O效率,减少访问磁盘次数,写入时写入到缓冲区,刷新后进入文件

    3.3.1BufferedInputStream : 字节缓冲输出流,缓冲流不具有实际传输功能,需要传入节点流

public class TestBufferedInputStream {
    public static void main(String[] args) throws Exception {
        //1创建流
        //1.1创建FileInputStream
        FileInputStream fis=new FileInputStream("d:\\bbb.txt");
        //1.2创建缓冲流(有缓冲区 8K)
        BufferedInputStream bis=new BufferedInputStream(fis);
        //2读取(输入)
        //2.1 单个字节读取
//        int data=0;
//        while((data=bis.read())!=-1) {
//            System.out.print((char)data);
//        }
        //2.2 一个读取多个字节
        byte[] buf=new byte[5];
        int len=0;
        while((len=bis.read(buf))!=-1) {
            System.out.print(new String(buf,0,len));
        }
        //3关闭
        bis.close();
        
    }

}

      3.3.2 BufferedOutputStream:字节缓冲输出流

public class TestBufferedOutputStream {
    public static void main(String[] args) throws Exception {
        //1创建流
        //1.1创建字节输出流(节点流,具有实际读写数据的功能)
        FileOutputStream fos=new FileOutputStream("d:\\buffer.txt");
        //1.2创建缓冲流,传递节点流,默认缓冲区大小8K
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        //2写入文件
        for(int i=0;i<10;i++) {
            bos.write("好好学习,天天向上
".getBytes());//写入缓冲区
            //bos.flush();//刷新缓冲
        }
        //3关闭(自带flush功能)
        bos.close();
        System.out.println("写入完毕");
    }
}

         3.3.3 缓冲流边读边写复制文件

public class CopyFile2 {
    public static void main(String[] args) throws Exception{
        //1创建流
        //1.1创建缓冲输入流
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("d:\\timg.jpg"));
        //1.2创建缓冲输出流
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("d:\\timg3.jpg"));
        //2边读边写
        int data=0;
        while((data=bis.read())!=-1) {
            bos.write(data);
        }
        //3关闭(带有刷新功能)
        bis.close();
        bos.close();
        System.out.println("复制完毕");
    }
}

3.4,对象流(序列化反序列化) :增强了缓冲区的功能,增强了读写八种基本数据类型和字符串的功能,增强了读写对象的功能。,使用流传输对象的过程,成为序列反序列

 序列化的前提:

 

     * 1 序列化要求:序列化类以及对象属性必须要实现Serializable (标记接口)
     * 2 使用transient(瞬间的)修饰属性,则此属性不参与序列化
     * 3 读取到文件尾部的标志:java.io.EOFException End Of File

 

    3.4.1 ObjectOutputStream : 序列化

               writeObject(Object obj) 向流中写入一个对象,序列化

public class TestObjectOuputStream {
    public static void main(String[] args) throws Exception {
        //1创建流
        //1.1创建节点流
        FileOutputStream fos=new FileOutputStream("d:\\object.txt");
        //1.2创建对象输出流
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        //2.1写入基本数据类型
//        oos.writeInt(97);
//        oos.writeDouble(3.14);
//        oos.writeBoolean(true);
        //2.2写入引用类型数据 
        Student mahuateng=new Student("马化腾", 20);
        Student mayun=new Student("马云", 22);
        
        ArrayList<Student> list=new ArrayList<Student>();
        list.add(mahuateng);
        list.add(mayun);
        
        oos.writeObject(list);
        //oos.writeObject(mayun);
        
        //3关闭
        oos.close();
        System.out.println("执行完毕");
    }
}

    3.4.2ObjectInputStream :反序列化

          readObject():从流中读取一个对象

public class TestObjectInputStream {
    public static void main(String[] args) throws Exception {
        //1创建流
        //1.1创建节点流
        FileInputStream fis=new FileInputStream("d:\\object.txt");
        //1.2创建对象输入流
        ObjectInputStream ois=new ObjectInputStream(fis);
        //2.1读取基本数据
//        int age=ois.readInt();
//        double pi=ois.readDouble();
//        boolean b=ois.readBoolean();
//        System.out.println(age+" "+pi+" "+b);
        //2.2读取引用类型数据
//        Student stu=(Student)ois.readObject();
//        Student stu2=(Student)ois.readObject();
//        System.out.println(stu.toString());
//        System.out.println(stu2.toString());
        ArrayList<Student> list=(ArrayList<Student>)ois.readObject();
        System.out.println(list.toString());
        //3关闭
        ois.close();
    }
    
}

四,字符编码

   4.1 计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制 数转换之后的结果。

         按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照 某种规则解析显示出来,称为解码 

         计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符 集有ASCII字符集、GBK字符集、Unicode字符集等。

   4.2  字符编码 Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。

           字符集:Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符 号、数字等。

技术图片

 

 

 4.2.1  ASCII字符集 : (American Standard Code for Information Interchange)基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits) 表示一个字符,共256字符。 

          ISO-8859-1字符集:拉丁码表,

          GB2312:简体中文码表。GBK:最常用的中文码表,完全兼容GB2312。GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节 组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。 

          Unicode字符集 :Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国 码。最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码。 

          UTF-8编码规则:

               1. 128个US-ASCII字符,只需一个字节编码。

               2. 拉丁文等字符,需要二个字节编码。

               3. 大部分常用字(含中文),使用三个字节编码。

               4. 其他极少使用的Unicode辅助字符,使用四字节编码。

 五,字符流

 5.1字符流抽象类

       5.1.1 Reader :字符输入流

                    public int read(){}

                    public int read(char[]c){}

                    public int read(char[]c,int off,int len){}

               Writer:字符输出流

                     public void write(int n){}

                     public void write(String str){}

                     public void write(char[] c){}

 5.2 字符节点流

     5.2.1 FileReader: 文件字符输入流

                   public int read(){} ,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1 

                   public int read(char[]c) 从流中读取多个字符,存储到数组中,返回读到的有效字符数,如果达到文件尾部返回-1.

 

public class TestFileReader {
    public static void main(String[] args) throws Exception{
        //1创建FileReader对象(默认编码utf-8)
        FileReader fr=new FileReader("d:\\writer.txt");
        //2读取
        //2.1单个字符读取
        //自动提升为int类型
//        int data=0;
//        while((data=fr.read())!=-1) {
//            System.out.print((char)data);
//        }
        //2.2多个字符读取
        char[] buf=new char[5];
        int len=0;
        while((len=fr.read(buf))!=-1) {
            System.out.print(new String(buf,0,len));
        }
        //3关闭
        fr.close();
        
    }
}

 

       5.2.2 FileWriter :文件字符输出流

               public void write(int c){} 写出一个字符 

                 1. 虽然参数为int类型四个字节,但是只会保留一个字符的信息写出。

                 2. 未调用close方法,数据只是保存到了缓冲区,并未写出到文件中FileWriter fw = new FileWriter("fw.txt");           

                     因为FileWriter内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据 的。如果我们既想写出数据,又想继续使用流,就需要 flush 方法了。

                     flush :刷新缓冲区,流对象可以继续使用。

                     close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

// 写出数据           
fw.write(97); 
// 写出第1个字符          
fw.write(‘b‘); 
// 写出第2个字符          
fw.write(‘C‘); 
// 写出第3个字符          
fw.write(30000); 
// 写出第4个字符,中文编码表中30000对应一个汉字。
// fw.close();

 

                public void  write(char[] cbuf) 和 write(char[] cbuf, int off, int len) ,write(String str) 将数组中的字符,字符串写入输出流。

public class TestFileWriter {
    public static void main(String[] args) throws Exception {
        //1创建文件字符输出流(编码默认utf-8)
        FileWriter fw=new FileWriter("d:\\writer.txt",true);//为true支持续写
        //2写入
        for(int i=0;i<10;i++) {
            fw.write("大连是个好地方...
");//换行
            fw.flush();
        }
        //3关闭
        fw.close();
        System.out.println("执行完毕");
    }
}    
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();     
}
}

数据追加续写:每次程序运行,创建输出流对象,都会清空目标文件的数据

      public FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的 文件。

      public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件。

       FileWriter fw = new FileWriter("fw.txt",true);  

      构造方法,参数中都需要传入一个boolean类型的值, true 表示追加数据, false 表示清空原有数据。

      这样创建的输出流对象,就可以指定是否追加续写了。 

      写出换行:     

                       小贴士:字符流,只能操作文本文件,不能操作图片,视频等非文本文件

 5.3字符过滤流(缓冲流)

      5.3.1 BufferedReader 字符缓冲输入流  读取文本    特有方法,读一行 readLine()     

public class TestBufferedReader {
    public static void main(String[] args) throws Exception{
        //1创建流
        //1.1创建节点流
        FileReader fr=new FileReader("d:\\buffer.txt");
        //1.2创建缓冲流
        BufferedReader br=new BufferedReader(fr);
        //2读取
        String line;
        while((line=br.readLine())!=null) {
            System.out.println(line);
        }
        
        //3关闭
        br.close();
    
    }
}

       5.3.2 BufferedWriter 字符缓冲输出流  写入文件  特有方法,写换行 newLine() 跨平台

public class TestBufferedWriter {
    public static void main(String[] args) throws Exception{
        //1创建流
        //1.1创建FileWriter
        FileWriter fw=new FileWriter("d:\\buffer.txt");
        //1.2创建缓冲流
        BufferedWriter bw=new BufferedWriter(fw);
        //2写入
        for(int i=0;i<5;i++) {
            bw.write("好好学习,天天向上");
            bw.newLine();//行终止符
            bw.flush();//刷新
        }
        //3关闭
        bw.close();
        System.out.println("执行完毕");
        
    }
}

5.4 打印流

     printWriter()  封装了print()/println()方法,支持写入后换行,支持数据原样打印。

public class TestPrintWriter {
    public static void main(String[] args) throws Exception{
        //1创建打印流
        PrintWriter pw=new PrintWriter("d:\\print.txt");
        //2打印方法
        pw.println(97);//"97"
        pw.println(3.14);//"3.14"
        pw.println(true);//"true"
        pw.println(new char[] {a,b,c});
        //3关闭
        pw.close();
        System.out.println("打印完毕");
    }
}

5.5 转换流

   InputStreamReader: 字节 转字符  转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定 的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。 

   OutputStreamWriter: 字符 转字节 转换流 java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符 编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。 

     InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。

     InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流

    OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。

    OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流。

public class TestInputStreamReader {
    public static void main(String[] args) throws Exception {
        //1创建流
        //1.1创建节点流
        FileInputStream fis=new FileInputStream("d:\\info.txt");
        //1.2创建转换流  参数1 节点流 参数2 字符编码
        InputStreamReader isr=new InputStreamReader(fis, "GBK");
        //2读取
        int data;
        while((data=isr.read())!=-1) {
            System.out.print((char)data);
        }
        //3关闭
        isr.close();

        //1创建流
        //1.1创建节点流
        FileOutputStream fos=new FileOutputStream("d:\\haha.txt");
        //1.2创建转换流
        OutputStreamWriter osw=new OutputStreamWriter(fos, "gbk");
        
        //BufferedWriter bw=new BufferedWriter(osw);
        //2写入
        osw.write("哈哈");
        //3关闭
        osw.close();
    }
}

六,File类,FileFilter

6.1 File类:代表物理盘符中的一个文件或文件夹,无论该路径下是否存在文件或者目录,都不影响File对象的创建。

      java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。 

6.2 构造方法

     public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。

 

     public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的 File实例。

     public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的 File实例。

 // 文件路径名 
String pathname = "D:\\aaa.txt"; 
File file1 = new File(pathname);  // 文件路径名 String pathname2 = "D:\\aaa\\bbb.txt"; File file2 new File(pathname2); // 通过父路径和子路径字符串   String parent = "d:\\aaa";   String child "bbb.txt";   File file3 new File(parent, child); // 通过父级File对象和子路径字符串 File parentDir = new File("d:\\aaa"); String child "bbb.txt"; File file4 new File(parentDir, child);

6.3 常用方法

public String getAbsolutePath() :返回此File的绝对路径名字符串。

public String getPath() :将此File转换为路径名字符串。

public String getName() :返回由此File表示的文件或目录的名称。

public boolean renameTo(File file):修改文件名(包含剪切功能)

lastModified() 最后一次修改的时间

canWrite():是否可写

isHidden():是否隐藏

isAbsolute():是否是绝对路径

public long length() :返回由此File表示的文件的长度。 API中说明:length(),表示文件的长度。但是File对象表示目录,则返回值未指定。

public boolean exists() :此File表示的文件或目录是否实际存在。

public boolean isDirectory() :此File表示的是否为目录。

public boolean isFile() :此File表示的是否为文件。

public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。

public boolean delete() :删除由此File表示的文件或目录。delete方法,如果此File表示目录,则目录必须为空才能删除,直接删除最深层目录。

deleteOnExit():退出JVM时,虚拟机删除

public boolean mkdir() :创建由此File表示的目录。

public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。

public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。调用listFiles方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历

6.4FileFilter接口,文件过滤器接口  

  是File的过滤器。 该接口的对象可以传递给File类的 listFiles(FileFilter) 作为参数, 接口中只有一个方法。boolean accept(File pathname) :测试pathname是否应该包含在当前File目录中,符合则返回true。

 

 //遍历目录,搜索指定文件
 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() {
            @Override
            public 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);
            }
        }
    }

//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);
             }
        }
}




技术图片

 

   技术图片

 

 6.5练习

/**
 * 案例2:
 *     需求:使用递归删除文件夹
 *
 */
public class TestFile3 {
    public static void main(String[] args) {
        deleteDir(new File("D:\\aaa"));
    }
    //递归删除文件夹
    public static void deleteDir(File dir) {
        File[] listFiles = dir.listFiles();
        if(listFiles!=null&&listFiles.length>0) {
            for (File file : listFiles) {//file可能是文件夹,也可能文件
                if(file.isDirectory()) {
                    //递归调用
                    deleteDir(file);
                }else {
                    //直接删除
                    System.out.println(file.getName()+"  删除结果:"+file.delete());
                }
            }
        }
        //删除文件夹
        System.out.println(dir.getName()+"  删除结果:"+dir.delete());

    }
}
/**
 * 案例1:需求:使用递归遍历文件夹中所有的文件,包括子文件夹中的文件。
分级打印 路径
 *
 */
public class TestFile2 {
    public static void main(String[] args) {
        listDir(new File("d:\\aaa"),0);
    }
    /**
     * 递归遍历文件夹
     */
    public static void listDir(File dir,int level) {
        System.out.println(getSeprator(level)+dir.getAbsolutePath());
        level++;//1
        File[] files=dir.listFiles();
        if(files!=null&&files.length>0) {
            for (File file : files) {//file 可能是一个文件夹,也可能是文件 
                if(file.isDirectory()) {
                    //递归调用
                    listDir(file,level);
                }else {
                    System.out.println(getSeprator(level)+file.getAbsolutePath());
                }
            }
        }
    }
    //根据级别获取空格
    public static String getSeprator(int level) { // 0 1 
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<level;i++) {
            sb.append("  ");
        }
        return sb.toString();
    }
}

 七,Properties实现流操作

java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其 对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时, System.getProperties 方法就是返回 一个 Properties 对象。

public Properties() :创建一个空的属性列表。

public Object setProperty(String key, String value) : 保存一对属性。

public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。

public Set<String> stringPropertyNames() :所有键的名称的集合。

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

与流相关:

public void load(InputStream inStream) : 从字节输入流中读取键值对

public void store (OutputStream):将键值对写入流

 

    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.tx

小贴士:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔

以上是关于JAVA学习之-I/O流的主要内容,如果未能解决你的问题,请参考以下文章

(笔记)Linux内核学习之I/O层和I/O调度机制

Linux学习之“exit函数”

Java学习之i/o系统

设计模式学习之:装饰器模式

Java学习之IO流(转换流)

java学习之IO字符流