黑马程序员--java学习之io总结

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

.io概览

java语言的i/o库提供了四大等级结构:InputStream,OutputStream,Reader,Writer四个系列的类。InputStreamOutputStream处理8位字节流数据, ReaderWriter处理16位的字符流数据。InputStreamReader处理输入, OutputStreamWriter处理输出。大家一定要到J2SE文档中看看这四大等级结构的类继承体系。

除了这四大系列类,i/o库还提供了少数的辅助类,其中比较重要的是InputStreamReaderOutputStreamWriterInputStreamReaderInputStream适配为Reader, OutputStreamWriterOutputStream适配为Writer;这样就架起了字节流处理类和字符流处理类间的桥梁。

例如:一个应用字符写入流的小程序

/*
需求:练习使用字符写入流的使用

*/

import java.io.*;
class FileWriteDemo 
{
	public static void main(String[] args) throws IOException
	{
		FileWriter fi=new FileWriter("abc.txt");                //定义一个字符写入流对象
		BufferedWriter bu=new BufferedWriter(fi);  //把字符流写入对象作为参数传递给缓冲区
		for (int x=0;x<8 ;x++ )
		{
			bu.write("abcde"+x);                      //利用for循环重复向缓冲区写入
			bu.newLine();                               //换行
			bu.flush();                              //每执行一次循环刷新一下
		}
		bu.close();                                     //相当于执行关闭字符流对象
		System.out.println("Hello World!");
	}
}

二.IO(BufferedReader)

       很明显Bufferedreader的用法比inputstream要复杂,复杂的存在必然会导致优势的存在!我们都知道Inputstream是一个字节一个字节的读取,每次读取都会执行一次IO,我们知道io的操作是很费时间的,这就必然会导致程序的效率,而Bufferedreader很好的解决这一问题,它可以一次读取大量的数据,大大减少了io次数,效率也就上去了,这就像有辆能乘坐一百人的大巴,从热力输送学生到理工本部,司机脑残,学生没睡醒,非要一次只坐一个同学,大巴的来回跑一百趟才能把这一百人全部送到学校,这就类似Inputstream,另一个司机是清华毕业,智商当然高了,他让这一百人全部上车,一次九ok了,虽然在学生上车时多用了点时间,但总时间要远比那个脑残司机要少的多。

示例1:
/*
需求:模拟BufferedReader的读取一行readLine功能
思路:可以利用StringBuilder来完成,文本数据实际上是一系列的字符所构成,换行符“\r”,回车符"\n"是其中两个较为
特殊的符号,可以利用字符读取流的read方法对文本读取,当读到"\r"令其继续,读到”\n“则表明一行数据已经读完,可以
返回打印,读到普通字符则令其存入StringBuilder.
*/
import java.io.*;
class BufReader
{
	FileReader fi=null;
	BufReader(FileReader f)
	{
		this.fi=f;
	}
	public String myReadLine() throws IOException
	{
		StringBuilder bu=new StringBuilder();//定义一个容器用来装入临时一行的数据
		int ch=0;
		while ((ch=fi.read())!=-1)
		{
			if (ch=='\r')// \r代表将输入点移到行首
			{
				continue;
			}
			else if (ch=='\n')// \n转义字符是移到下一行
			{
				return bu.toString();
			}
			else
				bu.append((char)ch);
		}
		if(bu.length()!=0)//防止最后没有回车的一行被漏读
			return bu.toString();
		return null;

	}



	public void myClose() throws IOException
	{
		fi.close();
	}
}

class FileReaderDemo3 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fi=new FileReader("write.txt");
		BufReader buR=new BufReader(fi);
		String line=null;
		while ((line=buR.myReadLine())!=null)
		{
			System.out.println(line);
		}
		buR.myClose();
		System.out.println("Hello World!");
	}
}
	示例2:利用BufferedReader,BufferedWriter复制一个文本文件
/*
需求:利用写入流和输出流完成一个文件内容的复制

*/
import java.io.*;
class FileReaderDemo2 
{
	public static void main(String[] args) 
	{
		BufferedReader fi=null;//将引用与对象的初始化分开写,有利于程序的健壮性
		BufferedWriter ri=null;
		try
		{	
			fi=new BufferedReader(new FileReader("FileReaderDemo.java"));
			ri=new BufferedWriter(new FileWriter("write.txt"));
			String line=null;
			while ((line=fi.readLine())!=null)
			{
			ri.write(line);
			ri.newLine();
			ri.flush();
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败");
		}
		
		finally
		{
			try
			{
				
				if (fi!=null)//防止fi初始化失败却被关闭
				{
					fi.close();
				}
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭读取流异常");
			}
			
			try
			{
					if (ri!=null)
				{
					ri.close();
				}
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭写入流异常");
			}
			
		}
		System.out.println("Hello World!");
	}
}

三.装饰设计模式

       装饰设计模式:当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类,装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

(1) 装饰者和被装饰者必须具有相同的超类型。

(2) 装饰者即可以包装被装饰者,也可以包装装饰者。往往利用多层包装来达到目的。

(3) 装饰者中组合了被装饰者对象 ( 装饰类的关键特征 ) 。使得我们能够通过嵌套装饰来动态扩展行为。

装饰与继承:

(1) 类的继承是高耦合,会产生更多的子类,从而引起类的爆炸 )

(2) 对象组合即装饰模式能够降耦,不会创造更多的子类,动态的为对象添加功能, 所以类应该对扩展开放,对修改关闭 ,装饰模式比继承要灵活。避免了继承体系臃肿。

示例代码:自定义装饰类的例子

/*
需求:模拟类MyLineNumberReader,并要求该类包含方法设置行号mySetNum和获取行号myGetNum两个方法
思路:采用装饰类的思想解决此问题,用一个变量lineNum记录读取'\r'的次数,此次数即为行号,而设置行号的方法对lineNum进行初始化动作。
步骤:定义类MyLineNumberReader,成员变量lineNum,用构造函数传入FileReader对象。
	  在此类定义myReaderLine方法,以便实现对实际字符流对象整行的读入,用lineNum记录行号信息;
	  用MySetNum方法实现对lineNum的初始化,用myGetNum方法实现获取最终的Numline
*/
import java.io.*;
class MyLineNumberReader
{
	private FileReader fi=null;
	private int lineNum=0;
	MyLineNumberReader(FileReader f)
	{
		this.fi=f;
	}
	public void mySetNum(int i)
	{
		this.lineNum=i;
	}
	public int myGetNum()
	{
		return this.lineNum;
	}
	public void myClose() throws IOException
	{
		fi.close();
	}
	public String myReaderLine() throws IOException
	{
		StringBuilder bu=new StringBuilder();
		int ch=0;
		while ((ch=fi.read())!=-1)
		{
			if (ch=='\r')
			{
				lineNum++;
				continue;
			}
			else if (ch=='\n')
			{
				return bu.toString();
			}
			else
			    bu.append((char)ch);
		}
		if(bu.length()!=0)
			return bu.toString();
		return null;
	}

}
class FileReaderDemo4 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fi=new FileReader("write.txt");
		MyLineNumberReader myl=new MyLineNumberReader(fi);
		String line=null;
		//myl.mySetNum(100);
		while ((line=myl.myReaderLine())!=null)
		{
			System.out.println(myl.myGetNum()+":"+line);
		}
		myl.myClose();
		System.out.println("Hello World!");
	}
}

四.IO(字节流File读写操作)

字节流:操作数组是字节数组,而字符流则是对字符数组进行操作,字符流的底层实际上也是字节流,但字符有编码,所以字符流需要有编码解码过程,也需要刷新,而字节流无须任何转换,是不需要刷新的。

实例:

/*
需求:用字节流实现对一个图片文件的复制
思路:1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/
import java.io.*;
class CopypicDemo 
{
	public static void main(String[] args) 
	{
		FileInputStream fis=null;
		FileOutputStream fos=null;
		try
		{
			fis=new FileInputStream("test.jpg");//定义读取的源文件
			fos=new FileOutputStream("shuchu.jpg");
			int num=0;          //用以判断输入流是否为空
			byte[] bt=new byte[1024];        //定义一个字节数组用以装临时数据
			while ((num=fis.read(bt))!=-1)        //fis为-1说明已经读完源文件
			{
				fos.write(bt,0,num);//向目标文件写入数据
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读取失败");
		}
		finally
		{
			try
			{
				if (fis!=null)//防止fis初始化失败却被关闭
				{
					fis.close();
				}
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if (fos!=null)
				{
					fos.close();
				}
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入关闭失败");
			}
		}
		System.out.println("Hello World!");
	}
}

五.IO-转换流

       通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。

也就是readLine方法。能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

readLine方法是字符流BufferedReader类中的方法。而键盘录入的read方法是字节流InputStream的方法。那么能不能将字节流转成字符流在使用字符流缓冲去的readLine方法呢?

       Java为我们提供了转换流实现此类需求

实例:
import java.io.*;

class  TransStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		
		BufferedReader bufr = 
		new BufferedReader(new InputStreamReader(System.in));//实现字节流向字符流的转换,就可以利用BufferedReader的读取一行的方法,提高效率
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
		String line = null;
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))//当输入over时结束整个程序
				break;
			bufw.write(line.toUpperCase());//变大写
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
	}
}

六.流操作规律

       流操作的基本规律:在实际应用过程中,最痛苦的就是流对象有很多,不知道该用哪一个,可以根据下面的方法进行分析明确:

1,明确源和目的。

       源:输入流。InputStream  Reader

       目的:输出流。OutputStream  Writer

2,操作的数据是否是纯文本。

       是:字符流。

       不是:字节流。

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

       通过设备来进行区分:

       源设备:内存,硬盘。键盘

       目的设备:内存,硬盘,控制台。

例如:将一个文本文件中数据存储到另一个文件中。复制文件,可以采用以下步骤进行分析:

源:因为是源,所以使用读取流,在InputStream Reader 中选择一个,再看是不是操作文本文件,是!这时就可以选择Reader这样体系就明确了,接下来明确要使用该体系中的哪个对象。明确读取的设备是硬盘,Reader体系中可以操作文件的对象是 FileReader,最后看是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.

       FileReader fr = new FileReader("a.txt");

       BufferedReader bufr = new BufferedReader(fr);

再看目的,是硬盘上另一个文件,所以可以选择OutputStream或者 Writer,是否是纯文本,是!那么选择WriterWriter体系中可以操作文件的对象FileWriter。再看是否需要提高效率:是!加入Writer体系中缓冲区 BufferedWriter

       FileWriter fw = new FileWriter("b.txt");

       BufferedWriter bufw = new BufferedWriter(fw);

 

再例如:将键盘录入的数据保存到一个文件中。这个需求中有源和目的都存在。那么分别分析如下:

       源:InputStream Reader

       是不是纯文本?是!Reader

       设备:键盘。对应的对象是System.in.

       不是选择Reader吗?System.in对应的不是字节流吗?

       为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。

       所以既然明确了Reader,那么就将System.in转换成Reader

       用了Reader体系中转换流,InputStreamReader

       InputStreamReader isr = new InputStreamReader(System.in);

 

       需要提高效率吗?需要!BufferedReader

       BufferedReader bufr = new BufferedReader(isr);

       目的:OutputStream  Writer

       是否是存文本?是!Writer

       设备:硬盘。一个文件。使用 FileWriter

       FileWriter fw = new FileWriter("c.txt");

       需要提高效率吗?需要。

       BufferedWriter bufw = new BufferedWriter(fw);

实例代码:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
class  TransStreamDemo2
{
	public static void main(String[] args) throws IOException
	{
		System.setIn(new FileInputStream("PersonDemo.java"));//设置输入流
		System.setOut(new PrintStream("zzz.txt"));//设置输出流

		//键盘的最常见写法。
		BufferedReader bufr = 
				new BufferedReader(new InputStreamReader(System.in));
		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());
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
	}
}

七.File对象

File类:直接用于对文件的各种操作,常见方法如下:

1,创建。

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

 

       boolean mkdir():创建文件夹。

       boolean mkdirs():创建多级文件夹。

2,删除。

       boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel

       void deleteOnExit();在程序退出时删除指定文件。

 

3,判断。

       boolean exists() :文件是否存在.

       isFile():

       isDirectory();

       isHidden();

       isAbsolute();

 

4,获取信息。

       getName()

       getPath()

       getParent()

       getAbsolutePath()

       long lastModified()

       long length()

实例:用File类实现显示某个目录下的文件列表

/*
需求:1.用File对象将指定目录“f:\\”下的各个文件以及子目录获取出来,并将路径打印在控制台
	  2.用File对象的list方法将“D:\\安装文件”下的rar文件找出来,并获得抽象路径名列表,打印到控制台
思路:1.用File对象的list方法可以返回指定目录下的文件及子目录信息,可以用file数组先存入这些数据,再遍历打印
      2.list的过滤器FilenameFilter有两个参数,File对象,要找的文件名String name,可以自己定义匿名内部类,覆盖accept方法
*/
import java.io.*;
class FileDemo2 
{
	public static void getInfo(String str)
	{
		File c=new File(str);           //通过传递路径字符串获取一个File对象
		String[] fi=c.list();			//list方法返回一个String类型的数组
		for (String f:fi)
		{
			System.out.println(f);       //将所有的文件或子目录抽象路径打印出来 
		}

	}
	public static void searchf()
	{
		File dir=new File("D:\\安装文件");
		String[] str=dir.list(new FilenameFilter()//用过滤器过滤出需要的文件名
		{
			public boolean accept(File f,String name)
			{
				System.out.println(f.getName());
				return name.endsWith("rar");
			}
		});
		for (String s:str )
		{
			System.out.println(s);
		}

	}
	public static void searchf2()                   
	{
		File dir=new File("D:\\安装文件");
		File[] str=dir.listFiles(new FileFilter()
		{
			public boolean accept(File c)
			{
				System.out.println(c.getName());
				return c.getName().contains("360");
			}
		}
		);
		for (File c:str)
		{
			System.out.println(c.getName()+"......"+c.length());
		}

	}
	public static void searchf3()                   //listFiles,返回的是文件以及目录的对象,可以进一步利用文件对象的各种方法来进一步操作
	{
		File dir=new File("D:\\安装文件");
		File[] str=dir.listFiles(new FilenameFilter()
		{
			public boolean accept(File c,String name)
			{
				System.out.println(c.getName());
				return name.contains("360");
			}
		}
		);
		for (File c:str)
		{
			System.out.println(c.getName()+"......"+c.length());
		}

	}
	public static void main(String[] args) 
	{
		//getInfo("F:\\java\\javastudy\\day15");
		//searchf2();
		searchf3();

		System.out.println("Hello World!");
	}
}

八.其他流对象

打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。

字节打印流:PrintStream

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

1file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

字符打印流:PrintWriter

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

1file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

4,字符输出流,Writer

 

合并流:用于合并多个流对象

实例:练习合并流的使用

/*
需求:练习合并流的使用
思路:SequenceInputStream的构造方法可以实现将枚举中的流对象合并
步骤:新建一个vector对象,此对象可以把多个读取流装起来,并通过elements方法返回到Enumeration即枚举
*/

import java.io.*;
import java.util.*;
class  SequenDemo
{
	public static void main(String[] args) throws IOException
	{
		Vector ve=new Vector();            //创建一个vector容器用于装读取流对象
		ve.add(new FileInputStream("public.txt"));
		ve.add(new FileInputStream("public2.txt"));
		ve.add(new FileInputStream("public3.txt"));
		Enumeration enu=ve.elements();         //枚举ve的元素
		SequenceInputStream seq=new SequenceInputStream(enu);  //创建合并流
		byte[] by=new byte[1024];
		int len=0;
		while ((len=seq.read(by))!=-1)
		{
			String s=new String(by);
			System.out.println(s);
		}
		System.out.println("Hello World!");
	}
}

切割流:用于把一个流分割成多个流

实例:用分割流的方法读取文件,并用合并流进行合并

/*
	需求:用分割流的方法把一个文件分成2m的小块
	思路:用一个读取流,多个写入流
	步骤:写出通常的读取写入程式,在遍历写入时加入创建新的写入流语句即可

	需求2:用合并流的方法把分割的文件再合并起来
	思路2:因为合并流,故要用到sequenceInputstream
	步骤2:用一个For循环同时新建多个读取流对象,把读取流对象装入枚举中,再用合并流合并输出


*/
import java.io.*;
import java.util.*;
class SequenDemo2 
{
	public static void splitrar() throws IOException
	{
		FileInputStream fis=new FileInputStream("yuan.rar");
		FileOutputStream fos=null;
		int len=0;                                             //用于判断读取是否结束
		int count=1;                                            //用于对分割文件进行命名
		byte[] by=new byte[1024*1024*2];
		while ((len=fis.read(by))!=-1)
		{
			fos=new FileOutputStream("yuan"+count+++".part");
			fos.write(by);
			fos.flush();
		}
		fos.close();   
		fis.close();
		
	}
	public static void merge()  throws IOException
	{
		FileInputStream fis=null;
		ArrayList<FileInputStream>  ar=new ArrayList<FileInputStream> ();
		for (int x=1; x<20; x++)
		{ 
			fis=new FileInputStream("yuan"+x+".part");
			ar.add(fis);                                //依次把多个读取流对象装入集合中
		}
		final Iterator<FileInputStream> it=ar.iterator();                      //使用迭代器取出元素,从内部类访问变量要加final
		Enumeration<FileInputStream>  enu=new Enumeration<FileInputStream>()               //覆盖枚举的两个方法返回迭代器中的元素
		{
			public boolean hasMoreElements()
			{
				return it.hasNext();
			}
			public FileInputStream nextElement()
			{
				return it.next();
			}
		};
		SequenceInputStream seq=new SequenceInputStream(enu);//再合并流
		FileOutputStream fos=new FileOutputStream("ji.rar");
		int len=0;
		byte[] by=new byte[1024];
		while ((len=seq.read(by))!=-1)
		{
			fos.write(by);
			fos.flush();
		}
		fos.close();
		seq.close();
		fis.close();

	}
	public static void main(String[] args)  throws IOException
	{
		//splitrar();
		merge();
		System.out.println("Hello World!");
	}
}


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值