Java基础---IO流(上)


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();
	}
}
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();

		
	}
}
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();
	}
}

第二种读取方式:使用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();
	}
}

将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();
	}
}
方式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();
	}
}
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();
	}
}

示例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();
	}
}

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();
	}
}

装饰设计模式
对原有类进行了功能的改变,增强。
示例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();
	}
}

装饰和继承都能实现一样的特点:进行功能的扩展增强。有什么区别呢?
首先有一个继承体系:
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();
	}
}

字节流
抽象基类:InputStream,OutputStream。
字节流可以操作任何数据。注意:字符流使用的数组是字符数组。char[] chs  字节流使用的数组是字节数组。byte[] bt
FileOutputStream 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();
	}
}

示例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();
	}
}

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而言,对于这种输入设备都有对应的对象
示例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();
	}
}
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对操作的数据进行转换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值