Java之------IO基础到加强

Posted 夏小弥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之------IO基础到加强相关的知识,希望对你有一定的参考价值。

RandomAccessFile

1、随机访问文件,自身具备读写的方法

new RandomAccessFile()之后,若文件不存在会自动创建,存在则不创建。——该类其实内部既封装了字节输入流,又封装了字节输出流。

该类若用write()方法写整数,每次只写它的最后一个字节。而采用writeInt()方法,则可把一个整数完整地写入。 

2、通过skipBytes(int x),seek(int x)来达到随机访问

通过seek方法设置数据的指针就可以实现对文件数据的随机读写。InputStream中的skip()方法只能从头往后跳,不能反向;而seek()方法可双向随便定位。

3、数据修改方面的特点

用RandomAccessFile类可以实现数据的修改,当然文件中的数据一般要有规律,以方便在编程时能够进行定位,让数据写对地方。 而用“流”实现数据修改时,则通常需要把数据从流读到数组当中,在数组中进行数据修改,然后再把修改后的数组

再重新写到流中。 


序列化

★ 序列化

将一个对象存放到某种类型的永久存储器上称为保持。如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。(在Java中,序列化、持久化、串行化是一个概念。)
java.io.Serializable接口没有任何方法,它只作为一个“标记者”,用来表明实现了这个接口的类可以考虑串行化。类中没有实现Serializable的对象不能保存或恢复它们的状态。 

★ 对象图

当一个对象被串行化时,只有对象的数据被保存;方法和构造函数不属于串行化流。如果一个数据变量是一个对象,那么这个对象的数据成员也会被串行化。树或者对象数据的结构,包括这些子对象,构成了对象图。

★ 瞬时 transient

防止对象的属性被序列化。

Address.java

package io.serializable;

import java.io.Serializable;

public class Address implements Serializable {
	//静态变量是不会被序列化的。对于非静态变量,一般情况下都会被序列化,但如果声明成transient型则不会。
	transient int num;//瞬时变量---该变量是不会被序列化的---不会出现在对象图中的
	private String name;
	private int age;
	private String tel;

	public Address(String name, int age, String tel) {
		super();
		this.name = name;
		this.age = age;
		this.tel = tel;
	}

	
	public Address(int num, String name, int age, String tel) {
		super();
		this.num = num;
		this.name = name;
		this.age = age;
		this.tel = tel;
	}


	@Override
	public String toString() {
		return "Address [num=" + num + ", name=" + name + ", age=" + age
				+ ", tel=" + tel + "]";
	}

}
SerializableDemo.java

package io.serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

public class SerializableDemo {

	public static void main(String[] args) throws Exception {
       //demo1();
		demo2();
	}

	private static void demo2() throws FileNotFoundException, IOException,
			ClassNotFoundException {
		//对象序列化---输出,写
			OutputStream fout = new FileOutputStream("a.txt");
			ObjectOutputStream out = new ObjectOutputStream(fout);
			out.writeObject( new Address(1,"aa",23,"13588888888"));
			out.writeObject( new Address(2,"bb",24,"13566666666"));
			out.writeObject( new Address(3,"cc",23,"13577777777"));
			out.writeObject( new Address(4,"dd",25,"13599999999"));
			out.close();
			
			//反序列化----读
			InputStream fin = new FileInputStream("a.txt");
			ObjectInputStream in = new ObjectInputStream(fin);
			System.out.println( in.readObject() );
			System.out.println( in.readObject() );
			System.out.println( in.readObject() );
			System.out.println( in.readObject() );
	}
	private static void demo1() throws FileNotFoundException, IOException,
	ClassNotFoundException {
		//对象序列化---输出,写
		OutputStream fout = new FileOutputStream("a.txt");
		ObjectOutputStream out = new ObjectOutputStream(fout);
		out.writeObject( new Address("aa",23,"13588888888"));
		out.writeObject( new Address("bb",24,"13566666666"));
		out.writeObject( new Address("cc",23,"13577777777"));
		out.writeObject( new Address("dd",25,"13599999999"));
		out.close();
		
		//反序列化----读
		InputStream fin = new FileInputStream("a.txt");
		ObjectInputStream in = new ObjectInputStream(fin);
		System.out.println( in.readObject() );
		System.out.println( in.readObject() );
		System.out.println( in.readObject() );
		System.out.println( in.readObject() );
	}

}

缓冲输入输出流
(BufferedInputStream和BufferedOutputStream)

方式一:

DataInputStream in = new DataInputStream(
                                            new BufferedInputStream( 
                                                        new FileInputStream("Test.txt")   );
方式二:

DataInputStream in = new DataInputStream(
                                                                 new FileInputStream("Test.txt")   );
方式三:

BufferedInputStream in = new BufferedInputStream( 
                                                       new DataInputStream( 
                                                           new FileInputStream("Test.java")  );

☆示例测试技术总结:方案1是最优的

1)有buffer比没有更快;

2)buffer放在中间层包装比放在外层更快;

3)按行或按块操作 比 按字节或字符操作更快(用Object流操作的速度 比 字节字符方式 更快)

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

package io.buffer;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;

public class BufferedStreamDemo {
	public static void main(String[] args) {
		try {
			//test1();
			//test2();
			test3();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static void test1() throws Exception {
		long t1 = System.currentTimeMillis();
		DataInputStream din = new DataInputStream(new BufferedInputStream(
				new FileInputStream("b.txt")));
		String str = null;
		while ((str = din.readLine()) != null) {
			System.out.println(str);
		}
		long t2 = System.currentTimeMillis();
		System.out.println("方式1运行时间(毫秒):" + (t2 - t1));
	}
	
	private static void test2() throws Exception {
		long t1 = System.currentTimeMillis();
		DataInputStream din = new DataInputStream(
				         new FileInputStream("b.txt"));
		String str = null;
		while ((str = din.readLine()) != null) {
			System.out.println(str);
		}
		long t2 = System.currentTimeMillis();
		System.out.println("方式2运行时间(毫秒):" + (t2 - t1));
	}
	
	private static void test3() throws Exception {
		long t1 = System.currentTimeMillis();
		BufferedInputStream bin = new BufferedInputStream(
				                     new DataInputStream(
				                    	new FileInputStream("b.txt")) );
		byte buf[] = new byte[20];
		int n=0;
		while ((n = bin.read(buf)) != -1) {
			System.out.println(new String(buf,0,n));
		}
		long t2 = System.currentTimeMillis();
		System.out.println("方式3运行时间(毫秒):" + (t2 - t1));
	}

}


转换流
(InputStreamReader和OutputStreamWriter)

★转换流功能1:充当字节流与字符流之间的桥梁

需求:模拟英文聊天程序,要求:
     (1) 从键盘录入英文字符,每录一行就把它转成大写输出到控制台;
     (2) 保存聊天记录到字节流文件。

 要求1的设计分析:

1)需要从键盘接收录入,得用System.in,它是字节输入流InputStream;
2)需要处理字符,可以自己把字节强转成字符,也可以用字符流;
3)需要类似readLine的功能,而这个方法在字符流BufferedReader中有(而且该类有缓冲增速)。
综上,采用转换流把字节流转成字符流处理比较合理,即使用InputStreamReader

 要求2的设计分析:

1)需要把字符数据按行保存到字节流文件 ;
2)字符流采用BufferedWriter比较合适,因为它有newLine方法且能实现高效;
3)字节流文件,得采用FileOutputStream。
综上,采用转换流把字符流转成字节流处理比较合理,即使用OutputStreamWriter

package io.transfer;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class TranStreamDemo {

	public static void main(String[] args) throws IOException {
		//输入
		InputStream in = System.in;
		InputStreamReader isr = new InputStreamReader(in);
		BufferedReader br = new BufferedReader(isr);
		
		//输出
		OutputStream out = new FileOutputStream("chat.txt");
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bw = new BufferedWriter(osw);
		
		String line = null;
		while( (line=br.readLine())!=null){
			if("over".equals(line)){//养成好的代码习惯:调用String中的方法时,把常量字符串放在前面,防止变量为null而导致异常
				break;
			}
			System.out.println( line.toUpperCase() );
			bw.write(line);
			bw.newLine();
			bw.flush();//字符流是带缓冲的,必须刷缓冲
		}
	}

}

★转换流功能2:字符编码转换

采用FileWriter以默认方式编码
    FileOutputStream+默认编码表

采用转换流以默认方式编码
   OutputStreamWriter + FileOutputStream + 默认编码表

采用转换流以指定编码方式编码
   OutputStreamWriter + FileOutputStream +指定编码表

采用转换流以指定编码方式解码
   InputStreamReader + FileInputStream +指定编码表

package io.transfer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class TranStreamDemo2 {

	public static void main(String[] args) {
		try {
			//readTextDecoding1();
			//readTextDecoding2();
			writeTextEncoding();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static void writeTextEncoding() throws IOException {
		//第一种: FileWriter+默认编码表
		FileWriter fw = new FileWriter("files\\\\w_utf-8.txt");//该文件的编码由平台(如MyEclipse或dos窗口)定,不一定是utf-8
		fw.write("每天进步一点点...");
		fw.close();
		
		//第二种: OutputStreamWriter+默认编码表
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("files\\\\w_utf-8_2.txt"));//该文件的编码由平台(如MyEclipse或dos窗口)定,不一定是utf-8
		osw.write("第天进步一点点...");//牛耳
		osw.close();
		
		//第三种: OutputStreamWriter+指定编码表
		OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("files\\\\w_utf-8_3.txt"),"utf-8");//该文件的编码一定是utf-8,因为是我们自己指定的
		osw2.write("第天进步一点点...");
		osw2.close();
		
	}

	private static void readTextDecoding1() throws IOException {
		FileReader fr = new FileReader("files\\\\utf-8.txt");//采用默认编码表解码
		char[] cbuf = new char[10];
		int len=0;
		while( (len=fr.read(cbuf))!=-1){
			String str = new String(cbuf,0,len);
			System.out.print(str);
		}
		fr.close();
	}
	
	private static void readTextDecoding2() throws IOException {
		//InputStreamReader isr = new InputStreamReader(new FileInputStream("files\\\\gbk.txt"));//如果不指定编码表,则是采用默认的
		
		//用转换流自己指定解码表----只要文件的编码表和这里指定的解码表相同,就不会出现乱码
		//InputStreamReader isr = new InputStreamReader( new FileInputStream("files\\\\gbk.txt"), "gbk"); //ok
		//InputStreamReader isr = new InputStreamReader( new FileInputStream("files\\\\utf-8.txt"), "gbk");//乱码
		InputStreamReader isr = new InputStreamReader( new FileInputStream("files\\\\utf-8.txt"), "utf-8");//ok
		char[] cbuf = new char[20];
		int len = isr.read(cbuf);
		String str = new String(cbuf,0,len);
		System.out.println(str);
		isr.close();
	}

}


打印流

(PrintStream和PrintWriter)

★打印流的特点:

1)只有输出没有输入。PrintStream是字节打印流,PrintWriter是字符打印流。
2)能够方便地打印各种数据“值表示形式”,提供了一系列的打印功能(只有它有,其它流都没有。)
3)和其他输出流不同,它永远不会抛出IOException异常(构造方法除外),异常内部解决且设置了内部标志。
4)可创建具有自动刷新的功能,可使用带换行符的println()方法。
5)(在构造方法中)可以指定字符集编码的。

package io.print;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

//System.out就是PrintStream类型
public class PrintStreamDemo {
	public static void main(String[] args) {
		try {
			demo1();
			demo2();
			for(int i=0;i<10;i++){
				System.out.println(i);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	//把System.out的输出目的地从屏幕更改到日志文件
	private static void demo2() throws IOException {
		FileOutputStream fout = new FileOutputStream("log.txt");
		PrintStream out = new PrintStream(fout, true);
		System.setOut(out);
	}

	private static void demo1() throws IOException {
		//
		PrintStream out = new PrintStream("print.txt");
		//out.write()只写入一个字节的信息,如果参数大于一个字节的范围,那么实际上只会写入最后一个字节的数据
		//out.write(97);
		out.write(353);//最后一个字节是97,因此写入的是一个字符'a'----写入的是值的表现形式
		//System.out.write(353);//输出'a'
		//System.out.flush();
		
		out.println(345);//把参数转换成字符串输出
		//上一句等价于out.write( String.valueOf(i) )
		
		
		//※总之,PrintStream中用write()输出的是字节数据且每次只输出一个字节,而print()输出的是数据的值的表现形式即转换成字符串输出。
		//JSP中的out对象就是这种类型。要输出字节数据如图片声明等二进制格式则必须用write(),而输出页面数据(字符)则要用print()或println()
	}

}

★关于打印流的自动刷新

只有遇到结束字符(换行符)时才会自动刷新,如在调用其中一个println方法或写入换行符或字节('\\n)时会自动刷新输出缓冲区。

package io.print;

import java.io.IOException;
import java.io.PrintWriter;

//演示PrintStream类的自动刷新功能
public class PrintStreamDemo2 {
	public static void main(String[] args) {
		try {
			//demo1();
			demo2();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	private static void demo1() throws IOException {
		//默认不自动刷新的
		PrintWriter out = new PrintWriter(System.out);
		out.print("Hello World");//不会自动刷新
		out.println("Hello World");//不会自动刷新
		out.flush();//手动刷新
	}

	private static void demo2() throws IOException {
		//设置自动刷新的
		PrintWriter out = new PrintWriter(System.out,true);
		out.print("Hello World");//不会自动刷新
		out.println("Hello World");//会----因为println()内部调用了out.flush()
	    out.print("Hello3 \\n");//不会
	    out.print("Hello3 \\r\\n");//不会
	    out.printf("%s", "Hello4");//会
	    
	    /*总之:
	     * autoFlush - boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区。
	     * ---其实是因为这几个方法中帮我们调用了out.flush()。
	     */
	}
}


IO包中的其他流
★字节数组流
    ByteArrayInputStream与ByteArrayOutputStream

package io.array;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class ByteArrayStreamDemo {
	public static void main(String[] args) {
		String str ="adhjsdhhsd";
		ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		int ch=0;
		while( (ch=bis.read())!=-1 ){
			bos.write(ch);
		}
		System.out.println(bos.toString());
	}
}
★字符数组流
    CharArrayReader与CharArrayWriter
★字符串流
    StringReader 与 StringWriter

★序列流

    SequenceInputStream  ——对多个流进行合并

将多个流进行逻辑串联(合并变成一个流,操作起来很方便,因为多个源变成了一个源) 

package io.sequence;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;

public class SequenceInputStreamDemo {

	public static void main(String[] args) throws IOException {
		FileInputStream fin1 = new FileInputStream("files\\\\seq1.txt");
		FileInputStream fin2 = new FileInputStream("files\\\\seq2.txt");
		FileInputStream fin3 = new FileInputStream("files\\\\seq3.txt");
		
		ArrayList<FileInputStream> v = new ArrayList<FileInputStream>();
		v.add(fin1);
		v.add(fin2);
		v.add(fin3);
		
		Enumeration<FileInputStream> en = Collections.enumeration(v);
		SequenceInputStream sis = new SequenceInputStream(en);
		
		//创建输出流---因为要把前3个文件中的内容读取出来合并到 seq4.txt文件
		FileOutputStream fos = new FileOutputStream("files\\\\seq4.txt");
		int len = 0;
		byte buf[] = new byte[10];
		while((len=sis.read(buf))!=-1){
			fos.write(buf, 0, len);
		}
		fos.close();
		sis.close();
	}
}


IO流知识点小结