注:本文轉載自coder-pig
原文請戳:http://blog.csdn.net/coder_pig/article/details/22667533
總體來說小豬的文章寫的挺詳細,這個傢伙挺有耐心的,稍安勿躁,一步步來~
Java面向對象 Part 3
前言
在前面的兩節中我們對面向對象已經有了差不多的瞭解了,
而這一節也是Java面向對象部分的一個結尾,
在這一節中我們會對抽象類,接口,修飾符,枚舉,封裝進行解析;
抽象類
什麼是抽象類?
我們將具有共同特點的類進行抽象後得到的一個類,而這個類本身的方法並沒有任何具體的實現;
eg:定義一個抽象的動物類,然後定義幾個抽象方法(動物共有的方法),比如設置有多少隻腳的方法
public abstract void setFoods();
抽象類的定義:
一般使用abstract關鍵字修飾
抽象類: 修飾符 abstract 類名{//類的內容}
抽象方法: 修飾符 abstract 返回值類型 方法名();
抽象類的使用規則:
①如果一個類有一個抽象方法,那麼這個類一定是抽象類;反之,如果一個類是抽象類,但可能包含有非抽象的方法
②如果一個類繼承了一個抽象類,那麼必須重寫抽象類中所有的抽象方法,是所有!所有!如果沒有全部實現的話
那麼子類必須也要定義爲一個抽象類
③abstract不能和static放在一起,否則會出現錯誤(因爲static不能被覆蓋,abstract爲了生效,必須被覆蓋)
④抽象類可不能定義爲最終哦!abstract不能與final共存!
⑤注意區分方法的空實現和方法的抽象
eg:方法的空實現:private void show(){} 方法的抽象,無實現: private void show();
⑥抽象類不能夠實例化哦!但是可以new的時候重寫裏面的抽象方法
抽象類使用實例
代碼:
- package com.jay.example;
- /*
- * 該代碼是定義一個抽象類,汽車類,定義幾個屬性和抽象方法
- * 然後定義客車,吊車類作爲它的子類,
- * 在客車類中即重寫抽象方法,也重寫了具體方法
- * 在吊車類中,只是重寫具體方法,不重寫抽象方法,所以吊車類
- * 需要被設置爲抽象類,否則會報錯
- * */
- abstract class Car
- {
- protected String color;
- protected int sudu;
- //定義一個具體的方法
- public void speed()
- {
- System.out.println("加速");
- }
- //定義一個抽象方法,是方法的空實現哦,子類繼承時需要覆蓋該方法
- public abstract void useful();
- }
- class Truck extends Car
- {
- public void speed()
- {
- System.out.println("卡車起步很慢");
- }
- public void useful() {
- System.out.println("卡車是用於貨運的!");
- }
- }
- //定義吊車類,沒有實現抽象方法,所以需爲抽象類
- abstract class Crane extends Car
- {
- public void speed() {
- System.out.println("吊車走得更加慢!");
- }
- }
- public class AbstractDemo {
- //抽象類可不能直接實例化哦!Car c = new Car()是會報錯的
- //當然可以通過向上轉型的規則:Car c = new Track();是可以的
- public static void main(String[] args) {
- Truck tr = new Truck();
- tr.speed();
- tr.useful();
- //下面是演示protected修飾的作用於,子類或者同包可見
- tr.color = "白色";
- tr.sudu = 40;
- System.out.println(tr.color + tr.sudu + "km/h");
- }
- }
運行截圖:
代碼解析:
該代碼演示了抽象類的基本用法,定義抽象類父類Car,定義了一個抽象方法和具體方法
然後在卡車類中都重寫了兩個方法;在吊車類中僅僅重寫了具體方法,所以吊車類需要被設置爲抽象類
最後還演示了protected的作用域:子類與同包可見
接口
接口的介紹:
因爲Java的數據結構是樹型的,所以不像C++那樣可以同時繼承多個父類;但是Java通過接口和內部類實現了多繼承
接口是一種特殊的抽象類,一個類實現一個接口,相當於它繼承了一個抽象類
接口的定義:
修飾符 inteface 接口名{"內容"}
接口的實現:
一個類可以在繼承一個父類同時實現多個接口,用","分隔
class 類名 extents 父類 implements 接口1,接口2...{}
使用規則:
①一個Java文件中只允許有一個public修飾的類或者接口,且需與文件名同名,是一個!!
②在一個接口中,所有方法都是公開的,抽象的方法!!所有的屬性都是公開,靜態,常量!
③如果一個類聲明實現一個接口,但是沒有實現接口中的所有方法,那麼這個類必須是抽象類哦!
④接口只關心功能,並不關心功能的具體實現
⑤接口也可以通過extends繼承哦!但是依舊需要實現子接口和父接口中的所有抽象方法
⑥接口中的方法可以不寫public,但是子類實現接口中的抽象方法時,要寫上public,不然會報錯哦!
代碼示例:
- package com.jay.example;
- /*
- * 在這個代碼中,我們定義了兩個父接口,又用一個子接口對兩個父接口進行了繼承
- * 同時爲子接口又定義了一個抽象方法;然後定義了一個Person實現兩個父接口
- * 重寫了兩個抽象方法;又定義了一個Worker實現了子接口,但同時也需要把父接口
- * 中的抽象方法進行覆蓋
- *
- * */
- //定義一個說話的接口
- interface Speak
- {
- void speak();
- }
- //定義一個吃飯的接口
- interface Eat
- {
- void eat();
- }
- //定義一個子接口,同時繼承說話和吃飯的接口,同時定義一個走路的方法
- interface Walk extends Speak,Eat
- {
- void walk();
- }
- //定義一個Person類,實現說話和吃飯接口,重寫接口中的抽象方法
- //子類在實現接口的時候,public修飾符不可以省略,不然會報錯
- class Person implements Speak,Eat
- {
- public void speak()
- {
- System.out.println("Person類可以說話");
- }
- public void eat()
- {
- System.out.println("Person類可以吃飯");
- }
- }
- //定義一個Worker類直接實現walk接口,同時要實現兩個父接口中的抽象方法
- class Worker implements Walk
- {
- public void speak()
- {
- System.out.println("Worker類可以說話");
- }
- public void eat()
- {
- System.out.println("Worker類可以吃飯");
- }
- public void walk()
- {
- System.out.println("Worker類可以走路");
- }
- }
- public class InterfaceTest {
- public static void main(String[] args) {
- //實例化兩個對象,輸出對應結果
- Person p = new Person();
- p.eat();
- p.speak();
- Worker w = new Worker();
- w.eat();
- w.speak();
- w.walk();
- }
- }
代碼截圖:
代碼解析:
該代碼演示了接口的繼承,接口的實現
比較簡單,這裏就略過了
修飾符詳解
訪問控制修飾符
public:公有的,被public修飾的部分可以被任何程序訪問
protected:受保護的,被它修飾的成員只能由同包中的類或者其子類訪問
default(默認):不寫修飾符就是默認的,別寫上defalut啊!同包與同類可見
private:私有的,是Java實現封裝的關鍵,被修飾的成員變量與方法只能被類本身訪問,同包也不行哦!
注意事項:
不能夠使用protected和private修飾類;要麼用public修飾,要麼不加任何修飾符
其他修飾符:
static:靜態修飾符
①靜態變量:
用static修飾的變量,在所有對象中共享的數據,都指向內存中的同一地址
即,任何對象對改值的修改都會使存儲空間的值發生改變
代碼演示:
- package com.jay.example;
- class Test
- {
- //定義初始的值爲3
- static int a = 3;
- }
- public class StaticTest {
- public static void main(String[] args) {
- //靜態成員變量可以直接通過類名進行訪問
- System.out.println(Test.a);
- //實例化兩個對象,在t1中修改a的值,發現t2中的a的值也發生改變
- Test t1 = new Test();
- t1.a = 4;
- Test t2 = new Test();
- System.out.println(t2.a);
- //輸出結果是:
- //3
- //4
- //說明了靜態變量都是公用一塊內存區域的,任何一個對象
- //對值的修改,都會改變對應內存區域的值
- }
- }
②靜態方法:
用static修飾的方法,要注意:
靜態方法中,只能夠訪問靜態數據或者直接調用靜態方法
可以直接通過類名進行調用
代碼演示:
- package com.jay.example;
- /*
- *因爲我們的main方法就是靜態的方法
- *所以我們直接在main方法外定義參數和方法
- *驗證靜態方法只能訪問靜態成員
- * */
- public class StaticTest2 {
- //定義非靜態的數據與方法
- int a = 3;
- void test(){System.out.println("非靜態方法可不能被main調用啊");}
- //定義靜態的數據和方法
- static int b = 4;
- static void test2()
- {System.out.println("靜態方法被main調用了");}
- public static void main(String[] args) {
- //下面兩個語句,如果調用了是會報錯的,不信可以試試
- //System.out.println(a);
- //test();
- System.out.println(b);
- test2();
- }
- }
③靜態代碼塊
放在類聲明的內部,成員方法與構造方法的外部,該代碼塊會在該類第一次使用時
執行一次,就不會再執行了,通常是在這裏寫一些初始化的代碼
代碼演示:
- package com.jay.example;
- public class static3 {
- static
- {
- System.out.println("靜態代碼塊,通常用於類的初始化");
- }
- }
④複雜的成員的初始化問題
當靜態數據,靜態代碼塊,對象成員,構造方法等同時存在時,那麼成員的初始化順序又是怎樣呢?
其實,初始化順序是:(靜態變量,靜態初始化塊) --->(變量,變量初始化塊)---->構造器
下面我們通過代碼來驗證這一規律:
- package com.jay.example;
- public class static3 {
- public static String staticStr = "靜態成員初始化";
- public String str = "普通成員初始化";
- //構造方法
- public static3() {
- System.out.println("構造方法初始化");
- }
- //普通的初始化塊
- {
- System.out.println(str);
- System.out.println("普通的初始化塊");
- }
- //靜態的初始化塊
- static
- {
- System.out.println(staticStr);
- System.out.println("靜態的初始化塊");
- }
- public static void main(String[] args) {
- static3 st = new static3();
- }
- }
運行截圖:
final:最終修飾符
使用方法
①使用final修飾屬性(變量),此時的屬性爲常量;Java中利用public static final int AGE = 10;對常量進行標識
②空白final變量(沒初始化的):空白的final數據成員必須在構造方法中進行初始化,否則會報錯
③final常量作爲方法的參數,只能夠對final進行簡單的引用,可不能改變常量的值哦!
④用final來修飾方法:那麼該方法爲一個不可覆蓋的方法,如果父類有final修飾的方法,那麼子類繼承同一個方法
⑤用final來修飾類:那麼該類不可以被繼承,final類沒有子類;同時該類中所有的方法都默認爲final
⑥final並不涉及繼承,繼承取決於類的修飾符是public還是其他,是否可以繼承取決於該類是否對其子類可見
如果一個方法前有private或static的修飾符,那麼系統在前面自動地加上final修飾
abstract:抽象修飾符
被abstract修飾的類爲抽象類,修飾的方法爲抽象方法
transient:
用於修飾不想序列化,持久化的成員
volatile:
保證可見性和防止重排序,用的比較少,是併發操作那塊的
枚舉類型
枚舉解析:
①作爲jdk 1.5 後引入的枚舉,用來代替以前定義多個同類型常量的public final static
②用enum表示,作用類似於class關鍵字;
③構造方法不要用public修飾
④變量和方法的定義需要在枚舉值的後面!!!
用法示例:
簡單的枚舉:
- package com.jay.example;
- /*
- * 本程序演示的是枚舉的最簡單用法
- * 定義枚舉Color有三個值,演示了switch和增強for循環遍歷枚舉
- * 中所有的原始的方法
- * */
- enum Color
- {
- BLUE,RED,YELLOW;
- }
- public class EnumTest {
- public void printColor(Color color)
- {
- switch(color)
- {
- case BLUE:
- System.out.println("輸出藍色!");
- break;
- case RED:
- System.out.println("輸出紅色!");
- break;
- case YELLOW:
- System.out.println("輸出黃色!");
- break;
- }
- }
- public static void main(String[] args) {
- EnumTest test = new EnumTest();
- test.printColor(Color.BLUE);
- test.printColor(Color.RED);
- test.printColor(Color.YELLOW);
- //當然也可以直接用增強for循環來遍歷枚舉中所有的值:
- //可以通過values()方法獲得枚舉的數組
- for(Color c : Color.values())
- {
- System.out.println(c);
- }
- }
- }
運行截圖:
深一步的使用:
- package com.jay.example;
- /*
- * 本代碼演示的是枚舉類的進一步使用
- * 和類一樣,枚舉可以定義自己的屬性和方法,但是
- * 必須寫在枚舉列表的後面,不然會報編譯時錯誤
- * */
- public class EnumTest2 {
- public enum Color
- {
- //枚舉可以像一般的類一樣添加方法和屬性
- Red("紅色"),BLUE("藍色"),YELLOW("黃色");
- //構造方法,不要寫public修飾!
- Color(String value)
- {
- this.value = value;
- }
- //定義一個成員變量
- private final String value;
- //定義一個獲取後面值得方法
- public String getValue()
- {
- return value;
- }
- }
- public static void main(String[] args) {
- EnumTest2 test = new EnumTest2();
- //可以通過for循環調用獲得枚舉中所有的值
- for(Color color : Color.values())
- {
- System.out.println(color);
- System.out.println(color.getValue());
- }
- //可以通過ordinal獲得枚舉值在枚舉中的索引位置,從0開始的
- System.out.println(Color.BLUE.ordinal());
- //枚舉默認實現了java.lang.Comparable接口,可以直接調用compareTo對枚舉值進行比較
- //這裏Red在Blue前面,所以是-1
- System.out.println(Color.Red.compareTo(Color.BLUE));
- }
- }
運行截圖:
小結:
在這裏,我們僅僅演示了enum枚舉類型的兩種基本用法,
如果有興趣深入研究枚舉的可以參考以下文檔:Java枚舉類型詳解
http://pan.baidu.com/s/1o6qHLUy