-------android培訓、java培訓、java學習型技術博客、期待與您交流! ----------
將堆內存中的對象存入硬盤,保留對象中的數據,稱之爲對象的持久化(或序列化)
二、特有方法:
1、write(int val) ---> 寫入一個字節(最低八位)
2、writeInt(int vale) ---> 吸入一個32爲int值
三、使用步驟:
說明:serialVersion
a、給類一個可被編譯器識別的的序列號,在編譯類時,會分配一個long型UID,通過序列號,將類存入硬盤中,並序列化,即持久化。序列號根據成員算出的。靜態不能被序列化。如果非靜態成員也無需序列化,可以用transien修飾。
b、接口Serializable中沒有方法,稱之爲標記接口
1、寫入流對象:
1)創建對象寫入流,與文件關聯,即傳入目的
2)通過寫入writeObject()方法,將對象作爲參數傳入,即可寫入文件
2、讀取流對象
1)創建對象讀取流,與文件關聯,即傳入源
2)通過writeObject()方法,讀取文件中的對象,並返回這個對象
示例:
/*
對象的序列化
*/
import java.io.*;
//對象序列化測試
class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
//對象寫入流
writeObj();
//對象讀取流
readObj();
}
//定義對象讀取流
public static void readObj()throws Exception
{
//ObjectInputStream對細節對象進行操作
//創建對象讀取流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
//通過讀取文件數據,返回對象
Person p = (Person)ois.readObject();
System.out.println(p);
//最終關閉流對象
ois.close();
}
//定義對象寫入流
public static void writeObj()throws IOException
{
//創建對象寫入流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
//寫入對象數據
oos.writeObject(new Person("lisi0",399,"kr"));
//關閉流資源
oos.close();
}
}
/*
沒有方法的接口通常稱爲標記
*/
import java.io.*;
//創建Person類,實現序列化
class Person implements Serializable
{
//定義自身的序列化方式
public static final long serialVersionUID = 42L;
//定義私有屬性
private String name;
//age被transient修飾後就不能被序列化了,保證其值只在堆內存中存在,而不再文本文件中存在
transient int age;
//靜態成員變量不能被序列化
static String country = "cn";
//構造Person類
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
//覆寫toString方法
public String toString()
{
return name+":"+age+":"+country;
}
}
知識點十一 管道流
一、概述:
1、管道流:PipedInputStream和PipedOutputStream
2、管道流輸入輸出可以直接進行連接,通過結合線程使用。
3、PipedInputStream和PipedOutputStream是涉及到多線程技術的IO流對象。
二、使用步驟:
1、要先創建一個讀和寫的兩個類,實現Runnable接口,因爲是兩個不同的線程,覆蓋run方法,注意,需要在內部拋異常
2、創建兩個管道流,並用connect()方法將兩個流連接
3、創建讀寫對象,並傳入兩個線程內,並start執行
示例:
/*
管道流技術,涉及多線程技術
*/
import java.io.*;
//創建Read類,實現run方法
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
//實現run方法
public void run()
{
try
{
byte[] buf = new byte[1024];
//讀取寫入的數據
System.out.println("讀取前。。沒有數據阻塞");
int len = in.read(buf);
System.out.println("讀到數據。。阻塞結束");
String s= new String(buf,0,len);
System.out.println(s);
in.close();
}
catch (IOException e)
{
throw new RuntimeException("管道讀取流失敗");
}
}
}
//創建Write類
class Write implements Runnable
{
private PipedOutputStream out;
//Write構造函數
Write(PipedOutputStream out)
{
this.out = out;
}
//實現run方法
public void run()
{
try
{
//寫入數據
System.out.println("開始寫入數據,等待6秒後。");
Thread.sleep(6000);
out.write("piped lai la".getBytes());
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道輸出流失敗");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args) throws IOException
{
//創建管道流對象
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
//將讀取流(輸入流)和寫入流(輸出流)關聯起來
in.connect(out);
Read r = new Read(in);
Write w = new Write(out);
//創建讀寫對象,並創建線程對象
new Thread(r).start();
new Thread(w).start();
}
}
知識點十二 RandomAccessFile 類
一、概述:
1、RandomAccessFile稱之爲隨機訪問文件的類,自身具備讀寫方法。
2、該類不算是IO體系中的子類,而是直接繼承Object,但是它是IO包成員,因爲它具備讀寫功能,內部封裝了一個數組,且通過指針對數組的元素進行操作,同時可通過seek改變指針的位置。
3、可以完成讀寫的原理:內部封裝了字節輸入流
4、構造函數:RandomAccessFile(File file,String mode),可已從它的構造函數中看出,該類只能操作文件(也有字符串),而且操作文件還有模式。
模式傳入值:”r“:以只讀方式打開;”rw“:打開以便讀寫
如果模式爲只讀,則不會創建文件,會去讀一個已存在的文件,若文件不存在,則會出現異常,如果模式爲rw,且該對象的構造函數要操作的文件不存在,會自動創建,如果存在,則不會覆蓋,也可通過seek方法修改。
5.RandomAccessFile的最大的作用是實現多線程的下載
二、特有方法:
1、seek(int n):設置指針,可以將指針設置到前面或後面
2、skipBytes(int n):跳過指定字節數,不可往前跳
三、使用步驟:
1、創建RandomAccessFile對象
2、將數據寫入到指定文件中
3、讀取數據,讀入到指定文件中
注意:要想取得後面的數據,需要調用數組指針,通過改變角標位置,取出相應的數據
a.調整對象的指針:seek()
b.跳過指定字節數
示例:
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//readFile();
//System.out.println(Integer.toBinaryString(258));
}
public static void readFile()throws IOException
{
//"r"代表模式,只讀
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//seek和skipBytes的區別是:skipBytes不能往回跳,seek可以前後的跳,可以隨意改變指針。
//調整對象中指針。
//raf.seek(8*1);
//跳過指定的字節數
raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
public static void writeFile_2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);
raf.write("週期".getBytes());
raf.writeInt(103);
raf.close();
}
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}
知識點十三 操作基本數據類型的流對象
一、概述:
1、操作基本數據類型的流對象:DataInputStream和DataOutputStream
2、這兩個讀寫對象,可用於操作基本數據類型的流對象,包含讀寫各種基本數據類型的方法
二、特有方法:
返回值類型 讀 寫
int型 writeInt(int n) int readInt()
boolean型 writeBoolean(boolean b) boolean readBoolean()
double型 writeDouble(double d) double readDouble()
示例:
import java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
// osw.write("你好");
// osw.close();
// readUTFDemo();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");
dos.close();
}
public static void readData()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
public static void writeData()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
ObjectOutputStream oos = null;
oos.writeObject(new O());
}
}
知識點十四 操作數組和字符串的流對象
一、操作字節數組的對象:ByteArrayInputStream和ByteArrayOutputStream
1、這個對象並沒有調用底層資源,所以不用關閉流資源
2、存入的是緩衝區,並未用到鍵盤和硬盤燈,所以不需要拋任何IO異常
3、對象中封裝了數組
4、構造函數:
1)ByteArrayInputStream:在構造函數的時候,需要接受數據源,而且數據源是一個字節數據。
2)ByteArrayOutputStream:在構造函數的時候,不用定義數據目的,因爲該對象中已經在內部封裝了可變長度的字節數組,這就是數據的目的地
4、因爲兩個流對象都是操作的是數據,並沒有使用系統資源,所以不用進行close關閉。
6、其實就是用流的思想操作數組
7、特有方法:writeTo(OutputStream out) 這個方法用到了字節輸出流,有異常存在,需要拋IO異常
二、對應的字符數組和字符串:
字符數組流對象:CharArrayReader和CharArrayWriter
字符串流對象: StringReader和StringWriter
示例:
/*
用於操作字節數組的流對象。
ByteArrayInputStream :在構造的時候,需要接收數據源,。而且數據源是一個字節數組。
ByteArrayOutputStream: 在構造的時候,不用定義數據目的,因爲該對象中已經內部封裝了可變長度的字節數組。
這就是數據目的地。
因爲這兩個流對象都操作的數組,並沒有使用系統資源。
所以,不用進行close關閉。
在流操作規律講解時:
源設備,
鍵盤 System.in,硬盤 FileStream,內存 ArrayStream。
目的設備:
控制檯 System.out,硬盤FileStream,內存 ArrayStream。
用流的讀寫思想來操作數據。
toString()方法是將字節緩衝區的數據轉化成字符串
*/
import java.io.*;
class ByteArrayStream
{
public static void main(String[] args)
{
//數據源。在內存當中
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
//數據目的 也在內存當中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
//讀取和寫入數據
while((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
try
{
//方法,此處拋異常,所以上面需要拋出去
baos.writeTo(new FileOutputStream("a.txt"));
}
catch (IOException e)
{
throw new RuntimeException("寫入文件失敗");
}
}
}
知識點十五 字符編碼
一、概述:
1、字符流的出現爲了方便操作字符,更重要的是加入了編碼的轉換,即轉換流。
2、通過子類進行轉換
3、在兩個對象進行構造時,可加入編碼表
4、可傳入編碼表的有:
1)轉換流:InuputStreamReader和OutputStreamWriter
2)打印流:PrintStream和PrintWriter,只有輸出流
5.編碼表的由來
1)計算機只能識別二進制數據,早期由來是電信號。
2)爲了方便應用計算機,讓它可以識別各個國家的文字。
3)就將各個國家的文字用數字來表示,並一一對應,形成一張表。
這就是編碼表
6、常見的編碼表:
1)ASCII:美國標準信息交換碼錶。用一個字節的7位表示
2)IOS8859-1:拉丁碼錶;歐洲碼錶。用一個字節的8位表示
3)GB2312:中國的中文編碼表
4)GBK:中國的中文編碼表升級,融合了更多的中文文字字符。打頭的是兩個高位爲1的兩個字節編碼。爲負數
5)Unicode:國際標準碼,融合了多種文字
6)UTF-8:最多用三個字節表示一個字符的編碼表,包括:一位、兩位、三位表示的字符
UTF-8有自己的字節碼:
一個字節:0開頭
兩個字節:字節一 ---> 110 位數:10 ~ 6
字節二 ---> 10 位數:5 ~ 0
三個字節:字節一 ---> 110 位數:15 ~ 12
字節二 ---> 10 位數:11 ~ 6
字節三 ---> 10 位數:5 ~ 0
示例:
import java.io.*;
class EncodeStream
{
public static void main(String[] args) throws IOException
{
//writeText();
readText();
}
public static void readText()throws IOException
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
//定義數組長度
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
public static void writeText()throws IOException
{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
}
二、編碼和解碼:
1、編碼:字符串變成字節數組
解碼:字節數組變成字符串
2、轉換:
1)默認字符集:
String ---> byte[] :srt.getBytes()
byte[] ---> String :new String(byte[])
2)指定字符集:
String ---> byte[] :srt.getBytes(charsetName)
byte[] ---> String :new String(byte[],charsetName)
示例:
import java.util.*;
class EncodeDemo
{
public static void main(String[] args)throws Exception
{
String s = "哈哈";
//對s進行編碼
byte[] b1 = s.getBytes("GBK");
//把數組變成字符串
System.out.println(Arrays.toString(b1));
String s1 = new String(b1,"utf-8");
System.out.println("s1="+s1);
//對s1進行iso8859-1編碼。
byte[] b2 = s1.getBytes("utf-8");
System.out.println(Arrays.toString(b2));
String s2 = new String(b2,"gbk");
System.out.println("s2="+s2);
}
}
三、對於編碼和解碼的字符集轉換
1、如果編碼失敗,解碼就沒意義了。
2、如果編碼成功,解碼出來的是亂碼,,則需對亂碼通過再次編碼(用解錯碼的編碼表),然後再通過正確的編碼表解碼。針對於IOS8859-1是通用的。
3、如果用的是GBK編碼,UTF-8解碼,那麼再通過2的方式,就不能成功了,因爲UTF-8也支持中文,在UTF-8解的時候,會將對應的字節數改變,所以不會成功。
四、'' 聯通產生的亂碼問題":
1)關於聯通兩個字在記事本爲什麼會顯示亂碼問題的總結聯通按照gbk的編碼進行存儲,但是這兩個字的二進制是11000001 10101010
正好符合utf-8的編碼規範進行解碼,直接去查UTF-8的碼錶進行解碼
2)“聯通”二字這樣的情況,還有別的,比如“透支”二字。
假設你保存文檔時選擇另存爲,編碼格式選擇UTF-8,那麼你再打開那個文本文檔時就不會發生亂碼了。
示例:
class EncodeDemo2
{
public static void main(String[] args) throws Exception
{
String s = "聯通";
byte[] by = s.getBytes("gbk");
for(byte b : by)
{
System.out.println(Integer.toBinaryString(b&255));
}
System.out.println("Hello World!");
}
}
知識點十六 練習
有五個學生,每個學生有3門課的成績,
從鍵盤輸入以上數據(包括姓名,三門課成績),
輸入的格式:如:zhagnsan,30,40,60計算出總成績,
並把學生的信息和計算出的總分數高低順序存放在磁盤文件"stud.txt"中。
步驟:
1,描述學生對象。
2,定義一個可操作學生對象的工具類。
思想:
1,通過獲取鍵盤錄入一行數據,並將該行中的信息取出封裝成學生對象。
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函數,初始化
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*78;
}
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()throws IOException
{
return getStudents(null);
}
//按照指定比較器的比較方式
//具備比較器的學生集合
public static Set<Student> getStudents(Comparator<Student> cmp)throws IOException
{
//讀取鍵盤的錄入,記牢固
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
Set<Student> stus = null;
//選擇集合是否有比較器
if(cmp==null)
stus = new TreeSet<Student>();
else
stus = new TreeSet<Student>(cmp);
//循環讀取鍵盤錄入的數據
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
//對讀取的數據進行分割並存入集合
String[] info = line.split(",");
//Integer.parseInt是將字符轉換成整形數據
Student stu = new Student(info[0],Integer.parseInt(info[1]),
Integer.parseInt(info[2]),
Integer.parseInt(info[3]));
stus.add(stu);
}
bufr.close();
return stus;
}
//將集合中的數據寫入到文件中去
public static void write2File(Set<Student> stus)throws IOException
{
// 創建寫入流對象,向文件中系寫入數據
BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
//循環寫入數據
for(Student stu : stus)
{
//bufw.write(stu.toString()+"\t");是爲什麼???
//\t爲製表符
bufw.write(stu.toString()+"\t");
bufw.write(stu.getSum()+"");
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
class StudentInfoTest
{
public static void main(String[] args) throws IOException
{
//強行逆轉排序比較器 ,反轉比較器,將成績從大到小排
Comparator<Student> cmp = Collections.reverseOrder();
//將錄入的學生信息存入集合
Set<Student> stus = StudentInfoTool.getStudents(cmp);
//將信息寫入指定文件中
StudentInfoTool.write2File(stus);
}
}
最新最全的的java學習視頻教程:http://pro.net.itcast.cn/View-22-1458.aspx