一、File类
public static void test(){
//File src = new File("C:/Users/yj/Desktop/1.txt");
File src = new File("1.txt");
System.out.println(src.getName());
System.out.println(src.getPath());//如果是绝对路径,否则返回相对路径
System.out.println(src.getAbsolutePath());//返回绝对路径
System.out.println(src.getParent());//返回上一级目录,如果是相对路径,没有上一级返回空
System.out.println("是否是绝对路径: " + src.isAbsolute());
}
//判断信息
public static void test1(){
//File src = new File("1.txt");
File src = new File("C:/Users/yj/Desktop/1.txt");
System.out.println("文件是否存在:" + src.exists());
System.out.println("文件是否可写: " + src.canWrite());
System.out.println("是不是文件夹: " + src.isDirectory());
System.out.println("是不是文件: " + src.isFile());
System.out.println("文件的长度: " + src.length());//只有文件才能读取长度
}
说明:以上是File
类的一些常用方法。下面我们看File
中对目录的操作方法:
mkdir()创建目录,必须确保父目录存在,否则创建失败
mkdirs(),创建目录,如果父目录不存在,也一同创建
list()以字符串形式列出所有文件和目录名字(不包含路径)
listFiles()以字符串形式列出所有文件和目录名字(包含完整路径)
static listRoots()根路径
二、流(stream)
2.1 概念
程序与文件、数组、网络连接、数据库都是使用流进行交互,但是都是以程序为中心。
2.2 分类
- 1、根据流向:输入流和输出流
- 2、根据数据类型:字节流(二进制,可以处理任何数据)和字符流(文本,只能处理纯文本)
- 3、根据功能:节点流,就是离数据源最近的,包裹源头的;处理流,是增强功能,提高性能的。
2.3 字符流和字节流
2.3.1 字节流
输入流:InputStream
(抽象类)(常用实现类FileInputStream
)
public int read(byte[] b)
public int read(byte[] b, int off, int len)
public void close()
输出流:OutputStream
(抽象类)(常用实现类FileOutputStream
)
public void write(byte[] b)
public void write(byte[] b,int off,int len)
public void flush()
public void close()
下面看使用字节流读取文件的基本步骤:
package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
//文件的读取
public class Demo01 {
public static void main(String[] args) {
//1、建立联系
File src = new File("D:/FQ/parent/FeiqCfg.xml");
//2、选择流
InputStream is = null;//提升作用域
try {
is = new FileInputStream(src);
//3、操作,不断读取
byte[] buffer = new byte[1024];//相当于一个缓冲数组,即每次读取的字节数不超过1024个
int len = 0;//接收实际读取的大小
//循环读取,每次读取1024个字节
while((len = is.read(buffer)) != -1){
//输出,需要将字节数组转换成字符串
String info = new String(buffer , 0, len);
System.out.println(info);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件不存在");
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取文件失败");
}finally{
//4、释放资源
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("关闭输入流失败");
}
}
}
}
}
下面我们看使用字节流写入文件的基本步骤:
package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
//写出文件
public class Demo02 {
public static void main(String[] args) {
//1、建立联系
File dest = new File("D:/FQ/parent/test.txt");
//2、选择流
OutputStream os = null;
try {
os = new FileOutputStream(dest, true);//写出文件,以追加的形式
//3、操作
String str = "something is so difficult \r\n";
//字符串转换成字节数组
byte[] data = str.getBytes();
os.write(data, 0, data.length);
os.flush();//刷新, 如果使用close方法会默认调用此方法
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件未找到");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件写出失败");
}finally{
try {
if(os != null){
os.close();//释放资源
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("关闭输出流失败");
}
}
}
}
说明:写入文件的基本步骤较为简单,首先将要写入的内容转换成字节数组,然后使用write
方法一次性写入即可。同时我们在关闭流之前最好手动刷新缓存,虽然关闭流的时候会自动刷新。我们将读取和写入结合起来就可以实现文件的拷贝了:
public static void fileCopy02(String srcPath, String destPath){
// 1、创建源(文件必须存在)+目的地(文件可以不存在)
File src = new File(srcPath);
File dest = new File(destPath);
// 2、选择流
try (InputStream is = new FileInputStream(src);
OutputStream os = new FileOutputStream(dest);) {
// 3、文件拷贝=读取+写出
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {// 读取
// 写出
os.write(buffer, 0, len);
}
os.flush();
}catch (Exception e) {
e.printStackTrace();
}
}
说明:这里要注意的是文件源必须存在,而目的地可以不存在,同时这里我们使用了1.7的新特性,即try with
,内部会帮我们关闭流。
2.3.2 字符流
输入流:Reader
(抽象类)(常用实现类FileReader
)
public int read(char[] cbuf)
public abstract int read(char[] cbuf,int off,int len)
public abstract void close()
输出流:Writer
(抽象类)(常用实现类FileWriter
)
public void write(char[] cbuf)
public abstract void write(char[] cbuf,int off,int len)
public abstract void close()
public void write(String str,int off,int len)
下面我们给出使用字符流读取文件的基本步骤:
package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
//文件的读取,使用字符流
public class Demo04 {
public static void main(String[] args) {
//创建源
File src = new File("D:/FQ/parent/test.txt");
//选择流
Reader reader = null;
try {
reader = new FileReader(src);
char[] buffer = new char[1024];
int len = 0;
try {
while((len = reader.read(buffer)) != -1){
//字符数组转换成字符串
String str = new String(buffer, 0, len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件读取失败");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("源文件不存在");
}finally{
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
说明:这里可以看到和使用字节流读取基本一样,只是使用的是字符数组。下面看使用字符流写入文件:
package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
//使用字符流写出文件
public class Demo05 {
public static void main(String[] args) {
//创建源
File src = new File("F:/FQ/parent/test.txt");
//选择流
Writer writer = null;
try {
writer = new FileWriter(src, true);//第二个参数为true表示追加
//写出
String msg = "狗蛋打了铁柱一顿";
writer.write(msg);//这里可以直接写字符串
writer.append("狗蛋");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
说明:这里要注意的是在字符流没有关闭的情况下我们可以直接使用append
方法进行写入。使用字符流进行文件的拷贝这里就不给出了。
2.4 缓冲流
缓冲流主要用于增强功能,提高性能。处理流一定要在节点流之上。我们推荐使用缓冲流,而不是直接使用节点流。
2.4.1字节缓冲流
针对字节的缓冲流,主要实现类有:
BufferedInputStream:没有新增方法
BufferedOutputStream: 没有新增方法
下面我们看使用的基本步骤:
package cn.itcast.day146.stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/*
* 文件拷贝,使用缓冲字节流,建议使用,提高性能
* 只是在节点流上面包上一层缓冲流
* */
public class Demo06 {
public static void fileCopy(String srcPath, String destPath) throws FileNotFoundException, IOException {
File src = new File(srcPath);
File dest = new File(destPath);
if(!src.isFile()){
System.out.println("只能拷贝文件");
throw new IOException("只能拷贝文件");
}
//如果目的地为已经存在的文件夹,不能建立与文件夹同名的文件或文件夹,当然如果是file则会覆盖
if(dest.isDirectory()){
System.out.println("能建立与文件夹同名的文件或文件夹");
throw new IOException("能建立与文件夹同名的文件或文件夹");
}
//2、选择流
InputStream is = new BufferedInputStream(new FileInputStream(src));
OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
//3、文件拷贝=读取+写出
byte[] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1){//读取
os.write(buffer, 0, len);//写出
}
os.flush();
//关闭:先打开的后关闭
os.close();
is.close();
}
}
说明:可以看到使用步骤和之前的字节流基本是一致的,只是我们使用缓冲流对字节流进行了包装,用以提高读取和写入的性能。
2.4.2 字符缓冲流
针对字符的缓冲流,主要实现类有:
BufferedReader:新增方法:readLine()
BufferedWriter:新增方法:newLine()
下面我们看基本的使用步骤:
package cn.itcast.day146.stream;
//文件拷贝,使用缓冲字符流
//新增方法
public class Demo07 {
public static void main(String[] args) {
File src = new File("D:/FQ/parent/test.txt");
BufferedReader reader = null;//使用新增方法不能有多态,即定义的时候需要使用缓冲字符流
File dest = new File("D:/FQ/parent/test01.txt");
BufferedWriter writer = null;//使用新增方法不能有多态,即定义的时候需要使用缓冲字符流
try {
//包装
reader = new BufferedReader(new FileReader(src));
writer = new BufferedWriter(new FileWriter(dest));
//使用之前的方式
// char[] buffer = new char[1024];
// int len = 0;
// while((len = reader.read(buffer)) != -1){
// writer.write(buffer, 0, len);
// }
//使用新增方法进行读取,使用新增方法不能有多态,即定义的时候需要使用缓冲字符流
String line = null;
while((line = reader.readLine()) != null){//一行一行的读取
writer.write(line);//写出
writer.newLine();//加上换行符
}
writer.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
说明:基本的使用步骤还是和之前一样,当然我们推荐使用新方法,这样比较简单。
2.5 转换流
将字节流转换为字符流,用于处理乱码问题。这里我们先看乱码产生原因:
- 1)编码与解码的字符集不统一
- 2)字符缺少,长度丢失
常用实现类:
InputStreamReader
OutputStreamWriter
例如:
//乱码原因一:解码和编码的字符集不统一
public static void test1() throws UnsupportedEncodingException {
String str = "中国";//utf-8
//编码 char-->二进制
byte[] data = str.getBytes();
//解码和编码的字符集统一
System.out.println(new String(data));//没有乱码
data = str.getBytes("gbk");//设定编码字符集
//解码和编码的字符集不统一
System.out.println(new String(data));//有乱码
//编码
byte[] data2 = "中国".getBytes("UTF-8");
//解码
str = new String(data2, "UTF-8");
System.out.println(str);
}
//乱码原因二:字节数不完整导致乱码
public static void test2(){
String str = "中国";
byte[] data = str.getBytes();
//字节数不完整导致乱码
System.out.println(new String(data, 0, 3));
}
下面我们看转换流的基本使用步骤:
/*转换流:字节-->字符
* 1、输出流 OutputStreamWriter编码
* 2、输入流 InputStreamReader解码
* */
public class Demo09 {
public static void main(String[] args) throws IOException {
//可以在转换流中指定解码字符集,注意:这里的字符集就是我们要读取文件的编码字符集
BufferedReader br = new BufferedReader(
new InputStreamReader(/*这是一个转换流,将字符流和字节流联系起来*/
new FileInputStream(new File("D:/FQ/parent/test.txt")), "UTF-8"));//底层是一个字节流
//写出文件
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(new File("D:/FQ/parent/test01.txt")), "UTF-8"));
char[] buffer = new char[1024];
int len = 0;
while((len = br.read(buffer)) != -1){
bw.write(buffer, 0, len);
}
bw.flush();
/*String info = null;
while(null != (info = br.readLine())){
System.out.println(info);
}
br.close();*/
br.close();
bw.close();
}
}
说明:这里我们知道转换流是将字节流转换成字符流,所以底层包装的是字节流,当然这里我们还使用了缓冲流对字符流进行了包装。这里我们使用字节流读取,在读取的时候进行解码,即将内容转换成了字节,而同时我们在写入的时候也是将字符转换成字节再写入,这样就不会出现乱码的问题。
2.6 其他流
2.6.1 字节流
这里我们看字节数组流,可将此流看作在其他电脑的内存中,所以我们不用关闭,关闭也是无效的。其常用的实现类有:
ByteArrayInputStream
ByteArrayOutputStream
下面给出基本的使用步骤:
//输入流操作与文件输入流操作一致
public static void read(byte[] src) throws IOException{
//String msg = "输入流操作与文件输入流操作一致";
//byte[] src = msg.getBytes();
//选择流
InputStream is = new BufferedInputStream(new ByteArrayInputStream(src));
//操作
byte[] buffer = new byte[1024];
int len = 0;
while(-1 != (len = is.read(buffer))){
System.out.println(new String(buffer, 0, len));
}
is.close();
}
//输出流与文件输出流有些不同,因为有新增方法,不能使用多态
public static byte[] write() throws IOException{
//目的地
byte[] dest;
//选择流,不同点
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//写出
String msg = "输入流操作与文件输入流操作一致";
byte[] info = msg.getBytes();
bos.write(info, 0, info.length);
//获取数据
dest = bos.toByteArray();
bos.close();
return dest;
}
说明:这里我们只是给出了读写的方法,并没有说从哪里读取,写到哪里去,这是因为在使用此类流的时候需要明白,比如在文件拷贝的时候,是按照这样一个步骤进行,首先是将文件内容拷贝到程序中,之后再从程序中拷贝到文件中,下面我们给出例子:
package cn.itcast.day146.stream;
import java.io.*;
/*
* 使用字节数组流读取写出文件
* 1、文件-->程序-->字节数组
* 文件输入流+字节数组输出流
*
* 2、字节数组-->程序-->文件
* 字节数组输入流+文件输出流
* */
public class Demo11 {
public static void main(String[] args) throws IOException {
byte[] data = getBytesFromFile("D:/FQ/parent/test.txt");
toFileFromByteArray(data, "D:/FQ/parent/test01.txt");
}
//文件-->程序
public static byte[] getBytesFromFile(String stcPath) throws IOException{
//创建源(文件)和目的地(字节数组)
File src = new File(stcPath);
byte[] dest = null;
//选择流
//文件输入流
InputStream is = new BufferedInputStream(new FileInputStream(src));
//字节数组输出流,不能使用多态
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//操作:不断读取文件,写出到字节数组流中
byte[] buffer = new byte[1024];
int len = 0;
while(-1 != (len = is.read(buffer))){
//写出到字节数组流中
bos.write(buffer, 0, len);
}
bos.flush();
//获取数据
dest = bos.toByteArray();//将字节数组流中的数据写到一个字节数组中
bos.close();
is.close();
return dest;
}
public static void toFileFromByteArray(byte[] src, String destPath) throws IOException{
//创建源,即src
//创建目的地, 即dest
File dest = new File(destPath);
//选择流
//字节数组输入流
InputStream is = new BufferedInputStream(new ByteArrayInputStream(src));
//文件输出流
OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
//不断读取
byte[] buffer = new byte[1024];
int len = 0;
while(-1 != (len = is.read(buffer))){
os.write(buffer, 0, len);//写出到文件中
}
os.flush();
os.close();
is.close();
}
}
说明:这里要注意的是此类流由于数组的大小有限,所以只适合读取少量数据的文件。同时我们不管在读取还是写的时候一定是以程序为中心,这样就不至于分不清到底是使用输入流还是输出流了。
2.6.2 处理流
这里我们先给出DataInputStream
和DataOutputStream
两个流,这个类型的流可以保留数据和数据类型,比如可以直接读取String
类型数据,并保留其类型。但是这个类有个限制就是读取和写入的顺序必须一致。下面给出基本的使用步骤:
public static void write(String destPath) throws IOException{
double point = 2.5;
long num = 100L;
String str = "数据类型";
//创建源
File dest = new File(destPath);
//选择流 DataInputStream
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(dest)));
//操作,写出,同时写出的顺序和读取的顺序必须一致
dos.writeDouble(point);
dos.writeLong(num);
dos.writeUTF(str);
dos.flush();
dos.close();
}
//读取数据加类型
public static void read(String srcPath) throws IOException{
//创建源
File src = new File(srcPath);
//选择流
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(src)));
//操作,读取的顺序必须和写出的数据一致, 必须存在才能读取
double num1 = dis.readDouble();
long num2 = dis.readLong();
String str = dis.readUTF();
System.out.println(str);
}
说明:这个类型的流了解即可。这里是对基本数据类型的处理流,下面我们看引用类型的处理流。
反序列化(输入流)ObjectInputStraem : readObject()
序列化(输出流)ObjectOutputStream : writeObject()
首先我们给出一个javabean
:
package cn.itcast.day159.stream;
import java.io.Serializable;
//序列化的类
public class Employee implements Serializable{
private transient String name ;//此属性不进行序列化
private double salary ;
public Employee() {
super();
}
public Employee(String name, double salary) {
super();
this.name = name;
this.salary = salary;
}
//getters and setters method
}
下面我们看基本的操作步骤:
package cn.itcast.day159.stream;
import java.io.*;
import java.util.Arrays;
public class Demo01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//write("F:/FQ/parent/data.txt");
read("F:/FQ/parent/data.txt");
}
//序列化
public static void write(String destPath) throws IOException{
Employee employee = new Employee("Tom", 10000);
int[] arr = {1,2,3,4,5};//数组内部实现了序列化接口
//创建源
File dest = new File(destPath);
//选择流 DataInputStream
ObjectOutputStream dos = new ObjectOutputStream(new BufferedOutputStream(
new FileOutputStream(dest)));
//操作,写出,同时写出的顺序和读取的顺序必须一致
dos.writeObject(employee);
dos.writeObject(arr);
dos.flush();
dos.close();
}
//反序列化
public static void read(String srcPath) throws IOException, ClassNotFoundException{
//创建源
File src = new File(srcPath);
//选择流
ObjectInputStream dis = new ObjectInputStream(
new BufferedInputStream(new FileInputStream(src)));
//操作,读取的顺序必须和写出的数据一致, 必须存在才能读取
Object obj = dis.readObject();
if(obj instanceof Employee){
Employee e = (Employee) obj;
//这里名字是取不到的,因为我们没有序列化此属性
System.out.println("名字: " + e.getName() + ", 薪水: " + e.getSalary());
}
int arr[] = (int[]) dis.readObject();
System.out.println(Arrays.toString(arr));
}
}
说明:我们在类中使用关键字transient
限定那些字段不进行序列化,同时在测试的时候注意写入在进行读取。
注意:
- 1、反序列化必须和序列化顺序一致,先序列化后反序列化
- 2、不是所有的对象都可以序列化,必须实现接口
java.io.Serializable
- 3、不是所有的属性都需要序列化,不想序列化的属性使用
transient
标识