一、文件管理
1.1 概述
java中,对文件和目录使用java.io.File类来管理。
主要是针对文件或目录进行管理,包括属性信息的查看、文件或目录的删除、文件或目录的创建。
1.2 File类
-
File 类:文件、目录的一个抽象封装
-
文件路径分隔符:
- windows:使用 \ 分割,java 中要使用\\ ,分割属性;(不区分大小写)
- Unix 系统:/ 分割(区分大小写)
-
获取当前系统的分割符:
//获取分割属性 File.pathSeparator; File.pathSeparatorChar; //分割符 File.separator; File.separatorChar;
-
获取路径和名称
-
相对路径:参考一个坐标,与这个坐标的路径
. 代表当前目录 ..返回上一层 ../test 上一层目录下面的 test 目录 注意,现在拷贝的时候,尽量使用绝对路径来做,以后到框架阶段,用相对路径来做
-
绝对路径:从磁盘的跟目录一直到你的文件
//获取绝对路径 public String getAbsolutePath() //获取文件路径 public File getAbsoluteFile() //获取文件路径 public String getPath() //获取上级目录文件 public File getParentFile() //获取上级目录路径 public String getParent()
-
-
构造方法
//直接使用路径创建文件 pathname 可以是文件也可以是目录 public File(String pathname) //从目录对象下创建文件对象 public File(File parent, String child) //目录路径和文件路径分开传 public File(String parent, String child) //第一种构造方法 //磁盘上的文件夹(目录) //File file=new File("d:/aa"); //磁盘上的文件 //File file=new File("d:/aa/hello.txt"); //第二种构造方法 //File file=new File("d:/aa","bb"); //File file=new File("d:/aa","hello.txt"); //第三种构造方法 //File dir=new File("d:/aa"); //File file=new File(dir,"hello.txt");
-
方法
//创建新文件 public boolean createNewFile() //判断是否为目录 public boolean isDirectory() //判断是否为文件 public boolean isFile() //删除文件 public boolean delete() //判断文件是否存在 public boolean exists() //创建文件目录 public boolean mkdir() //创建多个目录 public boolean mkdirs() //获取当前层级的文件名 public String[] list() //重命名(可以做拷贝) public boolean renameTo(File dest)
1.2.1 创建文件
createNewFile()方法
//创建一个新的空文件
//如果文件不存在,就创建一个新的空文件,并返回true
//如果文件存在,则直接返回false,不会创建新的文件
try {
boolean b = file.createNewFile();
System.out.println("b = " + b);
} catch (IOException e) {
e.printStackTrace();
}
1.2.2 创建目录
mkdir()
mkdirs()
//创建目录的方法
//如果目录不存在,则创建目录并返回true,如果目录存在,则不创建,返回false
//mkdir的方法,只能创建当前一级目录,如果上级目录不存在,则创建失败,返回false
File dir=new File("d:/aa/yy/mm");
//boolean b = dir.mkdir();
//mkdirs方法,如果父目录不存在,会一并创建出来
boolean b = dir.mkdirs();
System.out.println("b = " + b);
1.2.3 文件删除
delete()
public class TestDelete {
public static void main(String[] args) throws IOException,InterruptedException {
//如果删除成功,返回true,否则返回false
//File file=new File("d:/aa/hello.txt");
// file.createNewFile();
// boolean delete = file.delete();
//创建一个临时文件
File tempFile = File.createTempFile("order_", ".txt");
//当虚拟机退出时,删除文件
tempFile.deleteOnExit();
System.out.println(tempFile.getAbsolutePath());
}
}
1.2.4 删除目录
在删除目录时,要求目录中没有其它的文件和文件夹,是一个空目录。
如果目录下面还有文件,使用循环删除文件处理
File dir=new File("d:/aa/yy");
//获取目录下的所有文件
File[] files = dir.listFiles();
for (File file : files) {
//System.out.println(file.getName());//获取文件名
file.delete();
}
boolean delete = dir.delete();
System.out.println("delete = " + delete);
如果目录下面还有目录,则需要使用递归方式删除目录处理
public static void deleteDir(File dir){
if (dir.isDirectory()) {
File[] files = dir.listFiles();
for (File file : files) {
//判断两种情况,一种file是文件,一种file是目录
if (file.isFile()) {//判断是不是文件 file.isDirectory()判断是不是目录
file.delete();
} else {
deleteDir(file);//递归完成删除
}
}
}
dir.delete();//删除空目录
}
1.2.5 其他方法
-
获取文件名
String name = file.getName(); System.out.println("file name:"+name);
-
获取目录名
String parent = file.getParent(); System.out.println("parent = " + parent); //获取hello.txt所在的目录 File parentFile = file.getParentFile(); System.out.println(parentFile.getName());
-
获取文件大小
long length = file.length(); System.out.println("length = " + length);
-
设置文件只读
file.setWritable(false);
-
测试应用程序对文件是否可执行\可读\可写
boolean b = file.canExecute(); System.out.println("b = " + b); boolean b1 = file.canRead(); System.out.println("b1 = " + b1); boolean b2 = file.canWrite(); System.out.println("b2 = " + b2);
-
获取文件路径操作
//获取的是在File类构造方法中给出的路径 String path = file.getPath(); System.out.println("path = " + path); //获取绝对路径 String path1=file.getAbsolutePath(); System.out.println("path1 = " + path1); //精准获取绝对路径方式,可去除在构造时给的相对路径的. String path2=file.getCanonicalPath(); System.out.println("path2 = " + path2);
-
获取文件最后修改时间戳
long l = file.lastModified(); System.out.println(l);
-
获取磁盘空间操作
//获取磁盘分区总容量 long totalSpace = file.getTotalSpace(); System.out.println("totalSpace = " + totalSpace); //获取可用分区大小 long freeSpace = file.getFreeSpace(); System.out.println("freeSpace = " + freeSpace); //获取JVM可用空间大小 long usableSpace = file.getUsableSpace(); System.out.println("usableSpace = " + usableSpace);
-
获取所有磁盘分区
File[] disks = File.listRoots(); for (File disk : disks) { System.out.println(disk); }
-
对文件进行重命名
//可以将文件命名到其他目录或盘符下 //可以实现文件剪切工作 //boolean b = file.renameTo(new File("c:/hello_1.txt")); //System.out.println("b = " + b);
-
获取文件是否可隐藏
boolean hidden = file.isHidden(); System.out.println("hidden = " + hidden);
二、IO流
2.1 简介
是一串连续不断的数据集合,对于程序中数据的输入和输出,都是以数据流的方式进行操作。
Input流:输入流(读流)
Outpub流:输出流(写流)
站在当前正在运行的程序的角度,来看数据是输入还是输出
2.2 IO流分类
- 按流的方向划分:输入流、输出流
- 按处理数据单位划分:字节流、字符流
- 按功能不同:节点流、处理流
2.3 四个主要流的API
- InputStream: 面向字节的输入流
- OutputStream: 面向字节的输出流
- Reader: 面向字符的输入流
- Writer: 面向字符的输出流
2.4 文件操作流
FileReader、FileWriter、FileInputStream、FileOutputStream
2.4.1 FileWriter
字符流实现原理:字节流+编码表
字符集:unicode字符集 UTF-8、UTF-16、UTF-32、GB2312、GBK、GB18030
使用FileWriter将字符串写入磁盘文本文件
执行方式1:
FileWriter writer=new FileWriter("d:/aa/hello.txt");
writer.write("欢迎使用java系统");
//writer.flush();
writer.write("\n");
writer.write("hello world");
//writer.flush();
writer.close();
执行方式2:
使用try with resource
try (FileWriter writer = new FileWriter("d:/aa/hello.txt")) {
for (int i = 0; i < 10000; i++) {
writer.write(i + "\t");
}
} catch (IOException e) {
e.printStackTrace();
}
常用构造方法
写数据的五种方法
2.4.2 BufferedWriter
FileWriter writer=new FileWriter("d:/aa/buffer.txt");
BufferedWriter bufferedWriter=new BufferedWriter(writer);
bufferedWriter.write("hello buffer");
//bufferedWriter.write("\r\n");
bufferedWriter.newLine();
bufferedWriter.write("new line content");
bufferedWriter.close();
BufferedWriter和FileWriter的区别
- FileWriter内部有8192个字节缓冲区
- BufferedWriter内部有8192个字符(16384个字节)
- FileWriter效率低,每次来一个字符,要查一次码表,BufferedWriter,缓冲区字符存满或者close、flush之后,才会进行查码表.
- 使用BufferedWriter可以指定缓冲区大小
- 如果频繁去写文件,最好使用BufferedWriter处理,如果只写一次,FileWriter就可以使用。
2.4.3 FileReade
//每次读取一个字符,返回读取到的字符
int read;
while ((read = reader.read())!=-1) {//没有达到文件尾
System.out.print((char) read);
}
char[] chars=new char[1024];
//将字符存入数组中,并返回一共读取到多少个字符
int len = reader.read(chars);
//System.out.println(read);
String str=new String(chars,0,len);
System.out.println(str);
char[] chars=new char[1024];
//读取指定长度
int len = reader.read(chars, 0, 20);
System.out.println(len);
String str=new String(chars,0,len);
System.out.println(str);
2.4.4 BufferedReader
FileReader reader=new FileReader("d:/aa/buffer.txt");
BufferedReader bufferedReader=new BufferedReader(reader);
String str;
while ((str=bufferedReader.readLine())!=null){
System.out.println(str);
}
bufferedReader.close();
2.4.5 文本文件复制的几种方式
public static void copy(File srcFile, File destFile) throws IOException {
FileReader reader = new FileReader(srcFile);
FileWriter writer = new FileWriter(destFile);
int c;
char[] chars = new char[1024];
while ((c = reader.read(chars)) != -1) {
writer.write(chars, 0, c);
}
reader.close();
writer.close();
}
public static void copyBuffer(String srcFile, String destFile) throws IOException {
FileReader reader = new FileReader(srcFile);
FileWriter writer = new FileWriter(destFile);
BufferedReader bufferedReader = new BufferedReader(reader);
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String str;
while ((str = bufferedReader.readLine()) != null) {
bufferedWriter.write(str);
bufferedWriter.newLine();
}
bufferedReader.close();
bufferedWriter.close();
}
public static void copyFile(String srcFile, String destFile) {
try (FileReader reader = new FileReader(srcFile);
FileWriter writer = new FileWriter(destFile);) {
reader.transferTo(writer);//对文件复制的操作 jdk10版本后新增
} catch (IOException e) {
e.printStackTrace();
}
}
三、文件字节流
InputStream、OutputStream
FileInputStream、FileOutputStream、BufferedInputStream、BufferedOutputStream
public class TestCopy {
public static void main(String[] args) throws IOException {
copyBuffer(new File("C:" + File.separator +"Users\\Administrator\\Desktop\\resource\\nacos-server-2.0.4.zip"), new File("d:/aa/nacos-server-2.0.4.zip"));
}
public static void copy(File srcFile, File destFile) throws IOException{
long t1 = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
int x;
while ((x = fis.read()) != -1) {
fos.write(x);
}
fis.close();
fos.close();
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
public static void copyBuffer(File srcFile, File destFile) throws IOException {
long t1 = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
BufferedInputStream bis=new BufferedInputStream(fis);
BufferedOutputStream bos=new BufferedOutputStream(fos);
byte[] bytes = new byte[1024];
int x;
while ((x = bis.read(bytes)) != -1) {
bos.write(bytes, 0, x);
}
bis.close();
bos.close();
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
}
四、对象的序列化与反序列化
4.1 对象的序列化
4.1.1 简介
把在内存当中的数据,要远程传输到网络上或者保存到硬盘上,需要将java对象转换为字节序列,可以转换成可传输的文件流
4.1.2 对象序列化的使用
ObjectOutputStream完成对象序列化操作 把java对象转换成文件
为什么要使用序列化?
- 在分布式系统中,数据的共享都需要实现序列化(redis)
- 服务的钝化,把我们不常用的对象存储到磁盘中,节约内存开销
- 使用 json 传输的时候
什么样的对象可以被序列化
- Serializable
- Externalizable
要求:必须实现序列化接口 Serializable
序列化代码
public class Student implements Serializable {
private String name;
private String gender;
private int age;
private String address;
//构造方法
//getter and setter
}
public static void main(String[] args) throws IOException {
Student student=new Student("李白","男",22,"北京");
FileOutputStream fos=new FileOutputStream("d:/aa/stu.xxx");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(student);
oos.close();
}
4.2 对象反序列化
把字节序列重新恢复为java对象的过程。(磁盘文件的数据恢复为java对象)
4.2.1 对象反序列化使用
ObjectInputStream完成对象反序列化操作
4.2.2 关于serialVersionUID
每次调用ObjectOutputStream来完成序列化时,都会有一个序列化编号,如果没有显式地生成,系统会自动生成一个。在做反序列化,系统会重新生成一个新的版本号,和之前的去比较,就会出现版本号不一致的错误。所以为了避免出现这种问题,一般都会显示地为序列化声明一个版本号。
local class incompatible: stream classdesc serialVersionUID =
185946447685475229, local class serialVersionUID = 8090034645353830909
调整idea2021.3.3在没有序列化版本号时,提示警告
反序列化代码
FileInputStream fis=new FileInputStream("d:/aa/stu.xxx");
ObjectInputStream ois=new ObjectInputStream(fis);
Student student =(Student) ois.readObject();
System.out.println(student);
ois.close();
4.3 序列化注意的问题
- 要序列化的对象,属性是一个引用类型,要求这处引用类型也是可序列化的,否则,这个对象不可以被序列化
- 使用transient有选择的序列化
- 对象的类名、属性名和属性值都会被序列化,方法、static属性、transient属性都不会被序列
4.4 使用Externalizable实现序列化
serializable使用的问题:
- 序列化过程是递归,相对较缓慢
- 对于需要禁止序列化的变量,需要使用transient关键字修饰,对于属性较多,又有一些不需要序列化的变量,整体操作较复杂
- 无法控制字段的序列化和反序列化的方式
- 序列化过程在处理对象时,不会调用构造方法,因此会造成构造方法内的逻辑缺失。
Externalizable:
是Serializable接口的子接口,如果要使用Externalizable来实现序列化,只需要让序列化类实现 Externalizable接口,并重写writeExternal和readExternal方法。可以实现自定义序列化过程。
Externalizable在序列化流程上,和Serializable是一样的。主要差别在于使用Externalizable可以重写 writeExternal和readExternal方法,精准控制。
Externalizable做反序列化时,会调用序列化类的构造方法,要求类中要有默认无参的构造方法。
Externalizable对于static属性和transient属性,没有Serializable的限制,都可以进行序列化。
4.4.1 序列化类
public class Emp implements Externalizable {
private static final long serialVersionUID = -3582785108027188930L;
private static String name;
private transient String gender;
private int age;
public Emp(){
System.out.println("无参的构造方法");
}
public Emp(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeUTF(gender);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
name=in.readUTF();
gender=in.readUTF();
age=in.readInt();
}
}
4.4.2 序列化和反序列化操作
public class TestExternal {
public static void main(String[] args) throws IOException,ClassNotFoundException {
serialize();
deserialize();
}
public static void serialize() throws IOException {
Emp emp=new Emp("李白","男",22);
/* emp.setName("李白");
emp.setGender("男");
emp.setAge(20);*/
ObjectOutputStream oos=
new ObjectOutputStream(new
FileOutputStream("d:/aa/emp.yyy"));
oos.writeObject(emp);
oos.close();
}
public static void deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois=
new ObjectInputStream(new FileInputStream("d:/aa/emp.yyy"));
Emp emp=(Emp)ois.readObject();
System.out.println(emp);
}
}
五、标准输入输出
System.in:标准输入,默认是对应键盘
System.out:标准输出,默认是对应显示器
System.err:标准错误输出,默认是对应显示器
从键盘获取输入值的方法:
//从键盘接收参数
//方法一:
Scanner scanner=new Scanner(System.in);
//方法二:
InputStream inputStream=System.in;
InputStreamReader inputStreamReader=new InputStreamReader(inputStream);
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
System.out.println("请输入姓名");
String str=bufferedReader.readLine();
System.out.println("您的姓名是:");
System.out.println(str);
java输出及错误输出
PrintStream out = System.out;
out.println("hello");*/
/*System.err.println("错误输出");
格式化输出
double d=3456.7841;
System.out.printf("%10.2f",d);
String name="李白";
int age=20;
System.out.printf("你好,%s,你的年龄是:%d",name,age);
通过改变标准输入和标准输出流,完成文件的复制
//将标准输入和输出进行扭转的操作
FileInputStream fis=new FileInputStream("d:/aa/hello.txt");
System.setIn(fis);//重新分配“标准”输入流
FileOutputStream fos=new FileOutputStream("d:/aa/hhhh.txt");
System.setOut(new PrintStream(fos));//重新分配"标准"输出流
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String str;
while ((str=br.readLine())!=null){
System.out.println(str);
}
br.close();
六、ByteArrayInputStream和ByteArrayOutputStream
-
FileInputStream、FileOutputStream、FileReader、FileWriter是存储在硬盘上的
硬盘上的资源java虚拟机是无权直接访问的,必须借助操作系统,java虚拟机借助完了之后要通知操作系统释放资源。
-
我们可以把源头换成电脑上的一块内存,既然是一块内存那么java就可以直接访问,因为是java虚拟机的一块内存。不用关闭(释放)
-
所有的东西都可以转成字节数组(字符串转成字节数组、任何一个数据(包括整数、包括浮点数、包括一个一个的对象都可以转成字节数组))
-
字节数组就为二进制了,方便网络上进行传输
-
文件可以无限制的往里面加内容,但是内存速度快、量小,所以字节数组不建议数据量特别大的操作
示例代码
public class TestByteArray {
public static void main(String[] args) throws IOException {
/*String str="abcdefg";
byte[] bytes=str.getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
int i = bais.read();
System.out.println((char)i);
bais.mark(0);
int i1 = bais.read();
System.out.println((char)i1);
int i2 = bais.read();
System.out.println((char)i2);
int i3 = bais.read();
System.out.println((char)i3);
bais.reset();
int i4 = bais.read();
System.out.println((char)i4);*/
ByteArrayOutputStream baos=new ByteArrayOutputStream();
while (baos.size()!=10){
baos.write(System.in.read());
}
byte[] b=baos.toByteArray();
ByteArrayInputStream bais=new ByteArrayInputStream(b);
int x;
while ((x=bais.read())!=-1){
System.out.println(Character.toUpperCase((char)x));
}
/*//
String s = baos.toString();//new String(b);
System.out.println(s);*/
/*for (byte b1 : b) {
System.out.println((char)b1);
}*/
}
}
五、DataInputStream和DataOutputStream 数据流
在执行文件存取一个对象的数据成员时,如果只操作一个或几个属性,并且数据成员都是java的基本数据类型或字符串,则不需要使用到对象序列化技术。可以使用DataInputStream和DataOutputStream 来写入或读取数据操作。
写入和读取的时候,不用自行判断读入数据何时停止,只需要使用对应的方法(readUTF或readInt等等), 就可以能够正确读入数据类型。
写入的顺序和读取的顺序要保持一致。
数据流:提供了可以读/写任何数据类型的方法
- DataInputStream 提供了 readXXX()方法
- DataOutputStream 提供了 writeXX(obj)
public class TestData {
public static void main(String[] args) throws IOException {
/* DataOutputStream dos=new DataOutputStream(new
FileOutputStream("d:/aa/x.xx"));
Student student=new Student();
student.setName("李白");
student.setAge(20);
dos.writeUTF(student.getName());
dos.writeInt(student.getAge());
dos.close();*/
DataInputStream dis=new DataInputStream(new
FileInputStream("d:/aa/x.xx"));
String name = dis.readUTF();
int age=dis.readInt();
System.out.println(name+"\t"+age);
dis.close();
}
}
public static void read(String path) throws IOException {
DataInputStream in = new DataInputStream(new FileInputStream(path));
System.out.println(in.readInt());
System.out.println(in.readUTF());
in.close();
}
public static void write(String path) throws IOException {
DataOutputStream out = new DataOutputStream(new FileOutputStream(path));
out.writeInt(123123);
out.writeUTF("你好,这里是上云教学课");
out.close();
}