1. 程式人生 > 實用技巧 >File類、遞迴

File類、遞迴

主要內容

  • File類

  • 遞迴

第一章 File類

1.1 概述

java.io.File 類是檔案和目錄路徑名的抽象表示,主要用於檔案和目錄的建立、查詢和刪除等操作。

1.2 構造方法

  • public File(String pathname) :通過將給定的路徑名字串轉換為抽象路徑名來建立新的 File例項。

  • public File(String parent, String child) :從父路徑名字串和子路徑名字串建立新的 File例項。

  • public File(File parent, String child) :從父抽象路徑名和子路徑名字串建立新的 File例項。

構造舉例,程式碼如下:

// 檔案路徑名
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname); 

// 檔案路徑名
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2); 

// 通過父路徑和子路徑字串
 String parent = "d:\\aaa";
 String child = "bbb.txt";
 File file3 = new File(parent, child);

// 通過父級File物件和子路徑字串
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child);

小貼士:

  1. 一個File物件代表硬碟中實際存在的一個檔案或者目錄。

  2. 無論該路徑下是否存在檔案或者目錄,都不影響File物件的建立。

1.3 常用方法

獲取功能的方法

  • public String getAbsolutePath() :返回此File的絕對路徑名字串。

  • public String getPath() :將此File轉換為路徑名字串。

  • public String getName() :返回由此File表示的檔案或目錄的名稱。

  • public long length() :返回由此File表示的檔案的長度。

    方法演示,程式碼如下:

    public class FileGet {
        public static void main(String[] args) {
            File f = new File("d:/aaa/bbb.java");     
            System.out.println("檔案絕對路徑:"+f.getAbsolutePath());
            System.out.println("檔案構造路徑:"+f.getPath());
            System.out.println("檔名稱:"+f.getName());
            System.out.println("檔案長度:"+f.length()+"位元組");
    ​
            File f2 = new File("d:/aaa");     
            System.out.println("目錄絕對路徑:"+f2.getAbsolutePath());
            System.out.println("目錄構造路徑:"+f2.getPath());
            System.out.println("目錄名稱:"+f2.getName());
            System.out.println("目錄長度:"+f2.length());
        }
    }
    輸出結果:
    檔案絕對路徑:d:\aaa\bbb.java
    檔案構造路徑:d:\aaa\bbb.java
    檔名稱:bbb.java
    檔案長度:636位元組
    ​
    目錄絕對路徑:d:\aaa
    目錄構造路徑:d:\aaa
    目錄名稱:aaa
    目錄長度:4096

API中說明:length(),表示檔案的長度。但是File物件表示目錄,則返回值未指定。

絕對路徑和相對路徑

  • 絕對路徑:從碟符開始的路徑,這是一個完整的路徑。

  • 相對路徑:相對於專案目錄的路徑,這是一個便捷的路徑,開發中經常使用。

public class FilePath {
    public static void main(String[] args) {
      	// D盤下的bbb.java檔案
        File f = new File("D:\\bbb.java");
        System.out.println(f.getAbsolutePath());
      	
		// 專案下的bbb.java檔案
        File f2 = new File("bbb.java");
        System.out.println(f2.getAbsolutePath());
    }
}
輸出結果:
D:\bbb.java
D:\idea_project_test4\bbb.java

判斷功能的方法

  • public boolean exists() :此File表示的檔案或目錄是否實際存在。

  • public boolean isDirectory() :此File表示的是否為目錄。

  • public boolean isFile() :此File表示的是否為檔案。

方法演示,程式碼如下:

public class FileIs {
    public static void main(String[] args) {
        File f = new File("d:\\aaa\\bbb.java");
        File f2 = new File("d:\\aaa");
      	// 判斷是否存在
        System.out.println("d:\\aaa\\bbb.java 是否存在:"+f.exists());
        System.out.println("d:\\aaa 是否存在:"+f2.exists());
      	// 判斷是檔案還是目錄
        System.out.println("d:\\aaa 檔案?:"+f2.isFile());
        System.out.println("d:\\aaa 目錄?:"+f2.isDirectory());
    }
}
輸出結果:
d:\aaa\bbb.java 是否存在:true
d:\aaa 是否存在:true
d:\aaa 檔案?:false
d:\aaa 目錄?:true

建立刪除功能的方法

  • public boolean createNewFile() :當且僅當具有該名稱的檔案尚不存在時,建立一個新的空檔案。

  • public boolean delete() :刪除由此File表示的檔案或目錄。

  • public boolean mkdir() :建立由此File表示的目錄。

  • public boolean mkdirs() :建立由此File表示的目錄,包括任何必需但不存在的父目錄。

方法演示,程式碼如下:

public class FileCreateDelete {
    public static void main(String[] args) throws IOException {
        // 檔案的建立
        File f = new File("aaa.txt");
        System.out.println("是否存在:"+f.exists()); // false
        System.out.println("是否建立:"+f.createNewFile()); // true
        System.out.println("是否存在:"+f.exists()); // true
		
     	// 目錄的建立
      	File f2= new File("newDir");	
        System.out.println("是否存在:"+f2.exists());// false
        System.out.println("是否建立:"+f2.mkdir());	// true
        System.out.println("是否存在:"+f2.exists());// true

		// 建立多級目錄
      	File f3= new File("newDira\\newDirb");
        System.out.println(f3.mkdir());// false
        File f4= new File("newDira\\newDirb");
        System.out.println(f4.mkdirs());// true
      
      	// 檔案的刪除
       	System.out.println(f.delete());// true
      
      	// 目錄的刪除
        System.out.println(f2.delete());// true
        System.out.println(f4.delete());// false
    }
}

API中說明:delete方法,如果此File表示目錄,則目錄必須為空才能刪除。

1.4 目錄的遍歷

  • public String[] list() :返回一個String陣列,表示該File目錄中的所有子檔案或目錄。

  • public File[] listFiles() :返回一個File陣列,表示該File目錄中的所有的子檔案或目錄。

public class FileFor {
    public static void main(String[] args) {
        File dir = new File("d:\\java_code");
      
      	//獲取當前目錄下的檔案以及資料夾的名稱。
		String[] names = dir.list();
		for(String name : names){
			System.out.println(name);
		}
        //獲取當前目錄下的檔案以及資料夾物件,只要拿到了檔案物件,那麼就可以獲取更多資訊
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file);
        }
    }
}

小貼士:

呼叫listFiles方法的File物件,表示的必須是實際存在的目錄,否則返回null,無法進行遍歷。

第二章 遞迴

2.1 概述

  • 遞迴:指在當前方法內呼叫自己的這種現象。

  • 遞迴的分類:

    • 遞迴分為兩種,直接遞迴和間接遞迴。

    • 直接遞迴稱為方法自身呼叫自己。

    • 間接遞迴可以A方法呼叫B方法,B方法呼叫C方法,C方法呼叫A方法。

  • 注意事項

    • 遞迴一定要有條件限定,保證遞迴能夠停止下來,否則會發生棧記憶體溢位。

    • 在遞迴中雖然有限定條件,但是遞迴次數不能太多。否則也會發生棧記憶體溢位。

    • 構造方法,禁止遞迴

public class Demo01DiGui {
	public static void main(String[] args) {
		// a();
		b(1);
	}
	
	/*
	 * 3.構造方法,禁止遞迴
	 * 編譯報錯:構造方法是建立物件使用的,不能讓物件一直建立下去
	 */
	public Demo01DiGui() {
		//Demo01DiGui();
	}


	/*
	 * 2.在遞迴中雖然有限定條件,但是遞迴次數不能太多。否則也會發生棧記憶體溢位。
	 * 4993
	 * 	Exception in thread "main" java.lang.StackOverflowError
	 */
	private static void b(int i) {
		System.out.println(i);
		//新增一個遞迴結束的條件,i==5000的時候結束
		if(i==5000){
			return;//結束方法
		}
		b(++i);
	}

	/*
	 * 1.遞迴一定要有條件限定,保證遞迴能夠停止下來,否則會發生棧記憶體溢位。 Exception in thread "main"
	 * java.lang.StackOverflowError
	 */
	private static void a() {
		System.out.println("a方法");
		a();
	}
}

2.2 遞迴累加求和

計算1 ~ n的和

分析:num的累和 = num + (num-1)的累和,所以可以把累和的操作定義成一個方法,遞迴呼叫。

實現程式碼

public class DiGuiDemo {
	public static void main(String[] args) {
		//計算1~num的和,使用遞迴完成
		int num = 5;
      	// 呼叫求和的方法
		int sum = getSum(num);
      	// 輸出結果
		System.out.println(sum);
		
	}
  	/*
  	  通過遞迴演算法實現.
  	  引數列表:int 
  	  返回值型別: int 
  	*/
	public static int getSum(int num) {
      	/* 
      	   num為1時,方法返回1,
      	   相當於是方法的出口,num總有是1的情況
      	*/
		if(num == 1){
			return 1;
		}
      	/*
          num不為1時,方法返回 num +(num-1)的累和
          遞迴呼叫getSum方法
        */
		return num + getSum(num-1);
	}
}

程式碼執行圖解

小貼士:遞迴一定要有條件限定,保證遞迴能夠停止下來,次數不要太多,否則會發生棧記憶體溢位。

2.3 遞迴求階乘

  • 階乘:所有小於及等於該數的正整數的積。

n的階乘:n! = n * (n-1) *...* 3 * 2 * 1 

分析:這與累和類似,只不過換成了乘法運算,學員可以自己練習,需要注意階乘值符合int型別的範圍。

推理得出:n! = n * (n-1)!

程式碼實現

public class DiGuiDemo {
  	//計算n的階乘,使用遞迴完成
    public static void main(String[] args) {
        int n = 3;
      	// 呼叫求階乘的方法
        int value = getValue(n);
      	// 輸出結果
        System.out.println("階乘為:"+ value);
    }
	/*
  	  通過遞迴演算法實現.
  	  引數列表:int 
  	  返回值型別: int 
  	*/
    public static int getValue(int n) {
      	// 1的階乘為1
        if (n == 1) {
            return 1;
        }
      	/*
      	  n不為1時,方法返回 n! = n*(n-1)!
          遞迴呼叫getValue方法
      	*/
        return n * getValue(n - 1);
    }
}

2.4 遞迴列印多級目錄

分析:多級目錄的列印,就是當目錄的巢狀。遍歷之前,無從知道到底有多少級目錄,所以我們還是要使用遞迴實現。

程式碼實現

public class DiGuiDemo2 {
    public static void main(String[] args) {
      	// 建立File物件
        File dir  = new File("D:\\aaa");
      	// 呼叫列印目錄方法
        printDir(dir);
    }

    public static void  printDir(File dir) {
      	// 獲取子檔案和目錄
        File[] files = dir.listFiles();
      	// 迴圈列印
      	/*
      	  判斷:
      	  當是檔案時,列印絕對路徑.
      	  當是目錄時,繼續呼叫列印目錄的方法,形成遞迴呼叫.
      	*/
        for (File file : files) {
    		// 判斷
            if (file.isFile()) {
              	// 是檔案,輸出檔案絕對路徑
                System.out.println("檔名:"+ file.getAbsolutePath());
            } else {
              	// 是目錄,輸出目錄絕對路徑
                System.out.println("目錄:"+file.getAbsolutePath());
              	// 繼續遍歷,呼叫printDir,形成遞迴
                printDir(file);
            }
        }
    }
}

第三章 綜合案例

3.1 檔案搜尋

搜尋D:\aaa 目錄中的.java 檔案。

分析

  1. 目錄搜尋,無法判斷多少級目錄,所以使用遞迴,遍歷所有目錄。

  2. 遍歷目錄時,獲取的子檔案,通過檔名稱,判斷是否符合條件。

程式碼實現

public class DiGuiDemo3 {
    public static void main(String[] args) {
        // 建立File物件
        File dir  = new File("D:\\aaa");
      	// 呼叫列印目錄方法
        printDir(dir);
    }

    public static void printDir(File dir) {
      	// 獲取子檔案和目錄
        File[] files = dir.listFiles();
      	
      	// 迴圈列印
        for (File file : files) {
            if (file.isFile()) {
              	// 是檔案,判斷檔名並輸出檔案絕對路徑
                if (file.getName().endsWith(".java")) {
                    System.out.println("檔名:" + file.getAbsolutePath());
                }
            } else {
                // 是目錄,繼續遍歷,形成遞迴
                printDir(file);
            }
        }
    }
}

3.2 檔案過濾器優化

java.io.FileFilter是一個介面,是File的過濾器。 該介面的物件可以傳遞給File類的listFiles(FileFilter) 作為引數, 介面中只有一個方法。

boolean accept(File pathname) :測試pathname是否應該包含在當前File目錄中,符合則返回true。

分析

  1. 介面作為引數,需要傳遞子類物件,重寫其中方法。我們選擇匿名內部類方式,比較簡單。

  2. accept方法,引數為File,表示當前File下所有的子檔案和子目錄。保留住則返回true,過濾掉則返回false。保留規則:

    1. 要麼是.java檔案。

    2. 要麼是目錄,用於繼續遍歷。

  3. 通過過濾器的作用,listFiles(FileFilter)返回的陣列元素中,子檔案物件都是符合條件的,可以直接列印。

程式碼實現:

public class DiGuiDemo4 {
    public static void main(String[] args) {
        File dir = new File("D:\\aaa");
        printDir2(dir);
    }
  
    public static void printDir2(File dir) {
      	// 匿名內部類方式,建立過濾器子類物件
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".java")||pathname.isDirectory();
            }
        });
      	// 迴圈列印
        for (File file : files) {
            if (file.isFile()) {
                System.out.println("檔名:" + file.getAbsolutePath());
            } else {
                printDir2(file);
            }
        }
    }
}      

3.3 Lambda優化

分析:FileFilter是隻有一個方法的介面,因此可以用lambda表示式簡寫。

lambda格式:

()->{ }

程式碼實現:

public static void printDir3(File dir) {
  	// lambda的改寫
    File[] files = dir.listFiles(f ->{ 
      	return f.getName().endsWith(".java") || f.isDirectory(); 
    });
  	
	// 迴圈列印
    for (File file : files) {
        if (file.isFile()) {
            System.out.println("檔名:" + file.getAbsolutePath());
      	} else {
        	printDir3(file);
      	}
    }
}