[置顶] 黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

时间:2022-03-24 12:36:17

                                                                                 ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、 IO概述

(1) IO(Input Output)

IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式。

Java用于操作流的对象都在IO包中,流按操作分为两种:字节流与字符流。

流按流向分为:输入流,输出流。

(2) 基类

字节流的抽象基类:

    InputStream。OutPutStream。

字符流的抽象基类:

    Reader,Writer。

注:由这四个类派生出来的子类名称都是

以其父类名作为子类名的后缀。

  如:InputStream的子类FileInputStream

  如;Reader的子类FileReader。

(3) 字符流

既然IO流是用于操作数据的,那么数据最常见体现形式是:文件。

那么以操作文件为主来演示

需求:在硬盘上,创建一个文件并写入一些文字数据。

找到一个专门用于操作文件的Writer子类对象,FileWriter,

  后缀是父类名,前缀名是该流对象的功能。

               [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

(4) FileWriter类

      [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws Exception
{
//创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件
//该文件会被创建到指定目录下,如果该目录下已有同名文件,将被覆盖。
//其实该步就是在明确数据要存入的目的地
FileWriter fw = new FileWriter("demo.text");

//调用write方法,将字符串写入到流中
fw.write("absdnv");
//将数据刷入目的地中
fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,将数据刷到目的地中,
//flush和closed区别:flush刷新后,流可以继续使用,close刷新后,流会关闭。
//
fw.closed();
}
}

(5) IO 异常的处理方式

import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
FileWriter fw = null;
try
{
fw = new FileWriter("demo.txt");
fw.write("abbdfn");
}
catch (IOException e)
{
System.out.Println("catch:"+e.toString());
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.Println("catch:"+e.toString());
}
}
}
}

(6) 已有文件的数据的续写

           [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据的续写
FileWriter fw = new FileWriter("demo.txt",true);
fw.write("hh");
fw.close();
}
}

(7)Reader

           [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个文件读取流对象,和指定名称的文件相关联
//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr = new FileReader("Demo.txt");
//调用读取流对象的read方法
//read():一次读一个字符,而且会自动往下读。
int ch = 0;
while ((ch=fr.read())!=-1)
{
System.out.println((char))ch);
}
/*
while(true)
{
int ch = fr.read();
if(ch==-1)
break;
System.out.println((char))ch);
}
*/
fr.close;
}
}

 第二种方式

         [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class FileReaderDemo2
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("Demo.txt");
//定义一个字符数组,用于存储读到字符。
//该read(char [])返回的是读到字符个数。
char [] buf = new char[1024];
int num = 0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num));
}
fr.close();
}
}

练习读取一个.java文件,并打印在控制台上

import java.io.*;
class FileReaderDemo2
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("DateDemo.java");
char [] buf = new char[1024];
int num = 0;
while((num=fr.read(buf))!=-1)
{
System.out.print(new String(buf,0,num));
}

fr.close();
}
}

练习:将C盘一个文本文件复制到D盘

  复制原理:

  其实就是将C盘下的文件数据存储到D盘的一个文件

  步骤:

  1,在D盘创建一个文件,用于存储C盘文件中的数据

  2,定义读取流和C盘文件关联

  3,通过不断的读写完成数据存储

import java.io.*;
class CopyText
{
public static void main(String[] args) throws IOException
{
copy_1();
copy_2();
}
public static void copy_1() throws IOException
{
//创建目的地
FileWriter fw = new FileWriter("DateDemo_copy.txt");
//与已有文件关联
FileReader fr = new FileReader("DateDemo.java");

int ch = 0;
while((ch=fr.read())!-1)
{
fw.write(ch);
}
fw.close();
fr.close();
}
public static void copy_2() throws IOException
{
FileWriter = null;
FileReader = null;
try
{
fw = new FileWriter("DateDemo_copy.txt");
fr = new FileReader("DateDemo.java");
char [] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
if(fr!=null)
try
{
fr.close();
}
catch (IOException e)
{
}
if(fw!=null)
try
{
fw.close();
}
catch (IOException e)
{
}
}
}
}

(8) 字符流的缓冲区
对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用在流的基础上对流的功能,进行了增强

缓冲区的出现是为了提高流的操作效率而出现的所以在创建缓冲区之前,必须要先有流。

该缓冲区中提供了一个跨平台的换行符

newLine();

                [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class BufferWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符流写入对象
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入效率,加入了缓冲技术
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush();
}
//记住,只要用到缓冲区,就要记得刷新
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象
bufw.close();
}
}


(9) 字符流读取缓冲区:BufferedReader();

该缓冲区提供了一个一次读一行的方法 readLine(),方便于对文本数据的获取

当返回null时,表示读到文件末尾。

readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。

               [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联
FileWriter fw = new FileWriter("buf.txt");
//为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}


练习:通过缓冲区复制一个.java文件

import java.io.*;
class CopyTextByBuffalo
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader("Demo.java");
bufw = new BufferedWriter(new FileWriter("DemoCopy.txt");

String line = null;

while((line = bufr.readLine())!=null)
{
bufw.write(line);
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}

明白了BufferedReader类中特有方法readLine的原理后,

可以自定义一个类包含一个功能和readLine一致的方法

来模拟一下BufferRead

import java.io.*;
class MyBufferedReader
{
private FileReader r
MyBufferedReader(FileReader r)
{
this.r = r;
}
//可以一次读一行数据的方法
public String myReadLine() throws IOException
{
//定义一个临时容器,原BufferReader封装的是字符数组
//为了演示方便。定义一个StringBuilder容器,因为最终还是要将数据变成字符串
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch= r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line = myBuf.nyReadLine())!=null)
{
System.out.println(line);
}
myBuf.close();
}
}

(10)装饰设计模式:

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,

基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类。

装饰类通常会通过构造方法接收被装饰的对象。

并基于被装饰的对象的功能,提供更强的功能。

class Person
{
public void chifan()
{
System.out.println("吃饭");
}

}
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
public void superChifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
}
}
class
{
public static void main(String[] args) throws IOException
{
Person p = new Person();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}

MyReader 专门用于读取数据的类
     |---MyTextReader
    |---MyBufferTestReader
|---MyMediaReader
 |---MyBufferMediaReader  

class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader text)
{}
}
上面这个类扩展性很差,
找到其参数的共同类型,通过多态的形式,可以提供扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader 专门用于读取数据的类
     |---MyTextReader
|---MyMediaReader
|---MyBufferReader 
装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常都属于一个体系

LineNumber();

跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),

它们可分别用于设置和获取当前行号。 默认情况下,行编号从 0 开始。

该行号随数据读取在每个行结束符处递增,

并且可以通过调用 setLineNumber(int) 更改行号。但要注意的是,

setLineNumber(int) 不会实际更改流中的当前位置;

它只更改将由 getLineNumber() 返回的值。 

可认为行在遇到以下符号之一时结束:

换行符('\n')、回车符('\r')、回车后紧跟换行符。 

                [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("PersonDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
Lnr.setLineNumber(100);
while((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
}

字符流:

FileReader

FileWriter

BufferedReader

BufferedWriter

二、字节流
FileInputStream   

FileOutputStream

BufferedInputStream 

BufferedOutputStream

         [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

import java.io.*;
class FileInOutPutStreamDemo
{
public static void main(String[] args) throws IOException
{
writeFile();
readFile_1();
readFile_2();
readFile_3();
}
public static void writeFile() throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("abc".getBytes());
fos.close();
}
public static void readFile_1() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1)
{
System.out.println((char)ch);
}
fis.close();
}
public static void readFile_2() throws IOException
{
FileInputStream fis = new FileInputStream("fos.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();
}
public static void readFile_3() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
//int num = fis.available();
byte [] buf = new byte[fis.available()];//定义了一个刚刚好的缓冲区,不需要循环了。
//System.out.println("num="+num);
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
}

(1) 练习

复制一个图片

思路:

1,用字节流读取流对象和图片关联

2,用字节流写入流对象创建一个图片文件,用于存储获取到的图片数据

3,通过循环读写,完成数据的存储

4,关闭资源

import java.io.*;
class CopyPic
{
public static void main(String[] args) throws IOException
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
fos = new FileOutputStream("c:\\2.bmp");
fis = new FileInputStream("c:\\1.bmp");
byte [] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("复制文件失败");
}
finally
{
try
{
if (fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if (fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}



(2) 演示MP3的复制,通过缓冲区

BufferedOutputStream

BufferedInputStream

import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
copy_1();
copy_2()
}
public static void copy_1() throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\2.mp3"));

int by = 0;

while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
public static void copy_2() throws IOException
{
myBufferedInputStream bufis = new myBufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));

int by = 0;

while((by=bufis.read())!=-1)
{
bufos.write(by);
}

bufos.close();
bufis.close();
}
}
class MyBufferedInputStream
{
private InputStream in;
private byte [] buf = new byte[1024*4];
private int pos = 0; count = 0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区(字节数组)获取
public int myRead() throws IOException
{
//通过in对象读取硬盘上的数据,并存储buf中
if (count = 0)
{
count=in.read(buf);
if(count<0)
return -1;
pos = 0;
byte b = buf[pos];

count--;
pos++;
return b&255;
}
else if(count>0)
{
byte b = buf[pos];

count--;
pos++;
return b&255;
}
return -1;
}
public void myClose() throws IOException
{
in.close();
}
}

(3) 读取键盘录入

System.out:对应的是标准输出设备,控制台

System.in:对应的是标准输入设备,键盘

                [置顶]        黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律

需求:通过键盘录入数据。

当录入一行数据后,就将该行数据进行打印

如果录入的数据时over,那么停止录入。

import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true)
{
int ch = in.read();
if(ch=='\r')
continue;
if(ch=='\n')
{
String s= sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length())
}
else
sb.append((char)ch);
}
}
}

通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理,

也就是readLine()方法

能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

(4) readLine方法是字符流BufferedReader类中的方法。

而键盘录入的read方法是字节流InputStream的方法

那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?

import java.io.*;
class TransStreamDemo
{
public static void main(String[] args) throws IOException
{
//获取键盘录入对象
//InputStream in = System.in;
//将字节流对象转成字符流对象,使用转换流 InputStreamReader
//InputStreamReader isr = new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
//BufferedReader bufr = new BufferedReader(isr);
//键盘录入最常见写法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

//OutputStream out = System.out;
//OutputStreamWriter osw = new OutputStreamWriter(out);
//BufferedWriter bufw = new BufferedWriter(osw);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}

(5) 需求:想把键盘录入的数据存储到一个文件中
源:键盘
目的:文件

import java.io.*;
class TransStreamDemo2
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out.txt")));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}

(6) 需求:想要将一个文件的数据打印在控制台上
源:文件
目的:控制台

import java.io.*;
class TransStreamDemo3
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileOutputStream("out.txt")));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}

(7) 流操作的基本规律:
通过三个明确来完成
1,明确源和目的
源:输入流。InputStream   Reader
目的:输出流。OutputStream   Writer
2, 操作的数据是不是纯文本
是:字符流
        不是:字节流
3,当体系明确后,再明确要使用哪个具体的对象。
       通过设备来进行区分:
       源设备:内存,硬盘,键盘
       目的设备:内存,硬盘,控制台

(8)将一个文本文件中的数据存储到另一个文件中,复制文件
    源 :因为是源 所以使用读取流 InputStream Reader
  是不是操作文本文件
      是  这时就可以选择Reader
     这样体系就明确了
  接下来明确要使用该体系中的哪个对象
  明确设备:硬盘上一个文件
  Reader体系中可以操作文件的对象是FileReader
  是否需要提高效率: 是!加入Reader体系中缓冲区 BufferedReader
  FileReader fr = new FileReader("a.txt");
  BufferedReader bufr = new BufferedReader(fr);

    目的:OutputStream Writer
           是否是纯文本
   是  Writer
   设备:硬盘,一个文件
    Writer体系中可以操作文件的对象FileWriter
     是否需要提高效率: 是!加入Writer体系中缓冲区 BufferedWriter
    FileWriter fw = new FileWriter("b.txt");
     BufferedWriter bufw = new BufferedWriterr(fw);

练习 需求:将键盘录入的数据保存到一个文件中
            这个需求中有源和目的
         那么分别分析
源:InputStream Reader
是不是纯文本?  是,Reader
设备:键盘。对应的对象时System.in
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便,转成字节流按照字符串操作是最方便的
所以既然明确了Reader,那么就将System.in转成Reader
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗? 需要 BufferedReader
BufferedReader bufr = new BufferedReader(isr);

目的:OutputStream Writer
是否是纯文本?是 Writer
设备:硬盘。一个文件  使用FileWriter
FileWriter fw = new FileWriter(c.txt");
需要提高效率吗?需要
BufferedWriter  bufw = new BufferedWriter(fw);