黑马程序员-java-I/O

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


      java语言的输入输出功能是十分强大而且灵活的,它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流等。


流的概念:

当程序需要读取数据时,就会开启一个通向数据源的流,这个数据可以是文件,内存,或是网络连接。类似地,当需要写入数据的时候,就会开启一个通向目的的流,这时候,就可以想象数据好像在这其中'流动'一样。

javaI/O流:

1,I/O流用来处理设备之间的数据传输。
1,I/O流对数据的操作时通过流的方式。
2,java用于操作流的对象都在IO包中.
3,流操作按数据分为两种: 字节流和字符流.
4,流按流向分为: 输入流,输出流。
其中字节流的两个基类为:InputStream,OutputStream。字符流的两个基类为:Reader,Writer。
注:有这四个类派生出的子类名称都是以其父类名作为子类的后缀。
如:InputStream的子类: FileInputStream
如:Reade的子类: FileReader

java I/O框架图:



文件字符输出流FileWriter:

用FileWriter将数据写入到指定文件中的例子:

<span style="font-family:KaiTi_GB2312;font-size:18px;">pubic class test
{
	public static void main(String[] args) throws Exception
	{
		//FileWriter在构造函数中知指定目标文件的时候,如果在指定目录下有同名文件,则将其覆盖。
		FileWriter fileWriter = new FileWriter("g:\\java\\test.txt");

		//调用write方法,将字符串写入到该流的缓冲区
		fileWriter.write("abc");
		
		// 刷新该缓冲区,将缓冲区中的字符数据写入到目标文件中。
		fileWriter.flush();		
	
		//关闭流
		fileWriter.close();
		
	}	
}</span>

I/O异常处理:上例中的代码是很不严谨的,首先,指定的目标文件的存放位置可能不存在,还有就是无论如何,都必须关闭流,因此,应该将关闭流这个操作放在finally块中。
改写如下:

<span style="font-family:KaiTi_GB2312;font-size:18px;">public class  test
{
	public static void main(String[] args) throws Exception
	{
		FileWriter fileWriter  =  null;
		
		try 
		{
			fileWriter = new FileWriter("g:\\java\\test.txt");
			
			fileWriter.write("abc");
		} 
		catch (Exception e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try 
			{
				if(fileWriter != null) //判断fileWriter是否为null
				{
					fileWriter.close();
				}
			}
			catch (Exception e2) 
			{
				System.out.println(e2.toString());
			}
		}
		
	}
}</span>

文件的续写:在FileWriter的构造函数中传递一个true,代表不覆盖已有的文件,即不重新对该文件进行数据写入,而是从文件的末尾开始写入数据。
例如:fileWriter = new FileWriter("g:\\java\\test.txt",true);


文件字符输入FileReader:
FileReader类中的read()读取单个字符,返回字符的整数值,如果已经导到输入流尾部,则返回-1,它是输入流中的最基本的读取函数。
FileReader类中的read(char[] cbuf)将字符读入cbuf数组,返回读取到的字节书,如果已经导到输入流尾部,则返回-1。
例子:

<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
	public static void main(String[] args) throws Exception
	{
		FileReader fileReader  = null;
		int len = 0;
		char[] buf = null;
		try 
		{
			fileReader = new FileReader("g:\\java\\demo.txt");
			
			buf = new char[10];  //存放读取字符的数组,一次最多读取十个字符
			
			while((len = fileReader.read(buf)) != -1) //如果还没有读到末尾
			{
				System.out.print(String.valueOf(buf, 0, len)); //打印读取到的字符
			}
						
		} 
		catch (Exception e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try 
			{
				if(fileReader != null)
				{
					fileReader.close();
				}
			}
			catch (Exception e2) 
			{
				System.out.println(e2.toString());
			}
		}
		
	}
}</span>
因为字符输入输出流在数据上的基本操作实际上都是单个字符,这样效率是很低的,于是java提供了缓冲区技术。
缓冲字符输入流: BufferedReader
缓冲字符输出流: BufferedWriter
建立缓冲区之前,必须要先有相应的流对象。只要在缓冲字符类的构造函数中传入相应的流对象。而且关闭缓冲区实际上关闭的就是相应的流。
BufferedReader类中的readLine()读取的是文本中的一行数据,其中不包括换行符。
BufferedWriter类中的write(String line)将一行字符串写入流中,newLine()写入换行符(跨平台),flush()刷新缓冲区。
例子(复制文本文件):
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
	public static void main(String[] args)
	{
		BufferedReader bufferedReader = null;	
		BufferedWriter bufferedWriter = null;
		try
		{       //创建缓冲字符输入流和输出流
			bufferedReader = new BufferedReader(new FileReader("g:\\java\\test.java"));
			bufferedWriter = new BufferedWriter(new FileWriter("g:\\java\\copytest.java");
			
			String line = null;
			while((line = bufferedReader.readLine()) != null)
			{
				bufferedWriter.write(line); //将读取到的一行字符串(不含换行符)写到缓冲输出流中
				bufferedWriter.newLine();  //写入一个换行符
				bufferedWriter.flush();    //刷新缓冲区
			}
		}
		catch (Exception e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try 
			{
				if(bufferedReader != null)
					bufferedReader.close();
			}
			catch (Exception e2) 
			{
				System.err.println(e2.toString());
			}
		}				
	}
}</span>

字符缓冲区中实际上是封装了一个字符数组,根据这个原理,自定义一个缓冲字符输入流MyBufferedReader,它继承Reader,并覆盖close()和read(char[] cbuf,int offset,int len)两个抽象函数。

<span style="font-family:KaiTi_GB2312;font-size:18px;">class MyBufferedReader extends Reader
{
	private Reader reader;
	
	public MyBufferedReader(Reader reader)
	{
		this.reader = reader;
	}
	

	//自定义的读取一行数据的函数,返回的字符串不包括换行符。
	public String myReadeLine() throws IOException
	{
		StringBuilder sb = new StringBuilder();
		
		int ch = 0;
		
		
		while((ch = reader.read()) != -1)
		{
			if(ch == '\r')
				continue;
			else if(ch == '\n')
				return sb.toString();
			else sb.append((char)ch);
		}
		//如果长度不为0,返回最后一行字符串。
		if(sb.length() != 0)
			return sb.toString();
		return null;
	}
	
	public int read(char[] buf,int off, int len) throws IOException
	{
		return read(buf, off, len);
	}
	
	public void close() throws IOException
	{
		reader.close();
	}
}</span>


装饰设计模式:
上面自定义的输入缓冲流用到了装饰设计模式,当希望给一个对象增加一些新的功能而且是动态的时候,必须要求装饰对象和被装饰对象都实现同一个接口或抽象类。
装饰和继承的区别:
装饰模式比继承更灵活,避免了继承所带来的代码臃肿问题,而且降低了类与类之间的耦合性。
装饰类以为增强已有对象的功能,具备的功能金额已有的是相同的,只不过提供了更强的功能。所以装饰类和被装饰类都属于同一个体系。
装饰类模式的应用场景:
1,需要扩展一个类的功能。
2,动态地为一个对象增加功能,而且还能动态撤销(这源于装饰类和被装饰类都继承了同一接口)。
(继承做不到这一点,继承的功能时静态的,不能动态地增删)
装饰设计模式的弊端: 产生过多相似的对象,不易排错。


字节流:
FileInputStream:
1,int avalible()得到当前可读的输入字节数。
2,int read()读取单个字节的数据,如果当前已到文件末尾,则返回-1。
3,int read(byte[] buf)从输入源中读取buf.length个数据到buf字节数组中,返回实际成果读取到的字节数,若当前在文件末尾则返回-1.
FileOutputStream:
1,void flush()刷新输出缓冲区,即把缓冲区中的数据持久化到文件中,并清空缓冲区。
2,int write(int b想输出缓冲区写入b的低八位,b的高24位将被忽略。
3, void write(byte[] buf)将字节数组buf中的数据全部写入到文件输出流。
4,void write(byte[] buf,int offset,int len)写入字节数组中的指定位置和长度的连续字节。
用字节流复制文件的例子:
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
	public static void main(String[] args)
	{
		FileInputStream fileInputStream = null;
		FileOutputStream fileOutputStream  = null;
		byte[] buf = null;
		try 
		{
			fileInputStream = new FileInputStream("g:\\java\\test.java");
			fileOutputStream = new FileOutputStream("g:\\java\\copybyte.java");
			buf = new byte[1024]; //存放读取字节的数组,一次最多读1024个字节
			int len = 0;
			while((len = fileInputStream.read(buf)) != -1) 
			{
				fileOutputStream.write(buf, 0, len); //将数组中的字节写入字节输出流 
				fileOutputStream.flush();            //刷新字节输出流
			}
		} 
		catch (Exception e) 
		{
			e.toString();
		}
		finally
		{
			try 
			{
				if(fileInputStream != null)
					fileInputStream.close();
				
				if(fileOutputStream != null)
					fileOutputStream.close();
			} 
			catch (Exception e2) 
			{
				
			}
		}
	}
}</span>

java同样为字节流提供了缓冲区技术:
缓冲字节输入流 BufferedInputStream
int read(byte[] buf)从输入源中读取buf.length个数据到buf字节数组中,返回实际成果读取到的字节数,若当前在文件末尾则返回-1.
缓冲字节输出流 BufferedOutputStream
void write(byte[] buf,int offset,int len)写入字节数组中的指定位置和长度的连续字节。

<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
	public static void main(String[] args)
	{
		FileInputStream fileInputStream = null;
		FileOutputStream fileOutputStream  = null;
		BufferedInputStream bufferedInputStream = null;
		BufferedOutputStream bufferedOutputStream = null;
		byte[] buf = null;
		try 
		{
			fileInputStream = new FileInputStream("g:\\java\\test.java");
			fileOutputStream = new FileOutputStream("g:\\java\\copybyte.java");
			bufferedInputStream = new BufferedInputStream(fileInputStream);//缓冲字节输入流
			bufferedOutputStream = new BufferedOutputStream(fileOutputStream); //缓冲字节输出流
			buf = new byte[1024];
			int len = 0;
			while((len = bufferedInputStream.read(buf)) != -1)
			{
				bufferedOutputStream.write(buf, 0, len); //将读取到的字节写到缓冲区中
				bufferedOutputStream.flush();  //刷新缓冲区
			}
		} 
		catch (Exception e) 
		{
			e.toString();
		}
		finally
		{
			try 
			{
				if(bufferedInputStream != null)
					bufferedInputStream.close();
				
				if(bufferedOutputStream != null)
					bufferedOutputStream.close();
			} 
			catch (Exception e2) 
			{
				
			}
		}
	}
}</span>

同样在字节缓冲输入流中实际上是封装了一个字节数组,利用装饰设计模式,我们也设计一个自定义的缓冲字节输入流MyBufferedInputStream,继承InputStream,覆盖close方法和read方法。

<span style="font-family:KaiTi_GB2312;font-size:18px;">class MyBufferedInputStream extends InputStream
{
	private InputStream in;
	private byte[] buf = new byte[1024];
	private int pos = 0,count =0;
	
	MyBufferedInputStream(InputStream in)
	{
		this.in = in;
	}
	
	public int read() throws IOException
	{
		if(count == 0)
		{       
                       //将读取的字节放入buf数组中,并将读取到的字节个数返回给count
			 count = in.read(buf); 
			if(count < 0) //如果count为-1,说明已经到末尾,直接return -1结束读取
			return -1;
			pos = 0; //否则取第一个字节返回,并准备读取下一个字节
			byte b = buf[pos];
			count--;
			pos++;
			return b&0xff; //为避免类型提升时出现前面都补1导致出现返回值为-1的情况,与上0xff。
		}
		else
		{
			byte b = buf[pos]; //读取pos位置处的一个字节
			count--; //剩余字节数减一
			pos++; 
			return b&0xff; //返回此次读取到的字节
		}
	}
	public void close()throws IOException
	{
		in.close();
	}
}</span>


转换流:
为了便于对数据的操作,有时需要将字节流转换成缓冲字符流对象。
读取转换流:InputStreamReader。
例如将标准输入流对象转换成缓冲字符输入流对象:BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
写入转换流:OutputStream。
例如将标砖输出流对象转换成缓冲字符输出流对象:BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
流操作规律:
1,明确源和目的:
    源: 输入流: InputStream , Reader
  目的:输出流: OutputStream, Writer
2,操作的数据是否是纯文本。
 是: 字符流
 否: 字节流
3,当体系明确后,再明确具体要使用哪个具体的对象。
   通过设备来进行区分:
 源设备:内存,硬盘,键盘
 目的设备: 内存,硬盘,控制台


java文件操作
File类的常见方法:
1,boolean createNewFile()  创建文件,在指定位置创建文件,如果该文件已经存在,则不创建,放回。
2,boolean delete()   删除文件或目录。
3,String getAbsolutePath()  返回此抽象路径名的绝对路径名字符串。
4,String getCanonicalPath()    返回此抽象路径名的规范路径名字符串。
5,String getName()    返回由此抽象路径名表示的文件或目录的名称。
6,String getParent()  返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
7,boolean isDirectory()  测试此抽象路径名表示的文件是否是一个目录。
8,boolean isFile()      测试此抽象路径名表示的文件是否是一个标准文件。
9,boolean exists()    测试此抽象路径名表示的文件或目录是否存在。
10,long length()    返回由此抽象路径名表示的文件的长度   
 
11,File[] listFiles()   返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
12,boolean mkdir()   创建此抽象路径名指定的目录。
13,boolean mkdirs()   创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
 
14,static String separator     与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
下面以复制c盘中的一个文件夹到d盘中为例,说明各函数的用法:
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class CopyFile 
{

	public static void main(String[] args) throws Exception
	{
		String source = "c:\\test";
		String dest = "d:\\trytest";
		write2Dir(source,dest);
	}
	
	
	//将SourceFile标准文件对象中的数据复制到DestFile标准文件对象中
	public static void write2file(File SourceFile,File DestFile)
	{
		BufferedInputStream inputStream = null;
		BufferedOutputStream outputStream = null;
		try 
		{
			inputStream = new BufferedInputStream(new FileInputStream(SourceFile));
			outputStream = new BufferedOutputStream(new FileOutputStream(DestFile));
			
			byte[] buf = new byte[1024];
			int len = 0;
			while((len = inputStream.read(buf)) != -1)
			{
				outputStream.write(buf,0,len);
			}
			outputStream.flush();
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
		finally
		{
			try 
			{
				if(inputStream != null)
					inputStream.close();
				if(outputStream!=null)
					outputStream.close();
			} 
			catch (Exception e2) 
			{
				e2.printStackTrace();
			}
		}
	}
	
	//利用递归,将指定路径名中的一个文件夹复制到目标路劲所对应的位置。
	public static void write2Dir(String sourceDir,String desDir) throws IOException
	{
		//在目标路劲位置创建一个同名文件夹
		(new File(desDir)).mkdirs();
		//获得源路径名指定的文件夹中所有的File对象
		File[] files = (new File(sourceDir)).listFiles();
		for(int i=0; i<files.length; i++)
		{
			//如果是一个标准文件
			if(files[i].isFile())
			{
				File SourceFile = files[i]; 
				//根据目标路劲名创建一个文件对象。
				File DestFile = new File(new File(desDir).getCanonicalPath()+(String)File.separator+files[i].getName());
				
				//复制标准文件
				write2file(SourceFile,DestFile);
			}
			//如果是一个目录
			if(files[i].isDirectory())
			{
				//获得下以层源目录的路径名
				String source = sourceDir + File.separator  +  files[i].getName();
				//同时获得目标目录的路径名
				String dest = desDir + File.separator  +  files[i].getName();
				//递归
				write2Dir(source, dest);
			}
		}
	}
}</span>


合并流SequenceInputStream:
1,SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,
 该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
2,SequenceInputStream(InputStream s1, InputStream s2)通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),
 以提供从此 SequenceInputStream 读取的字节。
3, int read(byte[] buf) 将最数据字节从此输入流读入 byte 数组。
例子(将三个文件合并成一个文件)
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
	public static void main(String[] args) throws Exception
	{
		Vector<FileInputStream> vector = new Vector<FileInputStream>();		
		vector.add(new FileInputStream("g:\\java\\1.txt"));
		vector.add(new FileInputStream("g:\\java\\2.txt"));
		vector.add(new FileInputStream("g:\\java\\3.txt"));
		
		Enumeration<FileInputStream> enumeration = vector.elements(); //得到InputStream 对象的 Enumeration 型参数。
		
		FileOutputStream fos = new FileOutputStream("g:\\java\\4.txt");//文件输出流
		SequenceInputStream sequenceInputStream = new SequenceInputStream(enumeration);	
		byte[] buf = new byte[1024];	
		int len = 0;
		
		while((len = sequenceInputStream.read(buf)) != -1)
		{
			fos.write(buf, 0, len); //将合并文件流写入到相应的字符文件输出流
			fos.flush();            //刷新字符文件输出流
		}  
		
		fos.close();
		sequenceInputStream.close();
	}
}</span>

切割文件(将制定文件切割成一个个大小为1KB的文件):

<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
	public static void main(String[] args) throws Exception
	{
		FileInputStream fis = new FileInputStream("g:\\java\\test.java");
		FileOutputStream fos = null;
		byte[] buf = new byte[1024];
		int len = 0;
		int count = 1;
		while((len = fis.read(buf)) != -1)
		{
			String string =  "g:\\java\\test"+(count++)+".java"; //要写入的文件名称
			fos = new FileOutputStream(string);      //根据文件名创建一个文件
			fos.write(buf,0,len);        //将1KB数据写入该文件
			fos.close();                 //关闭刚创建的字符文件输出流
		}
		
		fis.close();
	}
}</span>

Properties:
Properties是Hashtable的子类。也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串。
是集合中和IO相结合的集合容器。
Properties中的常用方法:
1,String getProperty(String key)   用指定的键在此属性列表中搜索属性。  
2,void load(InputStream inStream)     从输入流中读取属性列表(键和元素对)。
3,Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
4,void list(PrintStream out)    将属性列表输出到指定的输出流。
 
5,Set<String> stringPropertyNames()   返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。
 
使用Properties的例子:

<span style="font-family:KaiTi_GB2312;font-size:18px;">public class test
{
 public static void main(String[] args) throws Exception
 {
  Properties pro = new Properties();
  InputStream in = new FileInputStream("g:\\java\\config.properties");
  pro.load(in);
  pro.list(System.out);
  pro.setProperty("zhangsan", "11");
  System.out.println(pro.get("zhangsan"));
  pro.list(System.out);
 }
}</span>

对象的序列化:
原理: java提供的输入输出流ObjectOutputStream和ObjectInputStream可以直接把java对象作为可存储可传输的对象写入文件,并在网络传输。
对象的序列原理其实很简单:
1,通过输出流保存的对象都获得一个序列号
2,当要保存一个对象时,先检查该对象的序列号是否已经存在
3,如果以前保存了,则只要写入‘与已经保存的具有序列号的对象相同’的标记,否则,保存该对象
java对象序列化机制专门设计了一个没有任何定义方法的java.io.Serializable接口,当一个java对象实现该接口后,java就为其提供一个唯一的序列号。
在基于对象输入输出流进行对象的存取的时候,有两个细节需要考虑:
1,某个对象有不希望进行存储和传输的部分属性,即使它们都实现了java.io.Serializable接口。
2,有些没有实现java.io.Serializable接口的类型的属性,但是却希望基于对象输入和输出流对其进行存取。
java提供了解决上述两种情况的方法
1,对于不行进行传输的对象的部分属性,可以用‘transient’关键字对其进行标识
2,对于被‘transient’标识的属性,java对象序列化机制设计了两个私有方法(readObject(ObjectInputStream in) 和 writeObject(ObjectOutputStream out)),
 这两个方法不需要显示调用,序列化机制会自动调用。
例子:

<span style="font-family:KaiTi_GB2312;font-size:18px;">class Person implements Serializable
{
	private int age;
	transient private String name; //声明name属性不进行传输
	
	public Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public int getAge() {
		return this.age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}
        //定义wirteObject和readObject方法,将name属性进行基于对象输入和输出流的存取
	private void writeObject(ObjectOutputStream out) throws IOException
	{
		out.defaultWriteObject();
		out.writeObject(this.name);
	}
	
	private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException
	{
		in.defaultReadObject();
		this.name = (String) in.readObject()+".................";
	}
}

public class TestSerilizable
{
	public static void main(String[] args) throws Exception
	{
		File personFile = new File("myperson");
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(personFile));
		Person p = new Person("zhangsan",11);
		oos.writeObject(p);  //将对象持久化到myperson文件
		oos.close();
		
		ObjectInputStream oin = new ObjectInputStream(new FileInputStream(personFile));
		Person p1 = (Person)oin.readObject(); //读取文件中的对象
		System.out.println("name = "+p1.getName()+"::::"+"age = "+p1.getAge()); //打印属性
		oin.close();
	}
}</span>
虽然name属性被transient关键字标识,但类中实现了readObject(ObjectInputStream)和writeObject(ObjectOutputStream out)两个函数,序列化机制会自动调用他们对name进行写和读的操作。

管道流:
管道输入流:PipedInputStream
构造函数PipedInputStream(PipedOutputStream src, int pipeSize)  创建一个 PipedInputStream,使其连接到管道输出流 src,并对管道缓冲区使用指定的管道大小。
管道输出流:pipedOInputStream
构造函数PipedOutputStream(PipedInputStream snk) 创建连接到指定管道输入流的管道输出流。
可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建
议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态。

<span style="font-family:KaiTi_GB2312;font-size:18px;">class Read implements Runnable
{
	private PipedInputStream inputStream;
	Read(PipedInputStream inputStream)
	{
		this.inputStream = inputStream;
	}
	
	public void run()
	{
		try 
		{
			byte[] buf = new byte[1024];
			int len = inputStream.read(buf); //读取管道中的数据到buf字节数组中,该读取函数时线程阻塞性的,当管道流中没有数据时,会一直等待,直到管道中有数据并将其取走
			String string = new String(buf,0,len);
			System.out.println(string); 
			inputStream.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("read failed");
		}
	}
	
}

class Write implements Runnable
{
	private PipedOutputStream outputStream;
	Write(PipedOutputStream outputStream)
	{
		this.outputStream = outputStream;
	}
	
	public void run()
	{
		try 
		{
			outputStream.write("piped is coming!!".getBytes()); //将数据写入管道流中
			outputStream.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("read failed");
		}
	}
	
}</span>


RandomAccessFile:
1,RandomAccessFile是不属于InputStream和OutputStream类系的,它是一个完全独立的类,多有的方法都没有用到InputStream和OutputStream类系中的功能,
2,RandomAccessFile是专门用来操作文件的I/O类,内部封装了一个数组,而且通过指针对数组的元素进行操作,因此它可以在文件里前后移动,通过getFilePointer获得该指针和seek设置文件指针。
3,RandomAccessFile实现了一个标记接口RandomAccess,该标记接口用来标识那些可以支持快速随机访问的类,
构造函数:
1,RandomAccessFile(File file, String mode) //指定操作文件
2,RandomAccessFile(String name, String mode) //指定文件的名称
如果构造函数要操作的文件不存在,则创建一个文件。如果存在,并不会覆盖已经存在的文件,这点跟其他的文件操作的输入输出类不一样。
mode: 
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。  
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。  
"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。 
如果模式为r,且文件不存在时,则会报异常,如果rw,操作文件不存在,则自动创建,否则也不对已经存在的文件进行覆盖。
一些方法的使用:
1,write(int val) 和 writeInt(int val):write(int val)只写入val中的低八位,而 writeInt(int val)按四个字节写入文件。
2,seek(long pos)和skipBytes(int n):seek(long pos)所操作的范围比skipBytes(int n)范围要广,而且skipBytes(int n)不支持往前指向跳过的字节,而seek能指向文件的任何地方所以一般使用seek。

可以直接操作基本数据类型的流:DataInputStream和DataOutputStream:
1,String readUTF(DataInput in)从流in中读取用 UTF-8 修改版格式编码的 Unicode 字符格式的字符串,然后以 String 形式返回此字符串。
2,void writeUTF(String str)以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。

当使用writeUTF将一个字符串写入文件中时,只能用相对应的readUTF将其读出!!!


----------------------ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值