十四、十五天筆記總結
阿新 • • 發佈:2017-05-18
io流、多線程
1. LineNumberReader: *public int getLineNumber():獲取行號 *public void setLineNumber(int lineNumber):設置起始行號 String readLine():讀取一行 案例:讀取文件,每次讀取一行打印並且加上行號 packagecom.edu_01; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; /** * 1. LineNumberReader: *public int getLineNumber():獲取行號 *public void setLineNumber(int lineNumber):設置起始行號 String readLine():讀取一行 案例:讀取文件,每次讀取一行打印並且加上行號 * */ public class LineNumberReaderDemo { publicstatic void main(String[] args) throws IOException { //創建LineNumberReader對象 //publicLineNumberReader(Reader in) LineNumberReaderlnr = new LineNumberReader(new FileReader("a.txt")); //默認起始行號從0開始 //設置其實行號為從10開始 lnr.setLineNumber(10); //一次讀取一行 Stringline; while((line = lnr.readLine())!=null) { //打印每一行的行號和內容 System.out.println(lnr.getLineNumber()+":"+line); } //關流 lnr.close(); } } 2.操作基本數據類型的流 * 可以操作基本類型的流對象。 *DataInputStream:讀數據 *DataOutputStream:寫數據 * 案例:給流中寫基本類型的數據,並且讀取 註意: * 讀寫順序必須一致,否則數據有問題。 package com.edu_02; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * 2.操作基本數據類型的流 * 可以操作基本類型的流對象。 *DataInputStream:讀數據 *DataOutputStream:寫數據 案例:給流中寫基本類型的數據,並且讀取 註意: * 讀寫順序必須一致,否則數據有問題。 * */ public class DataOutputStreamDemo { publicstatic void main(String[] args) throws IOException { //寫數據 //write(); read(); } privatestatic void read() throws IOException { //DataInputStream:讀數據 //創建對象:publicDataInputStream(InputStream in) DataInputStreamdis = new DataInputStream(new FileInputStream("dos.txt")); //讀數據了,按什麽順序寫入就必須按照什麽順序讀出來 System.out.println(dis.readByte()); System.out.println(dis.readShort()); System.out.println(dis.readInt()); System.out.println(dis.readLong()); System.out.println(dis.readChar()); System.out.println(dis.readFloat()); System.out.println(dis.readDouble()); System.out.println(dis.readBoolean()); //關流 dis.close(); } privatestatic void write() throws IOException { //publicDataOutputStream(OutputStream out) DataOutputStreamdos = new DataOutputStream(new FileOutputStream("dos.txt")); //給流關聯的文件中寫入基本類型的數據 dos.writeByte(20); dos.writeShort(200); dos.writeInt(2000); dos.writeLong(20000L); dos.writeChar(97); dos.writeFloat(12.34F); dos.writeDouble(23.34); dos.writeBoolean(true); //關流 dos.close(); } } 3. 內存操作流:解決臨時數據存儲的問題。 操作字節數組(演示著一個案例即可) ByteArrayInputStream ByteArrayOutputStream byte[] toByteArray() 將之前寫入內存的流轉換成字節數組 操作字符數組 CharArrayReader CharArrayWrite 操作字符串 StringReader StringWriter 案例:演示 操作字節數組 ByteArrayInputStream ByteArrayOutputStream 將數據寫到流中保存在內存,並且讀取 packagecom.edu_03; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * 操作字節數組(演示著一個案例即可) ByteArrayInputStream ByteArrayOutputStream byte[] toByteArray() 將之前寫入內存的流轉換成字節數組 * */ public class ByteArrayOutputStreamDemo { publicstatic void main(String[] args) throws IOException { //給內存中寫數據publicByteArrayOutputStream() ByteArrayOutputStreambaos = new ByteArrayOutputStream(); //給內存中調用方法寫數據 baos.write("hello".getBytes()); //將寫入內存中的數據讀取出來 byte[]buf = baos.toByteArray();//調用這個方法,將之前寫入內存中的數據存儲到字節數組中 ByteArrayInputStreambais = new ByteArrayInputStream(buf);//將剛才存儲到字節數組中的內容關聯上bais //只有這樣之後,我們才可以直接從bais中讀取我們想要的內容 //一次讀取一個字節 intby; while((by=bais.read())!=-1) { System.out.print((char)by); } //關流 bais.close(); baos.close(); } } 4. * 打印流: * 字節打印流 PrintStream * 字符打印流 PrintWriter * * 特點: * A:只能操作目的地,不能操作數據源 * B:可以操作任意類型的數據 * C:如果啟動了自動刷新,能夠自動刷新 * D:可以操作文件的流 * 註意:什麽流可以直接操作文件? 看流對象的API,如果其構造方法同時有File和String類型的參數,就可以直接操作文件。 案例1:利用字符打印流給文件中書寫數據(String類型),需要手動刷新 package com.edu_04; import java.io.IOException; import java.io.PrintWriter; /** * 4. * 打印流: * 字節打印流 PrintStream * 字符打印流 PrintWriter * * 特點: * A:只能操作目的地,不能操作數據源 * B:可以操作任意類型的數據 * C:如果啟動了自動刷新,能夠自動刷新 * D:可以操作文件的流 * 註意:什麽流可以直接操作文件? 看流對象的API,如果其構造方法同時有File和String類型的參數,就可以直接操作文件。 * */ public class PrintWriterDemo { publicstatic void main(String[] args) throws IOException { //使用打印流給文件中寫入hello,java,world //publicPrintWriter(String fileName) PrintWriterpw = new PrintWriter("pw.txt"); //給流關聯的文件中寫數據 pw.write("hello"); pw.write("java"); pw.write("world"); //刷新 pw.flush(); //3.關流 pw.close(); } } * 操作任意類型的數據呢? *print() *println():如果啟動了自動刷新,能夠實現刷新,而且還實現了自動換行。 * 如何啟動自動刷新:利用構造 *PrintWriter(OutputStream out, boolean autoFlush) *PrintWriter(Writer out, boolean autoFlush) * * 如果啟用了自動刷新,則只有在調用 println、printf 或 format 的其中一個方法時才可能完成此操作 案例3:利用字符打印流復制java文件(BufferedReader+PrintWriter) package com.edu_04; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class PrintWriterDemo3 { publicstatic void main(String[] args) throws IOException { /** * 案例3:利用字符打印流復制java文件(BufferedReader+PrintWriter) */ //數據源 BufferedReaderbr = new BufferedReader(new FileReader("PrintWriterDemo.java")); //目的地 //PrintWriter(Writerout, boolean autoFlush) PrintWriterpw = new PrintWriter(new FileWriter("copy.java"),true); //讀一行寫一行 Stringline; while((line=br.readLine())!=null) { pw.println(line);//1.寫數據 2.換行 3.刷新 } //關流 pw.close(); br.close(); } } 5.標準輸入輸出流 *System類下有這樣的兩個成員變量: * 標準輸入流: * public static final InputStream in 案例1:利用標註輸入流進行鍵盤錄入,錄入後讀取流並打印在控制臺 packagecom.edu_05; import java.io.IOException; import java.io.InputStream; public class SystemIn { publicstatic void main(String[] args) throws IOException { /** * public static final InputStream in */ //將鍵盤錄入的數據封裝在了輸入流中 //Scannersc = new Scanner(System.in); InputStreamis = System.in; //將鍵盤錄入的數據從輸入流中讀取出來 intby; while((by=is.read())!=-1) { System.out.print((char)by); } //關流 is.close(); } } 案例2:用IO流實現鍵盤錄入,一次讀取一行數據 分析: InputStream is = System.in; BufferedReader是字符緩沖流,是對字符流進行高效操作的 所以,參數必須是字符類型 而我們現在有的是字節類型的流 請問:怎麽辦呢?轉換流 packagecom.edu_05; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class SystemIn2 { publicstatic void main(String[] args) throws IOException { /** * 案例2:用IO流實現鍵盤錄入,一次讀取一行數據 * InputStream is = System.in; * InputSreamReader isr = newInputStreamReader(is) * BufferedReader br = new BufferedReader(isr); */ //將上面的分析寫為一部 BufferedReaderbr = new BufferedReader(new InputStreamReader(System.in)); //一次讀取一行數據 System.out.println("請輸入你的姓名"); Stringname = br.readLine(); System.out.println("請輸入你的年齡"); Stringage = br.readLine(); System.out.println(name+":"+age); } } * 標準輸出流: * public static final PrintStream out 案例:解析輸出語句System.out.println("helloworld"); packagecom.edu_05; import java.io.PrintStream; /** * 標準輸出流: * public static final PrintStream out 案例:解析輸出語句System.out.println("helloworld"); * */ public class SystemOut { publicstatic void main(String[] args) { // PrintStreamps = System.out; // ps.println(true); //上面兩行合並為一行,底層調用的字節打印流中的方法 System.out.println(true); } } 6. * 合並流:SequenceInputStream類可以將多個輸入流串流在一起,合並為一個輸入流,因此,該流也被稱為合並流。 構造: SequenceInputStream(InputStream s1, InputStream s2) :將s1和s2合並成一個輸入流,先讀取s1後讀取s2 * * 案例1: * 我要把DataStreamDemo.java和ByteArrayStreamDemo.java寫到一個文件Copy.java * * 數據源: * DataStreamDemo.java * ByteArrayStreamDemo.java * 目的地: * Copy.java packagecom.edu_06; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; /** * 6. * 合並流:SequenceInputStream類可以將多個輸入流串流在一起,合並為一個輸入流,因此,該流也被稱為合並流。 構造: SequenceInputStream(InputStream s1, InputStream s2) :將s1和s2合並成一個輸入流,先讀取s1後讀取s2 * * 案例1: * 我要把DataStreamDemo.java和ByteArrayStreamDemo.java寫到一個文件Copy.java * * 數據源: * DataStreamDemo.java * ByteArrayStreamDemo.java * 目的地: * Copy.java * */ public class SequenceInputStreamDemo { publicstatic void main(String[] args) throws IOException { //創建合並流對象 //SequenceInputStream(InputStreams1, InputStream s2) :將s1和s2合並成一個輸入流,先讀取s1後讀取s2 //將兩個數據源合而為一 SequenceInputStreamsis = new SequenceInputStream(newFileInputStream("PrintWriterDemo.java"), newFileInputStream("SystemIn2.java")); //封裝目的地 FileOutputStreamfos = new FileOutputStream("copy2.java"); //一下讀寫一個字節數組 byte[]buf = new byte[1024]; intlen; while((len=sis.read(buf))!=-1) { //讀多少寫多少 fos.write(buf,0, len); } //關流 fos.close(); sis.close(); } } 8. 對象的序列化和反序列化 * 序列化流:把對象按照流一樣的方式寫到文件或者在網絡中傳輸。 * 反序列化流:把文件或者網絡中的流對象數據還原對象。 * *ObjectOutputStream:序列化流 writeObject(Object obj) 將指定的對象寫入 ObjectOutputStream。 *ObjectInputStream:反序列化流 Object readObject() 從 ObjectInputStream 讀取對象。 * 註意:如果一個類不是實現Serializable接口無法把實例化,會報異常java.io.NotSerializableException * 類通過實現java.io.Serializable 接口以啟用其序列化功能。未實現此接口的類將無法使其任何狀態序列化或反序列化。 * * 如何實現序列化? * 讓對象所屬類的實現序列化接口。 9. Properties(查看api實現map接口本質是一個map集合) 9.1 *Properties:Properties 類表示了一個持久的屬性集。屬性列表中每個鍵及其對應值都是一個字符串。 * 特點:Properties可保存在流中或從流中加載。 案例:使用map集合的put方法給集合中存儲數據並且遍歷 9.2 *Properties的特有功能: * A:添加元素 * public ObjectsetProperty(String key,String value) * B:獲取元素 * public StringgetProperty(String key) * public Set<String>stringPropertyNames() 案例:使用它的特有功能添加元素並遍歷 packagecom.edu_08; import java.util.Properties; import java.util.Set; /** * 9.2 *Properties的特有功能: * A:添加元素 * public ObjectsetProperty(String key,String value) * B:獲取元素 * public StringgetProperty(String key) * public Set<String>stringPropertyNames() 案例:使用它的特有功能添加元素並遍歷 * */ public class PropertiesDemo2 { publicstatic void main(String[] args) { //創建Properties這個集合 Propertiesprop = new Properties(); //調用publicObject setProperty(String key,String value) prop.setProperty("huangxiaoming","baby"); prop.setProperty("dengchao","sunli"); prop.setProperty("xidada","pengliyuan"); //1.獲取所有鍵的集合 //publicSet<String> stringPropertyNames() Set<String>keys = prop.stringPropertyNames(); //遍歷鍵,根據鍵找值 for(String key : keys) { System.out.println(key+":"+prop.getProperty(key)); } } } 9.3 * 可以和IO流進行結合使用: * 把文件中的數據加載到集合中。註意:文件中的數據必須是鍵值對象形式的(例如:張傑=謝娜)。 * public void load(InputStreaminStream) * public void load(Reader reader) 案例:創建一個鍵值對文件,將文件中的鍵值對加載到集合中,輸出集合 packagecom.edu_08; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Properties; import java.util.Set; /** * 可以和IO流進行結合使用: * 把文件中的數據加載到集合中。註意:文件中的數據必須是鍵值對象形式的(例如:張傑=謝娜)。 * public void load(InputStreaminStream) * public void load(Reader reader) 案例:創建一個鍵值對文件,將文件中的鍵值對加載到集合中,輸出集合 * */ public class PropertiesDemo3 { publicstatic void main(String[] args) throws IOException { //創建集合 Propertiesprop = new Properties(); //將文件中的鍵值對,加載到集合中 prop.load(newFileReader("prop.txt")); //遍歷集合 Set<String>keys = prop.stringPropertyNames(); for(String key : keys) { System.out.println(key+":"+prop.getProperty(key)); } } } * * 把集合中的數據存儲到文本文件中,並且是按照鍵值對形式存儲的。 * public void store(OutputStreamout,String comments) * public void store(Writerwriter,String comments) 案例:將創建的鍵值對集合加載到文件中 packagecom.edu_08; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; /** * 把集合中的數據存儲到文本文件中,並且是按照鍵值對形式存儲的。 * public void store(OutputStreamout,String comments) * public void store(Writerwriter,String comments) 案例:將創建的鍵值對集合加載到文件中 * */ public class PropertiesDemo4 { publicstatic void main(String[] args) throws IOException { //創建集合 Propertiesprop = new Properties(); //給集合中存儲數據 prop.setProperty("liudehua","50"); prop.setProperty("liming","60"); prop.setProperty("zhangxueyou","40"); //將集合中的元素,存儲到文本文件中 prop.store(newFileWriter("prop2.txt"), "name=age"); } } 案例:我有一個文本文件,我知道數據是鍵值對形式的,但是不知道內容是什麽。 請寫一個程序判斷是否有“lisi”這樣的鍵存在,如果有就改變其值為”100” packagecom.edu_09; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; import java.util.Set; public class PropTest { publicstatic void main(String[] args) throws IOException { /** * 案例:我有一個文本文件,我知道數據是鍵值對形式的,但是不知道內容是什麽。 請寫一個程序判斷是否有“lisi”這樣的鍵存在,如果有就改變其值為”100” zhangsan=3 lisi=4 wangwu=5 1.創建集合對象 2.將文件中的鍵值對加載到集合中 3.獲取多有的鍵的集合,遍歷,判斷 4.如果存在lisi,的話,給集合中重新存儲鍵值對lisi=100 5.將集合中的數據存儲到文件中 */ //1.創建集合對象 Propertiesprop = new Properties(); //2.將文件中的鍵值對加載到集合中 prop.load(newFileReader("prop3.txt")); //3.獲取多有的鍵的集合,遍歷,判斷 Set<String>keys = prop.stringPropertyNames(); //4.如果存在lisi,的話,給集合中重新存儲鍵值對lisi=100 for(String key : keys) { if("lisi".equals(key)) { prop.setProperty(key,"100"); } } //5.將集合中的數據存儲到文件中 prop.store(newFileWriter("prop3.txt"), null); } } 案例:我有一個猜數字小遊戲的程序,請寫一個程序實現在測試類中只能用5次, 超過5次提示:遊戲試玩已結束,請付費。 package com.edu_01; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; /** * 案例:我有一個猜數字小遊戲的程序,請寫一個程序實現在測試類中只能用5次, 超過5次提示:遊戲試玩已結束,請付費。 * 1.將文件中的鍵值對加載到集合中 * 2.拿出已經玩耍的次數做判斷 * 3.如果已經超過5次,提示付費 * 4.如果少於5次開啟遊戲,將文件中的次數+1 */ public class PropTest { publicstatic void main(String[] args) throws IOException { //1.將文件中的鍵值對加載到集合中 Propertiesprop = new Properties(); prop.load(newFileReader("count.txt")); //2.拿出已經玩耍的次數做判斷 Stringcount = prop.getProperty("count"); //將String類型轉換為int類型 intnumber = Integer.parseInt(count); if(number>4) { System.out.println("次數已到,請付費"); }else{ //開啟遊戲 GuessNumber.startGame(); number++; //將自增的次數重新存儲到文件中 prop.setProperty("count",number+""); //將新的prop集合存儲到文件中 prop.store(newFileWriter("count.txt"), null); } } } package com.edu_01; import java.util.Scanner; public class GuessNumber { publicstatic void startGame(){ intran = (int) (Math.random()*100+1); Scannersc = new Scanner(System.in); System.out.println("請輸入你猜測得數字"); while(true) { intnumber = sc.nextInt(); if(number>ran) { System.out.println("大了"); }elseif (number<ran) { System.out.println("小了"); }elseif (number==ran) { System.out.println("猜對了"); break; } } } } 十五天: 1:多線程(理解) (1)線程是依賴於進程而存在的。 A:進程 正在運行的應用程序 B:線程 進程的執行路徑,執行單元 案例:畫圖理解單線程和多線程的代碼執行流程 (2)多線程的兩種方案:(掌握) 請參照:多線程兩種方式 繼承Thread類(查看api簡單介紹Thread類): 實現Runable接口: packagecom.edu_02; /** * 繼承Thread類(查看api簡單介紹Thread類): * */ public class MyThread extends Thread{ //1.繼承Thread類 //2.重寫run方法,重寫run方法中的代碼之後,當我們啟動了這個線程之後,我們的這個線程就會執行run方法中的代碼 @Override publicvoid run() { //需求:開啟該線程之後,執行一個for循環 for(int i = 0; i < 10; i++) { System.out.println(i); } } } package com.edu_02; public class Test { publicstatic void main(String[] args) { //只要我們創建了一個線程對象,並且啟動該線程的實例,我們就相當於開啟了一個線程 MyThreadmt = new MyThread(); mt.start();//1.開啟了一個線程 2.讓開啟的這個線程執行他對應的類中的run方法 //在次創建一個子線程,並開啟這個子線程執行他的run方法 MyThreadmt2 = new MyThread(); mt2.start(); } } (3)多線程的幾個問題: A:啟動線程用的是哪個方法 start() B:start()和run()的區別 start():1.開啟線程 2.執行run()方法裏面的代碼 run():執行的是線程裏面執行的代碼,並不會開啟線程 C:為什麽要重寫run() 因為每個線程需要執行的代碼都是都是不一樣的, 我們需要將每個線程自己獨立執行的代碼寫到run()方法中執行 D:線程可以多次啟動嗎 (4)線程的調度和控制 線程休眠(Thread.sleep(毫秒值)) 線程名稱(setName(),getName();) 線程的調度及優先級setPriority(10)(註意默認值是5,區間在1-10之間) 什麽叫線程優先級:說白了就是設置你搶占cpu執行權搶占到的概率 (5)多線程案例(兩種方式實現,睡一會出現線程安全問題): 5.1繼承Thread賣票 5.2實現Runnable賣票(睡一會出現線程安全問題) 問題: 按照真實的情景加入了延遲,確發現出現了這樣的兩個問題: A:相同的票賣了多次 CPU的一次操作必須是原子性的(操作是CPU執行一次就可以直接完成的) B:出現了負數的票 隨機性和延遲導致的 出現上面的問題稱為線程安全問題。 (6)多線程安全問題 A:是否是多線程環境 B:是否有共享數據 C:是否有多條語句操作共享數據 (7)如何解決多線程安全問題(掌握) *線程安全執行效率就低 A:同步代碼塊(測試不是同一個鎖的情況,測試是同一個鎖的情況) synchronized(對象) { 需要被同步的代碼。 } 需求:1.測試不是同一把鎖的時候線程安全嗎? 2.如果是同一把鎖線程安全嗎? 兩個問題:1.對象是什麽 ? 答:任意對象 ,相當於是一把鎖,只要線程進去就把鎖鎖上 2.需要同步的代碼? 答:被線程執行的代碼 C:鎖對象問題 a:同步代碼塊(定義一個抽象類,裏面專門定義一個鎖) 任意對象 b:同步方法(僅適用於實現runable接口) publicsynchronized void sellTicket(){同步代碼} this c:靜態同步方法 類的字節碼對象 publicstatic synchronized void sellTicket() { 需要同步的代碼 } (8)匿名內部類的方式使用多線程(掌握) newThread() { publicvoid run() { ... } }.start(); newThread(new Runnable(){ publicvoid run() { ... } }).start(); 案例:利用匿名內部類,啟動多個線程,驗證單例設計模式之懶漢式所存在的缺陷, 當使用多線程來搞的時候就不單例了。。 (9)JDK5的Lock鎖,我們之前造的所有的鎖都沒有手動釋放鎖 staticLock lock = new ReentrantLock(); 枷鎖:lock.lock(); 釋放鎖:lock.unlock(); 可以讓我們明確的知道在哪裏加鎖和釋放鎖。 依然寫一個賣票的demo,用lock枷鎖釋放鎖, 為了保證我們創建的鎖一定會被釋放,用一下代碼進行改進 try{....}finally{.....} (10)死鎖問題 同步嵌套,鎖裏面套了一個鎖,出現同步嵌套 (簡單介紹,要求大家以後寫的時候需要註意) (11)線程等待和喚醒機制(案例演示:waitThread,NotifyThread,MyLock,Test) 鎖對象調用wait()鎖對象調用notify() 註意: wait和sleep的區別 線程等待,在等待的同時釋放鎖,而sleep()方法在執行的過程中是不會釋放鎖的
十四、十五天筆記總結