天天看點

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

JAVA知識點複習

  • 多線程
    • 程式、程序、線程的了解
    • 并行與并發
    • 建立多線程的兩種方式
    • Thread類中的常用方法
    • 線程的生命周期
    • 線程的同步機制
      • 線程安全的單例模式(懶漢式)
      • 死鎖問題
    • 線程通信
    • JDK5.0新增線程建立的方式
  • String
    • 常用方法
    • String與其它結構的轉換
      • 與基本資料類型、包裝類之間的轉換
      • 與字元數組之間的轉換
      • 與位元組數組之間的轉換
      • 與StringBuffer、StringBuilder之間的轉換
  • StringBuffer、StringBuilder
  • JDK 8之前日期時間API
  • Java比較器
    • Java比較器的使用背景:
    • 自然排序:使用Comparable接口
      • 自定義類代碼舉例:
    • 定制排序:使用Comparator接口
      • 代碼舉例:
  • Java集合
    • 集合與數組存儲資料概述:
    • List
      • 常用方法:
      • 常用實作類:
    • Map
      • 常用方法
    • Collections工具類的使用
    • 常用方法:
  • 泛型
    • 泛型的了解
    • 泛型在集合中的使用
      • 在集合中使用泛型之前的例子
      • 在集合中使用泛型例子1
      • 在集合中使用泛型例子2
      • 集合中使用泛型總結:
    • 自定義泛型類、泛型接口、泛型方法
  • File類
    • File的執行個體化
    • File類的常用方法
  • IO流概述
    • 輸入、輸出的标準化過程
      • 輸入過程
      • 輸出過程
  • 緩沖流的使用
    • 緩沖流涉及到的類:
    • 作用:
    • 典型代碼
    • 使用BufferedReader和BufferedWriter:處理文本檔案
  • Java反射機制

多線程

程式、程序、線程的了解

  1. 程式(programm)

    概念:是為完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜态的代碼。

  2. 程序(process)

    概念:程式的一次執行過程,或是正在運作的一個程式。

    說明:程序作為資源配置設定的機關,系統在運作時會為每個程序配置設定不同的記憶體區域

  3. 線程(thread)

    概念:程序可進一步細化為線程,是一個程式内部的一條執行路徑。

    說明:線程作為排程和執行的機關,每個線程擁獨立的運作棧和程式計數器(pc),線程切換的開銷小。

并行與并發

  1. 并行與并發的了解

    并行:多個CPU同時執行多個任務。比如:多個人同時做不同的事。

    并發:一個CPU(采用時間片)同時執行多個任務。比如:秒殺、多個人做同一件事

建立多線程的兩種方式

方式一:繼承Thread類的方式:

    1. 建立一個繼承于Thread類的子類
    1. 重寫Thread類的run() --> 将此線程執行的操作聲明在run()中
    1. 建立Thread類的子類的對象
    1. 通過此對象調用start():①啟動目前線程 ② 調用目前線程的run()

說明兩個問題:

問題一:我們啟動一個線程,必須調用start(),不能調用run()的方式啟動線程。

問題二:如果再啟動一個線程,必須重新建立一個Thread子類的對象,調用此對象的start().

方式二:實作Runnable接口的方式:

    1. 建立一個實作了Runnable接口的類
    1. 實作類去實作Runnable中的抽象方法:run()
    1. 建立實作類的對象
    1. 将此對象作為參數傳遞到Thread類的構造器中,建立Thread類的對象
    1. 通過Thread類的對象調用start()

兩種方式的對比:

  • 開發中:優先選擇:實作Runnable接口的方式
  • 原因:1. 實作的方式沒類的單繼承性的局限性
  • 2. 實作的方式更适合來處理多個線程共享資料的情況。
               
  • 聯系:public class Thread implements Runnable
  • 相同點:兩種方式都需要重寫run(),将線程要執行的邏輯聲明在run()中。

    目前兩種方式,要想啟動線程,都是調用的Thread類中的start()。

Thread類中的常用方法

Thread類中的常用的方法:

    1. start():啟動目前線程;調用目前線程的run()
    1. run(): 通常需要重寫Thread類中的此方法,将建立的線程要執行的操作聲明在此方法中
    1. currentThread():靜态方法,傳回執行目前代碼的線程
    1. getName():擷取目前線程的名字
    1. setName():設定目前線程的名字
    1. yield():釋放目前cpu的執行權
    1. join():線上程a中調用線程b的join(),此時線程a就進入阻塞狀态,直到線程b完全執行完以後,線程a才結束阻塞狀态。
    1. stop():已過時。當執行此方法時,強制結束目前線程。
    1. sleep(long millitime):讓目前線程“睡眠”指定的millitime毫秒。在指定的millitime毫秒時間内,目前線程是阻塞狀态。
    1. isAlive():判斷目前線程是否存活
  • 線程的優先級:
  • MAX_PRIORITY:10
  • MIN _PRIORITY:1
  • NORM_PRIORITY:5 -->預設優先級
  • 2.如何擷取和設定目前線程的優先級:
  • getPriority():擷取線程的優先級
  • setPriority(int p):設定線程的優先級
  • 說明:高優先級的線程要搶占低優先級線程cpu的執行權。但是隻是從機率上講,高優先級的線程高機率的情況下被執行。并不意味着隻當高優先級的線程執行完以後,低優先級的線程才執行。

線程通信:wait() / notify() / notifyAll() :此三個方法定義在Object類中的。

補充:線程的分類

一種是守護線程,一種是使用者線程。

線程的生命周期

圖示:

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

說明:

1.生命周期關注兩個概念:狀态、相應的方法

2.關注:狀态a–>狀态b:哪些方法執行了(回調方法)

某個方法主動調用:狀态a–>狀态b

3.阻塞:臨時狀态,不可以作為最終狀态

死亡:最終狀态。

線程的同步機制

1.背景

例子:建立個視窗賣票,總票數為100張.使用實作Runnable接口的方式

*

  • 1.問題:賣票過程中,出現了重票、錯票 -->出現了線程的安全問題
  • 2.問題出現的原因:當某個線程操作車票的過程中,尚未操作完成時,其他線程參與進來,也操作車票。
  • 3.如何解決:當一個線程a在操作ticket的時候,其他線程不能參與進來。直到線程a操作完ticket時,其他線程才可以開始操作ticket。這種情況即使線程a出現了阻塞,也不能被改變。

2.Java解決方案:同步機制

在Java中,我們通過同步機制,來解決線程的安全問題。

方式一:同步代碼塊

synchronized(同步螢幕){
    //需要被同步的代碼 }
           

說明:

1.操作共享資料的代碼,即為需要被同步的代碼。 -->不能包含代碼多了,也不能包含代碼少了。

2.共享資料:多個線程共同操作的變量。比如:ticket就是共享資料。

3.同步螢幕,俗稱:鎖。任何一個類的對象,都可以充當鎖。

要求:多個線程必須要共用同一把鎖。

補充:在實作Runnable接口建立多線程的方式中,我們可以考慮使用this充當同步螢幕。

在繼承Thread類建立多線程的方式中,慎用this充當同步螢幕,考慮使用目前類充當同步螢幕。

方式二:同步方法

如果操作共享資料的代碼完整的聲明在一個方法中,我們不妨将此方法聲明同步的。

關于同步方法的總結:

  1. 同步方法仍然涉及到同步螢幕,隻是不需要我們顯式的聲明。
  2. 非靜态的同步方法,同步螢幕是:this

    靜态的同步方法,同步螢幕是:目前類本身

方式三:Lock鎖 — JDK5.0新增

  1. 面試題:synchronized 與 Lock的異同?

    相同:二者都可以解決線程安全問題

    不同:synchronized機制在執行完相應的同步代碼以後,自動的釋放同步螢幕

    Lock需要手動的啟動同步(lock(),同時結束同步也需要手動的實作(unlock())

使用的優先順序:

Lock —> 同步代碼塊(已經進入了方法體,配置設定了相應資源 ) —> 同步方法(在方法體之外)

3.利弊

同步的方式,解決了線程的安全問題。—好處

操作同步代碼時,隻能一個線程參與,其他線程等待。相當于是一個單線程的過程,效率低。

線程安全的單例模式(懶漢式)

使用同步機制将單例模式中的懶漢式改寫為線程安全的。

class Bank{

    private Bank(){}

    private static Bank instance = null;

    public static Bank getInstance(){
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if(instance == null){
//
//                instance = new Bank();
//            }
//            return instance;
//        }
        //方式二:效率更高
        if(instance == null){

            synchronized (Bank.class) {
                if(instance == null){

                    instance = new Bank();
                }

            }
        }
        return instance;
    }

}
           

面試題:寫一個線程安全的單例模式。

餓漢式。

懶漢式:上面提供的。

死鎖問題

1.死鎖的了解:

不同的線程分别占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了線程的死鎖

2.說明:

  • 1出現死鎖後,不會出現異常,不會出現提示,隻是所的線程都處于阻塞狀态,無法繼續
  • 2我們使用同步時,要避免出現死鎖。

    3.舉例:

public static void main(String[] args) {

    StringBuffer s1 = new StringBuffer();
    StringBuffer s2 = new StringBuffer();


    new Thread(){
        @Override
        public void run() {

            synchronized (s1){

                s1.append("a");
                s2.append("1");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                synchronized (s2){
                    s1.append("b");
                    s2.append("2");

                    System.out.println(s1);
                    System.out.println(s2);
                }


            }

        }
    }.start();


    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (s2){

                s1.append("c");
                s2.append("3");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (s1){
                    s1.append("d");
                    s2.append("4");

                    System.out.println(s1);
                    System.out.println(s2);
                }


            }



        }
    }).start();


}


           

線程通信

1.線程通信涉及到的三個方法:

  • wait():一旦執行此方法,目前線程就進入阻塞狀态,并釋放同步螢幕。
  • notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
  • notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。

2.說明:

  • 1.wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
  • 2.wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步螢幕。
  • 否則,會出現IllegalMonitorStateException異常
  • 3.wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。

    3.面試題:

    面試題:sleep() 和 wait()的異同?

  • 1.相同點:一旦執行方法,都可以使得目前的線程進入阻塞狀态。
  • 2.不同點:

    1)兩個方法聲明的位置不同:Thread類中聲明sleep() , Object類中聲明wait()

    2)調用的要求不同:sleep()可以在任何需要的場景下調用。 wait()必須使用在同步代碼塊或同步方法中

    3)關于是否釋放同步螢幕:如果兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放鎖,wait()會釋放鎖。

小結釋放鎖的操作:

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

小結不會釋放鎖的操作:

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

JDK5.0新增線程建立的方式

新增方式一:實作Callable接口。 — JDK 5.0新增

//1.建立一個實作Callable的實作類
class NumThread implements Callable{
    //2.實作call方法,将此線程需要執行的操作聲明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    public static void main(String[] args) {
        //3.建立Callable接口實作類的對象
        NumThread numThread = new NumThread();
        //4.将此Callable接口實作類的對象作為傳遞到FutureTask構造器中,建立FutureTask的對象
        FutureTask futureTask = new FutureTask(numThread);
        //5.将FutureTask的對象作為參數傳遞到Thread類的構造器中,建立Thread對象,并調用start()
        new Thread(futureTask).start();

        try {
            //6.擷取Callable中call方法的傳回值
            //get()傳回值即為FutureTask構造器參數Callable實作類重寫的call()的傳回值。
            Object sum = futureTask.get();
            System.out.println("總和為:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

           

說明:

  • 如何了解實作Callable接口的方式建立多線程比實作Runnable接口建立多線程方式強大?
    1. call()可以傳回值的。
    1. call()可以抛出異常,被外面的操作捕獲,擷取異常的資訊
    1. Callable是支援泛型的

新增方式二:使用線程池

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args) {
        //1. 提供指定線程數量的線程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //設定線程池的屬性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.執行指定的線程的操作。需要提供實作Runnable接口或Callable接口實作類的對象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.關閉連接配接池
        service.shutdown();
    }

}
           

說明:

  • 好處:
  • 1.提高響應速度(減少了建立新線程的時間)
  • 2.降低資源消耗(重複利用線程池中線程,不需要每次都建立)
  • 3.便于線程管理

    corePoolSize:核心池的大小

    maximumPoolSize:最大線程數

    keepAliveTime:線程沒任務時最多保持多長時間後會終止

面試題:Java中多線程的建立有幾種方式?四種。

String

常用方法

常用方法:

int length():傳回字元串的長度: return value.length

char charAt(int index): 傳回某索引處的字元return value[index]

boolean isEmpty():判斷是否是空字元串:return value.length == 0

String toLowerCase():使用預設語言環境,将 String 中的所字元轉換為小寫

String toUpperCase():使用預設語言環境,将 String 中的所字元轉換為大寫

String trim():傳回字元串的副本,忽略前導空白和尾部空白

boolean equals(Object obj):比較字元串的内容是否相同

boolean equalsIgnoreCase(String anotherString):與equals方法類似,忽略大小寫

String concat(String str):将指定字元串連接配接到此字元串的結尾。 等價于用“+”

int compareTo(String anotherString):比較兩個字元串的大小

String substring(int beginIndex):傳回一個新的字元串,它是此字元串的從beginIndex開始截取到最後的一個子字元串。

String substring(int beginIndex, int endIndex) :傳回一個新字元串,它是此字元串從beginIndex開始截取到endIndex(不包含)的一個子字元串。

boolean endsWith(String suffix):測試此字元串是否以指定的字尾結束

boolean startsWith(String prefix):測試此字元串是否以指定的字首開始

boolean startsWith(String prefix, int toffset):測試此字元串從指定索引開始的子字元串是否以指定字首開始

boolean contains(CharSequence s):當且僅當此字元串包含指定的 char 值序列時,傳回 true

int indexOf(String str):傳回指定子字元串在此字元串中第一次出現處的索引

int indexOf(String str, int fromIndex):傳回指定子字元串在此字元串中第一次出現處的索引,從指定的索引開始

int lastIndexOf(String str):傳回指定子字元串在此字元串中最右邊出現處的索引

int lastIndexOf(String str, int fromIndex):傳回指定子字元串在此字元串中最後一次出現處的索引,從指定的索引開始反向搜尋

注:indexOf和lastIndexOf方法如果未找到都是傳回-1

替換:

String replace(char oldChar, char newChar):傳回一個新的字元串,它是通過用 newChar 替換此字元串中出現的所 oldChar 得到的。

String replace(CharSequence target, CharSequence replacement):使用指定的字面值替換序列替換此字元串所比對字面值目标序列的子字元串。

String replaceAll(String regex, String replacement):使用給定的 replacement 替換此字元串所比對給定的正規表達式的子字元串。

String replaceFirst(String regex, String replacement):使用給定的 replacement 替換此字元串比對給定的正規表達式的第一個子字元串。

比對:

boolean matches(String regex):告知此字元串是否比對給定的正規表達式。

切片:

String[] split(String regex):根據給定正規表達式的比對拆分此字元串。

String[] split(String regex, int limit):根據比對給定的正規表達式來拆分此字元串,最多不超過limit個,如果超過了,剩下的全部都放到最後一個元素中。

String與其它結構的轉換

與基本資料類型、包裝類之間的轉換

String --> 基本資料類型、包裝類:調用包裝類的靜态方法:parseXxx(str)
    基本資料類型、包裝類 --> String:調用String重載的valueOf(xxx)

    @Test
    public void test1(){
        String str1 = "123";
//        int num = (int)str1;//錯誤的
        int num = Integer.parseInt(str1);

        String str2 = String.valueOf(num);//"123"
        String str3 = num + "";

        System.out.println(str1 == str3);
    }
           

與字元數組之間的轉換

String --> char[]:調用	String的toCharArray()
char[] --> String:調用	String的構造器

@Test
public void test2(){
    String str1 = "abc123";  //題目: a21cb3

    char[] charArray = str1.toCharArray();
    for (int i = 0; i < charArray.length; i++) {
        System.out.println(charArray[i]);
    }

    char[] arr = new char[]{'h','e','l','l','o'};
    String str2 = new String(arr);
    System.out.println(str2);
}

           

與位元組數組之間的轉換

編碼:String --> byte[]:調用String的getBytes()
解碼:byte[] --> String:調用String的構造器

編碼:字元串 -->位元組  (看得懂 --->看不懂的二進制資料)
解碼:編碼的逆過程,位元組 --> 字元串 (看不懂的二進制資料 ---> 看得懂

說明:解碼時,要求解碼使用的字元集必須與編碼時使用的字元集一緻,否則會出現亂碼。

@Test
public void test3() throws UnsupportedEncodingException {
    String str1 = "abc123中國";
    byte[] bytes = str1.getBytes();//使用預設的字元集,進行編碼。
    System.out.println(Arrays.toString(bytes));

    byte[] gbks = str1.getBytes("gbk");//使用gbk字元集進行編碼。
    System.out.println(Arrays.toString(gbks));

    System.out.println("******************");

    String str2 = new String(bytes);//使用預設的字元集,進行解碼。
    System.out.println(str2);

    String str3 = new String(gbks);
    System.out.println(str3);//出現亂碼。原因:編碼集和解碼集不一緻!


    String str4 = new String(gbks, "gbk");
    System.out.println(str4);//沒出現亂碼。原因:編碼集和解碼集一緻!


}
           

與StringBuffer、StringBuilder之間的轉換

String -->StringBuffer、StringBuilder:

調用StringBuffer、StringBuilder構造器

StringBuffer、StringBuilder -->String:

①調用String構造器;②StringBuffer、StringBuilder的toString()

StringBuffer、StringBuilder

1.String、StringBuffer、StringBuilder三者的對比

String:不可變的字元序列;底層使用char[]存儲

StringBuffer:可變的字元序列;線程安全的,效率低;底層使用char[]存儲

StringBuilder:可變的字元序列;jdk5.0新增的,線程不安全的,效率高;底層使用char[]存儲

2.StringBuffer與StringBuilder的記憶體解析

以StringBuffer為例:

String str = new String();//char[] value = new char[0];
String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};

StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底層建立了一個長度是16的數組。
System.out.println(sb1.length());//
sb1.append('a');//value[0] = 'a';
sb1.append('b');//value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
           

//問題1. System.out.println(sb2.length());//3

//問題2. 擴容問題:如果要添加的資料底層數組盛不下了,那就需要擴容底層的數組。

預設情況下,擴容為原來容量的2倍 + 2,同時将原數組中的元素複制到新的數組中。

指導意義:開發中建議大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity)
           

3.對比String、StringBuffer、StringBuilder三者的執行效率

從高到低排列:StringBuilder > StringBuffer > String

4.StringBuffer、StringBuilder中的常用方法

增:append(xxx)

删:delete(int start,int end)

改:setCharAt(int n ,char ch) / replace(int start, int end, String str)

查:charAt(int n )

插:insert(int offset, xxx)

長度:length();

*周遊:for() + charAt() / toString()

JDK 8之前日期時間API

1.擷取系統目前時間:System類中的currentTimeMillis()

long time = System.currentTimeMillis();
//傳回目前時間與1970年1月1日0時0分0秒之間以毫秒為機關的時間差。
//稱為時間戳
System.out.println(time);
           
  1. java.util.Date類與java.sql.Date類
/*
    java.util.Date類
           |---java.sql.Date類

    1.兩個構造器的使用
        >構造器一:Date():建立一個對應目前時間的Date對象
        >構造器二:建立指定毫秒數的Date對象
    2.兩個方法的使用
        >toString():顯示目前的年、月、日、時、分、秒
        >getTime():擷取目前Date對象對應的毫秒數。(時間戳)
    3. java.sql.Date對應着資料庫中的日期類型的變量
        >如何執行個體化
        >如何将java.util.Date對象轉換為java.sql.Date對象
*/
    @Test
    public void test2(){
        //構造器一:Date():建立一個對應目前時間的Date對象
        Date date1 = new Date();
        System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019

        System.out.println(date1.getTime());//1550306204104

        //構造器二:建立指定毫秒數的Date對象
        Date date2 = new Date(155030620410L);
        System.out.println(date2.toString());

        //建立java.sql.Date對象
        java.sql.Date date3 = new java.sql.Date(35235325345L);
        System.out.println(date3);//1971-02-13

        //如何将java.util.Date對象轉換為java.sql.Date對象
        //情況一:
//        Date date4 = new java.sql.Date(2343243242323L);
//        java.sql.Date date5 = (java.sql.Date) date4;
        //情況二:
        Date date6 = new Date();
        java.sql.Date date7 = new java.sql.Date(date6.getTime());


    }
           
  1. java.text.SimpleDataFormat類

    SimpleDateFormat對日期Date類的格式化和解析

    1.兩個操作:

    1.1 格式化:日期 —>字元串

    1.2 解析:格式化的逆過程,字元串 —> 日期

2.SimpleDateFormat的執行個體化:new + 構造器

//*************照指定的方式格式化和解析:調用帶參的構造器*****************
//        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa");
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        //格式化
        String format1 = sdf1.format(date);
        System.out.println(format1);//2019-02-18 11:48:27
        //解析:要求字元串必須是符合SimpleDateFormat識别的格式(通過構造器參數展現),
        //否則,抛異常
        Date date2 = sdf1.parse("2020-02-18 11:48:27");
        System.out.println(date2);
           

小練習:

/*
    練習一:字元串"2020-09-08"轉換為java.sql.Date

    練習二:"天打漁兩天曬網"   1990-01-01  xxxx-xx-xx 打漁?曬網?

    舉例:2020-09-08 ? 總天數

    總天數 % 5 == 1,2,3 : 打漁
    總天數 % 5 == 4,0 : 曬網

    總天數的計算?
    方式一:( date2.getTime() - date1.getTime()) / (1000 * 60 * 60 * 24) + 1
    方式二:1990-01-01  --> 2019-12-31  +  2020-01-01 -->2020-09-08
     */
    @Test
    public void testExer() throws ParseException {
        String birth = "2020-09-08";

        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf1.parse(birth);
//        System.out.println(date);

        java.sql.Date birthDate = new java.sql.Date(date.getTime());
        System.out.println(birthDate);
    }
           

4.Calendar類:月曆類、抽象類

//1.執行個體化
        //方式一:建立其子類(GregorianCalendar的對象
        //方式二:調用其靜态方法getInstance()
        Calendar calendar = Calendar.getInstance();
//        System.out.println(calendar.getClass());

        //2.常用方法
        //get()
        int days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);
        System.out.println(calendar.get(Calendar.DAY_OF_YEAR));

        //set()
        //calendar可變性
         calendar.set(Calendar.DAY_OF_MONTH,22);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

        //add()
        calendar.add(Calendar.DAY_OF_MONTH,-3);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

        //getTime():月曆類---> Date
        Date date = calendar.getTime();
        System.out.println(date);

        //setTime():Date ---> 月曆類
        Date date1 = new Date();
        calendar.setTime(date1);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);
           

Java比較器

Java比較器的使用背景:

Java中的對象,正常情況下,隻能進行比較:== 或 != 。不能使用 > 或 < 的

但是在開發場景中,我們需要對多個對象進行排序,言外之意,就需要比較對象的大小。

如何實作?使用兩個接口中的任何一個:Comparable 或 Comparator

自然排序:使用Comparable接口

說明

1.像String、包裝類等實作了Comparable接口,重寫了compareTo(obj)方法,給出了比較兩個對象大小的方式。

2.像String、包裝類重寫compareTo()方法以後,進行了從小到大的排列

3. 重寫compareTo(obj)的規則:

如果目前對象this大于形參對象obj,則傳回正整數,

如果目前對象this小于形參對象obj,則傳回負整數,

如果目前對象this等于形參對象obj,則傳回零。

4. 對于自定義類來說,如果需要排序,我們可以讓自定義類實作Comparable接口,重寫compareTo(obj)方法。在compareTo(obj)方法中指明如何排序

自定義類代碼舉例:

public class Goods implements  Comparable{

    private String name;
    private double price;

    //指明商品比較大小的方式:照價格從低到高排序,再照産品名稱從高到低排序
    @Override
    public int compareTo(Object o) {
//        System.out.println("**************");
        if(o instanceof Goods){
            Goods goods = (Goods)o;
            //方式一:
            if(this.price > goods.price){
                return 1;
            }else if(this.price < goods.price){
                return -1;
            }else{
//                return 0;
               return -this.name.compareTo(goods.name);
            }
            //方式二:
//           return Double.compare(this.price,goods.price);
        }
//        return 0;
        throw new RuntimeException("傳入的資料類型不一緻!");
    }
// getter、setter、toString()、構造器:省略
}
           

定制排序:使用Comparator接口

3.1 說明

1.背景:

當元素的類型沒實作java.lang.Comparable接口而又不友善修改代碼,或者實作了java.lang.Comparable接口的排序規則不适合目前的操作,那麼可以考慮使用 Comparator 的對象來排序

2.重寫compare(Object o1,Object o2)方法,比較o1和o2的大小:

(可使用系統自動生成的equals方法修改一下)

如果方法傳回正整數,則表示o1大于o2;

如果傳回0,表示相等;

傳回負整數,表示o1小于o2。

代碼舉例:

Comparator com = new Comparator() {
    //指明商品比較大小的方式:照産品名稱從低到高排序,再照價格從高到低排序
    @Override
    public int compare(Object o1, Object o2) {
        if(o1 instanceof Goods && o2 instanceof Goods){
            Goods g1 = (Goods)o1;
            Goods g2 = (Goods)o2;
            if(g1.getName().equals(g2.getName())){
                return -Double.compare(g1.getPrice(),g2.getPrice());
            }else{
                return g1.getName().compareTo(g2.getName());
            }
        }
        throw new RuntimeException("輸入的資料類型不一緻");
    }
}
           

使用:

Arrays.sort(goods,com);

Collections.sort(coll,com);

new TreeSet(com);

  1. 兩種排序方式對比
  • Comparable接口的方式一旦一定,保證Comparable接口實作類的對象在任何位置都可以比較大小。
  • Comparator接口屬于臨時性的比較。

Java集合

集合與數組存儲資料概述:

集合、數組都是對多個資料進行存儲操作的結構,簡稱Java容器。

說明:此時的存儲,主要指的是記憶體層面的存儲,不涉及到持久化的存儲(.txt,.jpg,.avi,資料庫中)

  1. 數組存儲的特點:

    一旦初始化以後,其長度就确定了。

    數組一旦定義好,其元素的類型也就确定了。我們也就隻能操作指定類型的資料了。

    比如:String[] arr;int[] arr1;Object[] arr2;

  2. 數組存儲的弊端:

    一旦初始化以後,其長度就不可修改。

    數組中提供的方法非常限,對于添加、删除、插入資料等操作,非常不便,同時效率不高。

    擷取數組中實際元素的個數的需求,數組沒有現成的屬性或方法可用

    數組存儲資料的特點:有序、可重複。對于無序、不可重複的需求,不能滿足。

  3. 集合存儲的優點:

    解決數組存儲資料方面的弊端。

List

常用方法:

增:add(Object obj)

删:remove(int index) / remove(Object obj)

改:set(int index, Object ele)

查:get(int index)

插:add(int index, Object ele)

長度:size()

周遊:① Iterator疊代器方式

② 增強for循環

③ 普通的循環

常用實作類:

|----Collection接口:單列集合,用來存儲一個一個的對象

  • |----List接口:存儲序的、可重複的資料。 -->“動态”數組,替換原的數組
  • |----ArrayList:作為List接口的主要實作類;線程不安全的,效率高;底層使用Object[] elementData存儲
               
  • |----LinkedList:對于頻繁的插入、删除操作,使用此類效率比ArrayList高;底層使用雙向連結清單存儲
               
  • |----Vector:作為List接口的古老實作類;線程安全的,效率低;底層使用Object[] elementData存儲
               

Map

常用方法

  • 添加:put(Object key,Object value)
  • 删除:remove(Object key)
  • 修改:put(Object key,Object value)
  • 查詢:get(Object key)
  • 長度:size()
  • 周遊:keySet() / values() / entrySet()

Collections工具類的使用

常用方法:

reverse(List):反轉 List 中元素的順序

shuffle(List):對 List 集合元素進行随機排序

sort(List):根據元素的自然順序對指定 List 集合元素升序排序

sort(List,Comparator):根據指定的 Comparator 産生的順序對 List 集合元素進行排序

swap(List,int, int):将指定 list 集合中的 i 處元素和 j 處元素進行交換

Object max(Collection):根據元素的自然順序,傳回給定集合中的最大元素

Object max(Collection,Comparator):根據 Comparator 指定的順序,傳回給定集合中的最大元素

Object min(Collection)

Object min(Collection,Comparator)

int frequency(Collection,Object):傳回指定集合中指定元素的出現次數

void copy(List dest,List src):将src中的内容複制到dest中

boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替換 List 對象的所舊值

泛型

泛型的了解

1.泛型的概念

所謂泛型,就是允許在定義類、接口時通過一個辨別表示類中某個屬性的類型或者是某個方法的返

回值及參數類型。這個類型參數将在使用時(例如,繼承或實作這個接口,用這個類型聲明變量、

建立對象時确定(即傳入實際的類型參數,也稱為類型實參)。

2.泛型的引入背景

集合容器類在設計階段/聲明階段不能确定這個容器到底實際存的是什麼類型的對象,是以在JDK1.5之前隻能把元素類型設計為Object,JDK1.5之後使用泛型來解決。因為這個時候除了元素的類型不确定,其他的部分是确定的,例如關于這個元素如何儲存,如何管理等是确定的,是以此時把元素的類型設計成一個參數,這個類型參數叫做泛型。Collection,List,ArrayList 這個就是類型參數,即泛型。

泛型在集合中的使用

在集合中使用泛型之前的例子

@Test
    public void test1(){
        ArrayList list = new ArrayList();
        //需求:存放學生的成績
        list.add(78);
        list.add(76);
        list.add(89);
        list.add(88);
        //問題一:類型不安全
//        list.add("Tom");

        for(Object score : list){
            //問題二:強轉時,可能出現ClassCastException
            int stuScore = (Integer) score;

            System.out.println(stuScore);

        }

    }
           

圖示:

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

在集合中使用泛型例子1

@Test
    public void test2(){
       ArrayList<Integer> list =  new ArrayList<Integer>();

        list.add(78);
        list.add(87);
        list.add(99);
        list.add(65);
        //編譯時,就會進行類型檢查,保證資料的安全
//        list.add("Tom");

        //方式一:
//        for(Integer score : list){
//            //避免了強轉操作
//            int stuScore = score;
//
//            System.out.println(stuScore);
//
//        }
        //方式二:
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            int stuScore = iterator.next();
            System.out.println(stuScore);
        }

    }
           

圖示:

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

在集合中使用泛型例子2

//在集合中使用泛型的情況:以HashMap為例
    @Test
    public void test3(){
//        Map<String,Integer> map = new HashMap<String,Integer>();
        //jdk7新特性:類型推斷
        Map<String,Integer> map = new HashMap<>();

        map.put("Tom",87);
        map.put("Jerry",87);
        map.put("Jack",67);

//        map.put(123,"ABC");
        //泛型的嵌套
        Set<Map.Entry<String,Integer>> entry = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();

        while(iterator.hasNext()){
            Map.Entry<String, Integer> e = iterator.next();
            String key = e.getKey();
            Integer value = e.getValue();
            System.out.println(key + "----" + value);
        }

    }
           

集合中使用泛型總結:

  • ① 集合接口或集合類在jdk5.0時都修改為帶泛型的結構。
  • ② 在執行個體化集合類時,可以指明具體的泛型類型
  • ③ 指明完以後,在集合類或接口中凡是定義類或接口時,内部結構(比如:方法、構造器、屬性等)使用到類的泛型的位置,都指定為執行個體化的泛型類型。
  • 比如:add(E e) —>執行個體化以後:add(Integer e)
  • ④ 注意點:泛型的類型必須是類,不能是基本資料類型。需要用到基本資料類型的位置,拿包裝類替換
  • ⑤ 如果執行個體化時,沒指明泛型的類型。預設類型為java.lang.Object類型。

自定義泛型類、泛型接口、泛型方法

1.舉例:

【Order.java】

public class Order<T> {

    String orderName;
    int orderId;

    //類的内部結構就可以使用類的泛型

    T orderT;

    public Order(){
        //編譯不通過
//        T[] arr = new T[10];
        //編譯通過
        T[] arr = (T[]) new Object[10];
    }

    public Order(String orderName,int orderId,T orderT){
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderT = orderT;
    }

    //如下的個方法都不是泛型方法
    public T getOrderT(){
        return orderT;
    }

    public void setOrderT(T orderT){
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }
    //靜态方法中不能使用類的泛型。
//    public static void show(T orderT){
//        System.out.println(orderT);
//    }

    public void show(){
        //編譯不通過
//        try{
//
//
//        }catch(T t){
//
//        }

    }

    //泛型方法:在方法中出現了泛型的結構,泛型參數與類的泛型參數沒任何關系。
    //換句話說,泛型方法所屬的類是不是泛型類都沒關系。
    //泛型方法,可以聲明為靜态的。原因:泛型參數是在調用方法時确定的。并非在執行個體化類時确定。
    public static <E>  List<E> copyFromArrayToList(E[] arr){

        ArrayList<E> list = new ArrayList<>();

        for(E e : arr){
            list.add(e);
        }
        return list;

    }
}
           

【SubOrder.java】

public class SubOrder extends Order<Integer> {//SubOrder:不是泛型類


    public static <E> List<E> copyFromArrayToList(E[] arr){

        ArrayList<E> list = new ArrayList<>();

        for(E e : arr){
            list.add(e);
        }
        return list;

    }


}


//執行個體化時,如下的代碼是錯誤的
SubOrder<Integer> o = new SubOrder<>();

【SubOrder1.java】
public class SubOrder1<T> extends Order<T> {//SubOrder1<T>:仍然是泛型類

}
           

【測試】

@Test
    public void test1(){
        //如果定義了泛型類,執行個體化沒指明類的泛型,則認為此泛型類型為Object類型
        //要求:如果大家定義了類是帶泛型的,建議在執行個體化時要指明類的泛型。
        Order order = new Order();
        order.setOrderT(123);
        order.setOrderT("ABC");

        //建議:執行個體化時指明類的泛型
        Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");

        order1.setOrderT("AA:hello");

    }

    @Test
    public void test2(){
        SubOrder sub1 = new SubOrder();
        //由于子類在繼承帶泛型的父類時,指明了泛型類型。則執行個體化子類對象時,不再需要指明泛型。
        sub1.setOrderT(1122);

        SubOrder1<String> sub2 = new SubOrder1<>();
        sub2.setOrderT("order2...");
    }

    @Test
    public void test3(){

        ArrayList<String> list1 = null;
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        //泛型不同的引用不能互相指派。
//        list1 = list2;

        Person p1 = null;
        Person p2 = null;
        p1 = p2;


    }

    //測試泛型方法
    @Test
    public void test4(){
        Order<String> order = new Order<>();
        Integer[] arr = new Integer[]{1,2,3,4};
        //泛型方法在調用時,指明泛型參數的類型。
        List<Integer> list = order.copyFromArrayToList(arr);

        System.out.println(list);
    }
           

2.注意點:

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制
Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

3.應用場景舉例:

【DAO.java】:定義了操作資料庫中的表的通用操作。 ORM思想(資料庫中的表和Java中的類對應)

public class DAO<T> {//表的共性操作的DAO

    //添加一條記錄
    public void add(T t){

    }

    //删除一條記錄
    public boolean remove(int index){

        return false;
    }

    //修改一條記錄
    public void update(int index,T t){

    }

    //查詢一條記錄
    public T getIndex(int index){

        return null;
    }

    //查詢多條記錄
    public List<T> getForList(int index){

        return null;
    }

    //泛型方法
    //舉例:擷取表中一共有多少條記錄?擷取最大的員工入職時間?
    public <E> E getValue(){

        return null;
    }

}

【CustomerDAO.java】:
public class CustomerDAO extends DAO<Customer>{//隻能操作某一個表的DAO
}

【StudentDAO.java】:
public class StudentDAO extends DAO<Student> {//隻能操作某一個表的DAO
}
           

File類

1.File類的了解

    1. File類的一個對象,代表一個檔案或一個檔案目錄(俗稱:檔案夾)
    1. File類聲明在java.io包下
    1. File類中涉及到關于檔案或檔案目錄的建立、删除、重命名、修改時間、檔案大小等方法,并未涉及到寫入或讀取檔案内容的操作。如果需要讀取或寫入檔案内容,必須使用IO流來完成。
    1. 後續File類的對象常會作為參數傳遞到流的構造器中,指明讀取或寫入的"終點".

File的執行個體化

2.1 常用構造器

File(String filePath)

File(String parentPath,String childPath)

File(File parentFile,String childPath)

2.2 路徑的分類

相對路徑:相較于某個路徑下,指明的路徑。

絕對路徑:包含盤符在内的檔案或檔案目錄的路徑

說明:

IDEA中:

如果大家開發使用JUnit中的單元測試方法測試,相對路徑即為目前Module下。

如果大家使用main()測試,相對路徑即為目前的Project下。

Eclipse中:

不管使用單元測試方法還是使用main()測試,相對路徑都是目前的Project下。

2.3 路徑分隔符

windows和DOS系統預設使用“\”來表示

UNIX和URL使用“/”來表示

File類的常用方法

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制
Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制
Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

IO流概述

輸入、輸出的标準化過程

輸入過程

① 建立File類的對象,指明讀取的資料的來源。(要求此檔案一定要存在)

② 建立相應的輸入流,将File類的對象作為參數,傳入流的構造器中

③ 具體的讀入過程:

建立相應的byte[] 或 char[]。

④ 關閉流資源

說明:程式中出現的異常需要使用try-catch-finally處理。

輸出過程

① 建立File類的對象,指明寫出的資料的位置。(不要求此檔案一定要存在)

② 建立相應的輸出流,将File類的對象作為參數,傳入流的構造器中

③ 具體的寫出過程:

write(char[]/byte[] buffer,0,len)

④ 關閉流資源

說明:程式中出現的異常需要使用try-catch-finally處理。

緩沖流的使用

緩沖流涉及到的類:

  • BufferedInputStream
  • BufferedOutputStream
  • BufferedReader
  • BufferedWriter

作用:

作用:提供流的讀取、寫入的速度

提高讀寫速度的原因:内部提供了一個緩沖區。預設情況下是8kb

Java學習知識小總結①多線程StringStringBuffer、StringBuilderJDK 8之前日期時間APIJava比較器Java集合泛型File類IO流概述緩沖流的使用Java反射機制

典型代碼

3.1 使用BufferedInputStream和BufferedOutputStream:處理非文本檔案

//實作檔案複制的方法
    public void copyFileWithBuffered(String srcPath,String destPath){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1.造檔案
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
            //2.造流
            //2.1 造節點流
            FileInputStream fis = new FileInputStream((srcFile));
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造緩沖流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.複制的細節:讀取、寫入
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.資源關閉
            //要求:先關閉外層的流,再關閉内層的流
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            //說明:關閉外層流的同時,内層流也會自動的進行關閉。關于内層流的關閉,我們可以省略.
//        fos.close();
//        fis.close();
        }
    }
           

使用BufferedReader和BufferedWriter:處理文本檔案

@Test
    public void testBufferedReaderBufferedWriter(){
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //建立檔案和相應的流
            br = new BufferedReader(new FileReader(new File("dbcp.txt")));
            bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

            //讀寫操作
            //方式一:使用char[]數組
//            char[] cbuf = new char[1024];
//            int len;
//            while((len = br.read(cbuf)) != -1){
//                bw.write(cbuf,0,len);
//    //            bw.flush();
//            }

            //方式二:使用String
            String data;
            while((data = br.readLine()) != null){
                //方法一:
//                bw.write(data + "\n");//data中不包含換行符
                //方法二:
                bw.write(data);//data中不包含換行符
                bw.newLine();//提供換行的操作

            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關閉資源
            if(bw != null){

                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

    }
           

Java反射機制

學藝不精 隻會用

參考代碼

package com.xzh.web;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * 這個類繼承了HttpServlet并且重寫了doPost方法
 * 友善以後其他繼承這個代碼用反射來調用其對應的方法
 *
 * @Author Lin
 * @CreateTime 2021/5/10 9:45
 */
public abstract class BaseServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解決請求和轉發亂碼
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        //得到網頁的資訊 判斷該頁面是什麼頁面
        //<%--action後面的值表示要調用的servlet的方法--%>
        String action = request.getParameter("action");
        try {
            //通過反射調用方法  getDeclaredMethod(方法名,參數類型 ...args){}
            Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
            //調用方法 并傳入參數
            method.invoke(this, request, response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

           

主要為這裡

//<%--action後面的值表示要調用的servlet的方法--%>
        String action = request.getParameter("action");
        try {
            //通過反射調用方法  getDeclaredMethod(方法名,參數類型 ...args){}
            Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
            //調用方法 并傳入參數
            method.invoke(this, request, response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }