面向過程:側重分步驟,考慮完成該功能的過程(自己一步一步做)
側重分模塊(找人分發任務解決任務)
總結
面向對象優勢:可擴展,可維護,靈活性高,程序耦合度低
面向對象缺點:性能比面向過程相對較差
💪 方法特點#
- 靜態方法用類名調用,構造方法創建實例化時對象調用,成員方法對象調用
- 構造方法
- 聲明:權限修飾符 方法名(參數列表){方法體}; 方法名和類名必須一致
- 類中沒有構造方法,默認有一個無參構造,如果自己編寫了則默認的就沒有了,一般自己寫完構造方法會再寫一個無參
- 只有成員方法才可以直接訪問成員屬性,靜態方法不能訪問成員變量(方法只要需要成員變量就定義為成員方法)
- 成員方法可以訪問靜態變量
🎗 類和對象之間的關係#
- 類:對事物客觀描述的標準
- 對象:一切皆對象,符合類標準的具體個體
對象使用#
實例化對象:Student s1 = new Student();
通過 get 和 set 方法獲取和設置成員屬性
🔌 JavaBean#
JavaBean
是一種Java
語言寫成的可重用組件,它是一個類(類必須是具體的公共的,並且具有無參構造器)
- 私有化成員變量
- 對外提供
get
和set
方法 - 顯式聲明無參和全參構造
get
和set
方法的作用- 保護成員變量
- 進行攔截
🎉 實例化的內存過程#
- 加載客戶端類
main()
方法壓棧- 加載服務端類
- 調用構造方法
- 在堆內存中開辟空間,對實例化對象初始化
- 構造方法出棧,實例化對象賦值給變量
💥 異常#
- 編譯時異常
- 運行時異常
- 空指針異常(用
null
訪問成員變量時) - 陣列下標越界
- 類型轉換異常
- 空指針異常(用
⚠️ 易錯點#
- 區分成員方法和構造方法:看有無返回值(構造方法無返回值)
- 對象可以調用靜態變量,
javac
編譯時會改成類名調用靜態變量(所以空指針也不影響) - 靜態方法不能調用成員變量
👈 this#
對象中第一個成員變量,保存了當前對象的內存地址(不能出現在靜態方法中)
作用#
- 用在成員方法 / 構造方法中 區分同名的成員變量和局部變量
- 重載調用當前類的有參構造方法
必須在當前構造方法方法體第一行 - 鏈式調用
⚙️ static#
修飾符 用來標註靜態屬性 如果沒有使用static
為成員屬性
功能#
- 靜態變量
- 靜態方法
- 靜態代碼塊
不能調用 在程序加載時調用 (在main()
方法之前運行) 只執行一次
代碼塊:無名稱方法 、自動調用
訪問一個類的靜態屬性時才會加載該類到內存中
實例語句塊可看作無名字成員方法
📦 封裝#
把所有組成部分組合到一起,還可以通過權限控制修飾符將數據隱藏起來,可以控制用戶對類數據的修改程度
適當的封裝可以讓代碼更容易理解,易於維護,更提高了代碼的安全性
導包#
放在 package
之下 class
之上
靜態導入
導入某個類的某個靜態變量
權限控制#
範圍 | public | protected | default | private |
---|---|---|---|---|
同類 | ✔️ | ✔️ | ✔️ | ✔️ |
同包 | ✔️ | ✔️ | ✔️ | ❌ |
子類 | ✔️ | ✔️ | ❌ | ❌ |
全局 | ✔️ | ❌ | ❌ | ❌ |
🧬 繼承#
從一個已有的類中衍生出一個新的類。該類往往包含著父類的特徵,還可以新增特有的屬性
📝 重寫#
必須有繼承關係
- 方法名、參數列表、返回值相同
- 不能比原方法有更低的訪問權限
- 不能比原方法有更寬泛的異常
重寫重載區別#
注解#
源碼注解
編譯注解
運行時注解
🛑 final#
可放置在
- 類
- 變量
- 方法
🌱 多態#
父類引用指向子類對象
- 父類
- 引用 指向的引用數據類型
- 指向 可以找到誰
- 子類對象 創建一個子類的實例化對象
使用父類創建的引用類型變量 可以找到子類對象
父類 變量名 = new 子類();
向上轉型(由子類轉換為父類) 類似於自動轉換類型
非成員方法
優點#
- 降低耦合度
- 擴展性 替換性 靈活性增強
缺點#
會丟失子類特有的屬性
成員方法 如果子類重寫了父類的成員方法則調用子類的成員屬性
非成員方法 調用父類方法
public class Poly {
public static void main(String[] args) {
Sup sub = new Sub();
// 2
System.out.println(sub.age);
sub.m1();
// Son m2
sub.m2();
// sub.m3();
}
}
class Sup{
int age = 2;
public void m1(){
System.out.println("Father m1");
}
public void m2(){
System.out.println("Father m2");
}
}
class Sub extends Sup{
int age = 1;
public void m2(){
System.out.println("Son m2");
}
public void m3(){
System.out.println("Son m3");
}
}
instanceof#
判斷某個對象是否由某個類實例化而來
向下轉型(由父類轉換為子類)
- 直接多態
父類 變量名 = new 子類();
- 實參形參多態
- 返回值多態
方法返回值使用父類聲明
隱蔽多態#
通過子類對象,調用繼承父類的成員方法時 此時上下文環境為多態環境
public class Poly_05 {
public static void main(String[] args) {
// Sup sup = new Sub();
// // 2 父類
// System.out.println(sup.age);
// // 父類 因為子類沒有
// sup.m1();
// // 子類 因為覆寫
// sup.m2();
// // 報錯,因為父類沒有,多態會丟失子類特有屬性
// // sup.m3();
// // 向下轉型
// Sub sub = (Sub) sup;
// // 1 子類
// System.out.println(sub.age);
// // 父類 繼承
// sub.m1();
// // 子類 因為子類有
// sub.m2();
// // 子類,又沒有使用多態
// sub.m3();
Sub sub = new Sub();
sub.m1();
}
}
class Sup {
int age = 2;
public void m1() {
/**
* this : 是對象中第一個成員變量,保存當前對象的內存地址
*
* this既然保存當前對象內存地址,那麼this的類型 可以是當前類類型,可以是父類類型
*
* this寫在哪個類中,哪個類就是當前類 所以 當前類 是Sup 父類 是 Object
*
* 因為this能調用當前類中所有的屬性,並沒有丟失,所以this是Sup 當前類類型
*
* Sup this;
*
* this : 哪個對象調用這個成員方法,this就指向誰
*
* 最終是Sub調用的m1方法,所以this 指向 Sub
*
* Sup this = Sub;
*/
// System.out.println("父類m1");
System.out.println(this);
// 2 父類
System.out.println(this.age);
System.out.println(this.sakjdkashdhqwrjnfaksf);
System.out.println(age);
// 子類
m2();
// 報錯
// m3();
}
public void m2() {
System.out.println("父類m2");
}
}
class Sub extends Sup {
int age = 1;
public void m2() {
System.out.println("子類m2");
}
public void m3() {
System.out.println("子類m3");
}
}
🐘 抽象#
abstract
修飾符 用來修飾抽象類和抽象方法
abstract
修飾的類是抽象類,並且抽象類不能創建對象而且抽象類一般主要用於被繼承
abstract
修飾的方法是抽象方法。該方法沒有方法體,不實現功能,用於讓不同的子類實現方法
抽象方法必須在抽象類中,而抽象類中可以存在普通方法
抽象類中可看作特殊的類,只不過不能創建對象而已
abstract
和final
不能同時出現
- 子類如果繼承了抽象類 那麼需要實現所有的抽象方法 否則該子類就需要使用
abstract
修飾 - 抽象類繼承抽象類,需要實現 0~N 個抽象方法
- 普通類繼承抽象類需要實現所有方法
👄 接口#
interface
關鍵字
1.8 之前接口是完全抽象的,只允許出現抽象方法和常量 (psf
)
在接口中只有常量沒有變量,並且psf
可以省略 且權限控制默認是public
抽象方法的 abstract
可以省略
interface A{
int age = 1;
// 默認方法
default void dmethod(){
System.out.println("默認方法");
}
}
class B{
void m(){
System.out.println(A.age);
}
}
🌌 Object#
Object
是 Java 提供的根類 所有類都直接或間接繼承Object
java.lang.Object
在java.lang
包下 核心包下的所有類不需要導入
toString#
equals#
==
基本類型比較值大小,引用類型比較地址
==
是 equal()
的默認底層實現
默認的 equals()
比較地址
finalize#
JVM 跨平台 多線程 面向對象 自動垃圾回收
垃圾 沒有任何引用指向該對象的時候,該對象成為垃圾對象
垃圾被回收時 自動調用該對象的 finalize()
方法 是在對象生命結束時候被調用
System.gc()
程序員可以建議 JVM 進行垃圾回收
🔗 類之間關係#
類與類之間的關係詳見 這裡
🧩 內部類#
成員內部類#
class A{
class B{
}
}
等同於成員變量 不能有靜態聲明
內部類可以使用權限控制修飾符
可以訪問外部類的私有化 (被 private
修飾) 屬性
成員內部類可以直接訪問外部類的所有數據
此時 B
類的全類名為 A$B
靜態內部類#
class A{
private int a = 1;
private static int b = 1;
static class B{
int c = 1;
static int d = 1;
public void method(){
// System.out.print(a);
A aaa = new A();
System.out.print(aaa.a);
System.out.print(b);
}
}
}
- 靜態內部類可以等同看作靜態變量
- 靜態內部類無法直接訪問外部類的的成員屬性
- 靜態內部類可以聲明任何數據 (靜態 / 成員)
- 創建外部類的對象後可訪問外部類的的成員屬性
局部內部類#
- 方法中的類是局部內部類
- 局部內部類可看作局部變量
- 如果通過局部內部類訪問外部方法中的局部變量的時候,該變量需要加
final
修飾 1.8 後final
可以省略 - 局部內部類不能使用權限修飾符
- 局部內部類不能有靜態聲明
class A{
void method(){
class B{
}
}
}
此時B
類的全類名為 A$1B
如果內部類名重複 則為外部類類名 $2 內部類類名
匿名內部類#
當一個方法的參數需要傳入一個類對象時,可以使用匿名內部類
語法#
new 實現接口(){
//匿名內部類類體部分
}
實現接口 接口沒有構造函數所以為空參數
new 父類構造器(實參列表){
//匿名內部類類體部分
}
調用父類構造器可以為空參數,也可以傳入參數
示例#
//接口部分
interface Product{
public double getPrice();
public String getName();
}
public class Anonymous{
public void test (Product p){
System.out.println(p.getName()+"--------"+p.getPrice());
}
public static void main(String[] args ){
Anonymous as = new Anonymous();
//此處實現接口並實現抽象方法
as.test(new Product(){
//實現方法
public double getPrice( ){
return 8888;
}
//實現方法
public String getName(){
return "I can do it ";
}
});
}
}
上面代碼很簡單,就是定義了一個類Anonymous
,在類裡定義了一個test
方法。然後就是創建 Anonymous 對象,調用他的實例方法 test ()
不過調用test()
方法時,要傳入一個Product
對象。但是由於Product
是一個接口,無法創建對象,所以要實現該接口。因此此處採用匿名內部類的方式進行,並實現接口中全部的抽象方法
作用#
無需通過實現接口或者繼承抽象類的方式來實現。但是使用匿名內部類的優點是顯而易見的,可以少些代碼,而且代碼更加簡潔