一、對象序列化
ObjectOutputStram和ObjectInputStream
1术陶、概述:
將堆內(nèi)存中的對象存入硬盤磕瓷,保留對象中的數(shù)據(jù)膘螟,稱之為對象的持久化(或序列化)
2笙僚、特有方法:
1芳肌、write(int val)
---> 寫入一個字節(jié)(最低八位)
2、writeInt(int vale)
---> 吸入一個32為int值
3肋层、使用步驟:
說明:serialVersion
a亿笤、給類一個可被編譯器識別的的序列號,在編譯類時栋猖,會分配一個long型UID责嚷,通過序列號,將類存入硬盤中掂铐,并序列化,即持久化揍异。序列號根據(jù)成員算出的全陨。靜態(tài)不能被序列化。如果非靜態(tài)成員也無需序列化衷掷,可以用transien修飾辱姨。
b、接口Serializable中沒有方法戚嗅,稱之為標記接口
1雨涛、寫入流對象:
1)創(chuàng)建對象寫入流枢舶,與文件關聯(lián),即傳入目的
2)通過寫入writeObject()方法替久,將對象作為參數(shù)傳入凉泄,即可寫入文件
2、讀取流對象
1)創(chuàng)建對象讀取流蚯根,與文件關聯(lián)后众,即傳入源
2)通過writeObject()方法,讀取文件中的對象颅拦,并返回這個對象
示例:
import java.io.*;
//創(chuàng)建Person類蒂誉,實現(xiàn)序列化
class Person implements Serializable{
//定義自身的序列化方式
public static final long serialVersionUID = 42L;
//定義私有屬性
private String name;
private int age;
transient String id;
static String country = "cn";
//構造Person類
Person(String name,int age,String id,String country){
this.name = name;
this.age = age;
this.id = id;
this.country = country;
}
//覆寫toString方法
public String toString(){
return name+ ":" + age + ":" + id + ":" + country;
}
}
//對象序列化測試
class ObjectStreamDemo{
public static void main(String[] args){
//對象寫入流
writeObj();
//對象讀取流
readObj();
}
//定義對象讀取流
public static void readObj(){
ObjectInputStream ois = null;
try{
//創(chuàng)建對象讀取流
ois = new ObjectInputStream(new FileInputStream("obj.txt"));
//通過讀取文件數(shù)據(jù),返回對象
Person p = (Person)ois.readObject();
System.out.println(p);
}catch (Exception e){
throw new RuntimeException("寫入文件失敗");
}
//最終關閉流對象
finally{
try{
if(ois!=null)
ois.close();
}catch (IOException e){
throw new RuntimeException("寫入流關閉失敗");
}
}
}
//定義對象寫入流
public static void writeObj(){
ObjectOutputStream oos = null;
try{
//創(chuàng)建對象寫入流
oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
//寫入對象數(shù)據(jù)
oos.writeObject(new Person("lisi",25,"01","cn"));
}catch (Exception e){
throw new RuntimeException("寫入文件失敗");
}
//關閉流資源
finally{
try{
if(oos!=null)
oos.close();
}catch (IOException e){
throw new RuntimeException("寫入流關閉失敗");
}
}
}
}
二距帅、管道流
1右锨、概述:
1、管道流:PipedInputStream和PipedOutputStream
2碌秸、管道流涉及到多線程的問題
2绍移、使用步驟:
1、要先創(chuàng)建一個讀和寫的兩個類哮肚,實現(xiàn)Runnable接口登夫,因為是兩個不同的線程,覆蓋run方法允趟,注意恼策,需要在內(nèi)部拋異常
2、創(chuàng)建兩個管道流潮剪,并用connect()方法將兩個流連接
3涣楷、創(chuàng)建讀寫對象,并傳入兩個線程內(nèi)抗碰,并start執(zhí)行
示例:
import java.io.*;
//創(chuàng)建Read類狮斗,實現(xiàn)run方法
class Read implements Runnable{
private PipedInputStream in;
Read(PipedInputStream in){
this.in = in;
}
//實現(xiàn)run方法
public void run(){
try{
//讀取寫入的數(shù)據(jù)
//System.out.println("開始寫入數(shù)據(jù),等待時間");//測試用
Thread.sleep(3000);
byte[] b = new byte[1024];
int len = in.read(b);
//System.out.println("讀取完畢");//測試用
String s = new String(b,0,len);
System.out.println(s);
in.close();
}catch (Exception e){
throw new RuntimeException("管道流讀取失敗");
}
}
}
//創(chuàng)建Write類
class Write implements Runnable{
private PipedOutputStream out;
//Write構造函數(shù)
Write(PipedOutputStream out){
this.out = out;
}
//實現(xiàn)run方法
public void run(){
try{
//寫入數(shù)據(jù)
//System.out.println("開始寫入數(shù)據(jù)");//測試用
out.write("管道流來嘍~~~~".getBytes());
out.close();
}catch (IOException e){
throw new RuntimeException("管道流寫入失敗");
}
}
}
class PipedStreamDemo{
public static void main(String[] args) throws IOException
{
//創(chuàng)建管道流對象
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
//將讀取流(輸入流)和寫入流(輸出流)關聯(lián)起來
in.connect(out);
//創(chuàng)建讀寫對象弧蝇,并創(chuàng)建線程對象
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
三碳褒、RandomAccessFile 類
1、概述:
1看疗、RandomAccessFile稱之為隨機訪問文件的類沙峻,自身具備讀寫方法。
2两芳、該類不算是IO體系中的子類摔寨,而是直接繼承Object,但是它是IO包成員怖辆,因為它具備讀寫功能是复,內(nèi)部封裝了一個數(shù)組删顶,且通過指針對數(shù)組的元素進行操作,同時可通過seek改變指針的位置淑廊。
3逗余、可以完成讀寫的原理:內(nèi)部封裝了字節(jié)輸入流
4、構造函數(shù):RandomAccessFile(File file,String mode)蒋纬,可已從它的構造函數(shù)中看出猎荠,該類只能操作文件(也有字符串),而且操作文件還有模式蜀备。
模式傳入值:”r“:以只讀方式打開关摇;”rw“:打開以便讀寫
如果模式為只讀,則不會創(chuàng)建文件碾阁,會去讀一個已存在的文件输虱,若文件不存在,則會出現(xiàn)異常脂凶,如果模式為rw宪睹,且該對象的構造函數(shù)要操作的文件不存在,會自動創(chuàng)建蚕钦,如果存在亭病,則不會覆蓋,也可通過seek方法修改嘶居。
2罪帖、特有方法:
1、seek(int n):設置指針邮屁,可以將指針設置到前面或后面
2整袁、skipBytes(int n):跳過指定字節(jié)數(shù),不可往前跳
3佑吝、使用步驟:
1坐昙、創(chuàng)建RandomAccessFile對象
2、將數(shù)據(jù)寫入到指定文件中
3芋忿、讀取數(shù)據(jù)炸客,讀入到指定文件中
注意:堯都區(qū)后面的數(shù)據(jù),需要調(diào)用數(shù)組指針戈钢,通過改變角標位置嚷量,取出相應的數(shù)據(jù)
a.調(diào)整對象的指針:seek()
b.跳過指定字節(jié)數(shù)
示例:
import java.io.*;
//注:這幾個函數(shù)內(nèi)部都需要try,為測試逆趣,在函數(shù)上拋異常
class RanAccFileDemo{
public static void main(String[] args) throws IOException{
//readRaf();
readRaf2();
//writeRaf();
}
//寫入數(shù)據(jù)
public static void writeRaf()throws IOException{
//創(chuàng)建對象,寫入數(shù)據(jù)
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("王五".getBytes());
raf.writeInt(99);
raf.write("李四".getBytes());
raf.writeInt(97);
raf.close();
}
//讀取數(shù)據(jù)
public static void readRaf()throws IOException{
//創(chuàng)建對象嗜历,讀取數(shù)據(jù)
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
byte[] b = new byte[4];
raf.read(b);
String name = new String(b);
int age = raf.readInt();
System.out.println("name="+ name);
System.out.println("age=" + age);
raf.close();
}
//讀取數(shù)據(jù)
public static void readRaf2()throws IOException {
//創(chuàng)建對象宣渗,讀取數(shù)據(jù)
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//調(diào)整對象中的指針
//raf.seek(8);
//跳過指定字節(jié)數(shù)
raf.skipBytes(8);
byte[] b = new byte[4];
raf.read(b);
String name = new String(b);
int age = raf.readInt();
System.out.println("name="+ name);
System.out.println("age=" + age);
raf.close();
}
}
四抖所、操作基本數(shù)據(jù)類型的流對象
1、概述:
1痕囱、操作基本數(shù)據(jù)類型的流對象:DataInputStream和DataOutputStream
2田轧、這兩個讀寫對象,可用于操作基本數(shù)據(jù)類型的流對象鞍恢,包含讀寫各種基本數(shù)據(jù)類型的方法
2傻粘、特有方法:
讀 | 寫 | |
---|---|---|
int型 | writeInt(int n) | int readInt() |
boolean型 | writeBoolean(boolean b) | boolean readBoolean() |
double型 | writeDouble(double d) | double readDouble() |
五、操作數(shù)組和字符串
1帮掉、操作字節(jié)數(shù)組的對象:ByteArrayInputStream和ByteArrayOutputStream
1弦悉、這個對象并沒有調(diào)用底層資源,所以不用關閉流資源
2蟆炊、存入的是緩沖區(qū)稽莉,并未用到鍵盤和硬盤燈,所以不需要拋任何IO異常
3涩搓、對象中封裝了數(shù)組
4污秆、構造函數(shù):
1)ByteArrayInputStream:在構造函數(shù)的時候,需要接受數(shù)據(jù)源昧甘,而且數(shù)據(jù)源是一個字節(jié)數(shù)據(jù)良拼。
2)ByteArrayOutputStream:在構造函數(shù)的時候,不用定義數(shù)據(jù)目的充边,因為該對象中已經(jīng)在內(nèi)部封裝了可變長度的字節(jié)數(shù)組庸推,這就是數(shù)據(jù)的目的地
4、因為兩個流對象都是操作的是數(shù)據(jù)痛黎,并沒有使用系統(tǒng)資源予弧,所以不用進行close關閉。
6湖饱、其實就是用流的思想操作數(shù)組
7掖蛤、特有方法:writeTo(OutputStream out) 這個方法用到了字節(jié)輸出流,有異常存在井厌,需要拋IO異常
2蚓庭、對應的字符數(shù)組和字符串:
字符數(shù)組流對象:CharArrayReader和CharArrayWriter
字符串流對象: StringReader和StringWriter
示例:
import java.io.*;
class ArrayStreamDemo
{
public static void main(String[] args)
{
//數(shù)據(jù)源
ByteArrayInputStream bais = new ByteArrayInputStream("ABCDEFF".getBytes());
//數(shù)據(jù)目的
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int by = 0;
//讀取和寫入數(shù)據(jù)
while((by=bais.read())!=-1)
{
baos.write(by);
}
System.out.println(baos.size());
System.out.println(baos.toString());
try
{
//方法,此處拋異常仅仆,所以上面需要拋出去
baos.writeTo(new FileOutputStream("a.txt"));
}
catch (IOException e)
{
throw new RuntimeException("寫入文件失敗");
}
}
}
六器赞、字符編碼
1、概述:
1墓拜、字符流的出現(xiàn)為了方便操作字符港柜,更重要的是加入了編碼的轉(zhuǎn)換,即轉(zhuǎn)換流。
2夏醉、通過子類進行轉(zhuǎn)換
3爽锥、在兩個對象進行構造時,可加入編碼表
4畔柔、可傳入編碼表的有:
1)轉(zhuǎn)換流:InuputStreamReader和OutputStreamWriter
2)打印流:PrintStream和PrintWriter氯夷,只有輸出流
5、常見的編碼表:
1)ASCII:美國標準信息交換碼表靶擦。用一個字節(jié)的7位表示
2)IOS8859-1:拉丁碼表腮考;歐洲碼表。用一個字節(jié)的8位表示
3)GB2312:中國的中文編碼表
4)GBK:中國的中文編碼表升級玄捕,融合了更多的中文文字字符踩蔚。打頭的是兩個高位為1的兩個字節(jié)編碼。為負數(shù)
5)Unicode:國際標準碼桩盲,融合了多種文字
6)UTF-8:最多用三個字節(jié)表示一個字符的編碼表寂纪,包括:一位、兩位赌结、三位表示的字符
UTF-8有自己的字節(jié)碼:
一個字節(jié):0開頭
兩個字節(jié):
》字節(jié)一 ---> 110 位數(shù):10 ~ 6
》字節(jié)二 ---> 10 位數(shù):5 ~ 0
三個字節(jié):
》字節(jié)一 ---> 110 位數(shù):15 ~ 12
》字節(jié)二 ---> 10 位數(shù):11 ~ 6
》字節(jié)三 ---> 10 位數(shù):5 ~ 0
2捞蛋、編碼和解碼:
1、編解碼:
編碼:字符串變成字節(jié)數(shù)組
解碼:字節(jié)數(shù)組變成字符串
2柬姚、轉(zhuǎn)換:
1)默認字符集:
String ---> byte[] :srt.getBytes()
byte[] ---> String :new String(byte[])
2)指定字符集:
String ---> byte[] :srt.getBytes(charsetName)
byte[] ---> String :new String(byte[],charsetName)
3拟杉、對于編碼和解碼的字符集轉(zhuǎn)換
1叫挟、如果編碼失敗貌虾,解碼就沒意義了。
2膀捷、如果編碼成功撕捍,解碼出來的是亂碼拿穴,,則需對亂碼通過再次編碼(用解錯碼的編碼表)忧风,然后再通過正確的編碼表解碼默色。針對于IOS8859-1是通用的。
3狮腿、如果用的是GBK編碼腿宰,UTF-8解碼,那么再通過2的方式缘厢,就不能成功了吃度,因為UTF-8也支持中文,在UTF-8解的時候贴硫,會將對應的字節(jié)數(shù)改變椿每,所以不會成功。
4、特別注意:
對于中文的”聯(lián)通“间护,這兩個字比較特別删壮,它的二進制位正好是和在UTF-8中兩個字節(jié)打頭的相同,可以找到對應的符號兑牡,但不再是”聯(lián)通“了。
示例:
import java.util.*;
class EncodeDemo
{
public static void main(String[] args) throws Exception{
CodeDemo();
//編譯成功税灌,解碼失敗后的解決方式
CodeBack();
}
public static void CodeDemo()throws Exception{
String s = "你好";
byte[] b1 = s.getBytes();
String s1 = new String(b1);
System.out.println(Arrays.toString(b1));
byte[] b2 = s.getBytes("GBK");//默認編碼
String s2 = new String(b2);
System.out.println("s1=" + s1 + ",s2=" + s2);
System.out.println(Arrays.toString(b2));
byte[] b3 = s.getBytes("UTF-8");//國際編碼
String s3 = new String(b3);
System.out.println("s3=" + s3);
System.out.println(Arrays.toString(b3));
byte[] b4 = s.getBytes("ISO8859-1");//歐洲編碼
String s4 = new String(b4);
System.out.println("s4=" + s4);
System.out.println(Arrays.toString(b4));
}
//編碼與解碼
public static void CodeBack()throws Exception{
String s = "你好";
System.out.println("原數(shù)據(jù):" + s);
byte[] b1 = s.getBytes("GBK");//默認編碼
System.out.println(Arrays.toString(b1));
String s1 = new String(b1,"ISO8859-1");
System.out.println("s1=" + s1);
System.out.println("----對s1進行ISO8859-1編碼-----");
//對s1進行ISO8859-1編碼
byte[] b2 = s1.getBytes("ISO8859-1");//歐洲編碼
System.out.println(Arrays.toString(b2));
String s2 = new String(b2,"GBK");
System.out.println("s2=" + s2);
}
}
七均函、練習
五個學生,每個學生有3門課程的成績菱涤,從鍵盤輸入以上數(shù)據(jù)(姓名苞也,三門課成績),
輸入格式:如:zahngsan粘秆,30,40,60計算出總成績如迟,并把學生的信息和計算出的總分數(shù)高低按順序存放在磁盤文件stud.txt中
步驟:
1、描述學生對象
2攻走、定義一個可操作學生對象的工具類
思路:
1殷勘、通過獲取鍵盤錄入一行的數(shù)據(jù),并將該行數(shù)據(jù)的信息取出昔搂,封裝成學生對象
2玲销、因為學生對象很多,則需要存儲摘符,使用集合贤斜,因為要對學生總分排序
所以可以使用TreeSet
3、將集合中的信息寫入到一個文件中
import java.io.*;
import java.util.*;
//定義學生類
class Student implements Comparable<Student>
{
//定義私有屬性
private String name;
private int ma,cn,en;
private int sum;
//構造Student函數(shù)逛裤,初始化
Student(String name,int ma,int cn,int en)
{
this.name = name;
this.ma = ma;
this.cn = cn;
this.en = en;
sum = ma+cn+en;
}
//覆寫compareTo方法瘩绒,按學生總成績排序
public int compareTo(Student s)
{
int num = new Integer(this.sum).compareTo(new Integer(s.sum));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
//獲取學生信息
public String getName()
{
return name;
}
public int getSum()
{
return sum;
}
//覆寫hasdCode()和equals()方法,排除相同的兩個學生
public int hashCode()
{
return name.hashCode() + sum*39;
}
public boolean equals(Object obj)
{
if(obj instanceof Student)
throw new ClassCastException("類型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name) && this.sum==s.sum;
}
//定義學生信息顯示格式
public String toString()
{
return "student[" + name + ", " + ma + ", " + cn + ", " + en + "]";
}
}
//工具類带族,將鍵盤錄入的輸入存入集合锁荔,并將集合的元素寫入文件中
class StudentInfoTool
{
//無比較器的學生集合
public static Set<Student> getStudents()
{
return getStudents(null);
}
//具備比較器的學生集合
public static Set<Student> getStudents(Comparator<Student> cmp)
{
BufferedReader bufr = null;
Set<Student> stus = null;
try
{
//創(chuàng)建讀取流對象緩沖區(qū),鍵盤錄入
bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
//選擇集合是否有比較器
if(cmp==null)
stus = new TreeSet<Student>();
else
stus = new TreeSet<Student>(cmp);
//循環(huán)讀取鍵盤錄入的數(shù)據(jù)
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
//對讀取的數(shù)據(jù)進行分割并存入集合
String[] info = line.split(",");
Student stu = new Student(info[0],Integer.parseInt(info[1]),
Integer.parseInt(info[2]),
Integer.parseInt(info[3]));
stus.add(stu);
}
}
catch (IOException e)
{
throw new RuntimeException("學生信息讀取失敗");
}
//關閉流資源
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
return stus;
}
}
//將數(shù)據(jù)寫入指定文件
public static void write2File(Set<Student> stus,String fileName)
{
BufferedWriter bufw = null;
try
{
//創(chuàng)建寫入流對象
bufw = new BufferedWriter(new FileWriter(fileName));
//循環(huán)寫入數(shù)據(jù)
for(Student stu : stus)
{
bufw.write(stu.toString() + "\t");
bufw.write(stu.getSum() + "");
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
//關閉流資源
finally
{
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("寫入流關閉失敗");
}
}
}
}
class Demo
{
public static void main(String[] args)
{
//反轉(zhuǎn)比較器炉菲,將成績從大到小排
Comparator<Student> cmp = Collections.reverseOrder();
//將錄入的學生信息存入集合
Set<Student> stus = StudentInfoTool.getStudents(cmp);
//將信息寫入指定文件中
StudentInfoTool.write2File(stus,"sudentinfo.txt");
}
}