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

4399 薪資開(kāi)了,要不要去?

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

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

大家好,我是小林。

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

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

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

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

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

4399一面

java是怎么學(xué)習(xí)的?

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

java語(yǔ)言的特點(diǎn)是什么?

主要有以下的特點(diǎn):

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

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

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

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

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

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


//?修飾對(duì)象
final?StringBuilder?sb?=?new?StringBuilder("Hello");
sb.append(",?World");?//?允許修改對(duì)象的內(nèi)部狀態(tài)
//?以下代碼將無(wú)法編譯
//?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)?{
????//?以下代碼將無(wú)法編譯
????//?value?=?10;?
????System.out.println(value);
}

使用hashmap時(shí),如果只重寫(xiě)了equals沒(méi)有重寫(xiě)hashcode會(huì)出現(xiàn)什么問(wèn)題?

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

如果只重寫(xiě)了 equals 方法而未重寫(xiě) hashCode 方法,那么即使兩個(gè)對(duì)象在邏輯上相等(根據(jù) equals 方法的判斷),它們的 hashCode 可能不同,這樣會(huì)導(dǎo)致兩個(gè)邏輯上相等的對(duì)象可能被存儲(chǔ)在 HashMap 的不同哈希桶中,因?yàn)樗鼈兊?hashCode 不同。

當(dāng)嘗試根據(jù)鍵來(lái)查找元素時(shí),可能無(wú)法找到元素。因?yàn)?HashMap 首先根據(jù) hashCode 找到哈希桶,而由于 hashCode 不同,會(huì)在錯(cuò)誤的哈希桶中查找,導(dǎo)致查找失敗,即使 equals 方法認(rèn)為它們相等。

為了避免這個(gè)問(wèn)題,當(dāng)重寫(xiě) equals 方法時(shí),應(yīng)該同時(shí)重寫(xiě) hashCode 方法,以保證對(duì)象的邏輯相等性和存儲(chǔ)查找的一致性。

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

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

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

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

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

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

使用Class類(lèi)的newInstance()方法:通過(guò)反射機(jī)制,可以使用Class類(lèi)的newInstance()方法創(chuàng)建對(duì)象。

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

使用Constructor類(lèi)的newInstance()方法:同樣是通過(guò)反射機(jī)制,可以使用Constructor類(lèi)的newInstance()方法創(chuàng)建對(duì)象。

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

使用clone()方法:如果類(lèi)實(shí)現(xiàn)了Cloneable接口,可以使用clone()方法復(fù)制對(duì)象。

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

使用反序列化:通過(guò)將對(duì)象序列化到文件或流中,然后再進(jìn)行反序列化來(lái)創(chuàng)建對(duì)象。

//?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類(lèi)

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

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

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

采用繼承Thread類(lèi)方式

    優(yōu)點(diǎn): 編寫(xiě)簡(jiǎn)單,如果需要訪問(wèn)當(dāng)前線程,無(wú)需使用Thread.currentThread ()方法,直接使用this,即可獲得當(dāng)前線程缺點(diǎn):因?yàn)榫€程類(lèi)已經(jīng)繼承了Thread類(lèi),所以不能再繼承其他的父類(lèi)

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

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

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

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

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

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

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

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

class?MyCallable?implements?Callable<Integer>?{
????@Override
????public?Integer?call()?throws?Exception?{
????????//?線程執(zhí)行的代碼,這里返回一個(gè)整型結(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();
????}
}

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

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

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

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

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)閉線程池
}

采用線程池方式:

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

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

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

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

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

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

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

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

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

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

MySQL InnoDB 引擎的默認(rèn)隔離級(jí)別;

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

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

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

也就是說(shuō):

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

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

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

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

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

    • 對(duì)于「讀未提交」隔離級(jí)別的事務(wù)來(lái)說(shuō),因?yàn)榭梢宰x到未提交事務(wù)修改的數(shù)據(jù),所以直接讀取最新的數(shù)據(jù)就好了;對(duì)于「串行化」隔離級(jí)別的事務(wù)來(lái)說(shuō),通過(guò)加讀寫(xiě)鎖的方式來(lái)避免并行訪問(wèn);對(duì)于「讀提交」和「可重復(fù)讀」隔離級(jí)別的事務(wù)來(lái)說(shuō),它們是通過(guò) Read View來(lái)實(shí)現(xiàn)的,它們的區(qū)別在于創(chuàng)建 Read View 的時(shí)機(jī)不同,

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

查詢(xún)當(dāng)前數(shù)據(jù)庫(kù)的事務(wù)隔離級(jí)別的命令是什么?

在 MySQL8.0+ 版本中:

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

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

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

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

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

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

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

spring的AOP的作用是什么?

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

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

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

AOP 常見(jiàn)的通知類(lèi)型有哪些?相關(guān)術(shù)語(yǔ)解釋

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

前置通知(BeforeAdvice):在目標(biāo)方法執(zhí)行之前調(diào)用通知。它可以用于執(zhí)行一些前置的操作,例如參數(shù)檢查、權(quán)限驗(yàn)證等。比如下面的代碼使用了@Before注解,該注解的參數(shù)是一個(gè)切點(diǎn)表達(dá)式,表示在com.example.service.MyService類(lèi)中的任何方法執(zhí)行之前,都會(huì)執(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):在目標(biāo)方法執(zhí)行完成之后調(diào)用通知,無(wú)論目標(biāo)方法是否正常結(jié)束或拋出異常。后置通知通常用于釋放資源或執(zhí)行一些清理工作。比如下面的代碼,這里使用了@After注解,意味著afterAdvice方法會(huì)在com.example.service.MyService類(lèi)中的任何方法執(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)繞通知是最強(qiáng)大的一種通知,它可以在目標(biāo)方法調(diào)用前后自定義操作,并且可以控制目標(biāo)方法是否執(zhí)行,以及修改其返回值。環(huán)繞通知可以實(shí)現(xiàn)更復(fù)雜的邏輯,如事務(wù)管理。比如下面的代碼,

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

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

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

proceed()方法,否則目標(biāo)方法不會(huì)被執(zhí)行,同時(shí)可以對(duì)返回值進(jìn)行修改。

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í)行目標(biāo)方法
????????System.out.println("This?is?around?advice.?After?target?method.");
????????return?result;
????}
}

linux命令行如何找到占用端口的進(jìn)程PID

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

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

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

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

netstat?-tulnpe?|?grep?:3306

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

相關(guān)推薦