15、IO流


十五、IO流

1、系统结构图(xmind)


2、IO流继承体系简图


3、tips

——1.字符流Writer

需求:将一些文字存储到硬盘一个文件中。

       如果要操作文字数据,建议优先考虑字符流。而且要将数据从内存写到硬盘上,要使用字符流中的输出流:Writer。硬盘的数据基本体现是文件,希望找到一个可以操作文件的Writer:FileWrite。

代码:

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

运行结果:
         

       1.close方法只能用一次。

       2. 流关闭以后不能,不能再调用write方法,否则会报异常错误:IOException。
       IO流的异常处理方式:为防止代码异常导致流无法关闭,因此在finally中对流进行关闭。如果构造函数中加入true,可以实现对文件进行续写。


——2.字符流Reader

1.使用read()方法读取文本文件数据。

栗:

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.print(( char)ch+" ");
		}
		fr.close();
	}
}

运行结果:

 

public int read()

                throw IOException

读取单个字符。在字符可用、发生I/O错误或者已达到流的末尾前,此方法一直阻塞。用于支持高效的单字符输入的子类应重写此方法。

返回:作为整数读取的字符,范围在0到65535之间(0x00-0xffff),如果已达到流的末尾,则返回-1

抛出:IOException-如果发生I/O错误。


2.使用read(char[])方法读取文本文件数据。

栗:

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[3];
		int len = 0;
		while((len = fr.read(buf)) != -1)
		{
			System.out.println( new String(buf,0,len));
		}
		fr.close();
	}
}

运行结果:


——3.字符流缓冲区

        写入换行使用BufferedWriter类中的newLine()方法。读取一行数据使用BufferedReader类中的readLine()方法。bufr.read():这个read方法是从缓冲区中读取字符数据,所以覆盖了父类中的read方法。bufr.readLine():另外开辟了一个缓冲区,存储的是原缓冲区一行的数据,不包含换行符。原理:使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓冲数据变成字符串返回。

栗:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

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

运行结果:


——4.装饰设计模式

1.当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。

2.特点:装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

3.装饰和继承的区别:

        1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。

        2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。

        3)从继承结构转为组合结构。

在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。



——5.字节流

栗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( "abcdefg".getBytes());
	
		//关闭资源动作要完成
		fos.close();
	}
}


运行结果:


栗2:

/*
练习:
复制一个图片
思路:
1、用字节读取流对象和图片关联。
2、用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3、通过循环读写,完成数据的存储。
4、关闭资源。
*/
import java.io.*;
class  CopyPic
{
	public static void main(String[] args) 
	{
		//常用方法复制
		byteArrayCopy();
		//利用输入流的available方法进行复制
		availableCopy();
		//利用available方法对复制文件大小有限制,慎用	
	}

	//使用available方法进行复制
	public static void availableCopy()
	{
		FileInputStream fis=null;
		FileOutputStream fos=null;
		try
		{
			//关联要复制的文件
			fis=new FileInputStream("D:/桌面/1.jpg");
			//指定复制的路径
			fos=new FileOutputStream("D:/桌面/2.jpg");
			//利用available方法指定数组长度
			byte[] b=new byte[fis.available()];

			fis.read(b);//复制关联文件数据

			fos.write(b);//粘贴到指定路径

		}
		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("写入字节流关闭失败");
			}
		}
	}
	//使用读数组方式进行复制
	public static void byteArrayCopy()
	{
		FileInputStream fis=null;
		FileOutputStream fos=null;
		try
		{
			//关联要复制的文件
			fis=new FileInputStream("D:/桌面/1.jpg");
			//指定复制的路径
			fos=new FileOutputStream("D:/桌面/3.jpg");
			//利用数组的读取方式
			byte[] b=new byte[1024];
			int len=0;
			while ((len=fis.read(b))!=-1)//复制文件的全部数据
			{
				fos.write(b,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("写入字节流关闭失败");
			}
		}
	}
}

运行结果:


——6.字节流缓冲区

需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。

注意:

       1、字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。

       2、byte类型的-1提升为int类型时还是-1。原因:因为在bit8个1前面补的全是1导致的。如果在bit8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff即255即可。

栗:

/*
自定义字节流读取缓冲区
思路:
1、定义一个固定长度的数组
2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0
3、每次将字节数据存入元素要先将数组中的元素取完
 
*/
import java.io.*;
class MyBufferedInputStream
{
       private InputStream in;
       private byte[] by=new byte[1024];
       private int count=0,pos=0;
       MyBufferedInputStream(InputStream in)
       {
              this.in = in;
       }

       //自定义读方法,一次读一个字节
       public int myRead()throws IOException
       {
              //通过in对象读取硬盘上数据,并存储by中。
              //存储在数组中的数据被读取完,再通过in对象从硬盘上读取数据
              if(count == 0)
              {
                     count = in.read(by);
                     if(count < 0)//文件数据全部被读取出来了
                            return -1;

 
                     pos = 0;//初始化指针
                     byte b = by[pos];

                     count--;//每被读一个字节,表示数组中的字节数少一个
                     pos++;//指针加1
                     return b&255;//返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。
                                          //通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。
                                          //而在输出字节流写入数据时,只写该int类型数据的最低8位。
              }
              else if(count > 0)//如果数组中的数据没被读取完,则继续读取
              {
                     byte b = by[pos];
 
                     count--;
                     pos++;
                     return b&0xff;
              }
              return -1;
       }
       
       //自定义关闭资源方法
       public void close()throws IOException
       {
              in.close();
       }
}

 
//测试自定义输入字节流缓冲区
class MyBufferedCopyMp3
{
       public static void main(String[] args) 
       {
              long start = System.currentTimeMillis();
              //利用字节流的缓冲区进行复制
              copy_2();
              long end=System.currentTimeMillis();
              System.out.println("复制共用时:"+(end-start)+"毫秒");
       }
       //使用字节流的缓冲区进行复制
       public static void copy_2()
       {
              BufferedOutputStream bout=null;
              MyBufferedInputStream bin=null;
              try
              {
                     //关联复制文件输入流对象到缓冲区
                     bin = new MyBufferedInputStream(new FileInputStream("D:/桌面/Sugar.mp3"));
                     //指定文件粘贴位置的输出流对象到缓冲区
                     bout = new BufferedOutputStream(new FileOutputStream("D:/桌面/Sugar2.mp3"));
                     int by=0;
 

                     while((by = bin.myRead())!=-1)
                     {
                            bout.write(by);//将缓冲区中的数据写入指定文件中
                     }
              }
              catch(IOException e)
              {
                     throw new RuntimeException("MP3复制失败");
              }
              finally
              {
                     try
                     {
                            if(bin != null)
                                   bin.close();//关闭输入字节流
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("读取字节流关闭失败");
                     }
                     try
                     {
                            if(bout != null)
                                   bout.close();//关闭输出字节流
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("写入字节流关闭失败");
                     }
              }
       }
}

运行结果:


——7.转换流

1.InputStreamReader将字节流通向字符流
       1、获取键盘录入对象。
              InputStream in=System.in;
       2、将字节流对象转成字符流对象,使用转换流。
              InputStreamReaderisr=new InputStreamReader(in);
       3、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
              BufferedReaderbr=new BufferedReader(isr);
       //键盘录入最常见写法
              BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
2. OutputStreamWriter字符流通向字节流
       字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。

/*
需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束
源:键盘录入。
目的:控制台。

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

		//键盘录入最常见写法
		BufferedReader in=new BufferedReader(new InputStreamReader(System.in));

		//字符流通向字节流
		BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));

		String s=null;
		while((s=in.readLine())!=null)
		{
			if("over".equals(s))
				break;
			bw.write(s.toUpperCase());//写入数据
			bw.newLine();//换行
			bw.flush();//刷新
			
		}
		bw.close();//关闭流资源
		in.close();
	}
}


运行结果:


——8.Properties集合

Map

      |--Hashtable

      |--Properties

特点:

1. 该集合中的键和值都是字符串类型。

2. 集合中的数据可以保存到流中,或者从流中获取。

3. 通常该集合用于操作以键值对形式存在的配置文件。

栗:

import java.util.Properties;
import java.util.Set;

public class PropertiesDemo
{
	public static void main(String[] args)
	{
		propertiesDemo();
	}
	
	public static void propertiesDemo()
	{
		//创建一个Properties集合
		Properties prop = new Properties();
	
		//存储元素
		prop.setProperty( "zhangsan","10" );
		prop.setProperty( "lisi","20" );
		prop.setProperty( "wangwu","30" );
		prop.setProperty( "zhaoliu","40" );
		
		//修改元素
		prop.setProperty( "wangwu","26" );
		
		//取出所有元素
		Set<String> names = prop.stringPropertyNames();
	
		for(String name : names)
		{
			String value = prop.getProperty(name);
			System.out.println(name + ":" + value);
		}
	}
}

运行结果:


——9.实例

/*
需求:获取指定目录下,指定扩展名的文件(包含子目录中的),并且将这些文件的绝对路径写入到
一个文本文件中。
简单说:就是建立一个指定扩展名的文件的列表。
思路:
1. 必须进行深度遍历。
2. 要在遍历的过程中进行过滤,将符合条件的内容都存储到容器中。
3. 对容器中的内容进行遍历并将绝对路径写入到文件中。
*/
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Test
{
	public static void main(String[] args) throws IOException 
	{
		File dir = new File("E:/Java/day21" );
		FilenameFilter filter = new FilenameFilter()
		{
			public boolean accept(File dir,String name)
			{
				return name.endsWith(".java" );
			}
		};

		List<File> list = new ArrayList<File>();
		getFiles(dir,filter,list);
		File destFile = new File(dir,"javalist.txt" );
		write2File(list,destFile);
	}
	/*
	对指定目录中的内容进行深度遍历,并按照指定过滤器,进行过滤。
	将过滤后的内容存储到指定容器List中。
	*/
	public static void getFiles(File dir,FilenameFilter filter,List<File>list)
	{
		File[] files = dir.listFiles();

		for(File file : files)
		{
			//递归
			if(file.isDirectory())
			{
				getFiles(file,filter,list);
			} 
			else
			{
				//对便利到的文件进行过滤器的过滤。将符合条件File对象,存储到List集合中
				if(filter.accept(dir,file.getName()))
				{
					list.add(file);
				}
			}
		}
	}

	public static void write2File(List<File> list,File destFile) throws IOException
	{
		BufferedWriter bufw = null;
		try
		{
			bufw = new BufferedWriter(new FileWriter(destFile));

			for(File file : list)
			{
				bufw.write(file.getAbsolutePath());
				bufw.newLine();
				bufw.flush();
			}
		} 
		finally
		{
			if(bufw!=null)
			try
			{
				bufw.close();
			} 
			catch(IOException e)
			{
				throw new RuntimeException("关闭失败");
			}
		}
	}
}

运行结果:


——10.小结

流的基本应用小结:
•流是用来处理数据的。
•处理数据时,一定要先明确数据源,与数据目的地(数据汇)。
•数据源可以是文件,可以是键盘。
•数据目的地可以是文件、显示器或者其他设备。
•而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值