黑马程序员-java基础(七)-IO流

------- android培训java培训、期待与您交流! ----------

IO流

1  IO流

IO流(Input Output)用来处理设备之间的数据传输。Java对数据的操作是通过流的方式。
按操作数据分:字节流、字符流;字节流抽象基类:InputStream,OutStream;字符流抽象基类:Reader,Writer
按流向分:输入流,输出流
输入输出都是相对于内存而言:
输入:将外设中的数据读取到内存中
输出:将内存的数写入到外设中
p.s.
字符流其实其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。再对这个文字进行操作。简单说:字节流+编码表。

2  字符流

1、创建文件写入字符
FileWriter(String fileName)
          根据给定的文件名构造一个 FileWriter 对象。
import java.io.*;
class  Test
{
	public static void main(String[] args) throws IOException
	{
		FileWriter fw=new FileWriter("Demo.txt"); //创建一个FileWriter对象,该对象一初始化被徐有明确被操作文件,有同名文件会直接覆盖
		fw.write("ok,success");				//调用write方法,将字符串写进流,待刷新后写入目的地
		fw.close();					//刷新流对象的缓存中的数据到目的地中,也可以用flush刷新,但是close刷新后会关闭流
	}
}

2、文件续写

/*
已有文件续写
*/

import java.io.*;
class  Test
{
	public static void main(String[] args) throws IOException
	{
		FileWriter fw=new FileWriter("Demo.txt",true); 
		fw.write("\r\nWrite again");							
		fw.close();										
	}
}



3.IO异常处理:
/*
IO异常处理方式
*/

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

4、字符流文件读取
第一种方式:读单个字符reader()
import java.io.*;
class  Test
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr=new FileReader("Demo.txt");	//创建一个文件读取流对象,且保证文件存在
		while (true)
		{
			int ch=fr.read();                    //一次读一个字符,且会自动往下读,读到末尾则返回-1
			if (ch==-1)
				break;
			System.out.println("ch="+(char)ch);//强转为char型
		}
		
		fr.close();
	}
}
运行结果:

第二种方式:通过字符数组进行reader(char[] cbuf)
import java.io.*;
class Test
{

    public static void main(String[] args) throws IOException
    {
        FileReader fr=new FileReader("Demo.txt");
        char[] buf=new char[1024];
        int len=0;
        while((num=fr.read(buf))!=-1)//read([])返回字符个数
        {
        System.out.println(new String (buf,0,len));
        }
        fr.close();
    }
}
5、字符流缓冲区(BufferReader、BufferWriter)
在流的基础上对流的功能进行增强,提高对数据的读写效率
P.S.
缓冲区要结合字符流才可以使用
/*
BufferedReader、BufferedWriter
*/

import java.io.*;
class  Test
{
	public static void main(String[] args) throws IOException
	{
		FileWriter fw=new FileWriter("Demo.txt"); //建立字符写入流对象
		BufferedWriter bw=new BufferedWriter(fw);//建立字符写入流流对应的缓冲区对象							
		for(int i=0;i<4;i++){
			bw.write("This is BufferedWriter"+i);//用字符缓冲区一次写入一个字符串		
			bw.newLine();//写入一个行分割符
		}
		bw.close();
		
		FileReader fr=new FileReader("Demo.txt"); //建立字符读取流对象
		BufferedReader br=new BufferedReader(fr);//建立字符读取流对应的缓冲区对象
		String line;
		while((line=br.readLine())!=null){//一次读取一行
			System.out.println(line);
		}
		br.close();
	}
}

P.S.
readLine()方法原理:


【自定义读一行】
/*
自定义读一行 MyReaderDemo
*/
import java.io.*;
class MyReader 
{
	private FileReader f;
	MyReader(FileReader f)//输入流对象
		{
		this.f=f;
		}
        public String MyReaderLine() throws IOException//读一行方法
	{
		StringBuilder sb=new StringBuilder();//定义一个临时容器StringBuilder
		int ch=0;
		while((ch=f.read())!=-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
	{
	f.close();
	}
}

class MyReaderDemo 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr=new FileReader("demo.txt");
	        MyReader mr=new MyReader(fr);
		String line=null;
		while((line=mr.MyReaderLine())!=null)
		{
			System.out.println(line);//打印一行
		}
		mr.MyClose();
	}
}
6、装饰设计模式
在原有类进行功能改变,增强已有对象的功能(如上:自定义的可以读一行类)
/*
装饰类和继承的区别
*/
class Person{
    void chifan(){
         System.out.println( "吃饭");
   }
}

//采用装饰的方式增强Person类
//这个类的出现是为了增强Person而出现的
class NewPerson{
    private Person p;
    NewPerson(Person p){
          this.p = p;
   }
   public void chifan(){
	   p.chifan();
	   System.out.println( "开胃酒");
	   System.out.println( "甜点");
   }
}

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

public class Test{
    public static void main(String[] args){
         Person p = new Person();
         NewPerson np1 = new NewPerson(p);
         np1.chifan();
         System.out.println( "---------------");
         NewPerson2 np2 = new NewPerson2();
         np2.chifan();
   }
}
运行结果:

P.S.

装饰体系较继承体系灵活,避免了装饰体系的臃肿,降低了类与类之间的关系


3  字节流

1、基本操作与字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。
/*
字节流输入、输出基本操作
*/

public class Test{
    public static void main(String[] args)throws IOException
    {
    	writeStream();
    	readStream1();
    	System.out.println();
    	System.out.println("--------");
    	readStream2();
    	System.out.println();
    	System.out.println("--------");
    	readStream3();
    }
    public static void writeStream()throws IOException
    {
    	FileOutputStream fos=new FileOutputStream("Demo.txt");
    	//getBytes()使用平台的默认字符集将此 String 编码为 byte 序列
    	fos.write("hallo java".getBytes());//字节为最小操作单位不用flush刷新,但要关流
    	fos.close();
    }
    //读取方式一:一次读取一个字节
    //比较慢
    public static void readStream1()throws IOException
    {
    	FileInputStream fis=new FileInputStream("Demo.txt");
    	int ch;
    	while((ch=fis.read())!=-1){//从输入流中读取一个字节
    		System.out.print((char)ch);
    	}
    }
    //读取方式二:定义一个1024的字节,一次读1024个字节
    //较合理,建议使用
    public static void readStream2()throws IOException
    {
    	FileInputStream fis=new FileInputStream("Demo.txt");
    	byte[] buf=new byte[1024];
    	int len=0;
    	while((len=fis.read(buf))!=-1){//从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
    		System.out.print(new String(buf,0,len));
    	}
    }
    //通过available(),获取字节长度,定义一个刚好的字节数组存放
    //如果字节长度过长,容易造成溢出
    public static void readStream3()throws IOException
    {
    	FileInputStream fis=new FileInputStream("Demo.txt");
    	byte[] buf=new byte[fis.available()];
    	fis.read(buf);
    	System.out.print(new String(buf,0,buf.length));
    }
}
运行结果:
/*
复制一个图片
*/
import java.io.*;
public class Test{
    public static void main(String[] args)throws IOException
    {
    	FileInputStream fis=new FileInputStream("123.jpg");//创建输入字节流,关联文件
    	FileOutputStream fos=new FileOutputStream("123_copy.jpg");//创建输出字节流,关联文件
    	byte[] buf=new byte[1024];
    	int len=0;
    	while((len=fis.read(buf))!=-1){
    	fos.write(buf);//每次存储2kb数据
    }}}

2、字节流缓冲区:
BufferedOutputStream、BufferedInputStream(和字符流缓冲区使用方法一致)
/*
 * 需求:拷贝一个mp3文件,并比较几种方式的效率
 * */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test1{
       public static void main(String[] args) throws IOException {
           long s1=System.currentTimeMillis(); 
    	   copy_1();//一次读取1kb到缓冲数组中,然后输出
    	   long s2=System.currentTimeMillis(); 
            copy_2();//一次读取一个字节,加入缓冲计数BufferInputStream
            long s3=System.currentTimeMillis(); 
            copy_3();//一次读取一个节,不使用缓冲区
            long s4=System.currentTimeMillis();
            System.out.println("1:"+(s2-s1)+"ms...2:"+(s3-s2)+"ms...2:"+(s4-s3)+"ms");
      }

       public static void copy_1() throws IOException {
            FileInputStream fis = new FileInputStream("1.mp3" );
            FileOutputStream fos = new FileOutputStream("1_copy.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("2.mp3" );
            BufferedInputStream bufis = new BufferedInputStream(fis);

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

             int ch = 0;
             //一次读取一个
             while((ch = bufis.read()) != -1){
                  bufos.write(ch);
            }

            bufis.close();
            bufos.close();
      }
       public static void copy_3() throws IOException {
    	   FileInputStream fis = new FileInputStream("2.mp3" );
           FileOutputStream fos = new FileOutputStream("3_copy.mp3" );
           int ch = 0;
            //一次读取一个
            while((ch = fis.read()) != -1){
                 fos.write(ch);
           }
           fis.close();
           fos.close();
       }
}
运行结果


从以上结果,可以很明显看出拷贝效率:加入缓冲的字节数组>使用字节流缓冲区>字节流一次读取一个字节

3、读取键盘输入
System.in:标准输入设备:键盘
System.out:标准输出设备:控制台
/*
获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。
1、键盘录入只读取一个字节,要判断是否为over,需要将读取的字节拼成字符串
2、定义StringBuilder来存储
3.在用户回车前,转换成字符串并判断
*/
import java.io.*;
public class Test{
    public static void main(String[] args)throws IOException
    {
    	InputStream is=System.in;//System.in:返回标准输入流
    	StringBuilder sb=new StringBuilder();
    	int ch=0;
    	while(true){
    		ch=is.read();
    		if(ch=='\r')
    			continue;
    		if(ch=='\n'){
    			String s=sb.toString();
    			System.out.println(s.toUpperCase());//转换为大写输出    			
    			if("over".equals(s))//判断是否输入的是over
    				break;
    			sb.delete(0, sb.length());//存入一行后要初始化一下
    		}
    		else
    		sb.append((char)(ch));
    	}}}



4、转换流
字符流和字节流之间的桥梁,方便字节流和字符流之间的转换
转换流:
    InputStreamReader:字节到字符的桥梁,解码。
    OutputStreamWriter:字符到字节的桥梁,编码。

转换流的应用:
    字节流中的数据都是字符时,转成字符流操作更高效。
/*
获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。
1、键盘录入是字节流,将字节流转换成字符流
2、定义字符流的缓冲区,用readLine方法高效读取
3、判断是否为over
*/
import java.io.*;
public class Test{
    public static void main(String[] args)throws IOException
    {
    	InputStream is=System.in;//System.in:返回标准输入流
    	InputStreamReader isr=new InputStreamReader(is);//将字节输入流转换成字符输入流
    	BufferedReader br=new BufferedReader(isr);//建立字符输入流的缓冲区
    	while(true){
    		String line=br.readLine();
    		if("over".equals(line))
    			break;
    		System.out.println(line.toUpperCase());
    	}}}
P.S.
    使用字节流读取一个中文字符需要读取两次,因为一个中文字符由两个字节组成,而使用字符流只需读取一次。
import java.io.*;
public class Test{
    public static void main(String[] args)throws IOException
    {
    	//将键盘输入字节流转换成字符流,并创建字符读取缓冲区
    	BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    	BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
    	String line;
    	while((line=br.readLine())!=null){
    		if("over".equals(line))
    			break;
    		bw.write(line.toUpperCase());
    		bw.newLine();
    		bw.flush();
    	}

		}}

5、改变标准输入输出设备
System类
static voidsetIn(InputStream in)
          重新分配“标准”输入流。
static voidsetOut(PrintStream out)
          重新分配“标准”输出流。
/*
改变标准输入、输出设备
*/
import java.io.*;
public class Test{
    public static void main(String[] args)throws IOException
    {
    	System.setIn(new FileInputStream("Demo.txt"));//改变标准的输入设备
    	System.setOut(new PrintStream("Demo_copy.txt"));//改变标准输出设备
    	BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    	BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
    	String line;
    	while((line=br.readLine())!=null){
    		if("over".equals(line))
    			break;
    		bw.write(line.toUpperCase());
    		bw.newLine();
    		bw.flush();
    	}

		}}



P.S.
    字符流继承体系简图


    字节流继承体系简图


  4  File类 

File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
import java.io.*;
import java.io.File;

public class Test{
       public static void main(String[] args){
            constructorDemo();
      }

       public static void constructorDemo(){
             //可以将一个已存在的,或者不存在的文件或者目录封装成file对象
             //方式一
            File f1 = new File("d:\\demo\\a.txt" );

             //方式二
            File f2 = new File("d:\\demo" ,"a.txt" );
      
             //方式三
            File f = new File("d:\\demo" );

            File f3 = new File(f,"a.txt" );

             //考虑到Windows和Linux系统通用
            File f4 = new File("d:" + File.separator + "demo" + File.separator + "a.txt" );
      }
}

P.S
流只能操作数据不能操作文件
File.separator是与系统有关的默认名称分隔符。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\\'。
【例】过滤指定目录中的文件
/*
 需求:获取指定目录下后缀名为java的文件。
思路:
用list(FilenameFilter filter)对文件进行过滤
*/
import java.io.*;

public class Test{
       public static void main(String[] args){
          File dir=new File("f:\\WJava\\Day18");//指定目录 
          String[] list=dir.list(new FilenameFilter(){//定义内部类获取FilenameFilter对象
        	  public boolean accept(File dir, String name){//dir:目录 name:文件名称,返回真则取出
        		return name.endsWith(".java");//过滤.java文件
        	  }
          });
          for(String name:list){
          System.out.println(name);}
       }
}

递归:
函数自身直接或者间接的调用到了自身。
    一个功能在被重复使用,并每次使用时,参与运算的结果和上一次调用有关。这时可以用递归来解决问题。

 P.S.
1、递归一定明确条件,否则容易栈溢出。
2、注意一下递归的次数。
/*
 列出目录下所有内容
*/
import java.io.*;

public class Test{
       public static void main(String[] args)
       {
    	   File dir=new File("f:\\WJava\\exam"); 
    	   showFile(dir);
       }
       public static void showFile(File f)//定义一个打印目录下所文件的方法
       {
    	   
    	   File[] files=f.listFiles();
    	   for(int i=0;i<files.length;i++){
    		   System.out.println(files[i]);
    		   if(files[i].isDirectory())
    			   showFile(files[i]);//如果是一个目录,递归调用该方法
    			   
    	   }
    			   
       }
}
【例】
/*
  需求:利用递归求1到10的和。
*/
import java.io.*;

public class Test{
       public static void main(String[] args)
       {
    	   
    	   System.out.println(getSum(10));
    	   
       }
       public static int getSum(int num)
       {
    	   int sum=0;
    	   if(num>=1)
    	   sum=num+getSum(num-1);
    	 return sum;
       }
}
运行结果:

【例】 需求:对指定目录进行删除
/*
  需求:删除一个带内容的目录 
  思考:
  1、目录里面有内容是无法删除,原理上必须从内往外删
  2、定义一个删除指定目录中文件的方法(当存在目录时递归此方法)
*/
import java.io.*;

public class Test{
       public static void main(String[] args)
       {
    	   
    	   File dir=new File("f:\\exam");
    	   removeDir(dir);
    	   
       }
       public static void removeDir(File f)
       {
    	   File[] files=f.listFiles();
    	  for(int i=0;i<files.length;i++){
    		  if(files[i].isDirectory())
    			  removeDir(files[i]);
    		  else
    		  System.out.println(files[i].toString()+"::"+files[i].delete());//将文件删除
    	  }
    	  System.out.println(f.toString()+"::"+f.delete());//将文件夹删除
       }
}
运行结果:


5 Properties

一、概述

Properties是Hashtable的子类,它具备Map集合的特点。而且它里面还有存储的键值对,都是字符串,无泛型定义。是集合中和IO技术想结合的集合容器。
特点:

1)可用于键值对形式的配置文件

2)在加载时,需要数据有固定的格式,常用的是:键=值

二、特有方法

1、设置

Object setProperty(String key,String value);//设置键和值,调用Hashtable的方法put

2、获取

String getProperty(String key);//指定key搜索value

Set<String> stringPropertyName();//返回属性列表的键集,存入Set集合

3、加载流和存入流

void load(InputStream ism);//从输入字节流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

void load(Readerreader);//从输入字符流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

voidlist(PrintStream out);//将属性列表输出到指定的输出流

void store(OutputStreamout,String comments);//对应load(InputStream )将属性列表(键值对)写入输出流。comments属性列表的描述。

void store(Writerwriter, String comments);//对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。

package cn.swu1;
/*
练习:用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。
	
分析:
很容易想到的是:计数器。可是该计数器定义在程序中,随着该应用程序的退出,该计数器也在内存中消失了。
所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。键值对数据是map集合。数据是以文件形式存储。使用io技术。那么map+io——>Properties。

思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建
	  2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。
	  3、将值小于等于5次的信息数据存入文件中
*/
import java.util.*;
import java.io.*;

class  APPNum
{
	public static void main(String[] args)throws IOException 
	{
		int count=runCount();
		if(count>5)//如果程序被使用了超过5次,则终止使用,并提示
		{
			System.out.println("次数到了,交钱!!!!!");
			return ;
		}
		else
			System.out.println("程序第"+count+"次Run!");
	}
	//获取程序运行的次数
	public static int runCount()throws IOException
	{
		Properties ps=new Properties();//创建集合对象

		File file=new File("info.ini");//将文件进行封装
		if(!file.exists())//判断是否存在
			file.createNewFile();
		FileReader fr=new FileReader(file);//将文件于读取流进行关联
		
		ps.load(fr);//加载流中的文件数据到集合中

		int count=0;//定义计数器
		String value=ps.getProperty("time");//获取次数值
		
		if(value!=null)//如过值不等于null,则将其赋值给count
		{
			count=Integer.parseInt(value);
		}
		count++;//每启动一次自增
		ps.setProperty("time",count+"");//将次数记录住集合

		FileWriter fw=new FileWriter(file);
		ps.store(fw,"");//将集合中的数据存入硬盘文件中
		
		fr.close();//关流
		fw.close();

		return count;//返回程序启动的次数
	}
}

运行结果:

6  打印流

概述
1、打印流包括:PrintStream和PrintWriter
2、该流提供了打印方法,可将各种类型的数据都原样打印。
字节打印流:PrintStream

构造方法中可接收的参数类型:

1、File对象。File

2、字符串路径:String

3、字符输出流:OutputStream

字符串打印流:PrintWriter

构造方法中可接受的参数类型

1、File对象:File

2、字符串路径:String

3、字节输出流:OutputStream

4、字符输出流:Writer

import java.io.*;

class  PrintStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		//键盘录入
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));

		//打印流关联文件,自动刷新
		PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);

		String line = null;

		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))//结束字符
				break;
			out.println(line.toUpperCase());
			//out.flush();
		}
		
		//关流
		out.close();
		bufr.close();

	}	
}

总结



1、流的对象很多,开发是具体选择那类流:
a、明确源和目的
         源:InputStream Reader
         目的:OutputStream Writer
b、明确数据是否是纯文本数据
         源:是纯文本:Reader
                否:InputStream
         目的:是纯文本:Writer
                否:OutputStream
         到这里,就可以明确需求中具体要使用哪个体系。
c、明确具体的设备(通过setIn和setOut可以改变默认输入输出设备)
         源设备:硬盘:File        键盘:System.in        内存:数组        网络:Socket流
         目的设备:硬盘:File     控制台:System.out   内存:数组        网络:Socket流
d、是否需要其他额外功能
         是否需要高效(缓冲区):     是,就加上buffer









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值