轉換流
- 輸入字節流的轉換流:InputStreamReader 是字節流通向字符流的橋InputStreamReader
- 輸出字節流的轉換流:OutputStreamWriter 可以把輸出字節流轉換成輸出字符流
- 轉換流的作用:
- 如果目前所 獲取到的是一個字節流需要轉換字符流使用,這時候就可以使用轉換流。 字節流----> 字符流
- 使用轉換流可以指定編碼表進行讀寫文件。
public class Demo{
public static void main(String[] args) throws IOException {
// readTest();
// writeTest();'
// writeTest2();
readTest2();
}
//使用輸入字節流的轉換流指定碼表進行讀取文件數據
public static void readTest2() throws IOException{
File file = new File("F:\\a.txt");
FileInputStream fileInputStream = new FileInputStream(file);
//創建字節流的轉換流并且指定碼表進行讀取
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
char[] buf = new char[1024];
int length = 0;
while((length = inputStreamReader.read(buf))!=-1){
System.out.println(new String(buf,0,length));
}
}
//使用輸出字節流的轉換流指定碼表寫出數據
public static void writeTest2() throws IOException{
File file = new File("F:\\a.txt");
//建立數據的輸出通道
FileOutputStream fileOutputStream = new FileOutputStream(file);
//把輸出字節流轉換成字符流并且指定編碼表。
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "utf-8");
outputStreamWriter.write("新中國好啊");
//關閉資源
outputStreamWriter.close();
}
public static void writeTest() throws IOException{
File file = new File("F:\\a.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
//把輸出字節流轉換成輸出字符流。
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
outputStreamWriter.write("大家好");
outputStreamWriter.close();
}
public static void readTest() throws IOException{
InputStream in = System.in; //獲取了標準的輸入流。
// System.out.println("讀到 的字符:"+ (char)in.read()); //read()一次只能讀取一個字節。
//需要把字節流轉換成字符流。
InputStreamReader inputStreamReader = new InputStreamReader(in);
//使用字符流的緩沖類
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
while((line = bufferedReader.readLine())!=null){
System.out.println("內容:"+ line);
}
}
}
輸入字符流
-
Reader 輸入字符流的基類抽象類
- FileReader 讀取文件的輸入字符流
-
FileReader的用法:
- 找到目標文件
- 建立數據的輸入通道
- 讀取數據
- 關閉資源
public class Demo {
public static void main(String[] args) throws IOException {
readTest2();
}
//使用緩沖字符數組讀取文件。
public static void readTest2() throws IOException{
//找到目標文件
File file = new File("F:\\my.java");
// 建立數據的輸入通道
FileReader fileReader = new FileReader(file);
//建立緩沖字符數組讀取文件數據
char[] buf = new char[1024];
int length = 0 ;
while((length = fileReader.read(buf))!=-1){
System.out.print(new String(buf,0,length));
}
}
public static void readTest1() throws IOException{
//找到目標文件
File file = new File("my.java");
//建立數據的輸入通道
FileReader fileReader = new FileReader(file);
int content = 0 ;
while((content = fileReader.read())!=-1){ //每次只會讀取一個字符,效率低。
System.out.print((char)content);
}
//關閉資源
fileReader.close();
}
}
輸出字符流
-
Writer 輸出字符流的基類 (抽象類)
- FileWriter 向文件數據數據的輸出字符流
-
FileWriter的使用步驟:
- 找到目標文件
- 建立數據輸出通道
- 寫出數據
- 關閉資源
-
FileWriter要注意的事項:
- 使用FileWriter寫數據的時候,FileWriter內部是維護了一個1024個字符數組的,寫數據的時候會先寫入到它內部維護的字符數組中,如果需要把數據真正寫到硬盤上,需要調用flush或者是close方法或者是填滿了內部的字符數組。
- 使用FileWriter的時候,如果目標文件不存在,那么會自動創建目標文件。
3.使用FileWriter的時候, 如果目標文件已經存在了,那么默認情況會先情況文件中的數據,然后再寫入數據 , 如果需要在原來的基礎上追加數據,需要使用“new FileWriter(File , boolean)”的構造方法,第二參數為true
練習: 使用字符流拷貝一個文本文件(java文件)接著使用字符流拷貝一個圖片(觀察圖片的大小變化,思考為什么會這樣子?)
public class Demo {
public static void main(String[] args) throws IOException {
writeTest1();
}
public static void writeTest1() throws IOException{
//找到目標文件
File file = new File("F:\\a.txt");
//建立數據輸出通道
FileWriter fileWriter = new FileWriter(file,true);
//準備數據,把數據寫出
String data = "今天天氣非常好!!";
fileWriter.write(data); //字符流具備解碼的功能。
//刷新字符流
// fileWriter.flush();
//關閉資源
fileWriter.close();
}
}
對象的序列化
-
當創建對象時, 程序運行時它就會存在, 但是程序停止時, 對象也就消失了. 但是如果希望對象在程序不運行的情況下仍能存在并保存其信息,將會非常有用,對象將被重建并且擁有與程序上次運行時擁有的信息相同。可以使用對象的序列化
- 對象的序列化 : 將內存中的對象直接寫入到文件設備中
- 對象的反序列化 : 將文件設備中持久化的數據轉換為內存對象
-
基本的序列化由兩個方法產生 : 一個方法用于序列化對象并將它們寫入一個流,另一個方法用于讀取流并反序列化對象
- ObjectOutput -
writeObject(Object obj)
: 將對象寫入底層存儲或流 - ObjectInput -
readObject()
: 讀取并返回對象
- ObjectOutput -
-
ObjectOutputStream(對象的輸出流) / ObjectInputStream(對象的輸入流)
- 對象的輸入輸出流 : 對象的輸入輸出流主要的作用是用于寫對象的信息與讀取對象的信息, 對象信息一旦寫到文件上那么對象的信息就可以做到持久化了
- ObjectOutputStream的使用步驟:(對象輸入輸出流要注意的細節)
- 如果對象需要被寫出到文件上,那么對象所屬的類必須要實現Serializable接口Serializable接口沒有任何的方法,是一個標識接口而已
- 對象的反序列化創建對象的時候并不會調用到構造方法的、
- serialVersionUID 是用于記錄class文件的版本信息的,serialVersionUID這個數字是通過一個類的類名、成員、包名、工程名算出的一個數字
- 使用ObjectInputStream反序列化的時候,ObjeectInputStream會先讀取文件中的serialVersionUID,然后與本地的class文件的serialVersionUID進行對比,如果這兩個id不一致,那么反序列化就失敗了
- 如果序列化與反序列化的時候可能會修改類的成員,那么最好一開始就給這個類指定一個serialVersionUID,如果一類已經指定的serialVersionUID,然后在序列化與反序列化的時候,jvm都不會再自己算這個 class的serialVersionUID了
- 如果一個對象某個數據不想被序列化到硬盤上,可以使用關鍵字transient修飾
- 如果一個類維護了另外一個類的引用,那么另外一個類也需要實現Serializable接口
- 案例: 序列化和反序列化Cat對象
public class Demo3 {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Cat cat = new Cat("tom", 3);
FileOutputStream fos = new FileOutputStream(new File("c:\\Cat.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cat);
System.out.println(cat);
oos.close();
// 反序列化
FileInputStream fis = new FileInputStream(new File("c:\\Cat.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
Object readObject = ois.readObject();
Cat cat2 = (Cat) readObject;
System.out.println(cat2);
fis.close();
}
class Cat implements Serializable {
public String name;
public int age;
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Cat [name=" + name + ", age=" + age + "]";
}
}
- 例子關鍵點:
1. 聲明Cat類實現了Serializable接口。是一個標示器,沒有要實現的方法
2. 新建Cat對象
3. 新建字節流對象(FileOutputStream)進序列化對象保存在本地文件中
4. 新建ObjectOutputStream對象,調用writeObject方法序列化Cat對象
5. writeObject方法會執行兩個工作:序列化對象,然后將序列化的對象寫入文件中
6. 反序列化就是調用ObjectInputStream的readObject()方法
7. 異常處理和流的關閉動作要執行
- Serializable
類通過實現 java.io.Serializable 接口以啟用其序列化功能。未實現此接口的類將無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用于標識可序列化的語義。所以需要被序列化的類必須是實現Serializable接口,該接口中沒有描述任何的屬性和方法,稱之為標記接口。如果對象沒有實現接口Serializable,在進行序列化時會拋出:NotSerializableException 異常
注意:保存一個對象的真正含義是什么?如果對象的實例變量都是基本數據類型,那么就非常簡單。但是如果實例變量是包含對象的引用,會怎么樣?保存的會是什么?很顯然在Java中保存引用變量的實際值沒有任何意義,因為Java引用的值是通過JVM的單一實例的上下文中才有意義。通過序列化后,嘗試在JVM的另一個實例中恢復對象,是沒有用處的
-
如下:
- 首先建立一個Dog對象,也建立了一個Collar對象。Dog中包含了一個Collar(項圈)現在想要保存Dog對象,但是Dog中有一個Collar,意味著保存Dog時也應該保存Collar。假如Collar也包含了其他對象的引用,那么會發生什么?意味著保存一個Dog對象需要清楚的知道Dog對象的內部結構。會是一件很麻煩的事情。
- Java的序列化機制可以解決該類問題,當序列化一個對象時,Java的序列化機制會負責保存對象的所有關聯的對象(就是對象圖),反序列化時,也會恢復所有的相關內容。本例中:如果序列化Dog會自動序列化Collar。但是,只有實現了Serializable接口的類才可以序列化。如果只是Dog實現了該接口,而Collar沒有實現該接口。會發生什么?
Dog類和Collar類
import java.io.Serializable;
public class Dog implements Serializable {
private Collar collar;
private String name;
public Dog(Collar collar, String name) {
this.collar = collar;
this.name = name;
}
public Collar getCollar() {
return collar;
}
}
class Collar {
private int size;
public int getSize() {
return size;
}
public Collar(int size) {
this.size = size;
}
}
- 序列化
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo4 {
public static void main(String[] args) throws IOException {
Collar coll = new Collar(10);
Dog dog = new Dog(coll, "旺財");
FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(dog);
}
}
- 執行程序,出現了運行時異常:
Exception in thread "main" java.io.NotSerializableException: Collar
- 所以我們也必須將Dog中使用的Collar序列化。但是如果我們無法訪問Collar的源代碼,或者無法使Collar可序列化,如何處理?
- 兩種解決方法:
- 繼承Collar類, 使子類可序列化, 但是如果Collar是final類, 就無法繼承了. 并且, 如果Collar引用了其他非序列化對象, 也無法解決該問題
- transient : 此時就可以使用transient修飾符, 可以將Dog類中的成員變量標識為transient, 那么在序列化Dog對象時, 序列化就會跳過Collar
- 兩種解決方法:
public class Demo4 {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Collar coll = new Collar(10);
Dog dog = new Dog(coll, "旺財");
System.out.println(dog.getCollar().getSize());
FileOutputStream fis = new FileOutputStream(new File("c:\\d og.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(dog);
// 反序列化
FileInputStream fos = new FileInputStream(new File("c:\\dog.txt"));
ObjectInputStream ois = new ObjectInputStream(fos);
Object readObject = ois.readObject();
Dog dog2 = (Dog) readObject;
// Collar未序列化
dog2.getCollar().getSize();
}
}
- 這樣我們具有一個序列化的Dog和非序列化的Collar。此時反序列化Dog后,訪問Collar,就會出現運行時異常 `` Exception in thread "main" java.lang.NullPointerException ``
- 注意:序列化不適用于靜態變量,因為靜態變量并不屬于對象的實例變量的一部分。靜態變量隨著類的加載而加載,是類變量。由于序列化只適用于對象。
- 基本數據類型可以被序列化
public class Demo5 {
public static void main(String[] args) throws IOException {
// 創建序列化流對象
FileOutputStream fis = new FileOutputStream(new File("c:\\basic.txt"));
ObjectOutputStream os = new ObjectOutputStream(fis);
// 序列化基本數據類型
os.writeDouble(3.14);
os.writeBoolean(true);
os.writeInt(100);
os.writeInt(200);
// 關閉流
os.close();
// 反序列化
FileInputStream fos = new FileInputStream(new File("c:\\basic.txt"));
ObjectInputStream ois = new ObjectInputStream(fos);
System.out.println(ois.readDouble());
System.out.println(ois.readBoolean());
System.out.println(ois.readInt());
System.out.println(ois.readInt());
fos.close();
}
}
-
serialVersionUID : 用于給類指定一個UID。該UID是通過類中的可序列化成員的數字簽名運算出來的一個long型的值。只要是這些成員沒有變化,那么該值每次運算都一樣。該值用于判斷被序列化的對象和類文件是否兼容。如果被序列化的對象需要被不同的類版本所兼容。可以在類中自定義UID。
- 定義方式:
static final long serialVersionUID = 42L;
- 定義方式:
對象序列化示例:
class Address implements Serializable{
String country;
String city;
public Address(String country,String city){
this.country = country;
this.city = city;
}
}
class User implements Serializable{
private static final long serialVersionUID = 1L;
String userName ;
String password;
transient int age; // transient 透明
Address address ;
public User(String userName , String passwrod) {
this.userName = userName;
this.password = passwrod;
}
public User(String userName , String passwrod,int age,Address address) {
this.userName = userName;
this.password = passwrod;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "用戶名:"+this.userName+ " 密碼:"+ this.password+" 年齡:"+this.age+" 地址:"+this.address.city;
}
}
public class Demo {
public static void main(String[] args) throws IOException, Exception {
writeObj();
// readObj();
}
//把文件中的對象信息讀取出來-------->對象的反序列化
public static void readObj() throws IOException, ClassNotFoundException{
//找到目標文件
File file = new File("F:\\obj.txt");
//建立數據的輸入通道
FileInputStream fileInputStream = new FileInputStream(file);
//建立對象的輸入流對象
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//讀取對象信息
User user = (User) objectInputStream.readObject(); //創建對象肯定要依賴對象所屬 的class文件。
System.out.println("對象的信息:"+ user);
}
//定義方法把對象的信息寫到硬盤上------>對象的序列化。
public static void writeObj() throws IOException{
//把user對象的信息持久化存儲。
Address address = new Address("中國","廣州");
User user = new User("admin","123",15,address);
//找到目標文件
File file = new File("F:\\obj.txt");
//建立數據輸出流對象
FileOutputStream fileOutputStream = new FileOutputStream(file);
//建立對象的輸出流對象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//把對象寫出
objectOutputStream.writeObject(user);
//關閉資源
objectOutputStream.close();
}
}
序列流 : SequenceInputStream
序列流,對多個流進行合并。SequenceInputStream 表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,并從第一個輸入流開始讀取,直到到達文件末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最后一個輸入流的文件末尾為止。
合并兩個流 : 使用構造函數
SequenceInputStream(InputStream s1, InputStream s2)
private static void testSequenceInputStream() throws IOException {
FileInputStream fis1 = new FileInputStream("c:\\a.txt");
FileInputStream fis2 = new FileInputStream("c:\\b.txt");
SequenceInputStream s1 = new SequenceInputStream(fis1, fis2);
int len = 0;
byte[] byt = new byte[1024];
FileOutputStream fos = new FileOutputStream("c:\\z.txt");
while ((len = s1.read(byt)) != -1) {
fos.write(byt, 0, len);
}
s1.close();
}
- 合并多個流:
public static void testSequenceInputStream() throws Exception {
InputStream in1 = new FileInputStream("c:/a.txt");
InputStream in2 = new FileInputStream("c:/b.txt");
InputStream in3 = new FileInputStream("c:/c.txt");
LinkedHashSet<InputStream> set = new LinkedHashSet<InputStream>();
set.add(in1);
set.add(in2);
set.add(in3);
final Iterator<InputStream> iter = set.iterator();
SequenceInputStream sin = new SequenceInputStream(
new Enumeration<InputStream>() {
@Override
public boolean hasMoreElements() {
return iter.hasNext();
}
@Override
public InputStream nextElement() {
return iter.next();
}
});
FileOutputStream out = new FileOutputStream("c:/z.txt");
for (int b = -1; (b = sin.read()) != -1;) {
out.write(b);
}
sin.close();
out.close();
}
- 案例:將map3歌曲文件進行切割拷貝,并合并.
public class Demo2 {
public static void main(String[] args) throws IOException {
split(new File("c:\\a.mp3"), 10, new File("c:\\"));
System.out.println("切割完畢");
LinkedHashSet<InputStream> hs = new LinkedHashSet<InputStream>();
hs.add(new FileInputStream(new File("c:\\part.1.mp3")));
hs.add(new FileInputStream(new File("c:\\part.2.mp3")));
hs.add(new FileInputStream(new File("c:\\part.3.mp3")));
hs.add(new FileInputStream(new File("c:\\part.4.mp3")));
merage(hs, new File("c:\\merage.mp3"));
System.out.println("合并完畢");
}
private static void merage(LinkedHashSet<InputStream> hs, File dest)
throws IOException {
final Iterator<InputStream> it = hs.iterator();
FileOutputStream fos = new FileOutputStream(dest);
SequenceInputStream seq = new SequenceInputStream(
new Enumeration<InputStream>() {
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public InputStream nextElement() {
return it.next();
}
});
byte[] byt = new byte[1024 * 1024];
int len = 0;
while ((len = seq.read(byt)) != -1) {
fos.write(byt, 0, len);
}
seq.close();
fos.close();
}
// 1. 切割文件
/*
* 切割文件,切割份數, 切割后保存路徑
*/
private static void split(File src, int count, File dir) throws IOException {
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = null;
byte[] byt = new byte[1024 * 1024];
int len = 0;
for (int i = 1; i <= count; i++) {
len = fis.read(byt);
if (len != -1) {
fos = new FileOutputStream(dir + "part." + i + ".mp3");
fos.write(byt, 0, len);
}
// fos.close();
}
fis.close();
}
}
- 合并文件
public class Demo {
public static void main(String[] args) throws IOException {
merge3();
}
// 把三個文件合并成一個文件
public static void merge3() throws IOException{
//找到目標文件
File file1 = new File("F:\\a.txt");
File file2 = new File("F:\\b.txt");
File file3 = new File("F:\\c.txt");
File file4 = new File("F:\\d.txt");
//建立對應 的輸入輸出流對象
FileOutputStream fileOutputStream = new FileOutputStream(file4);
FileInputStream fileInputStream1 = new FileInputStream(file1);
FileInputStream fileInputStream2 = new FileInputStream(file2);
FileInputStream fileInputStream3 = new FileInputStream(file3);
//創建序列流對象
Vector<FileInputStream> vector = new Vector<FileInputStream>();
vector.add(fileInputStream1);
vector.add(fileInputStream2);
vector.add(fileInputStream3);
Enumeration<FileInputStream> e = vector.elements();
SequenceInputStream sequenceInputStream = new SequenceInputStream(e);
//讀取文件數據
byte[] buf = new byte[1024];
int length = 0;
while((length = sequenceInputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//關閉資源
sequenceInputStream.close();
fileOutputStream.close();
}
//使用SequenceInputStream合并文件
public static void merge2() throws IOException{
//找到目標文件
File inFile1 = new File("F:\\a.txt");
File inFile2 = new File("F:\\b.txt");
File outFile = new File("F:\\c.txt");
//建立數據的輸入輸出通道
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
FileInputStream fileInputStream1 = new FileInputStream(inFile1);
FileInputStream fileInputStream2 = new FileInputStream(inFile2);
//建立序列流對象
SequenceInputStream inputStream = new SequenceInputStream(fileInputStream1,fileInputStream2);
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//關閉資源
inputStream.close();
fileOutputStream.close();
}
//需求:把a.txt與b.txt 文件的內容合并。
public static void merge1() throws IOException{
//找到目標文件
File inFile1 = new File("F:\\a.txt");
File inFile2 = new File("F:\\b.txt");
File outFile = new File("F:\\c.txt");
//建立數據的輸入輸出通道
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
FileInputStream fileInputStream1 = new FileInputStream(inFile1);
FileInputStream fileInputStream2 = new FileInputStream(inFile2);
//把輸入流存儲到集合中,然后再從集合中讀取
ArrayList<FileInputStream> list = new ArrayList<FileInputStream>();
list.add(fileInputStream1);
list.add(fileInputStream2);
// 準備一個緩沖數組
byte[] buf = new byte[1024];
int length = 0 ;
for(int i = 0 ; i< list.size() ; i++){
FileInputStream fileInputStream = list.get(i);
while((length = fileInputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//關閉資源
fileInputStream.close();
}
fileOutputStream.close();
}
}
Properties
- 可以和流相關聯的集合對象Properties。Map的子類:
Hashtable
&Properties
- Properties:該集合不需要泛型,因為該集合中的鍵值對都是String類型
- 存入鍵值對 :
setProperty(key,value);
- 獲取指定鍵對應的值 :
value getProperty(key);
- 獲取集合中所有鍵元素 :
Enumeration propertyNames()
; 在jdk1.6版本給該類提供一個新的方法 :Set<String> stringPropertyNames();
- 列出該集合中的所有鍵值對,可以通過參數打印流指定列出到的目的地。
list(PrintStream);
list(PrintWriter);
- 例:
list(System.out)
: 將集合中的鍵值對打印到控制臺
list(new PrintStream("prop.txt"))
: 將集合中的鍵值對存儲到prop.txt文件中
- 可以將流中的規則數據加載進行集合,并稱為鍵值對。
load(InputStream):
jdk1.6版本提供了新的方法 -load(Reader):
- 注意:流中的數據要是"鍵=值" 的規則數據
- 可以將集合中的數據進行指定目的的存儲。
store(OutputStram,String comment)
, jdk1.6版本提供了新的方法 -store(Writer ,String comment):
使用該方法存儲時,會帶著當時存儲的時間
- 存入鍵值對 :
- 注意:Properties只加載key=value這樣的鍵值對,與文件名無關,注釋使用#
public static void sysPropList() throws IOException {
Properties prop = System.getProperties();
// prop.list(System.out);// 目的是控制臺。
// 需求是:將jvm的屬性信息存儲到一個文件中。
prop.list(new PrintStream("java.txt"));
}
public static void sysProp() {
Properties prop = System.getProperties();
Set<String> keys = prop.stringPropertyNames();
for (String key : keys) {
System.out.println(key + ":" + prop.getProperty(key));
}
}
- Properties類與配置文件
- Properties類是一個Map集合,該集合中的鍵值對都是字符串。該集合通常用于對鍵值對形式的配置文件進行操作, 提供了可以快速操作配置文件的方法
- 配置文件:將軟件中可變的部分數據可以定義到一個文件中,方便以后更改,該文件稱之為配置文件
- 優勢: Properties類提高代碼的維護性
- Properties類的基本方法
-
load()
: 將文件設備數據裝載為Map集合數據 -
get(key)
: 獲取Map中的數據 -
getProperty()
: 獲取Map中的數據特有方法
-
- 案例 : 將配置文件中的數據通過流加載到集合中
public static void loadFile() throws IOException {
// 1,創建Properties(Map)對象
Properties prop = new Properties();
// 2.使用流加載配置文件。
FileInputStream fis = new FileInputStream("c:\\qq.txt");
// 3。使用Properties 對象的load方法將流中數據加載到集合中。
prop.load(fis);
// 遍歷該集合
Set<Entry<Object, Object>> entrySet = prop.entrySet();
Iterator<Entry<Object, Object>> it = entrySet.iterator();
while (it.hasNext()) {
Entry<Object, Object> next = it.next();
Object key = next.getKey();
Object value = next.getValue();
}
// 通過鍵獲取指定的值
Object object = prop.get("jack");
System.out.println(object);
// 通過鍵修改值
prop.setProperty("jack", "888888");
// 將集合中的數據寫入到配置文件中。
FileOutputStream fos = new FileOutputStream("c:\\qq.txt");
// 注釋:
prop.store(fos, "yes,qq");
fos.close();
fis.close();
}
- 綜合示例: 獲取記錄程序運行次數:
public class Demo {
public static void main(String[] args) throws IOException {
int count = 0;
Properties pro = new Properties();
File file = new File("c:\\count.ini");
FileInputStream fis = null;
if (!file.exists()) {
file.createNewFile();
}
fis = new FileInputStream(file);
pro.load(fis);
String str = pro.getProperty("count");
if (str != null) {
count = Integer.parseInt(str);
}
if (count == 3) {
System.out.println("使用次數已到,請付費");
System.exit(0);
}
count++;
System.out.println("歡迎使用本軟件" + "你已經使用了:" + count + " 次");
pro.setProperty("count", count + "");
FileOutputStream fos = new FileOutputStream(new File("c:\\count.ini"));
pro.store(fos, "請保護知識產權");
fis.close();
fos.close();
}
}
打印流 (PrintStream)
- 打印流概述:
- 打印流可以接受文件和其他字節輸出流,所以打印流是對普通字節輸出流的增強,其中定義了很多的重載的
print()
和println()
, 方便輸出各種類型的數據. 打印流可以打印任意類型的數據,而且打印數據之前都會先把數據轉換成字符串再進行打印 - 示例:
- 打印流可以接受文件和其他字節輸出流,所以打印流是對普通字節輸出流的增強,其中定義了很多的重載的
class Animal{
String name;
String color;
public Animal(String name,String color){
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "名字:"+this.name+ " 顏色:"+ this.color;
}
}
public class Demo {
public static void main(String[] args) throws IOException {
/*FileOutputStream fileOutputStream = new FileOutputStream("F:\\a.txt");
fileOutputStream.write("97".getBytes());
fileOutputStream.close();*/
//打印流可以打印任何類型的數據,而且打印數據之前都會先把數據轉換成字符串再進行打印。
File file = new File("F:\\a.txt");
//創建一個打印流
PrintStream printStream = new PrintStream(file);
/*
printStream.println(97);
printStream.println(3.14);
printStream.println('a');
printStream.println(true);
Animal a = new Animal("老鼠", "黑色");
printStream.println(a);
//默認標準的輸出流就是向控制臺輸出的,
System.setOut(printStream); //重新設置了標準的輸出流對象
System.out.println("哈哈,猜猜我在哪里!!");
*/
//收集異常的日志信息。
File logFile = new File("F:\\2015年1月8日.log");
PrintStream logPrintStream = new PrintStream( new FileOutputStream(logFile,true) );
try{
int c = 4/0;
System.out.println("c="+c);
int[] arr = null;
System.out.println(arr.length);
}catch(Exception e){
e.printStackTrace(logPrintStream);
}
}
}
- PrintStream
- 打印流(PrintStream) : 是一個字節打印流,System.out對應的類型就是PrintStream, 它的構造函數可以接收三種數據類型的值
- 字符串路徑
- File對象
- OutputStream
- 打印流(PrintStream) : 是一個字節打印流,System.out對應的類型就是PrintStream, 它的構造函數可以接收三種數據類型的值
public static void main(String[] args) throws IOException {
PrintStream ps = System.out;
// 普通write方法需要調用flush或者close方法才會在控制臺顯示
// ps.write(100);
// ps.close();
// 不換行打印
ps.print(100);
ps.print('a');
ps.print(100.5);
ps.print("世界");
ps.print(new Object());
System.out.println("--------------");
// 換行
ps.println(100);
ps.println('a');
ps.println(100.5);
ps.println("世界");
ps.println(new Object());
// 重定向打印流
PrintStream ps2 = new PrintStream(new File("c:\\a.txt"));
System.setOut(ps2);
// 換行
ps2.println(100);
ps2.println('a');
ps2.println(100.5);
ps2.println("世界");
ps2.println(new Object());
// printf(); 格式化
ps2.printf("%d,%f,%c,%s", 100, 3.14, '中', "世界你好!!!");
ps2.printf("%4s和%8s 打價格戰", "京東", "蘇寧");
}
}
注意 : print 方法和write方法的卻別在于, print提供自動刷新。普通的write方法需要調用flush或者close方法才可以看到數據, JDK1.5之后Java對PrintStream進行了擴展, 增加了格式化輸出方式, 可以使用printf()重載方法直接格式化輸出. 但是在格式化輸出的時候需要指定輸出的數據類型格式
-
PrintWriter
- 是一個字符打印流, 構造函數可以接收四種類型的值
- 字符串路徑。
- File對象。
- OutputStream
- Writer
- 對于1/2類型的數據, 還可以指定編碼表(也就是字符集); 對于3/4類型的數據, 可以指定自動刷新
- 注意:該自動刷新值為true時,只有三個方法可以用:println,printf,format.
如果想要既有自動刷新,又可執行編碼。如何完成流對象的包裝?
PrintWrter pw = new PrintWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8"), true);
- 如果想要提高效率。還要使用打印方法:
PrintWrter pw = newPrintWriter(new BufferdWriter(new OutputSteamWriter( newFileOutputStream("a.txt"), "utf-8")), true);
- 是一個字符打印流, 構造函數可以接收四種類型的值
public static void testPrintWriter() throws Exception {
PrintWriter pw = new PrintWriter("c:/b.txt", "gbk");
// pw.append("xxx");
// pw.println(55);
// pw.println('c');
// pw.printf("%.1s與%4s打價格戰, %c", "京東","蘇寧", 'a');
pw.close();
}
- Scanner
public static void testScanner() throws Exception {
// Scanner scanner = new Scanner(new File("c:/test.txt"));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextInt());
System.out.println(scanner.nextBoolean());
scanner.close();
}
操作數組的流對象
- 操作字節數組: ByteArrayInputStream / ByteArrayOutputStream
- 具體使用方法:
- toByteArray();
- toString();
- writeTo(OutputStream);
- 具體使用方法:
public static void testByteArrayInputStream() throws Exception {
InputStream in = new ByteArrayInputStream(new byte[] { 65, 66, 67 });
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int b = -1; (b = in.read()) != -1;) {
out.write(b);
}
in.close();
out.close();
System.out.println(Arrays.toString(out.toByteArray()));
System.out.println(out);
}
- 操作字符數組 : CharArrayReader / CharArrayWriter
- 對于這些流: 源是內存, 目的也是內存。而且這些流并未調用系統資源, 使用的就是內存中的數組, 所以在使用的時候無需close。操作數組的讀取流在構造時,必須要明確一個數據源, 所以要傳入相對應的數組。對于操作數組的寫入流,在構造函數可以使用空參數。因為它內置了一個可變長度數組作為緩沖區
public static void testCharArrayReader() throws Exception {
CharArrayReader reader = new CharArrayReader(new char[] { 'A', 'b', 'c' });
CharArrayWriter writer = new CharArrayWriter();
for (int b = -1; (b = reader.read()) != -1;) {
writer.write(b);
}
reader.close();
writer.close();
System.out.println(writer.toCharArray());
}
- 這幾個流的出現其實就是通過流的讀寫思想在操作數組。類似的對象同理:StringReader / StringWriter。
public static void testStringReader() throws Exception {
StringReader reader = new StringReader("test 中國");
StringWriter writer = new StringWriter();
for (int b = -1; (b = reader.read()) != -1;) {
writer.write(b);
}
reader.close();
writer.close();
System.out.println(writer.toString());
}
操作基本數據類型的流對象
DataInputStream以及DataOutputStreamg概述 :
查看API文檔DataInputStream的信息 : 發現從底層輸入流中讀取基本 Java 數據類型。查看方法, 有讀一個字節, 讀一個char, 讀一個double 的方法
DataInputStream從數據流讀取字節,并將它們轉換為正確的基本數據類型值或字符串。DataInputStream類繼承FilterInputStream類并實現了DataInput接口
DataOutputStream將基本類型的值或字符串轉換為字節,并且將字節輸出到數據流。DataOutputStream類繼承FilterOutputStream并實現了DataOutput接口
-
DataInputStream 的操作基本數據類型的方法:
-
int readInt()
: 一次讀取四個字節,并將其轉成int值 -
boolean readBoolean()
: 一次讀取一個字節 short readShort()
long readLong()
- 剩下的數據類型一樣
-
String readUTF()
: 按照utf-8修改版讀取字符。注意,它只能讀writeUTF()寫入的字符數據
-
-
DataOutputStream 的操作基本數據類型的方法:
-
writeInt(int)
: 一次寫入四個字節。注意和write(int)不同。write(int)只將該整數的最低一個8位寫入。剩余三個8位丟棄 writeBoolean(boolean)
writeShort(short)
writeLong(long)
- 剩下是數據類型也也一樣
-
writeUTF(String)
: 按照utf-8修改版將字符數據進行存儲。只能通過readUTF讀取
-
使用DataOutputStream寫數據文件
public static void testDataInputStream() throws Exception {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
"c:/a.txt"));
out.writeBoolean(true);
out.writeByte(15); // 0x05 1 個字節
out.writeBytes("abc"); // 0x 0041 2個字節
out.writeChar('X'); // ??
out.writeChars("xyz");
out.writeLong(111);
out.writeUTF("中國");
out.close();
DataInputStream in = new DataInputStream(
new FileInputStream("c:/a.txt"));
System.out.println(in.readBoolean());
System.out.println(in.readByte());
System.out.println(in.readByte());
System.out.println(in.readByte());
System.out.println(in.readByte());
System.out.println(in.readChar());
System.out.println(in.readChar());
System.out.println(in.readChar());
System.out.println(in.readChar());
System.out.println(in.readLong());
System.out.println(in.readUTF());
in.close();
}