2.其中,全部輸入流類都是抽象類InputStream(字節輸入流),或者抽象類Reader(字符輸入流)的子類;而全部輸出流都是抽象類OutputStream(字節輸出流)或者Writer(字符輸出流)的子類。php
3.InputStream類是字節輸入流的抽象類,是全部字節輸入流的父類,InputStream類具備層次結構以下圖所示
java
4.java中的字符是Unicode編碼的,是雙字節的。InputStream是用來處理字節的,在處理字符文本時很不方便。Java爲字符文本的輸入提供了專門的一套類Reader。Reader類是字符輸入流的抽象類,全部字符輸入流的實現都是它的子類。
程序員
5.輸出流OutputStream類是字節輸入流的抽象類,此抽象類表示輸出字節流的全部類的超類。
算法
6.Writer類是字符輸出流的抽象類,全部字符輸出類的實現都是它的子類。
編程
7.File類是IO包中惟一表明磁盤文件自己的對象。經過File來建立,刪除,重命名文件。File類對象的主要做用就是用來獲取文本自己的一些信息。如文本的所在的目錄,文件的長度,讀寫權限等等。(有的須要記憶,好比isFile(),isDirectory(),exits();有的瞭解便可。使用的時候查看API)
詳細以下:
File類(File類的概述和構造方法)
A:File類的概述
File更應該叫作一個路徑
文件路徑或者文件夾路徑
路徑分爲絕對路徑和相對路徑
絕對路徑是一個固定的路徑,從盤符開始
相對路徑相對於某個位置,在eclipse下是指當前項目下,在dos下
查看API指的是當前路徑
文件和目錄路徑名的抽象表示形式
B:構造方法
File(String pathname):根據一個路徑獲得File對象
File(String parent, String child):根據一個目錄和一個子文件/目錄獲得File對象
File(File parent, String child):根據一個父File對象和一個子文件/目錄獲得File對象數組
File類(File類的建立功能)
A:建立功能
public boolean createNewFile():建立文件 若是存在這樣的文件,就不建立了
public boolean mkdir():建立文件夾 若是存在這樣的文件夾,就不建立了
public boolean mkdirs():建立文件夾,若是父文件夾不存在,會幫你建立出來
(使用createNewFile()文件建立的時候不加.txt或者其餘後綴也是文件,不是文件夾;使用mkdir()建立文件夾的時候,若是起的名字是好比aaa.txt也是文件夾不是文件;)
注意事項:
若是你建立文件或者文件夾忘了寫盤符路徑,那麼,默認在項目路徑下。安全
File類(File類的重命名和刪除功能)
A:重命名和刪除功能
public boolean renameTo(File dest):把文件重命名爲指定的文件路徑
public boolean delete():刪除文件或者文件夾
B:重命名注意事項
若是路徑名相同,就是更名。
若是路徑名不一樣,就是更名並剪切。
C:刪除注意事項:
Java中的刪除不走回收站。
要刪除一個文件夾,請注意該文件夾內不能包含文件或者文件夾服務器
File類(File類的判斷功能)
A:判斷功能
public boolean isDirectory():判斷是不是目錄
public boolean isFile():判斷是不是文件
public boolean exists():判斷是否存在
public boolean canRead():判斷是否可讀
public boolean canWrite():判斷是否可寫
public boolean isHidden():判斷是否隱藏網絡
File類(File類的獲取功能)
A:獲取功能
public String getAbsolutePath():獲取絕對路徑
public String getPath():獲取路徑
public String getName():獲取名稱
public long length():獲取長度。字節數
public long lastModified():獲取最後一次的修改時間,毫秒值
public String[] list():獲取指定目錄下的全部文件或者文件夾的名稱數組
public File[] listFiles():獲取指定目錄下的全部文件或者文件夾的File數組
File類(文件名稱過濾器的概述及使用)
A:文件名稱過濾器的概述
public String[] list(FilenameFilter filter)
public File[] listFiles(FileFilter filter)多線程
package com.ningmeng; import java.io.File; public class Test { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub File file=new File("aa.txt");//文件默認就建立在你建立的項目下面,刷新便可看到 System.out.println(file.exists());//判斷文件是否存在 file.createNewFile();//建立文件,不是文件夾 System.out.println(file.exists());//再次判斷是否存在 System.out.println(file.getName());//獲取文件的名字 System.out.println(file.getAbsolutePath());//獲取文件的絕對路徑 System.out.println(file.getPath());//獲取文件的相對路徑 System.out.println(file.getParent());//獲取文件的父路徑 System.out.println(file.canRead());//文件是否可讀 System.out.println(file.canWrite());//文件是否可寫 System.out.println(file.length());//文件的長度 System.out.println(file.lastModified());//文件最後一次修改的時間 System.out.println(file.isDirectory());//判斷文件是不是一個目錄 System.out.println(file.isHidden());//文件是否隱藏 System.out.println(file.isFile());//判斷文件是否存在 } }
8.public String[] list():獲取指定目錄下的全部文件或者文件夾的名稱數組
public File[] listFiles():獲取指定目錄下的全部文件或者文件夾的File數組
list()獲取某個目錄下全部的文件或者文件夾:
package com.ningmeng; import java.io.File; public class FileTest { public static void main(String[] args){ File file=new File("D:/");//指定文件目錄 String[] str=file.list();//獲取指定目錄下的全部文件或者文件夾的名稱數組 for(String s : str){//增強for循環遍歷輸出 System.out.println(s); } } }
package com.ningmeng; import java.io.File; public class FileTest { public static void main(String[] args){ File file=new File("D:/");//指定文件路徑 File[] f=file.listFiles();//獲取指定目錄下的全部文件或者文件夾的File數組 for(File fi : f){//增強for循環遍歷輸出 System.out.println(fi); } } }
案例演示:
獲取某種格式的文件好比獲取某種後綴的圖片,並輸出文件名:
package com.ningmeng; import java.io.File; public class FileTest { public static void main(String[] args){ File file=new File("C:\\Users\\biehongli\\Pictures\\xuniji"); String[] str=file.list(); for(String s : str){ if(s.endsWith(".jpg") || s.endsWith(".png")){//若是後綴是這種格式的就輸出 System.out.println(s); } } } }
下面演示獲取文件夾下面子目錄裏面的文件獲取(並無徹底獲取子目錄的子目錄等等,僅僅獲取了子一級目錄):
package com.ningmeng; import java.io.File; public class FileTest { public static void main(String[] args){ File file=new File("C:\\Users\\biehongli\\Pictures\\Camera Roll"); File[] f=file.listFiles(); for(File fi : f){ if(fi.isDirectory()){//判斷若是是一個目錄 String[] s=fi.list(); for(String str : s){ if(str.endsWith(".jpg")){ System.out.println(str); } } } } } }
文件名稱過濾器的概述
public String[] list(FilenameFilter filter)
public File[] listFiles(FileFilter filter)
package com.ningmeng; import java.io.File; import java.io.FilenameFilter; public class FileTest { public static void main(String[] args){ File file=new File("C:\\Users\\biehongli\\Pictures\\Camera Roll"); String[] str=file.list(new FilenameFilter() {//過濾器,匿名內部類 @Override public boolean accept(File dir, String name) { // TODO Auto-generated method stub //System.out.println(dir);//獲取文件的路徑 //System.out.println(name);//獲取文件的名字 File f=new File(dir,name); return f.isFile() && f.getName().endsWith(".jpg"); } }); for(String s : str){ System.out.println(s); } } }
9.下面以一些字節輸入輸出流具體的案例操做(操做的時候認清本身使用的是字節流仍是字符流):
注意:read()方法讀取的是一個字節,爲何返回是int,而不是byte
字節輸入流能夠操做任意類型的文件,好比圖片音頻等,這些文件底層都是以二進制形式的存儲的,若是每次讀取都返回byte,有可能在讀到中間的時候遇到111111111;那麼這11111111是byte類型的-1,咱們的程序是遇到-1就會中止不讀了,後面的數據就讀不到了,因此在讀取的時候用int類型接收,若是11111111會在其前面補上;24個0湊足4個字節,那麼byte類型的-1就變成int類型的255了這樣能夠保證整個數據讀完,而結束標記的-1就是int類型
FileInputStream的單個字節讀取:
FileOutputStream的單個字節寫入:
package com.ningmeng; import java.io.FileInputStream; import java.io.FileOutputStream; public class FileTest { public static void main(String[] args) throws Exception{ FileInputStream fis=new FileInputStream("aaa.txt"); FileOutputStream fos=new FileOutputStream("bbb.txt",true); //FileOutputStream()後面加true指文件後面可追加 int a=fis.read();//read()一次讀取一個字節 System.out.println(a);//讀取的一個字節輸出 fos.write(101);//write()一次寫一個字節 fis.close();//必定記得關閉流,養成好習慣 fos.close(); } }
FileInputStream和FileOutputStream進行拷貝文本或者圖片或者歌曲:
package com.ningmeng; import java.io.FileInputStream; import java.io.FileOutputStream; public class FileTest { public static void main(String[] args) throws Exception{ FileInputStream fis=new FileInputStream("aaa.txt"); FileOutputStream fos=new FileOutputStream("bbb.txt"); //若是沒有bbb.txt,會建立出一個 int b; while((b=fis.read())!=-1){ fos.write(b); } fis.close(); fos.close(); } }
FileInputStream和FileOutputStream定義小數組進行讀寫操做:
package com.ningmeng; import java.io.FileInputStream; import java.io.FileOutputStream; public class FileTest { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("aaa.txt"); FileOutputStream fos = new FileOutputStream("bbb.txt"); int len; byte[] arr = new byte[1024 * 8];//自定義字節數組 while((len = fis.read(arr)) != -1) { //fos.write(arr); fos.write(arr, 0, len);//寫出字節數組寫出有效個字節個數 } //IO流(定義小數組) //write(byte[] b) //write(byte[] b, int off, int len)寫出有效的字節個數 fis.close(); fos.close(); } }
IO流(BufferedInputStream和BufferOutputStream拷貝)
package com.ningmeng; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; public class FileTest { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("aaa.txt"); FileOutputStream fos = new FileOutputStream("bbb.txt"); BufferedInputStream bis=new BufferedInputStream(fis); //使用裝飾模式,把fis裝飾進去bis中。使用緩衝讀取速度變快 BufferedOutputStream bos=new BufferedOutputStream(fos); int b; while((b=bis.read())!=-1){ bos.write(b); } bis.close(); bos.close(); } }
關於Java中線程的生命週期,首先看一下下面這張較爲經典的圖:
Java線程具備五中基本狀態
新建狀態(New):當線程對象對建立後,即進入了新建狀態,如:Thread t = new MyThread();
就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經作好了準備,隨時等待CPU調度執行,並非說執行了t.start()此線程當即就會執行;
運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就 緒狀態是進入到運行狀態的惟一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;
阻塞狀態(Blocked):處於運行狀態中的線程因爲某種緣由,暫時放棄對CPU的使用權,中止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的緣由不一樣,阻塞狀態又能夠分爲三種:
1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(由於鎖被其它線程所佔用),它會進入同步阻塞狀態;
3.其餘阻塞 -- 經過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
Java中線程的建立常見有如三種基本形式
1.繼承Thread類,重寫該類的run()方法
public class ThreadTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Thread myThread1 = new MyThread(); // 建立一個新的線程 myThread1 此線程進入新建狀態 Thread myThread2 = new MyThread(); // 建立一個新的線程 myThread2 此線程進入新建狀態 myThread1.start(); // 調用start()方法使得線程進入就緒狀態 myThread2.start(); // 調用start()方法使得線程進入就緒狀態 } } } }
如上所示,繼承Thread類,經過重寫run()方法定義了一個新的線程類MyThread,其中run()方法的方法體表明瞭線程須要完成的任務,稱之爲線程執行體。當建立此線程類對象時一個新的線程得以建立,並進入到線程新建狀態。經過調用線程對象引用的start()方法,使得該線程進入到就緒狀態,此時此線程並不必定會立刻得以執行,這取決於CPU調度時機。
public class ThreadTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Runnable myRunnable = new MyRunnable(); // 建立一個Runnable實現類的對象 Thread thread1 = new Thread(myRunnable); // 將myRunnable做爲Thread target建立新的線程 Thread thread2 = new Thread(myRunnable); thread1.start(); // 調用start()方法使得線程進入就緒狀態 thread2.start(); } } } }
經過 Callable 和 Future 建立線程
public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); for(int i = 0;i < 100;i++) { System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i); if(i==20) { new Thread(ft,"有返回值的線程").start(); } } try { System.out.println("子線程的返回值:"+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @Override public Integer call() throws Exception { int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } }
3.線程的優先級:每個 Java 線程都有一個優先級,這樣有助於操做系統肯定線程的調度順序。
Java 線程的優先級是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默認狀況下,每個線程都會分配一個優先級 NORM_PRIORITY(5)。
具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺。
4.多線程的使用
有效利用多線程的關鍵是理解程序是併發執行而不是串行執行的。例如:程序中有兩個子系統須要併發執行,這時候就須要利用多線程編程。
經過對多線程的使用,能夠編寫出很是高效的程序。不過請注意,若是你建立太多的線程,程序執行的效率其實是下降了,而不是提高了。
請記住,上下文的切換開銷也很重要,若是你建立了太多的線程,CPU 花費在上下文的切換的時間將多於執行程序的時間!
1.併發容器
工具包提供了隊列的併發實現類ConcurrentLinkedQueue和ConcurrentLinkedDeque,二者都是無界非阻塞線程安全的隊列。
ConcurrentMap接口繼承了普通的Map接口,提供了線程安全和原子操做特性。Java 8 提供了實現類ConcurrentHashMap,ConcurrentHashMap不鎖定整個Map,只鎖定須要寫入的部分,所以併發性能比HashTable要高不少。
ConcurrentNavigableMap接口繼承了ConcurrentMap和NavigableMap接口,支持併發訪問NavigableMap,還能讓子Map具有併發訪問的能力。NavigableMap是擴展的 SortedMap,具備了針對給定搜索目標返回最接近匹配項的導航方法。
Java 8 提供了實現類ConcurrentSkipListMap,並無使用lock來保證線程的併發訪問和修改,而是使用了非阻塞算法來保證併發訪問,高併發時相對於TreeMap有明顯的優點。
工具包提供了NavigableSet的併發實現類ConcurrentSkipListSet,是線程安全的有序集合,適用於高併發的場景,經過ConcurrentSkipListMap實現。
工具包提供了兩個寫時複製容器,即CopyOnWriteArrayList和CopyOnWriteArraySet。寫時複製技術是一種優化策略,多個線程能夠併發訪問同一份數據,當有線程要修改時才進行復制而後修改。在Linux系統中,fork進程後,子進程先與父進程共享數據,須要修改時才用寫時複製獲得本身的副本。在Java中,寫時複製容器在修改數據後,把原來容器的引用指向新容器,來實現讀寫分離,在併發讀寫中不須要加鎖。寫時複製容器適用於讀多寫少的場景,在複製時會佔用較多內存,可以保證最終一致性,但沒法保證瞬時一致性。
2.線程池
工具包中Executor接口定義了執行器的基本功能,即execute方法,接收Runnable對象參數並執行Runnable中的操做。
ExecutorService接口繼承Executor接口後增長了關於執行器服務的定義,如關閉、當即關閉、檢查關閉、等待終止、提交有返回值的任務、批量提交任務等。經過Executors的工廠方法獲取ExecutorService的具體實現,目前Executors能夠返回的實現類型以下:
FixedThreadPool:固定大小的線程池,建立時指定大小;
WorkStealingPool:擁有多個任務隊列(以便減小鏈接數)的線程池;
SingleThreadExecutor:單線程執行器,顧名思義只有一個線程執行任務;
CachedThreadPool:根據須要建立線程,能夠重複利用已存在的線程來執行任務;
SingleThreadScheduledExecutor:根據時間計劃延遲建立單個工做線程或者週期性建立的單線程執行器;
ScheduledThreadPool:可以延後執行任務,或者按照固定的週期執行任務。
若是但願在任務執行完成後獲得任務的返回值,能夠調用submit方法傳入Callable任務,並經過返回的Future對象查看任務執行是否完成,並獲取返回值。
3.線程分叉與合併
ForkJoinPool 讓咱們能夠很方便地把任務分裂成幾個更小的任務,這些分裂出來的任務也將會提交給 ForkJoinPool。任務能夠繼續分割成更小的子任務,只要它還能分割。分叉和合並原理包含兩個遞歸進行的步驟。兩個步驟分別是分叉步驟和合並步驟。
一個使用了分叉和合並原理的任務能夠將本身分叉(分割)爲更小的子任務,這些子任務能夠被併發執行。以下圖所示:
經過把本身分割成多個子任務,每一個子任務能夠由不一樣的 CPU 並行執行,或者被同一個 CPU 上的不一樣線程執行。
只有當給的任務過大,把它分割成幾個子任務纔有意義。把任務分割成子任務有必定開銷,所以對於小型任務,這個分割的消耗可能比每一個子任務併發執行的消耗還要大。
何時把一個任務分割成子任務是有意義的,這個界限也稱做一個閥值。這要看每一個任務對有意義閥值的決定。很大程度上取決於它要作的工做的種類。
當一個任務將本身分割成若干子任務以後,該任務將等待全部子任務結束。一旦子任務執行結束,該任務能夠把全部結果合併到同一個結果。圖示以下:
4.鎖
使用鎖實現的同步機制很像synchronized塊,可是比synchronized塊更靈活。鎖和synchronized的主要區別在於:
Synchronized塊不能保證等待進入塊的線程的訪問順序;
Synchronized塊沒法接收參數,不能在有超時時間限制的狀況下嘗試訪問;
Synchronized塊必須包含在單個方法中,而鎖的lock和unlock操做能夠在單獨的方法中。
工具包提供瞭如下幾種類型的鎖:
ReadWriteLock:讀寫鎖接口,容許多個線程讀取某個資源,可是一次只能有一個線程進行寫操做。內部有讀鎖、寫鎖兩個接口,分別保護讀操做和寫操做。實現類爲ReentrantReadWriteLock。
ReentrantLock:可重入鎖,具備與使用 synchronized 方法和語句所訪問的隱式監視器鎖定相同的一些基本行爲和語義,但功能更強大。ReentrantLock 將由最近成功得到鎖定,而且尚未釋放該鎖定的線程所擁有。當鎖定沒有被另外一個線程所擁有時,調用 lock 的線程將成功獲取該鎖定並返回。若是當前線程已經擁有該鎖定,此方法將當即返回。內部有一個計數器,擁有鎖的線程每鎖定一次,計數器加1,每釋放一次計數器減1。
5.原子類型
工具包提供了一些能夠用原子方式進行讀寫的變量類型,支持無鎖線程安全的單變量編程。
本質上,這些類都擴展了volatile的概念,使用一個volatile類型的變量來存儲實際數據。
工具包提供了4種類型的原子變量類型:
AtomicBoolean:可原子操做的布爾對象;
AtomicInteger:可原子操做的整形對象;
AtomicLong:可原子操做的長整形對象;
AtomicReference:可原子操做的對象引用。
網絡基礎知識
一、OSI分層模型和TCP/IP分層模型的對應關係
這裏對於7層模型不展開來說,只選擇跟此次系列主題相關的知識點介紹。
二、七層模型與協議的對應關係
網絡層 ------------ IP(網絡之間的互聯協議)
傳輸層 ------------ TCP(傳輸控制協議)、UDP(用戶數據報協議) 應用層 ------------ Telnet(Internet遠程登陸服務的標準協議和主要方式)、FTP(文本傳輸協議)、HTTP(超文本傳送協議)
三、IP地址和端口號
一、ip地址用於惟一標示網絡中的一個通訊實體,這個通訊實體能夠是一臺主機,能夠是一臺打印機,或者是路由器的某一個端口。而在基於IP協議網絡中傳輸的數據包,必須使用IP 地址來進行標示。ip地址就像寫一封信,必須指定收件人的地址同樣。每一個被傳輸的數據包中都包括了一個源IP和目標IP。
二、ip地址惟一標示了通訊實體,可是一個通訊實體能夠有多個通訊程序同時提供網絡服務。這個時候就要經過端口來區分開具體的通訊程序。一個通訊實體上不能有兩個通訊程序 使用同一個端口號。
IP地址和端口號,就像一個出差去外地入住酒店同樣,IP地址表示了酒店在具體位置,而端口號則表示了這我的在酒店的房間號。
四、TCP和UDP
一、TCP是一種面向鏈接的保證可靠傳輸的協議。經過TCP協議傳輸,獲得的是一個順序的無差錯的數據流。它可以提供兩臺計算機之間的可靠的數據流,HTTP、FTP、Telnet等應 用都須要這種可靠的通訊通道。
二、UDP是一種無鏈接的協議,每一個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳送目的地,至於可以達到目的地,達到目的地的時 間以及內容的正確性都是不能保證的。
既然有了保證可靠傳輸的TCP協議,爲何還要非可靠傳輸的UDP協議呢?緣由有兩個:
一、可靠的傳輸是要付出代價的,對數據內容的正確性的檢驗必然會佔用計算機處理時間和網絡帶寬。所以TCP的傳輸效率不如UDP高。
二、許多應用中並不須要保證嚴格的傳輸可靠性,好比視頻會議系統,並不要求視頻音頻數據絕對正確,只要可以連貫就能夠了。因此在這些場景下,使用UDP更合適些。
五、URL訪問網上資源
一、URL對象表明統一資源定位器,是指向互聯網「資源」的指針。它是用協議名、主機、端口和資源組成,即知足以下格式:
protocol://host:port/resourceName http://www.crazyit.org/index.php
二、經過URL對象的一些方法能夠訪問該URL對應的資源:
String getFile():獲取該URL的資源名 String getHost():獲取主機名 String getPath():獲取路徑部分 int getPort():獲取端口號
6.Java的網絡編程主要涉及到的內容是Socket編程,那麼什麼是Socket呢?簡單地說,Socket,套接字,就是兩臺主機之間邏輯鏈接的端點。TPC/IP協議是傳輸層協議,主要解決數據如何在網絡中傳輸,而HTTP是應用層協議,主要解決如何包裝數據。Socket,本質上就是一組接口,是對TCP/IP協議的封裝和應用(程序員層面上)。
Socket編程主要涉及到客戶端和服務器端兩個方面,首先是在服務器端建立一個服務器套接字(ServerSocket),並把它附加到一個端口上,服務器從這個端口監聽鏈接。端口號的範圍是0到65536,可是0到1024是爲特權服務保留的端口號,咱們能夠選擇任意一個當前沒有被其餘進程使用的端口。
客戶端請求與服務器進行鏈接的時候,根據服務器的域名或者IP地址,加上端口號,打開一個套接字。當服務器接受鏈接後,服務器和客戶端之間的通訊就像輸入輸出流同樣進行操做。
下面是一個客戶端和服務器端進行數據交互的簡單例子,客戶端輸入正方形的邊長,服務器端接收到後計算面積並返回給客戶端,經過這個例子能夠初步對Socket編程有個把握.
服務器端:
public class SocketServer { public static void main(String[] args) throws IOException { // 端口號 int port = 7000; // 在端口上建立一個服務器套接字 ServerSocket serverSocket = new ServerSocket(port); // 監聽來自客戶端的鏈接 Socket socket = serverSocket.accept(); DataInputStream dis = new DataInputStream( new BufferedInputStream(socket.getInputStream())); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(socket.getOutputStream())); do { double length = dis.readDouble(); System.out.println("服務器端收到的邊長數據爲:" + length); double result = length * length; dos.writeDouble(result); dos.flush(); } while (dis.readInt() != 0); socket.close(); serverSocket.close(); } }
**客戶端**: public class SocketClient { public static void main(String[] args) throws UnknownHostException, IOException { int port = 7000; String host = "localhost"; // 建立一個套接字並將其鏈接到指定端口號 Socket socket = new Socket(host, port); DataInputStream dis = new DataInputStream( new BufferedInputStream(socket.getInputStream())); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(socket.getOutputStream())); Scanner sc = new Scanner(System.in); boolean flag = false; while (!flag) { System.out.println("請輸入正方形的邊長:"); double length = sc.nextDouble(); dos.writeDouble(length); dos.flush(); double area = dis.readDouble(); System.out.println("服務器返回的計算面積爲:" + area); while (true) { System.out.println("繼續計算?(Y/N)"); String str = sc.next(); if (str.equalsIgnoreCase("N")) { dos.writeInt(0); dos.flush(); flag = true; break; } else if (str.equalsIgnoreCase("Y")) { dos.writeInt(1); dos.flush(); break; } } } socket.close(); } }