流概念
概念
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即 数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作
分类
按处理数据类型的不同,分为字节流和字符流
按数据流向的不同,分为输入流和输出流(入和出是相对与内存说的)
按功能不同,分为节点流和处理流
节点流;直接操作数据源
处理流;对其他流进行处理
抽象类定义
InputStream
InputStream的继承关系 ;蓝色为节点流,黑色为处理流
OutputStream
OutputStream的继承关系 为 蓝色为节点流,黑色为处理流
Reader
Reader的继承关系;蓝色为节点流,黑色为处理流
Writer
writer的继承关系;蓝色为节点流,黑色为处理流
文件流
FileInputStream
package day_7_20;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
*
* I; Input输出,指向内存中导入数据
* o;Output 输出,指从内存中写出数据
*
* 流是对数据传输的总称,也就是两台设备之家进行数据传输成为流,根据流传输的特性,抽象为各个类
* FileInoutStream;字节输入流
*
*
* @author 啊超
* @Date 2021年7月20日
*/
public class Io_1 {
public static void main(String[] args) throws IOException {
//读取文件首先需要找到文件
//绝对定位;指在计算机中那个盘里存储的地址
//相对地址;就是相对于这个文件的目录下的位置 ./../
FileInputStream fis=new FileInputStream("D://a.java");
//读取的,默认一次读取一个字节,并且返回值是下一个字节的值,到达文件末尾,返回-1
//循环结构
int temp=-1;
while((temp=fis.read())!=-1){
System.out.print((char)temp);
}
}
}
package day_7_20;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 异常处理
*
*
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_2 {
public static void main(String[] args) {
FileInputStream fis=null;
try{
fis=new FileInputStream("D://a.java");
//循环读取
int temp=-1;
while((temp=fis.read())!=-1){
System.out.print((char)temp);
}
}catch(IOException e){
e.printStackTrace();
}finally{ //需要在这里强制关闭
try{
if(fis!=null){ //如果运行了就关闭
fis.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
//1.7自动关闭
try(FileInputStream fis1=new FileInputStream ("D://a.java");){
//循环读取
int temp=-1;
while((temp=fis1.read())!=-1){
System.out.print((char)temp);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
package day_7_20;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
*
* read; 方法重载,可以传入一个字节数组,把数据放到数组中;
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_3FileInputStream {
public static void main(String[] args) {
try(FileInputStream fis1=new FileInputStream("D://a.java")){
循环读取
byte [] bytes =new byte[100];
int temp=-1;
while((temp=fis1.read(bytes))!=-1){
System.out.println(new String(bytes,0,temp));
}
}catch(Exception e){
e.printStackTrace();
}
}
}
FileRader
package day_7_20;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
*
*
* FileReaderyi一次读取一个字符,可以解决乱码问题,一般纯文本文件使用FileReader
* 向压缩包,图片,视频等,还是会使用字节流,字符流会出现问题
*
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_4FileReader {
public static void main(String[] args) throws FileNotFoundException, IOException {
try(FileReader fr=new FileReader("D://a.java");
){
int temp=-1;
//read;返回下一个字符,到达文件末尾返回-1;
while((temp=fr.read())!=-1){
System.out.print((char)temp);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
package day_7_20;
import java.io.FileReader;
/**
*
* FileReader; 一次读取一个字符,可以解决乱码问题,一般纯文本使用FileReader
* 压缩包,图片、视频,需要使用字节流,字符流会出问题
*
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_5FileReader {
public static void main(String[] args) {
try(FileReader fr=new FileReader("D://a.java");
){
char[] chars=new char[5];
int temp=-1;
while((temp=fr.read(chars))!=-1){
System.out.print(new String(chars,0,temp));
}
}catch(Exception e){
e.printStackTrace();
}
}
}
FileOutputStream
package day_7_20;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*
* FileOutoutStream;字节输出流,需要指定输出的文件路径,如果找不到该文件,会自动创建
*
* write();
* fluse();刷新
* 构造方法;FileOutputStream(String) ;传入文件地址,默认覆盖写入
* 覆盖写入就是先把文件中内容清空,在写入
* FileOutputStream(String,boolean); boolean类型为true就是追加写入,false就是覆盖写入
*
* @author 啊超
* @Date 2021年7月20日
*/
public class File_6OutpurStream {
public static void main(String[] args) throws IOException {
//覆盖写入,创建对象的时候会自动把源文件中内容清空
FileOutputStream fos=new FileOutputStream("F://b.java");
//FileOutputStream fos=new FileOutputStream("F://b.java",true);
fos.write(97);
//想要写出字符串,需要转换成字节数组
String string="哈哈哈";
byte [] bytes=string.getBytes();
fos.write(bytes);
//刷缓存
fos.flush();
//关闭资源
fos.close();
}
}
FileWrite
package day_7_20;
import java.io.FileWriter;
/**
*
* 字符输出流
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_7FileWrite {
public static void main(String[] args) {
//创建对象一运行就会清空文件!!!!,只要是输出流,创建对象就会清空,除非是追加
try(FileWriter fw=new FileWriter("F://b.java");){
fw.write("钱钱钱");
char [] chars={'a','b','c','d'};
fw.write(chars);
fw.flush();
}catch(Exception e){
e.printStackTrace();
}
}
}
缓冲流
特点
主要是为了提高效率而存在的,减少物理读取次数
提供read Line()、newLine()这样的便捷的方法(针对缓冲字符流)
在读取和写入时,会有缓存部分,调用flush为刷新缓存,将内存数据写入到磁盘
BufferedRead
package day_7_20;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class IO_8BufferedReader {
public static void main(String[] args) throws IOException, IOException {
try(FileReader fr=new FileReader("F://b.java");
BufferedReader br=new BufferedReader(fr);){
String temp=null;
//readerLine;读取下一行的数据,返回值就是读到的数据,如果达到文件默认,返回null
while((temp=br.readLine())!=null){
System.out.println(temp);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
BufferedWriter
package day_7_20;
import java.io.BufferedWriter;
import java.io.FileWriter;
/**
*
* 输出流缓冲流
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_9BufferedWriter {
public static void main(String[] args) {
try(FileWriter fw=new FileWriter("./src/day_7_20/call.txt");
BufferedWriter bw= new BufferedWriter(fw);
){bw.write("你好吗");
//换行
bw.newLine();
bw.write("脑壳疼");
bw.flush();
}catch(Exception e){
e.printStackTrace();
}
}
}
转换流
特点
转换流是指将字节流向字符流的转换,主要有InputSteamReader和OutputStreamWriter
InputStreamReader主要是将字节流输入转换成字符流输入流
OutputStreamWriter主要是将字节流输出转换成字符输出流
InputStreamReader
package day_7_20;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 字节流转换为 字符流
*
*
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_10InputStreamReader {
public static void main(String[] args) throws IOException, IOException {
try(
//得到字节流
FileInputStream fis=file("D://a.java"); //接收 //字节流可能出现乱码,需要转换成字符流
InputStreamReader isr=new InputStreamReader(fis); //转换为字符流
//使用缓冲流
BufferedReader br=new BufferedReader(isr);
){
String temp=null; //字符流缓冲流需要读一行,需要temp=null
while((temp=br.readLine())!=null){
System.out.println(temp);
}
}
catch(Exception e){
e.printStackTrace();
}
}
//传递一个文件路径,返回该文件的输入流对象
public static FileInputStream file(String filepath) throws FileNotFoundException{
return new FileInputStream(filepath);
}
}
OutputStreamWriter
参考InputStreamReader
打印流
特点
打印流是输出最方便的类
包含字节打印流PrintStream,字符打印流 PrintWriter
PrintStream是OutputStream的子类,把一个输出流的实例传递到打印流之后,可以更加方便地输出内容,相当于把输出流重新包装一下。
PrintStream类的print()方法被重载很多次print(int)、print(boolean b)、print(charc)
标准输入/输出
Java的标准输入/输出分别通过System.in和System.out来代表,在默认的情况下分别代表键盘和显示器,当程序通过System.in来获得输入时,实际上是通过键盘获得输入。当程序通过System.out执行输出时,程序总是输出到屏幕。
在System类中提供了三个重定向标准输入/输出的方法
- static void setErr(PrintStream err) 重定向“标准”错误输出流
- static void setIn(InputStream in) 重定向“标准”输入流
- static void setOut(PrintStream out)重定向“标准”输出流
package day_7_20;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class IO_11printStream {
public static void main(String[] args) throws FileNotFoundException {
System.out.println(1);
PrintStream ps=System.out;
ps.println("xxx");
//标准输出,默认打印到控制台
//设置默认打印位置
//创建字节输出流
FileOutputStream fos= new FileOutputStream("D://qqq.txt");
//封装为打印流
PrintStream ps1=new PrintStream(fos);
//设置到System中
System.setOut(ps1); //setOut 接收的是打印流,
System.out.println("==========");
System.out.println("m1开始执行");
m1();
System.out.println("m1执行结束");
}
public static void m1(){
System.out.println("m1 method execute");
}
}
数据流
为了方便操作java语言的基本数据类型和String的数据,可以使用数据流
数据流有两个类;(用于读取和写出基本数据类型、String类的数据)
DataInputStream和DataOutputStream
分别套接在InputStream和OutputStream子类的流上
DataInputStream中的方法
boolean readBoolean()
char readChar()
double readDouble()
long readLong()
String readUTF()
byte readByte()
float readFloat()
short readShort()
int readInt()
void readFully(byte[] b)
package day_7_20;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IO_12DataOutputStream {
public static void main(String[] args) throws IOException {
//为了解决不同语言的编码而产生的乱码
//使用了两个轻量化方法
DataOutputStream dos=new DataOutputStream(new FileOutputStream("./src/day_7_20/data"));
byte b=10;
short s=12;
char c='a';
int i=11;
dos.writeByte(b);
dos.writeShort(s);
dos.writeChar(c);
dos.writeInt(i);
dos.writeUTF("脑壳疼");
dos.flush();
dos.close();
}
}
package day_7_20;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class IO_13DataInputStream {
public static void main(String[] args) throws IOException {
DataInputStream dis=new DataInputStream(new FileInputStream("./src/day_7_20/data"));
//读写顺序与12一样
System.out.println(dis.readByte());
System.out.println(dis.readShort());
System.out.println(dis.readChar());
System.out.println(dis.readInt());
System.out.println(dis.readUTF());
}
}
File类
File类特点
- java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关
- File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。
如果需要访问文件内容本身,则需要使用输入/输出流。
- 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对 象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
- File对象可以作为参数传递给流的构造器
File类构造方法
- public File(String pathname)以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
- 绝对路径:是一个固定的路径,从盘符开始
- 相对路径:是相对于某个位置开始
- public File(String parent,String child)以parent为父路径,child为子路径创建File对象。
- public File(File parent,String child)根据一个父File对象和子文件路径创建File对象
File使用
- 路径中的每级目录之间用一个路径分隔符隔开。
- 路径分隔符和系统有关:
- windows和DOS系统默认使用“\”来表示
- UNIX和URL使用“/”来表示
- Java程序支持跨平台运行,因此路径分隔符要慎用。
- 为了解决这个隐患,File类提供了一个常量:public static final String separator。根据操作系统,动态的提供分隔符
File类常用方法
- public String getAbsolutePath():获取绝对路径
- public String getPath() :获取路径
- public String getName() :获取名称
- public String getParent():获取上层文件目录路径。若无,返回null
- public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
- public long lastModified() :获取最后一次的修改时间,毫秒值
- public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
- public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
重命名功能:
- public boolean renameTo(File dest):把文件重命名为指定的文件路径
判断功能:
- public boolean isDirectory():判断是否是文件目录
- public boolean isFile() :判断是否是文件
- public boolean exists() :判断是否存在
- public boolean canRead() :判断是否可读
- public boolean canWrite() :判断是否可写
- public boolean isHidden() :判断是否隐藏
创建删除功能:
- public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
- public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。
- public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目 路径下。
- public boolean delete():删除文件或者文件夹
删除注意事项:
Java中的删除不走回收站。
要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录
package day_7_20;
import java.io.File;
import java.io.IOException;
/**
*
* 文件操作
*
* @author 啊超
* @Date 2021年7月20日
*/
public class File_1 {
public static void main(String[] args) throws IOException {
//创建文件对象
File f1=new File("D://work//works");
//活得全路径
System.out.println(f1.getAbsolutePath());
//判断是否存在
System.out.println(f1.exists());
//判断是否是文件
System.out.println(f1.isFile());
//判断是否是目录
System.out.println(f1.isDirectory());
//上级路径
System.out.println(f1.getParent());
//上级对象
System.out.println(f1.getParentFile());
//获取所有子文件对象
File []subFiles=f1.listFiles();
for(File file:subFiles){
System.out.println(file.getAbsolutePath());
}
//删除就找不回来了
File f2=new File("D:/.a.java");
//判断是否存在
System.out.println(f2.exists());
//新建文件,不能新建文件夹,如果有就不创建,创建返回true,不创建返回false
System.out.println(f2.createNewFile());
//删除成功,返回true,否则返回false
System.out.println(f2.delete());
//文件夹创建和删除
File f3 = new File("D:/com/zrz/");
System.out.println(f3.exists());
// 只能创建zrz 不能创建com,
// f3.mkdir();
// 递归创建整个目录
f3.mkdirs();
// 删除,只会删除zrz文件夹,因为f3是zrz的文件夹对象,所以不会删除com
f3.delete();
}
}
package day_7_20;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
*
* 递归复制
*
* 1 单个文件复制
*
* 2 递归获取所有文件
*
* 3 整合
*
* @author 啊超
* @Date 2021年7月20日
*/
public class File_2 {
public static void main(String[] args) {
File f = new File("D:\\1work\\work");
checkMunu(f);
System.out.println("复制完成");
}
public static void checkMunu(File f) {
if (f.isFile()) {
// 原文件路径
String filePath = f.getAbsolutePath();
// 目标路径,把D盘的这个文件,复制到E盘
// 注意 !!!! 原目录 和 目标目录 不能一致,否则 如果是覆盖写入 所有数据就清空了
// 如果是追加写入,导致全是重复数据,因为写一行,读一行,导致永远读不完
String newFilePath = "E"+filePath.substring(1);
// 判断目标路径中,是否有对应的文件夹
File file = new File(newFilePath).getParentFile();
// 不存在就创建
if (!file.exists()) {
file.mkdirs();
}
// 复制
try (FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(newFilePath);
BufferedOutputStream bos = new BufferedOutputStream(fos);
) {
byte[] bytes = new byte[1024000];
int temp = -1;
while( (temp = bis.read(bytes)) != -1 ){
bos.write(bytes, 0, temp);
}
bos.flush();
} catch (Exception e) {
e.printStackTrace();
}
return;
}
// 能到这里 说明是 目录
// 获取所有子文件
File[] fs = f.listFiles();
for (File file : fs) {
checkMunu(file);
}
}
}
对象流
定义
- ObjectInputStream和OjbectOutputSteam
- 用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
- 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
- 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修
饰的成员变量
对象的序列化
- 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从 而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传 输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象
- 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据, 使其在保存和传输时可被还原
序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返 回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
- 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可 序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。 否则,会抛出NotSerializableException异常
- Serializable
- Externalizable
- 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
- private static final long serialVersionUID;
- serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象 进行版本控制,有关各版本反序列化时是否兼容。
- 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自 动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议, 显式声明。
- 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验 证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同 就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异 常。(InvalidCastException)
使用对象流序列化对象
- 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
- 创建一个 ObjectOutputStream
- 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
- 注意写出一次,操作flush()一次
- 反序列化
- 创建一个 ObjectInputStream
- 调用 readObject() 方法读取流中的对象
- 强调:如果某个类的属性不是基本数据类型或 String 类型,而是另一个 引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化
package day_7_20;
import java.io.Serializable;
/**
* 要被序列化的类,必须实现 Serializable接口
*
* Serializable接口 没有任何方法和功能,就是一个标识
*
* serialVersionUID 是控制版本的
*
* 如果不加,每次更改都会重新 生成一个UID,如果序列化对象中的UID和类中的UID不同,则不兼容
*
* 所以我们一般手动指定一个UID.比如现在更改内容了,之前的序列化对象不想让他兼容了,可以把UID更改
*
* 如果我们更改了内容.之前的序列化对象还需要兼容,则不需要更改UID的值
*/
/**
* transient : 防止该变量进行序列化
*
* 如果使用transient修饰之后,该变量的值,不会被序列化
*
* 可以把不必要的数据 使用 transient 进行修饰,这样可以提升序列化和反序列化的效率
* @author 啊超
* @Date 2021年7月20日
*/
public class User implements Serializable{
private static final long serialVersionUID = 1L;
String name;
transient int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void m1(){
System.out.println("m1------");
}
public void m2(){
System.out.println("m2------");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
package day_7_20;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
*
* 序列化;对象-->硬盘中进行持久化存储
* 反序列化; 硬盘中对象-->堆内存对象
* 创建对象
* 1 new 最常用
* 2反射机制
* 3反序列化
* 4不用管clone,被序列化代替
*应用场景 ; 序列化就是将数据转换为二进制流,然后能够进行持久话存储和进行网络传输
* 数据-->序列化-->加密处理-->网络传输--->解密处理--->反序列化--->数据
*
* @author 啊超
* @Date 2021年7月20日
*/
public class IO_14ObjectOutputStream {
public static void main(String[] args) throws FileNotFoundException, IOException {
User user = new User("张三", 18);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./src/com/obj"));
oos.writeObject(user);
oos.flush();
oos.close();
}
}