天天看點

Java面試總結(面試流程及核心面試題)

Java面試流程及核心面試題

面試整體流程

 1.1 簡單的自我介紹

      我是xxxx,工作xxx年.我先後在xxxx公司、yyyy公司工作。先後做個xxxx項目、yyyy項目。

 1.2 你簡單介紹一下xxxx項目

    為了解決xxxx問題,開發了一套xxxx系統,該系統主要有那些部分組成。簡單介紹項目的整體架構。參與某個子產品的開發。就要求你說一下這個子產品的業務及設計。

 1.3 會問一下JAVA的專業技能

     後面詳細講解

 1.4你還有什麼需要詢問我的嗎

     公司要做的項目?項目中會使用一下什麼技術?

注意:經曆了多輪面試後,對于你的自我介紹和項目項目經驗面試官就不太關心了。

   你說一下你最擅長的什麼?你簡單說一下?

最終技術面試完成後,都會讓你回家等消息,或者等hr來和你談薪資和福利。

java的專業技能

2.1 java的基礎部分

2.1.1 簡單講一下java的跨平台原理

      由于各作業系統(windows,liunx等)支援的指令集,不是完全一緻的。就會讓我們的程式在不同的作業系統上要執行不同程式代碼。Java開發了适用于不同作業系統及位數的java虛拟機來屏蔽個系統之間的差異,提供統一的接口。對于我們java開發者而言,你隻需要在不同的系統上安裝對應的不同java虛拟機、這時你的java程式隻要遵循java規範,就可以在所有的作業系統上面運作java程式了。

Java通過不同的系統、不同版本、不同位數的java虛拟機(jvm),來屏蔽不同的系統指令集差異而對外體統統一的接口(java API),對于我們普通的java開發者而言,隻需要按照接口開發即可。如果我系統需要部署到不同的環境時,隻需在系統上面按照對應版本的虛拟機即可。

2.2.2 搭建一個java開發環境的步驟

Java開發環境需要些什麼?

适用于我們開發環境的jdk

對應開發環境eclipse

還需要web伺服器(tomcat)

下載下傳對應元件

安裝

Jdk,安裝正常流程安裝即可,配置我們的JAVA_HOME,因為後面的eclispe和tomcat會依賴于這個變量.

Eclispe正常解壓就ok,設定workspace的預設編碼

Tomcat 正常解壓就ok,把tomcat內建到eclispe中,安裝插件就OK。

.......

Svn/git

2.1.3講一下java中int資料占幾個位元組

Java中有幾種基本資料類型?8種

Int占 4個位元組,32位

Boolean 1位

2.1.4 面向對象的特征有哪些方面

有四大基本特征:封裝、抽象、繼承、多态

      面向對象的封裝性,即将對象封裝成一個高度自治和相對封閉的個體,對象狀态(屬性)由這個對象自己的行為(方法)來讀取和改變。

張三這個人,他的姓名等屬性,要有自己提供的擷取或改變的方法來操作。private name setName getName

      抽象就是找出一些事物的相似和共性之處,然後将這些事物歸為一個類,這個類隻考慮這些事物的相似和共性之處,并且會忽略與目前主題和目标無關的那些方面,将注意力集中在與目前目标有關的方面。 就是把現實生活中的對象,抽象為類。

在定義和實作一個類的時候,可以在一個已經存在的類的基礎之上來進行,把這個已經存在的類所定義的内容作為自己的内容,并可以加入若幹新的内容,或修改原來的方法使之更适合特殊的需要,這就是繼承。遺産繼承

多态是指程式中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在程式設計時并不确定,而是在程式運作期間才确定,即一個引用變量倒底會指向哪個類的執行個體對象,該引用變量發出的方法調用到底是哪個類中實作的方法,必須在由程式運作期間才能決定。

Object obj = new xxx();

UserDao userDao = new UserDaoJdbcImpl();

UserDao userDao = new UserDaoHibernateImpl();

靠的是父類或接口定義的引用變量可以指向子類或具體實作類的執行個體對象,而程式調用的方法在運作期才動态綁定,就是引用變量所指向的具體執行個體對象的方法,也就是記憶體裡正在運作的那個對象的方法,而不是引用變量的類型中定義的方法。

原則:回答比較抽象問題的時候,要舉例說明

2.1.5有了基本資料類型,為什麼還需要包裝類型?

基本資料類型,java中提供了8中基本的資料類型。boolean int float等

包裝類型:每一個基本的資料類型都會一一對應一個包裝類型。

boolean ----->Boolean

Int -------->Integer

裝箱和拆箱

裝箱:把基本的資料類型轉換成對應的包裝類型.

Integer .valueOf(1)

Integer i = 1;自動裝箱,實際上在編譯時會調用Integer .valueOf方法來裝箱

拆箱:就是把包裝類型轉換為基本資料類型.基本資料類型 名稱 = 對應的包裝類型。

Integer i = 1;

int j = i;//自動拆箱//int j = i=intValue();手動拆箱

自動拆箱:實際上會在編譯調用intValue

Java是一個面向對象的語言,而基本的資料類型,不具備面向對象的特性。

null Integer--->null int---->0 用Integer和int分别表示Person這個類的ID

Max 最大值

min 最小值

緩存值:對象緩存,Integer i=1; integer j= 1;i ==j

2.1.6、說一下"=="和equals方法究竟有什麼差別?

非常經典的一個面試題?先說清楚一個,再來說另一個?

==用來判斷兩個變量之間的的值是否相等。變量就可以分為基本資料類型變量,引用類型。

如果是基本資料類型的變量直接比較值而引用類型要比較對應的引用的記憶體的首位址。

equals 用來比較兩個對象長得是否一樣。判斷兩個對象的某些特征是否一樣。實際上就是調用對象的equals方法進行比較。

2.1.7講一下String和StringBuilder的差別(final)?StringBuffer和StringBuilder的差別?

1.在java中提供三個類String StringBuillder StringBuffer來表示和操作字元串。字元串就是多個字元的集合。

String是内容不可變的字元串。String底層使用了一個不可變的字元數組(final char[])

String str =new String(“bbbb”);

而StringBuillder StringBuffer,是内容可以改變的字元串。StringBuillder StringBuffer底層使用的可變的字元數組(沒有使用final來修飾)

2.最經典就是拼接字元串。

String進行拼接.String c = “a”+”b”;

StringBuilder或者StringBuffer

StringBuilder sb = new StringBuilder(); sb.apend(“a”).apend(“b”)

拼接字元串不能使用String進行拼接,要使用StringBuilder或者StringBuffer

3.StringBuilder是線程不安全的,效率較高.而StringBuffer是線程安全的,效率較低。

2.1.8、講一下java中的集合?

Java中的集合分為value,key--vale(Conllection Map)兩種。

存儲值有分為List 和Set.

List是有序的,可以重複的。

Set是無序的,不可以重複的。根據equals和hashcode判斷,也就是如果

一個對象要存儲在Set中,必須重寫equals和hashCode方法。

存儲key-value的為map.

ArrayList和LinkedList的差別?

  List常用的ArrayList和LinkedList。差別和使用場景?

  ArrayList底層使用時數組。LinkedList使用的是連結清單。

  數組查詢具有所有查詢特定元素比較快。而插入和删除和修改比較慢(數組在記憶體中是一塊連續的記憶體,如果插入或删除是需要移動記憶體)。

  連結清單不要求記憶體是連續的,在目前元素中存放下一個或上一個元素的位址。查詢時需要從頭部開始,一個一個的找。是以查詢效率低。插入時不需要移動記憶體,隻需改變引用指向即可。是以插入或者删除的效率高。

ArrayList使用在查詢比較多,但是插入和删除比較少的情況,而LinkedList使用在查詢比較少而插入和删除比較多的情況。

2.1.9講一下HashMap哈HashTable的差別?HashTable和ConcurrentHashMap的差別?

相同點:HashMap和HasheTalbe都可以使用來存儲key--value的資料。

差別:

HashMap是可以把null作為key或者value的,而HashTable是不可以的。

HashMap是線程不安全的,效率較高。而HashTalbe是線程安全的,效率較低。

?我想線程安全但是我又想效率高?

通過把整個Map分為N個Segment(類似HashTable),可以提供相同的線程安全,但是效率提升N倍,預設提升16倍。

2.1.10、實作一個拷貝檔案的工具類使用位元組流還是字元流?

我們拷貝的檔案不确定是隻包含字元流,有可以能有位元組流(圖檔、聲音、圖像等),為考慮到通用性,要使用位元組流。

2.1.11、講一下線程的幾種實作方式?啟動方式?區分方式?

①實作方式

通過繼承Thread類實作一個線程

通過實作Runnable接口實作一個線程

   繼承擴充性不強,java總隻支援單繼承,如果一個類繼承Thread就不能繼承其他的類了。

②怎麼啟動?

   Thread thread = new Thread(繼承了Thread的對象/實作了Runnable的對象)

   thread.setName(“設定一個線程名稱”);

   thread.start();

   啟動線程使用start方法,而啟動了以後執行的是run方法。

③怎麼區分線程?在一個系統中有很多線程,每個線程都會列印日志,我想區分是哪個線程列印的怎麼辦?

   thread.setName(“設定一個線程名稱”); 這是一種規範,在建立線程完成後,都需要設定名稱。

2.1.12有沒有使用過線程并發庫?

簡單了解過?

JDK5中增加了Doug Lea的并發庫,這一引進給Java線程的管理和使用提供了強大的便利性。 java.util.current包中提供了對線程優化、管理的各項操作,使得線程的使用變得的心應手。該包提供了線程的運作,線程池的建立,線程生命周期的控制.

Java通過Executors提供四個靜态方法建立四種線程池,分别為:

newCachedThreadPool建立一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。

newFixedThreadPool 建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。

newScheduledThreadPool 建立一個定長線程池,支援定時及周期性任務執行。

newSingleThreadExecutor 建立一個單線程化的線程池,它隻會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行

2.1.13線程池的作用?

限定線程的個數,不會導緻由于線程過多導緻系統運作緩慢或崩潰

線程池不需要每次都去建立或銷毀,節約了資源、

線程池不需要每次都去建立,響應時間更快。

連接配接池也是一樣?

2.1.14講一下什麼是設計模式?常用的設計模式有哪些?

設計模式就是經過前人無數次的實踐總結出的,設計過程中可以反複使用的、可以解決特定問題的設計方法。

單例(飽漢模式、饑漢模式)

構造方法私有化,讓出了自己類中能建立外其他地方都不能建立

2、在自己的類中建立一個單執行個體(飽漢模式是一出來就建立建立單執行個體,而饑漢模式需要的時候才建立)

3、提供一個方法擷取該執行個體對象(建立時需要進行方法同步)

工廠模式:Spring IOC就是使用了工廠模式.

       對象的建立交給一個工廠去建立。

代理模式:Spring AOP就是使用的動态代理。

2.2 java web部分

2.2.1講一下http get和post請求的差別?

GET和POST請求都是http的請求方式,使用者通過不同的http的請求方式完成對資源(url)的不同操作。GET,POST,PUT,DELETE就對應着對這個資源的查 ,改 ,增 ,删 4個操作,具體點來講GET一般用于擷取/查詢資源資訊,而POST一般用于更新資源資訊

1、Get請求送出的資料會在位址欄顯示出來,而post請求不會再位址欄顯示出來.

GET送出,請求的資料會附在URL之後(就是把資料放置在HTTP協定頭中),以?分割URL和傳輸資料,多個參數用&連接配接;POST送出:把送出的資料放置在是HTTP包的包體中。 是以,GET送出的資料會在位址欄中顯示出來,而POST送出,位址欄不會改變

2、傳輸資料的大小

http Get請求由于浏覽器對位址長度的限制而導緻傳輸的資料有限制。而POST請求不會因為位址長度限制而導緻傳輸資料限制。

3、安全性,POST的安全性要比GET的安全性高。由于資料是會在位址中呈現,是以可以通過曆史記錄找到密碼等關鍵資訊。

2.2.2 說一下你對servlet的了解?或者servlet是什麼?

Servlet(Server Applet),全稱Java Servlet, 是用Java編寫的伺服器端程式。而這些Sevlet都要實作Servlet這個借口。其主要功能在于互動式地浏覽和修改資料,生成動态Web内容。Servlet運作于支援Java的應用伺服器中。

HttpServlet 重寫doGet和doPost方法或者你也可以重寫service方法完成對get和post請求的響應

2.2.3簡單說一下servlet的生命周期?

servlet有良好的生存期的定義,包括加載和執行個體化、初始化、處理請求以及服務結束。這個生存期由javax.servlet.Servlet接口的init,service和destroy方法表達。

加載Servlet的class---->執行個體化Servlet----->調用Servlet的init完成初始化---->響應請求(Servlet的service方法)----->Servlet容器關閉時(Servlet的destory方法)

Servlet啟動時,開始加載servlet生命周期開始。Servlet被伺服器執行個體化後,容器運作其init方法,請求到達時運作其service方法,service方法自動派遣運作與請求對應的doXXX方法(doGet,doPost)等,當伺服器決定将執行個體銷毀的時候(伺服器關閉)調用其destroy方法。

2.2.4 Servlet API中forward() 與redirect()的差別?

forward是伺服器端的轉向而redirect是用戶端的跳轉。

使用forward浏覽器的位址不會發生改變。而redirect會發生改變。

Forward是一次請求中完成。而redirect是重新發起請求。

Forward是在伺服器端完成,而不用用戶端重新發起請求,效率較高。

2.2.5 JSP和Servlet有哪些相同點和不同點?

JSP是Servlet技術的擴充,所有的jsp檔案都會被翻譯為一個繼承HttpServlet的類。也就是jsp最終也是一個Servlet.這個Servlet對外提供服務。

Servlet和JSP最主要的不同點在于JSP側重于視圖,Servlet主要用于控制邏輯。

Servlet如果要實作html的功能,必須使用Writer輸出對應的html,比較麻煩。而JSP的情況是Java和HTML可以組合成一個擴充名為.jsp的檔案,做界面展示比較友善而嵌入邏輯比較複雜。

2.2.6 jsp有哪些内置對象?作用分别是什麼?

9個内置的對象:

request 使用者端請求,此請求會包含來自GET/POST請求的參數

response 網頁傳回使用者端的回應

pageContext 網頁的屬性是在這裡管理

session 與請求有關的會話期

application servlet正在執行的内容

out 用來傳送回應的輸出

config servlet的構架部件

page JSP網頁本身

exception 針對錯誤網頁,未捕捉的例外

四大作用域:pageContext request session application 可以通過jstl從四大作用域中取值.

Jsp傳遞值request session application cookie也能傳值

2.2.7說一下session和cookie的差別?你在項目中都有哪些地方使用了?

Session和cookie都是會話(Seesion)跟蹤技術。Cookie通過在用戶端記錄資訊确定使用者身份,Session通過在伺服器端記錄資訊确定使用者身份。但是Session的實作依賴于Cookie,sessionId(session的唯一辨別需要存放在用戶端).

cookie 和session 的差別:

1、cookie資料存放在客戶的浏覽器上,session資料放在伺服器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并進行COOKIE欺騙

   考慮到安全應當使用session。

3、session會在一定時間内儲存在伺服器上。當通路增多,會比較占用你伺服器的性能,考慮到減輕伺服器性能方面,應當使用COOKIE。

4、單個cookie儲存的資料不能超過4K,很多浏覽器都限制一個站點最多儲存20個cookie。

5、是以個人建議:

   将登陸資訊等重要資訊存放為SESSION

   其他資訊如果需要保留,可以放在COOKIE中,比如購物車

購物車最好使用cookie,但是cookie是可以在用戶端禁用的,這時候我們要使用cookie+資料庫的方式實作,當從cookie中不能取出資料時,就從資料庫擷取。

2.2.8、MVC的各個部分都有那些技術來實作?

M(Model) 模型 javabean

V(View) 視圖  html jsp volicity freemaker

C(Control) 控制器 Servlet,Action

Jsp+Servlet+javabean 最經典mvc模式,實際上就是model2的實作方式,就是把視圖和邏輯隔離開來

Model1的方式 jsp+service+dao

MOdel2的方式 jsp+servlet+service+dao

使用struts2和springmvc這樣的mvc架構後,jsp+核心控制器+action+javabean

2.3資料庫部分

2.3.1資料庫的分類及常用的資料庫

      資料庫分為:關系型資料庫和非關系型資料庫

          關系型:mysql oracle sqlserver等

          非關系型:redis,memcache,mogodb,hadoop等

2.3.2簡單介紹一下關系資料庫三範式?

    範式就是規範,就是關系型資料庫在設計表時,要遵循的三個規範。

要想滿足第二範式必須先滿足第一範式,要滿足第三範式必須先滿足第二範式。

第一範式(1NF)是指資料庫表的每一列都是不可分割的基本資料項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重複的屬性。列資料的不可分割

二範式(2NF)要求資料庫表中的每個行必須可以被唯一地區分。為實作區分通常需要為表加上一個列,以存儲各個執行個體的唯一辨別。(主鍵)

 滿足第三範式(3NF)必須先滿足第二範式(2NF)。簡而言之,第三範式(3NF)要求一個資料庫表中不包含已在其它表中已包含的非主關鍵字資訊。(外鍵)

反三範式,有的時候為了效率,可以設定重複或者可以推導出的字段.

        訂單(總價)和訂單項(單價)

2.3.3事務四個基本特征或 ACID 特性。

事務是并發控制的機關,是使用者定義的一個操作序列。這些操作要麼都做,要麼都不做,是一個不可分割的工作機關。

一個轉賬必須 A賬号扣錢成功,B賬号加錢成功,才算正真的轉賬成功。

  事務必須滿足四大特征:原子性,一緻性,隔離性持久性/持續性

 原子性:表示事務内操作不可分割。要麼都成功、要麼都是失敗.

 一緻性:要麼都成功、要麼都是失敗.後面的失敗了要對前面的操作進行復原。

 隔離性:一個事務開始後,不能後其他事務幹擾。

 持久性/持續性:表示事務開始了,就不能終止。

2.3.4 mysql資料庫的預設的最大連接配接數?

100

 為什麼需要最大連接配接數?特定伺服器上面的資料庫隻能支援一定數目同時連接配接,這時候我們一般都會設定最大連接配接數(最多同時服務多少連接配接)。在資料庫安裝時都會有一個預設的最大連接配接數為100

2.3.5說一下msyql的分頁?Oracle的分頁?

為什麼需要分頁?在很多資料是,不可能完全顯示資料。進行分段顯示.

Mysql是使用關鍵字limit來進行分頁的 limit offset,size 表示從多少索引去多少位.

Oracle的分頁,大部分情況下,我們是記不住了。說思路,要使用三層嵌套查詢。

   Oracle的分頁有點兒記不住了,隻記得一些大概。是使用了三層嵌套查詢。如果在工作中使用了,可以到原來的項目中拷貝或上網查詢。

mysql:

String sql =

"select * from students order by id limit " + pageSize*(pageNumber-1) + "," + pageSize;

oracle:

String sql =

 "select * from " +  

 (select *,rownum rid from (select * from students order by postime desc) where rid<=" + pagesize*pagenumber + ") as t" +

 "where t>" + pageSize*(pageNumber-1);

2.3.6簡單講一下資料庫的觸發器的使用場景?

觸發器,需要有觸發條件,當條件滿足以後做什麼操作。

觸發器用處還是很多的,比如校内網、開心網、Facebook,你發一個日志,自動通知好友,其實就是在增加日志時做一個後觸發,再向通知表中寫入條目。因為觸發器效率高。而UCH沒有用觸發器,效率和資料處理能力都很低。

每插入一個文章,都希望将版面表中的最後發帖時間,文章總數字段進行同步更新,用觸發器做效率就很高。

create table board1(id int primary key auto_increment,name varchar(50),ar

ticleCount int);

create table article1(id int primary key auto_increment,title varchar(50)

,bid int references board1(id));

delimiter |#把分割符;改成|

create trigger insertArticle_Trigger after insert on article1 for each ro

w begin

    -> update board1 set articleCount=articleCount+1 where id= NEW.bid;

    -> end;

    -> |

delimiter ;

insert into board1 value (null,\'test\',0);

insert into article1 value(null,\'test\',1);

2.3.7 簡單講一下資料庫的存儲過程的使用場景?

資料庫存儲過程具有如下優點:

1、存儲過程隻在建立時進行編譯,以後每次執行存儲過程都不需再重新編譯,而一般 SQL 語句每執行一次就編譯一次,是以使用存儲過程可以大大提高資料庫執行速度。

2、通常,複雜的業務邏輯需要多條 SQL 語句。這些語句要分别地從客戶機發送到伺服器,當客戶機和伺服器之間的操作很多時,将産生大量的網絡傳輸。如果将這些操作放在一個存儲過程中,那麼客戶機和伺服器之間的網絡傳輸就會大大減少,降低了網絡負載。

3、存儲過程建立一次便可以重複使用,進而可以減少資料庫開發人員的工作量。

4、安全性高,存儲過程可以屏蔽對底層資料庫對象的直接通路,使用 EXECUTE 權限調用存儲過程,無需擁有通路底層資料庫對象的顯式權限。

正是由于存儲過程的上述優點,目前常用的資料庫都支援存儲過程,例如 IBM DB2,Microsoft SQL Server,Oracle,Access 等,開源資料庫系統 MySQL 也在 5.0 的時候實作了對存儲過程的支援。

定義存儲過程:

create procedure insert_Student (_name varchar(50),_age int ,out _id int)

begin

insert into student value(null,_name,_age);

select max(stuId) into _id from student;

end;

call insert_Student(\'wfz\',23,@id);

select @id;

2.3.8 用jdbc怎麼調用存儲過程?

賈琏欲執事

加載驅動

擷取連接配接

設定參數

執行

釋放連接配接

package com.huawei.interview.lym;

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

import java.sql.Types;

public class JdbcTest {

/**

 * @param args

 */

public static void main(String[] args) {

// TODO Auto-generated method stub

Connection cn = null;

CallableStatement cstmt = null;

try {

//這裡最好不要這麼幹,因為驅動名寫死在程式中了

Class.forName("com.mysql.jdbc.Driver");

//實際項目中,這裡應用DataSource資料,如果用架構,

//這個資料源不需要我們編碼建立,我們隻需Datasource ds = context.lookup()

//cn = ds.getConnection();

cn = DriverManager.getConnection("jdbc:mysql:///test","root","root");

cstmt = cn.prepareCall("{call insert_Student(?,?,?)}");

cstmt.registerOutParameter(3,Types.INTEGER);

cstmt.setString(1, "wangwu");

cstmt.setInt(2, 25);

cstmt.execute();

//get第幾個,不同的資料庫不一樣,建議不寫

System.out.println(cstmt.getString(3));

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

finally

{

/*try{cstmt.close();}catch(Exception e){}

try{cn.close();}catch(Exception e){}*/

try {

if(cstmt != null)

cstmt.close();

if(cn != null)

cn.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

2.3.9常用SQL

2.3.10簡單說一下你對jdbc的了解?

Java database connection java資料庫連接配接.資料庫管理系統(mysql oracle等)是很多,每個資料庫管理系統支援的指令是不一樣的。

Java隻定義接口,讓資料庫廠商自己實作接口,對于我們者而言。隻需要導入對應廠商開發的實作即可。然後以接口方式進行調用.(mysql + mysql驅動(實作)+jdbc)

2.3.11 寫一個簡單的jdbc的程式。寫一個通路oracle資料的jdbc程式?

賈琏欲執事

加載驅動(com.mysql.jdbc.Driver,oracle.jdbc.driver.OracleDriver)

擷取連接配接(DriverManager.getConnection(url,usernam,passord))

設定參數  Statement PreparedStatement

           cstmt.setXXX(index, value);

執行   executeQuery executeUpdate

釋放連接配接(是否連接配接要從小到大,必須放到finnaly)

2.3.12 JDBC中的PreparedStatement相比Statement的好處

大多數我們都使用PreparedStatement代替Statement

1:PreparedStatement是預編譯的,比Statement速度快 

2:代碼的可讀性和可維護性

雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來說.都比直接用Statement的代碼高很多檔次:

stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values (\'"+var1+"\',\'"+var2+"\',"+var3+",\'"+var4+"\')"); 

perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");

perstmt.setString(1,var1);

perstmt.setString(2,var2);

perstmt.setString(3,var3);

perstmt.setString(4,var4);

perstmt.executeUpdate();

不用我多說,對于第一種方法,别說其他人去讀你的代碼,就是你自己過一段時間再去讀,都會覺得傷心。

3:安全性

PreparedStatement可以防止SQL注入攻擊,而Statement卻不能。比如說:

String sql = "select * from tb_name where name= \'"+varname+"\' and passwd=\'"+varpasswd+"\'";

如果我們把[\' or \'1\' = \'1]作為varpasswd傳入進來.使用者名随意,看看會成為什麼?

select * from tb_name = \'随意\' and passwd = \'\' or \'1\' = \'1\';

因為\'1\'=\'1\'肯定成立,是以可以任何通過驗證,更有甚者:

把[\';drop table tb_name;]作為varpasswd傳入進來,則:

select * from tb_name = \'随意\' and passwd = \'\';drop table tb_name;有些資料庫是不會讓你成功的,但也有很多資料庫就可以使這些語句得到執行。

而如果你使用預編譯語句你傳入的任何内容就不會和原來的語句發生任何比對的關系,隻要全使用預編譯語句你就用不着對傳入的資料做任何過慮。而如果使用普通的statement,有可能要對drop等做費盡心機的判斷和過慮。

2.3.13 資料庫連接配接池作用

1、限定資料庫的個數,不會導緻由于資料庫連接配接過多導緻系統運作緩慢或崩潰

2、資料庫連接配接不需要每次都去建立或銷毀,節約了資源

3、資料庫連接配接不需要每次都去建立,響應時間更快。

2.4 前端部分

2.4.1簡單說一下html,css,javascript在網頁開發中的定位?

HTML 超文本标記語言 定義網頁的結構

CSS 層疊樣式表,用來美化頁面

JavaScript主要用來驗證表單,做動态互動(其中ajax)

2.4.2簡單介紹一下Ajax?

  什麼是Ajax? 異步的javascript和xml

  作用是什麼?通過AJAX與伺服器進行資料交換,AJAX可以使網頁實作布局更新。

      這意味着可以在不重新加載整個網頁的情況下,對網頁的某部分進行更新。

  怎麼來實作Ajax XmlHttpRequest對象,使用這個對象可以異步向伺服器發送請求,擷取擷取響應,完成局部更新。Open send responseText/responseXML 局部響應.

  使用場景 登陸失敗時不跳轉頁面,注冊時提示使用者名是否存在,二級關聯等等使用場景

2.4.3 js和jQuery的關系?

  jQuery是一個js架構,封裝了js的屬性和方法。讓使用者使用起來更加便利。

,并且增強了js的功能.

使用原生js是要處理很多相容性的問題(注冊事件等),由jQuery封裝了底層,就不用處理相容性問題。

原生的js的dom和事件綁定和Ajax等操作非常麻煩,jQuery封裝以後操作非常友善。

2.4.4 jQuery的常用選擇器?

ID選擇器   通過ID擷取一個元素

Class選擇器 通過類(css)擷取元素

标簽選擇器 通過标簽擷取元素

通用選擇器(*) 擷取是以的元素

div.myCls  擷取有myCls這個類的div

層次選擇器

     兒子選擇器 > 擷取下面的子元素

     後代選擇器 空格 擷取下面後代,包括兒子、孫子等後代

屬性選擇器

    Tag[attrName=’test’] 擷取有屬性名為xxxx并且屬性的值為test的所有xxx标簽

    <input type=”checkbox” name=”hobby”/> 吃飯<br/>

    <input type=”checkbox” name=”hobby”/> 睡覺<br/>

   Input[name=’hobby’],表示擷取屬性名為name并且name屬性值為hobby的的所有input标簽元素

2.4.5 jQuery的頁面加載完畢事件?

    很多時候我們需要擷取元素,但是必須等到該元素被加載完成後才能擷取。我們可以把js代碼放到該元素的後面,但是這樣就會造成js在我們的body中存在不好管理。所有頁面加載完畢後所有的元素當然已經加載完畢。一般擷取元素做操作都要在頁面加載完畢後操作。

第一種:

$(document).ready(function(){

});

$(document)把原生的document這個dom對象轉換為jQuery對象,轉換完成後才能調用ready方法

ready(fn),表示的是頁面結構被加載完畢後執行傳入函數fn

第二種:

$(function(){

});

當頁面加載完畢後執行裡面的函數,這一種相對簡單,用得最多。

window.onload的差別

jQuery中的頁面加載完畢事件,表示的是頁面結構被加載完畢。

window.onload 表示的是頁面被加載完畢。

  <img src=”htttp://baidu.com/1.jpg”/> onload必須等等頁面中的圖檔、聲音、圖像等遠端資源被加載完畢後才調用而jQuery中隻需要頁面結構被加載完畢。

2.4.6 Jquery的Ajax和原生Js實作Ajax有什麼關系?

jQuery中的Ajax也是通過原生的js封裝的。封裝完成後讓我們使用起來更加便利,不用考慮底層實作或相容性等處理。

如果采用原生js實作Ajax是非常麻煩的,并且每次都是一樣的。如果我們不使用jQuery我們也要封裝Ajax對象的方法和屬性。有像jQuery這些已經封裝完成,并經過很多企業實際的架構,比較可靠并且開源。我們就不需要封裝,直接使用成熟的架構(jQuery)即可.

2.4.7簡單說一下html5?你對現在的那些新技術有了解?

Html5是最新版本的html,是在原來html4的基礎上增強了一些标簽。

Html增加一些像畫闆、聲音、視訊、web存儲等進階功能。但是html5有一個不好的地方,那就是html5太強調語義了,導緻開發中都不知道要選擇那個标簽。

  在做頁面布局是,無論頭部、主題、導航等子產品都使用div來表示,但是html5的規範,需要使用不同的标簽來表示。(header footer等)

你對現在的那些新技術有了解

Html5 css3等

2.4.8 簡單說一下css3?

   Css3是最新版本的css,是對原理css2的功能增強。

   Css3中提供一些原來css2中實作起來比較困難或者不能實作的功能。

     1、盒子圓角邊框

     2、盒子和文字的陰影

     3、漸變

     4、轉換 移動、縮放、旋轉等

     5、過渡、動畫都可以使用動畫。

     6、可以使用媒體查詢實作響應式網站。

   Css3最大缺點就是要根據不同的浏覽器處理相容性。對應有一些處理相容性的工具。不用擔心.

2.4.9 bootstrap是什麼?

BootStrap是一個移動裝置優先的UI架構。我們可以不用謝任何css,js代碼就能實作比較漂亮的有互動性的頁面。我們程式員對頁面的編寫是有硬傷的,所有要自己寫頁面的話就要使用類似于bootstrap這樣的UI架構。

平時用得很多的:

模态框

表單,表單項

布局

删格系統

2.5 架構部分

2.5.1什麼是架構?

  架構(Framework)是一個框子——指其限制性,也是一個架子——指其支撐性。

  IT語境中的架構,特指為解決一個開放性問題而設計的具有一定限制性的支撐結構。在此結構上可以根據具體問題擴充、安插更多的組成部分,進而更迅速和友善地建構完整的解決問題的方案。

1)架構本身一般不完整到可以解決特定問題,但是可以幫助您快速解決特定問題;

   沒有架構所有的工作都從零開始做,有了架構,為我們提供了一定的功能,我們就可以在框 架的基礎上開發,極大的解放了生産力。

不同的架構,是為了解決不同領域的問題。一定要為了解決問題才去學習架構。

2)架構天生就是為擴充而設計的;

3)架構裡面可以為後續擴充的元件提供很多輔助性、支撐性的友善易用的實用工具(utilities),也就是說架構時常配套了一些幫助解決某類問題的庫(libraries)或工具(tools)。

  java中就是一系列的jar包,其本質就是對jdk功能的擴充.

2.5.2 MVC模式

   MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、界面顯示分離的方法組織代碼,将業務邏輯聚集到一個部件裡面,在改進和個性化定制界面及使用者互動的同時,不需要重新編寫業務邏輯。

最簡單的、最經典就是Jsp(view) +Servlet(controller) + JavaBean(model)

當控制器收到來自使用者的請求

控制器調用JavaBean完成業務

完成業務後通過控制器跳轉JSP頁面的方式給使用者回報資訊

Jsp個 使用者做出響應。

    控制器都是核心

2.5.3 MVC架構

什麼是MVC架構?

  是為了解決傳統MVC模式(Jsp + Servlet + JavaBean)的一些問題而出現的架構。

傳統MVC模式問題

所有的Servlet和Servlet映射都要配置在web.xml中,如果項目太大,web.xml就太龐大,并且不能實作子產品化管理。

Servlet的主要功能就是接受參數、調用邏輯、跳轉頁面,比如像其他字元編碼、檔案上傳等功能也要寫在Servlet中,不能讓Servlet主要功能而需要做處理一下特例。

3、接受參數比較麻煩(String name = request.getParameter(“name”),User user=new User user.setName(name)),不能通過model接收,隻能單個接收,接收完成後轉換封裝model.

4、跳轉頁面方式比較單一(forword,redirect),并且當我的頁面名稱發生改變時需要修改Servlet源代碼.

現在比較常用的MVC架構有:

   struts

   webwork

   Struts2

   Spring MVC

2.5.4 簡單講一下struts2的執行流程?

Struts2的原理?

一個請求在Struts2架構中的處理大概分為以下幾個步驟:

1、用戶端浏覽器發送請求

2、這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器,這個過濾器對于Struts2和其他架構的內建很有幫助,例如:SiteMesh Plugin);

3、接着FilterDispatcher(StrutsPrepareAndExecuteFilter)被調用,FilterDispatcher

(StrutsPrepareAndExecuteFilter)詢問ActionMapper來決定這個請求是否需要調用某個Action;

如果ActionMapper決定需要調用某個Action,FilterDispatcher

    (StrutsPrepareAndExecuteFilter)把請求的處理交給ActionProxy;

5、ActionProxy通過Configuration Manager詢問架構的配置檔案,找到需要調用的Action類;

6、ActionProxy建立一個ActionInvocation的執行個體。

7、ActionInvocation執行個體使用命名模式來調用,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用。

8、一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的傳回結果。傳回結果通常是(但不總是,也可能是另外的一個Action鍊)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts2架構中繼承的标簽。在這個過程中需要涉及到ActionMapper。

面試:

浏覽器發送請求,經過一系列的過濾器後,到達核心過濾器(StrutsPrepareAndExecuteFilter).

StrutsPrepareAndExecuteFilter通過ActionMapper判斷目前的請求是否需要某個Action處理,如果不需要,則走原來的流程。如果需要則把請求交給ActionProxy來處理

ActionProxy通過Configuration Manager詢問架構的配置檔案(Struts.xml),找到需要調用的Action類;

建立一個ActionInvocation執行個體,來調用Action的對應方法來擷取結果集的name,在調用前後會執行相關攔截器。

通過結果集的Name知道對應的結果集來對浏覽器進行響應。

攔截、判斷、尋找、執行、響應

2.5.5 Struts2中的攔截器,你都用它幹什麼?

java裡的攔截器是動态攔截Action調用的對象。它提供了一種機制可以使開發者可以定義在一個action執行的前後執行的代碼,也可以在一個action執行前阻止其執行,同時也提供了一種可以提取action中可重用部分的方式。

在AOP(Aspect-Oriented Programming)中攔截器用于在某個方法或字段被通路之前,進行攔截然後在之前或之後加入某些操作。

面試:

    struts2中的的功能(參數處理、檔案上傳、字元編碼等)都是通過系統攔截器實作的。

如果業務需要,當然我們也可以自定義攔截器,進行可插拔配置,在執行Action的方法前後、加入相關邏輯完成業務。

使用場景:

使用者登入判斷,在執行Action的前面判斷是否已經登入,如果沒有登入的跳轉到登入頁面。

使用者權限判斷,在執行Action的前面判斷是否具有,如果沒有權限就給出提示資訊。

記錄檔......

    4、......

2.5.6 簡單講一下SpringMVC的執行流程?

  1. 使用者向伺服器發送請求,請求被Spring 前端控制Servelt DispatcherServlet捕獲;

  2. DispatcherServlet對請求URL進行解析,得到請求資源辨別符(URI)。然後根據該URI,調用HandlerMapping獲得該Handler配置的所有相關的對象(包括Handler對象以及Handler對象對應的攔截器),最後以HandlerExecutionChain對象的形式傳回;

 3. DispatcherServlet 根據獲得的Handler,選擇一個合适的HandlerAdapter。(附注:如果成功獲得HandlerAdapter後,此時将開始執行攔截器的preHandler(...)方法)

   4.  提取Request中的模型資料,填充Handler入參,開始執行Handler(Controller)。 在填充Handler的入參過程中,根據你的配置,Spring将幫你做一些額外的工作:

      HttpMessageConveter: 将請求消息(如Json、xml等資料)轉換成一個對象,将對象轉換為指定的響應資訊

      資料轉換:對請求消息進行資料轉換。如String轉換成Integer、Double等

      資料根式化:對請求消息進行資料格式化。 如将字元串轉換成格式化數字或格式化日期等

      資料驗證: 驗證資料的有效性(長度、格式等),驗證結果存儲到BindingResult或Error中

      5.  Handler執行完成後,向DispatcherServlet 傳回一個ModelAndView對象;

      6.  根據傳回的ModelAndView,選擇一個适合的ViewResolver(必須是已經注冊到Spring容器中的ViewResolver)傳回給DispatcherServlet ;

      7. ViewResolver 結合Model和View,來渲染視圖

      8. 将渲染結果傳回給用戶端。

面試:

使用者向伺服器發送請求,請求被Spring 前端控制Servelt DispatcherServlet捕獲(捕獲)

2、 DispatcherServlet對請求URL進行解析,得到請求資源辨別符(URI)。然後根據該URI,調用HandlerMapping獲得該Handler配置的所有相關的對象(包括Handler對象以及Handler對象對應的攔截器),最後以HandlerExecutionChain對象的形式傳回;(查找handler)

3、 DispatcherServlet 根據獲得的Handler,選擇一個合适的HandlerAdapter。 提取Request中的模型資料,填充Handler入參,開始執行Handler(Controller), Handler執行完成後,向DispatcherServlet 傳回一個ModelAndView對象(執行handler)

4、DispatcherServlet 根據傳回的ModelAndView,選擇一個适合的ViewResolver(必須是已經注冊到Spring容器中的ViewResolver) (選擇ViewResolver)

5、通過ViewResolver 結合Model和View,來渲染視圖,DispatcherServlet 将渲染結果傳回給用戶端。(渲染傳回)

快速記憶技巧:

核心控制器捕獲請求、查找Handler、執行Handler、選擇ViewResolver,通過ViewResolver渲染視圖并傳回

2.5.7 說一下struts2和springMVC有什麼不同?

目前企業中使用SpringMvc的比例已經遠遠超過Struts2,那麼兩者到底有什麼差別,是很多初學者比較關注的問題,下面我們就來對SpringMvc和Struts2進行各方面的比較:

1. 核 心控制器(前端控制器、預處理控制器):對于使用過mvc架構的人來說這個詞應該不會陌生,核心控制器的主要用途是處理所有的請求,然後對那些特殊的請求 (控制器)統一的進行處理(字元編碼、檔案上傳、參數接受、異常處理等等),spring mvc核心控制器是Servlet,而Struts2是Filter。

2.控制器執行個體:Spring Mvc會比Struts快一些(理論上)。Spring Mvc是基于方法設計,而Sturts是基于對象,每次發一次請求都會執行個體一個action,每個action都會被注入 屬性,而Spring更像Servlet一樣,隻有一個執行個體,每次請求執行對應的方法即可(注意:由于是單例執行個體,是以應當避免全局變量的修改,這樣會産生線程安全問題)。

3. 管理方式:大部分的公司的核心架構中,就會使用到spring,而spring mvc又是spring中的一個子產品,是以spring對于spring mvc的控制器管理更加簡單友善,而且提供了全 注解方式進行管理,各種功能的注解都比較全面,使用簡單,而struts2需要采用XML很多的配置參數來管理(雖然也可以采用注解,但是幾乎沒有公司那 樣使用)。

4.參數傳遞:Struts2中自身提供多種參數接受,其實都是通過(ValueStack)進行傳遞和指派,而SpringMvc是通過方法的參數進行接收。

5.學習難度:Struts更加很多新的技術點,比如攔截器、值棧及OGNL表達式,學習成本較高,springmvc 比較簡單,很較少的時間都能上手。

6.intercepter 的實作機制:struts有以自己的interceptor機制,spring mvc用的是獨立的AOP方式。這樣導緻struts的配置檔案量還是比spring mvc大,雖然struts的配置能繼承,是以我覺得論使用上來講,spring mvc使用更加簡潔,開發效率Spring MVC确實比struts2高。spring mvc是方法級别的攔截,一個方法對應一個request上下文,而方法同時又跟一個url對應,是以說從架構本身上spring3 mvc就容易實作restful url。struts2是類級别的攔截,一個類對應一個request上下文;實作restful url要費勁,因為struts2 action的一個方法可以對應一個url;而其類屬性卻被所有方法共享,這也就無法用注解或其他方式辨別其所屬方法了。spring3 mvc的方法之間基本上獨立的,獨享request response資料,請求資料通過參數擷取,處理結果通過ModelMap交回給架構方法之間不共享變量,而struts2搞的就比較亂,雖然方法之間 也是獨立的,但其所有Action變量是共享的,這不會影響程式運作,卻給我們編碼,讀程式時帶來麻煩。

spring mvc處理ajax請求,直接通過傳回資料,方法中使用注解@ResponseBody,spring mvc自動幫我們對象轉換為JSON資料。而struts2是通過插件的方式進行處理

在SpringMVC流行起來之前,Struts2在MVC架構中占核心地位,随着SpringMVC的出現,SpringMVC慢慢的取代struts2,但是很多企業都是原來搭建的架構,使用Struts2較多。

2.5.8 說一下Spring中的兩大核心?

Spring是什麼?

spring是J2EE應用程式架構,是輕量級的IoC和AOP的容器架構(相對于重量級的EJB),主要是針對javaBean的生命周期進行管理的輕量級容器,可以單獨使用,也可以和Struts架構,ibatis架構等組合使用。

IOC(Inversion of Control )或DI(Dependency Injection)

       IOC控制權反轉

          原來:我的Service需要調用DAO,Service就需要建立DAO

          Spring:Spring發現你Service依賴于dao,就給你注入.

       核心原理:就是配置檔案+反射(工廠也可以)+容器(map)  

AOP:面向切面程式設計

       核心原理:使用動态代理的設計模式在執行方法前後或出現異常做加入相關邏輯。

      我們主要使用AOP來做:

      1、事務處理

      2、權限判斷

      3、日志

      4、....

2.5.9 AOP是什麼?你都拿它做什麼?

AOP:面向切面程式設計

       核心原理:使用動态代理的設計模式在執行方法前後或出現異常做加入相關邏輯。

      我們主要使用AOP來做:

      1、事務處理 執行方法前,開啟事務、執行完成後關閉事務、出現異常後復原事務

      2、權限判斷 在執行方法前,判斷是否具有權限

      3、日志     在執行前進行日志處理

      4、....

2.5.10講一下Spring的事務傳播特性

多個事務存在是怎麼處理的政策

PROPAGATION_REQUIRED: 如果存在一個事務,則支援目前事務。如果沒有事務則開啟

2. PROPAGATION_SUPPORTS: 如果存在一個事務,支援目前事務。如果沒有事務,則非事務的執行

3. PROPAGATION_MANDATORY: 如果已經存在一個事務,支援目前事務。如果沒有一個活動的事務,則抛出異常。

4. PROPAGATION_REQUIRES_NEW: 總是開啟一個新的事務。如果一個事務已經存在,則将這個存在的事務挂起。

5. PROPAGATION_NOT_SUPPORTED: 總是非事務地執行,并挂起任何存在的事務。

6. PROPAGATION_NEVER: 總是非事務地執行,如果存在一個活動事務,則抛出異常

7. PROPAGATION_NESTED:如果一個活動的事務存在,則運作在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行

Propagation

Required 需要 如果存在一個事務,則支援目前事務。如果沒有事務則開啟

Supports 支援 如果存在一個事務,支援目前事務。如果沒有事務,則非事務的執行

Mandatory 必要的 如果已經存在一個事務,支援目前事務。如果沒有一個活動的事務,則抛出異常。

required_new 總是開啟一個新的事務。如果一個事務已經存在,則将這個存在的事務挂起。

Not_support 總是非事務地執行,并挂起任何存在的事務。

Never 絕不 總是非事務地執行,如果存在一個活動事務,則抛出異常

Nested 嵌套的 如果有就嵌套、沒有就開啟事務

2.5.11 Spring事務的隔離級别

1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager預設的隔離級别,使用資料庫預設的事務隔離級别.

另外四個與JDBC的隔離級别相對應

2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級别,它充許令外一個事務可以看到這個事務未送出的資料。

      這種隔離級别會産生髒讀,不可重複讀和幻像讀。

3. ISOLATION_READ_COMMITTED: 保證一個事務修改的資料送出後才能被另外一個事務讀取。另外一個事務不能讀取該事務未送出的資料

4. ISOLATION_REPEATABLE_READ: 這種事務隔離級别可以防止髒讀,不可重複讀。但是可能出現幻像讀。

      它除了保證一個事務不能讀取另一個事務未送出的資料外,還保證了避免下面的情況産生(不可重複讀)。

5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級别。事務被處理為順序執行。

除了防止髒讀,不可重複讀外,還避免了幻像讀。

其中的一些概念的說明:

  髒讀: 指當一個事務正在通路資料,并且對資料進行了修改,而這種修改還沒有送出到資料庫中,這時,另外一個事務也通路這個資料,然後使用了這個資料。因為這個資料是還沒有送出的資料, 那麼另外一 個事務讀到的這個資料是髒資料,依據髒資料所做的操作可能是不正确的。

不可重複讀: 指在一個事務内,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也通路該同一資料。 那麼,在第一個事務中的兩次讀資料之間,由于第二個事務的修改,那麼第一個事務兩次讀到的資料可能是不一樣的。這樣就發生了在一個事務内兩次讀到的資料是不一樣的,是以稱為是不可重複讀。

 幻覺讀: 指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及 到表中的全部資料行。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。

2.5.12 什麼是ORM?

 對象關系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關系資料庫存在的互不比對的現象的技術。簡單的說,ORM是通過使用描述對象和資料庫之間映射的中繼資料,将程式中的對象自動持久化到關系資料庫中。那麼,到底如何實作持久化呢?一種簡單的方案是采用寫死方式(jdbc操作sql方式),為每一種可能的資料庫通路操作提供單獨的方法。

這種方案存在以下不足:

    1.持久化層缺乏彈性。一旦出現業務需求的變更,就必須修改持久化層的接口

    2.持久化層同時與域模型與關系資料庫模型綁定,不管域模型還是關系資料庫模型發生變化,都要修改持久化曾的相關程式代碼,增加了軟體的維護難度。

   ORM提供了實作持久化層的另一種模式,它采用映射中繼資料來描述對象關系的映射,使得ORM中間件能在任何一個應用的業務邏輯層和資料庫層之間充當橋梁。Java典型的ORM架構有:Hibernate,ibatis(mybatis),speedframework。

   ORM的方法論基于三個核心原則:

  簡單:以最基本的形式模組化資料。

  傳達性:資料庫結構被任何人都能了解的語言文檔化。

  精确性:基于資料模型建立正确标準化了的結構。

   對象關系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關系資料庫存在的互不比對的現象的技術。可以簡單的方案是采用寫死方式(jdbc操作sql方式),為每一種可能的資料庫通路操作提供單獨的方法。這種方法存在很多缺陷,使用

使用ORM架構(為了解決解決面向對象與關系資料庫存在的互不比對的現象的架構)來解決. 

   Hibernate,ibatis(mybatis),

2.5.13 iBatis(mybatis)與Hibernate有什麼不同?

相同點:

   都是java中orm架構、屏蔽jdbc api的底層通路細節,使用我們不用與jdbc api打交道,就可以完成對資料庫的持久化操作。jdbc api程式設計流程固定,還将sql語句與java代碼混雜在了一起,經常需要拼湊sql語句,細節很繁瑣。

   ibatis的好處:屏蔽jdbc api的底層通路細節;将sql語句與java代碼進行分離;提供了将結果集自動封裝稱為實體對象和對象的集合的功能.queryForList傳回對象集合,用queryForObject傳回單個對象;提供了自動将實體對象的屬性傳遞給sql語句的參數。

   Hibername的好處:Hibernate是一個全自動的orm映射工具,它可以自動生成sql語句,并執行并傳回java結果。

不同點:

   1、hibernate要比ibatis功能強大很多。因為hibernate自動生成sql語句。

   2、ibatis需要我們自己在xml配置檔案中寫sql語句,hibernate我們無法直接控制該語句,我們就無法去寫特定的高效率的sql。對于一些不太複雜的sql查詢,hibernate可以很好幫我們完成,但是,對于特别複雜的查詢,hibernate就很難适應了,這時候用ibatis就是不錯的選擇,因為ibatis還是由我們自己寫sql語句。

  ibatis可以出來複雜語句,而hibernate不能。

   3、ibatis要比hibernate簡單的多。ibatis是面向sql的,不同考慮對象間一些複雜的映射關系。

2.5.14 Hibernate映射對象的狀态

臨時狀态/瞬時狀态(transient):剛剛用new語句建立,沒有被持久化

不處于session中(沒有使用session的方法去操作臨時對象)。該對象成為臨時對象

持久化狀态/托管狀态(persistent):已經被持久化,加入到session的緩存中。session是沒有關閉該狀态的對象為持久化對象。

遊離狀态/脫管狀态(detached):已經被持久化,但不處于session中。

該狀态的對象為遊離對象。

删除狀态(removed):對象有關聯的ID,并且在Session管理下,但是已經被計劃(事務送出的時候,commit())删除。如果沒有事務就不能删除

互相轉換

2.5.15 介紹一下Hibernate的緩存?

一、why(為什麼要用Hibernate緩存?)

Hibernate是一個持久層架構,經常通路實體資料庫。

為了降低應用程式對實體資料源通路的頻次,進而提高應用程式的運作性能。

緩存内的資料是對實體資料源中的資料的複制,應用程式在運作時從緩存讀寫資料,在特定的時刻或事件會同步緩存和實體資料源的資料。

為了提供通路速度,把磁盤或資料庫通路變成記憶體通路。

二、what(Hibernate緩存原理是怎樣的?)Hibernate緩存包括兩大類:Hibernate一級緩存和Hibernate二級緩存。

1.Hibernate一級緩存又稱為“Session的緩存”。

Session緩存内置不能被解除安裝,Session的緩存是事務範圍的緩存(Session對象的生命周期通常對應一個資料庫事務或者一個應用事務)。

一級緩存中,持久化類的每個執行個體都具有唯一的OID。

2.Hibernate二級緩存又稱為“SessionFactory的緩存”。

由于SessionFactory對象的生命周期和應用程式的整個過程對應,是以Hibernate二級緩存是程序範圍或者叢集範圍的緩存,有可能出現并發問題,是以需要采用适當的并發通路政策,該政策為被緩存的資料提供了事務隔離級别。

第二級緩存是可選的,是一個可配置的插件,預設下SessionFactory不會啟用這個插件。

Hibernate提供了org.hibernate.cache.CacheProvider接口,它充當緩存插件與Hibernate之間的擴充卡。

面試:

   Hibernate中的緩存分一級緩存和二級緩存。

   一級緩存就是Session級别的緩存,在事務範圍内有效是,内置的不能被解除安裝。二級緩存是SesionFactory級别的緩存,從應用啟動到應用結束有效。是可選的,預設沒有二級緩存,需要手動開啟。

    儲存資料庫後,在記憶體中儲存一份,如果更新了資料庫就要同步更新。

 什麼樣的資料适合存放到第二級緩存中?

很少被修改的資料 文章的最後回複時間 

經常被查詢的資料   電商的地點

2) 不是很重要的資料,允許出現偶爾并發的資料   

3) 不會被并發通路的資料   

4) 常量資料

擴充:hibernate的二級緩存預設是不支援分布式緩存的。使用memcahe,redis等中央緩存來代替二級緩存。

2.5.16 簡單講一下webservice使用的場景?

webservice是一個SOA(面向服務的程式設計)的架構,它是不依賴于語言,不依賴于平台,可以實作不同的語言間的互相調用,通過Internet進行基于Http協定的網絡應用間的互動。

異構系統(不同語言)的整合

不同用戶端的整合 浏覽器、手機端(android,ios.塞班)、微信單、PC端等終端來通路

實實在在的列子:

       天氣預報:可以通過實作webservice用戶端調用遠端天氣服務實作的。

       單點登入:一個服務是所有系統的登入

2.5.17 簡單介紹一下activiti?

    Activiti是一個業務流程管理(BPM)和工作流系統,适用于開發人員和系統管理者。其核心是超快速,穩定的BPMN2流程引擎。它易于與 Spring內建使用。

主要要在OA中,把線下流程放到線上。 把現實生活中一些流程固話定義到系統中,然後通過輸入表單資料完成業務。

他可用在OA系統的流程管理中:

請假流程 小于三天,一級主管審批,大于三天二級才能審批。

報帳流程 1000 2000 3000>....

如果你想找專門這方面的工作,要下去複習。

2.6 進階部分

2.6.1 有沒有用過linux?你都用它來做什麼?

Linux是一個長時間運作比較穩定的作業系統,所有我們一般會拿它作為伺服器(web,db,app等)。

Linux本身具有C的編譯環境、我們的一些軟體是沒有軟體包(redis、nginx等)的,需要在Linux的C編譯環境編譯得到軟體包.

2.6.2 說一下linux下面的一下常用指令?

常用:

Pwd 擷取目前路徑

Cd 跳轉到目錄

Su -u 切換到管理者

Ls ls 列舉目錄

檔案操作指令:

檔案

   tail 檢視

   rm -rf

   vi

檔案夾

   mkdir

   rm -r

2.6.3 你是使用什麼來連接配接遠端的Linux伺服器的?

需要依賴于Linux伺服器安裝ssh服務端,一般這個ssh服務的端口22.

需要依賴于Linux伺服器安裝sftp服務端,一般這個sftp服務的端口25.

使用ssh用戶端連接配接linux伺服器,就有點兒像windows下面的遠端連接配接。但是linux通過ssh連接配接上以後是沒有圖形界面,全是指令行。

Putty

Xshell

使用sftp用戶端來連接配接sftp服務端,來上傳和下載下傳檔案.(上傳安裝包,修改了配置檔案上傳。)

Winscp

xftp

企業中常用的兩種組合:

     putty+winscp

     Xshell+xftp=xmanager

面試:使用xshell、putty等ssh用戶端來連接配接伺服器,使用xftp、winscp等sftp用戶端來上傳和現在檔案。連接配接和上傳、下載下傳必須依賴于伺服器的ssh、sftp服務,也就是linux伺服器需要啟動這兩個服務。

2.6.4 有沒有使用過雲主機?

使用過,在原來的公司,我們沒有使用自己的伺服器,而是租用阿裡的雲主機。

沒有使用過,但是有所了解。

雲主機就是一些雲服務營運商(阿裡、華為、西部數位、新浪等),提供的遠端的伺服器功能,我們開發者或者企業隻需按需付費就可以租用對應的伺服器。

使用ssh和sftp來進行操作。

2.6.5 有沒有做過資料庫優化方面的事情?

 做過mysql資料庫的優化、其他資料庫類似

定位:查找、定位慢查詢

優化手段:

建立索引:建立合适的索引,我們就可以現在索引中查詢,查詢到以後直接找對應的記錄。

分表   :當一張表的資料比較多或者一張表的某些字段的值比較多并且很少使用時,采用水準分表和垂直分表來優化

讀寫分離:當一台伺服器不能滿足需求時,采用讀寫分離的方式進行叢集。

緩存:使用redis來進行緩存

一些常用優化技巧

   2.6.6 查找慢查詢并定位慢查詢?

   在項目自驗項目轉測試之前,在啟動mysql資料庫時開啟慢查詢,并且把執行慢的語句寫到日志中,在運作一定時間後。通過檢視日志找到慢查詢語句。

要找出項目中的慢Sql時

1、關閉資料庫伺服器(關閉服務)

2、把慢查詢記錄到日志中

3,設定慢查詢時間

4、找出日志中的慢查詢SQL

   使用explain 慢查詢語句,來詳細分析語句的問題.

2.6.6 資料庫優化之遵循範式?

 資料庫表設計時需要遵循方式

 表的範式,是首先符合1NF, 才能滿足2NF , 進一步滿足3NF

1NF: 即表的列的具有原子性,不可再分解,即列的資訊,不能分解.隻要資料庫是關系型資料庫(mysql/oracle/db2/sysbase/sql server),就自動的滿足1NF.關系型資料庫中是不允許分割列的。

2NF:表中的記錄是唯一的.通常我們設計一個主鍵來實作

3NF:即表中不要有備援資料, 就是說,表的資訊,如果能夠被推導出來,就不應該單獨的設計一個字段來存放.(外鍵)

反3NF :沒有備援的資料庫未必是最好的資料庫,有時為了提高運作效率,就必須降低範式标準,适當保留備援資料。具體做法是: 在概念資料模型設計時遵守第三範式,降低範式标準的工作放到實體資料模型設計時考慮。降低範式就是增加字段,允許備援。 訂單和訂單項、相冊浏覽次數和照片的浏覽次數

2.6.7 選擇合适的存儲引擎

在開發中,我們經常使用的存儲引擎 myisam / innodb/ memory

MyISAM存儲引擎

如果表對事務要求不高,同時是以查詢和添加為主的,我們考慮使用myisam存儲引擎. 比如 bbs 中的 發帖表,回複表.

INNODB存儲引擎:

對事務要求高,儲存的資料都是重要資料,我們建議使用INNODB,比如訂單表,賬号表.

Memory 存儲

我們資料變化頻繁,不需要入庫,同時又頻繁的查詢和修改,我們考慮使用memory, 速度極快.

問 MyISAM 和 INNODB的差別(主要)

1. 事務安全 myisam不支援事務而innodb支援

2. 查詢和添加速度 myisam不用支援事務就不用考慮同步鎖,查找和添加和添加的速度快

3. 支援全文索引 myisam支援innodb不支援

4. 鎖機制 myisam支援表鎖而innodb支援行鎖(事務)

5. 外鍵 MyISAM 不支援外鍵, INNODB支援外鍵. (通常不設定外鍵,通常是在程式中保證資料的一緻)

2.6.8 資料庫優化之建立合适的索引?

索引(Index)是幫助DBMS高效擷取資料的資料結構。

分類:普通索引/唯一索引/主鍵索引/全文索引

普通索引:允許重複的值出現

唯一索引:除了不能有重複的記錄外,其它和普通索引一樣(使用者名、使用者身份證、email,tel)

主鍵索引:是随着設定主鍵而建立的,也就是把某個列設為主鍵的時候,資料庫就會給改列建立索引。這就是主鍵索引.唯一且沒有null值

全文索引:用來對表中的文本域(char,varchar,text)進行索引, 全文索引針對MyIsam

explain select * from articles where match(title,body) against(‘database’);【會使用全文索引】

2.6.9 索引使用小技巧?*****

索引弊端

1.占用磁盤空間。

2.對dml(插入、修改、删除)操作有影響,變慢。

使用場景:

a: 肯定在where條件經常使用,如果不做查詢就沒有意義

b: 該字段的内容不是唯一的幾個值(sex)

c: 字段内容不是頻繁變化.

具體技巧:

對于建立的多列索引(複合索引),不是使用的第一部分就不會使用索引。

alter table dept add index my_ind (dname,loc); // dname 左邊的列,loc就是右邊的列

explain select * from dept where dname=\'aaa\'\G 會使用到索引

explain select * from dept where loc=\'aaa\'\G 就不會使用到索引

2. 對于使用like的查詢,查詢如果是’%aaa’不會使用到索引而‘aaa%’會使用到索引。

   explain select * from dept where dname like \'%aaa\'\G不能使用索引

   explain select * from dept where dname like \'aaa%\'\G使用索引.

是以在like查詢時,‘關鍵字’的最前面不能使用 % 或者 _這樣的字元.,如果一定要前面有變化的值,則考慮使用 全文索引->sphinx.

如果條件中有or,有條件沒有使用索引,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有字段,都必須單獨使用時能使用索引.

如果列類型是字元串,那一定要在條件中将資料使用引号引用起來。否則不使用索引。

expain select * from dept where dname=’111’;

expain select * from dept where dname=111;(數值自動轉字元串)

expain select * from dept where dname=qqq;報錯

也就是,如果列是字元串類型,無論是不是字元串數字就一定要用 ‘’ 把它包括起來.

如果mysql估計使用全表掃描要比使用索引快,則不使用索引。

   表裡面隻有一條記錄

2.6.10 資料庫優化之分表?

分表分為水準(按行)分表和垂直(按列)分表

根據經驗,Mysql表資料一般達到百萬級别,查詢效率會很低,容易造成表鎖,甚至堆積很多連接配接,直接挂掉;水準分表能夠很大程度較少這些壓力。

按行資料進行分表。

如果一張表中某個字段值非常多(長文本、二進制等),而且隻有在很少的情況下會查詢。這時候就可以把字段多個單獨放到一個表,通過外鍵關聯起來。

     考試詳情,一般我們隻關注分數,不關注詳情。

水準分表政策:

1.按時間分表

這種分表方式有一定的局限性,當資料有較強的實效性,如微網誌發送記錄、微信消息記錄等,這種資料很少有使用者會查詢幾個月前的資料,如就可以按月分表。

2.按區間範圍分表

一般在有嚴格的自增id需求上,如按照user_id水準分表:

table_1  user_id從1~100w

table_2  user_id從101~200w

table_3  user_id從201~300w

3.hash分表*****

通過一個原始目标的ID或者名稱通過一定的hash算法計算出資料存儲表的表名,然後通路相應的表。

2.6.11 資料庫優化之讀寫分離

一台資料庫支援的最大并發連接配接數是有限的,如果使用者并發通路太多。一台伺服器滿足不要要求是就可以叢集處理。Mysql的叢集處理技術最常用的就是讀寫分離。

主從同步

資料庫最終會把資料持久化到磁盤,如果叢集必須確定每個資料庫伺服器的資料是一直的。能改變資料庫資料的操作都往主資料庫去寫,而其他的資料庫從主資料庫上同步資料。

讀寫分離

   使用負載均衡來實作寫的操作都往主資料去,而讀的操作往從伺服器去。

2.6.12 資料庫優化之緩存

  在持久層(dao)和資料庫(db)之間添加一個緩存層,如果使用者通路的資料已經緩存起來時,在使用者通路時直接從緩存中擷取,不用通路資料庫。而緩存是在操作記憶體級,通路速度快。

作用:減少資料庫伺服器壓力,減少通路時間。

Java中常用的緩存有,

   1、hibernate的二級緩存。該緩存不能完成分布式緩存。

可以使用redis(memcahe等)來作為中央緩存。

        對緩存的資料進行集中處理

2.6.13 語句優化小技巧

DDL優化:

1 、通過禁用索引來提供導入資料性能 。 這個操作主要針對有資料庫的表,追加資料

//去除鍵

alter table test3 DISABLE keys;

//批量插入資料

insert into test3 select * from test;

//恢複鍵

alter table test3 ENABLE keys;

2、 關閉唯一校驗

set unique_checks=0  關閉

set unique_checks=1  開啟

3、修改事務送出方式(導入)(變多次送出為一次)

set autocommit=0   關閉

//批量插入

set autocommit=1   開啟

DML優化(變多次送出為一次)

insert into test values(1,2);

insert into test values(1,3);

insert into test values(1,4);

//合并多條為一條

insert into test values(1,2),(1,3),(1,4)

DQL優化

Order by優化

  1、多用索引排序

普通結果排序(非索引排序)Filesort

   group by優化

      是使用order by null,取消預設排序

   子查詢優化

在客戶清單找到不在支付清單的客戶

#在客戶清單找到不在“支付清單”的客戶 , 查詢沒買過東西的客戶

explain

select * from customer where customer_id not in (select DISTINCT customer_id from payment); #子查詢      -- 這種是基于func外鍊

explain

select * from customer c left join payment p on(c.customer_id=p.customer_id) where p.customer_id is null   -- 這種是基于“索引”外鍊

   Or優化

在兩個獨立索引上使用or的性能優于

1、 or兩邊都是用索引字段做判斷,性能好!!

2、 or兩邊,有一邊不用,性能差

3、 如果employee表的name和email這兩列是一個複合索引,但是如果是 :name=\'A\' OR email=\'B\' 這種方式,不會用到索引!

  limit優化

select film_id,description from film order by title limit 50,5;

select a.film_id,a.description from film a inner join (select film_id from film order by title limit 50,5)b on a.film_id=b.film_id

2.6.14 jdbc批量插入幾百萬資料怎麼實作?*****

1、變多次送出為一次

3、使用批量操作

省出的時間可觀。

像這樣的批量插入操作能不使用代碼操作就不使用,可以使用存儲過程來實作。

2.6.15 有沒有使用過redis? Redis是什麼  

Redis是一個key-value的nosql資料庫.先存到記憶體中,會根據一定的政策持久化到磁盤,即使斷電也不會丢失資料。支援的資料類型比較多。

主要用來做緩存資料庫的資料和web叢集時當做中央緩存存放seesion

2.4.15 Redis和memche的比較?

1、 Redis和Memcache都是将資料存放在記憶體中,都是記憶體資料庫。不過memcache還可用于緩存其他東西,例如圖檔、視訊等等。

2、Redis不僅僅支援簡單的k/v類型的資料,同時還提供list,set,hash等資料結構的存儲。

3、虛拟記憶體--Redis當實體記憶體用完時,可以将一些很久沒用到的value 交換到磁盤

2.6.16 簡單說一下redis的使用場景?

緩存:

把經常需要查詢的、很少修改資料,放到讀速度很快的空間(記憶體),以便下次通路減少時間。減輕壓力,減少通路時間.

計數器:

     redis中的計數器是原子性的記憶體操作。

       可以解決庫存溢出問題.進銷存系統庫存溢出。

session緩存伺服器:

web叢集時作為session緩存伺服器

緩存隊列等

2.6.17 redis對象儲存方式?

Json字元串:

需要把對象轉換為json字元串,當做字元串處理。直接使用set get來設定或者或。

優點:設定和擷取比較簡單

缺點:沒有提供專門的方法,需要把把對象轉換為json。(jsonlib)

位元組:

   需要做序列号,就是把對象序列化為位元組儲存。

如果是擔心JSON轉對象會消耗資源的情況,這個問題需要考量幾個地方,

第一點:就是使用的JSON轉換lib是否就會存在性能問題。

第二點:就是資料的資料量級别,如果是存儲百萬級的大資料對象,建議采用存儲序列化對象方式。如果是少量的資料級對象,或者是資料對象字段不多,還是建議采用JSON轉換成String方式。

畢竟redis對存儲字元類型這部分優化的非常好。具體采用的方式與方法,還要看你所使用的場景。

2.6.18 Redis資料淘汰機制

在 redis 中,允許使用者設定最大使用記憶體大小 server.maxmemory,在記憶體限定的情況下是很有用的。譬如,在一台 8G 機子上部署了 4 個 redis 服務點,每一個服務點配置設定 1.5G 的記憶體大小,減少記憶體緊張的情況,由此擷取更為穩健的服務。

記憶體大小有限,需要儲存有效的資料?

redis 記憶體資料集大小上升到一定大小的時候,就會施行資料淘汰政策。

redis 提供 6種資料淘汰政策:

volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選将要過期的資料淘汰

volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

no-enviction(驅逐):禁止驅逐資料

2.6.19 Java通路Redis

1、使用jedis java用戶端來通路redis伺服器,有點類似通過jdbc通路mysql一樣。

2、當然如果是spring進行內建時,可以使用spring data來通路redis,spring data隻是對jedis的二次封裝。jdbcTemplate jdbc關系一樣

2.6.20 Redis叢集

當一台資料無法滿足要求,可以使用reids叢集來處理,類似于mysql的讀寫分離。

2.6.21簡單介紹一下微信公共号的分類?

   公衆号:個人和企業都能申請

   服務号:企業才能申請

   企業号:企業才能申請

2.6.22 微信開發原理

微信公衆平台開發者,通過接入認證的方式,讓我們的伺服器能處理來自微信伺服器轉發的微信使用者的請求,處理完成後傳回給微信伺服器,有微信伺服器對使用者響應。

2.6.23怎麼把微信和業務平台綁定?

微信使用者和注冊使用者綁定?

讓微信使用者也能完成注冊使用者的功能。

使用者注冊實體中包含一個微信号的字段,當我進行綁定時就是修改使用者的微信号字段。

當然我們在進行菜單跳轉到頁面後,我們是無法直接擷取微信号的。要通過微信網頁授權來擷取微信号。

第一步:使用者同意授權,擷取code

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“該連結無法通路”,請檢查參數是否填寫錯誤,是否擁有scope參數對應的授權作用域權限。

第二步:通過code換取網頁授權openId也就是我們微信号

擷取code後,請求以下連結擷取access_token:  https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

{ "access_token":"ACCESS_TOKEN",    

 "expires_in":7200,    

 "refresh_token":"REFRESH_TOKEN",    

 "openid":"OPENID",    

 "scope":"SCOPE" }

2.7 項目和業務部分  

2.7.1項目分類

在公司中做的項目可以分為兩種産品、項目.

項目:就是給一些公司接的項目,項目開發完成後。就互動,後面這個項目代碼就不在維護了

産品:充分考慮擴充性和基本業務,來做一個産品。在這個産品上可以進行定制開發。

2.7.2 項目參與者

   産品經理?PM?架構師(SE)?(開發PL? MDE?可能會有多個) (測試PL ? TSE可能會有多個)   UI 資料

開發團隊:開始代碼能完成需求

測試團隊:測試功能

UI:負責界面設計、靜态代碼的編寫

資料:負責界面的文字描述。

QA :通過項目品質監控,和PM,SE同級

開發和UI和資料,協同設計和開發,開發完成後轉測試(測試政策)交給測試團隊進行測試,測試完成後會出一個測試報告。

2.7.3 項目流程

   可行性分析和立項和開工會

   需求分析

   需求設計

   項目開發(多個疊代)

        疊代開工會

        疊代設計

    疊代開發

    疊代測試

    內建測試

    疊代釋出

        疊代總結

        ....不斷疊代

   項目驗收

   項目總結

2.7.4 業務注意事項

不要多個項目都說你同一個子產品。如果要說,就說後面是進行改進。

   在pss,crm,shopping都寫權限子產品

2、多寫點業務

---------------------

作者:多纖果凍

來源:CSDN

原文:https://blog.csdn.net/qq_37939251/article/details/83549802

版權聲明:本文為部落客原創文章,轉載請附上博文連結!

原連結:https://blog.csdn.net/qq_37939251/article/details/83549802