下载

1下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 JAVA_IO流学习总结

JAVA_IO流学习总结.pdf

JAVA_IO流学习总结

sunxing0302
2011-07-04 0人阅读 举报 0 0 暂无简介

简介:本文档为《JAVA_IO流学习总结pdf》,可适用于IT/计算机领域

JAVA期宝剑锋从磨砺出梅花香自苦寒来毕向东北京传智播客JAVA期预热班JAVA中为什么提出流的概念?JAVA中的流都有哪些对象?各有什么特点?流中都有哪些异常?都有了字节流为什么还需要字符流呢?在使用流时我们应该注意什么?所有的流都需要关闭吗?我们怎么能指定特定的编码格式?OOP的继承性和装饰模式有什么区别?IO流宝典JAVA改变生活IO流对象继承关系其他常用与流有关的对象:名称对应的对象文件类File打印流PrintStreamPrintWriter管道流PipedInputStreamPipedOutputStream序列流SequenceInputStream对象序列化流ObjectInputStreamObjectOutputStream流字符流ReaderBufferedReaderInputStreamReaderFileReaderWriterBufferedWriterOutputStreamWriterFileWriter字节流InputStreamFileInputStreamFilterInputStreamBufferedInputStreamOutputStreamFileOutputStreamFilterOutputStreamBufferedOutputStreamJAVA改变生活IO流学习毕向东编写wangboak整理IO流:用于处理设备上的数据。设备:硬盘内存键盘录入。IO有具体的分类:根据处理的数据类型不同:字节流和字符流。根据流向不同:输入流和输出流。字符流的由来:因为文件编码的不同而有了对字符进行高效操作的字符流对象。原理:其实就是基于字节流读取字节时去查了指定的码表。字节流和字符流的区别:字节流读取的时候读到一个字节就返回一个字节。字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个在码表中是个字节)时。先去查指定的编码表将查到的字符返回。字节流可以处理所有类型数据如图片mpavi。而字符流只能处理字符数据。结论:只要是处理纯文本数据就要优先考虑使用字符流。除此之外都用字节流。IO的体系。所具备的基本功能就有两个:读和写。字节流InputStream(读),OutputStream(写)。字符流:Reader(读)Writer(写)。基本的读写操作方式:因为数据通常都以文件形式存在。所以就要找到IO体系中可以用于操作文件的流对象。通过名称可以更容易获取该对象。因为IO体系中的子类名后缀绝大部分是父类名称。而前缀都是体现子类功能的名字。JAVA改变生活Reader|InputStreamReader|FileReader:专门用于处理文件的字符读取流对象。Writer|OutputStreamWriter|FileWriter:专门用于处理文件的字符写入流对象。Reader中的常见的方法:intread():读取一个字符。返回的是读到的那个字符。如果读到流的末尾返回intread(char):将读到的字符存入指定的数组中返回的是读到的字符个数也就是往数组里装的元素的个数。如果读到流的末尾返回close():读取字符其实用的是window系统的功能就希望使用完毕后进行资源的释放。Writer中的常见的方法:write(ch):将一个字符写入到流中。write(char):将一个字符数组写入到流中。write(String):将一个字符串写入到流中。flush():刷新流将流中的数据刷新到目的地中流还存在。close():关闭资源:在关闭前会先调用flush()刷新流中的数据去目的地。然流关闭。FileWriter:该类没有特有的方法。只有自己的构造函数。该类特点在于用于处理文本文件。该类中有默认的编码表该类中有临时缓冲。构造函数:在写入流对象初始化时必须要有一个存储数据的目的地。FileWriter(Stringfilename):该构造函数做了什么事情呢?调用系统资源。在指定位置创建一个文件。注意:如果该文件已存在将会被覆盖。FileWriter(Stringfilename,booleanappend):该构造函数:当传入的boolean类型值为true时会在指定文件末尾处进行数据的续写。JAVA改变生活FileReader:用于读取文本文件的流对象。用于关联文本文件。构造函数:在读取流对象初始化的时候必须要指定一个被读取的文件。如果该文件不存在会发生FileNotFoundExceptionFileReader(Stringfilename)清单:,将文本数据存储到一个文件中。classDemo{publicstaticvoidmain(Stringargs)throwsIOException{FileWriterfw=newFileWrier("demotxt")fwwrite("abcdec")fwflush()fwwrite("kkkk")fwclose()}}对于读取或者写入流对象的构造函数以及读写方法还有刷新关闭功能都会抛出IOException或其子类。所以都要进行处理。或者throws抛出或者trycatch处理。清单:完整的异常处理方式。classDemo{publicstaticvoidmain(Stringargs){FileWriterfw=try{fw=newFileWrier("z:demotxt")fwwrite("abcdec")JAVA改变生活fwflush()fwwrite("kkkk")}catch(IOExceptione){Systemoutprintln(etoString())}finally{if(fw!=)try{fwclose()}catch(IOExceptione){Systemoutprintln("close:"etoString())}}}}另一个小细节:当指定绝对路径时定义目录分隔符有两种方式:反斜线但是一定要写两个。newFileWriter("c:demotxt")斜线写一个即可。newFileWriter("c:demotxt")清单:读取一个已有的文本文件将文本数据打印出来。一次读一个字符就打印出来效率不高。classDemo{publicstaticvoidmain(Stringargs)throwsIOException{FileReaderfr=newFileReader("demotxt")intch=一次读一个字符。JAVA改变生活while((ch=frread())!=){Systemoutprint((char)ch)}frclose()}}读一个字符就存入字符数组里读完Kb再打印。classDemo{publicstaticvoidmain(Stringargs){FileReaderfr=try{fr=newFileReader("demotxt")charbuf=newchar该长度通常都是的整数倍。intlen=while((len=frread(buf))!=){Systemoutprintln(newString(buf,,len))}}catch(IOExceptione){Systemoutprintln(etoString())}finally{if(fr!=)try{frclose()}catch(IOExceptione){JAVA改变生活Systemoutprintln("close:"etoString())}}}}字符流的缓冲区:缓冲区的出现提高了对流的操作效率。原理:其实就是将数组进行封装。对应的对象:BufferedWriter:特有方法:newLine():跨平台的换行符。BufferedReader:特有方法:readLine():一次读一行到行标记时将行标记之前的字符数据作为字符串返回。当读到末尾时返回。在使用缓冲区对象时要明确缓冲的存在是为了增强流的功能而存在所以在建立缓冲区对象时要先有流对象存在。其实缓冲内部就是在使用流对象的方法只不过加入了数组对数据进行了临时存储。为了提高操作数据的效率。代码上的体现:写入缓冲区对象。建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。BufferedWriterbufw=newBufferedWriter(newFileWriter("buftxt"))bufwwrite("abce")将数据写入到了缓冲区。bufwflush()对缓冲区的数据进行刷新。将数据刷到目的地中。bufwclose()关闭缓冲区其实关闭的是被包装在内部的流对象。JAVA改变生活读取缓冲区对象。BufferedReaderbufr=newBufferedReader(newFileReader("buftxt"))Stringline=按照行的形式取出数据。取出的每一个行数据不包含回车符。while((line=bufrreadLine())!=){Systemoutprintln(line)}bufrclose()练习:通过缓冲区的形式对文本文件进行拷贝。publicstaticvoidmain(Stringargs){BufferedReaderbufr=newBufferedReader(newFileReader("atxt"))BufferedWriterbufw=newBufferedWriter(newFileWriter("btxt"))Stringline=while((line=bufrreadLine())!=){bufwwrite(line)bufwnewLine()bufwflush()}bufwclose()bufrclose()}readLine():方法的原理:其实缓冲区中的该方法用的还是与缓冲区关联的流对象的read方法。只不过每一次读到一个字符先不进行具体操作先进行临时存储。当读取到回车标记时将临时容器中存储的数据一次性返回。既然明确了原理我们也可以实现一个类似功能的方法。JAVA改变生活classMyBufferedReader{privateReaderrMyBufferedReader(Readerr){thisr=r}publicStringmyReadLine()throwsIOException{,创建临时容器。StringBuildersb=newStringBuilder(),循环的使用read方法不断读取字符。intch=while((ch=rread())!=){if(ch=='r')continueif(ch=='n')returnsbtoString()elsesbappend((char)ch)}if(sblength()!=)returnsbtoString()return}publicvoidmyClose()throwsIOException{rclose()}}main(){MyBufferedReadermyBufr=newMyBufferedReader(newFileReader("atxt"))Stringline=while((line=myBufrmyReadLine())!=){Systemoutprintln(line)}}JAVA改变生活它的出现基于流并增强了流的功能。这也是一种设计模式的体现:装饰设计模式。对一组对象进行功能的增强。该模式和继承有什么区别呢?它比继承有更好的灵活性。通常装饰类和被装饰类都同属与一个父类或者接口。Writer|MediaWriter|TextWriter(注:MediaWriter与TextWtiter两个类在JDK中并不存在为了更形象的举例说明而“创建”的不要误解。)需求:想要对数据的操作提高效率就用到了缓冲技术。通过所学习的继承特性。可以建立子类复写父类中的write方法。即可Writer:(注:不要误解以下两个对象不存在只为举例。)|MediaWriter|BufferedMediaWriter|TextWriter|BufferedTextWriter当Writer中子类对象过多那么为了提高每一个对象效率每一个对象都有一个自己的子类Buffered。虽然可以实现但是继承体系变的很臃肿。那么是否可以对其进行一下优化呢?其实子类都是在使用缓冲技术。可不可以对缓冲技术进行描述将需要增强的对象传递给缓冲区即可。classBufferdWriter{BufferedWriter(MediaWritermw){}BufferedWriter(TextWritermw){}}该类虽然完成了对已有两个对象的增强。但是当有新的对象出现时还要继续在该类中添加构造函数。这样不利于扩展和维护。将对这些对象父类型进行操作即可。这就是多态提高了程序的扩展性。JAVA改变生活同时BufferedWriter中一样具体write方法只不过是增强后的write。所以BuferedWriter也应该是Writer中的一个子类。classBufferedWriterextendsWriter{privateWriterwBufferedWriter(Writerw){thisw=w}}Writer|MediaWriter|TextWriter|BufferedWriter这样就会发现装饰设计模式优化增强功能的部分。比继承要灵活很多。可以在读一行的基础上添加一个行号。classMyLineNumberReaderextendsMyBufferedReader{privateintnumberMyLineNumberReader(Readerr){super(r)}publicStringmyReadLine(){numberreturnsupermyReadLine()}publicvoidsetNumber(intnumber){thisnumber=number}publicintgetNumber(){returnnumber}}JAVA改变生活字节流:抽象基类:InputStreamOutputStream。字节流可以操作任何数据。注意:字符流使用的数组是字符数组。charchs字节流使用的数组是字节数组。bytebtFileOutputStreamfos=newFileOutputStream("atxt")foswrite("abcde")直接将数据写入到了目的地。fosclose()只关闭资源。FileInputStreamfis=newFileInputStream("atxt")fisavailable()获取关联的文件的字节数。如果文件体积不是很大。可以这样操作。bytebuf=newbytefisavailable()创建一个刚刚好的缓冲区。但是这有一个弊端就是文件过大大小超出jvm的内容空间时会内存溢出。fisread(buf)Systemoutprintln(newString(buf))需求:copy一个图片。BufferedInputStreambufis=newBufferedInputStream(newFileInputStream("jpg"))BufferedOutputStreambufos=newBufferedOutputStream(newFileOutptStream("jpg"))intby=while((by=bufisread())!=){bufoswrite(by)}bufosclose()bufisclose()JAVA改变生活目前学习的流对象:字符流:FileReaderFileWriterBufferedReaderBufferedWriter字节流:FileInputStreamFileOutputStreamBufferedInputStreamBufferedOutputStream字节流的read()方法读取一个字节。为什么返回的不是byte类型而是int类型呢?因为read方法读到末尾时返回的是而在所操作的数据中的很容易出现连续多个的情况而连续读到个就是导致读取会提前停止。所以将读到的一个字节给提升为一个int类型的数值但是只保留原字节并在剩余二进制位补具体操作是:byteorbytexff对于write方法可以一次写入一个字节但接收的是一个int类型数值。只写入该int类型的数值的最低一个字节(位)。简单说:read方法对读到的数据进行提升。write对操作的数据进行转换。转换流:特点:是字节流和字符流之间的桥梁。该流对象中可以对读取到的字节数据进行指定编码表的编码转换。什么时候使用呢当字节和字符之间有转换动作时。流操作的数据需要进行编码表的指定时。具体的对象体现:InputStreamReader:字节到字符的桥梁。OutputStreamWriter:字符到字节的桥梁。JAVA改变生活这两个流对象是字符流体系中的成员。那么它们有转换作用而本身又是字符流。所以在构造的时候需要传入字节流对象进来。构造函数:InputStreamReader(InputStream):通过该构造函数初始化使用的是本系统默认的编码表GBK。InputStreamReader(InputStream,StringcharSet):通过该构造函数初始化可以指定编码表。OutputStreamWriter(OutputStream):通过该构造函数初始化使用的是本系统默认的编码表GBK。OutputStreamWriter(OutputStream,StringcharSet):通过该构造函数初始化可以指定编码表。操作文件的字符流对象是转换流的子类。Reader|InputStreamReader|FileReaderWriter|OutputStreamWriter|FileWriter转换流中的read方法。已经融入了编码表在底层调用字节流的read方法时将获取的一个或者多个字节数据进行临时存储并去查指定的编码表如果编码表没有指定查的是默认码表。那么转流的read方法就可以返回一个字符比如中文。转换流已经完成了编码转换的动作对于直接操作的文本文件的FileReaer而言就不用在重新定义了只要继承该转换流获取其方法就可以直接操作文本文件中的字符数据了。注意:在使用FileReader操作文本数据时该对象使用的是默认的编码表。如果要使用指定编码表时必须使用转换流。JAVA改变生活FileReaderfr=newFileReader("atxt")操作atxt的中的数据使用的本系统默认的GBK。操作atxt中的数据使用的也是本系统默认的GBK。InputStreamReaderisr=newInputStreamReader(newFileInputStream("atxt"))这两句的代码的意义相同。如果atxt中的文件中的字符数据是通过的形式编码。那么在读取时就必须指定编码表。那么转换流必须使用。InputStreamReaderisr=newInputStreamReader(newFileInputStream("atxt"),"")流操作的基本规律。明确数据源和数据汇(数据目的)。其实是为了明确输入流还是输出流。明确操作的数据是否是纯文本数据。其实是为了明确字符流还是字节流。数据源:键盘Systemin,硬盘File开头的流对象内存(数组)。数据汇:控制台Systemout,硬盘File开头的流对象内存(数组)。需求:将键盘录入的数据存储到一个文件中。数据源:Systemin既然是源使用的就是输入流可用的体系有InputStreamReader。因为键盘录入进来的一定是纯文本数据所以可以使用专门操作字符数据的Reader。发现Systemin对应的流是字节读取流。所以要将其进行转换将字节转成字符即可。所以要使用Reader体系中:InputStreamReader接下来是否需要提高效率呢?如果需要那么就加入字符流的缓冲区:BufferedReaderBufferedReaderbur=newBufferedReader(newInputStreamReader(Systemin))数据汇:一个文件硬盘。既然是数据汇那么一定是输出流可以用的OutputStream,Writer。往文件中存储的都是文本数据那么可以使用字符流较为方便:WriterJAVA改变生活因为操作的是一个文件。所以使用Writer中的FileWriter。是否要提高效率呢?是那就使用BufferedWriterBufferedWriterbufr=newBufferedWriter(newFileWriter("atxt"))附加需求:希望将这些文本数据按照指定的编码表存入文件中。既然是文本数据而是还是写入到文件中那么使用的体系还是Writer。因为要指定编码表所以要使用Writer中的转换流OutputStreamWriter。是否要提高效率是选择BufferedWriter。注意:虽然是最终是文件但是不可以选择FileWriter。因为该对象是使用默认编码表。输出转换流要接收一个字节输出流进来所以要是用OutputStram体系而最终输出到一个文件中那么就要使用OutputStream体系中可以操作的文件的字节流对象:FileOutputStream。StringcharSet=SystemgetProperty("fileencoding")StringcharSet=""BufferedWriterbufw=newBufferedWriter(newOutputStreamWriter(newFileOutputStream("atxt"),charSet)将一个文本文件的数据展示在控制台上。同上自己一定要动手。复制文件。同上自己一定要动手。这三个都可以独立完成并写对过程和代码。那么IO流的操作哦了!JAVA改变生活可以和流相关联的集合对象PropertiesMap|Hashtable|PropertiesProperties:该集合不需要泛型因为该集合中的键值对都是String类型。存入键值对:setProperty(key,value)获取指定键对应的值:valuegetProperty(key)获取集合中所有键元素:EnumerationpropertyNames()在jdk版本给该类提供一个新的方法。Set<String>stringPropertyNames()列出该集合中的所有键值对可以通过参数打印流指定列出到的目的地。list(PrintStream)list(PrintWriter)例:list(Systemout):将集合中的键值对打印到控制台。list(newPrintStream("proptxt")):将集合中的键值对存储到proptxt文件中。可以将流中的规则数据加载进行集合并称为键值对。load(InputStream):jdk版本。提供了新的方法。load(Reader):注意:流中的数据要是"键=值"的规则数据。可以将集合中的数据进行指定目的的存储。store(OutputStram,Stringcomment)方法。jdk版本。提供了新的方法。store(Writer,Stringcomment):使用该方法存储时会带着当时存储的时间。练习:记录一个程序运行的次数当满足指定次数时该程序就不可以再继续运行了。通常可用于软件使用次数的限定。JAVA改变生活File类:该类的出现是对文件系统的中的文件以及文件夹进行对象的封装。可以通过对象的思想来操作文件以及文件夹。构造函数:File(Stringfilename):将一个字符串路径(相对或者绝对)封装成File对象该路径是可存在的也可以是不存在。File(Stringparent,Stringchild)File(Fileparent,Stringchild)特别的字段:separator:跨平台的目录分隔符。例子:Filefile=newFile("c:"Fileseparator"abc"Fileseparator"atxt")常见方法:创建:booleancreateNewFile()throwsIOException:创建文件如果被创建的文件已经存在则不创建。booleanmkdir():创建文件夹。booleanmkdirs():创建多级文件夹。删除:booleandelete():可用于删除文件或者文件夹。注意:对于文件夹只能删除不带内容的空文件夹对于带有内容的文件夹不可以直接删除必须要从里往外删除。voiddeleteOnExit():删除动作交给系统完成。无论是否反生异常系统在退出时执行删除动作。判断:booleancanExecute():booleancanWrite():booleancanRead()booleanexists():判断文件或者文件夹是否存在。booleanisFile():判断File对象中封装的是否是文件。booleanisDirectory():判断File对象中封装的是否是文件夹。booleanisHidden():判断文件或者文件夹是否隐藏。在获取硬盘文件或者文件夹时对于系统目录中的文件java是无法访问的所以在遍历可以避免遍历隐藏文件。获取:JAVA改变生活getName():获取文件或者文件夹的名称。getPath():File对象中封装的路径是什么获取的就是什么。getAbsolutePath():无论File对象中封装的路径是什么获取的都是绝对路径。getParent():获取File对象封装文件或者文件夹的父目录。注意:如果封装的是相对路径那么返回的是longlength():获取文件大小。longlastModified():获取文件或者文件最后一次修改的时间。staticFilelistRoots():获取的是被系统中有效的盘符。Stringlist():获取指定目录下当前的文件以及文件夹名称。Stringlist(Filenamefilter):可以根据指定的过滤器过滤后的文件及文件夹名称。FilelistFiles():获取指定目录下的文件以及文件夹对象。重命名:renameTo(File):Filef=newFile("c:atxt")Filef=newFile("c:btxt")frenameTo(f)将c盘下的atxt文件改名为btxt文件。JAVA改变生活递归:其实就是在使用一个功能过程中又对该功能有需求。就出现了函数自身调用自身。注意:一定要限定条件否则内存溢出。使用递归时调用次数不要过多否则也会出现内存溢出。需求:想要列出指定目录下的文件以及文件夹中的文件(子文件)。列出指定目录下的当前的文件或者文件夹。想要列出当前目录下的文件夹中的文件其实就是在重新使用该功能。publicvoidlistDir(Filedir,intlevel){Systemoutprintln(getLevel(level)dirgetName())levelFilefiles=dirlistFiles()for(intx=x<fileslengthx){if(filesxisDirecotry())如果遍历到的是目录。listDir(filesx,level)那么就行该功能的重复使用。递归。elseSystemoutprintln(getLevel(level)filesxgetName())}}想要对列出的目录有一些层次关系。publicStringgetLevel(intlevel){StringBuildersb=newStringBuilder()sbappend("|")for(intx=x<levelx){sbinsert(,"|")}returnsbtoString()}JAVA改变生活需求:删除一个带内容的目录。原理:从里往外删除所以需要使用递归完成。publicvoiddeleteAll(Filedir){Filefiles=dirlistFiles()for(intx=x<fileslengthx){if(filesxisDirectory())deleteAll(filesx)elsefilesxdelete()}dirdelete()}IO包中的常见对象。字节流:FileInputStreamFileOutputStreamBufferedInputStreamBufferedOutputStream字符流:FileReaderFileWriterBufferedReaderBufferedWriter转换流:InputStreamReaderOutputStreamWriter文件对象:File打印流:PrintStreamPrintWriter所有的带File的流对象都可以直接操作File对象。JAVA改变生活IO包中的其他对象:打印流。PrintStream:是一个字节打印流Systemout对应的类型就是PrintStream。它的构造函数可以接收三种数据类型的值。字符串路径。File对象。OutputStream。PrintWriter:是一个字符打印流。构造函数可以接收四种类型的值。字符串路径。File对象。对于类型的数据还可以指定编码表。也就是字符集。OutputStreamWriter对于类型的数据可以指定自动刷新。注意:该自动刷新值为true时只有三个方法可以用:println,printf,format如果想要既有自动刷新又可执行编码。如何完成流对象的包装?PrintWrterpw=newPrintWriter(newOutputSteamWriter(newFileOutputStream("atxt"),""),true)如果想要提高效率。还要使用打印方法。PrintWrterpw=newPrintWriter(newBufferdWriter(newOutputSteamWriter(newFileOutputStream("atxt"),"")),true)管道流。PipedInputStreamPipedOutputStream特点:读取管道流和写入管道流可以进行连接。连接方式:通过两个流对象的构造函数。通过两个对象的connect方法。通常两个流在使用时需要加入多线程技术也就是让读写同时运行。注意对于read方法。该方法是阻塞式的也就是没有数据的情况该方法会等待。JAVA改变生活参考:PipedstreamjavaRandomAccessFile:该对象并不是流体系中的一员。该对象中封装了字节流同时还封装了一个缓冲区(字节数组)通过内部的指针来操作数组中的数据。该对象特点:该对象只能操作文件所以构造函数接收两种类型的参数。a字符串路径。bFile对象。该对象既可以对文件进行读取也可以写入。在进行对象实例化时必须要指定的该对象的操作模式rrw等。该对象中有可以直接操作基本数据类型的方法。该对象最有特点的方法:skipBytes():跳过指定的字节数。seek():指定指针的位置。getFilePointer():获取指针的位置。通过这些方法就可以完成对一个文件数据的随机的访问。想读哪里就读哪里想往哪里写就往哪里写。该对象功能可以读数据可以写入数据如果写入位置已有数据会发生数据覆盖。也就是可以对数据进行修改。在使用该对象时建议数据都是有规则的。或者是分段的。注意该对象在实例化时如果要操作的文件不存在会自动建立。如果要操作的文件存在则不会建立如果存在的文件有数据。那么在没有指定指针位置的情况下写入数据会将文件开头的数据覆盖。可以用于多线程的下载也就是通过多线程往一个文件中同时存储数据。序列流。也称为合并流。SequenceInputStream:特点:可以将多个读取流合并成一个流。这样操作起来很方便。原理:其实就是将每一个读取流对象存储到一个集合中。最后一个流对象结尾作为这个流的结尾。JAVA改变生活两个构造函数:SequenceInputStream(InputStreamin,InputStreamin)可以将两个读取流合并成一个流。SequenceInputStream(Enumeration<extendsInputStream>en

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/31

JAVA_IO流学习总结

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利