• 正文
    • 4399一面
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

4399 薪資開了,要不要去?

01/20 12:10
1424
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

圖解學習網(wǎng)站:https://xiaolincoding.com

大家好,我是小林。

4399 小游戲相信都是大家的童年回憶,每次上電腦課,防著老師悄悄打開 4399 網(wǎng)頁,玩轉(zhuǎn)上面各種琳瑯滿目的小游戲,當時最常同學友一起玩的是死神vs火影,兩個人在鍵盤上敲的非常激烈,以至于被老師發(fā)現(xiàn)了,老師就直接拔網(wǎng)線了。

如果長大之后沒有在關(guān)注 4399 小游戲的同學,估計都以為 4399 銷聲匿跡了,其實人家不光活著,而且活得還挺好,并且長期在互聯(lián)網(wǎng)百強企業(yè)名單里,在 2024 年百強企業(yè)名單里,4399 排名 46。

現(xiàn)在 4399 公司規(guī)模也有上千人了,總部在廈門,基本上有雙休,工作時間是早上九點到下午六點,但是畢竟是游戲公司,加班的情況可能還是存在的。

我看了一下 4399 開發(fā)崗位的校招薪資,大概范圍是 16k~19k x 13,也就是年薪在 20w~ 25w,薪資在二線城市還是比較有競爭力的,之前也有訓練營同學拿到了 4399 校招 offer,給他開了 17x13。

這次我們來看看 4399 Java 崗的校招面經(jīng),這次是一面,問的還是比較基礎(chǔ),感覺像八股問答賽,沒有算法(估計是因為筆試環(huán)節(jié)考察了算法,面試就沒有考察),問完八股就結(jié)束了,流程大概 20 多分鐘。

4399一面

java是怎么學習的?

學校有開設(shè)Java 的課程,除此之外,還看過《Java并發(fā)編程的藝術(shù)》、《深入理解Java虛擬機》、《Spring5 實戰(zhàn)》、《MySQL技術(shù)內(nèi)幕》、《Redis設(shè)計與實現(xiàn)》相關(guān)的書籍,同時為了增加Java 開發(fā)能力,做過 xxx 項目。

java語言的特點是什么?

主要有以下的特點:

平臺無關(guān)性:Java的“編寫一次,運行無處不在”哲學是其最大的特點之一。Java編譯器源代碼編譯成字節(jié)碼(bytecode),該字節(jié)碼可以在任何安裝了Java虛擬機的系統(tǒng)上運行。

面向?qū)ο?/strong>:Java是一門嚴格的面向?qū)ο缶幊陶Z言,幾乎一切都是對象。面向?qū)ο缶幊烫匦允沟么a更易于維護和重用,包括類、對象、繼承、多態(tài)、抽象和封裝。

內(nèi)存管理:Java有自己的垃圾回收機制,自動管理內(nèi)存和回收不再使用的對象。這樣,開發(fā)者不需要手動管理內(nèi)存,從而減少內(nèi)存泄漏和其他內(nèi)存相關(guān)的問題。

final關(guān)鍵字怎么用

final關(guān)鍵字可以用來修飾類、方法和變量,具有不同的作用:修飾類:將final關(guān)鍵字放在類的定義前,如final class MyClass {...}。被final修飾的類不能被繼承。這通常用于創(chuàng)建一些不希望被修改或擴展的類,例如 Java 中的String類就是final類。

final?class?FinalClass?{
????//?類的成員和方法
}
//?以下代碼將無法編譯
//?class?SubClass?extends?FinalClass?{}?
    • 修飾方法:將final
    • 關(guān)鍵字放在方法的聲明前,如public final void myMethod() {...}。
    • 被final
    修飾的方法不能被子類重寫。這可以確保方法的實現(xiàn)不被修改,通常用于保證一些關(guān)鍵方法的行為在子類中不會改變。
class?BaseClass?{
????public?final?void?finalMethod()?{
????????System.out.println("This?is?a?final?method.");
????}
}
class?SubClass?extends?BaseClass?{
????//?以下代碼將無法編譯
????//?public?void?finalMethod()?{
????//?????System.out.println("Trying?to?override?final?method.");
????//?}
}
    • 修飾變量:將final
    • 關(guān)鍵字放在變量的聲明前,如final int myVariable = 10;。
    • 對于基本數(shù)據(jù)類型,被final
    • 修飾的變量一旦賦值就不能再修改其值;對于引用數(shù)據(jù)類型(如對象和數(shù)組),被final
    修飾的變量一旦引用了一個對象或數(shù)組,就不能再引用其他對象或數(shù)組,但可以修改對象或數(shù)組的內(nèi)部狀態(tài)。
//?修飾基本數(shù)據(jù)類型
final?int?number?=?5;
//?以下代碼將無法編譯
//?number?=?10;?


//?修飾對象
final?StringBuilder?sb?=?new?StringBuilder("Hello");
sb.append(",?World");?//?允許修改對象的內(nèi)部狀態(tài)
//?以下代碼將無法編譯
//?sb?=?new?StringBuilder("Goodbye");?
    • 修飾參數(shù):在方法的參數(shù)列表中使用final
    • 關(guān)鍵字,如public void myMethod(final int parameter) {...}。
    • 被final
    修飾的參數(shù)在方法內(nèi)部不能被修改。這可以防止在方法中不小心修改了傳入的參數(shù)值。
public?void?printValue(final?int?value)?{
????//?以下代碼將無法編譯
????//?value?=?10;?
????System.out.println(value);
}

使用hashmap時,如果只重寫了equals沒有重寫hashcode會出現(xiàn)什么問題?

HashMap 存儲元素是基于哈希表的原理。它通過 hashCode 方法計算元素的哈希值,將元素存儲在對應(yīng)的哈希桶中。當查找元素時,首先根據(jù) hashCode 找到對應(yīng)的哈希桶,然后在該桶中使用 equals 方法精確查找元素。

如果只重寫了 equals 方法而未重寫 hashCode 方法,那么即使兩個對象在邏輯上相等(根據(jù) equals 方法的判斷),它們的 hashCode 可能不同,這樣會導致兩個邏輯上相等的對象可能被存儲在 HashMap 的不同哈希桶中,因為它們的 hashCode 不同。

當嘗試根據(jù)鍵來查找元素時,可能無法找到元素。因為 HashMap 首先根據(jù) hashCode 找到哈希桶,而由于 hashCode 不同,會在錯誤的哈希桶中查找,導致查找失敗,即使 equals 方法認為它們相等。

為了避免這個問題,當重寫 equals 方法時,應(yīng)該同時重寫 hashCode 方法,以保證對象的邏輯相等性和存儲查找的一致性。

深拷貝和淺拷貝的區(qū)別是什么?

    淺拷貝是指只復制對象本身和其內(nèi)部的值類型字段,但不會復制對象內(nèi)部的引用類型字段。換句話說,淺拷貝只是創(chuàng)建一個新的對象,然后將原對象的字段值復制到新對象中,但如果原對象內(nèi)部有引用類型的字段,只是將引用復制到新對象中,兩個對象指向的是同一個引用對象。深拷貝是指在復制對象的同時,將對象內(nèi)部的所有引用類型字段的內(nèi)容也復制一份,而不是共享引用。換句話說,深拷貝會遞歸復制對象內(nèi)部所有引用類型的字段,生成一個全新的對象以及其內(nèi)部的所有對象。

java創(chuàng)建對象有哪些方式?

創(chuàng)建對象的方式有多種,常見的包括:

使用new關(guān)鍵字:通過new關(guān)鍵字直接調(diào)用類的構(gòu)造方法來創(chuàng)建對象。

MyClass?obj?=?new?MyClass();

使用Class類的newInstance()方法:通過反射機制,可以使用Class類的newInstance()方法創(chuàng)建對象。

MyClass?obj?=?(MyClass)?Class.forName("com.example.MyClass").newInstance();

使用Constructor類的newInstance()方法:同樣是通過反射機制,可以使用Constructor類的newInstance()方法創(chuàng)建對象。

Constructor<MyClass>?constructor?=?MyClass.class.getConstructor();
MyClass?obj?=?constructor.newInstance();

使用clone()方法:如果類實現(xiàn)了Cloneable接口,可以使用clone()方法復制對象。

MyClass?obj1?=?new?MyClass();
MyClass?obj2?=?(MyClass)?obj1.clone();

使用反序列化:通過將對象序列化到文件或流中,然后再進行反序列化來創(chuàng)建對象。

//?SerializedObject.java
ObjectOutputStream?out?=?new?ObjectOutputStream(new?FileOutputStream("object.ser"));
out.writeObject(obj);
out.close();

//?DeserializedObject.java
ObjectInputStream?in?=?new?ObjectInputStream(new?FileInputStream("object.ser"));
MyClass?obj?=?(MyClass)?in.readObject();
in.close();

線程的創(chuàng)建方式有哪些?

1、繼承Thread類

這是最直接的一種方式,用戶自定義類繼承java.lang.Thread類,重寫其run()方法,run()方法中定義了線程執(zhí)行的具體任務(wù)。創(chuàng)建該類的實例后,通過調(diào)用start()方法啟動線程。

class?MyThread?extends?Thread?{
????@Override
????public?void?run()?{
????????//?線程執(zhí)行的代碼
????}
}

public?static?void?main(String[]?args)?{
????MyThread?t?=?new?MyThread();
????t.start();
}

采用繼承Thread類方式

    優(yōu)點: 編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread ()方法,直接使用this,即可獲得當前線程缺點:因為線程類已經(jīng)繼承了Thread類,所以不能再繼承其他的父類

2、實現(xiàn)Runnable接口

如果一個類已經(jīng)繼承了其他類,就不能再繼承Thread類,此時可以實現(xiàn)java.lang.Runnable接口。實現(xiàn)Runnable接口需要重寫run()方法,然后將此Runnable對象作為參數(shù)傳遞給Thread類的構(gòu)造器,創(chuàng)建Thread對象后調(diào)用其start()方法啟動線程。

class?MyRunnable?implements?Runnable?{
????@Override
????public?void?run()?{
????????//?線程執(zhí)行的代碼
????}
}

public?static?void?main(String[]?args)?{
????Thread?t?=?new?Thread(new?MyRunnable());
????t.start();
}

采用實現(xiàn)Runnable接口方式:

    優(yōu)點:線程類只是實現(xiàn)了Runable接口,還可以繼承其他的類。在這種方式下,可以多個線程共享同一個目標對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU代碼和數(shù)據(jù)分開,形成清晰的模型,較好地體現(xiàn)了面向?qū)ο蟮乃枷?。缺點:編程稍微復雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法。

3、實現(xiàn)Callable接口與FutureTask

java.util.concurrent.Callable接口類似于Runnable,但Callable的call()方法可以有返回值并且可以拋出異常。要執(zhí)行Callable任務(wù),需將它包裝進一個FutureTask,因為Thread類的構(gòu)造器只接受Runnable參數(shù),而FutureTask實現(xiàn)了Runnable接口。

class?MyCallable?implements?Callable<Integer>?{
????@Override
????public?Integer?call()?throws?Exception?{
????????//?線程執(zhí)行的代碼,這里返回一個整型結(jié)果
????????return?1;
????}
}

public?static?void?main(String[]?args)?{
????MyCallable?task?=?new?MyCallable();
????FutureTask<Integer>?futureTask?=?new?FutureTask<>(task);
????Thread?t?=?new?Thread(futureTask);
????t.start();

????try?{
????????Integer?result?=?futureTask.get();??//?獲取線程執(zhí)行結(jié)果
????????System.out.println("Result:?"?+?result);
????}?catch?(InterruptedException?|?ExecutionException?e)?{
????????e.printStackTrace();
????}
}

采用實現(xiàn)Callable接口方式:

    缺點:編程稍微復雜,如果需要訪問當前線程,必須調(diào)用Thread.currentThread()方法。優(yōu)點:線程只是實現(xiàn)Runnable或?qū)崿F(xiàn)Callable接口,還可以繼承其他類。這種方式下,多個線程可以共享一個target對象,非常適合多線程處理同一份資源的情形。

4、使用線程池(Executor框架)

從Java 5開始引入的java.util.concurrent.ExecutorService和相關(guān)類提供了線程池的支持,這是一種更高效的線程管理方式,避免了頻繁創(chuàng)建和銷毀線程的開銷。可以通過Executors類的靜態(tài)方法創(chuàng)建不同類型的線程池。

class?Task?implements?Runnable?{
????@Override
????public?void?run()?{
????????//?線程執(zhí)行的代碼
????}
}

public?static?void?main(String[]?args)?{
????ExecutorService?executor?=?Executors.newFixedThreadPool(10);??//?創(chuàng)建固定大小的線程池
????for?(int?i?=?0;?i?<?100;?i++)?{
????????executor.submit(new?Task());??//?提交任務(wù)到線程池執(zhí)行
????}
????executor.shutdown();??//?關(guān)閉線程池
}

采用線程池方式:

    缺點:程池增加了程序的復雜度,特別是當涉及線程池參數(shù)調(diào)整和故障排查時。錯誤的配置可能導致死鎖、資源耗盡等問題,這些問題的診斷和修復可能較為復雜。優(yōu)點:線程池可以重用預(yù)先創(chuàng)建的線程,避免了線程創(chuàng)建和銷毀的開銷,顯著提高了程序的性能。對于需要快速響應(yīng)的并發(fā)請求,線程池可以迅速提供線程來處理任務(wù),減少等待時間。并且,線程池能夠有效控制運行的線程數(shù)量,防止因創(chuàng)建過多線程導致的系統(tǒng)資源耗盡(如內(nèi)存溢出)。通過合理配置線程池大小,可以最大化CPU利用率和系統(tǒng)吞吐量。

線程的狀態(tài)有哪些?

源自《Java并發(fā)編程藝術(shù)》 java.lang.Thread.State枚舉類中定義了六種線程的狀態(tài),可以調(diào)用線程Thread中的getState()方法獲取當前線程的狀態(tài)

線程狀態(tài) 解釋
NEW 尚未啟動的線程狀態(tài),即線程創(chuàng)建,還未調(diào)用start方法
RUNNABLE 就緒狀態(tài)(調(diào)用start,等待調(diào)度)+正在運行
BLOCKED 等待監(jiān)視器鎖時,陷入阻塞狀態(tài)
WAITING 等待狀態(tài)的線程正在等待另一線程執(zhí)行特定的操作(如notify)
TIMED_WAITING 具有指定等待時間的等待狀態(tài)
TERMINATED 線程完成執(zhí)行,終止狀態(tài)

悲觀鎖和樂觀鎖的區(qū)別是什么?

    樂觀鎖:就像它的名字一樣,對于并發(fā)間操作產(chǎn)生的線程安全問題持樂觀狀態(tài),樂觀鎖認為競爭不總 是會發(fā)生,因此它不需要持有鎖,將比較-替換這兩個動作作為一個原子操作嘗試去修改內(nèi)存中的變量,如果失敗則表示發(fā)生沖突,那么就應(yīng)該有相應(yīng)的重試邏輯。悲觀鎖:還是像它的名字一樣,對于并發(fā)間操作產(chǎn)生的線程安全問題持悲觀狀態(tài),悲觀鎖認為競爭總 是會發(fā)生,因此每次對某資源進行操作時,都會持有一個獨占的鎖,就像 synchronized,不管三七二十一,直接上了鎖就操作資源了。

MySQL中的事務(wù)隔離級別有哪些?

讀未提交(read uncommitted),指一個事務(wù)還沒提交時,它做的變更就能被其他事務(wù)看到;

讀提交(read committed),指一個事務(wù)提交之后,它做的變更才能被其他事務(wù)看到;

可重復讀(repeatable read),指一個事務(wù)執(zhí)行過程中看到的數(shù)據(jù),一直跟這個事務(wù)啟動時看到的數(shù)據(jù)是一致的,

MySQL InnoDB 引擎的默認隔離級別;

串行化(serializable);會對記錄加上讀寫鎖,在多個事務(wù)對這條記錄進行讀寫操作時,如果發(fā)生了讀寫沖突的時候,后訪問的事務(wù)必須等前一個事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行;

按隔離水平高低排序如下:

針對不同的隔離級別,并發(fā)事務(wù)時可能發(fā)生的現(xiàn)象也會不同。

也就是說:

    在「讀未提交」隔離級別下,可能發(fā)生臟讀、不可重復讀和幻讀現(xiàn)象;在「讀提交」隔離級別下,可能發(fā)生不可重復讀和幻讀現(xiàn)象,但是不可能發(fā)生臟讀現(xiàn)象;在「可重復讀」隔離級別下,可能發(fā)生幻讀現(xiàn)象,但是不可能臟讀和不可重復讀現(xiàn)象;在「串行化」隔離級別下,臟讀、不可重復讀和幻讀現(xiàn)象都不可能會發(fā)生。

接下來,舉個具體的例子來說明這四種隔離級別,有一張賬戶余額表,里面有一條賬戶余額為 100 萬的記錄。然后有兩個并發(fā)的事務(wù),事務(wù) A 只負責查詢余額,事務(wù) B 則會將我的余額改成 200 萬,下面是按照時間順序執(zhí)行兩個事務(wù)的行為:

在不同隔離級別下,事務(wù) A 執(zhí)行過程中查詢到的余額可能會不同:

    在「讀未提交」隔離級別下,事務(wù) B 修改余額后,雖然沒有提交事務(wù),但是此時的余額已經(jīng)可以被事務(wù) A 看見了,于是事務(wù) A 中余額 V1 查詢的值是 200 萬,余額 V2、V3 自然也是 200 萬了;在「讀提交」隔離級別下,事務(wù) B 修改余額后,因為沒有提交事務(wù),所以事務(wù) A 中余額 V1 的值還是 100 萬,等事務(wù) B 提交完后,最新的余額數(shù)據(jù)才能被事務(wù) A 看見,因此額 V2、V3 都是 200 萬;在「可重復讀」隔離級別下,事務(wù) A 只能看見啟動事務(wù)時的數(shù)據(jù),所以余額 V1、余額 V2 的值都是 100 萬,當事務(wù) A 提交事務(wù)后,就能看見最新的余額數(shù)據(jù)了,所以余額 V3 的值是 200 萬;在「串行化」隔離級別下,事務(wù) B 在執(zhí)行將余額 100 萬修改為 200 萬時,由于此前事務(wù) A 執(zhí)行了讀操作,這樣就發(fā)生了讀寫沖突,于是就會被鎖住,直到事務(wù) A 提交后,事務(wù) B 才可以繼續(xù)執(zhí)行,所以從 A 的角度看,余額 V1、V2 的值是 100 萬,余額 V3 的值是 200萬。

這四種隔離級別具體是如何實現(xiàn)的呢?

    • 對于「讀未提交」隔離級別的事務(wù)來說,因為可以讀到未提交事務(wù)修改的數(shù)據(jù),所以直接讀取最新的數(shù)據(jù)就好了;對于「串行化」隔離級別的事務(wù)來說,通過加讀寫鎖的方式來避免并行訪問;對于「讀提交」和「可重復讀」隔離級別的事務(wù)來說,它們是通過 Read View來實現(xiàn)的,它們的區(qū)別在于創(chuàng)建 Read View 的時機不同,

「讀提交」隔離級別是在「每個語句執(zhí)行前」都會重新生成一個 Read View,而「可重復讀」隔離級別是「啟動事務(wù)時」生成一個 Read View,然后整個事務(wù)期間都在用這個 Read View

查詢當前數(shù)據(jù)庫的事務(wù)隔離級別的命令是什么?

在 MySQL8.0+ 版本中:

    • 查看當前會話隔離級別:select @@transaction_isolation;
    • 查看系統(tǒng)當前隔離級別:select @@global.transaction_isolation;

redis的常見數(shù)據(jù)結(jié)構(gòu)有哪些?

Redis 提供了豐富的數(shù)據(jù)類型,常見的有五種數(shù)據(jù)類型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。

隨著 Redis 版本的更新,后面又支持了四種數(shù)據(jù)類型:BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。Redis 五種數(shù)據(jù)類型的應(yīng)用場景:

    String 類型的應(yīng)用場景:緩存對象、常規(guī)計數(shù)、分布式鎖、共享 session 信息等。List 類型的應(yīng)用場景:消息隊列(但是有兩個問題:1. 生產(chǎn)者需要自行實現(xiàn)全局唯一 ID;2. 不能以消費組形式消費數(shù)據(jù))等。Hash 類型:緩存對象、購物車等。Set 類型:聚合計算(并集、交集、差集)場景,比如點贊、共同關(guān)注、抽獎活動等。Zset 類型:排序場景,比如排行榜、電話和姓名排序等。

Redis 后續(xù)版本又支持四種數(shù)據(jù)類型,它們的應(yīng)用場景如下:

    BitMap(2.2 版新增):二值狀態(tài)統(tǒng)計的場景,比如簽到、判斷用戶登陸狀態(tài)、連續(xù)簽到用戶總數(shù)等;HyperLogLog(2.8 版新增):海量數(shù)據(jù)基數(shù)統(tǒng)計的場景,比如百萬級網(wǎng)頁 UV 計數(shù)等;GEO(3.2 版新增):存儲地理位置信息的場景,比如滴滴叫車;Stream(5.0 版新增):消息隊列,相比于基于 List 類型實現(xiàn)的消息隊列,有這兩個特有的特性:自動生成全局唯一消息ID,支持以消費組形式消費數(shù)據(jù)。

spring的AOP的作用是什么?

Spring AOP 目的是對于面向?qū)ο笏季S的一種補充,而不是像引入命令式、函數(shù)式編程思維讓他順應(yīng)另一種開發(fā)場景。在我個人的理解下AOP更像是一種對于不支持多繼承的彌補,除開對象的主要特征(我更喜歡叫“強共性”)被抽象為了一條繼承鏈路,對于一些“弱共性”,AOP可以統(tǒng)一對他們進行抽象和集中處理。

舉一個簡單的例子,打印日志。需要打印日志可能是許多對象的一個共性,這在企業(yè)級開發(fā)中十分常見,但是日志的打印并不反應(yīng)這個對象的主要共性。而日志的打印又是一個具體的內(nèi)容,它并不抽象,所以它的工作也不可以用接口來完成。而如果利用繼承,打印日志的工作又橫跨繼承樹下面的多個同級子節(jié)點,強行侵入到繼承樹內(nèi)進行歸納會干擾這些強共性的區(qū)分。

這時候,我們就需要AOP了。AOP首先在一個Aspect(切面)里定義了一些Advice(增強),其中包含具體實現(xiàn)的代碼,同時整理了切入點,切入點的粒度是方法。最后,我們將這些Advice織入到對象的方法上,形成了最后執(zhí)行方法時面對的完整方法。

AOP 常見的通知類型有哪些?相關(guān)術(shù)語解釋

在 Spring AOP 中,通知(Advice)是切面在特定連接點(Join Point)采取的行動,常見的通知類型有以下三種,有“around”,“before”和“after”三種類型。在很多的 AOP 實現(xiàn)框架中,Advice 通常作為一個攔截器,也可以包含許多個攔截器作為一條鏈路圍繞著 Join point 進行處理。

前置通知(BeforeAdvice):在目標方法執(zhí)行之前調(diào)用通知。它可以用于執(zhí)行一些前置的操作,例如參數(shù)檢查、權(quán)限驗證等。比如下面的代碼使用了@Before注解,該注解的參數(shù)是一個切點表達式,表示在com.example.service.MyService類中的任何方法執(zhí)行之前,都會執(zhí)行beforeAdvice方法。

import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.Before;

@Aspect
public?class?MyAspect?{
????@Before("execution(*?com.example.service.MyService.*(..))")
????public?void?beforeAdvice()?{
????????System.out.println("This?is?before?advice.?Executing?before?the?target?method.");
????}
}

后置通知

(After Advice):在目標方法執(zhí)行完成之后調(diào)用通知,無論目標方法是否正常結(jié)束或拋出異常。后置通知通常用于釋放資源或執(zhí)行一些清理工作。比如下面的代碼,這里使用了@After注解,意味著afterAdvice方法會在com.example.service.MyService類中的任何方法執(zhí)行之后被調(diào)用。

import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.annotation.After;

@Aspect
public?class?MyAspect?{
????@After("execution(*?com.example.service.MyService.*(..))")
????public?void?afterAdvice()?{
????????System.out.println("This?is?after?advice.?Executing?after?the?target?method.");
????}
}

環(huán)繞通知

    • (Around Advice):環(huán)繞通知是最強大的一種通知,它可以在目標方法調(diào)用前后自定義操作,并且可以控制目標方法是否執(zhí)行,以及修改其返回值。環(huán)繞通知可以實現(xiàn)更復雜的邏輯,如事務(wù)管理。比如下面的代碼,

@Around注解表明這是一個環(huán)繞通知,

ProceedingJoinPoint參數(shù)表示正在執(zhí)行的連接點,可以調(diào)用

proceed()方法來執(zhí)行目標方法。環(huán)繞通知需要手動調(diào)用

proceed()方法,否則目標方法不會被執(zhí)行,同時可以對返回值進行修改。

import?org.aspectj.lang.annotation.Aspect;
import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.Around;

@Aspect
public?class?MyAspect?{
????@Around("execution(*?com.example.service.MyService.*(..))")
????public?Object?aroundAdvice(ProceedingJoinPoint?pjp)?throws?Throwable?{
????????System.out.println("This?is?around?advice.?Before?target?method.");
????????Object?result?=?pjp.proceed();?//?執(zhí)行目標方法
????????System.out.println("This?is?around?advice.?After?target?method.");
????????return?result;
????}
}

linux命令行如何找到占用端口的進程PID

可以通過 lsof 命令來找到占用端口的進程 ID,例如要查找占用 TCP 端口 3306 的進程 PID,可以執(zhí)行以下命令:

lsof?-i?:3306?-sTCP:LISTEN

上述命令中,-i參數(shù)用于指定要監(jiān)聽的網(wǎng)絡(luò)地址和端口號,:后面跟上端口號;-sTCP:LISTEN表示只列出狀態(tài)為監(jiān)聽(LISTEN)的 TCP 連接,這樣可以更精準地找到占用指定端口的進程。執(zhí)行該命令后,如果有進程占用 3306 端口,會顯示相關(guān)進程信息,其中PID列即為進程的 PID 號。

也可以通過 netstat 命令來找到占用端口的進程 ID,例如要查找占用 TCP 端口 3306 的進程 PID,可以執(zhí)行以下命令:

netstat?-tulnpe?|?grep?:3306

上述命令中,-t表示列出 TCP 連接,-u表示列出 UDP 連接,-l表示只列出處于監(jiān)聽狀態(tài)的連接,-n表示以數(shù)字形式顯示地址和端口號,避免進行 DNS 解析,從而加快命令執(zhí)行速度,-p表示顯示占用該連接的進程 ID 和進程名稱,-e表示顯示擴展信息。管道符|netstat命令的輸出作為grep命令的輸入,grep :3306用于在netstat命令的輸出結(jié)果中查找包含:8080的行,即找到占用 3306 端口的進程相關(guān)信息,其中包含進程的 PID 號。

相關(guān)推薦