File類:java.io.File,也是io流中的一部分,File類可以新建、刪除、重命名文件與目錄,可是不能訪問文件自己,如果須要訪問文件內容須要使用到輸入輸出流。html
建立對象:經過構造器獲取,介紹三個java
File(String pathname)
:可填入絕對路徑與至關路徑,相對路徑就是在本項目目錄下。File(String parent, String child)
:parent是父路徑、child是子文件路徑。File(File parent, String child)
:父File對象與子文件路徑。@Test public void test01(){ //第一個構造器:File(String pathname) File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner"); //第二個構造器:File(String parent, String child) File file1 = new File("C:\\Users\\93997\\Desktop\\fileexer","javahoner"); //第三個構造器:File(File parent, String child) File file2 = new File(new File("C:\\Users\\93997\\Desktop\\fileexer"),"javahoner"); System.out.println(file); System.out.println(file1); System.out.println(file2); }
string path
。File
類其toString()方法就是打印了String path
參數。路徑分隔符:就是上面構造器中的那個\\
,不一樣操做系統使用的也不一樣c++
windows與Dos系統默認:\
數據庫
Unix與URL使用:/
windows
由於Java跨平臺,因此對於路徑分隔符需慎用,Java也給出通用的:File.separator
,咱們在建立實例時可使用這個參數來代替咱們手寫/或\。(這個屬性是經過調用方法來獲取到本地文件系統的默認分隔符)數組
//至關於C:\Users\93997\Desktop\fileexer\javahoner File file = new File("C:\\Users\\93997\\Desktop\\fileexer\"+File.separator+\"javahoner");
獲取文件對象的相關信息緩存
String getAbsolutePath()
:獲取文件的絕對路徑安全
String getPath
:獲取路徑(也是絕對路徑)網絡
String getName
:獲取路徑下的最後一個文件名app
String getParent()
:獲取上層文件目錄路徑,若無,返回null
Long length()
:獲取文件長度(字節數)
Long lastModified()
:獲取最後一次修改文件的時間戳(毫秒值)
String[] list()
:獲取指定目錄下全部文件的名稱
File[] listFiles()
:獲取指定目錄下全部文件,以對象形式返回
.....
實際使用:
@Test public void test01(){ //第一個構造器:File(String pathname) File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner"); System.out.println(file.getAbsolutePath());//C:\Users\93997\Desktop\fileexer\javahoner System.out.println(file.getPath());//C:\Users\93997\Desktop\fileexer\javahoner System.out.println(file.getName());//javahoner System.out.println(file.getParent());//C:\Users\93997\Desktop\fileexer System.out.println(file.length());//0 System.out.println(file.lastModified());//1612056310161 for (String childFile : file.list()) { System.out.print(childFile+" "); }//文件1 文件2 System.out.println(); for (File listFile : file.listFiles()) { System.out.println(listFile); } //C:\Users\93997\Desktop\fileexer\javahoner\文件1 //C:\Users\93997\Desktop\fileexer\javahoner\文件2 }
方法介紹
public boolean renameTo(File dest)
:剪切文件更名到指定路徑(也能夠實現重命名效果)
實際應用:重命名與剪切功能
場景1:想將javahoner目錄下的文件名爲文件
修改成長路鍋鍋
File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件"); boolean b = file.renameTo(new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\重命名的文件")); System.out.println(b);
C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件
先剪切到C:\\Users\\93997\\Desktop\\fileexer\\javahoner
路徑下再更名爲不存在的文件名爲重命名文件。實現了修更名稱效果場景2:想將javahoner目錄下的文件長路鍋鍋移動到1(2)文件中
File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\長路鍋鍋"); boolean b = file.renameTo(new File("C:\\Users\\93997\\Desktop\\1(2)\\長路鍋鍋")); System.out.println(b);
總結:秒呀這個方法,看了下源碼最終定位到一個WinNTFileSystem
的private native boolean rename0(File f1, File f2);
根據native關鍵字,說明這個方法是一個原生函數,是使用c/c++來實現並編譯成DLL文件由java去調用。
這裏僅僅介紹下方法使用
public boolean isDirectory
:判斷是不是文件目錄
public boolean isFile()
:判斷是不是文件
public boolean exists()
:判斷是否存在
public boolean canRead()
:判斷是否可讀
public boolean canWrite()
:判斷是否可寫
public boolean is Hidden()
:判斷是否隱藏
建立功能
public boolean createNewFile()
:文件存在不建立返回false。
public boolean mkdir()
:文件目錄存在不建立,若此文件目錄的上級目錄不存在也不建立。
public boolean mkdirs()
:不只僅是此目錄文件,如果此目錄的上層文件目錄不存在一併建立。
刪除功能
public boolean delete()
:刪除此抽象路徑名錶示的文件或目錄,如果此路徑名錶示目錄,則目錄必須爲空才能刪除。
案例:刪除指定路徑下全部文件
public class Main { //生成num個空白字符串,如果num非0最後添加- public static String blankStr(int num){ StringBuilder str = new StringBuilder(); for (int i=0;i<num;i++){ str.append(" "); } if(num != 0){ str.append("-"); } return str.toString(); } //刪除路徑下內容(包含層級關係) public static void deleteAllFile(File file,int tier) throws RuntimeException{ //若是該路徑是一個文件直接刪除 if(file.isFile()){ System.out.println(file.delete()?blankStr(tier)+file.getName()+"已刪除":blankStr(tier)+file.getName()+"刪除失敗"); return; } //該路徑是目錄 File[] files = file.listFiles(); //空目錄直接刪除 if(files == null || files.length == 0){ System.out.println(file.delete()?blankStr(tier)+"成功刪除"+file.getName():blankStr(tier)+file.getName()+"空目錄刪除失敗"); return; } //目錄不爲空,遍歷當前文件 for (File dict : files) { //目錄:刪除目錄中內容+刪除本身自己 if(dict.isDirectory()){ //獲取目錄下的全部文件 File[] files2 = dict.listFiles(); //若是目錄爲空直接刪除 if(files2 == null || files2.length == 0){ System.out.println(dict.delete()?blankStr(tier)+"成功刪除"+dict.getName():blankStr(tier)+dict.getName()+"空目錄刪除失敗"); }else{ //非空目錄狀況 deleteAllFile(dict,tier+1); System.out.println(dict.delete()?blankStr(tier)+"成功刪除"+dict.getName():blankStr(tier)+dict.getName()+"空目錄刪除失敗"); } }else{ //非目錄:無提示直接調用 deleteAllFile(dict,tier+1); } } } //測試 @Test public void test01(){ File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件1"); try { deleteAllFile(file,0); } catch (RuntimeException e) { System.out.println("刪除非空文件時出現異常!!!"); } System.out.println("該文件路徑下內容已被刪除"); } }
deleteAllFile(File file,int tier)
便可:參數1就是刪除指定文件路徑,參數2表示層級關係(設置爲0最高級)Google將I/O譽爲"開放中創新",input/output:二進制1,0
IO原理
:是Input/Output的縮寫,I/O技術是很實用的技術,用來處理設備之間的數據傳輸。例如讀/寫文件,網絡通信等。
在Java程序中,對於數據的輸入/輸出操做以"流(stream
)"的方式進行。java.io包下提供了各類"流"類和接口,用以獲取不一樣種類的數據,並經過標準的方法輸入或輸出數據。
流的分類
根據數據單位不一樣分爲兩類:字節流與字符流
byte
的流,一般用來傳輸視頻、圖片(非文本的數據)char
的流,一般用來文本輸入輸出。
數據的流向不一樣分爲:輸入流、輸出流
角色不一樣分爲:節點流、處理流
Java的IO流所有都是由下面這4個抽象基類派生:
(抽象基類) | 字節流 | 字符流 |
---|---|---|
輸入流 | InputStream | Reader |
輸出流 | OutputStream | Writer |
I/O流體系圖:
關係圖:
對於字節流的輸入輸出流的基類,首先介紹一下InputSream
以及OutputSream
。
InputSream
爲字節輸入流的抽象基類,其基類定義瞭如下幾個通用方法,這裏列舉幾個
int read()
:從流中讀取1個字節並返回,若是達到文件末尾返回-1;read(byte b[])
:從流中讀取b的長度個字節的數據存儲到b中,返回的是讀取的字節個數,如果返回-1表示到告終尾,沒有數據。int read(byte b[], int off, int len)
:從流中的off位置開始讀取len個字節的數據到b中,返回結果是實際讀取的字節個數,如果返回-1表示沒有數據。void close()
:關閉輸入流OutputSream
爲字節輸出流的抽象基類,這裏列舉幾個該基類經常使用方法
void write(int b)
:將1個字節寫入到輸出流中void write(byte b[])
:從b的off位置開始,獲取len個字節數據,寫到輸出流中void flush()
:刷新此輸出流並強制任何緩衝的輸出字節被寫出。(爲以後緩衝流提供抽象方法)void close()
:關閉輸出流輸出入流關字節流的含節點流以及各類處理流它們的基類就是這兩個。
對於字符流的輸入輸出流的基類,介紹一下Reader
、Writer
Reader
:是字符輸入流的抽象基類 ,定義了幾個函數以下
int read()
:讀取單個字符,返回結果是一個int,若想要字符顯示須要轉爲char;如果到達流的末尾,返回-1int read(char cbuf[])
:從流中讀取b的長度個字符的數據存儲到b中,返回的是讀取的字節個數,如果返回-1表示到告終尾,沒有數據。void close()
:關閉字符輸入流writer
:是字符輸入流的抽象基類 ,定義了幾個函數以下
void write(int b)
:將1個字節寫入到輸出流中void write(byte b[])
:從b的off位置開始,獲取len個字節數據,寫到輸出流中void flush()
:刷新此輸出流並強制任何緩衝的輸出字節被寫出。(爲以後緩衝流提供抽象方法)void close()
:關閉輸出流與以前介紹的字節流大體相同,他們兩個區別之一就是它們傳遞的數據單位不一樣一個是字節、一個是流。
節點流也稱文件流,對應節點流包含了字節流與字符流
FileInputSream
、FileOutputSream
FileReader
、FileWriter
案例1:使用字節流來複製圖片
import org.junit.Test; import java.io.*; public class Main { @Test public void test02(){ FileInputStream fis = null; FileOutputStream fos = null; try { //目標圖片1234.jpg fis = new FileInputStream(new File("1234.jpg")); //複製地址 fos = new FileOutputStream(new File("圖片.jpg")); byte[] data = new byte[1024]; int len; //讀取字節 while((len = fis.read(data)) != -1){ //寫入data數組中讀取到的len個字節 fos.write(data,0,len); } } catch (IOException e) { e.printStackTrace(); }finally { if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
案例2:複製文本文件,而且在控制檯顯示
import org.junit.Test; import java.io.*; public class Main { //這裏主要爲了演示就不像以前那麼規範,直接拋出異常 @Test public void test02() throws IOException { FileReader fr = new FileReader("changlu.txt");; FileWriter fw = new FileWriter("cl.txt"); char[] data = new char[1024]; int len; while((len = fr.read(data)) != -1){ //顯示在控制檯中 System.out.print(new String(data,0,len)); fw.write(data,0,len); } fr.close(); fw.close(); } }
FileWriter(File file, boolean append)
這種構造器方式,第二個參數填true表示數據寫入從文件末尾開始。首先看一下緩衝流,前兩個是用於傳輸字節的緩衝流,後兩個是傳輸字符的緩衝流
看一下繼承關係:
傳輸字節的兩個緩衝流都是繼承於FilterInputStream:
傳輸字符的兩個緩衝流都是繼承於Writer
緩衝流的做用:提升文件的讀寫效率
提升讀寫速度的緣由:內部提供了一個緩衝區
使用了緩衝區與沒有使用的圖示:
實際上使用緩衝流很簡單,直接在節點流上包一層,緩衝流也是須要進行手動關閉的,關閉的同時會將節點流也關閉。
示例:這裏
//字節流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("changlu.txt")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("test.txt"))); //字符流 BufferedReader br = new BufferedReader(new FileReader("file.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("file2.txt"));
針對於緩衝流本身實現了一個方法
void newLine()
:向文件中寫入換行符通常寫入完以後咱們還須要使用flush()
方法來將緩存區的內容手動寫入到文件中。
測試複製66MB的視頻速度:
介紹圖片加密的方法:
//加密data數組中0-len中的字節 public static byte[] encryptChar(byte[] data,int len){ for (int i = 0; i < len; i++) { data[i] ^= 5;//經過異或的方式 } return data; } //寫入操做 省略了內容 byte[] data = new byte[1024]; int len; //讀取字節 while((len = bis.read(data)) != -1){ //寫入data數組中讀取到的len個字節 bos.write(encryptChar(data,len)); }
轉換流提供了在字節流和字符流之間的轉換
InputStreamReader
:將InputStream轉換爲Reader 字節轉字符OutputStreamWriter
:將Writer轉換爲OutputStream 字符轉字節當字節流中的數據都是字符時,轉成字符流更高效。大多經過使用轉換流來處理文件亂碼問題,實現編碼和解碼功能!
簡單舉個例子:將changlu.txt(UTF-8編碼)先經過InputStreamReader
轉爲字符,再經過使用OutputStreamWriter
指定另外一個編碼轉爲長changlu.txt。(gbk編碼)
轉換流的編碼應用:
使用轉換流將一個UTF-8編碼文件轉爲GBK編碼的文件
public class Main { public static void main(String[] args) throws IOException { //轉換流 將一個UTF-8編碼的轉爲GBK編碼的 InputStreamReader isr = new InputStreamReader(new FileInputStream("changlu.txt"), StandardCharsets.UTF_8); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("changluya.txt"), "GBK"); //使用緩衝流加速 BufferedReader br = new BufferedReader(isr); BufferedWriter bw = new BufferedWriter(osw); char[] data = new char[1024]; int len; while((len = br.read(data)) != -1){ bw.write(data,0,len); } br.close(); bw.close(); } }
系統標準的輸入和輸出設備分別爲:System.in與System.out。默認輸入設備:鍵盤;默認輸出設備:顯示器
System.in
:實際類型爲InputStream
System.out
:實際類型爲PrintStream
,其次是OutputStream的子類咱們能夠更改System的輸入輸出設備經過System的的setIn
,setOut
方法。
傳統咱們經過使用Scanner
來進行數據的輸入獲取,在這裏不容許使用Scanner
,要求從鍵盤輸入字符串,要求將讀取到的整行字符串轉成大寫輸出。而後繼續 進行輸入操做,直至當輸入「e」或者「exit」時,退出程序。
public static void main(String[] args) throws IOException { //System.in是InputStream實例(字節流),這裏包一層轉換流轉換爲字符流 InputStreamReader is = new InputStreamReader(System.in); //包裝上一層緩衝流 BufferedReader br = new BufferedReader(is); String str; //鍵盤中每讀取一行數據進行循環 while((str = br.readLine()) != null){ if("e".equals(str) || "exit".equals(str)){ System.out.println("安全退出"); break; } str = str.toUpperCase(); //System.out => PrintStream System.out.println(str); System.out.println("繼續輸入信息"); } br.close(); }
實現將基本數據的類型格式轉換爲字符串輸出
PrintStream
打印的全部字符都使用平臺的默認字符編碼轉換爲字節,在須要寫入字符而不是寫入字節的狀況下應該使用PrintWriter
單個介紹:
PrintStream
:在實現OutputStream接口上又實現了打印各類數據的print方法,一般使用系統默認的System.out
調用方法輸出
PrintWriter
:擴展了Write接口,也實現了許多print打印輸出方法
屬於字符流
最終輸出的是char字符
//使用方式:配合StringWriter獲取數據並打印到控制檯 public static void main(String[] args) throws IOException { //內部定義了一個StringBuffer存儲數據 StringWriter str = new StringWriter(); try (PrintWriter pw = new PrintWriter(str)){ //將指定內容寫入到str中,實際上仍是調用了write方法 pw.println("hello"); pw.println("changlu"); pw.println(2020); } //將StringBuffer對象打印 System.out.println(str.toString()); }
案例描述:將本來輸出到控制檯的內容輸入到文件中
思路:更改System中的輸出設備(顯示屏 =》文件)
public static void main(String[] args) throws IOException { //try(聲明1;聲明2;){ ... } 這種方式會自動執行close()方法 try(//①建立文件字節流 FileOutputStream fos = new FileOutputStream("changlu.txt"); //②PrintStream處理流包裹節點流 PrintStream ps = new PrintStream(fos);){ //更改System的輸出設備爲文件流 System.setOut(ps); //輸出到文件中 System.out.println("長路&林兒"); } }
執行結果:成功建立changlu.txt,並輸入到文件中。
InputStream
和OutputStream
子類的流上。目的:爲了方便操做Java的基本數據類型與String類型,可使用數據流。
這裏列舉一下DataInputStream
的幾個方法以下:byte readByte()
、char readChar()
、float readFloat()
、long readLong()
、int readInt()
、String readUTF()
int readInt()
一次性讀出四個字節並將其轉爲int值讀出OutputStream
幾個相似read換write便可。
void writeInt(int v)
一次寫入四個字節並將其轉爲字節寫出直接上小案例:輸出流與輸入流配合使用(增添了一個緩衝流來提高速度)
public class Main { //使用數據流的輸出流存儲不一樣類屬數據 @Test public void test01(){ try(BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream("changlu.data")); DataOutputStream dos = new DataOutputStream(bis);){ dos.writeUTF("長路"); dos.writeInt(666); dos.writeBoolean(false); dos.flush(); } catch (IOException e) { e.printStackTrace(); } } //使用數據流的輸入流來獲取指定順序的數據類型 @Test public void test02(){ try(BufferedInputStream bos = new BufferedInputStream(new FileInputStream("changlu.data")); DataInputStream dis = new DataInputStream(bos);) { String name = dis.readUTF(); int num = dis.readInt(); boolean bol = dis.readBoolean(); System.out.println(name+"\n"+num+"\n"+bol); } catch (IOException e) { e.printStackTrace(); } } }
詳細對象流以及序列化見:IO流—對象序列化
接下來要介紹的隨機存取文件流只有一個類RandomAccessFile
,它實現了DataOutput
、DataInput
接口,直接繼承於Object,說明其實現了讀取與寫入的功能。
RandomAccessFile
類功能描述:
long getFilePointer()
獲取指針的位置,void seek(long pos)
將指針定位到pos位置。構造器介紹:RandomAccessFile(String name, String mode)
、RandomAccessFile(File file, String mode)
r
:只讀方式打開。rw
:打開以便讀取和寫入。rwd
:打開以便讀取和寫入;同步文件內容的更新。rws
:打開以便讀取與寫入;同步文件內容和元數據的更新。注意點:若模式爲r
(只讀),不可以建立文件,只能讀取存在的文件,如果不存在就會出現異常;rw
模式是如果不存在就會去建立文件。
rw
模式,數據不會當即寫入到硬盤中,一旦寫入過程當中有異常數據所有丟失;rwd
模式數據會被當即寫入硬盤。一旦寫數據發生異常,rwd
模式中會將已被寫入的數據保存到硬盤中。案例1:複製圖片
@Test public void test01(){ //建立兩個隨機存取流的實例對象,分爲來進行讀或寫的操做 try(RandomAccessFile rafRead = new RandomAccessFile("1234.jpg", "r"); RandomAccessFile rafWrite = new RandomAccessFile("changlu.jpg", "rw");){ byte[] data = new byte[1024]; int len; while((len = rafRead.read(data))!=-1){ rafWrite.write(data,0,len); } }catch (IOException e) { e.printStackTrace(); } }
案例2:複製一個文件中的內容到另外一個文件追加內容
@Test public void test01(){ //建立兩個隨機存取流的實例對象,分爲來進行讀或寫的操做 try(RandomAccessFile rafRead = new RandomAccessFile("changlu.txt", "r"); RandomAccessFile rafWrite = new RandomAccessFile("changlu222.txt", "rw");){ //獲取其中的字節 int fileLength = (int) rafRead.length(); byte[] data = new byte[fileLength]; for(int i=0;i<data.length;i++){ data[i] = rafRead.readByte(); } //複製內容到其餘文件中 rafWrite.seek(2);//空兩格 for(int i = 0 ;i<data.length;i++){ rafWrite.writeByte(data[i]); } //新增指定內容 // byte[] bytes = "想對林兒說我想你了".getBytes("utf-8"); // for(int i=0;i<bytes.length;i++){ // rafWrite.writeByte(bytes[i]); // } rafWrite.writeUTF("想對林兒說我想你了"); }catch (IOException e) { e.printStackTrace(); } }
出現亂碼,比較迷糊搞不清
java.nio這個類帶來了重要的效能提高並能夠充分利用執行程序的機器上的原始容量。
java1.4版新增的輸入輸出java.nio這個類帶來了重要的效能提高並能夠充分利用執行程序的機器上的原始容量。
包含一項關鍵能力是能夠直接控制buffer
以及nonblocking
的輸入域輸出,它能讓你的輸入/輸出程序代碼在沒有東西可讀取或寫入
時不用等在那裏。
對於nio可能會引起效能損失,非nio的輸入/輸出適合九成以上的應用,依舊可使用FileInputStream
並經過getChannel()
方法來開始使用nio。
NIO.2中Path、Paths、Files類的使用見:語雀-NIO部分
[1]. Java中Native關鍵字的做用
[2]. Java一個漢字佔幾個字節(詳解與原理) 特別詳細
[3]. 爲何用字符流複製的圖片打不開,而用字節流複製的卻能夠打開?
[4]. 對比字節流和字符流,回答爲何FileReader不能用來拷貝圖片
[5]. Java I/O流之隨機流詳解,包含隨機流讀寫數據時編碼格式問題!
[6]. 尚硅谷Java視頻-IO流(宋紅康)
我是長路,感謝你的閱讀,若有問題請指出,我會聽取建議並進行修正。 歡迎關注個人公衆號:長路Java,其中會包含軟件安裝等其餘一些資料,包含一些視頻教程以及學習路徑分享。 學習討論qq羣:891507813 咱們能夠一塊兒探討學習 註明:轉載可,須要附帶上文章連接