基本類型(primitive type):其值直接存於變量中。「在這裏」java
引用型(reference type) 的變量除佔據必定的內存空間外,它所引用的對象實體(由new 建立)也要佔據必定空間。「在那裏」,能夠理解爲指針。git
MyDate m,n; m=new MyDate(); n=m;
m和n都指向同一個對象,二者均可以理解爲一個指針。經過m和n均可以操縱同一個對象。程序員
字段變量(Field):又稱成員變量(member variable),域變量,在類中;上圖的latitude
和longitude
都是字段變量。express
局部變量(Local variable):又稱本地變量(local variable),在方法中定義的變量或方法的參變量。上圖的args
和lima
以及latIn
和lonIn
都是局部變量。編程
Memory Model的創建流程以下所示數組
上圖首先在堆中新建SimpleLocation
類的對象,該對象擁有兩個變量latitude
和longtiude
-->>安全
上圖展現調用函數SimpleLocation(double latIn,double lonIn)
期間,內存中新建了臨時空間,如圖中constructor's scope所示。-->>app
如上所示,局部(臨時)變量laiIn
,lonIn
將值傳遞給SimpleLocation
類的對象latitude
和longtiude
後,則對象的創建成功。該函數調用結束後,局部變量latIn
和lonIn
以及this
會在內存中消失。該構造器(constructor) 先返回對象的地址(指針)給 lima
變量,而後消失。最終,lima將指向新對象。ide
生命週期:Field 是在隨着對象的建立,產生的在堆(The heap)中;Local variable
是隨着方法的調用期間按需生成相應的內存空間。
初始值:Filed 編譯器會自動賦初值,Local variable 須要顯式賦值,不然編譯沒法經過;函數
字段變量屬於類,能夠用public,private,static,final 修飾。
局部變量不可以被訪問控制符及static修飾
均可以被final修飾
調用對象方法時,要傳遞參數。
在傳遞參數時,Java 是值傳遞,即,是將表達式的值複製給形式參數。
對於引用型變量,傳遞的值是引用值,而不是複製對象實體,能夠改變對象的屬性。
返回基本類型。
返回引用類型。它就能夠存取對象實體。
Object getNewObject() { Object obj=new Object(); return obj; }
調用時:
Object p= GetNewObject();
是指一個程序中相同的名字表示不一樣的含義的狀況。
重載(overload) (多個同名的不一樣方法)。
p.sayHello(); p.sayHello(「Wang」);
覆蓋(override) (子類對父類方法進行覆蓋)
Polymorphism means that a variable of a supertype can refer to a subtype object.
動態綁定(dynamic binding) ----虛方法調用(virtual method invoking)
在調用方法時,程序會正確地調用子類對象的方法。
多態優勢:大大提升了程序的抽象程度和簡潔性。
public class DynamicBindingDemo { public static void main(String[] args){ m(new GraduateStudent());//傳遞的是子類對象,編譯經過 m(new Student());//同上! m(new Person());//同上! m(new Object()); } public static void m(Object x){ System.out.println(x.toString()); } } class GraduateStudent extends Student{ } class Student extends Person{ public String toString(){ return "student"; } } class Person{ public String toString(){ return "Person"; } }
運行結果:
student student Person java.lang.Object@60e53b93
從以上測試代碼看來,雖然 m(Object x)
定義的形參是Object
類的,但容許實際參數傳遞子類對象:m(new GraduateStudent());
,編譯經過。
另外:在類繼承關係鏈中,對同一個方法可能對應有多個實現,但在運行時由JVM自動搜索綁定哪一個實現。動態綁定流程:從最近的類(最低,最具體)找,直到 Object
類(最高,最抽象)。只要找到了實現方法就中止。好比如今GraduateStudent類中找,沒找到,就在Student
類中找,結果找到了,就中止該搜索。
這裏須要注意區別構造函數的執行流程。
上溯造型(upcasting) :是把派生類型(subclass)看成基本類型(upclass)處理.
It is always possible to cast an instance of a subclass to a variable of a superclass(knowns as upcasting) .
Person p = new Student(); void fun(Person p ){...}
在這裏,雖然P的聲明類型是Person,但它的實際類型是Student,Student是Person的子類。在被fun(Person p)調用時,仍能夠casting。這也是動態綁定的範疇。
m(new Student()); //等價於 Object o = new Student(); m(o);
如今假設,要將o分配給一個Student類的變量,該如何作?
方法1
Student b = o;//編譯報錯,由於在編譯器看來,o是一個Object類的變量,並不必定是個Student類的變量
方法2
Student b = (Student)o;//這實際上是downcasting了,把一個Object類的對象向下映射爲Student類的,編譯經過。可是前提是得確保o變量的真實類型是Student類的,不然會拋出ClasCastException錯誤。
子類重載了父類方法時,運行時系統根據調用該方法的真實類型(actual type)來決定選擇哪一個方法調用
全部的非final方法都會自動地進行動態綁定
instanceof
是 java 的關鍵字。
用變量 instanceof
來判斷一個對象的真實類型(actual type)。
結果是boolean 值
Object myObject = new Circle(); if(myObject instanceof Circle){ System.out.println("The circle diameter is " + ((Circle)myObject).getDiameter()); }
在Object myObject = new Circle();
新增一個Object類的myObject變量,可是,myObject
的真實類型(actual type)是Circle()
類型,因此myObject instanceof Circle
返回True
。
(Circle)myObject
?編譯時,myObject
的聲明類型是Object
,便於編譯器決定採用哪一個方法,好比myObject.getDiameter()
將會引發編譯錯誤,因此要類型轉換 (Circle)myObject
。
myObject
設定爲 Object
對象?將一個變量聲明爲父類類型,是爲了更好地抽象編程(generic programming),這樣myObject
可以接受任何子類對象
Java中,普通的方法是虛方法
但static,private方法不是虛方法調用
static的方法(從名字看,是靜態,與動態綁定相對),以聲明的類型爲準,與實例類型無關
private方法子類看不見,也不會被虛化
final方法子類不能覆蓋,不存在虛化問題
public class InvokeStaticMethod { /*調用靜態方法來 */ public static void main(String[] args){ Circle c = new Circle(); Shape s = new Shape(); Shape d = new Circle(); doSomething(c); doSomething(s); doSomething(d); doSomethingVer2(c); doSomethingVer2((Circle)d);//必須強制類型轉換爲Circle() } static void doSomething(Shape s){//注意該方法聲明爲靜態,非虛調用,參量的聲明類型是Shape,因此只能匹配到Shape類型的參數。 s.draw(); } static void doSomethingVer2(Circle s){//注意該方法聲明爲靜態,形參只能匹配到Circle類型的參數; s.draw(); } } class Shape { static void draw(){ System.out.println("draw shape"); } } class Circle extends Shape{ static void draw(){ System.out.println("draw circle"); } }
輸出結果:
draw shape draw shape draw shape draw circle draw circle
對象都有構造方法
若是沒有,編譯器加一個default構造方法
類的成員變量和方法是能夠繼承的,可是類的構造器是不能繼承的。只能經過調用,又分爲顯式調用和隱式調用。
this調用本類的其餘構造方法。
super調用直接父類的構造方法
this或super要放在第一條語句,且只可以有一條
若是既不是調用this,也不是調用super方法,則編譯器會自動加上super()
,也就是調用直接父類的無參方法。
能夠看出,原則上必須令全部父類的構造方法都獲得調用,不然對象的構建就不成功。
In any case, constructing an instance of a class invokes the constructors of all the superclasses along the inheritance chain. This is called constructor chaining.
--Introduction to Java Programming
調用本類或父類的構造方法,直至最高一層(Object)
按照聲明順序執行字段的初始化賦值
執行構造函數中的各語句。
簡單地說: 先父類構造,再本類成員賦值,最後執行構造方法中的語句。
p = new NoConstructorTest(){{ a="A"; b="B"; }};
這樣能夠針對沒有相應構造函數但又要賦值。
注意雙括號。
public class NoConstructorTest { String a; String b; //no constructors public static void main(String[] args){ NoConstructorTest p = new NoConstructorTest(){{ a="A"; b="B"; }}; System.out.println("the instance can be given value without defining constructor.\na is:\n"+p.a+"\nb is: \n"+p.b); } }
輸出結果
the instance can be given value without defining constructor. a is: A b is: B
可見,該對象的初始化是成功的。
內部類( inner class )是在所在類中的特殊成員
匿名類( anonymous class)是一種特殊的內部類,它沒有類名。
內部類是所在類的成員。
編譯器生成xxxx$xxxx
這樣的class文件
內部類不可以與外部類同名
優勢是:在某些程序中,使用內部類來簡化程序(好比減小source file的個數);內部類能夠引用所在外部類的屬性和方法。
在封裝它的類的內部使用內部類,與普通類的使用方式相同。
在其餘地方使用類名前要冠之外部類的名字。在用new
建立內部類實例時,也要在 new
前面冠以對象變量,能夠用 外部對象名.new 內部類名(參數)
。
內部類中能夠直接訪問外部類的字段及方法。即便private也能夠,由於內部類本質上也是一個類成員。
若是內部類中有與外部類同名的字段或方法,則能夠用 外部類名.this.字段及方法
。
class A { private int s=3; public class B{ private int s=2; public void mb(int s){ System.out.println(s); System.out.println(this.s); System.out.println(A.this.s); } } }
內部類與類中的字段、方法同樣是外部類的成員,它的前面也能夠有 訪問控制符和其餘修飾符。
訪問控制符:public
, protected
,默認及private
。 注:外部類只可以使用public修飾或者默認
final
, abstract
。
static
修飾符用static
修飾的內部類,其實是一種外部類。
由於它與外部類的實例無關
static
類的使用實例化static
類時,在 new
前面不須要用對象實例變量;
static
類中不能訪問其外部類的非static的字段及方法,既只可以訪問static
成員。
static
方法中不能訪問非static
的域及方法,也不可以不帶前綴地new
一個非
static的內部類。
在一個方法中也能夠定義類,這種類稱爲」方法中的內部類」 ,或者叫局部類(local class)
同局部變量同樣,方法中的內部類,不可以被public
, private
,protected
, static
修飾, 但能夠被 final
或者 abstract
修飾。
能夠訪問其外部類的成員
不可以訪問該方法的局部變量,除非是final
局部變量
匿名類( anonymous class)是一種特殊的內部類
它沒有類名,在定義類的同時就生成該對象的一個實例
「一次性使用」的類
不取名字,直接用其父類或接口的名字。
也就是說,該類是父類的子類,或者實現了一個接口
編譯器生成 xxxxx$1
之類的名字,其中1
表示第一個匿名類。
類的定義的同時就建立實例,即類的定義前面有一個new
new 類名或接口名(){......}
不使用關鍵詞class,也不使用extends
及implements
。
在構造對象時使用父類構造方法
不可以定義構造方法,由於它沒有名字
若是new
對象時,要帶參數,則使用父類的構造方法
import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.text.Text; import javafx.stage.Stage; public class AnonymousHandlerDemo extends Application { @Override // Override the start method in the Application class public void start(Stage primaryStage) { Text text = new Text(40, 40, "Programming is fun"); Pane pane = new Pane(text); // Hold four buttons in an HBox Button btUp = new Button("Up"); Button btDown = new Button("Down"); Button btLeft = new Button("Left"); HBox hBox = new HBox(btUp, btDown , btLeft); hBox.setSpacing(10); hBox.setAlignment(Pos.CENTER); BorderPane borderPane = new BorderPane(pane); borderPane.setBottom(hBox); //在方法中定義的內部類稱爲 局部類(local class) //能夠引用方法中的變量, 好比text class EnableEventHandler implements EventHandler<ActionEvent>{ public void handle(ActionEvent e) { text.setY(text.getY() > 10 ? text.getY() - 5 : 10); } } //注意:必須先定義了類,才能使用. //不然編譯器會找不到這個類而報錯. btUp.setOnAction( new EnableEventHandler()); //對比上面,非匿名類 //下面開始使用匿名類 btDown.setOnAction(new EventHandler<ActionEvent>() { @Override // Override the handle method public void handle(ActionEvent e) { text.setY(text.getY() < pane.getHeight() ? text.getY() + 5 : pane.getHeight()); } }); //可見,使用匿名類的好處是:精簡了先定義類,後使用類這一過程. //匿名類的使用是「一次性」的。 //進一步簡化,採用Lambda表達式 btLeft.setOnAction(e -> { //但得遵循SAM原則 text.setX(text.getX() > 0 ? text.getX() - 5 : 0); }); // Create a scene and place it in the stage Scene scene = new Scene(borderPane, 400, 350); primaryStage.setTitle("AnonymousHandlerDemo"); // Set title primaryStage.setScene(scene); // Place the scene in the stage primaryStage.show(); // Display the stage } /** * The main method is only needed for the IDE with limited * JavaFX support. Not needed for running from the command line. */ public static void main(String[] args) { launch(args); } }
運行截圖
Lambda表達式是從Java8增長的新語法
Lambda表達式(λ expression)的基本寫法
(參數)->結果
好比:(String s) -> s.length()
將會返回s
的長度
x->x*x
將會返回x*x
的運算結果,參數的類型都省略了。
大致上至關於其餘語言的「匿名函數」或「函數指針」 。
在Java中它其實是「 匿名類的一個實例」,便是定義後立刻使用,更加簡潔和高效。
A dolt = new A(){ public void run(){ System.out.println("OK"); } }
寫成Lamdba表達式:
() -> { System.out.println(「OK」); }
A dolt = new A(){ public double run(double x){ return Math.sin(x); } }
以上寫成Lambda表達式:
(x) -> Math.sin(x);
btn.addActionListener( e -> ... } ) );//編譯器可以自動識別e爲ActionEvent類。
以上三個例子說明:Lambda表達式是接口或者說是接口函數的簡寫,基本寫法是 (參數)->結果
,這裏,參數是()
或(1個參數)
或 (多個參數)
,結果是指 表達式
或 語句
或 {語句}
因爲Lambda只能表示一個函數,因此
能寫成Lambda的接口要求包含且最多隻能有一個抽象函數
這樣的接口能夠用註記(但不強求)@FunctionalInterface
來表示。稱爲函數式接口,用於對編譯器的提示:
@FunctionalInterface interface A { double A( double x );}
Comparator<Person> compareAge = (p1, p2) -> p1.age-p2.age; Arrays.sort(people, compareAge);
Lambda表達式,不只僅是簡寫了代碼, 更重要的是:它將代碼也當成數據來處理。
從JDK1.5起,增長了一些新的語法
大部分是編譯器自動翻譯的,稱爲Complier sugar。
基本類型並非對象,但經過使用Java API的包裝類可以把基本類型包裝成對象,從而方便某些方法的調用需求。
它將基本類型(primitive type) 包裝成Object(引用類型)
Java的八種包裝類(wrapper class)以下:Boolean
, Byte
, Short
, Character
, Integer
, Long
, Float
, Double
注:包裝類的名稱首字母亦是大寫。
裝箱(Boxing)
Integer I = new Integer(10);
或簡寫爲
Integer I = 10;//編譯器自動將基本類型包裝爲`Integer`對象
拆箱(Unboxing)
int i = I;
上述過程,被編譯器譯爲:
Integer I= Integer.valueOf(10); int i = I.intValue();
枚舉(enum)是一種特殊的class類型
在簡單的狀況下,用法與其餘語言的enum類似
enum Light { Red, Yellow, Green }; Light light = Light.Red;
但實際上,編譯後,它生成了 class Light extendsjava.lang.Enum
,因此能夠在enum
定義體中,添加字段、方法、構造方法,能夠當作通常的class,更加靈活。
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; } }
又稱爲註記、標記、標註、註釋(不一樣於comments)
是在各類語法要素上加上附加信息,以供編譯器或其餘程序使用
全部的註解都是 java.lang.annotation.Annotation
的子類
經常使用的註解,如
@Override
@Deprecated 表示過期的方法
@SuppressWarnings 表示讓編譯器不產生警告
自定義註解,這個不多見啦。
public @interface Author { String name(); }
引用(reference)實質就是指針(pointer),但在Java中,引用是安全的指針。
Java標榜其中對C/C++一個很大的改進就是:Java對程序員屏蔽了變量地址的概念,減小指針誤用。
好比:
會檢查空指引
沒有指針運算 *(p+5)
不能訪問沒有引用到的內存
自動回收垃圾
Java引用類型(reference type),引用自己就至關於指針,能夠用來修改對象的屬性、調用對象的方法。
如交換兩個整數,在C語言中:
void swap(int x, int y){ int t=x; x=y; y=t; } int a=8, b=9; swap(&a,&b);
在Java中,沒法實現該交換。但可使用一種變通的辦法,傳出一個有兩個份量x,y的對象。
變通辦法,但顯得很笨:
class Test{ public static void swap2(final int [] arr, final int pos1, final int pos2){ final int temp = arr[pos1]; arr[pos1] = arr[pos2]; arr[pos2] = temp; } public static void main(String [] args){ int [] a ={1,2}; swap2(a,0,1); System.out.println(a[0]+" "+a[1]); } }
運行結果:
2 1
在C中的指針*(p+5)
,在Java中則能夠用 args[5]
例如上述:求積分、線程、回調函數、事件處理。
在鏈表中有該類:
class Node { Object data; Node next; }
next就是一個指向對象的指針。
Java Native Interface(JNI) ,它容許Java代碼和其餘語言寫的代碼進行交互。