在程序中,变量,数组和对象中的数据都是暂时的,当程序结束后它里面的数据也就消失了。但是实际中我们需要将数据保存下来,这时就需要我们的I/O来保存数据,将数据写入文件中。
流
流是一组有序的数据序列,它分为输入流和输出流。
输入输出流就像一辆卡车,他把数据放入车里面,然后一块打包送到目的地。输入流输出流的目的地除了可以是磁盘文件,也可以是键盘,鼠标,显示器或内存
输入流指从目的地读取数据,运送到程序里,目的地可以是文件,网络,压缩包或其他数据源。
输出流指将数据送到目的地。目的地可以是文件,网络,压缩包和其他数据输出目标
所有输入流类都是抽象类InPutStream(字节输入流)或抽象类Reader(字符输入流)的子类
所有输出流类都是抽象类OutPutStream(字节输出流)或抽象类Writer(字符输出流)的子类
输入流
InputStream类是字节输入流的抽象类,是所有字节输入流的父类。
InPutStream类所有方法遇到错误时都会引发IOException
方法 | 说明 |
---|---|
read() | 从输入流中读取数据的下一个字节。返回0~255范围内的int字节值。如果已经到达流末尾而没有可用的字节,则返回值-1 |
read(byte[] b) | 从输入流中读入一定长度的字节,并以整数的形式返回字节数 |
mark(int readlimit) | 在输入流的当前位置放置一个标记,readlimit参数告知此输入流在标记位置失效之前允许读取的字节数 |
reset()方法 | 将输入指针返回到当前所做的标记处 |
skip(long n) | 跳过输入流上的n字节并返回实际跳过的字节数 |
markSupported() | 如果当前流支持mark()/reset()操作就返回true |
close() | 关闭此输入流并释放与该流关联的所有系统资源 |
并不是所有的InputStream类的子类都支持InputStream中的定义的所有方法,如skip(),mark(),reset()方法只对某些子类有用
java中的字符是Unicode编码,是双字节的。InputStream是用来处理字节的。并不适合处理字符文本。java为字符文本的输入专门提供了一套单独的类Reader,但Reader类并不是InputStream类的替代,它只是在处理字符串时简化了编程。
Reade类是字符输入流的抽象类,所有字符输入流的实现都是它的子类。
输出流
OutPutStream类的所有方法均为void
方法 | 说明 |
---|---|
write(int b) | 将指定的字节写入此输出流 |
write(byte[] b) | 将b个字节从指定的byte数组写入此输出流 |
write(byte[] b,int off,int len) | 将指定byte数组中偏移量off开始的len个字节写入此输出流 |
flush() | 彻底完成输出并清空缓存区 |
close() | 关闭输出流 |
Writer类是字符输出流的抽象类
文件类
File类是java.io包中唯一代表磁盘文件本身的对象。
可以调用File类的方法实现创建,删除,重命名文件等操作
可以获取文件本身的一些信息,如文件所在的目录,文件的长度,文件读写权限等。
(1)文件的创建与删除
1.File(String pathname)
通过给定路径名的字符串(包含文件名)创建一个新的File实例
默认是项目的路径下
获得项目路径:System.getProperty(“user.dir”);
new File(String pathname)
2.File(String parent,String child)
通过给定的父路径和子路径字符串(包含文件名)创建一个新的File对象
new File(String parent,String child);
File f=new File("D:/doc",Demo.txt);
3.File(File f,String child)
根据parent抽象路径名和child路径名字符串创建一个新的File实例
new File(File f,String child)
File f=new File("D:/doc/",Demo.txt);
比如File f=new File(“Demo.txt”);
如果当前路径不存在该文件,则会自动帮你创建给文件。
删除文件delete()方法
import java.io.File;
import java.io.IOException;
import java.util.*;
public class Test {
public static void main(String[] args) {
File f=new File("Demo.txt");
if(f.exists()){
f.delete();
System.out.println("文件已删除");
}else{
try {
f.createNewFile();
System.out.println("文件已创建");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
文件已创建
创建了一个Demo.txt
(2)常用方法
方法 | 返回值 | 说明 |
---|---|---|
getName() | String | 获取文件名称 |
canRead() | boolean | 判读文件是否可读 |
canWrite() | boolean | 判断文件是否可被写入 |
exits() | boolean | 判断文件是否存在 |
length() | long | 获取文件长度(以字节为单位 |
getAbsolutePath() | String | 获取文件的绝对路径 |
getParents() | String | 获取文件的父路径 |
isFile() | boolean | 判断文件是否存在 |
isDirectory() | boolean | 判断是否为一个目录 |
isHidden() | boolean | 判断文件是否为隐藏文件 |
lastModified() | long | 获取文件最后修改时间 |
mkdirs() | boolean | 创建目录 目录不存在也会创建 |
mkdir() | boolean | 创建目录,目录结构中有一个不存在,则不会创建整个目录树 |
打印文件结构
public class MyFile {
private void outFile(int undent,File file){
for(int i=0;i<undent;i++)
System.out.print("-");
System.out.println(file.getName());
if(file.isDirectory()) { //判断当前路劲名表示的文件名是否为一个目录
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
outFile(undent+1,files[i]);
}
}
}
public static void main(String[] args) {
File file=new File("E:/hello");
MyFile m=new MyFile();
m.outFile(1,file);
// System.out.println(file.isDirectory());
}
}
输出结果:
-hello
--文件夹1
---hello1.txt
---hello2.txt
--文件夹2
---my.txt
文件输入/输出流
(1)FileInputStream与FileOutputStream
FileInputStream与FileOutputStream用来操作磁盘文件。
FileInputStream与FileOutputStream提供基本的文件写入能力
构造方法
FileInputStream(String name) //通过文件名创建
FileInputStream(File file) //通过FIle对象创建
创建一个FileInputStream对象时,可以指定不存在的文件名。但此文件不能是一个已被其他程序打开的文件。
FileOutputStream (String name)
FileOutputStream(File file)
FileOutputStream (String name,boolean append) //append :是否是往文件里追加数据 默认是false(不追加)也就是覆盖原文件。想要追加,参数设为true
FileOutputStream(File file,boolean append)
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
File f=new File("Demo.txt");
FileOutputStream out= null;
try {
out = new FileOutputStream(f);
byte[] b="java java java ".getBytes();
out.write(b);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileInputStream in=new FileInputStream(f);
byte[] print=new byte[1024];
int len=in.read(print);
System.out.println("文件信息:"+new String(print,0,len));
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出信息:
文件信息:java java java
可以看出Demo.txt文件里也已经被写入数据
(2)FileReader和FileWriter类
FileOutputStream类和FileInputStream类都只提供了对字节或字节数组的读取方法。但是汉字在文件中占用2个字节,如果使用字节流,可能会出现乱码。可以采用Reader或Writer类
构造方法和FileOutputStream和FileInputStream类似
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
File f=new File("Demo.txt");
try {
FileWriter w=new FileWriter(f);
String s="无兄弟不篮球";
w.write(s);
w.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileReader r=new FileReader(f);
char[] c=new char[1024];
int len=r.read(c);
System.out.println(new String(c,0,len));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出结果:
无兄弟不篮球
字节数组流
文件流是操作文件,java不能直接访问硬盘上的文件,所以需要通过操作系统来间接的进行访问
而字节数组流是直接的与内存进行连接,在内存上创立一块空间。释放资源可以不处理,自动关闭
ByteArrayInputStream包含一个内部缓冲区,其中包含可以从流中读取的字节。
(1)ByteInputStream
构造方法
ByteArrayInputStream(byte[] buf) ; //创建一个 ByteArrayInputStream ,使其使用 buf作为其缓冲区数组。
ByteArrayInputStream(byte[] buf, int offset, int length); //创建 ByteArrayInputStream使用 buf作为其缓冲器阵列。
public class IOTest {
public static void main(String[] args) {
//1、创建源
byte[] src = "talk is cheap show me the code".getBytes();
//2、选择流
InputStream is =null;
try {
is =new ByteArrayInputStream(src); //将源读取到字节数组流
//3、操作 (分段读取)
byte[] flush = new byte[5]; //缓冲容器
int len = -1; //接收长度
while((len=is.read(flush))!=-1) { //从流中读取数据
//字节数组-->字符串 (解码)
String str = new String(flush,0,len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//4、释放资源
try {
if(null!=is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)ByteArrayOutputStream
该类实现了将数据写入字节数组的输出流。 当数据写入缓冲区时,缓冲区会自动增长
写入到缓冲区的.数据可以使用toByteArray()和toString()方法获取 。
构造方法
ByteArrayOutputStream() ;//创建一个新的字节数组输出流。
ByteArrayOutputStream(int size) ;//创建一个新的字节数组输出流,具有指定大小的缓冲区容量(以字节为单位)。
常用方法
void close() | 关闭 ByteArrayOutputStream没有任何效果。 |
void reset() | 将此字节数组输出流的 count字段重置为零,以便丢弃输出流中当前累积的所有输出。 |
int size() | 返回缓冲区的当前大小。 |
byte[] toByteArray() | 创建一个新分配的字节数组。 |
String toString() | 使用平台的默认字符集将缓冲区内容转换为字符串解码字节。 |
String toString(String charsetName) | 通过使用命名的charset解码字节,将缓冲区的内容转换为字符串。 |
void write(byte[] b, int off, int len) | 从指定的字节数组写入 len字节,从偏移量为 off开始,输出到这个字节数组输出流。 |
void write(int b) | 将指定的字节写入此字节数组输出流。 |
void writeTo(OutputStream out) | 将此字节数组输出流的完整内容写入指定的输出流参数,就像使用 out.write(buf, 0, count)调用输出流的写入方法 out.write(buf, 0, count) 。 |
public class IOTest {
public static void main(String[] args) {
//1、创建源
byte[] dest =null;
//2、选择流 (新增方法)
ByteArrayOutputStream baos =null;
try {
baos = new ByteArrayOutputStream();
//3、操作(写出)
String msg ="show me the code";
byte[] datas =msg.getBytes(); // 字符串-->字节数组(编码)
baos.write(datas,0,datas.length);
baos.flush();
//获取数据
dest = baos.toByteArray();
System.out.println(dest.length +"-->"+new String(dest,0,baos.size()));
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{
//4、释放资源
try {
if (null != baos) {
baos.close();
}
} catch (Exception e) {
}
}
}
}
带缓存的输入/输出流
缓存是I/O的一种性能优化。缓存流为I/O流增加了内存缓存区。
就像搬货物,有一堆货物其他字节流是一次只拿一个货物到目的地,这就要来回很多趟,而缓冲字节流就像一辆货车,一次拿好多东西,这样就减少了来回的趟数。
(1)BufferedInputStream与BufferedOutputStream类(缓冲字节流)
BufferedInputStream类可以对所有InputStream类进行带缓存区的包装以到达性能优化。
!!!BufferedInputStream要建立在其他字节流之上
他的过程就是先从文件读取到InputStream,再从InputStream到BufferedInputStream,再到目的地
他有两个构造方法
BufferedInputStream(InputStream in) //参数为其他的字节流
该构造方法创建了一个带有32个字节的缓存流
BufferedInputStream(InpuitStream in ,int size)
该构造方法按指定大小来创建缓冲区。
一个最优的缓冲区的大小取决于它所在的操作系统,可用的内存空间以及机器配置
利用缓冲字节流读取文件
import java.io.*;
public class Test2 {
public static void main(String[] args) {
File f=new File("Demo.txt");
BufferedInputStream bin=null;
FileInputStream in=null;
if(f.exists()){
try {
in=new FileInputStream(f);
bin=new BufferedInputStream(in); //BufferedInputStream要建立在其他字节流之上
byte[] b=new byte[1024]; //缓冲字节数组
int len=bin.read(b);
System.out.println(new String(b,0,len));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally { 流的关闭顺序 先创建的后关闭
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bin!=null){
try {
bin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
输出结果:
你好
BufferedOutputStream类输出信息和用OutStream输出信息完全一样,只不过BufferedOutStream有一个flush()方法用来将缓存区的数据强制输出完。
flush()方法就是用于即使在缓存区没有满的情况下,也将缓存区的内容强制写入到外设。flush()方法只对使用使用缓冲区的OutputStream类的子类有效。当调用close()方法时,系统在关闭流之前,也会将缓存区中的信息刷新到磁盘文件中
BufferedOutStream也有两个构造方法
BufferedOutputStream(OutputStream out)
该构造方法创建了一个带有32个字节的缓存流
BufferedOutputStream(OutpuitStream out ,int size)
该构造方法按指定大小来创建缓冲区。
import java.io.*;
public class Test3 {
public static void main(String[] args) {
File f=new File("Demo.txt");
BufferedOutputStream bout=null;
FileOutputStream out=null;
if(f.exists()){
try {
out=new FileOutputStream(f);
bout=new BufferedOutputStream(out);
String s="无兄弟不篮球";
byte[] b=s.getBytes(); //缓冲字节数组
bout.write(b);
bout.flush(); //刷新 强制将缓冲区数据写入数据,即使没有写满
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally { 流的关闭顺序 先创建的后关闭
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bout!=null){
try {
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
多使用flush()方法,就像一个货车,他还没有装满你就强制让他走。不使用flush()他就会等待货车装满再走。
(2)BufferedReader与BufferedWriter类(缓存字符流)
BufferedReader的常用方法
方法 | 说明 |
---|---|
read() | 读取单个字符 |
readLine() | 读取一行文本行,并将其返回为字符串。若无数据可读,则返回null |
BufferedWriter的常用方法
方法 | 说明 |
---|---|
write(String s) | 写入字符串 |
write(Sring s,int off,int len) | 写入字符串的某一部分 |
flush() | 刷新该流的缓存 |
newLine() | 写入一个换行,另起一行的意思 |
利用BufferedWriter写入数据
import java.io.*;
public class Test3 {
public static void main(String[] args) {
File f = new File("Demo.txt");
if (f.exists()) {
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(f,true); //追加
bw = new BufferedWriter(fw);
bw.newLine();
String s1 = "无兄弟不篮球";
String s2 = "欧文牛逼";
bw.write(s1);
bw.newLine();
bw.write(s2);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) { //流的关闭顺序 先创建的后关闭
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
利用BufferedReader读数据
import java.io.*;
public class Test3 {
public static void main(String[] args) {
File f = new File("Demo.txt");
if (f.exists()) {
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(f);
br = new BufferedReader(fr);
String s=null;
int i=1;
while((s=br.readLine())!=null){ //循环的读
System.out.println("第"+(i++)+"行:"+s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) { //流的关闭顺序 先创建的后关闭
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
输出结果:(根据文档显示)
无兄弟不篮球
欧文牛逼
无兄弟不篮球
欧文牛逼
无兄弟不篮球
欧文牛逼
转换流(InputStreamReader与OutputStreamWriter)
也就是字符流与字节流之间的桥梁,并且能为字节流指定字符集,可处理一个个的字符
(1)InputStreamReader
构造方法
InputStreamReader(InputStream in) ;//创建一个使用默认字符集的InputStreamReader
InputStreamReader(InputStream in, String charsetName); //创建一个使用命名字符集的InputStreamReader。
(2)OutputStreamWriter
构造方法
OutputStreamWriter(OutputStream out) ;//创建一个使用默认字符编码的OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) ;//创建一个使用命名字符集的OutputStreamWriter
以字符流的形式操作字节流(纯文本的)
public class ConvertTest {
public static void main(String[] args) {
//操作System.in 和System.out
try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer =new BufferedWriter(new OutputStreamWriter(System.out));){
//循环获取键盘的输入(exit退出),输出此内容
String msg ="";
while(!msg.equals("exit")) {
msg = reader.readLine(); //循环读取
writer.write(msg); //循环写出
writer.newLine();
writer.flush(); //强制刷新
}
}catch(IOException e) {
System.out.println("操作异常");
}
}
}
try-with结构,系统可以自动帮你关闭
System in就是一个字节流 System.out也是一个字节流
public class ConvertTest02 {
public static void main(String[] args) {
try(BufferedReader reader =
new BufferedReader(
new InputStreamReader(
new URL("http://www.baidu.com").openStream(),"UTF-8"));
BufferedWriter writer =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("baidu.html"),"UTF-8"));){
//3、操作 (读取)
String msg ;
while((msg=reader.readLine())!=null) {
//System.out.println(msg);
writer.write(msg); //字符集不统一不够出现乱码
writer.newLine();
}
writer.flush();
}catch(IOException e) {
System.out.println("操作异常");
}
}
public static void test2() {
//操作网络流 下载百度的源代码
try(InputStreamReader is =
new InputStreamReader(new URL("http://www.baidu.com").openStream(),"UTF-8");){ //以UTF-8的字符集下载
//3、操作 (读取)
int temp ;
while((temp=is.read())!=-1) {
System.out.print((char)temp);
}
}catch(IOException e) {
System.out.println("操作异常");
}
}
public static void test1() {
//操作网络流 下载百度的源代码
try(InputStream is =new URL("http://www.baidu.com").openStream();){
//3、操作 (读取)
int temp ;
while((temp=is.read())!=-1) {
System.out.print((char)temp); //字节数不够出现乱码
}
}catch(IOException e) {
System.out.println("操作异常");
}
}
}
数据流(DataInputStream 与DataOutputStream)
方便处理八大基本数据类型和字符串
读取的顺序和写出的顺序保持一致
可以从流中读取或写入java的基本类型(int,boolean,double…)
DataInputStream构造方法
DataInputStream(InpuStream in)
DataOutputStream构造方法
DataOutputStream(OutStream out)
DataInputStream常用方法
方法 | 返回值 | 说明 |
---|---|---|
read(byte[] b) | int | 从包含的输入流中读取一些字节数,并将它们存储到缓冲区数组 b |
read(byte[] b, int off, int len) | int | 从包含的输入流读取最多 len个字节的数据为字节数组。 |
readBoolean() | boolean | 读取一个输入字节,并返回 true如果该字节不为零, false如果该字节是零。 |
readByte() | byte | 读取并返回一个输入字节。 |
readChar() | char | 读取两个输入字节并返回一个 char值。 |
readDouble() | double | 读取八个输入字节并返回一个 double值。 |
readFloat() | float | 读取四个输入字节并返回一个 float值。 |
readInt() | int | |
readLong() | long | |
readShort() | short | 读取两个输入字节并返回一个 short值。 |
readUTF() | String | 读取已使用 modified UTF-8格式编码的字符串 |
readFully(byte[] b) | void | 从输入流读取一些字节,并将它们存储到缓冲区数组 b 。 |
readFully(byte[] b, int off, int len) | void | 从输入流读取 len个字节。 |
DataoutStream常用方法
方法 | 返回值 | 说明 |
---|---|---|
write(byte[] b) | void | 将输出流写入数组 b中的所有字节。 |
write(byte[] b, int off, int len | void | 将输出流写入数组 b中的特定长度字节。 |
write(int b) | void | 向输出流写入参数 b的八个低位。 |
writeBoolean(boolean v) | void | 将 boolean值写入此输出流。 |
writeByte(int v) | void | 向输出流写入参数 v的八个低位位。 |
writeBytes(String s) | void | 将一个字符串写入输出流。 |
writeChar(int v) | void | 将两个字节组成的 char值写入输出流。。 |
writeDouble(double v) | void | 将 double值(由8个字节组成)写入输出流 |
writeFloat(float v) | void | 将 float值写入输出流,该值由四个字节组成。 |
writeInt(int v) | void | 将 int值(由四个字节组成)写入输出流。 |
writeLong(long v) | void | 将 long值(由八个字节组成)写入输出流。 |
writeShort(int v) | void | 将两个字节写入输出流以表示参数的值。 |
writeUTF(String s) | void | 将两个字节的长度信息写入输出流,其后是 字符串 s中每个字符的 s 。 |
import java.io.*;
public class DataStream {
public static void main(String[] args) {
File f=new File("Demo.txt");
FileOutputStream fw=null;
DataOutputStream dw=null;
try {
fw=new FileOutputStream(f);
dw=new DataOutputStream(fw);
dw.writeUTF("写入字符串数据:");
dw.writeInt(123); //写入数字数据 尽量不要写在一起
dw.writeBoolean(true);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(dw!=null){
try {
dw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
写入之后在文档里是乱码的,需要DateInputStream来读
public class DataStream {
public static void main(String[] args) {
File f=new File("Demo.txt");
FileInputStream fr=null;
DataInputStream dr=null;
try {
fr=new FileInputStream(f);
dr=new DataInputStream(fr);
System.out.println("读取字符串:"+dr.readUTF());
System.out.println("读取int:"+dr.readInt());
System.out.println("读取boolean:"+dr.readBoolean());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(dr!=null){
try {
dw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fr!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出结果:
读取字符串:写入字符串数据:
读取int:123
读取boolean:true
写入数据尽量不要把两个数字连在一起,不然读出的数字会发生错误,比如你先writeInt(),然后又writeDouble().读的时候就不一定是你最初输入的那个数字了
```java
public class DataTest {
public static void main(String[] args) throws IOException {
//写出
ByteArrayOutputStream baos =new ByteArrayOutputStream();
DataOutputStream dos =new DataOutputStream(new BufferedOutputStream(baos)); //使用buffered提升效率
//操作数据类型 +数据
dos.writeUTF("无兄弟不篮球");
dos.writeInt(18);
dos.writeBoolean(false);
dos.writeChar('a');
dos.flush();
byte[] datas =baos.toByteArray();
System.out.println(datas.length);
//读取
DataInputStream dis =new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
//顺序与写出一致
String msg = dis.readUTF();
int age = dis.readInt();
boolean flag = dis.readBoolean();
char ch = dis.readChar();
System.out.println(flag);
}
}
对象流(ObjectInputStream与ObjectOutPutStream )
对象流和数据流的用法差不多,只不过有了对象的序列化和反序列化
对象要被序列化就要实现Serializable接口,这个接口就相当于一个通行证给java虚拟机看的,有了这个接口对象才可以被序列化
ObjectOutPutStream 比数据流多了一个wirteObject(Object obj)方法
ObjectInputStream比数据流多了一个readObject() 方法,返回一个Object
public class ObjectTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//写出 -->序列化
ByteArrayOutputStream baos =new ByteArrayOutputStream();
ObjectOutputStream oos =new ObjectOutputStream(new BufferedOutputStream(baos));
//操作数据类型 +数据
oos.writeUTF("无兄弟不篮球");
oos.writeInt(18);
oos.writeBoolean(false);
oos.writeChar('a');
//对象
oos.writeObject("篮球热血");
oos.writeObject(new Date());
Employee emp =new Employee("张三",30,4000);
oos.writeObject(emp);
oos.flush();
byte[] datas =baos.toByteArray();
System.out.println(datas.length);
//读取 -->反序列化
ObjectInputStream ois =new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
//顺序与写出一致
String msg = ois.readUTF();
int age = ois.readInt();
boolean flag = ois.readBoolean();
char ch = ois.readChar();
System.out.println(flag);
//对象的数据还原
Object str = ois.readObject();
Object date = ois.readObject();
Object employee = ois.readObject();
if(str instanceof String) { //判断一下是不是这个类的实例
String strObj = (String) str;
System.out.println(strObj);
}
if(date instanceof Date) {
Date dateObj = (Date) date;
System.out.println(dateObj);
}
if(employee instanceof Employee) {
Employee empObj = (Employee) employee;
System.out.println(empObj.getName()+"-->"+empObj.getAge()+"-->"+empObj.getSalary());
}
}
}
//javabean 封装数据
class Employee implements java.io.Serializable{
private String name;
private transient int age; //该数据不需要序列化 transient (透明)这个标识这个属性就不会被序列化 比如一些敏感数据
private double salary;
public Employee() {
}
public Employee(String name, int age,double salary) {
this.name = name;
this.age=age;
this.salary = salary;
}
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 getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
打印流
能够方便地打印各种数据值的表示。
(1)PrintStream
构造方法
PrintStream(File file) ; //使用指定的文件创建一个新的打印流,而不需要自动换行。
PrintStream(File file, String csn) ; //使用指定的文件和字符集创建新的打印流,而不需要自动换行。
PrintStream(OutputStream out) ; //创建一个新的打印流。
PrintStream(OutputStream out, boolean autoFlush) ; //创建一个新的打印流。自动刷新
PrintStream(OutputStream out, boolean autoFlush, String encoding) ;// 创建一个新的打印流。
PrintStream(String fileName) ; //使用指定的文件名创建新的打印流,无需自动换行
PrintStream(String fileName, String csn) ; // 创建一个新的打印流,不需要自动换行,具有指定的文件名和字符集。
常用方法
PrintStream append(char c) | 将指定的字符附加到此输出流。 |
PrintStream append(CharSequence csq) | 将指定的字符序列附加到此输出流。 |
PrintStream append(CharSequence csq, int start, int end) | 将指定字符序列的子序列附加到此输出流。 |
boolean checkError() | 刷新流并检查其错误状态。 |
protected void clearError() | 清除此流的内部错误状态。 |
void close() | 关闭流。 |
void flush() | 刷新流。 |
PrintStream format(Locale l, String format, Object… args) | 使用指定的格式字符串和参数将格式化的字符串写入此输出流。 |
PrintStream format(String format, Object… args) | 使用指定的格式字符串和参数将格式化的字符串写入此输出流。 |
void print(boolean b) | 打印布尔值。 |
void print(char c) | 打印一个字符 |
void print(char[] s) | 打印字符数组。 |
void print(double d) | 打印双精度浮点数。 |
void print(float f) | 打印浮点数。 |
void print(int i) | 打印一个整数。 |
void print(long l) | 打印一个长整数。 |
void print(Object obj) | 打印一个对象。 |
void print(String s) | 打印字符串。 |
PrintStream printf(Locale l, String format, Object… args) | 使用指定的格式字符串和参数将格式化的字符串写入此输出流的便利方法。 |
PrintStream printf(String format, Object… args) | 使用指定的格式字符串和参数将格式化的字符串写入此输出流的便利方法。 |
void println() | 通过写入行分隔符字符串来终止当前行。 |
void println(boolean x) | 打印一个布尔值,然后终止该行。 |
void println(char x) | 打印一个字符,然后终止该行。 |
void println(char[] x) | 打印一个字符数组,然后终止该行。 |
void println(double x) | 打印一次,然后终止行。 |
void println(float x) | 打印一个浮点数,然后终止该行。 |
void println(int x) | 打印一个整数,然后终止行。 |
void println(long x) | 打印很长时间,然后终止行。 |
void println(Object x) | 打印一个对象,然后终止该行。 |
void println(String x) | 打印一个字符串,然后终止行。 |
protected void setError() | 将流的错误状态设置为 true 。 |
void write(byte[] buf, int off, int len) | 从指定的字节数组写入 len个字节,从偏移 off开始到此流。 |
void write(int b) | 将指定的字节写入此流。 |
public class PrintText {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps=System.out; //System.out
ps.println("打印");
ps.println(1);
ps=new PrintStream(new BufferedOutputStream(new FileOutputStream("abc.txt")),true);
ps.println("打印");
ps.println(1);
//重定向输出端 (System.out默认的输出端是控制台,可以通过 System.setOut改变输出端
System.setOut(ps);
System.out.println("改变输出端");
//重定向控制台
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true)); //改变回控制台可以通过FileDescriptor.out
System.out.println("改变回来");
}
}
(2)PrintWriter
PrintWriter和PrintStream类似
public class PrintWriterText {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw=new PrintWriter(new BufferedOutputStream(new FileOutputStream("abc.txt")),true);
pw.println("打印");
pw.println(1);
}
}
随机流(RandomAccessFile)
该类的实例支持读取和写入随机访问文件。 随机访问文件的行为类似于存储在文件系统中的大量字节。 有一种游标,或索引到隐含的数组,称为文件指针 ;输入操作读取从文件指针开始的字节,如果在读/写模式下创建随机访问文件,则输出操作也可用。输出操作从文件指针开始写入字节,
其他流都是从文件的开头直接获取所有内容,而这个流可以通过文件指针的方式从中间的指定位置开始读取。即可以分割文件
构造方法
RandomAccessFile(File file, String mode); // 创建一个随机访问文件流从
RandomAccessFile(String name, String mode; //创建随机访问文件流,以从中指定名称的文件读取
mode参数有 “r” ,“rw”,",“rws”,或 “rwd” 常用的是 “r” ,“rw”
常用方法
方法 | 说明 |
---|---|
void seek(long pos) | 设置文件指针偏移,从该文件的开头测量,发生下一次读取或写入。 |
//指定起始位置,读取剩余所有内容
package Learn;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
public class RandomText {
public static void main(String[] args) throws IOException {
RandomAccessFile raf=new RandomAccessFile(new File("src/Learn/RandomText.java"),"r");
raf.seek(2);
byte[] data=new byte[1024] ; //缓冲容器
int len=-1;
while ((len=raf.read(data))!=-1){
System.out.println(new String(data,0,len));
}
raf.close();
}
}
输出结果:
ckage Learn;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
public class RandomText {
public static void main(String[] args) throws IOException {
RandomAccessFile raf=new RandomAccessFile(new File("src/Learn/RandomText.java"),"r");
raf.seek(2);
byte[] data=new byte[1024] ;
int len=-1;
while ((len=raf.read(data))!=-1){
System.out.println(new String(data,0,len));
}
raf.close();
}
}
可以看出开头的两个字已经没有了
读取指定起始位置,指定大小的数据
package Learn;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
public class RandomText {
public static void main(String[] args) throws IOException {
RandomAccessFile raf=new RandomAccessFile(new File("src/Learn/RandomText.java"),"r");
int beginpos=2;
int actualsize=10; //指定大小
raf.seek(beginpos);
byte[] data=new byte[1024] ;
int len=-1;
while ((len=raf.read(data))!=-1){
if(actualsize>len){
System.out.println(new String(data,0,len));
actualsize-=len;
}else{
System.out.println(new String(data,0,actualsize));
break;
}
}
raf.close();
}
}
输出结果:
ckage Lear
将文件分块截取获得
package Learn;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
public class RandomText {
public static void main(String[] args) throws IOException {
File src=new File("src/Learn/RandomText.java");
RandomAccessFile raf=new RandomAccessFile(src,"r");
long len=src.length(); //总长度
int blocksize=100; //每块大小
int size=(int)Math.ceil(len*1.0/blocksize); //块数
int beginpos=0; //起始位置
int actualsize= blocksize>len? (int) len :blocksize; //每次截取块的实际大小
for(int i=0;i<size;i++){
beginpos=i*blocksize;
if(i==size-1){ //最后一块
actualsize= (int) len;
}else{
actualsize=blocksize;
len-=actualsize;
}
System.out.println("编号:"+i+" 起始位置:"+beginpos +" 截取大小:"+actualsize);
spilt(raf,beginpos,actualsize);
}
raf.close();
}
public static void spilt(RandomAccessFile raf,int beginpos,int actualsize) throws IOException {
raf.seek(beginpos);
byte[] data=new byte[1024];
int len=-1;
while ((len=raf.read(data))!=-1){
if(actualsize>len){
System.out.println(new String(data,0,len));
}else{
System.out.println(new String(data,0,actualsize));
break;
}
}
}
}
输出结果:
编号:0 起始位置:0 截取大小:100
package Learn;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOExce
编号:1 起始位置:100 截取大小:100
ption;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
public class RandomText
编号:2 起始位置:200 截取大小:100
{
public static void main(String[] args) throws IOException {
File src=new File("src/L
编号:3 起始位置:300 截取大小:100
earn/RandomText.java");
RandomAccessFile raf=new RandomAccessFile(src,"r");
long l
编号:4 起始位置:400 截取大小:100
en=src.length(); //总长度
int blocksize=100; //每块大小
int si
编号:5 起始位置:500 截取大小:100
ze=(int)Math.ceil(len*1.0/blocksize); //块数
int beginpos=0; //起始位置
编号:6 起始位置:600 截取大小:100
int actualsize= blocksize>len? (int) len :blocksize; //每次截取块的实际大小
编号:7 起始位置:700 截取大小:100
for(int i=0;i<size;i++){
beginpos=i*blocksize;
if(i==size-1){ //�
编号:8 起始位置:800 截取大小:100
�后一块
actualsize= (int) len;
}else{
actualsize=b
编号:9 起始位置:900 截取大小:100
locksize;
len-=actualsize;
}
System.out.println("编号�
编号:10 起始位置:1000 截取大小:100
�"+i+" 起始位置:"+beginpos +" 截取大小:"+actualsize);
spilt(raf,beginpos,ac
编号:11 起始位置:1100 截取大小:100
tualsize);
}
raf.close();
}
public static void spilt(RandomAccessFile r
编号:12 起始位置:1200 截取大小:100
af,int beginpos,int actualsize) throws IOException {
raf.seek(beginpos);
byte[]
编号:13 起始位置:1300 截取大小:100
data=new byte[1024];
int len=-1;
while ((len=raf.read(data))!=-1){
编号:14 起始位置:1400 截取大小:100
if(actualsize>len){
System.out.println(new String(data,0,len));
}e
编号:15 起始位置:1500 截取大小:100
lse{
System.out.println(new String(data,0,actualsize));
break;
编号:16 起始位置:1600 截取大小:38
}
}
}
}
可以看到在上面输出中有乱码的情况,原因就是将某个字拆的时候拆开读取了,比如这个字有两个字节,一半字节在上一块截取的随机流中,另一半字节在下一块截取的随机流中,所以译码的时候出现了乱码
随机读取和随机写入 read和write 实现将一张图片拆分出不同的图片文件
package Learn;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomText02 {
public static void main(String[] args) throws IOException {
//分多少块
File src = new File("p.png");
//总长度
long len = src.length();
//每块大小
int blockSize =1024;
//块数: 多少块
int size =(int) Math.ceil(len*1.0/blockSize);
System.out.println(size);
//起始位置和实际大小
int beginPos = 0;
int actualSize = (int)(blockSize>len?len:blockSize);
for(int i=0;i<size;i++) {
beginPos = i*blockSize;
if(i==size-1) { //最后一块
actualSize = (int)len;
}else {
actualSize = blockSize;
len -=actualSize; //剩余量
}
System.out.println(i+"-->"+beginPos +"-->"+actualSize);
split(i,beginPos,actualSize);
}
}
public static void split(int i,int beginPos,int actualSize ) throws IOException {
RandomAccessFile raf =new RandomAccessFile(new File("p.png"),"r");
RandomAccessFile raf2 =new RandomAccessFile(new File("dest/"+i+"p.png"),"rw");
//随机读取
raf.seek(beginPos);
//读取
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=raf.read(flush))!=-1) {
if(actualSize>len) { //获取本次读取的所有内容
raf2.write(flush, 0, len);
actualSize -=len;
}else {
raf2.write(flush, 0, actualSize);
break;
}
}
raf2.close();
raf.close();
}
}
将文件分割封装成一个类
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
public class Spilt {
private File src; //源头
private long len;
private String destDir; //目的地(文件夹)
private List<String> destPaths; //存储分割后文件的路径
private int blockSize; //每块大小
private int size; //块数
public Spilt(String srcPath,String destDir){
this(srcPath,destDir,1024);
}
public Spilt(String srcPath,String destDir,int blockSize){
this.src=new File(srcPath);
this.destDir=destDir;
this.blockSize=blockSize;
this.destPaths=new ArrayList<>();
init();
}
private void init(){
this.len=this.src.length();
this.size= (int) Math.ceil(this.len*1.0/blockSize); //块数
for (int i=0;i<size;i++){
this.destPaths.add(this.destDir+"/"+i+"-"+this.src.getName());
}
}
public void spilt() throws IOException {
int beginpos=0;
int actualsize= blockSize>this.len? (int) this.len :blockSize;
for (int i=0;i<size;i++){
beginpos=i*beginpos;
if(i==size-1){
actualsize= (int) len;
}else{
actualsize=blockSize;
len-=actualsize;
}
spiltDetail(i,beginpos,actualsize);
}
}
private void spiltDetail(int i, int beginpos, int actualsize) throws IOException {
RandomAccessFile raf=new RandomAccessFile(this.src,"r");
RandomAccessFile warf=new RandomAccessFile(this.destPaths.get(i),"rw");
raf.seek(beginpos);
byte[] data=new byte[1024];
int len=-1;
while ((len=raf.read(data))!=-1){
if(actualsize>len){
warf.write(data,0,len);
actualsize-=len;
}else{
warf.write(data,0,actualsize);
break;
}
}
warf.close();
raf.close();
}
}
序列流SequenceInputStream
SequenceInputStream表示其他输入流的逻辑级联。
它从一个有序的输入流集合开始,从第一个读取到文件的结尾,然后从第二个文件读取,依此类推,直到最后一个输入流达到文件的结尾。
构造方法
SequenceInputStream(Enumeration<? extends InputStream> e) ; //初始化新创建 SequenceInputStream通过记住参数,它必须是一个 Enumeration产生对象,它们的运行时类型是 InputStream
SequenceInputStream(InputStream s1, InputStream s2) ; //通过记住两个 SequenceInputStream来初始化新创建的SequenceInputStream,这些参数将按顺序读取,首先是 s1 ,然后是 s2
Vecor类的elements()方法返回的就是Enumeration ,所以可以使用Vector.elemnets()传递参数
用序列流可以将随机流分割后的文件进行合并
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
public class Spilt {
private File src; //源头
private long len;
private String destDir; //目的地(文件夹)
private List<String> destPaths; //存储分割后文件的路径
private int blockSize; //每块大小
private int size; //块数
public Spilt(String srcPath,String destDir){
this(srcPath,destDir,1024);
}
public Spilt(String srcPath,String destDir,int blockSize){
this.src=new File(srcPath);
this.destDir=destDir;
this.blockSize=blockSize;
this.destPaths=new ArrayList<>();
init();
}
private void init(){
this.len=this.src.length();
this.size= (int) Math.ceil(this.len*1.0/blockSize); //块数
for (int i=0;i<size;i++){
this.destPaths.add(this.destDir+"/"+i+"-"+this.src.getName());
}
}
public void spilt() throws IOException { //分割
int beginpos=0;
int actualsize= blockSize>this.len? (int) this.len :blockSize;
for (int i=0;i<size;i++){
beginpos=i*beginpos;
if(i==size-1){
actualsize= (int) len;
}else{
actualsize=blockSize;
len-=actualsize;
}
spiltDetail(i,beginpos,actualsize);
}
}
private void spiltDetail(int i, int beginpos, int actualsize) throws IOException {
RandomAccessFile raf=new RandomAccessFile(this.src,"r");
RandomAccessFile warf=new RandomAccessFile(this.destPaths.get(i),"rw");
raf.seek(beginpos);
byte[] data=new byte[1024];
int len=-1;
while ((len=raf.read(data))!=-1){
if(actualsize>len){
warf.write(data,0,len);
actualsize-=len;
}else{
warf.write(data,0,actualsize);
break;
}
}
warf.close();
raf.close();
}
public void merge(String destpath) throws IOException { //合并
OutputStream os=new BufferedOutputStream(new FileOutputStream(destpath,true));
Vector<InputStream> vin=new Vector<>();
SequenceInputStream sis=null;
for (int i=0;i<destPaths.size();i++){
vin.add(new BufferedInputStream(new FileInputStream(destPaths.get(i))));
}
sis=new SequenceInputStream(vin.elements());
byte[] data=new byte[1024];
int len=-1;
while ((len=sis.read(data))!=-1){
os.write(data,0,len);
}
os.flush();
sis.close();
os.close();
}
}
CommonsIO
另一篇
https://blog.csdn.net/weixin_44035017/article/details/102526767