JavaDay23 內部類和線程


tags:

  • 內部類
  • 線程
  • 進程
    style: summer

JavaDay23 內部類和線程

@toc

一、複習

(一)Comparable 和 Comparator

兩者都是接口

  • Comparable 是一個接口,給自定義類提供比較方式,遵從 Comparable的實現類,必須實現 compareTo(T o)方法;即進行兩個對象的比較;
  • Comparator 是一個接口,用來做自定義比較器,遵從 Comparator接口的實現類,必須使用 compare(T o1, T o2);

方法1.實現自定義比較器

class MyCompare implements Comparator<Student>{
  @override
  public int compare(Student o1, Student o2){
    return o1.getAge() - o2.getAge();
  }
}

上面這種方式:好理解但是需要重新定義一個類,而且可能會統一放在一個包裏面,略顯複雜;

方法 2:使用匿名內部類的匿名對象
格式:Arrays.sort(T[] t, Comparator<? extends T> com)

Student[] array = {stu1, stu2, stu3};

Arrays.sort(array.new Comparator<Student>{
  @Override
  public int compare(Student o1, Student o2){
    return o1.getAge() - o2.getAge();
  }
});

二、內部類

  • 生活實例:
    在人類中,有些東西,比如內臟,用成員方法或者成員變量描述都顯着有點不太合適,因爲這些內臟首先是屬於【人體的一部分】,而且會使用【人體的一些屬性】,但是又擁有自己的一些【特徵】;能不能把這些器官,認爲是一個類,一個屬於人類內部的一個類。

  • 內部類分類:
    1.成員內部類
    2.局部內部類
    3.匿名內部類

(一)成員內部類

  • 內部類和外部類比較:
    • 1.成員內部類可以使用外部類的所有成員變量和成員方法, 不管用什麼權限修飾,不管是private還是public都可以使用,因爲,這是在內部類的內部使用。
    • 2.【在Outer類的外部創建Outer的Inner對象】
      格式如下:
      外部類名.內部類名 內部類對象名 = new 外部類名().new 內部類名();
      例如:Outer.inner inner = new Outer().new Inner();
      【第一個知識點】:普通的成員變量和成員方法,在沒有對象的情況下,不能再類外使用
    • 3.如果內部類和外部類存在同名的成員變量,這裏默認是就近原則,使用的是內部類的成員變量
      如果想要使用外部類的成員變量和成員方法的格式:
      外部類名.this.同名成員變量;
      外部類名.this.同名成員方法(參數列表);
    • 4.在外部類的類內方法中,可以創建內部類的對象
package com.qfedu.a_innnerclass;

class Outer {
	int num = 100; //外部類的成員變量

	private static int s = 10;

	class Inner { //這是Outer類的一個成員內部類
		int i = 10; //內部類的成員變量
		int num = 50;
		public void testInner() {
			System.out.println("內部類的成員方法");
			testOuter();
			System.out.println("內部類同名成員變量:" + num); // 50 就近原則
			//使用  外部類名.this.成員變量名  獲取同名外部成員變量
			System.out.println("外部類同名成員變量:" + Outer.this.num);
			
			//分別調用內部和外部成員方法
			test();
			Outer.this.test();

			System.out.println(s);
			testStatic();
		}

		public void test() {
			System.out.println("內部類的test方法");
		}
	}//inner

	
	public void testOuter() {
		System.out.println("外部類的成員方法");
	}

	public void test() {
		System.out.println("外部類的test方法");
	}

	//爲了將內部類的語句輸出
	//在【外部類】中定義【內部類的類對象】
	public void createInnnerClass() {
		//外部類的成員方法中調用內部類的構造方法,通過new關鍵字來創建內部類類對象使用
		Inner inner = new Inner();
		inner.testInner();
	}

	public static void testStatic() {
		System.out.println("外部類的靜態成員方法~");
	}
}

//☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
public class Demo1 {
	public static void main(String[] args) {
		//創建內部類對象的方法一:在外部類中創建內部類對象
//		Outer outer = new Outer();
//		outer.createInnnerClass();

		//創建內部類對象的方法一:在類外創建一個Inner類對象
		//數據類型是Outer.Inner 表示是外部類裏面的內部類數據類型
		Outer.Inner inner = new Outer().new Inner();
		inner.testInner();
	}
}

運行結果:

內部類的成員方法
外部類的成員方法
內部類成員變量:50
外部類成員變量:100
內部類的test方法
外部類的test方法
10
外部類的靜態成員方法~

(二)局部內部類

放在方法內或者函數內的類,稱之爲局部內部類

  • 【第二個知識點】

    • 1.局部變量的
      • 【生存期】
        從聲明定義位置開始,到代碼塊結束
      • 【作用域】
        只能在當前代碼塊內部
    • 2.類對象的
      • 【生存期】
        通過new關鍵字創建的時候開始,JVM垃圾回收機制調用時,銷燬該對象,結束
      • 【作用域】
        那個引用變量擁有這個對象的首地址,哪裏就是他的作用域
  • 說明:
    局部內部類只能在函數或者方法中使用,方法或者函數外不能使用

  • 發現:
    局部內部類中,貌似不能【修改】所在方法或者函數中的局部變量,原因如下:

局部內部類的對象是在函數或者說方法的內部通過JVM藉助於new關鍵字,和局部內部類的構造方法,創建的一個類對象,並且該對象是由JVM的垃圾回收機制回收的;
但是局部變量n是在testInner()方法中,而這個局部變量的n的生存週期是和testInner()該方法的大括號有關,生存期和作用域都是在大括號以內;
如果在testInner()方法的內部,MethodInner()這個類是方法中的局部內部類,而創建的對象在使用testInner()方法中的局部變量時,因爲對象的銷燬時間不確定,但是一定是晚於局部變量的銷燬的,這裏隱含了一個類對象【延長了局部變量生存期】,這個是不符合Java原理的;
這就是爲什麼不能修改,因爲有可能在這個對象被使用的時候,局部變量的內存空間已經被內存收回,換而言之這個局部變量不存在了。

  • 【解決方法】
    如果是在局部內部類中使用的所在函數或者方法的局部變量,該變量用final修飾
package com.qfedu.a_innnerclass;

class Test {
	int num = 100;
	
	public void testInner() {
		//這裏是在方法中定義了一個類,這個類只能在當前函數或者方法中使用
		final int n = 10;
		class MethodInner {
			int i = 10;
			
			public void function() {
				System.out.println(i);
				num = 20;
				System.out.println(num);
				//n = 20;//使用final之後就可以了
				System.out.println(n);
				System.out.println("局部內部類的成員方法");
			}
		}
		
		MethodInner methodInner = new MethodInner();
		methodInner.function();
	}
	
}

public class Demo2 {
	public static void main(String[] args) {
//		for (int i = 0; i < 10; i++) {
//			System.out.println(i);
//		}
		
//		System.out.println(i);
		new Test().testInner();
	}
}

程序運行結果:

10
20
10
局部內部類的成員方法

(三)匿名內部類

只是沒有了名字,但是類本體中該實現的還得實現;
匿名內部類必須繼承一個類或者實現一個接口
【第四個知識點】
類的本體:在類聲明部分,大括號裏面的內容就是類 的本體!!!

class Test {
	//成員變量
	//成員方法
}

匿名內部類就是沒有名字的類,但是有類的本體!!!

package com.qfedu.a_innnerclass;

import java.util.Arrays;
import java.util.Comparator;

abstract class Animal {
	int age;

	public abstract void test();
	abstract public void jump();
}

class CodingMonkey extends Animal {

	//以下兩個方法是類的本體!!!
	@Override
	public void test() {
		System.out.println("這是類本體中的一個成員方法");
	}

	@Override
	public void jump() {
		System.out.println("Hello");
	}
}



interface A {
	int i = 0; // public static final
	public void testA(); //abstract
}

public class Demo3 {
	public static void main(String[] args) {
		//方法一:正常方法: 可以創建CodingMonkey即Animal的子類的對象,進行方法的操作
		CodingMonkey cm = new CodingMonkey();

		//Animal a = cm; 多態,父類的引用指向子類的對象~
		cm.test();
		cm.jump();


		//方法二:匿名對象類: 也可以直接通過匿名對象類進行直接操作

     /*
		Animal是一個抽象類,ani 是抽象類的一個引用類型變量

		new Animal() {
			發現這裏面的內容和繼承該抽象類的子類內容一致,都是抽象類,要求子類實現的方法;同時這裏是類的本體,而這裏可以看做是一個類,但是這個類沒有名字,所以:這個就是【匿名內部類】;本質上這裏創建了一個【匿名內部類】的對象,賦值給了Animal的引用數據類型 ani,同時這裏隱含了一個【繼承】關係
			 };
			 */
		Animal ani = new Animal() {
			//同樣需要實現繼承的相關功能
			@Override
			public void test() {
				System.out.println("匿名內部類的裏面的test方法");
			}

			@Override
			public void jump() {
				System.out.println("匿名內部類的Hello");
			}
		};

		ani.jump();
		ani.test();


		//以上更加簡潔的使用方式:

		//匿名內部類的匿名對象直接調用方法
		new A() {
			//這裏是一個類【遵從】接口之後,要求實現接口中的抽象方法,這裏也是一個【匿名內部類】
			@Override
			public void testA() {
				System.out.println("匿名內部類實現interface A中的testA方法");
			}
		}.testA();//new A(){}可以看成匿名對象
		//Java Android開發

		Integer[] arr = {3, 2, 4, 5, 6, 1};

		//匿名內部類的匿名對象作爲方法的參數
		Arrays.sort(arr, new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				return o1 - o2;
			}
		});

		System.out.println(Arrays.toString(arr));
	}

}

程序運行結果:

這是類本體中的一個成員方法
Hello
匿名內部類的Hello
匿名內部類的裏面的test方法
匿名內部類實現interface A中的testA方法
[1, 2, 3, 4, 5, 6]
  • 在使用匿名內部類的過程中,我們需要注意如下幾點:
    1、使用匿名內部類時,我們必須是繼承一個類或者實現一個接口,但是兩者不可兼得,同時也只能繼承一個類或者實現一個接口。
    2、匿名內部類中是不能定義構造函數的
    3、匿名內部類中不能存在任何的靜態成員變量和靜態方法
    4、匿名內部類爲局部內部類,所以局部內部類的所有限制同樣對匿名內部類生效。
    5、匿名內部類不能是抽象的,它必須要實現繼承的類或者實現的接口的所有抽象方法。

三、多線程與多進程

  • 多線程和多進程:

    • 線程:
      在一個軟件中,負責不同功能的子程序,稱之爲線程。
    • 進程:
      是在計算機系統中所有正在運行的程序,都可以看做是一個進程。多進程的操作系統
  • 從實際出發:
    每一個CPU的內核在一個時間片內只能執行一件事情。但是這個時間片非常的短,不同的程序會出現在不同的時間片上,不斷的切換,所以你感覺是在同時運行,現在的CPU基本上都是多核多線程的。

  • 面試題:
    請問一個Java程序再運行的時候,最少有幾個線程???
    最少是兩個線程:1. main線程,即主線程 2. JVM的垃圾回收機制

  • 多線程的好處
    1.可以讓一個程序同時執行不同的任務
    2.可以提高資源的利用率

  • 多線程的弊端
    1.線程安全問題 之前的迭代器
    2.增加了CPU負擔
    3.降低了其他程序CPU的執行概率
    4.容易出現死鎖行爲

  • 如何創建一個線程
    兩種方式:
    1.自定義一個類繼承Thread類,那麼這個類就可以看做是一個多線程類
    2.要求【重寫】Thread類裏面的run方法,把需要執行的自定義線程代碼放入這個run方法

下面是使用迭代器出現的同時操作問題;

package com.qfedu.b_thread;

import java.util.ArrayList;
import java.util.Iterator;

class MyThread extends Thread {
	//要求重寫【run】方法
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("自定義Thread類:" + i);
		}
	}
}

public class Demo1 {
	public static void main(String[] args) {
		
		//1. 創建自定義線程類的類對象
		MyThread mt = new MyThread();
		
		//2. 啓動線程 。
		//mt.run();嘗試調用n方法 相當於調用了一個普通的方法,而不是一個線程
		mt.start(); //啓動線程的方式,會發現,MyThread線程和main線程在搶佔CPU資源
		
		for (int i = 0; i < 10; i++) {
			System.out.println("main線程:" + i);
		}




		/*
		ArrayList<String> list = new ArrayList<String>();
		
		list.add("hello, how do you do");
		list.add("I'm fine, Thank you , and you");
		list.add("I'm fine, too");

        //三種遍歷方式:
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
		
		for (String string : list) {
			System.out.println(string);
		}
		
		Iterator<String> it = list.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
		*/
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章