文件傳輸基礎(Java IO流)——文件操作
編碼
package com.zdy;
import java.io.UnsupportedEncodingException;
/**
* Created by zdy on 2017/1/19.
*/
public class EncodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String s="慕課ABC";
/*utf-8 中文 3個字節,英文 1個字節*/
byte[] bytes1=s.getBytes("utf-8");
for(byte b:bytes1){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println();
/*gbk 中文 2個字節,英文 1個字節*/
byte[] bytes2=s.getBytes("gbk");
for(byte b:bytes2){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println();
/*utf-16be 中文 2個字節,英文 2個字節*/
byte[] bytes3=s.getBytes("utf-16be");
for(byte b:bytes3){
System.out.print(Integer.toHexString(b&0xff)+" ");
}
System.out.println();
String str1=new String(bytes1,"utf-8");
System.out.println(str1);
String str2=new String(bytes2,"gbk");
System.out.println(str2);
String str3=new String(bytes3,"utf-16be");
System.out.println(str3);
}
}
File類
- java.io.File類用于表示文件(目錄)
- File類只用于表示文件(目錄)的信息(名稱、大小等),不能用于文件內容的訪問
提示:為了跨平臺,目錄分割符建議使用斜線/
或者File.separator
語法:
//文件對象 mac,linux中注意權限問題
File file=new File("/Users/zdy/Workspace/test");
//是否存在,存在true,不存在false
file.exists()
//是否是目錄,是目錄true,不是目錄或者目錄不存在false
file.isDirectory()
//是否是文件,是文件true,不是目錄或者文件不存在false
file.isFile()
//創建目錄
file.mkdir()
//創建多級目錄
file.mkdirs()
//創建文件
file.createNewFile()
//刪除目錄或文件
file.delete()
//打印文件路徑,file.toString(),例如 /Users/zdy/Workspace/test/1.txt
System.out.println(file);
//絕對路徑
file.getAbsolutePath();
//文件名或者路徑名(僅最后一個節點)
file.getName()
//父目錄 名
System.out.println(file.getParent());
System.out.println(file.getParentFile().getAbsolutePath());
【示例】
package com.zdy;
import java.io.File;
import java.io.IOException;
/**
* Created by zdy on 2017/1/19.
*/
public class FileDemo {
public static void main(String[] args) throws IOException {
File file=new File("/Users/zdy/Workspace/test/1.txt");
//File file2=new File(File.separator+"Users");
/*是否存在*/
System.out.println(file.exists());
/*是否是目錄*/
System.out.println(file.isDirectory());
/*是否是文件*/
System.out.println(file.isFile());
if (!file.exists()){
file.createNewFile();
}else{
//file.delete();
}
System.out.println(file);
System.out.println(file.getParentFile().getName());
}
}
目錄遍歷實例
【FileUtils.java】
package com.zdy;
import java.io.File;
/**
* Created by zdy on 2017/1/19.
*/
public class FileUtils {
public static void listDirectory(File dir){
if (!dir.exists()){
throw new IllegalArgumentException("目錄"+dir+"不存在.");
}
if (!dir.isDirectory()){
throw new IllegalArgumentException(dir+"不是目錄");
}
/*
String[] filenames=dir.list();//返回字符串數組,只列出直接子文件或子目錄
for (String filename:filenames){
System.out.println(dir+filename);
}*/
//遍歷子目錄,遞歸操作
File[] files=dir.listFiles();//返回直接子目錄文件的抽象
if (files!=null&&files.length>0){
for (File file:files){
if (file.isDirectory()){
listDirectory(file);
}else{
System.out.println(file);
}
}
}
}
}
【FileUtilTest.java】
package com.zdy;
import java.io.File;
/**
* Created by zdy on 2017/1/19.
*/
public class FileUtilTest {
public static void main(String[] args) {
FileUtils.listDirectory(new File("/Users/zdy/Workspace"));
}
}
RandomAccessFile
- java提供的對文件內容的訪問,既可以讀文件,也可以寫文件
- 支持隨機訪問文件,可以訪問文件的任意位置
語法
//打開文件。兩種模式,讀寫`rw`只讀`r`
RandomAccessFile randomAccessFile=new RandomAccessFile(file,"rw");
//寫,寫一個字節(8位),同時指針指向下一個位置,準備再次寫入
randomAccessFile.write();
//寫入字符‘A’
randomAccessFile.write('A');
//寫入數字1
int i=1;
randomAccessFile.writeInt(i);
//寫入 ”張“
String s="張";
byte[] b=s.getBytes();
randomAccessFile.write(b);
//讀,讀一個字節,或者讀到一個字節數組
randomAccessFile.read();
randomAccessFile.read(buf);
//獲取指針的位置
randomAccessFile.getFilePointer()
//指針移到某處,比如頭部
randomAccessFile.seek(0)
//文件的字節數(long型,可強轉為int)
randomAccessFile.length()
//關閉文件
randomAccessFile.close()
【示例】
package com.zdy;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* Created by zdy on 2017/1/20.
*/
public class RafDemo {
public static void main(String[] args) throws IOException {
File demo=new File("/Users/zdy/Workspace/test/demo.dat");
if (demo.exists()){
demo.delete();
}
if (!demo.exists()){
demo.createNewFile();
}
RandomAccessFile randomAccessFile=new RandomAccessFile(demo,"rw");
System.out.println(randomAccessFile.getFilePointer());
randomAccessFile.write('A');
//randomAccessFile.read();
int i=1;
randomAccessFile.write(i>>>24);
randomAccessFile.write(i>>>16);
randomAccessFile.write(i>>>8);
randomAccessFile.write(i);
randomAccessFile.writeInt(i);
String s="張";
byte[] b=s.getBytes();
randomAccessFile.write(b);
System.out.println(randomAccessFile.length());
randomAccessFile.seek(0);
byte[] buf=new byte[(int)randomAccessFile.length()];
randomAccessFile.read(buf);
System.out.println(Arrays.toString(buf));
String s1=new String(buf);
System.out.println(s1);
//以十六進制形式打印
for (byte b1:buf) {
System.out.print(Integer.toHexString(b1&0xff)+" ");
}
randomAccessFile.close();
}
}
IO流
字節流/字符流 ;輸入流/輸出流
字節流
InputStream OutputStream
- InputStream抽象了應用程序讀取數據的方式
- OutputStream抽象了應用程序寫入數據的方式
EOF(-1)
輸入流
//讀取一個字節無符號填充到int低八位。-1是EOF
int b=in.read()
//讀取數據填充到字節數組buf
in.read(byte[] buf)
//讀取數據到字節數組buf,從buf的start位置開始存放size長度的數據
in.read(byte[] buf,int start,int size)
輸出流
//寫出一個byte到流,b的第八位
out.write(int b)
//將buf字節數組寫入到流
out.write(byte[] buf)
//字節數組buf從start位置開始寫szie長度的字節到流
out.write(byte[],int start,int size)
FileInputStream
具體實現了在文件上讀取數據
示例:
【IOUtil.java】
package com.zdy;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Created by zdy on 2017/1/20.
*/
public class IOUtil {
/*讀取制定文件內容,按照16進制輸出到控制臺*/
public static void printHex(String fileName) throws IOException {
FileInputStream fileInputStream=new FileInputStream(fileName);
int b;
int i=1;
while ((b= fileInputStream.read())!=-1){
if (b<=0xf){
//單位數前面補零
System.out.print("0");
}
System.out.print(Integer.toHexString(b)+" ");
if (i++%10==0){
System.out.println();
}
}
fileInputStream.close();
}
public static void printHexByByteArray(String fileName) throws IOException {
FileInputStream fileInputStream=new FileInputStream(fileName);
byte[] buf=new byte[20*1024];
int bytes=0;
int j=1;
while ((bytes=fileInputStream.read(buf,0,buf.length))!=-1) {
for (int i=0;i<bytes;i++){
if((buf[i]&0xff) <=0xf){
System.out.print("0");
}
System.out.print(Integer.toHexString(buf[i]&0xff)+" ");
if (j++%10==0){
System.out.println();
}
}
}
fileInputStream.close();
}
}
【IOUtilTest.java】
package com.zdy;
import java.io.IOException;
/**
* Created by zdy on 2017/1/20.
*/
public class IOUtilTest {
public static void main(String[] args) {
try {
IOUtil.printHex("/Users/zdy/Workspace/test/demo.dat");
System.out.println();
IOUtil.printHexByByteArray("/Users/zdy/Workspace/test/demo.dat");
} catch (IOException e) {
e.printStackTrace();
}
}
}
【輸出示例】
41 00 00 00 01 00 00 00 01 e5
bc a0
41 00 00 00 01 00 00 00 01 e5
bc a0
FileOutputStream
【FIleOutputStreamDemo:寫文件demo】
package com.zdy;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by zdy on 2017/1/20.
*/
public class FIleOutputStreamDemo {
public static void main(String[] args) throws IOException {
//存在文件,刪除后重新創建;不存在,創建文件
FileOutputStream fileOutputStream=new FileOutputStream("/Users/zdy/Workspace/test/out.dat",true);
fileOutputStream.write('A');
fileOutputStream.write('B');
int a=10;
fileOutputStream.write(a>>>24);
fileOutputStream.write(a>>>16);
fileOutputStream.write(a>>>8);
fileOutputStream.write(a);
byte[] bytes="張丹陽".getBytes();
fileOutputStream.write(bytes);
fileOutputStream.close();
IOUtil.printHex("/Users/zdy/Workspace/test/out.dat");
}
}
復制文件示例
【IOUtil.java】
public class IOUtil {
/* 批量字節拷貝,效率最高*/
public static void copyFile(File srcFile,File destFile) throws IOException {
if (!srcFile.exists()){
throw new IllegalArgumentException("源文件"+srcFile+"不存在");
}
if (!srcFile.isFile()){
throw new IllegalArgumentException(srcFile+"不是文件");
}
FileInputStream fileInputStream=new FileInputStream(srcFile);
FileOutputStream fileOutputStream=new FileOutputStream(destFile);
byte[] buf=new byte[20*1024];
int b;
while ((b=fileInputStream.read(buf,0,buf.length))!=-1){
fileOutputStream.write(buf,0,b);
fileOutputStream.flush();
}
fileInputStream.close();
fileOutputStream.close();
}
}
【IOUtilTest.java】
package com.zdy;
import java.io.File;
import java.io.IOException;
/**
* Created by zdy on 2017/1/20.
*/
public class IOUtilTest {
public static void main(String[] args) {
try {
IOUtil.copyFile(new File("/Users/zdy/Workspace/test/demo.dat"),new File("/Users/zdy/Workspace/test/copydemo.dat"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
DataInputStream/DataOutputStream
對流功能的擴展,可以更加方便的讀取int,long,字符等類型數據。
【DataInputStreamDemo.java】
package com.zdy;
import java.io.*;
/**
* Created by zdy on 2017/1/20.
*/
public class DataInputStreamDemo {
public static void main(String[] args) throws IOException {
DataInputStream dataInputStream=new DataInputStream(new FileInputStream("/Users/zdy/Workspace/test/dos.txt"));
System.out.println(dataInputStream.readInt());
System.out.println(dataInputStream.readInt());
System.out.println(dataInputStream.readFloat());
System.out.println(dataInputStream.readLong());
System.out.println(dataInputStream.readUTF());//utf-8
System.out.println(dataInputStream.readChar());//utf-16be
dataInputStream.close();
}
}
【DataOutputStreamDemo.java】
package com.zdy;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by zdy on 2017/1/20.
*/
public class DataOutputStreamDemo {
public static void main(String[] args) throws IOException {
DataOutputStream dataOutputStream=new DataOutputStream(new FileOutputStream("/Users/zdy/Workspace/test/dos.txt"));
dataOutputStream.writeInt(10);
dataOutputStream.writeInt(-10);
dataOutputStream.writeFloat(10.5f);
dataOutputStream.writeLong(1000L);
dataOutputStream.writeUTF("張丹陽");//utf-8
dataOutputStream.writeChar('A');//utf-16be
dataOutputStream.close();
}
}
BufferedInputStream/BufferedOutputStream
這兩個流類為IO提供了帶緩沖區的操作,一般打開文件進行寫入或讀取操作時,都會加上緩沖,這種流模式提高了IO的性能,
【IOUtil.java】
public class IOUtil {
/*通過緩沖區拷貝*/
public static void copyFileByBuffer(File srcFile,File destFile) throws IOException {
if (!srcFile.exists()){
throw new IllegalArgumentException("源文件"+srcFile+"不存在");
}
if (!srcFile.isFile()){
throw new IllegalArgumentException(srcFile+"不是文件");
}
BufferedInputStream bufferedInputStream=new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(new FileOutputStream(destFile));
int c;
while ((c=bufferedInputStream.read())!=-1){
bufferedOutputStream.write(c);
bufferedOutputStream.flush();//刷新緩沖區
}
bufferedInputStream.close();
bufferedOutputStream.close();
}
}
【IOUtilTest.java】
package com.zdy;
import java.io.File;
import java.io.IOException;
/**
* Created by zdy on 2017/1/20.
*/
public class IOUtilTest {
public static void main(String[] args) {
try {
IOUtil.copyFileByBuffer(new File("/Users/zdy/Workspace/test/demo.dat"),new File("/Users/zdy/Workspace/test/demo.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流
認識文本和文本文件
- java的文本char是16位無符號整數,是字符的unicode編碼(雙字節編碼)
- 文件是byte的數據序列
- 文本文件是char序列按照某種編程方案(utf-8,utf-16be,gbk)序列華為byte的存儲
字符流
- 字符的處理,一次處理一個字符
- 字符的底層依然是基本的字節序列
用來操作文本文件。
字符流的基本實現
- InputStreamReader 完成byte流解析為char流,按照編碼解析
- OutputStreamWriter 提供char流到byte流,按照編碼處理
【示例:InputStreamReaderAndOutputStreamReaderDemo.java】
package com.zdy;
import java.io.*;
/**
* Created by zdy on 2017/1/22.
*/
public class InputStreamReaderAndOutputStreamReaderDemo {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream=new FileInputStream("/Users/zdy/Workspace/test/test.txt");
FileOutputStream fileOutputStream=new FileOutputStream("/Users/zdy/Workspace/test/test2.txt");
InputStreamReader inputStreamReader=new InputStreamReader(fileInputStream,"utf-8");
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(fileOutputStream,"utf-8");
/*int c;
while((c=inputStreamReader.read())!=-1){
System.out.println((char) c);
}*/
char[] buffer=new char[8*1024];
int c;
while ((c=inputStreamReader.read(buffer,0,buffer.length))!=-1){
String s=new String(buffer,0,c);
System.out.println(s);
outputStreamWriter.write(buffer,0,c);
}
inputStreamReader.close();
outputStreamWriter.close();
fileInputStream.close();
fileOutputStream.close();
}
}
- FileReader
- FileWriter
【示例:FileReaderAndFileWriterDemo.java】
package com.zdy;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* Created by zdy on 2017/1/22.
*/
public class FileReaderAndFileWriterDemo {
public static void main(String[] args) throws IOException {
FileReader fileReader=new FileReader("/Users/zdy/Workspace/test/test.txt");
FileWriter fileWriter=new FileWriter("/Users/zdy/Workspace/test/test2.txt",true);
char []buffer=new char[1024*8];
int c;
while ((c=fileReader.read(buffer,0,buffer.length))!=-1){
fileWriter.write(buffer,0,c);
}
fileWriter.flush();
fileReader.close();
fileWriter.close();
}
}
字符流的過濾器
- BufferedReader ,readLine一次讀一行
- BuffererWriter/PrintWriter 寫一行
【示例:BufferedReaderAndBufferedWriterAndPrintWriterDemo.java】
package com.zdy;
import java.io.*;
/**
* Created by zdy on 2017/1/22.
*/
public class BufferedReaderAndBufferedWriterAndPrintWriterDemo {
public static void main(String[] args) throws IOException{
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(new FileInputStream("/Users/zdy/Workspace/test/test.txt")));
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/Users/zdy/Workspace/test/test2.txt")));
PrintWriter printWriter=new PrintWriter("/Users/zdy/Workspace/test/test3.txt");
String line;
while ((line=bufferedReader.readLine())!=null){
System.out.println(line);
bufferedWriter.write(line);
//單獨寫換行
bufferedWriter.newLine();
printWriter.println(line);
}
bufferedWriter.flush();
printWriter.flush();
bufferedReader.close();
bufferedWriter.close();
printWriter.close();
}
}
對象序列化/反序列化
對象序列化,就是將Object轉換成byte序列,反之叫對象的反序列化
序列化流(ObjectOutputStream)---writeObject;反序列化流(ObjectInputStream)---readObject
序列化接口(Serializable):對象必須實現序列化接口,才能進行序列化,否則將出現異常。這個接口,沒有任何方法,知識一個標準。
Java序列化就是把對象轉換成字節序列,而Java反序列化就是把字節序列還原成Java對象。
采用Java序列化與反序列化技術,一是可以實現數據的持久化,在MVC模式中很是有用;二是可以對象數據的遠程通信。
【Student.java】
package com.zdy;
import java.io.Serializable;
/**
- Created by zdy on 2017/1/22.
*/
public class Student implements Serializable{
private String no;
private String name;
private int age;
public Student() {
}
public Student(String no, String name, int age) {
this.no = no;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"no='" + no + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
【ObjectSerializableDemo.java】
package com.zdy;
import java.io.*;
/**
* Created by zdy on 2017/1/22.
*/
public class ObjectSerializableDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String file="/Users/zdy/Workspace/test/obj.dat";
/*對象的序列化*/
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));
Student student=new Student("10001","張三",22);
objectOutputStream.writeObject(student);
objectOutputStream.flush();
objectOutputStream.close();
/*對象的反序列化*/
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));
Student stu=(Student)objectInputStream.readObject();
System.out.println(stu.toString());
objectInputStream.close();
}
}
【打印結果】
Student{no='10001', name='張三', age=22}
transient,不進行序列化
修改 Student.java
中private int age;
為private transient int age;
【打印結果】
Student{no='10001', name='張三', age=0}
單獨序列化
在某些情況下可以提高性能
在private transient int age;
的基礎上,向Student.java
添加下面方法:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
s.defaultWriteObject();
s.writeInt(age);
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
this.age=s.readInt();
}
【打印結果】
Student{no='10001', name='張三', age=22}
序列化中子類和父類
一個類實現了序列化接口,那么其子類都可以進行序列化。
對子類對象進行反序列化時,如果其父類沒有實現序列化接口,那么其父類的構造函數會被顯式調用。
[一條慕課網視頻評論]在創建子類對象的時候,同時就需要創建其所有直接以及間接父類對象(沒父親自然就沒兒子),所以,如果最早的父類實現了Serializable接口,那么將二級子類序列化寫入文件的時候,實際上是對父類,一級子類,二級子類三個對象都進行了序列化并寫入文件,所以,再將文件中內容反序列化到一個新創建的二級子類對象的時候(實際上就是將文件中的內容反序列化給這個新對象),自然就不需要調用那些父類的構造函數,因為文件里面已經都有了,干嘛還要再構造一次