1. 程式人生 > >Java多型+構造方法+垃圾回收+內部類+Lambda+裝箱列舉註解

Java多型+構造方法+垃圾回收+內部類+Lambda+裝箱列舉註解

文章目錄

1 . 變數及其傳遞

1.1 基本型別變數與引用型變數

基本型別(primitive type):其值直接存於變數中。“在這裡”
引用型(reference type) 的變數(class,interface,陣列)除佔據一定的記憶體空間外,它所引用 的物件實體(由new 建立)也要佔據一定空間。“在那裡”
引用變數在這裡只是存一個物件實體的地址,通過這個引用能夠操作這個物件

 MyDate m,n;
 m=new MyDate();
 n=m;
 n.addYear();

.

 public class MyDate {
    private int day;
    private int month;
    private int year;
    public MyDate(int y, int m, int d) {
		year = y;
		month = m;
		day = d;
	}     
	void addYear(){
		year ++;
	}
	public void display() {
		System.out.println(year + "-" + month + "-" +day);  
	}
	public static void main(String[] args) {
		MyDate m = new MyDate(2003, 9, 22);
		MyDate n = m;//複製只是複製了一個引用
		n.addYear();
		m.display();//操作的是同一個物件
		n.display();
    }    
}

1.2 欄位變數與區域性變數

欄位變數(field)與區域性變數(Local variable)
前者是在類中,後者是方法中定義的變數或方法的參變數
從記憶體角度看
儲存位置,欄位變數為物件的一部分、存在於堆中的,區域性變數是存在於棧中。
生命週期不同 欄位變數隨著物件的存在而存在,區域性變數隨著方法的存在而存在,隨著方法的結束而結束
初始值:欄位變數可以自動賦初值,區域性變數則須顯式賦值

 class Test() { 
    int a; 
    void m(){ 
    int b; 
    System.out.println(b);
    //編譯不能通過需要初始化。 
    } 
 }

從語法角度看
欄位變數屬於類,可以用public,private,static,final 修飾。
區域性變數不能夠被訪問控制符及static修飾
都可以被final修飾

1.2 變數的傳遞

呼叫物件方法時,要傳遞引數。在傳遞引數時,
Java 是值傳遞,即,是將表示式的值複製給形式引數。
對於引用型變數,傳遞的值是引用值,而不是複製物件實體
可以改變物件的屬性

1.3變數的返回

方法的返回:
返回基本型別。
返回引用型別。它就可以存取物件實體。

Object getNewObject() 
  { 
   Object obj=new Object(); 
   return obj; 
  }

呼叫時:Object p= GetNewObject();

不定長引數

不定長引數(Variable length arguments),從JDK1.5開始
用省略號表示, 並且是最後一個引數
實際上Java當成一個數組

  int sum( int … nums){ 
  int s=0; 
  for(int n : nums) s+=n; 
  return s; 
 } 

呼叫:sum(1,2,3,4);
又例如: public static void main( String…argv)

2 . 多型和虛方法呼叫

2.1多型

多型(Polymorphism)是指一個程式中相同的名字表示不同的含義的情況。
多型有兩種情形
編譯時多型:
過載(overload) (多個同名的不同方法)。
如 p.sayHello(); p.sayHello(“Wang”);
執行時多型:
覆蓋(override) (子類對父類方法進行覆蓋)
動態繫結(dynamic binding) ----虛方法呼叫(virtual method invoking)
在呼叫方法時,程式會正確地呼叫子類物件的方法。
多型的特點大大提高了程式的抽象程度和簡潔性

2.2上溯造型

上溯造型(upcasting)
是把派生型別當作基本型別處理
例子見下面部落格
https://blog.csdn.net/weijie_home/article/details/49105871

Person p = new Student(); 
 void fun(Person p ){
 …
 }        
 fun(new Person());

2.3虛方法呼叫

什麼是虛方法?
https://blog.csdn.net/vop444/article/details/69525124#commentBox
虛方法例子:
https://blog.csdn.net/qq_32863631/article/details/79227859
用虛方法呼叫,可以實現執行時的多型!
子類過載了父類方法時,執行時
執行時系統根據呼叫該方法的例項的型別(傳進去的時student,那麼就呼叫student)來決定選擇哪個方法呼叫
所有的非final方法都會自動地進行動態繫結
什麼是動態繫結?
https://blog.csdn.net/javamoo/article/details/78776150
虛方法呼叫示例

class TestStaticInvoke
{
	static void doStuff( Shape s ){
		s.draw();
	}
	public static void main( String [] args ){
		Circle c = new Circle();
		Triangle t = new Triangle();
		Line l = new Line();
		doStuff(c);
		doStuff(t);
		doStuff(l);
		
		Shape s = new Circle();
		doStuff(s);
		s.draw();
		
		Circle c2 = new Circle();
		c2.draw();
	}
}
class Shape
{
	 void draw(){ System.out.println("Shape Drawing"); }
}
class Circle extends Shape
{
	 void draw(){ System.out.println("Draw Circle"); }
}

class Triangle extends Shape
{
	 void draw(){ System.out.println("Draw Three Lines"); }
}

class Line extends Shape
{
	 void draw(){ System.out.println("Draw Line"); }
}
//輸出:
//Draw Circle
//Draw Three Lines
//Draw Line
//Draw Circle
//Draw Circle
//Draw Circle

動態型別確定
變數 instanceof 型別
結果是boolean 值(實際就是這個型別或者是他的子型別,則返回true)
對實際型別進行判斷
例子:

class Instanceof 
{
	public static void main(String[] args) 
	{
		Object [] things = new Object[3];//object所有類的父類
		things[0] = new Integer(4);
		things[1] = new Double(3.14);
		things[2] = new String("2.09");
		double s = 0;
		for( int i=0; i<things.length; i++ ){
			if( things[i] instanceof Integer )//看看是不是屬於這個型別
				s += ((Integer)things[i]).intValue();//強制型別轉換
			else if( things[i] instanceof Double )
				s += ((Double)things[i]).doubleValue();
		}
		System.out.println("sum=" + s);
	}
}
//輸出:sum=7.140000000000001

什麼情況不是虛方法呼叫

Java中,普通的方法是虛方法
(在呼叫過程中會根據實際的物件來決定方法的呼叫)
但static,private方法不是虛方法呼叫 (static是屬於類的,private是屬於這個類自己的)
static,private與虛方法編譯後用的指令是不同的

package text1;

public class JavaP3methods {
	void f(){}
	private void p(){}
	static void s(){}

	public static void main(String...argv){
		JavaP3methods obj = new JavaP3methods();
		obj.f();
		obj.p();
		obj.s();
	}
}

反彙編程式碼:

Compiled from "JavaP3methods.java"
public class text1.JavaP3methods {
  public text1.JavaP3methods();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  void f();
    Code:
       0: return

  static void s();
    Code:
       0: return

  public static void main(java.lang.String...);
    Code:
       0: new           #1                  // class text1/JavaP3methods
       3: dup
       4: invokespecial #19                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #20                 // Method f:()V
      12: aload_1
      13: invokespecial #22                 // Method p:()V
      16: invokestatic  #24                 // Method s:()V
      19: return
}

三種非虛的方法
static的方法,以宣告的型別為準,與例項型別無關
private方法子類看不見,也不會被虛化
final方法子類不能覆蓋,不存在虛化問題

class example
{
	static void doStuff( Shape s ){
		s.draw();
	}
	public static void main( String [] args ){
		Circle c = new Circle();
		Triangle t = new Triangle();
		Line l = new Line();
		doStuff(c);
		doStuff(t);
		doStuff(l);
		
		Shape s = new Circle();
		doStuff(s);
		s.draw();
		
		Circle c2 = new Circle();//可見static是非虛的,只跟宣告的型別有關
		c2.draw();
	}
}
class Shape
{
	 static void draw(){ System.out.println("Shape Drawing"); }
}
class Circle extends Shape
{
	static void draw(){ System.out.println("Draw Circle"); }
}

class Triangle extends Shape
{
	static void draw(){ System.out.println("Draw Three Lines"); }
}

class Line extends Shape
{
	static void draw(){ System.out.println("Draw Line"); }
}

//輸出:
//Shape Drawing
//Shape Drawing
//Shape Drawing
//Shape Drawing
//Shape Drawing
//Draw Circle


3 . 物件構造與初始化

3.1 構造方法(constructor)

物件都有構造方法
如果沒有,編譯器加一個default構造方法 (預設構造方法什麼都不幹)
抽象類也有構造方法,任何一個物件都需要構造

呼叫本類或父類的構造方法

this呼叫本類的其他構造方法。
super呼叫直接父類的構造方法
this或super要放在第一條語句,且只能夠有一條
如果沒有this及super,則編譯器自動加上super(),即呼叫直接父類 不帶引數的構造方法
因為必須令所有父類的構造方法都得到呼叫,否則整個物件的構建就 可能不正確。

例子:

class example2
{
	public static void main(String[] args){ 
		Person p = new Graduate();
	}
}

class Person
{
	String name; 
	int age;
	Person(){}
	Person( String name, int age ){
		this.name=name; this.age=age; 
		System.out.println("In Person(String,int)");
	}
}

class Student extends Person
{
	String school;
	Student(){
		this( null, 0, null );
		System.out.println("In Student()");
	}
	Student( String name, int age, String school ){
		super( name, age );
		this.school = school;
		System.out.println("In Student(String,int,String)");
	}
}

class Graduate extends Student
{
	String teacher="";
	Graduate(){
		//super();
		System.out.println("In Graduate()");
	}
}

//輸出結果:
//In Person(String,int)
//In Student(String,int,String)
//In Student()
//In Graduate()

上面程式碼可見雖然只寫了一個new,但是它是一直呼叫父類的構造方法,直到object

  class A 
    { 
     A(int a){} 
    } 
  class B extends A 
    { 
     B(String s){} //編譯不能夠通過.
    }

編譯器會自動呼叫B(String s){ super();} 他會呼叫不帶引數的構造方法,但是父類沒有不帶引數的,所以出錯.
解決方法:
在B的構造方法中,加入super(3);
在A中加入一個不帶引數的構造方法,A(){}
去掉A中全部的構造方法,則編譯器會自動加入一個不帶引數的構造方法,稱為預設的構造方法

3.2 建立物件時初始化

p = new Person(){{ age=18; name=“李明”; }};

這樣就 不 用 寫 p. name,p.age,方便一點
這樣可以針對沒有相應建構函式,但又要賦值
注意雙括號

3.3 例項初始化與靜態初始化

例項初始化(Instance Initializers)
在類中直接寫
{ 語句…. }
例項初始化,先於構造方法{}中的語句執行(先於this或super之外的那些語句)
儘量少用這種{},有點怪怪的感覺。。

靜態初始化(Static Initializers)
static { 語句…. } 只是與類有關的而不是跟例項有關的
靜態初始化,在第一次使用這個類時要執行,
但其執行的具體時機是不確定的
但是可以肯定的是:總是先於例項的初始化

例子:

class InitialTest
{
	public static void main(String[] args) 
	{
		new InitialTest2(6);
	}
	int n=10;  //step2
	{
		n++;
		System.out.println("InitialTest..."+n);
	}
	
	static int x;
	static 
	{
		x++;
		System.out.println("static..." +x);
	}
	
}

class examp extends InitialTest{
	examp(int a){ 
		this.a=a; 
		System.out.println("this.a=" + a );
	}
	int a;//例項初始化的語句
	{
		System.out.println("InitialTest2..."+this.a);
	}
	static//這個初始化要先於例項的初始化
	{
		x++;
		System.out.println("static2..." +x);
	}
}
//輸出:
//static...1
//static...1
//static2...2
//InitialTest...11
//InitialTest2...0
//this.a=6

3.4 構造方法的執行過程

構造方法的執行過程遵照以下步驟:
呼叫本類或父類的構造方法,直至最高一層(Object)
按照宣告順序執行欄位的初始化賦值
執行建構函式中的其它各語句(不包括this或super)
簡單地說:
先父類構造,再本類成員賦值,最後執行構造方法中的語句。

例子:


class JavaPConstructor
{
	int a=2000;
	JavaPConstructor(){
		this.a=3000;
	}
}

下面我們看一下反彙編後的結果

Compiled from "JavaPConstructor.java"
class text3.JavaPConstructor {
  int a;

  text3.JavaPConstructor();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: sipush        2000
       8: putfield      #12                 // Field a:I
      11: aload_0
      12: sipush        3000
      15: putfield      #12                 // Field a:I
      18: return
}

可見,它先呼叫了object方法,雖然沒有寫super,但實際上他會呼叫super。第二步把兩千賦值到欄位裡面。(執行例項初始化及對欄位的賦值)第三步才執行構造方法裡面的語句

例子:(這次有super有this)

class ConstructS
{
	public static void main(String[] args){ 
		Person p = new Student("李明", 18, "北大");
	}
}

class Person
{
	String name="未命名";  //step 2
	int age=-1;
	Person( String name, int age ){
		super(); //step 1
		//step 3
		System.out.println( "開始構造Person(),此時this.name="+this.name+",this.age="+this.age );
		this.name=name; this.age=age; 
		System.out.println( "Person()構造完成,此時this.name="+this.name+",this.age="+this.age );
	}
}

class Student extends Person
{
	String school="未定學校"; //step2 
	Student( String name, int age, String school ){
		super( name, age );  //step 1看似一步其實包含父類三步
		//step 3
		System.out.println( "開始構造Student(),此時this.name="+this.name+",this.age="+this.age+",this.school="+this.school );
		this.school = school;
		System.out.println( "Student()構造完成,此時this.name="+this.name+",this.age="+this.age+",this.school="+this.school );
	}
}
//輸出結果:
//開始構造Person(),此時this.name=未命名,this.age=-1
//Person()構造完成,此時this.name=李明,this.age=18
//開始構造Student(),此時this.name=李明,this.age=18,this.school=未定學校
//Student()構造完成,此時this.name=李明,this.age=18,this.school=北大

構造方法內部呼叫別的的方法
如果這個方法是虛方法,結果如何?
從語法上來說這是合法的,但有時會造成事實上的不合

class ConstructorInvokeVirtual 
{
	public static void main(String[] args){ 
		Person p = new Student("Li Ming", 18, "PKU");
	}
}

class Person
{
	String name="未命名"; 
	int age=-1;
	Person( String name, int age ){
		this.name=name; this.age=age; 
		sayHello();
	}
	void sayHello(){
		System.out.println( "A Person, name: " + name + ", age: "+ age );
	}
}

class Student extends Person
{
	String school="未定學校";
	Student( String name, int age, String school ){
		super( name, age );
		this.school = school;//賦值這是第三步才執行的
	}
	void sayHello(){//子類覆蓋了父類的sayhello,父類就會一下跳到子類,但是此時還沒有賦值好
		System.out.println( "A Student, name:" + name + ", age: "+ age + ", school: " + school );
	}
}

在本例中,在構造方法中呼叫了一個動態繫結的方法sayHello(),這時, 會使用那個方法被覆蓋的定義,而這時物件嘗未完全構建好,所以 School還沒有賦值。
在構造方法中儘量避免呼叫任何方法,儘可能簡單地使物件進入就緒 狀態
惟一能夠安全呼叫的是final的方法。這就不會有虛方法的問題

4 . 物件清除與垃圾回收

我們知道:new建立物件 那麼如何銷燬物件?
Java中是自動清除 不需要使用delete

4.1 物件的自動清除

垃圾回收(garbage collection )
物件回收是由 Java虛擬機器的垃圾回收執行緒來完成的。
為什麼系統知道物件是否為垃圾
任何物件都有一個引用計數器,當其值為0時,說明該物件可以回收
(任何物件我們要用它,它都是一個引用)(如果物件實體空間沒有被任何引用所引用,那麼其值為零)
引用計數示意(可見他是自動的)

 String method(){ 
 String a,b; 
 a=new String(“hello world”); 
 b=new String(“game over”); 
 System.out.println(a+b+”Ok”); 
 a=null; //hello world 沒被引用
 a=b; //game over有兩個引用
 return a; 
 }//結束了,引用都沒了,但是如果呼叫了這個函式,又有引用了

System.gc ()方法

System.gc()方法
它是System類的static方法
它可以要求系統進行垃圾回收
但它僅僅只是”建議(suggest)”
你沒法強制,只是希望虛擬機器有空有條件時候進行垃圾回收

finalize() 方法

Java中沒有“析構方法(destructor)”
但Object的finalize() 有類似功能
系統在回收時會自動呼叫物件的finalize() 方法。
protected void finalize() throws Throwable{}
子類的finalize()方法
可以在子類的finalize()方法釋放系統資源
**一般來說,子類的finalize()方法中應該呼叫父類的finalize()方法,**以保證父 類的清理工作能夠正常進行。

try -with-resources

由於finalize()方法的呼叫時機並不確定,所以一般不用finalize()
關閉開啟的檔案、清除一些非記憶體資源等工作需要進行處理
可以使用try-with-resources語句(JDK1.7以上)
對於實現了java.lang.AutoCloseable的物件

 try( Scanner scanner= new Scanner( … ) ){ 
    。。。。。。 
     } 

會自動呼叫其close()方法,相當於,不管你try裡面正常異常都會做下面的事情

  finally{ 
   Scanner.close(); 
    }

5 . 內部類與匿名類

定義

內部類( inner class )是在其他類中的類 (其他的類中再定義類)
匿名類( anonymous class)是一種特殊的內部類,它沒有類名。

內部類(Inner class)

內部類的定義
將類的定義class xxxx{…}置入一個類的內部即可
編譯器生成xxxx$xxxx這樣的class檔案
內部類不能夠與外部類同名

內部類的使用
在封裝它的類的內部使用內部類,與普通類的使用方式相同
在其他地方使用
類名前要冠以外部類的名字。
在用new建立內部類例項時,也要在 new前面冠以物件變數。
外部物件名.new 內部類名(引數)
例子:

class TestInnerClass{
	public static void main( String[] args ){
		Parcel p = new Parcel();
		p.testShip();

		Parcel.Contents c = p.new Contents(33);//前面要加上外部類的類名
		Parcel.Destination d = p.new Destination( "Hawii" );
		p.setProperty( c, d );
		p.ship();
	}
}

class Parcel {
  private Contents c;
  private Destination d;
  class Contents {
    private int i;
	Contents( int i ){ this.i = i; }
    int value() { return i; }
  }
  class Destination {
    private String label;
    Destination(String whereTo) {label = whereTo;}
    String readLabel() { return label; }
  }
  void setProperty( Contents c, Destination d ){
	this.c =c; this.d = d;
  }
  void ship(){
	System.out.println( "move "+ c.value() +" to "+ d.readLabel() );
  }
  public void testShip() {
    c = new Contents(22);
    d = new Destination("Beijing");
    ship();
  }
}

在內部類中使用外部類的成員
內部類中可以直接訪問外部類的欄位及方法
即使private也可以
如果內部類中有與外部類同名的欄位或方法,則可以用
外部類名.this.欄位及方法
(平時用this都是指當前的,但是你這個指的是外部的欄位及方法)
例子:

public class TestInnerThis
{    
	public static void main(String args[]){
	    A a = new A();
    	A.B b = a.new B();
	    b.mb(333); 
    }
}

class A
{
	private int s = 111;

	public class B {
	    private int s = 222;
    	public void mb(int s) {
	        System.out.println(s); // 區域性變數s
	        System.out.println(this.s); // 內部類物件的屬性s
	        System.out.println(A.this.s); //  外層類物件屬性s
	    }
    }
}


//333
//222
//111

內部類的修飾符
內部類與類中的欄位、方法一樣是外部類的成員,它的前面也可以有 訪問控制符和其他修飾符。
訪問控制符:public,protected,預設(沒有修飾符)及private
注:外部類只能夠使用public修飾或者預設 (package)
final,abstract(表示他是不可繼承的)

static 修飾符
用static修飾內部類 表明該內部類實際是一種外部類 (物件.new就不用了)
因為它與外部類的例項無關
有人認為static的類是巢狀類(nested class),不是內部類inner class
static類在使用時:
1、例項化static類時,在 new前面不需要用物件例項變數; (因為他和例項無關)
2、static類中不能訪問其外部類的非static的欄位及方法,既只能夠訪問static成員。
3、static方法中不能訪問非static的域及方法,也不能夠不帶字首地new 一個非 static的內部類。
例子:

class TestInnerStatic
{
	public static void main(String[] args) 
	{
		A.B a_b = new A().new B();  // ok
		A a = new A();
		A.B ab =  a.new B();

		Outer.Inner oi = new Outer.Inner();
		//Outer.Inner oi2 = Outer.new Inner();  //!!!error   
		//Outer.Inner oi3 = new Outer().new Inner(); //!!! error
	}
}

class A    
{
	private int x;
	void m(){
		new B();
	}
	static void sm(){
		//new B();  // error!!!!
	}
	class B
	{
		B(){ x=5; }
	}
}

class Outer   
{
	static class Inner
	{
	}
}

區域性類

在一個方法中也可以定義類,這種類稱為”方法中的內部類”
或者叫區域性類(local class)
例子:

class TestInnerInMethod 
{
	public static void main(String[] args) 
	{
		Object obj = new Outer().makeTheInner(47);
		System.out.println("Hello World!" + obj.toString() );
	}
}

class Outer
{
	private int size = 5;
	public Object makeTheInner( int localVar )//普通的方法
	{
		final int finalLocalVar = 99;
		class Inner  {//這就是區域性類
			public String toString() {
				return ( " InnerSize: " + size + //可以訪問外部類的成員
					// " localVar: " + localVar +   
					// Error! 不能訪問內部普通的變數
					" finalLocalVar: " + finalLocalVar
					//可以訪問該方法的final區域性變數
				);
			}
		}
		return new Inner();
	}
}
//Hello World! InnerSize: 5 finalLocalVar: 99

使用區域性類
1、同區域性變數一樣,方法中的內部類
不能夠用 public,private,protected,static修飾,(不能修飾區域性變數同類也不能修飾區域性類) 但可以被final(不可繼承)或者abstract(抽象)修飾。
2、可以訪問其外部類的成員
3、不能夠訪問該方法的區域性變數,(它是隨時產生隨時消失的,進到方法裡就有,退出就消失了,是不可捉摸的不可訪問的)除非是final區域性變數

匿名類

匿名類( anonymous class)是一種特殊的內部類
它沒有類名,在定義類的同時就生成該物件的一個例項
“一次性使用”的類(所以沒必要給他個名字)
(一般他是擴充套件一個或者說要繼承一個類,實現一個介面)
例子:

class TestInnerAnonymous 
{
	public static void main(String[] args) 
	{
		Object obj = new Outer().makeTheInner(47);
		System.out.println("Hello World!" + obj.toString() );
	}
}

class Outer
{
	private int size = 5;
	public Object makeTheInner( int localVar )
	{
		final int finalLocalVar = 99;
		return new Object()  {
		//這個類沒取名字,但是總得要寫,所以他寫它父類的名字
			//或者它實現介面的名字
			public String toString() {
				return ( " InnerSize: " + size + 
					" finalLocalVar: " + finalLocalVar
				);
			}
		};//實際上是方法體裡面的類的簡寫,把new這個物件以及定義這個物件一起寫
	}
}
//Hello World! InnerSize: 5 finalLocalVar: 99

匿名 類的使用
1、不取名字,直接用其父類或介面的名字
也就是說,該類是父類的子類,或者實現了一個介面
編譯器生成 xxxxx$1之類的名字
2、類的定義的同時就建立例項,即類的定義前面有一個new
new 類名或介面名(){……}
不使用關鍵詞class,也不使用extends及implements。直接寫父類的名字就完事
3、在構造物件時使用父類構造方法
不能夠定義構造方法,因為它沒有名字
如果new物件時,要帶引數,則使用父類的構造方法

匿名 類的應用
用到介面的事件處理 (繼承一個類)
註冊一個事件偵聽器

示例 AutoScore.java 中 
 //SymAction lSymAction = new SymAction(); 
  //btnNew.addActionListener(lSymAction);
 btnNew.addActionListener(new ActionListener(){
  public void actionPerformed(ActionEvent event) 
   { 
    btnNew_ActionPerformed(event); 
   } 
 });

作為方法的引數
排序,給一個比較大小的介面

如 SortTest.java 
Arrays.<Book>sort( books, new Comparator<Book>(){ 
//需要一個介面去比較這兩本書
public int compare(Book b1, Book b2){ 
 return b1.getPrice()-b2.getPrice(); 
} 
});

6 . Lambda表示式

Lambda表示式是從Java8增加的新語法
Lambda表示式(λ expression)的基本寫法
(引數)->結果
如 (String s) -> s.length()
如 x->x*x
如 () -> { System.out.println(“aaa”); }
大體上相當於其他語言的“匿名函式”或“函式指標”
在Java中它實際上是“ 匿名類的一個例項
例子:

class LambdaRunnable  {
    public static void main(String argv[]) {
		Runnable doIt =  new Runnable(){
			public void run(){ 
				System.out.println("aaa");
			}
		};
		new Thread( doIt ).start();
	Runnable doIt2 = ()->System.out.println("bbb");//簡潔
		new Thread( doIt2 ).start();

		new Thread( ()->System.out.println("ccc") ).start();
		//作為執行緒的一個引數更簡潔
    }
}

可以看出
Lambda表示式是介面或者說是介面函式的簡寫
其基本寫法是引數->結果
這裡,引數是()或1個引數或 (多個引數)
結果是指 表示式 或 語句 或 {語句}

例子:

@FunctionalInterface
interface Fun { double fun( double x );}

public class LambdaIntegral
{
	public static void main(String[] args)
	{
		double d = Integral( new Fun(){
			public double fun(double x){ 
				return Math.sin(x); 
			}
		}, 0, Math.PI, 1e-5 );
                //簡寫
		d = Integral( x->Math.sin(x),
			0, Math.PI, 1e-5 );
		System.out.println( d );

		d = Integral( x->x*x, 0, 1, 1e-5 );
		System.out.println( d );

	}

	static double Integral(Fun f, double a, double b, double eps)// 積分計算
	{
		int n,k;
		double fa,fb,h,t1,p,s,x,t=0;

		fa=f.fun(a); 
		fb=f.fun(b);

		n=1; 
		h=b-a;
		t1=h*(fa+fb)/2.0;
		p=Double.MAX_VALUE;

		while (p>=eps)
		{ 
			s=0.0;
			for (k=0;k<=n-1;k++)
			{ 
				x=a+(k+0.5)*h;
				s=s+f.fun(x);
			}

			t=(t1+h*s)/2.0;
			p=Math.abs(t1-t);
			t1=t; 
			n=n+n; 
			h=h/2.0;
		}
		return t;
	}

}

Lambda大大地簡化了書寫
線上程的例子中
new Thread( ()->{ … } ).start();
在積分的例子中
d = Integral( x->Math.sin(x), 0, 1, EPS );
d = Integral( x->x*x, 0, 1, EPS );
d = Integral( x->1, 0, 1, EPS );
在按鈕事件處理中
btn.addActionListener( e->{ … } ) );

能寫成 Lambda的介面的條件
由於Lambda只能表示一個函式,所以
能寫成Lambda的介面要求包含且最多隻能有一個抽象函式
這樣的介面可以(但不強求)用註記
@FunctionalInterface 來表示。稱為函式式介面

@FunctionalInterface
interface Fun { double fun( double x );}

再舉一例:排序

Comparator<Person> compareAge = (p1, p2) -> p1.age-p2.age; 
Arrays.sort(people, compareAge);
Arrays.sort(people, (p1, p2) -> p1.age-p2.age); 
Arrays.sort(people, (p1, p2) -> (int)(p1.score-p2.score)); 
Arrays.sort(people, (p1, p2) -> p1.name.compareTo(p2.name)); 
Arrays.sort(people, (p1, p2) -> -p1.name.compareTo(p2.name));

Lambda表示式,不僅僅是簡寫了程式碼,
更重要的是:
它將程式碼也當成資料來處理
函數語言程式設計

7 . 裝箱、列舉、註解

基本型別的包裝類

基本型別的包裝類
它將基本型別(primitive type) 包裝成Object(引用型別)(因為基本型別沒法當物件用)
如int->Interger
共8類:
Boolean, Byte, Short, Character, Integer, Long, Float, Double
Integer I = new Integer(10);

裝箱與拆箱

裝箱(Boxing) Integer I = 10;
拆箱(Unboxing) int i = I;
實際譯為
Integer I= Integer.valueOf(10);
int i = I.intValue();
主要方便用於集合中,如: Object [] ary = { 1, “aaa”};

列舉

列舉(enum)是一種特殊的class型別
在簡單的情況下,用法與其他語言的enum相似
enum Light { Red, Yellow, Green };
Light light = Light.Red;
但實際上,它生成了 class Light extends java.lang.Enum

自定義列舉
可以在enum定義體中,新增欄位、方法、構造方法

  enum Direction 
   { 
   EAST("東",1), SOUTH("南",2), 
   WEST("西",3), NORTH("北",4); 
   private Direction(String desc, int num){ 
   this.desc=desc; this.num=num; 
   } 
   private String desc; 
   private int num; 
   public String getDesc(){ return desc; } 
   public int getNum(){ return num; } 
   } 

註解

註解(annotation)
又稱為註記、標記、標註、註釋(不同於comments)
是在各種語法要素上加上附加資訊,以供編譯器或其他程式使用
所有的註解都是 java.lang.annotation. Annotation 的子類

常用的註解,如
@Override 表示覆蓋父類的方法
@Deprecated 表示過時的方法
@SuppressWarnings 表示讓編譯器不產生警告
自定義註解,比較複雜
public @interface Author {
String name();
}

8 . 沒有指標的Java語言

引用(reference)實質就是指標(pointer)
但是它是受控的、安全的
比如
會檢查空指引
沒有指標運算 *(p+5)
不能訪問沒有引用到的記憶體
自動回收垃圾

C 語言指標在Java中的體現
(1)傳地址 ->物件
引用型別,引用本身就相當於指標
可以用來修改物件的屬性、呼叫物件的方法
基本型別:沒用對應的
如交換兩個整數
void swap(int x, int y){ int t=x; x=y; y=t; }
int a=8, b=9; swap(a.b);
一種變通的辦法,傳出一個有兩個分量x,y的物件
(2)指標運算 -> 陣列
*(p+5) 則可以用 args[5]
(3)函式指標 -> 介面、Lambda表示式
例:求積分,執行緒 、回撥函式、事件處理

(4)指向結點的指標-> 物件的引用
class Node {
Object data;
Node next;
}

(5)使用JNI
Java Native Interface(JNI)
它允許Java程式碼和其他語言寫的程式碼進行互動

java中相等還是不等

基本 型別的相等

數值型別:轉換後比較
浮點數,最好不直接用==
Double.NAN==Double.NAN 結果為false
請參見JDK的API文件
boolean型無法與int相比較

裝箱 物件是否相等

 Integer i = new Integer(10); 
  Integer j = new Integer(10); 
   System.out.println(i==j);  //false,因為物件是兩個

 Integer m = 10; 
  Integer n = 10; 
   System.out.println(m==n); //true,因為物件有快取

 Integer p = 200; 
  Integer q = 200; 
  System.out.println(p==q); //false,因為物件是兩個,不能超過-128到+127

注意快取
If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f, or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

列舉、引用物件是否相等
列舉型別
內部進行了惟一例項化,所以可以直接判斷
引用物件
是直接看兩個引用是否一樣
如果要判斷內容是否一樣,則要重寫equals方法
如果重寫equals方法,則最好重寫 hashCode()方法

String 物件的特殊性
String物件
判斷相等,一定不要用==,要用equals
但是字串常量( String literal)及字串常量會進行內部化(interned), 相同的字串常量是 = =的

例子:

String hello = "Hello", lo = "lo"; 
 System.out.println( hello == "Hello");  //true 
 System.out.println( Other.hello == hello ); //true
System.out.println( hello == ("Hel"+"lo") ); //true 
System.out.println( hello == ("Hel"+lo) ); //false
System.out.println( hello == new String("Hello")); //false 
System.out.println( hello == ("Hel"+lo).intern()); //true