关于Java中的IO
概念
IO流就是内存与存储设备之间传输数据的通道,管道。
流的分类
1.按方向(以JVM虚拟机为参照物)
输入流:将存储设备中的内容读入到内存中
输出流:将内存中的内容写入到存储设备中
2.按单位(字节|字符)
字节流:以字节为单位,可以操作所有类型的文件。
字符流:以字符为单位,只能操作文本类型的文件。
3.按功能
节点流:具有基本的读写功能。
过滤流:在节点流的基础上,增加新的功能。
节点流:完成数据读写 过滤流:为其他流增强功能
IO编程的步骤
1.创建节点流
2.包装过滤流
3.读写数据
4.关闭流
BIO
概念:
Java BIO (blocking I/O):同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
应用场景:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
字节流
InputStream字节输入流的父类
InputStream里的方法
int read() 读一个字节 返回值:读到的字节数据,会维护读取文件的指针文件内容的位置,返回-1表示读取结束。
int read(byte [] bs):尝试读满bs数组,返回值:读到的字节的个数 返回-1表示结束。
int read(byte [] bs ,int start ,int length):尝试读满bs数组中的一段
FileInputStream文件输入,功能属于节点流
如果文件不存在,会抛出FileNotFoundException
OutputStream字节输出流的父类
OutputStream里的方法
write(int a) :(byte)a 写一个字节
write(byte [] bs):及那个bs数组中所有的数据写出
write(byte [] bs ,int start ,int length):将 bs数组中的一段写出
FileOutputStream文件输出,功能属于节点流
如果文件已存在就会覆盖源文件,如果文件不存在创建文件,如果在文件中追加内容就在声明的构造方法中设置一个参数true
文件的拷贝
//第一种 效率极低
FileInputStream fis = new FileInputStream("输入文件路径");
FileOutputStream fos = new FileOutputStream("输出文件路径");
while(true){
int a = fis.read();
if(a==-1)break;
fos.wirte(a);
}
fis.close();
fos.close();
//第二种 效率高
FileInputStream fis = new FileInputStream("输入文件路径");
FileOutputStream fos = new FileOutputStream("输出文件路径");
byte [] bs = new byte[1024];
while(true){
int len = fis.read(bs);
if(len==-1)break;
fos.wirte(bs,0,lne);
}
fis.close();
fos.close();
过滤流
DataOutputStream : 过滤流 增强功能 可以写出八种基本类型和字符串
//输出一个Long类型的长整数
FileOutputStream fos = new FileOutputStream("文件路径");
DataOutputStream out = new DataOutputStream(fos);
long a = 1234567890987654321L;
out.writeLong(a);
out.close();
DataInputStream过滤流 增强功能 从文件中读入八种基本类型及字符串
//从文件中读入一个Long类型的长整数
FileInputStream fis = new FileInputStream("文件路径");
DataInputStream in = new DataInputStream(fis);
Long a = in.readLong();
System.out.println(a);
in.close();
缓冲流
提高IO效率 减少磁盘访问次数 数据储存正在缓冲区中,flush是将缓冲区的内容写入文件中,也可以直接close
BufferedOutputStream/BufferedInputStream
FileOutputStream fos = new FileOutputStream("文件路径");
BufferedOutputStream bos = new BufferedOutputStream(fos);
out.flush();//刷新缓冲区
out.close();//关闭资源
//第三种 使用缓冲流写文件拷贝
FileInputStream fis = new FileInputStream("文件路径");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("文件路径");
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte [] bs = new byte [1024];
while(true){
int len = bis.read(bs);
bis.flush();
if(len==-1)break;
bos.write(bs,0,len);
bos.flush();
}
bis.close();
bos.close();
PrintStream 字节缓冲过滤流
ObjectOutputStream/ObjectInputStream 过滤流 通过流传续对象:对象序列化 用transient修饰的属性为临时属性 不参与对象序列化
增强了缓冲区功能 增加了读写对象的功能 增强了读写八种基本类型和字符串的功能
readObject() 从流中读取对象
writeObject(Object obj) 向流中写入一个对象
package io流.过滤流;
import java.io.*;
/**
* Created by 拂晓 on 2019/9/22:10:53
*/
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student jj = new Student("jj", 18, 60.5);
Student aa = new Student("aa", 18, 60.5);
Student bb = new Student("bb", 18, 60.5);
//输出对象
FileOutputStream fos = new FileOutputStream("文件路径");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(jj);
out.writeObject(aa);
out.close();
//读入对象
FileInputStream fis = new FileInputStream("文件路径");
ObjectInputStream in = new ObjectInputStream(fis);
while (true) {
Student s1 = (Student) in.readObject();
Student s2 = (Student) in.readObject();
in.close();
System.out.println(s1);
System.out.println(s2);
}
}
}
class Student implements Serializable{
String name ;
int age;
double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
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;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
关于对象序列化的细节
如何在不知道文件中有多少个对象的情况下,将所有的对象读出来?
FileInputStream fis = new FileInputStream("文件路径");
ObjectInputStream in = new ObjectInputStream(fis);
try{//利用抓取异常判断文件是否读完 跳出循环
while(true){
Object o = in.readObject();
System.out.Println(o);
}
}catch(Exception e){
System.out.println("文件读完了")
}finaly{
in.close();
}
对象的反序列化
如果该类没有实现序列化接口,那么会通过调用该类的无参构造实现反序列化,重建对象,如果实现了序列化接口,那么会通过序列化的规则重建该对象,不调构造方法。
自定义序列化
需要实现Serializable的子接口Externalizable,因为自定义了序列化规则所以反序列化时就不会使用默认的反序列化规则,而是使用调用无参构造使用自定义的序列化规则
package io流.过滤流;
import java.io.*;
/**
* Created by 拂晓 on 2019/9/22:10:53
*/
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student jj = new Student("jj", 18, 60.5);
Student aa = new Student("aa", 18, 60.5);
Student bb = new Student("bb", 18, 60.5);
//输出对象
FileOutputStream fos = new FileOutputStream("文件路径");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(jj);
out.writeObject(aa);
out.close();
//读入对象
FileInputStream fis = new FileInputStream("文件路径");
ObjectInputStream in = new ObjectInputStream(fis);
while (true) {
Student s1 = (Student) in.readObject();
Student s2 = (Student) in.readObject();
in.close();
System.out.println(s1);
System.out.println(s2);
}
}
}
class Student implements Externalizable{
String name ;
int age;
double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
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;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt();
}
}
对象的浅克隆和深克隆
克隆 要调用Object.clone()方法 , 实现Cloneable接口 覆盖clone方法
浅克隆:只克隆对象,无法实现克隆 对象里的对象属性
深克隆:不仅克隆该类对象,也克隆 对象里的对象属性
ByteArrayOutputStream / ByteArrayInputStream
往内存里的字节数组输出和输入
字符流
可以帮我们解决字符的编码问题。
Reader / Write 所有字符流的父类
FileReader / FileWrite 文件字符流
BufferedReader / BufferedWriter 缓冲 过滤流
PrintWirter 缓冲
使用字符流输出和读入数据
//使用字符流输出和读入数据
FileWrite fe = new FileWrite("文件路径");
PrintWriter out = new PrintWrite(fe);
out.println("白日依山尽");
out.Println("黄河入海流");
out.println("欲穷千里目");
out.println("更上一层楼");
out.close();
FileReader fr = new FIleReader("文件路径");
BufferedReader in = new BufferedReader(fr);
while(true){
String str = in.readLine();//读一行
if(str==null)break;
System.out.println(str);
}
in.close();
字节流转字符流
OutputStreamWrite / InputStreamWrite 过滤流
桥转换类,将字节流转换为字符流
//使用将字节流转成字符流输出和读入数据
OutputStream fos = new FileOutputStream("文件路径)//节点流
Writer ow = new OutputStreamWrite(fos,"UTF-8");//桥转换流 可以指定编码方式
//PrintWriter 1.5之后可以直接指定文件名输出 ,但是不能指定编码方式了
PrintWriter out = new PrintWrite(ow);//过滤流
out.println("白日依山尽");
out.Println("黄河入海流");
out.println("欲穷千里目");
out.println("更上一层楼");
out.close();
InputStream fis = new FileInputStream("文件路径")
Reader rd = new InputStreamReader(fis,"UTF-8");
BufferedReader in = new BufferedReader(rd);
while(true){
String str = in.readLine();//读一行
if(str==null)break;
System.out.println(str);
}
in.close();
File类
IO流: 对文件中的内容进行操作。
File 类:对文件自身进行操作,代表磁盘上的一个文件或者一个目录,例如删除文件,文件重命名等。
File f = new File("文件名");//创建文件对象
f.createNewFile(); //创建文件
f.delete(); //删除文件或者目录,但是只能删除空目录
f.mkdir(); //创建目录
f.renameTo("new 文件名");//文件重命名
f.setReadOnly(); //设置文件为只读
f.getName(); //获取文件名含扩展名
f.exists(); //判断文件或目录是否存在,存在返回true,不存在返回false
f.getAbsolutePath(); //获得绝对路径
f.listFiles(); //获取当前目录下所有的文件,文件夹
f.isFile(); //判断File对象所对应的是否为文件,而不是目录
f.isDirectory(); //判断File对象所对应的是否为目录,而不是文件
如何递归删除文件
//创建要删除的文件目录对象
File f = new File("文件路径");
deleteDir(f);//调用删除方法
//声明一个方法 准备递归调用
public static void deleteDir(File dir){
File [] fs = dir.listFiles();//获取到目录下的所有子目录及文件
for(File f : fs){
if(f.isFile())f.delete();
if(f.isDirectory()){
deleteDir(f);
}
}
dir.delete();
}
如何获取一个目录下所有的.java文件
//列出dir目录中所有的.java文件
static void listJavaFiles(File dir) {
dir.listFiles(new FileFilter(){//实现文件对象的文件过滤器
public boolean accept(File f) {
if(f.isDirectory()) return true;
if(f.isFile()) {
String name = f.getName();
return name.endWith(".java");
}
return false;
}
});
for(File f : fs){
if(f.isFile())System.out.println(f.getAbsolutePath());
if(f.isDirectory()) listJavaFiles(f);
}
}
NIO
概念
Java NIO (non-blocking I/O): Java1.4后出现的NIO, 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
应用场景:
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
ByteBuffer 获取buffer对象的常用方法
//创建容量为20的Buffer缓冲区对象
ByteBuffer buffer = ByteBuffer.allocate(20);
//返回一个字节数组的ByteBuffer对象
ByteBuffer buffer = ByteBuffer.wrap(byte [] bs);
三个指针
position:当前读写位置
limit:限制
capacity:容量
flip(): 把position重置为0 调用的时机是从写模式 -> 到读模式时
clear(): 把position和limit都重置为0 调用的时机是从读模式 -> 到写模式时
使用NIO写数据
//声明一个文件输出的节点流
FileOutputStream fos = new FileOutputStream("文件路径");
//变成了NIO文件输出通道
FileChannel channel = fos.getChannel();
ByteBuffer buffer = ByteBuffer.wrap("HelloWorld".getBytes());
channel.write(buffer);
channel.close();
使用NIO读数据
FileInputStream fis = new FileInputStream(文件路径);
FileChannel channel = fis.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(10);
while(true){
int len = channel.read(buffer);
if(len==-1)break;
buffer.flip();
while(buffer.hasRemaining()){
buffer.get();//获取一个字节
System.out.println((cahr)(buffer.get()));
}
buffer.clear();
}
channel.close();
NIO的字符流
Charset cs = Charset.forName("字符集");
//将字符串按照对应的字符集做编码 从字符到字节
ByteBuffer bb = cs.encode("输出的字符串内容");
//从字节到字符 解码
CharBuffer cb = cs.decode(bb);
NIO的文件拷贝
第一种
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileInputStream fis = new FileInputStream("文件路径");
FileOutputStream fos = new FileOutputStream("文件路径");
Filechannel channel1= fis.getChannel();
FileChannel channel2 =fos.getChannel();
while(true){
int a = channel1.read(buffer);
if(a==-1)break;
buffer.flip();//从写模式切换到读模式
channel2.write(buffer);
buffer.clear();//从读模式切换到写模式
}
channel1.close();
channel2.close();
第二种
使用MappedByteBuffer 虚拟内存映射 将大块数据放入内存的技术
FileInputStream fis = new FileInputStream("文件路径");
FileOutputStream fos = new FileOutputStream("文件路径");
Filechannel channel1= fis.getChannel();
FileChannel channel2 =fos.getChannel();
MappedByteBuffer buffer = channel1.map(FileChannel.MapMode,READ_ONLY,0,channel1.size());
channel2.write(buffer);
channel1.close();
channel2.close();
文件拷贝最快的方法
FileInputStream fis = new FileInputStream("文件路径");
FileOutputStream fos = new FileOutputStream("文件路径");
Filechannel channel1= fis.getChannel();
FileChannel channel2 =fos.getChannel();
channel1.transferTo(0,channel1.size(),channel2);
channel1.close();
channel2.close();
AIO(待补充)
概念:
Java AIO(NIO.2) (Asynchronous I/O) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
应用场景:
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。