关闭

黑马程序员—Java中的IO流

标签: java语言
213人阅读 评论(0) 收藏 举报

11. IO流

 

11.1IO流概述

 

IO流用来处理设备之间的数据传输。流按操作数据分为两种:字节流与字符流。

流按流向分为:输入流,输出流。

 

字节流的抽象基类:InputStream  OutputSream

字符流的抽象基类:Reader    Writer


11.2字符流

 

1FileWriter

import java.io.*;
class FilerWriterDemo 
{
	public static void main(String[] args) throws IOException
	{
		/*创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件
		  而且该文件会被创建到指定目录下,如果该目录下已有同名文件,将被覆盖。
		  其实该步就是在明确数据要存放的目的地*/
		FileWriter fw = new FileWriter("demo.txt");

		//调用write方法,将字符串写入到流中
		fw.write("abcde");

		//刷新流对象中的缓冲中的数据,将数据刷到目的地中
		fw.flush();

		/*关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,将数据刷到目的地
		  和flush区别:flush刷新后,流可以继续使用。close刷新后,会将流关闭。*/
		fw.close();
	}
}

IO异常处理方式:

import java.io.*;
class FilerWriterDemo2 
{
	public static void main(String[] args) 
	{
		FileWriter fw = null;
		try
		{
			//在此处进行fw初始化,以免出现异常时,finally语句无法读取到fw对象
			fw = new FileWriter("demo.txt");
			fw.write("abcdefg");		
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try
			{
				/*当上述语句出现异常时,fw不会创建,fw.close()语句将会出现fw类
				 文件不存在的异常,因此进行一次判定来解决*/
				if(fw!=null)
					fw.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}	
		}
	}
}

对已有文件的数据续写:

import java.io.*;
class FilerWriterDemo3 
{
	public static void main(String[] args) 
	{
		FileWriter fw = null;
		try
		{
			/*传递一个true参数,代表不覆盖已有的文件,
			  并在已有文件的末尾处进行数据的续写*/
			fw = new FileWriter("demo.txt",true);
			fw.write("\r\nhaha");		
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try
			{
				if(fw!=null)
					fw.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}	
		}
	}
}


2FileReader

import java.io.*;
class FileReaderDemo 
{
	public static void main(String[] args) 
	{
		/*创建一个文件读取流对象,和指定名称的文件相关联要保证该文件
		  是已经存在的,如果不存在会发生异常FileNotFoundException*/
		FileReader fr = null;
		try
		{
			fr = new FileReader("demo.txt");

		/*读取方法一:调用读取流对象的read方法
		  read()方法一次读一个字符,而且会自动往下读,已到末尾返回-1*/
			int ch = 0;
			while ((ch=fr.read())!=-1)
				System.out.println((char)ch);

		/*读取方法二:定义一个字符串数组,用于存储读到字符。该read(char[])
		  返回的是读到字符个数*/
			char[] buf = new char[1024];
			int num = 0;
		    while ((num=fr.read(buf))!=-1)
			{
				System.out.println("num="+num+"..."+new String(buf,0,num));
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}			
		}
	}
}

3)拷贝文本文件

import java.io.*;
class CopyText 
{
	public static void main(String[] args) 
	{
		copy_2();
	}
	//复制方式一:从C盘读一个字符,就往D盘写一个字符
	public static void copy_1()
	{
		//创建目的地
		FileWriter fw = null;
		//与已有文件关联
		FileReader fr = null;
		try
		{
			fw = new FileWriter("RuntimeDemo_copy.txt");
			fr = new FileReader("RuntimeDemo.java");
			int ch = 0;
			while ((ch = fr.read())!=-1)
			{
				fw.write(ch);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败");
		}
		finally
		{
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			finally
			{
				try
				{
					if(fw!=null)
						fw.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("写入关闭失败");
				}
			}	
		}
	}
	//复制方式二:从C盘读取一段,再存到D盘中
	public static void copy_2()
	{
		//创建目的地
		FileWriter fw = null;
		//与已有文件关联
		FileReader fr = null;
		try
		{
			fw = new FileWriter("SystemDemo_copy.txt");
			fr = new FileReader("SystemDemo.java");
			char[] buf = new char[1024];
			int len = 0;
			while ((len = fr.read(buf))!=-1)
			{
				fw.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败");
		}
		finally
		{
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			finally
			{
				try
				{
					if(fw!=null)
						fw.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("写入关闭失败");
				}
			}		
		}
	}
}


11.3字符流的缓冲区

 

缓冲区的出现提高了对数据的读写效率,缓冲区要结合流才可以使用,在创建缓冲区之前,必须要先有流对象


1)  BufferedWriter

该缓冲区中提供了一个跨平台的换行符:newLine();

import java.io.*;
class BufferedWriterDemo 
{
	public static void main(String[] args) throws IOException
	{
		//创建一个字符写入流对象
		FileWriter fw = new FileWriter("buf.txt");
		/*为了提高字符写入流效率,加入缓冲技术,只要将需要被提高效率的流对象
		  作为参数传递给缓冲区的构造函数即可*/
		BufferedWriter bufw = new BufferedWriter(fw);
		for (int x = 1;x<5 ;x++ )
		{
			bufw.write("abcd"+x);
			bufw.newLine();
			bufw.flush();
		}
		//只要用到缓冲区,就要记得刷新
		bufw.flush();
		//关闭缓冲区就是在关闭缓冲区中的流对象
		bufw.close();
	}
}

2)  BufferedReader

该缓冲区提供了一个一次读一行的方法,方便于对文本数据的获取:readLine()。当返回null时表示读到文件末尾,返回的时候只返回回车符之前的数据内容。

import java.io.*;
class BufferedReaderDemo 
{
	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);
		}
		bufr.close();
	}
}


LineNumberReader类:是BufferedReader类的子类,有两个新方法

getLineNumber():获得当前行号

setLineNumber():设置当前行号



11.4装饰设计模式

 

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

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

 

装饰模式比继承更灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于一个体系中。


装饰类和被装饰类都是,被装饰类父类的一个子类。创建自定义装饰类时,要继承父类,那么需要复写父类中的抽象方法


11.5字节流


1)  FileInputSream ()

public static void readFile_3() throws IOException
	{
		FileInputStream fis = new FileInputStream("fos.txt");
		//定义一个刚刚好的缓冲区,不用再循环了,数据不大时使用。
		byte[] buf = new byte[fis.available()];     //该文件可使用的容量
		fis.read(buf);
		System.out.println(new String(buf));
		fis.close();
	}
	public static void readFile_2() throws IOException
	{
		FileInputStream fis = new FileInputStream("fos.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();
	}


2)  FileOutputSream ()

public static void writeFile() throws IOException
	{
		FileOutputStream fos = new FileOutputStream("fos.txt");
		
		//字节流不能写入字符串,通过String.getBytes()方法将字符串转成byte数组写入
		fos.write("abcde".getBytes());
		/*不需要flush()。因为字符流也是写入字节,但是需要将写入的字节先临时缓存。
		  查完表后通过flush()再写入字符。而字节流不需要缓存,直接就将字节写入*/
		fos.close();
	}


11.6字节流的缓冲区

 

BufferedOutputStream

BufferedInputStream

//通过缓冲区拷贝mp3文件:
import java.io.*;
class CopyMp3 
{
	public static void main(String[] args) throws IOException
	{
		long start = System.currentTimeMillis();
		copy_1();
		long end = System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");
	}
	//通过字节流缓冲区完成复制
	public static void copy_1() throws IOException
	{
		BufferedInputStream bufis = 
<span style="white-space:pre">			</span>new BufferedInputStream(new FileInputStream("mp3.mp3"));
		BufferedOutputStream bufos = 
<span style="white-space:pre">			</span>new BufferedOutputStream(new FileOutputStream("mp3_copy.mp3"));
		int by = 0; 
		while ((by=bufis.read())!=-1)
		{
			bufos.write(by);
		}
		bufos.close();
		bufis.close();
	}
}


11.7读取键盘录入

 

System.in:对应的标准输入设备:键盘

 

1)  InputStreamReader

是字节流通向字符流的桥梁,将字节流对象转成字符流对象。

 

2)  OutputStreamWriter

字符流通向字节流的桥梁。

/*通过键盘录入数据,当录入一行数据后,就将该行数据进行打印
  如果录入的数据是over,那么停止录入*/
import java.io.*;
class ReadIn 
{
	public static void main(String[] args) throws IOException
	{
		//获取键盘录入对象
		InputStream in =System.in;
		//将字节流对象转成字符流对象,使用转换流InputStreamReader
		InputStreamReader isr = new InputStreamReader(in);
		//为了提高效率,将字符流进行缓存区技术高效操作,使用BufferedReader
		BufferedReader bufr = new BufferedReader(isr);
		//获取输出对象
		OutputStream out = System.out;
		//将字节流对象转成字符流对象,使用转换流OutputStreamWriter
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bufw = new BufferedWriter(osw);
		String line = null;
		while ((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
		bufw.close();
	}
}


11.8流操作规律

 

1)  明确源和目的

源:输入流。InputStream  Readerr

目的:输出流 OutputStream Writer

2)  操作的数据是否是纯文本

是:字符流

不是:字节流

3)  当体系明确后,再明确要使用哪个具体的对象

通过设备来进行区分

源设备:内存ArrayStream,硬盘 FileStream,键盘 System.in

目的设备:内存 ArrayStream,硬盘 FileStream,控制台 System.out


改变标准输入输出设备:

//将输入设备由键盘改成PersonDemo.java文件,需要建立一个字节流对象FileInputStream

System.setIn(new FileInputStream(“PersonDemo.java”)) 

//将输出设备由控制台改成zz.txt文件,需要建立一个字节输出流子类对象PrintStream

System.setOut(new PrintStream(“zz.txt”))


11.9File类

 

用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作

File类常见方法:

1)  创建

boolean mkdir():创建文件夹,只能创建一级目录

boolean mkdirs():创建多级目录,包括所有必需但不存在的父目录

boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立就会创建文件,而

且文件已经存在,会覆盖。File类要调用方法才创建文件。

 

2)  删除

boolean delete():如果此句前出现异常将不会执行到,文件就无法删除

void deleteOnExit():在程序退出时删除。在程序运行出现异常的时候也能删除文件。

3)  判断

boolean canExecute():判断文件是否可执行

boolean exists():判断文件是否存在

boolean isDirectory():           判断文件对象是否是文件或者目录时,必须要先判断

boolean isFile():               该文件对象封装的内容是否存在,通过exists判断

boolean isHidden():判断文件是否隐藏

4)  获取信息

getName()

getPath()

getParent()                该方法返回的是绝对路径中的父目录,如果File对象封装的是相对路径,返回null。如果相对路径中有上一层目录那么该目录就是返回结果

getAbsolutePath()    获取绝对路径

long lastModified()   获取文件最后被修改的时间

long length()

boolean renameTo()     f1.renameTo(f2),变更文件名,还会将文件剪切到指定目录下

File[] listRoots()         列出可用的系统盘符

String[] list()           列出目录下的文件和子目录。调用list方法的file对象必须是封装了一个目录,该目录还必须存在

File[] listFiles()         列出目录下的文件,返回的是File对象的数组

列出指定目录下文件或者文件夹,包含子目录中的内容(带层次):
import java.io.*;
class FileDemo3 
{
	public static void main(String[] args) 
	{
		File file = new File("c:\\java001");
		showDir(file,0);
	}
	public static void showDir(File dir,int level )
	{
		System.out.println(getLevel(level)+dir.getName());
		//level只对应于调用的函数中,递归调用时对应的是函数中相应的值
		level++;
		File[] files = dir.listFiles();
		for (int x = 0; x<files.length;x++ )
		{
			//如果是文件夹,可以调用方法本身继续遍历文件夹
			if(files[x].isDirectory())
				showDir(files[x],level);
			else
				System.out.println(getLevel(level)+files[x].getName());
		}
	}
	public static String getLevel(int level)
	{
		StringBuilder sb = new StringBuilder();
		//文件夹及文件名前带|--
		sb.append("|--");
		for (int x=0;x<level ;x++ )
		{
			//子级目录缩进
			sb.insert(0,"  ");
		}
		return sb.toString();
	}
}

函数自身调用自身,这种表现形式称为递归。使用时需要注意:

1)限定条件

2)要注意递归的次数,尽量避免内存溢出



11.10Properties类

 

Properties是hashtable的子类,具备map集合的特点,它里面存储的键值对都是字符串。是集合中和IO技术相结合的集合容器。该对象的特点:可以用于键值对形式的配置文件。

Properties中的方法:

setProperty(String key, String value)       向集合中存入键值对

String  getProperty(String key)           获取对应键的值

Set<String>  stringPropertyNames()      获取Properties对象中键的集合

 

list(PrintStream out)

list(PrintWriter out)

load(InputStream inStream)

load(Reader reader)

store(OutputStream out, String comments)       将Properties中的键值对存储到配置文件中

store(Writer writer, String comments)            并且加入注释信息comments。

import java.io.*;
import java.util.*;
class  PropertiesDemo
{
	public static void main(String[] args) throws IOException
	{
		//method_1();
		PropertiesMethod();
	}
	//load,list,store方法应用
	public static void PropertiesMethod() throws IOException
	{
		FileInputStream fis = new FileInputStream("Info.txt");
		Properties prop = new Properties();
		//将流中的数据加载进集合
		prop.load(fis);
		//设置配置文件中的信息
		prop.setProperty("wangwu","39");
		FileOutputStream fos = new FileOutputStream("Info.txt");
		prop.store(fos,"haha");
		//将集合中的数据输出到流中
		prop.list(System.out);
		fos.close();
		fis.close();
	}
	//load方法原理:将info.txt中键值数据存到集合中进行操作。
	public static void method_1() throws IOException
	{
		BufferedReader bufr = new BufferedReader(new FileReader("Info.txt"));
		String line = null;
		Properties prop = new Properties();
		while ((line=bufr.readLine())!=null)
		{
			//利用=将键与值分割
			String[] arr = line.split("=");
			//调用方法将键和值存入到集合中
			prop.setProperty(arr[0],arr[1]);
		}
		bufr.close();
	}
	//设置和获取元素
	public static void setAndGet()
	{
		Properties prop = new Properties();
		prop.setProperty("zhangsan","30");
		prop.setProperty("lisi","39");

		String value = prop.getProperty("lisi");
		System.out.println(value);
		Set<String> names = prop.stringPropertyNames();
		for(String s : names)
		{
			System.out.println(s);
		}
	}
}


11.11打印流

 

为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。该流提供了打印方法,可以将各种数据类型的数据都原样打印

 

字节打印流:

PrintStream

构造函数可以接收的参数类型:

1.      file对象    File

2.      字符串路径 String

3.      字节输出流 OutputStream

 

字符打印流:

PrintWriter

构造函数可以接收的参数类型:

1.      file对象    File

2.      字符串路径 String

3.      字节输出流 OutputStream

4.      字符输出流 Writer

PrintWriter(OutputStream out, booleanautoFlush)    autoFlush为true时,println方法会

PrintWriter(Writer out, boolean autoFlush)          自动刷新缓存


11.12 序列流SequenceInputStream

 

SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

import java.io.*;
import java.util.*;
class  SequenceDemo
{
	public static void main(String[] args) throws IOException
	{
		Vector<FileInputStream> v = new Vector<FileInputStream>();
		v.add(new FileInputStream("1.txt"));
		v.add(new FileInputStream("2.txt"));
		v.add(new FileInputStream("3.txt"));
		Enumeration<FileInputStream> en = v.elements();
		SequenceInputStream sis = new SequenceInputStream(en);
		FileOutputStream fos = new FileOutputStream("4.txt");
		byte[] buf = new byte[1024];
		int len = 0;
		while ((len=sis.read(buf))!=-1)
		{
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
	}
}


11.13对象的序列化

 

 

ObjectInputStream   ObjectOutputStream

 

将堆内存中的对象存入的文件当中,对象内的变量数据也被保存;读取文件的可以读取到所存储的对象,直接调用之前的数据。实现了Serializable接口的对象才能被序列化。

 

在需要序列化的对象中添加static final long serialVersionUID = 42L语句,即使本地的该对象有所修改,也可以读取被序列化的对象,否则会报出InvalidClassException异常。

 

static 修饰的变量不能被序列化,因为static修饰的内容存在于方法区;transient修饰的变量也不能被序列化,即使变量存在于堆内存中。


11.14管道流

 

PipedInputStream        PipedOutputStream

 

管道输入流和管道输出流通过不同线程运行。管道流是多线程和IO流的结合应用

import java.io.*;
class Read implements Runnable
{
	private PipedInputStream in;
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];
			//当没有读取到数据时read方法会阻塞,即线程等待。
			int len = in.read(buf);
			String s = new String(buf,0,len);
			System.out.println(s);
			in.close();
		}
		catch (IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}
class Write implements Runnable
{
	private PipedOutputStream out;
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	public void run()
	{
		try
		{
			out.write("piped lai le".getBytes());
			out.close();
		}
		catch (IOException e)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}
class  PipedStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);
		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();
	}
}


11.15RandomAccessFile

 

该类不是IO体系中的子类,而是直接继承自Object。但是它是IO包中的成员,因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组中的元素进行操作。可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。

 

能够完成读写的原理就是内部封装了字节输入流和输出流。该类只能操作文件,操作模式通常有只读r和读写rw。

 

如果模式为只读r,不会创建文件,会去读取一个已存在的文件,如果该文件不存在,则会出现异常。

如果模式为rw,操作的文件不存在,会自动创建;如果存在,则不会覆盖。

 

该类可以通过多个线程同时对一个文件进行读写操作,下载就是利用此原理

import java.io.*;
class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeFile2();
		//writeFile();
		//readFile();
	}
	public static void readFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
		//调整对象中的指针,来读取想要读取的内容,往前往后都能调整
		//raf.seek(8);
		//跳过指定的字节数,来读取想要读取的内容,只能往后跳
		//raf.skipBytes(8);
		byte[] buf = new byte[4];
		raf.read(buf);
		String name = new String(buf);
		//从此文件读取一个有符号的 32 位整数
		int age = raf.readInt();
		System.out.println("name="+name);
		System.out.println("age="+age);
		raf.close();
	}
	//向指定位置写入数据
	public static void writeFile2() throws IOException
	{
		//创建对象时与输出流不同,不会覆盖已有文件
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
		raf.seek(8*3);
		raf.write("周期".getBytes());
		raf.writeInt(103);
		raf.close();
	}
	public static void writeFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
		raf.write("李四".getBytes());
		//按4个字节写入int数字
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.writeInt(99);
		raf.close();
	}
}


11.16DataStream

 

可以用于操作基本数据类型的数据的流对象


11.17ByteArrayStream

 

ByteArrayInputStream   : 在构造的时候,需要接收数据源,而且数据源是一个字节数组

ByteArrayOutputStream  : 在构造的时候,不用定义数据目的,因为该对象中已经内部封

装了可变长度的字节数组,这就是数据目的地

 

因为这两个流对象都操作的数组,并没有使用系统资源,所以不用进行close关闭

ByteArrayStream是用流的思想来操作数组

import java.io.*;
class  ByteArrayStream
{
	public static void main(String[] args) 
	{
		//数据源
		ByteArrayInputStream bis = 
			new ByteArrayInputStream("ABCDE".getBytes());
		//数据目的
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		int by = 0;
		while ((by=bis.read())!=-1)
		{
			bos.write(by);
		}
		System.out.println(bos.toString());
		//将字节数组输出流中的数据写入到一个文件当中
		bos.writeTo(new FileOutputStream("a.txt"));
	}
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2862次
    • 积分:132
    • 等级:
    • 排名:千里之外
    • 原创:11篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章存档