(一)繼承增強一個類的功能
BufferedReader 對FileReader拓展了一個功能,readLine.
需求1:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有行號。
需求2:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有分號。//semicolon分號
需求3:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有雙引號。
需求4:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有行號和分號。
需求5:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有雙引號和行號。
需求6:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有雙引號和分號。
需求7:編寫一個類對BufferedReader的功能進行增強,增強其readLine方法,返回數據帶有雙引號和號。
解決方案:使用繼承,通過子類增強其功能。
增強一個類的功能:
通過繼承增強一個類的功能的優點:代碼結構清晰簡單。
缺點:代碼不靈活,會導致繼承體系過於龐大。
//帶行號的緩衝類
class BufferedLineNum extends BufferedReader{
int count=0;
public BufferedLineNum(Reader in) {
super(in);//此句話只是爲了讓編譯器編譯不報錯
}
@Override
public String readLine() throws IOException {
String line=super.readLine();//調用其父類的readLine()方法
if(line==null)
return null;
line=count+":"+line;
count++;
return line;//不可能爲null
}
}
//帶有分號的緩衝類
class BufferedSemi extends BufferedReader{
public BufferedSemi(Reader in) {//構造函數
super(in);
}
@Override
public String readLine() throws IOException {
String line=super.readLine();
if(line==null)
return null;
line=line+";";
return line;
}
}
//帶雙引號的緩衝類
class BufferedDoubleQuo extends BufferedReader{
public BufferedDoubleQuo(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String line=super.readLine();
if(line==null)
return null;
line="\""+line+"\"";//兩側添加雙引號
return line;
}
}
public class demo1 {
public static void main(String[] args) throws IOException {
File file =new File("C:\\Users\\lenovo\\eclipse-workspace\\day19\\src\\output\\copy_picture.java");
FileReader fileReader=new FileReader(file);
/*
BufferedLineNum bufferedLineNum =new BufferedLineNum(fileReader);
String line=null;
while((line=bufferedLineNum.readLine())!=null) {
System.out.println(line);
}
*/
/*
BufferedSemi bufferedSemi=new BufferedSemi(fileReader);
String line=null;
while((line=bufferedSemi.readLine())!=null) {
System.out.println(line);
}
*/
BufferedDoubleQuo BufferedDoubleQuo=new BufferedDoubleQuo(fileReader);
String line=null;
while((line=BufferedDoubleQuo.readLine())!=null) {
System.out.println(line);
}
}
}
(二)裝飾者設計模式增強功能
裝飾者設計模式:增強一個類的功能 。
步驟:
1.在增強類的內部維護一個被增強類的引用。
2.讓增強與被增強類有一個共同父類或父接口。
好處:利用了多態達到了類與類之間可以互相裝飾,比較靈活。
缺點:代碼結構不清晰,難以理解。
讓這些裝飾類有一個共同父類(父接口)的目的是爲了讓這些裝飾類可以互相裝飾,構成一條裝飾鏈。
//帶行號
class BufferedLineNum2 extends BufferedReader{
BufferedReader bufferedReader;//在內部維護一個被增強類的引用對象
int count=1;
//構造函數
public BufferedLineNum2(BufferedReader bufferedReader) {//BufferedReader bufferedReader=new BufferedSemi2();
super(bufferedReader);
this.bufferedReader=bufferedReader;
}
public String readLine() throws IOException {
String line=bufferedReader.readLine();//如果這裏的readLine方法調用的是BufferedSemi2的readLine方法,可以保證又有行號又有分號
if(line==null)
return null;
line=count+":"+line;
count++;
return line;
}
}
//帶分號
class BufferedSemi2 extends BufferedReader{//繼承是爲了讓該類的對象可以傳遞給BufferedLineNum2
BufferedReader bufferedReader;//在內部維護一個被增強類的引用對象
//構造函數
public BufferedSemi2(BufferedReader bufferedReader) {
super(bufferedReader);//該語句沒有任何作用,只是爲了編譯不報錯,因爲父類沒有無參的構造函數。
this.bufferedReader=bufferedReader;
}
public String readLine() throws IOException {
String line=bufferedReader.readLine();
if(line==null)
return null;
line=line+";";
return line;
}
}
//帶雙引號
class BufferedDoubleQuo2 extends BufferedReader{
BufferedReader bufferedReader;//在內部維護一個被增強類的引用對象
//構造函數
public BufferedDoubleQuo2(BufferedReader bufferedReader) {
super(bufferedReader);
this.bufferedReader=bufferedReader;
}
public String readLine() throws IOException {
String line=bufferedReader.readLine();
if(line==null)
return null;
line="\""+line+"\"";
return line;
}
}
public class demo2 {
public static void main(String[] args) throws IOException {
File file =new File("C:\\Users\\lenovo\\eclipse-workspace\\day19\\src\\output\\copy_picture.java");
//建立輸入字符流對象
FileReader fileReader=new FileReader(file);
//建立一個緩衝輸入字符流對象
BufferedReader bufferedReader=new BufferedReader(fileReader);
//建立一個帶行號的緩衝輸入字符流對象
BufferedSemi2 bufferedSemi2=new BufferedSemi2(bufferedReader);
BufferedLineNum2 bufferedLineNum2=new BufferedLineNum2(bufferedSemi2);
BufferedDoubleQuo2 bufferedDoubleQuo2=new BufferedDoubleQuo2(bufferedLineNum2);//行號+雙引號+分號
//BufferedDoubleQuo2 bufferedDoubleQuo2=new BufferedDoubleQuo2(bufferedSemi2);//雙引號+分號
//BufferedLineNum2 bufferedLineNum2=new BufferedLineNum2(bufferedDoubleQuo2);//行號+雙引號
//BufferedLineNum2 bufferedLineNum2=new BufferedLineNum2(bufferedSemi2);//行號+分號
String line=null;
while((line=bufferedDoubleQuo2.readLine())!=null) {
System.out.println(line);
}
}
}
(三)序列流對象
序列流:SequenceInputStream(序列流):可以把多個輸入流串聯起來讀取,先從第一個開始讀取,再讀下一個。。
兩個源文件:
public static void merge2() throws IOException{
//找到目標文件
File file1=new File("E:\\aa.txt");
File file2=new File("E:\\bb.txt");
File file3=new File("E:\\cc.txt");
//建立數據輸入輸出通道
FileInputStream fileInputStream1=new FileInputStream(file1);
FileInputStream fileInputStream2=new FileInputStream(file2);
FileOutputStream fileOutputStream=new FileOutputStream(file3);
//創建序列流對象
SequenceInputStream sequenceInputStream=new SequenceInputStream(fileInputStream1,fileInputStream2);
//邊讀邊寫
byte[] buf=new byte[1024];
int length=0;
while((length=sequenceInputStream.read(buf))!=-1) {
fileOutputStream.write(buf,0,length);
//fileOutputStream.flush();
}
//關閉資源
sequenceInputStream.close();//關閉的是傳入的兩個輸入流
fileOutputStream.close();
}
}
三個源文件:
public static void merge3() throws IOException{
//找到目標文件
File file1=new File("E:\\aa.txt");
File file2=new File("E:\\bb.txt");
File file3=new File("E:\\cc.txt");
File file4=new File("E:\\dd.txt");
//建立數據輸入輸出通道
FileInputStream fileInputStream1=new FileInputStream(file1);
FileInputStream fileInputStream2=new FileInputStream(file2);
FileInputStream fileInputStream3=new FileInputStream(file3);
FileOutputStream fileOutputStream=new FileOutputStream(file4);
Vector<FileInputStream> v=new Vector<FileInputStream>();
v.add(fileInputStream1);
v.add(fileInputStream2);
v.add(fileInputStream3);
//迭代器的接口
Enumeration<FileInputStream> e=v.elements();//通過vector得到一個迭代器
//創建一個序列流對象
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();
}
}
練習:把一首mp3分成n份,每份1m,然後合併起來。
public class demo5 {
public static void main(String[] args) throws IOException {
//cutFile();
mergeMp3();
}
//將一個mp3文件拆成多個文件,每個大小1M
public static void cutFile() throws IOException {
File file=new File("F:\\KwDownload\\song\\王一博-因爲我們在一起.mp3");
FileInputStream fileInputStream=new FileInputStream(file);
byte[] buf=new byte[1024*1024];//1M
int length=0;
int count=1;
while((length=fileInputStream.read(buf))!=-1) {
System.out.println("lll");
//每讀取一次,生成一個文件
FileOutputStream fileOutputStream=new FileOutputStream("E:\\music1\\part"+count+".mp3");
fileOutputStream.write(buf,0,length);
count++;
fileOutputStream.close();
}
fileInputStream.close();
}
//合併多個mp3文件爲一個
public static void mergeMp3() throws IOException {
File dir=new File("E:\\music1");
File destFile=new File("F:\\因爲我們在一起.mp3");
File[] files=dir.listFiles();//獲取文件夾中所有子文件
//創建一個Vector對象,存儲FileInputStream對象
Vector<FileInputStream> vector=new Vector<FileInputStream>();
//遍歷數組
for(File file1:files) {
if(file1.getName().endsWith(".mp3")) {
vector.add(new FileInputStream(file1));
}
}
//創建一個序列流對象
SequenceInputStream sequenceInputStream =new SequenceInputStream(vector.elements());
//創建一個輸出流對象
FileOutputStream fileOutputStream=new FileOutputStream(destFile);
//邊讀邊寫
byte[] buf=new byte[1024];
int length=0;
while((length=sequenceInputStream.read(buf))!=-1) {
fileOutputStream.write(buf,0,length);
}
fileOutputStream.close();
sequenceInputStream.close();
}
}
(四)對象輸入輸出流
ObjectOutputStream(對象的輸出流):主要用於將數據寫到文件上。(硬盤)
ObjectInputStream(對象的輸入流):把硬盤中的數據讀取回來。
ObjectOutputStream要注意的事項:
1.使用ObjectOutputStream的write方法時,只能寫出實現了Serializable接口的對象。
Serializable接口沒有任何方法,這種接口稱爲標識接口。
2.對象反序列化時創建對象是不會調用構造方法的。
3.把對象寫到文件上的時候。文件除了記錄對象的一些信息以外,還記錄了class的版本號,版本號是通過一個類的
類名、包名、工程名、成員一起算出來的一個id號。
4.在反序列化的時候,jvm會使用本地class文件算出一個id號與文件記錄的id號相比較,若不相等,則反序列化失敗。
5.如果一個類的成員後期可能會發生改動,那麼可以在序列化之前就指定一個serialVersionUID,如果一個類一旦制定了一個
serialVersionUID,那麼jvm就不會再計算該class文件的serialVersionUID。
6.如果一個類的某些成員不想報序列化到硬盤上,可以用關鍵字transient修飾。
7.如果一個類的內部維護了另外一個類的對象,那麼另外一個類也必須要實現Serializable接口。
構造方法調用了一定會創建對象,創建對象不一定會調用構造方法。(對象反序列化)
class Address implements Serializable{
String country;
String city;
public Address(String country, String city) {
super();
this.country = country;
this.city = city;
}
}
class User implements Serializable{//對象要寫入硬盤就必須實現該接口
public static final long serialVersionUID=1;
Address address=new Address("中國","北京");
String userName;
String password;
transient int age;//透明化,不會被序列化
public User(String userName, String password,int age) {
super();
this.userName = userName;
this.password = password;
this.age=age;
}
@Override
public String toString() {
return "{用戶名:"+this.userName+" 密碼:"+this.password+" 年齡:"+this.age+"}";
}
}
public class demo1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writeObj();
//readObj();
}
//對象的反序列化---->讀取硬盤中的對象到內存中。
//把對象寫到(文件)硬盤上--->對象的序列化
public static void writeObj() throws IOException {
User user=new User("admin","123",18);
//找到目標
File file=new File("E:\\Obj.txt");
//建立數據的輸出通道
FileOutputStream fileOutputStream=new FileOutputStream(file);
//建立對象的輸出流對象
ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);
//把對象寫出到輸出流中
objectOutputStream.writeObject(user);
//關閉資源
objectOutputStream.close();
}
public static void readObj() throws IOException, ClassNotFoundException {
//找到目標
File file=new File("E:\\Obj.txt");
//建立數據的輸入通道
FileInputStream fileInputStream=new FileInputStream(file);
//建立對象的輸入流
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
//讀取對象數據
User user=(User)objectInputStream.readObject();//反序列化的過程中創建了對象
//反序列化時需要創建對象,創建對象時需要依賴class文件
System.out.println("對象的信息是:"+user);
}
}
(五)Properties配置文件
Properties--配置文件類 屬於Map集合體系。
作用:
1.生成配置文件。
2.讀取配置文件。
Properties要注意的事項:
1.往Properties添加數據時,不要添加非字符串類型數據,如果添加了非字符串類型數據,那麼Properties的處理方式就是進行強制類型轉換,強轉報錯。
2.如果Properties的數據出現了中文字符,那麼使用store方法時千萬不要使用字節流,如果使用了字節流,那麼默認使用iso8859-1碼錶進行保存,
中文數據建議使用字符流。
3.如果修改了Properties裏面的數據,一定要重新生成一個配置文件。
public class demo1 {
public static void main(String[] args) throws IOException {
readProperties();
//createProperties();
}
//讀取配置文件---加載配置文件到Properties使用load方法。
public static void readProperties() throws IOException {
Properties properties =new Properties();
//創建輸入字符流對象
FileReader fileReader=new FileReader("E:\\users.properties.txt");
//加載配置文件的數據到Properties
properties.load(fileReader);
//遍歷元素
Set<Entry<Object,Object>> set=properties.entrySet();
for(Entry<Object,Object> entry:set) {
System.out.println("鍵:"+entry.getKey()+" 值:"+entry.getValue());
}
properties.setProperty("hongmi", "110");
//重新生成一個配置文件
properties.store(new FileWriter("E:\\users.properties.txt"), "heihei");//第一個參數是一個輸出流對象的匿名對象
}
//創建一個配置文件
public static void createProperties() throws IOException {
//創建一個Properties對象
Properties properties =new Properties();
properties.setProperty("xiaomi", "123");
properties.setProperty("大米", "456");
properties.put("hongmi", "147");
properties.setProperty("heimi", "789");
/*
//遍歷
Set<Entry<Object,Object>> set=properties.entrySet();
for(Entry<Object,Object> entry:set) {
System.out.println("鍵:"+entry.getKey()+" 值:"+entry.getValue());
}
*/
/*
FileOutputStream fileOutputStream=new FileOutputStream("E:\\users.properties.txt");
//利用Properties生成一個配置文件。
properties.store(fileOutputStream, "haha");//第二個參數是使用一段文字對對象進行描述
*/
FileWriter fileWriter=new FileWriter("E:\\users.properties.txt");
properties.store(fileWriter, "haha");//第二個參數是使用一段文字對對象進行描述
}
}