注:本文轉載自coder-pig
原文鏈接請戳:http://blog.csdn.net/coder_pig/article/details/22296069
Java面向對象 Part 2
前言
在上一節中,我們對面向對象進行了
初步的解析,掌握了類和對象的概念
以及定義,實現,在這一節中,我們將對面向對象的靈魂
繼承與多態進行剖析,同時也會講解下4種內部類的使用
好了,廢話不多說!
繼承:
繼承的理解:
比如:城管,公務員,學生,警察,他們的共性都是人,所以人類是他們的父類,而他們是人類的子類;
子類擁有父類的所有屬性,比如姓名,年齡等,同時子類也衍生了一些自己的特性:比如城管擁有暴力執法的屬性...
呃,這裏說笑而已= =,應該不會給查水錶吧
繼承的方式:
子類 extends 父類
eg: class Student extends Person{}
繼承的注意事項:
①Java的數據結構:樹形,所以不像C++一樣可繼承多個父類,只能單繼承;Java通過接口和內部類實現多繼承
②父類的私有屬性也可以繼承,但是不可以直接進行訪問,只能夠通過父類的方法進行訪問
③子類如果想調用父類的構造方法要使用super(參數),否則會提示找不到
④Object類是所有類的父類!!
子類構造對象的順序:
先構造父類的對象,再構造子類對象:
遞歸構造父類對象,順序地調用本類成員的賦初值語句,本類的構造方法
繼承代碼示例:
代碼解析:定義一個父類與子類,跟蹤對象的構造順序,同時在子類中調用父類的方法和重寫父類方法
Father.java
- package com.jay.example;
- public class Father {
- //定義兩個私有變量,子類無法直接訪問
- private String name;
- private int age;
- //一個好的編程習慣是寫上默認的缺省的構造方法
- //在下面的兩個構造方法中我們都加入一個println語句
- //以此來跟蹤構造對象的過程
- public Father()
- {
- System.out.println("父類無參構造方法被調用");
- }
- public Father(String name,int age)
- {
- this.name = name;
- this.age = age;
- System.out.println("父類的有參構造方法被調用");
- }
- /*設置這些get和set方法是因爲封裝
- *我們在子類中想訪問父類的私有元素就只能夠這樣了
- *通過在父類暴露一個藉口提供給子類訪問
- */
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- /*
- * 這個方法是想在子類中調用的
- * */
- public void printMessage()
- {
- System.out.println(name + "今年" + age + "歲");
- }
- /*
- * 這個方法是供子類覆蓋重寫的
- * */
- public void test()
- {
- System.out.println("我是父類的test");
- }
- }
Son.java
- package com.jay.example;
- public class Son extends Father{
- //定義子類自己的屬性
- private int grade;
- /*
- * 同理,爲兩個構造方法添加println語句用於跟蹤構造對象的順序
- * */
- Son(){
- System.out.println("子類的無參構造方法被調用了");
- }
- Son(String name,int age,int grade){
- //通過super關鍵字調用父類的構造方法,進行參數傳遞
- super(name,age);
- this.grade = grade;
- System.out.println("子類的有參構造方法被調用了");
- }
- public int getGrade() {
- return grade;
- }
- public void setGrade(int grade) {
- this.grade = grade;
- }
- //調用父類的方法訪問父類的私有變量
- public void printMsg()
- {
- System.out.println(getName()+"小學生今年"+getAge()+"歲,讀"+ grade + "年級");
- }
- //重寫父類的test方法
- public void test()
- {
- System.out.println("我是子類的test");
- }
- }
ExtendTest.java
- package com.jay.example;
- public class ExtendTest {
- public static void main(String[] args) {
- Son son1 = new Son();
- son1.setName("大豬");
- son1.setAge(25);
- son1.printMessage();
- System.out.println();
- Son son2 = new Son("小豬",20,1);
- son2.printMsg();
- System.out.println();
- son2.test();
- }
- }
輸出結果:
結果分析:
①從上面我們可以知道"先有父親,後又兒子";創建子類對象時會先創建一個父類對象,
不用我們自己實例化父類對象,系統自動創建;
②我們可以通過super()訪問父類構造方法或者用super.XXX訪問父類的同名成員
③子類可以重寫父類中的方法成員
super關鍵字:
子類中訪問父類的同名成員,用super語句向父類構造方法傳,那麼super(參數)方法要放在第一句
多態:
多態的分類:
編譯時多態(方法重載) OverLoading
在同一個類中可以定義多個同名的方法,各個方法的參數表一定不同(參數個數,類型)
返回值可以不同;比如構造方法的重載就是編譯時的多態了
代碼示例:
- package com.jay.example;
- class HeHe
- {
- public HeHe(){}
- //這裏重寫幾個一樣的show方法
- public void show()
- {
- System.out.println("①重寫");
- }
- public void show(int a)
- {
- System.out.println("②重寫");
- }
- public void show(float a)
- {
- System.out.println("③重寫");
- }
- public void show(int a,float b)
- {
- System.out.println("④重寫");
- }
- }
- public class OverLoadingTest
- {
- public static void main(String[] args) {
- HeHe he = new HeHe();
- he.show();
- he.show(1);
- he.show(0.1f);
- he.show(2, 0.2f);
- }
- }
運行結果:
運行時多態:(方法重定義/覆蓋) OverRidding
運行時多態 = 繼承 + 重定義 + 動態綁定
方法覆蓋:
子類方法與父類方法名,參數相同,返回值類型也相同;
規律:不能迴避比父類方法更多的異常,子類訪問控制必須必父類方法更公開
向上轉型規則(upcasting)
Animal a = new dog();
Dog d = (Dog)a;
這裏有個問題:
如果調用a的叫方法,jiao()那麼,調用的會是Animal的叫還是Dog的叫?
答案是:狗叫,new後面是什麼類,動態類型就是什麼類
不懂沒關係,看下面的演示代碼
- package com.jay.example;
- /*
- * 代碼解析:
- * 在代碼中我們定義了三個類,父類,子類以及一個測試類
- * 我們要驗證的是Father f = new Son(123);當我們調用相同名稱的方法時
- * 是以父類的方法還是以子類的方法爲準
- * 從代碼可知,我們對print方法進行了重載
- * */
- class Father {
- public Father() {
- System.out.println("父類構造方法打印前\t");
- print();
- System.out.println("父類構造方法打印後\t");
- }
- public void print()
- {
- System.out.println("父類的打印方法\t");
- }
- }
- class Son extends Father{
- int flag;
- public Son(int flag) {
- this.flag = flag;
- System.out.println("子類的構造方法\t"+ this.flag);
- }
- public void print()
- {
- System.out.println("子類打印方法\t" + flag);
- }
- }
- public class OverRridingTest
- {
- public static void main(String[] args) {
- Father f = new Son(123);
- }
- }
運行結果:
分析總結:
從上面的代碼,相信大家對運行時的多態有更深一步的瞭解了;
第二行輸出 0 ,說明了調用的是子類的print,這也說明了new後面什麼類型,動態類型就是什麼類型
內部類:
內部類有:成員內部類,靜態內部類,局部內部類,匿名內部類四種
成員內部類:
將一個類嵌套定義在另一個類中,包含內部類的類叫做外部類
內部類可以直接訪問外部類的成員和方法
注意:訪問內部類的話需要先創建外部類對象,接着纔可以用它來創建內部類對象
代碼示例:
- package com.jay.example;
- /*
- * 該代碼演示瞭如何訪問內部類,有以下兩種方式
- * ①在外部類中直接實例化內部類,並且調用內部類的方法或者參數
- * ②在測試類中先實例化外部類,在實例化內部類,從而調用對應的方法
- * eg:注意是這樣寫哦!Outer.Inner iner = out1.new Inner();
- * */
- class Outer
- {
- String outString = "外部類的成員變量被訪問";
- //在該方法中實例化內部類,並且調用show方法
- void showinner()
- {
- Inner in = new Inner();
- in.show();
- }
- //創建一個成員內部類
- class Inner
- {
- void show()
- {
- System.out.println("內部類的show方法被調用!");
- System.out.println(outString);
- }
- //用於演示在其他類中使用內部類的方法
- void test()
- {
- System.out.println("在測試類中訪問內部類的方法");
- }
- }
- }
- public class InnerTest {
- public static void main(String[] args) {
- Outer out1 = new Outer();
- out1.showinner();
- //直接實例化內部類,注意寫法!
- //如果寫成Outer.Inner iner = new Outer.Inner();是錯的哦!
- Outer.Inner iner = out1.new Inner();
- iner.test();
- }
- }
運行結果:
靜態內部類:
是成員內部類的一種,但又是特殊的,他具有以下特性:
靜態內部類只能夠訪問外部類的靜態成員;
創建內部類對象不需要外部對象,直接:new 外部類名.內部類的構造方法
代碼示例:
- package com.jay.example;
- /*
- * 該代碼演示了靜態內部類的使用
- * 內部類對象只能夠訪問外部類對象中的靜態成員
- * 如果調用非靜態成員會報錯
- * 也演示了兩種實例化靜態內部類成員的辦法
- * 直接new 外部類.內部類即可
- * */
- class Draw
- {
- public static int k = 250;
- public void print()
- {
- Pen p = new Pen("豬頭");
- p.speak();
- }
- static class Pen
- {
- int i = 100;
- String name ;
- public Pen(String name)
- {
- this.name = name;
- }
- void speak()
- {
- System.out.println(name + "你好!\t"+k);
- }
- }
- }
- public class StaticTest {
- public static void main(String[] args)
- {
- Draw d = new Draw();
- d.print();
- Draw.Pen pen= new Draw.Pen("呵呵");
- pen.speak();
- }
- }
運行截圖:
局部內部類:
與成員內部類不同,局部內部類是存在於類中的某個局部,可能是在某個方法或者某個塊語句中
是用的最少的一種內部類,和局部變量一樣,不能夠被public,protected,private和static修飾
只能夠訪問方法中定義的final類型的局部變量
只能夠在方法中生成局部內部類的對象並且調用它的方法
實例代碼:
- package com.jay.example;
- /*
- * 該代碼演示的是
- * 在外部類的方法中創建一個局部內部類
- * 同時需要在方法中實例化這個內部類對象
- * 而內部類可以訪問外部類成員和方法中final修飾的成員變量
- * */
- class Outer
- {
- int a = 1;
- public void show()
- {
- int b = 2;
- final int c = 3;
- //定義一個局部內部類
- class Inner
- {
- public void inPrint()
- {
- System.out.println("局部內部類的方法:");
- //可以訪問外部類中的變量
- System.out.println(a);
- //system.out.printf(b);這裏可是會報錯的哦!
- //可以訪問方法中的final的變量
- System.out.println(c);
- }
- }
- //需要在方法中實例化內部類對象哦!
- new Inner().inPrint();
- }
- }
- public class LoaclTest {
- public static void main(String[] args) {
- //創建外部類對象,同時調用外部類的方法
- Outer out = new Outer();
- out.show();
- }
- }
匿名內部類:
沒有名稱的內部類,必須是非靜態的類
通常是隱式地繼承一個父類或者實現一個接口
用的比較多的是作爲某個方法的參數,如Java中的GUI事件編程,android中事件處理機制
比較簡單,看下下面android中爲按鈕設置點擊事件的方法就瞭解了
- btnregister.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- Intent it = new Intent(LoginActivity.this,RegisterActivity.class);
- startActivityForResult(it, 0x123);
- overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
- }
- });
代碼解析:
代碼實現了爲按鈕設置一個點擊事件,參數,直接是通過new OnClickListener(){}這裏實例化一個匿名內部類對象
作爲參數,並且重寫OnClickListerner接口中的onClick()方法
總結:
在這一節中,我們深入面向對象,學習了面向對象的核心部分
繼承與多態,並且瞭解了四種內部類的使用
內容不多,讀者寫相關代碼體現其中的奧祕!
好的,最後謝謝各位讀者的支持
如果有什麼紕漏,錯誤,不好的,或者什麼好的建議;
望讀者指出,不甚感激,(*^__^*) 嘻嘻……!