面向對象 面向對象:以類的方式組織代碼,以對象組織數據 特性: 封裝 繼承 多態 類:抽象概念 對象:具體事物 面向對象是java學習的重中之重,畢竟java就是一個面向對象的語言~ 類 = 屬性+方法 面向對象的概念適合複雜系統、多人協作 從巨集觀上來說,java是面向對象的,但在微觀上是面向過程的 ...
面向對象
面向對象:以類的方式組織代碼,以對象組織數據
特性:
- 封裝
- 繼承
- 多態
類:抽象概念
對象:具體事物
- 面向對象是java學習的重中之重,畢竟java就是一個面向對象的語言~
- 類 = 屬性+方法
- 面向對象的概念適合複雜系統、多人協作
- 從巨集觀上來說,java是面向對象的,但在微觀上是面向過程的
創建
對象的創建
使用new實例化一個對象,如
Student student = new Student();//實例化對象
new時:
- 記憶體空間的分配
- 屬性的初始化
- 構造器的調用
- 返回一個對象的引用(指針)
構造器
構造器在實例化時首先被自動調用,用於初始化參數。
new的本質是調用了構造器,返回一個對象
-
名字和類名相同
-
沒有返回類型(不能寫!)
-
可以傳參
-
this是一個指針,指向這個對象本身
public class Person { String name; public Person(){ //構造器 this.name = "小明"; } }
封裝--訪問控制
“高耦合,低內聚”,內部數據操作細節自己完成,不由外部干涉, 暴露少部分方法給外部使用。
封裝:禁止訪問對象的實際表示,而應該通過介面來訪問。
修飾詞:
- public:可以由外部調用,公開使用
- private:不可由外部調用
- protected:由本包內或不同包的子類調用
繼承
使用extend關鍵字,表示子類是父類的擴展
public class Student extends Person{
Student(String name){
this.name = name;
}
}
- 子類擁有父類的全部public/protected方法和屬性
- 且子類可以對所有方法和屬性重寫
- private屬性無法被繼承
- java中所有類都是object類的子類
構造器
使用super可以訪問到父類,構造器中super.generator()可以調用父類的構造器。
public class Person {
String name;
public Person(String name){
//構造器
this.name = name;
}
}
public class Student extends Person{
Student(String name){
super(name);
}
}
public class Demo2 {
public static void main(String[] args){
Student s = new Student("小明");
System.out.println(s.name);
}
}
輸出“小明”。
如果在子類中不指定調用super,會自動調用
public class Person {
String name;
public Person() {
//構造器
System.out.println("父類Person無參數構造器執行");
}
}
public class Student extends Person{
Student(){
System.out.println("子類Student無參數構造器執行");
}
}
在new Student時輸出:
若將子類構造器改為有參,仍然會首先調用父類的無參構造器
大致邏輯如下:
註:
- 調用構造器時,需要將父類構造器調用語句放在子類構造器的第一句
- 父類沒寫無參,預設有一個空的構造器函數
- 如果寫了一個有參構造器,那麼父類就沒有無參構造器了,子類不能自動調用構造器,即子類中必須顯式調用有參構造器了。
方法重寫
- Person類:
public static void test(){
System.out.println("Person Test");
}
Student類:
public static void test(){
System.out.println("Student Test");
}
調用:
public static void main(String[] args){
Student s = new Student("小明");
s.test();
}
結果:
- 但是,如果修改main
public static void main(String[] args){
Person s = new Student("小明");
s.test();
}
會導致輸出:
這可以說明
- 調用的方法根據聲明的類型確定
-
以上結論來自於靜態方法
如果全部改為非靜態,即將test改為無static修飾
如
@Override public void test(){ System.out.println("Student Test"); }
註意點:
- override 的前提是繼承
- 方法名相同
- 參數列表相同(不是重載)
- 修飾符的範圍只能擴大不能縮小 public>protected>default>private
- 異常的範圍可以被縮小但不能擴大,如:ClassNotFoundException->Exception
多態
定義
同一方法根據對象的不同採用不同的行為
引用類型
一個對象的實際類型是確定的,但引用類型並不一致
如
Student s = new Student();
Person s1 = new Student();
Object s2 = new Student();
實際類型都是Student,而引用類型可以是其任意父類
對於這樣的對象s1/s2,如果沒有static修飾,調用一個方法時
-
若子類父類都有該方法,且子類未重寫:調用父類的方法
-
若都有,但子類重寫了:調用子類的方法
-
若只有子類有,則無法調用(需要強制類型轉換修改引用類型)
如在Student寫一個新的eat方法:
即能調用的方法取決於其引用類型而不是實際類型
方法修飾
-
static 屬於類,不屬於對象,不可重寫
-
final 無法修改,不可重寫
-
private 只屬於父類,無法重寫
instanceof操作符
語法:
obj instanceof class
System.out.println(s instanceof Student);//true
System.out.println(s1 instanceof Student);//true
System.out.println(s1 instanceof Object);//true
System.out.println(s2 instanceof Student);//true
System.out.println(s2 instanceof Teacher);//false
如果對象的類是class或class的子類,則為True
在編譯狀態中,class可以是object對象的父類,自身類,子類。在這三種情況下Java編譯時不會報錯。(需要在同一條繼承鏈上)
在運行轉態中,class可以是object對象的父類,自身類,不能是子類。在前兩種情況下result的結果為true,最後一種為false。但是class為子類時編譯不會報錯。運行結果為false。
編譯的時候查看其引用類型判斷是否報錯。
運行的時候查看其實際類型判斷是否為true。
強制類型轉換
優先順序:父類>子類。
子類轉父類自動轉換。
父類轉子類需要強制轉換。
轉父類後部分方法可能無法再調用。
static
-
static修飾(靜態)的從屬於類,普通的從屬於對象
-
靜態方法不能調用非靜態成員
變數
靜態變數(類變數)
- 有static修飾的變數為靜態變數,在該類的記憶體中只能存在一個,可以使用類名.變數名進行訪問
- 內部任何方法都可以直接訪問靜態變數(可以不使用類名.靜態成員進行訪問)
- 類外部可以使用類名訪問類中靜態變數
實例變數
-
無static修飾的變數
-
每創建一個實例就會生成一個新的記憶體空間
-
類內部只有非靜態方法可以訪問實例變數
-
靜態方法或其他類中只能通過實例對象訪問
靜態變數的作用
- 靜態變數被所有實例共用,可以作為實例對象間的共用數據
- 如果所有實例都有一個相同的常量屬性,可以定義為static以節省空間
方法
靜態方法(類方法)
- 靜態方法不需要通過任何實例就可以被調用,
- 不能使用this/super關鍵字
- 也不能直接訪問類內部的實例變數和實力方法
- 可以直接調用類內部的靜態變數和靜態方法
實例方法
- 通過實例對象訪問
代碼塊
靜態代碼塊
- static{}代碼塊
- 用於初始化類(一次性的),為類的靜態變數賦初值
- 類似於一個方法,但不在方法體中
- 可以在類的任意位置,可以有任意多個
- java虛擬機在載入類的時候執行靜態代碼塊
- 多個代碼塊按順序運行
- 靜態代碼塊和靜態方法類似,不能訪問非靜態成員
非靜態代碼塊
- {}
- 創建對象時自動執行,不創建對象不執行
- 代碼域中的變數都是局部的,只在內部使用
抽象
abstract修飾
抽象類
- abstract修飾的方法
- 抽象類中可以有抽象方法和具體方法
- 抽象類無法實例化
抽象方法
- 抽象方法只聲明沒有方法體
- 抽象方法必須在抽象類中
- 子類重寫父類時,必須重寫父類的所有抽象方法
- 不能用private修飾,因為private阻止重寫
實例
public abstract class Shape {
public int width; // 幾何圖形的長
public int height; // 幾何圖形的寬
public Shape(int width, int height) {
this.width = width;
this.height = height;
}
public abstract double area(); // 定義抽象方法,計算面積
}
public class Square extends Shape {
public Square(int width, int height) {
super(width, height);
}
// 重寫父類中的抽象方法,實現計算正方形面積的功能
@Override
public double area() {
return width * height;
}
}
public class Triangle extends Shape {
public Triangle(int width, int height) {
super(width, height);
}
// 重寫父類中的抽象方法,實現計算三角形面積的功能
@Override
public double area() {
return 0.5 * width * height;
}
}
介面
- 普通類:只有具體實現
- 抽象類:有具體實現和規範(抽象方法)
- 介面:只有規範,沒有具體實現 專業的約束,實現約束和實現的分離,比抽象類更加抽象
介面定義
[public] interface interface_name [extends interface1_name[, interface2_name,…]] {
// 介面體,其中可以包含定義常量和聲明方法
[public] [static] [final] type constant_name = value; // 定義常量
[public] [abstract] returnType method_name(parameter_list); // 聲明方法
}
- 介面只能繼承介面
- public定義的介面可以被任何類使用,而沒有public只能被包內使用
- 介面中的變數隱式聲明為public static final(可以不寫),即為常量,所以全部必須初始化
- 介面中的方法隱式聲明為 public abstract
介面實現
-
一個類可以實現一個或者多個介面
-
實現使用implements關鍵字
<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name…]] { // 主體 }
-
與繼承類似,可以獲得所有的常量和方法
-
implements在extend後
-
類實現介面後必須重寫所有抽象方法
例
public interface IMath {
public int sum(); // 完成兩個數的相加
public int maxNum(int a,int b); // 獲取較大的數
}
public class MathClass implements IMath {
private int num1; // 第 1 個操作數
private int num2; // 第 2 個操作數
public MathClass(int num1,int num2) {
// 構造方法
this.num1 = num1;
this.num2 = num2;
}
// 實現介面中的求和方法
public int sum() {
return num1 + num2;
}
// 實現介面中的獲取較大數的方法
public int maxNum(int a,int b) {
if(a >= b) {
return a;
} else {
return b;
}
}
}
內部類
類內部定義的類
分類:
- 成員內部類
- 靜態內部類
- 局部內部類
- 匿名內部類
- 內部類還是一個獨立的類,會編譯為獨立的.class文件,但前面會冠以類名和$符號
- 是內部類的一個成員,可以操作到外部類的私有屬性
- 外部類只有兩種級別:public和預設
- 內部類有四種級別:public、protected、private、預設
Outer o = new Outer();
//外部類可直接new
Inner in = new Inner();
//外部類外需要通過外部類來實例化內部類
Outer.Inner inner = o.new Inner();
實例內部類
沒有static修飾,也成為非靜態內部類,例:
public class Outer {
class Inner {
// 實例內部類
}
}
- 和實例方法、實例變數相同,在外部類/外部類以外,必須通過外部類的實例創建內部類的實例
- 實例內部類中可以訪問外部類的所有成員(多層嵌套也可)
- 外部類中不能直接訪問內部類的成員,而必須通過內部類的實例訪問(不是很懂)
- 實例內部類中的成員不能使用static修飾,除非同時有final修飾
靜態內部類
static修飾的內部類,例:
public class Outer {
static class Inner {
// 靜態內部類
}
}
- 可以通過外部類創建內部類的實例
- 類中可定義靜態成員/實例成員
- 可直接訪問外部類的靜態成員,如果要訪問外部類的實例成員,則需要通過外部類的實例去訪問。
局部內部類
一個方法中定義的類,如:
public class Test {
public void method() {
class Inner {
// 局部內部類
}
}
}
- 類似局部變數,不用訪問控制修飾符和static修飾符修飾
- 只在方法內可用
- 不能定義static成員
- 內部類的內部類也不能用訪問控制修飾符和static修飾符
- 可訪問外部類的所有成員
- 方法中的成員與外部類成員同名,可以使用
.this. 的形式訪問外部類中的成員。
匿名內部類
沒有類名的內部類,直接使用new來聲明,例:
new <類或介面>() {
// 類的主體
};
一般用法:
- 繼承一個類,重寫其方法。
- 實現一個介面(可以是多個),實現其方法。
public class Out {
void show() {
System.out.println("調用 Out 類的 show() 方法");
}
}
public class TestAnonymousInterClass {
// 在這個方法中構造一個匿名內部類
private void show() {
Out anonyInter = new Out() {
// 獲取匿名內部類的實例
void show() {
System.out.println("調用匿名類中的 show() 方法");
}
};
anonyInter.show();
}
public static void main(String[] args) {
TestAnonymousInterClass test = new TestAnonymousInterClass();
test.show();
}
}
- 和局部內部類相同,可訪問外部類所有成員。若位於方法中,只能訪問方法中final修飾的量
- 可以使用非靜態代碼塊進行初始化,在父類的構造函數後執行