Java知識點——第六週總結

第六週總結

TCP多人聊天室實現

分析

  • 客戶端
    功能:
    1. 數據發送
    2. 數據接收
    技術:
    1. socket
    2. 輸入流和輸出流
    3. 多線程,客戶端功能模塊有兩個線程
    聊天:
    1. 羣聊
    2. 私聊
    私聊前綴 @服務器用戶ID號:msg
  • 服務器
    功能:
    1. 數據轉發
    2. 用戶註冊
    技術:
    1. ServerSocket
    2. 每一個用戶對應的Sokcet對象
    3. 多線程同時在線
    4. HashMap<Integer, 用戶>
    數據轉發:
    私聊前綴判斷
    羣聊所有人發送

客戶端實現

數據發送:
使用輸出流發送數據給服務器
遵從Runnable接口
數據接收:
使用輸入流從服務器端接收數據
遵從Runnable接口

客戶端主方法:
用戶名提交
數據發送
數據接收
多線程啓動

資源關閉問題

-代碼中操作了大量的輸入流和輸出流,這裏都需要進行關閉操作。
DataInputStream, DataOutputStream, BufferedReader, Socket

以上這些資源都是Closeable接口的實現類,都有對應的Close方法
封裝一個工具類:
提供一個closeAll方法,參數爲符合Closeable接口的實現類對象。
這裏可以考慮可變長參數
Closeable… closeable

可變長參數在方法中使用的過程裏面是對應一個數組,這裏完成可以使用增強for來使用

工具類名:
CloseUtil
public static void closeAll(Closeable… closeable)

功能拓展

  1. 用戶退出
    用戶輸入指定字段之後可以退出
    客戶端Socket服務
    服務端Socket服務
    涉及資源關閉,線程關閉

  2. 用戶異常退出
    在運行過程中發現問題,需要及時處理,關閉對應的資源,終止對應的線程

  3. 服務器保存所有的聊天記錄

JSON

JSON格式概述

  • JSON
    JavaScript
    JavaScript Object Notation
    (JavaScript Object Notation,JavaScript對象表示法,讀作/ˈdʒeɪsən/)是一種由道格拉斯·克羅克福特構想和設計、輕量級的數據交換語言,該語言以易於讓人閱讀的文字爲基礎,用來傳輸由屬性值或者序列性的值組成的數據對象。儘管JSON是JavaScript的一個子集,但JSON是獨立於語言的文本格式,並且採用了類似於C語言家族的一些習慣

數據格式

JSON對象

{
“ID”:001,
“name”:“騷磊”,
“age”:16
}

特徵:
1. 數據形式鍵值對形式
“鍵”:值
2. 數據支持 字符串,數字,true false
3. {} 大括號以內的數據

  • JSON對象數組

    • [
      {
      “ID”:1,
      “name”:“騷磊”,
      “age”:16
      },
      {
      “ID”:2,
      “name”:“騷傑”,
      “age”:66
      },
      {
      “ID”:3,
      “name”:“康康”,
      “age”:15
      }
      ]

特徵:
1. 數據使用[]包含
2. 在[]都是JSON格式對象
3. 每一個對象之間使用逗號隔開,同時最後一個元素不需要逗號

  • JSON數據驗證

    • JSON格式驗證

解析JSON格式工具

  • 常用的工具:
    Gson,fastjson, Jackson
    以上都是第三方工具,需要導入對應的jar包按使用XML導包

  • FastJson內容

    • JSON核心類
      JSON核心類提供解析和轉化方法,用於解析JSON數據格式,同時用於轉換類對象到JSON格式,該類對象需要符合JavaBean規範
      –| JSONArray
      存在按照鍵值對方式解析獲取數據,同時存在一定的List方法
      –| JSONObject
      獲取對應的類對象,指定鍵值對對應數據的方法
  • 解析演示

註解

註解概述

  • 註解解釋

    • 註釋:
      解釋代碼,給程序員看

註解:
Java語言中的類、方法、變量、參數和包等都可以被標註。和Javadoc不同,Java標註可以通過反射獲取標註內容。在編譯器生成類文件時,標註可以被嵌入到字節碼中。Java虛擬機可以保留標註內容,在運行時可以獲取到標註內容。 當然它也支持自定義Java標註
JDK1.5之後的特徵
用於說明程序
一般在框架中使用
格式:
@AnnotationName

文檔註釋:
@param @return @Exeception 從根本上是一個註釋,不存在代碼編譯,不會生成對應的.class字節碼問題,只是提供給JavaDoc API文件生成工具。作爲標記生成對應的文檔。

註解是有一部分參與編譯
@Override並不是沒編譯就有效果了,是因爲不管是Eclipse還是IDEA都可以預編譯Java代碼生成對應的.class文件的

  • 註解作用

    • 生成文檔:
      代碼中生成對應的JavaDoc API文檔
      @param @return

    【IDEA JavaDoc工具使用參數】
    Other Command Line Arguments : -encoding utf-8 -charset utf-8
    解決中文亂碼,因爲IDEA默認編碼集爲UTF-8 Windows GKB

代碼檢查:
繼承重寫,或者說接口遵從之後的實現中,存在@Override

代碼數據獲取: [小框架]
通過反射獲取指定註解中的一些內容,例如 配置,數據,操作,驗證。。。

  • Java中預定義的一些註解

    • @Override:
      重寫/實現方法的情況下,檢查方法聲明是否和父類或者接口中的方法聲明一致。強制格式檢查。

@Deprecated
標註當前方法已過時,例如 Data日期類內的一些方法

@SuppressWarnings(“all”)
壓制警告,可以用於一些代碼中存在明確無異常的情況下,壓制一些警告

Java中自定義註解

  • Java中自定義註解的方式

    • 格式:
      public @interface AnnotationName {
      屬性列表;
      }

Annotation註解是可以編譯得到對應的.class字節碼文件,驗證了註解是可以參與編譯過程的

通過反編譯工具可以得到一下內容
【Annotation本質】
public interface MyAnnotation1 extends java.lang.annotation.Annotation {
}

MyAnnotation1
本質是一個interface,同時java.lang.annotation.Annotation 子接口

  • Annotation註解屬性【難點】

    • 屬性:
      開發書寫代碼使用註解的方式中,數據使用方式更加偏向於屬性概念。
      使用
      1. 在書寫代碼中使用
      @MyAnnotation(id=1, name=“騷磊”, age=16)
      2. 利用反射時,會涉及到getXXX方法
      通過屬性名獲取對應值的概念來完成的

    【但是實際上是利用abstract方法來完成屬性概念的】

屬性使用的格式[實際按照方法格式操作]
1. 屬性的值數據類型和對應具體數據 ==> 返回值類型和返回的數據
屬性類型支持:
a. 基本數據類型
b. String類型
c. 其他的註解類型
d. 枚舉類型
枚舉就是一個帶有名字的常量,爲了更好的域閱讀性和操作
e. 以上類型對相應的數組

	屬性值要求
		a. 定義屬性時可以使用default關鍵字,加上默認值,該屬性在使用的過程中是
		沒有強制要求屬性值,如果沒有賦予屬性值,採用對應的默認值操作,如果賦
		值,使用對應值
		
		b. 如果註解中有且只有一個value屬性,或者說註解中除value屬性之外,都有
		默認值,不管是類,方法,成員變量,包使用當前註解是可以直接在括號內加入
		對應數據類型數值、
		
		c. 如果屬性是數組類型, {}大括號保存,並且不同的內容,使用,隔開
2. 屬性的鍵名字 ==> 方法的名字
  • 元註解

    • 給予註解的解釋,用於約束註解的一些操作問題
      @Retention -
      標識這個註解怎麼保存,是隻在代碼中,還是編入class文件中,或者是在運行時可以通過反射訪問。
      RetentionPolicy.RUNTIME:當前註解會編譯生成對應的.class字節碼文件,並且可以加
      載到JVM中,參與代碼執行
      RetentionPolicy.CLASS:

別糾結,記下就好:
RetentionPolicy.SOURCE:註解將被編譯器丟棄(該類型的註解信息只會保留在源碼裏,源碼經過編譯後,註解信息會被丟棄,不會保留在編譯好的class文件裏)
@Override
對應屬性RetentionPolicy.SOURCE
在代碼編譯過程中,檢查方法格式是否正確,不參與代碼運行和解析。

@Documented
標記這些註解是否包含在用戶文檔中。
是否可以通過JavaDoc工具,生成對應的API文檔

@Target
標記這個註解應該是哪種 Java 成員。
屬性:
ElementType
TYPE: 當前註解可以用於類聲明
METHOD: 當前註解可以用於方法聲明位置
FIELD:當前註解可以用於成員變量聲明位置
@Inherited
標記這個註解是繼承於哪個註解類(默認 註解並沒有繼承於任何子類)

【重點】
@Target目標
可以作用範圍 類,方法,成員變量…
@Retention
RetentionPolicy.RUNTIME 常用

  • 使用反射獲取註解中的內容【用途】
  • 使用註解測試代碼運行【用途】

註解使用總結

    1. 註解以後大多數情況下,都是使用過程,而不是自定義,會使用到框架中預處理好的註解。
  1. 註解是給誰用的?
    a. 編譯器
    b. 解析代碼使用
    c. JVM運行代碼使用
  2. 註解是一個標籤,有時候是做標記的,有時候標籤是有屬性的。

函數式接口

函數式接口

  • 概述

    • 如果說一個接口內有且只有一個方法,而且該方法是一個缺省屬性爲public abstract方法,該接口可以稱之爲是一個函數式接口。
      自定義函數式接口,還有系統中提供的函數式接口
      Comparator Runnable

    可以直接理解JDK1.8的新特徵,Lambda表達式來使用。

    Lambda表達式對比匿名內部類使用
    1. 簡化了代碼結構
    2. 節約了內存資源
    3. 讓程序員更加關注,我要做什麼,而不是爲了做什麼需要完成什麼

  • @FunctionalInterface 使用

    • /**
  • 使用@FunctionalInterface檢查函數式接口格式問題
  • 要求當前接口中有且只有一個缺省屬性爲public abstract的方法
  • @author Anonymous 2020/3/11 9:55
    */
    @FunctionalInterface
    public interface FunctionalType {
    void test();
    }
  • 使用自定義的函數式接口作爲方法的參數使用

函數式編程思想

  • Lambda延遲執行

    • 日誌記錄

      • 日誌是否保存會存在等級限制
        演示一個根據不同的等級來記錄log日誌
        要求:
        等級 == 1 記錄log日誌,其他情況不記錄
    • 使用函數式接口提供日誌信息功能

  • Lambda作爲方法參數和返回值

Java中提供的常用函數式接口

  • JDK常用函數式接口概述

    • java.util.function包名 。提供了很多函數式接口
      規範了一些操作,提升了開發效率,更加專注於目的性!!!

    Supplier 生產者, 返回一個指定類型的數據
    Consumer 消費者, 消耗一個指定類型的數據
    Predicate 判斷調節,過濾使用
    Function<T,R> 類型轉換,根據你指定的類型T, 轉換成對應類型R

  • Supplier 生產者,返回一個指定的數據類型

    • java.util.function.Supplier
      有且只有一個方法
      T get();
      不需要參數,返回指定T類型數據
      什麼都不吃,擠的都是輸出。。。
    • 找出數組中最大值所在下標位置
    • 引出滿足更多普適性代碼的函數式接口使用方式
  • Consumer消費者,處理數據

    • Consumer
      操作使用的方式是
      void accept(T t);
      根據接口指定的數據類型接收對應數據,進行處理和消費,對外沒有任何的返回
      至於處理的過程,展示,處理,計算。。。
    • andThen
  • Predicate 判斷數據是否合適,返回true/false

    • Predicate一般用於條件判斷,過濾數據的方法
      函數式接口中指定的方法
      boolean test(T t);
      處理T類型數據,返回boolean true / false
    • and 與
    • or 或
    • negate 非
    • ArrayList中使用Predicate刪除指定數據
  • Function<T,R> 類型轉換

    • 使用R apply(T t)
      轉換指定類型T到R
    • andThen

Stream流

Stream流引入

  • Stream流完全不是I/O流,按照流水線處理方式來考慮代碼中的思想。
    JDK1.8 之後,我們擁有了Lambda表達式,讓代碼的中心偏向解決實際問題,直到重點,可以提高效率。
    Stream流中使用了大量Lambda表達式,利用Lambda操作方式,提供開發效率

傳統遍歷方式和Stream類處理方式對比

Stream流對應的思想

  • Stream流有一些特徵:
    1. 帶有很多Stream流操作的方法, filter,limit,map,sorted,skip…這些方法大多是都會使用到函數式接口,那就意味着有lambda表達式
    2. 整個Stream流模型操作過程中,只有執行到count,foreach這些方法,操作真正的執行中的模型,如果不存在結果導向,中間的所有操作是無效的,這裏得益於Lambda表達式的延後性
    3. Stream流是存在一定的管道性 Pipelining 流水線

獲取Stream流

  • java.util.stream.Stream JDK1.8的新特徵
    1. 所有的Collection集合都有對應的Stream();
    2. 可以通過Stream類中的static Stream of()獲取
      static Stream of(T… t);
      static Stream of(T t);

Stream常用方法

  • 延遲方法:
    返回值類型依然是Stream接口本身,並沒有影響我們操作真正的資源
    允許鏈式操作,
    例如
    filter(XXX).limit(XXX).sorted(XXX).
    終結方法:
    返回值類型不是Stream接口本身,要麼處理數據,要麼返回其他類型數據,並且不再支持Stream流對象鏈式操作,count,foreach

    • foreach方法【終結方法】

      • void foreach(Consumer<? super T> action);
        /*
        終結方法:
        需要一個Consumer接口進行操作處理,消耗一個數據
        Consumer接口是一個【函數式接口】那就可以使用Lambda表達式
        Consumer接口中方法是
        void accept(T t);
        */
    • filter方法

      • Stream filter(Predicate<? super T> condition);
        /*
        filter是過濾方式,需要的參數是Predicate接口,Predicate是一個函數式接口,可以直接使用Lambda表達運行。
        這裏返回值類型是Stream類對象,是經過過濾之後的Stream類型,可以進行鏈式操作
        Predicate接口中需要實現的方法
        boolean test(T t);
        */

        • stream has already been operated upon or closed

爲何會出現這個錯誤?

因爲調用終結方法後,Stream流已經被銷燬,所以不能再對Stream流進行操作。

- map方法

	- <R> Stream<R> map(Function<? super T, ? super R> fun);

/*
類型轉換操作,得到的一個轉換之後數據類型的Stream流對象
這裏需要的參數是Function函數式接口,
R apply(T t);
T類型的數據轉換成R類型數據
*/

- count方法【終結方法】

	- long count();

/*
返回當前Stream流對象中有多少個元素
類似有Collection接口下的size(). String的length();
【終結方法】
一旦執行Stream流對象被關閉
*/

-  limit方法

	- Stream<T> limit(long maxSize);

/*
對於當前Stream流對象操作的數據進行限制操作,限制個數到maxSize
例如:
Stream流中保存的有10個元素,limit 5 ==> 前五個元素
*/

- skip方法

	- Stream<T> skip(long n);

/*
返回值依然是一個Stream流對象,這裏跳過當前Stream流對象前n個元素
*/

- concat方法

	- static Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 

/*
拼接兩個Stream流對象,是一個靜態方法,得到新的Stream流對象
*/

- 原始操作方式和Stream流方式對比

	- 1. 一個String類型的字符串集合,"1,騷磊,16"
  1. 過濾沒有5的數據
  2. 跳過前三個數據
  3. 限制得到前5個數據
  4. 兩個String類型集合字符串合併
  5. 轉換成Person類型
  6. 展示數據

方法引用

Lambda冗餘問題以及方法引用初識

方法引用小要求

  • testPrint(“鄭州加油!!!”, str -> System.out.println(str));

testPrint(“鄭州加油!!!”, System.out::println);

  1. 明確對象
    對象 ==> 調用者
    類對象,類名,super,this,構造方法,數組構造方法
  2. 明確的執行方法
    該方法只有名字不需要顯式出現參數
  3. 需要處理的數據
    【聯想,推導,省略】
  4. :: 方法引用格式

通過類對象來執行方法引用

    1. 明確對象
      類對象
  1. 明確執行的方法
    自定義
  2. 處理的數據
    簡單要求爲String類型

通過類名來執行方法引用

通過super關鍵字執行方法引用

通過this關鍵字執行方法引用

類構造方法引用

數組創建方式引用

單例模式

要求

  • 當前類有且只有一個對象,一旦當前類存在一個對象之後,無法在重新創建當前類的對象。就算是你要創建,代碼返回的對象依然是上一次創建的對象。
    懶漢模式,餓漢模式

單例模式推導【懶漢】

另一種單例模式【餓漢】

NIO

BIO概述

  • BIO
    BIO ==> Basic IO (基本IO), Block IO(阻塞IO)
    Scanner操作,文件讀寫操作,Socket數據傳輸操作… 都是BIO

    比如TCP羣聊,私聊聊天室
    Socket涉及到的IO,也是BIO
    資源浪費:
    1. 多線程,每一個Socket會對應一個線程,如果用戶量巨大,會導致線程過
    多,資源處理過多
    2. 採用阻塞狀態,一旦進入阻塞,代碼無法執行其他操作。
    3. 承載量一般,吞吐量比較小,同時可靠性不佳

NIO概述

  • NIO
    NIO ==> New IO(新IO), Non-Block IO(非阻塞IO)
    NIO非阻塞IO,允許當前程序在處理IO事務時,不會影響其他程序的運行,可以在不使用多線程的情況下,滿足IO操作要求。
    三大核心部分:
    通道
    Channel
    文件操作,網絡數據傳遞操作使用的通道
    緩衝
    Buffer
    緩衝使用可以提高操作效率,減少不必要的讀寫次數
    選擇器
    Selector
    真·核心 老大 boss

Buffer Channel完成文件操作

  • 常用API

    • java.nio.Buffer
      Buffer緩衝區
      ByteBuffer 字節緩衝 常用
      ShortBuffer
      IntBuffer
      LongBuffer
      CharBuffer 字符緩衝 常用
      FloatBuffer
      DoubleBuffer

常用方法:
public static ByteBuffer allocate(int capacity);
按照指定的字節數分配對應的緩衝區空間,保存字節數據
public byte get();
從字節緩衝區對象中讀取一個byte類型數組
public final Buffer flip();
翻轉緩衝區,回到緩衝區的開始位置。
public static ByteBuffer wrap(byte[] arr);
存入一個byte類型數組到緩衝區,會得到一個新的ByteBuffer
public static ByteBuffer put(byte[] b);
將字節數組存入緩衝區

Channel接口,通道接口
FileChannel 文件操作通道
DatagramChannel UDP協議數據包操作的Channel
ServerSocketChannel TCP服務端ServerSocket對應Channel
SocketChannel TCP客戶端Socket對應Channel

首先操作文件,以FileChannel爲例
public long read(ByteBuffer buffer);
從通道中讀取數據到ByteBuffer中
public long write(ByteBuffer buffer);
從Buffer中寫數據到通道中
public long transferFrom(ReadableByteChannel src, long position, long count)
從指定srcChannel中,指定位置position開始,讀取count個元素,到當前通道中
文件複製操作。

public long	transferTo(long position, long count, WritableByteChannel target) 
將當前通道中的數據寫入到target中,從當前通道的position位置開始,計數count
  • 操作文件數據

網絡編程使用NIO【重點】

  • Selector選擇器老大

    • Selector
      選擇器,網絡編程使用NIO的大哥!!!
      服務器可以執行一個線程,運行Selector程序,進行監聽操作。
      新連接, 已經連接, 讀取數據,寫入數據

Selector常用方法:
public static Selector Open();
得到一個選擇器對象
public int select(long timeout);
監聽所有註冊通道,存在IO流操作是,會將對應的信息SelectionKey存入到內部的集
閤中,參數是一個超時時間
public Set selectionKeys();
返回當前Selector內部集合中保存的所有SelectionKey

  • SelectionKey

    • SelectionKey
      表示Selector和網絡通道之間的關係
      int OP_ACCEPT; 16 需要連接
      int OP_CONNECT; 8 已經連接
      int OP_READ; 1 讀取操作
      int OP_WRITE; 4 寫入操作
      SelectionKey
      public abstract Selector selector();
      得到與之關聯的 Selector 對象
      public abstract SelectableChannel channel();
      得到與之關聯的通道
      public final Object attachment();
      得到與之關聯的共享數據
      public abstract SelectionKey interestOps(int ops);
      設置或改變監聽事件
      public final boolean isAcceptable();
      是否可以 accept
      public final boolean isReadable();
      是否可以讀
      public final boolean isWritable();
      是否可以寫
  • ServerSocketChannel

    • ServerSocketChannel
      服務端Socket程序對應的Channel通道
      常用方法:
      public static ServerSocketChannel open();
      開啓服務器ServerSocketChannel通道,等於開始服務器程序
      public final ServerSocketChannel bind(SocketAddress local);
      設置服務器端端口號
      public final SelectableChannel configureBlocking(boolean block);
      設置阻塞或非阻塞模式, 取值 false 表示採用非阻塞模式
      public SocketChannel accept();
      [非阻塞]
      獲取一個客戶端連接,並且得到對應的操作通道
      public final SelectionKey register(Selector sel, int ops);
      [重點方法]
      註冊當前選擇器,並且選擇監聽什麼事件
  • SocketChannel

    • SocketChannel
      客戶端Socket對應的Channel對象

常用方法:
public static SocketChannel open();
打卡一個Socket客戶端Channel對象
public final SelectableChannel configureBlocking(boolean block)
這裏可以設置是阻塞狀態,還是非阻塞狀態
false,表示非阻塞
public boolean connect(SocketAddress remote);
連接服務器
public boolean finishConnect();
如果connect連接失敗,可以通過finishConnect繼續連接
public int write(ByteBuffer buf);
寫入數據到緩衝流中
public int read(ByteBuffer buf); 、
從緩衝流中讀取數據
public final SelectionKey register(Selector sel, int ops, Object attechment);
註冊當前SocketChannel,選擇對應的監聽操作,並且可以帶有Object attachment參數
public final void close();
關閉SocketChannel

XMind: ZEN - Trial Version

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章