程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

董雅洁發表於2024-11-25

程式碼隨想錄

滑動視窗

1、如果給兩個字串s和t,判斷t是否為s的子串或是否s包含t的排列,用t的長度固定滑動視窗的大小,初始化將s的前t.length()個長度的字元情況儲存在int陣列中,int陣列的大小由字串中字元的型別決定,最大為ascii表的長度,為128。

  每次迴圈滑動視窗向前移一位,即left++,right++,移完之後和儲存t字串情況的陣列進行比較,Arrays.equal(int[] a,int[] b)。

例:567. 字串的排列 - 力扣(LeetCode )

438. 找到字串中所有字母異位詞 - 力扣(LeetCode)  

  

2、對於找不重複的最長子串,可以用int[]陣列是否大於1來判斷,大於1時則right指向的字元子串中已經包含,此時先將left指向的字元移出,然後再left++(而不是直接移到right的位置×!!)。

例:3. 無重複字元的最長子串 - 力扣(LeetCode)

具體程式碼
  public int lengthOfLongestSubstring(String s) {
        int left=0;
        int right=0;
        int count=0;
        int[] curr=new int[128];
        while (right<s.length()){
            if (curr[s.charAt(right)]<1){
                curr[s.charAt(right)]++;
                right++;
            } else if (curr[s.charAt(right)]>=1) {
                curr[s.charAt(left)]--;
                left++;


            }

            count= count>right-left?count:right-left;
        }
        return count;
    }

螺旋矩陣

當輸入的矩陣並不一定行數列數相等時,要考慮到四種不同的情況,分別是:

    • 行數為1行或者列數為1列   直接從左往右 或者從上往下遍歷
    • 行數(列數)為2的倍數且行數≤列數(列數≤行數) 遍歷完整個迴圈之後 就可以結束 不需要後續操作
    • 行數=列數,且為奇數時 只需要在迴圈結束後再遍歷一下當前最中心的值
    • 行數(列數)為奇數且行數<列數(列數<行數) 在迴圈結束之後再繼續遍歷(列數-行數+1)箇中心列元素或者是再繼續遍歷(行數-列數+1)箇中心行元素
螺旋矩陣程式碼
  public List<Integer> spiralOrder(int[][] matrix) {

        int row=matrix.length;//行數
        int column=matrix[0].length;//列數
        ArrayList<Integer> list = new ArrayList<>();

        int x=0;//矩陣的行位置
        int y=0;//矩陣列位置

        int loop=1;//迴圈的次數
        int offset=1;//遍歷的長度 每次迴圈一遍右邊界減一
        int i=0,j=0;

        //針對一行或者一列的情況
        if (column==1 ){
            while (x<row)
            {
                list.add(matrix[x++][0]);
            }
        }else if (row==1){
            while (y<column){
                list.add(matrix[0][y++]);
            }

        }

        //迴圈主體
        while (loop<=Math.min(row/2,column/2)){

            for (j=y;j<column-offset;j++){
                list.add(matrix[x][j]);
            }

            for (i = x;i < row-offset; i++) {
                list.add(matrix[i][j]);
            }

            for (;j>y;j--){//注意:j是大於y而不是大於0
                list.add(matrix[i][j]);
            }
            for (;i>x;i--){//注意:i是大於x而不是大於0
                list.add(matrix[i][j]);
            }

            loop++;//迴圈次數++
            x++;//行位置往裡移動一位
            y++;//列位置往下移動一位
            offset++;//行和列中不需要遍歷的個數+1

        }

        //如果存在列的個數是2的倍數且行數>=列數時,迴圈完正好結束,沒有裡層剩餘,列和行反過來一樣
        if ((column%2==0 && column<row) ||(row%2==0 && column>row)){
            return list;
        }

        //當裡層有剩餘時
        i=x;
        j=y;

        if ( i<column && j<row){ //因為上面while大迴圈中最後x++,y++ 所以需要進行一下判斷
            if (row==column && row%2==1){//如果行數和列數相等且為奇數,那麼只剩最中間一個沒有遍歷
                list.add(matrix[i][j]);
            } else if (row>column) {//如果行數>列數 且列數是奇數 那麼中間有有一小列元素沒有遍歷
                while (i-j <=row-column){//沒有遍歷的個數是行數-列數+1
                    list.add(matrix[i++][j]);
                }
            }else if (row<column){//行數列數反過來 同上
                while (j-i <=column-row){
                    list.add(matrix[i][j++]);
                }
            }
        }

        return list;
    }

區間和

直接每次輸入區間後,都需要遍歷一次區間中的資料,因此時間消耗為0(m*n) m表示輸入區間的次數,n為陣列的元素個數

解決方法:使用字首和,將前i個元素之和儲存在一個陣列中,每次輸入區間之後,直接用末位置的和➖(初位置-1)的和,當初位置為0時區間和就為末位置之和。

區間和程式碼
 import java.util.*;
public class Main{
    public static void main (String[] args) {
        Scanner scan= new Scanner(System.in);
        int arrLength=scan.nextInt();
        int[] Array=new int[arrLength];
        int[] p=new int[arrLength];
        
        int i=0;
        Array[i]=scan.nextInt();
        p[i]=Array[0];
         
        for(i=1;i<arrLength;i++){
            Array[i]=scan.nextInt();
             p[i]=p[i-1]+Array[i]; 
        }
        
        while(scan.hasNextInt()){
            int a=scan.nextInt();
            int b=scan.nextInt();
            if (a==0){
                System.out.println(p[b]);
            }else{
                System.out.println(p[b]-p[a-1]);
            }
            
        }
        scan.close();
    }
}

開發商購買土地

其核心在於,劃分要麼橫著一刀,要麼豎著一刀,且只能是一刀。

思路:先驗證橫著一刀得到兩個區域差的最小值:遍歷每一行,在每行最後一個位置加入後計算區域差,sum-count-count,其中(sum-count)表示此一刀下面的區域,count表示一刀上面的區域,用abs(下面的區域和➖上面區域和)則表示區域差;同理再試豎著的一刀。

字首和和最佳化暴力求解都是o(m*n)

暴力求解程式碼
 public class Main{
    public static void main (String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        int m=scanner.nextInt();
        int[][] vec=new int[n][m];
        int sum=0;
        int result=Integer.MAX_VALUE;
        
        for (int i=0;i<n ;i++ ){
            for (int j=0;j<m ;j++ ){
               vec[i][j]=scanner.nextInt(); 
               sum+=vec[i][j];
            } 
        } 
        
        int count=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                count+=vec[i][j];
                
                if (j==m-1){
                    result=Math.min(result,Math.abs(sum-count-count));
                } 
            }
        }
        
        count=0;
        for(int j=0;j<m;j++){
            for(int i=0;i<n;i++){
                count+=vec[i][j];
                
                if(i==n-1){
                    result=Math.min(result,Math.abs(sum-count-count));
                }
            }
        }
        System.out.println(result);
        scanner.close();
    }
}

陣列基礎總結

陣列的元素是不能刪除的,只能覆蓋!!

二維陣列在記憶體中並不是連續的,其實質是一維陣列的一維陣列,即物件中儲存著一個一維陣列的地址,這個一維陣列中儲存著每行陣列的起始地址。

Java基礎學習

資料結構

1、在底層真實存在的資料結構:陣列、連結串列

  抽象資料型別:樹、棧、佇列(使用陣列或者是連結串列來構建)

集合原始碼

ArrayList

1、ArrayList的特點

  • 實現了List介面,儲存有序的,可重複的一個一個的資料
  • 底層使用object[]陣列儲存
  • 執行緒不安全

2、ArrayList原始碼解析

jdk7版本:

//底層會初始化陣列,陣列的長度為10,object[] elementDate=new object[10];
ArrayList arr=new ArrayList<>();

arr.add("AA");//elementDate[0]="AA";
arr.add("BB");//elementDate[1]="BB";

//一旦size>length,length變為原來的1.5倍,並將原ArrayList複製到擴容的ArrayList

jdk8版本:

//底層不會初始化陣列,陣列的長度為0,object[] elementDate=new Object[]{};
ArrayList arr=new ArrayList<>();

arr.add("AA");//首次新增元素時,會初始化陣列elementData=new Object[10];elementData[0]="AA";
arr.add("BB");//elementDate[1]="BB";

//一旦size>length,length變為原來的1.5倍,並將原ArrayList複製到擴容的ArrayList

3、在選擇ArrayList時,有兩種初始化方法: new ArrayList();//底層建立長度為10的陣列

                    new ArrayList(int capacity); //底層建立指定capacity長度的陣列

Vector

1、Vector的特點

  • 實現了List介面,儲存有序的,可重複的資料
  • 底層使用Object[]陣列儲存
  • 執行緒安全

2、Vector原始碼解析

Vector v=new Vector();//底層初始化陣列,長度為10  Object[] elementData=new Object[10]
v.add("AA");//elementData[0]="AA";
v.add("BB");//elementData[1]="BB";

//當新增第11個元素時,需要擴容,預設擴容為原來的2倍

LinkedList

1、LinkedList的特點

  • 實現了List介面,儲存有序的,可重複的資料
  • 底層使用雙向連結串列儲存
  • 執行緒不安全

2、LinkedList原始碼解析

LinkedList<String> list=new LinkedList<>();//因為底層不是陣列結構,不需要初始化分配陣列空間
list.add("AA");//將AA封裝到一個Node物件1中,list物件的屬性first、last都指向此Node物件1
list.add("BB");//將BB封裝到一個Node物件2中,與物件1構成一個雙向連結串列,此時last指向Node2物件

//由於LinkedList使用的是雙向連結串列,不考慮擴容的問題
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

HashMap⭐

1、HashMap特點

  • 實現Map介面,儲存一對一對的資料,可以新增null的key值和value值
  • 底層使用陣列+單向連結串列+紅黑樹(jdk8版本之後),jdk7版本沒有使用紅黑樹
  • 執行緒不安全,效率高的

2、HashMap原始碼解析

  • JDK7
//建立物件的過程中,底層初始化陣列Entry[] table=new Entry[16]
HashMap<String,Integer> map=new HashMap<>();

map.put("AA",78);//將AA和78封裝到一個Entry物件中,考慮將此物件新增到table陣列中

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

情況1:將(key1,value1)存放到陣列的索引i的位置

情況2,情況3:使用單向連結串列的頭插法,將(key1,value1)放在陣列中,並指向(key2,value2)

滿足下列條件,會考慮擴容,一般擴容為table.legth的2倍

(size>=threshold) && (null !=table[i]) threshold=陣列的長度*載入因子(預設為0.75),

載入因子過大,增加元素的數目較大時才會擴容,節省擴容所需的時間,但後期查詢和刪除比較麻煩

載入因子過小,沒新增幾個元素就開始擴容,浪費空間

HashMap有無參構造器,也有有參構造器,其中無參構造器預設capacity為16,LOAD_FACTOR為0.75,也可以直接呼叫有參構造器,手動輸入上面兩個引數的值,其中就算手動輸入的capacity不是2的倍數,經過構造器之後,也會構建大小為2的倍數的capacity

HashMap允許新增key為null的值,將此(key,value)存放到table索引0的位置,如果索引為0的位置已經有元素了且遍歷該位置的連結串列不為null,則將null用頭插法放入HashMap陣列中,若索引為0的key也為null或連結串列中有key為null,則將新的value賦給舊的value值

詳細可看:JDK7中HashMap的原始碼  影片171集33:49

  • JDK8(以jdk1.8.0_271為例)

    ① 在jdk8中,當我們建立了HashMap例項以後,底層並沒有初始化table陣列。當首次新增(key,value)時,進行判斷,如果發現table尚未初始化,則對陣列進行初始化。
    ② 在jdk8中,HashMap底層定義了Node內部類,替換jdk7中的Entry內部類。意味著,我們建立的陣列是Node[]

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)
    ③ 在jdk8中,如果當前的(key,value)經過一系列判斷之後,可以新增到當前的陣列角標i中。如果此時角標i位置上有元素。在jdk7中是將新的(key,value)指向已有的舊的元素(頭插法),而在jdk8中是舊的元素指向新的 (key,value)元素(尾插法)。 "七上八下"
    ④ jdk7:陣列+單向連結串列 ;jk8:陣列+單向連結串列 + 紅黑樹


3、單連結串列和紅黑樹轉換時機

  • 使用單向連結串列變為紅黑樹:如果陣列索引i位置上的元素的個數達到8,並且陣列的長度達到64時,我們就將此索引i位置上的多個元素改為使用紅黑樹的結構進行儲存。(為什麼修改呢?因為紅黑樹進行put()/get()/remove()操作的時間複雜度為O(logn),比單向連結串列的時間複雜度O(n)的好,效能更高。)
  • 使用紅黑樹變為單向連結串列:當使用紅黑樹的索引i位置上的元素的個數低於6的時候,就會將紅黑樹結構退化為單向連結串列。

   


   屬性/欄位:
  static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 預設的初始容量 16
  static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量 1 << 30
  static final float DEFAULT_LOAD_FACTOR = 0.75f; //預設載入因子
  static final int TREEIFY_THRESHOLD = 8; //預設樹化閾值8,當連結串列的長度達到這個值後,要考慮樹化
  static final int UNTREEIFY_THRESHOLD = 6;//預設反樹化閾值6,當樹中結點的個數達到此閾值後,要考慮變為連結串列

  //當單個的連結串列的結點個數達到8,並且table的長度達到64,才會樹化。
  //當單個的連結串列的結點個數達到8,但是table的長度未達到64,會先擴容(比JDK7多的一種擴容情況)
  static final int MIN_TREEIFY_CAPACITY = 64; //最小樹化容量64

  transient Node<K,V>[] table; //陣列
  transient int size; //記錄有效對映關係的對數,也是Entry物件的個數
  int threshold; //閾值,當size達到閾值時,考慮擴容
  final float loadFactor; //載入因子,影響擴容的頻率

LinkedHashMap

1、LinkedHashMap與HashMap的關係

  LinkedHashMap是HashMap的子類

  LinkedHashMap在HashMap陣列+單向連結串列+紅黑樹的基礎上,又增加了一對雙向連結串列(Entry類繼承了HashMap的Node,並新增before和after作為雙向指標),記錄新增的(key,value)先後順序

2、LinkedHashMap的put()方法使用了HashMap的put方法,但重寫了put方法中的NewNode()方法;

左

HashSet和LinkedHashSet

HashSet和LinkedHashSet的底層分別是HashMap和LinkedHashMap,新增元素相當於新增key,其中key都指向同一個value,為new Object() 

當對HashSet中已經存在的元素進行修改,並使用remove()方法對最新資料進行移除時,並不會刪除掉該元素,因為hash值發生改變,現set中儲存的卻是原hash值。

新增時,不論是新的元素還是舊元素都可以新增成功。

面試⭐:

1、ArrayList相當於對陣列的常見操作的封裝,對外暴露增刪改查插的操作方法

2、HashMap初始值為16(檢視原始碼),臨界值是12(threshold ,使用陣列的長度*載入因子)

3、HashMap為什麼是2的次方?看底層程式碼,方便計算在陣列中存放角標的位置,需要和陣列的length-1進行與運算,length必須為2的n次方

4、HashMap計算索引值,在1.7使用Indexfor()方法,在1.8中進行&運算  

5、雜湊衝突:

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)解決方法:頭插法、尾插法、紅黑樹、比較equals

6、HashMap退化是因為紅黑樹佔用空間大,TreeNode要佔據兩倍的普通Node的空間

7、hashCode()與equals()生成演算法、方法怎麼重寫?進行equals()判斷使用的屬性,通常也都會參與到hashCode()的計算中

  IDEA自動生成hashCode()自動使用相關演算法

File類和IO流

1、流的基礎操作

      輸入流    輸出流

位元組流: InputStream  OutputStream

字元流:  Reader   Writer

2、字元和位元組的關係 字元流和位元組流是兩個單位

一個char是兩個byte 一個byte佔8bit=>一個字元佔兩個位元組 一個位元組8位元(二進位制數)

3、new FileReader(char cbuffer);該方法會返回每次讀到的字元個數 且讀到的字元會存在cbuffer中,迴圈輸出時,必須要小於返回的字元個數len,因為後面迴圈讀到的字元會覆蓋前面迴圈讀到的字元,一但後續長度不夠上一輪結果有部分會儲存下來。

4、try-catch執行完後還會繼續往下執行,使用finally是避免catch丟擲異常,後面的語句沒有執行。

  當try中由語句丟擲異常,該語句後面的部分將不會繼續執行。

節點流(FileReader FileWriter FileInputStream FileOutputStream)

1、讀寫字元流資料都有一定的步驟,且關閉資料流必須要放在try-catch-finally的finally中,確保即使執行過程中出錯,也會關閉資料流,而當資料輸入輸出資料流為空時,則不需要進行關閉。

一定要關閉流資源,為了避免記憶體洩漏(資料已經使用完,但gc並不會回收)

public void test4(){
  //1.建立File類的物件
    File srcFile=new File("hello.txt");
    File destFile=new File("hello_copy.txt");

    //2.建立輸入輸出流
    FileReader fr = null;
    FileWriter fw= null;
    try {
        fr = new FileReader(srcFile);
        fw = new FileWriter(destFile);
        //3.資料的讀入和寫出的過程

        char[] cbuffer=new char[5];
        int len;
        while ((len=fr.read(cbuffer))!=-1){
            fw.write(cbuffer,0,len);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        //4.關閉流資源
        try {
            if (fw!=null)
                fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (fr!=null)
                fr.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

2、除了文字檔案以外,其它型別的檔案使用位元組流,和字元流的基礎操作一樣,除了方法裡面的引數變成byte型別,如:read(byte[] buffer)write(byte[] buffer,0,len)

3、IDEA中預設UTF-8儲存,漢字預設為3個位元組,其它為1個位元組,當使用位元組流讀取複製到另一個檔案時,複製成功,而如果是讀出來是到控制檯,如果正好中間斷開中文,則會報亂碼。

4、對於字元流,只能用來處理文字檔案,不能用來處理非文字檔案。

  對於位元組流,通常是用來處理非文字檔案,但是如果涉及到文字檔案的複製可以使用位元組流

 文字檔案:.txt .java .c .py等各種程式語言檔案

 非文字檔案:.doc等支援圖片的

處理流

緩衝流

1、使用緩衝流 BufferInputStream BufferOutPutStream 提高檔案讀寫的效率 預設緩衝區大小8*1024 即8kb

緩衝流的操作是先統一寫到記憶體緩衝區裡,在關閉資源時再統一寫入到檔案,以此提高效率,所以檔案的最後如果沒有關閉,則會缺失資料,此時可以用.flush()方法做到每用緩衝流寫一次都會重新整理資料

節點流不關閉也不會缺失資料,因為每呼叫一次wirite()方法都寫入到檔案.

轉換流

1、位元組流-》字元流 解碼 InputStreamReader    字元流-》位元組流 編碼 OutputStreamWrite

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

2、解碼時使用的字符集必須與當初編碼使用的字符集相容;如果檔案編碼使用的GBK,解碼是UTF-8,但檔案中只有abc等英文字元,此情況不會出現亂碼,因為GBK和UTF-都向下相容了ASCII。ASCII包括英文字母,數字和一些符號總共是128個,佔7位,用一個位元組表示

FileInputStream fis=new FileInputStream(file1);
FileOutputStream fos=new FileOutputStream(file2);

//對應的解碼,必須與原字符集相同
InputStreamReader isr=new InputStreamReader(fis,"gbk");
OutputStreamWriter osw=new OutputStreamWriter(fos,"utf8");
byte[] buffer=new byte[5];
int len;
while ((len=bis.read(buffer))!=-1){
       bos.write(buffer,0,len);
}

3、在儲存的檔案中的字元

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

資料流和物件流

1、物件的序列化機制

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

序列化過程:寫出到磁碟或透過網路傳輸出去的過程,使用ObjectOutputStream流實現。

反序列化過程:將檔案中的資料或網路傳輸過來的資料還原為記憶體中的Java物件,使用ObjectInputStream流實現。

2、流程

  • 建立File物件
  • File物件作為引數,建立節點流
  • 節點流物件作為引數,建立物件流,呼叫物件流的readObject()和writeObject()方法完成對物件流的輸入和輸出
  • 關閉最外層物件流
//資料流寫到磁碟
@Test
public void test1() throws IOException {
  File file=new File("object.dat");

  ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(file));
  Person p=new Person("enheng",12);

   oos.writeObject(p);
   oos.flush();

   oos.close();
}

//從磁碟寫到記憶體
@Test
public void test2() throws IOException, ClassNotFoundException {
  File file=new File("object.dat");

  ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
  Person p=(Person) ois.readObject();

    System.out.println(p);
    ois.close();
}

3、自定義類要實現序列化機制,要滿足

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

類中的屬性如果宣告為transient或static,則不會儲存在磁碟檔案上,輸出結果為預設值null或0

程序與程序之間通訊,客戶端與客戶端之間進行通訊,都需要物件是可序列化的。

網路程式設計

InetAddress

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

InetAddress類沒有明顯的建構函式,上面兩個方法為工廠方法,即一個類中的靜態方法返回該類的一個例項。

InetAddress inet1= InetAddress.getByName("192.168.23.21");
System.out.println(inet1);
InetAddress inet2=InetAddress.getByName("www.atguigu.com");
System.out.println(inet2);

TCP和UDP

1、TCP的三次握手

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

2、四次揮手

程式碼隨想錄之滑動視窗、螺旋矩陣、區間和、開發商土地;Java之資料結構、集合原始碼、File類和Io流以及網路程式設計(11.22)

3、Socket是Ip地址+埠號

檔案透過TCP從客戶端傳到伺服器
 package com.atguigu02.tcpudp;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * className:TCPTEst
 * Description:
 *
 * @Author 董雅潔
 * @Create 2024/11/22 15:07
 * @Version 1.0
 */
public class TCPTest {
    @Test
    //客戶端 傳送檔案給伺服器端
    public void client() throws IOException {
        Socket socket = null;
        FileInputStream fis= null;//從磁碟讀到記憶體 用InputStram
        OutputStream os = null;
        try {
            //1.建立Socket
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
            int port=9090;
            socket = new Socket(inetAddress, port);

            //2.建立File例項、FileInputStream的例項
            File file=new File("1.png");
            fis = new FileInputStream(file);

            //3.透過Socket,獲取輸出流
            os = socket.getOutputStream();

            //4.讀寫資料
            byte[] buffer=new byte[1024];
            int len;
            while ((len=fis.read(buffer))!=-1){
                os.write(buffer,0,len);
            }
            System.out.println("資料傳送完畢");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (os!=null){
                os.close();
            }
            if (fis!=null){
                fis.close();
            }
            if (socket!=null){
                socket.close();
            }


        }

    }

    //伺服器端 接受客戶端發來的檔案
    @Test
    public void server() throws IOException {
        //1.建立ServerSocket
        int port=9090;
        ServerSocket serverSocket = new ServerSocket(port);

        //2.呼叫accept() 接收客戶端的Socket
        Socket socket = serverSocket.accept();
        System.out.println("伺服器已開啟");

        //3.透過Socket獲取一個輸入流
        InputStream is = socket.getInputStream();

        //4.建立File類的例項 FileOutputStream的例項
        File file=new File("1_copy.png");
        FileOutputStream fos=new FileOutputStream(file);

        byte[] buffer=new byte[1024];//當內容為中文時,使用一般方式可能會亂碼
        //ByteArrayOutputStream bos=new ByteArrayOutputStream();
        int len;
        while ((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }

       fos.close();
        is.close();
        socket.close();
        serverSocket.close();

    }
}

相關文章