Java面向?qū)ο缶幊獭橄箢惡徒涌?/h1>
定義類的過程就是抽象和封裝的過程,而抽象類與接口則是對實(shí)體類進(jìn)行更高層次的抽象,進(jìn)定義公共行為和特征。
抽象類:
如果一個(gè)類沒有足夠的信息去描述一個(gè)具體的對象,那我們就稱之為抽象類。
語法格式:
public abstract class 類名{}
案例代碼:
package Demo01;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-13 10:31
*/
public class Demo01 {
// 定義動物抽象類
public abstract class Animal{
}
}
抽象方法:
有了抽象類,類中就要有與之對應(yīng)的抽象方法。抽象發(fā)發(fā)就是用abstract修飾的方法,這種方法值聲明返回的數(shù)據(jù)類型,方法名稱和所需要的參數(shù),沒有方法體。也就是說在抽響雷中聲明抽象方法時(shí),值需要聲明方法,不需要定義方法體。子類在繼承父類時(shí),必須重寫父類的抽象方法,這也是抽象方法存在的意義。
語法格式:
public abstract 返回值類型 方法名(參數(shù)列表);
案例代碼:
package Demo01;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-13 10:31
*/
public class Demo01 {
// 定義動物抽象類
public abstract class Animal{
public abstract void go(); // 名為go的抽象方法,注意方法沒有大括號
}
}
抽象類和抽象方法具體該怎么使用? 抽象方法的作用是 什么?
案例代碼
package Demo01;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-13 10:31
*/
public class Demo01 {
// 定義動物抽象類
public abstract class Animal{
public abstract void go(); // 名為go的抽象方法,注意方法沒有大括號
}
public class Demo02 extends Demo01 {
public void go(){
System.out.println("鳥飛的塊");
}
}
public class Demo03 extends Demo01{
public void go(){
System.out.println("狗跑的塊");
}
}
}
測試類:
package Demo01;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-13 10:42
*/
public class Demo04 {
public static void main(String[] args) {
Demo02 d = new Demo02();
d.go();
Demo03 demo03 = new Demo03();
demo03.go();
}
}
通過上面的代碼我們可以總結(jié):
定義抽象類就是用于繼承的。和一般類的繼承相比,子類繼承抽象類必然會實(shí)現(xiàn)抽象方法。由于抽象方法都是沒有方法體的,也就是說,抽象方法并沒有寫死,我么可以根據(jù)自己的需求去寫
抽象類是一種模板式設(shè)計(jì),通常是對同類事物相對具體的抽象。抽象類通常包含抽象方法、實(shí)體方法、屬性變量。。我們在使用抽象方法和抽象類時(shí),需要注意以下幾點(diǎn)。
(1)包含抽象方法的類一定時(shí)抽象類。
(2)抽象類中的方法不一定都是抽象方法。抽象類也可以沒有抽象方法
(3)構(gòu)造方法不能聲明為抽象方法
(4)abstract 不能與private 、static、final、native并修飾同一個(gè)方法
final修飾符
使用:
顯示生活中,我們的身份證一經(jīng)確定,身份證號就不能在此修改了。那我們Java中某些數(shù)據(jù)是定值,為了保證計(jì)算的正確性,不能在被修改,應(yīng)該怎末辦呢?這時(shí)候就用到了final修飾符,表示最終的。
final使用有以下幾種情況。
(1)修飾類
(2)修飾方法
(3)修飾對象和變量
修飾類:
被final修飾過的類不能被繼承
.因?yàn)椴荒鼙焕^承,所以此類中的所有方法默認(rèn)都是final修飾
-
該類如果不需要子類,不需要被擴(kuò)展,類中的方法不允許被重寫,就是用final
修飾方法:
被final修飾的方法可以被繼承,不能被覆蓋重寫
修飾對象和變量:
final修飾一個(gè)對象,那么這個(gè)對象的引用不能變,但是值是可以變的。如果是基礎(chǔ)類型,那么值不可變。
案例代碼:
package Demo02; ; /** * @version 1.0 * @author: jiazhihao * @date: 2021-05-13 10:31 */ public class Demo01 { public String name; public Demo01(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Demo01{" + "name='" + name + '\'' + '}'; } }測試類:
package Demo02; /** * @version 1.0 * @author: jiazhihao * @date: 2021-05-13 10:31 */ public class Demo02 { public static void main(String[] args) { // final 修飾的是李斯特這個(gè)對象 final Demo01 list = new Demo01("李四"); // 李四對象中name 屬性并沒有被final修飾 list.setName("張三"); //最終輸出內(nèi)容:Demo01{name='張三'} System.out.println(list.toString()); } }
接口
在生活中,接口時(shí)一套規(guī)范,滿足這個(gè)規(guī)范的設(shè)備,就可以組裝在一起。
接口就像插座一樣,一種插孔可以插很多種電器,這個(gè)插孔就是我們像外賣呢提供的接口,所有的電器都要實(shí)現(xiàn)這個(gè)接口
為什么需要接口?
和抽象類對同類事物進(jìn)行抽象不同,接口不是類,而是一組對類的需求描述(可以看成只有抽象方法的抽象類)。這要求接口采用鍥約式、開放式對需求進(jìn)行設(shè)計(jì),這樣才能滿足不同事物的相同要求。
接口的使用
在軟件中,接口同樣是一種規(guī)范和標(biāo)準(zhǔn),他們可以約束類的行為,是一些方法特征的集合,但沒有方法的實(shí)現(xiàn),需要類對接口進(jìn)行實(shí)現(xiàn)。
Java中接口的定義語法:
[修飾符] interface 接口名 extends 父接口1,父接口2 ..{
// 常量定義
// 方法定義
}
類實(shí)現(xiàn)接口的語法如下
class 類名 extends父類名 implement 接口1 ,接口2 ,,,{
//類成員
}
interface 之中可以定義變量和方法,變量必須是public static final的,方法必須是public 、abstract的
說明:
(1)接口的命名規(guī)則與類相同。如果修飾符是public,則該接口在整個(gè)項(xiàng)目中可見;如果省略修飾符,則該接口只在當(dāng)前包可見。
(2) 接口中可以定義常量,不能定義變量。接口中的屬性都會自動用public static final 修飾
(3)接口中的所有方法都是抽象方法。接口中方法都會自動用 public abstract修飾,即接口中只有全局抽象方法
(4)和抽象類一樣,接口也不能實(shí)例化,接口中不餓能有構(gòu)造方法
(5)接口之間可以通過extends來實(shí)現(xiàn)繼承關(guān)系,一個(gè)接口可以繼承多個(gè)接口,但接口不能繼承類。
(6)實(shí)現(xiàn)接口的類必須實(shí)現(xiàn)接口的全部方法,否則必須定義為抽象類
(7)一個(gè)類只能有一個(gè)直接父類,但可以通過implements是西安多個(gè)接口。當(dāng)類在繼承父類的同時(shí)有實(shí)現(xiàn)了多個(gè)接口時(shí),extends關(guān)鍵字必須位于implements 關(guān)鍵字之前
案例代碼:
(1)約定Usb接口標(biāo)準(zhǔn)
(2)制作符合Usb接口約定的各種具體設(shè)備
(3)將Usb設(shè)備插到Usb接口上進(jìn)行工作
package Demo03;/**
*@author: jiazhihao
*@date: 2021-05-14 09:08
*@version 1.0
*/public abstract class Demo01 {
public interface Usb{
public void run();
}
static class UsbPrinter implements Usb{
@Override
public void run(){
System.out.println("Usb連接中,打印機(jī)連接中,開始工作");
}
}
static class UsbMouse implements Usb{
@Override
public void run(){
System.out.println("Usb 連接中 ,鼠標(biāo)連接中,開始工作");
} }
static class Test{
public static void main(String[] args){
Usb a = new UsbPrinter();
Usb B = new UsbMouse();
a.run();
B.run();
}
}
}
接口是一種能力使用
接口是一種能力,一個(gè)類實(shí)現(xiàn)了某個(gè)接口,就表示這個(gè)類具備了某種能力。
案例代碼:
package Demo03;
import org.w3c.dom.ls.LSOutput;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 09:24
*/
public class Demo02 {
static public abstract class Venicle {
public abstract void run();
public abstract void stop();
}
static interface Lock {
void lock();
void open();
}
static class Car extends Venicle implements Lock{
@Override
public void run() {
System.out.println("開完鎖,發(fā)動引擎,駕駛員駕駛者汽車開始行駛");
}
@Override
public void stop() {
System.out.println("駕駛員將發(fā)動機(jī)熄火,準(zhǔn)備鎖上汽車");
}
@Override
public void lock() {
System.out.println("插進(jìn)鑰匙,向左旋轉(zhuǎn)三圈,拔出鑰匙");
}
@Override
public void open() {
System.out.println("插進(jìn)鑰匙,向右旋轉(zhuǎn)三圈,所打開了,拔出鑰匙");
}
}
}
測試類:
package Demo03;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 09:41
*/
public class TestDemo02 {
public static void main(String[] args){
Demo02.Car car = new Demo02.Car();
car.lock();
car.open();
car.run();
car.stop();
}
}
既然接口代表一種能力,那么我i們在開車和停車的基礎(chǔ)上,擴(kuò)展行車記錄儀記錄功能,需要增加一種能力
代碼:
package Demo03;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 09:24
*/
public class Demo03 {
static public abstract class Venicle {
public abstract void run();
public abstract void stop();
}
interface Lock {
void lock();
void open();
}
interface TripREC{
void record();
}
static class Car extends Venicle implements Lock{
@Override
public void run() {
System.out.println("開完鎖,發(fā)動引擎,駕駛員駕駛者汽車開始行駛");
}
@Override
public void stop() {
System.out.println("駕駛員將發(fā)動機(jī)熄火,準(zhǔn)備鎖上汽車");
}
@Override
public void lock() {
System.out.println("插進(jìn)鑰匙,向左旋轉(zhuǎn)三圈,拔出鑰匙");
}
@Override
public void open() {
System.out.println("插進(jìn)鑰匙,向右旋轉(zhuǎn)三圈,所打開了,拔出鑰匙");
}
public void record(){
System.out.println("行車記錄儀開始錄像");
}
}
}
測試類:
package Demo03;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 09:41
*/
public class TestDemo03 {
public static void main(String[] args){
Demo03.Car car = new Demo03.Car();
car.lock();
car.open();
car.run();
car.stop();
car.record();
}
}
從結(jié)果中可以看出:
接口在代碼的擴(kuò)展和維護(hù)方面十分方柏霓。接口類似于一個(gè)組件,需要時(shí)可以自由組裝。從使用角度來講,接口和抽象類的區(qū)別在于:抽象類利于代碼復(fù)用,接口利于代碼維護(hù)
接口是一種約定
接口其實(shí)就是一種約定,實(shí)現(xiàn)接口的類就必須遵守這個(gè)接口的約定
Java8中接口的變化
在 JDK 8及以后,允許我們在接口中定義static方法和default方法,這兩種方法可以有方法體
default方法屬于實(shí)例,static方法屬于接口或類。要注意的是 default方法可以被繼承,static方法不能被繼承
-
如果一個(gè)類實(shí)現(xiàn)了多個(gè)接口,并且這些接口之間沒有相互繼承關(guān)系,同時(shí)存在相同的default方法時(shí)會報(bào)錯(cuò),不過可以在實(shí)現(xiàn)類中重寫default方法并通過“<接口>.super.<方法名>();"
靜態(tài)方法:
在接口中增加靜態(tài)方法。理論上講。沒有任何理由認(rèn)為這是不合法的。只是這有違將接口作為抽象規(guī)范的初衷
案例代碼:
package Demo04;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 10:02
*/
public class Demo01 {
public static void main(String[] args) {
//靜態(tài)方法不會被繼承
TestInter.staticMethh();
TestInter2.staticMethh2();
}
}
interface TestInter{
static void staticMethh(){
System.out.println("TestInter中的靜態(tài)方法");
}
}
interface TestInter2{
static void staticMethh2(){
System.out.println("TestInter2中的靜態(tài)方法");
}
}
默認(rèn)方法:
default關(guān)鍵字修飾接口的方法,使其稱為默認(rèn)方法。目的時(shí)減少子類實(shí)現(xiàn)接口的工作量。
案例代碼:
先看沒有使用默認(rèn)方法的方式:
package Demo04;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 10:18
*/
public class Demo02 {
public interface Log {
void login(String name);
}
class Emp implements Log{
@Override
public void login(String name){
System.out.println("用戶"+name+":登陸系統(tǒng)");
}
}
class Student implements Log{
@Override
public void login(String name){
System.out.println("用戶"+name+";登陸系統(tǒng)");
}
}
}
再看使用默認(rèn)方法之后的代碼:
package Demo04;
/**
* @version 1.0
* @author: jiazhihao
* @date: 2021-05-14 10:21
*/
public class Demo03 {
public interface Log{
default void login(String name){
System.out.println("用戶"+name+":登陸系統(tǒng)");
}
}
static class Emp implements Log{}
static class Student implements Log{}
static class DefaultMethodTest{
public static void main(String[] args) {
Emp emp = new Emp();
emp.login("張三");
Student student = new Student();
student.login("李四");
}
}
}
結(jié)論:
如果多個(gè)子類實(shí)現(xiàn)某個(gè)接口方法,方法體都是一樣的,這就導(dǎo)致后期維護(hù)上的困難,如果在接口中定義了默認(rèn)的實(shí)現(xiàn),那么即減少了子類實(shí)現(xiàn)的接口的工作量,也為后期的維護(hù)提供了方便(只需要更改接口中的默認(rèn)實(shí)現(xiàn)即可)
總結(jié)
面向接口編程可以實(shí)現(xiàn)接口和現(xiàn)實(shí)的分離,這樣做最大的好處就是能夠在客戶端位置的情況下修改是西安代碼,那么怎樣抽象出接口呢?一種時(shí)用在層與層的調(diào)用。層與層之間最機(jī)會耦合度過高或修改過于頻繁。設(shè)計(jì)優(yōu)秀的接口能夠解決這個(gè)問題,
另一種時(shí)是使用在那些不穩(wěn)定的部分上。如果某些需求的變化性很大,那么定義接口也是一種解決方法,設(shè)計(jì)良好的接口就像日常使用的萬能插座,不論插頭如何變化,都可以使用
當(dāng)糾結(jié)定義接口還是抽象類時(shí),優(yōu)先推薦定義為接口,遵循接口隔離原則,按某個(gè)維度劃分成多個(gè)接口,然后在用抽象類去是實(shí)現(xiàn)某些接口,這樣做可方柏霓后續(xù)的擴(kuò)展和重構(gòu)。