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