精华内容
下载资源
问答
  • Java IO流知识点总结

    2021-04-22 12:36:32
    Java IO流总结java最近对java IO流进行了比较全面的学习,下面对知识点进行比较全面的总结:函数一.IO流的基本概念学习IO流主要用于硬板、内存、键盘等处理设备上得数据操做,根据处理数据的数据类型的不一样能够...

    Java IO流总结java

    最近对java IO流进行了比较全面的学习,下面对知识点进行比较全面的总结:函数

    一.IO流的基本概念学习

    IO流主要用于硬板、内存、键盘等处理设备上得数据操做,根据处理数据的数据类型的不一样能够分为:编码

    字节流(抽象基类为InPutStream和OutPutStream)和字符流(抽象基类为Reader和Writer)。spa

    根据流向不一样,能够分为:输入流和输出流。 orm

    字符流和字节流的主要区别:视频

    1.字节流读取的时候,读到一个字节就返回一个字节;对象

    字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8码表中是3个字节)时。blog

    先去查指定的编码表,将查到的字符返回。接口

    2.字节流能够处理全部类型数据,如:图片,MP3,AVI视频文件,而字符流只能处理字符数据。

    只要是处理纯文本数据,就要优先考虑使用字符流,除此以外都用字节流。

    二.IO流结构图表示:

    (一)基类图

    ebf293129cdcbe3eb3630144b842867a.png

    (二)字节流相关类图

    a6526c844ef7e9d0591084b166058a15.png

    (三)字符流相关类图

    d3f8e5e880cb3b1fce9b040073c26371.png

    (四)IO流主要能够分为节点流和处理流两大类。

    1.节点流类型

    该类型能够从或者向一个特定的地点或者节点读写数据。

    2.处理流类型

    该类型是对一个已存在的流的链接和封装,经过所封装的流的功能调用实现数据读写,处理流的构造方法老是要带一个其余流对象做为参数,一个流对象进过其余流的屡次包装,叫作流的连接。

    3.IO流的详细分类

    注:下表中带下划线的是抽象类,不能建立对象。粗体部分是节点流,其余就是经常使用的处理流。

    流分类

    使用分类

    字节输入流

    字节输出流

    字符输入流

    字符输出流

    抽象基类

    InputStream

    OutputStream

    Reader

    Writer

    节点流

    访问文件

    FileInputStream

    FileOutStream

    FileReader

    FileWriter

    访问数值

    ByteArrayInputStream

    ByteArrayOutStream

    CharArrayReader

    CharArrayWriter

    访问管道

    PipedInputStream

    PipedOutStream

    PipedReader

    PipedWriter

    访问字符串

    StringReader

    StringWriter

    处理流

    缓冲流

    BufferedInputStream

    BufferedOutputStream

    BufferedReader

    BufferedWriter

    转换流

    InputStreamReader

    OutputStreamWriter

    对象流

    ObjectInputStream

    ObjectOutputStream

    抽象基类(过滤)

    FilterInputStream

    FilterOutputStream

    FilterReader

    FilterWriter

    打印流

    PrintStream

    PrintWriter

    推回输入流

    PushbackInputStream

    PushbackReader

    特殊流

    DataInputStream

    DataOutputStream

    对于上面罗列的全部类,不必定要所有掌握,

    可是基本的输入和输出的类仍是要很熟练掌握的,不管是字节流仍是字符流。

    4.对部分类的具体描述

    (1)、缓冲流(BufferedInPutStream/BufferedOutPutStream和BufferedWriter/BufferedReader)

    他能够提升对流的操做效率。

    写入缓冲区对象:

    BufferedWriter bufw=new BufferedWriter(new FileWriter("buf.txt"));

    读取缓冲区对象:

    BufferedReader bufr=new BufferedReader(new FileReader("buf.txt"));

    该类型的流有一个特有的方法:readLine();一次读一行,到行标记时,

    将行标记以前的字符数据做为字符串返回,当读到末尾时,返回null,

    其原理仍是与缓冲区关联的流对象的read方法,只不过每一次读取到一个字符,

    先不进行具体操做,先进行临时储存,当读取到回车标记时,将临时容器中储存的数据一次性返回。

    WriteLine()同理。

    (2)、转换流(InputStreamReader/OutputStreamWriter)

    该类型时字节流和字符流之间的桥梁,该流对象中能够对读取到的字节数据进行指定编码的编码转换。

    构造函数主要有:

    1. InputStreamReader(InputStream);        //经过构造函数初始化,使用的是本系统默认的编码表GBK。

    2.  InputStreamWriter(InputStream,String charSet);   //经过该构造函数初始化,能够指定编码表。

    3.  OutputStreamWriter(OutputStream);      //经过该构造函数初始化,使用的是本系统默认的编码表GBK。

    4.  OutputStreamwriter(OutputStream,String charSet);   //经过该构造函数初始化,能够指定编码表。

    注意:在使用FileReader操做文本数据时,该对象使用的时默认的编码表,即

    FileReader fr=new FileReader(“a.txt”);

    与InputStreamReader isr=new InputStreamReader(new FileInputStream("a.txt"));的意义相同。

    若是要使用指定表编码表时,必须使用转换流,

    即若是a.txt中的文件中的字符数据是经过utf-8的形式编码,

    那么在读取时,就必须指定编码表,那么转换流时必须的。即:

    InputStreamReader isr=new InputStreamReader(new FileInputStream("a.txt"),utf-8);

    3、数据流(DataInputStream/DataOutputStream)

    该数据流能够方便地对一些基本类型数据进行直接的存储和读取,

    不须要再进一步进行转换,一般只要操做基本数据类型的数据,就须要经过DataStream进行包装。

    构造方法:

    1. DataInputStreamReader(InputStream);

    2.  DataInputStreamWriter(OutputStream);

    方法举例:

    1.int readInt();//一次读取四个字节,并将其转成int值

    2. writeInt(int);//一次写入四个字节,注意和write(int)不一样,write(int)只将该整数的最低一个8位写入,剩余三个8为丢失

    3. hort readShort();

    4. writeShort(short);

    5. String readUTF();//按照utf-8修改版读取字符,注意,它只能读writeUTF()写入的字符数据。

    6.  writeUTF(String);//按照utf-8修改版将字符数据进行存储,只能经过readUTF读取。

    注意:在使用数据流读/存数据的时候,须要有必定的顺序,即某个类型的数据先写入就必须先读出,服从先进先出的原则。

    4、打印流(PrintStream/PrintWriter)

    PrintStream是一个字节打印流,System.out对应的类型就是PrintStream,

    它的构造函数能够接受三种数据类型的值:1.字符串路径。2.File对象3.OutputStream

    PrintStream是一个字符打印流,它的构造函数能够接受四种类型的值:1.字符串路径。2.File对象3.OutputStream  4.Writer对于1、2类型的数据,能够指定编码表,也就是字符集,对于3、4类型的数据,能够指定自动刷新,当该自动刷新为True时,只有3个方法能够用:println,printf,format。

    5、对象流(ObjectInputStream/ObjectOutputStream)

    该类型的流能够把类做为一个总体进行存取,主要方法有:

    Object readObject();该方法抛出异常:ClassNotFountException。

    void writeObject(Object):被写入的对象必须实现一个接口:Serializable,不然就会抛出:NotSerializableException

    本文只是对java IO流知识点的归纳性总结和应用,适合那些对IO理解比较全面的人看。

    对于基础不强的同窗,随意浏览一下就能够了。

    本文实际操做的没有很详细的阐述,下面也会有比较详细描述操做的文章发表,主要针对初级开发者。

    展开全文
  • JavaIO流知识点整理

    2021-01-13 23:21:39
    IO流 字节流 从文件中读取字节问题 read()返回数字含义 原文件内容:1234 输出: 49 50 51 52 package anomyous; import java.io.*; public class Test { public static void main(String[] args) throws ...

    IO流

    字节流

    从文件中读取字节问题

    read()返回数字含义

    原文件内容:1234

    输出:

    49
    50
    51
    52

    package anomyous;
    
    import java.io.*;
    
    
    
    public class Test {
        public static void main(String[] args) throws IOException {
            File file=new File("speech.txt");
            FileInputStream fin=new FileInputStream(file);
            int data;
            while ((data=fin.read())!=-1) {
                System.out.println(data);
            }
            fin.close();
        }
    }
    
    
    
    字符Unicode编码10进制Unicode编码2进制Unicode编码16进制
    1490011 00010x31
    250001100100x32

    读取中文时输出负数?

    原文件:你好世界

    输出:

    228
    189
    160
    229
    165
    189
    228
    184
    150
    231
    149
    140

    发现每一个汉字用3个0~255的数字表示,代表utf8编码方式中一个汉字用3字节表示

    将上面的程序改动一下

    package anomyous;
    
    import java.io.*;
    import java.util.Arrays;
    
    
    public class Test {
        public static void main(String[] args) throws IOException {
            File file = new File("speech.txt");
            FileInputStream fin = new FileInputStream(file);
            int len;
            byte[] bytes = new byte[3];
            while ((len = fin.read(bytes)) != -1) {
                byte[] newByte = Arrays.copyOfRange(bytes, 0, len);
                for (byte b : newByte) {
                    System.out.println(b);
                }
            }
            fin.close();
        }
    }
    
    
    

    原文:你好世界

    输出:

    -28
    -67
    -96
    -27
    -91
    -67
    -28
    -72
    -106
    -25
    -107
    -116

    此时原本很大的正数变成了负数,之前很大的正数都大于128,也就是用8位2进制表示时第一位都为1

    然而java程序中byte采用补码形式输出,符号位为1,自然就是负数.

    -28和228是同一段二进制序列的不同表示形式,-28 等价于 1110 0100 等价于228

    测试:使用两种形式向文件中写入中文

    package anomyous;
    
    import java.io.*;
    
    
    public class Test {
        public static void main(String[] args) throws IOException {
            File file=new File("a.txt");
            FileOutputStream fos=new FileOutputStream(file,false);
            fos.write(228);//fos.write(-28);
            fos.write(-72);
            fos.write(-128);
            fos.write(228);//fos.write(-28);
            fos.write(-70);
            fos.write(-116);
            fos.close();
        }
    }
    

    结果都写入"一二"到文件中

    补充: 228-256=-28 , 228+256=-28(如果只用后1字节表示的话)

    byte b= (byte) 228;
    System.out.println(b);//输出-28
    
    int b = 228;
    b = (byte) (b + 256);
    System.out.println(b);//输出-28
    

    向文件写入中文

            File file=new File("zeroPath/zero.txt");
            FileOutputStream fos=new FileOutputStream(file,true);// true表示追加模式,默认为false,表示覆盖模式
            byte[] bytes="你好世界".getBytes(StandardCharsets.UTF_8);
            //utf8编码方式中一个中文占3字节,而数字和字母占1字节
            //一次写入一个汉字
            fos.write(bytes,0,3);//0偏移量,3写入长度
            fos.write(bytes,3,3);
            fos.write(bytes,6,3);
            fos.write(bytes,9,3);
            byte[] bytes1="\r\n".getBytes(StandardCharsets.UTF_8);
            fos.write(bytes1);//写入换行(/r/n)
            fos.close();
    

    复制图片文件

    该方法有缺陷

            File file=new File("zeroPath/tomcat.png");
      		 File file2=new File("zeroPath/tomcat2.png");
            FileInputStream fin=new FileInputStream(file);
            FileOutputStream fout=new FileOutputStream(file2);//不用开启追加模式
            byte[] cache=new byte[1024];//一次1KB
            int isRead=0;//表示一次被读取多少字节
            while ((isRead=fin.read(cache))!=-1){
                fout.write(cache);//最后一次会把多余的0写入,文件可正常查看,但会增加文件大小,解决方式见下面
                cache=new byte[1024];//使用后置0
            }
            fout.close();
            fin.close();//先打开的后关闭
    

    append:true 开启条件

    FileOutputStream fout=new FileOutputStream(file2,true);

    fout.write()存在游标机制,在fout未进行关闭前,每一次写入文件操作都会在上一次游标位置上向后移动,而在fout关闭后再打开,write游标默认会移动到开头,此时如果不以追加模式写入会覆盖原有内容.

    解决输出或写入多余内容方式

    读取文本

    File file=new File("D:/a.txt");
    FileInputStream fin=new FileInputStream(file);
    byte[] bytes=new byte[10];
    int result;
    while ((result=fin.read(bytes))!=-1){
        System.out.print(new String(bytes).substring(0,result));
    }
    fin.close();
    

    图片复制

    public class Test {
        public static void main(String[] args) throws IOException {
            File file=new File("D:/tomcat.png");
            File file2=new File("E:/tomcat2.png");
            FileInputStream fin=new FileInputStream(file);
            FileOutputStream fout=new FileOutputStream(file2);//不用开启追加模式
            byte[] cache=new byte[1024];//一次1KB
            int result=0;
            while ((result=fin.read(cache))!=-1){
                byte[] newCache=Arrays.copyOfRange(cache,0,result);
                fout.write(newCache);
            }
            fout.close();
            fin.close();//先打开的后关闭
        }
    }
    

    另一种方式(老师使用的)

    public class Test {
        public static void main(String[] args) throws IOException {
            File file=new File("D:/tomcat.png");
            FileInputStream fin=new FileInputStream(file);
            File file2=new File("E:/tomcat2.png");
            FileOutputStream fout=new FileOutputStream(file2);
    
            int len;
            byte[] bytes=new byte[1024];
            while ((len= fin.read(bytes))!=-1){
                fout.write(bytes,0,len);
            }
            fout.close();
            fin.close();
        }
    }
    

    read()方法返回值问题

    int data;
    byte[] cache=new byte[5];
    while ((data=fin.read(cache))!=-1){//read(byte[])时
          System.out.println(data);//输出5 read()返回读取的长度
    }
    
    int data;
    while ((data=fin.read())!=-1){//read()时
          System.out.println(data);//97 98 99...读取出的内容(int形式,不会有负数)
    }
    

    字符流

    虽然一个汉字占3字节,但字符流处理单位是1个字符

    这种方式输出中文也没问题

    File file=new File("zeroPath/zero.txt");
            FileReader fr=new FileReader(file);
    //        FileReader fr2=new FileReader("zeroPath/zero.txt");//第二种创建对象方法
            int b=0;
            while ((b=fr.read())!=-1){
                System.out.print((char) b);
            }
            fr.close();
    
            File file=new File("zeroPath/zero.txt");
            FileReader fr=new FileReader(file);
    //        FileReader fr2=new FileReader("zeroPath/zero.txt");//第二种创建对象方法
            char[] str=new char[10];
            int isRead=fr.read(str);
            if(isRead!=-1){
                System.out.println(str);
            }
            fr.close();
    

    复制文件

    File file=new File("zeroPath/zero.txt");
            File file2=new File("zeroPath/zero2.txt");
            FileReader fr=new FileReader(file);
            FileWriter fw=new FileWriter(file2);
            char[] str=new char[50];
            int isRead=fr.read(str);
    
            if(isRead!=-1){
                fw.write(str);
            }
            fw.close();//不关闭就写不进去,close()把流中的数据写入文件
            fr.close();
    

    flush()close()区别

    flush用于清空缓冲区

    close用于关闭流对象

    FileWriter缓存机制

    在这里插入图片描述

            File file=new File("zeroPath/zero99.txt");
            File file2=new File("zeroPath/zero2.txt");
            FileReader fr=new FileReader(file);
            FileWriter fw=new FileWriter(file2);
    
            char[] str=new char[50];
            int isRead=fr.read(str);
            //        fw.close(); //IOException: Stream closed
            fw.flush();//不会报错,一般放在后面,close()之前
            if(isRead!=-1){
                fw.write(str);
            }
    
            fr.close();
            fw.close();
    
    展开全文
  • java io流知识点

    2021-09-27 17:19:05
    IO的分类 根据数据的流向分为:输入和输出。 输入 :把数据从其他设备上读取到内存中的。 输出 :把数据从内存 中写出到其他设备上的。 根据数据的类型分为:字节和字符。 字节 :以字节为单位...

    IO的分类
    根据数据的流向分为:输入流和输出流。

    输入流 :把数据从其他设备上读取到内存中的流。
    输出流 :把数据从内存 中写出到其他设备上的流。
    根据数据的类型分为:字节流和字符流。

    字节流 :以字节为单位,读写数据的流。
    字符流 :以字符为单位,读写数据的流。

    输入流输出流
    字节流字节输入流
    InputStream
    字节输出流
    OutputStream
    字符流字符输入流
    Reader
    字符输出流
    Writer

    2. 字节流

     一切皆字节

    一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据,无论使用什么样的流对象,底层传输的始终为二进制数据。

    3.字符流
    中文字符时,可能不会显示完整的字符,因为一个中文字符可能占用多个字节存储,Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。



     

    展开全文
  • 文章目录IOOutputStream抽象类子类FileOutputStreamFileInputStreamWriterFileWriterReaderFileReaderFile类文件过滤器(...收集异常日志Properties控制台sout输出判断用户输入类型Java序列化Serializable实现Jav

    IO

    可以将这种数据的传输操作,看作是一种数据的流动,按照流动的方向分为输入Input和输出Output。

    Java中的IO操作主要指的是java.io包下的一些常用类的使用,通过这些常用类对数据进行读取(输入Input)和写出(Output)。

    IO流的分类:

    • 按照流的方向来分,可以分为:输入流和输出流;

    • 按照流动的数据类型来分,可以分为:字节流和字符流;

    字节流:

    1、输入流:InputStream

    2、输出流:OutputStream

    字符流:

    1、输入流:Reader

    2、输出流:Writer

    一切皆字节:

    1、计算机中的任何数据(文本、图片、视频、音乐等等)都是以二进制的形式存储的。8个二进制的比特位是1个字节。

    2、在数据传输时,也都是以二进制的形式传输的,而且是一个字节一个字节的传。

    3、任何流在传输时,底层都是二进制。

    OutputStream抽象类

    此抽象类是表示输出字节流的所有类的超类。

    public abstract void write(int b) throws IOException

    将指定的字节写入此输出流。 write的一般合同是将一个字节写入输出流。 要写入的字节是参数b的八个低位。 b的24个高位被忽略。

    int = 4个字节 = 32bit 1个字节 = 8bit 00000000 00000000 00000000 00000000

    字节输入流最大数只能是255(8个1的二进制转换成十进制就是255),超出255就会被忽略。

    子类FileOutputStream

    //示例一
    public static void main(String[] args) throws IOException{
        //如果没有文件,会自动创建文件;后面的true代办会在a.txt里追加内容。
        FileOutputStream fos = new FileOutputStream("c://a.txt",true);
        byte[] bytes = {65,66,67,68,69};
        fos.wtrite(bytes);//a.txt文件里写入ABCDE
        fos.close();
        System.out.println("已经写出")
    }
    //示例二
    public static void main(String[] args) throws IOException{
        FileOutputStream fos = new FileOutputStream("c://a.txt");
        byte[] bytes = {65,66,67};
        fos.wtrite(bytes);//a.txt文件里写入ABC
        byte[] bytes2 = {65,66};
        fos.wtrite(bytes2);//a.txt文件里写入AB
        fos.close();
        System.out.println("已经写出")
    }//结果是:ABCAB,所以同一个流下写入数据,也会几乎追加内容。只有一个流重新创建,如果不选择追加,则会清空文件里的内容再写入。
    //示例三
    public static void main(String[] args) throws IOException{
        FileOutputStream fos = new FileOutputStream("c://a.txt");
        byte[] bytes = "ABCDEF".getBytes();//转成bit,但是没必要,可以直接用字符流,它的内部已经对字符进行了转换处理。
        fos.wtrite(bytes,1,2);//a.txt文件里写入BC,这里的1代表下标,2代表长度。
        fos.close();
        System.out.println("已经写出")
    }
    

    FileInputStream

    从文件系统中的文件获取输入字节。

    //示例一
    public static void main(String[] args) throws IOException{
        FileInputStream fis = new FileInputStream("c://a.txt");//文件里有abcdef
        byte b = (byte)fis.read();//一次只读一个字节
        System.out.println(b);//输出:97,对应ASCLL码为a
        //System.out.println((char)b)//强制转换成char类型,为a
        byte b2 = (byte)fis.read();
        System.out.println(b2);//输出:98,对应ASCLL码为b
        fis.close();
    }
    //示例二
    public static void main(String[] args) throws IOException{
        FileInputStream fis = new FileInputStream("c://a.txt");//文件里有abcdefghijklmnopqrstuvwxyx
        byte[] bytes = new byte[10];//一次读10个字节
        fis.read(bytes);
        System.out.println(new String(bytes));//输出:abcdefghij
        fis.read(bytes);
        System.out.println(new String(bytes));//输出:klmnopqrst
        fis.read(bytes);
        System.out.println(new String(bytes));//输出:uvwxyxqrst,第三次读取只有6个,只覆盖了前6个,剩余的4个还是上一次读取的后4个字节。
        fis.close();
    }
    //示例三
    public static void main(String[] args) throws IOException{
        FileInputStream fis = new FileInputStream("c://a.txt");//文件里有abcdefghijklmnopqrstuvwxyx
        byte[] bytes = new byte[10];//一次读10个字节
        int len = fis.read(bytes);//10,这是读取到的字节数
        System.out.println(new String(bytes,0,len));//输出:abcdefghij
        len = fis.read(bytes);//10
        System.out.println(new String(bytes,0,len));//输出:klmnopqrst
        len = fis.read(bytes);//6
        System.out.println(new String(bytes,0,len));//输出:uvwxyxqrst,第三次读取只有6个,只覆盖了前6个,剩余的4个还是上一次读取的后4个字节。
        len = fis.read(bytes);//-1
        System.out.println(len);//输出:-1,因为文件没有内容可读,则返回-1
        fis.close();
    }
    

    注意:字节输入流读取文件中的内容会,有时会造成读一半字的情况,所以这时候就用到了字符输入流。

    Writer

    用于写入字符流的抽象类。

    FileWriter

    使用默认缓冲区大小将文本写入字符文件。

    //示例一
    public static void main(String[] args) throws IOException{
        FileWriter fw = new FileWriter("c://b.txt");
        fw.write("窗前明月光");
        fw.close();
    }
    //示例二
    public static void main(String[] args) throws IOException{
        FileWriter fw = new FileWriter("c://b.txt");
        //FileWriter fw2 = (FileWriter) fw.append("锄禾日当午");
        fw.append("锄禾日当午").append(",").append("汗滴禾下土");//append()方法返回的是fw自身,所以可以连续再写入文件字符流里追加内容,但是它和构造函数里的true不一样,并不是真正的追加文件里的内容。
        fw.close();//字符写入流,如果不加close()获取flush()方法结尾,那么内容还是在内存中保存着,并没有写入到文件中。
        //直接写close()方法也行,调用close()方法也相当于调用flush()方法
    }
    

    Reader

    用于读取字符流的抽象类。

    FileReader

    public static void main(String[] args){
        FileReader fr = new FileReader("b.txt");//文件内容:锄禾日当午,汗滴禾下土
        while(true){
            int c = fr.read();//一次读一个字符
            if(c == -1){
                break;
            }
            System.out.priny((char)c);//输出:锄禾日当午,汗滴禾下土
        }
        char[] chars = new char[100];//char型数组默认为空格,所以数组里面默认有100个空格。
        fr.read(chars);
        System.out.println(new String(chars));//输出:锄禾日当午,汗滴禾下土(后面剩余全是空格)
        //处理方法
        /***
        int len = fr.read(chars);
        String text = new String(chars,0,len);
        System.out.println(text);//输出:锄禾日当午,汗滴禾下土(后面没有空格)
        System.out.println(text.length());//输出:11
        */
        fr.close();
    }
    

    File类

    文件和目录路径名的抽象表示。

    public static void main(String[] args) throws IOException{
        //示例一
        File file = new File("c://1.txt");
        boolean flag = file.createNewFile();
        System.out.println(flag?"创建成功":"创建失败");//文件不存在时,创建成功,文件已存在,则创建失败。
        //示例二
        File dir = new File("c://1.txt");
        dir.mkdir();//会创建一个1.txt文件目录(文件夹)
        //示例三
        File dir2 = new File("c://haha");
        dir.mkdir();						//创建目录
        File a = new File(dir,"a.txt");
        a.createNewFile();					//在haha文件夹下创建文件a.txt
        File b = new File("c://haha","b.txt");
        b.createNewFile();					//在haha文件夹下创建文件b.txt
        //示例四
        a.delete();							//删除文件a.txt
        b.delete();							//删除文件b.txt
        //示例五
        File file2 = new File("C://360极速浏览器下载//apache-tomcat-9.0.31-windows-x64.zip");
        File newFile = new File("C://a.zip");
        file.renameTo(newFile);//"C://360极速浏览器下载//apache-tomcat-9.0.31-windows-x64.zip"的压缩包会转移到"C://a.zip"压缩包里。
    }
    
    文件遍历案例:
    
    public static void main(String[] args){
        File e = new File("e:\\");
        File[] files = e.listFiles();
        listFiles(files);
    }
    public static void listFiles(File[] files){
        if(files!=null && files.length>0){
            for(File file:files){
                if(file.isFile){
                    //文件
                    if(file.getName().endWith(".avi")){
                        //找到一个avi文件
                        if(file.length>200*1024*1024){//200单位B,200*1024单位KB,200*1024*1024单位M
                        	file.delete();
                            System.out.println(file.getAbsolutePath()+"已删除");
                        }
                    }
                } else {
                	//文件夹
                    File[] files2 = file.listFiles();
                    listFiles(files2);
                }
            }
        }
    }
    

    文件过滤器(Filter)

    public static void main(String[] args) throws IOException{
        File e = new File("E:\\");
        listFiles(e);
    }
    public static void listFiles(File file){
        //1. 创建一个过滤器 并 描述规则
        Filefilter filter = new AVIFileFilter();
        //此处可以用匿名内部类简化
        /***
        Filtefilter filter = new FileFilter(){
            @Override
        	public boolean accept(File pathname){
            	if(pathname.getName().endsWith(".avi") || pathname.isDirectory()){
                	return true;
            	}
            	return false;
        	}
        };
        */
        //2. 通过文件获取子文件
        File[] files = file.listFiles(filter);
        //匿名内部类更加简化的方式
        /***
        File[] files = file.listFiles(new FileFilter(){
            @Override
        	public boolean accept(File pathname){
            	if(pathname.getName().endsWith(".avi") || pathname.isDirectory()){
                	return true;
            	}
            	return false;
        	}
        })
        */
        if(files!=null && files.length>0)
        {
            for(File f:files){
            if(f.isDirectory()){
            	listFiles(f);
            } else {
                System.out.println("发现一个avi:"+f.getAbsolutePath());
            }
        }
        }
    }
    static class AVIFileFilter implements FileFilter{
        @Override
        public boolean accept(File pathname){
            if(pathname.getName().endsWith(".avi") || pathname.isDirectory()){
                return true;
            }
            return false;
        }
    }
    

    相对路径和绝对路径

    public static void main(String[] args){
        //绝对路径:从盘符开始,是一个完整的路径,列如:c://a.txt
        //相对路径:在Java代码中是相对于项目目录路径,这是一个不完整的便捷路径,在Java开发中很常用,例如:a.txt
        File file1 = new File("c://a.txt");
        File file2 = new File("a.txt");
        System.out.println("file1的路径:"+file1.getAbsolutePath());//file1的路径:c:\a.txt
        System.out.println("file2的路径:"+file2.getAbsolutePath());
        //file2的路径:c:\Users\cdd\IdeaProjects\Demo\a.txt
    }
    

    字节流装饰(转换)为字符流

    使用了装饰者设计模式

    InputStreamReader

    InputStreamReader是从字节流到字符流的桥接器:它使用指定的charset读取字节并将其解码为字符。

    public static void main(String[] args) throws IOException{
        FileInputStream fis = new FileInputStream("c://a.txt");//文件内容为:一二三四五
        //将字节输入流,转换为字符输入流
        //参数1.要转换的字节流
        //参数2.指定编码名称
        InputStreamReader isr = new InputStreamReader(fis,"gbk");
        while(true){
            int c = isr.read();
            if(c == -1){
                break;
            }
            System.out.print((char)c);//输出:一二三四五
        }
        isr.close();
    }
    

    OutputStreamWriter

    OutputStreamWriter是从字符流到字节流的桥接器:使用指定的charset写入其中的字符编码为字节。

    public static void main(String[] args) throws IOException{
        FileOutputStream fos = new FileOutputStream("c://a.txt");
        //将字节输出流,转换为字符输出流
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("窗前明月光");
        osw.flush();
        osw.close();
    }
    

    字符输出(打印流)

    PrintStream

    PrintStream向另一个输出流添加功能,即能够方便地打印各种数据值的表示。

    public static void main(String[] args){
        //字符输出(打印流)
        PrintStream ps = new PrintStream("c://c.txt");
        ps.println("锄禾日当午,汗滴禾下土");
        ps.close();
    }
    

    System.out.方法也是调用PrintStream里的方法

    PrintWriter

    • 将对象的格式化表示打印到文本输出流。这个类实现所有的print中发现的方法PrintStream 。它不包含写入原始字节的方法,程序应使用未编码的字节流。
    • 不像PrintStream类,如果启用自动刷新,将只有当一个做printlnprintf ,或format被调用的方法,而不是当一个换行符恰好是输出。 这些方法使用平台自己的行分隔符概念而不是换行符。
    • 这个类中的方法永远不会抛出I / O异常,尽管它的一些构造函数可能会。 客户端可以通过调用checkError()来查询是否发生了任何错误。
    • 此类始终使用charset的默认替换字符串替换格式错误且不可映射的字符序列。 当需要对编码过程进行更多控制时,应使用CharsetEncoder类。
    public static void main(String[] args){
        PrintWriter pw = new PrintWriter("c://c.txt");
        pw.println("锄禾日当午,汗滴禾下土");
        pw.flush();
        pw.close();
    }
    

    注意,PrintWriter也可以进行转换字节流为字符流

    public static void main(String[] args){
        FileOutputStream fos = new FileOutputStream("c://c.txt");
        PrintWriter pw = new PrintWriter(fos);
        pw.println("锄禾日当午,汗滴禾下土");
        pw.close();
    }
    

    缓存字符读取流

    从字符输入流中读取文本,缓冲字符,以便有效地读取字符,数组和行。可以指定缓冲区大小,或者可以使用默认大小。 对于大多数用途,默认值足够大。

    public static void main(String[] args){
        //缓存读取流,将字符输入流 转换为带有缓存 可以一次读取一行的缓存字符读取流
        FileReader fr = new FileReader("c://c.txt");//文件里的内容:锄禾日当午,汗滴禾下土嘿嘿嘿1
        BufferedReader br = new BufferedReader(fr);//			  锄禾日当午,汗滴禾下土嘿嘿嘿2
        String text = br.readLine();				//			  锄禾日当午,汗滴禾下土嘿嘿嘿3
        System.out.println(text);//输出:锄禾日当午,汗滴禾下土嘿嘿嘿1
    }
    

    收集异常日志

    public static void main(String[] args) throws FileNotFoundException{
        try{
            String s = null;
            s.toString();
        } catch(Exception e){
            PrintWriter pw = new PrintWriter("c://bug.txt");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            pw.println(sdf.format(new Date()));//往文件中写入日期
            e.printStackTrace();			   //往文件中写入异常,控制台不再显示
        }
    }
    

    Properties

    Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。Properties既属于流也属于集合(属于HashMap集合下的内容)

    .properties配置文件和Properties类:

    写入配置文件示例:

    public static void main(String[] args) throws IOException{
        Properties ppt = new Properties();
        ppt.put("name","金苹果");
        ppt.put("info","讲述了苹果种植的过程");
        FileWriter fw = new FileWriter("d://book.properties");
        ppt.store(fw,"存储的图书");//.properties配置文件中不允许直接出现中文字符串,会转换成unicode编码
        fw.close();
    }
    

    在这里插入图片描述

    读取配置文件示例:

    public static void main(String[] args) throws IOException{
        Properties ppt = new Properties();
        Reader r = new FileReader("c://book.properties");
        ppt.load(r);
        //System.out.println(ppt.get("name"));		//这是利用HashMap键值对获取
        //System.out.println(ppt.get("info"));
        System.out.println(ppt.getProperty("name"));//原理也是利用HashMap键值对获取,只是看着更见名知意
        System.out.println(ppt.getProperty("info"));
    }
    

    在这里插入图片描述

    控制台sout输出

    System.out.print输出的内容没有换行,System.out.println输出的内容会自动换行。

    判断用户输入类型

    Scanner input = new Scanner(System.in);
    System.out.println("请输入一个数字:");
    //在接收输入之前判断输入的类型是否为int
    if(input.hasNextInt()){
        int num = input.nextInt();
        System.out.println("你输入的是:"+num);
    }else{
        System.out.println("你输入的是个锤子");
    }
    
    input.nextLine();
    
    nextLine()next()不要混用,因为nextLine()会接收换行符\n,接收到换行符,则结束。而next()是以空格来切分的,不会接受换行符\n。
    

    Java序列化

    public static void main(String[] args) throws IOException {
        Book b = new Book("金苹果","描述了金苹果的种植的过程");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d://book.txt"));
        oos.writeObject(b);//序列化
        oos.close();
    }
    static class Book implements Serializable{
        private String name;
        private String info;
        public Book() {
        }
        public Book(String name, String info) {
            this.name = name;
            this.info = info;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getInfo() {
            return info;
        }
        public void setInfo(String info) {
            this.info = info;
        }
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", info='" + info + '\'' +
                    '}';
        }
    }
    

    上述代码运行结果:

    在这里插入图片描述

    public static void main(String[] args) throws IOException, ClassNotFoundException {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d://book.txt"));
            Object o = ois.readObject();//反序列化
            System.out.println(o);
        }
    

    在这里插入图片描述

    Serializable实现Java序列化

    要实现Java对象的序列化,只要将类实现标识接口——Serializable接口即可,不需要我们重写任何方法就可以实现序列化。

    /*** 学生实体类 */ 
    public class Student implements Serializable{ 
        /*** 学号 */ 
        private String stuNum; 
        /*** 姓名 */ 
        private String stuName; 
        /*** 教师姓名:一个学生可以有多个老师 */
        private List<String> teacherList; 
        //无参数构造方法 
        public Student() { }
        //全参构造方法 
        public Student(String stuNum, String stuName, List<String> teacherList) {
            this.stuNum = stuNum; 
            this.stuName = stuName;
            this.teacherList = teacherList;
        }
        @Override 
        public String toString() { 
            return "Student{" + "stuNum='" + stuNum + '\'' +
                ", stuName='" + stuName + '\'' + ", teacherList=" + teacherList + '}'; 
        }
        public String getStuNum() {
            return stuNum; 
        }
        public void setStuNum(String stuNum) { 
            this.stuNum = stuNum; 
        }
        public String getStuName() { 
            return stuName; 
        }
        public void setStuName(String stuName) { 
            this.stuName = stuName;
        }
        public List<String> getTeacherList() { 
            return teacherList;
        }
        public void setTeacherList(List<String> teacherList) { 
            this.teacherList = teacherList; 
        }
    }
    

    编写Java对象序列化和反序列化工具类

    /*** 序列化和反序列化的工具类 */
    public class MySerializeUtil { 
        /*** 
        将对象序列化到指定文件中 
        * @param obj 
        * @param fileName
        */ 
        public static void mySerialize(Object obj,String fileName) throws IOException { 
            OutputStream out=new FileOutputStream(fileName); 
            ObjectOutputStream objOut=new ObjectOutputStream(out);
            objOut.writeObject(obj); 
            objOut.close(); 
        }
        /*** 从指定文件中反序列化对象
        * @param fileName
        * @return
        */ 
        public static Object myDeserialize(String fileName) throws IOException, ClassNotFoundException { 			InputStream in=new FileInputStream(fileName); 
            ObjectInputStream objIn=new ObjectInputStream(in);
            Object obj=objIn.readObject(); 
            return obj; 
        } 
    }
    

    测试对象的序列化和反序列化

    /*** 测试类 */ 
    public class MainTest { 
        public static void main(String[] args) { 
            List<String> teacherList=new ArrayList<>(); 
            teacherList.add("空空道人");
            teacherList.add("贾代儒");
            Student stu1=new Student("1001", "贾宝玉", teacherList);
            System.out.println("原始对象:"+stu1);
            String fileName="stu01.txt"; 
            try {
                //对象序列化 
                MySerializeUtil.mySerialize(stu1, fileName); 
                System.out.println("序列化原始对象完成!OK!"); 
                //对象的反序列化 
                Object obj=MySerializeUtil.myDeserialize(fileName); 
                if(obj instanceof Student){ 
                    Student stuNew= (Student) obj; 
                    System.out.println("反序列化之后的对象:"+stuNew);
                } 
            } catch (Exception e) {
                e.printStackTrace();
            } 
        }
    }
    

    运行结果:

    在这里插入图片描述

    部分属性的序列化

    实现部分字段序列化的方式:

    • 使用transient修饰符

    • 使用static修饰符

    • 默认方法writeObject和readObject。

    使用transient修饰符

    修改实体类,将实体类中不想序列化的属性添加transient修饰词。

    public class Student implements Externalizable { 
        private String stuNum; 
        private transient String stuName; 
        private transient List<String> teacherList; 
        ......
    

    重新运行测试类的结果:

    我们将实体类中的stuName和teacherList属性添加了transient修饰词,因此对象被序列化的时候忽略这两个属性。通过运行结果可以看出。

    在这里插入图片描述

    使用static修饰符

    static修饰符修饰的属性也不会参与序列化和反序列化。

    修改实体类,将实体类中不想序列化的属性添加static修饰词。

    public class Student implements Externalizable { 
        private String stuNum;
        private static String stuName;
        private List<String> teacherList; 
        ......
    

    修改测试类,在完成原始对象的序列化之后再对static修饰的变量进行一次赋值操作:

    在这里插入图片描述

    我们将实体类中的stuName属性添加了static修饰词,因此对象被序列化的时候忽略这个属性。通过运行结果可以看出。

    在这里插入图片描述

    默认方法writeObject和readObject

    修改实体类,将transient修饰词去掉,添加两个方法。

    public class Student implements Serializable { 
        private String stuNum;
        private String stuName;
        private List<String> teacherList;
        private void writeObject(ObjectOutputStream objOut) throws IOException { 
            System.out.println("writeObject-----------"); 
            objOut.writeObject(stuNum); 
            objOut.writeObject(stuName); 
        }
        private void readObject(ObjectInputStream objIn) throws IOException, ClassNotFoundException { 
            System.out.println("readObject-----------"); 
            stuNum= (String) objIn.readObject(); 
            stuName= (String) objIn.readObject(); 
        }
        .....
    

    重新运行测试类的结果:

    我们在添加的方法中只对stuNum和stuName属性做了序列化和反序列化的操作,因此只有这个两个属性可以被序列化和反序列化。

    在这里插入图片描述

    源码分析:

    注意:添加的两个方法必须是private void,否则不生效。

    Java调用ObjectOutputStream类检查其是否有私有的、无返回值的writeObject方法,如果有,其会委托该方法进行对象序列化。

    Serializable接口的注释:

    在这里插入图片描述

    ObjectStreamClass类:在序列化(反序列化)的时候,ObjectOutputStream(ObjectInputStream)会寻找目标类中的私有的writeObject(readObject)方法,赋值给变量writeObjectMethod(readObjectMethod)。

    在这里插入图片描述

    在这里插入图片描述

    通过上面这两段代码可以知道,如果writeObjectMethod != null(目标类中定义了私有的writeObject方法),那么将调用目标类中的writeObject方法,如果如果writeObjectMethod == null,那么将调用默认的defaultWriteFields方法来读取目标类中的属性。

    readObject的调用逻辑和writeObject一样。

    总结一下,如果目标类中没有定义私有的writeObject或readObject方法,那么序列化和反序列化的时候将调用默认的方法来根据目标类中的属性来进行序列化和反序列化,而如果目标类中定义了私有的writeObject或readObject方法,那么序列化和反序列化的时候将调用目标类指定的writeObject或 readObject方法来实现。

    Externalizable实现Java序列化

    刚刚我们说实现部分属性序列化的方式有多种,最后一种来啦!就是通过实现Eexternalizable接口。

    Externalizable继承自Serializable,使用Externalizable接口需要实现readExternal方法和writeExternal方法来实现序列化和反序列化。

    来看看Externalizable接口的说明:

    在这里插入图片描述

    代码实现:

    public class Student implements Externalizable {
        /*** 学号 */ 
        private String stuNum; 
        /*** 姓名 */ 
        private String stuName;
        /*** 教师姓名:一个学生可以有多个老师 */ 
        private List<String> teacherList; 
        //无参数构造方法 
        public Student() { }
        //全参构造方法 
        public Student(String stuNum, String stuName, List<String> teacherList) { 
            this.stuNum = stuNum; 
            this.stuName = stuName;
            this.teacherList = teacherList;
        }
        @Override
        public void writeExternal(ObjectOutput out) throws IOException { 
            out.writeObject(stuNum); 
            out.writeObject(stuName); 
            //out.writeObject(teacherList); 
        }
        @Override 
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
            stuNum= (String) in.readObject(); 
            stuName= (String) in.readObject(); 
            //teacherList= (List<String>) in.readObject(); 
        }
        @Override 
        public String toString() { 
            return "Student{" + "stuNum='" + stuNum + '\'' + ", stuName='" + stuName + '\'' + ", 
                teacherList=" + teacherList + '}';
        }
        public String getStuNum() { 
            return stuNum; 
        }
        public void setStuNum(String stuNum) { 
            this.stuNum = stuNum;
        }
        public String getStuName() { 
            return stuName; 
        }
        public void setStuName(String stuName) {
            this.stuName = stuName;
        }
        public List<String> getTeacherList() {
            return teacherList; 
        }
        public void setTeacherList(List<String> teacherList) {
            this.teacherList = teacherList;
        }
    }
    

    测试结果:

    在这里插入图片描述

    Serializable VS Externalizable

    在这里插入图片描述

    展开全文
  • Java IO流知识点

    2021-04-14 22:01:53
    IO流 File 类的方法 package IOpackage; import org.junit.jupiter.api.Test; import java.io.File; /** * File 类的使用 * 1 File类的一个对象,代表一个文件或者一个文件目录 * 2 File类声明在java.io包下 ...
  • 2.IO流的分类IO流分为字符流和字节流.3.字节流和字符流的区别1.字节流读取的时候,读到一个字节就返回一个字节; 字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8码表中是3个字节)...
  • 此时此刻,我坐在电脑前整理这篇Java IO流知识点总结。希望能对需要它的人有帮助。 1、前言 我们在日常开发中,有许多方面都会涉及到IO流,比如上传,下载,设计模式等等。而所有的一切都是基于IO流来进行的,所以...
  • Java中的IO流一、File类的使用①File的概述②常用构造器③路径分隔符④常用方法⑤举例与练习二、IO流原理及流的分类①JavaIO流原理②流的分类③IO流的体系④节点流和处理流⑤InputStream & Reader①InputStream...
  • IO流 字节流、字节缓冲流 字符流、字符缓冲流 特殊操作流 Properties集合 总结: File类 概述: 它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言,其封装...
  • javaIO流相关知识点

    2021-03-13 12:31:53
    (一) 下边使用outputStream字节输出进行...import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;public class...
  • 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈...
  • 彻底明白JavaIO系统一. Input和Output1. stream代表的是任何有能力产出数据的数据源,或是任何有能力接收数据的接收源。在JavaIO中,所有的stream(包括Input和Out stream)都包括两种类型:1.1 以字节为导向的...
  • 之前已经给大家讲了关于字符和字节的一些相关知识,并且输入和输出都做了一些简单的讲解和举例,而这篇文章主要是讲解一下关于字符中缓冲区的相关知识点。其实在讲解输入和输出的时候我们就已经有定义一个...
  • 而每天加工的数据通常要求在上班工作时间之前加工完成,然后通过数据智能平台的查询系统供业务系统查询调用,这一次数据没有查询到是因为在第二天早上10,数据还没有加工完成。下面就是找问题优化了,因为正常来...
  • 该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架。在初学Java时,IO流是我遇到的一个很...
  • java知识点分享,IO流详解!Java知识IO流详解有人觉得IO知识不太重要,其实不然,IO的使用范围很广,最能体现IO价值的就是网络上的数据传递,尤其是进入互联网时代后,各种常见的分布式架构,都少不了IO的体现。并且...
  • IO流知识点整理

    2021-08-08 08:42:09
    一、IO概述 什么是IO 生活中,你肯定经历过这样的场景,当...Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。 IO的分类 根据数据的流向分为:输入.
  • Java IO知识点总结

    2021-10-29 08:53:54
    ------------------------------廖雪峰学Java----------------------------------> 目录1. File对象2. InputStream3. OutputStream4. Filter模式(装饰者模式)5. 直接读取Zip压缩文件的内容6. 读取classpath资源...
  • ** ### 1、服务端 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class MyServer { public static void main(String[] args) throws IOException { ServerSocket ...
  • 本题集列举了众多IT公司面试真题,对应聘Java程序员职位的常见考点和知识体系都进行的分类和归纳整理。 本题集适合应聘Java和JavaEE职位的程序员作为面试复习、学习和强化的资料,也适合其他程序员作为拓展读物进行...
  • Java知识点总结(JavaIO-字节)@(Java知识点总结)[Java, JavaIO][toc]字节在程序中所有的数据都是以的方式进行传输或保存的,程序需要数据时要使用输入读取数据,而当程序需要将一些数据保存起来时,就要使用...
  • IO流知识点大全

    2021-08-02 22:14:32
    文章目录IO流OutputStreamFileOutputStreamInputStreamcloseread子类FileInputStreamWriterFileWriter子类ReaderFileReaderFlush刷新管道字节流 "装饰"为 字符流PrintStreamBufferedReader收集异常日志properties...
  • IO框架 一、的概念 概念:内存与存储设备之间传输数据的通道。 二、的分类 按方向分类: 输入:将<存储设备>中的内容读入到<内存>中 输出:将<内存>中的内容读入到<存储设备&...
  • 输入和输出的不是简单的说文件从硬盘读取到内存就是输入,文件从内存保存到硬盘读取文件就是输出。应该已代码程序为参照物,此图可帮助一些小伙伴理解清楚这个与硬盘、内存之间的关系。 ...
  • 第十七天进程和线程--------1.进程:就是正在运行的程序,分配内存让应用程序能够运行。Windows系统号称:多任务...注意:java程序在运行的时候,jvm会帮我们创建一个主线程来执行代码。主线程主要负责main方法中的...
  • 问题:Java面试知识点:File、IO流 答案: 1.File listFiles方法注意事项: • 当调用者不存在时,返回null • 当调用者是一个文件时,返回null • 当调用者是一个空文件夹时,返回一体度为0的数组 • 当调用者...
  • 1、java.io.File类:文件和文件目录路径的抽象表达形式,与平台无关 2、File类能够新建、删除、重命名文件和目录,但不能访问文件内容本身,如果需要访问,需要使用输入、输出 3、想要在java中表示一个真正存在...
  • 比如说IO流中的释放资源 特点:被finally控制的语句一定会执行,除非JVM退出 2.字节流读数据 FileInputStream:从文件系统中的文件获取输入字节 FileInputStream(Stringname):通过打开与实际文件的连接来创建一个...
  • Java知识点总结(JavaIO-内存操作)@(Java知识点总结)[Java, JavaIO][toc]前面所讲的程序中输入、输出都是从文件中来,当然也可以将输出的位置设置在内存上。内存操作一般在生成一些临时信息是才会使用,而这些...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,679
精华内容 22,671
关键字:

javaio流知识点

java 订阅