IO
所謂IO就是輸出輸出(input/output),一般的理解都是相對于計算機而言的輸入輸出,
比如:
輸出設備:顯示幕,耳機,音響,列印機.....
輸入設備:鍵盤,滑鼠,麥克風......
上面的所有的輸入輸出都是相對于計算機而言的,
我們這里說的輸入輸出是相對于記憶體而言的
-
資料讀取到記憶體就是輸入,
-
資料從記憶體寫出就是輸出,
輸入輸出的原理:

輸出的資料流稱之為輸出流,輸入的資料流稱之為輸入流,
面試題:java的流是如何分類的?
按照方向分為:輸入流和輸出流,
按照處理方式:分為位元組流和字符流,


File類
在java.io包下有一個File, 這個類的所有物件都表示一個檔案,(一個File類的物件就是一個檔案或者檔案夾的抽象表示)
內部的常量
-
static String pathSeparator 與系統相關的路徑分隔符字符,為方便起見,表示為字串,
-
static char pathSeparatorChar 與系統相關的路徑分隔符,
-
static String separator 與系統相關的默認名稱 - 分隔符字符,以方便的方式表示為字串,
-
static char separatorChar 與系統相關的默認名稱分隔符,
前面兩個常量是路徑相關的分隔符,后面兩個是名稱分隔符,
以前在linux中分隔符是 "/" 在windows中是 "\"
構造方法
-
File(File parent, String child) 從父抽象路徑名和子路徑名字串創建新的 File實體,
-
File(String pathname) 通過將給定的路徑名字串轉換為抽象路徑名來創建新的 File實體,
-
File(String parent, String child) 從父路徑名字串和子路徑名字串創建新的 File實體,
-
File(URI uri) 通過將給定的 file: URI轉換為抽象路徑名來創建新的 File實體,
相對路徑和絕對路徑,
所謂相對路徑,就是相對于當前的檔案而言,目標的位置,
所謂絕對路徑,就是從盤符開始的路徑,
一般建議使用相對路徑,
public class HelloFile{
public static void main(String [] args){
// 我們這里先使用絕對路徑
File file = new File("E:\\202201\\第二階段\\01.java高級\\0317\\temp\\hello.txt");
// 當然我們也可以使用相對路徑表示
File file1 = new File("s.txt");
file1.createNewFile();
?
// 使用父子檔案創建(父路徑字串,子路徑字串)
File file2 = new File("E:\\202201\\第二階段\\01.java高級\\0317\\temp","hello.txt");
File file3_1 = new File("E:\\202201\\第二階段\\01.java高級\\0317\\temp");
File file3 = new File(file3_1,"hello.txt");
}
}
File類的一些API:
獲取資訊的---
-
exists() 測驗此抽象路徑名表示的檔案或目錄是否存在,
-
getName() 回傳由此抽象路徑名表示的檔案或目錄的名稱,
-
getAbsolutePath() 回傳此抽象路徑名的絕對路徑名字串,
-
getParent() 回傳此抽象路徑名的父 null的路徑名字串,如果此路徑名未命名為父目錄,則回傳null,
-
getParentFile() 回傳此抽象路徑名的父,或抽象路徑名 null如果此路徑名沒有指定父目錄,
-
isDirectory() 測驗此抽象路徑名表示的檔案是否為目錄,
-
isFile() 測驗此抽象路徑名表示的檔案是否為普通檔案,
-
lastModified() 回傳此抽象路徑名表示的檔案上次修改的時間,
-
length() 回傳由此抽象路徑名表示的檔案的長度,
-
list() 回傳一個字串陣列,命名由此抽象路徑名表示的目錄中的檔案和目錄,
-
listFiles() 回傳一個抽象路徑名陣列,表示由該抽象路徑名表示的目錄中的檔案,
案例:
public class FileTest1 {
public static void main(String[] args) {
// 檔案
String path = "E:\\202201\\第二階段\\01.java高級\\0317\\temp\\hello.txt";
File file = new File(path);
// 檔案夾
String path1 = "E:\\202201\\作業\\20220315";
File file1 = new File(path1);
System.out.println("------------------");
System.out.println("檔案名:"+file.getName());
System.out.println("檔案夾名:"+ file1.getName());
System.out.println("檔案的絕對路徑"+file.getAbsolutePath());
System.out.println("檔案夾的絕對路徑:"+file1.getAbsolutePath());
System.out.println("檔案是否存在:"+file.exists());
System.out.println("檔案夾是否存在:"+file1.exists());
System.out.println("檔案的父檔案夾路徑:"+file.getParent());
System.out.println("檔案是否是檔案:"+file.isFile());
System.out.println("檔案是否是檔案夾:"+file.isDirectory());
System.out.println("檔案的最后修改時間:"+new Date(file.lastModified()));
System.out.println("檔案夾的最后修改時間:"+new Date(file1.lastModified()));
System.out.println("檔案長度:"+file.length()+" 位元組");
System.out.println("-- 檔案夾的length是無效 --");
System.out.println("檔案夾長度:"+file1.length()+" 位元組");
// list是獲取當前檔案夾下的所有檔案和檔案夾的名字的字串陣列
String[] names = file1.list();
System.out.println(Arrays.toString(names));
// listFile獲取File型別的陣列
File[] files = file1.listFiles();
for (File f: files){
System.out.println(f.getAbsolutePath()+":"+f.length());
}
}
}
我們查看檔案的大小,從現在開始都是看位元組:

1B byte = 8bit
1K = 1024B
1M = 1024K
1G = 1024M
1T = 1024G
1P = 1024T
創建洗掉判斷的一些API----
-
canExecute() 測驗應用程式是否可以執行此抽象路徑名表示的檔案,
-
canRead() 測驗應用程式是否可以讀取由此抽象路徑名表示的檔案,
-
canWrite() 測驗應用程式是否可以修改由此抽象路徑名表示的檔案,
-
delete() 洗掉由此抽象路徑名表示的檔案或目錄,
-
boolean mkdir() 創建由此抽象路徑名命名的目錄,
-
boolean mkdirs() 創建由此抽象路徑名命名的目錄,包括任何必需但不存在的父目錄,
-
createNewFile() 當且僅當具有該名稱的檔案尚不存在時,原子地創建一個由該抽象路徑名命名的新的空檔案,
例外問題:

案例:
public class FileTest2 {
public static void main(String[] args) throws IOException {
File file = new File("file-list");
// 創建這個檔案夾
// 檔案不存在或者檔案不是檔案夾
if(!file.exists() || !file.isDirectory()){
// 創建它
file.mkdir();
}
// 創建一組檔案夾
File file1 = new File("E:\\202201\\第二階段\\01.java高級\\0317\\temp\\stu\\info\\names");
if(!file1.exists() || !file1.isDirectory()){
// 創建它 前往不要忘記s
boolean mkdirs = file1.mkdirs();
System.out.println(mkdirs);
}
// 創建檔案
File file2 = new File("file-list\\names.txt");
if(!file2.exists() || !file2.isFile()){
file2.createNewFile();// 這里會有例外,直接拋出
}
// 洗掉檔案
File file3 = new File("s.txt");
boolean delete = file3.delete();
System.out.println(delete);
}
}
位元組輸入流InputStream
所有的位元組輸入流的老祖宗就是 java.io.InputStream,
位元組流的特點:是以位元組的方式處理資料
這個類是一個抽象類,其目的就是為了讓其他的類實作,所以我們都是使用這個類的子類,
瀏覽API:
-
int available() 回傳從該輸入流中可以讀取(或跳過)的位元組數的估計值,而不會被下一次呼叫此輸入流的方法阻塞, (這個方法對于網路流是無效的)
-
void close() 關閉此輸入流并釋放與流相關聯的任何系統資源,
-
void mark(int readlimit) 標記此輸入流中的當前位置,
-
boolean markSupported() 測驗這個輸入流是否支持 mark和 reset方法,
-
abstract int read() 從輸入流讀取資料的下一個位元組,
-
int read(byte[] b) 從輸入流讀取一些位元組數,并將它們存盤到緩沖區 b ,
-
int read(byte[] b, int off, int len) 從輸入流讀取最多 len位元組的資料到一個位元組陣列,
-
void reset() 將此流重新定位到上次在此輸入流上呼叫 mark方法時的位置,
-
long skip(long n) 跳過并丟棄來自此輸入流的 n位元組資料,
FileInputStream--InputStream的實作類
構造方法:
-
FileInputStream(File file) 通過打開與實際檔案的連接創建一個 FileInputStream ,該檔案由檔案系統中的 File物件 file命名,
-
FileInputStream(String name) 通過打開與實際檔案的連接來創建一個 FileInputStream ,該檔案由檔案系統中的路徑名 name命名,
API:就是參考父類的API,
使用:按照單個的位元組讀取資料,
public class FileInputStreamTest {
public static void main(String[] args) {
// 通過檔案名構建一個位元組輸入流
InputStream in = null;
try {
// 創建輸入流
in = new FileInputStream("names.txt");
int len = in.available();// 可讀去位元組的長度
System.out.println("資料長度:"+len);
// 準備一個len長度的位元組陣列
byte [] buff = new byte[len];
int index = 0;
// 準備讀取資料
// 讀取一個位元組的資料
//int read = in.read();
// 回圈讀取
int data = -1;
while((data = in.read())!=-1){
//System.out.println(data);
// 將資料放入位元組陣列
buff[index++] = (byte)data;
// index++;
};
// 位元組陣列中的資料
System.out.println(Arrays.toString(buff));
// 根據位元組陣列創建一個字串
String str = new String(buff);
System.out.println(str);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
輸入流還提供了一些API可以按組讀取資料
按組讀取資料的API-1:
public class FileInputStreamTest1 {
public static void main(String[] args) {
// 通過檔案名構建一個位元組輸入流
InputStream in = null;
try {
// 根據檔案創建輸入流
File file = new File("names.txt");
in = new FileInputStream(file);
// 按組讀取
// int read(byte[] b)讀取最多b.length位元組的資料到位元組陣列 回傳值實際讀取的長度,如果到達檔案末尾回傳-1
// 準備一個位元組陣列
byte [] buff = new byte[500];
// 讀取資料到緩沖陣列
int len = in.read(buff);
System.out.println("實際讀取的長度是:"+len);
String str = new String(buff);// 根據整個陣列創建字串
System.out.println(str);
System.out.println("--------------");
// 根據實際讀取的位元組長度創建字串
//(位元組陣列,偏移量,使用長度)
String info = new String(buff,0,len);
System.out.println(info);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
按組讀取資料API-2:
public class FileInputStreamTest2 {
public static void main(String[] args) {
// 通過檔案名構建一個位元組輸入流
InputStream in = null;
try {
// 根據檔案創建輸入流
File file = new File("names.txt");
in = new FileInputStream(file);
// 按組讀取
// read(byte[] b, int off, int len) 讀取資料,從還陣列的off位置開始放資料,放len長度
// 準備一個位元組陣列
byte [] buff = new byte[500];
// 讀取資料
int len = in.read(buff, 100, 355);
// 創建字串
String str = new String(buff,100,len);
System.out.println(str);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
實際應用中讀取檔案:
上面的按組讀取的例子中,都是一次性將所有的資料讀取到記憶體中,
如果一個檔案有一個T,我們肯定無法一次性讀取到記憶體,
解決方案:按組,回圈讀取,重復使用緩沖陣列,
看程式:
public class FileInputStreamTest3 {
public static void main(String[] args) {
// 通過檔案名構建一個位元組輸入流
InputStream in = null;
try {
// 根據檔案創建輸入流
File file = new File("data.txt");
in = new FileInputStream(file);
// 準備一個緩沖陣列
byte [] buff = new byte[1024];
int len = -1;
long total = 0;
// 將每次讀取到陣列中的實際的長度賦值給len,如果len是-1表示已經到達檔案末尾,結束讀取
while((len = in.read(buff))!=-1){
total += len;
// 輸出緩沖陣列中的資料
String str = new String(buff,0,len);
System.out.print(str);
}
System.out.println("\n讀取的資料的總長度是:"+total);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
位元組輸出流OuputStream
OuputStream是所有的位元組輸出流的老祖宗,
API:
-
void close() 關閉此輸出流并釋放與此流相關聯的任何系統資源,
-
void flush() 重繪此輸出流并強制任何緩沖的輸出位元組被寫出,
-
void write(byte[] b) 將 b.length位元組從指定的位元組陣列寫入此輸出流,
-
void write(byte[] b, int off, int len) 從指定的位元組陣列寫入 len個位元組,從偏移 off開始輸出到此輸出流,
-
abstract void write(int b) 將指定的位元組寫入此輸出流,
FileOutputStream--OutputStream的實作類
-
FileOutputStream(File file) 創建檔案輸出流以寫入由指定的 File物件表示的檔案,
-
FileOutputStream(File file, boolean append) 創建檔案輸出流以寫入由指定的 File物件表示的檔案,
-
FileOutputStream(String name) 創建檔案輸出流以指定的名稱寫入檔案,
-
FileOutputStream(String name, boolean append) 創建檔案輸出流以指定的名稱寫入檔案,
api就是父類中的方法,
構造方法案例:
public class FileOutputStreamTest1 {
public static void main(String[] args) {
// 申明位元組輸出流
OutputStream out = null;
try{
// 創建流
// 根據檔案創建輸出流
File file = new File("st.txt");
// 從檔案開頭開始寫資料, 覆寫了原有的資料
out = new FileOutputStream(file);
// 根據檔案創建輸出流
// append:
// true 從檔案的末尾開始寫,續在檔案原有資料的后面,
// false 從檔案的開頭開始寫,覆寫原有資料
out = new FileOutputStream(file,true);
// 根據檔案名創建輸出流
// 從檔案開頭開始寫資料, 覆寫了原有的資料
out = new FileOutputStream("hell.txt");
// append:
// true 從檔案的末尾開始寫,續在檔案原有資料的后面,
// false 從檔案的開頭開始寫,覆寫原有資料
out = new FileOutputStream("hell.txt",true);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
tips:如果我們創建的輸出流指向的檔案不存在,當我們寫出資料的時候會自動創建這個檔案,
三個寫資料的API:
輸出一個位元組:
public class FileOutputStreamTest1 {
public static void main(String[] args) {
// 申明位元組輸出流
OutputStream out = null;
try{
//--------------------------------------------------
// 創建流
out = new FileOutputStream("hell.txt",true);// ***
// 準備一個字串
String str = "吃雞全靠茍";
// 將字串轉換為位元組陣列
byte[] bytes = str.getBytes();//???
// 回圈的將位元組陣列的內容寫入檔案
for(byte b : bytes) {
out.write(b);// ****
}
//-------------------------------------------------
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
寫出一個位元組陣列:
public class FileOutputStreamTest2 {
public static void main(String[] args) {
// 申明位元組輸出流
OutputStream out = null;
try{
//------------------------
// 創建流
// 根據檔案創建輸出流
File file = new File("st1.txt");
// 從檔案開頭開始寫資料, 覆寫了原有的資料
out = new FileOutputStream(file);
// 準備一個字串
String str = "吃雞全靠茍";
// 將字串轉換為位元組陣列
byte[] bytes = str.getBytes();
// 直接寫出一個位元組陣列
out.write(bytes);
//------------------
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
寫出位元組陣列的一部分:
public class FileOutputStreamTest3 {
public static void main(String[] args) {
// 申明位元組輸出流
OutputStream out = null;
try{
// 創建流
// 根據檔案創建輸出流
File file = new File("st2.txt");
// 從檔案開頭開始寫資料, 覆寫了原有的資料
out = new FileOutputStream(file);
// 準備一個字串
String str = "吃雞全靠茍";
// 將字串轉換為位元組陣列
byte[] bytes = str.getBytes();
// 直接寫出一個位元組陣列的一部分
// (位元組陣列,偏移量,長度) 從位元組陣列的偏移量位置開始寫資料,寫長度這么常
out.write(bytes,0,6);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
一個需求:
從控制臺輸入一個商品的資訊,將商品資訊寫入商品檔案,檔案中一行存盤一個商品資訊,?
/**
* 從控制臺輸入一個商品的資訊,將商品資訊寫入商品檔案,檔案中一行存盤一個商品資訊,
*/
public class Ex1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
OutputStream out = null;
try{
// 創建輸出流
out = new FileOutputStream("products.txt",true);
// 提示輸出商品的內容
String op = "";
do{
System.out.println("請輸入商品編號:");
String itemId = sc.next();
System.out.println("請輸入商品名稱:");
String itemName = sc.next();
System.out.println("請輸入商品價格:");
String itemPrice = sc.next();
// 用戶輸入的資料要寫入檔案
String info = itemId+"&"+itemName+"&"+itemPrice+"\n";
// 將字串寫入檔案
out.write(info.getBytes());
System.out.println("退出請輸入(exit):");
op = sc.next();
}while(!op.equalsIgnoreCase("exit"));
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
再來一個需求:
將上一個需求寫檔案的商品,讀取出來,使用[9527,尤克里里,1525] 形式輸出到控制臺,?
/**
* 將上一個需求寫檔案的商品,讀取出來,使用[9527,尤克里里,1525] 形式輸出到控制臺,
*/
public class Ex2 {
public static void main(String[] args) {
// 讀取所有的資料,再進行分割處理
InputStream in = null;
try{
// 創建輸入流
in = new FileInputStream("products.txt");
// 回圈的讀取所有的內容放在一個StringBuilde中
StringBuilder sb = new StringBuilder();
// 準備讀取
int len = -1;// 每次讀取的實際長度
byte [] buff = new byte[1024]; // 緩沖陣列
while((len = in.read(buff))!=-1){
// 將資料轉換為字串
String str = new String(buff,0,len);
// 將字串鍵入StringBuilder
sb.append(str);
}
System.out.println(sb.toString());
// 完整的商品資訊字串
String ps = sb.toString();
String[] rows = ps.split("\n");// 使用換行符拆分
for (String row : rows){
// 9530&路基亞打電腦&150
String[] attrs = row.split("&");
String info = "["+attrs[0]+","+attrs[1]+","+attrs[2]+"]";
System.out.println(info);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
檔案拷貝:
檔案拷貝流程:

我們來拷貝一個壓縮檔案:
public class CopyFileTest {
public static void main(String[] args) {
InputStream in = null;
OutputStream out = null;
try {
// -------------------------------------------
// 創建輸入流
in = new FileInputStream("java-SE.rar");
// 創建輸出流
out = new FileOutputStream("java-SE-V1.rar");
// 使用位元組流讀取資料
int len = -1;
byte [] buff = new byte[1024];
while((len = in.read(buff))!=-1){
// 寫出資料
out.write(buff,0,len);
}
// ---------------------------------------------
}catch (IOException e){
e.printStackTrace();;
}finally {
try{
if(in != null){
in.close();
}
if(out != null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字符流
字符流,就是按照字符處理檔案的,
位元組流,按照位元組處理檔案,可以處理所有的檔案,一般使用中,位元組流偏多,
文本檔案:純文本檔案,不保存任何的格式,檔案頭之類的資訊,
字符流往往是用來處理純文本檔案或者網路上的文本資訊,
字符輸入流Reader
Reader本身是一個抽象類
API:
-
abstract void close() 關閉流并釋放與之相關聯的任何系統資源,
-
void mark(int readAheadLimit) 標記流中的當前位置,
-
boolean markSupported() 告訴這個流是否支持mark()操作,
-
int read() 讀一個字符
-
int read(char[] cbuf) 將字符讀入陣列,
-
abstract int read(char[] cbuf, int off, int len) 將字符讀入陣列的一部分,
-
int read(CharBuffer target) 嘗試將字符讀入指定的字符緩沖區,
-
boolean ready() 告訴這個流是否準備好被讀取,
-
void reset() 重置流,
-
long skip(long n) 跳過字符
FileReader --Reader類的子類
也是我們常用的字符輸入流. (FileInputStream)
構造方法
-
FileReader(File file) 創建一個新的 FileReader ,給出 File讀取,
-
FileReader(String fileName) 創建一個新的 FileReader ,給定要讀取的檔案的名稱,
API:所有的API都是來自于父類,
使用:
public class FileReaderTest1 {
public static void main(String[] args) {
FileReader reader = null;
try{
// 創建reader
reader = new FileReader("names.txt");
// 讀取一個字符(這里回傳的是整形)
int read = reader.read();
// 將整形強制轉換為字符型
char ch = (char)read;
System.out.println(ch);
// 讀取一組字符
// 準備一個字符緩沖區
char [] buff = new char[20];
reader.read(buff);
System.out.println(Arrays.toString(buff));
// 讀取一部分字符,放在一個字符陣列的某一部分
int len = reader.read(buff,3,10);
System.out.println(Arrays.toString(buff));
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(reader!=null)
reader.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字符輸出流Writer
API:
-
Writer append(char c) 將指定的字符附加到此作者,
-
Writer append(CharSequence csq) 將指定的字符序列附加到此作者,
-
Writer append(CharSequence csq, int start, int end) 將指定字符序列的子序列附加到此作者,
-
abstract void close() 關閉流,先重繪,
-
abstract void flush() 重繪流,
-
void write(char[] cbuf) 寫入一個字符陣列,
-
abstract void write(char[] cbuf, int off, int len) 寫入字符陣列的一部分,
-
void write(int c) 寫一個字符
-
void write(String str) 寫一個字串
-
void write(String str, int off, int len) 寫一個字串的一部分,
FileWriter--在位元組流中對應:FileOutputStream
構造方法:
-
FileWriter(File file) 給一個File物件構造一個FileWriter物件,
-
FileWriter(File file, boolean append) 給一個File物件構造一個FileWriter物件,
-
FileWriter(String fileName) 構造一個給定檔案名的FileWriter物件,
-
FileWriter(String fileName, boolean append) 構造一個FileWriter物件,給出一個帶有布林值的檔案名,表示是否附加寫入的資料,
沒有自己的特殊API,全部是父類的API,
案例:
public class FileWriterTest1 {
public static void main(String[] args) {
FileWriter writer = null;
try{
// 創建writer (當寫入資料的時候,會自動創建這個檔案)
writer = new FileWriter("rzs.txt",true);
// 可以寫資料了
writer.write("木葉的旗木五五開老師!!!\n");
// 也可以寫一個字符
writer.write(96);
writer.write(97);
// 可以寫一個字符陣列
char [] chs = {'宇','智','波','五','五','開','\n'};
writer.write(chs);
// 寫字符陣列的一部分
writer.write(chs,3,4);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(writer!=null)
writer.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
下載一個HTML檔案
package com.qidian.spider;
?
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
?
public class DownloadHtml {
private static String path = "http://search.dangdang.com/?key=java&act=input";
public static void main(String[] args) {
// 申明
InputStream in = null;
OutputStream out = null;
try {
// 連接網路-------------
URL url = new URL(path);
HttpURLConnection urlConnection =
(HttpURLConnection) url.openConnection();
urlConnection.connect();
// 得到指向這個網頁的輸入流
in = urlConnection.getInputStream();
out = new FileOutputStream("hehe.html");
// 拷貝的程序
int len = -1;
byte [] buff = new byte[1024];
while((len = in.read(buff))!=-1){
out.write(buff,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null)
in.close();
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
字符流的檔案拷貝
package com.qidian.demo1;
?
import java.io.*;
public class CopyFileTest {
public static void main(String[] args) {
Reader reader = null;
Writer writer = null;
try {
reader = new FileReader("hehe.html");
writer = new FileWriter("haha.html");
int len = -1;
char [] buff = new char[256];
while((len = reader.read(buff))!=-1){
writer.write(buff,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(reader!=null)
reader.close();
if(writer!=null)
writer.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
緩沖字符輸入流:BufferedReader
構造方法:
-
BufferedReader(Reader in) 創建使用默認大小的輸入緩沖區的緩沖字符輸入流,
-
BufferedReader(Reader in, int sz) 創建使用指定大小的輸入緩沖區的緩沖字符輸入流,
在緩沖流中用到了“裝飾設計模式”, 使用BufferedReader包裹一個Reader,實作緩沖,
其中大部分API和Reader沒啥區別,有一個特殊的API:
-
String readLine() 讀一行文字,
案例:
public class BufferedReaderTest {
public static void main(String[] args) {
BufferedReader bufReader = null;
Reader reader = null;// 這個reader物件就是讓BufferedReader包裹的物件
try{
// 先創建reader
reader = new FileReader("names.txt");
// 創建緩沖輸入流 (任何需要父型別的地方,都可以使用子型別代替)
bufReader = new BufferedReader(reader);
// 按行讀取(讀取不到資料的時候,回傳null)
// String s = bufReader.readLine();
// System.out.println(s);
String line = null;
while((line = bufReader.readLine())!=null){
System.out.println(line);
};
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(bufReader!=null)
bufReader.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
緩沖字符輸出流--BufferedWriter
構造方法:
-
BufferedWriter(Writer out) 創建使用默認大小的輸出緩沖區的緩沖字符輸出流,
-
BufferedWriter(Writer out, int sz) 創建一個新的緩沖字符輸出流,使用給定大小的輸出緩沖區,
創建BufferedWriter的時候需要包裹一個Writer物件
特殊的API:
-
void newLine() 寫一行行分隔符,
緩沖輸出流要注意的問題:
當呼叫了緩沖輸出流的write方法之后,資料可能只是寫在緩沖區,并沒有實際寫到輸出流,理論上當關閉輸出流的時候,所有緩沖區的資料會刷出, 一般建議我們手動呼叫 flush方法刷出緩沖區資料,
案例:
public class BufferedWriterTest {
public static void main(String[] args) {
BufferedWriter bufWriter = null;
Writer writer = null;
try{
// 創建writer
writer = new FileWriter("rzs.txt");
// 創建BufferedWriter
bufWriter = new BufferedWriter(writer);
// 寫資料
// 回圈寫
for(int i = 0;i < 100;i++){
// 緩沖流在呼叫了write方法之后,不一定把資料寫出去了,
// 資料可能僅僅在緩沖區,
bufWriter.write("[adminName:'鳴人"+i+"',adminPass:'123456']");
// 寫入一個換行符
bufWriter.newLine();
}
// 刷出緩沖區的資料
//bufWriter.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
// 緩沖流在關閉的時候,理論上會將緩沖區所有的資料全部寫出去
try{
if(bufWriter!=null)
bufWriter.close();
if(writer!=null)
writer.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
緩沖位元組輸入流--BufferedInputStream
構造方法
-
BufferedInputStream(InputStream in) 創建一個 BufferedInputStream并保存其引數,輸入流 in ,供以后使用,
-
BufferedInputStream(InputStream in, int size) 創建 BufferedInputStream具有指定緩沖區大小,并保存其引數,輸入流 in ,供以后使用,
其他的API,不需要說明,和FileOutputStream用法完全一致,只不過提高了底層的效率,
緩沖位元組輸出流:BufferedOutputStream
構造方法
-
BufferedOutputStream(OutputStream out) 創建一個新的緩沖輸出流,以將資料寫入指定的底層輸出流,
-
BufferedOutputStream(OutputStream out, int size) 創建一個新的緩沖輸出流,以便以指定的緩沖區大小將資料寫入指定的底層輸出流,
API........你就把它當FileOutputStream使用就行了,
我們使用 緩沖(高效)位元組流完成一個檔案拷貝的程序,
案例:
public class CopyFileTest {
public static void main(String[] args) {
InputStream in = null;
OutputStream out = null;
BufferedInputStream bufIn = null;
BufferedOutputStream bufOut = null;
try{
// 創建4個流,
// 輸入流
in = new FileInputStream("java-SE.rar");
bufIn = new BufferedInputStream(in);
// 輸出流
out = new FileOutputStream("java-SE-V1.rar");
bufOut = new BufferedOutputStream(out);
// 拷貝程序,,,,,,
int len = -1;
byte [] buff = new byte[1024];
while((len = bufIn.read(buff))!=-1){
bufOut.write(buff,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
// 輸入流先關閉里面的流,在關閉外面的流
if(in!=null)
in.close();
if(bufIn!=null)
bufIn.close();
// 輸出流先關閉外面的流,再關閉里面的流
if(bufOut!=null)
bufOut.close();
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
轉換流目的:將位元組流轉換為字符流, 能不能將字符流轉換為位元組流?NO
轉換輸入流:InputStreamReader
構造方法:
-
InputStreamReader(InputStream in) 創建一個使用默認字符集的InputStreamReader,
-
InputStreamReader(InputStream in, Charset cs) 創建一個使用給定字符集的InputStreamReader,
-
InputStreamReader(InputStream in, CharsetDecoder dec) 創建一個使用給定字符集解碼器的InputStreamReader,
-
InputStreamReader(InputStream in, String charsetName) 創建一個使用命名字符集的InputStreamReader,
API和Reader一致,
轉換輸出流:OutputStreamWriter
-
OutputStreamWriter(OutputStream out) 創建一個使用默認字符編碼的OutputStreamWriter,
-
OutputStreamWriter(OutputStream out, Charset cs) 創建一個使用給定字符集的OutputStreamWriter,
-
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 創建一個使用給定字符集編碼器的OutputStreamWriter,
-
OutputStreamWriter(OutputStream out, String charsetName) 創建一個使用命名字符集的OutputStreamWriter,
API和writer完全一致,
案例:轉換輸入流,
public class InputStreamReaderTest {
public static void main(String[] args) {
InputStream in = null;
InputStreamReader reader = null;
BufferedReader bufReader = null;
try{
// 創建這些流
// 創建位元組流
in = new FileInputStream("names.txt");
// 將位元組流轉換字符流
reader = new InputStreamReader(in);
// 將字符流包裹為緩沖字符流
bufReader = new BufferedReader(reader);
// 回圈讀取資料
String line = null;
while((line = bufReader.readLine())!=null){
System.out.println(line);
};
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(bufReader!=null)
bufReader.close();
}catch (IOException ex){ex.printStackTrace();}
}
}
}
位元組輸出流轉換為字符輸出流:
案例:
public class OutputStreamWriterTest {
public static void main(String[] args) {
OutputStream out = null;
OutputStreamWriter writer = null;
try {
// 創建位元組流
out = new FileOutputStream("hehe.txt");
// 將位元組流包裹成字符流
writer = new OutputStreamWriter(out);
// 寫字串
writer.write("一個字串\n");
writer.write("下午兩點上課\n");
writer.write("上完可,做好防護,去做核算");
writer.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
物件流
序列化
為了方便資料的存盤或者傳輸,將資料或者物件轉換為可存盤或者傳輸的形式的程序稱之為序列化,相反就是反序列化,
例子:在線購物,賣家給你打包的程序就是序列化,你拆包裹的程序就是反序列化,
專案中的:
將一個物件轉換為json輸出到客戶端,將物件轉換為json就是序列化,
將JSON轉換物件的程序就是反序列化,
物件流就是專門用來對物件進行序列化和反序列化,
物件輸出流:ObjectOutputStream(寫出)
專門用來序列化物件的,(賣家打包)
構造方法:
-
ObjectOutputStream(OutputStream out) 創建一個寫入指定的OutputStream的ObjectOutputStream,
物件輸出流內部其實是包裹了一個位元組輸出流,
物件輸出流的作用:

API:
-
void close() 關閉流,
-
void write(byte[] buf) 寫入一個位元組陣列,
-
void write(byte[] buf, int off, int len) 寫入一個子位元組陣列,
-
void write(int val) 寫一個位元組,
-
void writeBoolean(boolean val) 寫一個布林值,
-
void writeByte(int val) 寫入一個8位位元組,
-
void writeBytes(String str) 寫一個字串作為位元組序列,
-
void writeChar(int val) 寫一個16位的字符,
-
void writeChars(String str) 寫一個字串作為一系列的字符,
-
void writeDouble(double val) 寫一個64位的雙倍,
-
void writeFloat(float val) 寫一個32位浮點數,
-
void writeInt(int val) 寫一個32位int,
-
void writeLong(long val) 寫一個64位長
-
void writeShort(int val) 寫一個16位短,
-
void writeObject(Object obj) 將指定的物件寫入ObjectOutputStream,
物件輸出流是寫一個物件出去的操作,
java中都有哪些物件?
java中的物件真的很多,但是所有的物件統稱為Object,其實物件流特意準備了幾個方法,寫這些物件,
-
writeXxxx(Xxx xxx) 這里的Xxxx表示任何一個基本型別,
-
writeObject(Object obj) 其余的型別全部使用Object,
案例1: 序列化基本型別
public class ObjectWriteTest1 {
public static void main(String[] args) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try{
// 創建位元組輸出流
out = new FileOutputStream("ints.txt");
// 創建物件流
objOut = new ObjectOutputStream(out);
// 寫出一個整形
objOut.writeInt(12);
objOut.writeDouble(12.5);
// 刷出資料
objOut.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
案例2: 序列化自定義型別
準備一個物體類:
public class User {
private int id;
private String name;
}
測驗:

出現例外,
這個例外產生的原因是因為ObjectOutputStream寫的物件必須實作介面java.io.Serializable,否則無法序列化, 這個介面本身是沒有任何的方法和成員的,它就是用來標記一個類的物件是否可以被序列化,
public interface Serializable {
}
我們讓我們的User類實作這個介面:

完整的測驗代碼:
public class ObjectWriteTest2 {
public static void main(String[] args) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try{
// 創建位元組輸出流
out = new FileOutputStream("u.txt");
// 創建物件流
objOut = new ObjectOutputStream(out);
// 創建一個User
User u = new User(1,"就烏爾奇");
// 寫出物件u
objOut.writeObject(u);
// 刷出資料
objOut.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
物件輸入流:ObjectInputStream(讀取)
專門用來反序列化物件的,(買家拆包裹)
構造方法:
-
ObjectInputStream(InputStream in) 創建從指定的InputStream讀取的ObjectInputStream,
ObjectInputStream會包裹一個InputStream
其他的API:
-
int read() 讀取一個位元組的資料,
-
int read(byte[] buf, int off, int len) 讀入一個位元組陣列,
-
boolean readBoolean() 讀取布林值,
-
byte readByte() 讀取一個8位位元組,
-
char readChar() 讀一個16位字符,
-
double readDouble() 讀64位雙倍,
-
float readFloat() 讀32位浮點數,
-
int readInt() 讀取一個32位int,
-
long readLong() 讀64位長,
-
short readShort() 讀取16位短,
-
Object readObject() 從ObjectInputStream讀取一個物件,
有個小問題:無法判定是否到達了檔案的末尾,
當使用ObjectInputStream讀取資料的時候,如果出現了EOFException,說明到達了檔案的末尾,

案例1: 讀取上一小節寫入檔案的int和double,
public class ObjectInputTest1 {
public static void main(String[] args) {
InputStream in = null;
ObjectInputStream objIn = null;
try{
// 創建位元組流
in = new FileInputStream("ints.txt");
// 創建物件流
objIn = new ObjectInputStream(in);
// 反序列化資料
int i = objIn.readInt();
System.out.println(i);
double d = objIn.readDouble();
System.out.println(d);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(objIn!=null)
objIn.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
案例2:讀取寫入的自定義物件
public class ObjectInputTest2 {
public static void main(String[] args) {
InputStream in = null;
ObjectInputStream objIn = null;
try{
// 創建位元組流
in = new FileInputStream("u.txt");
// 創建物件流
objIn = new ObjectInputStream(in);
// 反序列化資料
Object o = objIn.readObject();
// 型別轉換
User u = (User) o;
System.out.println(u);
}catch (Exception e){
e.printStackTrace();
}finally {
try{
if(objIn!=null)
objIn.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
一組物件的讀寫
情況1: 回圈讀寫
回圈的寫入資料
public class ObjectWriteTest3 {
public static void main(String[] args) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try{
// 創建位元組輸出流
out = new FileOutputStream("us.txt");
// 創建物件流
objOut = new ObjectOutputStream(out);
// 創建一組User
List<User> us = new ArrayList<>();
for (int i = 0;i< 100; i++){
us.add(new User(i+1,"鳴人"+i));
}
// 回圈的寫出物件
for (User u : us) {
objOut.writeObject(u);
}
// 刷出資料
objOut.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
回圈的讀資料
public class ObjectInputTest3 {
public static void main(String[] args) {
InputStream in = null;
ObjectInputStream objIn = null;
try{
// 創建位元組流
in = new FileInputStream("us.txt");
// 創建物件流
objIn = new ObjectInputStream(in);
// 反序列化資料
// 回圈讀取資料
// 知道回圈次數的處理
// for (int i = 0;i < 100;i++){
// Object o = objIn.readObject();
// User u = (User) o;
// System.out.println(u);
// }
// 不知道回圈次數
while(true){
// 當到達檔案的末尾的時候,會拋出例外EOFException
Object o = objIn.readObject();
User u = (User) o;
System.out.println(u);
}
}catch(EOFException e){
System.out.println("讀取完成");
} catch (Exception e){
e.printStackTrace();
}finally {
try{
if(objIn!=null)
objIn.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
本文來自博客園,作者:{瀟瀟消消氣},轉載請注明原文鏈接:{https://www.cnblogs.com/xiaoxiaodeboke/}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/445858.html
標籤:Java
