文章目錄
- 抽象類
-
- 概述&特點
- 細節
- 練習
- 接口
-
- 概述
- 特點
- 好處&作用
- 避免單繼承的局限性
- 接口多繼承
- 抽象類是否可以不定義抽象方法
- 接口的思想
- 抽象類和接口的差別
- 多态
-
- 概述
- 多态-向上向下轉型
- 練習
- 多态-USB接口練習
- 多态中對成員的調用
-
- 1.成員變量
- 2.成員函數
- 3.靜态函數
- 練習
抽象類
概述&特點
描述 狗。吼叫。
描述 狼。吼叫。
兩個事物具備共性,向上抽取。犬科,具備吼叫功能。
描述一個事物,卻沒有足夠資訊。這時就将這個事物稱為抽象事物。
面向抽象的事物,雖然不具體,但是可以簡單化。
不用面對具有的事物。
特點:
1.抽象方法一定定義在抽象類中,都需要用abstract來修飾。
2.抽象類不能執行個體化,不能用new關鍵字建立對象。
3.隻有子類覆寫了所有的抽象方法後,子類具體化,子類就可以建立對象。
如果沒有覆寫所有的抽象方法,那麼子類還是一個抽象類。
抽象類也是不斷地向上抽取而來的。抽取了方法的聲明而不确定具體的方法内容。
由不同的子類來完成具體的方法内容。
abstract class 犬科//抽象類
//類也要辨別為abctract
{
abstract void 吼叫(); //抽象函數
//抽象關鍵詞abstract
}
class 狗 extends 犬科
{
void 吼叫()
{
System.out.println("汪汪");
}
}
class 狼 extends 犬科
{
void 吼叫()
{
System.out.println("嗷嗷");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
狗 x=new 狗();
x.吼叫();
}
}
細節
問題:
1.抽象類中有構造函數嗎?
有。抽象類的構造函數雖然不能給抽象類對象執行個體化。因為抽象類不能建立對象。
但是抽象類有子類,它的構造函數可以子類的對象執行個體化。
抽象類和一般類的異同點?
相同:都是用來描述事物的,都可以進行屬性和行為的描述。
不同:抽象類描述事物的資訊不具體,一般類描述事物的資訊具體。
代碼的不同:
抽象類中可以定義抽象方法,一般類不行。
抽象類不可以執行個體化,一般類可以。
2.抽象類一定是個父類麼啊?
是的。必須需要子類覆寫抽象方法後,才可以執行個體化。使用這些方法。
abstract class Demo extends XXX
{
}
class sub extends Demo
{
}
3.抽象類中可以不定義抽象方法嗎?
可以的。僅僅是讓該類不能建立對象。(技巧應用)
4.抽象關鍵字abstract和哪些關鍵字不能共存呢?
(非法修飾符子符)
final;子類不可以覆寫
private;子類不可以直接通路。
static;跑到靜态區,不需要建立對象,用類名就可以調用方法。抽象方法被調用沒有意義。
練習
需求:
公司中程式猿有姓名,工号,薪水,工作内容。
項目經理有姓名,工号,薪水,工作内容。還有獎金。
對給出需求進行資料模組化。
分析:問題領域中:
程式猿:
屬性:姓名,工号,薪水
行為:工作内容
項目經理:
屬性:姓名,工号,薪水,獎金
行為:工作内容。
兩者不存在所屬關系,但是有共性内容,可以向上抽取。
兩者的共性類型是什麼?雇員。
雇員:
屬性:姓名,工号,薪水
行為:工作内容
abstract class Employee {
private String name;
private String id;
private double pay;
Employee(String name,String id,double pay){
this.name=name;
this.id=id;
this.pay=pay;
}
//工作内容
public abstract void work();
}
//描述程式猿
class Programmer extends Employee
{
Programmer(String name,String id,double pay){
super(name,id,pay);
}
public void work(){
System.out.print("code");
}
}
//描述項目經理
class Manager extends Employee
{
//特殊屬性:獎金
private double bonus;
Manager(String name,String id,double pay,double bonus){
super(name,id,pay);
this.bonus=bonus;
}
public void work(){
System.out.print("manager");
}
}
接口
概述
抽象類中多有的方法都是抽象的。
這時,可以把抽象類用另一種形式來表示:接口。
初期可以了解為接口是特殊的抽象類。
abstract class AbsDemo{}
{
abstract void show1();
abstract void show2();
}
接口中的成員和class定義不同之處:
接口中常見的成員有兩種:1.全局常量;2.抽象方法。
而且都有固定的修飾符。
共性:成員都是public修飾的。
是以上述代碼可以改為:
//定義接口
interface Inter
{
public static final int NUM=4;
public abstract void show1();
public abstract void show2();
}
class InterfaceDemo
{
public static void main(String[] args){
System.out.println("Hello World");
}
}
特點
1.接口不可以執行個體化
2.需要覆寫了接口中的所有的抽象方法的子類,才可以執行個體化。否則,該子類還是一個抽象類。
3.接口是用來被實作的。
類與接口之間的關系是 實作關系。implements
實作方式如下:
interface Inter
{
public static final int NUM=4;
public abstract void show1();
public abstract void show2();
}
class Demo implements Inter {
public void show1() {};
public void show2() {};
}
class InterfaceDemo {
public static void main(String[] args) {
Demo d=new Demo();
}
好處&作用
接口的好處或者解決了什麼問題?
多繼承:好處:可以讓子類具備更多的功能。弊端:調用的不确定性。
弊端舉例如下:
class Fu1
{
void show1()
{
sop("fu1 show");
}
}
class Fu2
{
void show2()
{
sop("fu2 show");
}
}
class Zi extends Fu1,Fu2
{
}
Zi z=new Zi();
z.show1();
z.show2();
這時候Zi同時繼承show1和show2,但如果将上述代碼中的show1和show2均改為show,就會産生調用的不确定性。
原因在于方法主體内容不同。
Java中不直接支援多繼承,而是對該機制進行改良。
通過接口來解決問題。将多繼承轉換成了多實作。
interface InterA
{
void show1();
}
interface InterB
{
void show2();
}
class SubInter implements InterA,InterB//多實作
{
public void show1()
{
sop("inter show1");
}
public void show2()
{
sop("inter show2");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
subInter in=new subInter();
in.show1();
in.show2();
};
}
如果把上述代碼中的show1和show2改為show呢?
interface InterA
{
void show();
}
interface InterB
{
void show();
}
class SubInter implements InterA,InterB//多實作
{
public void show()
{
sop("inter show");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
subInter in=new subInter();
in.show();
};
}
直接實作有實體的部分,而不會産生不可調用的不确定性。
避免單繼承的局限性
一個類繼承一個類的同時,還可以實作多個接口。避免了單繼承的局限性。
繼承是為了擷取體系的基本功能。
想要擴充功能就可以通過實作接口來完成。
class Fu
{
void show()
{
sop("fu show");
}
}
class Zi extends Fu//因為繼承Zi具備所屬的體系具備了該體系的基本功能
{
public void method()
{
sop("zi show");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
Zi z=new Zi();
z.show();
z.method();
};
}
但如果還想擴充一些功能,具備show1功能。
class Fu
{
void show()
{
sop("fu show");
}
}
interface Inter
{
void show1();
}
class Zi extends Fu implements Inter
{
public void method()
{
sop("zi method");
}
public void show1()
{
sop("zi show1");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
Zi z=new Zi();
z.show();
z.method();
z.show1();
};
}
接口多繼承
類與類之間 繼承關系. is a
類與接口之間 實作關系. like a
接口與接口之間關系:是繼承關系,而且可以多繼承。
interface Inter1
{
void show1();
}
interface InterA
{
void showA();
}
interface Inter2 extends Inter1,InterA//接口與接口之間可以多繼承,類不可以多繼承是因為主體調動不明确,而接口根本沒有主體。
{
void show2();
}
class Demo implements Inter2//覆寫兩個才能執行個體化
{
public void show1(){}
public void showA(){}
public void show2(){}
}
抽象類是否可以不定義抽象方法
interface Inter
{
void show1();
void show2);
void show3();
void show4();
}
//DemoA需要使用接口中的部分方法,比如使用show1.
class DemoA implements Inter
{
public void show1(){
sop("Demo show1");
}
void show2(){}
void show3(){}
void show4(){}
}
//DemoB需要使用接口中的部分方法,比如使用show3.
class DemoB implements Inter
{
public void show3(){
sop("Demo show1");
}
void show2(){}
void show1(){}
void show4(){}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
DemoA a=new DemoA();
a.show1();
};
}
這個代碼存在的問題就是:
隻需要部分功能,但是為了執行個體化,必須要全部覆寫。代碼的複用性很差。
是以上述代碼改成:
interface Inter
{
void show1();
void show2();
void show3();
void show4();
}
為了友善建立Inter接口的子類對象。
可以用一個類先把接口中的所有方法都空實作。該類建立對象沒有意義,是以可以将該類抽象。
這就是傳說中的沒有抽象方法的抽象類。
abstract class Demo implements Inter//Demo是空實作,那麼就可以用抽象類。即為沒有抽象方法的抽象類。友善建立接口的對象。
{
void show1(){}
void show2(){}
void show3(){}
void show4(){}
}
//DemoA需要使用接口中的部分方法,比如使用show1.
class DemoA extends Demo
{
public void show1(){
sop("Demo show1");
}
}
//DemoB需要使用接口中的部分方法,比如使用show3.
class DemoB extend Demo
{
public void show3(){
sop("Demo show1");
}
}
class InterfaceDemo
{
public static void main(String[],args[])
{
DemoA a=new DemoA();
a.show1();
};
}
接口的思想
筆記本電腦的USB接口
1.接口的出現擴充了功能。
2.接口其實就是暴露出來的規則。
3.接口的出現降低了耦合性。即解耦。
class Mouse
{
}
interface USB
{
}
class newMouse extends Mouse implements USB
{
}
接口的出現,一方在使用接口,一方在實作接口。
interface Inter
{
void show();
}
class Demo implements Inter
{
void show(){}
}
class Demoa implements Inter
{
void show(){}
}
抽象類和接口的差別
例子:
描述犬:裡面有吃,叫,都是抽象的,具體由子類完成。
問題:定義成抽象類呢?還是定義成接口?
abstract class 犬
{
abstract void 吃();
abstract void 叫();
}
還是
interface 犬
{
abstract void 吃();
abstract void 叫();
}
添加一個功能,緝毒。單獨描述一個緝毒功能的犬。
class 緝毒犬 extends 犬
{
void 吃();
void 叫();
void 緝毒();
}
還是
class 緝毒犬 inplements 犬
{
void 吃(){}
void 叫(){}
void 緝毒(){}
}
緝毒犬是犬中的一種。is a關系。
犬用于描述所有功能犬的基本功能。用class定義。父類。
是以犬不适合定義成接口。
具備緝毒功能的有很多,緝毒功能需要抽取。抽取到類中?還是抽取到接口中?
都試一下,先定義成類。
abstract class 緝毒
{
abstract void 緝毒();
}
不行,緝毒犬繼承犬類,就不能繼承其他類。因為類不能多繼承。
定義成接口試試。
interface 緝毒
{
abstract void 緝毒();
}
class 緝毒犬 extends 犬 implements 緝毒
{
void 吃(){}
void 叫(){}
void 緝毒(){}
}
這是可行的。
類用于描述的是事物的共性基本功能。
接口用于定義的都是事物的額外功能。
抽象類和接口的差別?
1.類與類之間是繼承關系,is a.
類與接口之間是實作關系,like a.
2.類中可以定義抽象和非抽象方法,子類可以直接使用,或者覆寫使用。
接口中定義都是抽象方法,必須實作才能用。
多态
概述
多态:多種形态。
重點說的是:對象的多态性。
class 動物
{
}
class 狗 extends 動物
{
}
//狗 x=new 狗();
動物 x=new 狗();//狗執行個體既是狗類型,又是動物類型。多态性。
多态在程式中的展現,父類的引用或者接口的引用指向了子類的對象。
多态的好處:提高了代碼的擴充性。
多态的弊端:不能使用子類的特有方法。
多态的前提:
1.必須有關系,繼承,實作。
2.通常有覆寫。
abstract class Animal
{
abstract void eat();
}
class Dog extends Animal {
void eat() {
System.out.println("骨頭");
}
void lookHome() {
System.out.println("看家");
}
}
class Cat extends Animal
{
void eat()
{
System.out.println("魚") ;
}
void catchMouse(){
System.out.println("抓老鼠");
}
}
class Pig extends Animal
{
void eat()
{
System.out.println("飼料") ;
}
void gongDi(){
System.out.println("拱地");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
//Dog d=new Dog();
//d.eat();
//多态形式
Animal a=new Dog();
a.eat();
Animal a1=new Cat();//向上轉型
a1.eat();
Dog d1=new Dog();
Dog d2=new Dog();
Dog d3=new Dog();
method(d1);
method(d2);
method(d3);
Cat c1=new Cat();
Cat c2=new Cat();
Cat c3=new Cat();
method(c1);
method(c2);
method(c3);
method (new pig());
}
//定義了狗和貓的調用方式。但是如果再出現一個子類比如豬,那麼還要定義一個調用豬的方法。
//不利于擴充
//當面對共性類型時,所有的子類對象都可以接受。
//說明提高了代碼的擴充性
public static void method( Animal a) //a=new Dog();a=new Cat();a=new Pig();
{
a.eat();
}
//既然調用的都是eat方法,而eat是動物共性行為,為什麼不面對動物呢?
//直接定義共性類型的參數更合适。
/*
public static void method( d) {
d.eat();
}
public static void method(Cat c) {
c.eat();
}
}
*/
多态-向上向下轉型
Animal a=new Cat();//向上轉型
向上轉型好處:隐藏了子類型,提高了代碼的擴充性。
弊端:隻能使用父類中的功能,不能使用子類特有功能,功能被限定住了。
如果不需要面對子類型,通過提高擴充性,或者使用父類的功能即可完成操作,就使用向上轉型。
class DuoTaiDemo {
public static void main(String[] args) {
Animal a=new Dog();//向上轉型
a.eat();
//如果想要使用子類的特有功能呢?比如看家。這時是不是就需要子類型。
if(!(a instanceof Dog))
{
System.out.println("類型不比對");
return;
}
Dog d=(Dog)a;//向下轉型
d.eat();
d.lookHome();
向下轉型
好處:可以使用子類型的特有功能。
弊端:面對具體的子類型。向下轉型有風險。容易發生ClassCastException.隻要轉換類型和對象類型不比對就會發生。
想要安全,必須要進行判斷。判斷一個對象是否比對某一類型,需要使用一個關鍵字 instanceof
如何使用:對象 instanceof 類型
什麼時候用向下轉型:需要子類型的特有方法時。但一定要判斷。
練習
畢外公
講課。
釣魚。
畢老師 extends 畢外公
講課。
看電影。
要求展現多态。
要看到向上轉型,向下轉型
//描述畢外公
class 畢外公
{
public void 講課(){
System.out.println("管理");
}
public void 釣魚(){
System.out.println("釣魚");
}
}
//描述畢老師
class 畢老師 extends 畢外公
{
public void 講課(){
System.out.println("技術");
}
public void 看電影(){
System.out.println("看電影");
}
}
Class DuoTaiTest
{
public static void main(String[],args){
//多态形式。
畢外公 x=new 畢老師();//向上轉型
x.講課();
x.釣魚();
//要想使用畢外公的特有方法 看電影
畢老師 y=(畢老師)x;
y.看電影();//向下轉型
y.釣魚();
}
}
轉型過程中,至始至終,之有子類對象在做着類型的變化。
多态-USB接口練習
階段一:
筆記本電腦運作。筆記本中有一個運作功能。
階段二:
想使用一個滑鼠。又有一個功能使用滑鼠。并多了一個滑鼠對象。
階段三:
還想使用一個鍵盤功能。又要多一個功能。
問題:每多一個功能就需要在筆記本對象中定義一個函數。不爽!擴充性差!
怎麼解決?
降低滑鼠,鍵盤等外圍裝置和筆記本電腦的耦合性。
階段一
//描述筆記本電腦
class NoteBook
{
void run() {
System.out.println("book run");
}
}
class USBTest {
public static void main(String[] args) {
NoteBook book=new NoteBook();
book.run();
}
}
運作結果
book run
階段二
需要一個滑鼠。說明筆記本中多了一個使用滑鼠的功能。
多了一個對象:滑鼠。
class Mouse {
/**
* 開啟功能。
*/
public void open() {
System.out.println("Mouse open");
}
/**
* 開啟功能。
*/
public void close() {
System.out.println("Mouse close");
}
}
class NoteBook
{
public void run() {
System.out.println("book run");
}
/**
* 定義使用滑鼠功能。滑鼠不确定,定義成參數。
*/
public void useMouse(Mouse m) {
//判斷一下,m必須指向滑鼠對象才可以調用方法
if(m!=null) {
m.open();
m.close();
}
}
}
class USBTest {
public static void main(String[] args) {
NoteBook book=new NoteBook();
book.run();
book.useMouse(new Mouse());
}
}
結果
book run
Mouse open
Mouse close
階段三:如果還想使用其他裝置呢?比如鍵盤,外置硬碟。
可以通過再筆記本對象中繼續定義useKey()等方法來完成。
但是出現問題:每增加一個裝置都需要不斷地改動筆記本類中的内容。擴充性太差,維護性也不好。
怎麼辦?
後期的裝置是不确定的,每多加一個裝置就加一個功能,就說明裝置和筆記本的耦合性太強。
我不要每次都面對具體的類型,隻要定義一個規則,讓後期的裝置都符合這個規則。這樣隻要面對規則就可以了。
java中可以通過接口的形式來完成規則的定義,進行解耦。
重新設計:
interface USB {
/**
* 開啟功能。
*/
public void open();
/**
* 開啟功能。
*/
public void close();
}
class NoteBook
{
/**
* 運作
*/
public void run() {
System.out.println("book run");
}
/**
* 使用符合規則的外圍裝置。
*/
public void useUSB(USB usb) {//定義了一個接口類型的引用。//USB usb=new MouseByUSB();多态,提高了筆記本的擴充性。
//判斷一下,m必須指向滑鼠對象才可以調用方法
if(usb!=null) {
usb.open();
usb.close();
}
}
}
//添加滑鼠
class MouseByUSB implements USB
{
public void open()
{
System.out.println("mouse open") ;
}
public void close(){
System.out.println("mouse close");
}
}
//添加鍵盤
classKeyByUSB implements USB
{
public void open()
{
System.out.println("Key open") ;
}
public void close(){
System.out.println("Key close");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
NoteBook book=new NoteBook();
book.run();
book.useUSB(null);
book.useUSB(new MouseByUSB());
book.useUSB(new KeyByUSB());
}
}
結果:
book run
mouse open
mouse close
Key open
Key close
多态中對成員的調用
1.成員變量
當子父類中出現同名成員變量時,多态調用時,隻看調用該成員變量的引用所屬的類中的成員變量。
簡單地說:無論編譯或者運作,都看等号的左邊。
class Fu {
int num=4;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new Fu();
f.num=8;
}
}
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL6lEVNlXRU5UeNpHWwYUbiBnTzwEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcuEzN4ETM1QTMzIjMxgTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
class Fu {
int num=4;
}
class Zi extends Fu
{
int num2=6;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z=new Zi();
z.num=9;
z.num2=10;
}
}
class Fu {
int num=4;
}
class Zi extends Fu
{
int num=6;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z=new Zi();
System.out.println(z.num)
}
}
結果
6
把上述代碼中最後一個class改為如下:
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new Zi();
System.out.println(f.num);
}
}
結果:
4
2.成員函數
出現一模一樣函數時,
多态調用。
編譯時,看的是引用變量所屬的類中的方法。
運作時,看的是對象所屬的類的方法。
簡單說:編譯看左邊,運作右邊。
成員方法動态綁定到目前對象上。
class Fu {
void show(){
System.out.println("fu show");
}
}
class Zi extends Fu
{
void show(){
System.out.println("zi show");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z=new zi();
z.show();
}
}
結果
zi show
把上述代碼中最後一個class改為如下:
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new Zi();
f.show();
}
}
3.靜态函數
出現一模一樣函數時,
多态調用。
編譯和運作時看引用變量所屬的類中的方法。
簡單說:編譯運作看左邊。
其實大家要知道,真正調用靜态方法時不需要對象的。直接類名調用。因為靜态方法綁定到類上。
是以上述情況,更多用于面試。
靜态跟對象沒有關系,隻跟類有關。
class Fu {
static void staticMethod(){
System.out.println("fu static method");
}
}
class Zi extends Fu
{
static void staticMethod(){
System.out.println("zi static method");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new zi();
f.staticMethod();
}
}
練習
class Fu {
int num=5;
void show(){
System.out.println("num="+this.num);
}
}
class Zi extends Fu
{
int num=6;
}
class ExtendsDemo3 {
public static void main(String[] args) {
Fu f=new zi();
f.show();
}
}
結果
num=5