各大廠面試整理 20 道 Java 後端開發面試題總結

1、Zookeeper 中都有哪些服務器角色?

Leader

Leader服務器是整個ZooKeeper集羣工作機制中的核心,其主要工作有以下兩個。

1)事務請求的唯一調度和處理者,保證集羣事務處理的順序性。

2)集羣內部各服務器的調度者。

Follower

從角色名字上可以看出,Follower服務器是ZooKeeper集羣狀態的跟隨者,其主要工作有以下三個。

1)處理客戶端的非事務請求,轉發事務請求給Leader服務器。

2)參與事務請求Proposal的投票。

3)參與Leader選舉投票。

Observer

Zookeeper3.3.0版本以後引入的一個全新的服務器角色,在不影響集羣事務處理能力的基礎上提升集羣的非事務處理能力從字面意思看,該服務器充當了一個觀察者的角色—其觀察ZooKeeper集羣的最新狀態變化並將這些狀態變更同步過來。

1)Observer服務器在工作原理上和Follower基本是一致的,對於非事務請求,都可以進行獨立的處理,而對於事務請求,則會轉發給Leader服務器進行處理。

2)對比Follower唯一的區別是Observer不參與任何形式的投票,包括事務請求Proposal的投票和Leader選舉投票。簡單地講,Observer服務器只提供非事務服務,通常用於在不影響集羣事務處理能力的前提下提升集羣的非事務處理能力。另外,Observer的請求處理鏈路和Follower服務器也非常相近。

2、RabbitMQ 有幾種廣播類型?

direct(默認方式):最基礎最簡單的模式,發送方把消息發送給訂閱方,如果有多個訂閱者,默認採取輪詢的方式進行消息發送。

headers:與direct類似,只是性能很差,此類型幾乎用不到。

fanout:分發模式,把消費分發給所有訂閱者。

topic:匹配訂閱模式,使用正則匹配到消息隊列,能匹配到的都能接收到。

3、Java 中 i++ 和 ++i 有什麼區別?

概念區別

i++表示先引用i變量的數值然後再對i進行加1的操作,而++i是先對i變量進行加1的操作,然後再引用i變量的數值。

表達式形式區別

i++將“++”放在變量的後面,++i將“++”放在變量的前面。

預算優先級區別

i++中的“++”運算符的優先級比++i中“++”運算符的優先級高。

int i = 1,j = 1,h = 1;
j = i++;
System.out.println(j);

i = 1;
h = ++i;
System.out.println(h);

執行j = i++,先將i變量的值1賦值給j,此時j=1,i纔等於2;執行h = ++i,會先將i變量的值1加1變成2,然後賦值給h,此時h的值爲2。

4、什麼是網絡層?

網絡層的任務就是選擇合適的網間路由和交換結點,確保計算機通信的數據及時傳送。在發送數據時,網絡層把運輸層產生的報文段或用戶數據報封裝成分組和包進行傳送。在TCP/IP體系結構中,由於網絡層使用IP協議,因此分組也叫IP數據報 ,簡稱數據報。

互聯網是由大量的異構(heterogeneous)網絡通過路由器(router)相互連接起來的。互聯網使用的網絡層協議是無連接的網際協議(Intert Prococol)和許多路由選擇協議,因此互聯網的網絡層也叫做網際層或IP層。

發送端在層與層之間傳輸數據時,每經過一層時會被打上一個該層所屬的首部信息。反之,接收端在層與層之間傳輸數據時,每經過一層時會把對應的首部信息去除。

5、JSP 模版引擎如何解析 ${} 表達式?

目前開發中已經很少使用JSP模版引擎,JSP雖然是一款功能比較強大的模板引擎,並被廣大開發者熟悉,但它前後端耦合比較高。

其次是JSP頁面的效率沒有HTML高,因爲JSP是同步加載。而且JSP需要Tomcat應用服務器部署,但不支持Nginx等,已經快被時代所淘汰。

JSP頁面中使用${表達式}展示數據,但是頁面上並沒有顯示出對應數據,而是把${表達式}當作純文本顯示。

原因分析:這是由於jsp模版引擎默認會無視EL表達式,需要手動設置igNoreEL爲false。

<%@ page isELIgnored="false" %>

6、什麼是雙親委派模型?

雙親委派模型要求除了頂層的啓動類加載器外,其他的類加載器都應當有自己的父類加載器。

這裏類加載器之間的父子關係一般不會以繼承關係來實現,而是都使用組合關係來複用父加載器的代碼。

工作過程

如果一個類加載器收到了類加載的請求時,它首先不會自己去嘗試加載這個類,而是把這個請求委派給自己的父類加載器去加載,每一個層次的類加載器都是如此。

因此所有的加載請求最終都應該傳遞到頂層的啓動類加載器中,只有當父類加載器反饋自己無法完成這個請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試自己去加載。

好處

Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係。例如類Object,它放在rt.jar中,無論哪一個類加載器要加載這個類,最終都是委派給啓動類加載器進行加載。

因此Object類在程序的各種類加載器環境中都是同一個類,判斷兩個類是否相同是通過classloader.class這種方式進行的,所以哪怕是同一個class文件如果被兩個classloader加載,那麼他們也是不同的類。

7、Callable 和 Runnable 有什麼區別?

Callable接口比Runnable接口要新一點,它是在JDK1.5版本時發行的。

Callable接口和Runnable接口都是設計來代表一個任務(task),這個任務可以被任意線程執行, 但兩者間還是有一些明顯的差異。最主要的差異體現在在Callable接口可以在內部的call()方法返回執行的結果,而Runnable接口則不行。

通俗易懂的解釋就是Callable接口和Runnable接口都能用來編寫多線程,但實現Callable接口的任務線程能返回執行結果,而實現Runnable接口的任務線程不能返回結果。另一個明顯的差別是Callable接口可以拋出checked exception,因爲它的call()方法拋出了這個異常。

Callable接口通常需要和Future/FutureTask結合使用,用於獲取異步任務中的結果。

總結

Callable接口比Runnable接口要新一些,前者源於JDK1.5版本.前者源於JDK1.0版本。

Runnable接口使用run()方法來描述一個任務(task),而Callable接口使用call()方法。run()方法不會返回結果, 因爲它的返回類型是void,而Callable是個支持泛型的接口,當要實現(implement)一個Callable接口時就會提供一個返回值類型。run()方法不會拋出checked exception異常, 而call()方法可以。

8、Spring Boot 中如何解決跨域問題?

跨域可以在前端通過JSONP來解決,但是JSONP只可以發送GET請求,無法發送其他類型的請求。

在RESTful風格的應用中,就顯得非常雞肋,因此推薦在後端通過(CORS,Cross-origin resource sharing)來解決跨域問題。

這種解決方案並非Spring Boot特有的,在傳統的SSM框架中,就可以通過CORS來解決跨域問題,只不過之前是在XML文件中配置CORS,現在可以通過實現WebMvcConfigurer接口然後重寫addCorsMappings方法解決跨域問題。

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .maxAge(3600);
    }
}

項目中前後端分離部署,所以需要解決跨域的問題。使用cookie存放用戶登錄的信息,在spring攔截器進行權限控制,當權限不符合時,直接返回給用戶固定的json結果。

注意:當用戶退出登錄狀態時或者token過期時,由於攔截器和跨域的順序有問題,出現了跨域的現象。http請求先經過filter,到達servlet後才進行攔截器的處理,如果把cors放在filter中就可以優先於權限攔截器執行。

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

9、Netty 和 Tomcat 有什麼區別?

作用不同:Tomcat是Servlet容器,可以視爲Web服務器,而Netty是異步事件驅動的網絡應用程序框架和工具用於簡化網絡編程,例如TCP和UDP套接字服務器。

協議不同:Tomcat是基於http協議的Web服務器,而Netty能通過編程自定義各種協議,因爲Netty本身自己能編碼/解碼字節流,所有Netty可以實現,HTTP服務器、FTP服務器、UDP服務器、RPC服務器、WebSocket服務器、Redis的Proxy服務器、MySQL的Proxy服務器等等。

10、你都知道哪些微服務技術棧?

微服務描述 技術名稱
服務開發 Springboot、Spring、SpringMVC
服務配置與管理 Netflix公司的Archaius、阿里的Diamond等
服務註冊與發現 Eureka、Consul、Zookeeper等
服務調用 REST、RPC、gRPC
服務熔斷器 Hystrix、Envoy等
負載均衡 Ribbon、Nginx等
服務接口調用(客戶端調用服務發簡單工具) Feign等
消息隊列 kafka、RabbitMQ、ActiveMQ等
服務配置中心管理 SpringCloudConfig、Chef等
服務路由(API網關) Zuul等
服務監控 Zabbix、Nagios、Metrics、Spectator等
全鏈路追蹤 Zipkin、Brave、Dapper等
服務部署 Docker、OpenStack、Kubernetes等
數據流操作開發包 SpringCloud Stream(封裝與Redis,Rabbit、Kafka等發送接收消息)
事件消息總線 Spring Cloud Bus

11、TCP 中什麼是粘包和拆包?

TCP的數據發送都是靠流,流由一個接一個的數據包組成。

發送過程中tcp會把數據拆成很多個包,也有可能將小的數據合成一個大包。

12、Java 中什麼是 ParNew 垃圾收集器?

ParNew垃圾收集器其實是Serial收集器的多線程版本,也使用複製算法,除了使用多線程進行垃圾收集之外,其餘的行爲和Serial 收集器完全一樣,ParNew垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作線程。

ParNew收集器默認開啓和CPU數目相同的線程數,可以通過-XX:ParallelGCThreads 參數來限制垃圾收集器的線程數。

【Parallel:平行的】

ParNew雖然是除了多線程外和Serial收集器幾乎完全一樣,但是ParNew垃圾收集器是很多java虛擬機運行在Server模式下新生代的默認垃圾收集器。

13、Zookeeper 中什麼是 ZAB 協議?

ZAB協議是爲分佈式協調服務Zookeeper專門設計的一種支持崩潰恢復的原子廣播協議。

ZAB協議包括兩種基本的模式:崩潰恢復和消息廣播。

崩潰恢復:在正常情況下運行非常良好,一旦Leader出現崩潰或者由於網絡原因導致Leader服務器失去了與過半Follower的聯繫,那麼就會進入崩潰恢復模式。爲了程序的正確運行,整個恢復過程後需要選舉出一個新的Leader,因此需要一個高效可靠的選舉方法快速選舉出一個Leader。

消息廣播:類似一個兩階段提交過程,針對客戶端的事務請求, Leader服務器會爲其生成對應的事務Proposal,並將其發送給集羣中的其餘所有機器,再分別收集各自的選票,最後進行事務提交。

當整個zookeeper集羣剛剛啓動或者Leader服務器宕機、重啓或者網絡故障導致不存在過半的服務器與Leader服務器保持正常通信時,所有進程(服務器)進入崩潰恢復模式,首先選舉產生新的Leader服務器,然後集羣中Follower服務器開始與新的Leader服務器進行數據同步,當集羣中超過半數機器與該Leader服務器完成數據同步之後,退出恢復模式進入消息廣播模式,Leader服務器開始接收客戶端的事務請求生成事物提案來進行事務請求處理。

14、AMQP是什麼?

AMQP(Advanced Message Queueing Protocol)協議是一個開放的標準的的協議,它定義了系統之間如何傳遞消息。

AMQP不僅定義了consumer、producer、broker之間如何交互,也定義了消息的格式和命令的交換。

RabbitMQ就是AMQP協議的Erlang的實現(當然RabbitMQ還支持STOMP2、MQTT3等協議)AMQP的模型架構和RabbitMQ的模型架構是一樣的,生產者將消息發送給交換器,交換器和隊列綁定。

RabbitMQ中的交換器、交換器類型、隊列、綁定、路由鍵等都是遵循的AMQP協議中相應的概念。

目前RabbitMQ最新版本默認支持的是AMQP0-9-1。

15、說一說 Spring MVC 註解原理?

註解本質是一個繼承了Annotation的特殊接口,其具體實現類是JDK動態代理生成的代理類。

通過反射獲取註解時,返回的也是Java運行時生成的動態代理對象。

通過代理對象調用自定義註解的方法,會最終調用AnnotationInvocationHandler的invoke方法,該方法會從memberValues這個Map中查詢出對應的值,而memberValues的來源是Java常量池。

16、Mybatis 中如何實現模糊查詢 like 語句?

方式一:在Java代碼中添加sql通配符。

string name = “%微信公衆號“Java精選”%”;
list<name> names = mapper.selectJingXuanLike(name);
<select id=”selectJingXuanLike”>
 select * from foo where t_article like #{value}
</select>

方式二:在sql語句中拼接通配符,會引起sql注入。

string name = “微信公衆號“Java精選””;
list<name> names = mapper.selectJingXuanLike(name);
<select id=”selectJingXuanLike”>
     select * from foo where t_article like '%${value}%'
</select>

17、說一說 Java 中方法區\永久代(線程共享)?

永久代(Permanent Generation)用於存儲被 JVM 加載的類信息、 常量、 靜態變量、 即時編譯器編譯後的代碼等數據。

HotSpot VM把GC分代收集擴展至方法區,即使用Java堆的永久代來實現方法區,這樣HotSpot的垃圾收集器就可以像管理Java堆一樣管理這部分內存,而不必爲方法區開發專門的內存管理器(永久帶的內存回收的主要目標是針對常量池的回收和類型的卸載,因此收益一般很小)。

運行時常量池(Runtime Constant Pool)是方法區的一部分。

Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量池 (Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後存放到方法區的運行時常量池中。 Java虛擬機對Class文件的每一部分(自然也包括常量池)的格式都有嚴格的規定,每一個字節用於存儲哪種數據都必須符合規範上的要求,這樣纔會被虛擬機認可、裝載和執行。

18、a==b 與 a.equals(b) 有什麼區別? a==b 與 a.equals(b) 有什麼區別?

假設a和b都是對象

a==b是比較兩個對象內存地址,當a和b指向的是堆中的同一個對象纔會返回true。

a.equals(b)是比較的兩個值內容,其比較結果取決於equals()具體實現。

多數情況下需要重寫這個方法,如String類重寫equals()用於比較兩個不同對象,但是包含的字母相同的比較:

public boolean equals(Object obj) {
	if (this == obj) {// 相同對象直接返回true
		return true;
	}
	if (obj instanceof String) {
		String anotherString = (String)obj;
		int n = value.length;
		if (n == anotherString.value.length) {
			char v1[] = value;
			char v2[] = anotherString.value;
			int i = 0;
			while (n-- != 0) {
				if (v1[i] != v2[i])
					return false;
				i++;
			}
			return true;
		}
	}
	return false;
}

19、應用服務 8080 端口被意外佔用如何解決?

1)按鍵盤WIN+R鍵,打開後在運行框中輸入“CMD”命令,點擊確定。

2)在CMD窗口,輸入“netstat -ano”命令,按回車鍵,即可查看所有的端口占用情況。

3)找到本地地址一覽中類似“0.0.0.0:8080”信息,通過此列查看8080端口對應的程序PID。

4)打開任務管理器,詳細信息找到對應的應用PID(若不存在通過設置可以調出來),右鍵結束任務即可。

20、JSP 中靜態包含和動態包含有什麼區別?

動態包含

<jsp:include page="jingxuan.jsp" flush="true"/> 

比如a.jsp動態導入了b.jsp,只有當服務器訪問a.jsp中的b.jsp模塊時,java纔會編譯執行b.jsp文件,將其結果動態包含進來。

1、會將多個jsp頁面分別再編寫成java文件,編譯成class文件。
2、jsp文件中允許有相同的變量名,每個頁面互不影響。
3、當java代碼比較多優先選用動態導入。
4、效率相對較低,耦合性低。

動態包含用於加載經常變化的、要求顯示最新版本內容的數據。

靜態包含

<%@ include file="jingxuan.htm"%> 

1、會將多個jsp頁面合成一個jsp頁面,再編寫成java文件,編譯成class文件。
2、jsp文件中不允許有相同的變量名。
3、當java代碼比較少或者沒有java代碼是優先選用靜態導入。
4、效率相對較高,耦合性高。

靜態包含一般用於加載進頁面顯示後就再也不變的數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章