We believe that writing is about content, about what you want to say – not about fancy formatting.
我們堅信寫作寫的是內容蜕猫,所思所想绑警,而不是花樣格式。
— Ulysses
Java第5天上午
上午主要講了抽象類和接口
抽象類:
問題提出:
利用多態(tài)性實現(xiàn)不合理:
抽象的概念定義為抽象類/抽象方法更加合理
注意上圖中抽象方法的形式 沒有{ }!
抽象類的特點:
- 抽象類不能直接實例化岂却,但可以聲明出對象的引用以指向非抽象子類的對象(格式統(tǒng)一 方便編程)
- 除非子類采用具體方法替代抽象類中的全部抽象方法,否則子類本身也被自動認為是抽象的泽西!此時必須手動在子類上加上abstract關鍵字7鹑摇7毂颉萌焰!
編程練習:編寫程序,創(chuàng)建兩個幾何對象谷浅,一個圓和一個正方形扒俯,調用方法檢查兩個對象是否有相同的面積,
// Shape.java:
public abstract class Shape {
/**
* 抽象獲取面積的方法
* @return 形狀面積
*/
public abstract double getArea();
/**
* 比較兩個形狀的大小
* @param p 另一個形狀的實例
* @return 如果當前形狀較大則返回true
*/
public boolean compareArea(Shape p){
return this.getArea()>p.getArea();
}
}
// Circle.java:
public class Circle extends Shape{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI*radius*radius;
}
}
// Square.java:
public class Square extends Shape{
private double length;
public Square(double length) {
this.length = length;
}
@Override
public double getArea() {
return length*length;
}
}
// Test.java:
public class Test {
public static void main(String[] args) {
Shape s1 = new Square(2);
Shape s2 = new Circle(2);
System.out.println(s1.getArea());
System.out.println(s2.getArea());
System.out.println(s1.compareArea(s2));
}
}
// 結果:
4.0
12.566370614359172
false
接口:
接口更抽象壳贪,僅聲明了方法:
- 注意接口用 interface 關鍵字
implements關鍵字實現(xiàn)接口:
- 通過這樣的定義陵珍,Professor類替代了Teacher接口的所有方法,因此Professor類是一個具體的類违施。
- 但是如果Professor類沒有替換所有的接口互纯,則Professor類只能看成是一個抽象的類。編譯器會要求在Professor類的前面加上abstract關鍵字磕蒲。
- 因此留潦,實現(xiàn)接口實際上就是在創(chuàng)建抽象類的子類時,給抽象方法“加上具體的內容”辣往。
接口和抽象類區(qū)別:
- 接口與抽象類的不同:
- 接口只抽象行為兔院,而抽象類則要指定屬性、具體的方法和抽象的方法站削。
- 工程中坊萝,接口用的比抽象類更多一些
- 抽象類只能單繼承,而接口可以多實現(xiàn)许起!但是十偶,當將繼承(或實現(xiàn))的子類有默認方法實現(xiàn)時,必須用抽象類(畢竟是個class园细,功能多一些)
Abstract class | Interface | |
---|---|---|
實例化 | 不能 | 不能 |
類 | 一種繼承關系惦积,一個類只能使用一次繼承關系∶推担可以通過繼承多個接口實現(xiàn)多重繼承 | 一個類可以實現(xiàn)多個interface |
數(shù)據(jù)成員 | 可有自己的 | 靜態(tài)的不能被修改即必須是static final狮崩,一般不在此定義 |
方法 | 可以私有的蛛勉,非abstract方法,必須實現(xiàn) | 不可有私有的睦柴,默認是public诽凌,abstract 類型 |
變量 | 可有私有的,默認是friendly 型坦敌,其值可以在子類中重新定義皿淋,也可以重新賦值 | 不可有私有的,默認是public static final 型恬试,且必須給其初值,實現(xiàn)類中不能重新定義疯暑,不能改變其值训柴。 |
設計理念 | 表示的是“is-a”關系 | 表示的是“l(fā)ike-a”關系 |
實現(xiàn) | 需要繼承,要用extends | 要用implements |
一個類可以實現(xiàn)自多個接口:
類需要替代這兩個接口所定義的所有方法
重點說明: (用接口實現(xiàn)多重繼承)
多重繼承發(fā)生問題原因之一在于屬性(數(shù)據(jù)結構)沖突妇拯,也就是存儲空間的沖突幻馁。由于接口不與任何存儲空間相關聯(lián),因此可以解決存儲空間沖突的問題
對于繼承的方法的沖突越锈,當使用接口之后仗嗦,由于接口只定義了方法的抽象,沒有具體的執(zhí)行代碼甘凭,因此也不會發(fā)生代碼沖突的問題稀拐。
拓展示例:
- 非抽象類(具體類)不能包含抽象方法
- 如果抽象父類的子類,沒有實現(xiàn)所有的抽象方法丹弱,它必須聲明為抽象的(在這個類聲明的前面加上abstract關鍵詞) (貌似和前面重復了德撬,但是很重要,再說一次也無妨)
- 在一個由抽象類擴展出來的非抽象類中躲胳,所有的抽象方法都必須實現(xiàn)蜓洪,即使這個子類不使用它們。
- 抽象類不能用new運算符實例化坯苹,但仍應定義它的構造方法隆檀,這種構造方法將在它子類的構造方法中被調用。
- 允許聲明沒有抽象方法的抽象類粹湃。(用于定義新子類)
- 子類可以被聲明是抽象的恐仑,即使它的父類是具體的。
- 子類可以覆蓋它父類的方法再芋,并將其聲明為抽象的菊霜。(當父類方法中的實現(xiàn)在子類中無效時)
對比補充:
抽象類——模板設計模式:
抽象類小練習:
- 抽象類不能實例化,有構造方法济赎;接口也不能實例化鉴逞,但沒有構造方法记某!
- 接口不是類,不能繼承類构捡,可以繼承一個或多個接口
- 接口不能實現(xiàn)接口R耗稀!(只有類可以實現(xiàn)接口勾徽,注意區(qū)分實現(xiàn)和繼承接口的區(qū)別;埂)
- 抽象類是類,既可以繼承類喘帚,也可以實現(xiàn)很多其他接口
final:
拓展閱讀:
1 - Java 接口的作用和好處
2 - Java 接口(interface)的用途和好處
3 - Java 抽象類與接口的區(qū)別
4 - 詳細解析Java中抽象類和接口的區(qū)別
拓展:
了解Math類:
The class contains methods for performing basic numeric operations such as the elementary exponential, logarithm, square root, and trigonometric functions
里面包含了很多常用常量(如e和PI)和數(shù)學函數(shù)(如sin()畅姊、con()等):
下午講了內部類:
Java內部類
在Java中,可以將一個類定義在另一個類里面或者一個方法(類成員函數(shù))里面吹由,這樣的類稱為內部類若未。廣泛意義上的內部類一般來說包括四種:
- 成員內部類
- 局部內部類
- 匿名內部類
- 靜態(tài)內部類
成員內部類:
- 最普通的內部類,它的定義位于另一個類的內部
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { // 成員內部類
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
看起來倾鲫,類Draw像是類Circle的一個成員粗合,Circle稱為外部類。成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態(tài)成員)
class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //內部類
public void drawSahpe() {
System.out.println(radius); //外部類的private成員
System.out.println(count); //外部類的靜態(tài)成員
}
}
}
要注意的是乌昔,當成員內部類擁有和外部類同名的成員變量或者方法時隙疚,會發(fā)生隱藏現(xiàn)象,即默認情況下訪問的是成員內部類的成員磕道。如果要訪問外部類的同名成員供屉,需要以下面的形式進行訪問:
外部類.this.成員變量
外部類.this.成員方法
成員內部類可以無條件地訪問外部類的成員,而外部類想訪問成員內部類的成員卻不是這么隨心所欲了溺蕉。在外部類中如果要訪問成員內部類的成員贯卦,必須先創(chuàng)建一個成員內部類的對象,再通過指向這個對象的引用來訪問:
class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必須先創(chuàng)建成員內部類的對象焙贷,再進行訪問
}
private Draw getDrawInstance() {
return new Draw();
}
class Draw { //內部類
public void drawSahpe() {
System.out.println(radius); //外部類的private成員
}
}
}
成員內部類是依附外部類而存在的撵割,也就是說,如果要創(chuàng)建成員內部類的對象辙芍,前提是必須存在一個外部類的對象啡彬。創(chuàng)建成員內部類對象的一般方式如下:
public class Test {
public static void main(String[] args) {
//第一種方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必須通過Outter對象來創(chuàng)建
//第二種方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
內部類可以擁有private訪問權限、protected訪問權限故硅、public訪問權限及包訪問權限庶灿。比如上面的例子,如果成員內部類Inner用private修飾吃衅,則只能在外部類的內部訪問往踢,如果用public修飾,則任何地方都能訪問徘层;如果用protected修飾峻呕,則只能在同一個包下或者繼承外部類的情況下訪問利职;如果是默認訪問權限,則只能在同一個包下訪問瘦癌。這一點和外部類有一點不一樣猪贪,外部類只能被public和包訪問兩種權限修飾。
局部內部類:
局部內部類是定義在一個方法或者一個作用域里面的類讯私,它和成員內部類的區(qū)別在于局部內部類的訪問僅限于方法內或者該作用域內热押。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部內部類
int age =0;
}
return new Woman();
}
}
注意,局部內部類就像是方法里面的一個局部變量一樣斤寇,是不能有public桶癣、protected、private以及static修飾符的娘锁。
匿名內部類:
匿名內部類應該是平時我們編寫代碼時用得最多的鬼廓,在編寫事件監(jiān)聽的代碼時使用匿名內部類不但方便,而且使代碼更加容易維護致盟。
下面這段代碼是一段Android事件監(jiān)聽代碼:
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
這段代碼為按鈕設置監(jiān)聽器,這段代碼中的:
new OnClickListener() {
public void onClick(View v) {
}
}
上面就是匿名內部類的使用尤慰。代碼中需要給按鈕設置監(jiān)聽器對象馏锡,使用匿名內部類能夠在實現(xiàn)父類或者接口中的方法情況下同時產生一個相應的對象,但是前提是這個父類或者接口必須先存在才能這樣使用伟端。
當然像下面這種寫法也是可以的杯道,跟上面使用匿名內部類達到效果相同:
private void setListener()
{
scan_bt.setOnClickListener(new Listener1());
history_bt.setOnClickListener(new Listener2());
}
class Listener1 implements View.OnClickListener{
@Override
public void onClick(View v) {
}
}
class Listener2 implements View.OnClickListener{
@Override
public void onClick(View v) {
}
}
這種寫法雖然能達到一樣的效果,但是既冗長又難以維護责蝠,所以一般使用匿名內部類的方法來編寫事件監(jiān)聽代碼党巾。同樣的,匿名內部類也是不能有訪問修飾符和static修飾符的霜医。
匿名內部類是唯一一種沒有構造器的類齿拂。正因為其沒有構造器,所以匿名內部類的使用范圍非常有限肴敛,大部分匿名內部類用于接口回調署海。匿名內部類在編譯的時候由系統(tǒng)自動起名為Outter$1.class。一般來說医男,匿名內部類用于繼承其他類或是實現(xiàn)接口砸狞,并不需要增加額外的方法,只是對繼承方法的實現(xiàn)或是重寫镀梭。
靜態(tài)內部類:
靜態(tài)內部類也是定義在另一個類里面的類刀森,只不過在類的前面多了一個關鍵字static。靜態(tài)內部類是不需要依賴于外部類的报账,這點和類的靜態(tài)成員屬性有點類似研底,并且它不能使用外部類的非static成員變量或者方法埠偿,這點很好理解,因為在沒有外部類的對象的情況下飘哨,可以創(chuàng)建靜態(tài)內部類的對象胚想,如果允許訪問外部類的非static成員就會產生矛盾,因為外部類的非static成員必須依附于具體的對象芽隆。
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
內部類的使用場景和好處:
為什么在Java中需要內部類浊服?
總結一下主要有以下四點:
- 每個內部類都能獨立的繼承一個接口的實現(xiàn),所以無論外部類是否已經繼承了某個(接口的)實現(xiàn)胚吁,對于內部類都沒有影響牙躺。內部類使得多繼承的解決方案變得完整
- 方便將存在一定邏輯關系的類組織在一起,又可以對外界隱藏
- 方便編寫事件驅動程序
- 方便編寫線程代碼
認為第一點是最重要的原因之一腕扶,內部類的存在使得Java的多繼承機制變得更加完善
補充:
關于成員內部類的繼承問題孽拷。一般來說,內部類是很少用來作為繼承用的半抱。但是當用來繼承的話脓恕,要注意兩點:
- 成員內部類的引用方式必須為 Outter.Inner 形式
- 構造器中必須有指向外部類對象的引用,并通過這個引用調用super() (見下面代碼)
class WithInner {
class Inner{
}
}
class InheritInner extends WithInner.Inner {
// InheritInner() 是不能通過編譯的窿侈,一定要加上形參
InheritInner(WithInner wi) {
wi.super(); //必須有這句調用
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner obj = new InheritInner(wi);
}
}
每日一練:
課堂作業(yè):
沒留 暫無
課外練習:
課外作業(yè)點我查看
拓展閱讀:
1 - Java內部類的使用小結
2 - java提高篇(八)----詳解內部類
3 - java中的內部類總結
后記
世界上所有的追求都是因為熱愛
一枚愛編碼 愛生活 愛分享的IT信徒
— hongXkeX