Java【第八篇】面向對象之高級類特性

static 關鍵字

當咱們編寫一個類時,其實就是在描述其對象的屬性和行爲,而並無產生實質上的對象,只有經過new關鍵字纔會產生出對象,這時系統纔會分配內存空間給對象,其方法才能夠供外部調用。咱們有時候但願不管是否產生了對象或不管產生了多少對象的狀況下,某些特定的數據在內存空間裏只有一份,例如全部的中國人都有個國家名稱,每個中國人都共享這個國家名稱,沒必要在每個中國人的實例對象中都單獨分配一個用於表明國家名稱的變量。在Java類中聲明變量、方法和內部類時,可以使用關鍵字static作爲修飾符。static標記的變量或方法由整個類(全部實例)共享,如訪問控制權限容許,可沒必要建立該類對象而直接用類名加‘.’調用。static成員也稱類成員或靜態成員,如:類變量、類方法、靜態方法等。java

類變量

類變量(類屬性)由該類的全部實例共享,類屬性相似於全局變量。編程

類方法

沒有對象的實例時,能夠用類名.方法名()的形式訪問由static標記的類方法;
在static方法內部只能訪問類的static屬性,不能訪問類的非static屬性。
由於不須要實例就能夠訪問static方法,所以static方法內部不能有this,(也不能有super ) 設計模式

在靜態方法裏只能直接調用同類中其它的靜態成員(包括變量和方法),而不能直接訪問類中的非靜態成員。這是由於,對於非靜態的方法和變量,須要先建立類的實例對象後纔可以使用,而靜態方法在使用前不用建立任何對象。
靜態方法不能以任何方式引用this和super關鍵字。與上面的道理同樣,由於靜態方法在使用前不用建立任何實例對象,當靜態方法被調用時,this所引用的對象根本就沒有產生。
main() 方法是靜態的,所以JVM在執行main方法時不建立main方法所在的類的實例對象,於是在main()方法中,咱們不能直接訪問該類中的非靜態成員,必須建立該類的一個實例對象後,才能經過這個對象去訪問類中的非靜態成員,這種狀況,咱們在之後的例子中會屢次碰到。數組

類屬性、類方法的設計思想

類屬性做爲該類各個對象之間共享的變量。在設計類時,分析哪些類屬性不因對象的不一樣而改變,將這些屬性設置爲類屬性。相應的方法設置爲類方法。
若是方法與調用者無關,則這樣的方法一般被聲明爲類方法,因爲不須要建立對象就能夠調用類方法,從而簡化了方法的調用安全

示例

練習1:編寫一個類,實現銀行帳戶的概念,包含的屬性有「賬號」、「密碼」、「存款餘額」、「利率」、「最小余額」,定義封裝這些
屬性的方法。帳號要自動生成。
編寫主類,使用銀行帳戶類,輸入、輸出3個儲戶的上述信息。
考慮:哪些屬性能夠設計成static屬性。 Bank.java ide

package com.uncleyong;

/**
 * 編寫一個類,實現銀行帳戶的概念,包含的屬性有「賬號」、「密碼」、「存款餘額」、「利率」、「最小余額」,定義封裝這些
 * 屬性的方法。帳號要自動生成。
 */
public class BankAccount {
    //初始化的 id
    private static int initId = 1000;

    //帳號
    private String id;

    //密碼
    private String password;

    //餘額
    private int balance;

    //利率
    private static double rate;

    //最小余額
    private static int minBalance;

    public BankAccount(String password, int balance) {
        this.id = "" + (initId++);  // initId自增
        this.password = password;
        this.balance = balance;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public static double getRate() {  // 對象有static修飾符,因此對應的get方法也會自動加static
        return rate;
    }

    public static void setRate(double rate) {
        BankAccount.rate = rate;
    }

    public static int getMinBalance() {  // 對象有static修飾符,因此對應的get方法也會自動加static
        return minBalance;
    }

    public static void setMinBalance(int minBalance) {
        BankAccount.minBalance = minBalance;
    }

    @Override
    public String toString() {
        return "BankAccount{" +
                "id='" + id + "'" +
                ", password='" + password + "\'" +
                ", balance=" + balance +
                ", rate=" + rate +
                ", minBalance=" + minBalance +
                '}';
    }
}

  

package com.uncleyong;

/**
 * 編寫主類,使用銀行帳戶類,輸入、輸出3個儲戶的上述信息。
 */
public class TestBank {

    public static void main(String[] args) {

        //統一設置 rate 和 minBalance
        BankAccount.setMinBalance(100);
        BankAccount.setRate(0.01);

        BankAccount acc1 = new BankAccount("1234", 100);
        BankAccount acc2 = new BankAccount("1235", 200);
        BankAccount acc3 = new BankAccount("1236", 300);

        System.out.println(acc1);
        System.out.println(acc2);
        System.out.println(acc3);
    }

}

靜態初始化

一個類中可使用不包含在任何方法體中的靜態代碼塊(static block ),當類被載入時,靜態代碼塊被執行,且只被執行一次,靜態塊常常用來進行類屬性的初始化。
static塊一般用於初始化static (類)屬性函數

class Person {
    public static int total;
    static {
        total = 100;//爲total賦初值 
    }
    …… //其它屬性或方法聲明
} 

單子 Singleton 設計

設計模式是在大量的實踐中總結和理論化以後優選的代碼結構、編程風格、以及解決問題的思考方式。設計模式就想是經典的棋譜,不一樣的棋局,咱們用不一樣的棋譜,省得咱們本身再去思考和摸索。
所謂類的單態設計模式,就是採起必定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,而且該類只提供一個取得其對象實例的方法。若是咱們要讓類在一個虛擬機中只能產生一個對象,咱們首先必須將類的構造方法的訪問權限設置爲private,這樣,就不能用new 操做符在類的外部產生類的對象了,但在類內部仍能夠產生該類的對象。由於在類的外部開始還沒法獲得類的對象,只能調用該類的某個靜態方法以返回類內部建立的對象,靜態方法只能訪問類中的靜態成員變量,因此,指向類內部產生的該類對象的變量也必須定義成靜態的。測試

class Single{
 	private static Single onlyone = new Single();//私有的,只能在類的內部訪問
 	private String name;
	public static Single getSingle() {   //getSingle()爲static,不用建立對象  
                                //便可訪問
 		return onlyone;
 	}
 	private Single() {}     //private的構造器,不能在類的外部建立該類的對象
  	}

public class TestSingle{
	public static void main(String args[]) {		
	Single  s1 = Single.getSingle();      //訪問靜態方法
	Single  s2 = Single.getSingle();
	if (s1==s2){
			    System.out.println("s1 is equals to s2!");
		         }
	}
}

理解main方法的語法

因爲java虛擬機須要調用類的main()方法,因此該方法的訪問權限必須是public,又由於java虛擬機在執行main()方法時沒必要建立對象,因此該方法必須是static的,該方法接收一個String類型的數組參數,該數組中保存執行java命令時傳遞給所運行的類的參數。this

package com.uncleyong;

public class CommandPara{
    public static void main(String[] args) {
        for ( int i = 0; i < args.length; i++ ) {
            System.out.println("args[" + i + "] = " + args[i]);
        }
    }
}  

結果spa

final 關鍵字 

在Java中聲明類、屬性和方法時,可以使用關鍵字final來修飾。
final標記的變量(成員變量或局部變量)即成爲常量,只能賦值一次。final PI=3.14;
final標記的類不能被繼承。提升安全性,提升程序的可讀性。
final標記的方法不能被子類重寫。增長安全性。
final標記的成員變量必須在聲明的同時或在每一個構造方法中顯式賦值,而後才能使用。

示例

package com.uncleyong;

public class TestFinal {

    int i = 10;
    int j;

    final int m = 10;
    final int n;

    public TestFinal() {  // 構造方法中賦值
        n = 100;
    }

    public void test(){  // 能夠在方法中爲成員變量賦值
        i = 10;
        j = 20;

//		m = 100;
    }

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        System.out.println(testFinal.i + " "+ testFinal.j);  // 10 0
        testFinal.test();
        System.out.println(testFinal.i + " "+ testFinal.j);  // 10 20

    }

}

final class A{

}

//class B extends A{
//
//}

class C{
    void method1(){}
}

class D extends C{
    @Override
    void method1() {
        super.method1();
    }
}

抽象類(abstract 關鍵字) 

隨着繼承層次中一個個新子類的定義,類變得愈來愈具體,而父類則更通常,更通用。類的設計應該保證父類和子類可以共享特徵。有時將一個父類設計得很是抽象,以致於它沒有具體的實例,這樣的類叫作抽象類。
用abstract關鍵字來修飾一個類時,這個類叫作抽象類;用abstract來修飾一個方法時,該方法叫作抽象方法。抽象方法只有方法的聲明,沒有方法的實現,以分號結束。abstract int abstractMethod1( int a );
含有抽象方法的類必須被聲明爲抽象類。抽象類不能被實例化。抽象類是用來被繼承的,抽象類的子類必須重寫父類的抽象方法,並提供方法體。不能用abstract修飾私有方法,構造方法,靜態方法。

抽象類是用來模型化那些父類沒法肯定所有實現,而是由其子類提供具體實現的對象的類。  

示例

package com.uncleyong;

public class TestAbstract {

    public static void main(String[] args) {
        E e = new F();
    }

}

abstract class E{
    abstract void test();  // 抽象方法沒有方法體

    private void aa(){}
}

class F extends E{

    @Override
    void test() {

    }

    void aa(){}

}

abstract class G extends E{

}  

接口(interface 關鍵字) 

有時必須從幾個類中派生出一個子類,繼承它們全部的屬性和方法。可是,Java不支持多重繼承。有了接口,就能夠獲得多重繼承的效果。
接口(interface)是抽象方法和常量值的定義的集合。
從本質上講,接口是一種特殊的抽象類,這種抽象類中只包含常量和方法的定義,而沒有變量和方法的實現。接口定義舉例

public interface Runner {
    int id = 1;
    public void start();
    public void run();
    public void stop();
}

接口的特色:

用 interface 來定義。
接口中的全部成員變量都默認是由public static final修飾的。
接口中的全部方法都默認是由public abstract修飾的。接口沒有構造方法。
實現接口的類中必須提供接口中全部方法的具體實現內容。
多個無關的類能夠實現同一個接口
一個類能夠實現多個無關的接口
與繼承關係相似,接口與實現類之間存在多態性
接口也能夠繼承另外一個接口,使用extends關鍵字。

定義Java類的語法格式:

< modifier> class < name> [extends < superclass>][implements < interface> [,< interface>]* ] {
    < declarations>*
}

示例

定義一個接口用來實現兩個對象的比較。

interface CompareObject{
    public int compareTo(Object o); 
    //若返回值是 0 , 表明相等; 若爲正數,表明當前對象大;負數表明當前對象小
}

定義一個Circle類。
定義一個ComparableCircle類,繼承Circle類而且實現CompareObject接口。在ComparableCircle類中給出接口中方法compareTo的實現體,用來比較兩個圓的半徑大小。
定義一個測試類TestInterface,建立兩個ComaparableCircle對象,調用compareTo方法比較兩個類的半徑大小。
思考:參照上述作法定義矩形類Rectangle和ComparableRectangle類,在ComparableRectangle類中給出compareTo方法的實現,比較兩個矩形的面積大小。 

package com.uncleyong;

interface CompareObject{
    //若返回值是 0 , 表明相等; 若爲正數,表明當前對象大;負數表明當前對象小
    public int compareTo(Object o);  // 當前對象和對象o比較
}

  

package com.uncleyong;


public class Circle {

    protected double radius;

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public Circle(double radius) {
        this.radius = radius;
    }

}

  

package com.uncleyong;

/**
 * 定義一個ComparableCircle類,繼承Circle類而且實現CompareObject接口。
 * 在ComparableCircle類中給出接口中方法compareTo的實現體,用來比較兩個圓的半徑大小。
 */
public class ComparableCircle extends Circle implements CompareObject{

    public ComparableCircle(double radius) {
        super(radius);  // 父類只有一個有參構造函數
    }

    @Override
    public int compareTo(Object o) {  // 當前對象和對象o比較

        //1. 檢驗 o 是否爲 Circle 類型
        if(o instanceof Circle){
            Circle circle = (Circle) o;  // 強轉
            //2. 比較半徑
            return (int)(this.radius - circle.radius);
        }

        return 0;
    }

}

  

package com.uncleyong;

public class TestInterface {
    public static void main(String[] args) {
        ComparableCircle cc1 = new ComparableCircle(2);
        ComparableCircle cc2 = new ComparableCircle(7);

        ComparableCircle cc3 = new ComparableCircle(2);
        ComparableCircle cc4 = new ComparableCircle(1);

        ComparableCircle cc5 = new ComparableCircle(2);
        ComparableCircle cc6 = new ComparableCircle(2);

        System.out.println(cc1.compareTo(cc2));
        System.out.println(cc3.compareTo(cc4));
        System.out.println(cc5.compareTo(cc6));
    }
}

內部類及匿名內部類

在Java中,容許一個類的定義位於另外一個類的內部,前者稱爲內部類
內部類和外層封裝它的類之間存在邏輯上的所屬關係
Inner class通常用在定義它的類或語句塊以內,在外部引用它時必須給出完整的名稱。 Inner class的名字不能與包含它的類名相同;
Inner class可使用包含它的類的靜態和實例成員變量,也可使用它所在方法的局部變量;

Inner class能夠聲明爲抽象類 ,所以能夠被其它的內部類繼承。也能夠聲明爲final的。
和外層類不一樣,Inner class能夠聲明爲private或protected;
Inner class 能夠聲明爲static的,但此時就不能再使用外層封裝類的非static的成員變量;
非static的內部類中的成員不能聲明爲static的,只有在頂層類或static的內部類中才可聲明static成員。

示例

package com.uncleyong;

public class OuterClass {

    int age;

    // 靜態內部類,不能夠引用外部類非靜態成員,由於有靜態內部類的時候,可能還沒外部類的對象
    static class StaticInnerClass{
        void test(){
//            System.out.println(age);  // 報錯
        }
    }

    String name = "-1";  // 寫爲static String name = "-1";,也能夠被非靜態內部類引用

    // 非靜態內部類,能夠引用外部類的非靜態和靜態成員
    class InnerClass{
        String name = "0";
        public void test(){
            String name = "1";

            System.out.println(name); //1
            System.out.println(this.name); //0
            System.out.println(OuterClass.this.name); //-1
        }

    }
}

  

package com.uncleyong;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestInnerClass {
    public static void main(String[] args) {

        OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();  // 靜態內部類的建立: 再也不須要外部類的實例

        OuterClass oc = new OuterClass();  // 非靜態內部類的建立, 先建立外部類的實例, 再經過 外部類名.new 建立內部類的實例
        OuterClass.InnerClass in = oc.new InnerClass();
        in.test();

        // InvocationHandler是一個接口,直接建立接口new InvocationHandler(),同時提供其方法的實現
        // 實際上是建立了一個內部類對象,可是不知道其名字(有對象名,沒內部類名),因此叫匿名內部類對象
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        };

        // 寫得更確切一點(純匿名內部類對象,連對象的引用都沒有;建立一個接口的對象,直接提供其方法的實現)
        Proxy.newProxyInstance(null, null, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });
    }
}
相關文章
相關標籤/搜索