補全Java基礎(一)JavaSE基礎

1. super和this

    類在繼承時,會用到this和super,以前也不多用到,之前看過,今天看了一下,只記得之前看過,理解仍是不深。html

  • thisjava

    1. 直接引用,至關於指向對象自己算法

    2. 型參與成員變量名字相同,用this來區分bash

      class A{
          private int number = 15;
          A(){
              System.out.println("number:"+number);
          }
          public int getNumber(int number){
              this.number = number; //用來區分參數number和A類中的屬性number(this.number)
              return this.number;
          }
      }
      複製代碼
    3. 引用構造函數多線程

      this(參數):調用本類中另外一種構造函數(應在構造函數的第一行)ide

  • super函數

    1. 直接引用,指向當前對象的父類,能夠用super.xx來引用父類的屬性ui

    2. 子類的變量/方法與父類的變量/方法重名this

      在子類種調用時,重名的方法/變量,直接使用是子類的,this.xx或this.xx()屬於調用父類的。spa

    3. 引用構造函數

      super(參數):調用父類中某種構造函數(應在構造函數的第一行)


參考:Java中this和super的用法總結


2. equals()和hashcode()

    這兩個方法都是Object中帶有的方法,即全部方法都有equals和hashcode方法。

    在沒有重寫的狀況下,equals方法即"==",hashcode方法獲取哈希碼,即肯定對象在散列表中的索引位置。

    若是不重寫equals方法,那麼好比User這種對象,通常咱們equals比較的是它包含的屬性是否相同,不重寫的話,依照Object的equals方法,兩個對象的內存地址不一樣,因此返回false,這顯然不是咱們想要要的。因此要重寫,在方法中判斷屬性是否相同。

    若是隻重寫equals方法,而沒有重寫hashcode方法,好比User對象放在hashset中,hashset的特性是不容許重複。當咱們放入兩個數據相同的User時,重寫後的equals方法返回true,可是沒有重寫hashcode方法,依照Object的hashcode方法,兩個對象的hash值不一樣,因此不是同一對象,hashset中均可以存入,可是當咱們將User放入hashset中,要求若是是要求數據不一樣的話,就會沒法實現。因此須要重寫hashcode方法,使得包含數據相同的兩個User對象,返回的hash值相同,而此時,hashset會判斷重複,就只能存入一個。

```
    而這其中就涉及到hashset判斷重複的依據
    add(object)會先用Object的equals方法和hashcode方法判斷set中,
    是否已有重複元素,固然,若是兩個元素若是不是指向同一對象,不管如何也不會重複。
    因此,當咱們須要剔除數據相同的元素,就須要在此基礎上從新給元素對應的類型重寫hashcode方法。
```
複製代碼

參考:
重寫equals方法後重寫hashCode方法的必要性
如何重寫hashCode算法
HashSet重複元素判斷


3. 線程

    對我來講,是急需增強的一個點,由於以前用到的很少,因此也就不求甚解。

    Java多線程的兩種實現方式:繼承Thread類、實現Runnable接口

  • 繼承Thread類

    package cn.wh3t;
    
    public class MultiThread extends Thread{
    	private String name;
    	
    	public MultiThread(String name) {
    		// TODO 自動生成的構造函數存根
    		this.name = name;
    	}
    	
    	@Override
    	public void run() {
    		// TODO 自動生成的方法存根
    		for(int i =0;i<5;i++) {
    			System.out.println("Thread-"+name+"----hello"+i);
    		}
    	}
    	
    	public static void main(String[] args) {
    		MultiThread multiThread1 = new MultiThread("A");
    		MultiThread multiThread2 = new MultiThread("B");
    		multiThread1.start();
    		System.out.println("hello1");
    		System.out.println("hello2");
    		multiThread2.start();
    		System.out.println("hello3");
    		System.out.println("hello4");
    	}
    }
    複製代碼
    輸出結果://屢次運行結果都會不一樣
    hello1
    hello2
    Thread-A----hello0
    Thread-A----hello1
    Thread-A----hello2
    hello3
    Thread-A----hello3
    hello4
    Thread-A----hello4
    Thread-B----hello0
    Thread-B----hello1
    Thread-B----hello2
    Thread-B----hello3
    Thread-B----hello4
    複製代碼

    實際啓動的時候並非調用Thread的run方法,而是strat方法。

    那麼run()和start()有什麼區別

    在Java中,線程一般都有5種狀態:建立、就緒、運行、阻塞、死亡

        1. 建立:生成線程對象時,還未調用start方法

        2. 就緒:調用了該線程對象的start,線程進入就緒狀態,可是並未把該線程設成當前線程。在線程運行以後,從等待或者睡眠中回來,也會進入就緒狀態

        3. 運行:將該線程設爲當前線程,線程進入運行狀態,開始運行run()中的代碼

        4. 阻塞:線程正在運行時,被暫停,一般是爲了某個時間發生或者加載某個資源以後再繼續運行。sleep、suspend、wait都會形成阻塞

        5. 死亡:線程的run()結束,或者調用了stop方法,線程就會死亡。對於死亡的線程,再使用start方法也不會使其進入就緒狀態

    總之就是說,若是沒有start方法,只有run方法。就不是多線程執行了。由於start方法使線程進入就緒狀態,並無當即執行,經過上面代碼的運行結果也能夠看出,線程1的start要早於hello一、hello2的打印,卻比他們輸出要晚。若是沒有start,只是run(),就會依次執行,迴歸單線程(當前主線程)執行的狀態。


  • 實現Runnable接口

    package cn.wh3t;
    
    public class MultiThreadByRunnable implements Runnable{
    
    	private int count = 20;
    	
    	@Override
    	public void run() {
    		// TODO 自動生成的方法存根
    		for(int i =0;i<40;i++) {
    			if(count>0) {
    				System.out.println(Thread.currentThread().getName()+" "+count--);
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		MultiThreadByRunnable runnable = new MultiThreadByRunnable();
    		Thread thread1 = new Thread(runnable,"A");
    		Thread thread2 = new Thread(runnable,"B");
    		thread1.start();
    		System.out.println("hello1");
    		System.out.println("hello2");
    		thread2.start();
    		System.out.println("hello3");
    		System.out.println("hello4");
    	}
    }
    複製代碼
    輸出結果://屢次運行結果都會不一樣
    hello1
    A  20
    hello2
    A  19
    A  18
    A  17
    hello3
    hello4
    A  16
    B  15
    B  13
    B  12
    A  14
    B  11
    B  10
    B  9
    B  8
    B  6
    B  5
    B  4
    B  3
    B  2
    B  1
    A  7
    
    複製代碼

    兩種實現方式不一樣:相比之下,實現Runnable接口更有優點

      1. 適合多個代碼相同的程序去處理同一資源;

      2. 能夠避免Java中的單繼承,能夠擴展實現更多接口

      3. 增長程序的健壯性,代碼能夠被多個程序共享,代碼和數據獨立


  • wait()和notify()

    sleep()、suspend()、yield()等都是Thread類的方法,wait()和notify()確實屬於Object的方法,即全部類均可以執行這兩個方法。由於這兩個方法阻塞時要釋放佔用的鎖,而全部對象都有鎖。wait()致使線程阻塞,釋放該對象上佔用的鎖。調用notify()則致使因調用該對象的wait()而阻塞的線程中,隨機選擇一個解除阻塞(可是要等到真正得到鎖之後才能執行)。這兩個方法必須在synchronized方法或塊中執行,由於synchronized方法/塊才能佔有鎖,有鎖才能釋放。

    關於wait和notify要注意的三個點:

    1. 調用notify方法而接觸阻塞的線程是從wait方法阻塞的線程中隨機選取的,因此沒法預料哪一個線程被選取,因此要當心,避免不肯定性產生的問題

    2. 除了notify,notifyAll方法也能夠喚醒,解除阻塞的是因wait方法而阻塞的全部線程,固然,只有得到鎖的那個線程才能進入可執行狀態

    3. wait和notify必須成對存在

package cn.wh3t;

    public class CountThread extends Thread{
    	int total;
    
    	@Override
    	public void run() {
    		// TODO 自動生成的方法存根
    		synchronized (this) {
    			for(int i=0;i<100;i++) {
    				total = total +1 ;
    			}
    			this.notify();//喚醒被阻塞的線程
    			System.out.println("notify"+total);
    		}
    	}
    }
複製代碼
package cn.wh3t;
    
    public class TestWaitAndNotify {
    	public static void main(String[] args) {
    		CountThread thread = new CountThread();
    		thread.start();
    		
    		synchronized(thread) {
    			System.out.println("等到線程結束"+thread.total);
    			try {
    				thread.wait();
    				System.out.println("wait"+thread.total);
    			}catch(Exception e) {
    				e.printStackTrace();
    			}
    			System.out.println("計算的結果是:"+thread.total);
    		}
    	}
    }   
複製代碼
(主線程等待技術線程結束後纔打印輸出)
等到線程結束0
notify100
wait100
計算的結果是:100
複製代碼

參考:
java 線程詳解
Thread的run()與start()的區別
java thread中的wait()和notify()

相關文章
相關標籤/搜索