Java 知识点整理-17.IO流 其他流 序列流+序列化+反序列化+内存输出流+对象操作流+打印流+标准输入输出流+键盘录入方式+随机访问流+数据输入输出流+Properties

目录

序列流

内存输出流

内存输出流的面试题

对象操作流ObjectOutputStream

对象操作流ObjectInputStream

对象操作流优化

序列化加上id号

打印流的概述和特点

标准输入输出流概述和输出语句

修改标准输入输出流拷贝图片

两种方式实现键盘录入

随机访问流概述和读写数据

数据输入输出流

Properties的概述和作为Map集合的使用

Properties的特殊功能使用

Properties的load()和store()功能


序列流

什么是序列流?

序列流可以把多个字节输入流整合成一个。从序列流中读取数据时,将从被整合的第一个流开始读,读完一个之后继续读第二个,以此类推。

SequenceInputStream类概述

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

SequenceInputStream类构造方法

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

SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的SequenceInputStream(将按顺序读取这两个参数,先读取s1,然后读取s2,),以提供从此SequenceInputStream读取的字节。

使用方式:

整合两个输入流

SequenceInputStream类构造方法

SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的SequenceInputStream(将按顺序读取这两个参数,先读取s1,然后读取s2,),以提供从此SequenceInputStream读取的字节。InputStream是抽象的,所以传其子类对象

演示:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;

public class Demo1_SequenceInputStream {
	public static void main(String[] args) throws IOException {
//		demo1_olddata();
		FileInputStream fis1 = new FileInputStream("a.txt");
		FileInputStream fis2 = new FileInputStream("b.txt");
		//对fis1和fis2进行封装,封装成SequenceInputStream。其实也是一种装饰设计模式,使功能更加强大。
		SequenceInputStream sis = new SequenceInputStream(fis1, fis2);
		FileOutputStream fos = new FileOutputStream("c.txt");
		
		int b;
		while((b = sis.read()) != -1) {
			fos.write(b);
		}
		
		sis.close();		//sis在关闭的时候,会将构造方法中传入的流对象也都关闭。
		fos.close();
		//SequenceInputStream底层源码
/*		InputStream in;		//in就是我们传的InputStream。当我们传fis1,fis2的时候,相当于对in赋值了。
		
		public void close() throws IOException {
	        do {
	            nextStream();
	        } while (in != null);
	    }
		
		final void nextStream() throws IOException {
	        if (in != null) {
	            in.close();
	        }

	        if (e.hasMoreElements()) {
	            in = (InputStream) e.nextElement();
	            if (in == null)
	                throw new NullPointerException();
	        }
	        else in = null;

	    }
*/		
	}

	public static void demo1_olddata() throws FileNotFoundException, IOException {
		//代码重复性高,复用性差
		FileInputStream fis1 = new FileInputStream("a.txt");	//创建字节输入流,关联a.txt
		FileOutputStream fos = new FileOutputStream("c.txt");	//创建字节输出流,关联c.txt。如果c.txt存在,是在创建对象的时候清空的。
		
		int b1;
		while((b1 = fis1.read()) != -1) {	 //不断的在a.txt上读取字节
			fos.write(b1);		//将读取的字节写到c.txt上
		}
		fis1.close();		//关闭字节输入流
		
		FileInputStream fis2 = new FileInputStream("b.txt");	//创建字节输入流,关联b.txt
		
		int b2;
		while((b2 = fis2.read()) != -1) {	 //不断的在b.txt上读取字节
			fos.write(b2);		//没关流,所以会继续将读取的字节写到c.txt上,不会先清空掉文件,这不是在创建输出流对象。
		}
		fis2.close();		//关闭字节输入流
		fos.close();		//关闭字节输出流
	}
}

整合多个输入流:

SequenceInputStream类构造方法

SequenceInputStream(Enumeration<?extends InputStream> e) 通过记住参数来初始化新创建的SequenceInputStream,该参数必须是生成运行时类型为InputStream对象的Enumeration型参数。InputStream是抽象的,所以传其子类对象

Enumeration<E>接口概述

public interface Enumeration<E> 实现Enumeration接口的对象,它生产一系列元素,一次生成一个。连续调用nextElement方法将返回一系列的连续元素。例如,要输出Vector<E> v的所有元素,可使用以下方法:for(Enumeration<E> e = v.elements(); e.hasMoreElements();) System.out.println(e.nextElement());这些方法主要通过向量的元素、哈希表的键以及哈希表中的值进行枚举。枚举也用于将输入流指定到SequenceInputStream中。

注:此接口的功能与Iterator接口的功能是重复的。此外,Iterator接口添加了一个可选的移除操作,并使用较短的方法名。新的实现应该优先考虑使用Iterator接口而不是Enumeration接口。

Enumeration<E>接口方法

public boolean hasMoreElements() 测试此枚举是否包含更多的元素。返回:当且仅当此枚举对象至少还包含一个可提供的元素时,才返回true;否则返回false。

public E nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。

Vector类中的方法:

public Enumeration<E> elements() 返回此向量组件的枚举。返回值类型就是Enumeration。

将流对象都装到Vector集合中,通过该elements方法就拿到了他的枚举。再把枚举赋给SequenceInputStream(Enumeration<?extends InputStream> e) 的构造即可。

序列流应用:多首mp3整合做歌曲串烧等

演示:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;

public class Demo1_SequenceInputStream {
	public static void main(String[] args) throws IOException {
//		demo1_olddata();
		FileInputStream fis1 = new FileInputStream("a.txt");
		FileInputStream fis2 = new FileInputStream("b.txt");
		//对fis1和fis2进行封装,封装成SequenceInputStream。其实也是一种装饰设计模式,使功能更加强大。
		SequenceInputStream sis = new SequenceInputStream(fis1, fis2);
		FileOutputStream fos = new FileOutputStream("c.txt");
		
		int b;
		while((b = sis.read()) != -1) {
			fos.write(b);
		}
		
		sis.close();		//sis在关闭的时候,会将构造方法中传入的流对象也都关闭。
		fos.close();
		//SequenceInputStream底层源码
/*		InputStream in;		//in就是我们传的InputStream。当我们传fis1,fis2的时候,相当于对in赋值了。
		
		public void close() throws IOException {
	        do {
	            nextStream();
	        } while (in != null);
	    }
		
		final void nextStream() throws IOException {
	        if (in != null) {
	            in.close();
	        }

	        if (e.hasMoreElements()) {
	            in = (InputStream) e.nextElement();
	            if (in == null)
	                throw new NullPointerException();
	        }
	        else in = null;

	    }
*/		
	}

	public static void demo1_olddata() throws FileNotFoundException, IOException {
		//代码重复性高,复用性差
		FileInputStream fis1 = new FileInputStream("a.txt");	//创建字节输入流,关联a.txt
		FileOutputStream fos = new FileOutputStream("c.txt");	//创建字节输出流,关联c.txt。如果c.txt存在,是在创建对象的时候清空的。
		
		int b1;
		while((b1 = fis1.read()) != -1) {	 //不断的在a.txt上读取字节
			fos.write(b1);		//将读取的字节写到c.txt上
		}
		fis1.close();		//关闭字节输入流
		
		FileInputStream fis2 = new FileInputStream("b.txt");	//创建字节输入流,关联b.txt
		
		int b2;
		while((b2 = fis2.read()) != -1) {	 //不断的在b.txt上读取字节
			fos.write(b2);		//没关流,所以会继续将读取的字节写到c.txt上,不会先清空掉文件,这不是在创建输出流对象。
		}
		fis2.close();		//关闭字节输入流
		fos.close();		//关闭字节输出流
	}
}

内存输出流

什么是内存输出流?

该输出流可以向内存中写数据,把内存当作一个缓冲区,写出之后可以一次性获取出所有数据。

ByteArrayOutputStream类概述

public class ByteArrayOutputStream extends OutputStream,此类实现了一个输出流,其中的数据被写入一个byte数组。缓冲区会随着数据的不断写入而自动增长。可使用toByteArray()和toString()获取数据。(自动增长:其实是创建了一个新的更大的字节数组,并把原数组的内容拷过来,之后再继续添加新内容)

关闭ByteArrayOutputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。

ByteArrayOutputStream类的构造方法

ByteArrayOutputStream() 创建一个新的byte数组输出流。

ByteArrayOutputStream(int size) 创建一个新的byte数组输出流,它具有指定的大小的缓冲区容量(以字节为单位)。

ByteArrayOutputStream类的成员方法

public void write(int b) 将指定的字节写入此byte数组输出流。

public void write(byte[] b, int off, int len) 将指定byte数组中从偏移量off开始的len个字节写入此byte数组输出流。

public byte[] toByteArray() 创建一个新分配的byte数组。

public String toString() 使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。

public String toString(String charsetName) 使用指定的charsetName,通过解码字节将缓冲区内容转换为字符串。

使用方式:

Ⅰ.创建对象:new ByteArrayOutputStream()

Ⅱ.写出数据:write(int), write(byte[])

Ⅲ.获取数据:toByteArray()

演示:

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo3_ByteArrayOutputStream {
	public static void main(String[] args) throws IOException {
		//FileInputStream读取中文的时候出现了乱码。
/*		FileInputStream fis = new FileInputStream("e.txt");
		byte[] arr = new byte[3];
		
		int len;
		while((len = fis.read(arr)) != -1) {
			System.out.println(new String(arr, 0, len));
		}
		
		fis.close();
*/		
		/*
		 * 解决方案:
		 * 1.字符流读取
		 * 2.ByteArrayOutputStream
		 */
		FileInputStream fis = new FileInputStream("e.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();		//创建该类的对象就相当于在内存中创建了一个可以增长的字节数组,把我们的内容存进来,所以他是可以没有构造的。
		
		int b;
		while((b = fis.read()) != -1) {
			baos.write(b);		//将读取到的数据逐个写到内存中
		}
		
/*		byte[] arr = baos.toByteArray();		//将缓冲区的数据全部获取出来,并赋值给arr数组
		System.out.println(new String(arr));
*/	
		System.out.println(baos.toString()); 	//效果一致,将缓冲区的内容转换为字符串,在输出语句中可以省略调用toString方法,打印索引系统默认调用toString()
		fis.close();
	}
}

内存输出流的面试题

定义一个文件输入流,调用read(byte[] b)方法,将a.txt文件中的内容打印出来(byte数组大小限制为5)。

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 定义一个文件输入流,调用read(byte[] b)方法,将a.txt文件中的内容打印出来(byte数组大小限制为5)。
 * 分析:
 * 1.read(byte[] b)是字节输入流的方法,所以我们应该创建的是FileInputStream,关联a.txt
 * 2.创建内存输出流,将独到的数据写到内存输出流中。
 * 3.创建一个字节数组,长度为5。
 * 4.将内存输出流的数据全部转换为字符串打印。
 * 5.关闭输入流。
 */
public class Test1 {
	public static void main(String[] args) throws IOException {
		//1.read(byte[] b)是字节输入流的方法,所以我们应该创建的是FileInputStream,关联a.txt
		FileInputStream fis = new FileInputStream("a.txt");
		//2.创建内存输出流,将独到的数据写到内存输出流中。
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		//3.创建一个字节数组,长度为5。
		byte[] arr = new byte[5];
		int len;
		
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		//4.将内存输出流的数据全部转换为字符串打印。
		System.out.println(baos);		//底层默认帮我们调用toString()
		//5.关闭输入流。
		fis.close();
	}
}

对象操作流ObjectOutputStream

什么是对象操作流

该流可以将一个对象写出,或者读取一个对象到程序中。也就是执行了序列化和反序列化的操作。将对象从内存中按照某种规定写到文档上去这个动作叫做序列化,通俗来说,就像玩游戏的存档。读档这个动作叫做反序列化。反序列化就是把那些写到文档中的字节数据翻译成对应的对象,再去使用。

ObjectOutputStream类概述

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

只有支持java.io.Serializable接口的对象才能写入流中。每个可序列化对象的类被编码,包括类的类名和签名,对象的字段和数组的值以及从初始对象引用的任何其他对象的关闭。

方法writeObject用于将一个对象写入流中。任何对象,包括字符串和数组,都是用writeObject编写的。多个对象或原语可以写入流。 必须从对应的ObjectInputstream读取对象,其类型和写入次序相同。

writeObject方法负责为其特定的类编写对象的状态,以便相应的readObject方法可以恢复它。该方法不需要关心属于对象的超类或子类的状态。 通过使用writeObject方法或通过使用DataOutput支持的原始数据类型的方法将各个字段写入ObjectOutputStream来保存状态。

序列化不会写出任何不实现java.io.Serializable接口的对象的字段。不可序列化的对象的子类可以是可序列化的。在这种情况下,非可序列化类必须有一个无参数构造函数,以允许其字段被初始化。在这种情况下,子类有责任保存并恢复不可序列化类的状态。通常情况下,该类的字段是可访问的(public,package或protected),或者可以使用get和set方法来恢复状态。

可以通过实现抛出NotSerializableException的writeObject和readObject方法来防止对象的序列化。 异常将被ObjectOutputStream捕获并中止序列化过程。

ObjectOutputStream类的成员方法

public voice writeObject(Object obj) 将指定的对象写入ObjectOutPutStream。写入对象的类,类的签名以及类的非瞬态和非静态字段的值以及其所有超类型。可以使用writeObject和readObject方法覆盖类的默认序列化。

Serializable接口概述

public interface Serializable,类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。未实现序列化系统会报NotSerializableException。

使用方式:

写出:new ObjectOutputStream(OutputStream), writeObject()

演示:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

import com.bean.Person;

public class Demo4_ObjectOutputStream {
	public static void main(String[] args) throws IOException {
		Person p1 = new Person("张三", 23);
		Person p2 = new Person("李四", 24);
		//序列化:将对象写到文件上。
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));
		oos.writeObject(p1);		//NotSerializableException 无法序列化异常,需要让自定义类实现Serializable接口。
		oos.writeObject(p2);		//Person对象自动类型提升为Object
		
		oos.close();
	}
}

对象操作流ObjectInputStream

ObjectInputStream类概述

public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants,ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。

ObjectOutputStream和ObjectInputStream可以分别为与FileOutputStream和FileInputStream一起使用的对象图提供持久性存储的应用程序。ObjectInputStream用于恢复先前序列化的对象。

ObjectInputStream确保从流中创建的图中的所有对象的类型与Java虚拟机中存在的类匹配。 根据需要使用标准机制加载类。

只能从流中读取支持java.io.Serializable或java.io.Externalizable接口的对象。

方法readObject用于从流中读取对象。在Java中,字符串和数组是对象,在序列化过程中被视为对象。读取时,需要将其转换为预期类型。

可以使用DataInput上的适当方法从流中读取原始数据类型。

对象的默认反序列化机制将每个字段的内容恢复为写入时的值和类型。声明为瞬态或静态的字段被反序列化过程忽略。对其他对象的引用导致根据需要从流中读取这些对象。使用参考共享机制正确恢复对象的图形。反序列化时总是分配新对象,这样可以防止现有对象被覆盖。

读取对象类似于运行新对象的构造函数。为对象分配内存,并初始化为零(NULL)。对非可序列化类调用无索引构造函数,然后从最接近java.lang.object的可序列化类开始,从串中还原可序列化类的字段,并使用对象的最特定类完成。

类通过实现java.io.Serializable或java.io.Externalizable接口来控制它们是如何序列化的。

实现Serializable接口允许对象序列化保存和恢复对象的整个状态,并允许类在流被写入的时间和读取时间之间演变。它自动遍历对象之间的引用,保存和恢复整个图形。

readObject方法负责使用通过相应的writeObject方法写入流的数据来读取和恢复其特定类的对象的状态。该方法不需要关注属于其超类或子类的状态。通过从ObjectInputStream读取各个字段的数据并对对象的相应字段进行分配来恢复状态。DataInput支持读取原始数据类型。

任何尝试读取超过相应writeObject方法写入的自定义数据边界的对象数据将导致使用eof字段值为true的抛出OptionalDataException。超过分配数据结束的非对象读取将以与指示流结尾相同的方式反映数据的结尾:Bytewise读取将返回-1作为字节读取或读取的字节数,并且原语读取将抛出EOFExceptions。如果没有相应的writeObject方法,则默认序列化数据的结尾标记分配的数据的结尾。

序列化不会读取或赋值任何不实现java.io.Serializable接口的对象的值。不可序列化的对象的子类可以是可序列化的。在这种情况下,非可序列化类必须有一个无参数构造函数,以允许其字段被初始化。在这种情况下,子类有责任保存并恢复不可序列化类的状态。通常情况下,该类的字段是可访问的(public,package或protected),或者可以使用get和set方法来恢复状态。

反序列化对象时发生的任何异常都将被ObjectInputStream捕获并中止读取过程。

使用方式:

读取:new ObjectInputStream(InputStream), readObject()

演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

import javax.print.attribute.standard.PresentationDirection;

import com.bean.Person;

public class Demo5_ObjectInputStream {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		//ObjectInputStream对象输入流,反序列化
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
		
		Person p1 = (Person) ois.readObject();		//Object强制类型转换成Person
		Person p2 = (Person) ois.readObject();		
//		Person p3 = (Person) ois.readObject();		//EOFException文件到末尾异常 写俩读三
		
		System.out.println(p1);
		System.out.println(p2);
		
		ois.close();
	}
}

对象操作流优化

将对象存储在集合中写出,读取到的是一个集合对象。

演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import com.bean.Person;

public class Demo6_ObjectOperationStream {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
//		demo_write();
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
		//读取到的是一个集合对象。
		ArrayList<Person> list = (ArrayList<Person>) ois.readObject();		//将集合对象一次读取
		for (Person person : list) {
			System.out.println(person);
		}
		
		ois.close();
	}

	public static void demo_write() throws IOException, FileNotFoundException {
		Person p1 = new Person("张三", 23);
		Person p2 = new Person("李四", 24);
		Person p3 = new Person("王五", 25);
		Person p4 = new Person("赵六", 26);
		//将对象存储在集合中写出
		ArrayList<Person> list = new ArrayList<>();
		list.add(p1);
		list.add(p2);
		list.add(p3);
		list.add(p4);
		
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));
		oos.writeObject(list);		//把整个集合对象一次写出
		oos.close();
	}
}

序列化加上id号

注意

要写出的对象必须实现Serializable接口才能被序列化。

不用必须加id号。不加系统会随机生成一串很长的id,加上id的作用是当忘记存档就读档时方便我们查看错误。


打印流的概述和特点

什么是打印流?

该流可以很方便的将对象的toString()结果输出,并且自动加上换行,而且可以使用自动刷出的模式。

System.out就是一个PrintStream,其默认向控制台输出信息。

System类概述

public final class System extends Object,System类包含一些有用的类字段和方法。它不能被实例化。

在System类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和 环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的使用方法。

System类的成员变量:

public static PrintStream out,“标准”输出流。

public static InputStream in,”标准“输入流。

public static PrintStream err,“标准”错误输出流。

PrintStream类概述

public class PrintStream extends FilterOutputStream implements Appendable, Closeable,PrintStream为其他输出流添加了功能,使他们能够方便地打印各种数据值表示形式。他还提供其他两项功能。与其他输出流不同,PrintStream永远不会抛出IOException;而是,异常情况仅设置可通过的checkError方法测试的内部标志。另外,为了自动刷新,可以创建一个PrintStream;这意味着可在写入byte数组之后自动调用flush方法,可调用其中一个println方法,或写入一个换行符或字节('\n')。

PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是字节的情况下,应该使用PrintWriter类。

print方法和println方法都属于PrintStream类,具体请查阅api。

演示:

import java.io.PrintStream;

import com.bean.Person;

public class Demo7_PrintStream {
	public static void main(String[] args) {
		System.out.println("aaa");		//平时这么用输出语句就行,不用先获取输出流,再打印
		PrintStream ps = System.out;	//获取标准输出流
		ps.println(97);		//97 底层通过Integer.toString()将97转换成对应的字符串并打印
		ps.write(97);		//a 查找码表,找到对应的a并打印
		
		//println底层源码 println→print→valueof→toString
/*		public void println(int x) {
	        synchronized (this) {
	            print(x);
	            newLine();
	        }
	    }
		
		public void print(int i) {
	        write(String.valueOf(i));
	    }
		
		public static String valueOf(int i) {
	        return Integer.toString(i);
	    }
		
		 public static String toString(int i) {  //传进一个整数,经过一系列变化转成字符串返回
	        if (i == Integer.MIN_VALUE)
	            return "-2147483648";
	        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
	        char[] buf = new char[size];
	        getChars(i, size, buf);
	        return new String(buf, true);
	    }
*/		 
		Person p1 = new Person("张三", 23);	//默认调用p1的toString方法
		ps.println(p1);
		
		Person p2 = null;	//打印引用数据类型,如果是null就打印null;如果不是null就打印对象的toString方法。
		ps.println(p2);
		ps.close();
		//println底层源码 println→valueof
/*		public void println(Object x) {
	        String s = String.valueOf(x);
	        synchronized (this) {
	            print(s);
	            newLine();
	        }
	    }
		 
		public static String valueOf(Object obj) {
	        return (obj == null) ? "null" : obj.toString();		//不为空,就打印该对象的toString();为空,则打印null
	    }
*/		
	}
}

使用方式:

打印:print(), println()

自动刷出:PrintWriter(OutputStream out, boolean autoFlush, String encoding)  可以自动刷新。

PrintWriter类概述

public class PrintWriter extends Writer 向文本输出流打印对象的格式化表示形式。此类实现在PrintStream中的所有print方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

与PrintStream类不同,如果启用了自动刷新,则只有在调用println、printf或format的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。

此类中的方法不会抛出I/O异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用checkError()是否出现错误。

PrintWriter类构造方法

PrintWriter(File file) 使用指定文件创建不具有自动行刷新的新PrintWriter。

PrintWriter(File file, String csn) 创建具有指定文件和字符集且不带自动行刷新的新PrintWriter。

PrintWriter(OutputStream out) 根据现有的OutputStream创建不带自动行刷新的新PrintWriter。

PrintWriter(OutputStream out, boolean autoFlush) 通过现有的OutputStream创建新的PrintWriter。

PrintWriter(String fileName) 创建具有指定文件名称且不带自动行刷新的新PrintWriter。

PrintWriter(String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新PrintWriter。

PrintWriter(Writer out) 创建不带自动行刷新的新PrintWriter。

PrintWriter(Writer out, boolean autoFlush) 创建新的PrintWriter。

演示:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo8_PrintWriter {
	public static void main(String[] args) throws IOException {
		PrintWriter pw = new PrintWriter(new FileOutputStream("f.txt"), true);
//		pw.println(97);		//97	自动刷出功能只针对println方法
		pw.write(97);		//a		其他方法不行,print都不行
		pw.println();		//		但把println放最后就都能刷新出来
		pw.close();			//      关流也刷新,其实没啥用
	}
}

PrintStream和PrintWriter分别是打印的字节流和字符流。

打印流只操作 数据目的(内存从硬盘读取数据的文件叫数据源,内存写到硬盘数据的文件叫数据目的)。


标准输入输出流概述和输出语句

什么是标准输入输出流?

System.in是InputStream,标准输入流,默认可以从键盘输入读取字节数据。

System.out是PrintStream,标准输出流,默认可以向Console中输出字符和字节数据。

修改标准输入输出流

修改输入流:System.setIn(InputStream)

修改输出流:System.setOut(PrintStream)

演示:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

public class Demo9_SystemInOut {
	public static void main(String[] args) throws IOException {
//		demo_systemIn();
		//可以改变输入、输出流
		System.setIn(new FileInputStream("a.txt"));	//改变标准输入流
		System.setOut(new PrintStream("b.txt"));	//改变标准输出流
		
		InputStream is = System.in;		//获取标准的键盘输入流,默认指向键盘,改变后指向文件。
		PrintStream ps = System.out;	//获取标准输出流,默认指向控制台,改变后指向文件。
		
		int b;
		while((b = is.read()) != -1) {
			ps.write(b);
		}
		System.out.println();	//System.out也是一个PrintStream标准输出流,如果没有和硬盘上的文件产生关联的管道的话,是不用关的
		is.close();
		ps.close();
	}

	public static void demo_systemIn() throws IOException {
		InputStream is = System.in;
		int x = is.read();		//只读第一个字节
		System.out.println(x);
		
		is.close();
		//输入流只有一个,即使重新创建也是原来那个。之前关闭了流,再想开就开不了了。而且这个流其实是不用关的,因为并没有让内存和硬盘文件产生关联。
		InputStream is2 = System.in;
		int y = is.read();		
		System.out.println(x);
	}
}

修改标准输入输出流拷贝图片

演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;

public class Test2 {
	public static void main(String[] args) throws IOException {
		//修改标准输入输出流拷贝图片,但开发中不推荐这么用
		System.setIn(new FileInputStream("demo.jpg"));	//改变标准输入流
		System.setOut(new PrintStream("copy.jpg"));		//改变标准输出流
		
		InputStream is = System.in;
		PrintStream ps = System.out;
		
		byte[] arr = new byte[1024];
		int len;
		
		while((len = is.read(arr)) != -1) {
			ps.write(arr, 0, len);
		}
		
		is.close();
		ps.close();
	}
}

两种方式实现键盘录入

BufferedReader的readLine方法。

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

Scanner。

演示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

public class Demo10_SystemIn {
	public static void main(String[] args) throws IOException {
//		demo_bufferedReader();
		//Scanner进行键盘录入,更推荐,功能更加强大。
		Scanner sc = new Scanner(System.in);	//Scanner也可以用来读某个文件
		String line = sc.nextLine();
		System.out.println(line);
		sc.close();		//Scanner是对流进行封装,所以他也有关闭的功能。但传System.in的时候可以是不关的,因为没有和硬盘上某个文件发生关联。
	}

	public static void demo_bufferedReader() throws IOException {
		//BufferedReader进行键盘录入读取一行
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));	//中间InputStreamReader是转换流,将字节流转换成字符流。  对System.in进行包装,通过转换流转换为了字符流,再通过BufferedReader使其功能更加强大。
		String line = br.readLine();
		System.out.println(line);
		br.close();
	}
}

随机访问流概述和读写数据

随机访问流概述

RandomAccessFile类概述

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

通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出EOFException(是一种IOException)。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出IOException,而不是EOFException。需要指出的是,如果流已被关闭,则可能抛出IOException。

RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。即可以读也可以写。java.io包下,使用需要导包。

支持对随机访问文件的读取和写入。

RandomAccessFile类的构造方法

RandomAccessFile(File file, String mode) 创建从中读取和其中写入(可选)的随机访问文件流,该文件由File参数指定。mode参数指定用以打开文件的访问模式。允许的值及其含义为:

含义

"r"

以只读方式打开。调用结果对象的任何write方法都将导致IOException。

"rw"

打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

"rws"

打开以便读取和写入,对于“rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。同步更新:边写变存储。元数据:即文件属性中的详细信息。

"rwd"

打开以便读取和写入,对于“rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。

RandomAccessFile(String name, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。

RandomAccessFile类的成员方法

public void seek(long pos) 设置文件指针偏移,从该文件的开头测量,发生下一次读取或写入。 偏移可以设置为超出文件的末尾。设置超出文件末尾的偏移量不会更改文件长度。文件长度只有在偏移设置超出文件结尾之后才会通过写入进行更改。

参数:pos - 从文件开头测量的偏移位置,用于设置文件指针。

异常:IOException - 如果 pos小于 0或发生I / O错误。 

演示:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Demo11_RandomAccessFile {
	public static void main(String[] args) throws IOException {
		RandomAccessFile raf = new  RandomAccessFile("g.txt", "rw");
//		raf.write(97);		//写
/*		int x = raf.read();	//读
		System.out.println(x);		//97
*/		
		raf.seek(10);		//在指定位置设置指针。可以进行读写操作,且不会清空文件。
		raf.write(98);
		raf.close();
	}
}

指定位置读写的好处,可以进行多线程下载。提高效率。


数据输入输出流

什么是数据输入输出流

DataInputStream,DataOutputStream可以按照基本数据类型大小读写数据。

例如按Long大小写出一个数字,写出时该数据占8字节。读取的时候也可以按照Long类型读取,一次读取8个字节。

DataInputStream类概述

public class DataInputStream extends FilterInputStream implements DataInput,数据输入流允许应用程序以独立于机器的方式从底层输入流读取原始Java数据类型。应用程序使用数据输出流来写入稍后可以被数据输入流读取的数据。DataInputStream对于多线程访问来说不一定是安全的。

DataInputStream类的构造方法

public DataInputStream(InputStream in) 创建使用指定的底层InputStream的DataInputStream。

DataInputStream类详细成员方法见api。

DataOutputStream类概述

public class DataOutputStream extends FilterOutputStream implements DataOutput 数据输出流使应用程序以便携式方式将原始Java数据类型写入输出流。然后应用程序可以使用数据输入流来读取数据。

DataOutputStream类的构造方法

public DataOutputStream(OutputStream out) 创建一个新的数据输出流,以将数据写入指定的底层输出流。计数器written设置为零。

DataOutputStream类的成员方法

public final void writeInt(int v) 将int写入底层输出流作为四字节值。如果没有抛出异常,计数器written递增4。

DataOutputStream类详细成员方法见api。

使用方式:

DataOutputStream(OutputStream), writeInt(), writeLong() 

演示:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo12_Data {
	public static void main(String[] args) throws IOException {
//		demo_write();
//		demo_read();
		
/*		DataOutputStream dos = new DataOutputStream(new FileOutputStream("h.txt"));
		dos.writeInt(997);
		dos.writeInt(998);
		dos.writeInt(999);
		
		dos.close();
*/
		DataInputStream dis = new DataInputStream(new FileInputStream("h.txt"));
		int x = dis.readInt();
		int y = dis.readInt();
		int z = dis.readInt();
		
		System.out.println(x);  //997
		System.out.println(y);	//998
		System.out.println(z);	//999
		
		dis.close();
	}

	public static void demo_read() throws FileNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("h.txt");
		int x = fis.read();		//00000000 00000000 00000000 11100101‬
		int y = fis.read();
		int z = fis.read();
		System.out.println(x);  //229
		System.out.println(y);	//230
		System.out.println(z);	//231
		
		fis.close();
	}

	public static void demo_write() throws FileNotFoundException, IOException {
		FileOutputStream fos = new FileOutputStream("h.txt");
		fos.write(997);		//int类型997 00000000 00000000 ‭00000011 11100101	写入时前三个8位被砍去,只剩11100101‬
		fos.write(998);		//写的时候不能按照基本数据类型大小写过去,本来想写四个字节,结果写过去的是一个字节。读的时候当然也是一个字节。
		fos.write(999);
		
		fos.close();
	}
}

Properties的概述和作为Map集合的使用

Properties的概述:

public class Properties extends Hashtable<Object, Object> Properties类表示了一个持久的属性集。Properties可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

不是一个流,他可以用流读取配置文件。是一个没有泛型的双列集合,父类Hashtable。没有指定泛型,他有一个固定的作用,就是去当做配置文件,存储一些信息,基本都是字符串类型的。如果加了泛型,就可以设定任意类型。不加泛型,通过某种方法限制指定你为String类型。

演示:Properties作为Map集合的使用。

import java.util.Properties;

public class Demo13_Properties {
	public static void main(String[] args) {
		Properties prop = new Properties();
		prop.put("abc", 123);
		System.out.println(prop);	//{abc=123}
	}
}	

Properties的特殊功能使用

Properties类的特殊功能:

public Object setProperty(String key, String value) 调用Hashtable的方法put。

public String getProperty(String key) 用指定的键在此属性列表中搜索属性。

public Enumeration<?> PropertyNames() 返回属性列表中所有键的枚举,如果在主属性列表中未找到同名的键,则包括默认属性列表中不同的键。

演示:

import java.util.Enumeration;
import java.util.Properties;

public class Demo14_Properties {
	public static void main(String[] args) {
		Properties prop = new Properties();
		prop.setProperty("name", "张三");
		prop.setProperty("tel", "18912345678");
		
//		System.out.println(prop);
		Enumeration<String> en = (Enumeration<String>) prop.propertyNames();		//没有加泛型,默认Object,所以要加一次强转。
		while(en.hasMoreElements()) {
			String key = en.nextElement();			//获取Properties中的每一个键
			String value = prop.getProperty(key);	//根据建获取值
			System.out.println(key + "=" + value);
		}
	}
}

Properties的load()和store()功能

Properties类的load()和store()功能:

public void load(InputStream inStream) 从输出流中读取属性列表(键和元素对)。

public void load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

public void store(OutputStream out, String comments) 以适合使用load(InputStream)方法加载到Properties表中的格式,将此Properties表中的属性列表(键和元素对)写入输出流。

public void store(Writer writer, String comments) 以适合使用load(Reader)方法的格式,将此Properties表中的属性列表(键和元素对)写入输出字符。参数:comments-属性列表的描述。可以不给,写个null。如果给了在文件中会以#注释作开头加上我们给的描述。

演示:

准备一个config.properties配置文件,写入

username=zhangsan

tel=18912345678

qq=12345

等号、冒号系统都是可以识别的。等号前面是键,后面是值。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class Demo15_Properties {
	public static void main(String[] args) throws IOException {
		Properties prop = new Properties();
		prop.load(new FileInputStream("config.properties"));	//将文件上的键值对读取到集合中
		prop.setProperty("tel", "18612345678");		//只改了内存的,没有写到文件上去
		prop.store(new FileOutputStream("config.properties"), null);	//第二个参数是对列表参数的描述,可以给值,也可以给null
		System.out.println(prop);
	}
}

猜你喜欢

转载自blog.csdn.net/from_heat/article/details/84678779
今日推荐