Input、output Stream流
java File文件操作
在了解输入输出流之前,先总结一下java文件相关操作
相对路径
相对某个基准⽬录或者⽂件的路径, ./ 表示当前路径; …/ 表示上级⽬录
绝对路径
存储在硬盘上的真正路径
windows 中的路径
windows中以 \ 作为 文件目录分割符
在java中,需要用到 \\ 表示, 第一个 \ 用来转义
Linux 和 Mac 中的路径
/ 表示 Linux或者Mac的路径分隔符
如果是Java代码在Linux或者Mac下写某个⽂件的话直接写 / 就可以了
File文件类
-
File类表示磁盘中存在的⽂件和⽬录,对⽂件的和⽬录的增删改查
-
File类的包名是java.io,也实现了Serializable, Comparable两⼤接⼝以便于其对象可序列化 和⽐较
-
File.separator ⽬录分隔符,在不同的系统下不⼀样, windows和 mac /Linux,当不知道系统分割符的时候,可以使用以代替手写
常见构造函数:
//路径和⽂件拼接
public File(String pathname)
//⽗路径,⼦路径
public File(String parent, String child)
//测试⽅法:获取⽂件的路径,即new File构造函数传⼊的路径
String getPath()
常用方法:
public static void main(String[] args) {
String dir = "C:\\Users\\22496\\Desktop";
String name = "a.txt";
File file1 = new File("C:\\Users\\22496\\Desktop\\b.txt");
try {
file1.createNewFile(); // 创建文件
} catch (IOException e) {
e.printStackTrace();
}
File file =new File(dir,name);
System.out.println("文件路径:"+file.getPath());
System.out.println("系统分割符:"+File.separator);
// 判断文件是否存在
System.out.println(file.exists());
// ⽬录中的⽂件和⽬录的名称所组成字符串数组
String[] filelist = file.list();
System.out.println(Arrays.toString(filelist));
//mkdir() 创建一个指定的目录,
File mkdirFile = new File(dir+File.separator+"mkdir1");
mkdirFile.mkdir();
// mkdir() 多级目录时, 一个目录都不会创建
// File mkdirFile1 = new File(dir+File.separator+"mkdir"+File.separator+"mkdir2.txt");
// mkdirFile1.mkdir();
//mkdirs() 创建多个层级的⽬录
File mkdirFile3 = new File(dir+File.separator+"mkdir"+File.separator+"mkdir2");
mkdirFile3.mkdirs();
//判断是否是目录文件,即文件夹
System.out.println(mkdirFile3.isDirectory());
//判断是否是文件
System.out.println(file.isFile());
//删除文件夹
file1.delete();
}
文件目录就是文件夹;
可以理解为:
mkdirs( ) 和mkdir( ) 都只能创建windows上的文件夹,其他文件不可,例如 .txt,.DOC文件等,当你把文件路径写成"c:\ \ Desktop\ \ a \ \ b.txt " 也只会创建 名为 b.txt 的文件夹 ;
mkdirs( ) 可以创建多级 ,多级包括一级目录,所以使用中推荐使用 mkdirs( );
delete( )
删除文件夹或者文件,删除路径末端的文件夹或者文件,若是文件夹必须为空才会删除; 例如文件路径为"c:\ \ Desktop\ \ a \ \ b.txt "
只会删除 b.txt; 若对"c:\ \ Desktop\ \ a " 路径删除,无效,a目录下还有 b.txt 文件
File 操作中,对已经删除,或者已经新建的文件夹或者文件,再次删除或者新建,程序会直接return,不操作;
createNewFile() 创建文件时,如果不写文件后缀,默认是 文件类型的文件
-
什么是IO?
-
input : 输出流 内存——>外界设备
-
output : 输入流 外界设备 ——>内存
-
输出流 都可以帮我们在当目的操作对象不存在时,创建目的对象,例如文件,数组等;当为文件时,只会创建文件,不会创建目录,new 时 ,若传入相对路径进行构造,则文件会创建在该项目 Module 目录下;
输出流 new 文件对象时,只能是文件,若为目录,则报文件找不到的异常
-
-
java 把数据分为两类
- 字符流 : 处理字符相关,如处理⽂本数据(如txt⽂件), Reader/Writer
- 字节流 : 处理字节相关,如声⾳或者图⽚等⼆进制,InputStream/OutputStream
字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,⼀次可能读多个
字节
字节流可以处理⼏乎所有⽂件,字符流只能处理字符类型的数据
字节流
字节流,一个字节一个字节的读,
inputStream 输入流
-
InputStream是输⼊字节流的⽗类,它是⼀个抽象类(⼀般⽤他的⼦类)
int read()
讲解:从输⼊流中读取单个字节,返回0到255范围内的int字节值,字节数据可直接转换为int类
型, 如果已经到达流末尾⽽没有可⽤的字节,则返回-1
int read(byte[] buf)
讲解:从输⼊流中读取⼀定数量的字节,并将其存储在缓冲区数组buf中, 返回实际读取的字节
数,如果已经到达流末尾⽽没有可⽤的字节,则返回-1
long skip(long n)
讲解:从输⼊流中跳过并丢弃 n 个字节的数据。
int available()
讲解:返回这个流中有多少个字节数,可以把buf数组⻓度定为这个
void close() throws IOException
讲解:关闭输⼊流并释放与该流关联的系统资源
-
常见子类
-
FileInputStream
具体对文件进行操作的文件字节输入流
构造函数:
//传⼊⽂件所在地址 public FileInputStream(String name) throws FileNotFoundException //传⼊⽂件对象 public FileInputStream(File file) throws FileNotFoundException
使用:
public static void main(String[] args) throws IOException { String dir = "C:\\Users\\22496\\Desktop"; String name = "a.txt"; File file =new File(dir,name); FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } test(fileInputStream); } public static void test(FileInputStream fileInputStream) throws IOException { // long l=fileInputStream.skip(2); // System.out.println(l); // //read() 会读取一个字节 ;返回读取到的内容编码;汉字>1字节 会乱码 // int read = fileInputStream.read(); // System.out.println((char) read); //每次读取b 的长度 ;若长度为0, 不读取,返回0; // read(byte [] b ) 返回读取到的字节长度 byte []b =new byte[1024]; while(fileInputStream.read(b)!=-1){ int length=fileInputStream.read(b); System.out.println(length); // -1 System.out.println(new String(b,0,length)); } //关闭资源 fileInputStream.close(); }
byte 数组在读取的时候,是在原有数组的基础上依次替换元素,若最后只剩1个字节了,我们只替换一个字节元素,那我们在 转String的时候,就会把后面的字节元素读出来,所以在 new String(b,0,length) 时,加上范围参数;
-
outputStream 输出流
- OutputStream是输出字节流的⽗类,它是⼀个抽象类
void write(int b)
讲解:将指定的字节写⼊输出流
void write(byte[] b)throws IOException
讲解:将b.length个字节的byte数组写⼊当前输出流
void flush() throws IOException
讲解:write是写到缓冲区中,可以认为是内存中,当缓冲区满时系统会⾃动将缓冲区的内容写⼊
⽂件,但是⼀般还有⼀部分有可能会留在内存这个缓冲区中, 所以需要调⽤flush空缓冲区数据。
void close() throws IOException
讲解:关闭输⼊流并释放与该流关联的系统资源
- 常见子类
FileOutputStream
⽂件字节输出流,对⽂件数据以字节 的形式进⾏输出的操作
构造函数:
//传⼊输出的⽂件地址 public FileOutputStream(String name) //传⼊⽬标输出的⽂件对象 public FileOutputStream(File file) //传⼊⽬标输出的⽂件对象, 是否可以追加内容 public FileOutputStream(File file, boolean append)
使用:
public static void main(String[] args) throws IOException {
String dir = "C:\\Users\\22496\\Desktop";
String name = "a.txt";
String target = "b.txt";
File file = new File(dir, name);
InputStream inputStream = new FileInputStream(file);
//会⾃动创建⽂件,但是不会创建多级⽬录下的⽂件
OutputStream outputStream = new
FileOutputStream("C:\\Users\\22496\\Desktop\\" + target);
// true,不覆盖⽂件,追加数据 若要写入换行,加 \n
//OutputStream outputStream = new
// FileOutputStream("C:\\Users\\22496\\Desktop\\"+target,true);
//testOutBuf(inputStream ,outputStream);
testOut(inputStream, outputStream);
}
//单个字节读取,中⽂会有问题
public static void testOut(InputStream inputStream, OutputStream
outputStream) throws IOException {
int value = 0;
while (value != -1) {
value = inputStream.read();
outputStream.write(value);
}
//最后记得关闭流
inputStream.close();
outputStream.close();
}
public static void testOutBuf(InputStream inputStream, OutputStream
outputStream) throws IOException {
byte[] buf = new byte[1024];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
//最后记得关闭流
inputStream.close();
outputStream.close();
}
Buffer 输入输出流
-
什么是缓冲 Buffffer 它是内存空间的⼀部分,在内存空间中预留了⼀定的存储空间,这些存储空间
⽤来缓冲输⼊或输出的数据,这部分空间就叫做缓冲区,缓冲区是具有⼀定⼤⼩的,
-
缓冲 cache ,解决磁盘与内存速度不匹配的问题,加入缓冲可大大提高读写效率
BufffferedInputStream 和 BufffferedOutputStream
BufffferedInputStream 缓冲字节输⼊流
默认缓冲区⼤⼩是8k = 8*1024 字节
构造函数:
//对输⼊流进⾏包装,⾥⾯默认的缓冲区是8k
public BufferedInputStream(InputStream in);
//对输⼊流进⾏包装,指定创建具有指定缓冲区⼤⼩的
public BufferedInputStream(InputStream in,int size);
常用方法:
/从输⼊流中读取⼀个字节
public int read();
//从字节输⼊流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
public int read(byte[] buf,int off,int len);
//关闭释放资源,关闭的时候这个流即可,InputStream会在⾥⾯被关闭
void close();
BufffferedOutputStream 缓冲字节输出流
构造函数:
//对输出流进⾏包装,⾥⾯默认的缓冲区是8k
public BufferedOutputStream(OutputStream out);
//对输出流进⾏包装,指定创建具有指定缓冲区⼤⼩的
public BufferedOutputStream(OutputStream out,int size)
常用方法:
//向输出流中输出⼀个字节
public void write(int b);
//将指定 byte 数组中从偏移量 off 开始的 len 个字节写⼊缓冲的输出流。
public void write(byte[] buf,int off,int len);
//刷新此缓冲的输出流,强制使所有缓冲的输出字节被写出到底层输出流中。
public void flush();
//关闭释放资源,关闭的时候这个流即可,OutputStream会在⾥⾯被关闭, JDK7新特性try(在这
⾥声明的会⾃动关闭){}
void close();
需求 把 a.txt 文件里的内容拷贝到 b.txt
public static void main(String[] args) throws Exception {
FileInputStream fi =new FileInputStream("C:\\Users\\22496\\Desktop\\a.txt");
BufferedInputStream bi=new BufferedInputStream(fi);
FileOutputStream fo=new FileOutputStream("C:\\Users\\22496\\Desktop\\b.txt");
BufferedOutputStream bo=new BufferedOutputStream(fo);
int size;
byte [] b =new byte[1024];
while((size=bi.read(b))!=-1){
bo.write(b,0,size);
}
//刷新缓存区,确保全部写入磁盘;不用显示写出来;close()包含flush()
// bo.flush();
//关闭的时候只需要关闭最外层的流就⾏了
// 关闭顺序 后开先关, 如果A依赖B,先关闭B
bo.close();
bi.close();
}
BufffferedOutputStream在不调⽤close()的情况下,缓冲区不满,⼜需要把缓冲区的内容写⼊到⽂件或通过⽹络发送到别的机器时,才需要调⽤flush
在关闭流之前 ,不能删除流所依赖得文件, 正确的顺序是,先close(),再delete(); 若顺序相反,不删除,也不关闭流,也不报错;
File file = new File("C:\\Users\\22496\\Desktop\\c.txt"); InputStream inputStream = new FileInputStream(file); inputStream.close(); file.delete(); // 删除失败 // file.delete(); // inputStream.close(); // System.out.println(file.getName());
BuffferedReader 和 BufferedWriter
BuffferedReader 有一个读一行的方法 (这里的一行指的是遇到换行符结束); readLine( );
BufferedWriter 使用与Writer 类似,追加写入时,参数true,写在 : Writer w = new FileWriter(“C:\Users\22496\Desktop\b.txt”,true); BufferedOutputStream同理
Reader r = new FileReader("C:\\Users\\22496\\Desktop\\b.txt");
BufferedReader br = new BufferedReader(r);
String str = null;
// 读一行,读到文件末尾返回null;
while((str=br.readLine())!=null){
System.out.println(str);
}
字符流
字符流,一个字符一个字符的读,
字符流操作跟字节流差不多,只是每次读取的内容不一样,字符流 int read(char cbuf[]) char 数组,字节流是byte数组,输出字符流中的 write() 中可以直接传String 类型,char [] 类型 ,
Reader
Reader是输⼊字符流的⽗类,它是⼀个抽象类, 部分库不推荐使⽤Reader/Writer,所以简单了解
即可
int read()
讲解:⼀个字符⼀个字符的读,只能⽤来操作⽂本(不能写图⽚ ⾳频 视频)
int read(char cbuf[])
讲解:从输⼊字符流中读取⼀定数量的字符,并将其存储在缓冲区数组cbuf中, 返回实际读取的字符
数,如果已经到达流末尾⽽没有可⽤的字节,则返回-1
void close() throws IOException
讲解:关闭输⼊流并释放与该流关联的系统资源
常见子类
FileReader ⽤来读取字符⽂件的实现类
构造
public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}
使用:
public static void main(String[] args) throws Exception {
String url = "C:\\Users\\22496\\Desktop\\b.txt";
File file = new File(url);
Reader fileReader = new FileReader(file);
char[] b = new char[4];
//读取单个字符
// int ch;
// while((ch=fileReader.read())!=-1){
// System.out.print( (char) ch);
// }
//读出多个字符
int ch = -1;
while((ch=fileReader.read(b))!=-1){
System.out.print(new String(b,0,ch));
}
}
Writer
直接看使用吧:
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("C:\\Users\\22496\\Desktop\\b.txt",true);
// write() 也可传 char数组 ,String 有 getChar() 方法直接返回char数组
writer.write("哈哈!");
// 换行 写入
writer.write("\n嘻嘻!");
if(writer!=null)
writer.close();
}
转换流
InputStreamReader 和OutputStreamWriter,这两个类是只能将字节流转换为字符流的类,不能反向转换;InputStreamReader 可以将一个InputStream转换为Reader,OutputStreamWriter可以将一个OutputStream转换为Writer。
public static void test1(String path) throws IOException {
//读取字节流
InputStream in = new FileInputStream(path);
//将字节流向字符流的转换。
InputStreamReader isr = new InputStreamReader(in,"GBK");
//创建字符流缓冲区
BufferedReader reader = new BufferedReader(isr);//缓冲
//String line;
//while((line = reader.readLine())!=null){
// System.out.println(line);
//}
//isr.close();
int size;
char[] cbuf = new char[1024];
while ((size = reader.read(cbuf, 0, cbuf.length)) != -1) {
System.out.println(new String(cbuf, 0, size));
}
reader.close();
}
打印流
主要包括字节打印流(PrintStream)和字符打印流(PrintWriter)。打印流提供了非常方便的打印功能,可以打印任何的数据类型。如:小数、整数、字符串等。
PrintStream和PrintWriter都属于输出流,分别针对输出字节和字符。
PrintStream和PrintWriter提供了重载的print()、println()方法用于多种数据类型的输出。
PrintStream和PrintWriter不会抛出异常,用户通过检测错误状态获取错误信息。
PrintStream和PrintWriter有自动flush 功能。
PrintStream 构造方法:
- PrintStream(OutputStream out)
- PrintStream(OutputStream out, boolean auotflush)
- PrintStream(OutputStream out, boolean auotflush, String encoding)
PrintWriter 构造方法
- PrintWriter(OutputStream out)
- PrintWriter(OutputStream out, boolean autoflush)
- PrintWriter(Writer out)
- PrintWriter(Writerout, boolean autoflush)
其中autoflush控制在Java中遇到换行符(\n)时是否自动清空缓冲区,encoding是指定编码方式。
关于 autoflush 自动清空缓冲区
PrintWriter即使遇到换行符(\n)也不会自动清空缓冲区,只在设置了autoflush模式下使用了println方法后才自动清空缓冲区。PrintWriter相对PrintStream最有利的一个地方就是println方法的行为,在Windows的文本换行是"\r\n",而Linux下的文本换行是"\n",如果希望程序能够生成平台相关的文本换行,而不是在各种平台下都用"\n"作为文本换行,那么就应该使用PrintWriter的println方法时,PrintWriter的println方法能根据不同的操作系统而生成相应的换行符。
使用:
// 控制台打印
PrintWriter out = new PrintWriter(System.out);
// 向屏幕上输出
out. println("Hello World!");
out.close(); //如果此句不写,则没有内容,跟PrintStream有区别
//在文件中打印
PrintWriter out = null;
File f = new File("c:\\temp.txt");
try {
// 由FileWriter实例化,则向文件中输出
// 也可以直接用文件实例化
out = new PrintWriter(new FileWriter(f));
} catch (IOException e) {
e.printStackTrace();
}
out.print("Hello World!" + "\r\n");
out.close();
对象流
对象序列化
Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。通过将对象序列化,可以方便的实现对象的传输及保存。
Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等,而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,实现进程间的对象传送,就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。
在Java中提供了ObjectlnputStream与ObjectOutputStream这两个类用于序列化对象的操作。
ObjectlnputStream 提供反序列化, ObjectOutputStream 提供序列化
在实现对象序列化之前,要求对象实现java.io.Serializable接口,但Serializable接口中没有定义任何方法,仅仅被用作一种标记,以被编译器作特殊处理。
使用:
// 以下方法为序列化对象方法,将对象保存在文件之中
public static void serialize(File f) throws Exception{
OutputStream outputFile = new FileOutputStream(f);
ObjectOutputStream cout = new ObjectOutputStream(outputFile)
cout.writeObject(new Person("张三",25));
cout.close();
}
// 以下方法为反序列化对象方法,从文件中读取已经保存的对象
public static void deserialize(File f) throws Exception {
InputStream inputFile = new FileInputStream(f);
ObjectInputStream cin = new ObjectInputStream(inputFile);
Person p = (Person) cin.readObject();
System.out.println(p);
}
public static void main(String args[]) {
File f = new File("SerializedPerson");
serialize(f);
deserialize(f);
}
serialVersionUID 常量
在对象进行序列化或反序列化操作的时候,要考虑 JDK 版本的问题。如果序列化的 JDK 版本和反序列化的 JDK 版本不统一,则可能造成异常。因此在序列化操作中引入了一个serialVersionUID 的常量来验证版本的一致性。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体(类)的serialVersionUID 进行比较。如果相同就认为是一致的,可以进行反序列化,否则就会出现反序列化版本不一致的异常
transient关键字
如果不希望类中的属性被序列化,可以在声明属性之前加上transient关键字。如下所示,下面的代码修改自前面所用到的Person.java程序,在声明属性时,前面多加了一个transient关键字
private transient String name;
private transient int age;
注意:
序列化细节:
1) 被序列化的类的内部的所有属性,必须是可序列化的
2) static,transient修饰的属性,不可以被序列化