**数据源**data source,提供数据的原始媒介。常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO设备。数据源分为:源设备、目标设备。<br /> **流**是一个抽象、动态的概念,是一连串连续动态的数据集合。
IO可以理解成bio
- BIO:同步阻塞IO。一个线程触发io操作后必须等待这个io操作执行完成,期间不能去做其它的事
- NIO:同步非阻塞。一个线程触发io操作后它可以立即返回,但是需要不断地去获取执行结果
- AIO:异步非阻塞。一个线程触发io操作后它可以立即返回去做其它工作,内核系统将io操作执行完成后会通知该线程
流的概念细分
按流的方向分类:
1. 输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
2. 输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。
按处理的数据单元分类:
1. 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。
2. 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。
按处理对象不同分类:
1. 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
2. 处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过它们进行; 处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
四大抽象类
抽象类 | 说明 | 常用方法 |
---|---|---|
InputStream | 字节输入流的父类,单位字节 | - int read(); - void close(); |
OutputStream | 字节输出流的父类,单位字节 | - void write(int); - void flush(); - void close(); |
Reader | 字符输入流的父类,单位字符 | - int read(); - void close(); |
Writer | 字符输出流的父类,单位字符 | - void write(string); - void flush(); - void close(); |
FileInputStream
通过字节的方式读取文件,适合读取所有类型的文件
////写入字节流 按字段读取
public class ReadTxt {
public static void main(String[] args) {
File file2 = new File("a.txt");
FileInputStream in2 = null;
try {
in2 = new FileInputStream(file2);
//缓冲容器 接收字节数组
byte[] temp = new byte[5];
int len=-1; //接收长度
//read( byte[]): 返回读入缓冲区的字节总数,到末尾返回-1
while((len = in2.read(temp)) != -1) { //in2把字节读入到temp中
//字节数组到字符(使用默认字符集解码指定的 byte 子数组)
String s1 = new String(temp,0,len);
System.out.println(s1);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(in2 != null) {
in2.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream
通过字节的方式写数据到文件中,适合所有类型的文件
//写出字节流
public class OutStream {
public static void main(String[] args) {
File f1 = new File("t3.txt");
OutputStream os1 = null;
try {
os1 = new FileOutputStream(f1);
String s1 = "IO stream";
byte[] dates = s1.getBytes();//字符串到字节数组(编码)
os1.write(dates, 0, dates.length);
os1.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件的拷贝
//文件的拷贝
public static void copy(String srcpath,String destpath) {
File src = new File(srcpath); //源头
File dest = new File(destpath); //目的地
InputStream in1 = null;
OutputStream os1 = null;
try {
in1 = new FileInputStream(src);
os1 = new FileOutputStream(dest);
byte[] temp = new byte[10]; //缓冲容器
int len =-1;
while((len=in1.read(temp))!= -1) { //read方法已将它转为字节
os1.write(temp,0,len); //write方法直接写出字节
os1.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源 分别关闭 先打开的后关闭
try {
os1.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
in1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileReader
通过字符的方式读取文件
文件字符输入流
//文件字符流(可输出文字)
public class CharStream {
public static void main(String[] args) {
File f1 = new File("a.txt");
Reader read= null;
try {
read = new FileReader(f1);
char[] temp = new char[1024]; //缓冲容器
int len =-1;
while((len=read.read(temp))!= -1) {
//字符数组到字符串
//String str = new String(temp,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(read!=null) {
read.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileWriter
通过字节的方式或追加数据到文件中,仅适合字符文件
文件字符输出流
//FileWriter
public class CharOutStream {
public static void main(String[] args) {
File f1 = new File("a.txt");
FileWriter fw1 = null;
try {
fw1 = new FileWriter(f1);
/*法1
String str1 = "开心每一天";
char[] date = str1.toCharArray();//字符串转换成字符数组
fw1.write(date,0,date.length);
*/
/*法2 直接写入字符串
String str1 = "开心每一天";
fw1.write(str1);
*/
//法3
fw1.append("一天").append("快乐");
//字符读取不够整个数组长度时 不会写入文件中 要刷新一下
fw1.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fw1!=null) {
fw1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ByteArrayInputStream和ByteArrayOutputStream
经常用在需要流和数组之间转化的情况,字节数组是二进制,转换后方便网络传输
注:在内存里运行,适用于量小的字节数据
字节数组输入流
//字节数组输入流
public class ByteArrayStream {
public static void main(String[] args) throws IOException {
byte[] src = "abcde".getBytes();//字符串转换成字节数组
InputStream in1 = null;
in1 = new ByteArrayInputStream(src); //字节数组
byte[] temp =new byte[5];
int len = -1;
while((len=in1.read(temp))!=-1) {
//转回字符串输出
String str1 = new String(temp,0,len);
System.out.println(str1);
}
}
}
字节数组输出流
//字节数组输出流
public class ByteoutStream {
public static void main(String[] args) throws IOException {
byte[] dest = null;
ByteArrayOutputStream b1 = null;
b1 = new ByteArrayOutputStream();//不用指定目的地 内存会自动分配
//写出操作
String str1 = "abcdefg";
byte[] dates = str1.getBytes(); //字符串写入字节数组
b1.write(dates, 0, dates.length);
b1.flush();
//使用toByteArray() 或 tostring() 检索数据
dest = b1.toByteArray();
System.out.println(dest.length+"--"+new String(dest,0,b1.size()));
}
}
练习:图片的写入写出
public class ByteStream2 {
public static void main(String[] args) {
byte[] data = filetoByte("1.jpg");
System.out.println(data.length);
Bytetofile(data,"3.jpg");
}
//图片到字节数组中
public static byte[] filetoByte(String filepath) {
File f1 = new File(filepath);
byte[] dest = null;
InputStream in1 = null;
ByteArrayOutputStream b1 = null;
try {
in1 = new FileInputStream(f1);
b1 = new ByteArrayOutputStream();
byte[] temp = new byte[1024];
int len =-1; //接收长度
while((len=in1.read(temp))!=-1) {
b1.write(temp,0,len); //写出到字节数组中
}
b1.flush();
return b1.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//字节数组写出到图片
public static void Bytetofile(byte[] src,String filepath) {
File dest = new File(filepath);
InputStream in1 = null;
OutputStream o1 = null;
try {
in1 = new ByteArrayInputStream(src);
o1 = new FileOutputStream(dest);
byte[] temp = new byte[5];
int len = -1;
while((len = in1.read(temp))!=-1) {
o1.write(temp,0,len);
}
o1.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(o1!=null) {
o1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注:某类型数据转为字节数组 除了字符串 其它的都是流的对接
装饰器模式
将一个对象嵌入到另一个对象中去,实际上相当于这个对象被另一个对象包装起来
public class Decorate2 {
public static void main(String[] args) {
coffee c1 = new coffee();
System.out.println(c1.info()+c1.cost());
suger s1 = new suger(c1);
System.out.println(s1.info()+s1.cost());
milk m1 = new milk(s1);
System.out.println(m1.info()+m1.cost());
}
}
//抽象组件:需要装饰的抽象对象(接口或抽象父类)
interface drink{
double cost(); //费用
String info(); //说明
}
//具体组件:需要装饰的对象
class coffee implements drink{
private String name = " 原味咖啡 ";
@Override
public double cost() {
return 10;
}
@Override
public String info() {
return name;
}
}
//抽象装饰类:包含了对抽象组件的引用以及装饰者共有的方法
abstract class decorate implements drink{
private drink d1;
public decorate(drink d) {
this.d1 = d;
}
@Override
public double cost() {
return this.d1.cost();
}
@Override
public String info() {
return this.d1.info();
}
}
//具体装饰类:被装饰的对象
class milk extends decorate{
public milk(drink drink) {
super(drink);
}
@Override
public double cost() {
return super.cost()*2;
}
@Override
public String info() {
return super.info()+" 加入牛奶 ";
}
}
class suger extends decorate{
public suger(drink drink) {
super(drink);
}
@Override
public double cost() {
return super.cost()*2;
}
@Override
public String info() {
return super.info()+"加入糖";
}
}
装饰流部分
**BufferedInputStream**和**BufferedOutputStream**这两个流是**缓冲字节流**,通过内部缓存数组来提高操作流的效率。 当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果**使用缓冲流就能够更高效的读写信息 **
BufferedInputStream/BufferedOutputStream
public class BufferedOutStream {
public static void main(String[] args) {
File f1 = new File("a.txt");
FileOutputStream os1 = null;
BufferedOutputStream bs1 = null;
try {
os1 = new FileOutputStream(f1);
bs1 = new BufferedOutputStream(os1);
String msg = "java2缓存流";
byte[] temp = msg.getBytes();
bs1.write(temp);
bs1.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(bs1!=null) { //释放外部流 内部字节流会自动释放
bs1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedReader/BufferedWriter
BufferedReader/BufferedWriter字符缓存流增加了缓存机制,大大提高了读写文本文件的效率
readLine()方法是BufferedReader特有的方法,可以对文本文件进行更加方便的读取操作。
写入一行后使用newLine()方法换行。
public class BufferedT3 {
public static void main(String[] args) {
File f1 = new File("a.txt");
Reader r1 = null;
BufferedReader br1 = null;
try {
r1 = new FileReader(f1);
br1 = new BufferedReader(r1);
String str1 = null;
while((str1=br1.readLine())!=null) { //readLine()方法实现一次读取一行操作
System.out.println(str1);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(br1!=null) {
br1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
InputStreamReader/OutputStreamWriter 转换流
用来实现将字节流转化成字符流,并且能指定字符集(防止乱码)
//以字符流的形式操作字节流(纯文本)
public class Convert1 {
public static void main(String[] args) throws IOException {
//用buffered建立缓冲 字节流转为字符流处理
BufferedReader in1 = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter os1 = new BufferedWriter(new OutputStreamWriter(System.out));
String msg = "";
while(!msg.equals("exit")) {
msg = in1.readLine();
os1.write(msg);
os1.newLine();
os1.flush();
}
}
}
//读写网络源码
public class Prac {
public static void main(String[] args) throws IOException{
InputStream in1 = new URL("https://www.baidu.com/").openStream();//打开一个网络流
BufferedReader br1 = new BufferedReader(new InputStreamReader(in1));
FileOutputStream fo1 = new FileOutputStream("b1.html");
BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(fo1));
String msg;
while((msg=br1.readLine())!=null) {
bw1.write(msg);
bw1.newLine();
}
bw1.flush();
}
}
DataInputStream和DataOutputStream 数据流
提供了可以存取与机器无关的所有Java基础类型数据 方便后期不用强转数据类型
public class DataStream {
public static void main(String[] args) throws IOException {
//写出
ByteArrayOutputStream b1 = new ByteArrayOutputStream();
DataOutputStream d1 = new DataOutputStream(b1);
//操作数据类型加数据
d1.writeUTF("java学习");
d1.writeInt(18);
d1.writeBoolean(false);
d1.writeChar('a');
d1.flush();
byte[] datas = b1.toByteArray();
//读取
ByteArrayInputStream b2 = new ByteArrayInputStream(datas);
DataInputStream d2 = new DataInputStream(b2);
//写入的顺序与写出要一致
String msg = d2.readUTF();
int a1 = d2.readInt();
boolean b3 = d2.readBoolean();
System.out.println(msg);
}
}
ObjectInputStream/ObjectOutputStream 对象流
以 “对象” 为数据源,但是必须将传输的对象进行序列化与反序列化操作
注:不是所有的对象都可以序列化 必须要实现一个Serializable接口 起标识作用
序列化
无论是何种类型的数据,都会以二进制序列的形式在网络上传送;
处理对象流的一种机制,即可以很方便的保存内存中java对象的状态,同时也为了方便传输
序列化:将java对象转化为字节序列的过程
反序列化:将字节序列转化为java对象的过程
对象序列化的作用有如下两种:
1. 持久化: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,比如:休眠的实现。以后服务器session管理,hibernate将对象持久化实现
2. 网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递
注:
1. static属性不参与序列化。
2. 对象中的某些属性如果不想被序列化,不能使用static,而是使用transient修饰
3. 为了防止读和写的序列化ID不一致,一般指定一个固定的序列化ID
ObjectOutputStream 对Java对象进行序列化处理,处理后的数据,不是文本数据,该数据保存到文件中(给系统看的),用文本编辑器打开是乱码。
public class ObjectStream1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
ByteArrayOutputStream b1 = new ByteArrayOutputStream();
ObjectOutputStream d1 = new ObjectOutputStream(new BufferedOutputStream(b1));
//操作数据类型加数据
employee emp1 = new employee("h",12312);
d1.writeObject(emp1);
d1.flush(); //每次写出都要强制刷新 避免后面找不到数据
byte[] datas = b1.toByteArray();
//对象数据还原 反序列化
ByteArrayInputStream b2 = new ByteArrayInputStream(datas);
ObjectInputStream d2 = new ObjectInputStream(new BufferedInputStream(b2));
//写出的顺序与写入要一致
Object o2 = d2.readObject();
//添加类型转换避免类型转换错误 还原数据
if(o2 instanceof employee) { //判断是否为其实例
employee empobj =(employee)o2; //转为employee类型
System.out.println(empobj.getName()+" "+empobj.getSalary());
}
}
}
//封装employee javabean
class employee implements Serializable{
private String name; //属性前加关键字transient,这个属性就不会被序列化
private double salary;
....
}
PrintStream打印流
将OutputStream进行包装,可以方便地输出字符,更加灵活
//法一 PrintStream
public class printstream {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps= System.out;
ps.print("??");
ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")),true); //true表示自动刷新
ps.println("abc");
//ps.flush();
//重定向输出端
System.setOut(ps);
System.out.println("学习");
//重定向会控制台 通过系统定义好的FileDescriptor描述符
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true));
ps.close();
}
}
//法2 PrintWrite
public class PrintStream2 {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw=new PrintWriter(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
pw.println("pw");
pw.close();
}
}
Commons IO
Apache-commons工具包中提供了IOUtils/FileUtils,可以方便的对文件和目录进行操作
public class test1 {
public static void main(String[] args) throws IOException {
//文件大小
long len = FileUtils.sizeOf(new File("src/RwArrayList/RwArrayList2.java"));
//目录大小
//len =FileUtils.sizeOf(new File("C:/C语言/eclipse"));
System.out.println(len);
//子孙级目录
Collection<File> list3=FileUtils.listFiles(new File("C:/C语言/eclipse"),
EmptyFileFilter.NOT_EMPTY, null);
for(File temp:list3) {
System.out.println(temp.getAbsolutePath());
}
Collection<File> list2=FileUtils.listFiles(new File("C:/C语言/eclipse"),
EmptyFileFilter.NOT_EMPTY, DirectoryFileFilter.INSTANCE);
for(File temp:list2) {
System.out.println(temp.getAbsolutePath());
}
//读取文件
String msg = FileUtils.readFileToString(new File("a.txt"),"UTF-8");
System.out.println(msg);
//读到字节数组中
byte[] datas = FileUtils.readFileToByteArray(new File("a.txt"));
System.out.println(datas.length);
//逐行读取
List<String> msgs=FileUtils.readLines(new File("a.txt"),"UTF-8");
for(String temp:msgs) {
System.out.println(temp);
}
//读取迭代器
LineIterator lt = FileUtils.lineIterator(new File("a.txt"),"UTF-8");
while(lt.hasNext()) {
System.out.println(lt.nextLine());
}
//写出文件
FileUtils.write(new File("t3.txt"), "沁园春","UTF-8",true); //true作为是否追加
FileUtils.writeStringToFile(new File("t3.txt"), "沁园春","UTF-8",true);
FileUtils.writeByteArrayToFile(new File("t3.txt"), "沁园春".getBytes("UTF-8"),true);
//写出列表
List<String> list4 = new ArrayList<String>();
list4.add("H");
list4.add("M");
list4.add("N");
FileUtils.writeLines(new File("t3.txt"), list4,true);
}
}