案例:
package org.westos.Demo6;
import java.io.*;
public class Test1 {
public static void main(String[] args) throws IOException {
DataInputStream in = new DataInputStream(new FileInputStream("a.txt"));
DataOutputStream out = new DataOutputStream(new FileOutputStream("a.txt"));
//写入一个int类型的数据
out.writeInt(100);
//写入一个字节数据
out.writeByte(97);
//写入一个Boolean类型的数据
out.writeBoolean(true);
//读取的时候要按照写入类型的顺序读取。
//in.readByte();
/*Exception in thread "main" java.io.EOFException
at java.io.DataInputStream.readFully(DataInputStream.java:197)
at java.io.DataInputStream.readLong(DataInputStream.java:416)
at java.io.DataInputStream.readDouble(DataInputStream.java:468)
at org.westos.Demo6.Test1.main(Test1.java:19)
Process finished with exit code 1*/
//先写入的是int类型,所以先读取Byte类型时会出现以上错误
in.readInt();
in.readByte();
in.readDouble();
}
}
ByteArrayInputStream
ByteArrayOutputStream
特点:此流关闭无效,所以不需要 .close()
ByteArrayOutputStream 在内存中维护了一个字节数组,作为缓冲区,随着数据的不断写入,缓冲区会不断的扩充。
可使用 toByteArray () 和 toString () 获取缓冲区中的数据。
案例:
package Demo1;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class Test1 {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("锄禾日当午".getBytes());
out.write("汗滴禾下土".getBytes());
out.write("谁知盘中餐".getBytes());
out.write("粒粒皆辛苦".getBytes());
//toByteArray(); 取出ByteArrayOutputStream所维护的那个字节数组
byte[] bytes = out.toByteArray();
//将ByteArrayOutputStream所维护的字节数组转换为字符串
String s = new String(bytes);
System.out.println(s);
//toString();将ByteArrayOutputStream所维护的那个字节数组处理成字符串数据
String s1 = out.toString();
System.out.println(s1);
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
//读取ByteArrayOutputStream所维护的那个字节数组,获取存取的数据所占字节数。
int read = in.read(bytes);
System.out.println(read);
//打印读取到的数据内容
String s2 = new String(bytes,0,read);
System.out.println(s2);
}
}
结果:
锄禾日当午汗滴禾下土谁知盘中餐粒粒皆辛苦
锄禾日当午汗滴禾下土谁知盘中餐粒粒皆辛苦
60
锄禾日当午汗滴禾下土谁知盘中餐粒粒皆辛苦
Process finished with exit code 0
CharInputStream
CharOutputStream
ByteArrayOutputStream 在内存中维护了一个字符数组,作为缓冲区,随着数据的不断写入,缓冲区会不断的扩充。
案例:
package Demo1;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;
public class Test2 {
public static void main(String[] args) throws IOException {
CharArrayWriter wr = new CharArrayWriter();
//写入一个字符数组
wr.write(new char[]{'a','b','c','d'});
//写入一个字符串,off:0 表示从0索引开始,len:3 表示元素个数
wr.write("efgh",0,3);
//toCharArray():获取CharArrayWriter()所维护的字符数组
char[] chars = wr.toCharArray();
//将字符数组转换为字符串
System.out.println(String.valueOf(chars));
//toString():直接将获取CharArrayWriter()所维护的字符数组转换为字符串
System.out.println(wr.toString());
CharArrayReader re = new CharArrayReader(chars);
//re.read(chars): 读取CharArrayWriter()所维护的字符数组的长度;
int read = re.read(chars);
System.out.println(read);
}
}
结果:
abcdefg
abcdefg
7
Process finished with exit code 0
StringWriter
StringReader
案例:
package Demo1;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
public class Test3 {
public static void main(String[] args) throws IOException {
StringWriter wr = new StringWriter();
//写去一个字符串数据
wr.write("asdf");
//写入一个字节数组
wr.write(new char[]{'q','w','e','r'});
//操作字符串数组只需要toString();s就是StringWriter()所维护的字符串
String s = wr.toString();
System.out.println(s);
StringReader re = new StringReader(s);
char[] chars = new char[1024];
//读取StringWriter()所维护的字符串的长度,缓存在chars数组中。
//如果StringWriter()所维护的字符串的长度大于chars的容量,则存满chars后不再读取
int read = re.read(chars);
System.out.println(read);
}
}
结果:
asdfqwer
8
Process finished with exit code 0
字节打印流:PrintStream
字符打印流:PrintWriter
打印流:只能写出数据,不能读取数据,单个的,不是成对。
字节打印流 PrintStream
字符打印流 PrintWriter
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
PrintStream(File file)
创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(OutputStream out, boolean autoFlush)
创建新的打印流。
案例:字节打印流的构造方法和out 标准”输出流
package Demo1;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class Test4 {
public static void main(String[] args) throws IOException {
//创建具有指定文件且不带自动行刷新的新打印流。
PrintStream printStream = new PrintStream("a.txt");
//可以使用write()方法。写入数据
printStream.write("abcd".getBytes());
//println()带有自动换行
printStream.println("qwer");
//可以写入基本数据类型
printStream.print(100);
//创建新的打印流。带有自动刷新
PrintStream printStream1 = new PrintStream(new FileOutputStream("b.txt"), true);
printStream1.println("admin1");
printStream1.println("admin2");
//out 标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。对于简单独立的 Java 应用程序,编写一行输出数据的典型方式是:
PrintStream out =System.out;
out.println("打印1");
out.println("打印2");
}
}
结果:
打印1
打印2
Process finished with exit code 0
打开相对路径下的"a.txt"文件查看:
abcdqwer
100
打开相对路径下的"b.txt"文件查看:
admin1
admin2
案例:字符打印流的构造方法:
package Demo1;
import java.io.*;
public class Test5 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
PrintWriter printWriter = new PrintWriter("c.txt");
printWriter.println("锄禾日当午");
printWriter.println("汗滴禾下土");
printWriter.flush();
printWriter.close();
PrintWriter printWriter1 = new PrintWriter(new FileOutputStream("d.txt"), true);
printWriter1.println("桌子");
printWriter1.println("柜子");
}
}
结果:
打开相对路径下的"c.txt"文件查看:
锄禾日当午
汗滴禾下土
打开相对路径下的"d.txt"文件查看:
桌子
柜子
案例:使用打印流复制一个文本:
将"e.txt"文件里的数据复制到"f.txt"、
package Demo1;
import java.io.*;
public class Test6 {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("e.txt"));
PrintWriter printWriter = new PrintWriter(new FileOutputStream("f.txt"), true);
while (true){
String s = reader.readLine();
if (s != null) {
printWriter.println(s);
}else {
break;
}
}
reader.close();
printWriter.close();
}
}
标准输入输出流概述:
在System这个类中存在两个静态的成员变量:
public static final InputStream in: 标准输入流, 对应的设备是键盘
public static final PrintStream out: 标准输出流 , 对应的设备就是显示器
System.in的类型是InputStream.
System.out的类型是PrintStream,是OutputStream的孙子类FilterOutputStream 的子类.
案例:
package Demo1;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Test7 {
public static void main(String[] args) throws FileNotFoundException {
//构造一个新的Scanner,它生成的值是从指定文件扫描的。
Scanner sc1 = new Scanner(new File("a.txt"));
while (sc1.hasNextLine()){
String s = sc1.nextLine();
System.out.println(s);
}
Scanner sc2 = new Scanner(new FileInputStream("b.txt"));
while (sc2.hasNextLine()){
String s = sc2.nextLine();
System.out.println(s);
}
sc1.close();
sc2.close();
}
}
结果:
锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
Process finished with exit code 0
案例:
package Demo1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Test8 {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入数据");
String s = reader.readLine();
System.out.println(s);
}
}
结果:
请输入数据
asd
asd
Process finished with exit code 0
RandomAccessFile概述:
最大特点 能读能写
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。
RandomAccessFile的父类是Object , 这个流对象可以用来读取数据也可以用来写数据.可以操作任意数据类型的数据.
我们可以通过getFilePointer方法获取文件指针,并且可以通过seek方法设置文件指针.
案例:
package Demo2;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Test1 {
public static void main(String[] args) throws IOException {
RandomAccessFile rw = new RandomAccessFile("a.txt", "rw");
//获取RandomAccessFile的文件指针
long l = rw.getFilePointer();
System.out.println(l);
byte[] bytes = new byte[3];
//读取一段字节存入字节数组,存满为止。
int read = rw.read(bytes);
//将字节数组转换为字符串
System.out.println(new String(bytes));
l = rw.getFilePointer();
System.out.println(l);
rw.read();
l = rw.getFilePointer();
System.out.println(l);
rw.read();
l = rw.getFilePointer();
System.out.println(l);
rw.read();
l = rw.getFilePointer();
System.out.println(l);
rw.read();
l = rw.getFilePointer();
System.out.println(l);
//向文件中写入数据
rw.write("\t\n".getBytes());
rw.write("谁知盘中餐".getBytes());
rw.write("\t\n".getBytes());
rw.write("粒粒皆辛苦".getBytes());
}
}
结果:
0
锄
3
4
5
6
7
Process finished with exit code 0
打开"a.txt"文件,查看数据:
锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
案例代码:
package Demo2;
import java.io.*;
public class Test2 {
public static void main(String[] args) throws IOException {
//使用随机访问流将一个4.5mb的文件切割为每个大小为1mb的小文件。
int n=1;
RandomAccessFile r = new RandomAccessFile("a.mp3", "r");
int len=0;
byte[] bytes = new byte[1024 * 1024];
while ((len=r.read(bytes))!=-1){
File file = new File(n + "a.mp3");
FileOutputStream w = new FileOutputStream(file);
w.write(bytes,0,len);
n++;
}
}
}
结果:
序列化流的概述:
所谓的序列化:就是把对象通过流的方式存储到文件中.
注意:此对象 要重写Serializable 接口才能被序列化
反序列化:就是把文件中存储的对象以流的方式还原成对象
序列化流: ObjectOutputStream
反序列化流: ObjectInputStream
像这样一个接口中如果没有方法,那么这样的接口我们将其称之为标记接口(用来给类打标记的,相当于猪肉身上盖个章)
一个对象可以被序列化的前提是这个对象对应的类必须实现Serializable接口
案例代码:
package Demo2;
import java.io.*;
import java.util.ArrayList;
public class Test3 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream w1 = new ObjectOutputStream(new FileOutputStream("Student1.txt"));
ObjectInputStream r1 = new ObjectInputStream(new FileInputStream("Student.txt"));
//向指定文件写入对象数据,
w1.writeObject(new Student("张小凡",18));
w1.writeObject(new Student("林惊羽",19));
w1.writeObject(new Student("陆雪琪",18));
//从指定文件读取数据,每次读取一个,并且只能按照顺序读取。
Student s1 = (Student) r1.readObject();
Student s2 = (Student) r1.readObject();
Student s3 = (Student) r1.readObject();
System.out.println(s1.getName()+"=="+s1.getAge());
System.out.println(s2.getName()+"=="+s2.getAge());
System.out.println(s3.getName()+"=="+s3.getAge());
System.out.println("=================================================");
//为了方便读取,可以将对象录入集合中,将集合写入文档。
ObjectOutputStream w2 = new ObjectOutputStream(new FileOutputStream("Student2.txt"));
ObjectInputStream r2 = new ObjectInputStream(new FileInputStream("Student2.txt"));
ArrayList<Student> list = new ArrayList<>();
Student ss1 = new Student("令狐冲", 23);
Student ss2 = new Student("任盈盈", 22);
Student ss3 = new Student("林平之", 21);
list.add(ss1);
list.add(ss2);
list.add(ss3);
//将集合写入文档
w2.writeObject(list);
//读取集合
ArrayList ss = (ArrayList) r2.readObject();
//根据集合索引选择想要读取的数据
Student student = (Student) ss.get(1);
System.out.println(student.getName()+"=="+student.getAge());
}
}
class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
结果:
张小凡==18
林惊羽==19
陆雪琪==18
=================================================
任盈盈==22
Process finished with exit code 0
使用transient关键字声明不需要序列化的成员变量
package Demo2;
import java.io.*;
public class Test4 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream w1 = new ObjectOutputStream(new FileOutputStream("Boss.txt"));
ObjectInputStream r1 = new ObjectInputStream(new FileInputStream("Boss.txt"));
w1.writeObject(new Boss("灭霸",20000));
Boss b = (Boss) r1.readObject();
System.out.println(b.getName()+"=="+b.getAge());
}
}
class Boss implements Serializable {
private String name;
private transient int age;
public Boss(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
结果:
//因为Boss类的成员变量age被transient修饰,所以读取出来的age是默认的0;
灭霸==0
Process finished with exit code 0
Properties 类表示了一个持久的属性集。
Properties 可保存在流中或从流中加载。
属性列表中每个键及其对应值都是一个字符串。
Properties父类是Hashtable
属于双列集合,这个集合中的键和值都是字符串 Properties不能指定泛型
package Demo2;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Test5 {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
HashMap<String, String> map = new HashMap<>();
map.put("步惊云","排云掌");
map.put("聂风","风神腿");
map.put("雄霸","三分归元气");
PrintWriter wr = new PrintWriter(new FileOutputStream("test.properties"), true);
//写入数据到指定Properties文件。
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
wr.println (entry.getKey()+"="+entry.getValue());
}
//读取"test.properties"文件中的数据
HashMap<String, String> readMap = new HashMap<>();
BufferedReader read = new BufferedReader(new FileReader("test.properties"));
while(true){
String s = read.readLine();
if (s != null) {
String[] split = s.split("=");
readMap.put(split[0],split[1]);
}else {
break;
}
}
Set<Map.Entry<String, String>> entries1 = readMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey()+"="+entry.getValue());
}
wr.close();
//写入数据:使用setProperty()方法将数据写入缓存。
pro.setProperty("步惊云","排云掌");
pro.setProperty("聂风","风神腿");
pro.setProperty("雄霸","三分归元气");
//根据键找值:使用getProperty()方法将缓存中的数据根据键找到对应值
String s = pro.getProperty("聂风");
System.out.println(s);
}
}
结果:
步惊云=排云掌
雄霸=三分归元气
聂风=风神腿
风神腿
Process finished with exit code 0
Properties和IO流进行配合使用:
public void load(Reader reader): 读取键值对数据把数据存储到Properties中
public void store(Writer writer, String comments)把Properties集合中的键值对数据写入到文件中,
案例:Properties的store()功能
package Demo2;
import java.io.*;
import java.util.Properties;
public class Test6 {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
pro.setProperty("aaaaa","11111");
pro.setProperty("bbbbb","22222");
pro.setProperty("ccccc","33333");
//把Properties集合中的键值对数据写入到文件中,
pro.store(new FileOutputStream("a.properties"), null);
}
}
案例:Properties的load()功能
package Demo2;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class Test7 {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
pro.load(new FileInputStream("a.properties"));
System.out.println(pro.getProperty("aaaaa"));
System.out.println(pro.getProperty("bbbbb"));
System.out.println(pro.getProperty("ccccc"));
//如果在"a.properties"文件中没有找到"eeeee"这个键,返回null
System.out.println(pro.getProperty("eeeee"));
}
}
结果:
11111
22222
33333
null
Process finished with exit code 0
SequenceInputStream的概念
表示其他输入流的逻辑串联。
它从输入流的有序集合开始,
并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,
依次类推,直到到达包含的最后一个输入流的文件末尾为止
构造方法1
SequenceInputStream(InputStream s1, InputStream s2)
通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),
以提供从此 SequenceInputStream 读取的字节。
构造方法2
SequenceInputStream(Enumeration<? extends InputStream> e)
通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
案例:将两个文件合并为一个。
package Demo2;
import java.io.*;
public class Test8 {
public static void main(String[] args) throws IOException {
SequenceInputStream seq = new SequenceInputStream(new FileInputStream("a.properties"), new FileInputStream("test.properties"));
FileOutputStream out = new FileOutputStream("b.properties");
int len=0;
byte[] bytes = new byte[1024];
while ((len=seq.read(bytes))!=-1){
out.write(bytes,0,len);
}
seq.close();
out.close();
}
}
结果:
文件:b.properties文件的数据。
文件:a.properties文件的数据。
文件:test.properties文件的数据。
案例:多个文件合成为一个文件
package Demo2;
import java.io.*;
import java.util.Enumeration;
import java.util.Vector;
public class Test9 {
public static void main(String[] args) throws IOException {
FileInputStream in1 = new FileInputStream("a.properties");
FileInputStream in2 = new FileInputStream("b.properties");
FileInputStream in3 = new FileInputStream("test.properties");
//将多个文件存入一个Vector集合
Vector<FileInputStream> list = new Vector<>();
list.add(in1);
list.add(in2);
list.add(in3);
//使用Vector集合的elements()方法迭代,
Enumeration<FileInputStream> ele = list.elements();
//将Vector集合的elements()方法迭代结果作为SequenceInputStream()的形参。
SequenceInputStream seq = new SequenceInputStream(ele);
FileOutputStream out = new FileOutputStream("c.properties");
int len=0;
byte[] bytes = new byte[1024];
while ((len=seq.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
seq.close();
out.close();
}
}
结果: