IO(Input Output)流
用来处理设备之间的数据传输
Java对数据的操作都是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流
流按流向分为输入流,输出流。
输入流与输出流
输入流和输出流相对于内存设备而言
将外设中的数据读取到内存中:输入
将内存的数据写入到外设中:输出
字符流的由来
其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。
再对这个文字进行操作,简单说:字节流+编码表
字节流的两个顶层父类
InputStream
FilterInputStream
FilterInputStream
包含一些其他输入流,它用作其基本的数据源,可能会沿途转换数据或提供附加功能。 FilterInputStream
本身简单地覆盖了所有InputStream的方法, InputStream
版本将所有请求传递给包含的输入流。 FilterInputStream的FilterInputStream
可以进一步覆盖这些方法中的一些,并且还可以提供附加的方法和领域。
int available() //返回从该输入流中可以读取(或跳过)的字节数的估计,而不会被下一个调用者阻塞该输入流的方法。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo {
public static void main(String[] args)throws IOException{
demo_write();
demo_read();
}
public static void demo_read()throws IOException{
//创建一个读取流对象,和指定文件关联
FileInputStream fis=new FileInputStream("bytedemo.txt");
/*byte[] buf=new byte[fis.available()];//返回从该输入流中可以读取(或跳过)的字节数的估计,而不会被下一个调用者阻塞该输入流的方法。
fis.read(buf);
System.out.println(new String(buf));*/
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_write() throws IOException{
//创建字节输出流对象,用于操作文件
FileOutputStream fos=new FileOutputStream("bytedemo.txt");
//写数据,直接写入到了目的地中
fos.write("abcde".getBytes());
}
}
OutputStream
FilterOutputStream
这个类是过滤输出流的所有类的超类。 这些流位于已经存在的输出流( 底层输出流) 之上 ,它使用它作为数据的基本接收器,但是可能沿着数据方向转换或提供附加功能。
类FilterOutputStream
本身就简单地覆盖了所有OutputStream的方法, OutputStream
版本将所有请求传递给底层输出流。 FilterOutputStream的FilterOutputStream
可以进一步覆盖这些方法中的一些,并提供其他方法和字段。
public static void demo_write() throws IOException{
//创建字节输出流对象,用于操作文件
FileOutputStream fos=new FileOutputStream("bytedemo.txt");
//写数据,直接写入到了目的地中
fos.write("abcde".getBytes());
}
复制mp3的几种方式
package com.monfolld.IO流.ByteStream;
import java.io.*;
public class CopyMp3Test {
public static void main(String[] args)throws IOException{
copy_1();
}
public static void copy_1()throws IOException{
FileInputStream fis=new FileInputStream("D:\\CloudMusic\\0.mp3");
FileOutputStream fos=new FileOutputStream("D:\\CloudMusic\\1.MP2");
byte[] buf=new byte[1024];
int len=0;
while ((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
fis.close();
}
public static void copy_2()throws IOException{//建议使用
FileInputStream fis=new FileInputStream("D:\\CloudMusic\\0.mp3");
BufferedInputStream bufis=new BufferedInputStream(fis);
FileOutputStream fos=new FileOutputStream("D:\\CloudMusic\\1.MP2");
BufferedOutputStream bufos=new BufferedOutputStream(fos);
int ch=0;
while ((ch=bufis.read())!=-1){
bufos.write(ch);
bufos.flush();
}
bufos.close();
bufis.close();
}
public static void copy_3()throws IOException{//不建议
FileInputStream fis=new FileInputStream("D:\\CloudMusic\\0.mp3");
FileOutputStream fos=new FileOutputStream("D:\\CloudMusic\\1.MP2");
byte[] buf=new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
public static void copy_4()throws IOException{//千万不要有,效率没有
FileInputStream fis=new FileInputStream("D:\\CloudMusic\\0.mp3");
FileOutputStream fos=new FileOutputStream("D:\\CloudMusic\\1.MP2");
int ch=0;
while ((ch=fis.read())!=-1){
fos.write(ch);
}
fos.close();
fis.close();
}
}
字符流的两个顶层父类
Reader
需求:读取一个文本文件。将读取到的字符打印到控制台
同上,找到了FileReader
读取方式一
import java.io.FileReader;
import java.io.IOException;
//需求:读取一个文本文件。将读取到的字打印到控制台
public class FileReaderDemo {
public static void main(String[] args)throws IOException {
//1.创建读取字符串数据的流对象
/*
* 在创建读取流对象时,必须要明确被读取的文件,一定要确定该文件是否存在
* 用一个读取流关联一个已存在文件*/
FileReader fr=new FileReader("demo.txt");
int ch=0;
while ((ch=fr.read())!=-1){
System.out.println((char)ch);
}
//用Reader中的read方法读取字符。
/*int ch=fr.read();
System.out.println((char) ch);//a
int ch1=fr.read();
System.out.println((char) ch1);//b
int ch2=fr.read();
System.out.println(ch2);//-1*/
fr.close();
}
}
读取方式二
import java.io.FileReader;
import java.io.IOException;
//需求:读取一个文本文件。将读取到的字打印到控制台
public class FileReaderDemo2{
public static void main(String[] args)throws IOException {
FileReader fr=new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据
* 先创建字符数组*/
char[] buf=new char[1024];
int len=0;
while ((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
/*int num=fr.read(buf);//将独到的字符存储到数组中
System.out.println(num+":"+new String(buf,0,num));//3:abc
int num1=fr.read(buf);//将独到的字符存储到数组中
System.out.println(num1+":"+new String(buf,0,num1));//2:de*/
fr.close();
}
}
Writer
需求:将一些文字存储到硬盘的一个文件中。
记住:如果要操作文字数据,建议优先考虑字符流。
而且要将数据从内存写到硬盘上,要使用字符流中的输出流。Writer
硬盘的数据基本体现是文件,希望找到一个可以操作文件的Writer。
找到了FileWriter
换行和续写
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
private static final String LINE_SEPARATOR=System.getProperty("line.separator");
public static void main(String[] args)throws IOException {
//创建一个可以往文件中写入字符数据的字符输入流对象
/*
* 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确改文件(用于存储数据的目的地)
* 如果文件不存在,则会自动创建
* 如果文件存在,则会被覆盖
*
* 如果构造函数中加入true,可以实现对文件的续写*/
FileWriter fw=new FileWriter("demo.txt",true);
/*
* 调用Writer对象中的write(string)方法,写入数据
* 其实数据写入到临时存储缓冲区中*/
fw.write("ab\rcde");
//fw.write("abc"+LINE_SEPARATOR+"ABC");
//fw.flush();//进行刷新,将数据写入目的地中
fw.close();//关闭流,关闭资源,在关闭前会先调用flush刷新缓冲中的数据到达目的地。
}
}
IO异常处理
import java.io.FileWriter;
import java.io.IOException;
public class IOExceptionDemo {
private static final String LINE_SEPARATOR=System.getProperty("line.separator");
public static void main(String[] args){
FileWriter fw=null;
try {
fw=new FileWriter("k:\\demo.txt",true);
fw.write("abc"+LINE_SEPARATOR+"ABC");
}catch (IOException e){
System.out.println(e.toString());
}finally {
if (fw !=null)
try {
fw.close();
}catch (IOException e){
throw new RuntimeException("关闭失败");
}
}
}
}
结果:
java.io.FileNotFoundException: k:\demo.txt (系统找不到指定的路径。)
练习
复制文本文件
将c盘的一个文本文件复制到d盘
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTextTest {
public static void main(String[] args)throws IOException {
//读取一个已有文本文件,使字符读取流和文件想关联
FileReader fr=new FileReader("IO流_2.txt");
//创建一个目的,用于存储读到数据
FileWriter fw=new FileWriter("copytext_1.txt");
//频繁的读写操作
int ch=0;
while ((ch=fr.read())!=0){
fw.write(ch);
}
//关闭流资源
fw.close();
fr.close();
}
}
import java.io.FileReader;
import java.io.FileWriter;
public class CopyTextTest_2 {
private static final int BUFFER_SIZE=1024;
public static void main(String[] args){
FileReader fr=null;
FileWriter fw=null;
try {
fr=new FileReader("IO流_2.txt");
fw=new FileWriter("copytext_1.txt");
//创建一个临时容器,用于缓冲读到的字符
char[] buf=new char[BUFFER_SIZE];//缓冲区
//定义一个变量记录读取到的字符数,其实就是往数组里装字符的个数
int len=0;
while ((len=fr.read(buf))!=-1){
fw.write(buf,0,len);
}
}catch (Exception e){
throw new RuntimeException("读写失败");
}finally {
if (fw!=null)
try {
fw.close();
}catch (Exception e){
e.printStackTrace();
}
if (fr!=null)
try {
fr.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
字符缓冲区
BufferedWriter
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。
提供了一个newLine()方法,它使用平台自己的系统属性line.separator定义的行分隔符概念。 并非所有平台都使用换行符('\ n')来终止行。 因此,调用此方法来终止每个输出行,因此优选直接写入换行符。
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo {
public static void main(String[] args)throws IOException {
FileWriter fw=new FileWriter("buf.txt");
//为了提高写入的效率,使用字符流的缓冲区
//创建一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
BufferedWriter bufw=new BufferedWriter(fw);//起到一个提高效率的作用
//使用缓冲区的写入方法,将数据线写入缓冲区中
/*bufw.write("abcde");
bufw.newLine();//
bufw.write("fghij");*/
for (int x=1;x<=4;x++){
bufw.write("abcdef"+x);
bufw.newLine();
bufw.flush();
}
//使用缓冲区的刷新方法将数据刷目的地中
//bufw.flush();
bufw.close();//其实关闭的就是被缓冲的流对象
}
}
BufferedReader
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。
每次调用read()或readLine()可能会导致从文件中读取字节,转换成字符,然后返回,这可能非常低效。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferReaderDemo {
public static void main(String[] args)throws IOException {
FileReader fr=new FileReader("buf.txt");
BufferedReader bufr=new BufferedReader(fr);
String line=null;
while ((line=bufr.readLine())!=null){
System.out.println(line);
}
/*String line1=bufr.readLine();
System.out.println(line1);
String line2=bufr.readLine();
System.out.println(line2);*/
}
public static void demo()throws IOException{
FileReader fr=new FileReader("buf.txt");
char[] buf=new char[1024];
int len=0;
while ((len=fr.read())!=-1){
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
利用缓冲区复制文本文件
import java.io.*;
import java.nio.Buffer;
public class CopyTextByBufTest {
public static void main(String[] args)throws IOException {
FileReader fr=new FileReader("buf.txt");
BufferedReader bufr=new BufferedReader(fr);
FileWriter fw=new FileWriter("bufcopy.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);
}*/
bufw.close();
bufr.close();
}
}
自定义MyBufferedReader_read方法
自定义的读取缓冲区,其实就是模拟一个BufferedReadder
分析:缓冲区中无非就是封装了一个数组,并多外提供了更多的方法对数组进行访问,
其实这些方法最终操作的都是数组的指针(角标)。
缓冲的原理:其实就是从源中获取一批数据装进缓冲区中,再从缓冲区中不断的取出一个一个数据,
在此次取完后,再从源中继续取一批数据进缓冲区。当源中的数据取光时,用-1作为结束标记。
package com.monfolld.IO流.buffer;
import java.io.FileReader;
import java.io.IOException;
public class MyBufferedReader {
private FileReader r;
//定义一个数组作为缓冲区
private char[] buf=new char[1024];
//定义一个指针用于操作这个数组中的元素,当操作最后一个元素后,指针应该归零
private int pos=0;
//定义一个计数器用于记录缓冲区的数据个数,当该数据减到0,就从源中继续获取数据到缓冲区中
private int count=0;
MyBufferedReader(FileReader r){
this.r=r;
}
public int myRead()throws IOException {
//1.从源中获取一批数据到缓冲区中,需要先做判断,只有计数器为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);
}
return null;
}
public void myClose()throws IOException{
r.close();
}
}
LineNumberReader
缓冲字符输入流,跟踪行号。 该类定义方法setLineNumber(int)
和getLineNumber()
用于分别设置和获得当前行号。
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("IO流_2.txt");
LineNumberReader lnr=new LineNumberReader(fr);
String line=null;
lnr.setLineNumber(100);//设置当前行号
while ((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);//106:qwerdf
}
lnr.close();
}
}
流转换
InputStreamReader
InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset
将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class TransStreamDemo {
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());
}
}
}
OutputStreamWriter
OutputStreamWriter是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节charset
。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
import java.io.*;
public class TransStreamDemo {
public static void main(String[] args)throws IOException {
/*InputStream in=System.in;
InputStreamReader isr=new InputStreamReader(in);
//字符流
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()+"\r\n");//将字符数据用缓冲区对象将数据写入缓冲区,目的地是osw
bufw.flush();
}
}
}
需求演示
package com.monfolld.IO流;
import java.io.*;
public class TransStreamDemo {
public static void main(String[] args)throws IOException {
/*
* 1.需求:将键盘录入的数据写入到一个文件中
* BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));//键盘录入
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("bufcopy.txt")));
* 2.需求:将一个文本文件内容显示在控制台上
*BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));//键盘录入
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
* 3:需求:将一个文本文件中的内容复制到另一个文件中*/
BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));//键盘录入
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt")));
String line=null;
while ((line=bufr.readLine())!=null){
if ("over".equals(line))
break;
bufw.write(line.toUpperCase()+"\r\n");//将字符数据用缓冲区对象将数据写入缓冲区,目的地是osw
bufw.flush();
}
}
}
流的操作规律: 之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。 想知道开发时用到哪些对象,只要通过四个明确即可。 1.明确源和目的(汇) 源:InputStream Reader 目的:OutputStream Writer 2.明确数据是否是纯文本数据 源:是纯文本 Reader 否:InputStream 目的:是纯文本 Writer 否:OutputStream 到这里,就可以明确需求中具体要使用哪个体系。 3.明确具体的设备 原设备: 硬盘:File 键盘:System.in 内存:数组 网络:Socket流 目的设备: 硬盘:File 控制台:System.out 内存:数组 网络:Socket流 4.是否需要其他额外功能 1.是否需要高效(缓冲区); 是,就加上buffer。
转换流编码解码
import java.io.*;
public class TranStreamDemo3 {
public static void main(String[] args)throws IOException {
writeText();
}
public static void writeText_2()throws IOException{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("dbk_2.txt"));
/*这两句代码的功能是等同的
* FileWriter:其实就是转换流制定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件
* 简单说:操作文件的字节流+本机默认码表
* 这是按照默认码表来操作文件的便捷类
*
* 如果操作文本文件需要默认明确具体的编码,FileWriter就不行了。必须用转换流*/
osw.write("你好");
osw.close();
}
public static void writeText_3()throws IOException{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("u8_1.txt"),"utf-8");
osw.write("你好");
osw.close();
}
public static void writeText()throws IOException{
FileWriter fw=new FileWriter("dbk_1.txt");//等同于2
fw.write("你好");
fw.close();
}
}