黑马程序员——Java基础之IO流

------ Java培训、Android培训、iOS培训、.Net培训 、期待与您交流! -------


IO

 

一、概述:

IO流用来处理设备之间的数据传输

Java对数据的操作是通过流的方式

Java用于操作流的对象都在IO包里


流按操作数据分为两种:字节流字符流;[编码表的存在][汉字与1010之间的对应的表GBK]

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

Unicode码表,都是两个字节。优化——>Unicode-8,需要几个字节就给几个字节。

问题:如你好在不同的编码表中的二进制表示不一样,所以就会出现乱码。

根据这个问题,----融合了编码表,基于字节流——>java出现了字符流。

字节流是通用的。


IO流常用基类:

字节流的抽象基类:InputStreamOutputStream                        

字符流的抽象基类:ReaderWriter                    

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

如:InputStream的子类FileInputStream;Reader的子类FileReader

IO流都是围绕着读写展开的。


二、字符流:

类 Writer是字符流基类的一个,位于顶层。它有很多抽象类[也就是共性方法]

既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件

1、写入字符流步骤

【题目】——>需求:在硬盘上创建一个文件,然后写入一些文字数据。

   ——>找到一个专门用于操作文件的writer子类对象。FileWriter。后缀名是父类名,前缀名是该流对象的功能

   ——>步骤:①创建一个FileWriter对象,该对象一旦被初始化就必须要明确被操作的文件。

  而且该文件会被创建到指定目录下。如果该目录下已有同名文件将被覆盖。[小心]

       其实该步就是在明确数据要存放的目的地。

Filewriter fw = new FileWriter(“demo.txt”);

调用write方法,将字符串写入到流中。//缓冲的

fw.write(“abcde”);

刷新流对象中的缓冲中的数据。将数据刷到目的地中。

fw.flush();

fw.writer(“haha”);

fw.flush(“”)

fw.close();//关闭流资源,但是关闭之前会刷新一下缓冲,将数据植入目的地。

close()flush()的区别:

flush():将缓冲区的数据刷到目的地中后,流可以使用。

close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。


调用了底层创建文件的方法,都在调用资源,所以都会发生IO异常。

凡是可以和设备数据发生数据关系的,无论读写创建。必须执行close,关闭资源

 

IO异常的处理方式:

IO异常很常见,如何处理呢?抛是不合适的。

关闭流时必须先判断文件是否为null,不为null时才能关闭。

IO异常处理基本方式:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件
		//而且该文件会被创建到指定目录下,如果该目录下已有同名文件,将被覆盖
			FileWriter f = null;//在外面引用
			try{
			f = FileWriter("nihao.txt");
			f.write("黑马欢迎你!");
			}  //在try内初始化
			catch(IOException e)
			{
				System.out.println("捕获的异常是:"+e.toString());
			}
			finally{
				try{
					if(f!=null) 
						f.close();
					} 
				catch(IOException e)
				{
					System.out.println(“finally:”+e.toString());
					}
				}
			}
}//IO异常的基本处理方式结束。

/*对已有文件的数据续写:
对上述程序只需要需改第一句话:
FileWriter f = new FileWriter(“nihao.txt,true”);
传递true参数,代表不覆盖已有的文件,并在已有文件的末尾进行数据续写
f.write(“shijie\r\ndiqiu”);//\n在window记事本里不可以识别。所以是\r\n。
*/



2、读取字符流步骤:

Reader

读是不需要“刷”,没有读字符串的。流的好处是几乎都是对应的。

字符流都有默认编码。-----系统所有的如GBK

构造:指定文件。

步骤:

Public int read() throws IOException[简介]

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

返回:作为整数读取的字符,范围在0~65535之间,如果已经到达末尾,就返回-1.

①创建一个文件读取流对象,和指定名称的文件相关联。

  要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException

FileReader fr = new FileReader(“nihao.txt”);

②调用读取流对象的read方法。

int ch = fr.read();//reader()一次读一个字符,而且会自动往下读

System.out.println(“ch=”+(char)ch1); 

③关闭流

fr.close();

循环读一:int ch= 0;

//reader()一次读一个字符,而且会自动往下读,到达末尾返回-1

while((ch=fr.read())!=-1){

System.out.println((char)ch);

//优化的写法。

 二:while(true){

int ch = fr.read();

if(ch==-1)break;

System.out.println(“ch=”+(char)ch);

}


 

public int read(char[] cbuf) throws IOException[简介]

将字符读入数组。在某个输入可用、发生IO错误或者已到达流的末尾前,此方法一直阻塞。

参数:cbuf-目标缓冲区  返回:读取的字符数,如果已到达流的末尾,则返回-1


①定义一个字符数组,用于存储读到的字符。该read(char[])返回的是读到字符个数。

char[] buf = new char[3];//通常我们定义缓冲区的大小为1024=2k,即char[1024]

int num = fr.read(buf);//把流关联的数据读取一个存储到数组里面,多次读取的存放到的都是同一个数组,所以有覆盖现象。

SOP(“num=”+num+”....”+new String(buf));

fr.close();

循环形式:

int num = 0;

while((num=fr.read(buf))!=-1){

System.out.println(new String(buf,0,num));

}

----第一种方法:读一个打一个

----第二种方法:读一个村一个,一次性打印。

/*练习:读取一个.java文件,并打印在控制台上。
   //将C盘一个文本文件复制到D盘。
--->复制原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
--->步骤:1、在D盘创建一个文件、用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据的存储。
4,关闭资源。*/
//方法一:从C盘读取一个字符,就往D盘写一个字符
public static void copy_1() throws IOException{
//创建目的地
FileWriter fw = new FileWriter(“RuntimeDemo.txt”);
//与已有文件关联
FileReader fr = new FileReader(“RuntimeDemo.java”);
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);	//核心所在
}fw.close();fr.close();}

//方法二:从C盘读取后一次性写入
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);
//把数组写入流的缓冲区,从0脚标开始,长度为len
			}
		}
		catch(IOException e){
			throw new RuntimeException("读写失败");
		}
		finally{
			if(fr!=null)
				try{
					fr.close();
				   }
				catch(IOException e){}
			if(fw!=null)
				try{
					fw.close();
				   }
				catch(IOException e){}
			}//重点掌握!!!
		}
	}

图解:

黑马程序员——Java基础之IO流_第1张图片



三、字符流的缓冲区——BufferedReaderBufferedWriter

缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象。即先将流对象初始化到构造函数中。 

缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。

对应类:

|BufferedWriter

|BufferedReader

缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强

--->没有缓存时,磁头在硬盘上不停的切换。

字符写入流缓冲区BufferedWriter 

public class BufferedWriter  extends Writer

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓 冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。该类提供了 newLine()  法,它使用平台自己的行分隔符概念。

构造:BufferedWriter(Writer out)创建一个使用默认大小输出缓冲区的缓冲字符输出流。需要有流对象

 BufferedWriter(Writer out,int sz)创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

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

换行:原始为

private static final String LINE_SEPARATORSystem.getProperty(“line.separator”);

bufw.write(“abcdef”+LINE_SEPARATOR+”hahaha”);

新方法:

bufw.write(“abcdef”); bufw.newLine(); bufw.write(”hahaha”); newLine()只在BufferedWrite中有。。非替代方案。是跨平台通用的换行


字符读取流缓冲区BufferedReader:

public class BufferedReader extends Reader

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和的高效读取。

构造:BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。

   BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。

步骤:

FileReader fr = new FileReader(“buf.txt”);//创建一个读取流对象和文件相关联
 BufferedReader bufr = new BufferedReader(fr);
 //使用缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数
 String s1 = bufr.readLine();//当读到末尾则返回null,一行一行的读取。方便对文本数据的获取。
 bufr.close();//关闭缓冲区

——> readLine方法的原理:无论读一行,还是获取多个字符。其实最终都是在在硬盘上一个 一 个的读取,所以最终使用的还是read方法一次读一个的方法。
模拟readLine();代码:

public String myReadLine() throws IOException{//谁调用谁处理
	//定义一个临时容器,原BufferReader封装的是字符数组。
	//为了演示方便,定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
	StringBuilder sb = new StringBuilder();
	int ch = 0;
	while((ch=r.read())!=-1){
		if(ch=='\r')
			continue;
		if(ch=='\n')
			return sb.toString();
		else
			sb.append((char)ch);
	}
	if(sb.length()!=0)
		return sb.toString(); //有问题的小地方,避免漏掉最后一行
	return null;
	}
	public void myClose() throws IOException{
		r.close();
	}
}
//[很重要!]

自定义BufferedReader:

/*
需求:根据readLine方法原理,模拟BufferedReader写一个自己的MyBufferedReader
*/
import java.io.*;
//自定义缓冲类
class MyBufferedReader extends Reader
{
	private Reader r;//定义接收的流对象
	MyBufferedReader(Reader r)
	{
		this.r=r;
	}
	//自定义整行读取
	public String myReadLine()throws IOException
	{
		//创建一个容器,用来存储一行的字符
		StringBuilder sb =new StringBuilder();
		//一个字符一个字符读取
		for (int ch=0;(ch=r.read())!=-1 ; )
		{
			if(ch=='\r')//如果遇到换行符,则继续
				continue;
			if(ch=='\n')//如果遇到回车符,表示该行读取完毕
				return sb.toString();
			else
				sb.append((char)ch);//将该行的字符添加到容器
		}
		if(sb.length()!=0)//如果读取结束,容器中还有字符,则返回元素
			return sb.toString();
		return null;
	}

	//复写父类中的抽象方法
	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return r.read(cbuf,off,len);
	}

	//复写父类的close方法
	public void close()throws IOException
	{
		r.close();
	}
}
//测试MyBufferedReader
class  MyBufferedReaderDemo
{
	public static void main(String[] args) 
	{
		MyBufferedReader mbr=null;
		try
		{
			mbr=new MyBufferedReader(new FileReader("BufferedCopyDemo.java"));
			for (String line=null;(line=mbr.myReadLine())!=null ; )
			{
				System.out.println(line);//显示效果
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读取数据失败");
		}
		finally
		{
			try
			{
				if(mbr!=null)
					mbr.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取流关闭失败");
			}
		}	
	}
}

设计模式之 装饰设计模式:

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,

基于已有的功能,并提供加强功能。那么自定义的该类成为装饰类。
装饰类通常会通过构造方法接受被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

 

装饰和继承有什么区别呢?

装饰和继承都能实现一样的特点:进行功能的扩展增强。

Writer

|--TextWriter:用于操作文本

|--MediaWriter:用于操作媒体。

想要对操作的动作进行效率的提高。

按照面向对象,可以通过继承对具体的进行功能的扩展。 

效率提高需要加入缓冲技术。

Writer

|--TextWriter:用于操作文本

|--BufferTextWriter:加入了缓冲技术的操作文本的对象。

|--MediaWriter:用于操作媒体。

|--BufferMediaWriter:加入了缓冲技术的操作媒体的对象。


如果这个体系进行功能扩展,有多了流对象。

那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现只为提高功能,进行的继承,

导致继承体系越来越臃肿。不够灵活。 

 

重新思考这个问题?

既然加入的都是同一种技术--缓冲。

前一种是让缓冲和具体的对象相结合。 

可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。

class Buffer{

Buffer(TextWriter w){}

Buffer(MediaWirter w){}

}

class BufferWriter extends Writer{

BufferWriter(Writer w){}

}

Writer

|--TextWriter:用于操作文本

|--MediaWriter:用于操作媒体。

|--BufferWriter:用于提高效率。

装饰比继承灵活。


特点:装饰类和被装饰类都必须所属同一个接口或者父类。 

public int read(char[] cbuf , int off, int len) throws IOException{

return r.read(cbuf, off, len)

//继承Reader之后,需要复写其抽象方法,懒的写时则可以让传进来的子类对象来实现。

BufferedReader是一个可以进行对read类当中的子类对象进行功能增强的一个缓冲区。

public class LineNumberReader extends BufferedReader[功能增强的类](多了行号功能)

跟踪行号的缓冲字符输入流。默认情况下,行编号从 开始

构造:LineNumberReader(Reader in) 使用默认输入缓冲区的大小创建新的行编号 reader

方法: getLineNumber() 获得当前行号。

  setLineNumber(int lineNumber) 设置当前行号。

 LineNumberReader原理:[自写]优化

class MyLineNumberReader{
	private Reader r ;
	private int lineNumber;
	MyLineNumber(Reader r){
		//super(r);
		this.r=r;
	}
	public String myReadLine() throws IOException{
		return super.myReadLine();
		lineNumber++;
		StringBuilder sb = new StringBuilder();
		int ch = 0;
		while((ch=r.read())!=-1){
			if(ch=='\r') 
				continue;
			if(ch=='\n') 
				return sb.toString();
			else sb.append((char)ch);
		}
		if(sb.length()!=0) 
			return sb.toString();
			return null;
		}
	public void getLineNumber(){
		return lineNumber;
	}
	public void setLineNumber(int lineNumber){
		this.lineNumber = llineNumber;
	}
	public void myClose(){
		r.close();//关闭流
	}
}

三、字节流:

1、字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。

2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。

3、读写字节流:InputStream   输入流(读)

                             OutputStream  输出流(写)

4、为何不用进行刷流动作:

        因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。所以可直接将字节数据写入到指定文件中。

5InputStream特有方法:

        int available();//返回文件中的字节个数

注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。

/*
 * 练习:复制一个图片
 * 思路:图片是媒体文件,所有用到字节流(不能拿字符流来处理媒体文件,字符流自能用来处理文字文件)
 * 1,用字节读取流对象和图片关联
 * 2,用自己写入流对象创建一个图片文件,用于存储获取到的图片
 * 3,通过循环读写,完成数据的存储
 * 4,关闭资源
 */


package stream;
import java.io.*;
public class CopyPicture {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		FileOutputStream fos=null;
		FileInputStream fis=null;
		try{
			 //指定复制的路径 
			fos=new FileOutputStream("C:\\Users\\zhudehao\\Desktop\\2.jpg");
			//关联要复制的文件  
			fis=new FileInputStream("C:\\Users\\zhudehao\\Desktop\\1.jpg");
			
			//建立数组,空间大小可以定义成1024*n,n为整数
			byte[] buf=new byte[1024];
			int len=0;
			while((len=fis.read(buf) )!= -1){
				fos.write(buf,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 e1){
					throw new RuntimeException("写入关闭失败");
			}
		}
	}
}




字节流缓冲区:

类似与字符流的缓冲区,同样是提高了字节流的读写效率。

BufferedOutputStream

BufferedIntputStream

1、读写特点:

        read():会将字节byte型值提升为int型值

        write():会将int型强转为byte型,即保留二进制数的最后八位。

2、原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。

        1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区。

        2)循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。

        3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。

        4)取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。

        5)当文件中的全部数据都被读取出时,read()方法就返回-1

3、自定义读取字节流缓冲区

自定义缓冲区存储代码【难点】

class MyBuffereInputStream{//包装类
	private InputStream in;
	private byte[] buf = new byte[1024*4];
	private int pos=0,count=0;
	MyBuffereInputStream(InputStream  in){
		this.in = in;//通过in对象读取硬盘上数据,并存储buf中。
	}
	public int myRead() throws IOException{//一次读一个字节,从缓冲区(字节数组)获取。
		if(count==0){//byte:-1---->int:-1。会被提升。
			count=in.read(buf);
			pos=0 ; 
			byte b=buf[pos] ; 
			count-- ; 
			pos++ ; 
			return b&255 ;
		}
		else if(count>0){
			byte b = buf[pos] ; 
			count-- ; 
			pos++ ; 
			return b&0xff ;
		}
		return -1;
	}
	public void myClose()throws IOException{
		in.close();
	}
}//很快!问题来了,为何复制为0kb的文件?(代码没问题)

解释:MP3数据为二进制数据,1010...组成。

---->测试:SOP(“第一个字节:”+bufis.myRead()); --------> 结果为-1

1111-1111-1

复制失败的原因是读到了11111111,那么我们在将byte提升int时,在前面不补1,补0就可以避免次情况。

 怎么补0?做法:将int-1255相与即可

问题:1字节--->4字节。空间为何没有变大?Write方法在做强转动作,只写最低八位。

Read提升,Write降低


小结:

字节流: FileInputStream 字符流:FileReader

    FileOutputStreamFIleWriter

BufferedInputStreamBufferedReader

BufferedOutputStreamBufferedWriter

读取键盘录入:System.out:对应的是标准输出设备,控制台。

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

main(){

//public static final InputStream in标准输入流。此流已打开并准备提供输入数据。

//通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。阻塞。

InputStream in = System.in;

int by = in.read();

System.out.println(by);

}//回车也是录入。\r--13   \n---10


/*
 * 需求:通过键盘录入数据,当录入一行数据后就将该行数据进行打印,如果录入数据是over,则停止录入
 */

package stream;
import java.io.*;
public class ReadIn {

/*	
 * 方法1:使用字节流操作
 * public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		InputStream in=System.in;//获取键盘录入对象
		StringBuilder sb=new StringBuilder();//建立缓冲区
		while(true){
			int ch=in.read();
			if(ch=='\r')
				continue;
			if(ch=='\n'){
				String s=sb.toString();
				if("over".equals(s))//识别到"over"后跳出循环,结束
					break;
				System.out.println(s.toUpperCase());
				sb.delete(0,sb.length());//打印完毕后清空缓冲区
			}
			else
				sb.append((char)ch);
		}
	*/	

为了使可以使用readLine()方法,所以需要字节流与字符流转换

public class InputStreamReader extends Reader

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。 它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

构造:InputStreamReader(InputStream in)创建一个使用默认字符集的 InputStreamReader。接受一个输入流

 

public class OutputStreamWriter extends Writer

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成 字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

构造:OutputStreamWriter(OutputStream out)创建使用默认字符编码的 OutputStreamWriter。收一个输出流

//方法2:使用readLine()方法
	public static void main(String[] args) throws IOException {
		/*InputStream in=System.in;//获取键盘录入对象
		//将字节流对象转换成字符流对象,使用转换流:InputStreamReader()
		InputStreamReader isr=new InputStreamReader(in);
		//为提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
		BufferedReader bufr=new BufferedReader(isr);
		*/
		//以上三句可以简写成一句:——>键盘录入最常见写法!!!
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		
		/*
		 * OutputStream out=System.out;
		 * OutputStreamWriter osw=new OutputStreamWriter(out);
		 * BufferedWriter bufw =new BufferedWriter(osw);
		 * 以上三句可以简写成一句:
		 */
		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();	
		}	
	}
}//要求自己可以写得出来!!![读一行的原理]。



四、流操作的基本规律:

键盘录入最常见写法:【掌握!!】

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

控制台-->文本

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(”1.txt”)));

文本--->控制台

BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream(“1.txt”)));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));


IO流的操作规律总结:

流对象有很多,何时该用哪个对象

1,明确体系:源和目的: 源:输入流:InputStream Reader

目的:输出流:OutputStreamWriter

2,明确数据:因为数据分两种:字节,字符。

数据源:是否是纯文本数据呢?是:Reader 否:InputStream

数据流:是:Writer 否:OutputStream

到这里就可以明确具体要使用哪一个体系了。

剩下的就是要明确使用这个体系中的哪个对象。

3,明确设备

数据源: 键盘:System.in

硬盘:FileXXX

内存:数组。

网络:socket  socket.getInputStream();

数据汇: 控制台:System.out

硬盘:FileXXX

内存:数组

网络:socket socket.getOutputStream();

4,明确额外功能:  ①,需要转换?是,使用转换流。InputStreamReader OutputStreamWriter

,需要高效?是,使用缓冲区。Buffered

,需要其他?

——————————————————————————————————————————————

1,复制一个文本文件。

,明确体系: 源:InputStream Reader

目的:OutputStream Writer

,明确数据: 源:是纯文本吗?是 Reader

目的;是纯文本吗?是 Writer

,明确设备: 源:硬盘上的一个文件。 FileReader

目的:硬盘上的一个文件。FileWriter

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

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

,需要提高效率?需要,高效,使用buffer

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));

2,读取键盘录入,将数据存储到一个文件中。

,明确体系: 源:InputStream Reader

目的:OutputStream Writer

,明确数据: 源:是纯文本吗?是 Reader

目的;是纯文本吗?是 Writer

,明确设备: 源:键盘,System.in

目的:硬盘,FileWriter

InputStream in = System.in;

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

,需要额外功能吗?需要,因为源明确的体系时Reader。可是源的设备是System.in

所以为了方便于操作文本数据,将源转成字符流。需要转换流。InputStreamReader

InputStreamReader isr = new InputStreamReader(System.in);

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

需要高效不?需要。Buffer

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));

3,读取一个文本文件,将数据展现在控制台上。

,明确体系: 源:InputStream Reader

目的:OutputStream Writer

,明确数据: 源:是纯文本吗?是 Reader

目的;是纯文本吗?是 Writer

,明确设备: 源:硬盘文件,FileReader

目的:控制台:System.out

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

OutputStream out = System.out;

,需要额外功能?

因为源是文本数据,确定是Writer体系。所以为了方便操作字符数据,

需要使用字符流,但是目的又是一个字节输出流。

需要一个转换流,OutputStreamWriter

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

OutputStreamWriter osw = new OutputStreamWriter(System.out);

需要高效吗?需要。

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

4,读取键盘录入,将数据展现在控制台上。

,明确体系: 源:InputStream Reader

目的:OutputStream Writer

,明确数据: 源:是纯文本吗?是 Reader 

目的;是纯文本吗?是 Writer

,明确设备: 源:键盘:System.in

目的:控制台:System.out

InputStream in = System.in;

OutputStream out = System.out;

,需要额外功能吗?

因为处理的数据是文本数据,同时确定是字符流体系。

为方便操作字符数据的可以将源和目的都转成字符流。使用转换流。

为了提高效率,使用Buffer

BufferedReader bufr  =new BufferedReader(new InputStreamReader(Systme.in));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

5,读取一个文本文件,将文件按照指定的编码表UTF-8进行存储,保存到另一个文件中。

,明确体系: 源:InputStream Reader

目的:OutputStream Writer

,明确数据: 源:是纯文本吗?是 Reader

目的;是纯文本吗?是 Writer

,明确设备: 源:硬盘:FileReader.

目的:硬盘:FileWriter

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

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

,额外功能:

注意:目的中虽然是一个文件,但是需要指定编码表。

而直接操作文本文件的FileWriter本身内置的是本地默认码表。无法明确具体指定码表。

这时就需要转换功能。OutputStream Writer,而这个转换流需要接受一个字节输出流,而且

对应的目的是一个文件。这时就使用字节输出流中的操作文件的流对象。FileOutputStream.

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

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");

需要高效吗?

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = 

new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8"));



目前为止,10个流对象重点掌握。

字符流:

FileReader  BufferedReaderInputStreamReader

FileWriterBufferedWriterOutputStreamWrier

字节流:

FileInputStreamBufferedInputStream

FileOutputStreamBufferedOutputStream

 

System.setOut(new PrintStream(“zz.txt”));修改目的

System.setIn(new FileInputStream(“ni.java”));修改源|||同时修改就是复制。

 

catch(Exception e){e.printStackTrace(new PrintStream(“1.txt”))}

System.setOut(new PrintStream(“exex.log”));e.printStackTrace(System.out);

//修改异常报告显示目的地。

Properties prop = System.System.getProperties();

prop.list(new PrintSteam(“sysinfo.txt”));//打印虚拟机启动时的状态

 

 

五、File:

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

——》File对象可以作为参数传递给流的构造函数。

File f1 = new File(“c:\\”,“1.txt”);//表示可以将一个已存在或者不存在的文件或者目录封装成file对象。

File f2= new File(“c:\\nihao\\1.txt”);//表示系统分隔符:+System.getProperty(“file.separator”)+

而现在则是:file.separator字符串类型,file.separatorChar字符类型。系统默认名称分隔符。(;:路径分隔符)


File类常见方法:

1,创建。boolean createNewFile();在指定位置创建文件,如果存在该文件则创建失败返回false

和输出流不一样,输出流建立则是覆盖[强制建立]。(临时文件创建..

注意:

File file = new File(“adn”);//

File file = new File(“abd.txt”)//创建文件

创建目录:file.mkdir();一级file.mkdirs();多级。boolean类型。

2,删除

boolean delete();删除调用此方法的文件对象。

boolean deleteOnExit();同上。在退出系统时删除指定文件。(一般使用在临时文件)

3,判断

boolean exists();文件是否存在[使用频率很高,文件存在了才可以使用流,不然就会抛出异常]

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

int compareTo();比较

isFile();isDirectory();对一个文件的判断是文件或目录时,必须判断是否存在通过exists();

isHidden();是否隐藏。Java在取文件时不判断是不是隐藏,所以程序员需要判断。。

isAbsolute();判断是否为绝对路径。[不判断文件是否存在]

4,获取信息

getName();getPath(); getAbsolutePath();[路径和文件是否存在无关]length();[返回为long]

lastModified();获取最后一次修改的时间;返回为long,毫秒值。

getParent();//返回的是绝对路径中的父目录[调用者上一层的目录]。不然返回为null

f1.renameTo(f2);返回为boolean,类似不同文件里时剪切粘贴。

listRoots();获取盘符,单个。

list();获取指定目录下的所有文件以及文件夹,还含有隐藏文件。。路径不对则为null

listFiles();返回为File[]对象数组,可操作。开发使用这个。

List(FilenameFilter filter);文件名过滤器FilenameFilter是接口,实现它的实例可以过滤..

File dir=new File(“d:\\java123\\day23”);
String[] arr=dir.list(new FilenameFilter(){
	public boolean accept(File dri,String name){
		SOP(dir+"---"+name);
		return name.endWith(“.bmp”);
	}
});}//匿名类的使用,和dir,name 都是什么都可以知道。


/*
 * 列出指定目录文件或者文件夹,包含子目录中的内容,也就是列出指定目录下的所有内容
 */

package stream;
import java.io.*;
public class FileDemo3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		File dir=new File("C:\\Users");
		showDir(dir);
	}
	public static void showDir(File dir){
		System.out.println(dir);
		File[] files=dir.listFiles();
		for(int x=0;x

递归:1,限定条件,也就是判断是否进行递归的条件。

void showA(){showB();Sop(A);}

//求和:

getSum(int n){if(n==1) 

void showB(){showC();Sop(B);}return 1;

void showC(){Sop(C);}//调用A

return n+getSum(n-1);}//递归会出现内存溢出

  2,要注意递归的次数,避免内存溢出。(栈内存)

/*删除一个带有内容的目录,删除原理:从里往外删。递归使用。*/

C盘里的隐藏文件是不能被java访问。

public static void removeDir(File dir){
	File[] files=dir.listFiles();
	for(int x=0;x

/*将一个指定目录下的java文件的绝对路径,存储到一个文本文件中*/

/*
 * 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中
 * 步骤:
 * 1,对指定的目录进行递归
 * 2,获取递归过程所有的Java文件路径
 * 3,将这些路径存储到集合中
 * 4,将集合中的数据写入到一个文件中
 */

package stream;
import java.util.*;
import java.io.*;
public class JavaFileList {

	public static void main(String[] args)throws IOException {
		// TODO Auto-generated method stub
		File dir=new File("d:\\abc");
		List list =new ArrayList();
		fileToList(dir,list);//将集合[源],写到本地硬盘中。
		File file =new File(dir,"javalist.txt");
		writeToFile(list,file.toString());//下方法演示。这里字符串可以,file对象也可以的。
		

	}
	public static void fileToList(File dir,List list){
		File[] files=dir.listFiles();
		//高级for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)
		for(File file:files){
			if(file.isDirectory())
				fileToList(file, list);
			else{
				if(file.getName().endsWith(".java"))
					list.add(file);//将file添加进list集合
			}
		}
	}
	
	public static void writeToFile(List list,String javaListFile) throws IOException{
		BufferedWriter bufw=null;
		try{
			bufw=new BufferedWriter(new FileWriter(javaListFile));
			for(File f : list){
				String path = f.getAbsolutePath();
				bufw.write(path);
				bufw.newLine();
				bufw.flush(); 
				}
			}
		catch(IOException e){
			throw e;
			}//不想抛,可以抛RuntimeException;
		finally{  
				try{
						if(bufw!=null) bufw.close();
					}
				catch(IOException e){
					throw e;
					}
				}
		}
}

Propertieshashtable的子类。

它具备map集合的特点。而且它里面存储的键值对都是字符串。

是集合中和IO技术相结合的集合容器。该对象的特点:可以用于键值对形式的配置文件。

那么在加载数据时,需要数据有固定格式:键——》值。

//设置和获取元素
public static void setAndGet(){
	Properties prop=new Properties();
	prop.setProperty(“zhangsan”,”20”);
	prop.setProperty(“lisi”,”40”);
	System.out.println(pron);//键值对信息;
	String value=prop.getProperty(“lisi”);//由键获取值
	System.out.println(value);
	prop.setProperty(“lisi”,89);//修改,
	Set name=prop.stringPropertyNames();//全部获取键
	for(String s:name){
		System.out.println(s+”----”+prop.getProerty(s));//键值对
		}
	}
}

/*演示,如何将流中的数据存储到集合中,想要将info.txt中的键值数据存到集合中进行操作*/

思路:1,用一个流和info.txt文件关联
2,读取一行数据,将该行数据用”=”进行切割
3,等号左边作为键,右边作为值。存入到Properties集合中即可。
BufferedReader bufr = new BufferedReader(new FileReader(“info.txt”));
String lline=null;
Properties prop=new Properties();
while((line=bufr.readLine())!=null){
String[] arr=line.spit(“=”);System.out.println(arr[0]+”...”+arr[1]);
prop.setProperty(arr[0],arr[1]);
}
bufr.close();
System.out.println(prop);


Load();方法的原理:
Properties prop=new Properties();  
FileInputStream fis=new FileInputStream(“info.txt”);
prop.load(fis); //将流中数据加载进集合。[加载过程(已封装)]system.out.println(prop);
或者prop.list(System.out);
---->当信息错误时,可以:prop.setProperty(“wangwu”,”29”);//修改内存结果,不改变原文件.
----->针对上面的错误:FileOutputStream fos=new FileOutputStream(“info.txt”);
Prop.store(fos,”nihaoheima!”)//#都是注释信息不会被键值对加载,可修改源文件[主]


/*用于记录应用程序运行次数,如果使用次数已到,就给出注册提示*/

思路:很容易想到的是:计数器//可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。///可是随着该应用程序的退出,该计数器也在内存中消失了。///下一次启动改程序,又重新开始从0计数,这样不是我们想要的。[问题所在]

我们的任务是 :程序即使结束,该计数器的值也存在,下次程序启动在会先加载该计数器的值并加1后在重新存储起来。所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。这样便于阅读数据,并操作数据。键值对数据是map集合,数据时以文件形式存储,使用io技术。那么map+io--->Properties。配置文件可以实现应用程序数据的共享。

class RunConunt{
	public static void main(String[]args) throws IOException{
		Properties prop=new Properties();
		File file=new File("count.ini");//封装成对象
		if(!file.exists()) //不存在则创建,可以对文件进行判断,但是流做不了。
			file.createNewFile();
		FileInputStream fis = new FileInputStream(file);//搞一个文件读取流--扩展名就是集合名,无文件所以先把文件封装成对象,避免异常。
		prop.load(fis);//把流中的数据加载到集合当中
		int count=0;
		String value = prop.getProperty("time");//获取次数,time就是那个键,然后获取那个值。
		if(value!=null)//第一次没有time,则会返回null,
			count=Integer.parseInt(value);//count记录住这个次数
			if(count>=5){///对次数判断
				System.out.println("你好,使用次数已到,请注册!");
				return;
			}
			count++;//自增,增完了就需要将这个数据从新存回去。
			prop.setProperty("time",count++);//存回去。
			FileOutputStream fos=new FileOutputStream(file);//存回去的数急写回去。
			prop.store(fos,"");//修改
			fos.close();
			fis.close();
			}
	}

早期的,现在的,这个文件放在System32中。

遇到这样的键值对直接使用Properties即可。

dom4j==dom for java,专门处理XML文档中的数据。


打印流:

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

凡是可以和文件相关联的流对象都是比较重要的流对象。

字节打印流:

PrintStream

构造函数可以接受的参数类型

1file对象File2字符串路径String3字节输出流  OutputStream 

字符打印流:

PrintWriter[很重要,开发常用,将数据一条一条的打印到客户端]

构造函数可以接受的参数类型

1file对象File   2字符串路径String    3字节输出流  OutputStream   4字节输出流  Writer


//首先,将键盘录入的数据进行操作:
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));//保持数据的原样输出可以	
//使用打印流的方法:
PrintWriter out = PrintWriter(Syatem.out new FileWriter("1.txt",true));//自动刷新	可以将文件封装成流对象
//将数据直接打印到控制台上,可以直接接收字节流。
String 	line=null;
while((line=bufr.readLine())!=null){
	out.writeprintln(line.toUpperCase());
}
out.close();
bufr.close();
//需要out.flush(),但是当write为println时,则不需要out.flush();(针对流,刷新)。提高效率可以将其	封装在BufferedWriter中。



序列流

SequenceInputStream:对多个流进行合并

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

构造:

SequenceInputStream(Enumeration e)
通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream  对象的 Enumeration 型参数。

import java.util.*;
import java.io.*;
public class SequenceDemo {

	public static void main(String[] args)throws IOException {
		// TODO Auto-generated method stub
		Vector v=new Vector();
		v.add(new FileInputStream("C:\\Users\\zhudehao\\Desktop\\1.txt"));//集合来存储多流
		v.add(new FileInputStream("C:\\Users\\zhudehao\\Desktop\\2.txt"));
		v.add(new FileInputStream("C:\\Users\\zhudehao\\Desktop\\3.txt"));
		
		Enumeration en=v.elements();//拿到Enumeration。这个方法要记得。把3个流对象添加到集合中
		SequenceInputStream sis= new SequenceInputStream(en);//多个流对象合并成一个
		
		FileOutputStream fos =new FileOutputStream("C:\\Users\\zhudehao\\Desktop\\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();
	}

}

 
  

切割:

/*
 * 练习:将图片切割后,合并转存到指定位置
 */

package stream;
import java.io.*;
import java.util.*;

public class SplitFile {

	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		splitFlie();
		merge();
	}
	
	//分割成碎片
	public static void splitFlie()throws IOException{
		FileInputStream fis =new FileInputStream("C:\\Users\\Lenovo\\Desktop\\1.jpg");//建立文件关联
		FileOutputStream fos=null;
		byte[] buf=new byte [1024*1024];//建立1M的缓冲区,切割区
		int len=0;
		int count=1;//被切割后的碎片文件计数器
		while((len=fis.read(buf))!=-1){
			 fos= new FileOutputStream("C:\\Users\\zhudehao\\Desktop\\"+(count++)+".part");//建立碎片流,碎片文件可以任意自定义r容易识别的后缀名
			 fos.write(buf,0,len);
		}
		fis.close();	
	}
	
	//合并
	public static void merge()throws IOException{
		//首先创建集合,Vector效率低,用ArryList
		ArrayList al=new ArrayList();
		for(int x=1;x<3;x++){
			//把碎片文件和读取流对象相关联并添加到集合中
			al.add(new FileInputStream("C:\\Users\\zhudehao\\Desktop\\"+x+".part"));
		}
		//ArrayList里面没有Enumeration,只能通过Iterator实现
		final Iterator it=al.iterator();//因为Enumeration{}是匿名内部类,所以要对访问的局部变量进行final修饰
		Enumeration en=new Enumeration(){
			public boolean hasMoreElements(){
				return it.hasNext();
			}
			public FileInputStream nextElement(){
				return it.next();
			}
		};
		SequenceInputStream sis=new SequenceInputStream(en);
		
		FileOutputStream fos =new FileOutputStream("C:\\Users\\Lenovo\\Desktop\\0.jpg");
		byte[] buf=new byte[1024];//合并区
		int len=0;
		while((len=sis.read(buf))!=-1){
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
			
	}

}


注意:切割电影,定义多个输出流对象,一个输出流对象操作100M对象。



操作对象的流被操作的对象需要实现Serializable(标记接口)

ObjectInputStream

ObjectOutputStream

以上两个方法一般成对存在

public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants

ObjectOutputStream将Java对象的基本数据类型和图形写入OutputStream。可以使用 ObjectInputStream  读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以 在另一台主机上或另一个进程中重构对象。

构造:ObjectOutputStream(OutputStream out)创建写入指定OutputStream的ObjectOutputStream.构造时要有 一个目的。

方法:write(int val)写入一个字节。writeInt(int val)写入一个32位的 int 值。[区别]

  writeObjectOverride(Object obj)子类用于重写默认writeObject方法的方法。

例子

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(“obj.txt person.object”));
oos.writeObject(new Person("lisi",29)); 
oos.close();//写对象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(“obj.txt person.object”));
Person p=(Person)ois.readObject(); 
System.out.println(p); 
ois.close();//读对象/抛异常

问题public interface Serializable类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。[没有方法,称为标记接口,就是给这个类盖个章,加了个serialVersionUID,id是给编译器使用的。每一个类在序列化的时候都希望有个标识,一个类产生一个对象之后。但是类是可以改变的,类重新编译会产生一个新的序列号。那么持久化的对象可能会和这个新的序列号不匹配。所以就是用这个id号来判断这个对象和这个类是不是用一个序列号产生的。

UID号怎么算出来的。其实是根据类中的成员计算出来的。因为类中的成员都有成员标识。成员一旦变化就无法使用原有的持久化对象。破解:不让jvm生成新的UID,自己写一个UID:

public static final long serialVersionUID=42L;那么新的类就可以对持久化对象的访问和操作了。


注意:静态是不可以被序列化:静态在方法区里面,而堆里面的数据可以被序列化。如果不想将非静态的age被序列化,那么可以加修饰:transient 保证其值在堆内存中存在,而不在文本文件中存在]

 


管道流:涉及多线程

PipedInputStream

PipedOutputStream

public class PipedInputStream extends InputStream

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据 由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream 建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓 冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在, 则认为该管道,输入输出可以直接进行连接,通过结合线程使用。

一般读取流和写入流通常之间没有关系,中间需要一个中转站:读的流将数据存到一个数组里面,然后写的流操作这个数组就行了。这个是读写操作的特点。而管道流可以使他们接在一起。一边读一边写,谁先执行?

构造:PipedInputStream(PipedOutputStream src)创建 PipedInputStream,使其连接到管道输出流 src。


import java.io.*;

class Read implements Runnable{
	private PipedInputStream in;
	Read(PipedInputStream in){//构造来一个管道
		this.in=in;
	}
	//不能抛异常,复写run方法
	public void run(){
		try{
			byte[] buf =new byte[1024];//读取数据,一个够装就不循环
			System.out.println("读取前,没有数据阻塞");
			int len=in.read(buf);//来个read往buf里面读,返回一个len
			
			System.out.println("读取数据,阻塞结束");//抢到资源但是没有数据,所以就等在这里
			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{
			System.out.println("开始写入数据,等待6秒后。");
			Thread.sleep(6000);
			out.write("pipedStream is coming!!!".getBytes());//写数据,字节流跟上.getBytes(),数据写到Read管道里面了
			out.close();
		}
		catch(IOException e){
			throw new RuntimeException("管道输出流失败");
		}
	}
}

public class PipedStreamDemo {

	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		//创建管道流
		PipedInputStream in=new PipedInputStream();
		PipedOutputStream out=new PipedOutputStream();
		//将输入流和输出流连接起来
		in.connect(out);
		//创建子类对象,将in,out传入
		Read r=new Read(in);
		Write w=new Write(out);
		
		new Thread(r).start();
		new Thread(w).start();
	}

}


RandomAccessFile

public class RandomAccessFile extends Object implements DataOutput, DataInput, Closeable

此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大 型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字 节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也 可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的 当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过  seek 方法设置。

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

其实完成读写的原理就是内部封装了字节输出流和输入流。

局限:通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式:只读r读写rw

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

如果模式rw,该对象的构造函数要操作的文件不存在,会自动创建。如果存在则不会覆盖。

方法:InputStream的方法。

seek(long pos)设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。

public class RandomAccessFileDemo {
	public static void main(String[] args) throws IOException{
		writeFile_2();
		//readFile();
		//System.out.println(Integer.toBinaryString(258));//查表,取最低八位。丢失数据。由write方法引起。
		
	}
	public static void writeFile() throws IOException{
		RandomAccessFile raf=new RandomAccessFile("ran.txt","rw");
		raf.write("李四".getBytes());
		raf.writeInt(97);//操作基本数据类型需要加Int
		raf.close(); 
	}
	public static void readFile() throws IOException{
		RandomAccessFile raf=new RandomAccessFile("ran.txt","rw");//当只读时可以屏蔽掉写的动作。
		raf.seek(8*1);//调整对象中的指针。8字节数。*n乘以n表示从第n+1个开始
		raf.skipBytes(8);//它跳过几位,只能往后跳,不能往前跳。而seek想指哪指哪
		byte[] buf=new byte[4];
		raf.read(buf);
		String name=new String(buf);//由于名字都是四个字节,所以往buf里面读一次,会往这里面装四个字节。然后把数组变成字符串。
		int age=raf.readInt();
		System.out.println("name"+name);
		System.out.println("age"+age);
		raf.close();
	}//数据都在数组里面,所有我们可以调整指针来去要取的数。不如,获取王武的名字年龄。有两种方式:①使用seek方法。②是
	public static void writeFile_2() throws IOException{
		RandomAccessFile raf=new RandomAccessFile("ran.txt","rw");
		raf.seek(8*0);//数据要分段,名字可以用16个字节存储。
		raf.write("周七".getBytes());raf.writeInt(103);raf.close();
	}
}//它可以实现数据的分段写入。【下载软件的原理,多线程下载】(一般流不行),随机读写访问。



操作基本数据类型:

DataInputStreamDataOutputStream

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

class DataStreamDemo{//记事本会把存进来的字节进行查表,翻译成字符。所以会出现乱码。
	public static void main()throws IOException{
		//writeData();
		readData();
	}
	public static void writeData() throws IOException{
		//存储数据
		DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(9887.543);
		dos.close();
	}
	public static void readData() throws IOException{
		//读取数据
		DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
		int num=dis.readInt();//按顺序读写,先写入的int先读int,否则数据出错
		boolean b=dis.readBoolean();
		double d=dis.readDouble();
		System.out.println("num"+num);
		System.out.println("b"+b);
		System.out.println("d"+d);
		
		dis.close();
	}
}//使用dis.writeUTF()写的,只能使用dis.readUTF()来读取。别的读不了。



操作数组的流对象:

ByteArrayInputStream    和    ByteArrayOutputStream      操作字节数组

CharArrayReader     与  CharArrayWrite   操作字符数组

 

public class ByteArrayInputStream extends InputStream

ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read  方法要提供的下一个字节。关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用, 而不会产生任何 IOException。处理的是数据源。所以初始化就要有一个文件存在。没有调用底层资。(在构造的时候,需要接收数据源,而且数据源是一个字节数组)

public class ByteArrayOutputStream extends OutputStream

此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增 长。可使用 toByteArray() 和 toString() 获取数据。(在构造的时候,不用定义数据目的,因为该对象 中已经内部封装了可变长度的字节数组。这就是数据目的地。)

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



class ByteArrayStream{//使用流的读写思想来操作数组。
	public static void main(String[]args){
		ByteArrayInputStream bis=new 								
		ByteArrayInputStream("ABCDEF".getBytes());//数据源
		ByteArrayOutputStream bos=new ByteArrayOutputStream();//数据目的[内存]
		int by=0;
		while((by=bis.read())!=-1){
			bos.write(by);
			}
		System.out.println(bos.size()+"----"+bos.toString());
		//bos.writeTo(new FileOutputStream("a.txt"));
//涉及异常.将缓冲区的内容写到另一个字节流中.
}
}//内存就是数组流。

可以操作字符串的流对象:StringReaderStringWriter与上面的类似。举一反三。

字符编码:

字符流的出现为了方便操作字符。更重要的是加入了人编码转换。通过子类专函流来完成:

InputStreamReader OutputStreamWriter在两个对象进行构造的时候可以加入字符集。

常见的编码表:

ASCII:美国标准信息交换码

ISO8859-1:拉丁码表,欧洲码表---用一个字节的8位表示。

GB2312:中国的中文编码表。(6000~7000

GBK:中国的中文编码表升级,融合了更多的中文文字符号。(2W

Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,java使用的就是unicode

UTF-8:最多用三个字节来表示一个字符。每一个字节都有标识头,使用非常方便。

class EncodeStream{
	public static void main(String[] args){
		//writeText();
	}
	public static void writeText() throws IOException{
		OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"UTF-8");
		osw.write("你好");//4个字节(默认GBK);6个字节按照UTF-8编码表写进去。
		osw.close();
	}
	public static void readText() throws IOException{
		InputStreamReader isr=new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");//互换
		char[] buf=new char[10];//当GBK变为UTF-8时,则会取内存六个字节去编码表里去找,位置符号为?
		int len=isr.read(buf);
		String str=new String(buf,0,len);
		System.out.println(str);
		isr.close();
	}
}//用gbk读,utf-8写.用utf-8读,gbk写.你好-??说明:①gbk编U8解②你好-浣豺YU8编gbk解》


/*有五个学生,每个学生有三门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩)输入的格式:张三,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件”stud.txt”中
 * 思路:1,描述学生对象  2,定义一个可操作学生对象的工具类
 * 步骤:	1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象
 * 2,因为学生有很多,那么就需要存储,使用到集合,因为要对学生的总分排序。
 * 3,将集合的信息写到一个文件中。	
*/
package stream;
import java.io.*;
import java.util.*;
public class StudentInfoTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//Comparator cmp=Collection.reverseOrder();//逆转比较器,从高到低排序
		//Set stus=StudentInfoTool.getStudents(cmp);
		//StudentInfoTool.write2File(stus);
	}
	
}
class Student implements Comparable{
	private String name;
	private double ma,cn,en;
	private double sum;
	Student(String name,double ma,double cn,double en){
		this.name=name;
		this.ma=ma;
		this.cn=cn;
		this.en=en;
		sum=ma+cn+en;
	}
	//增加学生比较性
	public int compareTo(Student s){
		int num=new Double(this.sum).compareTo(new Double(s.sum));
		if(num==0)
			return num;
		return num;
	}
	public String getName(){
		return name;
	}
	public double getSum(){
		return sum;
	}
	//不确定,有可能存到hashMap里面
	public int hashCode(){
		return name.hashCode()+(int)sum*71;
	}
	public boolean equals(Object obj){
		if(!(obj instanceof Student))//判断是否是学生类,否则抛出异常
			throw new ClassCastException("类型不匹配");
		Student s=(Student)obj;
		return this.name.equals(s.name) &&this.sum==s.sum;
	}
	public String toString(){
		return "student["+name+","+ma+","+cn+","
				+en+","+sum+"]";
	}
}
//定义学生工具类
class StudentInfoTool{
	public static void getStudents()throws IOException{
		getStudents(null);
	}
	public static Set getStudents(Comparator cmp)throws IOException{
		BufferedReader bufr=
				new BufferedReader(new InputStreamReader(System.in));
				String line=null;
				Set stus=null;
				if(cmp==null)
					stus=new TreeSet();
				else
					stus=new TreeSet(cmp);
				while ((line=bufr.readLine())!=null){
					if("over".equals(line))
						break;
					String[] info=line.split(",");//切割分离信息
					Student stu=new Student(info[0],Double.parseDouble(info[1])
								,Double.parseDouble(info[2]),Double.parseDouble(info[3]));
					stus.add(stu);
				}
				bufr.close();
				return stus;
	}
	//把信息写入文件
	public static void write2File (Set stus)throws IOException{
		BufferedWriter bufw=new BufferedWriter(new FileWriter("stuinfo.txt"));
		for (Student stu:stus){
			bufw.write(stu.getName()+"\t");
			//bufw.write(stu.getSum());
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();
	}
}




你可能感兴趣的:(自学笔记)