Summary
Java是面向?qū)ο?/strong>的語(yǔ)言,應(yīng)用Java就要深入理解并掌握J(rèn)ava中類(lèi)的定義、繼承、訪問(wèn)控制、修飾符和接口等有關(guān)面向?qū)ο蟮膬?nèi)容。
- 類(lèi)的組成、初始化和調(diào)用
- 類(lèi)的繼承
- 包
- 修飾符
- 接口
類(lèi)的組成、初始化和調(diào)用
提到面向?qū)ο笳Z(yǔ)言,直接想到的是類(lèi)的概念,類(lèi)是把對(duì)象抽象出來(lái)形成一類(lèi)對(duì)象的想象,類(lèi)封裝了一類(lèi)對(duì)象的狀態(tài)和方法。類(lèi)包含field和method。
field中文叫做字段、域、域變量、屬性、成員變量等,它是類(lèi)的屬性,通過(guò)變量來(lái)表示;method中文叫做方法,它是類(lèi)的功能和操作,通過(guò)函數(shù)來(lái)表示。
作者語(yǔ):類(lèi)就像現(xiàn)實(shí)物品的一個(gè)分類(lèi),比如鞋、衣服,它使得我們能夠更加高效的溝通,不僅在現(xiàn)實(shí)生活的對(duì)話中,也在編程的代碼間。
難以想象我們現(xiàn)實(shí)生活中的對(duì)話是:“請(qǐng)幫我拿一下那邊紅色的用來(lái)穿在腳上走路的物品”(請(qǐng)幫我拿下那邊紅色的鞋),所以寫(xiě)程序要用好類(lèi)的概念。
類(lèi)可以實(shí)例化,也就是建立一個(gè)具體的對(duì)象,這個(gè)時(shí)候需要初始化(通常用關(guān)鍵字new),Java是通過(guò)構(gòu)造方法constructor完成的。構(gòu)造方法和類(lèi)名是同名的,且不寫(xiě)返回值。
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}
如果類(lèi)沒(méi)有寫(xiě)構(gòu)造方法,則系統(tǒng)生成默認(rèn)構(gòu)造方法(default constructor)。
class Person{
String name;
int age;
//Person(){},這是系統(tǒng)生成的默認(rèn)構(gòu)造方法,編譯時(shí)會(huì)補(bǔ)充。
}
初始化并使用對(duì)象,Person類(lèi)新建一個(gè)對(duì)象p,輸出p的名字,如下:
Person p = new Person();
System.out.println(p.name);
擴(kuò)展:
1.如果多個(gè)方法有相同的名字,編譯時(shí)可以根據(jù)方法的簽名signature(參數(shù)類(lèi)型和個(gè)數(shù))識(shí)別出,即Java有方法重載overloading的能力,體現(xiàn)了面向?qū)ο笳Z(yǔ)言的多態(tài)polymorphism。下面這段程序sayHello有兩個(gè)方法,運(yùn)行結(jié)果是:
Hello! My name is Xiaoming
Hello Xiaohong! My name is Xiaoming
public class Example{
public static void main(String[] argv){
Person p1 = new Person("Xiaoming", 18);
Person p2 = new Person("Xiaohong", 14);
p1.sayHello();
p1.sayHello(p2);
}
}
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
void sayHello(){
System.out.println("Hello! My name is " + name);
}
void sayHello(Person another){
System.out.println("Hello " + another.name + "! My name is " + name);
}
}
2.在上述程序的構(gòu)造方法中出現(xiàn)了關(guān)鍵詞this,它的作用是引用字段field,添加它的目的是區(qū)分字段和局部變量。比如第一段程序構(gòu)造函數(shù)中有這樣一句代碼:this.name = name;
this.name代表Person的字段name,而等號(hào)后面的name是構(gòu)造方法的參數(shù)變量name。
類(lèi)的繼承
面向?qū)ο蟮娜蠛诵奶卣魇欠庋b、多態(tài)和繼承,在上一節(jié)中提到了封裝和多態(tài),這一節(jié)要談繼承。
說(shuō)到繼承,至少涉及兩個(gè)對(duì)象,稱為子類(lèi)subclass和父類(lèi)superclass。Java規(guī)定只能單繼承,一個(gè)類(lèi)只能有一個(gè)直接父類(lèi)。繼承符合DRY(Don't repeat yourself)原則,如果子類(lèi)有和父類(lèi)一致的字段或者方法,那么直接繼承,不要寫(xiě)重復(fù)的代碼。Java的繼承是通過(guò)關(guān)鍵詞extends來(lái)實(shí)現(xiàn)的,沿用上文Person的代碼,如下:
class Student extends Person{
String teacher;
int grades;
Student(String teacher, int grades, String name, int age){
super(name, age);
this.teacher = teacher;
this.grades = grades;
}
boolean isGood(){return grades > 80;}
}
subclass能繼承superclass的所有字段和非private方法,但不能繼承superclass的構(gòu)造方法。JDK1.5后可以用@Override標(biāo)記來(lái)表示subclass對(duì)superclass同名方法的覆蓋override。
上述程序的構(gòu)造方法中出現(xiàn)了關(guān)鍵詞super,它的作用是引用父類(lèi)的字段和方法。
作者語(yǔ):繼承是面向?qū)ο笳Z(yǔ)言中容易混亂的部分,尤其是多重繼承的時(shí)候,假設(shè)A繼承于B,B繼承于C,而C如果又繼承于A,這就是個(gè)死循環(huán)。Java單繼承的規(guī)定在一定程度上增強(qiáng)了安全性。
包
包package的出現(xiàn)是為了解決名字空間和名字沖突,一方面包是名字空間和存儲(chǔ)路徑,另一方面也規(guī)定了訪問(wèn)性,比如同一個(gè)包中默認(rèn)情況可互相訪問(wèn)。注:包與繼承沒(méi)有關(guān)系,一個(gè)superclass只要是public的,子類(lèi)可以在不同package中繼承superclass。
import語(yǔ)句可以導(dǎo)入包,基本語(yǔ)法是:
import package1[.package2...].classname;
例如:import java.util.Date;
作者語(yǔ):包可以理解為OS里的文件夾。
修飾符
修飾符modifier分為兩類(lèi):訪問(wèn)修飾符access modifier和其他修飾符。modifer可以修飾類(lèi)和類(lèi)的成員(field和method)。
1.訪問(wèn)修飾符
| | 同一個(gè)類(lèi)中 | 同一個(gè)包中(包括子類(lèi)) | 不同包中子類(lèi) | 不同包中非子類(lèi) |
|:--- :|:-----------:|:----------:|:-------------: |:-------------: |
|private| Yes | | | |
|默認(rèn)(包可訪問(wèn))| Yes | Yes | | |
|protected| Yes | Yes | Yes | |
|public| Yes | Yes | Yes| Yes |
private只能在同一個(gè)類(lèi)中訪問(wèn),可以更好的將信息進(jìn)行封裝和隱藏,存取時(shí)使用set和get方法;
public class Person2{
private int age;
public void setAge(int age){
if(age>0 && age <150){
this.age = age;
}
}
public int getAge(){
return age;
}
}
默認(rèn)是class前面不加修飾符,默認(rèn)同一個(gè)包和類(lèi)可以訪問(wèn);
protected只是不允許不同包當(dāng)中的非子類(lèi)訪問(wèn);
public的作用是任何包中都可以調(diào)用。
作者語(yǔ):訪問(wèn)權(quán)限由嚴(yán)格到寬松大體流程是,從最嚴(yán)格只能在同一個(gè)類(lèi)中訪問(wèn)的private開(kāi)始,到同一個(gè)包中可以訪問(wèn)的默認(rèn)修飾,再到寬松一些可以在不同包的子類(lèi)中調(diào)用的protected修飾,最后是最寬松的在任何包中都可以調(diào)用的public修飾。
2.其他修飾符
| 基本含義 | 修飾類(lèi) | 修飾成員 | 修飾局部變量 | ||
|---|---|---|---|---|---|
| static | 靜態(tài)的、非實(shí)例的、類(lèi)的 | 可以修飾內(nèi)部類(lèi) | Yes | ||
| final | 最終的、不可改變的 | Yes | Yes | Yes | |
| abstract | 抽象的、非實(shí)例的 | Yes | Yes |
static字段只屬于類(lèi),不屬于任何對(duì)象實(shí)例,例如:定義Person類(lèi),其中定義一個(gè)統(tǒng)計(jì)人數(shù)的參數(shù),并不屬于任何一個(gè)對(duì)象實(shí)例,
class Person{
static int totalNumber;
String name;
int age;
Person(String n, int a){
name = n;
age = a;
}
}
static方法是屬于整個(gè)類(lèi)的方法,不屬于某個(gè)實(shí)例,不能操縱處理某個(gè)實(shí)例的成員變量;
final修飾的類(lèi)不能被繼承,也就是說(shuō)不能有子類(lèi),final修飾的方法是不能被子類(lèi)覆蓋的方法,final修飾的字段是只讀的不能修改;
abstract修飾的類(lèi)不能實(shí)例化,abstract修飾的方法是抽象方法,為所有子類(lèi)定義統(tǒng)一接口,只需聲明,格式如下:
abstarct returnType abstractMethod([paramlist]);
接口
接口interface是某種特征的約定,它也是面向?qū)ο缶幊趟枷氲暮诵慕M成部分。
作者語(yǔ):比如,我們把現(xiàn)實(shí)中的飛機(jī)寫(xiě)成一個(gè)類(lèi),其中的戰(zhàn)斗機(jī)、運(yùn)輸機(jī)都是飛機(jī)這個(gè)類(lèi)的子類(lèi),有自己的特征。戰(zhàn)斗機(jī)中,殲擊機(jī)10是殲擊機(jī)9的新一代,我們說(shuō)殲擊機(jī)10繼承了殲擊機(jī)9,在殲擊機(jī)9的動(dòng)力系統(tǒng)基礎(chǔ)上,殲擊機(jī)10開(kāi)發(fā)了應(yīng)急情況另一個(gè)加速系統(tǒng),也就是說(shuō)有兩個(gè)動(dòng)力系統(tǒng)方法,一個(gè)是繼承殲擊機(jī)9,另一個(gè)是應(yīng)急下新增加的動(dòng)力系統(tǒng),這兩個(gè)就是多態(tài)的應(yīng)用。
除了飛機(jī)外,天空中還有鳥(niǎo)。鳥(niǎo)和飛機(jī)兩個(gè)類(lèi)都會(huì)寫(xiě)一個(gè)特征——它們能夠離開(kāi)地面XX米,且有自主控制能力……本著DRY的原則,這里需要優(yōu)化,接口能很好的解決這個(gè)問(wèn)題,我們可以寫(xiě)一個(gè)特征“飛”的接口,飛機(jī)和鳥(niǎo)以及其他能飛的物體都可以使用這個(gè)接口。
根據(jù)接口的定義,它的modifier默認(rèn)是public+abstarct,因?yàn)榻涌诘某霈F(xiàn)就是要讓其他類(lèi)使用,所以默認(rèn)是public的;而接口本身就是特征的約定,不是實(shí)例化的,所以默認(rèn)是abstract的。且接口是可以多繼承的,與類(lèi)的繼承不一樣,甚至有人會(huì)說(shuō)面向?qū)ο蟮木幊虒?shí)質(zhì)上是“面向接口的變成”,可見(jiàn)接口在面向?qū)ο笳Z(yǔ)言中的重要性。
作者語(yǔ):Java規(guī)定類(lèi)是單繼承,而接口在某種程度上說(shuō)可以打破這個(gè)限制,接口不關(guān)注類(lèi)之間的層次,可以直接使用某個(gè)特征方法函數(shù)。
接口的定義一般如下:
[public] interface InterfaceName [extends listOfSuperInterface]{
returnType methodName([paramlist]);
}
如果定義中沒(méi)有public聲明,則只能被同一個(gè)包中的類(lèi)訪問(wèn);
extends繼承部分與類(lèi)的繼承語(yǔ)法一致,不同的是接口可以多重繼承,用逗號(hào)隔開(kāi)即可;
接口中只進(jìn)行方法的聲明,不提供具體實(shí)現(xiàn),所以只有方法體,用“;”結(jié)束,這里的方法具有public和abstract屬性。
接口的實(shí)現(xiàn)用implements子句
class className implements interfaceName{
returnType methodName([paramlist]){
//寫(xiě)接口中所聲明方法的實(shí)現(xiàn)
}
}
接口中定義的常量具有public, static, final的屬性,一般大寫(xiě)名字:
type NAME = value;
下面的代碼用接口定義了人
interface Runner(){public void run();}
interface Swimmer(){public void swim();}
interface Flyable(){
abstract public void fly();
public void land();
public void takeoff();
}
abstract class Animal{
int age;
abstract public void eat();
}
class Person extends Animal implements Runner, Swimmer, Flyable{
public void run(){System.out.println("run");}
public void swim(){System.out.println("swim");}
public void fly(){System.out.println("fly");}
public void land(){System.out.println("land");}
public void takeoff(){System.out.println("takeoff");}
public void eat(){System.out.println("eat");}
}
public class Test{
public static void main(String[] args){
Test t = new Test();
Person p = new Person();
t.m1(p);
t.m2(p);
t.m3(p);
t.m4(p);
}
public void m1(Runner r){r.run();}
public void m2(Swimmer s){s.swim();}
public void m3(Flyable f){f.fly(); f.land(); f.takeoff();}
public void m4(Animal a){a.eat();}
}
To be continued
Java完整的源文件格式如下:
package packageName//指定文件中的類(lèi)所在的包,0/1個(gè)
import packageName.[className]; //指定引入的類(lèi),0/多個(gè)
public class classDefinition //屬性為public的類(lèi)定義,0/1個(gè),名字必須與文件名一致
{
[public] interface InterfaceName [extends listOfSuperInterface]//接口的定義
{
type constantName = value; //接口常量聲明,默認(rèn)是public, static, final的
returnType methodName([paramlist]); //接口方法聲明,沒(méi)有方法體
}
[public] [abstract|final] class className [extends superClassName] [implements InterfaceNameList] //類(lèi)的聲明
{
[public|protected|private] [static] [final] type variableName; //成員變量聲明
[public|protected|private] [static] [final] returnType methodName([paramlist]) [throws exceptionlist] //方法聲明及實(shí)現(xiàn)
{
statement;
}
}
}
有三種方法要求固定的聲明方式:
- 構(gòu)造方法
className([paramlist]){
statements;
}
- main方法
public static void main(String[] args){
statements;
}
- finalize方法
protected void finalize() throw throwable{
statements;
}