物件導向
注:
看此篇時強烈建議有一定的物件導向思想基礎,有一定的基礎後先翻到下面看第九條:
9.物件導向: 從未封裝→封裝→繼承→多型→抽象類→介面的程式碼演變按這個邏輯去看,,哪有不理解的再回頭看知識點,這是掌握理解最好最快的方法,切記切記
萬物皆物件
描述一個物質都可以通過兩方面說明:資料模型(屬性)、行為模型(行為)。
在Java程式設計中,我們使用成員變數表示資料模型,用成員方法表示行為模型。
使用類表示某些具有相同屬性和行為的事物。
1. 方法:
構造方法
語法:
[訪問修飾符] 類名(){ }
注意:
當類中沒有顯式的構造方法,例項化該類的物件時,程式會自動建立一個公開的無參構造方法;
如果類中有顯示的構造方法,程式就不會建立無參構造;
構造方法,一般是用於為成員屬性賦初始化值;
靜態方法
static 關鍵字用來宣告獨立於物件的靜態方法。靜態方法不能使用類的非靜態變數。靜態方法從引數列表得到資料,然後計算這些資料。
對類變數和方法的訪問可以直接使用 classname.variablename 和 classname.methodname 的方式訪問。
如下例所示,static修飾符用來建立類方法和類變數:
public class InstanceCounter { private static int numInstances = 0; protected static int getCount() { return numInstances; } private static void addInstance() { numInstances++; } InstanceCounter() { InstanceCounter.addInstance(); } public static void main(String[] arguments) { System.out.println("Starting with " + InstanceCounter.getCount() + " instances"); for (int i = 0; i < 500; ++i){ new InstanceCounter(); } System.out.println("Created " + InstanceCounter.getCount() + " instances"); } }
執行結果: Starting with 0 instances Created 500 instances
成員方法(例項方法)
2. 變數:
成員變數(例項變數)
-
-
-
宣告在所有方法體和程式碼塊之外,並且沒有使用static修飾的變數,叫做例項變數;
-
可以使用訪問修飾符和final修飾;
-
使用final修飾時,一定要賦值;
-
例項變數是在物件被建立時建立,物件被銷燬時銷燬;
-
作用域範圍在整個類中;
-
-
區域性變數
-
-
-
宣告在構造方法、靜態方法、例項方法、程式碼塊中的變數,都是區域性變數;
-
不能使用static和訪問修飾符修飾;
-
可以使用final修飾,即為常量,不必在宣告語句中賦值;
-
當執行區域性變數所在的方法或程式碼塊時,才有機會被建立,在方法或程式碼塊執行結束後被自動銷燬;
-
區域性變數在記憶體的棧區分配;
-
區域性變數在使用之前必須要先賦值;
-
-
靜態變數(類的變數)
-
-
-
宣告在所有方法體和程式碼塊之外,並且使用static修飾的變數;
-
可以使用訪問修飾符修飾;
-
一般配合final使用,即public static fianl,識別符號使用大寫;
-
類變數被分配在靜態儲存區,是被所有該類的物件共享資料;
-
類變數是在程式開始時被建立,程式結束時銷燬;
-
3. 修飾符
訪問修飾符
Java中,可以使用訪問控制符來保護對類、變數、方法和構造方法的訪問。Java 支援 4 種不同的訪問許可權。
-
-
-
default (即預設,什麼也不寫): 在同一包內可見,不使用任何修飾符。使用物件:類、介面、變數、方法。
-
private : 在同一類內可見。使用物件:變數、方法。 注意:不能修飾類(外部類)
-
public : 對所有類可見。使用物件:類、介面、變數、方法
-
protected : 對同一包內的類和所有子類可見。使用物件:變數、方法。 注意:不能修飾類(外部類)
-
我們可以通過以下表來說明訪問許可權:
修飾符 | 當前類 | 同包的類 | 同包的子孫類 | 異包類 | 異包的子孫類 |
public | √ | √ | √ | √ | √ |
protected | √ | √ | √ | × | × |
default | √ | √ | √ | × | × |
private | √ | × | × | × | × |
非訪問修飾符
為了實現一些其他的功能,Java 也提供了許多非訪問修飾符。
static 修飾符,用來修飾類方法和類變數。
final 修飾符,用來修飾類、方法和變數,final 修飾的類不能夠被繼承,修飾的方法不能被繼承類重新定義,修飾的變數為常量,是不可修改的。
final 變數:
final 表示"最後的、最終的"含義,變數一旦賦值後,不能被重新賦值。被 final 修飾的例項變數必須顯式指定初始值。
final 修飾符通常和 static 修飾符一起使用來建立類常量。
//例項 public class Test{ final int value = 10; // 下面是宣告常量的例項 public static final int BOXWIDTH = 6; static final String TITLE = "Manager"; public void changeValue(){ value = 12; //將輸出一個錯誤 } }
final 方法:
類中的 final 方法可以被子類繼承,但是不能被子類修改。
宣告 final 方法的主要目的是防止該方法的內容被修改。
如下所示,使用 final 修飾符宣告方法:
public class Test{ public final void changeName(){ // 方法體 } }
final 類:
final 類不能被繼承,沒有類能夠繼承 final 類的任何特性。
//例項 public final class Test { // 類體 }
注意:
-
-
-
-
當final修飾類時,當前類不能被繼承;
-
當final修飾方法時,該方法不能被重寫;
-
當final修飾變數時,變數的值不能被修改,即為常量;
-
-
-
abstract 修飾符,用來建立抽象類和抽象方法。
抽象類:
抽象類不能用來例項化物件,宣告抽象類的唯一目的是為了將來對該類進行擴充。
一個類不能同時被 abstract 和 final 修飾。如果一個類包含抽象方法,那麼該類一定要宣告為抽象類,否則將出現編譯錯誤。
抽象類可以包含抽象方法和非抽象方法。
規則:
含有抽象方法的類,一定是抽象類;
-
-
-
-
抽象類中可以宣告成員變數、常量、成員方法、抽象方法,抽象類中不一定要有抽象方法;
-
抽象類不能被例項化;
-
抽象類可以被繼承;
-
可以通過兩種方式獲得抽象類物件:父類引用指向子類物件、匿名內部類;
-
子類必須重寫抽象父類的所有抽象方法,或者是把子類也定義為抽象類;
-
如果一個類繼承的抽象父類還有上級抽象父類,那麼子類中需要要重寫所有抽象父類的所有抽象方法;
-
抽象類也可以繼承非抽象類,同時繼承了父類的所有非私有的屬性和方法;
-
-
-
例項 abstract class Caravan{ private double price; private String model; private String year; public abstract void goFast(); //抽象方法 public abstract void changeColor(); }
抽象方法
抽象方法是一種沒有任何實現的方法,該方法的的具體實現由子類提供。
抽象方法不能被宣告成 final 和 static。
任何繼承抽象類的子類必須實現父類的所有抽象方法,除非該子類也是抽象類。
如果一個類包含若干個抽象方法,那麼該類必須宣告為抽象類。抽象類可以不包含抽象方法。
抽象方法的宣告以分號結尾,例如:public abstract sample();。
例項 public abstract class SuperClass{ abstract void m(); //抽象方法 } class SubClass extends SuperClass{ //實現抽象方法 void m(){ ......... } }
synchronized 和 volatile 修飾符,主要用於執行緒的程式設計。
synchronized 修飾符
synchronized 關鍵字宣告的方法同一時間只能被一個執行緒訪問。synchronized 修飾符可以應用於四個訪問修飾符。
例項 public synchronized void showDetails(){ ....... }
transient 修飾符
序列化的物件包含被 transient 修飾的例項變數時,java 虛擬機器(JVM)跳過該特定的變數。
該修飾符包含在定義變數的語句中,用來預處理類和變數的資料型別。
例項 public transient int limit = 55; // 不會持久化 public int b; // 持久化
volatile 修飾符
volatile 修飾的成員變數在每次被執行緒訪問時,都強制從共享記憶體中重新讀取該成員變數的值。而且,當成員變數發生變化時, 會強制執行緒將變化值回寫到共享記憶體。這樣在任何時刻,兩個不同的執行緒總是看到某個成員變數的同一個值。
一個 volatile 物件引用可能是 null。
例項 public class MyRunnable implements Runnable { private volatile boolean active; public void run() { active = true; while (active) // 第一行 { // 程式碼 } } public void stop() { active = false; // 第二行 } }
通常情況下,在一個執行緒呼叫 run() 方法(在 Runnable 開啟的執行緒),在另一個執行緒呼叫 stop() 方法。 如果 第一行 中緩衝區的 active 值被 使用,那麼在 第二行 的 active 值為 false 時迴圈不會停止。
但是以上程式碼中我們使用了 volatile 修飾 active,所以該迴圈會停止。
4. this關鍵字
this指當前物件,用法:
-
-
-
-
-
當區域性變數名和例項變數名同名時,使用this.變數名來表示例項變數;
-
this()表示當前類的構造方法,只能在構造方法中使用該寫法,並且是寫在構造方法內的第一行。
-
-
-
-
物件導向三大特徵:封裝、繼承、多型
5. 封裝
私有的屬性,公開的方法。
封裝的步驟:
宣告私有(private)的屬性;
宣告公開(public)的geter和seter方法;
6. 繼承
Java中的繼承是單繼承,可以實現多層繼承,繼承的關鍵字extends
語法:
public class Son extends Father{ }
規則:
-
-
-
子類繼承父類非私有的所有屬性和方法,不能繼承父類的構造方法;
-
例項化子類物件的步驟:先執行父類的構造方法,再執行子類的構造方法;
-
-
重寫定義:
-
-
-
子類重新宣告從父類繼承來的方法,稱為方法重寫;
-
方法重寫時,方法的宣告部分要和父類保持一致(返回值型別,方法名,引數);
-
重寫方法的訪問許可權要大於等於父類中方法的訪問許可權;
-
子類重寫父類方法,子類物件呼叫的是子類中重寫後的方法;
-
使用static修飾的方法不能被重寫,但是可以被子類重寫宣告;
-
不同包的子類可以重寫父類中protected修飾的方法,但是不能以繼承的形式,用子類物件直接呼叫父類的該方法;
-
7. 多型
實現的必要條件:
-
-
-
繼承
-
重寫
-
父類引用指向子類物件
-
-
instanceof關鍵字:
語法: if (物件名 instanceof 類名) { 型別轉換程式碼; }
例項: Animal cat = new Cat(); if (cat instanceof Cat) {//返回結果為boolean型別 Cat c = (Cat) cat; c.eat(); }else{ System.out.println("型別不匹配"); }
8. 介面
語法: public interface ITest { }
規則:
-
-
-
介面使用interface關鍵字修飾;
-
介面是一個完全抽象的抽象類;
-
介面中沒有構造方法;
-
介面不能被例項化物件;
-
介面中可以宣告靜態常量、抽象方法、靜態方法;
-
介面中不能宣告例項方法,宣告抽象方法時,不能使用static關鍵字修飾;
-
宣告介面語句中,預設含有abstract關鍵字,抽象方法中也預設含有abstract關鍵字;
-
介面可以被實現,使用implements關鍵字,一個類實現一個介面,必須重寫該介面中所有的抽象方法;
-
一個類可以實現多個介面,每個介面名用英文的逗號隔開,該類中必須重寫所有已實現介面中的抽象方法;
-
-
介面可以繼承介面,介面與介面間是多繼承關係,介面不能繼承類;
9.物件導向: 從未封裝→封裝→繼承→多型→抽象類→介面的程式碼演變:
未封裝:
public class Person { int age;//年齡 String name;//姓名 public void print() { System.out.println("age:"+this.age+" "+"name:"+this.name); } }
class Text{ public static void main(String[] args) { Person person = new Person();//例項化物件 person.age = -100;//賦值 person.name = "張三"; person.print();//執行類中的方法 } }
執行結果:
age:-100 name:張三
封裝:
public class Person { private int age;//年齡 private String name;//姓名 public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "age=" + this.age + ", name='" + this.name + '\'' + '}'; } }
class Text{ public static void main(String[] args) { Person person = new Person(); person.setAge(20); person.setName("張三"); ; String s = person.toString(); System.out.println(s); } }
執行結果: Person{age=20, name='張三'}
繼承:
public class Office { public void print(){ System.out.println("列印"); } }
class Word extends Office { @Override public void print() { System.out.println("Word列印"); } }
class Test{ public static void main(String[] args) { Word word = new Word(); word.print(); } }
執行結果: Word列印
一個Java原始檔中可以宣告多個class類,但只能有一個public修飾的類。
多型:
public class Office { public void print(){ System.out.println("列印"); } }
class Word extends Office {
@Override
public void print() {
System.out.println("Word列印");
}
}
class Excle extends Office{
@Override
public void print() {
System.out.println("Excel列印");
}
}
class Ppt extends Office{ public void daYin() {//子類可以重寫父類方法,也可以自己定義方法 System.out.println("PPT列印"); } }
class Test{
public static void main(String[] args) {
Office o = new Word();//多種形態,即多型
o.print();
}
}
執行結果: Word列印
class Test{ public static void main(String[] args) { Office o = new Excle(); o.print(); } }
執行結果: Excel列印
class Test{ public static void main(String[] args) { Office o = new Ppt(); o.print(); } }
執行結果:
列印
為避免這種方法名稱多樣的情況,用一種約束叫抽象類
public abstract class Office { public abstract void print(); } class Word extends Office { @Override public void print() { System.out.println("Word列印"); } } class Excle extends Office{ @Override public void print() { System.out.println("Excel列印"); } } class Ppt extends Office{ @Override public void print() { System.out.println("PPT列印"); } } class Test{ public static void main(String[] args) { Office o = new Ppt();//父類引用指向子類物件,子類重寫父類方法,父類引用呼叫子類重寫後的方法,執行的結果是子類重寫後的方法 o.print(); } }
執行結果: PPT列印
介面是完全抽象的抽象類,並且可以多實現
介面預設為:
public class abstract interface Office {
public abstract void print();
}
public interface Office { void print(); } class Word implements Office { @Override public void print() { System.out.println("Word列印"); } } class Excle implements Office{ @Override public void print() { System.out.println("Excel列印"); } } class Ppt implements Office{ @Override public void print() { System.out.println("PPT列印"); } } class Test{ public static void main(String[] args) { Office o = new Ppt(); o.print(); } }
執行結果為: PPT列印
10. 內部類
成員內部類
成員內部類宣告在類中,方法體、程式碼塊之外。和成員變數、成員方法在同一級別。
語法: public class Out { //成員內部類 public class Inner{ } }
例項化成員內部類: //先例項化外部類 Out o = new Out(); //使用外部類物件,再例項化內部 Out.Inner inner = o.new Inner();
例項: public class Out { //成員變數 public int a = 1; //成員內部類 public class Inner{ public int a = 2; //內部類的成員方法 public void print(){ //執行內部類中的例項變數a System.out.println(a); //執行外部類的例項變數a System.out.println(Out.this.a); } } }
靜態內部類
宣告的位置參考成員內部類。
語法: public class Out { //靜態內部類 public static class Inner{ } }
例項化靜態內部的物件: Out.Inner inner = new Out.Inner();
例項: public class Out { public static int a = 1; public int b = 3; //靜態內部類 public static class Inner{ public static int a = 2; public static void print(){ //執行靜態內部的靜態變數 System.out.println(a); //執行外部類的靜態變數 System.out.println(Out.a); //執行外部類的例項變數 Out o = new Out(); System.out.println(o.b); } } }
區域性內部類
宣告在方法體或程式碼塊內,作用域範圍在方法體或程式碼塊內。
語法: public class Out { public void method(){ //區域性內部類 class Inner{ //區域性內部類的成員方法 public void print(){ System.out.println("區域性內部類"); } } //例項化區域性內部類 Inner inner = new Inner(); inner.print(); } }
執行區域性內部類的方法: Test類: public static void main(String[] args) { Out o = new Out(); o.method(); }
匿名內部類
-
-
-
宣告位置同區域性內部類一樣,前提條件:必須繼承一個類或實現一個介面,匿名內部類的宣告和例項化物件是同時進行的;
-
一般使用於獲得抽象類或介面物件;
-
-
語法: 父類名/介面名 物件名 = new 父類名/介面名(){ //匿名內部類成員 };
例項: 父類 public class Father { }
匿名內部類: public class Out { public void method(){ //匿名內部類物件 Father f = new Father(){ }; } }