java.io包
java.io包下,是Java对文件的io操作的类库,由相关interface,class,exception,error构成
File
简介:
-
在Java.io.file 是对文件和路径的抽象表示,主要用于对文件的创建,查找,删除等操作。
-
IO大多数情况下就是对文件的操作,所以File也是至关重要,在File类提供了丰富的方法来处理和文件或目录相关的操作,如创建和删除文件、创建和删除文件夹以及通过和其他类配合使用实现文件的复制和移动等。
-
File的构造方法
File(File parent, String child)
创建从一个家长的抽象路径名和一个孩子的路径字符串的新 File实例。
File(String pathname)
通过将给定的路径名的字符串转换成一个抽象路径名创建一个新的 File实例。
File(String parent, String child)
创建从父路径名的字符串和一个孩子的一个新的 File实例文件。
File(URI uri)
通过将给定的 file: URI到一个抽象路径名创建一个新的 File实例。 -
File的静态成员方法;
public static final char separatorChar
系统依赖的默认名称分隔符字符。这场被初始化为包含系统属性 file.separator值的第一个字符。UNIX系统上,这个字段的值是 ‘/’;微软Windows系统是 ‘\’。 -
public static final char pathSeparatorChar
系统依赖路径分隔符字符。这场被初始化为包含系统属性 pathSeparator值的第一个字符,该字符用于分隔在一个序列作为一个给定的路径列表文件的文件名。在UNIX系统中,这个角色是 ‘:’;微软Windows系统是 ‘;’。 -
public static final String separator
系统依赖的默认名称分隔符字符,表示为方便的字符串。此字符串包含一个字符,即 separatorChar。 -
public static final String pathSeparator
系统依赖路径分隔符的字符,表示为方便的字符串。此字符串包含一个字符,即 pathSeparatorChar。 -
File的成员方法
获取功能:
1、public String getAbsolutePath() :返回此File的绝对路径名字符串。
2、public String getPath() :将此File转换为路径名字符串。
3、public String getName() :返回由此File表示的文件或目录的名称。
4、public long length() :返回由此File表示的文件的长度。
判断功能:
1、 public boolean exists() :此File表示的文件或目录是否实际存在。
2、 public boolean isDirectory() :此File表示的是否为目录。
3、public boolean isFile() :此File表示的是否为文件。
创建删除功能:
public boolean createNewFile() :文件不存在,创建一个新的空文件并返回true,文件存在,不创建文件并返回false。
public boolean delete() :删除由此File表示的文件或目录。
public boolean mkdir() :创建由此File表示的目录。
public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
注意
绝对路径:一个完整的路径,以盘符开头,例如F://aaa.txt。
相对路径:一个简化的路径,不以盘符开头,例如//aaa.txt//b.txt
1、路径是不区分大小写
2、路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠
文件遍历及递归
递归
-
直接递归:方法调用自己本身
-
间接递归:方法A调用方法B方法B调用方法C方法C调用方法A
-
注意:
-
递归必须有条件限制
-
递归次数不可以太多,栈溢出
-
构造方法不可以递归
-
@Test
public void recursion() throws IOException {
int run = run(9);
System.out.println(run);
}
public int run(int num){
num--;
if (num>0){
return num+run(num);
}else {
return 0;
}
利用递归遍历文件夹
@Test
public void recursionFile() {
File file = new File("D:"+File.separator+"java_file"+File.separator+"test02");
addFileList(file);
for (File file1 : fileList) {
logger.info(file1);
}
}
public static ArrayList<File> fileList = new ArrayList<>();
public void addFileList (File file){
if (file.isDirectory()){
File[] files = file.listFiles();
for (File file1 : files) {
if (file1.isDirectory()){
addFileList(file1);
}else {
fileList.add(file1);
}
}
}else {
fileList.add(file);
}
}
FileFilter
- @FunctionalInterface
public interface FileFilter
一个抽象路径名的过滤器。 一个函数式接口,可用lambda表达式去实现该方法。 - String[] list(FilenameFilter
filter)返回的字符串在该目录下的抽象路径名,满足指定的过滤器表示文件和目录命名数组。
@Test
public void fileFilter() {
File file = new File("D:"+File.separator+"java_file"+File.separator+"test02");
File[] files = file.listFiles((File f)->{return f.getName().endsWith(".txt");});
logger.info(Arrays.toString(files));
}
IO流
在文件的世界里,一切都是字节,我们所看到的数据文字图片不过就是一群0和1组成的数据。
IO流的分类
- Java的I/O系统涉及流的概念。一个读取字节序列的对象被称为输入流,一个可以写入字节序列的对象称为输出流。输出流和输入流是相对于程序本身而言的。
- 根据数据的流向分为:输入流 和 输出流。
输入流 :把数据从其他设备上读取到内存中的流。
输出流 :把数据从内存 中写出到其他设备上的流。 - 根据数据的类型分为:字节流 和 字符流。
字节流 :以字节为单位,读写数据的流。
字符流 :以字符为单位,读写数据的流。
字节输入流InputStream
- ByteArrayInputStream:允许带缓冲区的输入流,显然该类的功能就是允许将内存缓冲区作为输入流使用,它包装了FilterInputStream对象,把该对象的输入文件作为实际数据输入到ByteArrayInputStream流对象的缓冲区中。FilterInputStream包含几个子类,分别是LineNumberInputStream、DataInputStream、BufferedInputStream和PushbackInputStream。
- FileInputStream:从文件中读取数据,其构造函数参数可以是文件对象、字符串或FileDescriptor对象。通过FilterInputStream流类的包装对程序提供读数据的接口。
- PipedInputStream:该类产生输入管道流,该流又产生写入输出管道流的数据,这样就实现了管道化通信,通常在多线程编程中作为线程间通信的实现方式。
- SequenceInputStream:该流把多个输入流对象链接成一个输入流,该类的构造函数参数是两个InputStream对象或一个包含InputStream对象的枚举器对象。
- StringBufferInputStream:该类的功能是把String转换成输入流。应用程序创建一个该类的输入流,把构造函数的参数中的String数据作为程序的输入。构造函数为StringBufferInputStream(String str)。
- ObjectInputStream:对象输入流读取输入流对象中的各种对象类型数据,用来处理序列化对象的传输。
- FilterInputStream:该类继承了抽象类InputStream,实现了抽象方法read()。
- LineNumberInputStream:该类实现对输入流中的行数的计数,对输入流增加了行号,该类的构造函数是InputStream流对象。
- DataInputStream:数据输入流允许程序从底层的输入流中读取基本类型的数据,如int型、float型和Byte型等。该类包含了读取基本类型的所有方法,如readByte()、readBoolean()、readChar()、 readDouble()和readFloat()等。其构造函数参数为一个InputStream流对象。
- BufferedInputStream:带缓冲的输入流,可以把数据先放入缓冲区,防止每次读取时进行实际的读写操作,减少了数据实际访问的时间开销。数据以字节数组的形式存放在缓冲区中,该类提供了read() 方法,每次从输入流读取一个字节数据。构造函数的参数为InputStream对象。
- PushbackInputStream:该类一般不被程序员使用,是为Java编译器而设计的。
public class InputStreamTest {
Logger logger = Logger.getLogger(InputStreamTest.class);
private final String PATH_FILE = "D:"+File.separator+"java_file"+File.separator+"newFile"+File.separator+"test.txt";
@Test
public void testInputStream() throws IOException {
File file = new File(PATH_FILE);
FileInputStream inputStream = new FileInputStream(file);
//字节输入流
BufferedInputStream buffer = new BufferedInputStream(inputStream);
//字节缓冲流
byte [] bytes = new byte[1024];
int len = 0;
while ((len=buffer.read(bytes))!=-1){
logger.info(new String(bytes,0,len));
}
//释放资源
inputStream.close();
buffer.close();
}
}
字节输出流OutputStream
- ByteArrayOutputStream:在向输出流写入数据前先将数据缓冲处理,其缓冲区大小通过构造函数的参数设置。
- FileOutputStream:通过该输出流把数据写入文件,构造函数的参数可以是字符串、文件对象、文件或FileDescriptor对象。
- ObjectOutputStream:对象输出流把各种对象类型数据写入输出流文件中,用来处理序列化对象的传输。
- PipedOutputStream:与管道输入流(PipedInputStream)对应,任何管道输入流都通过输出流写出。两者搭配使用实现管道通信。
- FilterOutputStream:该类继承了抽象类OutputStream,实现了抽象方法write()。
下面3个类继承自FilterOutputStream(过滤器输出流):
- DataOutputStream:数据输出流同数据输入流(DataInputStream)对应,实现把基本类型数据写入输出流。该类提供了把数据写入输出流的所有方法,如writeBoolean(Boolean v)、writeByte(int v)、writeBytes(String s)、writeChar(int v)和wirteChars(String s)等。
- BufferedOutputStream:该类实现输出数据时首先进行数据缓冲,该类提供了flush()方法实现清空数据缓冲区。该类的构造函数参数为输出流对象或输出流对象和输出缓冲区大小,即:
BufferedOutputStream(OutputStream out)和BufferedOutputStream(OutputStream out,int size)
- PrintStream:该类的目的是实现Java基本数据类型的格式化输出。
public class OutPutStreamTest {
Logger logger = Logger.getLogger(InputStreamTest.class);
private final String PATH_FILE = "D:"+ File.separator+"java_file"+File.separator+"newFile"+File.separator+"test.txt";
@Test
public void testOutputStream() throws IOException {
File file = new File(PATH_FILE);
FileOutputStream outputStream = new FileOutputStream(file,true);//true表示可以续写
BufferedOutputStream buffer = new BufferedOutputStream(outputStream,1024);
String str = "你好,输出流";
for (int i = 0; i < 10; i++) {
buffer.write("\r\n".getBytes());//换行
buffer.write(str.getBytes(StandardCharsets.UTF_8));
buffer.flush();//真正写入
logger.info(true);
}
outputStream.close();
buffer.close();
}
}
字符输入流Reader
-
BufferedReader:该类完成从字符输入流中读取文本,且缓冲读到的字符。用户可以指定缓冲区的大小,或者使用默认的缓冲区大小。为了实现高效的读取文本建议read()操作开销高的Reader,如
FileReader采用BufferedReader包装,将缓冲FileReader指定文件的输入。如果不使用缓冲机制,则每次调用read()或readLine()都会导致从文件中读取字节而后直接转换成字符返回退出方法,显然这样的效率很低。 -
CharArrayReader:该类继承了Reader抽象类,实现用做字符输入流的字符缓冲区读取字符。
-
FileReader:该类继承自InputStreamReader类,读取字符流,其构造函数默认采用了合适的字符编码和字节缓冲区大小。该类通过指定一个File对象、一个文件名或指定FileDescriptor的条件下创建一个 新FileReader对象。
-
FilterReader:该类是个抽象类,用于读取已过滤的字符流。
-
InputStreamReader:该类提供了字节流向字符流的转换功能,并读取字符流。每次调用该类的read()方法都会从输入流中读取一个或多个字节。为了提高字节到字符的转换效率,可以使用缓冲机制从 输入流中读取更多的字节后再进行转换,如可以把InputStreamReader类包装在BufferedReader中以提高字符读取的效率。
-
LineNumberReader:跟踪行号的缓冲字符输入流。该类提供了两个方法void setLineNumber(int)和int getLineNumber(),分别用于设置和获取当前行号。
-
PipedReader:字符输入流,通过管道的方式从PipedWriter流读字符流。
-
PushbackReader:该类读取字符流,允许字符退回到流中的字符流。
-
StringReader:从源数据流为字符串流的源读取字符串。
字符输出流Writer
- BufferedWriter:该类将文本写入字符流,将字节转换为字符同时缓冲读取每个字符,提供单个字符、数据和字符串的写入。该类提供两种构造函数指定缓冲区大小,一种是指定大小,另一种是采用默认值。一般情况下采用默认值就足够使用。在使用Writer类向文件写入数据时最好使用BufferedWriter包装开销大的write操作,如FileWriter类。这样可以缓冲字节或字符流,而不是把字符转换成字节后立即写入到文件(这样造成不断地读写数据,显然效率低)。
- CharArrayWriter:该类作为Writer的字符缓冲区。该缓冲区随着向流中写入数据而自动增长。该类的构造函数提供两种形式,一种是默认的形式,另一种是有整型参数的形式。第二种构造函数构造指 定初始容量的CharArrayWriter。
- FileWriter:该类把字符写入文件,文件位置在构造函数中指定,其构造函数接受默认的字符编码和默认的字节缓冲区大小。该类用于写入字符流,如果要写入的是原始字节流,则参考 FiltOutputStream流来实现。
- FilterWriter:该类是一个抽象类,用于写入已经过滤的字符流。该类提供了写字符流的方法write,其子类必须重写这些方法,也可以提供其他的方法或属性。
- OutputStreamWriter:该类提供了字符流向字节流的转换功能,并向流中写入字符。每次调用该类的write()方法都会导致向输出流写入一个或多个字节。为了提高字节到字符的转换效率,可以使用缓 冲机制。如可以把OutputStreamReader类包装在BufferedReader中以提高字符读取的效率。
- PrintWriter:该类完成向文本输出流打印格式化数据的形式,它实现了在PrintStream中的所有print()方法,但是不包含写入原始字节的方法,此时要求程序使用未编码的字节流进行写入。注意虽然该类 的某些构造函数可能抛出异常,但是该类中的方法不会抛出I/O异常。
- PipedWriter:管道Writer类创建管道通信的输出流,通过该输出流把数据写入文件,和管道Reader相对应建立起管道通信。
- StringWriter:显然该类是一个字符流,利用其字符串缓冲区中的输出字符构造字符串。该类的构造函数有两种,一种是创建默认初始字符串缓冲区大小的新字符串Writer,另一种是创建具有指定初始 字符串缓冲区大小的新字符串Writer。
对象序列化
(1)可以把对象写入文本文件或者在网络中传输
(2)如何实现序列化呢?
让被序列化的对象所属类实现序列化接口。该接口是一个标记接口。没有功能需要实现。
(3)注意问题:
把数据写到文件后,在去修改类会产生一个问题。
如何解决该问题呢?
在类文件中,给出一个固定的序列化id值。而且,这样也可以解决黄色警告线问题
- Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
序列化操作
- 该类必须实现java.io.Serializable 接口,Serializable是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException 。
- 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
@Test
public void testObjectOutputStream() throws IOException {
File file = new File(PATH_FILE);
OutputStream os = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(os);
People people = new People("jack",18,"beijing");
//写出对象
oos.writeObject(people);
oos.flush();
os.close();
oos.close();
}
反序列化操作
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。
@Test
public void testObjectInputStream() throws IOException, ClassNotFoundException {
File file = new File(PATH_FILE);
InputStream is = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(is);
//获取对象
Object readObject = ois.readObject();
People people = (People) readObject;
System.out.println(people.getName());
ois.close();
is.close();
}
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:
1、该类的序列版本号与从流中读取的类描述符的版本号不匹配
2、该类包含未知数据类型
2、该类没有可访问的无参数构造方法
Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
public class People implements Serializable {
private static final long serialVersionUID = 10086L;//给该类一个版本号
private String name;
private Integer age;
private transient String address;
}
转换流
- 转换流java.io.InputStreamReader,是Reader的子类,从字面意思可以看出它是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
- 转换流java.io.OutputStreamWriter ,是Writer的子类,字面看容易混淆会误以为是转为字符流,其实不然,OutputStreamWriter为从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
打印流
- 在控制台打印输出,是调用print方法和println方法完成的,各位用了这么久的输出语句肯定没想过这两个方法都来自于java.io.PrintStream类吧,哈哈。该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
- 分类
字节打印流PrintStream,字符打印流PrintWriter - 特点:
A:只操作目的地,不操作数据源
B:可以操作任意类型的数据
C:如果启用了自动刷新,在调用println()方法的时候,能够换行并刷新
D:可以直接操作文件
Properties属性类
Properties类并不在IO包下,properties类经常和io流的联合一起使用。
(1)是一个集合类,Hashtable的子类
(2)特有功能
- A:public Object setProperty(String key,String value)
- B:public String getProperty(String key)
- C:public Set stringPropertyNames()
(3)和IO流结合的方法
-
把键值对形式的文本文件内容加载到集合中
public void load(Reader reader)
public void load(InputStream inStream) -
把集合中的数据存储到文本文件中
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
java.util.Properties 继承于Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。
public class ProDemo {
public static void main(String[] args) throws FileNotFoundException {
// 创建属性集对象
Properties pro = new Properties();
// 加载文本中信息到属性集
pro.load(new FileInputStream("Properties.txt"));
// 遍历集合并打印
Set<String> strings = pro.stringPropertyNames();
for (String key : strings ) {
System.out.println(key+" -- "+pro.getProperty(key));
}
}
}
文件操作
创建文件夹(目录)
File类提供了丰富的接口函数供用户调用。创建目录是文件操作中经常遇到的情形,目录提供了文件存放的位置,用户可以根据需要在磁盘空间上建立目录。
File file = new File("D:"+File.separator+"java_file"+File.separator+"newFile");
if (!file.exists()){
file.mkdir();
}
创建文件
在Java的File类中创建新文件只需要调用该类的一个方法createNewFile(),但是在实际操作中需要注意一些事项,如判断文件是否存在,以及如何向新建文件中写入数据等。
@Test
public void createFiles(){
File file = new File(PATH_FILE);
if (!file.exists()){
//不存在则创建
file.mkdir();
}
try {
FileOutputStream fis = new FileOutputStream(file);//创建文件
BufferedOutputStream bos = new BufferedOutputStream(fis);//建立其缓冲流
OutputStreamWriter ops = new OutputStreamWriter(bos);//通过转换流转化字符
ops.write("这是测试案例");
//释放资源
ops.close();
bos.close();
fis.close();
logger.info("OK");
} catch (IOException e) {
e.printStackTrace();
}
}
文件复制
@Test
public void copyFiles() throws IOException {
String path = "D:"+ File.separator+"java_file"+File.separator+"newFile"+File.separator+"test.txt";
File file = new File(path);
String copyPath = "D:"+ File.separator+"java_file"+File.separator+"newFile"+File.separator+"copy"+File.separator+file.getName();
File copyFile = new File(copyPath);
if (!copyFile.exists()){
copyFile.createNewFile();
}
if (file.exists()){
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(copyFile);
byte [] bytes = new byte[1024];
int len = 0;
while ((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fis.close();
fos.close();
logger.info("OK");
}
}
文件夹删除
- 在Java的File类中删除文件夹,需要首先删除掉文件夹中的文件,再删除空文件夹。删除空文件夹的方法与删除文件的方法相同,所以关键是如何实现删除文件夹下的所有文件。已经知道如何删除一个文件,可以想象欲删除一个目录下的所有文件只要获得该文件的目录和文件名,使用一个循环调用来依次删除文件夹中的文件即可。
@Test
public void deleteFiles(){
String path = "D:"+ File.separator+"java_file"+File.separator+"newFile";
File file = new File(path);
delete(file);
}
public void delete(File file){
if (file.isDirectory()){
File[] files = file.listFiles();
for (File file1 : files) {
if (file1.isDirectory()){
delete(file1);
}else {
System.out.println(file1.delete());
}
}
if (files.length==0){
file.delete();
}
}else {
System.out.println(file.delete());
}
}
除此之外关于IO流的操作还有很多…多看文档