Java基础---IO流(上)

时间:2022-06-12 11:49:23

IO流对象关系图 Java基础---IO流(上)
其他常用与流有关的对象 Java基础---IO流(上)
IO流 IO流用来处理设备之间的数据传输。Java对数据的操作是通过流的方式。Java用于操作流的对象都在IO包中。 输入流和输出流相对于内存设备而言。 IO流:用于处理设备上的数据。设备:硬盘,内存,键盘录入。 IO有具体的分类: 1、根据处理的数据类型不同:字节流和字符流。 2、根据流向不同:输入流和输出流。 字符流的由来:因为文件编码不同,而有了对字符进行高效操作的字符流对象。原理:其实就是基于字节流读取字节时,去查了指定的码表。 字节流和字符流的区别: 1、字节流读取时候,读到一个字节就返回一个字节。字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在utf-8码表中是3个字节)时。先去查指定的编码表,将查到的字符返回。 2、字节流可以处理所有类型数据,如图片,avi。而字符流只能处理字符数据。 结论:只要是处理纯文本数据,就要优先考虑使用字符流。除此之外都是用字节流。 IO的体系。所具备的基本功能就有两个:读 和 写。 1、子节流: InputStream(读),OutputStream (写)。 2、字符流:Reader(读),Writer(写)。 字符流 Reader      |--InputStreamReader                |--FileReader:专门用于处理文件的字符读取流对象。 Writer      |--OutputStreamWriter                |--FileWriter:专门用于处理文件的字符写入流对象。 Reader中的常见方法: 1、int read():读取一个字符。返回的是读到的那个字符。如果读到流的末尾,返回-1 2、int read(char[]):将读到的字符存入指定的数组中,分那会的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1 3、close():读取到字符其实用的是window系统的功能,就希望使用完毕后,进行资源释放。 Writer中常见的方法: 1、write(char):将一个字符写入到流中 2、write(char[]):将一个字符数组写入到流中 3、write(String):将一个字符串写入到流中 4、flush():刷新流,将流中的数据刷新到目的地中,流还存在。 5、close():关闭资源:在关闭前会先调用flush(),刷新流中的数据去目的地。然流关闭。 FileWriter: 该类没有特有的方法。只有自己的构造函数。该类的特点在于 1、用于处理文本文件。 2、该类中有默认的编码表, 3、该类中有临时缓冲。 构造函数:在写入流对象初始化时,必须要有一个存储数据的目的地。 FileWriter(String filename):该构造函数做了什么事情呢? 1、调用系统资源。 2、在指定位置,创建一个文件。注意:如果该文件已经存在,将会被覆盖。 FileWriter(String filename,boolean append):该构造函数:当传入boolean类型为true时,会在指定文件末尾进行行数数据的续写。 小细节:当指定绝对路径时,定义目录分隔符有两种方式: 1、反斜线 但是一定要写两个。\\  new FileWriter("c:\\demo.txt"); 2、斜线 /写一个即可 new FileWriter("c:/demo.txt"); FileReader: 1、用于读取文本文件流对象。2、用于关联文本文件。 构造函数:在读取流对象初始化的时候,必须要指定一个被读取的文件。如果该文件不存在会发生FileNotFoundException。FileReader(String filename);
class Demo 
{
public static void main(String[] args) throws IOException
{
FileWriter fw = new FileWriter("demo.txt");
fw.write("abcde");
fw.flush();
fw.write("kkkk");
fw.close();
}
}
示例1:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个可以往文件中写入字符串数据的字符输出对象
//既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)
//如果文件不存在,则会自动创建
//如果文件存在,则会被覆盖
FileWriter fw = new FileWriter("demo.txt");

//调用Writer对象中的write(string)方法,写入数据
fw.write("abcde");
//进行刷新,将数据直接写入目的地中
fw.flush();
//关闭流,关闭资源,在关闭前会先调用flush刷新缓冲中的数据到目的地。
fw.close();
}
}
Java基础---IO流(上)
P.S.
1. close方法只能用一次。
2. 流关闭以后不能,不能再调用write方法,否则会报如下异常错误:
示例2:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
FileWriter fw = new FileWriter("demo.txt",true);
fw.write("xixi");
fw.close();


}
}
Java基础---IO流(上)
IO流的异常处理方式:为防止代码异常导致流无法关闭,因此在finally中对流进行关闭。示例3:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw = null;
try
{
fw = new FileWriter("demo.txt");
fw.write("abcde");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
fw.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭失败");
}
}
}
}
需求:读取一个文本文件,将读取到的字符打印到控制台。(使用FileReader)
第一种读取方式:使用read()方法读取文本文件数据。
示例4:
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt");
//用Reader中的read方法读取字符串
int ch = 0;
while((ch = fr.read())!=-1)
{
System.out.println((char)ch);
}
fr.close();
}
}
Java基础---IO流(上)
第二种读取方式:使用read(char[])方法读取文本文件数据。
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt");
//使用read(char[])读取文本文件数据
//先创建字符数组
char[] buf = new char[3];
int len = 0;
while((len = fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
Java基础---IO流(上)
将d盘的一个文本文件复制到d盘 示例5:方式1:使用read()读取文本文本文件数据。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTestDemo
{
public static void main(String[] args) throws IOException
{
//1、读取一个已有的文本文件,使用字符读取流和文件相关联
FileReader fr = new FileReader("demo.txt");
//2、创建一个目的,用于存储读到的数据
FileWriter fw = new FileWriter("copyText_1.txt");

//3、频繁的读写操作
int ch = 0;
while((ch=fr.read())!=-1)
{
fw.write(ch);
}
//4、关闭字节流
fw.close();
fr.close();
}
}
Java基础---IO流(上)
方式2:使用read(char[])读取文本文本文件数据。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTestDemo
{
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("copytest_2.txt");
//创建一个临时容器,用于缓存读取到的数据
char[] buf = new char[BUFFER_SIZE];

//定义一个变量记录读取到的字符数(其实就是往数组里装字符个数)
int len = 0;
while((len = fr.read(buf))!= -1)
{
fw.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败!");
}
finally
{
if(fw!=null)
{
try
{
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
if(fr!=null)
{
try
{
fr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
}
字符流的缓冲区
缓冲区的出现提高了对数据的读写效率。
原理:其实就是数组进行封装。对应的对象:BufferedWriter 特有方法:newLine():跨平台的换行符。BufferedReader 特有方法:readLine():一次读一行,到行标记时,将行标记之前的字符串返回。当读到末尾时们,返回null。在使用缓冲区对象时,要明确,缓冲的窜在是为了增强流的功能而存在,所有在建立缓冲区对象时,要先有流对象的存在。其实缓冲内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储。为了提高操作数据的效率。代码上的体现:写入缓冲区对象//建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));bufw.write("abce");//将数据写入到了缓冲区。bufw.flush();//对缓冲区的数据进行刷新。将数据刷到目的地中。bufw.close();//关闭缓冲区,其实关闭的是被包装在内部的流对象。读取缓冲区对象。BufferedReader bufr = new BufferedReader(new FileReader("buf.txt"));String line = null;//按照行的形式取出数据。取出的每一个行数据不包含回车符。while((line = bufr.readLine())!=null){System.out.println(line);}bufr.close();  示例6:(提高写入效率,使用缓冲区)
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTestDemo
{
public static void main(String[] args) throws IOException
{
FileWriter fw = new FileWriter("buf.txt");
//为了提高写入的效率,使用了字符流的缓冲区
//创建了一个字符写入流的缓冲区对象,并且指定与被缓冲的流对象相关联
BufferedWriter bufw = new BufferedWriter(fw);
for(int x = 1;x<=4;x++)
{
bufw.write("abcde"+x);
//写入内容换行方法:newLine();
bufw.newLine();
//bufw.flush();
}
//使用缓冲区的刷新方法将数据刷目的地中
bufw.flush();
//关闭缓冲区,其实关闭的就是被缓冲的流对象
fw.close();
}
}
Java基础---IO流(上)
readLine():方法的原理:其实缓冲区中的该方法,用的还是缓冲区关联的流对象的read方法。只不过,每次读到一个字符,先不进行具体操作,先进行临时存储。当读取到回车标记时,将临时容器中存储的数据一次性返回。示例7:(提高读取效率,使用缓冲区)
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
class BufferedReadDemo
{
public static void main(String[] args) throws IOException,FileNotFoundException
{
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line = bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}
Java基础---IO流(上)
示例8:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
public class CopyTextBufTest
{
public static void main(String[] args) throws Exception
{
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);

FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);

String line = null;
//方式一
while((line = bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}

//方式二
/*int ch = 0;
while((ch = bufr.read())!=-1)
{
bufw.write(ch);
}*/

bufr.close();
bufw.close();
}
}
Java基础---IO流(上)
LineNumberReader
跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
示例1:
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class LineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("LineNumberReaderDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(1000);
while((line = lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
}
Java基础---IO流(上)
装饰设计模式对原有类进行了功能的改变,增强。示例1:
class Person
{
void chifan()
{
System.out.println("吃饭");
}
}

//采用装饰的方式增强Person类
//这个类的出现是为了增强Person而出现的
class NewPerson
{
private Person p;
NewPerson(Person p)
{
this.p = p;
}

public void chifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
}
}

//采用继承的方式增强Person类
class NewPerson2 extends Person
{
public void chifan()
{
System.out.println( "开胃酒");
super.chifan();
System.out.println( "甜点");
}
}

public class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
NewPerson np1 = new NewPerson(p);
np1.chifan();
}
}
Java基础---IO流(上)
装饰和继承都能实现一样的特点:进行功能的扩展增强。有什么区别呢?
首先有一个继承体系:
Writer
      |--TextWriter:用于操作文本
      |--MediaWriter:用于操作媒体
如果想要对操作的动作进行效率的提高,按照面向对象,可以通过继承的方式对具体的对象进行功能的扩展,那么就需要加入缓冲技术。
Writer
      |--TextWriter:用于操作文本
             |--BufferTextWriter:加入了缓冲技术的操作文本的对象
      |--MediaWriter:用于操作媒体
             |--BufferMediaWriter:加入了缓冲技术的操作媒体的对象
以上方式并不理想,如果这个体系需要再进行功能扩展,又多了更多流对象。这样就会发现只为提高功能,导致继承体系越来越臃肿,不够灵活。
重新思考问题:
既然加入的都是同一种技术--缓冲。前一种是让缓冲和自己的流对象相结合。可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
Writer
      |--TextWriter:用于操作文本
      |--MediaWriter:用于操作媒体
      |--BufferedWriter:用于提高效率
可见:装饰比继承灵活。
特点:装饰类和被装饰类都必须所属同一个接口或者父类。
示例2:(自定义一个读取缓冲区类,模拟一个BufferedReader)
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
class MyBufferedReader
{
private Reader r;
//定义一个数组作为缓冲区
private char[] buf = new char[1024];
//定义一个指针用于操作这个数组中的元素,当操作到最后一个元素后,指针应该归零
private int pos = 0;
//定义一个计数器用于记录缓冲区中的数据个数,当该数据减到0,就从源中继续获取数据到缓冲区中
private int count = 0;
MyBufferedReader(Reader r)
{
this.r = r;
}
//该方法从缓冲区一次取一个字符
public int myRead() throws IOException
{
//从源中获取一批数据到缓冲区中,需要先做判断,只有计数器为0时,才需要从源中获取数据
if(count==0)
{
count = r.read(buf);
//每次获取数据到缓冲区后,角标归零
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos];
pos++;
count--;
return ch;
}

public String myReadLine() throws IOException
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = myRead())!=-1)
{
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length()!=0)
{
return sb.toString();
}
return null;
}

public void myClose() throws IOException
{
r.close();
}
}

public class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader bufr = new MyBufferedReader(fr);
String line = null;
while((line = bufr.myReadLine())!=null)
{
System.out.println(line);
}
bufr.myClose();
}
}
Java基础---IO流(上)
字节流抽象基类:InputStream,OutputStream。字节流可以操作任何数据。注意:字符流使用的数组是字符数组。char[] chs  字节流使用的数组是字节数组。byte[] btFileOutputStream fos = new FileOutputStream("a.txt");fos.write("abcde");//直接将数据写入到了目的地。fos.close();//只关闭资源。FileInputStream fis = new FileInjputStream("a.txt");//fis.available();//获取关联的文件的字节数。//如果文件体积不是很大。可以这样操作。byte[] buf = new byte[fis.available()];//创建一个刚刚好的缓冲区。//但是这有个弊端,就是文件过大,大小超出jvm的内容空间时,会内存溢出。fis.read(buf);System.out.println(new String(buf));需求:copy 一个图片。BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("1.jpg"));BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("2.jpg"));int by = 0;while((by=bufis.read())!=-1){bufos.write(by);}bufos.close();bufis.close();示例1:
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamDemo
{
public static void main(String[] args)throws IOException
{
demo_write();
}

public static void demo_write() throws IOException
{
//1、创建字节输出流对象,用于操作文件
FileOutputStream fos = new FileOutputStream("bytedemo.txt");
//2、写数据,直接写入到了目的地中
fos.write("abcde".getBytes());
//关闭资源
fos.close();
}
}
Java基础---IO流(上)
示例2:
import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamDemo
{
public static void main(String[] args) throws IOException
{
demo_read1();
System.out.println( "---------------");
demo_read2();
System.out.println( "---------------");
demo_read3();
}
public static void demo_read1() throws IOException
{
//1、创建一个读取流对象,和指定文件关联
FileInputStream fis = new FileInputStream("bytedemo.txt");
//打印字符字节大小,不过要少用,文件太大,可能内存溢出
byte[] buf = new byte[fis.available()];
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}

public static void demo_read2() throws IOException
{
FileInputStream fis = new FileInputStream("bytedemo.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 demo_read3() throws IOException
{
FileInputStream fis = new FileInputStream("bytedemo.txt");
//一次读取一个字节
int ch = 0;
while((ch=fis.read())!=-1)
{
System.out.print((char)ch);
}
fis.close();
}
}
Java基础---IO流(上)
P.S.
FileOutputStream、FileInputStream的flush方法内容为空,没有任何实现,调用没有意义。
字节流的缓冲区:同样是提高了字节流的读写效率。
示例3:(通过几种方式对MP3的进行拷贝,比较他们的效率。)
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyMp3Test
{
public static void main(String[] args) throws IOException
{
long l1 = System.currentTimeMillis();
copy_1();
long l2 = System.currentTimeMillis();
System.out.println(l2-l1);//34
copy_2();
long l3 = System.currentTimeMillis();
System.out.println(l3-l2);//154
}

public static void copy_1() throws IOException
{
FileInputStream fis = new FileInputStream("0.mp3");
FileOutputStream fos = new FileOutputStream("1.mp3");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fis.close();
fos.close();
}

public static void copy_2() throws IOException
{
FileInputStream fis = new FileInputStream("0.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);

FileOutputStream fos = new FileOutputStream("2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);


int ch = 0;
while((ch=bufis.read())!=-1)
{
bufos.write(ch);
}

bufis.close();
bufos.close();

}
}
Java基础---IO流(上)
读取一个键盘录入的数据,并打印在控制台上。键盘本身就是一个标准的输入设备。对于java而言,对于这种输入设备都有对应的对象示例3:
import java.io.IOException;
import java.io.InputStream;

public class ReadKey
{
public static void main(String[] args) throws IOException
{
readKey();
}

public static void readKey() throws IOException
{
InputStream in = System.in;
int ch = in.read();//阻塞式方法
System.out.println(ch);

ch = in.read();
System.out.println(ch);

ch = in.read();
System.out.println(ch);

in.close();
}
}
Java基础---IO流(上)
P.S.1、通过键盘录入数据,然后将数据流向显示器,那么显示器就是目的地。通过System类的setIn,setOut方法可以默认设备进行改变。System.setIn(new FileInputStream("1.txt"));//将源改成文件1.txt。System.setOut(new PrintStream("2.txt"));//将目的改成文件2.txt。因为是字节流处理的是文本数据,可以转换成字符流,操作更方便。BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));2、默认的输入输出系统不需要关,它会随着系统的结束而消失
目前学习的流对象: 字符流: FileReader FileWriter BufferedReader BufferedWriter 字节流: FileInputStream FileOutputStream BufferedInputStream BufferedOutputStream 字节流的read()方法读取一个字节。为什么返回不是byte类型,而是int类型呢?因为read方法读到末尾返回-1.。而在所操作的数据很容易连续多个1的情况,而连续8个1,就是-1 。导致读取会提前停止。所以将读到的一个字节给提升一个int类型数值,但是只保留原字节,并在剩余而进制位补0。具体操作是:byte&255 or byte&0xff 对于write方法,可以一次写入一个字节,但接收的是一个int类型数值。只写入该int类型的数值最低一个字节(8位)。 简单说:read方法读到数据进行提升。write对操作的数据进行转换。