JAVA之旅(七)——final關鍵字 , 抽象類abstract,模闆方法模式,接口interface,implements,特點,擴充
OK,我們繼續學習JAVA,美滋滋的
一.final
我們來聊聊final這個關鍵字
- final可以修飾類,方法和變量
- final修飾的類不可以被繼承
- final修飾的方法不可以被覆寫
- final修飾的變量是一個常量,隻能被指派一次
- 内部類隻能通路被final修飾的局部變量
final,故名思意,就是最終的意思,由以上的五種特性,不過final的出現,也是有弊端的,他破壞了封裝性,對繼承有了挑戰,為了避免被繼承,被子類複寫功能,還有,當你描述事物時,一些資料的出現值是固定的,那麼,這時為了增強閱讀行,都給這個值起個名字友善閱讀,而這值不需要改變,就會用到final去修飾,作為常量,常量的書寫規範是所有字母都大寫,如果由多個單詞組成,單詞間通過下劃線連結!而且内部類定義在類中的局部位置隻能通路該局部被final修飾的局部變量
final大家隻要記住他的特性就行
二.抽象類
這個抽象類應該是屬于繼承的下半部分的,我們看一個标注的代碼
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
}
}
/**
* 學生
*
* @author LGL
*
*/
class student extends Base {
void speak() {
System.out.println("學習");
}
}
/**
* 勞工
*
* @author LGL
*
*/
class worker extends Base {
void speak() {
System.out.println("工作");
}
}
/**
* 基類
*
* @author LGL
*
*/
class Base {
void speak() {
System.out.println("Hello");
}
}
這裡,學生和勞工都是要說話,是以我們可以抽取,但是這裡,他們說話的内容卻是不同的,當多個類出現相同功能,但是功能主體不同,這個時候就可以進行向上抽取,這時隻抽取功能主體;這個時候就得用到我們的抽象類了abstract;是以我們的基類是這樣的
/**
* 基類
*
* @author LGL
*
*/
abstract class Base {
abstract void speak();
}
什麼叫抽象?
- 看不懂
特點
- 1.抽象方法一定定義在抽象類中
- 2.抽象方法和抽象類都必須被abstract關鍵字修飾
- 3.抽象類不可以用new建立對象,因為調用抽象方法沒意義
- 4.抽象類中的方法要被使用必須由子類複寫其所有的抽象方法後建立子類對象調用
- 如果子類隻覆寫了部分抽象方法,那麼該子類還是一個抽象類
其實抽象類和一般的類沒有什麼太大的不同,隻是要注意該怎麼描述事物就怎麼描述事物,隻不過該事物中出現了一些看不懂的東西,這些不确定的部分也是該事物的功能,需要明确出來,但是無法定義主體,通過抽象方法來表示!
- 抽象類比其他類多了抽象函數,就是在類中可以定義抽象方法
- 抽象類不可以執行個體化
特殊:抽象類中可以不頂用抽象方法,看上去很沒有意義,但是這樣做可以做到不讓該類建立對象,不是很多見
抽象方法文字部分說了這, 多,我們做一個小練習
- 題目:假如我們在開發一個系統時需要對員工進行模組化,員工包含三個屬性 姓名,工号和工資,經理也是員工,除了含有員工的屬性外,另外還 有一個獎金屬性,請使用繼承的思路設計出員工類和經理類,要求類 種提供必要的方法進行屬性通路!
我們實作之前可以簡單的分析一下,我們的員工類應該有三個屬性,name,id,pay,而經曆類,理論上是繼承了員工類并且有資金的獎金屬性,行,這樣的話我們可以用代碼去測試一下
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
/**
* 假如我們在開發一個系統時需要對員工進行模組化,員工包含三個屬性 姓名,工号和工資,經理也是員工,除了含有員工的屬性外,另外還
* 有一個獎金屬性,請使用繼承的思路設計出員工類和經理類,要求類 種提供必要的方法進行屬性通路!
*/
}
}
/**
* 員工類
*
* @author LGL
*
*/
abstract class Employee {
// 姓名
private String name;
// 工号
private String id;
// 工資
private double pay;
// 這個員工一生成,這三個屬性必須有
public Employee(String name, String id, double pay) {
this.name = name;
this.id = id;
this.pay = pay;
}
// 員工做什麼是不确定的
public abstract void work();
}
/**
* 經理類
*
* @author LGL
*
*/
class Manager extends Employee {
// 獎金
private int bonus;
public Manager(String name, String id, double pay, int bonus) {
super(name, id, pay);
this.bonus = bonus;
}
// 複寫
@Override
public void work() {
System.out.println("管理");
}
}
這代碼很清晰的就表現了抽象的關系
三.模闆方法模式
這是一個小案例,擷取一段程式的運作時間,這個需求應該很簡單吧,計時,
- 原理:擷取程式開始和結束的時間并相減
擷取時間的方法:System.currentTimeMillis()
代碼邏輯是這樣寫的
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
GetTime gt = new GetTime();
gt.getTime();
}
}
/**
* 時間類
*
* @author LGL
*
*/
class GetTime {
// 擷取時間
public void getTime() {
long start = System.currentTimeMillis();
// 耗時
for (int i = 0; i < 10000; i++) {
System.out.print("" + i);
}
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end - start));
}
}
我們就可以得到你代碼運作的毫秒數了
但是我們發現,其實這個耗時的部分是不确定的,對吧,那既然這樣,我們複寫的話,就有點多餘了,我們可以使用使用模闆方法模式,也就是抽成一個方法公用
import org.ietf.jgss.Oid;
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
GetTime gt = new GetTime();
gt.getTime();
}
}
/**
* 時間類
*
* @author LGL
*
*/
class GetTime {
// 擷取時間
public void getTime() {
long start = System.currentTimeMillis();
runCode();
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end - start));
}
/**
* 耗時方法
*/
private void runCode() {
// 耗時
for (int i = 0; i < 10000; i++) {
System.out.print("" + i);
}
}
}
這個時候,要是其他類想使用的話,就隻要複寫一個方法就行了,Test想使用的話,就可以直接繼承複寫了
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
// GetTime gt = new GetTime();
Test t = new Test();
t.getTime();
}
}
/**
* 時間類
*
* @author LGL
*
*/
class GetTime {
// 擷取時間
public void getTime() {
long start = System.currentTimeMillis();
runCode();
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end - start));
}
/**
* 耗時方法
*/
public void runCode() {
// 耗時
for (int i = 0; i < 10000; i++) {
System.out.print("" + i);
}
}
}
class Test extends GetTime {
@Override
public void runCode() {
// 耗時
for (int i = 0; i < 50000; i++) {
System.out.print("" + i);
}
}
}
這樣,輸出的内容就是我們随便改的了
我們還可以用抽象去做,你就一定要去做這件事兒
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
// GetTime gt = new GetTime();
Test t = new Test();
t.getTime();
}
}
/**
* 時間類
*
* @author LGL
*
*/
abstract class GetTime {
// 擷取時間
public void getTime() {
long start = System.currentTimeMillis();
runCode();
long end = System.currentTimeMillis();
System.out.println("耗時:" + (end - start));
}
/**
* 耗時方法
*/
public abstract void runCode();
}
class Test extends GetTime {
@Override
public void runCode() {
// 耗時
for (int i = 0; i < 50000; i++) {
System.out.print("" + i);
}
}
}
我把這個延時的操作留給子類,你愛咋地就咋滴,這樣是不是更友善?當然,我們可以給getTime加一個final修飾,這樣就讓程式更加的健壯;
當代碼完成優化之後,就可以解決這類問題了,我們把這種方式叫做:模闆方法設計模式
- 什麼是模闆方法?
在定義功能的時候,功能的一部分是不确定的,而确定的部分在使用不确定的部分的時候,那麼這時就将不确定的 部分暴露出去讓子類去完成,這就是嗎,嗎,模闆方法模式了
四.接口
接口的關鍵字是interface,接口中的成員修飾符是固定的
- 成員常量:public static final
- 成員函數:public abstract
接口的出現将“多繼承”通過另一種形勢展現,即“多實作”
上面的都是概念。我還是通俗易懂的來說吧,接口,初期了解,你可以認為是一個特殊的抽象類,當抽象類中的方法都是抽象的,那麼該類可以通過接口的形式表示,interface,class用于定義類,定義接口
接口定義時,格式特點在于
- 1.接口中常見的定義一個是常量,一個是抽象方法
- 2.接口中的成員都有固定修飾符
- 常量:public static final
- 方法:public abstract
具體格式:
/**
* 人的接口
*
* @author LGL
*
*/
interface Person {
public static final int AGE = 20;
/**
* 說話
*/
public abstract void speak();
}
這裡注意,接口中的成員都是public,我們要想使用這個接口,需要用到另一個關鍵字了implements
為什麼我們類不能繼承類呢?因為接口,是不可以建立對象的,因為有抽象方法,需要被子類去實作,子類對接口中的抽象方法全都覆寫過後子類才可以執行個體化,否則子類是一個抽象類
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
Student s = new Student();
System.out.println(s.AGE);
System.out.println(Student.AGE);
System.out.println(Person.AGE);
}
}
class Student implements Person {
@Override
public void speak() {
}
}
/**
* 人的接口
*
* @author LGL
*
*/
interface Person {
public static final int AGE = 20;
/**
* 說話
*/
public abstract void speak();
}
這樣執行的後,我們就等得到資料了
不過接口不僅僅是這麼簡單,接口是可以被類多實作了,什麼叫做多實作?就是一個類單繼承,但是可以實作多個接口,對繼承不支援的形式,java支援多實作
class Student implements Person, Person2 {
@Override
public void speak() {
}
@Override
public void work() {
}
}
/**
* 人的接口
*
* @author LGL
*
*/
interface Person {
public static final int AGE = 20;
/**
* 說話
*/
public abstract void speak();
}
interface Person2 {
public abstract void work();
}
可以看到實作了;兩個接口,但是他為什麼沒有繼承的弊端呢?因為他沒有方法主體,子類愛怎麼着就怎麼着了
接口間的關系時繼承
接口的特點
這個接口的重點比較實在,是以單獨提取出來講一下,首先我們來連接配接一下接口的特點
- 接口誰對外暴露的規則
- 接口是程式的功能擴充
- 接口可以用力多實作
- 類和接口之間是實作關系,而且類可以繼承一個類的同時實作多個接口
- 接口與接口之間可以有繼承關系
就好比筆記本,我們擴充一樣,這就是接口的概念,說不如做,我們寫個例子來展現:
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
}
}
/**
* 學生類
*
* @author LGL
*
*/
abstract class Student {
// 學習不确定
abstract void study();
// 都要睡覺
void Sleep() {
System.out.println("sleep");
}
//都抽煙
abstract void smoke();
}
/**
* 我
* @author LGL
*
*/
class lgl extends Student{
@Override
void study() {
System.out.println("lgl學習");
}
@Override
void smoke() {
System.out.println("lgl 抽煙");
}
}
我這裡定義了一個人,他有抽煙,睡覺,學習的方法,學習的方法不确定,是以要抽象,睡覺都要,抽煙,有的人抽,有的不抽,品牌也不一樣,是以也得弄成這樣,現在我去繼承這個人,機會有學習和睡覺的方法,但是強制性的抽煙了,這就是這個例子的問題,那我們要怎麼改善?還得使用接口了,我們可以把抽煙的方法抽寫成接口,有需要接實作這個接口
//公共的 類 類名
public class HelloJJAVA {
// 公共的 靜态 無傳回值 main方法 數組
public static void main(String[] str) {
lgl l = new lgl();
l.smoke();
l.study();
}
}
/**
* 學生類
*
* @author LGL
*
*/
abstract class Student {
// 學習不确定
abstract void study();
// 都要睡覺
void Sleep() {
System.out.println("sleep");
}
}
/**
* 我
*
* @author LGL
*
*/
class lgl extends Student implements smoke{
@Override
void study() {
System.out.println("lgl學習");
}
@Override
public void smoke() {
System.out.println("lgl 抽煙");
}
}
/**
* 抽煙的接口
* @author LGL
*
*/
interface smoke {
void smoke();
}
這樣就可以輸出