JAVA 面向物件·基礎語法3
學過c++的同學肯定都知道PC暫存器,它其實就是指令地址,和組合語言中的IP暫存器差不多。
PC暫存器:儲存JAVA虛擬機器正在執行的位元組碼指令(.class檔案)的地址。
JAVA虛擬機器棧:儲存棧幀
堆:儲存GC(垃圾回收器)所管理的各種物件(一個程式中new出來的所有物件)。
方法區:儲存每一個類的結構資訊(比如構造方法和普通方法)。
this:是一個指向當前物件的引用。
*********this的本質是一個隱藏的 位置最靠前的方法引數。(隱藏引數)
*********只能在構造方法中使用this去引用其他構造方法。
包的本質其實就是資料夾。
在JAVA中,任何新建的類都會預設為繼承JAVA的基類—Object(它的包名為 Java.lang.object)。
子類的構造方法必須先呼叫父類的構造方法,在執行後面的程式碼。
方法簽名=方法名+引數型別
重寫與過載——Super
在JAVA 中,重寫需要注意以下幾個方面:
1.子類的返回型別一定要小於或等於父類的的返回型別
Super:訪問父類中定義的成員變數,可以呼叫父類中定義的方法(包括構造方法)。
******************JAVA 的硬性規定:子類的構造方法必須先呼叫父類的構造方法,再執行後面的程式碼。
Java中的覆蓋指的是例項方法。
封裝
1.成員變數private化,提供public的setter,getter
Static類
1.被static修飾:成員變數,類變數,靜態變數,靜態欄位
(在程式執行的過程中,只佔用一份固定的記憶體,一般儲存在方法區)
2.沒有被static修飾:例項變數
(每個例項內部都有一份記憶體,儲存在堆空間)
3.被static修飾的方法:類方法,靜態方法
—方法內部是不可以使用this關鍵字的
—可以直接訪問類變數,類方法
—不可以直接使用例項變數,例項方法
—可以通過例項,類訪問
4.沒有被static修飾的方法:例項方法(只能通過例項呼叫,不能通過類名呼叫)
*************(例項內部必有this)
初始化塊·靜態初始化塊
每建立一次例項時,初始化塊就會去執行一次,而靜態初始化塊與例項是不掛鉤的
*******************手動為例項變數賦上初始值:
1.在宣告中
2.在構造方法中
3.在初始化塊中
public class Person{
public int age;
//初始化塊
{
age = 10;
}
}
//編譯器會將初始化塊複製到每個構造方法的頭部(每建立一個例項物件,就會執行一次程式碼塊)
*******************手動為類變數賦上初始值:
1.在聲名中
2.在靜態初始化塊中
public class Person{
public static int count;
//靜態初始化塊
static{
count = 10;
}
}
//當一個類被第一次主動使用時,JVM會自動對類進行初始化
//當一個類被進行初始化時會執行靜態初始程式碼塊
以上程式碼執行結果如圖
一段程式碼中可以有多個初始化塊和靜態初始化塊,按照在原始碼中出現的順序被執行
單例模式
構造一個單例模式(餓漢式單例模式):
public class Rocket{
//私有的靜態的例項變數
private static Rocket instance = new Rocket();
//首先不能讓外界訪問你的構造方法,即構造方法私有化(就不能隨便去建立物件)
private Rocket() {
//其次 提供一個公共的靜態的方法,返回唯一的那個例項
public static Rocket getInstance(){
return instance;
}
}
}
構造一個單例模式(懶漢式單例模式):
public class Rocket{
private static Rocket instance = null;
private Rocket() {
if(instance==null){
instance = new Rocket();
}
return instance;
}
}
//有執行緒安全問題
Final類
子類物件的內部不僅有自己的成員變數,還有父類的所有成員變數。
被final修飾的類:不能被繼承。
被final修飾的方法:不能被重寫。
被final修飾的變數:只能進行1次賦值。
凡是static final修飾的變數都採用大寫字母,若有多個單詞中間需用下劃線連線。
被static final修飾的可以看作一個常量。//也叫做編譯時常量(compile—time constant)。
//巨集替換:
巨集
1.預處理:主要任務包括刪除註釋、插入被#include進來的檔案內容、定義和替換由#define 定義的符號以及確定程式碼部分內容是否根據條件編譯(#if )來進行編譯。
2.巨集定義:⑴巨集常量:用#define來定義一個符號常量
⑵巨集語句:定義一條或多條語句
⑶巨集函式:用巨集來定義函式,因為巨集定義也可以帶引數
⑷其他:#undef 是用來撤銷巨集定義的
靜態匯入
用一段程式碼實現靜態匯入:
import static com.mj.other.Test.*;
經典使用場景:圓周率PI的使用。
import static java.lang.Math.PI;
//import static java.lang.Math.*;
public class Main{
public static void main(String args[]){
//max(a,b);
System.out.println(" 2 * PI * 10");
}
}
但是過度使用靜態匯入,會產生歧義,讓讀者不明白這些到底是在哪個類中定義的。
巢狀類 Nested Class
巢狀類:定義在另一個類中的類,分為靜態巢狀類(被static修飾)和非靜態巢狀類(沒有static修飾)。//***非靜態巢狀類也叫內部類
外部類:在巢狀類外層的類。
頂級類:最外層的外部類。
內部類:跟例項變數,例項方法一樣,內部類與外部類的例項相關聯。
必須先建立外部類例項,在呼叫外部類例項去建立內部類例項。(記得導包)
如下圖 內部類不可以定義任何static成員。(除非是編譯時常量——即被static final修飾)
內部類可以直接訪問外部類中的所有成員,即使該成員被聲名為private。
外部類可以直接訪問內部類的任何成員變數和方法,即使該成員被聲名為private。
package www;
//內部類舉例(公司名字,公司解僱,員工名字,序號,顯示員工資訊)
public class Company {
private String name;
public Company(String name) {
this.name =name;
}
public void Fire(Employee e) {
System.out.println(name+"fire"+e.number);
}
public class Employee{
private int number;
public Employee(int number) {
this.number = number;
}
public void show() {
System.out.println(name+":"+number);
}
}
}
內部類細節:當外部類和內部類中都出現了相同的變數名,若想在內部類中呼叫外部類的那個變數,應表示為:
public class OuterClass{
private int x = 1;
class InnerClass{
private int x = 2;
System.out.println(OterClass.this.x);
}
}
靜態巢狀類 Static Nested Class
靜態巢狀類:在行為上相當於頂級類,只是定義的程式碼寫到了另一個類中。(可以理解為:借另一個類的的空間去放一下程式碼)
(與一般頂級類相比)靜態巢狀類的特殊許可權:可以直接訪問外部類裡除了例項變數和例項方法的其他成員,即使該成員被聲名為private。
*************若靜態巢狀類想要訪問外部類中的例項變數和例項方法,則必須先要new一個物件,通過物件呼叫例項變數和方法。
什麼情況下使用巢狀類?
1.如果類A只用在類C內部,可以考慮將類A巢狀在類C內部。
2.封裝性更好,程式包更加簡化
3.增強可讀性,維護性
4.如果類A經常訪問類C中的非公共成員,可以考慮將類A巢狀在類C內部。
5.也可以根據需要將類A隱藏起來,不對外暴露
6.如果類A要經常訪問類C中的非公共的例項成員,則設計成內部巢狀類,否則設計為靜態巢狀類。
7.如果只有例項A才能建立例項C,那麼可以把C作為A的一個內部類來使用
區域性類 Local Class
區域性類:定義在程式碼塊中的類(可定義在方法中,for迴圈中,if語句中)。
1.區域性類不能定義除了編譯時常量以外的任何static成員
2.區域性類只能訪問 final類或者 有效final類 (只進行一次賦值)的區域性變數
3.從Java 8 開始,凡是沒有進行第二次賦值的區域性變數就被稱為 有效final 。
4.區域性類可以直接訪問外部類中的所有成員,即使該成員被聲名為private。
5.區域性類只有定義在例項相關的程式碼塊中,才能直接訪問外部類中的例項成員(例項變數,方法)
抽象類
1.抽象方法
抽象方法:被abstract修飾的方法。
注意事項:1.只有方法宣告,沒有方法實現(引數列表後沒有大括號,而是分號)
2.不能是private許可權(因為定義抽象方法的目的是讓子類去實現)
3.*************只能是例項方法,不能是類方法
4.只能定義在抽象類和介面中
抽象類:
1.是為了給別人繼承的,所以不能使用final 修飾,也不能例項化,子類必須實現抽象父類中的所有的構造方法
**********************抽象類不能建立例項物件
2.抽象類其實可以理解為在原來的普通類基礎上增加了一個新的功能——構造抽象方法
介面 Interface
API:應用程式設計介面,提供給開發者一組呼叫的功能。
而Java中的介面,是一系列方法宣告的集合。(抽象方法)
implements 是類使用介面時的關鍵字。
介面可以定義抽象方法,常量,巢狀型別,預設方法,靜態方法
上述可以定義的內容都含有隱式public,所以在寫此段程式碼時可以省略不寫
介面中的常量可以省去static final ,介面中是不能出現成員變數的。
介面中不能自定義構造方法,不能例項化,不能定義(靜態)程式碼塊。
介面名稱可以在任何使用型別的地方使用,可以理解為介面也是一種型別。如果一個類實現的多個介面中有相同的抽象方法,那麼只需要實現此方法一次。
介面一般是放一些行為,能力的程式碼塊,而繼承是代表你屬於哪一類
在接口裡寫的方法都是抽象方法.
抽象類與介面的對比
抽象類
1.繼承:
class A extendS D{}//A是D
2.何時選擇抽象類?
⑴在緊密相關的類之間共享程式碼
⑵除public以外的訪問許可權
⑶需要建立例項變數和非final類的靜態變數
介面:
1.實現:
A implements D{ //A會D中的所有行為
}
2.何時選擇介面?
⑴不相關的類實現相同的方法
⑵只是定義行為,不關心是誰具體實現了這個行為
⑶想實現型別的多重繼承
介面的升級問題
預設方法
1.用default修飾預設方法,並且此方法能夠具體實現
2.預設方法只能是例項方法
3.重新宣告預設方法,將預設方法宣告為抽象方法(此類必須是抽象類)
4.如果父類的非抽象方法與介面的預設方法相同時,最終呼叫父類的方法
5.可以通過super關鍵字來呼叫介面的預設方法(……忘了……{{{(>_<)}}})
靜態方法
1.介面中的定義的靜態方法只能通過介面名呼叫,不能被繼承
使用介面的好處
客戶端→伺服器→業務解析→業務層→DAO層
1.業務層呼叫DAO層 最好通過介面去呼叫。
2.涉及到了一些架構相關的問題,具體內容會在第二、三部分進行講解。
多型
多型:具有多種形態。(開發中經常會用到介面,也可以叫做 面向介面程式設計)
體現:
1.父類(介面)型別指向子類物件
2.呼叫子類重寫的方法
虛方法呼叫:JVM會根據引用變數指向的具體物件來呼叫相應的方法。(相當於c++虛擬函式呼叫)
Instanceof : 可以通過instanceof來判斷某個型別是否屬於某種型別。(非常常用)
父類型別指向子類物件,這個順序是絕對不能顛倒的。
((Dog)Animal).wang();//強制轉換,將animal類轉化為Dog類
Dog dog1 = new Dog();
dog1.run(); //Dog-run
Animal dog2 = new Dog();
dog2.run(); //Animal-run
以上屬於類方法(靜態方法)呼叫,類方法是不看具體的例項物件的,只看物件屬於哪一類。
成員變數的訪問細節:
和類方法呼叫相似,在訪問成員變數之前,也只會看物件屬於所在的類,根據就近原則來進行下一步操作。
所以這樣會為程式帶來歧義,最好是將類中的public許可權變成private。
匿名類(常用)
當介面,抽象類的實現類,只在專案中出現過一次,可以考慮使用匿名類.
public class Main{
public static void main(String args[]){
Runnable person = new Runnable() { //匿名類
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("person-run");
}
};
person.run();
}
}
匿名類不能定義除編譯時常量以外的任何static成員.
匿名類不可以定義構造方法。
匿名類其實和區域性類十分相似,只能訪問final或者 有效final的區域性變數.
舉個例子(如下圖)
public interface Eatable {
String name() ; //相當於抽象方法,所以格式不能出錯
int enery() ;
}
public class Person {
public void eat(Eatable e) {
System.out.println("person-"+e.name()+"-"+e.enery());
}
}
public static void main(String args[]){
Person person = new Person();
person.eat(new Eatable() {
@Override
public String name() {
return "apple";
}
@Override
public int enery() {
return 50;
}
});
}
} //person-apple-50
或
public static void main(String args[]){
Person person = new Person();
Eatable beef = new Eatable() {
@Override
public String name() {
// TODO Auto-generated method stub
return "beef";
}
@Override
public int enery() {
// TODO Auto-generated method stub
return 500;
}
};
person.eat(beef);
}
} //person-beef-500
匿名類的常見用途
1.程式碼傳遞:可以節省很多空間,不需要再去建立一個新的類。
舉個例子,測試一段程式碼所用的時間
2.過濾器
3.回撥:callback,其實與程式碼傳遞相類似。
匿名類的排序
1.java的自動排序(升序)
Arrays.sort(a);
2.Java的倒序排序
Arrays.sort(a,new comparator<Integer>()){
@Override
public int compare(Integer o1, Integer o2){ //這裡不能直接寫int,不然就會報錯
return o2-o1;
}
});
原理如下:
3.列印陣列
System.out.println(Arrays.toString(a));//括號中填陣列名
Lambda Expression
1.函式式介面:只包含一個抽象方法的介面。
可以在上面加上一個註解,表明它是一個函式式介面。
@FunctionalInterface
public interface Testable{
void test(int b);
}
當匿名類實現的是函式式介面時,可以使用Lambda進行簡化。(相當於是用Lambda替換掉匿名類)
Lambda 的使用格式:
(引數列表) -> {
return XXXX;
}
Lambda的使用注意:
1.Lambda只能訪問final或者有效final的區域性變數。
2.Lambda沒有引入新的作用域
匿名類與Lambda的對比
1.在作用域方面是有所區別的
方法引用
1.引用類方法
類名::方法名
2.引用特定物件的例項方法
system.out.println 的本質就是引用特定物件的例項方法。
也可以簡化為 具體的物件::呼叫的方法名
3.引用特定型別的任意物件的例項方法
本來字串底層就有compare方法,呼叫它可以比較大小
忽略大小寫進行比較
也可以將上述程式碼簡化成下面這種形式
所以引用特定型別的任意物件的例項方法是 類名::方法名
4.引用構造方法
所以引用構造方法的格式為 類名::new;
5.引用陣列的構造方法
格式為 int[ ] :: new ;
6.引用當前類中定義的例項方法
格式為 this::方法名 ;
7.引用父類中定義的例項方法
格式為 super::方法名 ;
方法引用的最後總結: