黑马程序员java基础篇----I/O总结(下)

android培训java培训期待与您交流

一、Properties

1、Properties概述
   Properties类表示了一个持久的属性集,是集合与I/O结合的容器。Properties可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。一个属性列表可包含另一个属性列表作为它的“默认值”,如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表,因为Properties继承于Hashtable,所以可对Properties对象应用put和putAll方法,但不建议使用这两个方法,因为它们允许调用者插入其键或值不是String的项,相反,应该使用setProperty方法。
2、Properties方法列举与解析
   (1)、getProperty(String key)用指定的键在此属性列表中搜索属性。如果在此属性列表中未找到该键,则接着递归检查默认属性列表及其默认值。如果未找到属性,则此方法返回 null。其重载方法getProperty(String key,String defaultValue)用指定的键在属性列表中搜索属性。如果在属性列表中未找到该键,则接着递归检查默认属性列表及其默认值。如果未找到属性,则此方法返回默认值变量。 
   (2)、list(PrintStream out)&list(PrintWriter out)将属性列表输出到指定的输出流,此方法对调试很有用。
   (3)、load(InputStream inStream)从输入流中读取属性列表(键和元素对)。load(Reader reader)按简单的面向行的格式从输入字符流中读取属性列表。
   (4)、store(OutputStream out,String comments)以适合使用load(InputStream)方法加载到Properties表中的格式,将此Properties表中的属性列表写入输出流。

          store(Writer writer,String comments)以适合使用load(Reader)方法的格式,将此Properties表中的属性列表(键和元素对)写入输出字符,写入各个项后,刷新输出流,两方法返回后,输出流仍保持打开状态。 
   (5)、 propertyNames()返回属性列表中所有键的枚举,如果在主属性列表中未找到同名的键,则包括默认属性列表中不同的键。 
            stringPropertyNames()返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键,其键或值不是 String 类型的属性被忽略。
   (6)、setProperty(String key,String value)调用Hashtable的方法put。强制要求属性的键和值使用字符串。返回值是Hashtable调用put的结果。
   (7)、loadFromXML(InputStream in)将指定输入流中由XML文档所表示的所有属性加载到此属性表中。
   (8)、storeToXML(OutputStream os,String comment)发出一个表示此表中包含的所有属性的 XML 文档。
           storeToXML(OutputStream os,String comment,String encoding)使用指定的编码发出一个表示此表中包含的所有属性的 XML 文档。

二、PipedWriter&PrintStream

1、PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream永远不会抛出IOException,而是,异常情况仅设置可通过checkError方法测试的内部标志。另外,为了自动刷新,可以创建一个PrintStream,这意味着可在写入byte数组之后自动调用flush方法,可调用其中一个println方法,或写入一个换行符或字节('\n')。PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用PrintWriter类。
2、向文本输出流打印对象的格式化表示形式。此类实现在PrintStream中的所有print方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。与PrintStream类不同,如果启用了自动刷新,则只有在调用println、printf或format的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分 隔符概念,而不是换行符。此类中的方法不会抛出I/O异常,尽管其某些构造方法可能抛出异常。

三、对象的序列化

1、序列化简单来讲就是使用流的思想将对象持久化存储,只有支持java.io.Serializable或java.io.Externalizable接口的对象才能从流读取。涉及到的流:ObjectOutPutStream对以前使用ObjectOutputStream写入的基本数据和对象进行反序列化。每个serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
2、反序列化就是对将序列化到持久层的对象读取到内存中来。涉及到的流:ObjectInputStream 对以前使用 ObjectOutputStream写入的基本数据和对象进行反序列化。默认情况下,对象的反序列化机制会将每个字段的内容恢复为写入时它所具有的值和类型。反序列化进程将忽略声明为瞬态或静态的字段。对其他对象的引用使得根据需要从流中读取这些对象。
3、要使对象可序列化,则相应的类需要实现Serializable接口,该接口是一个标记接口,没有任何方法。当需要对可序列化对象在序列化过程中进行一些特殊处理时,则此对象所属的类在定义时需要实现一些特殊的方法。
   (1)、private void readObject(java.io.ObjectInputStream stream)throws IOException, ClassNotFoundException
        此方法负责写入特定类的对象状态,以便相应的readObject方法可以恢复它。该方法本身不必与属于对象的超类或子类的状态有关。状态是通过使用writeObject方法或使用DataOutput支持的用于基本数据类型的方法将各个字段写入ObjectOutputStream 来保存的。
   (2)、private void writeObject(java.io.ObjectOutputStream stream)throws IOException
        readObject 方法负责使用通过对应的 writeObject 方法写入流的数据,为特定类读取和恢复对象的状态。该方法本身的状态,不管是属于其超类还是属于其子类,都没有关系。恢复状态的方法是,从个别字段的 ObjectInputStream 读取数据并将其分配给对象的适当字段。DataInput 支持读取基本数据类型。
   (3)、private Object writeReplace()throws ObjectStreamException
          该方法返回值可以替换writeObject方法所进行的操作
   (4)、private Object readResolve()throws ObjectStreamException
         该方法返回值会替换掉readObject方法反序列化时读取的对象值
4、下面通过案例分析来看一下更多的细节部分

	public class Person implements(Serializable){
		
	      //priavte 因为UID类相关,子类继承了也没有意义,且因为UID算法的系统与平台敏感性,推荐自定义该属性值
		private static final long serialVersionUID = 1L; 
		private static final Person p = new Person("jzk",1);;
		public  String name;   
		public  int age;
		//对于不可序列化的超类(即假设Person不可序列化),子类(Student)要实现序列化,超类(Person)需要有无参构造器
		public  Person() {}  
		public  Person(String name, int age) {
			this.name = name;
			this.age = age;
		}
		public static Person getInstance(){
			return p;
		}
		public String getName() {
			return name;
		}
		public int getAge() {
			return age;
		}
		
		private Object writeReplace()throws ObjectStreamException{
		//此对象将会替代writeObject的操作,即文件中保存的其实是对象new Person("jkk",1);
			return new Person("jkk",1);      
		}
		
		public void writeObject(ObjectOutputStream out)   //可以直接被子类继承
				throws IOException,ClassNotFoundException{
			
			out.writeObject(name);
			out.writeInt(age);
		}
		
		public void readObject(ObjectInputStream in)  //可以直接被子类继承
				throws IOException,ClassNotFoundException{
			
			name = (String) in.readObject();
			age = in.readInt();
		}
		
		private Object readResolve()throws ObjectStreamException{
		//此处读取的时候用new Person("jkk",2)来替换readObject方法读取的对像值
			return new Person("jkk",2);
		//为了保证不被反序列化话破坏单例类的安全性,可考虑使用下边这条语句来返回类中包装的静态实例对象
		//	return p;   
		}
		@Override
		public String toString() {
			return "Person [name=" + name + ", age=" + age + "]";
		}
	}

	class Student extends Person implements Serializable{
		//注意一点:静态属性存在于方法区,而序列化操作只对堆内存中数据有效
		//时使用writeObject方法对其进行更改也无效
	//	public  String name;   当子类具有父类相同的属性时,序列化为null
	//	public  int age;       当子类具有父类相同的属性时,序列化为0
		public Student(String name, int age) {
			super(name,age);
	//		this.name = name;   当子类具有父类相同的属性时,这种初始化方式才可被序列化
	//		this.age = age;     当子类具有父类相同的属性时,这种初始化方式才可被序列化
		}
		public void setName(String name){
			this.name = name;
		}
		@Override
		public String toString() {
			return "Student [name=" + name + ", age=" + age + "]";
		}
	}

四、RandomAccessFile

1、此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型byte数组。存在指向该隐含数组的光标或索引,称为文件指针,输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用,输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过getFilePointer方法读取,并通过seek方法设置。通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出EOFException。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出IOException,而不是EOFException。需要特别指出的是,如果流已被关闭,则可能抛出IOException。
2、可以借助于RandomAccessFile的seek方法来实现多线程文件传输,下面就是一个例子

	 class DownLoad implements Runnable{
		
		private RandomAccessFile rafw = null;
		private InputStream is;
		private int start,eachlen;
		public DownLoad(RandomAccessFile rafw, InputStream is, int start,int eachlen) {
			this.rafw = rafw;
			this.is = is;
			this.start = start;
			this.eachlen = eachlen;
		}
		
		@Override
		public void run() {
			byte[] buf = new byte[1024];
			int len=0,sumlen=0;
			try {
				is.skip(start);
				rafw.seek(start);
				while (((len = is.read(buf)) != -1)&&sumlen<eachlen) {
					sumlen += len;
					rafw.write(buf, 0, len);
				}
			} catch (IOException e) {
				e.printStackTrace();
				throw new RuntimeException("文件下载异常");
			}finally{
				try {
					if (is != null) {
						is.close();
					}
				} catch (Exception e) {
					e.printStackTrace();
					throw new RuntimeException("关闭输入流异常");
				}
				try {
					if (rafw != null) {
						rafw.close();
					}
				} catch (Exception e) {
					e.printStackTrace();
					throw new RuntimeException("关闭下载流异常");
				}
			}
		}
	}

	public class RandomAccessFileDemo {

		public static void main(String[] args) {
			File f = new File("E:\\一丝不挂.mp3");   //源文件
			File fd = new File("E:\\一丝不挂2.mp3"); //目标文件
			long len = f.length();
			int eachLen = (int)(len/4+1);  //每条线程读取的字节数
			int start = 0;   
			try {
				for(int i = 0 ;i<4; i++){
					start = i*eachLen;    //计算每条线程的偏移量
					//创建并发传输的线程
					new Thread(new DownLoad(new RandomAccessFile(fd, "rw"),
							new FileInputStream(f),start,eachLen)).start();
				}
			} catch (IOException e) {
				e.printStackTrace();
				throw new RuntimeException("文件流创建异常");
			}		
		}

	}

五、其他常用流

1、DataInputStream&DataOutputStream
   (1)、DataInputStream数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
 
            DataOutputStream数据输出流允许应用程序以适当方式将基本Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
   (2)、 DataInputStream(InputStream in)使用指定的底层InputStream创建一个DataInputStream。
            DataOutputStream(OutputStream out)创建一个新的数据输出流,将数据写入指定基础输出流。

2、ByteArrayInputStream&ByteArrayOutputStream
    (1)、ByteArrayInputStream(byte[] buf)创建一个ByteArrayInputStream,使用buf作为其缓冲区数组。该缓冲区数组不是复制得到的。pos的初始值是 0,count的初始值是 buf 的长度。
 
            ByteArrayInputStream(byte[] buf,int offset,int length)创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组。pos的初始值是offset,count的初始值是offset+length和buf.length 中的最小值。该缓冲区数组不是复制得到的。将该缓冲区的标记设置为指定的偏移量。
    (2)、ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。关闭ByteArrayInputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
       ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个byte数组。缓冲区会随着数据的不断写入而自动增长。可使用toByteArray()和toString()获取数据。关闭ByteArrayOutputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
    (3)、toByteArray()创建一个新分配的byte数组。其大小是此输出流的当前大小,并且缓冲区的有效内容已复制到该数组中。
            toString()使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。新 String 的长度是字符集的函数,因此可能不等于缓冲区的大小。

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

            PipedOutputStream可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream对象,并由其他线程从连接的PipedInputStream读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态。
    (2)、PipedInputStream(PipedOutputStream src)创建PipedInputStream,使其连接到管道输出流src。写入src的数据字节可用作此流的输入。
            PipedOutputStream(PipedInputStream snk)创建连接到指定管道输入流的管道输出流。写入此流的数据字节稍后将用作 snk 的输入。
    (3)、connect(PipedOutputStream src)使此管道输入流连接到管道输出流src。如果此对象已经连接到其他某个管道输出流则抛出 IOException。如果src为未连接的管道输出流,snk为未连接的管道输入流,则可以通过以下任一调用使其连接。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值