IO流(二)(其他常用流)
(一)数据输入输出流
数据输入流: DataInputStream
数据输出流: DataOutputStream
特点 可以写基本数据类型,可以读取基本数据类型
特有方法
write(基本类型)可以写基本类型
read()读写顺序一致
(二)内存操作流
A:内存操作流的概述
a:操作字节数组
ByteArrayOutputStream
ByteArrayInputStream
此流关闭无效,所以无需关闭
b:操作字符数组
CharArrayWrite
CharArrayReader
c:操作字符串
StringWriter
StringReader
ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪
read` 方法要提供的下一个字节。
关闭 ByteArrayInputStream
无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException
。
不关联任何文件,中对内存中的数据进行读写
(用StringBuffer当缓冲区)
文件合并1
public class Demo13 {
public static void main(String[] args) throws IOException {
FileInputStream inputStream = new FileInputStream("G:\\20190724-字符流-上午1\\20190724-杂七杂八流-下午\\许巍 - 曾经的你.mp3");
FileInputStream inputStream1 = new FileInputStream("G:\\20190724-字符流-上午1\\20190724-杂七杂八流-下午\\许巍 - 蓝莲花.mp3");
FileOutputStream out = new FileOutputStream("E:\\音乐\\歌曲大合唱.mp3");
ArrayList<FileInputStream> list = new ArrayList<>();
list.add(inputStream);
list.add(inputStream1);
int len=0;
byte[] bytes = new byte[1024*8];
for (FileInputStream stream : list) {
while ((len=stream.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
stream.close();
}
out.close();
}
}
方法2
public class 把多个文件合成一个文件 {
public static void main(String[] args) throws IOException {
FileInputStream in1 = new FileInputStream("许巍 - 曾经的你.mp3");
FileInputStream in2 = new FileInputStream("许巍 - 蓝莲花.mp3");
FileOutputStream out = new FileOutputStream("歌曲大联唱.mp3");
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
//创建一个集合
ArrayList<FileInputStream> list = new ArrayList<>();
list.add(in1);
list.add(in2);
int len=0;
byte[] bytes = new byte[1024 * 8];
for (FileInputStream in : list) {
while ((len=in.read(bytes))!=-1){
byteOut.write(bytes,0,len);
byteOut.flush();
}
in.close();
}
//取出两首歌的字节数据
byte[] allBytes = byteOut.toByteArray();
System.out.println(allBytes.length);
//将两首歌的字节数据,写到硬盘上
ByteArrayInputStream byteIn = new ByteArrayInputStream(allBytes);
int len2 = 0;
byte[] bytes2 = new byte[1024 * 8];
while ((len2 = byteIn.read(bytes)) != -1) {
out.write(bytes, 0, len2);
out.flush();
}
out.close();
}
}
(三)打印流
PrintStream
-
打印流的特点 a: 打印流只能操作目的地,不能操作数据源(不能进行读取数据) - b: 可以操作任意数据类型的数据 调用print() 方法可以写任意数据类型 - c: 如果我们启用自动刷新,那么在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新 /** 通过以下构造创建对象 能够启动自动刷新 然后调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新 /** 通过以下构造创建对象 能够启动自动刷新 然后调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新 * public PrintWriter(OutputStream out, boolean autoFlush) 启动 自动刷新 * public PrintWriter(Writer out, boolean autoFlush) 启动自动刷新 */ - d: 这个流可以直接对文件进行操作(可以直接操作文件的流: 就是构造方法的参数可以传递文件或者文件路径)
字节打印流(文件和屏幕)
字符打印流(只能关联文件)
键盘录入的第二种方法
public class 键盘录入的第二种方式 {
public static void main(String[] args) throws IOException {
//Scanner scanner = new Scanner(System.in);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
System.out.println("请输入数据");
String s = reader.readLine();
//自定义一个结束标记
if("886".equals(s)){
break;
}
System.out.println(s);
}
}
}
PrintWriter实现自动刷新和换行
A:案例演示:PrintWriter实现自动刷新和换行
PrintWriter pw = new PrintWriter(new FileWriter("printWriter2.txt") , true) ;
pw.println(true) ;
pw.println(100) ;
pw.println("中国") ;
(四)随机访问流
A:随机访问流概述
RandomAccessFile概述 最大特点 能读能写 注:你咋么写,就咋么读取 顺序不能乱
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。
支持对随机访问文件的读取和写入。
RandomAccessFile的父类是Object , 这个流对象可以用来读取数据也可以用来写数据.可以操作任意数据类型的数据.
我们可以通过getFilePointer方法获取文件指针,并且可以通过seek方法设置文件指针
文件复制多份
public class Demo4 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("你想要复制几份");
int i = scanner.nextInt();
try {
RandomAccessFile f1 = new RandomAccessFile(new File("G:\\20190724-字符流-上午1\\20190724-杂七杂八流-下午\\许巍 - 曾经的你.mp3"),"rw");
for ( i = 0; i < 3; i++) {
f1.seek(0);
RandomAccessFile f2 = new RandomAccessFile(new File("G:\\20190724-字符流-上午1\\20190724-杂七杂八流-下午\\曾经的你"+ i+".mp3"), "rw");
int len = 0;
byte[] bytes = new byte[1024*1024];
while ((len = f1.read(bytes)) != -1) {
f2.write(bytes, 0, len);
}
}
f1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(五)序列化流
A:序列化流的概述
所谓的序列化:就是把对象通过流的方式存储到文件中.注意:此对象 要重写Serializable 接口才能被序列化
反序列化:就是把文件中存储的对象以流的方式还原成对象
序列化流: ObjectOutputStream
反序列化流: ObjectInputStream
```
像这样一个接口中如果没有方法,那么这样的接口我们将其称之为标记接口(用来给类打标记的,相当于猪肉身上盖个章)
```
一个对象可以被序列化的前提是这个对象对应的类必须实现Serializable接口
如何解决序列化时候的黄色警告线问题
我们的一个类可以被序列化的前提是需要这个类实现Serializable接口,就需要给这个类添加一个标记.
在完成序列化以后,序列化文件中还存在一个标记,然后在进行反序列化的时候,
会验证这个标记和序列化前的标记是否一致,如果一致就正常进行反序列化,如果
不一致就报错了. 而现在我们把这个类做了修改,将相当于更改了标记,而导致这两个标记不一致,就报错了.
解决问题: 只要让这个两个标记一致,就不会报错了吧
怎么让这两个标记一致呢? 不用担心,很简单,难道你们没有看见黄色警告线吗? alt+enter, 生成出来
private static final long serialVersionUID = -7602640005373026150L;
如何让对象的成员变量不被序列化
使用transient关键字声明不需要序列化的成员变量
private transient int age ;// 可以阻止成员变量的序列化使用transient
(六)Properties
A:Properties的概述
Properties 类表示了一个持久的属性集。
Properties 可保存在流中或从流中加载。
属性列表中每个键及其对应值都是一个字符串。
Properties父类是Hashtable
- 属于双列集合,这个集合中的键和值都是字符串 Properties不能指定泛型
Properties的特殊功能使用
A:Properties的特殊功能
public Object setProperty(String key,String value)
public String getProperty(String key)
public Set<String> stringPropertyNames()
Properties的load()和store()功能
-
A:Properties的load()和store()功能 Properties和IO流进行配合使用: - public void load(Reader reader): 读取键值对数据把数据存储到Properties中 - public void store(Writer writer, String comments)把Properties集合中的键值对数据写入到文件中, comments注释
A:案例演示
需求:我有一个文本文件,我知道数据是键值对形式的,但是不知道内容是什么。 请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其值为”100”案例
分析:
- a: 把文本文件中的数据加载到Properties集合中
- b: 判断这个集合中是否有"lisi"这个键
- 如果有直接修改其值为100
- c: 把集合中的数据再次存储到文本文件中
public class Demo{
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.load(new FileInputStream("user.propreties"));
if(properties.containsKey("lisi")){
//如果这个键存在,把值改为100
properties.setProperty("lisi","100");//键相同,值覆盖
}
//改完之后再写回源文件
properties.store(new FileWriter("user.propreties"),null);
}
}
(七)SequenceInputStream
SequenceInputStream
表示其他输入流的逻辑串联。
它从输入流的有序集合开始,
并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,
依次类推,直到到达包含的最后一个输入流的文件末尾为止
a:构造方法
SequenceInputStream(InputStream s1, InputStream s2)
通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),
以提供从此 SequenceInputStream 读取的字节。
b:构造方法
SequenceInputStream(Enumeration<? extends InputStream> e)
通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数
案例:将一个music.mp3文件,拆分成多个小文件,再将多个小文件,合并成一个mp3文件
public class Demo2 {
public static void main(String[] args) throws IOException {
heBing();
}
private static void heBing() throws IOException {
File file = new File("E:\\music");
File[] files = file.listFiles();
Vector<FileInputStream> vector = new Vector<>();
for (File f : files) {
FileInputStream in = new FileInputStream(f);
vector.add(in);
}
Enumeration<FileInputStream> elements = vector.elements();
SequenceInputStream sequenceInputStream = new SequenceInputStream(elements);
FileOutputStream out = new FileOutputStream(new File(file, "合并.mp3"));
int len = 0;
byte[] bytes = new byte[1024 * 1024];
while ((len=sequenceInputStream.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
out.close();
sequenceInputStream.close();
//合并完之后,把小文件删掉
File file1 = new File("E:\\music");
File[] files1 = file.listFiles();
for (File ff : files1) {
if(ff.length()<=1024*1024){
ff.delete();
}
}
}
private static void chaiFen() throws IOException {
FileInputStream in = new FileInputStream("歌曲串烧.mp3");
//我们封装一个文件夹,放我们拆分的文件
File file = new File("E:\\music");
if(!file.exists()){
file.mkdirs();
}
//拆分
int len=0;
byte[] bytes=new byte[1024*1024];
int index=0;
while ((len=in.read(bytes))!=-1){
index++;
FileOutputStream out = new FileOutputStream(new File(file, index + ".mp3"));
out.write(bytes,0,len);
out.close();
}
in.close();
}
}