目录
IO
File 类
- java.io.File类:文件和目录路径名的抽象表示形式,与平台无关
- File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
- File对象可以作为参数传递给流的构造函数
- File类的常见构造方法:
- public File(String pathname) :以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
- public File(String parent,String child):以parent为父路径,child为子路径创建File对象。
- File的静态属性String separator存储了当前系统的路径分隔符。 在UNIX中,此字段为‘/’,在Windows中,为‘\\’
//访问文件名:
getName()//获取文件名
getPath()//获取文件路径
getAbsoluteFile()//获取绝对路径文件名
getAbsolutePath()//获取绝对路径
getParent()//文件所在目录
renameTo(File newName)
/*当前file必须存在,newName文件必须不存在,当文件名中存在目录,则目录必须存在,可实现不同盘之间的重命名(文件存在新盘下,旧盘中不存在)
*/
//文件检测
//获取常规文件信息
lastModified()
length()
/*
File file1 = new File("iofiletest.txt");
*/
//exists();
file1.exists();//true
//canRead():文件是否可读
file1.canRead();//true
//canWrite():文件是否可写
file1.canWrite();//true
//canExecute():文件是否可执行
file1.canExecute();//true
//isFile():判断是否是文件
file1.isFile();//true
//isDirectory():是否是文件目录
file1.isDirectory();//false
//lastModified():文件的最后的修改时间
file1.lastModified();//1588062890754L
new Date(file1.lastModified());//Tue Apr 28 16:34:50 CST 2020
//length():文件的长度/大小
file1.length();//45
//文件操作相关
createNewFile()//新建文件
delete()//删除文件
//目录操作相关
mkDir()//新建文件夹(文件目录)
mkDirs()//新建多层目录文件
list()//String[]: 返回目录下的文件列表(string[]数组存储文件名)
listFiles()//File[]: 返回文件列表
IO流
IO原理
- IO流用来处理设备之间的数据传输。
- Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行。
- java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
流的分类
- 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,处理流
抽象基类 | 字节流 | 字符流 |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
- Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
- 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
- 节点流可从一个特定的数据源读写数据
- 处理流是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
- 缓冲流“套接”在节点流之上
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipeInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputSreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
FilterInputStream | FilterOutputStream | FilterReader | FilterWriter | |
打印流 | PrintStream | |||
退回输入流 | PushbackInputStream | PushbackReader | PrintWriter | |
特殊流 | DataInputStream | DataOutputStream |
- InputStream&Reader
- InputStream 和 Reader 是所有输入流的基类。
- InputStream(典型实现:FileInputStream)【int read();int read(byte[] b) ;int read(byte[] b, int off , int len);】
- Reader(典型实现:FileReader)【int read();int read(char [] c);int read(char [] c, int off, int len)】
- 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。
- OutputStream & Writer
- OutputStream 和 Writer 也非常相似:【void write(int b/int c);void write(byte[] b/char[] cbuf);void write(byte[] b/char[] buff, int off, int len);void flush();void close(); 需要先刷新,再关闭此流】
- 因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数【void write(String str);void write(String str, int off, int len);】
// 使用try-catch处理异常更合理:保证流的关闭一定可以执行
// 创建FileInputStream类的对象
FileInputStream fis = null;
try {
// 创建File类的对象
File file = new File("iofiletest.txt");
fis = new FileInputStream(file);
// 3、调用FileInputStream的方法,实现file文件的读取
/*
* read():读取一个文件的字节。当执行到文件结尾,返回-1 多次读取文件
*/
int b = fis.read();
while (b != -1) {// 正常读取文件
// while((b=fis.read())!=-1) {//出现文件内容读取缺失的问题
System.out.print((char) b);// abcdefghijklmnopqrstuvwxy z
b = fis.read();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4、关闭相应的流
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* read(byte[] b1):将文件的字节码一次(依次)读入到byte[]中
*/
byte[] b = new byte[1];// 读取的数据存放的字节数组
int len;// 每次读入到byte中字节的长度
while ((len=fis.read(b)) != -1) {
// for (int i=0;i<len;++i) {
// System.out.print((char) b[i]);// abcdefghijklmnopqrstuvwxy z
// }
String str = new String(b);
System.out.print(str);//abcdefghijklmnopqrstuvwxy z
}
//FileInputStream & File OutputStream 实现文件的复制
//1、提供读入、写出的文件
File filein = new File("6.png");
File fileout = new File("1.png");
//2、提供相应的类
FileInputStream fis = new FileInputStream(filein);
FileOutputStream fos = new FileOutputStream(fileout);
//3、实现文件的复制
byte[] b= new byte[20];
int length;
while((length=fis.read(b))!=-1) {
fos.write(b,0,length);
}
fos.close();
fis.close();
//FileReader & FileWriter 实现文件的复制
File file = new File("iofile2.txt");
File file1 = new File("iofile2W.txt");
FileReader fr = new FileReader(file);
FileWriter fw = new FileWriter(file1);
char[] c= new char[50];
int length;
while((length=fr.read(c))!=-1) {
String str = new String(c,0,length);
System.out.print(str);
str+="\nFileWriter end OVER";
fw.write(str);
}
if(fr!=null)
fr.close();
if(fw!=null)
fw.close();
【注意】 对于非文本文件只能使用 字节流 操作;
- 缓冲流
- 为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组
- 根据数据操作单位可以把缓冲流分为:【BufferedInputStream 和 BufferedOutputStream、BufferedReader 和 BufferedWriter】
- 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法
- 对于输出的缓冲流,写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出
/**
**缓冲流:FileBufferedInputStream、FileBufferedOutputStream
*加速字节流的操作
*/
// 处理字节流文件
// 实现非文本文件的复制
// 1、提供读入、写出的文件
File file = new File("1.png");
File file1 = new File("2.png");
// 2、创建相应的节点流:FileInputStream\FileutputStream
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file1);
// 3、将创建的节点流的对象作为形参传递给缓冲流的构造器中
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
// 4、具体的实现文件按复制的操作
byte[] b = new byte[100];
int length;
while ((length = bis.read(b)) != -1) {
bos.write(b, 0, length);
bos.flush();
}
// 5、关闭相应的流
bos.close();
bis.close();
/**
**缓冲流:BufferReader、BufferWriter
*/
FileReader fr = new FileReader(file);
FileWriter fw = new FileWriter(file1);
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
// char[] c = new char[1024];
// int length;
// while ((length = br.read(c)) != -1) {
// String str = new String(c, 0, length);
// System.out.print(str);
// }
String str;
while((str=br.readLine())!=null) {
bw.write(str);
System.out.println(str);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
- 转换流
- 转换流提供了在字节流和字符流之间的转换
- Java API提供了两个转换流:【InputStreamReader和OutputStreamWriter】
- 字节流中的数据都是字符时,转成字符流操作更高效。
/**
*InputStreamReader:
* 用于将字节流中读取到的字节按指定字符集解码成字符。需要和InputStream“套接”。
* 构造方法
* 1、public InputStreamReader(InputStream in)
* 2、public InputSreamReader(InputStream in,String charsetName)
*/
/**
*OutputStreamWriter:
* 用于将要写入到字节流中的字符按指定字符集编码成字节。需要和OutputStream“套接”。
* 构造方法:
* 1、public OutputStreamWriter(OutputStream out)
* 2、public OutputSreamWriter(OutputStream out,String charsetName)
*/
/**
* 转换流:InputStreamReader、OutputStreamWriter 编码:字符串-->字节数组 解码:字节数组-->字符串
*/
// 解码
File file = new File("iofile2.txt");
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, "GBK");
BufferedReader br = new BufferedReader(isr);
// 编码
File file1 = new File("iofile21.txt");
FileOutputStream fos = new FileOutputStream(file1);
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
BufferedWriter bw = new BufferedWriter(osw);
String str;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
- 输入输出流
- System.in和System.out分别代表了系统标准的输入和输出设备
- 默认输入设备是键盘,输出设备是显示器
- System.in的类型是InputStream
- System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
- 通过System类的setIn,setOut方法对默认设备进行改变。
①public static void setIn(InputStream in)
②public static void setOut(PrintStream out)
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
System.out.println("请输入字符串: ");
String str;
while(true) {
str = br.readLine();//hello world!
if(str.equalsIgnoreCase("e")||str.equalsIgnoreCase("exit")) {
break;
}
String str1=str.toUpperCase();
System.out.println(str1);//HELLO WORLD!
br.close();
- 打印流
- 在整个IO包中,打印流是输出信息最方便的类。
- 【PrintStream(字节打印流)和PrintWriter(字符打印流)】PrintStream(字节打印流)和PrintWriter(字符打印流),PrintStream和PrintWriter的输出不会抛出异常,PrintStream和PrintWriter有自动flush功能,System.out返回的是PrintStream的实例
public void testPrintStream() {
//字节型输出流
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("iofile2.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
PrintStream ps = new PrintStream(fos, true);
// if (ps != null) {
// // 把标准输出流(控制台输出)改成文件iofile2.txt
// System.setOut(ps);
// }
for (int i = 0; i <= 255; i++) {
// 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
ps.close();
}
- 数据流
- 为了方便地操作Java语言的基本数据类型的数据,可以使用数据流。
- 数据流有两个类:(用于读取和写出基本数据类型的数据)【DataInputStream 和 DataOutputStream】分别“套接”在 InputStream 和 OutputStream 节点流上
- DataInputStream中的方法
- DataOutputStream中的方法
boolean readBoolean()
byte readByte()
char readChar()
float readFloat()
double readDouble()
short readShort()
long readLong()
int readInt()
String readUTF()
void readFully(byte[] b)
/**
* 数据流:
*/
@Test
public void testDataInputStream() {
DataInputStream dis = null;
try {
// 创建连接到指定文件的数据输出流对象
dis = new DataInputStream(new FileInputStream("destData.txt"));
//byte[] b=new byte[10];
//int length;
//while((length = dis.read(b))!=-1) {
// System.out.println(new String(b,0,length));
//}
String str = dis.readUTF();
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} finally { // 关闭流对象
try {
if (dis != null) {
// 关闭过滤流时,会自动关闭它包装的底层节点流
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testDataOutputStream() {
DataOutputStream dos = null;
try {
// 创建连接到指定文件的数据输出流对象
dos = new DataOutputStream(new FileOutputStream("destData.txt"));
dos.writeUTF("ab中国"); // 写UTF字符串
dos.writeBoolean(false); // 写入布尔值
dos.writeLong(1234567890L); // 写入长整数
System.out.println("写文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 关闭流对象
try {
if (dos != null) {
// 关闭过滤流时,会自动关闭它包装的底层节点流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 对象流
- 【ObjectInputStream和OjbectOutputSteam 】用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
- 序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中
- 反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
- 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
- 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
- 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
- 如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一: Serializable、 Externalizable
- 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:【private static final long serialVersionUID; serialVersionUID用来表明类的不同版本间的兼容性;如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显式声明】
- 显示定义serialVersionUID的用途【希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID;不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID】
- 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:【创建一个 ObjectOutputStream;调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象。注意写出一次,操作flush()】
- 反序列化【创建一个 ObjectInputStream ;调用 readObject() 方法读取流中的对象】
- 强调:如果某个类的字段不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化
// 对象的序列化过程
Person p1 = new Person("马冬梅", 18);
Person p2 = new Person("墨菲", 13);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
oos.writeObject(p1);
oos.flush();
oos.writeObject(p2);
oos.flush();
oos.close();
//
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
Person p1 = (Person) ois.readObject();
System.out.println(p1);
Person p2 = (Person) ois.readObject();
System.out.println(p2);
ois.close();
//Person [name=马冬梅, id=18]
//Person [name=墨菲, id=13]
class Person implements Serializable{
private static final long serialVersionUID = 2718931278391L;
……}
- RandomAccessFile 类
- RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件;【支持只访问文件的部分内容 ;可以向已存在的文件后追加内容】
- RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:【long getFilePointer():获取文件记录指针的当前位置;void seek(long pos):将文件记录指针定位到 pos 位置】
- 构造器 【public RandomAccessFile(File file, String mode) ;public RandomAccessFile(String name, String mode)】
- 创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:【r: 以只读方式打开; rw:打开以便读取和写入 ;rwd:打开以便读取和写入;同步文件内容的更新 ;rws:打开以便读取和写入;同步文件内容和元数据的更新】
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
raf1 = new RandomAccessFile(new File("iofile21.txt"),"r");
raf2 = new RandomAccessFile(new File("file1.txt"),"rw");
//实现复制
byte[] b = new byte[50];
int length;
while((length = raf1.read(b))!=-1) {
raf2.write(b,0,length);
}
//实现覆盖(20位置,“12”覆盖两个字符)
raf2.seek(20);
raf2.write("12".getBytes());
//实现单行插入
raf2.seek(20);
String str = raf2.readLine();
long lpos = raf2.getFilePointer();
System.out.println(lpos);
raf2.write("123".getBytes());
raf2.write(str.getBytes());
//实现多行插入
raf2.seek(20);
byte[] b = new byte[20];
int length;
StringBuffer strb = new StringBuffer();
while((length=raf2.read(b))!=-1) {
strb.append(new String(b,0,length));
}
raf2.seek(20);
raf2.write("123456789".getBytes());
raf2.write(strb.toString().getBytes());
raf1.close();
raf2.close();
补充
- 字符编码
- 编码表的由来 计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
- 常见的编码表
- ASCII:美国标准信息交换码。 用一个字节的7位可以表示。
- ISO8859-1:拉丁码表。欧洲码表 用一个字节的8位表示。
- GB2312:中国的中文编码表。
- GBK:中国的中文编码表升级,融合了更多的中文文字符号。
- Unicode:国际标准码,融合了多种文字。 所有文字都用两个字节来表示,Java语言使用的就是unicode
- UTF-8:最多用三个字节来表示一个字符。
- 编码:字符串字节数组
- 解码:字节数组字符串
- 转换流的编码应用 【可以将字符按指定编码格式存储;可以对文本数据按指定编码格式来解读;指定编码表的动作由构造器完成。】
【注】
本文章属个人整理学习使用,如有不当之处望联系指正或删除。
【学习视频来源】尚硅谷http://www.atguigu.com/