摘要:在那里,可以理解為指針。局部變量不能夠被訪問控制符及修飾都可以被修飾變量的傳遞與語言相似調用對象方法時要傳遞參數。內部類內部類是所在類的成員。大體上相當于其他語言的匿名函數或函數指針。
1. 變量及其傳遞 基本類型變量(primitive type)和引用類型變量(reference type)
基本類型(primitive type):其值直接存于變量中。“在這里”
引用型(reference type) 的變量除占據一定的內存空間外,它所引用的對象實體(由new 創建)也要占據一定空間。“在那里”,可以理解為指針。
代碼MyDate m,n; m=new MyDate(); n=m;
m和n都指向同一個對象,兩者都可以理解為一個指針。通過m和n都可以操縱同一個對象。
字段變量(Field)與局部變量(Local variable)區別 從位置看字段變量(Field):又稱成員變量(member variable),域變量,在類中;上圖的latitude和longitude都是字段變量。
局部變量(Local variable):又稱本地變量(local variable),在方法中定義的變量或方法的參變量。上圖的args和lima以及latIn和lonIn都是局部變量。
從內存角度看Memory Model的建立流程如下所示
上圖首先在堆中新建SimpleLocation類的對象,該對象擁有兩個變量latitude和longtiude-->>
上圖展示調用函數SimpleLocation(double latIn,double lonIn)期間,內存中新建了臨時空間,如圖中constructor"s scope所示。-->>
如上所示,局部(臨時)變量laiIn,lonIn將值傳遞給SimpleLocation類的對象latitude和longtiude后,則對象的建立成功。該函數調用結束后,局部變量latIn和lonIn以及this會在內存中消失。該構造器(constructor) 先返回對象的地址(指針)給 lima 變量,然后消失。最終,lima將指向新對象。
生命周期:Field 是在隨著對象的創建,產生的在堆(The heap)中;Local variable 是隨著方法的調用期間按需生成相應的內存空間。
初始值:Filed 編譯器會自動賦初值,Local variable 需要顯式賦值,否則編譯無法通過;
字段變量屬于類,可以用public,private,static,final 修飾。
局部變量不能夠被訪問控制符及static修飾
都可以被final修飾
變量的傳遞(與c語言相似)調用對象方法時,要傳遞參數。
在傳遞參數時,Java 是值傳遞,即,是將表達式的值復制給形式參數。
對于引用型變量,傳遞的值是引用值,而不是復制對象實體,可以改變對象的屬性。
方法的返回返回基本類型。
返回引用類型。它就可以存取對象實體。
代碼Object getNewObject() { Object obj=new Object(); return obj; }
調用時:
Object p= GetNewObject();2. 多態和虛方法調用 多態(Polymorphism)
是指一個程序中相同的名字表示不同的含義的情況。
1. 編譯時多態重載(overload) (多個同名的不同方法)。
代碼p.sayHello(); p.sayHello(“Wang”);2. 運行時多態(更重要)
覆蓋(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。
另外,為什么要進行對myObject進行downcasting,即 (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 circle3. 對象的構造和初始化 構造方法(constructor)
對象都有構造方法
如果沒有,編譯器加一個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. a is: "+p.a+" b is: "+p.b); } }
輸出結果
the instance can be given value without defining constructor. a is: A b is: B
可見,該對象的初始化是成功的。
4. 對象清除與垃圾回收 遲點準備兩個例子作為解析 5. 內部類與匿名類內部類( inner class )是在所在類中的特殊成員
匿名類( anonymous class)是一種特殊的內部類,它沒有類名。
內部類(inner 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)匿名類( 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{ public void handle(ActionEvent e) { text.setY(text.getY() > 10 ? text.getY() - 5 : 10); } } //注意:必須先定義了類,才能使用. //否則編譯器會找不到這個類而報錯. btUp.setOnAction( new EnableEventHandler()); //對比上面,非匿名類 //下面開始使用匿名類 btDown.setOnAction(new EventHandler () { @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只能表示一個函數,所以
能寫成Lambda的接口要求包含且最多只能有一個抽象函數
這樣的接口可以用注記(但不強求)@FunctionalInterface來表示。稱為函數式接口,用于對編譯器的提示:
@FunctionalInterface interface A { double A( double x );}代碼
ComparatorcompareAge = (p1, p2) -> p1.age-p2.age; Arrays.sort(people, compareAge);
Lambda表達式,不僅僅是簡寫了代碼, 更重要的是:它將代碼也當成數據來處理。
7. 裝箱、枚舉、注解從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; } }注解(annotation)
又稱為注記、標記、標注、注釋(不同于comments)
是在各種語法要素上加上附加信息,以供編譯器或其他程序使用
所有的注解都是 java.lang.annotation.Annotation 的子類
常用的注解,如
@Override
@Deprecated 表示過時的方法
@SuppressWarnings 表示讓編譯器不產生警告
自定義注解,這個很少見啦。
public @interface Author { String name(); }8. 沒有指針的 JAVA 語言
引用(reference)實質就是指針(pointer),但在Java中,引用是安全的指針。
Java標榜其中對C/C++一個很大的改進就是:Java對程序員屏蔽了變量地址的概念,減少指針誤用。
比如:
會檢查空指引
沒有指針運算 *(p+5)
不能訪問沒有引用到的內存
自動回收垃圾
C語言指針在Java中的體現 1. 傳地址 -> 對象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 12. 指針運算 -> 數組
在C中的指針*(p+5) ,在Java中則可以用 args[5]
3. 函數指針 -> 接口、Lambda表達式例如上述:求積分、線程、回調函數、事件處理。
4. 指向結點的指針 -> 對象的引用在鏈表中有該類:
class Node { Object data; Node next; }
next就是一個指向對象的指針。
5. 使用JNIJava Native Interface(JNI) ,它允許Java代碼和其他語言寫的代碼進行交互。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65865.html
摘要:由虛擬機加載的類,被加載到虛擬機內存中之后,虛擬機會讀取并執行它里面存在的字節碼指令。虛擬機中執行字節碼指令的部分叫做執行引擎。 什么是Java虛擬機? 作為一個Java程序員,我們每天都在寫Java代碼,我們寫的代碼都是在一個叫做Java虛擬機的東西上執行的。但是如果要問什么是虛擬機,恐怕很多人就會模棱兩可了。在本文中,我會寫下我對虛擬機的理解。因為能力所限,可能有些地方描述的不夠欠...
在社會化分工、軟件行業細分專業化的趨勢下,會真的參與到底層系統實現的人肯定是越來越少(比例上說)。真的會參與到JVM實現的人肯定是少數。 但如果您對JVM是如何實現的有興趣、充滿好奇,卻苦于沒有足夠系統的知識去深入,那么可以參考RednaxelaFX整理的這個書單。 showImg(http://segmentfault.com/img/bVbGzn); 本豆列的脈絡是: 1. JV...
摘要:對象創建與訪問指令雖然類實例和數組都是對象,但虛擬機對類實例和數組的創建和操作使用了不同的字節碼指令。異常處理指令在虛擬機中,處理異常語句不是由字節碼指令來實現的,而是采用異常表的方式。 《深入理解Java虛擬機:JVM高級特性與最佳實踐(第二版》讀書筆記與常見面試題總結 本節常見面試題(推薦帶著問題閱讀,問題答案在文中都有提到): 簡單介紹一下Class類文件結構(常量池主要存放的是...
摘要:虛擬機發展史注本文大部分摘自深入理解虛擬機第二版作為一名開發人員,不能局限于語言規范,更需要對虛擬機規范有所了解。虛擬機規范有多種實現,其中是和中所帶的虛擬機,也是目前使用范圍最廣的虛擬機。世界第一款商用虛擬機。號稱世界上最快的虛擬機。 Java虛擬機發展史 注:本文大部分摘自《深入理解Java虛擬機(第二版)》 作為一名Java開發人員,不能局限于Java語言規范,更需要對Java虛...
閱讀 2416·2021-11-11 11:01
閱讀 3287·2021-10-11 10:57
閱讀 2645·2021-09-30 09:46
閱讀 3492·2021-07-26 23:38
閱讀 1564·2019-08-29 12:22
閱讀 650·2019-08-29 11:28
閱讀 2352·2019-08-26 14:04
閱讀 3050·2019-08-23 18:34