IO流
01-IO流(输入流&输出流)
1、IO(Input Output)流:IO流用来处理设备之间的数据传输。
2、流,按操作数据分为两种:字节流、字符流。
3、流,按流向分氛围两种:输入流、输出流。
4、哪到哪是输入,哪到哪是输出?
把数据往内存里放,是输入。把数据从内存中取出,是输出。输入、输出是相对于内存而言的。
day20 02-IO流(字节流&字符流)
1、数据存储方式全都是以字节为单位的二进制。
2、很简单,字符流的底层就是字节流。而字符流主要是读取文本文件内容的,可以一个字符一个字符的读取,也可以一行一行的读取文本文件内容。而字节流读取单位为byte.byte作为计算机存储最基本单位,可以用字节流来读取很多其他格式的文件,比如图片视频等等。基于B/S和C/S的文件传输都可以采用字节流的形式。
你可以这样理解:字符比字节要大,也就是分别用这两流时,都作为最小的单位。
3、字符流的由来:其实就是字节流读取文字字节数据后,不直接操作,而是先查指定的编码表,获取对应的文字。再对这个文字进行操作。简单说就是:字节流+编码表。
4、FileReaderfr = new FileReader(“source.txt”);——字符流
InputStreamReaderisr = new InputStreamReader(new FileInputSTream(“source.txt”,”GBK”);——转换流
这两句话功能等同,一次可以进一步理解字符流做了什么。字符流确实是先查本地默认码表获得文字后再进行操作。
day20 03-IO流(字符流-FileWriter)
1、IO流框架图
2、IO流常用基类(这四个基类全是抽象类,都不能直接用)
字节流的两个顶层父类:InputStream、OutputStream
字符流的两个顶层父类:Reader、Writer
3、由这四各类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类——FileInputStream
如:Reader的子类——FileReader
4、需求,将文字"abcde"存储到硬盘的一个文件myText.txt中
5、使用类:
类FileWriter
用来写入字符文件的便捷类
构造方法摘要 |
|
FileWriter(File file) |
|
FileWriter(File file, boolean append) |
|
FileWriter(FileDescriptor fd) |
|
FileWriter(String fileName) |
|
FileWriter(String fileName, boolean append) |
|
6、代码示例:
public class AwtDemo18 {
//创建一个可以往文件中写入字符数据的字符输出流对象
/*
* 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)
* 如果文件不存在,则会自动创建。
* 如果文件存在,则会被覆盖。
*/
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("myText.txt");
//调用Writer对象中的write(String)方法,写入数据
fw.write("abcde");
/*
* 一运行,打开myText.txt,啥玩意没有,怎么回事?
* 其实数据写入到了临时存储缓冲区中。(通俗说,是把数据写到“流”里去了)
* 那我要东到目的地怎么办?进行刷新,将数据直接写到目的地中。
*/
//fw.flush();
//close会先刷新再关闭
fw.close();
}
}
运行结果:
系统中多了一个的txt文件,里面内容为。
7、flush()和close()的区别:
close()就是txt的关闭,flush()就是txt的保存。
你一点关闭,它让你保存一下。这正如同,你关闭流,但之前会先刷新他。对于流来说,flush()可以多次,close()就一次。你一关,这个流就拜拜了。想再写,你就得再new流。
8、java.io
类Writer
方法摘要 |
|
Writer |
append(char c) |
Writer |
append(CharSequence csq) |
Writer |
append(CharSequence csq, int start, int end) |
abstract void |
close() |
abstract void |
flush() |
void |
write(char[] cbuf) |
abstract void |
write(char[] cbuf, int off, int len) |
void |
write(int c) |
void |
write(String str) |
void |
write(String str, int off, int len) |
day20 04-IO流(字符流-FileWriter-细节(换行和续写))
1、需求1换行:现在要往一个文本文件中写入。
2、代码示例1:系统通用型不强
public class AwtDemo19 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("myText.txt");
fw.write("abcdefg\r\nhijklmn");
fw.close();
}
}
运行结果:哦了
3、但是存在这样一个问题。Windows的换行是\r\n,但是Linux的换行是\n。如果这个程序要跨平台,怎么办?方法就是,你这个系统的换行符是什么,我就拿什么。
4、代码示例2:可以跨平台跨系统
public class AwtDemo19 {
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("myText.txt");
fw.write("abcdefg"+LINE_SEPARATOR+"hijklmn");
fw.close();
}
}
5、需求2续写:现在要求在需求1完成的txt文件基础上(即前面一个流已经关闭了),再进行续写。写入。
6、如果仍然用:FileWriter fw = new FileWriter("myText.txt");那么原文件会被覆盖,达不到续写的目的。
7、FileWriter类中还有一个构造方法
FileWriter
(String fileName,boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
8、代码示例
public class AwtDemo19 {
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
//使用这个构造方法,表示如果有myText.txt,不覆盖这个文件。而是继续在这个文件中续写。
FileWriter fw = new FileWriter("myText.txt",true);
fw.write(LINE_SEPARATOR+"opqrst"+LINE_SEPARATOR+"uvwxyz");
fw.close();
}
}
运行结果:。
day20 05-IO流(字符流-FileWriter-IO异常处理)
1、一般只要进行读写,都会发生IO异常。
如下图,全是红色波浪线。
2、IO异常的简单处理方法
public class Demo20 {
public static void main(String[] args) {//若我在方法上不抛出IO异常,下面3句话全部红线
FileWriter fw = null;//①把命令放到外面是为了防止局部变量出了范围就没了。因为后面的代码块中还有fw
try{//②有可能在new对象时就出了问题
fw = new FileWriter("myText.txt");
fw.write("abcde");
}catch(IOException e){//③处理new对象与write()可能会抛的异常。
System.out.println(e.toString());
}
finally{//④close方法之所以放到finally中,是因为必须要关闭资源
if(fw!= null){//⑤加判断是为了防止空指向异常,因为有可能你new对象时,就完了。对象都没创建,你调用什么close方法
try {
fw.close();
} catch (IOException e) {//⑥处理close可能会抛出的异常
throw new RuntimeException("关闭失败");
}
}
}
}
}
day20 06-IO流(字符流-FileReader-读取方式1-一个字符一个字符的读)
1、需求:读取一个文件,将读取到的字符打印到控制台。
2、FileReader:用来读取字符文件的便捷类。
构造方法摘要 |
|
FileReader(File file) |
|
FileReader(FileDescriptor fd) |
|
FileReader(String fileName) |
|
方法摘要 |
|
abstract void |
close() |
void |
mark(int readAheadLimit) |
boolean |
markSupported() |
int |
read() |
int |
read(char[] cbuf) |
abstract int |
read(char[] cbuf, int off, int len) |
int |
read(CharBuffer target) |
boolean |
ready() |
void |
reset() |
long |
skip(long n) |
3、假设现在有一个demo.txt的文件,里面的内容:abcd
4、代码示例1——读取1个字符
public class Demo49 {
public static void main(String[] args) throws IOException {
//创建读取字符数据的流对象
/*
* 在创建读取流对象时,必须要明确被读取的文件,一定要确定该文件是存在的。
* 用一个读取流关联一个已存在的文件
*/
FileReader fr = new FileReader("demo.txt");
//读取单个字符
int ch = fr.read();
System.out.println(ch);
fr.close();
}
}
运行结果:97
5、代码示例2——连续读取单个字符
public class Demo49 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){
System.out.println(ch);
}
fr.close();
}
}
运行结果:97
98
99
100
6、类Reader中的read方法
public int read()throws IOException
读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。
用于支持高效的单字符输入的子类应重写此方法。
返回:
作为整数读取的字符,范围在0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1
抛出:
IOException - 如果发生 I/O 错误
7、示意图
day20 07-IO流(字符流-FileReader-读取方式2-先把字符读到缓冲区再从缓冲区拿)
1、类Reader
public int read(char[] cbuf) throwsIOException
将字符读入数组。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。
参数:
cbuf - 目标缓冲区
返回:
读取的字符数,如果已到达流的末尾,则返回 -1
抛出:
IOException - 如果发生 I/O 错误
2、代码示例1——理解read(char[])的工作方式
public class Demo50 {
public static void main(String[] args) throws IOException {
// TODO Auto-generatedmethod stub
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据
* 先创建字符数组
*/
char[] buf = new char[3];
int num = fr.read(buf);//将读取到的字符存储到数组中
System.out.println(num+":"+new String(buf));
int num1 = fr.read(buf);
System.out.println(num1+":"+new String(buf));
int num2 = fr.read(buf);
System.out.println(num2+":"+new String(buf));
}
}
运行结果:3:abc
1:dbc
-1:dbc
3、图解read(char[])方法
4、代码示例2——利用缓冲区连续进行读取
public class Demo51 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
char[] buf = new char[3];
int len = 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
运行结果:abc
d
5、两种读取方式,哪种好?
利用缓冲区更好。循环次数少。我吃花生,原来一个一个抓着吃,后来我搞一勺,一勺好几个的吃。明显拿勺子吃更快。
6、定义数组作为缓冲区的时候,长度定多少合适?
最好就是1024的整数倍。
day20 08-IO流(字符流-练习-复制文本文件1)
1、需求:将一个文本文件复制一份。
2、思路:
①这个问题的本质就是连续读写。
②需要读取源。
③将读到的源数据写入到目的地。
④既然是操作文本数据,那么使用字符流。
3、步骤:
①读取一个已有的文本文件,使用字符读取流与文件相关联。
②创建一个目的,用于存储读到的数据。
③频繁的读写操作。
④关闭流资源。
4、代码示例1——读一个字符写一个字符
public class Demo53 {
public static void main(String[] args) throws IOException {
// TODO Auto-generatedmethod stub
//1、读取一个已有的文本文件,使用字符读取流和文件相关联。
FileReader fr = new FileReader("demo_GB.txt");
//2、创建一个目的,用于存储读到的数据。
FileWriter fw = new FileWriter("demo3.txt");
//3、频繁的读写操作。
int ch = 0;
while((ch=fr.read())!=-1){
fw.write((char)ch);
}
//4、关闭流资源。
fr.close();
fw.close();
}
}
day20 09-IO流(字符流-练习-复制文本文件2)
1、代码示例2——先读进缓冲数组,再从数组往文件里写
public class Demo54 {
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader("demo.txt");
fw = new FileWriter("demo3.txt");
//创建一个临时容器,用于缓存读取到的字符
char[] buf = new char[BUFFER_SIZE];
//定义一个变量记录读取到的字符数,(其实也是往数组里装的字符个数)
int len = 0;
while((len = fr.read(buf))!=-1){
fw.write(buf,0,len);
}
}catch(IOException e){
System.out.println(e.toString());
}
finally{
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2、相对来说,代码示例2更好,因为效率更高。
3、两种方式的区别图解
day21 11-IO流(字符流-缓冲区-解释)
1、缓冲区的出现提高了对数据的读写效率。
没有缓冲区,读一次写一次,磁头的切换频率很高。
有缓冲区,先一通读,再一通写。不用来回进行切换,效率更高。
2、BufferedWriter与BufferedReader工作情况图解
day21 12-IO流(字符流-缓冲区-BufferedWriter)
1、类BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够了。
构造方法摘要 |
|
BufferedWriter(Writer out) |
|
BufferedWriter(Writer out, int sz) |
|
方法摘要 |
|
void |
close() |
void |
flush() |
void |
newLine() |
void |
write(char[] cbuf, int off, int len) |
void |
write(int c) |
void |
write(String s, int off, int len) |
2、代码示例1——不用缓冲区,效率低下
public class Demo55 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("demo.txt");
fw.write("abcdef");
fw.close();
}
}
3、代码示例2——使用缓冲区包装类
public class Demo56 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("demo1.txt");
//为了提高写入的效率,使用了字符流的缓冲区。
//创建了一个字符写入流的缓冲区对象。
BufferedWriter bufw = new BufferedWriter(fw);
//使用缓冲区的下入方法,将数据先写入到缓冲区中。
bufw.write("abcdef");
//使用缓冲区的刷新方法将数据刷到目的地中。
bufw.flush();
//关闭缓冲区
bufw.close();
}
}
4、问,这里写了bufw.close();还需要再写fw.close();吗?
答:不用了!关缓冲区的时候,流也被关掉了。缓冲区仅仅起到了一个提高效率的功能,他没有调用底层资源。真正调用底层资源的是流。BufferedWriter里面的close()方法,写的其实就是:
void close(){
fw.close();
}
5、BufferedWriter类中换行,除了write(System.getProperty("line.separator"))之外。还有另一个方式,就是BufferedWriter的newLine()方法。注意newLine方法只在这个地方具备,别的地方不具备。
6、代码示例1——newLine方法换行
public class Demo57 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("demo5.txt");
BufferedWriter bufw = new BufferedWriter(fw);
bufw.write("abcdefg");
bufw.newLine();
bufw.write("higklmn");
bufw.flush();
bufw.close();
}
}
运行结果:
7、注意:虽然close()带着刷新功能(close关闭此流,但要先刷新它),但是close不能替代flush方法。
8、代码示例1——close不能替代flush方法
day21 13-IO流(字符流-缓冲区-BufferedReader)
1、类BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
构造方法摘要 |
|
BufferedReader(Reader in) |
|
BufferedReader(Reader in, int sz) |
|
方法摘要 |
|
void |
close() |
void |
mark(int readAheadLimit) |
boolean |
markSupported() |
int |
read() |
int |
read(char[] cbuf, int off, int len) |
String |
readLine() |
boolean |
ready() |
void |
reset() |
long |
skip(long n) |
2、代码示例1——BufferedReader类以及readLine方法
文件demo.txt——
public class Demo60 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo01.txt");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line = bufr.readLine())!=null){
System.out.println(line);
}
bufr.close();
}
}
day21 14-IO流(字符流-缓冲区-BufferedReader-readLine方法原理)
1、查看BufferedReader类中的方法摘要,发现他覆盖了int read()方法,但却没有覆写int read(char[] cbuf)。
2、底层流对象,它从硬盘往外读。缓冲区对象操作的都是缓冲区中的内容。
3、由上图可知,readLine方法使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记。将标记前的缓存数据变成字符串返回。
4、缓冲区其实就是在内存当中定义的容器,把硬盘上或者源中的数据,抓一部分放到缓冲区里边来。然后,我们以后再操作的时候,就不再一个个操作硬盘,而是操作内存。取完了,再抓一批进内存。
day21 15-IO流(字符流-缓冲区-复制文本文件)
1、需求:利用缓冲包装类来复制一个文件。
2、代码示例1——从缓冲区一次只读取一个字符
public class Demo61 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);
int ch = 0;
while((ch = bufr.read())!=-1){
bufw.write(ch);
}
bufw.close();
bufr.close();
}
}
3、代码示例2——从缓冲区一次读取一行字符
public class Demo62 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy2.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while((line = bufr.readLine())!=null){
bufw.write(line);
bufw.newLine();
}
bufr.close();
bufw.close();
}
}
day21 16-IO流(字符流-缓冲区-自定义MyBufferedReader-read方法)
1、我想自己实现BufferedReader中的read方法。
2、自定义的读取缓冲区,其实就是模拟一个BufferedReader。
分析:缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。其实这些方法最终操作的都是数组的角标。
缓冲的原理:其实就是从源中获取一批数据装进缓冲区中。再从缓冲区中不断的取出一个个数据。在此次取完后,再从源中继续取一批数据进缓冲区。当源中的数据取光时,用-1作为结束标记。
3、java.io
类BufferedReader
方法摘要
int read()//这一节就是要模拟这个方法
读取单个字符。(注意是单个!从缓冲区中一次只读取一个字符。别一个循环全给搞完了。读完一个,下次读的时候,他会再读下一个。)
4、代码示例:模拟BufferedReader中的read方法。
day21 17-IO流(字符流-缓冲区-自定义MyBufferedReader-readLine方法)
day21 18-IO流(字符流-缓冲区-装饰设计模式)
1、缓冲区的出现,将源中的数据存储到了我们自定义的数组里边,进行了缓存。并对数组进行操作,而提高了性能。BufferedReader类的出现,对FileReader类进行了功能上的增强。这种增强在设计模式中也有一种体现,叫做装饰设计模式。
2、装饰设计模式:对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
3、打死都不要过分修改代码,那是灾难。
day21 19-IO流(字符流-缓冲区-装饰设计模式和继承的区别)
1、装饰和继承都能实现一样的特点:进行功能的扩展增强。那么他们有什么区别呢?
2、装饰和继承的区别分析
首先,我们这里有一个继承体系:
Writer
|-TextWriter:用于操作文本
|-MediaWriter:用于操作媒体
想要对操作的动作进行效率的提高。按照面向对象,可以通过继承对具体对象进行功能的扩展。比如,我现在需要提高效率,来加入缓冲技术。
Writer
|-TextWriter:用于操作文本。
|-BufferedTextWriter:加入了缓冲技术的操作文本的对象。
|-MediaWriter:用于操作媒体。
|-BufferedMediaWriter:加入了缓冲技术的操作媒体的对象。
但这么做好像并不理想。如果这体系进行功能扩展,又多了。那么是不是要继续产生子类呢?是的!这时就会发现,只为提高功能而进行的继承,导致继承体系越来越臃肿,不够灵活。
我们重新思考这个问题。
既然加入的都是同一种技术——缓冲。前一种是让缓冲和具体的对象相结合。可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲相关联。
如下图示意:
以后,我们的体系就变成这样:
Writer
|-TextWriter:用于操作文本。
|-MediaWriter:用于操作媒体。
|-BufferedWriter:用于提高效率。
3、装饰比继承更为灵活。
4、特点:装饰类和被装饰类都必须所属同一个接口或者父类。
day21 20-IO流(字符流-缓冲区-LineNumberReader)
1、类LineNumberReader
跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int)和getLineNumber(),它们可分别用于设置和获取当前行号。
默认情况下,行编号从 0 开始。该行号随数据读取在每个行结束符处递增,并且可以通过调用 setLineNumber(int) 更改行号。
构造方法摘要 |
|
LineNumberReader(Reader in) |
|
LineNumberReader(Reader in, int sz) |
|
方法摘要 |
|
int |
getLineNumber() |
void |
mark(int readAheadLimit) |
int |
read() |
int |
read(char[] cbuf, int off, int len) |
String |
readLine() |
void |
reset() |
void |
setLineNumber(int lineNumber) |
long |
skip(long n) |
2、代码示例1:LineNumberReader是装饰类,有装饰类,那就得有被装饰类。另外注意LineNumberReader继承了Bufferedreader。
public class Demo41 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("MyText.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
while((line = lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
}
3、代码示例2——自定义行号开始的数字
public class Demo41 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("MyText.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);
while((line = lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
}
day21 21-IO流(字节流-操作文件基本演示)
1、字节流:他操作的单位是字节,基本操作与字符流类相同。但他不仅可以操作字符,还可以操作其他媒体文件。
2、字节流与字符流的区别:①无非是它操作的单位不一样。②处理的数据格式也不一样。③字符流只能处理文字。
3、字节流的两个抽象基类
(1)
构造方法摘要 |
|
InputStream() |
|
方法摘要 |
|
int |
available() |
void |
close() |
void |
mark(int readlimit) |
boolean |
markSupported() |
abstract int |
read() |
int |
read(byte[] b) |
int |
read(byte[] b, int off, int len) |
void |
reset() |
long |
skip(long n) |
(2)
构造方法摘要 |
|
OutputStream() |
|
方法摘要 |
|
void |
close() |
void |
flush() |
void |
write(byte[] b) |
void |
write(byte[] b, int off, int len) |
abstract void |
write(int b) |
4、代码示例1——利用字节流写数据
public class TestDemo16 {
public static void main(String[] args) throws IOException {
//1、创建字节输出流对象,用于操作文件
FileOutputStream fos = new FileOutputStream("demo.txt");
//2、写数据。注意,你这里只写"abcdefg"不行!字节流操作字节,你得转化成字节数组。
//3、直接写入到了目的地中。字节流不用flush。FileOutputStream继承了父类的flush方法,但在方法代码中什么都没写。
fos.write("abcdefg".getBytes());
//但是字节流是需要close的。
fos.close();
}
}
5、代码示例2——利用字节流读数据
public class TestDemo17 {
public static void main(String[] args) throws IOException {
//1、创建一个读取流对象,和指定文件关联
FileInputStream fis = new FileInputStream("MyText.txt");
int ch = 0;
while((ch = fis.read())!=-1){
System.out.println((char)ch);
}
fis.close();
}
}
6、代码示例3——手动建立一个缓冲区
public class Demo42 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("MyText.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fis.close();
}
}
7、下面介绍一个方法:
int |
available() |
这个方法,你可以在new缓冲区的时候调用。他会拿所要读取的源文件一样内存大小的数值。
我在定义字节数组时可以写:byte[] buf = new byte[fis.available()];这个数组大小刚刚好,就是你要读取的文件大小。
但是这个方法少用。万一我关联了一个2G多的电影,你也去new一个2G大的数组?直接内存溢出!
所以,在读小文件的时候你可以用。另外,再读文件前你可以先获取这个文件的大小,再分段对这个文件进行读取。
day21 22-IO流(字节流-练习-复制MP3)
day22 23-IO流(演示键盘录入)
1、需求:读取一个键盘录入的数据,并打印在控制台上。
2、
字段摘要 |
|
static PrintStream |
err |
static InputStream |
in |
static PrintStream |
out |
对于键盘录入,使用的就是in这个字段摘要。
3、代码示例——键盘输入一个打印一个
public class Demo43 {
public static void main(String[] args) throws IOException {
InputStream in = System.in;
int ch1 = in.read();
System.out.println(ch1);
int ch2 = in.read();
System.out.println(ch2);
int ch3 = in.read();
System.out.println(ch3);
}
}
4、以上方法为什么没有关流?in.close()
从系统获取的流对象,就in一个。你把in一关,再造个in2。不行!没有了。这与其他流对象不同。所以这东西尽量别关,它会随着系统的消失而消失,随着系统的出现而出现。记住,默认的输入设备和默认的输出设备都不需要关。你关了,就再也获取不到了。除非你的系统关掉,重新再启动,才能获取。
day22 24-IO流(读取键盘录入)
1、需求:获取用户键盘录入的数据,并将数据变成大写显示在控制台上。如果用户输入的是over,结束键盘录入。
2、思路:
(1)因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
(2)那就需要一个容器,StringBuilder
(3)在用户回车之前,将录入的数据变成字符串判断即可。
3、代码示例
public class Demo44 {
public static void main(String[] args) throws IOException {
//1、创建容器
StringBuilder sb = new StringBuilder();
//2、获取键盘读取流
InputStream in = System.in;
//3、定义变量记录读取到的字节,并循环获取
int ch = 0;
while((ch = in.read())!=-1){
//在存储之前需要判断是否是换行标记,因为换行标记不存储
if(ch == '\r'){
continue;
}
if(ch == '\n'){
String temp = sb.toString();
if("over".equals(temp)){
break;
}
System.out.println(temp.toUpperCase());
sb.delete(0,sb.length());
}else{
//将读取到的字节存储到StringBuilder中
sb.append((char)ch);
}
}
}
}
4、结束录入,一般不是点Ctrl+C,而是自定义结束标记。
day22 25-IO流(转换流)
1、上节中的方法很像readLine方法。但字节流不具备readLine方法。readLine方法是BufferedReader的方法。是字符流缓冲区的方法,而这是字节流。
2、能不能把字节流转换成字符流?
3、类InputStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
构造方法摘要 |
|
InputStreamReader(InputStream in) |
|
InputStreamReader(InputStream in,Charset cs) |
|
InputStreamReader(InputStream in,CharsetDecoder dec) |
|
InputStreamReader(InputStream in,String charsetName) |
|
方法摘要 |
|
void |
close() |
String |
getEncoding() |
int |
read() |
int |
read(char[] cbuf, int offset, int length) |
boolean |
ready() |
4、代码示例——重写上节中的需求
public class Demo45 {
public static void main(String[] args) throws IOException {
//字节流
InputStream in = System.in;
//将字节流转换成字符流的桥梁——转换流
InputStreamReader isr = new InputStreamReader(in);
//字符流
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while((line = bufr.readLine())!=null){
if("over".equals(line)){
break;
}
System.out.println(line.toUpperCase());
}
}
}
5、字节流操作与字符流操作的对比,观察区别。文本文件中只有一个“你”字。
6、代码示例1——字节流读取两次
public class TestDemo18 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("test.txt");
System.out.println(fis.read());
System.out.println(fis.read());
}
}
运行结果:196
227
“你”这个字的GBK(简中Window默认码表)码为C4E3。E3(227),C4(196)。可见,字节流内部不做任何编码转换。
7、代码示例2——字符流读取一次
public class TestDemo19 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader fir = new InputStreamReader(fis);
System.out.println(fir.read());
}
}
运行结果:20320
“你”这个字的unicode码为4F60,即20320(十进制)。
8、有字节转化成字符的桥梁,那么就应该有字符转化成字节的桥梁。
9、类OutputStreamWriter
OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
构造方法摘要 |
|
OutputStreamWriter(OutputStream out) |
|
OutputStreamWriter(OutputStream out,Charset cs) |
|
OutputStreamWriter(OutputStream out,CharsetEncoder enc) |
|
OutputStreamWriter(OutputStream out,String charsetName) |
|
方法摘要 |
|
void |
close() |
void |
flush() |
String |
getEncoding() |
void |
write(char[] cbuf, int off, int len) |
void |
write(int c) |
void |
write(String str, int off, int len) |
10、什么是编码与解码?
编码:将你的文字(字符)编成电报码(字节)。即:把看得懂的编成看不懂的。
解码:解析电报码(字节)成文字(字符)。即:把看不懂的,编成看得懂的。解密
11、需求:键盘上输入一行,回车一下就转换成大写打印到控制台上。输入over,结束程序。
12、代码示例:
public class TestDemo20 {
public static void main(String[] args) throws IOException {
//字节流
InputStream in = System.in;
//将字节转化成字符的桥梁,转换流。
InputStreamReader isr = new InputStreamReader(in);
//字符流
BufferedReader bufr = new BufferedReader(isr);
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line)){
break;
}
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
}
13、转换方向
14、举个例子,我从硬盘中读了一堆文件,硬盘中的文件是字节。我读到的是“你好”,这是字符。读完我想写的时候,“你好”是字符,我写到硬盘上是字节。字节→字符,字符→字节。
15、类OutputStreamWriter是FileWriter的爹。FileWriter操作文本文件,它本身读到的是字节,而出来的却是字符。那是因为字节到字符间做了转化。FileWriter用的就是他爹的方法。
16、下面这句话背过,一提到键盘录入就直接写这句话:
BufferedReader bufr=new BufferedReader(newInputStreamReader(System.in));
day22 26-IO流(转换流——需求演示)
1、需求1:将键盘录入的数据写入到一个文件中,输入over停止写入。且over不写入到文件中去。
2、代码示例1
public class TestDemo20 {
public static void main(String[] args) throws IOException {
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt")));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line)){
break;
}
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
}
3、需求2:将一个文本文件内容显示在控制台上。
4、代码示例2
只需要改俩地方即可完成:
(1)、BufferedReader bufr = new BufferedReader(newInputStreamReader(new FileInputStream("a.txt")));
(2)、BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(System.out));
5、需求3:将一个文本文件中的内容复制到另一个文本文件中。
6、代码示例3
只需要改俩地方即可完成
(1)、BufferedReader bufr = new BufferedReader(newInputStreamReader(new FileInputStream("a.txt")));
(2)、BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(new FileOutputStream("b.txt")));
day22 27-IO流(流的基本操作规律)
1、流的基本操作规律:
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。想要知道开发时用到那些对象。只要通过4个明确即可。
①、明确源和目的
源:InputStream、Reader
目的:OutputStream、writer
②、明确数据是否是纯文本数据。是不是纯文本就看内容是不是纯文字。
源:是纯文本:Reader
否 :InputStream
目的:是纯文本:Writer
否 :OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
③、明确具体的设备
源设备:硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:硬盘:File
控制台:System.out
内存:数组
网络:Socket流
④、是否需要其他额外功能(各种装饰类)
是否需要高效(缓冲区)
是:就加上Buffered
day22 28-IO流(流的基本操作规律-需求体现1)
1、需求1:复制一个文本文件
1)明确源和目的
源:InputStream、Reader
目的:OutputStream、Writer
2)是否是纯文本
是!源:Reader
目的:Writer
3)明确具体设备
源:硬盘:File
目的:硬盘:File
FileReader fr= new FileReader("a.txt");
FileWriter fw= new FileWriter("b.txt");
4)需要额外功能吗
需要,需要高效
BufferedReaderbufr = new BufferedReader(new FileReader("a.txt"))
BufferedWriterbufw = new BufferedWriter(new FileWriter("b.txt"))
2、需求2:读取键盘录入信息,并写入到一个文件中。键盘录入→文本文件
1)明确源和目的
源:InputStream、Reader
目的:OutputStream、Writer
2)是否是纯文本
是!源:Reader
目的:Writer
3)明确设备
源:键盘:System.in
目的:硬盘:File
InputStream in= System.in;
FileWriter fw= new FileWriter("b.txt");
4)需要额外功能吗
需要。将字节流转换成字符流。因为明确的源是Reader,这样操作文本数据更便捷。所以要将已有的字节流转成字符流。InputStreamReader
InputStreamReaderisr = new InputStreamReader(System.in);
FileWriter fw= new FileWriter("b.txt");
还需要功能吗?需要。高效!
BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in))
BufferedWriterbufw = new BufferedWriter(new FileWriter("b.txt"))
day22 29-IO流(流的基本操作规律-需求体现2)
1、需求3:将一个文本文件数据显示在控制台上。文本文件→控制台
1)明确源和目的
源:InputStream、Reader
目的:OutputStream、Writer
2)是否是纯文本
是!源:Reader
目的:Writer
3)明确具体设备
源:硬盘:File
目的:控制台:System.out
FileReader fr= new FileReader("a.txt");
OutputStreamout = System.out;
4)需要额外功能吗
需要:转换流
FileReader fr= new FileReader("a.txt");
OutputStreamWriterosw = new OutputStreamWriter(System.out);
还需要吗?需要,高效
BufferedReaderbufr = new BufferedReader(new FileReader("a.txt"))
BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out))
2、需求4:读取键盘录入数据,显示在控制台上。键盘录入→控制台
1)明确源和目的
源:InputStream、Reader
目的:OutputStream、Writer
2)是否是纯文本
是!源:Reader
目的:Writer
3)明确设备
源:键盘:System.in
目的:控制台:System.out
InputStreamin = System.in;
OutputStreamout = System.out;
4)需要额外功能吗
需要转换,因为前面已经明确了是Reader和Writer。所以要把字节流转换成字符流。
InputStreamReaderisr = new InputStreamReader(System.in);
OutputStreamWriterosw = new OutputStreamWriter(System.out);
还需要吗?需要!高效。
BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));