1、字节数组流
我们之前的流都是直接操作文件的,而字节数组流则是操作的字节数组。我们可以把我们要操作的字节数组看成是其他电脑的内存中的数据或是网络中的数据。字节数组流是作为中间人的作用,存储到内存里面,数据量不能太大。
文件------->通过程序读取------->字节数组
字节数组---------> 通过程序写出---->文件
1.1 字节数组输入输出流
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 字节数组 节点流
* 数组的长度有限 ,数据量不会很大
*
* 文件内容不用太大
* 1、文件 --程序->字节数组
* 2、字节数组 --程序->文件
*/
public class ByteArrayDemo01 {
public static void main(String[] args) throws IOException {
read(write());
}
/**
* 输出流 操作与文件输出流 有些不同, 有新增方法,不能使用多态
*/
public static byte[] write() throws IOException{
//目的地
byte[] dest;
//选择流 不同点
ByteArrayOutputStream bos =new ByteArrayOutputStream();
//操作 写出
String msg ="操作与 文件输入流操作一致";
byte[] info =msg.getBytes();
bos.write(info, 0, info.length);//把数据写到字节数组流bos中
//获取数据
dest =bos.toByteArray();//从字节数组流中得到字节数组
//释放资源
bos.close();//close可以不调用,因为字节数组流的数据是在内存中的,会自动释放
return dest;
}
/**
* 输入流 操作与 文件输入流操作一致
* 读取字节数组
*/
public static void read(byte[] src) throws IOException{
//数据源传入
//String msg ="操作与 文件输入流操作一致";
//byte[] src=msg.getBytes();
//选择流
InputStream is =new BufferedInputStream(new ByteArrayInputStream(src));
//操作
byte[] flush =new byte[1024];
int len =0;
while(-1!=(len=is.read(flush))){
System.out.println(new String(flush,0,len));
}
//释放资源
is.close();
}
}
1.2 字节数组流作为中间人进行文件的复制
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
*1、文件 --程序->字节数组
*1)、文件输入流
* 字节数组输出流
* 2、字节数组 --程序->文件
* 1)、字节数组输入流
* 文件输出流
*/
public class ByteArrayDemo02 {
public static void main(String[] args) throws IOException {
byte[] data =getBytesFromFile("e:/xp/test/1.jpg");//把文件内容写到字节数组中
toFileFromByteArray(data,"e:/xp/test/arr.jpg");//把字节数组内容写出到文件中
}
/**
* 2、字节数组 --程序->文件
*/
public static void toFileFromByteArray(byte[] src,String destPath) throws IOException{
//目的地
File dest=new File(destPath);
//选择流
//字节数组输入流
InputStream is =new BufferedInputStream(new ByteArrayInputStream(src));
//文件输出流
OutputStream os =new BufferedOutputStream(new FileOutputStream(dest));
//操作 不断读取字节数组
byte[] flush =new byte[1];
int len =0;
while(-1!=(len =is.read(flush))){
//写出到文件中
os.write(flush, 0, len);
}
os.flush();
//释放资源
os.close();
is.close();
}
/**
* 1、文件 --程序->字节数组
*/
public static byte[] getBytesFromFile(String srcPath) throws IOException{
//创建文件源
File src =new File(srcPath);
//创建字节数组目的地
byte[] dest =null;
//选择流
//文件输入流
InputStream is =new BufferedInputStream(new FileInputStream(src));
//字节数组输出流 不能使用多态
ByteArrayOutputStream bos =new ByteArrayOutputStream();
//操作 不断读取文件 写出到字节数组流中
byte[] flush =new byte[1024];
int len =0;
while(-1!=(len =is.read(flush))){
//写出到字节数组流中
bos.write(flush, 0, len);
}
bos.flush();
//获取数据
dest =bos.toByteArray();
bos.close();
is.close();
return dest;
}
}
2、基本数据类型和字符串的处理流
有时候我们的需求是读取和写出数据的时候,要保留数据的类型,比如double类型的2.5,int类型的100,我们希望把这些数据写到文件或者字节数组中的时候,能保留其数据类型。那么就要用到:输入流:DataInputStream和DataOutputStream。
应用场景:比如游戏当中发送坐标,需要保留类型,就不需要再次解析了。
注意:如果是通过该流写到文件中的内容,我们人是读不懂的,是给机器读的。读取的顺序要和写入的顺序一致,否则数据会混乱。
2.1 读写基本类型+字符串到文件中
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 数据类型(基本+String)处理流
* 1、输入流 DataInputStream readXxx()
* 2、输出流 DataOutputStream writeXxx()
* 新增方法不能使用多态
*
* java.io.EOFException :已经读到了文件末尾,没有读取到相关的内容
*/
public class DataDemo01 {
public static void main(String[] args) {
try {
//write("e:/xp/test/data.txt");
//read("e:/xp/test/arr.txt"); //非法内容
read("e:/xp/test/data.txt");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 从文件读取数据+类型
*/
public static void read(String destPath) throws IOException{
//创建源
File src =new File(destPath);
//选择流
DataInputStream dis =new DataInputStream(new BufferedInputStream(new FileInputStream(src)));
//操作 读取的顺序与写出一致 必须存在才能读取
//不一致,数据存在问题
long num2 =dis.readLong();
double num1 =dis.readDouble();
String str =dis.readUTF();
dis.close();
System.out.println(num2+"-->"+str);
}
/**
* 数据+类型输出到文件
*/
public static void write(String destPath) throws IOException{
double point =2.5;
long num=100L;
String str ="数据类型";
//创建源
File dest =new File(destPath);
//选择流 DataOutputStream
DataOutputStream dos =new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
//操作 写出的顺序 为读取准备
dos.writeDouble(point);
dos.writeLong(num);
dos.writeUTF(str);
dos.flush();
//释放资源
dos.close();
}
}
2.2 读写基本数据类型+字符串到字节数组中
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class DataDemo02 {
public static void main(String[] args) {
try {
byte[] data=write();
read(data);
System.out.println(data.length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 从字节数组读取数据+类型
*/
public static void read(byte[] src) throws IOException{
//选择流
DataInputStream dis =new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(src)));
//操作 读取的顺序与写出一致 必须存在才能读取
double num1 =dis.readDouble();
long num2 =dis.readLong();
String str =dis.readUTF();
dis.close();
System.out.println(num1+"-->"+num2+"-->"+str);
}
/**
* 数据+类型输出到字节数组中
*/
public static byte[] write() throws IOException{
//目标数组
byte[] dest =null;
double point =2.5;
long num=100L;
String str ="数据类型";
//选择流 ByteArrayOutputStream DataOutputStream
ByteArrayOutputStream bos =new ByteArrayOutputStream();
DataOutputStream dos =new DataOutputStream(new BufferedOutputStream(bos));
//操作 写出的顺序 为读取准备
dos.writeDouble(point);
dos.writeLong(num);
dos.writeUTF(str);
dos.flush();
dest =bos.toByteArray();
//释放资源
dos.close();
return dest;
}
}
3、对象处理流
上面第二点讲的是基本数据类型的处理,那么引用类型怎么读写呢?JDK为我们提供了对象处理流:ObjectInputStream;
反序列化 输入流 :ObjectInputStream readObject()
序列化 输出流 :ObjectOutputStream writeObject()
注意:
1)、先序列化后反序列化;反序列化顺序必须与序列化一致
2)、不是所有的对象都可以序列化, java.io.Serializable,不是所有的属性都需要序列化,不想序列化的属性加上关键字transient
/**
* Serializable :接口只是标识
*/
public class Employee implements java.io.Serializable {
//不需要序列化
private transient String name;
private double salary;
public Employee() {
}
public Employee(String name, double salary) {
super();
this.name = name;
this.salary = salary;
}
//get/set方法
}
案例:
package com.bjsxt.io.others;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
/**
* 不是所有的對象都可以序列化 java.io.NotSerializableException
* 不是所有的屬性都需要序列化 transient
*/
public class ObjectDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
try {
seri("e:/xp/test/ser.txt");
read("e:/xp/test/ser.txt");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//反序列化
public static void read(String destPath) throws IOException, ClassNotFoundException{
//创建源
File src =new File(destPath);
//选择流
ObjectInputStream dis =new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(src)
)
);
//操作 读取的顺序与写出一致 必须存在才能读取
//不一致,数据存在问题
Object obj =dis.readObject();
if(obj instanceof Employee){
Employee emp=(Employee)obj;
System.out.println(emp.getName());
System.out.println(emp.getSalary());
}
obj =dis.readObject();
int[] arr=(int[])obj;
System.out.println(Arrays.toString(arr));
dis.close();
}
//序列化
public static void seri(String destPath) throws IOException{
Employee emp =new Employee("bjsxt",1000000);
int[] arr ={1,2,3,45};
//创建源
File dest =new File(destPath);
//选择流 ObjectOutputStream
ObjectOutputStream dos =new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream(dest)
)
);
//操作 写出的顺序 为读取准备
dos.writeObject(emp);
dos.writeObject(arr);
//释放资源
dos.close();
}
}
4、编写工具类实现关闭的方法
1、可变参数:用 ... 表示,只能放在形参最后一个位置,处理方式(遍历,获取等)与数组一致。
import java.io.Closeable;
public class FileUtil {
/**
* 工具类关闭流
* 可变参数: ... 只能形参最后一个位置,处理方式与数组一致
*/
public static void close(Closeable ... io){
for(Closeable temp:io){
try {
if (null != temp) {
temp.close();
}
} catch (Exception e) {
}
}
}
/**
* 使用泛型方法
*/
public static <T extends Closeable> void closeAll(T ... io){
for(Closeable temp:io){
try {
if (null != temp) {
temp.close();
}
} catch (Exception e) {
}
}
}
}
注意:JDK7中新增了try-with-resource语法,可以自动实现io流从关闭。
将IO流对象的创建放在try关键字
后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用
。代码是不是瞬间简洁许多!
结构:
try{
//IO流对象的创建
}{
//其他业务代码
}catch(Exceptiion e){
//异常处理
}
参考:https://blog.csdn.net/csdnlijingran/article/details/88935744
5、打印流
我们经常使用的System.out就是一个打印流PrintStream ,它也属于处理流。
5.1 打印流输出内容到文件
我们经常使用的是System.out.println(),把内容输出到控制台,下面我们创建把内容打印到指定文件的打印流:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/**
* PrintStream 打印流 -->处理流
*/
public class PrintStreamDemo01 {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("test");
PrintStream ps =System.out;
ps.println(false);
//输出到文件
File src = new File("e:/xp/test/print.txt");
ps = new PrintStream(new BufferedOutputStream(new FileOutputStream(src)));
ps.println("io is so easy....");//这行内容会输出到print.txt文件中
ps.close();
}
}
5.2 System类中的三个常量
我们经常使用的System.in,System.out,System.err,in,out和err是三个常量,下面看看它们的使用
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
/**
* 三个常量
* 1、System.in 输入流 键盘输入
* 2、System.out 输出流 控制台输出
* System.err
*
* ==>默认System的三个常量都是把内容输出到控制台的,但是System也提供了如下的 重定向 方法
* setIn() //重定向从哪里录入
* setOut() //重定向输出到哪里
* setErr()
* FileDescriptor.in //标准的输入
* FileDescriptor.out //标准的输出
*/
public class SystemDemo01 {
public static void main(String[] args) throws FileNotFoundException {
//test1();
//test2();
//重定向,接下来调用System.out方法就会把内容输出到文件print.txt中
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream("e:/xp/test/print.txt")),true));
System.out.println("a"); //控制台 -->文件
System.out.println("test");
//输出的内容回控制台,true表示自动刷新,也可以手动调用flush方法
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true));
System.out.println("back....");
}
public static void test2() throws FileNotFoundException{
InputStream is =System.in; //键盘输入
is = new BufferedInputStream(new FileInputStream("e:/xp/test/print.txt"));
Scanner sc = new Scanner(is);
//System.out.println("请输入:");
System.out.println(sc.nextLine());
}
public static void test1(){
System.out.println("test");
System.err.println("err");
}
}
5.3 手动封装控制台输入
之前我们要录入控制台用户的用户输入都是使用 Scanner sc = new Sacnner(System.in); 接下来我们自己封装一个类似Scanner的功能:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* 封装输入
如果要处理数据类型的话,就要另外自己去转化了
*/
public class BuffereIn {
public static void main(String[] args) throws IOException {
InputStream is =System.in;
BufferedReader br = new BufferedReader(new InputStreamReader(is));
System.out.println("请输入。。。。");
String msg =br.readLine();
System.out.println(msg);
}
}
6、IO流之设计模式(装饰者设计模式)
装饰模式:增强功能。比如:扩音器就用到了装饰模式,可以增大我们的音量。下面用代码实现:
定时声音类:Voice
public class Voice {
private int voice =10;
public Voice() {
}
public int getVoice() {
return voice;
}
public void setVoice(int voice) {
this.voice = voice;
}
public void say(){
System.out.println(voice);
}
}
扩音器类:
/**
* 扩音器
* 类与类之间的关系
* 1、依赖:形参|局部变量
* 2、关联:属性
* 聚合:属性 整体与部分 不一致的生命周期 人与手
* 组合:属性 整体与部分 一致的生命周期 人与大脑
* 3、继承:父子类关系
* 4、实现: 接口与实现类关系
*/
public class Amplifier {
private Voice voice;
public Amplifier() {
}
public Amplifier(Voice voice) {
super();
this.voice = voice;
}
public void say(){
System.out.println(voice.getVoice()*1000);//增大音量
}
}
测试代码:
public class App {
public static void main(String[] args) {
Voice v =new Voice();
v.say();
Amplifier am =new Amplifier(v);
am.say();
}
}
7、IO流总结
节点流:离数据源|程序 最近的流 。
处理流:装饰模式,提高性能增强功能 。
1、 字节流:可以处理一切(纯文本、音频、视频等)
1)、输入流 InputStream FileInputStream ByteArrayInputStream 操作::read(字节数组)
a)、中间容器 byte[] flush=new byte[长度]
b)、接收长度 int len =0;
c)、循环读取 while(-1!=(len=流.read(flush))){}
d)、操作:输出、拷贝
2)、输出流 OutputStream FileOutputStream ByteArrayOutputStream 操作:write(字节数组,0,长度) 输出
2、 字符流:只能处理纯文本
1)、输入流:Reader FileReader 操作:read(字符数组)
a)、中间容器 char[] flush=new char[长度]
b)、接收长度 int len =0;
c)、循环读取 while(-1!=(len=流.read(flush))){}
d)、操作:输出、拷贝
2)、输出流:Writer FileWriter 操作:write(字符数组,0,长度) 输出
1、 转换流:解码与编码字符集问题
1)、输入流:InputStreamReader 解码
2)、输出流:OutputStreamWriter 编码
2、缓冲流:提高性能
1)、输入流:BufferedInputStream BufferedReader
2)、输出流:BufferedOutputStream BufferedWriter
3、处理数据+类型
1)、基本+字符串:必须存在才能读取 读取与写出顺序一致
a)、输入流:DataInputStream readXxx
b)、输出流:DataOutputStream writeXxx
2)、引用类型:Serializable transient
a)、反序列化:ObjectInputStream readObject
b)、序列化:ObjectOutputStream writeObject
4、打印流: PrintStream
5、System.in out err setIn setOut
以下流使用新增方法不能发生多态
1、 ByteArrayOutputStream: toByteArray()
2、 BufferedReader: readLine()
3、 BufferedWriter:newLine()
4、 DataInputStream DataOutputStream
5、 ObjectInputStream ObjectOutputStream
6、 PrintStream