java final类的写法_重拾JavaSE基础——抽象类、接口、代码块、final和枚举
今天繼續(xù)回顧Java基礎(chǔ),有些東西用得不多,大家看看知道語法就好
主要內(nèi)容
抽象類
抽象方法
抽象方法的寫法
抽象方法是否可以私有化
抽象類的特征
抽象類有無構(gòu)造器,能否實例化對象
抽象類的結(jié)構(gòu)
抽象類的核心意義
被繼承
部分實現(xiàn),部分抽象
接口
為什么要使用接口
接口和實現(xiàn)類的格式
接口的結(jié)構(gòu)
JDK1.8之前
JDK1.8之后新增
接口VS父類
接口VS抽象類
代碼塊
靜態(tài)代碼塊
構(gòu)造代碼塊
普通代碼塊
構(gòu)造器和代碼塊的執(zhí)行順序
final 關(guān)鍵字
修飾類
修飾方法
修飾變量
final和abstract的關(guān)系
單例模式(概念)
枚舉
為什么要用枚舉
枚舉的寫法
枚舉是個普通的類
寫在最后
抽象類
自我學(xué)習(xí)Java以來,我在實際開發(fā)中基本沒用過抽象類。但框架和JDK中很喜歡使用抽象類。抽象類作為一個父類,被用來描述一類事物應(yīng)該具有的基本特征和功能,子類在此基礎(chǔ)上進(jìn)行擴(kuò)展。是對子類的一種約束。
舉個例子吧,學(xué)校的老師、校長和學(xué)生都是學(xué)校的一員,必須具備工作的能力(學(xué)習(xí)可以看作學(xué)生的工作),但是三者具體怎么工作是有差異的,具體怎么工作是自己決定的。看完這篇文章就會有概念了
構(gòu)造方法
就上面的例子,如果寫成代碼的話應(yīng)該是這樣的,SchoolMember類代表學(xué)校成員,是一個父類,Teacher、Student和Principle都繼承SchoolMember類,他們都有work方法
public class SchoolMember {
public String schoolName = "GDPU";
public void work() {
System.out.println("作為學(xué)校的成員一定要工作")
}
}
class Teacher extends SchoolMember{
@Override
public void work() {
System.out.println("我是個普通教師,教好自己的科目就可以了")
}
}
class Student extends SchoolMember{
@Override
public void work() {
System.out.println("我是個學(xué)生,讀好書對得住爸媽就ok了")
}
}
class Principal extends SchoolMember{
@Override
public void work() {
System.out.println("我是校長,要讓學(xué)校有條有理")
}
}
大家應(yīng)該注意到了,其實SchoolMember類中的work方法中的內(nèi)容并沒有什么作用,反而浪費了內(nèi)存空間。還不如不寫
public class SchoolMember {
public String schoolName = "GDPU";
public void work() {
}
}
有一天,小明被爸媽安排到這個學(xué)校,小明便成為了學(xué)校的一員,但他無心向?qū)W在學(xué)校混日子,沒有工作(學(xué)習(xí))的能力
class Principal extends SchoolMember{
public void play() {
System.out.println("我就不讀怎么滴")
}
}
學(xué)校覺得小明老這樣很影響學(xué)校風(fēng)氣,于是把小明趕出了學(xué)校,學(xué)校一片祥和。但過一段時間又有像小明一樣的學(xué)生進(jìn)了這個學(xué)校,學(xué)校受不了了,最后決定只讓一心向?qū)W的學(xué)生入學(xué),便在自己的work方法上加了abstract關(guān)鍵字,變成抽象方法,學(xué)生必須重寫這個方法才能算是學(xué)校的一員,才能繼承SchoolMember類
抽象方法的寫法
抽象方法被abstract修飾,沒有方法體,只有方法簽名
`public abstract 返回類型 方法簽名(參數(shù)列表);
抽象方法是否可以私有化
可以,但是作為抽象方法不被子類重寫還不如寫成普通方法
抽象類的特征
我們可以把抽象類的特征概括為有得有失,抽象類得到了抽象方法的能力,卻失去創(chuàng)建對象的能力。這里要注意,雖然抽象類有對方法進(jìn)行抽象的能力,但他可以選擇使用或不使用這種能力,也就是說,抽象類不一定有抽象方法,但是有抽象方法的類一定是抽象類
抽象類有無構(gòu)造器,能否實例化對象
具有構(gòu)造器
抽象類誕生的意義就是要給子類繼承的,子類初始化一定會調(diào)用父類構(gòu)造器super(...),所以抽象類必須有構(gòu)造器
不能實例化對象
就拿上面的例子,如果`SchoolMember類被實例化,調(diào)用抽象方法將沒有意義
SchoolMember s = new SchoolMember();
s.work(); // 方法沒有實現(xiàn),沒有意義
抽象類本來就意味著一類事物是抽象的(不具體的),沒有必要將他實例化
抽象類的結(jié)構(gòu)
抽象類除了可以有普通類都有可以有的成員變量、成員方法、代碼塊、內(nèi)部類*和構(gòu)造器以外,還可以有抽象方法
再次強(qiáng)調(diào),可以有不代表一定有哈
抽象類的核心意義
抽象類有沒有體現(xiàn)他存在的價值,主要是看兩方面,一是有沒有被子類繼承,二是有沒有做到部分實現(xiàn)部分抽象的效果
被繼承
一個抽象類如果不被子類繼承,還不如做回普通類,還能被實例化
部分實現(xiàn),部分抽象
一個抽象類作為父類,它可以先為子類實現(xiàn)相同的部分公共代碼,剩下的由子類自由發(fā)揮。這里運用了設(shè)計模式中的模板模式
設(shè)計模式:前輩在生產(chǎn)實踐中發(fā)明的優(yōu)秀軟件架構(gòu)或思想,后人可以直接使用這些優(yōu)秀架構(gòu)和思想生產(chǎn)優(yōu)秀的代碼,
模板模式:使用部分實現(xiàn)部分抽象的思想編寫模板,相同的功能不需要重復(fù)寫,提高代碼的可擴(kuò)性,系統(tǒng)的可維護(hù)性,這就類似于我們使用英語作文模板,作文比較容易拿高分,第一段和最后一段都模板已經(jīng)寫好了,中間的部分自己寫就得了
abstract class EnglishModel{
public void wirteTitle() {
System.out.println("這是模板的標(biāo)題");
}
public void wirteHead() {
System.out.println("這是模板的第一段");
}
public void wirteTail() {
System.out.println("這是模板的最后一段");
}
public abstract void wirteTitle();
}
class MyEnglishWord extends EnglishModel{
@Override
public void wirteTitle() {
System.out.println("用模板真的爽");
}
}
這樣我們只要負(fù)責(zé)寫MyEnglishWord類就可以完成這份連半個英文都沒有的英語作文了
接口
說到接口,一個帶我做項目的師兄跟我說,接口是業(yè)務(wù)代碼的抽象。當(dāng)時沒懂,之后自己學(xué)框架搭項目寫代碼的時候慢慢就有感覺啦,我們寫Service層的代碼應(yīng)該都是先創(chuàng)建一個接口再創(chuàng)建一個實現(xiàn)類實現(xiàn)這個接口的吧,為什么要這樣做呢
為什么要使用接口
我的理解是這樣的,通常一個項目是由很多人一起做,你負(fù)責(zé)一個模塊我負(fù)責(zé)另一個模塊,試想一下,如果沒有接口,我開發(fā)的是用戶基礎(chǔ)信息模塊,你開發(fā)的是訂單管理模塊,現(xiàn)在有一個場景,你創(chuàng)建訂單后需要查一下這個用戶是不是VIP,是的話給他免運費,那你的代碼會有這樣一句話
UserService userService = new UserService();
boolean isVIP = userService.isVIP(userId);
但是我可能還沒有寫這個isVIP方法,可能我判斷是不是VIP的方法不叫這個名字,甚至我連這個UserService類都還沒創(chuàng)建呢,那你的代碼一定是被編譯器標(biāo)紅,這就很難受了,你得跑過來問我判斷VIP的方法叫什么名字,要傳入什么參數(shù),出來什么結(jié)果,什么時候才開始寫這個方法
如果我先創(chuàng)建接口可以解決上面的問題
你不用問我判斷VIP的方法叫什么,你看我的接口文件就可以了
你的代碼不會被標(biāo)紅,因為判斷`VIP的方法存在,只是還沒實現(xiàn)
你不用管我是怎么實現(xiàn)這個方法的,你調(diào)用就行了
所以總結(jié)一下:
接口是一種代碼規(guī)范,你我都遵守才能同時一起做開發(fā)
接口也是業(yè)務(wù)代碼的抽象,調(diào)用者不需要知道這個方法內(nèi)部的實現(xiàn)過程
同時Java的接口可以彌補(bǔ)類的單繼承的短板
接口和實現(xiàn)類的格式
接口
public interface 接口名 extends 接口1, 接口2...
實現(xiàn)類
修飾符 class 實現(xiàn)類的名稱 implements 接口1, 接口2...
接口的結(jié)構(gòu)
jdk1.8以后對接口的結(jié)構(gòu)進(jìn)行了修改,這里分開講
JDK1.8以前
接口只有全局常量和抽象方法l兩部分
全局變量
public static final 類型 常量名 = 常量;
常量名一般用英文大寫加下劃線的形式,public static可以省略不寫
抽象方法
`public abstract 返回類型 方法簽名(參數(shù)列表);
這里的`public abstract可以省略不寫
JDK1.8以后新增
默認(rèn)方法 (相當(dāng)于實例成員方法,接口實現(xiàn)類可以直接調(diào)用)
class Main implements A{
public static void main(String[] args) {
Main m = new Main();
m.test();
}
}
interface A {
public default void test() {
// 默認(rèn)方法
}
}
其中public default中的public可以省略。還有一種情況,如果Main類實現(xiàn)了兩個接口,兩個接口都有一樣名字的方法怎么辦?
class Main implements A{
public static void main(String[] args) {
Main m = new Main();
m.test();
}
@Override
public void test() {
// 真正執(zhí)行的方法
System.out.println("重寫方法");
}
}
interface A {
public default void test() {
// 默認(rèn)方法A
System.out.println("默認(rèn)方法A");
}
}
interface B {
public default void test() {
// 默認(rèn)方法B
System.out.println("默認(rèn)方法B");
}
}
重寫方法
靜態(tài)方法 (必須用接口名調(diào)用)
public static void test(){
// 靜態(tài)方法
system.out.println("靜態(tài)方法");
}
Test.test();
私有方法 (只有內(nèi)部可以調(diào)用)
private void run() {
// 私有方法
system.out.println("私有方法");
}
這是JDK1.9后才有的部分
接口VS父類
繼承父類的叫子類,父類只有一個父類,可以有多個子類
實現(xiàn)接口的叫實現(xiàn)類,接口可以有多個父接口,多個實現(xiàn)類
如果父類和接口同時存在一樣的方法,優(yōu)先執(zhí)行父類中的方法
接口VS抽象類
相同點:接口和抽象類都不能被實例化,都可以包含抽象方法
不同點:抽象類具有普通類的結(jié)構(gòu),但他只能單繼承一個父類,接口的組成成分比抽象類少,但可以繼承多個父接口
代碼塊
代碼塊也是類的五個組成成分之一,分為靜態(tài)代碼塊、構(gòu)造代碼塊和普通代碼塊
靜態(tài)代碼塊
靜態(tài)代碼塊屬于類,語法如下,主要作用是初始化靜態(tài)資源,如靜態(tài)成員變量、數(shù)據(jù)庫連接等
public static String schoolName
static{
// 靜態(tài)代碼塊
schoolName = "GDPU";
}
構(gòu)造代碼塊
構(gòu)造代碼塊屬于對象,對象被創(chuàng)建的時候內(nèi)部代碼會被執(zhí)行,用于初始化對象的資源
private String studentName;
{
// 動態(tài)代碼塊
this.studentName = "Rhythm";
}
普通代碼塊
在成員方法里我們可以用大括號把一段代碼包起來,在里面聲明的局部變量在外面不能使用
public void test() {
{
int i = 0;
}
System.out.println(i); // 報錯
}
構(gòu)造器和代碼塊的執(zhí)行順序
如果是個普通類
靜態(tài)代碼塊
構(gòu)造代碼塊
構(gòu)造器
普通代碼塊
如果是個子類,這個非常重要,大家要理解并記住
父類靜態(tài)代碼塊(先加載父類.class文件)
子類靜態(tài)代碼塊(再加載子類.class文件)
父類構(gòu)造代碼塊(父類對象被創(chuàng)建前執(zhí)行)
父類構(gòu)造器(子類構(gòu)造器執(zhí)行super())
子類構(gòu)造代碼塊(子類對象被創(chuàng)建前執(zhí)行)
子類構(gòu)造器(創(chuàng)建子類對象)
final 關(guān)鍵字
這個大家平時應(yīng)該用過吧,它可以在類、變量和方法上出現(xiàn)
修飾類
該類不能被繼承,斷子絕孫。String類就是用final修飾的
修飾方法
修飾靜態(tài)成員方法 (沒有什么效果)
修飾實例成員方法 (方法不能被子類重寫)
變量
Java中變量分為局部變量和成員變量
成員變量
靜態(tài)成員變量:這個其實就是我們平時定義常量的方式
public static final String USER_NAME = "Rhythm"
實例成員變量:只允許一次復(fù)賦值
private final int i = 10;
局部變量
final修飾局部變量是為了保護(hù)數(shù)據(jù),防止程序員不小心把值給改了。比如下面這段代碼,parse方法計算折后價,rate表示折扣,我們不希望rate在計算中被修改,可以加上final關(guān)鍵字
public class Test {
public static void main(String[] args) {
System.out.println(parse(10, 0.8));
System.out.println(parse(100, 0.7));
}
public double parse(double rmb, final double rate) {
return rmb * rate;
}
}
final和abstract的關(guān)系
互斥關(guān)系,final修飾的類不能被繼承,但抽象類不被繼承沒有意義,final修飾方法不能被重寫,抽象方法也沒有意義
單例模式 (概念)
單例模式保證對象在運行過程中只被實例化一次,具體實現(xiàn)方式有很多種,這里只是對單例模式的簡單引出,沒有考慮并發(fā)情況
餓式
就是在類中的資源被使用時初始化對象
public class Main {
public static void main(String[] args) {
Demo demo = Demo.getDemo();
}
}
class Demo {
public static Demo demo;
static {
demo = new Demo();
System.out.println("對象被初始化");
}
private Demo() {
// 構(gòu)造器私有
}
public static Demo getDemo() {
return demo;
}
public static void other() {
System.out.println("這是個其他方法");
}
}
缺點就是如果類中有其他靜態(tài)成員方法如other被調(diào)用的時候?qū)ο笠矔怀跏蓟?/p>
懶式
就是真的需要的時候才加載,大家看代碼就懂了
class Demo {
public static Demo demo;
private Demo() {
// 構(gòu)造器私有
}
public static Demo getDemo() {
if (demo == null) {
demo = new Demo();
}
return demo;
}
public static void other() {
System.out.println("這是個其他方法");
}
}
調(diào)用類中的其他方法不會初始化對象
注意:
構(gòu)造器必須私有化
這里只是簡單介紹下概念,以后再來總結(jié)詳細(xì)的單例模式
枚舉
枚舉在實際開發(fā)中常常使用到,這里對他進(jìn)行了總結(jié)
為什么要使用枚舉
大家在代碼中常常會使用常量來代替一些數(shù)字,提高代碼可讀性,對比一下下面兩段代碼,顯然第二種可讀性更高
public static void control(Integer i) {
if (i = 1) {
// 打開
} else {
// 關(guān)閉
}
}
public static void main(String[] args) {
control(1)
}
public static final Integer OPEN = 1;
public static final Integer CLOSE = 2;
public static void control(Integer i) {
if (i = OPEN) {
// 打開
} else {
// 關(guān)閉
}
}
public static void main(String[] args) {
control(OPEN)
}
但是對于我們來說,我們還是可以通過control(1)來調(diào)用方法的,為了保證代碼的可讀性,我們可以對這些常量進(jìn)行約束管理,把常量抽出來放到一個類中
class Counst {
public static final Integer OPEN = 1;
public static final Integer CLOSE = 2;
}
以后使用就要這樣寫
if(i = Counst.OPEN) {
// 打開
}
于是Java將這些專門存放常量的類稱為枚舉類,并賦予他特殊的語法
枚舉的寫法
舉個例子,我們運行完業(yè)務(wù)代碼之后需要將數(shù)據(jù)返回到前端時要帶上狀態(tài)碼code和提示信息message,這些值都是固定的,我們可以把它們抽出來做成枚舉
public enum ResultCodeEnum {
SUCCESS(200, "操作成功"),
FAILED(500, "操作失敗");
private long code;
private String message;
ResultCodeEnum(long code, String message) {
this.code = code;
this.message = message;
}
public long getCode() {
return code;
}
public String getMessage() {
return message;
}
}
總結(jié)起來就是:
enum定義類
定義常量對應(yīng)的變量,可以定義多個,如上面的Integer code和String message
定義常量,如上面的SUCCESS(200, "操作成功"),用逗號隔開
提供get和set方法
定義一些自定義方法,如
public static String getMessageCode(Integer code) {
for (ResultCodeEnum item : ResultCodeEnum.values()) {
if (item.getCode() == code) {
return item.message;
}
}
return null;
}
枚舉類是個普通的類
我們可以用javap查看一下枚舉類的字節(jié)碼
public final class ResultCodeEnum extends java.lang.Enum {
public static final ResultCodeEnum SUCCESS;
public static final ResultCodeEnum FAILED;
public static ResultCodeEnum[] values();
public static ResultCodeEnum valueOf(java.lang.String);
public long getCode();
public java.lang.String getMessage();
public static java.lang.String getMessageCode(java.lang.Integer);
static {};
枚舉類繼承了lang.Enum,并初始化了SUCCESS和FAILED兩個ResultCodeEnum對象,提供了幾個方法,所以我們在使用過程中把枚舉類看成普通類就可以了
寫在最后
這篇文章主要講述了抽象類、接口、代碼塊、final關(guān)鍵字、單例模式和枚舉,有些我們平時用不上的記住語法就好,面試的時候還能說一說,如果我的理解有誤的話歡迎大家評論告訴我
總結(jié)
以上是生活随笔為你收集整理的java final类的写法_重拾JavaSE基础——抽象类、接口、代码块、final和枚举的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java chars_Java getC
- 下一篇: java取number长度_Java中常