第一章內容介紹 20javascript
1、Java面向對象 21html
2. 訪問權限修飾符public、private、protected, 以及不寫(默認)時的區別(2017-11-12) 22java
3. 如何理解clone對象 22mysql
2、JavaSE語法(2017-11-12-wl) 26linux
1. Java有沒有goto語句?(2017-11-12-wl) 26nginx
2. & 和 && 的區別(2017-11-12-wl) 27程序員
3. 在Java中,如何跳出當前的多重嵌套循環(2017-11-14-wl) 27web
4. 兩個對象值相同(x.equals(y) == true),但卻可有不一樣的hashCode,這句話對不對?(2017-11-14-wl) 27
5. 是否能夠繼承String (2017-11-14-wl) 28
6. 當一個對象被看成參數傳遞到一個方法後,此方法可改變這個對象的屬性,並可返回變化後的結果,那麼這裏究竟是值傳遞仍是引用傳遞?(2017-11-14-wl) 29
7. 重載(overload)和重寫(override)的區別?重載的方法可否根據返回類型進行區分?(2017-11-15-wl) 29
8. 爲何函數不能根據返回類型來區分重載?(2017-11-15-wl) 30
9. char 型變量中能不能存儲一箇中文漢字,爲何?(2017-11-16-wl) 31
10. 抽象類(abstract class)和接口(interface)有什麼異同?(2017-11-16-wl) 31
11. 抽象的(abstract)方法是否可同時是靜態的(static), 是否可同時是本地方法(native),是否可同時被synchronized(2017-11-16-wl) 32
12. 闡述靜態變量和實例變量的區別?(2017-11-16-wl) 32
13. ==和equals的區別?(2017-11-22-wzz) 33
14. break和continue的區別?(2017-11-23-wzz) 33
15. String s = "Hello";s = s + " world!";這兩行代碼執行後,原始的String對象中的內容到底變了沒有?(2017-12-1-lyq) 33
3. error和exception的區別?(2017-2-23) 36
5. 請寫出你最多見的5個RuntimeException(2017-11-22-wzz) 37
6. throw和throws的區別(2017-11-22-wzz) 38
7. final、finally、finalize的區別?(2017-11-23-wzz) 38
1. Math.round(11.5)等於多少?Math.round(- 11.5) 又等於多少?(2017-11-14-wl) 39
2. switch是否能做用在byte上,是否能做用在long上,是否能做用在String上?(2017-11-14-wl) 39
3. 數組有沒有length() 方法?String有沒有length() 方法?(2017-11-14-wl) 39
4. String 、StringBuilder 、StringBuffer的區別?(2017-11-14-wl) 39
5. 什麼狀況下用「+」運算符進行字符串鏈接比調用StringBuffer/StringBuilder 對象的append方法鏈接字符串性能更好?(2017-11-14-wl) 40
6. 請說出下面程序的輸出(2017-11-14-wl) 47
7. Java中的日期和時間(2017-11-19-wl) 48
2. String是基本數據類型嗎?(2017-11-12-wl) 71
3. short s1 = 1; s1 = s1 + 1; 有錯嗎?short s1 = 1; s1 += 1有錯嗎;(2017-11-12-wl) 71
4. int 和 和 Integer有什麼區別?(2017-11-12-wl) 71
5. 下面Integer類型的數值比較輸出的結果爲?(2017-11-12-wl) 72
6. String類經常使用方法(2017-11-15-lyq) 74
7. String、StringBuffer、StringBuilder的區別?(2017-11-23-wzz) 74
8. 數據類型之間的轉換(2017-11-23-wzz) 75
1. Java中有幾種類型的流(2017-11-23-wzz) 75
4. 字節流和字符流的區別(2017-11-23-wzz) 77
5. 如何實現對象克隆?(2017-11-12-wl) 77
6. 什麼是java序列化,如何實現java序列化?(2017-12-7-lyq) 80
1. HashMap排序題,上機題。(本人主要靠這道題入職的第一家公司) 81
3. ArrayList內部用什麼實現的?(2015-11-24) 83
4. 併發集合和普通集合如何區別?(2015-11-24) 89
6. List和Map、Set的區別(2017-11-22-wzz) 91
7. HashMap 和HashTable有什麼區別?(2017-2-23) 92
8. 數組和鏈表分別比較適合用於什麼場景,爲何?(2017-2-23) 93
9. Java中ArrayList和Linkedlist區別?(2017-2-23) 96
10. List a=new ArrayList()和ArrayList a =new ArrayList()的區別?(2017-2-24) 97
11. 要對集合更新操做時,ArrayList和LinkedList哪一個更適合?(2017-2-24) 97
12. 請用兩個隊列模擬堆棧結構(2017-2-24) 101
13. Collection和Map的集成體系(2017-11-14-lyq) 102
14. Map中的key和value能夠爲null麼?(2017-11-21-gxb) 103
(一)多線程基礎知識--傳統線程機制的回顧(2017-12-11-wl) 104
(二)多線程基礎知識--線程併發庫(2017-12-11-wl) 118
1. 靜態嵌套類(Static Nested Class) 和內部類(Inner Class)的不一樣?(2017-11-16-wl) 272
2. 下面的代碼哪些地方會產生編譯錯誤?(2017-11-16-wl) 272
1. 寫一個ArrayList的動態代理類(筆試題) 273
2. 動靜態代理的區別,什麼場景使用?(2015-11-25) 274
12. heap和stack有什麼區別(2017-2-23) 294
13. 解釋內存中的棧 (stack) 、堆 (heap) 和方法區 (method area) 的用法(2017-11-12-wl) 301
3. Java類加載體系之ClassLoader雙親委託機制 (2017-2-24) 302
4. 描述一下JVM加載class (2017-11-15-wl) 306
5. 得到一個類對象有哪些方式?(2017-11-23-wzz) 307
1. 既然有GC機制,爲何還會有內存泄露的狀況(2017-11-16-wl) 307
1. Java中爲何會有GC機制呢?(2017-11-16-wl) 309
2. 對於Java的GC哪些內存須要回收(2017-11-16-wl) 309
3. Java的GC何時回收垃圾(2017-11-16-wl) 309
7、Java8的新特性以及使用(2017-12-02-wl) 311
1. 經過10個示例來初步認識Java8中的lambda表達式(2017-12-02-wl) 311
2. Java8中的lambda表達式要點(2017-12-02-wl) 319
3. Java8中的Optional類的解析(2017-12-02-wl) 321
8、在開發中遇到過內存溢出麼?緣由有哪些?解決方法有哪些?(2017-11-23-gxb) 328
1. 說下原生jdbc操做數據庫流程?(2017-11-25-wzz) 329
2. 什麼要使用PreparedStatement?(2017-11-25-wzz) 330
3. 關係數據庫中鏈接池的機制是什麼?(2017-12-6-lyq) 330
1. http的長鏈接和短鏈接(2017-11-14-lyq) 331
2. HTTP/1.1與HTTP/1.0的區別(2017-11-21-wzy) 332
3. http常見的狀態碼有哪些?(2017-11-23-wzz) 335
4. GET和POST的區別?(2017-11-23-wzz) 335
5. http中重定向和請求轉發的區別?(2017-11-23-wzz) 337
1. Cookie和Session的區別(2017-11-15-lyq) 337
2. session共享怎麼作的(分佈式如何實現session共享)? 337
3. 在單點登陸中,若是cookie被禁用了怎麼辦?(2017-11-23-gxb) 340
1. 什麼是jsp,什麼是Servlet?jsp和Servlet有什麼區別?(2017-11-23-wzz) 341
2. jsp有哪些域對象和內置對象及他們的做用?(2017-11-25-wzz) 342
1. 什麼是xml,使用xml的優缺點,xml的解析器有哪幾種,分別有什麼區別?(2017-11-25-wzz) 343
1. 談談你對ajax的認識?(2017-11-23-wzz) 344
2. jsonp原理(2017-11-21-gxb) 345
2. Linux中如何查看日誌?(2017-11-21-gxb) 347
3. Linux怎麼關閉進程(2017-11-21-gxb) 348
1. jQueryUI(2017-11-23-lyq) 352
3. AngularJS (2017-11-23-lyq) 355
1. SQL的select語句完整的執行順序(2017-11-15-lyq) 359
2. SQL之聚合函數(2017-11-15-lyq) 361
3. SQL之鏈接查詢(左鏈接和右鏈接的區別)(2017-11-15-lyq) 361
4. SQL之sql注入(2017-11-15-lyq) 362
5. Mysql性能優化(2017-11-15-lyq) 362
6. 必看sql面試題(學生表_課程表_成績表_教師表)(2017-11-25-wzz) 363
7. Mysql數據庫架構圖(2017-11-25-wzz) 364
8. Mysql架構器中各個模塊都是什麼?(2017-11-25-wzz) 365
9. Mysql存儲引擎有哪些?(2017-11-25-wzz) 366
10. MySQL事務介紹(2017-11-25-wzz) 367
11. MySQL怎麼建立存儲過程(2017-11-25-wzz) 369
12. MySQL觸發器怎麼寫?(2017-11-25-wzz) 370
13. MySQL語句優化(2017-11-26-wzz) 371
14. MySQL中文亂碼問題完美解決方案(2017-12-07-lwl) 372
15. 如何提升MySQL的安全性(2017-12-8-lwl) 374
1. 什麼是存儲過程,使用存儲過程的好處?(2017-11-25-wzz) 376
2. Oracle存儲過程怎麼建立?(2017-11-25-wzz) 377
3. 如何使用Oracle的遊標?(2017-11-25-wzz) 378
4. Oracle中字符串用什麼鏈接?(2017-11-25-wzz) 378
5. Oracle中是如何進行分頁查詢的?(2017-11-25-wzz) 379
6. 存儲過程和存儲函數的特色和區別?(2017-11-25-wzz) 379
7. 存儲過程與SQL的對比?(2017-11-21-gxb) 379
8. 你以爲存儲過程和SQL語句該使用哪一個?(2017-11-21-gxb) 380
9. 觸發器的做用有哪些?(2017-11-21-gxb) 381
10. 在千萬級的數據庫查詢中,如何提升效率?(2017-11-23-gxb) 381
1. SpringMVC的工做原理(2017-11-13-lyq) 385
2. SpringMVC經常使用註解都有哪些?(2017-11-24-gxb) 386
3. 如何開啓註解處理器和適配器?(2017-11-24-gxb) 386
4. 如何解決get和post亂碼問題?(2017-11-24-gxb) 386
1. 談談你對Spring的理解(2017-11-13-lyq) 387
2. Spring中的設計模式(2017-11-13-lyq) 387
3. Spring的經常使用註解(2017-11-13-lyq) 388
4. 簡單介紹一下Spring bean的生命週期(2017-11-21-gxb) 389
5. Spring結構圖(2017-11-22-lyq) 390
6. Spring能幫咱們作什麼?(2017-11-22-lyq) 392
7. 請描述一下Spring的事務(2017-11-22-lyq) 393
8. BeanFactory 經常使用的實現類有哪些?(2017-12-03-gxb) 396
9. 解釋Spring JDBC、Spring DAO和Spring ORM(2017-12-03-gxb) 397
10. 簡單介紹一下Spring WEB 模塊。(2017-12-03-gxb) 397
11. Spring配置文件有什麼做用?(2017-12-03-gxb) 398
12. 什麼是Spring IOC 容器?(2017-12-03-gxb) 398
14. ApplicationContext的實現類有哪些?(2017-12-03-gxb) 398
15. BeanFactory與AppliacationContext有什麼區別(2017-12-03-gxb) 399
16. 什麼是Spring的依賴注入?(2017-12-04-gxb) 399
17. 有哪些不一樣類型的IOC(依賴注入)方式?(2017-12-04-gxb) 399
18. 什麼是Spring beans?(2017-12-04-gxb) 400
19. 一個Spring Beans的定義須要包含什麼?(2017-12-04-gxb) 400
20. 你怎樣定義類的做用域?(2017-12-04-gxb) 401
21. Spring支持的幾種bean的做用域。(2017-12-04-gxb) 401
22. Spring框架中的單例bean是線程安全的嗎?(2017-12-04-gxb) 401
23. 什麼是Spring的內部bean?(2017-12-04-gxb) 401
24. 在Spring中如何注入一個java集合?(2017-12-04-gxb) 402
25. 什麼是bean的自動裝配?(2017-12-04-gxb) 402
26. 解釋不一樣方式的自動裝配。(2017-12-04-gxb) 402
27. 什麼是基於Java的Spring註解配置? 給一些註解的例子(2017-12-05-gxb) 403
28. 什麼是基於註解的容器配置?(2017-12-05-gxb) 403
29. 怎樣開啓註解裝配?(2017-12-05-gxb) 403
30. 在Spring框架中如何更有效地使用JDBC?(2017-12-05-gxb) 403
31. 使用Spring經過什麼方式訪問Hibernate?(2017-12-05-gxb) 404
32. Spring支持的ORM框架有哪些?(2017-12-05-gxb) 404
33. 簡單解釋一下spring的AOP(2017-12-05-gxb) 404
34. 在Spring AOP 中,關注點和橫切關注的區別是什麼?(2017-12-05-gxb) 405
35. 什麼是鏈接點?(2017-12-05-gxb) 405
36. Spring的通知是什麼?有哪幾種類型?(2017-12-05-gxb) 405
37. 什麼是切點?(2017-12-05-gxb) 406
38. 什麼是目標對象?(2017-12-05-gxb) 406
39. 什麼是代理?(2017-12-05-gxb) 406
40. 什麼是織入?什麼是織入應用的不一樣點?(2017-12-05-gxb) 406
1. 簡單介紹一下Shiro框架(2017-11-23-gxb) 406
2. Shiro主要的四個組件(2017-12-2-wzz) 407
3. Shiro運行原理(2017-12-2-wzz) 408
4. Shiro的四種權限控制方式(2017-12-2-wzz) 408
1. Mybatis中#和$的區別?(2017-11-23-gxb) 410
2. Mybatis的編程步驟是什麼樣的?(2017-12-2-wzz) 411
3. JDBC編程有哪些不足之處,MyBatis是如何解決這些問題的?(2017-12-2-wzz) 411
4. 使用MyBatis的mapper接口調用時有哪些要求?(2017-12-2-wzz) 411
5. Mybatis中一級緩存與二級緩存?(2017-12-4-lyq) 412
6. MyBatis在insert插入操做時返回主鍵ID(2017-12-4-lyq) 412
1. 簡單介紹一下Struts2(2017-11-24-gxb) 413
2. Struts2的執行流程瞭解麼?(2017-11-24-gxb) 414
3. Struts2中Action配置的注意事項有哪些?(2017-11-24-gxb) 416
4. 攔截器和過濾器有哪些區別?(2017-11-24-gxb) 417
5. Struts2的封裝方式有哪些?(2017-11-24-gxb) 417
6. 簡單介紹一下Struts2的值棧。(2017-11-24-gxb) 419
7. SpringMVC和Struts2的區別?(2017-11-23-gxb) 420
8. Struts2中的 # 和 % 分別是作什麼的?(2017-11-30-wzz) 421
9. Struts2中有哪些經常使用結果類型?(2017-12-1-lyq) 422
1. 簡述一下hibernate的開發流程(2017-11-24-gxb) 422
2. hibernate中對象的三種狀態(2017-11-24-gxb) 423
3. hibernate的緩存機制。(2017-11-24-gxb) 423
4. Hibernate的查詢方式有哪些?(2017-11-24-gxb) 424
5. Hibernate和Mybatis的區別?(2017-11-23-gxb) 424
6. Hibernate和JDBC優缺點對比(2017-11-29-wzz) 425
7. 關於Hibernate的orm思想你瞭解多少?(2017-11-29-wzz) 426
8. get和load的區別?(2017-11-30-wzz) 426
9. 如何進行Hibernate的優化?(2017-11-30-wzz) 427
10. 什麼是Hibernate 延遲加載?(2017-12-1-lyq) 428
11. No Session問題原理及解決方法?(2017-12-4-lyq) 428
12. Spring的兩種代理JDK和CGLIB的區別淺談(2017-12-4-lyq) 429
13. 敘述Session的緩存的做用(2017-12-9-lwl) 430
14. Session的清理和清空有什麼區別?(2017-12-10-lwl) 430
15. 請簡述Session的特色有哪些?(2017-12-10-lwl) 430
16. 比較Hibernate三種檢索策略的優缺點(2017-12-10-lwl) 431
1. 什麼是Quartz 框架(2017-12-2-wzz) 432
2.配置文件 applicationContext_job.xml各個屬性做用(2017-12-2-wzz) 432
3.Cron表達式詳解(2017-12-2-wzz) 432
4. 如何監控 Quartz 的 job 執行狀態:運行中,暫停中,等待中? (2017-12-2-wzz) 433
1. Redis的特色?(2017-11-25-wzz) 433
2. 爲何redis須要把全部數據放到內存中?(2017-11-25-wzz) 434
3. Redis常見的性能問題都有哪些?如何解決?(2017-11-25-wzz) 434
4. Redis最適合的場景有哪些?(2017-11-25-wzz) 435
5. Memcache與Redis的區別都有哪些?(2017-11-25-wzz) 435
6. Redis用過RedisNX嗎?Redis有哪幾種數據結構?(2017-11-14-lyq) 435
7. Redis的優缺點(2017-11-22-lyq) 436
8. Redis的持久化(2017-11-23-lyq) 437
1. 如何使用ActiveMQ解決分佈式事務?(2017-11-21-gxb) 439
2. 瞭解哪些消息隊列?(2017-11-24-gxb) 440
3. ActiveMQ若是消息發送失敗怎麼辦?(2017-11-24-gxb) 442
1. Dubbo的容錯機制有哪些。(2017-11-23-gxb) 443
2. 使用dubbo遇到過哪些問題?(2017-11-23-gxb) 444
3. Dubbo的鏈接方式有哪些?(2017-12-1-lyq) 445
1. 如何測試併發量?(2017-11-23-gxb) 448
1. Nginx反向代理爲何可以提高服務器性能?(2017-11-24-gxb) 448
2. Nginx 和 Apache 各有什麼優缺點? (2017-11-24-gxb) 449
3. Nginx 多進程模型是如何實現高併發的?(2017-12-5-lyq) 449
1. 簡單介紹一下zookeeper以及zookeeper的原理。(2017-11-24-gxb) 450
1. 簡單介紹一下solr(2017-11-24-gxb) 451
2. solr怎麼設置搜索結果排名靠前?(2017-11-24-gxb) 452
3. solr中IK分詞器原理是什麼?(2017-11-24-gxb) 452
1. 什麼是webService?(2017-11-24-lyq) 452
2. 常見的遠程調用技術(2017-11-24-lyq) 452
1. 談談你對restful的理解以及在項目中的使用?(2017-11-30-wzz) 453
3、騰訊(2016年校招面試題2017-11-29-wzy) 464
4、北京寶藍德股份科技有限公司(2017-12-03-wmm) 477
1. 什麼叫對象?什麼叫類?什麼面向對象(OOP)? 551
2. 相對於JDK1.4,JDK1.5 有哪些新特性? 552
3. JAVA中使用final修飾符,對程序有哪些影響? 552
4. Java環境變量Unix/Linux下如何配置? 553
5. 寫出5個你在JAVA開發中經常使用的包含(全名),並簡述其做用。 554
6. 寫出5個常見的運行時異常(RuntimeException)。 555
7. 方法重載(overload)須要知足什麼條件,方法覆蓋/方法重寫(override)須要知足什麼條件?(二選一) 555
8. 繼承(inheritance)的優缺點是什麼? 556
12. 什麼叫對象持久化(OBJect PERSIstence),爲何要進行對象持久化? 558
15. 什麼叫髒數據,什麼叫髒讀(Dirty Read) 561
該寶典是一份知識點全面又能不斷更新,與時俱進的學習手冊,不只收錄了做者親身面試遇到的問題,還收錄了近上萬名黑馬學子面試時遇到的問題。咱們會一直不斷地更新和充實該寶典,同時也但願讀者朋友可以多多提供優質的面試題,也許下一個版本就有你提供的面試題哦。
本人的面試實戰記錄發佈在黑馬論壇:http://bbs.itheima.com/thread-196394-1-1.html
你們能夠訪問上面的網址,經過陽哥的實戰記錄略微感知一下真實面試的狀況,從中學習一些面試技巧以便讓本身在將來的面試中可以駕輕就熟,順利拿到本身喜歡的offer。
注意:該面試寶典僅供參考,因爲做者本人的知識水平有限加之編寫時間倉促所以不免有bug的存在,但願你們見諒。
該寶典的一個明確目標是可以讓90%以上的Java技術面試題都落到該寶典中,若是您有不錯的知識或者面試題,您能夠發送到wangzhenyang@itcast.cn,本人將不勝感激。讓天下沒有難學的知識,但願你個人努力能幫到更多的莘莘學子。
世間事,不少均可投機取巧,但技術卻必須靠日積月累的努力來提升。本寶典更加註重的是知識的掌握,而不只僅是對面試題的應付。在展現常見的面試問題以及回答技巧的同時還詳細講解了每一道題所包含的知識點,讓讀者不只知其然,更知其因此然。
1)繼承:繼承是從已有類獲得繼承信息建立新類的過程。提供繼承信息的類被稱爲父類(超類、基類);獲得繼承信息的類被稱爲子類(派生類)。繼承讓變化中的軟件系統有了必定的延續性,同時繼承也是封裝程序中可變因素的重要手段。
子類比父類拋出更少的異常 ,且子類比父類的訪問修飾符更寬
2)封裝:一般認爲封裝是把數據和操做數據的方法綁定起來,對數據的訪問只能經過已定義的接口。面向對象的本質就是將現實世界描繪成一系列徹底自治、封閉的對象。咱們在類中編寫的方法就是對實現細節的一種封裝;咱們編寫一個類就是對數據和數據操做的封裝。能夠說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口。
Integer是int的包裝類 爲何要用包裝類 由於包裝類是一個對象 能夠調用包裝類的方法去操做數據
3)多態性:多態性是指容許不一樣子類型的對象對同一消息做出不一樣的響應。簡單的說就是用一樣的對象引用調用一樣的方法可是作了不一樣的事情。多態性分爲編譯時的多態性和運行時的多態性。若是將對象的方法視爲對象向外界提供的服務,那麼運行時的多態性能夠解釋爲:當A系統訪問B系統提供的服務時,B系統有多種提供服務的方式,但一切對A系統來講都是透明的。方法重載(overload)實現的是編譯時的多態性(也稱爲前綁定),而方法重寫(override)實現的是運行時的多態性(也稱爲後綁定)。運行時的多態是面向對象最精髓的東西,要實現多態須要作兩件事:1. 方法重寫(子類繼承父類並重寫父類中已有的或抽象的方法);2. 對象造型(用父類型引用引用子類型對象,這樣一樣的引用調用一樣的方法就會根據子類對象的不一樣而表現出不一樣的行爲)。
子類調用父類的成員變量, 編譯看左邊,運行看左邊, 子類調用父類的成員方法,編譯左邊,運行看右邊.
4)抽象:抽象是將一類對象的共同特徵總結出來構造類的過程,包括數據抽象和行爲抽象兩方面。抽象只關注對象有哪些屬性和行爲,並不關注這些行爲的細節是什麼。
把一些類的共有特性抽取出來. 子類繼承抽象類必須重寫全部抽象方法 要不子類仍是一個抽象類3
注意:默認狀況下面向對象有3大特性,封裝、繼承、多態,若是面試官問讓說出4大特性,那麼咱們就把抽象加上去。
2. 訪問權限修飾符public、private、protected, 以及不寫(默認)時的區別(2017-11-12)
該題目比較簡單,不一樣的權限修飾符的區別見下表。
修飾符 當前類 同包 子類 其餘包
public √√√√
protected√√√×
default√√××
private√×××
3.2 new一個對象的過程和clone一個對象的過程區別
new操做符的本意是分配內存。程序執行到new操做符時,首先去看new操做符後面的類型,由於知道了類型,才能知道要分配多大的內存空間。分配完內存以後,再調用構造函數,填充對象的各個域,這一步叫作對象的初始化,構造方法返回後,一個對象建立完畢,能夠把他的引用(地址)發佈到外部,在外部就能夠使用這個引用操縱這個對象。
1. Java有沒有goto語句?(2017-11-12-wl)
goto是Java 中的保留字,在目前版本的Java中沒有使用。根據 James Gosling(Java 之父)編寫的《The Java Programming Language》一書的附錄中給出了一個 Java 關鍵字列表,其中有 goto 和 const,可是這兩個是目前沒法使用的關鍵字,所以有些地方將其稱之爲保留字,其實保留字這個詞應該有更普遍的意義,由於熟悉 C 語言的程序員都知道,在系統類庫中使用過的有特殊意義的單詞或單詞的組合都被視爲保留字。
2. & 和 && 的區別(2017-11-12-wl)
&運算符有兩種用法:(1)按位與;(2)邏輯與。
&&運算符是短路與運算。邏輯與跟短路與的差異是很是巨大的,雖然兩者都要求運算符左右兩端的布爾值都是 true 整個表達式的值纔是 true。
&&之因此稱爲短路運算是由於,若是&&左邊的表達式的值是 false,右邊的表達式會被直接短路掉,不會進行運算。不少時候咱們可能都須要用&&而不是&,例如在驗證用戶登陸時斷定用戶名不是 null 並且不是空字符串,應當寫爲username != null &&!username.equals(""),兩者的順序不能交換,更不能用&運算符,由於第一個條件若是不成立,根本不能進行字符串的 equals 比較,不然會產生 NullPointerException 異常。注意:邏輯或運算符(|)和短路或運算符(||)的差異也是如此。
3. 在Java中,如何跳出當前的多重嵌套循環(2017-11-14-wl)
在最外層循環前加一個標記如A,而後用 break A;能夠跳出多重循環。(Java 中支持帶標籤的break和continue 語句,做用有點相似於 C 和 C++中的 goto 語句,可是就像要避免使用 goto 同樣,應該避免使用帶標籤的 break 和 continue,由於它不會讓你的程序變得更優雅,不少時候甚至有相反的做用)。
break是跳出多重循環 不執行循環後的代碼 continue是跳出當前循環執行下一次循環的代碼
4. 兩個對象值相同(x.equals(y) == true),但卻可有不一樣的hashCode,這句話對不對?(2017-11-14-wl)
不對,若是兩個對象x 和 y 知足 x.equals(y) == true,它們的哈希碼(hashCode)應當相同。
Java 對於eqauls 方法和 hashCode 方法是這樣規定的:(1)若是兩個對象相同(equals 方法返回 true),那麼它們的hashCode 值必定要相同;(2)若是兩個對象的 hashCode 相同,它們並不必定相同。固然,你未必要按照要求去作,可是若是你違背了上述原則就會發如今使用容器時,相同的對象能夠出如今 Set 集合中,同時增長新元素的效率會大大降低(對於使用哈希存儲的系統,若是哈希碼頻繁的衝突將會形成存取性能急劇降低)。
關於equals 和 hashCode方法,不少Java程序員都知道,但不少人也就是僅僅知道而已,在Joshua Bloch
的大做《Effective Java》(不少軟件公司,《Effective Java》、《Java 編程思想》以及《重構:改善既有代碼質量》是 Java 程序員必看書籍,若是你還沒看過,那就趕忙去買一本吧)中是這樣介紹 equals 方法的。
首先equals 方法必須知足自反性(x.equals(x)必須返回 true)、對稱性(x.equals(y)返回 true 時,y.equals(x)也必須返回 true)、傳遞性(x.equals(y)和 y.equals(z)都返回 true 時,x.equals(z)也必須返回 true)和一致性(當x 和 y 引用的對象信息沒有被修改時,屢次調用 x.equals(y)應該獲得一樣的返回值),並且對於任何非 null值的引用 x,x.equals(null)必須返回false。實現高質量的equals方法的訣竅包括:1. 使用==操做符檢查"參數是否爲這個對象的引用";2. 使用 instanceof 操做符檢查"參數是否爲正確的類型";3. 對於類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;4. 編寫完 equals 方法後,問本身它是否知足對稱性、傳遞性、一致性;5. 重寫 equals 時老是要重寫 hashCode;6. 不要將 equals 方法參數中的 Object 對象替換爲其餘的類型,在重寫時不要忘掉@Override 註解。
5. 是否能夠繼承String (2017-11-14-wl)
String類是final類,不能夠被繼承。
繼承String 自己就是一個錯誤的行爲,對 String 類型最好的重用方式是關聯關係(Has-A)和依賴關係(Use-A)而不是繼承關係(Is-A)。
6. 當一個對象被看成參數傳遞到一個方法後,此方法可改變這個對象的屬性,並可返回變化後的結果,那麼這裏究竟是值傳遞仍是引用傳遞?(2017-11-14-wl)
是值傳遞。Java語言的方法調用只支持參數的值傳遞。當一個對象實例做爲一個參數被傳遞到方法中時,參數的值就是對該對象的引用。對象的屬性能夠在被調用過程當中被改變,但對對象引用的改變是不會影響到調用者的。C++和 C#中能夠經過傳引用或傳輸出參數來改變傳入的參數的值。說明:Java中沒有傳引用實在是很是的不方便,這一點在 Java 8 中仍然沒有獲得改進,正是如此在 Java 編寫的代碼中才會出現大量的 Wrapper 類(將須要經過方法調用修改的引用置於一個 Wrapper 類中,再將Wrapper 對象傳入方法),這樣的作法只會讓代碼變得臃腫,尤爲是讓從 C 和 C++轉型爲 Java 程序員的開發者沒法容忍。
7. 重載(overload)和重寫(override)的區別?重載的方法可否根據返回類型進行區分?(2017-11-15-wl)
方法的重載和重寫都是實現多態的方式,區別在於前者實現的是編譯時的多態性,然後者實現的是運行時的多態性。重載發生在一個類中,同名的方法若是有不一樣的參數列表(參數類型不一樣、參數個數不一樣或者兩者都不一樣)則視爲重載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求。
方法重載的規則:
1.方法名一致,參數列表中參數的順序,類型,個數不一樣。
2.重載與方法的返回值無關,存在於父類和子類,同類中。
3.能夠拋出不一樣的異常,能夠有不一樣修飾符。
方法重寫的規則:
1.參數列表必須徹底與被重寫方法的一致,返回類型必須徹底與被重寫方法的返回類型一致。
2.構造方法不能被重寫,聲明爲final的方法不能被重寫,聲明爲static的方法不能被重寫,可是可以被再次聲明。
3.訪問權限不能比父類中被重寫的方法的訪問權限更低。
4.重寫的方法可以拋出任何非強制異常(UncheckedException,也叫非運行時異常),不管被重寫的方法是否拋出異常。可是,重寫的方法不能拋出新的強制性異常,或者比被重寫方法聲明的更普遍的強制性異常,反之則能夠。
8. 爲何函數不能根據返回類型來區分重載?(2017-11-15-wl)
該道題來自華爲面試題。
由於調用時不能指定類型信息,編譯器不知道你要調用哪一個函數。例如:
1.float max(int a, int b);
2.int max(int a, int b);
當調用max(1, 2);時沒法肯定調用的是哪一個,單從這一點上來講,僅返回值類型不一樣的重載是不該該容許的。
再好比對下面這兩個方法來講,雖然它們有一樣的名字和自變量,但實際上是很容易區分的:
1.void f() {}
2.int f() {}
若編譯器可根據上下文(語境)明確判斷出含義,好比在int x=f()中,那麼這樣作徹底沒有問題。然而,咱們也可能調用一個方法,同時忽略返回值;咱們一般把這稱爲「爲它的反作用去調用一個方法」,由於咱們關心的不是返回值,而是方法調用的其餘效果。因此假如咱們像下面這樣調用方法:f(); Java 怎樣判斷f()的具體調用方式呢?並且別人如何識別並理解代碼呢?因爲存在這一類的問題,因此不能。
函數的返回值只是做爲函數運行以後的一個「狀態」,他是保持方法的調用者與被調用者進行通訊的關鍵。並不能做爲某個方法的「標識」。
9. char 型變量中能不能存儲一箇中文漢字,爲何?(2017-11-16-wl)
char 類型能夠存儲一箇中文漢字,由於Java中使用的編碼是 Unicode(不選擇任何特定的編碼,直接 一個字符佔2個字節
使用字符在字符集中的編號,這是統一的惟一方法),一個char 類型佔 2 個字節(16 比特),因此放一箇中文是沒問題的。
補充:使用Unicode意味着字符在JVM內部和外部有不一樣的表現形式,在JVM內部都是 Unicode,當這個字符被從JVM內部轉移到外部時(例如存入文件系統中),須要進行編碼轉換。因此Java中有字節流和字符流,以及在字符流和字節流之間進行轉換的轉換流,如 InputStreamReader 和 OutputStreamReader,這兩個類是字節流和字符流之間的使用適配器模式,承擔了編碼轉換的任務;對於 C 程序員來講,要完成這樣的編碼轉換恐怕要依賴於 union(聯合體/共用體)共享內存的特徵來實現了。
10. 抽象類(abstract class)和接口(interface)有什麼異同?(2017-11-16-wl)
不一樣:
抽象類:
1.抽象類中能夠定義構造器
2.能夠有抽象方法和具體方法
3.接口中的成員全都是public的
4.抽象類中能夠定義成員變量
5.有抽象方法的類必須被聲明爲抽象類,而抽象類未必要有抽象方法
6.抽象類中能夠包含靜態方法
7.一個類只能繼承一個抽象類
接口:
1.接口中不能定義構造器
2.方法所有都是抽象方法
3.抽象類中的成員能夠是 private、默認、protected、public
4.接口中定義的成員變量實際上都是常量
5.接口中不能有靜態方法
6.一個類能夠實現多個接口
相同:
1.不可以實例化
2.都是頂層繼承或實現的頂層 .
3.一個類若是繼承了某個抽象類或者實現了某個接口都須要對其中的抽象方法所有進行實現,不然該類仍然須要被聲明爲抽象類
11. 抽象的(abstract)方法是否可同時是靜態的(static), 是否可同時是本地方法(native),是否可同時被synchronized(2017-11-16-wl)
都不能。抽象方法須要子類重寫,而靜態的方法是沒法被重寫的,所以兩者是矛盾的。本地方法是由
本地代碼(如C 代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。synchronized 和方法的實現細節有關,抽象方法不涉及實現細節,所以也是相互矛盾的。
12. 闡述靜態變量和實例變量的區別?(2017-11-16-wl)
類建立靜態變量初始化. 屬於類 在內存的方法區中
實例變量 構造方法初始化. 屬於方法 內存的堆中
靜態變量: 是被static 修飾符修飾的變量,也稱爲類變量,它屬於類,不屬於類的任何一個對象,一個類無論建立多少個對象,靜態變量在內存中有且僅有一個拷貝;
實例變量: 必須依存於某一實例,須要先建立對象而後經過對象才能訪問到它。靜態變量能夠實現讓多個對象共享內存。
13. ==和equals的區別?(2017-11-22-wzz)
equals和== 最大的區別是一個是方法一個是運算符。
==:若是比較的對象是基本數據類型,則比較的是數值是否相等;若是比較的是引用數據類型,則比較的是對象的地址值是否相等。
equals():用來比較方法兩個對象的內容是否相等。
注意:equals方法不能用於基本數據類型的變量,若是沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址。
14. break和continue的區別?(2017-11-23-wzz)
break和continue都是用來控制循環的語句。
break用於徹底結束一個循環,跳出循環體執行循環後面的語句。
continue用於跳過本次循環,執行下次循環。
15. String s = "Hello";s = s + " world!";這兩行代碼執行後,原始的String對象中的內容到底變了沒有?(2017-12-1-lyq)
沒有。由於String被設計成不可變(immutable)類,因此它的全部對象都是不可變對象。在這段代碼中,s原先指向一個String對象,內容是 "Hello",而後咱們對s進行了「+」操做,那麼s所指向的那個對象是否發生了改變呢?答案是沒有。這時,s不指向原來那個對象了,而指向了另外一個 String對象,內容爲"Hello world!",原來那個對象還存在於內存之中,只是s這個引用變量再也不指向它了。
經過上面的說明,咱們很容易導出另外一個結論,若是常常對字符串進行各類各樣的修改,或者說,不可預見的修改,那麼使用String來表明字符串的話會引發很大的內存開銷。由於 String對象創建以後不能再改變,因此對於每個不一樣的字符串,都須要一個String對象來表示。這時,應該考慮使用StringBuffer類,它容許修改,而不是每一個不一樣的字符串都要生成一個新的對象。而且,這兩種類的對象轉換十分容易。同時,咱們還能夠知道,若是要使用內容相同的字符串,沒必要每次都new一個String。例如咱們要在構造器中對一個名叫s的String引用變量進行初始化,把它設置爲初始值,應當這樣作:
1.public class Demo {
2. private String s;
3. ...
4. s = "Initial Value";
5. ...
6.}
而非
1.s = new String("Initial Value");
後者每次都會調用構造器,生成新對象,性能低下且內存開銷大,而且沒有意義,由於String對象不可改變,因此對於內容相同的字符串,只要一個String對象來表示就能夠了。也就說,屢次調用上面的構造器建立多個對象,他們的String類型屬性s都指向同一個對象。
上面的結論還基於這樣一個事實:對於字符串常量,若是內容相同,Java認爲它們表明同一個String對象。而用關鍵字new調用構造器,老是會建立一個新的對象,不管內容是否相同。 至於爲何要把String類設計成不可變類,是它的用途決定的。其實不僅String,不少Java標準類庫中的類都是不可變的。在開發一個系統的時候,咱們有時候也須要設計不可變類,來傳遞一組相關的值,這也是面向對象思想的體現。不可變類有一些優勢,好比由於它的對象是隻讀的,因此多線程併發訪問也不會有任何問題。固然也有一些缺點,好比每一個不一樣的狀態都要一個對象來表明,可能會形成性能上的問題。因此Java標準類庫還提供了一個可變版本,即 StringBuffer。
靠的是父類或接口定義的引用變量能夠指向子類或具體實現類的實例對象,而程序調用的方法在運行期才動態綁定,就是引用變量所指向的具體實例對象的方法,也就是內存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。
1)按照異常須要處理的時機分爲編譯時異常(也叫強制性異常)也叫CheckedException和運行時異常(也叫非強制性異常)也叫RuntimeException。只有java語言提供了Checked異常,Java認爲Checked異常都是能夠被處理的異常,因此Java程序必須顯式處理Checked異常。若是程序沒有處理Checked異常,該程序在編譯時就會發生錯誤沒法編譯。這體現了Java的設計哲學:沒有完善錯誤處理的代碼根本沒有機會被執行。對Checked異常處理方法有兩種:
1 編譯期異常,則用try...catch塊來處理該異常 及Exception異常。
2 runtimeexception 是 不須要提早處理的 當發生異常 程序中止 須要程序員修改代碼。
運行時異常只有當代碼在運行時才發行的異常,編譯時不須要try catch。Runtime如除數是0和數組下標越界等,其產生頻繁,處理麻煩,若顯示申明或者捕獲將會對程序的可讀性和運行效率影響很大。因此由系統自動檢測並將它們交給缺省的異常處理程序。固然若是你有處理要求也能夠顯示捕獲它們。
1. public int getNum(){
2. try {
3. int a = 1/0;
4. return 1;
5. } catch (Exception e) {
6. return 2;
7. }finally{
8. return 3;
9. }
代碼在走到第3行的時候遇到了一個MathException,這時第四行的代碼就不會執行了,代碼直接跳轉到catch語句中,走到第6行的時候,異常機制有這麼一個原則若是在catch中遇到了return或者異常等能使該函數終止的話那麼有finally就必須先執行完finally代碼塊裏面的代碼而後再返回值。所以代碼又跳到第8行,惋惜第8行是一個return語句,那麼這個時候方法就結束了,所以第6行的返回結果就沒法被真正返回。若是finally僅僅是處理了一個釋放資源的操做,那麼該道題最終返回的結果就是2。所以上面返回值是3。
3. error和exception的區別?(2017-2-23)
Error類和Exception類的父類都是Throwable類,他們的區別以下。
Error類通常是指與虛擬機相關的問題,如系統崩潰,虛擬機錯誤,內存空間不足,方法調用棧溢出等。對於這類錯誤的致使的應用程序中斷,僅靠程序自己沒法恢復和和預防,遇到這樣的錯誤,建議讓程序終止。
Exception包含編譯期異常checked Exception類表示程序能夠處理的異常,能夠捕獲且可能恢復。遇到這類異常,應該儘量處理異常,使程序恢復運行,而不該該隨意終止異常。 這類編譯期異常包括 sqlException 和IOexception
Exception類又分爲運行時異常(Runtime Exception)和受檢查的異常(Checked Exception ),運行時異常;ArithmaticException,IllegalArgumentException,編譯能經過,可是一運行就終止了,程序不會處理運行時異常,出現這類異常,程序會終止。而受檢查的異常,要麼用try。。。catch捕獲,要麼用throws字句聲明拋出,交給它的父類處理,不然編譯不會經過。
運行時異常包括 數組越界異常, 類型轉換異常, 類找不到,
Java對異常進行了分類,不一樣類型的異常分別用不一樣的Java類表示,全部異常的根類爲java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception,Error表示應用程序自己沒法克服和恢復的一種嚴重問題。Exception表示程序還可以克服和恢復的問題,其中又分爲系統異常和普通異常,系統異常是軟件自己缺陷所致使的問題,也就是軟件開發人員考慮不周所致使的問題,軟件使用者沒法克服和恢復這種問題,但在這種問題下還能夠讓軟件系統繼續運行或者讓軟件死掉,例如,數組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉換異常(ClassCastException);普通異常是運行環境的變化或異常所致使的問題,是用戶可以克服的問題,例如,網絡斷線,硬盤空間不夠,發生這樣的異常後,程序不該該死掉。
java爲系統異常和普通異常提供了不一樣的解決方案,編譯器強制普通異常必須try..catch處理或用throws聲明繼續拋給上層調用方法處理,因此普通異常也稱爲checked異常,而系統異常能夠處理也能夠不處理,因此,編譯器不強制用try..catch處理或用throws聲明,因此係統異常也稱爲unchecked異常。
5. 請寫出你最多見的5個RuntimeException(2017-11-22-wzz)
下面列舉幾個常見的RuntimeException。
1)java.lang.NullPointerException 空指針異常;出現緣由:調用了未經初始化的對象或者是不存在的對象。
2)java.lang.ClassNotFoundException 指定的類找不到;出現原因:類的名稱和路徑加載錯誤;一般都是程序試圖經過字符串來加載某個類時可能引起異常。
3)java.lang.NumberFormatException 字符串轉換爲數字異常;出現緣由:字符型數據中包含非數字型字符。
4)java.lang.IndexOutOfBoundsException 數組角標越界異常,常見於操做數組對象時發生。
5)java.lang.IllegalArgumentException 方法傳遞參數錯誤。
6)java.lang.ClassCastException 數據類型轉換異常。
7)java.lang.NoClassDefFoundException 未找到類定義錯誤。
8)SQLException SQL異常,常見於操做數據庫時的SQL語句錯誤。
9)java.lang.InstantiationException實例化異常。
10)java.lang.NoSuchMethodException 方法不存在異常。
6. throw和throws的區別(2017-11-22-wzz)
throw:
1)throw語句用在方法體內,表示拋出異常,由方法體內的語句處理。 拋出一個異常實力. 且只能拋出一個.在方法內
2)throw是具體向外拋出異常的動做,因此它拋出的是一個異常實例,執行throw必定是拋出了某種異常。
throws:在方法上能夠拋出多個異常名稱.
1)throws語句是用在方法聲明後面,表示若是拋出異常,由該方法的調用者來進行異常的處理。
2)throws主要是聲明這個方法會拋出某種類型的異常,讓它的使用者要知道須要捕獲的異常的類型。
3)throws表示出現異常的一種可能性,並不必定會發生這種異常。
7. final、finally、finalize的區別?(2017-11-23-wzz)
1)final:用於聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,被其修飾的類不可繼承。
2)finally:異常處理語句結構的一部分,表示老是執行。
3)finalize:Object類的一個方法,在垃圾回收器執行的時候會調用被回收對象的此方法,能夠覆蓋此方法提供垃圾收集時的其餘資源回收,例如關閉文件等。該方法更像是一個對象生命週期的臨終方法,當該方法被系統調用則表明該對象即將「死亡」,可是須要注意的是,咱們主動行爲上去調用該方法並不會致使該對象「死亡」,這是一個被動的方法(其實就是回調方法),不須要咱們調用。
finalize垃圾回收器執行的一個方法. 但不保證垃圾回收器必定執行
1. Math.round(11.5)等於多少?Math.round(- 11.5) 又等於多少?(2017-11-14-wl)
Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四捨五入的原理是在參數上加 0.5而後進行取整。
2. switch是否能做用在byte上,是否能做用在long上,是否能做用在String上?(2017-11-14-wl)
Java5之前switch(expr)中,expr 只能是 byte、short、char、int。從 Java 5 開始,Java 中引入了枚舉類型,expr 也能夠是 enum 類型。
從Java 7 開始,expr 還能夠是字符串(String),可是長整型(long)在目前全部的版本中都是不能夠的。
3. 數組有沒有length() 方法?String有沒有length() 方法?(2017-11-14-wl)
數組沒有length()方法,而是有length 的屬性。String有length()方法。JavaScript 中,得到字符串的長度是經過 length 屬性獲得的,這一點容易和 Java 混淆。 list集合是size()
4. String 、StringBuilder 、StringBuffer的區別?(2017-11-14-wl)
Java平臺提供了兩種類型的字符串:String和StringBuffer/StringBuilder,它們均可以儲存和操做字符串,區別以下。
1)String是隻讀字符串,也就意味着String引用的字符串內容是不能被改變的。初學者可能會有這樣的誤解:
1. String str = 「abc」;
2. str = 「bcd」;
如上,字符串str明明是能夠改變的呀!其實否則,str僅僅是一個引用對象,它指向一個字符串對象「abc」。第二行代碼的含義是讓str從新指向了一個新的字符串「bcd」對象,而「abc」對象並無任何改變,只不過該對象已經成爲一個不可及對象罷了。
2)StringBuffer/StringBuilder表示的字符串對象能夠直接進行修改。
3)StringBuilder是Java5中引入的,它和 StringBuffer的方法徹底相同,區別在於它是在單線程環境下使用的,由於它的全部方法都沒有被synchronized修飾,所以它的效率理論上也比StringBuffer要高。
5. 什麼狀況下用「+」運算符進行字符串鏈接比調用StringBuffer/StringBuilder 對象的append方法鏈接字符串性能更好?(2017-11-14-wl)
該題來自華爲。
字符串是Java程序中最經常使用的數據結構之一。在Java中String類已經重載了"+"。也就是說,字符串能夠直接使用"+"進行鏈接,以下面代碼所示:
1.String s = "abc" + "ddd";
但這樣作真的好嗎?固然,這個問題不能簡單地回答yes or no。要根據具體狀況來定。在Java中提供了一個StringBuilder類(這個類只在J2SE5及以上版本提供,之前的版本使用StringBuffer類),這個類也能夠起到"+"的做用。那麼咱們應該用哪一個呢?
下面讓咱們先看看以下的代碼:
1. package string;
2.
3. public class TestSimplePlus
4. {
5. public static void main(String[] args)
6. {
7. String s = "abc";
8. String ss = "ok" + s + "xyz" + 5;
9. System.out.println(ss);
10. }
11. }
上面的代碼將會輸出正確的結果。從表面上看,對字符串和整型使用"+"號並無什麼區別,但事實真的如此嗎?下面讓咱們來看看這段代碼的本質。
咱們首先使用反編譯工具(如jdk帶的javap、或jad)將TestSimplePlus反編譯成Java Byte Code,其中的奧祕就一目瞭然了。在本文將使用jad來反編譯,命令以下:jad -o -a -s d.java TestSimplePlus.class
反編譯後的代碼以下:
1. package string;
2.
3. import java.io.PrintStream;
4.
5. public class TestSimplePlus
6. {
7. public TestSimplePlus()
8. {
9. // 0 0:aload_0
10. // 1 1:invokespecial #8
11. // 2 4:return
12. }
13.
14. public static void main(String args[])
15. {
16. String s = "abc";
17. // 0 0:ldc1 #16
18. // 1 2:astore_1
19. String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
20. // 2 3:new #18
21. // 3 6:dup
22. // 4 7:ldc1 #20
23. // 5 9:invokespecial #22
24. // 6 12:aload_1
25. // 7 13:invokevirtual #25
26. // 8 16:ldc1 #29
27. // 9 18:invokevirtual #25
28. // 10 21:iconst_5
29. // 11 22:invokevirtual #31
30. // 12 25:invokevirtual #34
31. // 13 28:astore_2
32. System.out.println(ss);
33. // 14 29:getstatic #38
34. // 15 32:aload_2
35. // 16 33:invokevirtual #44
36. // 17 36:return
37. }
38. }
讀者可能看到上面的Java字節碼感到迷糊,不過你們沒必要擔憂。本文的目的並非講解Java Byte Code,所以,並不用瞭解具體的字節碼的含義。
使用jad反編譯的好處之一就是能夠同時生成字節碼和源代碼。這樣能夠進行對照研究。從上面的代碼很容易看出,雖然在源程序中使用了"+",但在編譯時仍然將"+"轉換成StringBuilder。所以,咱們能夠得出結論,在Java中不管使用何種方式進行字符串鏈接,實際上都使用的是StringBuilder。
那麼是否是能夠根據這個結論推出使用"+"和StringBuilder的效果是同樣的呢?這個要從兩個方面的解釋。若是從運行結果來解釋,那麼"+"和StringBuilder是徹底等效的。但若是從運行效率和資源消耗方面看,那它們將存在很大的區別。
固然,若是鏈接字符串行表達式很簡單(如上面的順序結構),那麼"+"和StringBuilder基本是同樣的,但若是結構比較複雜,如使用循環來鏈接字符串,那麼產生的Java Byte Code就會有很大的區別。先讓咱們看看以下的代碼:
1. package string;
2.
3. import java.util.*;
4.
5. public class TestComplexPlus
6. {
7. public static void main(String[] args)
8. {
9. String s = "";
10. Random rand = new Random();
11. for (int i = 0; i < 10; i++)
12. {
13. s = s + rand.nextInt(1000) + " ";
14. }
15. System.out.println(s);
16. }
17. }
上面的代碼返編譯後的Java Byte Code以下:
1. package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestComplexPlus
7. {
8.
9. public TestComplexPlus()
10. {
11. // 0 0:aload_0
12. // 1 1:invokespecial #8
13. // 2 4:return
14. }
15.
16. public static void main(String args[])
17. {
18. String s = "";
19. // 0 0:ldc1 #16
20. // 1 2:astore_1
21. Random rand = new Random();
22. // 2 3:new #18
23. // 3 6:dup
24. // 4 7:invokespecial #20
25. // 5 10:astore_2
26. for(int i = 0; i < 10; i++)
27. //* 6 11:iconst_0
28. //* 7 12:istore_3
29. //* 8 13:goto 49
30. s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
31. // 9 16:new #21
32. // 10 19:dup
33. // 11 20:aload_1
34. // 12 21:invokestatic #23
35. // 13 24:invokespecial #29
36. // 14 27:aload_2
37. // 15 28:sipush 1000
38. // 16 31:invokevirtual #32
39. // 17 34:invokevirtual #36
40. // 18 37:ldc1 #40
41. // 19 39:invokevirtual #42
42. // 20 42:invokevirtual #45
43. // 21 45:astore_1
44.
45. // 22 46:iinc 3 1
46. // 23 49:iload_3
47. // 24 50:bipush 10
48. // 25 52:icmplt 16
49. System.out.println(s);
50. // 26 55:getstatic #49
51. // 27 58:aload_1
52. // 28 59:invokevirtual #55
53. // 29 62:return
54. }
55. }
你們能夠看到,雖然編譯器將"+"轉換成了StringBuilder,但建立StringBuilder對象的位置卻在for語句內部。這就意味着每執行一次循環,就會建立一個StringBuilder對象(對於本例來講,是建立了10個StringBuilder對象),雖然Java有垃圾回收器,但這個回收器的工做時間是不定的。若是不斷產生這樣的垃圾,那麼仍然會佔用大量的資源。解決這個問題的方法就是在程序中直接使用StringBuilder來鏈接字符串,代碼以下:
1. package string;
2.
3. import java.util.*;
4.
5. public class TestStringBuilder
6. {
7. public static void main(String[] args)
8. {
9. String s = "";
10. Random rand = new Random();
11. StringBuilder result = new StringBuilder();
12. for (int i = 0; i < 10; i++)
13. {
14. result.append(rand.nextInt(1000));
15. result.append(" ");
16. }
17. System.out.println(result.toString());
18. }
19. }
上面代碼反編譯後的結果以下:
1. 20.package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestStringBuilder
7. {
8.
9. public TestStringBuilder()
10. {
11. // 0 0:aload_0
12. // 1 1:invokespecial #8
13. // 2 4:return
14. }
15.
16. public static void main(String args[])
17. {
18. String s = "";
19. // 0 0:ldc1 #16
20. // 1 2:astore_1
21. Random rand = new Random();
22. // 2 3:new #18
23. // 3 6:dup
24. // 4 7:invokespecial #20
25. // 5 10:astore_2
26. StringBuilder result = new StringBuilder();
27. // 6 11:new #21
28. // 7 14:dup
29. // 8 15:invokespecial #23
30. // 9 18:astore_3
31. for(int i = 0; i < 10; i++)
32. //* 10 19:iconst_0
33. //* 11 20:istore 4
34. //* 12 22:goto 47
35. {
36. result.append(rand.nextInt(1000));
37. // 13 25:aload_3
38. // 14 26:aload_2
39. // 15 27:sipush 1000
40. // 16 30:invokevirtual #24
41. // 17 33:invokevirtual #28
42. // 18 36:pop
43. result.append(" ");
44. // 19 37:aload_3
45. // 20 38:ldc1 #32
46. // 21 40:invokevirtual #34
47. // 22 43:pop
48. }
49.
50. // 23 44:iinc 4 1
51. // 24 47:iload 4
52. // 25 49:bipush 10
53. // 26 51:icmplt 25
54. System.out.println(result.toString());
55. // 27 54:getstatic #37
56. // 28 57:aload_3
57. // 29 58:invokevirtual #43
58. // 30 61:invokevirtual #47
59. // 31 64:return
60. }
61. }
從上面的反編譯結果能夠看出,建立StringBuilder的代碼被放在了for語句外。雖然這樣處理在源程序中看起來複雜,但卻換來了更高的效率,同時消耗的資源也更少了。
在使用StringBuilder時要注意,儘可能不要"+"和StringBuilder混着用,不然會建立更多的StringBuilder對象,以下面代碼所:
for (int i = 0; i < 10; i++) { result.append(rand.nextInt(1000)); result.append(" "); }
改爲以下形式:
for (int i = 0; i < 10; i++){ result.append(rand.nextInt(1000) + " ");}
則反編譯後的結果以下:
for(int i = 0; i < 10; i++) //* 10 19:iconst_0 //* 11 20:istore 4 //* 12 22:goto 65 { result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString()); // 13 25:aload_3 // 14 26:new #21 // 15 29:dup
從上面的代碼能夠看出,Java編譯器將"+"編譯成了StringBuilder,這樣for語句每循環一次,又建立了一個StringBuilder對象。 若是將上面的代碼在JDK1.4下編譯,必須將StringBuilder改成StringBuffer,而JDK1.4將"+"轉換爲StringBuffer(由於JDK1.4並無提供StringBuilder類)。StringBuffer和StringBuilder的功能基本同樣,只是StringBuffer是線程安全的,而StringBuilder不是線程安全的。所以,StringBuilder的效率會更高。
6. 請說出下面程序的輸出(2017-11-14-wl)
1. class StringEqualTest {
2. public static void main(String[] args) {
3. String s1 = "Programming";
4. String s2 = new String("Programming");
5. String s3 = "Program";
6. String s4 = "ming";
7. String s5 = "Program" + "ming";
8. String s6 = s3 + s4;
9. System.out.println(s1 == s2); //false
10. System.out.println(s1 == s5); //true
11. System.out.println(s1 == s6); //false
12. System.out.println(s1 == s6.intern()); //true
13. System.out.println(s2 == s2.intern()); //false
14. }
15. }
補充:解答上面的面試題須要知道以下兩個知識點:
1. String對象的intern()方法會獲得字符串對象在常量池中對應的版本的引用(若是常量池中有一個字符串與String 對象的 equals結果是 true),若是常量池中沒有對應的字符串,則該字符串將被添加到常量池中,而後返回常量池中字符串的引用;
2. 字符串的+操做其本質是建立了StringBuilder 對象進行 append 操做,而後將拼接後的 StringBuilder 對象用 toString 方法處理成 String 對象,這一點能夠用 javap -c StringEqualTest.class 命令得到 class 文件對應的 JVM 字節碼指令就能夠看出來。
7.1如何取得年月日、小時分鐘秒?(2017-11-19-wl)
1. public class DateTimeTest {
2. public static void main(String[] args) {
3. Calendar cal = Calendar.getInstance();
4. System.out.println(cal.get(Calendar.YEAR));
5. System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
6. System.out.println(cal.get(Calendar.DATE));
7. System.out.println(cal.get(Calendar.HOUR_OF_DAY));
8. System.out.println(cal.get(Calendar.MINUTE));
9. System.out.println(cal.get(Calendar.SECOND));
10. // Java 8
11. LocalDateTime dt = LocalDateTime.now();
12. System.out.println(dt.getYear());
13. System.out.println(dt.getMonthValue()); // 1 - 12
14. System.out.println(dt.getDayOfMonth());
15. System.out.println(dt.getHour());
16. System.out.println(dt.getMinute());
17. System.out.println(dt.getSecond());
18. }
19. }
7.2如何取得從1970年1月1日0時0分0 秒到如今的毫秒數?(2017-11-19-wl)
1. Calendar.getInstance().getTimeInMillis(); //第一種方式
2. System.currentTimeMillis(); //第二種方式
3. // Java 8
4. Clock.systemDefaultZone().millis();
7.3如何取得某月的最後一天?(2017-11-19-wl)
1. //獲取當前月第一天:
2. Calendar c = Calendar.getInstance();
3. c.add(Calendar.MONTH, 0);
4. c.set(Calendar.DAY_OF_MONTH,1);//設置爲1號,當前日期既爲本月第一天
5. String first = format.format(c.getTime());
6. System.out.println("===============first:"+first);
7.
8. //獲取當前月最後一天
9. Calendar ca = Calendar.getInstance();
10. ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
11. String last = format.format(ca.getTime());
12. System.out.println("===============last:"+last);
13.
14. //Java 8
15. LocalDate today = LocalDate.now();
16. //本月的第一天
17. LocalDate firstday = LocalDate.of(today.getYear(),today.getMonth(),1);
18. //本月的最後一天
19. LocalDate lastDay =today.with(TemporalAdjusters.lastDayOfMonth());
20. System.out.println("本月的第一天"+firstday);
21. System.out.println("本月的最後一天"+lastDay);
7.4如何格式化日期?(2017-11-19-wl)
1)Java.text.DataFormat 的子類(如 SimpleDateFormat 類)中的 format(Date)方法可將日期格式化。
2)Java 8 中能夠用 java.time.format.DateTimeFormatter來格式化時間日期,代碼以下所示:
1. import java.text.SimpleDateFormat;
2. import java.time.LocalDate;
3. import java.time.format.DateTimeFormatter;
4. import java.util.Date;
5. class DateFormatTest {
6.
7. public static void main(String[] args) {
8. SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
9. Date date1 = new Date();
10. System.out.println(oldFormatter.format(date1));
11.
12. // Java 8
13. DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
14. LocalDate date2 = LocalDate.now();
15. System.out.println(date2.format(newFormatter));
16. }
17. }
補充:Java的時間日期API一直以來都是被詬病的東西,爲了解決這一問題,Java 8中引入了新的時間日期API,其中包括 LocalDate、LocalTime、LocalDateTime、Clock、Instant 等類,這些的類的設計都使用了不變模式,所以是線程安全的設計。
7.5打印昨天的當前時刻? (2017-11-19-wl)
1. import java.util.Calendar;
2. class YesterdayCurrent {
3. public static void main(String[] args){
4. Calendar cal = Calendar.getInstance();
5. cal.add(Calendar.DATE, -1);
6. System.out.println(cal.getTime());
7. }
8. }
9.
10.
11. //java-8
12. import java.time.LocalDateTime;
13. class YesterdayCurrent {
14. public static void main(String[] args) {
15. LocalDateTime today = LocalDateTime.now();
16. LocalDateTime yesterday = today.minusDays(1);
17. System.out.println(yesterday);
18. }
19. }
以下表所示:
四類八種字節數數據表示範圍
整型
byte1-128~127 8位
short2-32768~32767
int4-2147483648~2147483647
long8-263~263-1
浮點型float4-3.403E38~3.403E38
double8-1.798E308~1.798E308
字符型char2表示一個字符,如('a','A','0','家')
布爾型boolean1只有兩個值true與false 八分之一個字節 1bit位
2. String是基本數據類型嗎?(2017-11-12-wl)
String是引用類型,底層用char數組實現的。
3. short s1 = 1; s1 = s1 + 1; 有錯嗎?short s1 = 1; s1 += 1有錯嗎;(2017-11-12-wl)
前者不正確,後者正確。對於short s1 = 1; s1 = s1 + 1;因爲1是 int 類型,所以 s1+1 運算結果也是 int 型,須要強制轉換類型才能賦值給short型。而 short s1 = 1; s1 += 1;能夠正確編譯,由於 s1+= 1;至關於 s1 = (short)(s1 + 1);其中有隱含的強制類型轉換。
4. int 和 和 Integer有什麼區別?(2017-11-12-wl)
Java是一個近乎純潔的面向對象編程語言,可是爲了編程的方便仍是引入了基本數據類型,爲了可以將這些基本數據類型當成對象操做,Java爲每個基本數據類型都引入了對應的包裝類型(wrapper class),int 的包裝類就是 Integer,從 Java 5 開始引入了自動裝箱/拆箱機制,使得兩者能夠相互轉換。
Integer是int的包裝類型, 包裝類型是一個對象, 能夠操做包裝類的方法.
int默認值是0 Integer默認值是null, 在判斷一個學生沒參加考試,和學生0分的狀況 只能用Integer, 在jsp頁面作EL表達式顯示數據的時候 int默認值0. 可是Integer默認的是空字符串.
Java爲每一個原始類型提供了包裝類型:
- 原始類型: boolean,char,byte,short,int,long,float,double
- 包裝類型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
5. 下面Integer類型的數值比較輸出的結果爲?(2017-11-12-wl)
若是不明就裏很容易認爲兩個輸出要麼都是true 要麼都是 false。首先須要注意的是 f一、f二、f三、f4 四個變量都是 Integer 對象引用,因此下面的==運算比較的不是值而是引用。裝箱的本質是什麼呢?當咱們給一個Integer 對象賦一個 int 值的時候,會調用 Integer 類的靜態方法 valueOf,若是看看 valueOf 的源代碼就知道發生了什麼。
源碼:
IntegerCache 是 Integer 的內部類,其代碼以下所示:
簡單的說,若是整型字面量的值在-128 到 127 之間,那麼不會 new 新的 Integer 對象,而是直接引用常量池中的 Integer 對象,因此上面的面試題中 f1==f2 的結果是 true,而 f3==f4 的結果是 false。
提醒:越是貌似簡單的面試題其中的玄機就越多,須要面試者有至關深厚的功力。
6. String類經常使用方法(2017-11-15-lyq)
7. String、StringBuffer、StringBuilder的區別?(2017-11-23-wzz)
(1)、可變不可變
String:字符串常量,在修改時不會改變自身;若修改,等於從新生成新的字符串對象。
StringBuffer:在修改時會改變對象自身,每次操做都是對StringBuffer對象自己進行修改,不是生成新的對象;使用場景:對字符串常常改變狀況下,主要方法:append(),insert()等。
(2)、線程是否安全
String:對象定義後不可變,線程安全。
StringBuffer:是線程安全的(對調用方法加入同步鎖),執行效率較慢,適用於多線程下操做字符串緩衝區大量數據。
StringBuilder:是線程不安全的,適用於單線程下操做字符串緩衝區大量數據。
(3)、共同點
StringBuilder與StringBuffer有公共父類AbstractStringBuilder(抽象類)。
StringBuilder、StringBuffer的方法都會調用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer會在方法上加synchronized關鍵字,進行同步。最後,若是程序不是多線程的,那麼使用StringBuilder效率高於StringBuffer。
(1)、字符串如何轉基本數據類型?
調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)便可返回相應基本類型。
(2)、基本數據類型如何轉字符串?
一種方法是將基本數據類型與空字符串(「」)鏈接(+)便可得到其所對應的字符串;另外一種方法是調用String 類中的valueOf()方法返回相應字符串。
1. Java中有幾種類型的流(2017-11-23-wzz)
按照流的方向:輸入流(inputStream)和輸出流(outputStream)。
按照實現功能分:節點流(能夠從或向一個特定的地方(節點)讀寫數據。如FileReader)和處理流(是對一個已存在的流的鏈接和封裝,經過所封裝的流的功能調用實現數據讀寫。如BufferedReader。處理流的構造方法老是要帶一個其餘的流對象作參數。一個流對象通過其餘流的屢次包裝,稱爲流的連接。)
按照處理數據的單位:字節流和字符流。字節流繼承於InputStream和OutputStream,字符流繼承於InputStreamReader 和OutputStreamWriter。
字節輸入流轉字符輸入流經過InputStreamReader實現,該類的構造函數能夠傳入InputStream對象。
字節輸出流轉字符輸出流經過OutputStreamWriter實現,該類的構造函數能夠傳入OutputStream對象。
在java中可以被序列化的類必須先實現Serializable接口,該接口沒有任何抽象方法只是起到一個標記做用。
1. //對象輸出流
2. ObjectOutputStream objectOutputStream =
3. new ObjectOutputStream(new FileOutputStream(new File("D://obj")));
4. objectOutputStream.writeObject(new User("zhangsan", 100));
5. objectOutputStream.close();
6. //對象輸入流
7. ObjectInputStream objectInputStream =
8. new ObjectInputStream(new FileInputStream(new File("D://obj")));
9. User user = (User)objectInputStream.readObject();
10. System.out.println(user);
11. objectInputStream.close();
4. 字節流和字符流的區別(2017-11-23-wzz)
字節流讀取的時候,讀到一個字節就返回一個字節;字符流使用了字節流讀到一個或多個字節(中文對應的字節數是兩個,在UTF-8碼錶中是3個字節)時。先去查指定的編碼表,將查到的字符返回。 字節流能夠處理全部類型數據,如:圖片,MP3,AVI視頻文件,而字符流只能處理字符數據。只要是處理純文本數據,就要優先考慮使用字符流,除此以外都用字節流。字節流主要是操做byte類型數據,以byte數組爲準,主要操做類就是OutputStream、InputStream
字符流處理的單元爲2個字節的Unicode字符,分別操做字符、字符數組或字符串,而字節流處理單元爲1個字節,操做字節和字節數組。因此字符流是由Java虛擬機將字節轉化爲2個字節的Unicode字符爲單位的字符而成的,因此它對多國語言支持性比較好!若是是音頻文件、圖片、歌曲,就用字節流好點,若是是關係到中文(文本)的,用字符流好點。在程序中一個字符等於兩個字節,java提供了Reader、Writer兩個專門操做字符流的類。
6. 什麼是java序列化,如何實現java序列化?(2017-12-7-lyq)
序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。能夠對流化後的對象進行讀寫操做,也可將流化後的對象傳輸於網絡之間。序列化是爲了解決在對對象流進行讀寫操做時所引起的問題。
序列化的實現:將須要被序列化的類實現Serializable接口,該接口沒有須要實現的方法,implements Serializable只是爲了標註該對象是可被序列化的,而後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象,接着,使用ObjectOutputStream對象的writeObject(Object obj)方法就能夠將參數爲obj的對象寫出(即保存其狀態),要恢復的話則用輸入流。
原文連接:https://www.cnblogs.com/yangchunze/p/6728086.html
1. HashMap排序題,上機題。(本人主要靠這道題入職的第一家公司)
已知一個HashMap集合, User有name(String)和age(int)屬性。請寫一個方法實現對HashMap的排序功能,該方法接收HashMap爲形參,返回類型爲HashMap,要求對HashMap中的User的age倒序進行排序。排序時key=value鍵值對不得拆散。
注意:要作出這道題必須對集合的體系結構很是的熟悉。HashMap自己就是不可排序的,可是該道題恰恰讓給HashMap排序,那咱們就得想在API中有沒有這樣的Map結構是有序的,LinkedHashMap,對的,就是他,他是Map結構,也是鏈表結構,有序的,更可喜的是他是HashMap的子類,咱們返回LinkedHashMap便可,還符合面向接口(父類編程的思想)。
但凡是對集合的操做,咱們應該保持一個原則就是能用JDK中的API就有JDK中的API,好比排序算法咱們不該該去用冒泡或者選擇,而是首先想到用Collections集合工具類。
1. public class HashMapTest {
2. public static void main(String[] args) {
3. HashMap users = new HashMap<>();
4. users.put(1, new User("張三", 25));
5. users.put(3, new User("李四", 22));
6. users.put(2, new User("王五", 28));
7. System.out.println(users);
8. HashMap sortHashMap = sortHashMap(users);
9. System.out.println(sortHashMap);
10. /**
11. *控制檯輸出內容
12. * {1=User [name=張三, age=25], 2=User [name=王五, age=28], 3=User [name=李四, age=22]}
13. {2=User [name=王五, age=28], 1=User [name=張三, age=25], 3=User [name=李四, age=22]}
14. */
15. }
16.
17. public static HashMap sortHashMap(HashMap map) {
18. //首先拿到map的鍵值對集合
19. Set> entrySet = map.entrySet();
20.
21. //將set集合轉爲List集合,爲何,爲了使用工具類的排序方法
22. List> list = new ArrayList>(entrySet);
23. //使用Collections集合工具類對list進行排序,排序規則使用匿名內部類來實現
24. Collections.sort(list, new Comparator>() {
25.
26. @Override
27. public int compare(Entry o1, Entry o2) {
28. //按照要求根據User的age的倒序進行排
29. return o2.getValue().getAge()-o1.getValue().getAge();
30. }
31. });
32. //建立一個新的有序的HashMap子類的集合
33. LinkedHashMap linkedHashMap = new LinkedHashMap();
34. //將List中的數據存儲在LinkedHashMap中
35. for(Entry entry : list){
36. linkedHashMap.put(entry.getKey(), entry.getValue());
37. }
38. //返回結果
39. return linkedHashMap;
40. }
41. }
42.
請問ArrayList、HashSet、HashMap是線程安全的嗎?若是不是我想要線程安全的集合怎麼辦?
咱們都看過上面那些集合的源碼(若是沒有那就看看吧),每一個方法都沒有加鎖,顯然都是線程不安全的。話又說過來若是他們安全了也就沒第二問了。
在集合中Vector和HashTable卻是線程安全的。你打開源碼會發現其實就是把各自核心方法添加上了synchronized關鍵字。
Collections工具類提供了相關的API,能夠讓上面那3個不安全的集合變爲安全的。
1. // Collections.synchronizedCollection(c)
2. // Collections.synchronizedList(list)
3. // Collections.synchronizedMap(m)
4. // Collections.synchronizedSet(s)
上面幾個函數都有對應的返回值類型,傳入什麼類型返回什麼類型。打開源碼其實實現原理很是簡單,就是將集合的核心方法添加上了synchronized關鍵字。
3. ArrayList內部用什麼實現的?(2015-11-24)
(回答這樣的問題,不要只回答個皮毛,能夠再介紹一下ArrayList內部是如何實現數組的增長和刪除的,由於數組在建立的時候長度是固定的,那麼就有個問題咱們往ArrayList中不斷的添加對象,它是如何管理這些數組呢?)
ArrayList內部是用Object[]實現的。接下來咱們分別分析ArrayList的構造、add、remove、clear方法的實現原理。
1、構造函數
1)空參構造
/**
* Constructs a new {@code ArrayList} instance with zero initial capacity.
*/
public ArrayList() {
array = EmptyArray.OBJECT;
}
array是一個Object[]類型。當咱們new一個空參構造時系統調用了EmptyArray.OBJECT屬性,EmptyArray僅僅是一個系統的類庫,該類源碼以下:
public final class EmptyArray {
private EmptyArray() {}
public static final boolean[] BOOLEAN = new boolean[0];
public static final byte[] BYTE = new byte[0];
public static final char[] CHAR = new char[0];
public static final double[] DOUBLE = new double[0];
public static final int[] INT = new int[0];
public static final Class[] CLASS = new Class[0];
public static final Object[] OBJECT = new Object[0];
public static final String[] STRING = new String[0];
public static final Throwable[] THROWABLE = new Throwable[0];
public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
}
也就是說當咱們new 一個空參ArrayList的時候,系統內部使用了一個new Object[0]數組。
2)帶參構造1
/**
* Constructs a new instance of {@code ArrayList} with the specified
* initial capacity.
*
* @param capacity
* the initial capacity of this {@code ArrayList}.
*/
public ArrayList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}
該構造函數傳入一個int值,該值做爲數組的長度值。若是該值小於0,則拋出一個運行時異常。若是等於0,則使用一個空數組,若是大於0,則建立一個長度爲該值的新數組。
3)帶參構造2
/**
* Constructs a new instance of {@code ArrayList} containing the elements of
* the specified collection.
*
* @param collection
* the collection of elements to add.
*/
public ArrayList(Collection collection) {
if (collection == null) {
throw new NullPointerException("collection == null");
}
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);
a = newArray;
}
array = a;
size = a.length;
}
若是調用構造函數的時候傳入了一個Collection的子類,那麼先判斷該集合是否爲null,爲null則拋出空指針異常。若是不是則將該集合轉換爲數組a,而後將該數組賦值爲成員變量array,將該數組的長度做爲成員變量size。這裏面它先判斷a.getClass是否等於Object[].class,其實通常都是相等的,我也暫時沒想明白爲何多加了這個判斷,toArray方法是Collection接口定義的,所以其全部的子類都有這樣的方法,list集合的toArray和Set集合的toArray返回的都是Object[]數組。
這裏講些題外話,其實在看Java源碼的時候,做者的不少意圖都很費人心思,我能知道他的目標是啥,可是不知道他爲什麼這樣寫。好比對於ArrayList, array是他的成員變量,可是每次在方法中使用該成員變量的時候做者都會從新在方法中開闢一個局部變量,而後給局部變量賦值爲array,而後再使用,有人可能說這是爲了防止併發修改array,畢竟array是成員變量,你們均可以使用所以須要將array變爲局部變量,而後再使用,這樣的說法並非都成立的,也許有時候就是老外們寫代碼的一個習慣而已。
2、add方法
add方法有兩個重載,這裏只研究最簡單的那個。
/**
* Adds the specified object at the end of this {@code ArrayList}.
*
* @param object
* the object to add.
* @return always true
*/
@Override public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
一、首先將成員變量array賦值給局部變量a,將成員變量size賦值給局部變量s。
二、判斷集合的長度s是否等於數組的長度(若是集合的長度已經等於數組的長度了,說明數組已經滿了,該從新分配新數組了),從新分配數組的時候須要計算新分配內存的空間大小,若是當前的長度小於MIN_CAPACITY_INCREMENT/2(這個常量值是12,除以2就是6,也就是若是當前集合長度小於6)則分配12個長度,若是集合長度大於6則分配當前長度s的一半長度。這裏面用到了三元運算符和位運算,s >> 1,意思就是將s往右移1位,至關於s=s/2,只不過位運算是效率最高的運算。
三、將新添加的object對象做爲數組的a[s]個元素。
四、修改集合長度size爲s+1
五、modCotun++,該變量是父類中聲明的,用於記錄集合修改的次數,記錄集合修改的次數是爲了防止在用迭代器迭代集合時避免併發修改異常,或者說用於判斷是否出現併發修改異常的。
六、return true,這個返回值意義不大,由於一直返回true,除非報了一個運行時異常。
3、remove方法
remove方法有兩個重載,咱們只研究remove(int index)方法。
/**
* Removes the object at the specified location from this list.
*
* @param index
* the index of the object to remove.
* @return the removed object.
* @throws IndexOutOfBoundsException
* when {@code location < 0 || location >= size()}
*/
@Override public E remove(int index) {
Object[] a = array;
int s = size;
if (index >= s) {
throwIndexOutOfBoundsException(index, s);
}
@SuppressWarnings("unchecked")
E result = (E) a[index];
System.arraycopy(a, index + 1, a, index, --s - index);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return result;
}
一、先將成員變量array和size賦值給局部變量a和s。
二、判斷形參index是否大於等於集合的長度,若是成了則拋出運行時異常
三、獲取數組中腳標爲index的對象result,該對象做爲方法的返回值
四、調用System的arraycopy函數,拷貝原理以下圖所示。
五、接下來就是很重要的一個工做,由於刪除了一個元素,並且集合總體向前移動了一位,所以須要將集合最後一個元素設置爲null,不然就可能內存泄露。
六、從新給成員變量array和size賦值
七、記錄修改次數
八、返回刪除的元素(讓用戶再看最後一眼)
4、clear方法
/**
* Removes all elements from this {@code ArrayList}, leaving it empty.
*
* @see #isEmpty
* @see #size
*/
@Override public void clear() {
if (size != 0) {
Arrays.fill(array, 0, size, null);
size = 0;
modCount++;
}
}
若是集合長度不等於0,則將全部數組的值都設置爲null,而後將成員變量size設置爲0便可,最後讓修改記錄加1。
併發集合常見的有ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque等。併發集合位於java.util.concurrent包下,是jdk1.5以後纔有的,主要做者是Doug Lea(http://baike.baidu.com/view/3141057.htm)完成的。
在java中有普通集合、同步(線程安全)的集合、併發集合。普通集合一般性能最高,可是不保證多線程的安全性和併發的可靠性。線程安全集合僅僅是給集合添加了synchronized同步鎖,嚴重犧牲了性能,並且對併發的效率就更低了,併發集合則經過複雜的策略不只保證了多線程的安全又提升的併發時的效率。
參考閱讀:
ConcurrentHashMap是線程安全的HashMap的實現,默認構造一樣有initialCapacity和loadFactor屬性,不過還多了一個concurrencyLevel屬性,三屬性默認值分別爲1六、0.75及16。其內部使用鎖分段技術,維持這鎖Segment的數組,在Segment數組中又存放着Entity[]數組,內部hash算法將數據較均勻分佈在不一樣鎖中。
put操做:並無在此方法上加上synchronized,首先對key.hashcode進行hash操做,獲得key的hash值。hash操做的算法和map也不一樣,根據此hash值計算並獲取其對應的數組中的Segment對象(繼承自ReentrantLock),接着調用此Segment對象的put方法來完成當前操做。
ConcurrentHashMap基於concurrencyLevel劃分出了多個Segment來對key-value進行存儲,從而避免每次put操做都得鎖住整個數組。在默認的狀況下,最佳狀況下可容許16個線程併發無阻塞的操做集合對象,儘量地減小併發時的阻塞現象。
get(key)
首先對key.hashCode進行hash操做,基於其值找到對應的Segment對象,調用其get方法完成當前操做。而Segment的get操做首先經過hash值和對象數組大小減1的值進行按位與操做來獲取數組上對應位置的HashEntry。在這個步驟中,可能會由於對象數組大小的改變,以及數組上對應位置的HashEntry產生不一致性,那麼ConcurrentHashMap是如何保證的?
對象數組大小的改變只有在put操做時有可能發生,因爲HashEntry對象數組對應的變量是volatile類型的,所以能夠保證如HashEntry對象數組大小發生改變,讀操做可看到最新的對象數組大小。
在獲取到了HashEntry對象後,怎麼能保證它及其next屬性構成的鏈表上的對象不會改變呢?這點ConcurrentHashMap採用了一個簡單的方式,即HashEntry對象中的hash、key、next屬性都是final的,這也就意味着沒辦法插入一個HashEntry對象到基於next屬性構成的鏈表中間或末尾。這樣就能夠保證當獲取到HashEntry對象後,其基於next屬性構建的鏈表是不會發生變化的。
ConcurrentHashMap默認狀況下采用將數據分爲16個段進行存儲,而且16個段分別持有各自不一樣的鎖Segment,鎖僅用於put和remove等改變集合對象的操做,基於volatile及HashEntry鏈表的不變性實現了讀取的不加鎖。這些方式使得ConcurrentHashMap可以保持極好的併發支持,尤爲是對於讀遠比插入和刪除頻繁的Map而言,而它採用的這些方法也可謂是對於Java內存模型、併發機制深入掌握的體現。
推薦博客地址:http://m.oschina.net/blog/269037
ArrayList 底層結構是數組,底層查詢快,增刪慢。
LinkedList 底層結構是鏈表型的,增刪快,查詢慢。
voctor 底層結構是數組線程安全的,增刪慢,查詢慢。
6. List和Map、Set的區別(2017-11-22-wzz)
List和Set是存儲單列數據的集合,Map是存儲鍵和值這樣的雙列數據的集合;List中存儲的數據是有順序,而且容許重複;Map中存儲的數據是沒有順序的,其鍵是不能重複的,它的值是能夠有重複的,Set中存儲的數據是無序的,且不容許有重複,但元素在集合中的位置由元素的hashcode決定,位置是固定的(Set集合根據hashcode來進行數據的存儲,因此位置是固定的,可是位置不是用戶能夠控制的,因此對於用戶來講set中的元素仍是無序的);
List接口有三個實現類(LinkedList:基於鏈表實現,鏈表內存是散亂的,每個元素存儲自己內存地址的同時還存儲下一個元素的地址。鏈表增刪快,查找慢;ArrayList:基於數組實現,非線程安全的,效率高,便於索引,但不便於插入刪除;Vector:基於數組實現,線程安全的,效率低)。
Map接口有三個實現類(HashMap:基於hash表的Map接口實現,非線程安全,高效,支持null值和null鍵;HashTable:線程安全,低效,不支持null值和null鍵;LinkedHashMap:是HashMap的一個子類,保存了記錄的插入順序;SortMap接口:TreeMap,可以把它保存的記錄根據鍵排序,默認是鍵值的升序排序)。
Set接口有兩個實現類(HashSet:底層是由HashMap實現,不容許集合中有重複的值,使用該方式時須要重寫equals()和hashCode()方法;LinkedHashSet:繼承與HashSet,同時又基於LinkedHashMap來進行實現,底層使用的是LinkedHashMp)。
List集合中對象按照索引位置排序,能夠有重複對象,容許按照對象在集合中的索引位置檢索對象,例如經過list.get(i)方法來獲取集合中的元素;Map中的每個元素包含一個鍵和一個值,成對出現,鍵對象不能夠重複,值對象能夠重複;Set集合中的對象不按照特定的方式排序,而且沒有重複對象,但它的實現類能對集合中的對象按照特定的方式排序,例如TreeSet類,能夠按照默認順序,也能夠經過實現Java.util.Comparator接口來自定義排序方式。
7. HashMap 和HashTable有什麼區別?(2017-2-23)
HashMap是線程不安全的,HashMap是一個接口,是Map的一個子接口,是將鍵映射到值得對象,不容許鍵值重複,容許空鍵和空值;因爲非線程安全,HashMap的效率要較HashTable的效率高一些.
HashTable 是線程安全的一個集合,不容許null值做爲一個key值或者Value值;
HashTable是sychronize,多個線程訪問時不須要本身爲它的方法實現同步,而HashMap在被多個線程訪問的時候須要本身爲它的方法實現同步;
8. 數組和鏈表分別比較適合用於什麼場景,爲何?(2017-2-23)
8.1 數組和鏈表簡介
在計算機中要對給定的數據集進行若干處理,首要任務是把數據集的一部分(當數據量很是大時,可能只能一部分一部分地讀取數據到內存中來處理)或所有存儲到內存中,而後再對內存中的數據進行各類處理。
例如,對於數據集S{1,2,3,4,5,6},要求S中元素的和,首先要把數據存儲到內存中,而後再將內存中的數據相加。
當內存空間中有足夠大的連續空間時,能夠把數據連續的存放在內存中,各類編程語言中的數組通常都是按這種方式存儲的(也可能有例外),如圖1(b);當內存中只有一些離散的可用空間時,想連續存儲數據就很是困難了,這時能想到的一種解決方式是移動內存中的數據,把離散的空間彙集成連續的一塊大空間,如圖1(c)所示,這樣作固然也能夠,可是這種狀況由於可能要移動別人的數據,因此會存在一些困難,移動的過程當中也有可能會把一些別人的重要數據給丟失。另一種,不影響別人的數據存儲方式是把數據集中的數據分開離散地存儲到這些不連續空間中,如圖(d)。這時爲了能把數據集中的全部數據聯繫起來,須要在前一塊數據的存儲空間中記錄下一塊數據的地址,這樣只要知道第一塊內存空間的地址就能環環相扣地把數據集總體聯繫在一塊兒了。C/C++中用指針實現的鏈表就是這種存儲形式。
圖內存分配
由上可知,內存中的存儲形式能夠分爲連續存儲和離散存儲兩種。所以,數據的物理存儲結構就有連續存儲和離散存儲兩種,它們對應了咱們一般所說的數組和鏈表,
8.2 數組和鏈表的區別
數組是將元素在內存中連續存儲的;它的優勢:由於數據是連續存儲的,內存地址連續,因此在查找數據的時候效率比較高;它的缺點:在存儲以前,咱們須要申請一塊連續的內存空間,而且在編譯的時候就必須肯定好它的空間的大小。在運行的時候空間的大小是沒法隨着你的須要進行增長和減小而改變的,當數據兩比較大的時候,有可能會出現越界的狀況,數據比較小的時候,又有可能會浪費掉內存空間。在改變數據個數時,增長、插入、刪除數據效率比較低
鏈表是動態申請內存空間,不須要像數組須要提早申請好內存的大小,鏈表只需在用的時候申請就能夠,根據須要來動態申請或者刪除內存空間,對於數據增長和刪除以及插入比數組靈活。還有就是鏈表中數據在內存中能夠在任意的位置,經過應用來關聯數據(就是經過存在元素的指針來聯繫)
8.3 鏈表和數組使用場景
數組應用場景:數據比較少;常常作的運算是按序號訪問數據元素;數組更容易實現,任何高級語言都支持;構建的線性表較穩定。
鏈表應用場景:對線性表的長度或者規模難以估計;頻繁作插入刪除操做;構建動態性比較強的線性表。
參考博客:http://blog.csdn.net/u011277123/article/details/53908387
8.4 跟數組相關的面試題
用面向對象的方法求出數組中重複value的個數,按以下個數輸出:
1出現:1次
3出現:2次
8出現:3次
2出現:4次
int[] arr = {1,4,1,4,2,5,4,5,8,7,8,77,88,5,4,9,6,2,4,1,5};
經過map的key是否存在 value+1
9. Java中ArrayList和Linkedlist區別?(2017-2-23)
ArrayList和Vector使用了數組的實現,能夠認爲ArrayList或者Vector封裝了對內部數組的操做,好比向數組中添加,刪除,插入新的元素或者數據的擴展和重定向。
LinkedList使用了循環雙向鏈表數據結構。與基於數組的ArrayList相比,這是兩種大相徑庭的實現技術,這也決定了它們將適用於徹底不一樣的工做場景。
LinkedList鏈表由一系列表項鍊接而成。一個表項老是包含3個部分:元素內容,前驅表和後驅表,如圖所示:
在下圖展現了一個包含3個元素的LinkedList的各個表項間的鏈接關係。在JDK的實現中,不管LikedList是否爲空,鏈表內部都有一個header表項,它既表示鏈表的開始,也表示鏈表的結尾。表項header的後驅表項即是鏈表中第一個元素,表項header的前驅表項即是鏈表中最後一個元素。
10. List a=new ArrayList()和ArrayList a =new ArrayList()的區別?(2017-2-24)
List list = new ArrayList();這句建立了一個ArrayList的對象後把上溯到了List。此時它是一個List對象了,有些ArrayList有可是List沒有的屬性和方法,它就不能再用了。而ArrayList list=new ArrayList();建立一對象則保留了ArrayList的全部屬性。 因此須要用到ArrayList獨有的方法的時候不能用前者。實例代碼以下:
1.List list = new ArrayList();
2.ArrayList arrayList = new ArrayList();
3.list.trimToSize(); //錯誤,沒有該方法。
4.arrayList.trimToSize(); //ArrayList裏有該方法。
11. 要對集合更新操做時,ArrayList和LinkedList哪一個更適合?(2017-2-24)
1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。 2.若是集合數據是對於集合隨機訪問get和set,ArrayList絕對優於LinkedList,由於LinkedList要移動指針。 3.若是集合數據是對於集合新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。
ArrayList和LinkedList 是兩個集合類,用於存儲一系列的對象引用(references)。例如咱們能夠用ArrayList來存儲一系列的String或者Integer。那 麼ArrayList和LinkedList在性能上有什麼差異呢?何時應該用ArrayList何時又該用LinkedList呢?
一.時間複雜度 首先一點關鍵的是,ArrayList的內部實現是基於基礎的對象數組的,所以,它使用get方法訪問列表中的任意一個元素時(random access),它的速度要比LinkedList快。LinkedList中的get方法是按照順序從列表的一端開始檢查,直到另一端。對LinkedList而言,訪問列表中的某個指定元素沒有更快的方法了。 假設咱們有一個很大的列表,它裏面的元素已經排好序了,這個列表多是ArrayList類型的也多是LinkedList類型的,如今咱們對這個列表來進行二分查找(binary search),比較列表是ArrayList和LinkedList時的查詢速度,看下面的程序:
1.public class TestList{
2. public static final int N=50000; //50000個數
3. public static List values; //要查找的集合
4.//放入50000個數給value;
5. static{
6. Integer vals[]=new Integer[N];
7. Random r=new Random();
8. for(int i=0,currval=0;i
9. vals=new Integer(currval);
10. currval+=r.nextInt(100)+1;
11. }
12. values=Arrays.asList(vals);
13. }
14.//經過二分查找法查找
15. static long timeList(List lst){
16. long start=System.currentTimeMillis();
17. for(int i=0;i
18. int index=Collections.binarySearch(lst, values.get(i));
19. if(index!=i)
20.System.out.println("***錯誤***");
21. }
22. return System.currentTimeMillis()-start;
23. }
24. public static void main(String args[])...{
25.System.out.println("ArrayList消耗時間:"+timeList(new ArrayList(values)));
26.System.out.println("LinkedList消耗時間:"+timeList(new LinkedList(values)));
27. }
28.}
獲得的輸出是:
1. ArrayList消耗時間:15
2. LinkedList消耗時間:2596
這個結果不是固定的,可是基本上ArrayList的時間要明顯小於LinkedList的時間。所以在這種狀況下不宜用LinkedList。二分查找法使用的隨機訪問(random access)策略,而LinkedList是不支持快速的隨機訪問的。對一個LinkedList作隨機訪問所消耗的時間與這個list的大小是成比例的。而相應的,在ArrayList中進行隨機訪問所消耗的時間是固定的。 這是否代表ArrayList老是比 LinkedList性能要好呢?這並不必定,在某些狀況下LinkedList的表現要優於ArrayList,有些算法在LinkedList中實現 時效率更高。比方說,利用Collections.reverse方法對列表進行反轉時,其性能就要好些。看這樣一個例子,加入咱們有一個列表,要對其進行大量的插入和刪除操做,在這種狀況下LinkedList就是一個較好的選擇。請看以下一個極端的例子,咱們重複的在一個列表的開端插入一個元素:
1.import java.util.*;
2.public class ListDemo {
3. static final int N=50000;
4. static long timeList(List list){
5. long start=System.currentTimeMillis();
6. Object o = new Object();
7. for(int i=0;i
8. list.add(0, o);
9. return System.currentTimeMillis()-start;
10. }
11. public static void main(String[] args) {
12.System.out.println("ArrayList耗時:"+timeList(new ArrayList()));
13.System.out.println("LinkedList耗時:"+timeList(new LinkedList()));
14. }
15.}
這時個人輸出結果是
1. ArrayList耗時:2463
2. LinkedList耗時:15
二.空間複雜度在LinkedList中有一個私有的內部類,定義以下:
1.private static class Entry {
2. Object element;
3. Entry next;
4. Entry previous;
5. }
每一個Entry對象reference列表 中的一個元素,同時還有在LinkedList中它的上一個元素和下一個元素。一個有1000個元素的LinkedList對象將有1000個連接在一塊兒 的Entry對象,每一個對象都對應於列表中的一個元素。這樣的話,在一個LinkedList結構中將有一個很大的空間開銷,由於它要存儲這1000個 Entity對象的相關信息。 ArrayList使用一個內置的數組來存 儲元素,這個數組的起始容量是10.當數組須要增加時,新的容量按以下公式得到:新容量=(舊容量*3)/2+1,也就是說每一次容量大概會增加50%。 這就意味着,若是你有一個包含大量元素的ArrayList對象,那麼最終將有很大的空間會被浪費掉,這個浪費是由ArrayList的工做方式自己形成 的。若是沒有足夠的空間來存放新的元素,數組將不得不被從新進行分配以便可以增長新的元素。對數組進行從新分配,將會致使性能急劇降低。若是咱們知道一個 ArrayList將會有多少個元素,咱們能夠經過構造方法來指定容量。咱們還能夠經過trimToSize方法在ArrayList分配完畢以後去掉浪 費掉的空間。
三.總結 ArrayList和LinkedList在性能上各有優缺點,都有各自所適用的地方,總的說來能夠描述以下: 1.對ArrayList和 LinkedList而言,在列表末尾增長一個元素所花的開銷都是固定的。對ArrayList而言,主要是在內部數組中增長一項,指向所添加的元素,偶 爾可能會致使對數組從新進行分配;而對LinkedList而言,這個開銷是統一的,分配一個內部Entry對象。 2.在ArrayList的中間插入或刪除一個元素意味着這個列表中剩餘的元素都會被移動;而在LinkedList的中間插入或刪除一個元素的開銷是固定的。 3.LinkedList不支持高效的隨機元素訪問。 4.ArrayList的空間浪費主要體如今在list列表的結尾預留必定的容量空間,而LinkedList的空間花費則體如今它的每個元素都須要消耗至關的空間 能夠這樣說:當操做是在一列數據的後面添加數據而不是在前面或中間,而且須要隨機地訪問其中的元素時,使用ArrayList會提供比較好的性能;當你的操做是在一列數據的前面或中間添加或刪除數據,而且按照順序訪問其中的元素時,就應該使用LinkedList了。
兩個隊列模擬一個堆棧,隊列是先進先出,而堆棧是先進後出。模擬以下
隊列a和b
(1)入棧:a隊列爲空,b爲空。9/例:則將」a,b,c,d,e」須要入棧的元素先放a中,a進棧爲」a,b,c,d,e」
(2)出棧:a隊列目前的元素爲」a,b,c,,d,e」。將a隊列依次加入Arraylist集合a中。以倒序的方法,將a中的集合取出,放入b隊列中,再將b隊列出列。代碼以下:
1. public static void main(String[] args) {
2. Queue queue = new LinkedList(); //a隊列
3. Queue queue2=new LinkedList(); //b隊列
4. ArrayList a=new ArrayList(); //arrylist集合是中間參數
5. //往a隊列添加元素
6. queue.offer("a");
7. queue.offer("b");
8. queue.offer("c");
9. queue.offer("d");
10. queue.offer("e");
11. System.out.print("進棧:");
12.//a隊列依次加入list集合之中
13. for(String q : queue){
14. a.add(q);
15. System.out.print(q);
16. }
17.//以倒序的方法取出(a隊列依次加入list集合)之中的值,加入b對列
18. for(int i=a.size()-1;i>=0;i--){
19. queue2.offer(a.get(i));
20. }
21.//打印出棧隊列
22. System.out.println("");
23. System.out.print("出棧:");
24. for(String q : queue2){
25. System.out.print(q);
26. }
27. }
打印結果爲(遵循棧模式先進後出):
進棧:a b c d e
出棧:e d c b a
13. Collection和Map的集成體系(2017-11-14-lyq)
Collection:
Map:
14. Map中的key和value能夠爲null麼?(2017-11-21-gxb)
HashMap對象的key、value值都可爲null。
HahTable對象的key、value值均不可爲null。
且二者的的key值均不能重複,若添加key相同的鍵值對,後面的value會自動覆蓋前面的value,但不會報錯。測試代碼以下:
1. public class Test {
2.
3. public static void main(String[] args) {
4. Map map = new HashMap();//HashMap對象
5. Map tableMap = new Hashtable();//HashTable對象
6.
7. map.put(null, null);
8. System.out.println("hashMap的[key]和[value]都可覺得null:" + map.get(null));
9.
10. try {
11. tableMap.put(null, "3");
12. System.out.println(tableMap.get(null));
13. } catch (Exception e) {
14. System.out.println("【ERROR】:hashTable的[key]不能爲null");
15. }
16.
17. try {
18. tableMap.put("3", null);
19. System.out.println(tableMap.get("3"));
20. } catch (Exception e) {
21. System.out.println("【ERROR】:hashTable的[value]不能爲null");
22. }
23. }
24.
25. }
運行結果:
hashMap的[key]和[value]都可覺得null:null 【ERROR】:hashTable的[key]不能爲null 【ERROR】:hashTable的[value]不能爲null
對於Java程序員來講,多線程在工做中的使用場景仍是比較常見的,而僅僅掌握了Java中的傳統多線程機制,仍是不夠的。在JDK5.0以後,Java增長的併發庫中提供了不少優秀的API,在實際開發中用的比較多。所以在看具體的面試題以前咱們有必要對這部分知識作一個全面的瞭解。
(一)多線程基礎知識--傳統線程機制的回顧(2017-12-11-wl)
( 1 ) 傳統使用類Thread和接口Runnable實現
1. 在Thread子類覆蓋的run方法中編寫運行代碼
方式一
new Thread(){
@Override
public void run(){
while(true){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
2. 在傳遞給Thread對象的Runnable對象的run方法中編寫代碼
new Thread(new Runnable(){
public void run(){
while(true){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}).start();
3. 總結
查看Thread類的run()方法的源代碼,能夠看到其實這兩種方式都是在調用Thread對象的run方法,若是Thread類的run方法沒有被覆蓋,而且爲該Thread對象設置了一個Runnable對象,該run方法會調用Runnable對象的run方法
/**
* If this thread was constructed using a separate
* Runnable
run object, then that
* Runnable
object's run
method is called;
* otherwise, this method does nothing and returns.
*
* Subclasses of Thread
should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
( 2 ) 定實現時器Timer和TimerTask
Timer在實際開發中應用場景很少,通常來講都會用其餘第三方庫來實現。但有時會在一些面試題中出現。下面咱們就針對一道面試題來使用Timer定時類。
1. 請模擬寫出雙重定時器(面試題)
要求:使用定時器,間隔4秒執行一次,再間隔2秒執行一次,以此類推執行。
class TimerTastCus extends TimerTask{
@Override
public void run() {
count = (count +1)%2;
System.err.println("Boob boom ");
new Timer().schedule(new TimerTastCus(), 2000+2000*count);
}
}
Timer timer = new Timer();
timer.schedule(new TimerTastCus(), 2000+2000*count);
while (true) {
System.out.println(new Date().getSeconds());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//PS:下面的代碼中的count變量中
//此參數要使用在你匿名內部類中,使用final修飾就沒法對其值進行修改,
//只能改成靜態變量
private static volatile int count = 0;
( 3 ) 線程互斥與同步 --沒看
在引入多線程後,因爲線程執行的異步性,會給系統形成混亂,特別是在急用臨界資源時,如多個線程急用同一臺打印機,會使打印結果交織在一塊兒,難於區分。當多個線程急用共享變量,表格,鏈表時,可能會致使數據處理出錯,所以線程同步的主要任務是使併發執行的各線程之間可以有效的共享資源和相互合做,從而使程序的執行具備可再現性。
當線程併發執行時,因爲資源共享和線程協做,使用線程之間會存在如下兩種制約關係。
1. 間接相互制約。一個系統中的多個線程必然要共享某種系統資源,如共享CPU,共享I/O設備,所謂間接相互制約即源於這種資源共享,打印機就是最好的例子,線程A在使用打印機時,其它線程都要等待。
2. 直接相互制約。這種制約主要是由於線程之間的合做,若有線程A將計算結果提供給線程B做進一步處理,那麼線程B在線程A將數據送達以前都將處於阻塞狀態。
間接相互制約能夠稱爲互斥,直接相互制約能夠稱爲同步,對於互斥能夠這樣理解,線程A和線程B互斥訪問某個資源則它們之間就會產個順序問題——要麼線程A等待線程B操做完畢,要麼線程B等待線程操做完畢,這其實就是線程的同步了。所以同步包括互斥,互斥實際上是一種特殊的同步。
下面咱們經過一道面試題來體會線程的交互。
要求:子線程運行執行10次後,主線程再運行5次。這樣交替執行三遍
public static void main(String[] args) {
final Bussiness bussiness = new Bussiness();
//子線程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
bussiness.subMethod();
}
}
}).start();
//主線程
for (int i = 0; i < 3; i++) {
bussiness.mainMethod();
}
}
}
class Bussiness {
private boolean subFlag = true;
public synchronized void mainMethod() {
while (subFlag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ " : main thread running loop count -- " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
subFlag = true;
notify();
}
public synchronized void subMethod() {
while (!subFlag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 10; i++) {
System.err.println(Thread.currentThread().getName()
+ " : sub thread running loop count -- " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
subFlag = false;
notify();
}
}
( 4 ) 線程局部變量ThreadLocal
l ThreadLocal的做用和目的:用於實現線程內的數據共享,即對於相同的程序代碼,多個模塊在同一個線程中運行時要共享一份數據,而在另外線程中運行時又共享另一份數據。
l 每一個線程調用全局ThreadLocal對象的set方法,在set方法中,首先根據當前線程獲取當前線程的ThreadLocalMap對象,而後往這個map中插入一條記錄,key實際上是ThreadLocal對象,value是各自的set方法傳進去的值。也就是每一個線程其實都有一份本身獨享的ThreadLocalMap對象,該對象的Key是ThreadLocal對象,值是用戶設置的具體值。在線程結束時能夠調用ThreadLocal.remove()方法,這樣會更快釋放內存,不調用也能夠,由於線程結束後也能夠自動釋放相關的ThreadLocal變量。
l ThreadLocal的應用場景:
Ø 訂單處理包含一系列操做:減小庫存量、增長一條流水臺帳、修改總帳,這幾個操做要在同一個事務中完成,一般也即同一個線程中進行處理,若是累加公司應收款的操做失敗了,則應該把前面的操做回滾,不然,提交全部操做,這要求這些操做使用相同的數據庫鏈接對象,而這些操做的代碼分別位於不一樣的模塊類中。
Ø 銀行轉帳包含一系列操做:把轉出賬戶的餘額減小,把轉入賬戶的餘額增長,這兩個操做要在同一個事務中完成,它們必須使用相同的數據庫鏈接對象,轉入和轉出操做的代碼分別是兩個不一樣的賬戶對象的方法。
Ø 例如Strut2的ActionContext,同一段代碼被不一樣的線程調用運行時,該代碼操做的數據是每一個線程各自的狀態和數據,對於不一樣的線程來講,getContext方法拿到的對象都不相同,對同一個線程來講,無論調用getContext方法多少次和在哪一個模塊中getContext方法,拿到的都是同一個。
綁定session讓 service 層和dao層在一個事務中.
1. ThreadLocal的使用方式
(1) 在關聯數據類中建立private static ThreadLocal
在下面的類中,私有靜態 ThreadLocal 實例(serialNum)爲調用該類的靜態SerialNum.get() 方法的每一個線程維護了一個「序列號」,該方法將返回當前線程的序列號。(線程的序列號是在第一次調用SerialNum.get() 時分配的,並在後續調用中不會更改。)
public class SerialNum {
// The next serial number to be assigned
private static int nextSerialNum = 0;
private static ThreadLocal serialNum = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Integer(nextSerialNum++);
}
};
public static int get() {
return ((Integer) (serialNum.get())).intValue();
}
}
另外一個例子,也是私有靜態 ThreadLocal 實例:
public class ThreadContext { private String userId; private Long transactionId; private static ThreadLocal threadLocal = new ThreadLocal(){ @Override protected ThreadContext initialValue() { return new ThreadContext(); } }; public static ThreadContext get() { return threadLocal.get(); }
public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public Long getTransactionId() { return transactionId; } public void setTransactionId(Long transactionId) { this.transactionId = transactionId; }}
補充:在JDK的API對ThreadLocal私有化的說明。並舉例‘線程惟一標識符’UniqueThreadIdGenerator ,你們學習是能夠結合官方API來學習。
2. 在Util類中建立ThreadLocal
這是上面用法的擴展,即把ThreadLocal的建立放到工具類中。
public class HibernateUtil { private static Log log = LogFactory.getLog(HibernateUtil.class);private static final SessionFactory sessionFactory; //定義SessionFactory static { try {//經過默認配置文件hibernate.cfg.xml建立SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) {log.error("初始化SessionFactory失敗!", ex); throw new ExceptionInInitializerError(ex); } }//建立線程局部變量session,用來保存Hibernate的Session public static final ThreadLocal session = new ThreadLocal(); /***獲取當前線程中的Session * @return Session * @throws HibernateException */ public static Session currentSession() throws HibernateException { Session s = (Session) session.get();//若是Session尚未打開,則新開一個Session if (s == null) { s = sessionFactory.openSession();session.set(s); //將新開的Session保存到線程局部變量中 } return s; } public static void closeSession() throws HibernateException {//獲取線程局部變量,並強制轉換爲Session類型 Session s = (Session) session.get(); session.set(null); if (s != null) s.close(); }}
3. 在Runnable中建立ThreadLocal
在線程類內部建立ThreadLocal,基本步驟以下:
①、在多線程的類(如ThreadDemo類)中,建立一個ThreadLocal對象threadXxx,用來保存線程間須要隔離處理的對象xxx。
②、在ThreadDemo類中,建立一個獲取要隔離訪問的數據的方法getXxx(),在方法中判斷,若ThreadLocal對象爲null時候,應該new()一個隔離訪問類型的對象,並強制轉換爲要應用的類型
③、在ThreadDemo類的run()方法中,經過調用getXxx()方法獲取要操做的數據,這樣能夠保證每一個線程對應一個數據對象,在任什麼時候刻都操做的是這個對象。
public class ThreadLocalTest implements Runnable{ ThreadLocal studenThreadLocal = new ThreadLocal(); @Override public void run() { String currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + " is running..."); Random random = new Random(); int age = random.nextInt(100); System.out.println(currentThreadName + " is set age: " + age); Studen studen = getStudent(); //經過這個方法,爲每一個線程都獨立的new一個student對象,每一個線程的的student對象均可以設置不一樣的值 studen.setAge(age); System.out.println(currentThreadName + " is first get age: " + studen.getAge()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( currentThreadName + " is second get age: " + studen.getAge()); } private Studen getStudent() { Studen studen = studenThreadLocal.get(); if (null == studen) { studen = new Studen(); studenThreadLocal.set(studen); } return studen; } public static void main(String[] args) { ThreadLocalTest t = new ThreadLocalTest(); Thread t1 = new Thread(t,"Thread A"); Thread t2 = new Thread(t,"Thread B"); t1.start(); t2.start(); } }class Studen{ int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
( 5 ) 多線程共享數據
在Java傳統線程機制中的共享數據方式,大體能夠簡單分兩種狀況:
Ø 多個線程行爲一致,共同操做一個數據源。也就是每一個線程執行的代碼相同,能夠使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,賣票系統就能夠這麼作。
Ø 多個線程行爲不一致,共同操做一個數據源。也就是每一個線程執行的代碼不一樣,這時候須要用不一樣的Runnable對象。例如,銀行存取款。
下面咱們經過兩個示例代碼來分別說明這兩種方式。
1. 多個線程行爲一致共同操做一個數據
若是每一個線程執行的代碼相同,能夠使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,買票系統就能夠這麼作。
/**
*共享數據類
**/
class ShareData{
private int num = 10 ;
public synchronized void inc() {
num++;
System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
*多線程類
**/
class RunnableCusToInc implements Runnable{
private ShareData shareData;
public RunnableCusToInc(ShareData data) {
this.shareData = data;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.inc();
}
}
}
/**
*測試方法
**/
public static void main(String[] args) {
ShareData shareData = new ShareData();
for (int i = 0; i < 4; i++) {
new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
}
}
}
2. 多個線程行爲不一致共同操做一個數據
若是每一個線程執行的代碼不一樣,這時候須要用不一樣的Runnable對象,有以下兩種方式來實現這些Runnable對象之間的數據共享:
1) 將共享數據封裝在另一個對象中,而後將這個對象逐一傳遞給各個Runnable對象。每一個線程對共享數據的操做方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操做的互斥和通訊。
public static void main(String[] args) {
ShareData shareData = new ShareData();
for (int i = 0; i < 4; i++) {
if(i%2 == 0){
new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
}else{
new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();
}
}
}
//封裝共享數據類
class RunnableCusToInc implements Runnable{
//封裝共享數據
private ShareData shareData;
public RunnableCusToInc(ShareData data) {
this.shareData = data;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.inc();
}
}
}
//封裝共享數據類
class RunnableCusToDec implements Runnable{
//封裝共享數據
private ShareData shareData;
public RunnableCusToDec(ShareData data) {
this.shareData = data;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.dec();
}
}
}
/**
*共享數據類
**/
class ShareData{
private int num = 10 ;
public synchronized void inc() {
num++;
System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2) 將這些Runnable對象做爲某一個類中的內部類,共享數據做爲這個外部類中的成員變量,每一個線程對共享數據的操做方法也分配給外部類,以便實現對共享數據進行的各個操做的互斥和通訊,做爲內部類的各個Runnable對象調用外部類的這些方法。
public static void main(String[] args) {
//公共數據
final ShareData shareData = new ShareData();
for (int i = 0; i < 4; i++) {
if(i%2 == 0){
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.inc();
}
}
},"Thread "+ i).start();
}else{
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
shareData.dec();
}
}
},"Thread "+ i).start();
}
}
}
class ShareData{
private int num = 10 ;
public synchronized void inc() {
num++;
System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void dec() {
num--;
System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
補充:上面兩種方式的組合:將共享數據封裝在另一個對象中,每一個線程對共享數據的操做方法也分配到那個對象身上去完成,對象做爲這個外部類中的成員變量或方法中的局部變量,每一個線程的Runnable對象做爲外部類中的成員內部類或局部內部類。
總之,要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通訊。
(二)多線程基礎知識--線程併發庫(2017-12-11-wl)
Java 5 添加了一個新的包到 Java 平臺,java.util.concurrent 包。這個包包含有一系列可以讓 Java 的併發編程變得更加簡單輕鬆的類。在這個包被添加之前,你須要本身去動手實現本身的相關工具類。下面帶你認識下java.util.concurrent包裏的這些類,而後你能夠嘗試着如何在項目中使用它們。本文中將使用Java 6 版本,我不肯定這和 Java 5 版本里的是否有一些差別。我不會去解釋關於 Java 併發的核心問題 – 其背後的原理,也就是說,若是你對那些東西感興趣,參考《Java 併發指南》。
( 1 ) Java的線程併發庫介紹
Java5的多線程並有兩個大發庫在java.util.concurrent包及子包中,子包主要的包有一下兩個
1) java.util.concurrent包 (多線程併發庫)
Ø java.util.concurrent 包含許多線程安全、測試良好、高性能的併發構建塊。不客氣地說,建立 java.util.concurrent 的目的就是要實現 Collection 框架對數據結構所執行的併發操做。經過提供一組可靠的、高性能併發構建塊,開發人員能夠提升併發類的線程安全、可伸縮性、性能、可讀性和可靠性,後面、咱們會作介紹。
Ø 若是一些類名看起來類似,多是由於java.util.concurrent 中的許多概念源自 Doug Lea 的 util.concurrent 庫。
2) java.util.concurrent.atomic包 (多線程的原子性操做提供的工具類)
Ø 查看atomic包文檔頁下面的介紹,它能夠對多線程的基本數據、數組中的基本數據和對象中的基本數據進行多線程的操做(AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpDater…)
Ø 經過以下兩個方法快速理解atomic包的意義:
n AtomicInteger類的boolean compareAndSet(expectedValue, updateValue);
n AtomicIntegerArray類的int addAndGet(int i, int delta);
Ø 順帶解釋volatile類型的做用,須要查看java語言規範。
n volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最的值。(具備可見性)
n volatile沒有原子性。
3) java.util.concurrent.lock包 (多線程的鎖機制)
爲鎖和等待條件提供一個框架的接口和類,它不一樣於內置同步和監視器。該框架容許更靈活地使用鎖和條件。本包下有三大接口,下面簡單介紹下:
Ø Lock接口:支持那些語義不一樣(重入、公平等)的鎖規則,能夠在非阻塞式結構的上下文(包括hand-over-hand 和鎖重排算法)中使用這些規則。主要的實現是ReentrantLock。
Ø ReadWriteLock接口:以相似方式定義了一些讀取者能夠共享而寫入者獨佔的鎖。此包只提供了一個實現,即ReentrantReadWriteLock,由於它適用於大部分的標準用法上下文。但程序員能夠建立本身的、適用於非標準要求的實現。
Ø Condition接口:描述了可能會與鎖有關聯的條件變量。這些變量在用法上與使用Object.wait 訪問的隱式監視器相似,但提供了更強大的功能。須要特別指出的是,單個 Lock 可能與多個 Condition 對象關聯。爲了不兼容性問題,Condition 方法的名稱與對應的 Object 版本中的不一樣。
( 2 ) Java的併發庫入門
下面咱們將分別介紹java.util.concurrent包下的經常使用類的使用。
1) java.util.concurrent包
java.util.concurrent包描述:
在併發編程中很經常使用的實用工具類。此包包括了幾個小的、已標準化的可擴展框架,以及一些提供有用功能的類。此包下有一些組件,其中包括:
l 執行程序(線程池)
l 併發隊列
l 同步器
l 併發Collocation
下面咱們將java.util.concurrent包下的組件逐一簡單介紹:
A. 執行程序
Ø Executors線程池工廠類
首次咱們來講下線程池的做用:
線程池做用就是限制系統中執行線程的數量。 根據系統的環境狀況,能夠自動或手動設置線程數量,達到運行的最佳效果;少了浪費了系統資源,多了形成系統擁擠效率不高。用線程池控制線程數量,其餘線程 排隊等候。一個任務執行完畢,再從隊列的中取最前面的任務開始執行。若隊列中沒有等待進程,線程池的這一資源處於等待。當一個新任務須要運行時,若是線程 池中有等待的工做線程,就能夠開始運行了;不然進入等待隊列。
爲何要用線程池:
l 減少了建立和銷燬線程的次數,每一個工做線程均可以被重複利用,可執行多個任務
l 能夠根據系統的承受能力,調整線程池中工做線線程的數目,防止由於由於消耗過多的內存,而把服務器累趴下(每一個線程須要大約1MB內存,線程開的越多,消耗的內存也就越大,最後死機)
Executors詳解:
Java裏面線程池的頂級接口是Executor,可是嚴格意義上講Executor並非一個線程池,而只是一個執行線程的工具。真正的線程池接口是ExecutorService。ThreadPoolExecutor是Executors類的底層實現。咱們先介紹下Executors。
線程池的基本思想仍是一種對象池的思想,開闢一塊內存空間,裏面存放了衆多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣能夠避免反覆建立線程對象所帶來的性能開銷,節省了系統的資源。
Java5中併發庫中,線程池建立線程大體能夠分爲下面三種:
//建立固定大小的線程池
ExecutorService fPool = Executors.newFixedThreadPool(3);
//建立緩存大小的線程池
ExecutorService cPool = Executors.newCachedThreadPool();
//建立單一的線程池
ExecutorService sPool = Executors.newSingleThreadExecutor();
newSchaduleThreadPool()
//建立能夠週期性執行任務的線程池
下面咱們經過簡單示例來分別說明:
l 固定大小鏈接池
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
/**
* Java線程:線程池-
*
* @author Administrator 2009-11-4 23:30:44
*/
public class Test {
public static void main(String[] args) {
//建立一個可重用固定線程數的線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//建立實現了Runnable接口對象,Thread對象固然也實現了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//將線程放入池中進行執行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//關閉線程池
pool.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在執行。。。");
}
}
運行結果:
pool-1-thread-1正在執行。。。 pool-1-thread-1正在執行。。。 pool-1-thread-2正在執行。。。 pool-1-thread-1正在執行。。。 pool-1-thread-2正在執行。。。
從上面的運行來看,咱們Thread類都是在線程池中運行的,線程池在執行execute方法來執行Thread類中的run方法。無論execute執行幾回,線程池始終都會使用2個線程來處理。不會再去建立出其餘線程來處理run方法執行。這就是固定大小線程池。
l 單任務鏈接池
咱們將上面的代碼
//建立一個可重用固定線程數的線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
改成:
//建立一個使用單個 worker 線程的 Executor,以無界隊列方式來運行該線程。
ExecutorService pool = Executors.newSingleThreadExecutor();
運行結果:
pool-1-thread-1正在執行。。。 pool-1-thread-1正在執行。。。 pool-1-thread-1正在執行。。。 pool-1-thread-1正在執行。。。 pool-1-thread-1正在執行。。。
運行結果看出,單任務線程池在執行execute方法來執行Thread類中的run方法。無論execute執行幾回,線程池始終都會使用單個線程來處理。
補充:在java的多線程中,一但線程關閉,就會成爲死線程。關閉後死線程就沒有辦法在啓動了。再次啓動就會出現異常信息:Exception in thread "main" java.lang.IllegalThreadStateException。那麼如何解決這個問題呢?
咱們這裏就能夠使用Executors.newSingleThreadExecutor()來再次啓動一個線程。(面試)
。
B. 併發隊列-阻塞隊列
經常使用的併發隊列有阻塞隊列和非阻塞隊列,前者使用鎖實現,後者則使用CAS非阻塞算法實現。
PS:至於非阻塞隊列是靠CAS非阻塞算法,在這裏再也不介紹,你們只用知道,Java非阻塞隊列是使用CAS算法來實現的就能夠。感興趣的童鞋能夠維基網上自行學習.
下面咱們先介紹阻塞隊列。
阻塞隊列:
阻塞隊列(BlockingQueue)是Java util.concurrent包下重要的數據結構,BlockingQueue提供了線程安全的隊列訪問方式:當阻塞隊列進行插入數據時,若是隊列已滿,線程將會阻塞等待直到隊列非滿;從阻塞隊列取數據時,若是隊列已空,線程將會阻塞等待直到隊列非空。併發包下不少高級同步類的實現都是基於BlockingQueue實現的。
Ø BlockingQueue阻塞隊列
BlockingQueue 一般用於一個線程生產對象,而另一個線程消費這些對象的場景。下圖是對這個原理的闡述:
一個線程往裏邊放,另一個線程從裏邊取的一個BlockingQueue。
一個線程將會持續生產新對象並將其插入到隊列之中,直到隊列達到它所能容納的臨界點。也就是說,它是有限的。若是該阻塞隊列到達了其臨界點,負責生產的線程將會在往裏邊插入新對象時發生阻塞。它會一直處於阻塞之中,直到負責消費的線程從隊列中拿走一個對象。負責消費的線程將會一直從該阻塞隊列中拿出對象。若是消費線程嘗試去從一個空的隊列中提取對象的話,這個消費線程將會處於阻塞之中,直到一個生產線程把一個對象丟進隊列。
BlockingQueue的方法:
BlockingQueue 具備 4 組不一樣的方法用於插入、移除以及對隊列中的元素進行檢查。若是請求的操做不能獲得當即執行的話,每一個方法的表現也不一樣。這些方法以下:
阻塞隊列提供了四種處理方法:
方法\處理方式拋出異常返回特殊值一直阻塞超時退出
插入方法add(e)offer(e)put(e)offer(e,time,unit)
移除方法remove()poll()take()poll(time,unit)
檢查方法element()peek()不可用不可用
四組不一樣的行爲方式解釋:
拋異常:若是試圖的操做沒法當即執行,拋一個異常。
特定值:若是試圖的操做沒法當即執行,返回一個特定的值(經常是 true / false)。
阻塞:若是試圖的操做沒法當即執行,該方法調用將會發生阻塞,直到可以執行。
超時:若是試圖的操做沒法當即執行,該方法調用將會發生阻塞,直到可以執行,但等待時間不會超過給定值。返回一個特定值以告知該操做是否成功(典型的是true / false)。
沒法向一個BlockingQueue 中插入 null。若是你試圖插入 null,BlockingQueue 將會拋出一個 NullPointerException.
BlockingQueue的實現類:
BlockingQueue 是個接口,你須要使用它的實現之一來使用BlockingQueue,Java.util.concurrent包下具備如下 BlockingQueue 接口的實現類:
l ArrayBlockingQueue:ArrayBlockingQueue 是一個有界的阻塞隊列,其內部實現是將對象放到一個數組裏。有界也就意味着,它不可以存儲無限多數量的元素。它有一個同一時間可以存儲元素數量的上限。你能夠在對其初始化的時候設定這個上限,但以後就沒法對這個上限進行修改了(譯者注:由於它是基於數組實現的,也就具備數組的特性:一旦初始化,大小就沒法修改)。
l DelayQueue:DelayQueue 對元素進行持有直到一個特定的延遲到期。注入其中的元素必須實現 java.util.concurrent.Delayed 接口。
l LinkedBlockingQueue:LinkedBlockingQueue 內部以一個鏈式結構(連接節點)對其元素進行存儲。若是須要的話,這一鏈式結構能夠選擇一個上限。若是沒有定義上限,將使用 Integer.MAX_VALUE 做爲上限。
l PriorityBlockingQueue:PriorityBlockingQueue 是一個無界的併發隊列。它使用了和類 java.util.PriorityQueue 同樣的排序規則。你沒法向這個隊列中插入 null 值。全部插入到 PriorityBlockingQueue 的元素必須實現 java.lang.Comparable 接口。所以該隊列中元素的排序就取決於你本身的 Comparable 實現。
l SynchronousQueue:SynchronousQueue 是一個特殊的隊列,它的內部同時只可以容納單個元素。若是該隊列已有一元素的話,試圖向隊列中插入一個新元素的線程將會阻塞,直到另外一個線程將該元素從隊列中抽走。一樣,若是該隊列爲空,試圖向隊列中抽取一個元素的線程將會阻塞,直到另外一個線程向隊列中插入了一條新的元素。據此,把這個類稱做一個隊列顯然是誇大其詞了。它更多像是一個匯合點。
Ø ArrayBlockingQueue阻塞隊列
ArrayBlockingQueue類圖
如上圖ArrayBlockingQueue內部有個數組items用來存放隊列元素,putindex下標標示入隊元素下標,takeIndex是出隊下標,count統計隊列元素個數,從定義可知道並無使用volatile修飾,這是由於訪問這些變量使用都是在鎖塊內,並不存在可見性問題。另外有個獨佔鎖lock用來對出入隊操做加鎖,這致使同時只有一個線程能夠訪問入隊出隊,另外notEmpty,notFull條件變量用來進行出入隊的同步。
另外構造函數必須傳入隊列大小參數,因此爲有界隊列,默認是Lock爲非公平鎖。
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
p
C. 併發(Collection)隊列-非阻塞隊列
Ø 非阻塞隊列
首先咱們要簡單的理解下什麼是非阻塞隊列:
與阻塞隊列相反,非阻塞隊列的執行並不會被阻塞,不管是消費者的出隊,仍是生產者的入隊。
在底層,非阻塞隊列使用的是CAS(compare andswap)來實現線程執行的非阻塞。
非阻塞隊列簡單操做
與阻塞隊列相同,非阻塞隊列中的經常使用方法,也是出隊和入隊。
入隊方法:
n add():底層調用offer();
n offer():Queue接口繼承下來的方法,實現隊列的入隊操做,不會阻礙線程的執行,插入成功返回true;
出隊方法:
n poll():移動頭結點指針,返回頭結點元素,並將頭結點元素出隊;隊列爲空,則返回null;
n peek():移動頭結點指針,返回頭結點元素,並不會將頭結點元素出隊;隊列爲空,則返回null;
Ø 非阻塞算法CAS
首先咱們須要瞭解悲觀鎖和樂觀鎖
悲觀鎖:假定併發環境是悲觀的,若是發生併發衝突,就會破壞一致性,因此要經過獨佔鎖完全禁止衝突發生。有一個經典比喻,「若是你不鎖門,那麼搗蛋鬼就回闖入並搞得一團糟」,因此「你只能一次打開門放進一我的,才能時刻盯緊他」。樂觀鎖:假定併發環境是樂觀的,即,雖然會有併發衝突,但衝突可發現且不會形成損害,因此,能夠不加任何保護,等發現併發衝突後再決定放棄操做仍是重試。可類比的比喻爲,「若是你不鎖門,那麼雖然搗蛋鬼會闖入,但他們一旦打算破壞你就能知道」,因此「你大能夠放進全部人,等發現他們想破壞的時候再作決定」。
一般認爲樂觀鎖的性能比悲觀所更高,特別是在某些複雜的場景。這主要因爲悲觀鎖在加鎖的同時,也會把某些不會形成破壞的操做保護起來;而樂觀鎖的競爭則只發生在最小的併發衝突處,若是用悲觀鎖來理解,就是「鎖的粒度最小」。但樂觀鎖的設計每每比較複雜,所以,複雜場景下仍是多用悲觀鎖。
首先保證正確性,有必要的話,再去追求性能。
CAS
樂觀鎖的實現每每須要硬件的支持,多數處理器都都實現了一個CAS指令,實現「Compare And Swap」的語義(這裏的swap是「換入」,也就是set),構成了基本的樂觀鎖。
CAS包含3個操做數:
n 須要讀寫的內存位置V
n 進行比較的值A
n 擬寫入的新值B
當且僅當位置V的值等於A時,CAS纔會經過原子方式用新值B來更新位置V的值;不然不會執行任何操做。不管位置V的值是否等於A,都將返回V原有的值。
一個有意思的事實是,「使用CAS控制併發」與「使用樂觀鎖」並不等價。CAS只是一種手段,既能夠實現樂觀鎖,也能夠實現悲觀鎖。樂觀、悲觀只是一種併發控制的策略。下文將分別用CAS實現悲觀鎖和樂觀鎖?
3) java.util.concurrent.lock包
待續...
(1)、繼承Thread類:但Thread本質上也是實現了Runnable接口的一個實例,它表明一個線程的實例,而且,啓動線程的惟一方法就是經過Thread類的start()實例方法。start()方法是一個native方法,它將啓動一個新線程,並執行run()方法。這種方式實現多線程很簡單,經過本身的類直接extend Thread,並複寫run()方法,就能夠啓動新線程並執行本身定義的run()方法。例如:繼承Thread類實現多線程,並在合適的地方啓動線程
1.public class MyThread extends Thread {
2. public void run() {
3. System.out.println("MyThread.run()");
4. }
5.}
6.MyThread myThread1 = new MyThread();
7.MyThread myThread2 = new MyThread();
8.myThread1.start();
9.myThread2.start();
(2)、實現Runnable接口的方式實現多線程,而且實例化Thread,傳入本身的Thread實例,調用run( )方法
1.public class MyThread implements Runnable {
2. public void run() {
3. System.out.println("MyThread.run()");
4. }
5.}
6.MyThread myThread = new MyThread();
7.Thread thread = new Thread(myThread);
8.thread.start();
(3)、使用ExecutorService、Callable、Future實現有返回結果的多線程:ExecutorService、Callable、Future這個對象實際上都是屬於Executor框架中的功能類。想要詳細瞭解Executor框架的能夠訪問http://www.javaeye.com/topic/366591,這裏面對該框架作了很詳細的解釋。返回結果的線程是在JDK1.5中引入的新特徵,確實很實用,有了這種特徵我就不須要再爲了獲得返回值而大費周折了,並且即使實現了也可能漏洞百出。可返回值的任務必須實現Callable接口,相似的,無返回值的任務必須Runnable接口。執行Callable任務後,能夠獲取一個Future的對象,在該對象上調用get就能夠獲取到Callable任務返回的Object了,再結合線程池接口ExecutorService就能夠實現傳說中有返回結果的多線程了。下面提供了一個完整的有返回結果的多線程測試例子,在JDK1.5下驗證過沒問題能夠直接使用。代碼以下:
1.import java.util.concurrent.*;
2.import java.util.Date;
3.import java.util.List;
4.import java.util.ArrayList;
5.
6./**
7.*有返回值的線程
8.*/
9.@SuppressWarnings("unchecked")
10.public class Test {
11.public static void main(String[] args) throws ExecutionException,
12. InterruptedException {
13.System.out.println("----程序開始運行----");
14. Date date1 = new Date();
15.
16. int taskSize = 5;
17.//建立一個線程池
18. ExecutorService pool = Executors.newFixedThreadPool(taskSize);
19.//建立多個有返回值的任務
20. List list = new ArrayList();
21. for (int i = 0; i < taskSize; i++) {
22. Callable c = new MyCallable(i + " ");
23.//執行任務並獲取Future對象
24. Future f = pool.submit(c);
25. // System.out.println(">>>" + f.get().toString());
26. list.add(f);
27. }
28.//關閉線程池
29. pool.shutdown();
30.
31.//獲取全部併發任務的運行結果
32. for (Future f : list) {
33.//從Future對象上獲取任務的返回值,並輸出到控制檯
34. System.out.println(">>>" + f.get().toString());
35. }
36.
37. Date date2 = new Date();
38.System.out.println("----程序結束運行----,程序運行時間【"
39.+ (date2.getTime() - date1.getTime()) + "毫秒】");
40.}
41.}
42.
43.class MyCallable implements Callable {
44.private String taskNum;
45.
46.MyCallable(String taskNum) {
47. this.taskNum = taskNum;
48.}
49.
50.public Object call() throws Exception {
51.System.out.println(">>>" + taskNum + "任務啓動");
52. Date dateTmp1 = new Date();
53. Thread.sleep(1000);
54. Date dateTmp2 = new Date();
55. long time = dateTmp2.getTime() - dateTmp1.getTime();
56.System.out.println(">>>" + taskNum + "任務終止");
57.return taskNum + "任務返回運行結果,當前任務時間【" + time + "毫秒】";
58.}
59.}
最大的不一樣是在等待時wait會釋放鎖,而sleep一直持有鎖。wait一般被用於線程間交互,sleep一般被用於暫停執行。
3. synchronized和volatile關鍵字的做用
一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾以後,那麼就具有了兩層語義:
1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的。 不可被緩存的
2)禁止進行指令重排序。
volatile本質是在告訴jvm當前變量在寄存器(工做內存)中的值是不肯定的,須要從主存中讀取;
synchronized則是鎖定當前變量,只有當前線程能夠訪問該變量,其餘線程被阻塞住。
1.volatile僅能使用在變量級別;
synchronized則能夠使用在變量、方法、和類級別的
2.volatile僅能實現變量的修改可見性,並不能保證原子性;
synchronized則能夠保證變量的修改可見性和原子性
3.volatile不會形成線程的阻塞;
synchronized可能會形成線程的阻塞。
1. public class Counter {
2. private volatile int count = 0;
3. public void inc(){
4. try {
5. Thread.sleep(3);
6. } catch (InterruptedException e) {
7. e.printStackTrace();
8. }
9. count++;
10. }
11. @Override
12. public String toString() {
13. return "[count=" + count + "]";
14. }
15. }
16. //---------------------------------華麗的分割線-----------------------------
17. public class VolatileTest {
18. public static void main(String[] args) {
19. final Counter counter = new Counter();
20. for(int i=0;i<1000;i++){
21. new Thread(new Runnable() {
22. @Override
23. public void run() {
24. counter.inc();
25. }
26. }).start();
27. }
28. System.out.println(counter);
29. }
30. }
上面的代碼執行完後輸出的結果肯定爲1000嗎?
答案是不必定,或者不等於1000。這是爲何嗎?
在java 的內存模型中每個線程運行時都有一個線程棧,線程棧保存了線程運行時候變量值信息。當線程訪問某一個對象時候值的時候,首先經過對象的引用找到對應在堆內存的變量的值,而後把堆內存變量的具體值load到線程本地內存中,創建一個變量副本,以後線程就再也不和對象在堆內存變量值有任何關係,而是直接修改副本變量的值,在修改完以後的某一個時刻(線程退出以前),自動把線程變量副本的值回寫到對象在堆中變量。這樣在堆中的對象的值就產生變化了。
也就是說上面主函數中開啓了1000個子線程,每一個線程都有一個變量副本,每一個線程修改變量只是臨時修改了本身的副本,當線程結束時再將修改的值寫入在主內存中,這樣就出現了線程安全問題。所以結果就不可能等於1000了,通常都會小於1000。
上面的解釋用一張圖表示以下:
(圖片來自網絡,非本人所繪)
線程池就是事先將多個線程對象放到一個容器中,當使用的時候就不用new線程而是直接去池中拿線程便可,節省了開闢子線程的時間,提升的代碼執行效率。
在JDK的java.util.concurrent.Executors中提供了生成多種線程池的靜態方法。
1. ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
2. ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);
3. ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);
4. ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
而後調用他們的execute方法便可。
6. 經常使用的線程池有哪些?(2017-11-23-wzz)
newSingleThreadExecutor:建立一個單線程的線程池,此線程池保證全部任務的執行順序按照任務的提交順序執行。
newFixedThreadPool:建立固定大小的線程池,每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。
newCachedThreadPool:建立一個可緩存的線程池,此線程池不會對線程池大小作限制,線程池大小徹底依賴於操做系統(或者說JVM)可以建立的最大線程大小。
newScheduledThreadPool:建立一個大小無限的線程池,此線程池支持定時以及週期性執行任務的需求。
newSingleThreadExecutor:建立一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求。
(若是問到了這樣的問題,能夠展開的說一下線程池如何用、線程池的好處、線程池的啓動策略)
合理利用線程池可以帶來三個好處。
第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。
第二:提升響應速度。當任務到達時,任務能夠不須要等到線程建立就能當即執行。
第三:提升線程的可管理性。線程是稀缺資源,若是無限制的建立,不只會消耗系統資源,還會下降系統的穩定性,使用線程池能夠進行統一的分配,調優和監控。 會根據項目的最大併發數 設計線程池
官方對線程池的執行過程描述以下:
26. /*
27. * Proceed in 3 steps:
28. *
29. * 1. If fewer than corePoolSize threads are running, try to
30. * start a new thread with the given command as its first
31. * task. The call to addWorker atomically checks runState and
32. * workerCount, and so prevents false alarms that would add
33. * threads when it shouldn't, by returning false.
34. *
35. * 2. If a task can be successfully queued, then we still need
36. * to double-check whether we should have added a thread
37. * (because existing ones died since last checking) or that
38. * the pool shut down since entry into this method. So we
39. * recheck state and if necessary roll back the enqueuing if
40. * stopped, or start a new thread if there are none.
41. *
42. * 3. If we cannot queue task, then we try to add a new
43. * thread. If it fails, we know we are shut down or saturated
44. * and so reject the task.
45. */
一、線程池剛建立時,裏面沒有一個線程。任務隊列是做爲參數傳進來的。不過,就算隊列裏面有任務,線程池也不會立刻執行它們。
二、當調用execute() 方法添加一個任務時,線程池會作以下判斷:
a. 若是正在運行的線程數量小於 corePoolSize,那麼立刻建立線程運行這個任務;
b. 若是正在運行的線程數量大於或等於 corePoolSize,那麼將這個任務放入隊列。
c. 若是這時候隊列滿了,並且正在運行的線程數量小於 maximumPoolSize,那麼仍是要建立線程運行這個任務;
d. 若是隊列滿了,並且正在運行的線程數量大於或等於 maximumPoolSize,那麼線程池會拋出異常,告訴調用者「我不能再接受任務了」。
三、當一個線程完成任務時,它會從隊列中取下一個任務來執行。
四、當一個線程無事可作,超過必定的時間(keepAliveTime)時,線程池會判斷,若是當前運行的線程數大於 corePoolSize,那麼這個線程就被停掉。因此線程池的全部任務完成後,它最終會收縮到 corePoolSize 的大小。
9. 如何控制某個方法容許併發訪問線程的個數?(2015-11-30)
1. package com.yange;
2.
3. import java.util.concurrent.Semaphore;
4. /**
5. *
6. * @author wzy 2015-11-30
7. *
8. */
9. public class SemaphoreTest {
10. /*
11. * permits the initial number of permits available. This value may be negative,
12. in which case releases must occur before any acquires will be granted.
13. fair true if this semaphore will guarantee first-in first-out granting of
14. permits under contention, else false
15. */
16. static Semaphore semaphore = new Semaphore(5,true);
17. public static void main(String[] args) {
18. for(int i=0;i<100;i++){
19. new Thread(new Runnable() {
20.
21. @Override
22. public void run() {
23. test();
24. }
25. }).start();
26. }
27.
28. }
29.
30. public static void test(){
31. try {
32. //申請一個請求
33. semaphore.acquire();
34. } catch (InterruptedException e1) {
35. e1.printStackTrace();
36. }
37. System.out.println(Thread.currentThread().getName()+"進來了");
38. try {
39. Thread.sleep(1000);
40. } catch (InterruptedException e) {
41. e.printStackTrace();
42. }
43. System.out.println(Thread.currentThread().getName()+"走了");
44. //釋放一個請求
45. semaphore.release();
46. }
47. }
能夠使用Semaphore控制,第16行的構造函數建立了一個Semaphore對象,而且初始化了5個信號。這樣的效果是控件test方法最多隻能有5個線程併發訪問,對於5個線程時就排隊等待,走一個來一下。第33行,請求一個信號(消費一個信號),若是信號被用完了則等待,第45行釋放一個信號,釋放的信號新的線程就能夠使用了。
10. 三個線程a、b、c併發運行,b,c須要a線程的數據怎麼實現(上海3期學員提供)
根據問題的描述,我將問題用如下代碼演示,ThreadA、ThreadB、ThreadC,ThreadA用於初始化數據num,只有當num初始化完成以後再讓ThreadB和ThreadC獲取到初始化後的變量num。
分析過程以下:
考慮到多線程的不肯定性,所以咱們不能確保ThreadA就必定先於ThreadB和ThreadC前執行,就算ThreadA先執行了,咱們也沒法保證ThreadA何時才能將變量num給初始化完成。所以咱們必須讓ThreadB和ThreadC去等待ThreadA完成任何後發出的消息。
如今須要解決兩個難題,一是讓ThreadB和ThreadC等待ThreadA先執行完,二是ThreadA執行完以後給ThreadB和ThreadC發送消息。
解決上面的難題我能想到的兩種方案,一是使用純Java API的Semaphore類來控制線程的等待和釋放,二是使用Android提供的Handler消息機制。
1. package com.example;
2. /**
3. *三個線程a、b、c併發運行,b,c須要a線程的數據怎麼實現(上海3期學員提供)
4. *
5. */
6. public class ThreadCommunication {
7. private static int num;//定義一個變量做爲數據
8.
9. public static void main(String[] args) {
10.
11. Thread threadA = new Thread(new Runnable() {
12.
13. @Override
14. public void run() {
15. try {
16. //模擬耗時操做以後初始化變量num
17. Thread.sleep(1000);
18. num = 1;
19.
20. } catch (InterruptedException e) {
21. e.printStackTrace();
22. }
23. }
24. });
25. Thread threadB = new Thread(new Runnable() {
26.
27. @Override
28. public void run() {
29. System.out.println(Thread.currentThread().getName()+"獲取到num的值爲:"+num);
30. }
31. });
32. Thread threadC = new Thread(new Runnable() {
33.
34. @Override
35. public void run() {
36. System.out.println(Thread.currentThread().getName()+"獲取到num的值爲:"+num);
37. }
38. });
39. //同時開啓3個線程
40. threadA.start();
41. threadB.start();
42. threadC.start();
43.
44. }
45. }
46.
解決方案一:
1. public class ThreadCommunication {
2. private static int num;
3. /**
4. *定義一個信號量,該類內部維持了多個線程鎖,能夠阻塞多個線程,釋放多個線程,
5. 線程的阻塞和釋放是經過permit概念來實現的
6. *線程經過semaphore.acquire()方法獲取permit,若是當前semaphore有permit則分配給該線程,
7. 若是沒有則阻塞該線程直到semaphore
8. *調用release()方法釋放permit。
9. *構造函數中參數:permit(容許) 個數,
10. */
11. private static Semaphore semaphore = new Semaphore(0);
12. public static void main(String[] args) {
13.
14. Thread threadA = new Thread(new Runnable() {
15.
16. @Override
17. public void run() {
18. try {
19. //模擬耗時操做以後初始化變量num
20. Thread.sleep(1000);
21. num = 1;
22. //初始化完參數後釋放兩個permit
23. semaphore.release(2);
24.
25. } catch (InterruptedException e) {
26. e.printStackTrace();
27. }
28. }
29. });
30. Thread threadB = new Thread(new Runnable() {
31.
32. @Override
33. public void run() {
34. try {
35. //獲取permit,若是semaphore沒有可用的permit則等待,若是有則消耗一個
36. semaphore.acquire();
37. } catch (InterruptedException e) {
38. e.printStackTrace();
39. }
40. System.out.println(Thread.currentThread().getName()+"獲取到num的值爲:"+num);
41. }
42. });
43. Thread threadC = new Thread(new Runnable() {
44.
45. @Override
46. public void run() {
47. try {
48. //獲取permit,若是semaphore沒有可用的permit則等待,若是有則消耗一個
49. semaphore.acquire();
50. } catch (InterruptedException e) {
51. e.printStackTrace();
52. }
53. System.out.println(Thread.currentThread().getName()+"獲取到num的值爲:"+num);
54. }
55. });
56. //同時開啓3個線程
57. threadA.start();
58. threadB.start();
59. threadC.start();
60.
61. }
62. }
11. 同一個類中的2個方法都加了同步鎖,多個線程能同時訪問同一個類中的這兩個方法嗎?(2017-2-24)
這個問題須要考慮到Lock與synchronized 兩種實現鎖的不一樣情形。由於這種狀況下使用Lock 和synchronized 會有大相徑庭的結果。Lock能夠讓等待鎖的線程響應中斷,Lock獲取鎖,以後須要釋放鎖。以下代碼,多個線程不可訪問同一個類中的2個加了Lock鎖的方法。
63. package com;
64. import java.util.concurrent.locks.Lock;
65. import java.util.concurrent.locks.ReentrantLock;
66. public class qq {
67.
68. private int count = 0;
69. private Lock lock = new ReentrantLock();//設置lock鎖
70. //方法1
71. public Runnable run1 = new Runnable(){
72. public void run() {
73. lock.lock(); //加鎖
74. while(count < 1000) {
75. try {
76. //打印是否執行該方法
77. System.out.println(Thread.currentThread().getName() + " run1: "+count++);
78. } catch (Exception e) {
79. e.printStackTrace();
80. }
81. }
82. }
83. lock.unlock();
84. }};
85. //方法2
86. public Runnable run2 = new Runnable(){
87. public void run() {
88. lock.lock();
89. while(count < 1000) {
90. try {
91. System.out.println(Thread.currentThread().getName() +
92. " run2: "+count++);
93. } catch (Exception e) {
94. e.printStackTrace();
95. }
96. }
97. lock.unlock();
98. }};
99.
100.
101.
102. public static void main(String[] args) throws InterruptedException {
103. qq t = new qq(); //建立一個對象
104. new Thread(t.run1).start();//獲取該對象的方法1
105.
106. new Thread(t.run2).start();//獲取該對象的方法2
107. }
108. }
結果是:
Thread-0 run1: 0
Thread-0 run1: 1
Thread-0 run1: 2
Thread-0 run1: 3
Thread-0 run1: 4
Thread-0 run1: 5
Thread-0 run1: 6
........
而synchronized卻不行,使用synchronized時,當咱們訪問同一個類對象的時候,是同一把鎖,因此能夠訪問該對象的其餘synchronized方法。代碼以下:
1.package com;
2.import java.util.concurrent.locks.Lock;
3.import java.util.concurrent.locks.ReentrantLock;
4.public class qq {
5. private int count = 0;
6. private Lock lock = new ReentrantLock();
7. public Runnable run1 = new Runnable(){
8. public void run() {
9.synchronized(this) { //設置關鍵字synchronized,以當前類爲鎖
10. while(count < 1000) {
11. try {
12.//打印是否執行該方法
13. System.out.println(Thread.currentThread().getName() + " run1: "+count++);
14. } catch (Exception e) {
15. e.printStackTrace();
16. }
17. }
18. }
19. }};
20. public Runnable run2 = new Runnable(){
21. public void run() {
22. synchronized(this) {
23. while(count < 1000) {
24. try {
25. System.out.println(Thread.currentThread().getName()
26. + " run2: "+count++);
27. } catch (Exception e) {
28. e.printStackTrace();
29. }
30. }
31. }
32. }};
33. public static void main(String[] args) throws InterruptedException {
34. qq t = new qq(); //建立一個對象
35.new Thread(t.run1).start(); //獲取該對象的方法1
36.new Thread(t.run2).start(); //獲取該對象的方法2
37. }
38.}
結果爲:
Thread-1 run2: 0
Thread-1 run2: 1
Thread-1 run2: 2
Thread-0 run1: 0
Thread-0 run1: 4 Thread-0 run1: 5 Thread-0 run1: 6
......
12. 什麼狀況下致使線程死鎖,遇到線程死鎖該怎麼解決?(2017-2-24)
11.1 死鎖的定義:所謂死鎖是指多個線程因競爭資源而形成的一種(互相等待),若無外力做用,這些進程都將沒法向前推動。
11.2 死鎖產生的必要條件:
互斥條件:線程要求對所分配的資源(如打印機)進行排他性控制,即在一段時間內某資源僅爲一個線程所佔有。此時如有其餘線程請求該資源,則請求線程只能等待。
不剝奪條件:線程所得到的資源在未使用完畢以前,不能被其餘線程強行奪走,即只能由得到該資源的線程本身來釋放(只能是主動釋放)。
請求和保持條件:線程已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其餘線程佔有,此時請求進程被阻塞,但對本身已得到的資源保持不放。
循環等待條件:存在一種線程資源的循環等待鏈,鏈中每個線程已得到的資源同時被鏈中下一個線程所請求。即存在一個處於等待狀態的線程集合{Pl, P2, ..., pn},其中Pi等待的資源被P(i+1)佔有(i=0, 1, ..., n-1),Pn等待的資源被P0佔有,如圖2-15所示。
11.3 產生死鎖的一個例子
1.package itheima.com;
2./**
3.*一個簡單的死鎖類
4.*當DeadLock類的對象flag==1時(td1),先鎖定o1,睡眠500毫秒
5.*而td1在睡眠的時候另外一個flag==0的對象(td2)線程啓動,先鎖定o2,睡眠500毫秒
6.* td1睡眠結束後須要鎖定o2才能繼續執行,而此時o2已被td2鎖定;
7.* td2睡眠結束後須要鎖定o1才能繼續執行,而此時o1已被td1鎖定;
8.* td一、td2相互等待,都須要獲得對方鎖定的資源才能繼續執行,從而死鎖。
9.*/
10.public class DeadLock implements Runnable {
11. public int flag = 1;
12.//靜態對象是類的全部對象共享的
13. private static Object o1 = new Object(), o2 = new Object();
14. public void run() {
15. System.out.println("flag=" + flag);
16. if (flag == 1) {
17. synchronized (o1) {
18. try {
19. Thread.sleep(500);
20. } catch (Exception e) {
21. e.printStackTrace();
22. }
23. synchronized (o2) {
24. System.out.println("1");
25. }
26. }
27. }
28. if (flag == 0) {
29. synchronized (o2) {
30. try {
31. Thread.sleep(500);
32. } catch (Exception e) {
33. e.printStackTrace();
34. }
35. synchronized (o1) {
36. System.out.println("0");
37. }
38. }
39. }
40. }
41. public static void main(String[] args) {
42. DeadLock td1 = new DeadLock();
43. DeadLock td2 = new DeadLock();
44. td1.flag = 1;
45. td2.flag = 0;
46.//td1,td2都處於可執行狀態,但JVM線程調度先執行哪一個線程是不肯定的。
47.//td2的run()可能在td1的run()以前運行
48. new Thread(td1).start();
49. new Thread(td2).start();
50. }
51.}
11.4 如何避免死鎖
在有些狀況下死鎖是能夠避免的。兩種用於避免死鎖的技術:
1)加鎖順序(線程按照必定的順序加鎖)
1.package itheima.com;
2.public class DeadLock {
3. public int flag = 1;
4.//靜態對象是類的全部對象共享的
5. private static Object o1 = new Object(), o2 = new Object();
6. public void money(int flag) {
7. this.flag=flag;
8. if( flag ==1){
9. synchronized (o1) {
10. try {
11. Thread.sleep(500);
12. } catch (Exception e) {
13. e.printStackTrace();
14. }
15. synchronized (o2) {
16.System.out.println("當前的線程是"+
17. Thread.currentThread().getName()+" "+"flag的值"+"1");
18. }
19. }
20. }
21. if(flag ==0){
22. synchronized (o2) {
23. try {
24. Thread.sleep(500);
25. } catch (Exception e) {
26. e.printStackTrace();
27. }
28. synchronized (o1) {
29.System.out.println("當前的線程是"+
30. Thread.currentThread().getName()+" "+"flag的值"+"0");
31. }
32. }
33. }
34. }
35.
36. public static void main(String[] args) {
37. final DeadLock td1 = new DeadLock();
38. final DeadLock td2 = new DeadLock();
39. td1.flag = 1;
40. td2.flag = 0;
41.//td1,td2都處於可執行狀態,但JVM線程調度先執行哪一個線程是不肯定的。
42.//td2的run()可能在td1的run()以前運行
43. final Thread t1=new Thread(new Runnable(){
44. public void run() {
45. td1.flag = 1;
46. td1.money(1);
47. }
48. });
49. t1.start();
50. Thread t2= new Thread(new Runnable(){
51. public void run() {
52. // TODO Auto-generated method stub
53. try {
54. //讓t2等待t1執行完
55. t1.join();//核心代碼,讓t1執行完後t2纔會執行
56. } catch (InterruptedException e) {
57. // TODO Auto-generated catch block
58. e.printStackTrace();
59. }
60. td2.flag = 0;
61. td1.money(0);
62. }
63. });
64. t2.start();
65. }
66.}
在t2的run() 中調用t1.join() 讓t2等待t1運行完畢再運行
結果:
當前的線程是Thread-0 flag的值1
當前的線程是Thread-1 flag的值0
2)加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖)
1. package itheima.com;
2.import java.util.concurrent.TimeUnit;
3.import java.util.concurrent.locks.Lock;
4.import java.util.concurrent.locks.ReentrantLock;
5.public class DeadLock {
6. public int flag = 1;
7.//靜態對象是類的全部對象共享的
8. private static Object o1 = new Object(), o2 = new Object();
9. public void money(int flag) throws InterruptedException {
10. this.flag=flag;
11. if( flag ==1){
12. synchronized (o1) {
13. Thread.sleep(500);
14. synchronized (o2) {
15.System.out.println("當前的線程是"+
16. Thread.currentThread().getName()+" "+"flag的值"+"1");
17. }
18. }
19. }
20. if(flag ==0){
21. synchronized (o2) {
22. Thread.sleep(500);
23. synchronized (o1) {
24.System.out.println("當前的線程是"+
25. Thread.currentThread().getName()+" "+"flag的值"+"0");
26. }
27. }
28. }
29. }
30.
31. public static void main(String[] args) {
32. final Lock lock = new ReentrantLock();
33. final DeadLock td1 = new DeadLock();
34. final DeadLock td2 = new DeadLock();
35. td1.flag = 1;
36. td2.flag = 0;
37.//td1,td2都處於可執行狀態,但JVM線程調度先執行哪一個線程是不肯定的。
38.//td2的run()可能在td1的run()以前運行
39.
40. final Thread t1=new Thread(new Runnable(){
41. public void run() {
42. // TODO Auto-generated method stub
43. String tName = Thread.currentThread().getName();
44.
45. td1.flag = 1;
46. try {
47.//獲取不到鎖,就等5秒,若是5秒後仍是獲取不到就返回false
48. if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
49. System.out.println(tName + "獲取到鎖!");
50. } else {
51. System.out.println(tName + "獲取不到鎖!");
52. return;
53. }
54. } catch (Exception e) {
55. e.printStackTrace();
56. }
57.
58. try {
59. td1.money(1);
60. } catch (Exception e) {
61. System.out.println(tName + "出錯了!!!");
62. } finally {
63.System.out.println("當前的線程是"+Thread.currentThread().getName()+"釋放鎖!!");
64. lock.unlock();
65. }
66. }
67. });
68. t1.start();
69. Thread t2= new Thread(new Runnable(){
70. public void run() {
71. String tName = Thread.currentThread().getName();
72. // TODO Auto-generated method stub
73. td1.flag = 1;
74. try {
75.//獲取不到鎖,就等5秒,若是5秒後仍是獲取不到就返回false
76. if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
77. System.out.println(tName + "獲取到鎖!");
78. } else {
79. System.out.println(tName + "獲取不到鎖!");
80. return;
81. }
82. } catch (Exception e) {
83. e.printStackTrace();
84. }
85. try {
86. td2.money(0);
87. } catch (Exception e) {
88. System.out.println(tName + "出錯了!!!");
89. } finally {
90.System.out.println("當前的線程是"+Thread.currentThread().getName()+"釋放鎖!!");
91. lock.unlock();
92. }
93. }
94. });
95. t2.start();
96. }
97.}
打印結果:
Thread-0獲取到鎖!
當前的線程是Thread-0 flag的值1
當前的線程是Thread-0釋放鎖!!
Thread-1獲取到鎖!
當前的線程是Thread-1 flag的值0
當前的線程是Thread-1釋放鎖!!
13. Java中多線程間的通訊怎麼實現?(2017-2-24)
線程通訊的方式:
1.共享變量
線程間通訊能夠經過發送信號,發送信號的一個簡單方式是在共享對象的變量裏設置信號值。線程A在一個同步塊裏設置boolean型成員變量hasDataToProcess爲true,線程B也在同步塊裏讀取hasDataToProcess這個成員變量。這個簡單的例子使用了一個持有信號的對象,並提供了set和get方法:
1.package itheima.com;
2.public class MySignal{
3.//共享的變量
4. private boolean hasDataToProcess=false;
5.//取值
6. public boolean getHasDataToProcess() {
7. return hasDataToProcess;
8. }
9.//存值
10. public void setHasDataToProcess(boolean hasDataToProcess) {
11. this.hasDataToProcess = hasDataToProcess;
12. }
13. public static void main(String[] args){
14.//同一個對象
15. final MySignal my=new MySignal();
16.//線程1設置hasDataToProcess值爲true
17. final Thread t1=new Thread(new Runnable(){
18. public void run() {
19. my.setHasDataToProcess(true);
20. }
21. });
22. t1.start();
23.//線程2取這個值hasDataToProcess
24. Thread t2=new Thread(new Runnable(){
25. public void run() {
26. try {
27.//等待線程1完成而後取值
28. t1.join();
29. } catch (InterruptedException e) {
30. e.printStackTrace();
31. }
32. my.getHasDataToProcess();
33. System.out.println("t1改變之後的值:" + my.isHasDataToProcess());
34. }
35. });
36. t2.start();
37.}
38.}
結果:
t1改變之後的值:true
2.wait/notify機制
以資源爲例,生產者生產一個資源,通知消費者就消費掉一個資源,生產者繼續生產資源,消費者消費資源,以此循環。代碼以下:
1.package itheima.com;
2.//資源類
3. class Resource{
4. private String name;
5. private int count=1;
6. private boolean flag=false;
7. public synchronized void set(String name){
8. //生產資源
9. while(flag) {
10. try{
11. //線程等待。消費者消費資源
12. wait();
13. }catch(Exception e){}
14. }
15. this.name=name+"---"+count++;
16.System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);
17. flag=true;
18.//喚醒等待中的消費者
19. this.notifyAll();
20. }
21. public synchronized void out(){
22. //消費資源
23. while(!flag) {
24. //線程等待,生產者生產資源
25. try{wait();}catch(Exception e){}
26. }
27.System.out.println(Thread.currentThread().getName()+"...消費者..."+this.name);
28. flag=false;
29.//喚醒生產者,生產資源
30. this.notifyAll();
31. }
32.}
33.//生產者
34. class Producer implements Runnable{
35. private Resource res;
36. Producer(Resource res){
37. this.res=res;
38. }
39. //生產者生產資源
40. public void run(){
41. while(true){
42. res.set("商品");
43. }
44. }
45. }
46.//消費者消費資源
47. class Consumer implements Runnable{
48. private Resource res;
49. Consumer(Resource res){
50. this.res=res;
51. }
52. public void run(){
53. while(true){
54. res.out();
55. }
56. }
57. }
58.public class ProducerConsumerDemo{
59. public static void main(String[] args){
60. Resource r=new Resource();
61. Producer pro=new Producer(r);
62. Consumer con=new Consumer(r);
63. Thread t1=new Thread(pro);
64. Thread t2=new Thread(con);
65. t1.start();
66. t2.start();
67. }
68.}
進程:具備必定獨立功能的程序關於某個數據集合上的一次運行活動,是操做系統進行資源分配和調度的一個獨立單位。
線程:是進程的一個實體,是cpu調度和分派的基本單位,是比進程更小的能夠獨立運行的基本單位。
特色:線程的劃分尺度小於進程,這使多線程程序擁有高併發性,進程在運行時各自內存單元相互獨立,線程之間內存共享,這使多線程編程能夠擁有更好的性能和用戶體驗
注意:多線程編程對於其它程序是不友好的,佔據大量cpu資源。
15. 請說出同步線程及線程調度相關的方法?(2017-11-23-wzz)
wait():使一個線程處於等待(阻塞)狀態,而且釋放所持有的對象的鎖;
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要處理InterruptedException異常;
notify():喚醒一個處於等待狀態的線程,固然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM肯定喚醒哪一個線程,並且與優先級無關;
notityAll():喚醒全部處於等待狀態的線程,該方法並非將對象的鎖給全部線程,而是讓它們競爭,只有得到鎖的線程才能進入就緒狀態;
注意:java 5 經過Lock接口提供了顯示的鎖機制,Lock接口中定義了加鎖(lock()方法)和解鎖(unLock()方法),加強了多線程編程的靈活性及對線程的協調
16. 啓動一個線程是調用run()方法仍是start()方法?(2017-11-23-wzz)
啓動一個線程是調用start()方法,使線程所表明的虛擬處理機處於可運行狀態,這意味着它能夠由JVM 調度並執行,這並不意味着線程就會當即運行。
run()方法是線程啓動後要進行回調(callback)的方法。
1. 靜態嵌套類(Static Nested Class) 和內部類(Inner Class)的不一樣?(2017-11-16-wl)
靜態嵌套類:Static Nested Class 是被聲明爲靜態(static)的內部類,它能夠不依賴於外部類實例被實例化。
內部類:須要在外部類實例化後才能實例化,其語法看起來挺詭異的。
2. 下面的代碼哪些地方會產生編譯錯誤?(2017-11-16-wl)
1.class Outer {
2.
3.class Inner {}
4.
5.public static void foo() { new Inner(); }
6.
7.public void bar() { new Inner(); }
8.
9.public static void main(String[] args) {
10.new Inner();
11.}
12.}
注意:Java 中非靜態內部類對象的建立要依賴其外部類對象,上面的面試題中 foo 和 main 方法都是靜態方法,靜態方法中沒有 this,也就是說沒有所謂的外部類對象,所以沒法建立內部類對象,若是要在靜態方法中建立內部類對象,能夠這樣作
1.new Outer().new Inner();
Java中的反射首先是可以獲取到Java中要反射類的字節碼,獲取字節碼有三種方法,1.Class.forName(className) 2.類名.class 3.this.getClass()。而後將字節碼中的方法,變量,構造函數等映射成相應的Method、Filed、Constructor等類,這些類提供了豐富的方法能夠被咱們所使用。
1. final List list = new ArrayList();
2.
3. List proxyInstance =
4. (List)Proxy.newProxyInstance(list.getClass().getClassLoader(),
5. list.getClass().getInterfaces(),
6. new InvocationHandler() {
7.
8. @Override
9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
10. return method.invoke(list, args);
11. }
12. });
13. proxyInstance.add("你好");
14. System.out.println(list);
2. 動靜態代理的區別,什麼場景使用?(2015-11-25)
靜態代理一般只代理一個類,動態代理是代理一個接口下的多個實現類。
靜態代理事先知道要代理的是什麼,而動態代理不知道要代理什麼東西,只有在運行時才知道。
動態代理是實現JDK裏的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的業務類必需要實現接口,經過Proxy裏的newProxyInstance獲得代理對象。
還有一種動態代理CGLIB,代理的是類,不須要業務類繼承接口,經過派生的子類來實現代理。經過在運行時,動態修改字節碼達到修改類的目的。
AOP編程就是基於動態代理實現的,好比著名的Spring框架、Hibernate框架等等都是動態代理的使用例子。
Java中通常認爲有23種設計模式,咱們不須要全部的都會,可是其中經常使用的幾種設計模式應該去掌握。下面列出了全部的設計模式。須要掌握的設計模式我單獨列出來了,固然能掌握的越多越好。
整體來講設計模式分爲三大類:
建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
最好理解的一種設計模式,分爲懶漢式和餓漢式。
餓漢式:
1. public class Singleton {
2. //直接建立對象
3. public static Singleton instance = new Singleton();
4.
5. //私有化構造函數
6. private Singleton() {
7. }
8.
9. //返回對象實例
10. public static Singleton getInstance() {
11. return instance;
12. }
13. }
懶漢式:
1. public class Singleton {
2. //聲明變量
3. private static volatile Singleton singleton = null;
4.
5. //私有構造函數
6. private Singleton() {
7. }
8.
9. //提供對外方法
10. public static Singleton getInstance() {
11. if (singleton == null) {
12. synchronized (Singleton.class) {
13. if (singleton == null) {
14. singleton = new Singleton();
15. }
16. }
17. }
18. return singleton;
19. }
20. }
工廠模式分爲工廠方法模式和抽象工廠模式。
工廠方法模式
工廠方法模式分爲三種:普通工廠模式,就是創建一個工廠類,對實現了同一接口的一些類進行實例的建立。
多個工廠方法模式,是對普通工廠方法模式的改進,在普通工廠方法模式中,若是傳遞的字符串出錯,則不能正確建立對象,而多個工廠方法模式是提供多個工廠方法,分別建立對象。
靜態工廠方法模式,將上面的多個工廠方法模式裏的方法置爲靜態的,不須要建立實例,直接調用便可。
普通工廠模式
1. public interface Sender {
2. public void Send();
3. }
4. public class MailSender implements Sender {
5.
6. @Override
7. public void Send() {
8. System.out.println("this is mail sender!");
9. }
10. }
11. public class SmsSender implements Sender {
12.
13. @Override
14. public void Send() {
15. System.out.println("this is sms sender!");
16. }
17. }
18. public class SendFactory {
19. public Sender produce(String type) {
20. if ("mail".equals(type)) {
21. return new MailSender();
22. } else if ("sms".equals(type)) {
23. return new SmsSender();
24. } else {
25. System.out.println("請輸入正確的類型!");
26. return null;
27. }
28. }
29. }
多個工廠方法模式
該模式是對普通工廠方法模式的改進,在普通工廠方法模式中,若是傳遞的字符串出錯,則不能正確建立對象,而多個工廠方法模式是提供多個工廠方法,分別建立對象。
1. public class SendFactory {
2. public Sender produceMail(){
3. return new MailSender();
4. }
5.
6. public Sender produceSms(){
7. return new SmsSender();
8. }
9. }
10.
11. public class FactoryTest {
12. public static void main(String[] args) {
13. SendFactory factory = new SendFactory();
14. Sender sender = factory.produceMail();
15. sender.send();
16. }
17. }
靜態工廠方法模式,將上面的多個工廠方法模式裏的方法置爲靜態的,不須要建立實例,直接調用便可。
1. public class SendFactory {
2. public static Sender produceMail(){
3. return new MailSender();
4. }
5.
6. public static Sender produceSms(){
7. return new SmsSender();
8. }
9. }
10.
11.
12. public class FactoryTest {
13. public static void main(String[] args) {
14. Sender sender = SendFactory.produceMail();
15. sender.send();
16. }
17. }
抽象工廠模式
工廠方法模式有一個問題就是,類的建立依賴工廠類,也就是說,若是想要拓展程序,必須對工廠類進行修改,這違背了閉包原則,因此,從設計角度考慮,有必定的問題,如何解決?就用到抽象工廠模式,建立多個工廠類,這樣一旦須要增長新的功能,直接增長新的工廠類就能夠了,不須要修改以前的代碼。
1. public interface Provider {
2. public Sender produce();
3. }
4. -------------------------------------------------------------------------------------
5. public interface Sender {
6. public void send();
7. }
8. -------------------------------------------------------------------------------------
9. public class MailSender implements Sender {
10.
11. @Override
12. public void send() {
13. System.out.println("this is mail sender!");
14. }
15. }
16. -------------------------------------------------------------------------------------
17. public class SmsSender implements Sender {
18.
19. @Override
20. public void send() {
21. System.out.println("this is sms sender!");
22. }
23. }
24. -------------------------------------------------------------------------------------
25. public class SendSmsFactory implements Provider {
26.
27. @Override
28. public Sender produce() {
29. return new SmsSender();
30. }
31. }
1. public class SendMailFactory implements Provider {
2.
3. @Override
4. public Sender produce() {
5. return new MailSender();
6. }
7. }
8. -------------------------------------------------------------------------------------
9. public class Test {
10. public static void main(String[] args) {
11. Provider provider = new SendMailFactory();
12. Sender sender = provider.produce();
13. sender.send();
14. }
15. }
工廠類模式提供的是建立單個類的模式,而建造者模式則是將各類產品集中起來進行管理,用來建立複合對象,所謂複合對象就是指某個類具備不一樣的屬性,其實建造者模式就是前面抽象工廠模式和最後的Test結合起來獲得的。
1. public class Builder {
2. private List list = new ArrayList();
3.
4. public void produceMailSender(int count) {
5. for (int i = 0; i < count; i++) {
6. list.add(new MailSender());
7. }
8. }
9.
10. public void produceSmsSender(int count) {
11. for (int i = 0; i < count; i++) {
12. list.add(new SmsSender());
13. }
14. }
15. }
1. public class Builder {
2. private List list = new ArrayList();
3.
4. public void produceMailSender(int count) {
5. for (int i = 0; i < count; i++) {
6. list.add(new MailSender());
7. }
8. }
9.
10. public void produceSmsSender(int count) {
11. for (int i = 0; i < count; i++) {
12. list.add(new SmsSender());
13. }
14. }
15. }
1. public class TestBuilder {
2. public static void main(String[] args) {
3. Builder builder = new Builder();
4. builder.produceMailSender(10);
5. }
6. }
適配器模式將某個類的接口轉換成客戶端指望的另外一個接口表示,目的是消除因爲接口不匹配所形成的類的兼容性問題。主要分爲三類:類的適配器模式、對象的適配器模式、接口的適配器模式。
類的適配器模式
1. public class Source {
2. public void method1() {
3. System.out.println("this is original method!");
4. }
5. }
6. -------------------------------------------------------------
7. public interface Targetable {
8. /*與原類中的方法相同 */
9. public void method1();
10. /*新類的方法 */
11. public void method2();
12. }
13. public class Adapter extends Source implements Targetable {
14. @Override
15. public void method2() {
16. System.out.println("this is the targetable method!");
17. }
18. }
19. public class AdapterTest {
20. public static void main(String[] args) {
21. Targetable target = new Adapter();
22. target.method1();
23. target.method2();
24. }
25. }
對象的適配器模式
基本思路和類的適配器模式相同,只是將Adapter類做修改,此次不繼承Source類,而是持有Source類的實例,以達到解決兼容性的問題。
1. public class Wrapper implements Targetable {
2. private Source source;
3.
4. public Wrapper(Source source) {
5. super();
6. this.source = source;
7. }
8.
9. @Override
10. public void method2() {
11. System.out.println("this is the targetable method!");
12. }
13.
14. @Override
15. public void method1() {
16. source.method1();
17. }
18. }
19. --------------------------------------------------------------
20. public class AdapterTest {
21.
22. public static void main(String[] args) {
23. Source source = new Source();
24. Targetable target = new Wrapper(source);
25. target.method1();
26. target.method2();
27. }
28. }
接口的適配器模式
接口的適配器是這樣的:有時咱們寫的一個接口中有多個抽象方法,當咱們寫該接口的實現類時,必須實現該接口的全部方法,這明顯有時比較浪費,由於並非全部的方法都是咱們須要的,有時只須要某一些,此處爲了解決這個問題,咱們引入了接口的適配器模式,藉助於一個抽象類,該抽象類實現了該接口,實現了全部的方法,而咱們不和原始的接口打交道,只和該抽象類取得聯繫,因此咱們寫一個類,繼承該抽象類,重寫咱們須要的方法就行。
顧名思義,裝飾模式就是給一個對象增長一些新的功能,並且是動態的,要求裝飾對象和被裝飾對象實現同一個接口,裝飾對象持有被裝飾對象的實例。
1. public interface Sourceable {
2. public void method();
3. }
4. ----------------------------------------------------
5. public class Source implements Sourceable {
6. @Override
7. public void method() {
8. System.out.println("the original method!");
9. }
10. }
11. ----------------------------------------------------
12. public class Decorator implements Sourceable {
13. private Sourceable source;
14. public Decorator(Sourceable source) {
15. super();
16. this.source = source;
17. }
18.
19. @Override
20. public void method() {
21. System.out.println("before decorator!");
22. source.method();
23. System.out.println("after decorator!");
24. }
25. }
26. ----------------------------------------------------
27. public class DecoratorTest {
28. public static void main(String[] args) {
29. Sourceable source = new Source();
30. Sourceable obj = new Decorator(source);
31. obj.method();
32. }
33. }
策略模式定義了一系列算法,並將每一個算法封裝起來,使他們能夠相互替換,且算法的變化不會影響到使用算法的客戶。須要設計一個接口,爲一系列實現類提供統一的方法,多個實現類實現該接口,設計一個抽象類(無關緊要,屬於輔助類),提供輔助函數。策略模式的決定權在用戶,系統自己提供不一樣算法的實現,新增或者刪除算法,對各類算法作封裝。所以,策略模式多用在算法決策系統中,外部用戶只須要決定用哪一個算法便可。
1. public interface ICalculator {
2. public int calculate(String exp);
3. }
4. ---------------------------------------------------------
5. public class Minus extends AbstractCalculator implements ICalculator {
6.
7. @Override
8. public int calculate(String exp) {
9. int arrayInt[] = split(exp, "-");
10. return arrayInt[0] - arrayInt[1];
11. }
12. }
13. ---------------------------------------------------------
14. public class Plus extends AbstractCalculator implements ICalculator {
15.
16. @Override
17. public int calculate(String exp) {
18. int arrayInt[] = split(exp, "\\+");
19. return arrayInt[0] + arrayInt[1];
20. }
21. }
22. --------------------------------------------------------
23. public class AbstractCalculator {
24. public int[] split(String exp, String opt) {
25. String array[] = exp.split(opt);
26. int arrayInt[] = new int[2];
27. arrayInt[0] = Integer.parseInt(array[0]);
28. arrayInt[1] = Integer.parseInt(array[1]);
29. return arrayInt;
30. }
31. }
1. public class StrategyTest {
2. public static void main(String[] args) {
3. String exp = "2+8";
4. ICalculator cal = new Plus();
5. int result = cal.calculate(exp);
6. System.out.println(result);
7. }
8. }
觀察者模式很好理解,相似於郵件訂閱和RSS訂閱,當咱們瀏覽一些博客或wiki時,常常會看到RSS圖標,就這的意思是,當你訂閱了該文章,若是後續有更新,會及時通知你。其實,簡單來說就一句話:當一個對象變化時,其它依賴該對象的對象都會收到通知,而且隨着變化!對象之間是一種一對多的關係。
1. public interface Observer {
2. public void update();
3. }
4.
5. public class Observer1 implements Observer {
6. @Override
7. public void update() {
8. System.out.println("observer1 has received!");
9. }
10. }
11.
12. public class Observer2 implements Observer {
13. @Override
14. public void update() {
15. System.out.println("observer2 has received!");
16. }
17. }
18.
19. public interface Subject {
20. /*增長觀察者*/
21. public void add(Observer observer);
22.
23. /*刪除觀察者*/
24. public void del(Observer observer);
25./*通知全部的觀察者*/
1. public void notifyObservers();
2.
3. /*自身的操做*/
4. public void operation();
5. }
6.
7. public abstract class AbstractSubject implements Subject {
8.
9. private Vector vector = new Vector();
10.
11. @Override
12. public void add(Observer observer) {
13. vector.add(observer);
14. }
15.
16. @Override
17. public void del(Observer observer) {
18. vector.remove(observer);
19. }
20.
21. @Override
22. public void notifyObservers() {
23. Enumeration enumo = vector.elements();
24. while (enumo.hasMoreElements()) {
25. enumo.nextElement().update();
26. }
27. }
28. }
29.
30. public class MySubject extends AbstractSubject {
31.
32. @Override
33. public void operation() {
34. System.out.println("update self!");
35. notifyObservers();
36. }
37. }
38.
39. public class ObserverTest {
40. public static void main(String[] args) {
41. Subject sub = new MySubject();
42. sub.add(new Observer1());
43. sub.add(new Observer2());
44. sub.operation();
45. }
46. }
理論上來說Sun公司只定義了垃圾回收機制規則而不侷限於其實現算法,所以不一樣廠商生產的虛擬機採用的算法也不盡相同。
GC(Garbage Collector)在回收對象前首先必須發現那些無用的對象,如何去發現定位這些無用的對象?經常使用的搜索算法以下:
1)引用計數器算法(廢棄)
引用計數器算法是給每一個對象設置一個計數器,當有地方引用這個對象的時候,計數器+1,當引用失效的時候,計數器-1,當計數器爲0的時候,JVM就認爲對象再也不被使用,是「垃圾」了。
引用計數器實現簡單,效率高;可是不能解決循環引用問問題(A對象引用B對象,B對象又引用A對象,可是A,B對象已不被任何其餘對象引用),同時每次計數器的增長和減小都帶來了不少額外的開銷,因此在JDK1.1以後,這個算法已經再也不使用了。
2)根搜索算法(使用)
根搜索算法是經過一些「GC Roots」對象做爲起點,從這些節點開始往下搜索,搜索經過的路徑成爲引用鏈(Reference Chain),當一個對象沒有被GC Roots的引用鏈鏈接的時候,說明這個對象是不可用的。
GC Roots對象包括:
a) 虛擬機棧(棧幀中的本地變量表)中的引用的對象。
b) 方法區域中的類靜態屬性引用的對象。
c) 方法區域中常量引用的對象。
d) 本地方法棧中JNI(Native方法)的引用的對象。
經過上面的算法搜索到無用對象以後,就是回收過程,回收算法以下:
1)標記—清除算法(Mark-Sweep)(DVM使用的算法)
標記—清除算法包括兩個階段:「標記」和「清除」。在標記階段,肯定全部要回收的對象,並作標記。清除階段緊隨標記階段,將標記階段肯定不可用的對象清除。標記—清除算法是基礎的收集算法,標記和清除階段的效率不高,並且清除後回產生大量的不連續空間,這樣當程序須要分配大內存對象時,可能沒法找到足夠的連續空間。
2)複製算法(Copying)
複製算法是把內存分紅大小相等的兩塊,每次使用其中一塊,當垃圾回收的時候,把存活的對象複製到另外一塊上,而後把這塊內存整個清理掉。複製算法實現簡單,運行效率高,可是因爲每次只能使用其中的一半,形成內存的利用率不高。如今的JVM用複製方法收集新生代,因爲新生代中大部分對象(98%)都是朝生夕死的,因此兩塊內存的比例不是1:1(大概是8:1)。
3)標記—整理算法(Mark-Compact)
標記—整理算法和標記—清除算法同樣,可是標記—整理算法不是把存活對象複製到另外一塊內存,而是把存活對象往內存的一端移動,而後直接回收邊界之外的內存。標記—整理算法提升了內存的利用率,而且它適合在收集對象存活時間較長的老年代。
4)分代收集(Generational Collection)
分代收集是根據對象的存活時間把內存分爲新生代和老年代,根據各個代對象的存活特色,每一個代採用不一樣的垃圾回收算法。新生代採用複製算法,老年代採用標記—整理算法。垃圾算法的實現涉及大量的程序細節,並且不一樣的虛擬機平臺實現的方法也各不相同。
a) Java內存模型
Java虛擬機將其管轄的內存大體分三個邏輯部分:方法區(Method Area)、Java棧和Java堆。
一、方法區是靜態分配的,編譯器將變量綁定在某個存儲位置上,並且這些綁定不會在運行時改變。
常數池,源代碼中的命名常量、String常量和static變量保存在方法區。
二、Java Stack是一個邏輯概念,特色是後進先出。一個棧的空間多是連續的,也多是不連續的。
最典型的Stack應用是方法的調用,Java虛擬機每調用一次方法就建立一個方法幀(frame),退出該方法則對應的 方法幀被彈出(pop)。棧中存儲的數據也是運行時肯定的。
三、Java堆分配(heap allocation)意味着以隨意的順序,在運行時進行存儲空間分配和收回的內存管理模型。
堆中存儲的數據經常是大小、數量和生命期在編譯時沒法肯定的。Java對象的內存老是在heap中分配。
咱們天天都在寫代碼,天天都在使用JVM的內存。
b) java內存分配 ---看到
一、基礎數據類型直接在棧空間分配;
二、方法的形式參數,返回值,直接在棧空間分配,當方法調用完成後從棧空間回收;
三、引用數據類型,須要用new來建立,既在棧空間分配一個地址空間,又在堆空間分配對象的類變量;
四、方法的引用參數,在棧空間分配一個地址空間,並指向堆空間的對象區,當方法調用完後從棧空間回收;
五、局部變量 new 出來時,在棧空間和堆空間中分配空間,當局部變量生命週期結束後,棧空間馬上被回收,堆空間區域等待GC回收;
六、方法調用時傳入的實際參數,先在棧空間分配,在方法調用完成後從棧空間釋放;
七、字符串常量在 DATA 區域分配 ,this 在堆空間分配;
八、數組既在棧空間分配數組名稱, 又在堆空間分配數組實際的大小!
Java中對象的引用分爲四種級別,這四種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。
強引用(StrongReference)
這個就很少說,咱們寫代碼每天在用的就是強引用。若是一個對象被被人擁有強引用,那麼垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足問題。
Java的對象是位於heap中的,heap中對象有強可及對象、軟可及對象、弱可及對象、虛可及對象和不可到達對象。應用的強弱順序是強、軟、弱、和虛。對於對象是屬於哪一種可及的對象,由他的最強的引用決定。以下代碼:
1. String abc=new String("abc"); //1
2. SoftReference softRef=new SoftReference(abc); //2
3. WeakReference weakRef = new WeakReference(abc); //3
4. abc=null; //4
5. softRef.clear();//5
第一行在heap堆中建立內容爲「abc」的對象,並創建abc到該對象的強引用,該對象是強可及的。
第二行和第三行分別創建對heap中對象的軟引用和弱引用,此時heap中的abc對象已經有3個引用,顯然此時abc對象還是強可及的。
第四行以後heap中對象再也不是強可及的,變成軟可及的。
第五行執行以後變成弱可及的。
軟引用(SoftReference)
若是一個對象只具備軟引用,那麼若是內存空間足夠,垃圾回收器就不會回收它,若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存。
軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
軟引用是主要用於內存敏感的高速緩存。在jvm報告內存不足以前會清除全部的軟引用,這樣以來gc就有可能收集軟可及的對象,可能解決內存吃緊問題,避免內存溢出。何時會被收集取決於gc的算法和gc運行時可用內存的大小。當gc決定要收集軟引用時執行如下過程,以上面的softRef爲例:
1 首先將softRef的referent(abc)設置爲null,再也不引用heap中的new String("abc")對象。
2 將heap中的new String("abc")對象設置爲可結束的(finalizable)。
3 當heap中的new String("abc")對象的finalize()方法被運行並且該對象佔用的內存被釋放, softRef被添加到它的ReferenceQueue(若是有的話)中。
注意:對ReferenceQueue軟引用和弱引用能夠有可無,可是虛引用必須有。
被Soft Reference 指到的對象,即便沒有任何 Direct Reference,也不會被清除。一直要到 JVM 內存不足且沒有Direct Reference 時纔會清除,SoftReference 是用來設計 object-cache 之用的。如此一來 SoftReference 不但能夠把對象 cache 起來,也不會形成內存不足的錯誤 (OutOfMemoryError)。
弱引用(WeakReference)
若是一個對象只具備弱引用,那該類就是無關緊要的對象,由於只要該對象被gc掃描到了隨時都會把它幹掉。弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。
弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
虛引用(PhantomReference)
"虛引用"顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收。虛引用主要用來跟蹤對象被垃圾回收的活動。
虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中。程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序若是發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。
創建虛引用以後經過get方法返回結果始終爲null,經過源代碼你會發現,虛引用通向會把引用的對象寫進referent,只是get方法返回結果爲null。先看一下和gc交互的過程再說一下他的做用。
1 不把referent設置爲null, 直接把heap中的new String("abc")對象設置爲可結束的(finalizable)。
2 與軟引用和弱引用不一樣, 先把PhantomRefrence對象添加到它的ReferenceQueue中.而後在釋放虛可及的對象。
12. heap和stack有什麼區別(2017-2-23)
從如下幾個方面闡述堆(heap)和棧(stack)的區別。
1. 申請方式
stack:由系統自動分配。例如,聲明在函數中一個局部變量 int b; 系統自動在棧中爲b開闢空間
heap:須要程序員本身申請,並指明大小,在c中malloc函數,對於Java須要手動new Object()的形式開闢
2. 申請後系統的響應
stack:只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,不然將報異常提示棧溢出。
heap:首先應該知道操做系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,
會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,而後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序。另外,因爲找到的堆結點的大小不必定正好等於申請的大小,系統會自動的將多餘的那部分從新放入空閒鏈表中。
3. 申請大小的限制
stack:棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就肯定的常數),若是申請的空間超過棧的剩餘空間時,將提示overflow。所以,能從棧得到的空間較小。
heap:堆是向高地址擴展的數據結構,是不連續的內存區域。這是因爲系統是用鏈表來存儲的空閒內存地址的,天然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。因而可知,堆得到的空間比較靈活,也比較大。
4. 申請效率的比較:
stack:由系統自動分配,速度較快。但程序員是沒法控制的。
heap:由new分配的內存,通常速度比較慢,並且容易產生內存碎片,不過用起來最方便。
5. heap和stack中的存儲內容
stack: 在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,而後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,而後是函數中的局部變量。注意靜態變量是不入棧的。
當本次函數調用結束後,局部變量先出棧,而後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
heap:通常是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。
6. 數據結構層面的區別
還有就是數據結構方面的堆和棧,這些都是不一樣的概念。這裏的堆實際上指的就是(知足堆性質的)優先隊列的一種數據結構,第1個元素有最高的優先權;棧實際上就是知足先進後出的性質的數學或數據結構。
雖然堆棧,堆棧的說法是連起來叫,可是他們仍是有很大區別的,連着叫只是因爲歷史的緣由。
7. 拓展知識(Java中堆棧的應用)
1). 棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不一樣,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
2). 棧的優點是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。另外,棧數據能夠共享,詳見第3點。堆的優點是能夠動態地分配內存大小,生存期也沒必要事先告訴編譯器,Java的垃圾回收器會自動收走這些再也不使用的數據。但缺點是,因爲要在運行時動態分配內存,存取速度較慢。
3). Java中的數據類型有兩種。
一種是基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意,並無string的基本類型)。這種類型的定義是經過諸如int a = 3; long b = 255L;的形式來定義的,稱爲自動變量(自動變量:只在定義它們的時候才建立,在定義它們的函數返回時系統回收變量所佔存儲空間。對這些變量存儲空間的分配和回收是由系統自動完成的。)。值得注意的是,自動變量存的是字面值,不是類的實例,即不是類的引用,這裏並無類的存在。如int a = 3; 這裏的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,因爲大小可知,生存期可知(這些字面值固定定義在某個程序塊裏面,程序塊退出後,字段值就消失了),出於追求速度的緣由,就存在於棧中。
另外,棧有一個很重要的特殊性,就是存在棧中的數據能夠共享。假設咱們同時定義
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中建立一個變量爲a的引用,而後查找有沒有字面值爲3的地址,沒找到,就開闢一個存放3這個字面值的地址,而後將a指向3的地址。接着處理int b = 3;在建立完b的引用變量後,因爲在棧中已經有3這個字面值,便將b直接指向3的地址。這樣,就出現了a與b同時均指向3的狀況。
特別注意的是,這種字面值的引用與類對象的引用不一樣。假定兩個類對象的引用同時指向一個對象,若是一個對象引用變量修改了這個對象的內部狀態,那麼另外一個對象引用變量也即刻反映出這個變化。相反,經過字面值的引用來修改其值,不會致使另外一個指向此字面值的引用的值也跟着改變的狀況。如上例,咱們定義完a與 b的值後,再令a=4;那麼,b不會等於4,仍是等於3。在編譯器內部,遇到a=4;時,它就會從新搜索棧中是否有4的字面值,若是沒有,從新開闢地址存放4的值;若是已經有了,則直接將a指向這個地址。所以a值的改變不會影響到b的值。
另外一種是包裝類數據,如Integer, String, Double等將相應的基本數據類型包裝起來的類。這些類數據所有存在於堆中,Java用new()語句來顯示地告訴編譯器,在運行時才根據須要動態建立,所以比較靈活,但缺點是要佔用更多的時間。
4).每一個JVM的線程都有本身的私有的棧空間,隨線程建立而建立,java的stack存放的是frames,java的stack和c的不一樣,只是存放本地變量,返回值和調用方法,不容許直接push和pop frames ,由於frames 多是有heap分配的,因此java的stack分配的內存不須要是連續的。java的heap是全部線程共享的,堆存放全部 runtime data ,裏面是全部的對象實例和數組,heap是JVM啓動時建立。
5). String是一個特殊的包裝類數據。便可以用String str = new String("abc");的形式來建立,也能夠用String str = "abc";的形式來建立(做爲對比,在JDK 5.0以前,你從未見過Integer i = 3;的表達式,由於類與字面值是不能通用的,除了String。而在JDK 5.0中,這種表達式是能夠的!由於編譯器在後臺進行Integer i = new Integer(3)的轉換)。前者是規範的類的建立過程,即在Java中,一切都是對象,而對象是類的實例,所有經過new()的形式來建立。那爲何在String str = "abc";中,並無經過new()來建立實例,是否是違反了上述原則?其實沒有。
5.1). 關於String str = "abc"的內部工做。Java內部將此語句轉化爲如下幾個步驟:
(1)先定義一個名爲str的對String類的對象引用變量:String str;
(2)在棧中查找有沒有存放值爲"abc"的地址,若是沒有,則開闢一個存放字面值爲"abc"的地址,接着建立一個新的String類的對象o,並將o 的字符串值指向這個地址,並且在棧中這個地址旁邊記下這個引用的對象o。若是已經有了值爲"abc"的地址,則查找對象o,並返回o的地址。
(3)將str指向對象o的地址。
值得注意的是,通常String類中字符串值都是直接存值的。但像String str = "abc";這種場合下,其字符串值倒是保存了一個指向存在棧中數據的引用!
爲了更好地說明這個問題,咱們能夠經過如下的幾個代碼進行驗證。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
注意,咱們這裏並不用str1.equals(str2);的方式,由於這將比較兩個字符串的值是否相等。==號,根據JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而咱們在這裏要看的是,str1與str2是否都指向了同一個對象。
結果說明,JVM建立了兩個引用str1和str2,但只建立了一個對象,並且兩個引用都指向了這個對象。
咱們再來更進一步,將以上代碼改爲:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
這就是說,賦值的變化致使了類對象引用的變化,str1指向了另一個新對象!而str2仍舊指向原來的對象。上例中,當咱們將str1的值改成"bcd"時,JVM發如今棧中沒有存放該值的地址,便開闢了這個地址,並建立了一個新的對象,其字符串的值指向這個地址。
事實上,String類被設計成爲不可改變(immutable)的類。若是你要改變其值,能夠,但JVM在運行時根據新值悄悄建立了一個新對象,而後將這個對象的地址返回給原來類的引用。這個建立過程雖然說是徹底自動進行的,但它畢竟佔用了更多的時間。在對時間要求比較敏感的環境中,會帶有必定的不良影響。
再修改原來代碼:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
String str3 = str1;
System.out.println(str3); //bcd
String str4 = "bcd";
System.out.println(str1 == str4); //true
str3 這個對象的引用直接指向str1所指向的對象(注意,str3並無建立新對象)。當str1改完其值後,再建立一個String的引用str4,並指向因str1修改值而建立的新的對象。能夠發現,這回str4也沒有建立新的對象,從而再次實現棧中數據的共享。
咱們再接着看如下的代碼。
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
建立了兩個引用。建立了兩個對象。兩個引用分別指向不一樣的兩個對象。
以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中建立,並且其字符串是單獨存值的,即便與棧中的數據相同,也不會與棧中的數據共享。
6). 數據類型包裝類的值不可修改。不只僅是String類的值不可修改,全部的數據類型包裝類都不能更改其內部的值。
7). 結論與建議:
(1)咱們在使用諸如String str = "abc";的格式定義類時,老是想固然地認爲,咱們建立了String類的對象str。擔憂陷阱!對象可能並無被建立!惟一能夠確定的是,指向 String類的引用被建立了。至於這個引用究竟是否指向了一個新的對象,必須根據上下文來考慮,除非你經過new()方法來顯要地建立一個新的對象。所以,更爲準確的說法是,咱們建立了一個指向String類的對象的引用變量str,這個對象引用變量指向了某個值爲"abc"的String類。清醒地認識到這一點對排除程序中難以發現的bug是頗有幫助的。
(2)使用String str = "abc";的方式,能夠在必定程度上提升程序的運行速度,由於JVM會自動根據棧中數據的實際狀況來決定是否有必要建立新對象。而對於String str = new String("abc");的代碼,則一律在堆中建立新對象,而無論其字符串值是否相等,是否有必要建立新對象,從而加劇了程序的負擔。這個思想應該是享元模式的思想,但JDK的內部在這裏實現是否應用了這個模式,不得而知。
(3)當比較包裝類裏面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==。
(4)因爲String類的immutable性質,當String變量須要常常變換其值時,應該考慮使用StringBuffer類,以提升程序效率。
若是java不能成功分配heap的空間,將拋出OutOfMemoryError。
13. 解釋內存中的棧 (stack) 、堆 (heap) 和方法區 (method area) 的用法(2017-11-12-wl)
一般咱們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用JVM中的棧空間;而經過new關鍵字和構造器建立的對象則放在堆空間,堆是垃圾收集器管理的主要區域,因爲如今的垃圾收集器都採用分代收集算法,因此堆空間還能夠細分爲新生代和老生代,再具體一點能夠分爲 Eden、Survivor(又可分爲 From Survivor 和 To Survivor)、Tenured;方法區和堆都是各個線程共享的內存區域,用於存儲已經被JVM加載的類信息、常量、靜態變量、JIT 編譯器編譯後的代碼等數據;程序中的字面量(literal)如直接書寫的 100、"hello"和常量都是放在常量池中,常量池是方法區的一部分。棧空間操做起來最快可是棧很小,一般大量的對象都是放在堆空間,棧和堆的大小均可以經過 JVM 的啓動參數來進行調整,棧空間用光了會引起 StackOverflowError,而堆和常量池空間不足則會引起 OutOfMemoryError。
String str = new String("hello");
上面的語句中變量str 放在棧上,用 new 建立出來的字符串對象放在堆上,而"hello"這個字面量是放在方法區的。
一、根類加載器(Bootstrap) --C++寫的 ,看不到源碼
2、擴展類加載器(Extension) --加載位置 :jre\lib\ext中
三、系統(應用)類加載器(System\App) --加載位置 :classpath中 加載第三方jar包
四、自定義加載器(必須繼承ClassLoader)
1)建立類的實例,也就是new一個對象
2)訪問某個類或接口的靜態變量,或者對該靜態變量賦值
3)調用類的靜態方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一個類的子類(會首先初始化子類的父類)
6)JVM啓動時標明的啓動類,即文件名和類名相同的那個類
只有這6中狀況纔會致使類的類的初始化。
類的初始化步驟:
1)若是這個類尚未被加載和連接,那先進行加載和連接
2)假如這個類存在直接父類,而且這個類尚未被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用於接口)
3)加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。
3. Java類加載體系之ClassLoader雙親委託機制 (2017-2-24)
java是一種類型安全的語言,它有四類稱爲安全沙箱機制的安全機制來保證語言的安全性,這四類安全沙箱分別是:
1) 類加載體系
2) .class文件檢驗器
主要講解類的加載體系:
java程序中的 .java文件編譯完會生成 .class文件,而 .class文件就是經過被稱爲類加載器的ClassLoader加載的,而ClassLoder在加載過程當中會使用「雙親委派機制」來加載 .class文件,先上圖:
BootStrapClassLoader:啓動類加載器,該ClassLoader是jvm在啓動時建立的,用於加載 $JAVA_HOME$/jre/lib下面的類庫(或者經過參數-Xbootclasspath指定)。因爲啓動類加載器涉及到虛擬機本地實現細節,開發者沒法直接獲取到啓動類加載器的引用,因此不能直接經過引用進行操做。
ExtClassLoader:擴展類加載器,該ClassLoader是在sun.misc.Launcher裏做爲一個內部類ExtClassLoader定義的(即 sun.misc.Launcher$ExtClassLoader),ExtClassLoader會加載 $JAVA_HOME/jre/lib/ext下的類庫(或者經過參數-Djava.ext.dirs指定)。
AppClassLoader:應用程序類加載器,該ClassLoader一樣是在sun.misc.Launcher裏做爲一個內部類AppClassLoader定義的(即 sun.misc.Launcher$AppClassLoader),AppClassLoader會加載java環境變量CLASSPATH所指定的路徑下的類庫,而CLASSPATH所指定的路徑能夠經過System.getProperty("java.class.path")獲取;固然,該變量也能夠覆蓋,能夠使用參數-cp,例如:java -cp 路徑 (能夠指定要執行的class目錄)。
CustomClassLoader:自定義類加載器,該ClassLoader是指咱們自定義的ClassLoader,好比tomcat的StandardClassLoader屬於這一類;固然,大部分狀況下使用AppClassLoader就足夠了。
前面談到了ClassLoader的幾類加載器,而ClassLoader使用雙親委派機制來加載class文件的。ClassLoader的雙親委派機制是這樣的(這裏先忽略掉自定義類加載器CustomClassLoader):
1)當AppClassLoader加載一個class時,它首先不會本身去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成。
2)當ExtClassLoader加載一個class時,它首先也不會本身去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。
3)若是BootStrapClassLoader加載失敗(例如在$JAVA_HOME$/jre/lib裏未查找到該class),會使用ExtClassLoader來嘗試加載;
4)若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,若是AppClassLoader也加載失敗,則會報出異常ClassNotFoundException。
下面貼下ClassLoader的loadClass(String name, boolean resolve)的源碼:
1.protected synchronized Class loadClass(String name, boolean resolve)
2. throws ClassNotFoundException {
3. // 首先找緩存是否有class
4. Class c = findLoadedClass(name);
5. if (c == null) {
6.//沒有判斷有沒有父類
7. try {
8. if (parent != null) {
9.//有的話,用父類遞歸獲取class
10. c = parent.loadClass(name, false);
11. } else {
12.//沒有父類。經過這個方法來加載
13. c = findBootstrapClassOrNull(name);
14. }
15. } catch (ClassNotFoundException e) {
16. // ClassNotFoundException thrown if class not found
17. // from the non-null parent class loader
18. }
19. if (c == null) {
20. // 若是仍是沒有找到,調用findClass(name)去找這個類
21. c = findClass(name);
22. }
23. }
24. if (resolve) {
25. resolveClass(c);
26. }
27. return c;
28. }
代碼很明朗:首先找緩存(findLoadedClass),沒有的話就判斷有沒有parent,有的話就用parent來遞歸的loadClass,然而ExtClassLoader並無設置parent,則會經過findBootstrapClassOrNull來加載class,而findBootstrapClassOrNull則會經過JNI方法」private native Class findBootstrapClass(String name)「來使用BootStrapClassLoader來加載class。
而後若是parent未找到class,則會調用findClass來加載class,findClass是一個protected的空方法,能夠覆蓋它以便自定義class加載過程。
另外,雖然ClassLoader加載類是使用loadClass方法,可是鼓勵用 ClassLoader 的子類重寫 findClass(String),而不是重寫loadClass,這樣就不會覆蓋了類加載默認的雙親委派機制。
雙親委派託機制爲何安全
舉個例子,ClassLoader加載的class文件來源不少,好比編譯器編譯生成的class、或者網絡下載的字節碼。而一些來源的class文件是不可靠的,好比我能夠自定義一個java.lang.Integer類來覆蓋jdk中默認的Integer類,例以下面這樣:
1. package java.lang;
2. public class Integer {
3. public Integer(int value) {
4. System.exit(0);
5. }
6. }
初始化這個Integer的構造器是會退出JVM,破壞應用程序的正常進行,若是使用雙親委派機制的話該Integer類永遠不會被調用,覺得委託BootStrapClassLoader加載後會加載JDK中的Integer類而不會加載自定義的這個,能夠看下下面這測試個用例:
1.public static void main(String... args) {
2. Integer i = new Integer(1);
3. System.err.println(i);
4. }
執行時JVM並未在new Integer(1)時退出,說明未使用自定義的Integer,因而就保證了安全性。
4. 描述一下JVM加載class (2017-11-15-wl)
JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java 中的類加載器是一個重要的Java 運行時系統組件,它負責在運行時查找和裝入類文件中的類。
因爲Java的跨平臺性,通過編譯的 Java 源程序並非一個可執行程序,而是一個或多個類文件。當Java程序須要使用某個類時,JVM會確保這個類已經被加載、鏈接(驗證、準備和解析)和初始化。類的加載是指把類的.class 文件中的數據讀入到內存中,一般是建立一個字節數組讀入.class 文件,而後產生與所加載類對應的 Class 對象。加載完成後,Class 對象還不完整,因此此時的類還不可用。當類被加載後就進入鏈接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後 JVM 對類進行初始化,包括:
若是類存在直接的父類而且這個類尚未被初始化,那麼就先初始化父類;
若是類中存在初始化語句,就依次執行這些初始化語句。類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader 的子類)。
從Java 2(JDK 1.2)開始,類加載過程採起了父親委託機制(PDM)。PDM 更好的保證了 Java 平臺的安全性,在該機制中,JVM 自帶的 Bootstrap是根加載器,其餘的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM不會向Java 程序提供對 Bootstrap 的引用。
下面是關於幾個類加載器的說明:
• Bootstrap:通常用本地代碼實現,負責加載 JVM 基礎核心類庫(rt.jar);
• Extension:從 java.ext.dirs 系統屬性所指定的目錄中加載類庫,它的父加載器是 Bootstrap;
• System:又叫應用類加載器,其父類是 Extension。它是應用最普遍的類加載器。它從環境變量classpath
或者系統屬性java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。
5. 得到一個類對象有哪些方式?(2017-11-23-wzz)
類型.class,例如:String.class
對象.getClass(),例如:」hello」.getClass()
Class.forName(),例如:Class.forName(「java.lang.String」)
1. 既然有GC機制,爲何還會有內存泄露的狀況(2017-11-16-wl)
理論上Java 由於有垃圾回收機制(GC)不會存在內存泄露問題(這也是 Java 被普遍使用於服務器端編程的一個重要緣由)。然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被 GC 回收,所以也會致使內存泄露的發生。
例如hibernate 的 Session(一級緩存)中的對象屬於持久態,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,若是不及時關閉(close)或清空(flush)一級緩存就可能致使內存泄露。
1. Java中爲何會有GC機制呢?(2017-11-16-wl)
Java中爲何會有GC機制呢?
· 安全性考慮;-- for security.
· 減小內存泄露;-- erase memory leak in some degree.
· 減小程序員工做量。-- Programmers don't worry about memory releasing.
2. 對於Java的GC哪些內存須要回收(2017-11-16-wl)
內存運行時JVM會有一個運行時數據區來管理內存。它主要包括5大部分:程序計數器(Program Counter Register)、虛擬機棧(VM Stack)、本地方法棧(Native Method Stack)、方法區(Method Area)、堆(Heap).
而其中程序計數器、虛擬機棧、本地方法棧是每一個線程私有的內存空間,隨線程而生,隨線程而亡。例如棧中每個棧幀中分配多少內存基本上在類結構肯定是哪一個時就已知了,所以這3個區域的內存分配和回收都是肯定的,無需考慮內存回收的問題。
但方法區和堆就不一樣了,一個接口的多個實現類須要的內存可能不同,咱們只有在程序運行期間纔會知道會建立哪些對象,這部份內存的分配和回收都是動態的,GC主要關注的是這部份內存。
總而言之,GC主要進行回收的內存是JVM中的方法區和堆;
3. Java的GC何時回收垃圾(2017-11-16-wl)
在面試中常常會碰到這樣一個問題(事實上筆者也碰到過):如何判斷一個對象已經死去?
很容易想到的一個答案是:對一個對象添加引用計數器。每當有地方引用它時,計數器值加1;當引用失效時,計數器值減1.而當計數器的值爲0時這個對象就不會再被使用,判斷爲已死。是否是簡單又直觀。然而,很遺憾。這種作法是錯誤的!爲何是錯的呢?事實上,用引用計數法確實在大部分狀況下是一個不錯的解決方案,而在實際的應用中也有很多案例,但它卻沒法解決對象之間的循環引用問題。好比對象A中有一個字段指向了對象B,而對象B中也有一個字段指向了對象A,而事實上他們倆都再也不使用,但計數器的值永遠都不可能爲0,也就不會被回收,而後就發生了內存泄露。
因此,正確的作法應該是怎樣呢? 在Java,C#等語言中,比較主流的斷定一個對象已死的方法是:可達性分析(Reachability Analysis).全部生成的對象都是一個稱爲"GC Roots"的根的子樹。從GC Roots開始向下搜索,搜索所通過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈能夠到達時,就稱這個對象是不可達的(不可引用的),也就是能夠被GC回收了。
不管是引用計數器仍是可達性分析,斷定對象是否存活都與引用有關!那麼,如何定義對象的引用呢?
咱們但願給出這樣一類描述:當內存空間還夠時,可以保存在內存中;若是進行了垃圾回收以後內存空間仍舊很是緊張,則能夠拋棄這些對象。因此根據不一樣的需求,給出以下四種引用,根據引用類型的不一樣,GC回收時也會有不一樣的操做:
1)強引用(Strong Reference):Object obj = new Object();只要強引用還存在,GC永遠不會回收掉被引用的對象。
2)軟引用(Soft Reference):描述一些還有用但非必需的對象。在系統將會發生內存溢出以前,會把這些對象列入回收範圍進行二次回收(即系統將會發生內存溢出了,纔會對他們進行回收。)
弱引用(Weak Reference):程度比軟引用還要弱一些。這些對象只能生存到下次GC以前。當GC工做時,不管內存是否足夠都會將其回收(即只要進行GC,就會對他們進行回收。)
虛引用(Phantom Reference):一個對象是否存在虛引用,徹底不會對其生存時間構成影響。
關於方法區中須要回收的是一些廢棄的常量和無用的類。
8、在開發中遇到過內存溢出麼?緣由有哪些?解決方法有哪些?(2017-11-23-gxb)
引發內存溢出的緣由有不少種,常見的有如下幾種:
資源須要及時關閉 例如session
1.內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
2.集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
3.代碼中存在死循環或循環產生過多重複的對象實體;
4.使用的第三方軟件中的BUG;
5.啓動參數內存值設定的太小;
內存溢出的解決方案:
第一步,修改JVM啓動參數,直接增長內存。(-Xms,-Xmx參數必定不要忘記加。)
第二步,檢查錯誤日誌,查看「OutOfMemory」錯誤前是否有其它異常或錯誤。
第三步,對代碼進行走查和分析,找出可能發生內存溢出的位置。
重點排查如下幾點:
1.檢查對數據庫查詢中,是否有一次得到所有數據的查詢。通常來講,若是一次取十萬條記錄到內存,就可能引發內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線後,數據庫中數據多了,一次查詢就有可能引發內存溢出。所以對於數據庫查詢儘可能採用分頁的方式查詢。
2.檢查代碼中是否有死循環或遞歸調用。
3.檢查是否有大循環重複產生新對象實體。
4.檢查對數據庫查詢中,是否有一次得到所有數據的查詢。通常來講,若是一次取十萬條記錄到內存,就可能引發內存溢出。這個問題比較隱蔽,在上線前,數據庫中 數據較少,不容易出問題,上線後,數據庫中數據多了,一次查詢就有可能引發內存溢出。所以對於數據庫查詢儘可能採用分頁的方式查詢。
5.檢查List、MAP等集合對象是否有使用完後,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。
第四步,使用內存查看工具動態查看內存使用狀況。
1、JDBC技術
1. 說下原生jdbc操做數據庫流程?(2017-11-25-wzz)
第一步:Class.forName()加載數據庫鏈接驅動;
第二步:DriverManager.getConnection()獲取數據鏈接對象;
第三步:根據SQL獲取sql會話對象,有2種方式 Statement、PreparedStatement ;
第四步:執行SQL處理結果集,執行SQL前若是有參數值就設置參數值setXXX();
第五步:關閉結果集、關閉會話、關閉鏈接。
詳細代碼請看(封裝):http://blog.csdn.net/qq_29542611/article/details/52426006
2. 什麼要使用PreparedStatement?(2017-11-25-wzz)
一、 PreparedStatement接口繼承Statement, PreparedStatement 實例包含已編譯的 SQL 語句,因此其執行速度要快於 Statement 對象。
二、做爲 Statement 的子類,PreparedStatement 繼承了 Statement 的全部功能。三種方法 execute、 executeQuery 和 executeUpdate 已被更改以使之再也不須要參數
三、在JDBC應用中,在任什麼時候候都不要使用Statement,緣由以下:
1、代碼的可讀性和可維護性.Statement須要不斷地拼接,而PreparedStatement不會。
2、PreparedStatement盡最大可能提升性能.DB有緩存機制,相同的預編譯語句再次被調用不會再次須要編譯。
3、最重要的一點是極大地提升了安全性.Statement容易被SQL注入,而PreparedStatementc傳入的內容不會和sql語句發生任何匹配關係。
3. 關係數據庫中鏈接池的機制是什麼?(2017-12-6-lyq)
前提:爲數據庫鏈接創建一個緩衝池。
1:從鏈接池獲取或建立可用鏈接
2:使用完畢以後,把鏈接返回給鏈接池
3:在系統關閉前,斷開全部鏈接並釋放鏈接佔用的系統資源
4:可以處理無效鏈接,限制鏈接池中的鏈接總數不低於或者不超過某個限定值。
其中有幾個概念須要你們理解:
最小鏈接數是鏈接池一直保持的數據鏈接。若是應用程序對數據庫鏈接的使用量不大,將會有大量的數據庫鏈接資源被浪費掉。
最大鏈接數是鏈接池能申請的最大鏈接數。若是數據鏈接請求超過此數,後面的數據鏈接請求將被加入到等待隊列中,這會影響以後的數據庫操做。
若是最小鏈接數與最大鏈接數相差太大,那麼,最早的鏈接請求將會獲利,以後超過最小鏈接數量的鏈接請求等價於創建一個新的數據庫鏈接。不過,這些大於最小鏈接數的數據庫鏈接在使用完不會立刻被釋放,它將被放到鏈接池中等待重複使用或是空閒超時後被釋放。
上面的解釋,能夠這樣理解:數據庫池鏈接數量一直保持一個很多於最小鏈接數的數量,當數量不夠時,數據庫會建立一些鏈接,直到一個最大鏈接數,以後鏈接數據庫就會等待。
3、Http協議
1. http的長鏈接和短鏈接(2017-11-14-lyq)
HTTP協議有HTTP/1.0版本和HTTP/1.1版本。HTTP1.1默認保持長鏈接(HTTP persistent connection,也翻譯爲持久鏈接),數據傳輸完成了保持TCP鏈接不斷開(不發RST包、不四次握手),等待在同域名下繼續用這個通道傳輸數據;相反的就是短鏈接。
在HTTP/1.0中,默認使用的是短鏈接。也就是說,瀏覽器和服務器每進行一次HTTP操做,就創建一次鏈接,任務結束就中斷鏈接。從HTTP/1.1起,默認使用的是長鏈接,用以保持鏈接特性。
2. HTTP/1.1與HTTP/1.0的區別(2017-11-21-wzy)
參考原文:http://blog.csdn.net/forgotaboutgirl/article/details/6936982
1 可擴展性
a) HTTP/1.1 在消息中增長版本號,用於兼容性判斷。
b) HTTP/1.1增長了OPTIONS方法,它容許客戶端獲取一個服務器支持的方法列表。
c) 爲了與將來的協議規範兼容,HTTP/1.1在請求消息中包含了Upgrade頭域,經過該頭域,客戶端能夠讓服務器知道它可以支持的其它備用通訊協議,服務器能夠據此進行協議切換,使用備用協議與客戶端進行通訊。
2 緩存
在HTTP/1.0中,使用Expire頭域來判斷資源的fresh或stale,並使用條件請求(conditional request)來判斷資源是否仍有效。HTTP/1.1在1.0的基礎上加入了一些cache的新特性,當緩存對象的Age超過Expire時變爲stale對象,cache不須要直接拋棄stale對象,而是與源服務器進行從新激活(revalidation)。
3 帶寬優化
HTTP/1.0中,存在一些浪費帶寬的現象,例如客戶端只是須要某個對象的一部分,而服務器卻將整個對象送過來了。例如,客戶端只須要顯示一個文檔的部份內容,又好比下載大文件時須要支持斷點續傳功能,而不是在發生斷連後不得不從新下載完整的包。
HTTP/1.1中在請求消息中引入了range頭域,它容許只請求資源的某個部分。在響應消息中Content-Range頭域聲明瞭返回的這部分對象的偏移值和長度。若是服務器相應地返回了對象所請求範圍的內容,則響應碼爲206(Partial Content),它能夠防止Cache將響應誤覺得是完整的一個對象。
另一種狀況是請求消息中若是包含比較大的實體內容,但不肯定服務器是否可以接收該請求(如是否有權限),此時若貿然發出帶實體的請求,若是被拒絕也會浪費帶寬。
HTTP/1.1加入了一個新的狀態碼100(Continue)。客戶端事先發送一個只帶頭域的請求,若是服務器由於權限拒絕了請求,就回送響應碼401(Unauthorized);若是服務器接收此請求就回送響應碼100,客戶端就能夠繼續發送帶實體的完整請求了。注意,HTTP/1.0的客戶端不支持100響應碼。但能夠讓客戶端在請求消息中加入Expect頭域,並將它的值設置爲100-continue。
節省帶寬資源的一個很是有效的作法就是壓縮要傳送的數據。Content-Encoding是對消息進行端到端(end-to-end)的編碼,它多是資源在服務器上保存的固有格式(如jpeg圖片格式);在請求消息中加入Accept-Encoding頭域,它能夠告訴服務器客戶端可以解碼的編碼方式。
4 長鏈接
HTTP/1.0規定瀏覽器與服務器只保持短暫的鏈接,瀏覽器的每次請求都須要與服務器創建一個TCP鏈接,服務器完成請求處理後當即斷開TCP鏈接,服務器不跟蹤每一個客戶也不記錄過去的請求。此外,因爲大多數網頁的流量都比較小,一次TCP鏈接不多能經過slow-start區,不利於提升帶寬利用率。
HTTP 1.1支持長鏈接(PersistentConnection)和請求的流水線(Pipelining)處理,在一個TCP鏈接上能夠傳送多個HTTP請求和響應,減小了創建和關閉鏈接的消耗和延遲。例如:一個包含有許多圖像的網頁文件的多個請求和應答能夠在一個鏈接中傳輸,但每一個單獨的網頁文件的請求和應答仍然須要使用各自的鏈接。
HTTP 1.1還容許客戶端不用等待上一次請求結果返回,就能夠發出下一次請求,但服務器端必須按照接收到客戶端請求的前後順序依次回送響應結果,以保證客戶端可以區分出每次請求的響應內容,這樣也顯著地減小了整個下載過程所須要的時間。
5 消息傳遞
HTTP消息中能夠包含任意長度的實體,一般它們使用Content-Length來給出消息結束標誌。可是,對於不少動態產生的響應,只能經過緩衝完整的消息來判斷消息的大小,但這樣作會加大延遲。若是不使用長鏈接,還能夠經過鏈接關閉的信號來斷定一個消息的結束。
HTTP/1.1中引入了Chunkedtransfer-coding來解決上面這個問題,發送方將消息分割成若干個任意大小的數據塊,每一個數據塊在發送時都會附上塊的長度,最後用一個零長度的塊做爲消息結束的標誌。這種方法容許發送方只緩衝消息的一個片斷,避免緩衝整個消息帶來的過載。
在HTTP/1.0中,有一個Content-MD5的頭域,要計算這個頭域須要發送方緩衝完整個消息後才能進行。而HTTP/1.1中,採用chunked分塊傳遞的消息在最後一個塊(零長度)結束以後會再傳遞一個拖尾(trailer),它包含一個或多個頭域,這些頭域是發送方在傳遞完全部塊以後再計算出值的。發送方會在消息中包含一個Trailer頭域告訴接收方這個拖尾的存在。
6 Host頭域
在HTTP1.0中認爲每臺服務器都綁定一個惟一的IP地址,所以,請求消息中的URL並無傳遞主機名(hostname)。但隨着虛擬主機技術的發展,在一臺物理服務器上能夠存在多個虛擬主機(Multi-homed Web Servers),而且它們共享一個IP地址。
HTTP1.1的請求消息和響應消息都應支持Host頭域,且請求消息中若是沒有Host頭域會報告一個錯誤(400 Bad Request)。此外,服務器應該接受以絕對路徑標記的資源請求。
7 錯誤提示
HTTP/1.0中只定義了16個狀態響應碼,對錯誤或警告的提示不夠具體。HTTP/1.1引入了一個Warning頭域,增長對錯誤或警告信息的描述。
此外,在HTTP/1.1中新增了24個狀態響應碼,如409(Conflict)表示請求的資源與資源的當前狀態發生衝突;410(Gone)表示服務器上的某個資源被永久性的刪除。
3. http常見的狀態碼有哪些?(2017-11-23-wzz)
200 OK //客戶端請求成功
301 Moved Permanently(永久移除),請求的URL已移走。Response中應該包含一個Location URL, 說明資源如今所處的位置
302 found 重定向
400 Bad Request //客戶端請求有語法錯誤,不能被服務器所理解
401 Unauthorized //請求未經受權,這個狀態代碼必須和WWW-Authenticate報頭域一塊兒使用
403 Forbidden //服務器收到請求,可是拒絕提供服務
404 Not Found //請求資源不存在,eg:輸入了錯誤的URL
500 Internal Server Error //服務器發生不可預期的錯誤
503 Server Unavailable //服務器當前不能處理客戶端的請求,一段時間後可能恢復正常
4. GET和POST的區別?(2017-11-23-wzz)
從表面現像上面看GET和POST的區別:
1. GET請求的數據會附在URL以後(就是把數據放置在HTTP協議頭中),以?分割URL和傳輸數據,參數之間以&相連,如:login.action?name=zhagnsan&password=123456。POST把提交的數據則放置在是HTTP包的包體中。
2. GET方式提交的數據最多隻能是1024字節,理論上POST沒有限制,可傳較大量的數據。其實這樣說是錯誤的,不許確的:
「GET方式提交的數據最多隻能是1024字節",由於GET是經過URL提交數據,那麼GET可提交的數據量就跟URL的長度有直接關係了。而實際上,URL不存在參數上限的問題,HTTP協議規範沒有對URL長度進行限制。這個限制是特定的瀏覽器及服務器對它的限制。IE對URL長度的限制是2083字節(2K+35)。對於其餘瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決於操做系統的支持。
3.POST的安全性要比GET的安全性高。注意:這裏所說的安全性和上面GET提到的「安全」不是同個概念。上面「安全」的含義僅僅是不做數據修改,而這裏安全的含義是真正的Security的含義,好比:經過GET提交數據,用戶名和密碼將明文出如今URL上,由於(1)登陸頁面有可能被瀏覽器緩存,(2)其餘人查看瀏覽器的歷史紀錄,那麼別人就能夠拿到你的帳號和密碼了,除此以外,使用GET提交數據還可能會形成Cross-site request forgery攻擊。
Get是向服務器發索取數據的一種請求,而Post是向服務器提交數據的一種請求,在FORM(表單)中,Method默認爲"GET",實質上,GET和POST只是發送機制不一樣,並非一個取一個發!
參考原文:https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html
5. http中重定向和請求轉發的區別?(2017-11-23-wzz)
本質區別:轉發是服務器行爲,重定向是客戶端行爲。
重定向特色:兩次請求,瀏覽器地址發生變化,能夠訪問本身web以外的資源,傳輸的數據會丟失。
請求轉發特色:一次強求,瀏覽器地址不變,訪問的是本身自己的web資源,傳輸的數據不會丟失。
4、Cookie和Session
1. Cookie和Session的區別(2017-11-15-lyq)
Cookie是web服務器發送給瀏覽器的一塊信息,瀏覽器會在本地一個文件中給每一個web服務器存儲cookie。之後瀏覽器再給特定的web服務器發送請求時,同時會發送全部爲該服務器存儲的cookie。
Session是存儲在web服務器端的一塊信息。session對象存儲特定用戶會話所需的屬性及配置信息。當用戶在應用程序的 Web 頁之間跳轉時,存儲在 Session 對象中的變量將不會丟失,而是在整個用戶會話中一直存在下去。
Cookie和session的不一樣點:
一、不管客戶端作怎樣的
設置,session都可以正常工做。當客戶端禁用cookie時將沒法使用cookie。
二、在存儲的數據量方面:session可以存儲任意的java對象,cookie只能存儲String類型的對象。
2. session共享怎麼作的(分佈式如何實現session共享)?
參考原文:http://blog.csdn.net/sxiaobei/article/details/57086489
問題描述:一個用戶在登陸成功之後會把用戶信息存儲在session當中,這時session所在服務器爲server1,那麼用戶在session失效以前若是再次使用app,那麼可能會被路由到server2,這時問題來了,server沒有該用戶的session,因此須要用戶從新登陸,這時的用戶體驗會很是很差,因此咱們想如何實現多臺server之間共享session,讓用戶狀態得以保存。
一、服務器實現的session複製或session共享,這類型的共享session是和服務器緊密相關的,好比webSphere或JBOSS在搭建集羣時候能夠配置實現session複製或session共享,可是這種方式有一個致命的缺點,就是很差擴展和移植,好比咱們更換服務器,那麼就要修改服務器配置。
二、利用成熟的技術作session複製,好比12306使用的gemfire,好比常見的內存數據庫如redis或memorycache,這類方案雖然比較普適,可是嚴重依賴於第三方,這樣當第三方服務器出現問題的時候,那麼將是應用的災難。
三、將session維護在客戶端,很容易想到就是利用cookie,可是客戶端存在風險,數據不安全,並且能夠存放的數據量比較小,因此將session維護在客戶端還要對session中的信息加密。
咱們實現的方案能夠說是第二種方案和第三種方案的合體,能夠利用gemfire實現session複製共享,還能夠將session維護在redis中實現session共享,同時能夠將session維護在客戶端的cookie中,可是前提是數據要加密。這三種方式能夠迅速切換,而不影響應用正常執行。咱們在實踐中,首選gemfire或者redis做爲session共享的載體,一旦session不穩定出現問題的時候,能夠緊急切換cookie維護session做爲備用,不影響應用提供服務。
這裏主要講解redis和cookie方案,gemfire比較複雜你們能夠自行查看gemfire工做原理。利用redis作session共享,首先須要與業務邏輯代碼解耦,否則session共享將沒有意義,其次支持動態切換到客戶端cookie模式。redis的方案是,重寫服務器中的HttpSession和HttpServletRequest,首先實現HttpSession接口,重寫session的全部方法,將session以hash值的方式存在redis中,一個session的key就是sessionID,setAtrribute重寫以後就是更新redis中的數據,getAttribute重寫以後就是獲取redis中的數據,等等須要將HttpSession的接口一一實現。
實現了HttpSesson,那麼咱們先將該session類叫作MySession(固然實踐中不是這麼命名的),當MySession出現以後問題纔開始,怎麼能在不影響業務邏輯代碼的狀況下,還能讓本來的request.getSession()獲取到的是MySession,而不是服務器原生的session。這裏,我決定重寫服務器的HttpServletRequet,這裏先稱爲MyRequest,可是這可不是單純的重寫,我須要在原生的request基礎上重寫,因而我決定在filter中,實現request的偷樑換柱,個人思路是這樣的,MyRequest的構建器,必須以request做爲參數,因而我在filter中將服務器原生的request(也有多是框架封裝過的request),當作參數new出來一個MyRequest,而且MyRequest也實現了HttpServletRequest接口,其實就是對原生request的一個加強,這裏主要重寫了幾個request的方法,可是最重要的是重寫了request.getSession(),寫到這裏你們應該都明白爲何重寫這個方法了吧,固然是爲了獲取MySession,因而這樣就在filter中,偷偷的將原生的request換成MyRequest了,而後再將替換過的request傳入chan.doFilter(),這樣filter時候的代碼都使用的是MyRequest了,同時對業務代碼是透明的,業務代碼獲取session的方法仍然是request.getSession(),但其實獲取到的已是MySession了,這樣對session的操做已經變成了對redis的操做。這樣實現的好處有兩個,第一開發人員不須要對session共享作任何關注,session共享對用戶是透明的;第二,filter是可配置的,經過filter的方式能夠將session共享作成一項可插拔的功能,沒有任何侵入性。
這個時候已經實現了一套可插拔的session共享的框架了,可是咱們想到若是redis服務出了問題,這時咱們該怎麼辦呢,因而咱們延續redis的想法,想到能夠將session維護在客戶端內(加密的cookie),固然實現方法仍是同樣的,咱們重寫HttpSession接口,實現其全部方法,好比setAttribute就是寫入cookie,getAttribute就是讀取cookie,咱們能夠將重寫的session稱做MySession2,這時怎麼讓開發人員透明的獲取到MySession2呢,實現方法仍是在filter內偷樑換柱,在MyRequest加一個判斷,讀取sessionType配置,若是sessionType是redis的,那麼getSession的時候獲取到的是MySession,若是sessionType是coolie的,那麼getSession的時候獲取到的是MySession2,以此類推,用一樣的方法就能夠獲取到MySession 3,4,5,6等等。
這樣兩種方式都有了,那麼咱們怎實現兩種session共享方式的快速切換呢,剛剛我提到一個sessionType,這是用來決定獲取到session的類型的,只要變換sessionType就能實現兩種session共享方式的切換,可是sessionType必須對全部的服務器都是一致的,若是不一致那將會出現比較嚴重的問題,咱們目前是將sessionType維護在環境變量裏,若是要切換sessionType就要重啓每一臺服務器,完成session共享的轉換,可是當服務器太多的時候將是一種災難。並且重啓服務意味着服務的中斷,因此這樣的方式只適合服務器規模比較小,並且用戶量比較少的狀況,當服務器太多的時候,務必須要一種協調技術,可以讓服務器可以及時獲取切換的通知。基於這樣的緣由,咱們選用zookeeper做爲配置平臺,每一臺服務器都會訂閱zookeeper上的配置,當咱們切換sessionType以後,全部服務器都會訂閱到修改以後的配置,那麼切換就會當即生效,固然可能會有短暫的時間延遲,但這是能夠接受的。
3. 在單點登陸中,若是cookie被禁用了怎麼辦?(2017-11-23-gxb)
單點登陸的原理是後端生成一個session ID,而後設置到 cookie,後面的全部請求瀏覽器都會帶上 cookie,而後服務端從 cookie 裏獲取 session ID,再查詢到用戶信息。因此,保持登陸的關鍵不是 cookie,而是經過 cookie 保存和傳輸的 session ID,其本質是能獲取用戶信息的數據。除了 cookie,還一般使用 HTTP 請求頭來傳輸。可是這個請求頭瀏覽器不會像 cookie 同樣自動攜帶,須要手工處理。
1. 什麼是jsp,什麼是Servlet?jsp和Servlet有什麼區別?(2017-11-23-wzz)
jsp本質上就是一個Servlet,它是Servlet的一種特殊形式(由SUN公司推出),每一個jsp頁面都是一個servlet實例。
Servlet是由Java提供用於開發web服務器應用程序的一個組件,運行在服務端,由servlet容器管理,用來生成動態內容。一個servlet實例是實現了特殊接口Servlet的Java類,全部自定義的servlet均必須實現Servlet接口。
區別:
jsp是html頁面中內嵌的Java代碼,側重頁面顯示;
Servlet是html代碼和Java代碼分離,側重邏輯控制,mvc設計思想中jsp位於視圖層,servlet位於控制層
Jsp運行機制:以下圖
JVM只能識別Java類,並不能識別jsp代碼!web容器收到以.jsp爲擴展名的url請求時,會將訪問請求交給tomcat中jsp引擎處理,每一個jsp頁面第一次被訪問時,jsp引擎將jsp代碼解釋爲一個servlet源程序,接着編譯servlet源程序生成.class文件,再有web容器servlet引擎去裝載執行servlet程序,實現頁面交互。
2. jsp有哪些域對象和內置對象及他們的做用?(2017-11-25-wzz)
四大域對象:
(1)pageContext page域-指當前頁面,在當前jsp頁面有效,跳到其它頁面失效
(2)request request域-指一次請求範圍內有效,從http請求到服務器處理結束,返回響應的整個過程。在這個過程當中使用forward(請求轉發)方式跳轉多個jsp,在這些頁面裏你均可以使用這個變量
(3)session session域-指當前會話有效範圍,瀏覽器從打開到關閉過程當中,轉發、重定向都可以使用
(4)application context域-指只能在同一個web中使用,服務器未關閉或者重啓,數據就有效
九大內置對象:
生命週期做用域使用狀況
Request一次請求只在Jsp頁面內有效用於接受經過HTTP協議傳送到服務器的數據(包括頭信息、系統信息、請求方式以及請求參數等)。
Reponse一次響應只在Jsp頁面內有效表示服務器端對客戶端的迴應。主要用於設置頭信息、跳轉、Cookie等
Session從存入數據開始,默認閒置30分鐘後失效會話內有效用於存儲特定的用戶會話所需的信息
Outhttp://www.cnblogs.com/leirenyuan/p/6016063.html 用於在Web瀏覽器內輸出信息,而且管理應用服務器上的輸出緩衝區
PageContext詳細瞭解:
http://www.cnblogs.com/leirenyuan/p/6016063.html
用於存取其餘隱含對象,如request、reponse、session、application 等對象。(實際上,pageContext對象提供了對JSP頁面全部的對象及命名空間的訪問。
Pagehttp://www.cnblogs.com/leirenyuan/p/6016063.html page對象表明JSP自己(對應this),只有在JSP頁面內纔是合法的
Exceptionhttp://www.cnblogs.com/leirenyuan/p/6016063.html 顯示異常信息,必須在page 指令中設定< %@ page isErrorPage="true" %>才能使用,在通常的JSP頁面中使用該對象將沒法編譯JSP文件
Application服務器啓動發送第一個請求時就產生了Application對象,直到服務器關閉。 用於存儲和訪問來自任何頁面的變量
全部的用戶分享一個 Application 對象
Confighttp://www.cnblogs.com/leirenyuan/p/6016063.html 取得服務器的配置信息
1. 什麼是xml,使用xml的優缺點,xml的解析器有哪幾種,分別有什麼區別?(2017-11-25-wzz)
xml是一種可擴展性標記語言,支持自定義標籤(使用前必須預約義)使用DTD和XML Schema標準化XML結構。
具體瞭解xml詳見:http://www.importnew.com/10839.html
Schema和dtd的區別
、DTD只能指定元素含有文本,不能定義元素文本的具體類型,如字符型、整型、日期型、自定義類型等。Schema在這方面比DTD強大
對命名空間的支持。DTD沒法利用XML的命名空間,Schema很好知足命名空間。而且,Schema還提供了include和import兩種引用命名空間的方法。
優勢:用於配置文件,格式統一,符合標準;用於在互不兼容的系統間交互數據,共享數據方便;
缺點:xml文件格式複雜,數據傳輸佔流量,服務端和客戶端解析xml文件佔用大量資源且不易維護
Xml經常使用解析器有2種,分別是:DOM和SAX;
主要區別在於它們解析xml文檔的方式不一樣。使用DOM解析,xml文檔以DOM
樹形結構加載入內存,而SAX採用的是事件模型,
詳細區別:https://wenku.baidu.com/view/fc3fb5610b1c59eef8c7b410.html
未完待續......
2、AJAX
1. 談談你對ajax的認識?(2017-11-23-wzz)
Ajax是一種建立交互式網頁應用的的網頁開發技術;Asynchronous JavaScript and XML」的縮寫。
Ajax的優點:
經過異步模式,提高了用戶體驗。
優化了瀏覽器和服務器之間的傳輸,減小沒必要要的數據往返,減小了帶寬佔用。
Ajax引擎在客戶端運行,承擔了一部分原本由服務器承擔的工做,從而減小了大用戶量下的服務器負載。
Ajax的最大特色:
能夠實現局部刷新,在不更新整個頁面的前提下維護數據,提高用戶體驗度。
注意:
ajax在實際項目開發中使用率很是高(牢固掌握),針對ajax的詳細描述:http://www.jb51.net/article/93258.htm
JavaScript是一種在Web開發中常用的前端動態腳本技術。在JavaScript中,有一個很重要的安全性限制,被稱爲「Same-Origin Policy」(同源策略)。這一策略對於JavaScript代碼可以訪問的頁面內容作了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內容。
JavaScript這個安全策略在進行多iframe或多窗口編程、以及Ajax編程時顯得尤其重要。根據這個策略,在baidu.com下的頁面中包含的JavaScript代碼,不能訪問在google.com域名下的頁面內容;甚至不一樣的子域名之間的頁面也不能經過JavaScript代碼互相訪問。對於Ajax的影響在於,經過XMLHttpRequest實現的Ajax請求,不能向不一樣的域提交請求,例如,在abc.example.com下的頁面,不能向def.example.com提交Ajax請求,等等。
然而,當進行一些比較深刻的前端編程的時候,不可避免地須要進行跨域操做,這時候「同源策略」就顯得過於苛刻。JSONP跨域GET請求是一個經常使用的解決方案,下面咱們來看一下JSONP跨域是如何實現的,而且探討下JSONP跨域的原理。
jsonp的最基本的原理是:動態添加一個標籤,使用script標籤的src屬性沒有跨域的限制的特色實現跨域。首先在客戶端註冊一個callback, 而後把callback的名字傳給服務器。此時,服務器先生成 json 數據。 而後以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的參數 jsonp。最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。
客戶端瀏覽器,解析script標籤,並執行返回的 javascript 文檔,此時數據做爲參數,傳入到了客戶端預先定義好的 callback 函數裏。
參考資料:http://www.nowamagic.net/librarys/veda/detail/224
3、Linux
列出文件列表:ls 【參數 -a -l】
建立目錄和移除目錄:mkdir rmdir
用於顯示文件後幾行內容:tail
打包:tar -xvf
打包並壓縮:tar -zcvf
查找字符串:grep
顯示當前所在目錄:pwd
建立空文件:touch
編輯器:vim vi
列出文件列表:ls 【參數 -a -l】
建立目錄和移除目錄:mkdir rmdir
用於顯示文件後幾行內容:tail
打包:tar -xvf
打包並壓縮:tar -zcvf
查找字符串:grep
顯示當前所在目錄:pwd
建立空文件:touch
編輯器:vim vi
2. Linux中如何查看日誌?(2017-11-21-gxb)
動態打印日誌信息:tail –f 日誌文件
參考資料:https://www.cnblogs.com/zdz8207/p/linux-log-tail-cat-tac.html
3. Linux怎麼關閉進程(2017-11-21-gxb)
一般用ps 查看進程PID ,用kill命令終止進程。
ps 命令用於查看當前正在運行的進程。
grep 是搜索
例如:ps -ef | grep java
表示查看全部進程裏CMD是java的進程信息。
ps -aux | grep java
-aux 顯示全部狀態
kill 命令用於終止進程。
例如:kill -9 [PID]
-9表示強迫進程當即中止。
EasyUI是一種基於jQuery的用戶界面插件集合。easyui爲建立現代化,互動,JavaScript應用程序,提供必要的功能。使用easyui你不須要寫不少代碼,你只須要經過編寫一些簡單HTML標記,就能夠定義用戶界面。優點:開源免費,頁面也還說的過去。
easyUI入門:
頁面引入必要的js和css樣式文件,文件引入順序爲:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
而後在頁面寫easyUI代碼就行,easyUI提供了不少樣式:
示例以下:
實現代碼以下:
1.
2.
3.
4.
5. Basic Dialog - jQuery EasyUI Demo
6.
7.
8.
9.
10.
11.
12.
13.
14.
Click below button to open or close dialog.
15.
16. Open
17. Close
18.
19.
20. The dialog content.
21.
22.
23.
24.
jQuery UI 是一套 jQuery 的頁面 UI 插件,包含不少種經常使用的頁面空間,例如 Tabs(如本站首頁右上角部分) 、拉簾效果(本站首頁左上角)、對話框、拖放效果、日期選擇、顏色選擇、數據排序、窗體大小調整等等很是多的內容。
技術亮點:
簡單易用:繼承jQuery 簡易使用特性,提供高度抽象接口,短時間改善網站易用性。
開源免費:採用MIT & GPL 雙協議受權,輕鬆知足自由產品至企業產品各類受權需求。
普遍兼容:兼容各主流桌面瀏覽器。包括IE 6+、Firefox 2+、Safari 3+、Opera 9+、Chrome 1+。
輕便快捷:組件間相對獨立,可按需加載,避免浪費帶寬拖慢網頁打開速度。
標準先進:支持WAI-ARIA,經過標準 XHTML 代碼提供漸進加強,保證低端環境可訪問性。
美觀多變:提供近20 種預設主題,並可自定義多達 60 項可配置樣式規則,提供 24 種背景紋理選擇。
度娘上搜jQueryUI的api,其用法與easyUI、MiniUI都大同小異,此處將再也不舉例。
1. SQL的select語句完整的執行順序(2017-11-15-lyq)
SQL Select語句完整的執行順序:
一、from子句組裝來自不一樣數據源的數據;
二、where子句基於指定的條件對記錄行進行篩選;
三、group by子句將數據劃分爲多個分組;
四、使用匯集函數進行計算;
五、使用having子句篩選分組;
六、計算全部的表達式;
七、select 的字段;
八、使用order by對結果集進行排序。
SQL語言不一樣於其餘編程語言的最明顯特徵是處理代碼的順序。在大多數據庫語言中,代碼按編碼順序被處理。但在SQL語句中,第一個被處理的子句式FROM,而不是第一齣現的SELECT。SQL查詢處理的步驟序號:
(1) FROM
(2) JOIN
(3) ON
(4) WHERE
(5) GROUP BY
(6) WITH {CUBE | ROLLUP}
(7) HAVING
(8) SELECT
(9) DISTINCT
(9) ORDER BY
(10)
以上每一個步驟都會產生一個虛擬表,該虛擬表被用做下一個步驟的輸入。這些虛擬表對調用者(客戶端應用程序或者外部查詢)不可用。只有最後一步生成的表纔會會給調用者。若是沒有在查詢中指定某一個子句,將跳過相應的步驟。
邏輯查詢處理階段簡介:
一、 FROM:對FROM子句中的前兩個表執行笛卡爾積(交叉聯接),生成虛擬表VT1。
二、 ON:對VT1應用ON篩選器,只有那些使爲真才被插入到TV2。
三、 OUTER (JOIN):若是指定了OUTER JOIN(相對於CROSS JOIN或INNER JOIN),保留表中未找到匹配的行將做爲外部行添加到VT2,生成TV3。若是FROM子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重複執行步驟1到步驟3,直處處理完全部的表位置。
四、 WHERE:對TV3應用WHERE篩選器,只有使爲true的行才插入TV4。
五、 GROUP BY:按GROUP BY子句中的列列表對TV4中的行進行分組,生成TV5。
六、 CUTE|ROLLUP:把超組插入VT5,生成VT6。
七、 HAVING:對VT6應用HAVING篩選器,只有使爲true的組插入到VT7。
八、 SELECT:處理SELECT列表,產生VT8。
九、 DISTINCT:將重複的行從VT8中刪除,產品VT9。
十、 ORDER BY:將VT9中的行按ORDER BY子句中的列列表順序,生成一個遊標(VC10)。
十一、 TOP:從VC10的開始處選擇指定數量或比例的行,生成表TV11,並返回給調用者。
where子句中的條件書寫順序
聚合函數是對一組值進行計算並返回單一的值的函數,它常常與select語句中的group by子句一同使用。
a. avg():返回的是指定組中的平均值,空值被忽略。
b. count():返回的是指定組中的項目個數。
c. max():返回指定數據中的最大值。
d. min():返回指定數據中的最小值。
e. sum():返回指定數據的和,只能用於數字列,空值忽略。
f. group by():對數據進行分組,對執行完group by以後的組進行聚合函數的運算,計算每一組的值。最後用having去掉不符合條件的組,having子句中的每個元素必須出如今select列表中(只針對於mysql)。
3. SQL之鏈接查詢(左鏈接和右鏈接的區別)(2017-11-15-lyq)
外鏈接:
左鏈接(左外鏈接):以左表做爲基準進行查詢,左表數據會所有顯示出來,右表若是和左表匹配的數據則顯示相應字段的數據,若是不匹配則顯示爲null。
右鏈接(右外鏈接):以右表做爲基準進行查詢,右表數據會所有顯示出來,左表若是和右表匹配的數據則顯示相應字段的數據,若是不匹配則顯示爲null。
全鏈接:先以左表進行左外鏈接,再以右表進行右外鏈接。
內鏈接:
顯示錶之間有鏈接匹配的全部行。
經過在Web表單中輸入(惡意)SQL語句獲得一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。舉例:當執行的sql爲 select * from user where username =「admin」 or 「a」=「a」時,sql語句恆成立,參數admin毫無心義。
防止sql注入的方式:
1. 預編譯語句:如,select * from user where username = ?,sql語句語義不會發生改變,sql語句中變量用?表示,即便傳遞參數時爲「admin or‘a’= ‘a’」,也會把這總體當作一個字符創去查詢。
2. Mybatis框架中的mapper方式中的 # 也能很大程度的防止sql注入($沒法防止sql注入)。
一、當只要一行數據時使用limit 1
查詢時若是已知會獲得一條數據,這種狀況下加上limit 1 會增長性能。由於mysql數據庫引擎會在找到一條結果中止搜索,而不是繼續查詢下一條是否符合標準直到全部記錄查詢完畢。
二、選擇正確的數據庫引擎
Mysql中有兩個引擎MyISAM和InnoDB,每一個引擎有利有弊。
MyISAM適用於一些大量查詢的應用,但對於有大量寫功能的應用不是很好。甚至你只須要update一個字段整個表都會被鎖起來。而別的進程就算是讀操做也不行要等到當前update操做完成以後才能繼續進行。另外,MyISAM對於select count(*)這類操做是超級快的。
InnoDB的趨勢會是一個很是複雜的存儲引擎,對於一些小的應用會比MyISAM還慢,可是支持「行鎖」,因此在寫操做比較多的時候會比較優秀。而且,它支持不少的高級應用,例如:事物。
3. 用not exists代替not in
Not exists用到了鏈接可以發揮已經創建好的索引的做用,not in不能使用索引。Not in是最慢的方式要同每條記錄比較,在數據量比較大的操做紅不建議使用這種方式。
4. 對操做符的優化,儘可能不採用不利於索引的操做符
如:in not in is null is not null <> 等
某個字段總要拿來搜索,爲其創建索引:
Mysql中能夠利用alter table語句來爲表中的字段添加索引,語法爲:alter table 代表 add index (字段名);
6. 必看sql面試題(學生表_課程表_成績表_教師表)(2017-11-25-wzz)
給你們推薦一篇很是好的博客,該博客中收集了最多見的Mysql常見面試題和筆試題。
博客連接:http://www.cnblogs.com/qixuejia/p/3637735.html ---未看
7. Mysql數據庫架構圖(2017-11-25-wzz)
MyISAM和InnoDB是最多見的兩種存儲引擎,特色以下。
MyISAM存儲引擎
MyISAM是MySQL官方提供默認的存儲引擎,其特色是不支持事務、表鎖和全文索引,對於一些OLAP(聯機分析處理)系統,操做速度快。
每一個MyISAM在磁盤上存儲成三個文件。文件名都和表名相同,擴展名分別是.frm(存儲表定義)、.MYD (MYData,存儲數據)、.MYI (MYIndex,存儲索引)。這裏特別要注意的是MyISAM不緩存數據文件,只緩存索引文件。
InnoDB存儲引擎
InnoDB存儲引擎支持事務,主要面向OLTP(聯機事務處理過程)方面的應用,其特色是行鎖設置、支持外鍵,並支持相似於Oracle的非鎖定讀,即默認狀況下讀不產生鎖。InnoDB將數據放在一個邏輯表空間中(相似Oracle)。InnoDB經過多版本併發控制來得到高併發性,實現了ANSI標準的4種隔離級別,默認爲Repeatable,使用一種被稱爲next-key locking的策略避免幻讀。
對於表中數據的存儲,InnoDB採用相似Oracle索引組織表Clustered的方式進行存儲。
InnoDB 存儲引擎提供了具備提交、回滾和崩潰恢復能力的事務安全。可是對比Myisam的存儲引擎,InnoDB 寫的處理效率差一些而且會佔用更多的磁盤空間以保留數據和索引。
InnoDB體系架構
8. Mysql架構器中各個模塊都是什麼?(2017-11-25-wzz)
(1)、鏈接管理與安全驗證是什麼?
每一個客戶端都會創建一個與服務器鏈接的線程,服務器會有一個線程池來管理這些 鏈接;若是客戶端須要鏈接到MYSQL數據庫還須要進行驗證,包括用戶名、密碼、 主機信息等。
(2)、解析器是什麼?
解析器的做用主要是分析查詢語句,最終生成解析樹;首先解析器會對查詢語句的語法進行分析,分析語法是否有問題。還有解析器會查詢緩存,若是在緩存中有對應的語句,就返回查詢結果不進行接下來的優化執行操做。前提是緩存中的數據沒有被修改,固然若是被修改了也會被清出緩存。
(3)、優化器怎麼用?
優化器的做用主要是對查詢語句進行優化操做,包括選擇合適的索引,數據的讀取方式,包括獲取查詢的開銷信息,統計信息等,這也是爲何圖中會有優化器指向存儲引擎的箭頭。以前在別的文章沒有看到優化器跟存儲引擎之間的關係,在這裏我我的的理解是由於優化器須要經過存儲引擎獲取查詢的大體數據和統計信息。
(4)、執行器是什麼?
執行器包括執行查詢語句,返回查詢結果,生成執行計劃包括與存儲引擎的一些處理操做。
9. Mysql存儲引擎有哪些?(2017-11-25-wzz)
(1)、InnoDB存儲引擎
InnoDB是事務型數據庫的首選引擎,支持事務安全表(ACID),支持行鎖定和外鍵,InnoDB是默認的MySQL引擎。
(2)、MyISAM存儲引擎
MyISAM基於ISAM存儲引擎,並對其進行擴展。它是在Web、數據倉儲和其餘應用環境下最常使用的存儲引擎之一。MyISAM擁有較高的插入、查詢速度,但不支持事物。
(3)、MEMORY存儲引擎
MEMORY存儲引擎將表中的數據存儲到內存中,未查詢和引用其餘表數據提供快速訪問。
(4)、NDB存儲引擎
DB存儲引擎是一個集羣存儲引擎,相似於Oracle的RAC,但它是Share Nothing的架構,所以能提供更高級別的高可用性和可擴展性。NDB的特色是數據所有放在內存中,所以經過主鍵查找很是快。
關於NDB,有一個問題須要注意,它的鏈接(join)操做是在MySQL數據庫層完成,不是在存儲引擎層完成,這意味着,複雜的join操做須要巨大的網絡開銷,查詢速度會很慢。
(5)、Memory (Heap) 存儲引擎
Memory存儲引擎(以前稱爲Heap)將表中數據存放在內存中,若是數據庫重啓或崩潰,數據丟失,所以它很是適合存儲臨時數據。
(6)、Archive存儲引擎
正如其名稱所示,Archive很是適合存儲歸檔數據,如日誌信息。它只支持INSERT和SELECT操做,其設計的主要目的是提供高速的插入和壓縮功能。
(7)、Federated存儲引擎
Federated存儲引擎不存放數據,它至少指向一臺遠程MySQL數據庫服務器上的表,很是相似於Oracle的透明網關。
(8)、Maria存儲引擎
Maria存儲引擎是新開發的引擎,其設計目標是用來取代原有的MyISAM存儲引擎,從而成爲MySQL默認的存儲引擎。
上述引擎中,InnoDB是事務安全的存儲引擎,設計上借鑑了不少Oracle的架構思想,通常而言,在OLTP應用中,InnoDB應該做爲核心應用表的首先存儲引擎。InnoDB是由第三方的Innobase Oy公司開發,現已被Oracle收購,創始人是Heikki Tuuri,芬蘭赫爾辛基人,和著名的Linux創始人Linus是校友。
MySQL和其它的數據庫產品有一個很大的不一樣就是事務由存儲引擎所決定,例如MYISAM,MEMORY,ARCHIVE都不支持事務,事務就是爲了解決一組查詢要麼所有執行成功,要麼所有執行失敗。
MySQL事務默認是採起自動提交的模式,除非顯示開始一個事務。
SHOW VARIABLES LIKE 'AUTOCOMMIT';
修改自動提交模式,0=OFF,1=ON
注意:修改自動提交對非事務類型的表是無效的,由於它們自己就沒有提交和回滾的概念,還有一些命令是會強制自動提交的,好比DLL命令、lock tables等。
SET AUTOCOMMIT=OFF或 SET AUTOCOMMIT=0
數據庫事務transanction正確執行的四個基本要素。ACID,原子性(Atomicity)、一致性(Correspondence)、隔離性(Isolation)、持久性(Durability)。
(1)原子性:整個事務中的全部操做,要麼所有完成,要麼所有不完成,不可能停滯在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。
(2)一致性:在事務開始以前和事務結束之後,數據庫的完整性約束沒有被破壞。
(3)隔離性:隔離狀態執行事務,使它們好像是系統在給定時間內執行的惟一操做。若是有兩個事務,運行在相同的時間內,執行 相同的功能,事務的隔離性將確保每一事務在系統中認爲只有該事務在使用系統。這種屬性有時稱爲串行化,爲了防止事務操做間的混淆, 必須串行化或序列化請求,使得在同一時間僅有一個請求用於同一數據。
(4)持久性:在事務完成之後,該事務所對數據庫所做的更改便持久的保存在數據庫之中,並不會被回滾。
隔離級別髒讀不可重複讀幻讀
Read uncommitted(讀未提交)是是是
Read committed(讀已提交)否是是
Repeatable read(可重複讀)否否是
Serializable(串行讀)否否否
讀未提交(READ UNCOMMITTED):未提交讀隔離級別也叫讀髒,就是事務能夠讀取其它事務未提交的數據。
讀已提交(READ COMMITTED):在其它數據庫系統好比SQL Server默認的隔離級別就是提交讀,已提交讀隔離級別就是在事務未提交以前所作的修改其它事務是不可見的。
可重複讀(REPEATABLE READ):保證同一個事務中的屢次相同的查詢的結果是一致的,好比一個事務一開始查詢了一條記錄而後過了幾秒鐘又執行了相同的查詢,保證兩次查詢的結果是相同的,可重複讀也是mysql的默認隔離級別。
可串行化(SERIALIZABLE):可串行化就是保證讀取的範圍內沒有新的數據插入,好比事務第一次查詢獲得某個範圍的數據,第二次查詢也一樣獲得了相同範圍的數據,中間沒有新的數據插入到該範圍中。
11. MySQL怎麼建立存儲過程(2017-11-25-wzz)
MySQL存儲過程是從MySQL5.0開始增長的新功能。存儲過程的優勢有一籮筐。不過最主要的仍是執行效率和SQL 代碼封裝。特別是 SQL 代碼封裝功能,若是沒有存儲過程,在外部程序訪問數據庫時,要組織不少 SQL 語句。特別是業務邏輯複雜的時候,一大堆的 SQL 和條件夾雜在代碼中,讓人毛骨悚然。如今有了 MySQL 存儲過程,業務邏輯能夠封裝存儲過程當中,這樣不只容易維護,並且執行效率也高。
1、建立MySQL存儲過程
下面代碼建立了一個叫pr_add 的MySQL 存儲過程,這個MySQL 存儲過程有兩個int 類型的輸入參數 「a」、「b」,返回這兩個參數的和。
1)drop procedure if exists pr_add; (備註:若是存在pr_add的存儲過程,則先刪掉)
2)計算兩個數之和(備註:實現計算兩個整數之和的功能)
create procedure pr_add ( a int, b int ) begin declare c int;
if a is null then set a = 0;
end if;
if b is null then set b = 0;
end if;
set c = a + b;
select c as sum;
2、調用MySQL 存儲過程
call pr_add(10, 20);
12. MySQL觸發器怎麼寫?(2017-11-25-wzz)
MySQL包含對觸發器的支持。觸發器是一種與表操做有關的數據庫對象,當觸發器所在表上出現指定事件時,將調用該對象,即表的操做事件觸發表上的觸發器的執行。
在MySQL中,建立觸發器語法以下:
CREATE TRIGGER trigger_name
trigger_time
trigger_event ON tbl_name
FOR EACH ROW
trigger_stmt
其中:
trigger_name:標識觸發器名稱,用戶自行指定;
trigger_time:標識觸發時機,取值爲 BEFORE 或 AFTER;
trigger_event:標識觸發事件,取值爲 INSERT、UPDATE 或 DELETE;
tbl_name:標識創建觸發器的表名,即在哪張表上創建觸發器;
trigger_stmt:觸發器程序體,能夠是一句SQL語句,或者用 BEGIN 和 END 包含的多條語句。
因而可知,能夠創建6種觸發器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE。
另外有一個限制是不能同時在一個表上創建2個相同類型的觸發器,所以在一個表上最多創建6個觸發器。
假設系統中有兩個表:
1)班級表 class(班級號 classID, 班內學生數 stuCount)
2)學生表 student(學號 stuID, 所屬班級號 classID)
要建立觸發器來使班級表中的班內學生數隨着學生的添加自動更新,代碼以下:
create trigger tri_stuInsert after insert
on student for each row
begin
declare c int;
set c = (select stuCount from class where classID=new.classID);
update class set stuCount = c + 1 where classID = new.classID;
查看觸發器:
和查看數據庫(show databases;)查看錶格(show tables;)同樣,查看觸發器的語法以下:
SHOW TRIGGERS [FROM schema_name];
其中,schema_name 即 Schema 的名稱,在 MySQL 中 Schema 和 Database 是同樣的,也就是說,能夠指定數據庫名,這樣就沒必要先「USE database_name;」了。
刪除觸發器:
和刪除數據庫、刪除表格同樣,刪除觸發器的語法以下:
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name
能夠,好比select id from t where num is null 這樣的sql也是能夠的。可是最好不要給數據庫留NULL,儘量的使用 NOT NULL填充數據庫。不要覺得 NULL 不須要空間,好比:char(100) 型,在字段創建時,空間就固定了,無論是否插入值(NULL也包含在內),都是佔用100個字符的空間的,若是是varchar 這樣的變長字段,null不佔用空間。能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢:select id from t where num = 0。
13.2 select * from admin left join log on admin.admin_id = log.admin_id where log.admin_id>10如何優化?
優化爲:select * from (select * from admin where admin_id>10) T1 lef join log on T1.admin_id = log.admin_id。
使用JOIN 時候,應該用小的結果驅動大的結果(left join 左邊表結果儘可能小若是有條件應該放到左邊先處理,right join 同理反向),同時儘可能把牽涉到多表聯合的查詢拆分多個query(多個連表查詢效率低,容易到以後鎖表和阻塞)。
例如:select * from admin order by admin_id limit 100000,10
優化爲:select * from admin where admin_id between 100000 and 100010 order by admin_id。
例如:select * from admin where year(admin_time)>2014
優化爲:select * from admin where admin_time> '2014-01-01′
14. MySQL中文亂碼問題完美解決方案(2017-12-07-lwl)
解決亂碼的核心思想是統一編碼。咱們在使用MySQL建數據庫和建表時應儘可能使用統一的編碼,強烈推薦的是utf8編碼,由於該編碼幾乎能夠兼容世界上全部的字符。
數據庫在安裝的時候能夠設置默認編碼,在安裝時就必定要設置爲utf8編碼。設置以後再建立的數據庫和表若是不指定編碼,默認都會使用utf8編碼,省去了不少麻煩。
數據庫軟件安裝好以後能夠經過以下命令查看默認編碼:
一、查詢數據庫軟件使用的默認編碼格式
show variables like「%colla%」;
show varables like「%char%」
其中collation,表明了字符串排序(比較)的規則,若是值是utf8_general_ci,表明使用utf8字符集大小寫不敏感的天然方式比較。
若是character_set的值不爲utf8,那麼能夠使用以下命令修改成utf8。
二、修改數據庫默認編碼爲utf8
SET character_set_client='utf8';
SET character_set_connection='utf8';
SET character_set_results='utf8';
若是不想設置數據庫軟件的全局默認編碼,也能夠單獨修改或者設置某個具體數據庫的編碼也能夠單獨修改或設置某個數據庫中某個表的編碼。
三、建立數據庫的時候指定使用utf8編碼
CREATE DATABASE `test`
CHARACTER SET 'utf8'
COLLATE 'utf8_general_ci';
四、建立表的時候指定使用utf8編碼
CREATE TABLE `database_user` (
`ID` varchar(40) NOT NULL default '',
`UserID` varchar(40) NOT NULL default '',
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
若是數據庫已經建立好了,能夠使用show database 數據庫名;和 show create table 表名;查看一下數據庫和表的字符集是否爲utf8,若是不是則在命令行下面能夠用以下命令,將數據庫和表編碼修改成utf8.
五、修改具體某數據庫或表的編碼
ALTER DATABASE `db_name` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE `tb_name` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
15. 如何提升MySQL的安全性(2017-12-8-lwl)
1.若是MySQL客戶端和服務器端的鏈接須要跨越並經過不可信任的網絡,那麼須要使用ssh隧道來加密該鏈接的通訊。
2.使用set password語句來修改用戶的密碼,先「mysql -u root」登錄數據庫系統,而後「mysql> update mysql.user set password=password(’newpwd’)」,最後執行「flush privileges」。
3.MySQL須要提防的攻擊有,防偷聽、篡改、回放、拒絕服務等,不涉及可用性和容錯方面。對全部的鏈接、查詢、其餘操做使用基於ACL(ACL(訪問控制列表)是一種路由器配置和控制網絡訪問的一種有力的工具,它可控制路由器應該容許或拒絕數據包經過,可監控流量,可自上向下檢查網絡的安全性,可檢查和過濾數據和限制沒必要要的路由更新,所以讓網絡資源節約成本的ACL配置技術在生活中愈來愈普遍應用。)即訪問控制列表的安全措施來完成。
4.設置除了root用戶外的其餘任何用戶不容許訪問mysql主數據庫中的user表;
5.使用grant和revoke語句來進行用戶訪問控制的工做;
6.不要使用明文密碼,而是使用md5()和sha1()等單向的哈系函數來設置密碼;
7.不要選用字典中的字來作密碼;
8.採用防火牆能夠去掉50%的外部危險,讓數據庫系統躲在防火牆後面工做,或放置在DMZ(DMZ是英文「demilitarized zone」的縮寫,隔離區,它是爲了解決安裝防火牆後外部網絡的訪問用戶不能訪問內部網絡服務器的問題,而設立的一個非安全系統與安全系統之間的緩衝區。)區域中;
9.從因特網上用nmap來掃描3306端口,也可用telnet server_host 3306的方法測試,不容許從非信任網絡中訪問數據庫服務器的3306號tcp端口,須要在防火牆或路由器上作設定;
10.服務端要對SQL進行預編譯,避免SQL注入攻擊,例如where id=234,別人卻輸入where id=234 or 1=1。
11.在傳遞數據給mysql時檢查一下大小;
12.應用程序鏈接到數據庫時應該使用通常的用戶賬號,開放少數必要的權限給該用戶;
13.學會使用tcpdump和strings工具來查看傳輸數據的安全性,例如tcpdump -l -i eth0 -w -src or dst port 3306 strings。以普通用戶來啓動mysql數據庫服務;
14.確信在mysql目錄中只有啓動數據庫服務的用戶才能夠對文件有讀和寫的權限;
15.不準將process或super權限付給非管理用戶,該mysqladmin processlist能夠列舉出當前執行的查詢文本;super權限可用於切斷客戶端鏈接、改變服務器運行參數狀態、控制拷貝複製數據庫的服務器;
16.若是不相信dns服務公司的服務,能夠在主機名稱容許表中只設置ip數字地址;
17.使用max_user_connections變量來使mysqld服務進程,對一個指定賬戶限定鏈接數;
18.grant語句也支持資源控制選項;
19.啓動mysqld服務進程的安全選項開關,–local-infile=0或1,如果0則客戶端程序就沒法使用local load data了,賦權的一個例子grant insert(user) on mysql.user to ‘user_name’@'host_name’;若使用–skip-grant-tables系統將對任何用戶的訪問不作任何訪問控制,但能夠用 mysqladmin flush-privileges或mysqladmin reload來開啓訪問控制;默認狀況是show databases語句對全部用戶開放,能夠用–skip-show-databases來關閉掉。
23.碰到error 1045(28000) access denied for user ‘root’@'localhost’ (using password:no)錯誤時,你須要從新設置密碼,具體方法是:先用–skip-grant-tables參數啓動mysqld,而後執行 mysql -u root mysql,mysql>update user set password=password(’newpassword’) where user=’root’;mysql>flush privileges;,最後從新啓動mysql就能夠了。
1. 什麼是存儲過程,使用存儲過程的好處?(2017-11-25-wzz)
存儲過程(Stored Procedure )是一組爲了完成特定功能的SQL 語句集,經編譯後存儲在數據庫中。用戶經過指定存儲過程的名字並給出參數(若是該存儲過程帶有參數)來執行它。存儲過程是數據庫中的一個重要對象,任何一個設計良好的數據庫應用程序都應該用到存儲過程。
優勢:
(1)容許模塊化程序設計,就是說只須要建立一次過程,之後在程序中就能夠調用該過程任意次。
(2)容許更快執行,若是某操做須要執行大量 SQL 語句或重複執行,存儲過程比 SQL 語句執行的要快。
(3)減小網絡流量,例如一個須要數百行的 SQL 代碼的操做有一條執行語句完成,不須要在網絡中發
送數百行代碼。
(4) 更好的安全機制,對於沒有權限執行存儲過程的用戶,也可受權他們執行存儲過程。
存儲過程的具體使用詳見:http://www.cnblogs.com/yank/p/4235609.html
2. Oracle存儲過程怎麼建立?(2017-11-25-wzz)
存儲過程建立語法:
create or replace procedure存儲過程名(param1 in type,param2 out type)
as
變量1類型(值範圍);
變量2類型(值範圍);
Begin
Select count(*) into變量1 from 表A where列名=param1;
If (判斷條件) then
Select列名 into 變量2 from 表A where列名=param1;
Dbms_output。Put_line(‘打印信息’);
Elsif (判斷條件) then
Dbms_output。Put_line(‘打印信息’);
Else
Raise異常名(NO_DATA_FOUND);
End if;
Exception
When others then
Rollback;
End;
注意事項:
1. 存儲過程參數不帶取值範圍,in表示傳入,out表示輸出
2. 變量帶取值範圍,後面接分號
3. 在判斷語句前最好先用count(*)函數判斷是否存在該條操做記錄
4. 用select 。。。into。。。給變量賦值
5. 在代碼中拋異經常使用 raise+異常名
3. 如何使用Oracle的遊標?(2017-11-25-wzz)
參考博客:https://www.cnblogs.com/sc-xx/archive/2011/12/03/2275084.html
(1)、Oracle中的遊標分爲顯示遊標和隱式遊標
(2)、顯示遊標是用cursor...is命令定義的遊標,它能夠對查詢語句(select)返回的多條記錄進行處理;
(3)、隱式遊標是在執行插入 (insert)、刪除(delete)、修改(update) 和返回單條記錄的查詢(select)語句時由PL/SQL自動定義的。
(4)、顯式遊標的操做:打開遊標、操做遊標、關閉遊標;PL/SQL隱式地打開SQL遊標,並在它內部處理SQL語句,而後關閉它。
4. Oracle中字符串用什麼鏈接?(2017-11-25-wzz)
Oracle中使用 || 這個符號鏈接字符串 如 ‘abc’ || ‘d’ 的結果是abcd。
5. Oracle中是如何進行分頁查詢的?(2017-11-25-wzz)
Oracle中使用rownum來進行分頁, 這個是效率最好的分頁方法,hibernate也是使用rownum來進行Oralce分頁的
select * from
( select rownum r,a from tabName where rownum <= 20 )
where r > 10
6. 存儲過程和存儲函數的特色和區別?(2017-11-25-wzz)
特色:
(1)、通常來講,存儲過程實現的功能要複雜一點,而函數的實現的功能針對性比較強。
(2)、對於存儲過程來講能夠返回參數,而函數只能返回值或者表對象。
(3)、存儲過程通常是做爲一個獨立的部分來執行,而函數能夠做爲查詢語句的一個部分來調用,因爲函數能夠返回一個表對象,所以它能夠在查詢語句中位於FROM關鍵字的後面。
區別:
(1)、函數必須有返回值,而過程沒有.
(2)、函數能夠單獨執行.而過程必須經過execute執行.
(3)、函數能夠嵌入到SQL語句中執行.而過程不行.
其實咱們能夠將比較複雜的查詢寫成函數.而後到存儲過程當中去調用這些函數.
7. 存儲過程與SQL的對比?(2017-11-21-gxb)
優點:
一、提升性能 SQL語句在建立過程時進行分析和編譯。 存儲過程是預編譯的,在首次運行一個存儲過程時,查詢優化器對其進行分析、優化,並給出最終被存在系統表中的存儲計劃,這樣,在執行過程時即可節省此開銷。二、下降網絡開銷 存儲過程調用時只需用提供存儲過程名和必要的參數信息,從而可下降網絡的流量。三、便於進行代碼移植 數據庫專業人員能夠隨時對存儲過程進行修改,但對應用程序源代碼卻毫無影響,從而極大的提升了程序的可移植性。四、更強的安全性 1)系統管理員能夠對執行的某一個存儲過程進行權限限制,避免非受權用戶對數據的訪問 2)在經過網絡調用過程時,只有對執行過程的調用是可見的。 所以,惡意用戶沒法看到表和數據庫對象名稱、嵌入本身的 Transact-SQL 語句或搜索關鍵數據。 3)使用過程參數有助於避免 SQL 注入攻擊。 由於參數輸入被視做文字值而非可執行代碼,因此,攻擊者將命令插入過程內的 Transact-SQL 語句並損害安全性將更爲困難。 4)能夠對過程進行加密,這有助於對源代碼進行模糊處理。
劣勢:
一、存儲過程須要專門的數據庫開發人員進行維護,但實際狀況是,每每由程序開發員人員兼職
二、設計邏輯變動,修改存儲過程沒有SQL靈活
8. 你以爲存儲過程和SQL語句該使用哪一個?(2017-11-21-gxb)
一、在一些高效率或者規範性要求比較高的項目,建議採用存儲過程 二、對於通常項目建議採用參數化命令方式,是存儲過程與SQL語句一種折中的方式 三、對於一些算法要求比較高,涉及多條數據邏輯,建議採用存儲過程
9. 觸發器的做用有哪些?(2017-11-21-gxb)
1)觸發器可經過數據庫中的相關表實現級聯更改;經過級聯引用完整性約束能夠更有效地執行這些更改。
2)觸發器能夠強制比用 CHECK 約束定義的約束更爲複雜的約束。與 CHECK 約束不一樣,觸發器能夠引用其它表中的列。例如,觸發器能夠使用另外一個表中的 SELECT 比較插入或更新的數據,以及執行其它操做,如修改數據或顯示用戶定義錯誤信息。
3)觸發器還能夠強制執行業務規則
4)觸發器也能夠評估數據修改先後的表狀態,並根據其差別採起對策。
參考資料:http://www.cnblogs.com/yank/p/4193820.html
10. 在千萬級的數據庫查詢中,如何提升效率?(2017-11-23-gxb)
1)數據庫設計方面 a. 對查詢進行優化,應儘可能避免全表掃描,首先應考慮在 where 及 order by 涉及的列上創建索引。 b. 應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如: select id from t where num is null 能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢: select id from t where num=0
c. 並非全部索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那麼即便在sex上建了索引也對查詢效率起不了做用。
d. 索引並非越多越好,索引當然能夠提升相應的 select 的效率,但同時也下降了 insert 及 update 的效率,由於 insert 或 update 時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
e. 應儘量的避免更新索引數據列,由於索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新索引數據列,那麼須要考慮是否應將該索引建爲索引。
f. 儘可能使用數字型字段,若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接時會逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。
g. 儘量的使用 varchar/nvarchar 代替 char/nchar ,由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。
h. 儘可能使用表變量來代替臨時表。若是表變量包含大量數據,請注意索引很是有限(只有主鍵索引)。
i. 避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。
j. 臨時表並非不可以使用,適當地使用它們能夠使某些例程更有效,例如,當須要重複引用大型表或經常使用表中的某個數據集時。可是,對於一次性事件,最好使用導出表。
k. 在新建臨時表時,若是一次性插入數據量很大,那麼能夠使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先create table,而後insert。
l. 若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先 truncate table ,而後 drop table ,這樣能夠避免系統表的較長時間鎖定。
2)SQL語句方面
a. 應儘可能避免在 where 子句中使用!=或<>操做符,不然將引擎放棄使用索引而進行全表掃描。
b. 應儘可能避免在 where 子句中使用 or 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,如: select id from t where num=10 or num=20 能夠這樣查詢: select id from t where num=10 union all select id from t where num=20
c. in 和 not in 也要慎用,不然會致使全表掃描,如: select id from t where num in(1,2,3) 對於連續的數值,能用 between 就不要用 in 了: select id from t where num between 1 and 3
d. 下面的查詢也將致使全表掃描: select id from t where name like ‘%abc%’
e. 若是在 where 子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描: select id from t where num=@num 能夠改成強制查詢使用索引: select id from t with(index(索引名)) where num=@num
f. 應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如: select id from t where num/2=100 應改成: select id from t where num=100*2
g. 應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如: select id from t where substring(name,1,3)=‘abc’–name以abc開頭的id select id from t where datediff(day,createdate,’2005-11-30′)=0–‘2005-11-30’生成的id 應改成: select id from t where name like ‘abc%’ select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
h. 不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算,不然系統將可能沒法正確使用索引。
i. 不要寫一些沒有意義的查詢,如須要生成一個空表結構: select col1,col2 into #t from t where 1=0 這類代碼不會返回任何結果集,可是會消耗系統資源的,應改爲這樣: create table #t(…)
j. 不少時候用 exists 代替 in 是一個好的選擇: select num from a where num in(select num from b) 用下面的語句替換: select num from a where exists(select 1 from b where num=a.num)
k. 任何地方都不要使用 select * from t ,用具體的字段列表代替「*」,不要返回用不到的任何字段。
l. 儘可能避免使用遊標,由於遊標的效率較差,若是遊標操做的數據超過1萬行,那麼就應該考慮改寫。
m. 儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
n. 儘可能避免大事務操做,提升系統併發能力。
3)java方面:重點內容
a.儘量的少造對象。
b.合理擺正系統設計的位置。大量數據操做,和少許數據操做必定是分開的。大量的數據操做,確定不是ORM框架搞定的。,
c.使用jDBC連接數據庫操做數據
d.控制好內存,讓數據流起來,而不是所有讀到內存再處理,而是邊讀取邊處理;
e.合理利用內存,有的數據要緩存
1. SpringMVC的工做原理(2017-11-13-lyq)
a. 用戶向服務器發送請求,請求被springMVC前端控制器DispatchServlet捕獲;
b. DispatcherServle對請求URL進行解析,獲得請求資源標識符(URL),而後根據該URL調用HandlerMapping將請求映射處處理器HandlerExcutionChain;
c. DispatchServlet根據得到Handler選擇一個合適的HandlerAdapter適配器處理;
d. Handler對數據處理完成之後將返回一個ModelAndView()對象給DisPatchServlet;
e. Handler返回的ModelAndView()只是一個邏輯視圖並非一個正式的視圖,DispatcherSevlet經過ViewResolver試圖解析器將邏輯視圖轉化爲真正的視圖View;
h. DispatcherServle經過model解析出ModelAndView()中的參數進行解析最終展示出完整的view並返回給客戶端;
2. SpringMVC經常使用註解都有哪些?(2017-11-24-gxb)
@requestMapping 用於請求 url 映射。
@RequestBody 註解實現接收 http 請求的 json 數據,將 json 數據轉換爲 java 對象。
@ResponseBody 註解實現將 controller 方法返回對象轉化爲 json 響應給客戶。
3. 如何開啓註解處理器和適配器?(2017-11-24-gxb)
咱們在項目中通常會在springmvc.xml 中經過開啓 來實現註解處
理器和適配器的開啓。
4. 如何解決get和post亂碼問題?(2017-11-24-gxb)
解決post 請求亂碼:咱們能夠在 web.xml 裏邊配置一個 CharacterEncodingFilter 過濾器。 設置爲 utf-8.
解決get 請求的亂碼:有兩種方法。對於 get 請求中文參數出現亂碼解決方法有兩個:
1. 修改tomcat 配置文件添加編碼與工程編碼一致。在server.xml 找到uriEcoding 配置
2.另一種方法對參數進行從新編碼 String userName = New String(Request.getParameter(「userName」).getBytes(「ISO8859-1」), 「utf-8」);
1. 談談你對Spring的理解(2017-11-13-lyq)
Spring是一個開源框架,爲簡化企業級應用開發而生。Spring能夠是使簡單的JavaBean實現之前只有EJB才能實現的功能。Spring是一個IOC和AOP容器框架。
Spring容器的主要核心是:
控制反轉(IOC),傳統的java開發模式中,當須要一個對象時,咱們會本身使用new或者getInstance等直接或者間接調用構造方法建立一個對象。而在spring開發模式中,spring容器使用了工廠模式爲咱們建立了所須要的對象,不須要咱們本身建立了,直接調用spring提供的對象就能夠了,這是控制反轉的思想。
依賴注入(DI),spring使用javaBean對象的set方法或者帶參數的構造方法爲咱們在建立所需對象時將其屬性自動設置所須要的值的過程,就是依賴注入的思想。
面向切面編程(AOP),在面向對象編程(oop)思想中,咱們將事物縱向抽成一個個的對象。而在面向切面編程中,咱們將一個個的對象某些相似的方面橫向抽成一個切面,對這個切面進行一些如權限控制、事物管理,記錄日誌等公用操做處理的過程就是面向切面編程的思想。AOP底層是動態代理,若是是接口採用JDK動態代理,若是是類採用CGLIB方式實現動態代理。
2. Spring中的設計模式(2017-11-13-lyq)
a. 單例模式——spring中兩種代理方式,若目標對象實現了若干接口,spring使用jdk的java.lang.reflect.Proxy類代理。若目標兌現沒有實現任何接口,spring使用CGLIB庫生成目標類的子類。
單例模式——在spring的配置文件中設置bean默認爲單例模式。
b. 模板方式模式——用來解決代碼重複的問題。 避免重複的try catch
好比:RestTemplate、JmsTemplate、JpaTemplate
d. 前端控制器模式——spring提供了前端控制器DispatherServlet來對請求進行分發。
e. 試圖幫助(view helper)——spring提供了一系列的JSP標籤,高效宏來幫助將分散的代碼整合在試圖中。
f. 依賴注入——貫穿於BeanFactory/ApplacationContext接口的核心理念。
g. 工廠模式——在工廠模式中,咱們在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用同一個接口來指向新建立的對象。Spring中使用beanFactory來建立對象的實例。
3. Spring的經常使用註解(2017-11-13-lyq)
Spring在2.5版本之後開始支持註解的方式來配置依賴注入。能夠用註解的方式來代替xml中bean的描述。註解注入將會被容器在XML注入以前被處理,因此後者會覆蓋掉前者對於同一個屬性的處理結果。
註解裝配在spring中默認是關閉的。因此須要在spring的核心配置文件中配置一下才能使用基於註解的裝配模式。配置方式以下:
經常使用的註解:
@Required:該註解應用於設值方法
@Autowired:該註解應用於有值設值方法、非設值方法、構造方法和變量。
@Qualifier:該註解和@Autowired搭配使用,用於消除特定bean自動裝配的歧義。
4. 簡單介紹一下Spring bean的生命週期(2017-11-21-gxb)
bean定義:在配置文件裏面用來進行定義。
bean初始化:有兩種方式初始化:
1.在配置文件中經過指定init-method屬性來完成
2.實現org.springframwork.beans.factory.InitializingBean接口
bean調用:有三種方式能夠獲得bean實例,並進行調用
bean銷燬:銷燬有兩種方式
1.使用配置文件指定的destroy-method屬性
2.實現org.springframwork.bean.factory.DisposeableBean接口
參考資料:https://www.cnblogs.com/zrtqsk/p/3735273.html
(1)核心容器:包括Core、Beans、Context、EL模塊。---未看
Core模塊:封裝了框架依賴的最底層部分,包括資源訪問、類型轉換及一些經常使用工具類。
Beans模塊:提供了框架的基礎部分,包括反轉控制和依賴注入。其中Bean Factory是容器核心,本質是「工廠設計模式」的實現,並且無需編程實現「單例設計模式」,單例徹底由容器控制,並且提倡面向接口編程,而非面向實現編程;全部應用程序對象及對象間關係由框架管理,從而真正把你從程序邏輯中把維護對象之間的依賴關係提取出來,全部這些依賴關係都由BeanFactory來維護。
Context模塊:以Core和Beans爲基礎,集成Beans模塊功能並添加資源綁定、數據驗證、國際化、Java EE支持、容器生命週期、事件傳播等;核心接口是ApplicationContext。
EL模塊:提供強大的表達式語言支持,支持訪問和修改屬性值,方法調用,支持訪問及修改數組、容器和索引器,命名變量,支持算數和邏輯運算,支持從Spring 容器獲取Bean,它也支持列表投影、選擇和通常的列表聚合等。
(2)AOP、Aspects模塊:
AOP模塊:Spring AOP模塊提供了符合 AOP Alliance規範的面向方面的編程(aspect-oriented programming)實現,提供好比日誌記錄、權限控制、性能統計等通用功能和業務邏輯分離的技術,而且能動態的把這些功能添加到須要的代碼中;這樣各專其職,下降業務邏輯和通用功能的耦合。
Aspects模塊:提供了對AspectJ的集成,AspectJ提供了比Spring ASP更強大的功能。
數據訪問/集成模塊:該模塊包括了JDBC、ORM、OXM、JMS和事務管理。
事務模塊:該模塊用於Spring管理事務,只要是Spring管理對象都能獲得Spring管理事務的好處,無需在代碼中進行事務控制了,並且支持編程和聲明性的事務管理。
JDBC模塊:提供了一個JBDC的樣例模板,使用這些模板能消除傳統冗長的JDBC編碼還有必須的事務控制,並且能享受到Spring管理事務的好處。
ORM模塊:提供與流行的「對象-關係」映射框架的無縫集成,包括Hibernate、JPA、MyBatis等。並且能夠使用Spring事務管理,無需額外控制事務。
OXM模塊:提供了一個對Object/XML映射實現,將java對象映射成XML數據,或者將XML數據映射成java對象,Object/XML映射實現包括JAXB、Castor、XMLBeans和XStream。
JMS模塊:用於JMS(Java Messaging Service),提供一套 「消息生產者、消息消費者」模板用於更加簡單的使用JMS,JMS用於用於在兩個應用程序之間,或分佈式系統中發送消息,進行異步通訊。
Web/Remoting模塊:Web/Remoting模塊包含了Web、Web-Servlet、Web-Struts、Web-Porlet模塊。
Web模塊:提供了基礎的web功能。例如多文件上傳、集成IoC容器、遠程過程訪問(RMI、Hessian、Burlap)以及Web Service支持,並提供一個RestTemplate類來提供方便的Restful services訪問。
Web-Servlet模塊:提供了一個Spring MVC Web框架實現。Spring MVC框架提供了基於註解的請求資源注入、更簡單的數據綁定、數據驗證等及一套很是易用的JSP標籤,徹底無縫與Spring其餘技術協做。
Web-Struts模塊:提供了與Struts無縫集成,Struts1.x 和Struts2.x都支持
Test模塊: Spring支持Junit和TestNG測試框架,並且還額外提供了一些基於Spring的測試功能,好比在測試Web框架時,模擬Http請求的功能。
6. Spring能幫咱們作什麼?(2017-11-22-lyq)spring的優勢
a. Spring能幫咱們根據配置文件建立及組裝對象之間的依賴關係。
Spring根據配置文件來進行建立及組裝對象間依賴關係,只須要改配置文件便可
b. Spring 面向切面編程能幫助咱們無耦合的實現日誌記錄,性能統計,安全控制。
Spring 面向切面編程能提供一種更好的方式來完成,通常經過配置方式,並且不須要在現有代碼中添加任何額外代碼,現有代碼專一業務邏輯。
c. Spring能很是簡單的幫咱們管理數據庫事務。
採用Spring,咱們只需獲取鏈接,執行SQL,其餘事物相關的都交給Spring來管理了。
d. Spring還能與第三方數據庫訪問框架(如Hibernate、JPA)無縫集成,並且本身也提供了一套JDBC訪問模板,來方便數據庫訪問。
e. Spring還能與第三方Web(如Struts、JSF)框架無縫集成,並且本身也提供了一套Spring MVC框架,來方便web層搭建。
f. Spring能方便的與Java EE(如Java Mail、任務調度)整合,與更多技術整合(好比緩存框架)。
7. 請描述一下Spring的事務(2017-11-22-lyq)
聲明式事務管理的定義:用在Spring配置文件中聲明式的處理事務來代替代碼式的處理事務。這樣的好處是,事務管理不侵入開發的組件,具體來講,業務邏輯對象就不會意識到正在事務管理之中,事實上也應該如此,由於事務管理是屬於系統層面的服務,而不是業務邏輯的一部分,若是想要改變事務管理策劃的話,也只須要在定義文件中從新配置便可,這樣維護起來極其方便。
基於TransactionInterceptor 的聲明式事務管理:兩個次要的屬性: transactionManager,用來指定一個事務治理器,並將具體事務相關的操做請託給它;其餘一個是 Properties 類型的 transactionAttributes 屬性,該屬性的每個鍵值對中,鍵指定的是方法名,方法名能夠行使通配符,而值就是表現呼應方法的所運用的事務屬性。
1.
2. ......
3.
4. class="org.springframework.transaction.interceptor.TransactionInterceptor">
5.
6.
7.
8. PROPAGATION_REQUIRED
9.
10.
11.
12.
13. class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
14.
15.
16.
17. class="org.springframework.aop.framework.ProxyFactoryBean">
18.
19.
20.
21.
22.
23.
24.
25. ......
26.
基於TransactionProxyFactoryBean 的聲明式事務管理:設置配置文件與先前比照簡化了許多。咱們把這類設置配置文件格式稱爲 Spring 經典的聲明式事務治理
1.
2. ......
3.
4. class="footmark.spring.core.tx.declare.classic.BankServiceImpl">
5.
6.
7.
8. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
9.
10.
11.
12.
13. PROPAGATION_REQUIRED
14.
15.
16.
17. ......
18.
基於 命名空間的聲明式事務治理:在前兩種方法的基礎上,Spring 2.x 引入了 命名空間,連絡行使 命名空間,帶給開發人員設置配備聲明式事務的全新體驗。
1.
2. ......
3.
4. class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17. ......
18.
基於@Transactional 的聲明式事務管理:Spring 2.x 還引入了基於 Annotation 的體式格式,具體次要觸及@Transactional 標註。@Transactional 能夠浸染於接口、接口方法、類和類方法上。算做用於類上時,該類的一切 public 方法將都具備該類型的事務屬性。
1. @Transactional(propagation = Propagation.REQUIRED)
2. public boolean transfer(Long fromId, Long toId, double amount) {
3. return bankDao.transfer(fromId, toId, amount);
4. }
編程式事物管理的定義:在代碼中顯式挪用beginTransaction()、commit()、rollback()等事務治理相關的方法,這就是編程式事務管理。Spring對事物的編程式管理有基於底層API的編程式管理和基於 TransactionTemplate 的編程式事務管理兩種方式。
基於底層API的編程式管理:憑證PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三個焦點接口,來實現編程式事務管理。
5. Public class BankServiceImpl implements BancService{
6. Private BanckDao bankDao;
7. private TransactionDefinition txDefinition;
8. private PlatformTransactionManager txManager;
9. ......
10. public boolean transfer(Long fromId, Long toId, double amount) {
11. TransactionStatus txStatus = txManager.getTransaction(txDefinition);
12. boolean result = false;
13. try {
14. result = bankDao.transfer(fromId, toId, amount);
15. txManager.commit(txStatus);
16. } catch (Exception e) {
17. result = false;
18. txManager.rollback(txStatus);
19. System.out.println("Transfer Error!");
20. }
21. return result;
22. }
23. }
基於TransactionTemplate 的編程式事務管理:爲了避免損壞代碼原有的條理性,避免出現每個方法中都包括相同的啓動事物、提交、回滾事物樣板代碼的現象,spring提供了transactionTemplate模板來實現編程式事務管理。
1. public class BankServiceImpl implements BankService {
2. private BankDao bankDao;
3. private TransactionTemplate transactionTemplate;
4. ......
5. public boolean transfer(final Long fromId, final Long toId, final double amount) {
6. return (Boolean) transactionTemplate.execute(new TransactionCallback(){
7. public Object doInTransaction(TransactionStatus status) {
8. Object result;
9. try {
10. result = bankDao.transfer(fromId, toId, amount);
11. } catch (Exception e) {
12. status.setRollbackOnly();
13. result = false;
14. System.out.println("Transfer Error!");
15. }
16. return result;
17. }
18. });
19. }
20. }
編程式事務與聲明式事務的區別:
1)編程式事務是本身寫事務處理的類,而後調用
2)聲明式事務是在配置文件中配置,通常搭配在框架裏面使用!
8. BeanFactory 經常使用的實現類有哪些?(2017-12-03-gxb)
Bean 工廠是工廠模式的一個實現,提供了控制反轉功能,用來把應用的配置和依賴從正真的應用代碼中分離。經常使用的BeanFactory 實現有DefaultListableBeanFactory 、 XmlBeanFactory 、 ApplicationContext等。
XMLBeanFactory,最經常使用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據並用它去建立一個徹底配置的系統或應用。
9. 解釋Spring JDBC、Spring DAO和Spring ORM(2017-12-03-gxb)
Spring-DAO 並不是 Spring 的一個模塊,它其實是指示你寫 DAO 操做、寫好 DAO 操做的一些規範。所以,對於訪問你的數據它既沒有提供接口也沒有提供實現更沒有提供模板。在寫一個 DAO 的時候,你應該使用 @Repository 對其進行註解,這樣底層技術(JDBC,Hibernate,JPA,等等)的相關異常才能一致性地翻譯爲相應的 DataAccessException 子類。
Spring-JDBC 提供了 Jdbc 模板類,它移除了鏈接代碼以幫你專一於 SQL 查詢和相關參數。Spring-JDBC 還提供了一個 JdbcDaoSupport,這樣你能夠對你的 DAO 進行擴展開發。它主要定義了兩個屬性:一個 DataSource 和一個 JdbcTemplate,它們均可以用來實現 DAO 方法。JdbcDaoSupport 還提供了一個將 SQL 異常轉換爲 Spring DataAccessExceptions 的異常翻譯器。
Spring-ORM 是一個囊括了不少持久層技術(JPA,JDO,Hibernate,iBatis)的總括模塊。對於這些技術中的每個,Spring 都提供了集成類,這樣每一種技術都可以在遵循 Spring 的配置原則下進行使用,並平穩地和 Spring 事務管理進行集成。
對於每一種技術,配置主要在於將一個DataSource bean 注入到某種 SessionFactory 或者 EntityManagerFactory 等 bean 中。純 JDBC 不須要這樣的一個集成類(JdbcTemplate 除外),由於 JDBC 僅依賴於一個 DataSource。
若是你計劃使用一種ORM 技術,好比 JPA 或者 Hibernate,那麼你就不須要 Spring-JDBC 模塊了,你須要的是這個 Spring-ORM 模塊。
10. 簡單介紹一下Spring WEB 模塊。(2017-12-03-gxb)
Spring的WEB模塊是構建在application context 模塊基礎之上,提供一個適合web應用的上下文。這個模塊也包括支持多種面向web的任務,如透明地處理多個文件上傳請求和程序級請求參數的綁定到你的業務對象。它也有對Jakarta Struts的支持。
11. Spring配置文件有什麼做用?(2017-12-03-gxb)
Spring配置文件是個XML 文件,這個文件包含了類信息,描述瞭如何配置它們,以及如何相互調用。
12. 什麼是Spring IOC 容器?(2017-12-03-gxb)
IOC 控制反轉:Spring IOC 負責建立對象,管理對象。經過依賴注入(DI),裝配對象,配置對象,而且管理這些對象的整個生命週期。
IOC 或 依賴注入把應用的代碼量降到最低。它使應用容易測試,單元測試再也不須要單例和JNDI查找機制。最小的代價和最小的侵入性使鬆散耦合得以實現。IOC容器支持加載服務時的餓漢式初始化和懶加載。
14. ApplicationContext的實現類有哪些?(2017-12-03-gxb)
FileSystemXmlApplicationContext :此容器從一個XML文件中加載beans的定義,XML Bean 配置文件的全路徑名必須提供給它的構造函數。
ClassPathXmlApplicationContext:此容器也從一個XML文件中加載beans的定義,這裏,你須要正確設置classpath由於這個容器將在classpath裏找bean配置。
WebXmlApplicationContext:此容器加載一個XML文件,此文件定義了一個WEB應用的全部bean。
15. BeanFactory與AppliacationContext有什麼區別(2017-12-03-gxb)
1. BeanFactory
基礎類型的IOC容器,提供完成的IOC服務支持。若是沒有特殊指定,默認採用延遲初始化策略。相對來講,容器啓動初期速度較快,所需資源有限。
2.ApplicationContext
ApplicationContext是在BeanFactory的基礎上構建,是相對比較高級的容器實現,除了BeanFactory的全部支持外,ApplicationContext還提供了事件發佈、國際化支持等功能。ApplicationContext管理的對象,在容器啓動後默認所有初始化而且綁定完成。
16. 什麼是Spring的依賴注入?(2017-12-04-gxb)
日常的java開發中,程序員在某個類中須要依賴其它類的方法,則一般是new一個依賴類再調用類實例的方法,這種開發存在的問題是new的類實例很差統一管理,spring提出了依賴注入的思想,即依賴類不禁程序員實例化,而是經過spring容器幫咱們new指定實例而且將實例注入到須要該對象的類中。依賴注入的另外一種說法是「控制反轉」,通俗的理解是:日常咱們new一個實例,這個實例的控制權是咱們程序員,而控制反轉是指new實例工做不禁咱們程序員來作而是交給spring容器來作。
17. 有哪些不一樣類型的IOC(依賴注入)方式?(2017-12-04-gxb)
Spring提供了多種依賴注入的方式。
參考資料:https://www.cnblogs.com/java-class/p/4727775.html
18. 什麼是Spring beans?(2017-12-04-gxb)
Spring beans 是那些造成Spring應用的主幹的java對象。它們被Spring IOC容器初始化,裝配,和管理。這些beans經過容器中配置的元數據建立。好比,以XML文件中 的形式定義。
Spring 框架定義的beans都是單例beans。
19. 一個Spring Beans的定義須要包含什麼?(2017-12-04-gxb)
一個Spring Bean 的定義包含容器必知的全部配置元數據,包括如何建立一個bean,它的生命週期詳情及它的依賴。
20. 你怎樣定義類的做用域?(2017-12-04-gxb)
當定義一個 在Spring裏,咱們還能給這個bean聲明一個做用域。它能夠經過bean 定義中的scope屬性來定義。如,當Spring要在須要的時候每次生產一個新的bean實例,bean的scope屬性被指定爲prototype。另外一方面,一個bean每次使用的時候必須返回同一個實例,這個bean的scope 屬性必須設爲 singleton。
21. Spring支持的幾種bean的做用域。(2017-12-04-gxb)
Spring框架支持如下五種bean的做用域:
singleton : bean在每一個Spring ioc 容器中只有一個實例。
prototype:一個bean的定義能夠有多個實例。
request:每次http請求都會建立一個bean,該做用域僅在基於web的Spring ApplicationContext情形下有效。
session:在一個HTTP Session中,一個bean定義對應一個實例。該做用域僅在基於web的Spring ApplicationContext情形下有效。
global-session:在一個全局的HTTP Session中,一個bean定義對應一個實例。該做用域僅在基於web的Spring ApplicationContext情形下有效。
缺省的Spring bean 的做用域是Singleton。
22. Spring框架中的單例bean是線程安全的嗎?(2017-12-04-gxb)
Spring框架中的單例bean不是線程安全的。
23. 什麼是Spring的內部bean?(2017-12-04-gxb)
當一個bean僅被用做另外一個bean的屬性時,它能被聲明爲一個內部bean,爲了定義inner bean,在Spring 的 基於XML的 配置元數據中,能夠在 或 元素內使用 元素,內部bean一般是匿名的,它們的Scope通常是prototype。
24. 在Spring中如何注入一個java集合?(2017-12-04-gxb)
Spring提供如下幾種集合的配置元素:
類型用於注入一列值,容許有相同的值。
類型用於注入一組值,不容許有相同的值。
類型用於注入一組鍵值對,鍵和值均可覺得任意類型。
類型用於注入一組鍵值對,鍵和值都只能爲String類型。
25. 什麼是bean的自動裝配?(2017-12-04-gxb)
無須在Spring配置文件中描述javaBean之間的依賴關係(如配置、)。IOC容器會自動創建javabean之間的關聯關係。
26. 解釋不一樣方式的自動裝配。(2017-12-04-gxb)
有五種自動裝配的方式,能夠用來指導Spring容器用自動裝配方式來進行依賴注入。
1)no:默認的方式是不進行自動裝配,經過顯式設置ref 屬性來進行裝配。
2)byName:經過參數名自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,以後容器試圖匹配、裝配和該bean的屬性具備相同名字的bean。
3)byType::經過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,以後容器試圖匹配、裝配和該bean的屬性具備相同類型的bean。若是有多個bean符合條件,則拋出錯誤。
4)constructor:這個方式相似於byType, 可是要提供給構造器參數,若是沒有肯定的帶參數的構造器參數類型,將會拋出異常。
5)autodetect:首先嚐試使用constructor來自動裝配,若是沒法工做,則使用byType方式。
27. 什麼是基於Java的Spring註解配置? 給一些註解的例子(2017-12-05-gxb)
基於Java的配置,容許你在少許的Java註解的幫助下,進行你的大部分Spring配置而非經過XML文件。
以@Configuration 註解爲例,它用來標記類能夠當作一個bean的定義,被Spring IOC容器使用。另外一個例子是@Bean註解,它表示此方法將要返回一個對象,做爲一個bean註冊進Spring應用上下文。
28. 什麼是基於註解的容器配置?(2017-12-05-gxb)
相對於XML文件,註解型的配置依賴於經過字節碼元數據裝配組件,而非尖括號的聲明。開發者經過在相應的類,方法或屬性上使用註解的方式,直接組件類中進行配置,而不是使用xml表述bean的裝配關係。
註解裝配在默認狀況下是不開啓的,爲了使用註解裝配,咱們必須在Spring配置文件中配置 元素。
30. 在Spring框架中如何更有效地使用JDBC?(2017-12-05-gxb)
使用SpringJDBC 框架,資源管理和錯誤處理的代價都會被減輕。因此開發者只需寫statements 和 queries從數據存取數據,JDBC也能夠在Spring框架提供的模板類的幫助下更有效地被使用,這個模板叫JdbcTemplate。
JdbcTemplate 類提供了不少便利的方法解決諸如把數據庫數據轉變成基本數據類型或對象,執行寫好的或可調用的數據庫操做語句,提供自定義的數據錯誤處理。
31. 使用Spring經過什麼方式訪問Hibernate?(2017-12-05-gxb)
在Spring中有兩種方式訪問Hibernate:
1)控制反轉HibernateTemplate和 Callback。
2)繼承HibernateDAOSupport提供一個AOP 攔截器。
32. Spring支持的ORM框架有哪些?(2017-12-05-gxb)
Spring支持如下ORM:
Hibernate、iBatis、JPA (Java Persistence API)、TopLink、JDO (Java Data Objects)、OJB
33. 簡單解釋一下spring的AOP(2017-12-05-gxb)
AOP(Aspect Oriented Programming),即面向切面編程,能夠說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概念來創建一種對象層次結構,用於模擬公共行爲的一個集合。不過OOP容許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。日誌代碼每每橫向地散佈在全部對象層次中,而與它對應的對象的核心功能毫無關係對於其餘類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散佈在各處的無關的代碼被稱爲橫切(cross cutting),在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。
AOP技術偏偏相反,它利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊之間的耦合度,並有利於將來的可操做性和可維護性。
使用"橫切"技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特色是,他們常常發生在覈心關注點的多處,而各處基本類似,好比權限認證、日誌、事物。AOP的做用在於分離系統中的各類關注點,將核心關注點和橫切關注點分離開來。
AOP核心就是切面,它將多個類的通用行爲封裝成可重用的模塊,該模塊含有一組API提供橫切功能。好比,一個日誌模塊能夠被稱做日誌的AOP切面。根據需求的不一樣,一個應用程序能夠有若干切面。在Spring AOP中,切面經過帶有@Aspect註解的類實現。
34. 在Spring AOP 中,關注點和橫切關注的區別是什麼?(2017-12-05-gxb)
關注點是應用中一個模塊的行爲,一個關注點可能會被定義成一個咱們想實現的一個功能。橫切關注點是一個關注點,此關注點是整個應用都會使用的功能,並影響整個應用,好比日誌,安全和數據傳輸,幾乎應用的每一個模塊都須要的功能。所以這些都屬於橫切關注點。
被攔截到的點,由於Spring只支持方法類型的鏈接點,因此在Spring中鏈接點指的就是被攔截到的方法,實際上鍊接點還能夠是字段或者構造器。
36. Spring的通知是什麼?有哪幾種類型?(2017-12-05-gxb)
通知是個在方法執行前或執行後要作的動做,其實是程序執行時要經過SpringAOP框架觸發的代碼段。
Spring切面能夠應用五種類型的通知:
1)before:前置通知,在一個方法執行前被調用。
2)after: 在方法執行以後調用的通知,不管方法執行是否成功。
3)after-returning: 僅當方法成功完成後執行的通知。
4)after-throwing: 在方法拋出異常退出時執行的通知。
5)around: 在方法執行以前和以後調用的通知。
切入點是一個或一組鏈接點,通知將在這些位置執行。能夠經過表達式或匹配的方式指明切入點。
被一個或者多個切面所通知的對象。它一般是一個代理對象。也指被通知(advised)對象。
代理是通知目標對象後建立的對象。從客戶端的角度看,代理對象和目標對象是同樣的。
40. 什麼是織入?什麼是織入應用的不一樣點?(2017-12-05-gxb)
織入是將切面和到其餘應用類型或對象鏈接或建立一個被通知對象的過程。織入能夠在編譯時,加載時,或運行時完成。
1. 簡單介紹一下Shiro框架(2017-11-23-gxb)
Apache Shiro是Java的一個安全框架。使用shiro能夠很是容易的開發出足夠好的應用,其不只能夠用在JavaSE環境,也能夠用在JavaEE環境。Shiro能夠幫助咱們完成:認證、受權、加密、會話管理、與Web集成、緩存等。
三個核心組件:Subject, SecurityManager 和 Realms.
Subject:即「當前操做用戶」。可是,在Shiro中,Subject這一律念並不只僅指人,也能夠是第三方進程、後臺賬戶(Daemon Account)或其餘相似事物。它僅僅意味着「當前跟軟件交互的東西」。但考慮到大多數目的和用途,你能夠把它認爲是Shiro的「用戶」概念。 Subject表明了當前用戶的安全操做,SecurityManager則管理全部用戶的安全操做。 SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro經過SecurityManager來管理內部組件實例,並經過它來提供安全管理的各類服務。 Realm: Realm充當了Shiro與應用安全數據間的「橋樑」或者「鏈接器」。也就是說,當對用戶執行認證(登陸)和受權(訪問控制)驗證時,Shiro會從應用配置的Realm中查找用戶及其權限信息。
2. Shiro主要的四個組件(2017-12-2-wzz)
1)SecurityManager
典型的Facade,Shiro 經過它對外提供安全管理的各類服務。 ü
2)Authenticator
對「Who are you ?」進行覈實。一般涉及用戶名和密碼。 這個組件負責收集 principals 和 credentials,並將它們提交給應用系統。若是提交的 credentials 跟應用系統中提供的 credentials 吻合,就可以繼續訪問,不然須要從新提交 principals 和 credentials, 或者直接終止訪問。 ü
3)Authorizer
身份份驗證經過後,由這個組件對登陸人員進行訪問控制的篩查,好比「who can do what」, 或者「who can do which actions」。 Shiro 採用「基於 Realm」的方法,即用戶(又稱 Subject)、 用戶組、角 色和 permission 的聚合體。
4)Session Manager
這個組件保證了異構客戶端的訪問,配置簡單。它是基於POJO/J2SE 的,不跟任何的客戶 端或者協議綁定。
一、Application Code:應用程序代碼,就是咱們本身的編碼,若是在程序中須要進 行權限控制,須要調用 Subject 的 API。
二、Subject:主體,表明的了當前用戶。全部的 Subject 都綁定到 SecurityManager, 與 Subject 的全部交互都會委託給 SecurityManager,能夠將 Subject 當成一個 門面,而真正執行者是 SecurityManager 。ü
三、SecurityManage:安全管理器,全部與安全有關的操做都會與 SecurityManager 交互,而且它管理全部的 Subject 。ü
四、Realm:域 shiro 是從 Realm 來獲取安全數據(用戶,角色,權限)。就是說 SecurityManager
要驗證用戶身份,那麼它須要從Realm 獲取相應的用戶進行比較以肯定用戶 身份是否合法;也須要從Realm 獲得用戶相應的角色/權限進行驗證用戶是否 能進行操做; 能夠把 Realm 當作 DataSource,即安全數據源 。
4. Shiro的四種權限控制方式(2017-12-2-wzz)
1)url 級別權限控制
2)方法註解權限控制
3)代碼級別權限控制
頁面標籤權限控制
詳見網址:https://www.cnblogs.com/cocosili/p/7103025.html
(1)、什麼是粗顆粒和細顆粒權限?
對資源類型的管理稱爲粗顆粒度權限控制,即只控制到菜單、按鈕、方法,粗粒度的例子好比:用戶具備用 戶管理的權限,具備導出訂單明細的權限。對資源實例的控制稱爲細顆粒度權限管理,即控制到數據級別的權限, 好比:用戶只容許修改本部門的員工信息,用戶只容許導出本身建立的訂單明細。
總結:
粗顆粒權限:針對url連接的控制。
細顆粒權限:針對數據級別的控制。
好比:查詢用戶權限。
衛生局能夠查詢全部用戶。
衛生室能夠查詢本單位的用戶。
一般在service中編程實現。
(2)、粗顆粒和細顆粒如何受權?
對於粗顆粒度的受權能夠很容易作系統架構級別的功能,即系統功能操做使用統一的粗顆粒度的權限管理。
對於細顆粒度的受權不建議作成系統架構級別的功能,由於對數據級別的控制是系統的業務需求,隨着業務需求的變動業務功能變化的可能性很大,建議對數據級別的權限控制在業務層個性化開發,好比:用戶只容許修改本身建立的商品信息能夠在service接口添加校驗實現,service接口須要傳入當前操做人的標識,與商品信息建立人標識對比,不一致則不容許修改商品信息。
粗顆粒權限:能夠使用過慮器統一攔截url。
細顆粒權限:在service中控制,在程序級別來控制,個性化 編程。
1. Mybatis中#和$的區別?(2017-11-23-gxb)
#至關於對數據 加上 雙引號,$至關於直接顯示數據
1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,若是傳入的值是111,那麼解析成sql時的值爲order by "111", 若是傳入的值是id,則解析成的sql爲order by "id".2. $將傳入的數據直接顯示生成在sql中。如:order by $user_id$,若是傳入的值是111,那麼解析成sql時的值爲order by user_id, 若是傳入的值是id,則解析成的sql爲order by id.3. #方式可以很大程度防止sql注入。4.$方式沒法防止Sql注入。5.$方式通常用於傳入數據庫對象,例如傳入表名.6.通常能用#的就別用$.
2. Mybatis的編程步驟是什麼樣的?(2017-12-2-wzz)
一、建立SqlSessionFactory
二、經過SqlSessionFactory建立SqlSession
三、經過sqlsession執行數據庫操做
四、調用session.commit()提交事務
五、調用session.close()關閉會話
3. JDBC編程有哪些不足之處,MyBatis是如何解決這些問題的?(2017-12-2-wzz)
1. 數據庫連接建立、釋放頻繁形成系統資源浪費從而影響系統性能,若是使用數據庫連接池可解決此問題。
解決:在SqlMapConfig.xml中配置數據連接池,使用鏈接池管理數據庫鏈接。
2. Sql語句寫在代碼中形成代碼不易維護,實際應用sql變化的可能較大,sql變更須要改變java代碼。
解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。
3. 向sql語句傳參數麻煩,由於sql語句的where條件不必定,可能多也可能少,佔位符須要和參數一一對應。
解決:Mybatis自動將java對象映射至sql語句。
4. 對結果集解析麻煩,sql變化致使解析代碼變化,且解析前須要遍歷,若是能將數據庫記錄封裝成pojo對象解析比較方便。
解決:Mybatis自動將sql執行結果映射至java對象。
4. 使用MyBatis的mapper接口調用時有哪些要求?(2017-12-2-wzz)
1. Mapper接口方法名和mapper.xml中定義的每一個sql的id相同
2. Mapper接口方法的輸入參數類型和mapper.xml中定義的每一個sql 的parameterType的類型相同
3. Mapper接口方法的輸出參數類型和mapper.xml中定義的每一個sql的resultType的類型相同
4. Mapper.xml文件中的namespace便是mapper接口的類路徑。
5. Mybatis中一級緩存與二級緩存?(2017-12-4-lyq)
1. 一級緩存: 基於PerpetualCache 的 HashMap本地緩存,其存儲做用域爲 Session,當 Session flush 或 close 以後,該Session中的全部 Cache 就將清空。
2. 二級緩存與一級緩存其機制相同,默認也是採用PerpetualCache,HashMap存儲,不一樣在於其存儲做用域爲 Mapper(Namespace),而且可自定義存儲源,如 Ehcache。做用域爲namespance是指對該namespance對應的配置文件中全部的select操做結果都緩存,這樣不一樣線程之間就能夠共用二級緩存。啓動二級緩存:在mapper配置文件中:。
二級緩存能夠設置返回的緩存對象策略:。當readOnly="true"時,表示二級緩存返回給全部調用者同一個緩存對象實例,調用者能夠update獲取的緩存實例,可是這樣可能會形成其餘調用者出現數據不一致的狀況(由於全部調用者調用的是同一個實例)。當readOnly="false"時,返回給調用者的是二級緩存總緩存對象的拷貝,即不一樣調用者獲取的是緩存對象不一樣的實例,這樣調用者對各自的緩存對象的修改不會影響到其餘的調用者,便是安全的,因此默認是readOnly="false";
3. 對於緩存數據更新機制,當某一個做用域(一級緩存Session/二級緩存Namespaces)的進行了 C/U/D 操做後,默認該做用域下全部 select 中的緩存將被clear。
6. MyBatis在insert插入操做時返回主鍵ID(2017-12-4-lyq)
數據庫爲MySql時:
1.
「keyProperty」表示返回的id要保存到對象的那個屬性中,「useGeneratedKeys」表示主鍵id爲自增加模式。MySQL中作以上配置就OK了
數據庫爲Oracle時:
1.
2.
3. SELECT SEQ_USER.NEXTVAL as userId from DUAL
4.
5. insert into user (user_id, user_name, modified, state)
6. values (#{userId,jdbcType=INTEGER}, #{userName,jdbcType=VARCHAR}, #{modified,jdbcType=TIMESTAMP}, #{state,jdbcType=INTEGER})
7.
因爲Oracle沒有自增加一說法,只有序列這種模仿自增的形式,因此不能再使用「useGeneratedKeys」屬性。
而是使用將ID獲取並賦值到對象的屬性中,insert插入操做時正常插入id。
1. 簡單介紹一下Struts2(2017-11-24-gxb)
Struts2 框架是一個按照 MVC 設計模式設計的 WEB 層框架,是在 struts 1 和 WebWork 的技術基礎上進行了合併的全新的框架。其全新的 Struts 2 的體系結構與 Struts 1 的體 繫結構差異巨大。Struts 2 以 WebWork 爲核心,採用攔截器的機制來處理用戶的請求, 這樣的設計也使得業務邏輯控制器可以與 ServletAPI 徹底脫離開。
咱們能夠把struts2 理解爲一個大大的 servlet,而這個 servlet 就是 ActionServlet。struts2 在處理客戶端請求時,會先讀取 web.xml 配置文件,根據前端控制器將符合條件的請求 分給各個不一樣的 Action 處理。 在此以前,會把 ActionServlet 會把數據封裝成一個 javaBean。
Struts2 框架提供了許多的攔截器,在封裝數據的過程當中,咱們能夠對數據進行一些操 做,例如:數據校驗等等。
當Action 執行完後要返回一個結果視圖,這個結果視圖能夠跟據 struts2 的配置文件中 配置,選擇轉發或者重定向。
2. Struts2的執行流程瞭解麼?(2017-11-24-gxb)
參考資料:http://blog.csdn.net/wjw0130/article/details/46371847
Struts2的官方文檔附帶了Struts2的架構圖。從這張圖能夠很好的去理解Struts2
關於圖中的Key:
Servlet Filters:過濾器鏈,客戶端的全部請求都要通過Filter鏈的處理。
Struts Core:Struts2的核心部分,可是Struts2已經幫咱們作好了,咱們不須要去作這個
Interceptors,Struts2的攔截器。Struts2提供了不少默認的攔截器,能夠完成平常開發的絕大部分工做;而咱們自定義的攔截器,用來實現實際的客戶業務須要的功能。
User Created,由開發人員建立的,包括struts.xml、Action、Template,這些是每一個使用Struts2來進行開發的人員都必須會的。
1. FilterDispatcher是整個Struts2的調度中心,也就是MVC中的C(控制中心),根據ActionMapper的結果來決定是否處理請求,若是ActionMapper指出該URL應該被Struts2處理,那麼它將會執行Action處理,並中止過濾器鏈上尚未執行的過濾器。
2. ActionMapper 會判斷這個請求是否應該被Struts2處理,若是須要Struts2處理,ActionMapper會返回一個對象來描述請求對應的ActionInvocation的信息。
3. ActionProxy,它會建立一個ActionInvocation實例,位於Action和xwork之間,使得咱們在未來有機會引入更多的實現方式,好比經過WebService來實現等。
4. ConfigurationManager是xwork配置的管理中心,能夠把它看作struts.xml這個配置文件在內存中的對應。
5. struts.xml,是開發人員必須光顧的地方。是Stuts2的應用配置文件,負責諸如URL與Action之間映射關係的配置、以及執行後頁面跳轉的Result配置等。
6. ActionInvocation:真正調用並執行Action,它擁有一個Action實例和這個Action所依賴的攔截器實例。ActionInvocation會按照指定的順序去執行這些攔截器、Action以及相應的Result。
Interceptor(攔截器):是Struts2的基石,相似於JavaWeb的Filter,攔截器是一些無狀態的類,攔截器能夠自動攔截Action,它們給開發者提供了在Action運行以前或Result運行以後來執行一些功能代碼的機會。
7. Action:用來處理請求,封裝數據。
3. Struts2中Action配置的注意事項有哪些?(2017-11-24-gxb)
1. name 包名稱,在 struts2 的配置文件中,包名不能重複,name 並非真正包名,只是爲
了管理Action
2. namespace 和 的 name 屬性,決定 Action 的訪問路徑 (以/開始 )
3. extends 繼承哪一個包,一般開發中繼承 struts-default 包 (struts-default 包 在 struts-default.xml 中定義 )【能夠使用包中默認的攔截器和結果集】
4. 攔截器和過濾器有哪些區別?(2017-11-24-gxb)
* 攔截器是基於 java 的反射機制的,而過濾器是基於函數回調
* 攔截器不依賴與 servlet 容器,而過濾器依賴與 servlet 容器
* 攔截器只能對 action 請求起做用,而過濾器則能夠對幾乎全部的請求起做用
* 攔截器能夠訪問 action 上下文、值棧裏的對象,而過濾器不能
* 在 action 的生命週期中,攔截器能夠屢次被調用,而過濾器只能在容器初始化時被調用一 次
5. Struts2的封裝方式有哪些?(2017-11-24-gxb)
1、屬性封裝
1. 在action中設置成員變量,變量名與表單中的name屬性值相同
2. 生成變量的set方法
實例:獲取用戶輸入的用戶名和密碼
jsp頁面以下:
java代碼以下:
2、模型驅動(經常使用)
1. action實現 ModeDriven 接口
2. 在action裏建立實體類對象
3. 實現接口的getModel方法並返回所建立的對象
示例:獲取用戶輸入的用戶名和密碼
jsp頁面以下:
java代碼以下:
需注意的是表單name的值應與類的屬性名相同。
3、表達式封裝
1. 在action 中聲明實體類
2. 生成實體類的set 和 get 方法
3. 在表單輸入項的name 屬性值裏面寫表達式
jsp頁面以下:
java代碼以下:
6. 簡單介紹一下Struts2的值棧。(2017-11-24-gxb)
值棧是對應每個請求對象的數據存儲中心。Struts2的一個很重要的特色就是引入了值棧。以前咱們經過緩存或者模型驅動在action和頁面之間傳遞數據,數據混亂,而且
難以管理,緩存還有時間和數量限制,使用起來很是的困難。值棧的引入解決了這個問題,它能夠統一管理頁面和action之間的數據,供action、result、interceptor等使用。咱們大多數情
況下不須要考慮值棧在哪裏,裏面有什麼,只須要去獲取本身須要的數據就能夠了,大大的下降了開發人員的工做量和邏輯複雜性。
參考資料:https://www.cnblogs.com/hlhdidi/p/6185836.html
7. SpringMVC和Struts2的區別?(2017-11-23-gxb)
一、Struts2是類級別的攔截, 一個類對應一個request上下文,SpringMVC是方法級別的攔截,一個方法對應一個request上下文,而方法同時又跟一個url對應,因此說從架構自己上SpringMVC就容易實現restful url,而struts2的架構實現起來要費勁,由於Struts2中Action的一個方法能夠對應一個url,而其類屬性卻被全部方法共享,這也就沒法用註解或其餘方式標識其所屬方法了。
二、由上邊緣由,SpringMVC的方法之間基本上獨立的,獨享request response數據,請求數據經過參數獲取,處理結果經過ModelMap交回給框架,方法之間不共享變量,而Struts2搞的就比較亂,雖然方法之間也是獨立的,但其全部Action變量是共享的,這不會影響程序運行,卻給咱們編碼 讀程序時帶來麻煩,每次來了請求就建立一個Action,一個Action對象對應一個request上下文。 三、因爲Struts2須要針對每一個request進行封裝,把request,session等servlet生命週期的變量封裝成一個一個Map,供給每一個Action使用,並保證線程安全,因此在原則上,是比較耗費內存的。
四、 攔截器實現機制上,Struts2有以本身的interceptor機制,SpringMVC用的是獨立的AOP方式,這樣致使Struts2的配置文件量仍是比SpringMVC大。
5、SpringMVC的入口是servlet,而Struts2是filter(這裏要指出,filter和servlet是不一樣的。),這就致使了兩者的機制不一樣,這裏就牽涉到servlet和filter的區別了。
六、SpringMVC集成了Ajax,使用很是方便,只需一個註解@ResponseBody就能夠實現,而後直接返回響應文本便可,而Struts2攔截器集成了Ajax,在Action中處理時一般必須安裝插件或者本身寫代碼集成進去,使用起來也相對不方便。
七、SpringMVC驗證支持JSR303,處理起來相對更加靈活方便,而Struts2驗證比較繁瑣,感受太煩亂。
八、Spring MVC和Spring是無縫的。從這個項目的管理和安全上也比Struts2高(固然Struts2也能夠經過不一樣的目錄結構和相關配置作到SpringMVC同樣的效果,可是須要xml配置的地方很多)。
九、 設計思想上,Struts2更加符合OOP的編程思想, SpringMVC就比較謹慎,在servlet上擴展。
十、SpringMVC開發效率和性能高於Struts2。 十一、SpringMVC能夠認爲已經100%零配置。
8. Struts2中的 # 和 % 分別是作什麼的?(2017-11-30-wzz)
(1)使用#獲取context裏面數據
(2)向request域放值(獲取context裏面數據,寫ognl時候,首先添加符號#context的key名稱.域對象名稱)
(3)在頁面中使用ognl獲取
(4)%在struts2標籤中表單標籤
在struts2標籤裏面使用ognl表達式,若是直接在struts2表單標籤裏面使用ognl表達式不識別,只有%以後纔會識別。
9. Struts2中有哪些經常使用結果類型?(2017-12-1-lyq)
1)dispatcher :默認的請求轉發的結果類型,Action 轉發給 JSP
2) chain :Action轉發到另外一個Action (同一次請求)
3) redirect : 重定向,重定向到一個路徑信息,路徑信息沒有限制(不在一個請求中),Action重定向到 JSP
4) redirectAction :Action重定向到另外一個Action
5)stream :將原始數據做爲流傳遞迴瀏覽器端,該結果類型對下載的內容和圖片很是有用。
6)freemarker :呈現freemarker模板。
7)plaintext :返回普通文本內容。
1. 簡述一下hibernate的開發流程(2017-11-24-gxb)
第一步:加載 hibernate 的配置文件,讀取配置文件的參數(jdbc 鏈接參數,數據 庫方言,hbm 表與對象關係映射文件)
第二步:建立 SessionFactory 會話工廠(內部有鏈接池)
第三步:打開 session 獲取鏈接,構造 session 對象(一次會話維持一個數據鏈接,也是一級緩存)
第四步:開啓事務
第五步:進行操做
第六步:提交事務
第七步:關閉 session(會話)將鏈接釋放
第八步:關閉鏈接池
2. hibernate中對象的三種狀態(2017-11-24-gxb)
瞬時態(臨時態、自由態):不存在持久化標識 OID,還沒有與 Hibernate Session 關聯對象, 被認爲處於瞬時態,失去引用將被 JVM 回收
持久態:存在持久化標識 OID,與當前 session 有關聯,而且相關聯的 session 沒有關閉 , 而且事務未提交
脫管態(離線態、遊離態):存在持久化標識 OID,但沒有與當前 session 關聯,脫管狀態 改變 hibernate 不能檢測到
3. hibernate的緩存機制。(2017-11-24-gxb)
Hibernate緩存分爲兩層:Hibernate的一級緩存和Hibernate二級緩存。
1.Hibernate一級緩存(Session的緩存):
(1)Session實現了第一級Cache,屬於事務級數據緩衝。一旦事務結束,緩存隨之失效。一個Session的生命週期對應一個數據庫事務或一個程序事務。
(2)Session-Cache老是被打開而且不能被關閉的。
(3)Session-Cache保證一個Session中兩次請求同一個對象時,取得的對象是同一個Java實例,有時它能夠避免沒必要要的數據衝突。
a.在對於同一個對象進行循環引用時,不至於產生堆棧溢出。
b.當數據庫事務結束時,對於同一數據錶行,不會產生數據衝突。由於對於數據庫中的一行,最多有一個對象來表示它。
c.一個事務中可能會有不少個處理單元,在每個處理單元中作的操做都會當即被其餘的數據單元得知。
2.Hibernate二級緩存(SessionFactory的緩存):
(1)二級緩存是SessionFactory範圍內的緩存,全部的Session共享同一個二級緩存。在二級緩存中保存持久化實例的散裝形式的數據。
(2)持久化不一樣的數據須要不一樣的Cache策略,好比一些因素將影響Cache策略的選擇:數據的讀/寫比例、數據表是否能被其餘的應用程序所訪問等。
(3)設置Hibernate二級緩存須要分兩步:首先,確認使用什麼數據併發策略。而後,配置緩存過時時間並設置Cache提供器。
4. Hibernate的查詢方式有哪些?(2017-11-24-gxb)
Hibernate的查詢方式常見的主要分爲三種: HQL, QBC(命名查詢), 以及使用原生SQL查詢(SqlQuery)
參考資料:http://blog.csdn.net/u010963948/article/details/16818043
5. Hibernate和Mybatis的區別?(2017-11-23-gxb)
二者相同點:
1)Hibernate與MyBatis均可以是經過SessionFactoryBuider由XML配置文件生成SessionFactory,而後由SessionFactory 生成Session,最後由Session來開啓執行事務和SQL語句。其中SessionFactoryBuider,SessionFactory,Session的生命週期都是差很少的。
2)Hibernate和MyBatis都支持JDBC和JTA事務處理。
1)MyBatis能夠進行更爲細緻的SQL優化,能夠減小查詢字段。
2)MyBatis容易掌握,而Hibernate門檻較高。
1)Hibernate的DAO層開發比MyBatis簡單,Mybatis須要維護SQL和結果映射。
2)Hibernate對對象的維護和緩存要比MyBatis好,對增刪改查的對象的維護要方便。
3)Hibernate數據庫移植性很好,MyBatis的數據庫移植性很差,不一樣的數據庫須要寫不一樣SQL。
4)Hibernate有更好的二級緩存機制,能夠使用第三方緩存。MyBatis自己提供的緩存機制不佳。
6. Hibernate和JDBC優缺點對比(2017-11-29-wzz)
相同點:
1)二者都是java數據庫操做的中間件、
2)二者對數據庫進行直接操做的對象都是線程不安全的,都須要及時關閉。
3)二者均可對數據庫的更新操做進行顯式的事務處理。
不一樣點:
JDBC是SUN公司提供一套操做數據庫的規範,使用java代碼操做數據庫。Hibernate是一個基於jdbc的主流持久化框架,對JDBC訪問數據庫的代碼作了封裝。
使用的SQL語言不一樣:JDBC使用的是基於關係型數據庫的標準SQL語言,Hibernate使用的是HQL(Hibernate query language)語言。
操做的對象不一樣:JDBC操做的是數據,將數據經過SQL語句直接傳送到數據庫中執行,Hibernate操做的是持久化對象,由底層持久化對象的數據更新到數據庫中。
數據狀態不一樣:JDBC操做的數據是「瞬時」的,變量的值沒法與數據庫中的值保持一致,而Hibernate操做的數據是可持久的,即持久化對象的數據屬性的值是能夠跟數據庫中的值保持一致的。
7. 關於Hibernate的orm思想你瞭解多少?(2017-11-29-wzz)
ORM指的是對象關係型映射(Object RelationShip Mapping ),指的就是咱們經過建立實體類對象和數據庫中的表關係進行一一對應,來實現經過操做實體類對象來更改數據庫裏邊的數據信息。這裏邊起到關鍵做用的是經過Hibernate的映射文件+Hibernate的核心配置文件。
詳細內容請見:http://blog.csdn.net/wanghuan203/article/details/7566518
8. get和load的區別?(2017-11-30-wzz)
(1)get是當即加載,load是延時加載。
(2)get會先查一級緩存,再查二級緩存,而後查數據庫;load會先查一級緩存,若是沒有找到,就建立代理對象,等須要的時候去查詢二級緩存和數據庫。(這裏就體現load的延遲加載的特性。)
(3)get若是沒有找到會返回null,load若是沒有找到會拋出異常。
(4)當咱們使用session.load()方法來加載一個對象時,此時並不會發出sql語句,當前獲得的這個對象實際上是一個代理對象,這個代理對象只保存了實體對象的id值,只有當咱們要使用這個對象,獲得其它屬性時,這個時候纔會發出sql語句,從數據庫中去查詢咱們的對象;相對於load的延遲加載方式,get就直接的多,當咱們使用session.get()方法來獲得一個對象時,無論咱們使不使用這個對象,此時都會發出sql語句去從數據庫中查詢出來。
9. 如何進行Hibernate的優化?(2017-11-30-wzz)
(1)數據庫設計調整。
(2)HQL優化。
(3)API的正確使用(如根據不一樣的業務類型選用不一樣的集合及查詢API)。
(4)主配置參數(日誌,查詢緩存,fetch_size, batch_size等)。
(5)映射文件優化(ID生成策略,二級緩存,延遲加載,關聯優化)。
(6)一級緩存的管理。
(7)針對二級緩存,還有許多特有的策略。
(8)事務控制策略。
詳情解釋請見:https://www.cnblogs.com/xhj123/p/6106088.html
10. 什麼是Hibernate 延遲加載?(2017-12-1-lyq)
延遲加載機制是爲了不一些無謂的性能開銷而提出來的,所謂延遲加載就是當在真正須要
數據的時候,才真正執行數據加載操做。在Hibernate中提供了對實體對象的延遲加載以及
對集合的延遲加載,另外在Hibernate3中還提供了對屬性的延遲加載。
延遲加載的過程:經過代理(Proxy)機制來實現延遲加載。Hibernate從數據庫獲取某一個對象數據時、獲取某一個對象的集合屬性值時,或獲取某一個對象所關聯的另外一個對象時,因爲沒有使用該對象的數據(除標識符外),Hibernate並不從數據庫加載真正的數據,而只是爲該對象建立一個代理對象來表明這個對象,這個對象上的全部屬性都爲默認值;只有在真正須要使用該對象的數據時才建立這個真正的對象,真正從數據庫中加載它的數據。
11. No Session問題原理及解決方法?(2017-12-4-lyq)
Nosession問題報錯以下:
根據字面上的意思,是指代理不能被初始化,session已經關閉。
Nosession問題產生的緣由:
當執行Session的load()方法時,Hibernate不會當即執行查詢所查詢對象關聯的對象(在此咱們統稱被關聯的對象類爲A類),僅僅返回A類的代理類的實例,這個代理類具由如下特徵:
(1)由Hibernate在運行時動態生成,它擴展了A類,所以它繼承了A類的全部屬性和方法,但它的實現對於應用程序是透明的。
(2)當Hibernate建立A類代理類實例時,僅僅初始化了它的OID屬性,其餘屬性都爲null,所以這個代理類實例佔用的內存不多。
(3)當應用程序第一次訪問A代理類實例時(例如調用a..getXXX()或a.setXXX()方法),Hibernate會初始化代理類實例,在初始化過程當中執行select語句,真正從數據庫中加載A對象的全部數據。但有個例外,那就是當應用程序訪問A代理類實例的getId()方法時,Hibernate不會初始化代理類實例,由於在建立代理類實例時OID就存在了,沒必要到數據庫中去查詢。
提示:Hibernate採用CGLIB工具來生成持久化類的代理類。CGLIB是一個功能強大的Java字節碼生成工具,它可以在程序運行時動態生成擴展 Java類或者實現Java接口的代理類。
由於Hibernate中若是採用load加載的話(默認的是延遲加載),也就是lazy=true操做,所以,當調用完load後,session便可關閉。由於咱們的session只是放置到了Dao層,表現層根本獲取不到,因此在表現層調用的時候,session已經關閉,報錯。
12. Spring的兩種代理JDK和CGLIB的區別淺談(2017-12-4-lyq)
Java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。
一、若是目標對象實現了接口,默認狀況下會採用JDK的動態代理實現AOP
二、若是目標對象實現了接口,能夠強制使用CGLIB實現AOP
三、若是目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
參考資料:http://blog.csdn.net/tanga842428/article/details/52716875
13. 敘述Session的緩存的做用(2017-12-9-lwl)
(1)減小訪問數據庫的頻率。應用程序從內存中讀取持久化對象的速度顯然比到數據庫中查詢數據的速度快多了,所以Session的緩存能夠提升數據訪問的性能。
(2)保證緩存中的對象與數據庫中的相關記錄保持同步。當緩存中持久化對象的狀態發生了變換,Session並不會當即執行相關的SQL語句,這使得Session可以把幾條相關的SQL語句合併爲一條SQL語句,以便減小訪問數據庫的次數,從而提升應用程序的性能。
14. Session的清理和清空有什麼區別?(2017-12-10-lwl)
Session清理緩存是指按照緩存中對象的狀態的變化來同步更新數據庫;清空是Session的關閉;
15. 請簡述Session的特色有哪些?(2017-12-10-lwl)
(1)不是線程安全的,所以在設計軟件架構時,應該避免多個線程共享同一個Session實例。
(2)Session實例是輕量級的,所謂輕量級是指它的建立和銷燬不須要消耗太多的資源。這意味着在程序中能夠常常建立或銷燬Session對象,例如爲每一個客戶請求分配單獨的Session實例,或者爲每一個工做單元分配單獨的Session實例。
(3)在Session 中,每一個數據庫操做都是在一個事務(transaction)中進行的,這樣就能夠隔離開不一樣的操做(甚至包括只讀操做)。
16. 比較Hibernate三種檢索策略的優缺點(2017-12-10-lwl)
一、當即檢索
優勢:對應用程序徹底透明,無論對象處於持久化狀態,仍是遊離狀態,應用程序均可以方便的從一個對象導航到與它關聯的對象;
缺點:1.select語句太多;2.可能會加載應用程序不須要訪問的對象白白浪費許多內存空間;
二、延遲檢索
優勢:由應用程序決定須要加載哪些對象,能夠避免可執行多餘的select語句,以及避免加載應用程序不須要訪問的對象。所以能提升檢索性能,而且能節省內存空間;
缺點:應用程序若是但願訪問遊++離狀態代理類實例,必須保證他在持久化狀態時已經被初始化;
三、 迫切左外鏈接檢索
優勢:一、對應用程序徹底透明,無論對象處於持久化狀態,仍是遊離狀態,應用程序均可以方便地衝一個對象導航到與它關聯的對象。二、使用了外鏈接,select語句數目少;
缺點:一、可能會加載應用程序不須要訪問的對象,白白浪費許多內存空間;二、複雜的數據庫錶鏈接也會影響檢索性能;
配置job 任務發節日郵件 配置bean標籤jobdetail注入job 配置trigger定時器注入detail 並配置corn 週期執行表達式 配置週期執行工廠 引入trigger
Quartz 是一個開源的做業調度框架,它徹底由 Java 寫成,並設計用於 J2SE 和 J2EE 應用中。它提供了巨大的靈活性而不犧牲簡單性。你可以用它來爲執行一個做業而建立簡單的或複雜的調度。
2.配置文件 applicationContext_job.xml各個屬性做用(2017-12-2-wzz)
(1)、Job:表示一個任務(工做),要執行的具體內容。
(2)、JobDetail:表示一個具體的可執行的調度程序,Job是這個可執行程調度程序所要執行的內容,另外JobDetail還包含了這個任務調度的方案和策略。
(3)、Trigger:表明一個調度參數的配置,何時去調。
(4)、Scheduler:表明一個調度容器,一個調度容器中能夠註冊多個JobDetail和Trigger。當Trigger與JobDetail組合,就能夠被Scheduler容器調度了。
Cron表達式是一個字符串,字符串以5或6個空格隔開,分爲6或7個域,每個域表明一個含義。
域:
Seconds(秒):可出現", - * /"四個字符,有效範圍爲0-59的整數。
Minutes(分鐘):可出現", - * /"四個字符,有效範圍爲0-59的整數 。
Hours(小時):可出現", - * /"四個字符,有效範圍爲0-23的整數 。
DayofMonth(日of月):可出現", - * / ? L W C"八個字符,有效範圍爲0-31的整數。
Month(月):可出現", - * /"四個字符,有效範圍爲1-12的整數。
DayofWeek(日of星期):可出現", - * / ? L C #"四個字符,有效範圍爲1-7的整數1表示星期天,2表示星期一, 依次類推。
Year(年):可出現", - * /"四個字符,有效範圍爲1970-2099年。
4.如何監控Quartz 的 job 執行狀態:運行中,暫停中,等待中? (2017-12-2-wzz)
經過往表(新建一個操做日誌表)裏插入日誌的形式:
1)運行中:經過 JobListener 監聽器來實現運行時更改表信息。
2)暫停中:調用 scheduler.pauseTrigger()方法時,更改表中 job 信息。
3)等待中:新添加的 job 默認給其等待中的狀態,也是更改表中的 job 信息 可是上面這種形式的麻煩之處是得頻繁的往表裏插入數據。
Redis是由意大利人Salvatore Sanfilippo(網名:antirez)開發的一款內存高速緩存數據庫。Redis全稱爲:Remote Dictionary Server(遠程數據服務),該軟件使用C語言編寫,典型的NoSQL數據庫(非關係型數據庫 現實世界中的各類實體以及實體之間的各類聯繫均用關係模型來表示 實體和表有對應關係)服務器,Redis是一個key-value存儲系統,它支持豐富的數據類型,如:string、list、set、zset(sorted set)
Sorted-Sets中的每個成員都會有一個分數(score)與之關聯,Redis正是經過分數來爲集合中的成員進行從小到大的排序。然而須要額外指出的是,儘管Sorted-Sets中的成員必須是惟一的,可是分數(score)倒是能夠重複的。
Redis本質上是一個Key-Value類型的內存數據庫,很像memcached,整個 數據庫通通加載在內存當中進行操做,按期經過異步操做把數據庫數據flush到硬盤 上進行保存 並將更新操做 追加到記錄文件中。由於是純內存操做,Redis的性能很是出色,每秒能夠處理超過 10萬次 讀寫操做,是已知性能最快的Key-Value DB。
Redis的出色之處不只僅是性能,Redis最大的魅力是支持保存多種數據結構,此外單 個value的最大限制是1GB,不像 memcached只能保存1MB的數據,另外Redis 也能夠對存入的Key-Value設置expire時間。
Redis的主要缺點是數據庫容量受到物理內存的限制,不能用做海量數據的高性能讀寫,所以Redis適合的場景主要侷限在較小數據量的高性能操做和運算上。
2. 爲何redis須要把全部數據放到內存中?(2017-11-25-wzz)
Redis爲了達到最快的讀寫速度將數據都讀到內存中,並經過異步的方式將數據寫入磁盤。因此redis具有快速和數據持久化的特徵。若是不將數據放在內存中,磁盤I/O速度爲嚴重影響redis的性能。在內存愈來愈便宜的今天,redis將會愈來愈受歡迎。若是設置了最大使用的內存,則數據已有記錄數達到內存限值後不能繼續插入新值。
3. Redis常見的性能問題都有哪些?如何解決?(2017-11-25-wzz)
(1)、Master寫內存快照,save命令調度rdbSave函數,會阻塞主線程的工做,當快照比較大時對性能影響是很是大的,會間斷性暫停服務,因此Master最好不要寫內存快照。
(2)、Master AOF持久化,若是不重寫AOF文件,這個持久化方式對性能的影響是最小的,可是AOF文件會不斷增大,AOF文件過大會影響Master重啓的恢復速度。Master最好不要作任何持久化工做,包括內存快照和AOF日誌文件,特別是不要啓用內存快照作持久化,若是數據比較關鍵,某個Slave開啓AOF備份數據,策略爲每秒同步一次。
(3)、Master調用BGREWRITEAOF重寫AOF文件,AOF在重寫的時候會佔大量的CPU和內存資源,致使服務load太高,出現短暫服務暫停現象。
(4)、Redis主從複製的性能問題,爲了主從複製的速度和鏈接的穩定性,Slave和Master最好在同一個局域網內
4. Redis最適合的場景有哪些?(2017-11-25-wzz)
(1)、會話緩存(Session Cache)
(2)、全頁緩存(FPC)
(3)、隊列
(4)、排行榜/計數器
(5)、發佈/訂閱
5. Memcache與Redis的區別都有哪些?(2017-11-25-wzz)
(1)、存儲方式不一樣,Memcache是把數據所有存在內存中,數據不能超過內存的大小,斷電後數據庫會掛掉。Redis有部分存在硬盤上,這樣能保證數據的持久性。
(2)、數據支持的類型不一樣 memcahe對數據類型支持相對簡單,redis有複雜的數據類型。
(3)、使用底層模型不一樣 它們之間底層實現方式 以及與客戶端之間通訊的應用協議不同。Redis直接本身構建了VM 機制 ,由於通常的系統調用系統函數的話,會浪費必定的時間去移動和請求。
(4)、支持的value大小不同 redis最大能夠達到1GB,而memcache只有1MB。
6. Redis用過RedisNX嗎?Redis有哪幾種數據結構?(2017-11-14-lyq)
反正我是不知道redisnx是什麼,度娘也不清楚,若是面試中問道本身沒有接觸過或者沒有聽過的技術能夠直接大膽的告訴他,沒有接觸過,或者沒有聽過。
Redis的數據結構有五種,分別是:
String——字符串
String 數據結構是簡單的 key-value 類型,value 不只能夠是 String,也能夠是數字(當數字類型用 Long 能夠表示的時候encoding 就是整型,其餘都存儲在 sdshdr 當作字符串)。 Hash——字典
在Memcached 中,咱們常常將一些結構化的信息打包成 hashmap,在客戶端序列化後存儲爲一個字符串的值(通常是 JSON 格式),好比用戶的暱稱、年齡、性別、積分等。 List——列表
List 說白了就是鏈表(redis 使用雙端鏈表實現的 List),相信學過數據結構知識的人都應該能理解其結構。
Set——集合
Set 就是一個集合,集合的概念就是一堆不重複值的組合。利用 Redis 提供的 Set 數據結構,能夠存儲一些集合性的數據。 Sorted Set——有序集合
和Sets相比,Sorted Sets是將 Set 中的元素增長了一個權重參數 score,使得集合中的元素可以按 score 進行有序排列,
1. 帶有權重的元素,好比一個遊戲的用戶得分排行榜 2.比較複雜的數據結構,通常用到的場景不算太多
優勢:
a) 性能極高– Redis 能支持超過 100K+ 每秒的讀寫頻率。
b) 豐富的數據類型– Redis 支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets數據類型操做。
c) 原子– Redis 的全部操做都是原子性的,同時 Redis 還支持對幾個操做全並後的原子性執行。
attention原子性定義:例如,A想要從本身的賬戶中轉1000塊錢到B的賬戶裏。那個從A開始轉賬,到轉賬結束的這一個過程,稱之爲一個事務。若是在A的賬戶已經減去了1000塊錢的時候,突然發生了意外,好比停電什麼的,致使轉賬事務意外終止了,而此時B的賬戶裏尚未增長1000塊錢。那麼,咱們稱這個操做失敗了,要進行回滾。回滾就是回到事務開始以前的狀態,也就是回到A的賬戶還沒減1000塊的狀態,B的賬戶的原來的狀態。此時A的賬戶仍然有3000塊,B的賬戶仍然有2000塊。咱們把這種要麼一塊兒成功(A賬戶成功減小1000,同時B賬戶成功增長1000),要麼一塊兒失敗(A賬戶回到原來狀態,B賬戶也回到原來狀態)的操做叫原子性操做。若是把一個事務可看做是一個程序,它要麼完整的被執行,要麼徹底不執行,這種特性就叫原子性。
·d)豐富的特性 – Redis 還支持 publish/subscribe, 通知, key 過時等等特性。
缺點:
a). 因爲是內存數據庫,因此,單臺機器,存儲的數據量,跟機器自己的內存大小。雖然redis自己有key過時策略,可是仍是須要提早預估和節約內存。若是內存增加過快,須要按期刪除數據。
b). 若是進行完整重同步,因爲須要生成rdb文件,並進行傳輸,會佔用主機的CPU,並會消耗現網的帶寬。不過redis2.8版本,已經有部分重同步的功能,可是仍是有可能有完整重同步的。好比,新上線的備機。
c). 修改配置文件,進行重啓,將硬盤中的數據加載進內存,時間比較久。在這個過程當中,redis不能
提供服務。
RDB 持久化:該機制能夠在指定的時間間隔內生成數據集的時間點快照(point-in-time snapshot)。
AOF 持久化:記錄服務器執行的全部寫操做命令,並在服務器啓動時,經過從新執行這些命令來還原數據集。 AOF 文件中的命令所有以 Redis 協議的格式來保存,新命令會被追加到文件的末尾。 Redis 還能夠在後臺對 AOF 文件進行重寫(rewrite),使得 AOF 文件的體積不會超出保存數據集狀態所需的實際大小
無持久化:讓數據只在服務器運行時存在。
同時應用AOF 和 RDB:當 Redis 重啓時, 它會優先使用 AOF 文件來還原數據集, 由於 AOF 文件保存的數據集一般比 RDB 文件所保存的數據集更完整。
RDB的優缺點:
優勢:RDB 是一個很是緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件很是適合用於進行備份: 好比說,你能夠在最近的 24 小時內,每小時備份一次 RDB 文件,而且在每月的每一天,也備份一個 RDB 文件。 這樣的話,即便趕上問題,也能夠隨時將數據集還原到不一樣的版本。RDB 很是適用於災難恢復(disaster recovery):它只有一個文件,而且內容都很是緊湊,能夠(在加密後)將它傳送到別的數據中心,或者亞馬遜 S3 中。RDB 能夠最大化 Redis 的性能:父進程在保存 RDB 文件時惟一要作的就是 fork 出一個子進程,而後這個子進程就會處理接下來的全部保存工做,父進程無須執行任何磁盤 I/O 操做。RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
缺點:若是你須要儘可能避免在服務器故障時丟失數據,那麼RDB 不適合你。 雖然 Redis 容許你設置不一樣的保存點(save point)來控制保存 RDB 文件的頻率, 可是, 由於RDB 文件須要保存整個數據集的狀態, 因此它並非一個輕鬆的操做。 所以你可能會至少 5 分鐘才保存一次 RDB 文件。 在這種狀況下, 一旦發生故障停機, 你就可能會丟失好幾分鐘的數據。每次保存 RDB 的時候,Redis 都要 fork() 出一個子進程,並由子進程來進行實際的持久化工做。 在數據集比較龐大時, fork() 可能會很是耗時,形成服務器在某某毫秒內中止處理客戶端; 若是數據集很是巨大,而且 CPU 時間很是緊張的話,那麼這種中止時間甚至可能會長達整整一秒。
AOF的優缺點。
優勢:
一、使用 AOF 持久化會讓 Redis 變得很是耐久(much more durable):你能夠設置不一樣的 fsync 策略,好比無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。 AOF 的默認策略爲每秒鐘 fsync 一次,在這種配置下,Redis 仍然能夠保持良好的性能,而且就算髮生故障停機,也最多隻會丟失一秒鐘的數據( fsync 會在後臺線程執行,因此主線程能夠繼續努力地處理命令請求)。AOF 文件是一個只進行追加操做的日誌文件(append only log), 所以對 AOF 文件的寫入不須要進行 seek , 即便日誌由於某些緣由而包含了未寫入完整的命令(好比寫入時磁盤已滿,寫入中途停機,等等), redis-check-aof 工具也能夠輕易地修復這種問題。
二、Redis 能夠在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操做是絕對安全的,由於 Redis 在建立新 AOF 文件的過程當中,會繼續將命令追加到現有的 AOF 文件裏面,即便重寫過程當中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件建立完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操做。
缺點:
對於相同的數據集來講,AOF 文件的體積一般要大於 RDB 文件的體積。根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在通常狀況下, 每秒 fsync 的性能依然很是高, 而關閉 fsync 能夠讓 AOF 的速度和 RDB 同樣快, 即便在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 能夠提供更有保證的最大延遲時間(latency)。
AOF 在過去曾經發生過這樣的 bug : 由於個別命令的緣由,致使 AOF 文件在從新載入時,沒法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 BRPOPLPUSH 就曾經引發過這樣的 bug 。) 測試套件裏爲這種狀況添加了測試: 它們會自動生成隨機的、複雜的數據集, 並經過從新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中並不常見, 可是對比來講, RDB 幾乎是不可能出現這種 bug 的。
1. 如何使用ActiveMQ解決分佈式事務?(2017-11-21-gxb)
在互聯網應用中,基本都會有用戶註冊的功能。在註冊的同時,咱們會作出以下操做:
1. 收集用戶錄入信息,保存到數據庫
2. 向用戶的手機或郵箱發送驗證碼
等等…
若是是傳統的集中式架構,實現這個功能很是簡單:開啓一個本地事務,往本地數據庫中插入一條用戶數據,發送驗證碼,提交事物。
可是在分佈式架構中,用戶註冊和發送驗證碼是兩個獨立的服務,它們都有各自的數據庫,那麼就不能經過本地事物保證操做的原子性。這時咱們就須要用到ActiveMQ(消息隊列)來爲咱們實現這個需求。
在用戶進行註冊操做的時候,咱們爲該操做建立一條消息,當用戶信息保存成功時,把這條消息發送到消息隊列。驗證碼系統會監聽消息,一旦接受到消息,就會給該用戶發送驗證碼。
問題:
1.如何防止消息重複發送?
解決方法很簡單:增長消息狀態表。通俗來講就是一個帳本,用來記錄消息的處理狀態,每次處理消息以前,都去狀態表中查詢一次,若是已經有相同的消息存在,那麼不處理,能夠防止重複發送。
ActiveMQ、RabbitMQ、kafka。
RabbitMQ: RabbitMQ是使用Erlang編寫的一個開源的消息隊列,自己支持不少的協議:AMQP,XMPP, SMTP, STOMP,也正因如此,它很是重量級,更適合於企業級的開發。同時實現了Broker構架,這意味着消息在發送給客戶端時先在中心隊列排隊。對路由,負載均衡或者數據持久化都有很好的支持。
ActiveMQ: ActiveMQ是Apache下的一個子項目。 相似於ZeroMQ,它可以以代理人和點對點的技術實現隊列。同時相似於RabbitMQ,它少許代碼就能夠高效地實現高級應用場景。
Kafka/Jafka: Kafka是Apache下的一個子項目,是一個高性能跨語言分佈式發佈/訂閱消息隊列系統,而Jafka是在Kafka之上孵化而來的,即Kafka的一個升級版。具備如下特性:快速持久化,能夠在O(1)的系統開銷下進行消息持久化;高吞吐,在一臺普通的服務器上既能夠達到10W/s的吞吐速率;徹底的分佈式系統,Broker、Producer、Consumer都原生自動支持分佈式,自動實現負載均衡;支持Hadoop數據並行加載,對於像Hadoop的同樣的日誌數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。Kafka經過Hadoop的並行加載機制統一了在線和離線的消息處理。Apache Kafka相對於ActiveMQ是一個很是輕量級的消息系統,除了性能很是好以外,仍是一個工做良好的分佈式系統。
MQ選型對比圖
3. ActiveMQ若是消息發送失敗怎麼辦?(2017-11-24-gxb)
Activemq 有兩種通訊方式,點到點形式和發佈訂閱模式。
若是是點到點模式的話,若是消息發送不成功此 消息默認會保存到 activemq 服務端直到有消費者將其消費,因此此時消息是不會丟失的。
若是是發佈訂閱模式的通訊方式,默認狀況下只通知一次,若是接收不到此消息就沒有了。這種場景只適 用於對消息送達率要求不高的狀況。若是要求消息必須送達不能夠丟失的話,須要配置持久訂閱。每一個訂閱端定義一個 id,在訂閱是向 activemq 註冊。發佈消息和接收消息時須要配置發送模式爲持久化。此時 若是客戶端接收不到消息,消息會持久化到服務端,直到客戶端正常接收後爲止。
1. Dubbo的容錯機制有哪些。(2017-11-23-gxb)
Dubbo官網提出總共有六種容錯策略
1)Failover Cluster 模式
失敗自動切換,當出現失敗,重試其它服務器。(默認)
2)Failfast Cluster
快速失敗,只發起一次調用,失敗當即報錯。一般用於非冪等性的寫操做,好比新增記錄。
3)Failsafe Cluster
失敗安全,出現異常時,直接忽略。一般用於寫入審計日誌等操做。
4)Failback Cluster
失敗自動恢復,後臺記錄失敗請求,定時重發。一般用於消息通知操做。
5)Forking Cluster
並行調用多個服務器,只要一個成功即返回。一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。可經過forks=」2」來設置最大並行數。
6)Broadcast Cluster
廣播調用全部提供者,逐個調用,任意一臺報錯則報錯。(2.1.0開始支持) 一般用於通知全部提供者更新緩存或日誌等本地資源信息。
總結:在實際應用中查詢語句容錯策略建議使用默認Failover Cluster ,而增刪改建議使用 Failfast Cluster 或者 使用 Failover Cluster(retries=」0」) 策略 防止出現數據 重複添加等等其它問題!建議在設計接口時候把查詢接口方法單獨作一個接口提供查詢。
2. 使用dubbo遇到過哪些問題?(2017-11-23-gxb)
1. 增長提供服務版本號和消費服務版本號
這個具體來講不算是一個問題,而是一種問題的解決方案,在咱們的實際工做中會面臨各類環境資源短缺的問題,也是很實際的問題,剛開始咱們還能夠提供一個服務進行相關的開發和測試,可是當有多個環境多個版本,多個任務的時候就不知足咱們的需求,這時候咱們能夠經過給提供方增長版本的方式來區分.這樣可以剩下不少的物理資源,同時爲從此更換接口定義發佈在線時,可不停機發布,使用版本號.
引用只會找相應版本的服務,例如:
2. dubbo reference註解問題
@Reference只能在springbean實例對應的當前類中使用,暫時沒法在父類使用;若是確實要在父類聲明一個引用,可經過配置文件配置dubbo:reference,而後在須要引用的地方跟引用springbean同樣就能夠了.
3.出現RpcException: No provider available for remote service異常,表示沒有可用的服務提供者
1). 檢查鏈接的註冊中心是否正確
2). 到註冊中心查看相應的服務提供者是否存在
3). 檢查服務提供者是否正常運行
4. 服務提供者沒掛,但在註冊中內心看不到
首先,確認服務提供者是否鏈接了正確的註冊中心,不僅是檢查配置中的註冊中心地址,並且要檢查實際的網絡鏈接。
其次,看服務提供者是否很是繁忙,好比壓力測試,以致於沒有CPU片斷向註冊中心發送心跳,這種狀況,減少壓力,將自動恢復。
3. Dubbo的鏈接方式有哪些?(2017-12-1-lyq)
Dubbo的客戶端和服務端有三種鏈接方式,分別是:廣播,直連和使用zookeeper註冊中心。
3.一、Dubbo廣播
這種方式是dubbo官方入門程序所使用的鏈接方式,可是這種方式有不少問題。在企業開發中,不使用廣播的方式。
taotao-manager服務端配置:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
客戶端配置taotao-manager-web的配置以下:
1.
2.
3.
4.
5.
6.
7.
8.
3.二、Dubbo直連
這種方式在企業中通常在開發中環境中使用,可是生產環境不多使用,由於服務是直接調用,沒有使用註冊中心,很難對服務進行管理。Dubbo直連,首先要取消廣播,而後客戶端直接到指定須要的服務的url獲取服務便可。
服務端配置:taotao-manager的修改以下,取消廣播,註冊中心地址爲N/A
1.
2.
3.
4. -->
5.
6.
7.
8.
9.
10.
11.
客戶端配置:taotao-manager-web配置以下,取消廣播,從指定的url中獲取服務
1.
2.
3.
4.
5. -->
6.
7.
8.
9.
3.三、zookeeper註冊中心
Dubbo註冊中心和廣播註冊中心配置相似,不過須要指定註冊中心類型和註冊中心地址,這個時候就不是把服務信息進行廣播了,而是告訴給註冊中心進行管理,這個時候咱們就須要有一個註冊中心。
官方推薦使用zookeeper做爲註冊中心。
3.3.一、Zookeeper介紹
zookeeper在dubbo所處的位置:
1)Provider: 暴露服務的服務提供方。
2)Consumer: 調用遠程服務的服務消費方。
3)Registry: 服務註冊與發現的註冊中心。
4) Monitor: 統計服務的調用次調和調用時間的監控中心。
5)Container: 服務運行容器。
調用關係說明:
1)服務容器負責啓動,加載,運行服務提供者。
2)服務提供者在啓動時,向注冊中心註冊本身提供的服務。
3)服務消費者在啓動時,向註冊中心訂閱本身所需的服務。
4)註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者。
5)服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。
6)服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。
能夠使用apache提供的ab工具測試。
參考資料:http://blog.csdn.net/whynottrythis/article/details/46495309
1. Nginx反向代理爲何可以提高服務器性能?(2017-11-24-gxb)
對於後端是動態服務來講,好比 Java 和 PHP。這類服務器(如 JBoss 和 PHP-FPM)的 IO 處理能力每每不高。 Nginx 有個好處是它會把 Request 在讀取完整以前 buffer 住,這樣交給後端的就是一個完整的 HTTP
請求,從而提升後端的效率,而不是斷斷續續的傳遞(互聯網上鍊接速度通常比較慢)。 一樣,Nginx 也能夠把 response 給 buffer 住,一樣也是減輕後端的壓力。
2. Nginx 和 Apache 各有什麼優缺點? (2017-11-24-gxb)
nginx 相對 apache 的優勢:
1)輕量級,一樣起web服務,比apache佔用更少的內存及資源
2)抗併發,nginx處理請求是異步非阻塞的,而apache則是阻塞型的,在高併發下nginx能保持
3)低資源低消耗高性能
4)高度模塊化的設計,編寫模塊相對簡單
5)社區活躍,各類高性能模塊出品迅速啊
apache 相對 nginx 的優勢:
1)rewrite,比nginx的rewrite強大
2)模塊超多,基本想到的均可以找到
3)少bug,nginx的bug相對較多
4)超穩定,通常來講,須要性能的 web 服務,用 nginx 。 若是不須要性能只求穩定,那就 apache 吧。
3. Nginx 多進程模型是如何實現高併發的?(2017-12-5-lyq)
進程數與併發數不存在很直接的關係。這取決取server 採用的工做方式。若是一個 server 採用一個進程負責一個 request 的方式,那麼進程數就是併發數。那麼顯而易見的,就是會有不少進程在等待中。等什麼?最多的應該是等待網絡傳輸。
Nginx 的異步非阻塞工做方式正是利用了這點等待的時間。在須要等待的時候,這些進程就空閒出來待命了。所以表現爲少數幾個進程就解決了大量的併發問題。apache 是如何利用的呢,簡單來講:一樣的 4 個進程,若是採用一個進程負責一個 request 的方式,那麼,同時進來 4 個 request 以後,每一個進程就負責其中一個,直至會話關閉。期間,若是有第5 個 request 進來了。就沒法及時反應了,由於 4 個進程都沒幹完活呢,所以,通常有個調度進程,每當新進來了一個 request,就新開個進程來處理。nginx 不這樣,每進來一個 request,會有一個 worker 進程去處理。但不是全程的處理,處理到什麼程度呢?處理到可能發生阻塞的地方,好比向上遊(後端)服務器轉發 request,並等待請求返回。那麼,這個處理的 worker 不會這麼傻等着,他會在發送完請求後,註冊一個事件: 「若是 upstream
返回了,告訴我一聲,我再接着幹」。因而他就休息去了。此時,若是再有 request 進來,他就能夠很快再按這種方式處理。而一旦上游服務器返回了,就會觸發這個事件, worker 纔會來接手,這個request 纔會接着往下走。
因爲web server 的工做性質決定了每一個 request 的大部份生命都是在網絡傳輸中,實際上花費在 server機器上的時間片很少。這是幾個進程就解決高併發的祕密所在。webserver 恰好屬於網絡 io 密集型應用,不算是計算密集型。異步,非阻塞,使用 epoll,和大量細節處的優化。也正是 nginx 之因此然的技術基石。
1. 簡單介紹一下zookeeper以及zookeeper的原理。(2017-11-24-gxb)
ZooKeeper 是一個分佈式的,開放源碼的分佈式應用程序協調服務,是Google 的 Chubby 一個開源的實現,是Hadoop 和 Hbase 的重要組件。它是一個爲分佈式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分佈式同步、組服務等。
ZooKeeper 的目標就是封裝好複雜易出錯的關鍵服務,將簡單易用的接口和性能高效、 功能穩定的系統提供給用戶。
ZooKeeper 包含一個簡單的原語集, 提供 Java 和 C 的接口。
ZooKeeper 代碼版本中,提供了分佈式獨享鎖、選舉、隊列的接口,代碼在 zookeeper-3.4.3\src\recipes。其中分佈鎖和隊列有 Java 和 C 兩個版本,選舉只有 Java 版本。
原理:
ZooKeeper 是以 Fast Paxos 算法爲基礎的,Paxos 算法存在活鎖的問題,即當有多個 proposer 交錯提交時,有可能互相排斥致使沒有一個 proposer 能提交成功,而 Fast Paxos 做了一些優化,經過選舉產生一個 leader (領導者),只有 leader 才能提交 proposer,具體 算法可見 Fast Paxos。所以,要想弄懂 ZooKeeper 首先得對 Fast Paxos 有所瞭解。
ZooKeeper 的基本運轉流程:一、選舉 Leader。二、同步數據。三、選舉 Leader 過程當中算法有不少,但要達到的選舉標準是一致的。 四、Leader 要具備最高的執行 ID,相似 root 權限。 五、集羣中大多數的機器獲得響應並 follow 選出的 Leader。
Solr 是一個獨立的企業級搜索應用服務器,它對外提供相似於Web-service 的 API 接口。 用戶能夠經過 http 請求,向搜索引擎服務器提交必定格式的 XML 文件,生成索引;也能夠 經過 Http Get 操做提出查找請求,並獲得 XML 格式的返回結果。
特色:
Solr 是一個高性能,採用 Java5 開發,基於 Lucene 的全文搜索服務器。同時對其進行 了擴展,提供了比 Lucene 更爲豐富的查詢語言,同時實現了可配置、可擴展並對查詢性能 進行了優化,而且提供了一個完善的功能管理界面,是一款很是優秀的全文搜索引擎。
工做方式:
文檔經過Http 利用 XML 加到一個搜索集合中。查詢該集合也是經過 http 收到一個 XML/JSON 響應來實現。它的主要特性包括:高效、靈活的緩存功能,垂直搜索功能,高亮 顯示搜索結果,經過索引複製來提升可用性,提供一套強大 Data Schema 來定義字段,類 型和設置文本分析,提供基於Web 的管理界面等。
2. solr怎麼設置搜索結果排名靠前?(2017-11-24-gxb)
能夠設置文檔中域的boost 值,boost 值越高,計算出來的相關度得分就越高,排名也就越靠前。此方法可以把熱點商品或者推廣商品的排名提升。
3. solr中IK分詞器原理是什麼?(2017-11-24-gxb)
Ik 分詞器的分詞原理本質上是詞典分詞。先在內存中初始化一個詞典,而後在分詞過程當中挨個讀取字符,和字典中的字符相匹配,把文檔中的全部的詞語拆分出來的過程。
1. 什麼是webService?(2017-11-24-lyq)
WebService 是一種跨編程語言和跨操做系統平臺的遠程調用技術。所謂跨編程語言和跨操做平臺,就是說服務端程序採用 java 編寫,客戶端程序則能夠採用其餘編程語言編寫,反之亦然!跨操做系統平臺則是指服務端程序和客戶端程序能夠在不一樣的操做系統上。
RMI是java語言自己提供的遠程通信協議,穩定高效,是EJB的基礎。但它只能用於JAVA程序之間的通信。
Hessian和Burlap是caucho公司提供的開源協議,基於HTTP傳輸,服務端不用開防火牆端口。協議的規範公開,能夠用於任意語言。跨平臺有點小問題。
Httpinvoker是SpringFramework提供的遠程通信協議,只能用於JAVA程序間的通信,且服務端和客戶端必須使用SpringFramework。
Web service是鏈接異構系統或異構語言的首選協議,它使用SOAP形式通信,能夠用於任何語言,目前的許多開發工具對其的支持也很好。
效率相比:RMI > Httpinvoker >= Hessian >> Burlap >> web service。
1. 談談你對restful的理解以及在項目中的使用?(2017-11-30-wzz)
注意:下面回答內容來自百度百科。
一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。REST 指的是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是 RESTful。它結構清晰、符合標準、易於理解、擴展方便,因此正獲得愈來愈多網站的採用。
給你們推薦以下一篇博客,該博客從多個維度講解了什麼是Restful而且給了Restful風格樣式的API接口。
http://blog.csdn.net/liuwenbiao1203/article/details/52351129
A. 應用服務器
B. 虛擬機
C. 垃圾回收器
D. 編譯器
1.2 一個棧的輸入序列爲1 2 3 4 5,則下列序列中不多是棧輸出的序列的是(A)
A. 5 4 1 3 2
B. 2 3 4 1 5
C. 1 5 4 3 2
D. 2 3 1 4 5
1.3下列那一個選項按照順序包括了OSI模型的7個層次( C)
A. 物理層 數據鏈路層 傳輸層 網絡層 會話層 表示層 應用層
B. 物理層 數據鏈路層 會話層 網絡層 傳輸層 表示層 應用層
C. 物理層 數據鏈路層 網絡層 傳輸層 會話層 表示層 應用層
D. 網絡層 傳輸層 物理層 數據鏈路層 會話層 表示層 應用層
1.4當客戶度關閉一個從鏈接池中獲取的鏈接, 會發生下面哪種狀況?(A)
A. 鏈接不會關閉, 只是簡單地歸還給鏈接池
B. 鏈接被關閉 , 但又被從新打開並歸還給鏈接池
C. 鏈接永久性關閉
A. eval
B. escape
C. setTimeout
D. parseFloat
1.6你使用mkdir命令建立一個臨時的文件夾/tmp/aaa, 並將一些文件複製其中,使用完後要刪除/mnt/tmp文件夾及其中的全部文件, 應該使用命令(B)
A. rm /tmp/aaa
B. rm –r /tmp/aaa
C. rmdir –r /tmp/aaa
D. rmdir /tmp/aaa
1.7在UML提供的圖中, ( C)用於按數據順序描述對象間的交互
A. 協做圖
B. 網絡圖
C. 序列圖
D. 狀態圖
A. 高峯時段日處理業務量100000
B. 高峯時段平均每秒請求數80
C. 同時在線用戶100
D. 平均每秒用戶請求 50
1.9不一樣級別的用戶對同一對象擁有不一樣的訪問權利或某個客戶端不能直接操做到某個對象,但有必須和那個對象有所互動, 這種狀況最好使用什麼設計模式.( D)
A.Bridge 模式
B. Fa?ade 模式
C. Adapter 模式
D. Proxy 模式
A. LinkedHashSet
B. HashSet
C. TreeSet
D. AbstractSet
import java.util.Iterator;
import java.util.TreeSet;
public class numberRandom {
String[] stra = {"1","2","2","3","4","5"};
int n = stra.length;
boolean[] visited = new boolean[n];
String result = "";
TreeSet ts = new TreeSet();
int[][] a = new int[n][n];
private void searchMap()
{
for(int i=0;i
{
for(int j=0;j
{
if(i==j)
{
a[i][j]=0;
}else{
a[i][j]=1;
}
}
}
//3和5不能相連
a[3][5]=0;
a[5][3]=0;
//開始遍歷
for(int i=0;i
{
search(i);
}
Iterator it = ts.iterator();
while(it.hasNext())
{
String str =it.next();
//4不能在第三位
if(str.indexOf("4")!=2){
System.out.println(str);
}
}
}
private void search(int startIndex){
visited[startIndex] = true;
result = result + stra[startIndex];
if(result.length() ==n)
{
ts.add(result);
}
for(int j=0;j
{
if(a[startIndex][j]==1&&visited[j]==false)
{
search(j);
}else
{
continue;
}
}
//一個result結束後踢掉最後一個,尋找別的可能性,若沒有的話,則繼續向前踢掉當前最後一個
result = result.substring(0,result.length()-1);
visited[startIndex] = false;
}
public static void main(String[] args){
new numberRandom().searchMap();
}
}
2.2一個數若是剛好等於它的因子之和,這個數就稱爲」完數」.例如 6 = 1+2+3。編程找出1000之內的全部完數。
public class wsTest { public static void main(String[] args) { for(int m=2;m<1000;m++){ int s=0; for(int i=1;i
1. 問答題
String str1 = 「hello」;
String str2 = 「he」+new String(「llo」);
String str3 = 「he」+」llo」;
System.err.println(str1== str2);
System.err.println(str1 == str3);
答案: false true
1.2 HashSet裏的元素是不能重複的, 那用什麼方法來區分重複與否呢?
答案:當往集合在添加元素時,調用add(Object)方法時候,首先會調用Object的hashCode()方法判hashCode是否已經存在,如不存在則直接插入元素;
若是已存在則調用Object對象的equals()方法判斷是否返回true,若是爲true則說明元素已經 存在,如爲false則插入元素。
1.3. List ,Set, Map是否繼承來自Collection接口? 存取元素時, 有何差別?
答案:List,Set是繼承Collection接口; Map不是。
List:元素有放入順序,元素可重複 ,經過下標來存取 和值來存取
Map:元素按鍵值對存取,無放入順序
Set:元素無存取順序,元素不可重複(注意:元素雖然無放入順序,可是元素在set中的位置是有該元素的HashCode決定的,其位置實際上是固定的)
參考網址:https://www.cnblogs.com/zhangshiwen/p/5830062.html
按值傳遞是指的是在方法調用時,傳遞的參數是按值的拷貝傳遞。
按值傳遞重要特色:傳遞的是值的拷貝,也就是說傳遞後就互不相關了
示例以下:
public class TempTest {
private void test1(int a){
a = 5;
System.out.println("test1方法中的a="+a);
}
public static void main(String[] args) {
TempTest t = new TempTest();
int a = 3;
t.test1(a);//傳遞後,test1方法對變量值的改變不影響這裏的a
System.out.println(」main方法中的a=」+a);
}
}
運行結果是:
test1方法中的a=5
main方法中的a=3
按引用傳遞是指的是在方法調用時,傳遞的參數是按引用進行傳遞,其實傳遞的引用的地址,也就是變量所對應的內存空間的地址。傳遞的是值的引用,也就是說傳遞前和傳遞後都指向同一個引用(也就是同一個內存空間)。
示例以下:
public class TempTest {
private void test1(A a){
a.age = 20;
System.out.println("test1方法中的age="+a.age);
}
public static void main(String[] args) {
TempTest t = new TempTest();
A a = new A();
a.age = 10;
t.test1(a);
System.out.println(」main方法中的age=」+a.age);
}
}
class A{
public int age = 0;
運行結果以下:
[java] view plain copy
test1方法中的age=20
main方法中的age=20
1.5 switch是否做用在byte上, 是否能做用在long上, 是否能做用在String上?
答案:switch可做用於char byte short int;
switch可做用於char byte short int對應的包裝類;
switch不可做用於long double float boolean,包括他們的包裝類;
1.6 Java語言如何進行異常處理? 請寫出幾個常見的運行時異常的編譯時異常
答案:java語言進行異常處理的方式有:
throws: throws是方法可能拋出異常的聲明。(用在聲明方法時,表示該方法可能要拋出異常) 在方法上,
throw : throw是語句拋出一個異常。拋出一個異常對象.在方法內
常見的運行時異常的編譯時異常:
NullPointerException - 空指針引用異常
ClassCastException - 類型強制轉換異常。
IllegalArgumentException - 傳遞非法參數異常。
ClassNotFoundException - 類找不到異常
ArrayStoreException - 向數組中存放與聲明類型不兼容對象異常
IndexOutOfBoundsException - 下標越界異常
NegativeArraySizeException - 建立一個大小爲負數的數組錯誤異常
NumberFormatException - 數字格式異常
SecurityException - 安全異常
UnsupportedOperationException - 不支持的操做異常
所謂事務是用戶定義的一個數據庫操做序列,這些操做要麼全作要麼全不作,是一個不可分割的工做單位。例如,在關係數據庫中,一個事務能夠是一條SQL語句、一組SQL語句或整個程序。
簡單舉個例子就是你要同時修改數據庫中兩個不一樣表的時候,若是它們不是一個事務的話,當第一個表修改完,但是第二表改修出現了異常而沒能修改的狀況下,就只有第二個表回到未修改以前的狀態,而第一個表已經被修改完畢。而當你把它們設定爲一個事務的時候,當第一個表修改完,但是第二表改修出現了異常而沒能修改的狀況下,第一個表和第二個表都要回到未修改的狀態!這就是所謂的事務回滾。
例如,在將資金從一個賬戶轉移到另外一個賬戶的銀行應用中,一個賬戶將必定的金額貸記到一個數據庫表中,同時另外一個賬戶將相同的金額借記到另外一個數據庫表中。因爲計算機可能會因停電、網絡中斷等而出現故障,所以有可能更新了一個表中的行,但沒有更新另外一個表中的行。若是數據庫支持事務,則能夠將數據庫操做組成一個事務,以防止因這些事件而使數據庫出現不一致。若是事務中的某個點發生故障,則全部更新均可以回滾到事務開始以前的狀態。若是沒有發生故障,則經過以完成狀態提交事務來完成更新。
3、騰訊(2016年校招面試題2017-11-29-wzy)
1.1已知一棵二叉樹,若是先序遍歷的節點順序是: ADCEFGHB ,中序遍歷是: CDFEGHAB ,則後序遍歷結果爲:( D )
A. CFHGEBDA
B. CDFEGHBA
C. FGHCDEBA
D. CFHGEDBA
知識點
對於二叉樹的遍歷方式通常分爲三種先序、中序、後序三種方式:
先序遍歷(根左右)
若二叉樹爲空,則不進行任何操做:不然
一、訪問根結點。
二、先序方式遍歷左子樹。
三、先序遍歷右子樹。
中序遍歷(左根右)
若二叉樹爲空,則不進行任何操做:不然
一、中序遍歷左子樹。
二、訪問根結點。
三、中序遍歷右子樹。
後序遍歷(左右根)
若二叉樹爲空,則不進行任何操做:不然
一、後序遍歷左子樹。
二、後序遍歷右子樹。
三、放問根結點。
所以,根據題目給出的先序遍歷和中序遍歷,能夠畫出二叉樹:
1.2下列哪兩個數據結構,同時具備較高的查找和刪除性能?(CD)
A. 有序數組
B. 有序鏈表
C. AVL 樹
D. Hash 表
知識點:
平衡二叉樹的查找,插入和刪除性能都是O(logN) ,其中查找和刪除性能較好;哈希表的查找、插入和刪除性能都是 O(1) ,都是最好的。因此最後的結果選擇: CD
1.3下列排序算法中,哪些時間複雜度不會超過 nlogn?(BC)
A. 快速排序
B. 堆排序
C. 歸併排序
D. 冒泡排序
知識點
根據上圖,觀察平均狀況,最好最差狀況的時間複雜度基本能夠知道答案了,最後結果選擇:BC。
1.4初始序列爲 1 8 6 2 5 4 7 3 一組數採用堆排序,當建堆(小根堆)完畢時,堆所對應的二叉樹中序遍歷序列爲:( A )
A. 8 3 2 5 1 6 4 7
B. 3 2 8 5 1 4 6 7
C. 3 8 2 5 1 6 7 4
D. 8 2 3 5 1 4 7 6
初始化序列:1 8 6 2 5 4 7 3,,小根堆就是要求結點的值小於其左右孩子結點的值,左右孩子的大小沒有關係,那麼小根堆排序以後爲:1 2 4 3 5 6 7 8;
中序遍歷:左根右,故遍歷結果爲:8 3 2 5 1 6 4 7
故最後選擇的結果:A
int foo(int n)
{
if(n<2)return n;
return foo(n-1)+foo(n-2);
}
A.5
B.7
C.8
D.1
A.37.5%
B.32.5%
C.28.6%
D.26.1%
這道題首先得了解犯罪率是什麼?犯罪率就是犯罪人數與總人口數的比。所以能夠直接得出公式:( 3 0.01% ) / ( 3 0.01% + 5 * 0.015% ) = 28.6%
固然若是很差理解的話,咱們能夠實例化,好比B 區假設 5000 人,A 區 3000 人,A 區的犯罪率爲 0.01%,那麼 A 區犯罪人數爲 30 人,B 區的犯罪率爲 0.015% ,那麼 B 區的犯罪人數爲 75 人 ,求發生在 A 區的可能性,就是說 A 區的犯罪人數在總犯罪人數的多少,也就是 30/(30+75)=0.2857
固然,也能夠迴歸到咱們高中遺忘的知識:
假設C表示犯案屬性
在A區犯案几率:P(C|A)=0.01%
在B區犯案几率:P(C|B)=0.015%
在A區機率:P(A)=3/8
在B區機率:P(B)=5/8
犯案几率:P(C)=(3/80.01%+5/80.015%)
根據貝葉斯公式:P(A|C) = P(A,C) / P(C) = [P(C|A) P(A)] / [ P(C|A) P(A)+ P(C|B) P(B) ] 也能夠算出答案來
故,最後結果選擇爲:C
1.6 Unix系統中,哪些能夠用於進程間的通訊?(ABCD )
A.Socket
B.共享內存
C.消息隊列
D.信號量
知識點
管道(Pipe)及有名管道(named pipe):管道可用於具備親緣關係進程間的通訊,有名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊;
信號(Signal):信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進程還能夠發送信號給進程自己;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又可以統一對外接口,用sigaction函數從新實現了signal函數);
報文(Message)隊列(消息隊列):消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺點。
共享內存:使得多個進程能夠訪問同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。每每與其它通訊機制,如信號量結合使用,來達到進程間的同步及互斥。
信號量(semaphore):主要做爲進程間以及同一進程不一樣線程之間的同步手段。
套接口(Socket):更爲通常的進程間通訊機制,可用於不一樣機器之間的進程間通訊。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
故最後選擇的結果爲:ABCD
A.棧區
B.堆區
C.全局區
D.代碼區
靜態變量的修飾關鍵字:static,又稱靜態全局變量。故最後選擇的結果爲: C
A. 在Name字段上添加主鍵
B. 在Name字段上添加索引
C. 在Age字段上添加主鍵
D. 在Age字段上添加索引
1.9 IP地址131.153.12.71是一個(B)類IP地址
A.A
B.B
C.C
D.D
知識點
1.10瀏覽器訪問某頁面,HTTP 協議返回狀態碼爲 403 時表示:( B )
A. 找不到該頁面
B. 禁止訪問
C. 內部服務器訪問
D. 服務器繁忙
1.11若是某系統 15*4=112 成立,則系統採用的是( A )進制
A.6
B.7
C.8
D.9
這題由於是選擇題,咱們能夠直接從A 的選項開始,假設是 6 進制的,咱們把等式 15 4 = 112 轉爲十進制,就是 11 4 = 44,最後驗證等式是否成立,明顯等式是成立的,所以答案已經出來了,選擇 A 。
固然咱們也能夠假設是X 進制,且咱們知道 X 大於 5, 則:(x+5)4 = xx +x +2,因此最後計算的結果也爲 6。
1.12 TCP和 IP 分別對應了 OSI 中的哪幾層?(CD)
A. Application layer
B. Presentation layer
C. Transport layer
D. Network layer
知識點
1.13一個棧的入棧序列是A,B,C,D,E,則棧的不可能的輸出序列是?(C)
A.EDCBA
B.DECBA
C.DCEAB
D.ABCDE
堆棧分別是先進後出,後進先出,
選項a 是 abcde 先入棧,而後依次出棧,正好是 edcba
選項b 是 abcd 先依次入棧,而後 d 出棧, e 再入棧, e 出棧
選項c 是錯誤的,不可能 a 先出棧
選項d 是 a 入棧,而後 a 出棧;b 再入棧, b 出棧.依此類推
最後的結果選擇C。
A.stack
B.data section
C.register set
D.file fd
知識點
線程共享的內容包括:
1.進程代碼段
2.進程的公有數據(利用這些共享的數據,線程很容易的實現相互之間的通信)
3.進程打開的文件描述符、
4.信號的處理器、
5.進程的當前目錄和
6.進程用戶ID與進程組ID
線程獨有的內容包括:
1.線程ID
2.寄存器組的值
3.線程的堆棧
4.錯誤返回碼
5.線程的信號屏蔽碼
因此選擇爲BD。
1.15對於派生類的構造函數,在定義對象時構造函數的執行順序爲?(D)
1:成員對象的構造函數
2:基類的構造函數
3:派生類自己的構造函數
A.123
B.231
C.321
D.213
A. 使用了局部變量
B. 有一個分支不調用自身
C. 使用了全局變量或者使用了一個或多個參數
D. 沒有循環調用
A. 分析單詞是怎樣構成的
B. 分析單詞串是如何構成語言和說明的
C. 分析語句和說明是如何構成程序的
D. 分析程序的結構
知識點
1.詞法分析(lexical analysis)
詞法分析是編譯過程的第一個階段。這個階段的任務是從左到右的讀取每一個字符,而後根據構詞規則識別單詞。詞法分析能夠用lex等工具自動生成。
2.語法分析(syntax analysis)
語法分析是編譯過程的一個邏輯階段。語法分析在詞法分析的基礎上,將單詞序列組合成各種語法短語,如「程序」,「語句」,「表達式」等等。語法分析程序判斷程序在結構上是否正確。
3.語義分析(semantic analysis)
屬於邏輯階段。對源程序進行上下文有關性質的審查,類型檢查。如賦值語句左右端類型匹配問題。
因此BCD 都屬於詞法分析,選擇結果爲 BCD。
A.空閒讓進
B.忙則等待
C.有限等待
D.讓權等待
A. CPU調度給優先級更高的線程
B. 阻塞的線程得到資源或者信號
C. 在時間片輪轉的狀況下,若是時間片到了
D. 得到spinlock未果
A. 狀態模式
B. 裝飾模式
C. 代理模式
D. 觀察者模式
4、北京寶藍德股份科技有限公司(2017-12-03-wmm)
public class Test{
public static void main (String[] args){
List a = null;
test(a);
System.out.println(a.size());
}
public static void test(List a){
a=new arrayList();
a.add(「abc」);
}
}
A.0
B.1
C.Java.lang.NullPointerException
D.以上都不正確
1.2 Linux下查看進程佔用的CPU的百分比, 使用工具(A)
A. Ps
B. Cat
C. More
D. Sep
1.3 JVM內存裏哪一個區域不可能發生OutOfMerncyError( A)
A. 程序計數器
B. 堆
C. 方法區
D. 本地方法棧
1.4 下面關於阻塞隊列(java.util.concurrent.BlockingQueue)的說法不正確的是(C)
A. 阻塞隊列是線程安全的
B. 阻塞隊列的主要應用場景是「生產者-消費者」模型
C. 阻塞隊列裏的元素不能爲null
D. 阻塞隊列的實現必須顯示地設置容量
A. CountDownLatch
B. CyclicBarrier
C. Semaphore
D. FutureTask
2.1 java中, 爲何基類不能作爲HashMap的鍵值, 而只能是引用類型,把引用類型做爲HashMap的鍵值, 須要注意哪些地方?
答案:引用類型和原始類型的行爲徹底不一樣,而且它們具備不一樣的語義。引用類型和原始類型具備不一樣的特徵和用法,它們包括:大小和速度問題,這種類型以哪一種類型的數據結構存儲,當引用類型和原始類型用做某個類的實例數據時所指定的缺省值。對象引用實例變量的缺省值爲null,而原始類型實例變量的缺省值與它們的類型有關。
public class StringUtil{
int compare(char[] v1,char[] v2) {
String str1 = new String(v1);
String str2 = new String(v2);
int result = str1.compareTo(str2);
return result == 0 ? 0 : (result > 0 ? 1 : -1);
}
}
2.3 Java出現OutOfMemoryError(OOM)的緣由有那些?出現OOM錯誤後,怎麼解決?內存泄漏
參考播客:http://www.jianshu.com/p/2fdee831ed03
觸發java.lang.OutOfMemoryError:最多見的緣由就是應用程序需要的堆空間是大的,但是JVM提供的卻小。這個的解決方法就是提供大的堆空間便可。除此以外還有複雜的緣由:,
內存泄露:特定的編程錯誤會致使你的應用程序不停的消耗更多的內存,每次使用有內存泄漏風險的功能就會留下一些不能被回收的對象到堆空間中,隨着時間的推移,泄漏的對象會消耗全部的堆空間,最終觸發java.lang.OutOfMemoryError: Java heap space錯誤。
解決方案:
第一個解決方案是顯而易見的,你應該確保有足夠的堆空間來正常運行你的應用程序,在JVM的啓動配置中增長以下配置:
-Xmx1024m
流量/數據量峯值:應用程序在設計之初均有用戶量和數據量的限制,某一時刻,當用戶數量或數據量忽然達到一個峯值,而且這個峯值已經超過了設計之初預期的閾值,那麼之前正常的功能將會中止,並觸發java.lang.OutOfMemoryError: Java heap space異常
解決方案,若是你的應用程序確實內存不足,增長堆內存會解決GC overhead limit問題,就以下面這樣,給你的應用程序1G的堆內存:
java -Xmx1024m com.yourcompany.YourClass
A.棧是先進後出的線性表
B.棧只能順序存儲
C.棧具備記憶功能
D.對棧的插入和刪除操做中,不須要改變棧底指針
1.2 對於長度爲n的線性表,在最壞的狀況下,下列個排序法所對應的比較次數中正確的是(D)
A.冒泡排序爲n/2
B.冒泡排序爲n
C.快速排序爲n
D.快速排序爲n(n-1)/2
public class Person{
int arr[] = new int[10];
public static void main(String args[ ]){
System.out.println(arr[1]);
}
}
A 編譯時將產生錯誤
B 編譯時正確,運行時將產生錯誤
C 輸出空
D 輸出0
public class Test {
public static void main(String[] args) {
StringBuffer a = new StringBuffer("A");
StringBuffer b = new StringBuffer("B");
operator(a,b);
System.out.println(a+","+b);
}
public static void operator(StringBuffer x,StringBuffer y){
x.append(y);
y=x;
}
}
A . A,A
B. A,B
C. B,B
D. AB,B
A. 把對象轉換成爲字符串的形式經過網絡傳輸,在另外一端接收到字符串把對象還原出來
B. 把程序數據從數據庫中讀出來
C. 從XML配置文件中讀取程序的配置信息
D. 把程序數據保存爲文件
int x= 0;
int y=10;
do{
y--;
++x;
}while(x<6);
System.out.println();
}
A. 5,6
B. 5,5
C. 6,5
D. 6,6
Void complicatedexpression_f(){
int x=20,y=30;
boolean j;
j=x>50&&y>60|| x>50&& y<-60 || x<-50&&y>60 || x<-50&& y<-60;
System.out.println(j);
}
A. true
B. false
C. 1
D. 001
1.8一個棧的輸入序列爲123,則下列序列中不多是棧輸出的序列的是(C)
A. 2 3 1
B. 3 2 1
C. 3 1 2
D. 1 2 3
八、當n = 5時, 下列函數的返回值是(D)
int foo(int n){
if(n<2) return n;
return foo(n-1)+foo(n-2);
}
A. 1
B. 8
C. 7
D. 5
A. 688
B. 678
C. 692
D. 699
public static void main (String args[]){
Thread t = new Thread(){
public void run(){
pong();
}
};
t.run();
System.out.print("ping");
}
static void pong(){
System.out.print("pong");
}
A. pingpong
B. pongping
C. pingpong和pongping都有可能
D.都有可能
Public class NULL{
Public static void haha(){
System.out.println(「haha」);
}
Public static void main(String[] args){
((NULL)null).haha();
}
}
2.1 解釋一下什麼是Servlet,說一說Servlet的生命週期
Servlet是一種服務器端的Java應用程序,具備獨立於平臺和協議的特性,能夠生成動態的Web頁面。 它擔當客戶請求(Web瀏覽器或其餘HTTP客戶程序)與服務器響應(HTTP服務器上的數據庫或應用程序)的中間層。 Servlet是位於Web 服務器內部的服務器端的Java應用程序,與傳統的從命令行啓動的Java應用程序不一樣,Servlet由Web服務器進行加載,該Web服務器必須包含支持Servlet的Java虛擬機
Servlet生命週期能夠分紅四個階段:加載和實例化、初始化、服務、銷燬。
當客戶第一次請求時,首先判斷是否存在Servlet對象,若不存在,則由Web容器建立對象,然後調用init()方法對其初始化,此初始化方法在整個Servlet生命週期中只調用一次。
完成Servlet對象的建立和實例化以後,Web容器會調用Servlet對象的service()方法來處理請求。
當Web容器關閉或者Servlet對象要從容器中被刪除時,會自動調用destory()方法。
對於一個web應用程序來講,過濾器是處於web容器內的一個組件,它會過濾特定請求資源請求信息和響應信息。一個請求來到時,web容器會判斷是否有過濾器與該信息資源相關聯,若是有則交給過濾器處理,而後再交給目標資源,響應的時候則以相反的順序交給過濾器處理,最後再返回給用戶瀏覽器。
常見的過濾器用途主要包括:對用戶請求進行統一認證、對用戶的訪問請求進行記錄和審覈、對用戶發送的數據進行過濾或替換、轉換圖象格式、對響應內容進行壓縮以減小傳輸量、對請求或響應進行加解密處理、觸發資源訪問事件等。
從大到小:
public void BigAndSmall(){
int arr[]={-5,29,7,10,5,16};
for(int i=1;i
for(int j=0;j
if(arr[j]
int temp;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
for(int i =0;i
System.out.print(" "+arr[i]+" ");
}
}
1. 寫出一個單例的實現(懶加載方式)
public class LazySingleton {
private LazySingleton(){
}
private static class SingletonHolder{
private static LazySingleton instance = new LazySingleton();
}
public static LazySingleton getInstance(){
return SingletonHolder.instance;
}
}
A. 124
B. 96
C. 216
D. 348
A. 2月6日
B. 2月14日
C. 2月18日
D. 2月21日
A. 15
B. 14
C. 13
D.12
2.4 編號爲1至10的10個果盤中,每盤都盛有水果,共盛放100個。其中第一盤裏有16個,而且編號相鄰的三個果盤中水果是的和都相等,求第8盤中水果最多可能有幾個(A)
A. 11
B. 12
C. 13
14. 14
2.6 一隻蝸牛掉進20米深的井中,白天往上爬3米,晚上有掉下去2米,請問要幾天才能爬出來?
第一天爬了3米,而後掉了2米,實際上爬了1米;
次日從1米處開紿向上爬了3米,而後掉了2米,實際上爬了2米;
第三天從2米處開紿向上爬了3米,而後掉了2米,實際上爬了3米;
. .......
第十八天從17米處開始向上爬了3米,嘿恰好是20米.到了
正解:18
答案:5。
2.8 假設一個池塘,裏面有無窮多的水,如今有2個空水壺容積分別是5升和6升,問如何用這兩隻水壺取得3升水。
答案:5L桶打滿水,所有倒入6L桶;
5L桶再次打滿,往6L桶倒水至其滿。此時5L桶留下4L水;
6L桶清空,將5L桶中的4L水倒入6L桶;
5L桶打滿水,往6L桶倒水至其滿,則5L桶中得3L水。
2.9 在房裏有三盞燈,房外有三個開關,在房外看不見房內的狀況,你只能進門一次,你用什麼方法來區分那個開關控制哪一盞燈。
答案:先打開第一個開關,開一會再關上,而後打開第二個開關進入房間
再摸一下每一個燈,發熱的那盞是第一個開關的,
亮的那盞是第二個開關的,
沒變化的那盞是第三個開關的。
2.10 兩個盲人,他們各自買個兩雙黑襪和白襪,8雙襪子的布質,大小徹底相同,每雙襪子都有1張商標紙連着,兩位盲人不當心把8雙襪子混在的一塊兒,問他們怎樣才能取回黑襪和白襪各兩雙。
答案:把每雙襪子分紅兩隻。 每人各拿一隻。 這樣,每人手中就有四隻黑襪,四隻白襪。 每人也就有兩雙黑襪,兩雙白襪了。
2.11 一樓到十樓的每層電梯門口都方和一顆鑽石,鑽石大小不一,你乘坐電梯從一樓到十樓,每層樓電梯門都會打開一次,手裏只能拿一顆鑽石,問怎樣才能拿到最大的鑽石。
答案:電梯每層都會開一下的,因此,在第一層就拿,到第二層,看到更大就換一下,更小就不換,一直這樣上去,到最上層後,拿到的就是最大的
1.1 ArrayList list = new ArrayList(20);語句中的list集合大小擴充了幾回(A)
A.0
B.1
C.2
D.3
1.2 若是去掉了main方法的static修飾符會怎樣(B)
A.程序沒法翻譯
B.程序能正常編譯,運行時或拋出NoSuchMethodError異常
C.程序能正常編譯,正常運行
D.程序能正常編譯,正常運行一會會馬上退出
1.3啓動java程序進程時,輸入一下哪一個參數能夠實現年輕代的堆大小爲50M(C )
A.-Xms50M
B.-Xmx50M
C.-Xmn50M
D.-Xss50M
static boolean foo(char c) {
System.out.print(c);
return true;
}
public static void main(String[] args) {
int i = 0;
for (foo('A'); foo('B') && (i < 2); foo('C')) {
i++;
foo('D');
}
}
輸出結果爲:(A)
A. ABDCBDCB
B. ABDCDBCB
C. ABDBCDCB
D. ABDBCDCB
A.start()
B.run()
C.exit()
D.getPriority()
System.out.print(Integer.MAX_VALUE*2);
System.out.print(Integer.MIN_VALUE*2);
A. -2-1
B. -1-2
C. -20
D. -1-1
A. error>warn>info>debug
B. warn>info>debug>error
C. warn >debug>error>info
D. error>warn>debug>info
1.8下列哪些方法能夠使線程從運行狀態進入到阻塞狀態(BCD)
A.notify
B.wait
C.sleep
D.yield
1.9下列關於Thread類提供的線程控制的方法中,錯誤的一項是(A)
A. 在線程A中執行線程B的join()方法,則線程A等待直到B執行完成
B. 線程A經過調用interrupt()方法來中斷其阻塞狀態。
C. currentThread()方法返回當前線程的引用
D. 若線程A調用方法isAlive()返回爲true,則說明A正在執行中
1.10設String s1 =」Topwalk」;String s2 =」Company」;如下方法能夠獲得字符串「TopwalkCompany」有:(ABD)
A. s2+s1;
B. s1.concat(s2)
C. s1.append(s2); buffere纔有append方法
D.StringBuffer buf = new StringBuffer(s1);buf.append(s2);
1.11 String a = new String(「1」+」2」)最終建立了幾個對象(B)
A.1
B.2
C.3
D.4
A.2
B.4
C.8
D.16
1.13.下列那一條語句能夠實現快速的複製一張數據庫表(C)
A. select * into b from a where 1<>1;
B. creat table b as select * from a where 0=1;
C. insert into b as select * from a where 1<>1;
D. insert into b select * from a where 1<>1;
A. 提供了對惟一實現的受控訪問
B. 容許可變數目的實例
C. 單利模式的抽象層會致使單例類擴展有和那的困難
D. 單利模式很容易致使數據庫的鏈接池溢出
A. rownum
B. limit
C.TOP
D. pagenum
A.preparedStatement.getMetaData().getTables(***);
B.connection.getMetaData().getTables(***);
C.result.getMetaData().getTables(***);
D..DiverManager.getMeta().getTables(***);
A. 存儲過程
B. 數據庫日誌
C. 觸發器
D. 物化視圖
A. delete from tsuer;
B. truncate table tuser;ss
C. drop table tuser;
D. delete tuser;
A. FTP
B. Windows共享
C. TCP
D.SSH
A. 判斷一個字符串str中是否含有「.」,能夠根據str.indexOf(「.」)是否等於-1判斷。
B. 判斷一個字符串str是否含有「.」,能夠根據str.indexOf(「\\.」)是否等於-1判斷。
C. 根據「.」分隔字符串str的寫法能夠是str.split(「\\.」)
D. 根據「.」分隔字符串str的寫法能夠是str.split(「.」)
1.21 根據以下代碼回答問題,放置什麼方法在地6行,會引發編譯錯誤的是(B)
1 class Super{
2 public float getNum(){
3 }
4 }
5 public class Sub extends Super{
6
7 }
A. public float getNum{return 4,0f;}
B.public void getNum(){};
C.public void getNum(double d()){}
D.public double getNum(float d){return 4,0d;}
public class Foo{
public static void main(String args[]){
try{return;}
finally{System.out.println(「Finally」);}
}
}
A. print out nothing;
B. print out 「Finally」
C. 編譯錯誤
D. 以上都不對
1.23根據以下代碼回答問題,請問輸出i和j的值是多少(D)
int i=1,j=10;
do{
if(i++>--j) continue;
}while(i<5)
A. i=6 j=5
B i=5 j=5
C i=6 j=4
D i=5 j=6
A. run
B. low
C. import
D. implement
A.主鍵
B.外鍵
C.索引
D.惟一索引
E.not null
A. 服務器啓動時會初始創建必定數量的池鏈接,並一直維持很多於此數目的池鏈接
B.客戶端程序須要鏈接時,池驅動程序會返回一個使用的池鏈接並將其使用計數加1;
C. 若是當前沒有空閒鏈接,驅動程序就會再新建必定數量的鏈接,新建鏈接的數量能夠由配置參數決定。
D. 當使用池鏈接調用完成後,池驅動程序將此鏈接標記爲空間,其餘調用就能夠使用這個鏈接
A. 選擇性差的索引只會下降DML語句的執行速度
B. 選擇性強的索引只有被Access Path使用到纔是有用的索引
C. 過多的索引只會阻礙性能的提高,而不是加速性能
D.在適當的時候將最經常使用的列放在複合索引的最前面
E. 索引和表的數據都存儲在同一個Segment中
A. 當一個事務在表上防止了共享鎖(shared lock),其餘事務,能閱讀表裏的數據
B. 當一個事務在表上防止了共享鎖(shared lock),其餘事務,能更新表裏的數據
C. 當一個事務在表上防止了排他鎖(exclusive lock),其餘事務,能閱讀表裏的數據
D. 當一個事務在表上防止了排他鎖(exclusive lock),其餘事務,能更新表裏的數據
1.29 以下那種狀況下,Oracle不會使用Full Table Scean(D)
A.缺少索引,特別是在列上使用了函數,若是要利用索引,則須要使用函數索引。
B.當訪問的數據佔整個表中的大部分數據時
C.若是時一個表的high water mark 數據塊數少於初始化參數DB_FILE_MULTIBLOCK_READ_COUNT
D.本次查詢能夠用到該張表的一個引用,可是該表具備多個索引包含用於過濾的字段
答案參考:http://blog.csdn.net/jiary5201314/article/details/50990349
第一種狀況:兩個鏈表均不含有環
思路:
1)、直接法
採用暴力的方法,遍歷兩個鏈表,判斷第一個鏈表的每一個結點是否在第二個鏈表中,時間複雜度爲O(len1*len2),耗時很大。
2)、hash計數法
若是兩個鏈表相交,則兩個鏈表就會有共同的結點;而結點地址又是結點惟一標識。於是判斷兩個鏈表中是否存在地址一致的節點,就能夠知道是否相交了。能夠對第一個鏈表的節點地址進行hash排序,創建hash表,而後針對第二個鏈表的每一個節點的地址查詢hash表,若是它在hash表中出現,則說明兩個鏈表有共 同的結點。這個方法的時間複雜度爲:O(max(len1+len2);但同時還得增長O(len1)的存儲空間存儲哈希表。這樣減小了時間複雜度,增長 了存儲空間。
以鏈表節點地址爲值,遍歷第一個鏈表,使用Hash保存全部節點地址值,結束條件爲到最後一個節點(無環)或Hash中該地址值已經存在(有環)。
再遍歷第二個鏈表,判斷節點地址值是否已經存在於上面建立的Hash表中。
這個方面能夠解決題目中的全部狀況,時間複雜度爲O(m+n),m和n分別是兩個鏈表中節點數量。因爲節點地址指針就是一個整型,假設鏈表都是在堆中動態建立的,能夠使用堆的起始地址做爲偏移量,以地址減去這個偏移量做爲Hash函數
3)、第三種思路是比較奇特的,在編程之美上看到的。先遍歷第一個鏈表到他的尾部,而後將尾部的next指針指向第二個鏈表(尾部指針的next原本指向的是null)。這樣兩個鏈表就合成了一個鏈表,判斷原來的兩個鏈表是否相交也就轉變成了判斷新的鏈表是否有環的問題了:即判斷單鏈表是否有環?
這樣進行轉換後就能夠從鏈表頭部進行判斷了,其實並不用。經過簡單的瞭解咱們就很容易知道,若是新鏈表是有環的,那麼原來第二個鏈表的頭部必定在環上。所以咱們就能夠從第二個鏈表的頭部進行遍歷的,從而減小了時間複雜度(減小的時間複雜度是第一個鏈表的長度)。
下圖是一個簡單的演示:
這種方法能夠判斷兩個鏈表是否相交,但不太容易找出他們的交點。
4)、仔細研究兩個鏈表,若是他們相交的話,那麼他們最後的一個節點必定是相同的,不然是不相交的。所以判斷兩個鏈表是否相交就很簡單了,分別遍歷到兩個鏈表的尾部,而後判斷他們是否相同,若是相同,則相交;不然不相交。示意圖以下:
判斷出兩個鏈表相交後就是判斷他們的交點了。假設第一個鏈表長度爲len1,第二個問len2,而後找出長度較長的,讓長度較長的鏈表指針向後移動|len1 - len2| (len1-len2的絕對值),而後在開始遍歷兩個鏈表,判斷節點是否相同便可。
答案:session 在服務器端,cookie 在客戶端(瀏覽器),session 默認被存在在服務器的一個文件裏(不是內存),session 的運行依賴 session id,而 session id 是存在 cookie 中的,也就是說,若是瀏覽器禁用了 cookie ,同時 session 也會失效(可是能夠經過其它方式實現,好比在 url 中傳遞 session_id); session 能夠放在 文件、數據庫、或內存中均可以。用戶驗證這種場合通常會用session ,所以,維持一個會話的核心就是客戶端的惟一標識,即 session id
2.3. System.out.println(3/2); System.out.println(3.0/2); System.out.println(3.0/2.0); 分別會打印什麼結果?
答案:1, 1.5,1.5
String[] arr1 = {"112","wqw","2121"};
String[] arr2 = {"112","aad","ewqw"};
打印出兩個數組的交集,結果不能重複
答案:
public class test {
@Test//測試程序
public void test()
String[] arr1 = {"112","wqw","2121"};
String[] arr2 = {"112","aad","ewqw"};
String[] result=StringIntersection(arr1,arr2);
for (String str:result){
System.out.printf(str);
}
}
//取兩個string數組的交集
public String[] StringIntersection(String[] arr1,String[] arr2){
Map map = new HashMap();
List list = new LinkedList();
//取出str1數組的值存放到map集合中,將值做爲key,因此的value都設置爲false
for (String str1:arr1){
if (!map.containsKey(str1)){
map.put(str1,Boolean.FALSE);
}
}
//取出str2數組的值循環判斷是否有重複的key,若是有就將value設置爲true
for (String str2:arr2){
if (map.containsKey(str2)){
map.put(str2,Boolean.TRUE);
}
}
//取出map中全部value爲true的key值,存放到list中
for (Map.Entry entry:map.entrySet()){
if (entry.getValue().equals(Boolean.TRUE)){
list.add(entry.getKey());
}
}
//聲明String數組存儲交集
String[] result={};
return list.toArray(result);
}
}
2.5 SpringMvc 攔截器用過嗎?什麼場景會用到,過濾器,攔截器,監聽器有什麼區別?
攔截器是指經過統一攔截從瀏覽器發往服務器的請求來完成功能的加強。
使用場景:解決請求的共性問題(亂碼問題、權限驗證問題)
過濾器
Servlet中的過濾器Filter是實現了javax.servlet.Filter接口的服務器端程序,主要的用途是過濾字符編碼、作一些業務邏輯判斷等。其工做原理是,只要你在web.xml文件配置好要攔截的客戶端請求,它都會幫你攔截到請求,此時你就能夠對請求或響應(Request、Response)統一設置編碼,簡化操做;同時還可進行邏輯判斷,如用戶是否已經登錄、有沒有權限訪問該頁面等等工做。它是隨你的web應用啓動而啓動的,只初始化一次,之後就能夠攔截相關請求,只有當你的web應用中止或從新部署的時候才銷燬。
監聽器
如今來講說Servlet的監聽器Listener,它是實現了javax.servlet.ServletContextListener 接口的服務器端程序,它也是隨web應用的啓動而啓動,只初始化一次,隨web應用的中止而銷燬。主要做用是: 作一些初始化的內容添加工做、設置一些基本的內容、好比一些參數或者是一些固定的對象等等
攔截器
攔截器是在面向切面編程中應用的,就是在你的service或者一個方法前調用一個方法,或者在方法後調用一個方法。是基於JAVA的反射機制。攔截器不是在web.xml
1).過濾器:所謂過濾器顧名思義是用來過濾的,在java web中,你傳入的request,response提早過濾掉一些信息,或者提早設置一些參數,而後再傳入servlet或者struts的action進行業務邏輯,好比過濾掉非法url(不是login.do的地址請求,若是用戶沒有登錄都過濾掉),或者在傳入servlet或者struts的action前統一設置字符集,或者去除掉一些非法字符(聊天室常常用到的,一些罵人的話)。filter 流程是線性的, url傳來以後,檢查以後,可保持原來的流程繼續向下執行,被下一個filter, servlet接收等.
2).監聽器:這個東西在c/s模式裏面常常用到,他會對特定的事件產生產生一個處理。監聽在不少模式下用到。好比說觀察者模式,就是一個監聽來的。又好比struts能夠用監聽來啓動。Servlet監聽器用於監聽一些重要事件的發生,監聽器對象能夠在事情發生前、發生後能夠作一些必要的處理。
3).java的攔截器 主要是用在插件上,擴展件上好比 hivernate spring struts2等 有點相似面向切片的技術,在用以前先要在配置文件即xml文件裏聲明一段的那個東西。
參考文檔:http://blog.csdn.net/sonny543/article/details/51336457
每個ThreadLocal可以放一個線程級別的變量,但是它自己可以被多個線程共享使用,而且又可以達到線程安全的目的,且絕對線程安全。
ThreadLocal的應用場景:
最多見的ThreadLocal使用場景爲 用來解決數據庫鏈接、Session管理等
在TCP/IP協議中,TCP協議提供可靠的鏈接服務,採用三次握手創建一個鏈接。
1).第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態, 等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)
2).第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
3)第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1), 此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據。
2.8 SpringMVC request接收設置是線程安全的嗎?
參考文檔:http://blog.csdn.net/q1512451239/article/details/53122512
答案:是線程安全的,request、response以及requestcontext在使用時不須要進行同步。而根據spring的默認規則,controller對於beanfactory而言是單例的。即controller只有一個, controller中的request等實例對象也只有一個
答案:
1.compile:編譯依賴範圍(默認),對其三種都有效
2.test:測試依賴範圍,只對測試classpath有效
3.runtime:運行依賴範圍,只對測試和運行有效,編譯主代碼無效,例如JDBC
4.provided:已提供依賴範圍,只對編譯和測試有效,運行時無效,例如selvet-api
5.system:系統依賴範圍.謹慎使用.例如本地的,maven倉庫以外的類庫文件
6.import(maven2.0.9以上):導入依賴範圍,不會對其餘三種有影響
2.10 Mybatis如何防止sql注入?mybatis攔截器瞭解過嗎,應用場景是什麼?
答案:Mybatis使用#{}通過預編譯的,是安全的,防止sql注入。
Mybatis攔截器只能攔截四種類型的接口:Executor、StatementHandler、 ParameterHandler和ResultSetHandler。這是在Mybatis的Configuration中寫死了的,若是要支持攔截其餘接口就須要咱們重寫Mybatis的Configuration。Mybatis能夠對這四個接口中全部的方法進行攔截。Mybatis攔截器經常會被用來進行分頁處理。
在Spring框架中共有5種自動裝配:
no:這是Spring框架的默認設置,在該設置下自動裝配是關閉的,開發者須要自行在bean 定義中用標籤明確的設置依賴關係。
byName:該選項能夠根據bean名稱設置依賴關係。當向一個bean中自動裝配一個屬 性時,容器將根據bean的名稱自動在在配置文件中查詢一個匹配的bean。若是找到的 話,就裝配這個屬性,若是沒找到的話就報錯。
byType:該選項能夠根據bean類型設置依賴關係。當向一個bean中自動裝配一個屬性時,容器將根據bean的類型自動在在配置文件中查詢一個匹配的bean。若是找到的話,就裝配這個屬性,若是沒找到的話就報錯。
constructor:造器的自動裝配和byType模式相似,可是僅僅適用於與有構造器相同參數 的bean,若是在容器中沒有找到與構造器參數類型一致的bean,那麼將會拋出異常。
autodetect:該模式自動探測使用構造器自動裝配或者byType自動裝配。首先,首先會 嘗試找合適的帶參數的構造器,若是找到的話就是用構造器自動裝配,若是在bean內部 沒有找到相應的構造器或者是無參構造器,容器就會自動選擇byTpe的自動裝配方式。
MVC 是 Model-View-Controller 的簡寫。 Model 表明的是應用的業務邏輯( 經過JavaBean, EJB 組件實現), View 是應用的表示面( 由 JSP 頁面產生), Controller 是提供應用的處理過程控制(通常是一個Servlet), 經過這種設計模型把應用邏輯, 處 理過程和顯示邏輯分紅不一樣的組件實現。這些組件能夠進行交互和重用。
答案:反射機制的應用場景:
1)逆向代碼 ,例如反編譯
3)與註解相結合的框架 例如Retrofit
4)單純的反射機制應用框架 例如EventBus 2.x
5)動態生成類框架 例如Gson
2.14設計Java程序,假設有50瓶飲料,喝完三個空瓶能夠換一瓶飲料,依次類推,請問總共喝了多少飲料。
答案:
public class Buy {
public static void main(String[] args) {
int n = 50; //初始飲料總數
int i=0; //兌換次數
while(true){
n -= 3; //喝3瓶
n++; //兌換1瓶
i++; //兌換次數+1
if(n<3)
{
System.out.println ("共喝了"+(50+i)+"瓶");
break;
}
}
}
}
答案:
/定義存儲年月日的變量
int year = 0, month = 0, day = 0;
//提示用戶輸入
printf("請輸入年月日(好比1990-1-1):");
//接受用戶輸入, 切記,scanf中""內的格式是什麼, 輸入的格式必須一致
scanf("%d-%d-%d", &year,&month, &day);
//定義一個數組存放每月的天數
int dayOfMonth[12] = {31, 28, 31, 30, 31,30, 31, 31, 30, 31, 30, 31};
//判斷是否不是閏年
if (year % 400 == 0 || (year % 4 == 0&& year % 100 != 0)) {
//閏年二月29天
dayOfMonth[1] = 29;
}
//定義變量記錄是第幾天
int whichDay = 0;
//例如3月20日是一年的第幾天, 計算方法 1月的天數 + 2月的天數 + 20
for (int i = 0; i < month - 1; i++) {
whichDay += dayOfMonth[i];
}
whichDay += day;
printf("是一年的%d天\n", whichDay);
答案:
public class Test1 {
public static void main(String[] args) {
float jiangjin=0;
Scanner scan=new Scanner(System.in);
System.out.print("請輸入利潤:");
float num=scan.nextInt();
if(num<=100000){
jiangjin=(float) (num*0.1);
}
else if(num<=200000){
jiangjin=(float) ((num-100000)*0.075+100000*0.1);
}
else if(num<=400000){
jiangjin=(float) ((num-200000)*0.5 +100000*0.175);
}
else if(num<=600000){
jiangjin=(float) ((num-400000)*0.3 +100000*0.175+200000*0.5);
}
else if(num<=1000000){
jiangjin=(float) ((num-600000)*0.015 +100000*0.175+200000*0.5+200000*0.3);
}
else{
jiangjin=(float) ((num-1000000)*0.01 +100000*0.175+200000*0.5+200000*0.3+400000*0.015);
}
System.out.println("獎金:"+jiangjin);
}
}
參考:http://blog.csdn.net/leroy008/article/details/8058187
答案:
第一種:雙重查鎖模式
public class DoubleCheckLock {
private static DoubleCheckLock instance = null;
private DoubleCheckLock() {
}
public static DoubleCheckLock getInstance() {
if (instance == null) {
synchronized (DoubleCheckLock.class) {
if (instance == null) {
instance = new DoubleCheckLock();
}
}
}
return instance;
}
}
第二種:枚舉單例
public enum SingleEnum {
INSTANCE;
public void doSomething() {
ToastUtils.showLongToast("do something...");
}
}
第三種:靜態內部類方式
public class StaticInner {
private static StaticInner instance;
public static StaticInner getInstance() {
return SingletonHolder.STATIC_INNER;
}
private static class SingletonHolder {
private static final StaticInner STATIC_INNER = new StaticInner();
}
}
SSH是 struts+spring+hibernate的一個集成框架,是目前比較流行的一種Web應用程序開源框架。
集成SSH框架的系統從職責上分爲四層:表示層、業務邏輯層、數據持久層和域模塊層,以幫助開發人員在短時間內搭建結構清晰、可複用性好、維護方便的Web應用程序。其中使用Struts做爲系統的總體基礎架構,負責MVC的分離,在Struts框架的模型部分,控制業務跳轉,利用Hibernate框架對持久層提供支持,Spring作管理,管理struts和hibernate。具體作法是:用面向對象的分析方法根據需求提出一些模型,將這些模型實現爲基本的Java對象,而後編寫基本的DAO(Data Access Objects)接口,並給出Hibernate的DAO實現,採用Hibernate架構實現的DAO類來實現Java類與數據庫之間的轉換和訪問,最後由Spring作管理
要讓程序在後臺執行,只需在命令行的最後加上「&」符號。
例如:$ find . -name abc -print&;
rm -i --interactive 交互模式刪除文件,刪除文件前給出提示。
rm -r -r或-R:遞歸處理,將指定目錄下的全部文件與子目錄一併處理。
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了不少這種鎖機制,好比行鎖,表鎖等,讀鎖,寫鎖等,都是在作操做以前先上鎖。它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,能夠使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣能夠提升吞吐量,像數據庫若是提供相似於write_condition機制的其實都是提供的樂觀鎖。
兩種鎖各有優缺點,不可認爲一種好於另外一種,像樂觀鎖適用於寫比較少的狀況下,即衝突真的不多發生的時候,這樣能夠省去了鎖的開銷,加大了系統的整個吞吐量。但若是常常產生衝突,上層應用會不斷的進行retry,這樣反卻是下降了性能,因此這種狀況下用悲觀鎖就比較合適。
答案:%t 輸出產生該日誌事件的線程名
擴展:%M是輸出方法的名字、%m是輸出代碼指定的日誌信息。
指定的打印信息的具體格式ConversionPattern,具體參數:
%m 輸出代碼中指定的消息
%p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
%r 輸出自應用啓動到輸出該log信息耗費的毫秒數
%c 輸出所屬的類目,一般就是所在類的全名
%t 輸出產生該日誌事件的線程名
%n 輸出一個回車換行符,Windows平臺爲"rn」,Unix平臺爲"n」
%d 輸出日誌時間點的日期或時間,默認格式爲ISO8601,也能夠在其後指定格式,好比:%d{yyyy MM dd HH:mm:ss,SSS},輸出相似:2002年10月18日 22:10:28,921 %l 輸出日誌事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。
%x: 輸出和當前線程相關聯的NDC(嵌套診斷環境),尤爲用到像java servlets這樣的多客戶多線程的應用中。
%%: 輸出一個」%」字符
%F: 輸出日誌消息產生時所在的文件名稱
%M: 輸出執行方法
%L: 輸出代碼中的行號
出現NotWritablePropertyException異常的緣由通常是在ApplicationContext.xml中property name的錯誤等相關錯誤。
1.1 關於Web應用程序,下列說法錯誤的是(b)。
a) WEB-INF目錄存在於web應用的根目錄下
b) WEB-INF目錄與classes 目錄平行
c) web.xml在WEB-INF目錄下
d) Web應用程序能夠打包爲war文件
解釋:classes目錄位於WEB-INF目錄之下,而不是平行的關係。
1.2 有關Servlet的生命週期說法正確的有( cd)。
a) Servlet的生命週期由Servlet實例控制
b) init()方法在建立完Servlet實例後對其進行初始化,傳遞的參數爲實現ServletContext接口的對象
c) service()方法響應客戶端發出的請求
d) destroy()方法釋放Servlet實例
解釋:Servlet的生命週期是由Servlet容器(Tomcat就是常見的Servlet容器)管理的,所以a不對。
Servlet中的init()方法有兩個重載,一個是空參的,另一個是帶ServletConfig形參的,而不是ServletContext,所以b不對。
關於d選項,說法其實並很差,正確的表達應該是由於Servlet實例要釋放(銷燬)了,纔會先調用destroy()方法。
1.3 有關會話跟蹤技術描述正確的是(abc)。
a) Cookie是Web服務器發送給客戶端的一小段信息,客戶端請求時,能夠讀取該信息發送到服務器端
b) 關閉瀏覽器意味着會話ID丟失,但全部與原會話關聯的會話數據仍保留在服務器上,直至會話過時
c) 在禁用Cookie時能夠使用URL重寫技術跟蹤會話
d) 隱藏表單域將字段添加到HTML表單並在客戶端瀏覽器中顯示
1.4 如下web.xml片段( d )正確地聲明servlet 上下文參數。
a)
MAX
100
b)
c)
d)
MAX
100
補充:關於context-param和init-param的做用,請參考下文。
https://www.cnblogs.com/hzj-/articles/1689836.html
1.5 如下(a)可用於檢索session屬性userid的值。
a) session. getAttribute (「userid」);
b) session. setAttribute (「userid」);
c) request. getParameter (「userid」);
d) request. getAttribute (「userid」);
1.6 下列JSP代碼,如下(cd)可放置在//1處,不會發生編譯錯誤。
<%
for(int i = 0; i < 10; i++) {
//1
}
%>
a) <%= i %>
b) i
c) %><%= i %><%
d) 不寫任何內容
1.7 考慮下面兩個JSP文件代碼片段:
test1.jsp:
<% pageContext.setAttribute(」ten」,new Integer(10));%>
//1
test2.jsp:
數字爲:<%= pageContext.getAttribute(」ten」)%>
如下(c )放置在test1.jsp中的//1處,當請求test1.jsp時正確輸出test2.jsp中的內容。
a)
b)
c) <%@ include file=」test2.jsp」 %>
d) 因爲pageContext對象的scope屬性爲page,因此test2.jsp不能訪問test1.jsp定義的屬性
解釋:JSP中include的兩種方法,一、屬於動態引入/二、<%@ include file=」test2.jsp」 %>屬於靜態引入。二者的區別以下:
1.執行時間上的區別
<%@ include file=」test2.jsp」%> 是在翻譯階段執行(將JSP頁面轉換成servlet的階段)。通俗的話講就是先合併在一塊兒,而後再編譯成一個Servlet文件。
在請求處理階段執行。通俗的話講就是先各自編譯,而後在處理請求的時候結果再合併到一塊兒。
2.引入內容的方式區別
<%@ include file=」test2.jsp」%>是純粹的把部分代碼寫到了另外一頁面(或者說是共享),而那另外一頁面中不能有相同的變量名,但能夠借用主頁面的內容。
引入執行頁面或servlet所生成的應答文本。
屬於請求轉發,因爲pageContext對象的scope屬性爲page,因此若是請求轉發了,那麼test2.jsp就不能訪問test1.jsp定義的屬性了。因此該題選C。
1.8 有關JSP隱式對象,如下(acd )描述正確。
a) 隱式對象是WEB容器加載的一組類的實例,能夠直接在JSP頁面使用
b) 不能經過config對象獲取ServletContext對象
c) response對象經過sendRedirect方法實現重定向
d) 只有在出錯處理頁面纔有exception對象
解釋:jsp的九大內置對象分別是:config、request、response、out,page、pageContext、session、exception、application。其中exception是特殊的內置對象,只有當在jsp中添加isErrorPage="true"屬性時以下配置時才能夠使用。該屬性通常出如今設定的錯誤界面。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" isErrorPage="true" %>
1.9 考慮下面JSP文件代碼片段:
如下(c)代碼片段放置在test2.jsp中不會致使錯誤。
a)
b)
c) <%=request.getParameter(「username」)%>
d) <%=request.getAttribute(「username」)%>
解釋:屬於動態調用test2.jsp界面,至關於動態去請求test2.jsp所生成的Servlet,在請求的同時攜帶了請求參數「username」,咱們知道在Servlet中獲取請求攜帶的參數就是經過request.getParameter(key)來獲取的,所以C正確。
1.10 如下是login.jsp文件的代碼片段:
<%@ page isELIgnored="false"%>
用戶名爲: ${param.name}
如下(C )描述正確。
a) 發生運行錯誤
b) 頁面會出現一文本框,而且文本框中內容爲${param['name']}
c) 當用戶輸入名字並單擊「提交」按鈕時,在同一頁面中的「用戶名爲:」字樣後面會顯示用戶輸入的內容
d) 當用戶輸入名字並單擊「提交」按鈕時,在同一頁面中的「用戶名爲:」字樣後面會顯示${param.name}
1.11 doAfterBody()方法是在( b )接口中定義的。
a) Tag
b) IterationTag
c) BodyTag
d) TagSupport
解釋:該知識點屬於jsp自定義標籤中的知識,比較生僻。若是想了解如何在jsp中使用自定義標籤的能夠參考下文。
https://www.cnblogs.com/flying607/p/5063207.html
1.12 編寫一個Filter,須要( b)。
a) 繼承Filter 類
b) 實現Filter 接口
c) 繼承HttpFilter 類
d) 實現HttpFilter接口
1.13 有關MVC設計模式(b )描述不正確。
a) 使用Servlet做爲控制器
b) MVC設計模式增大了維護難度
c) MVC設計模式屬於Model 2
d) 模型對象向客戶端顯示應用程序界面
補充知識:
Model 1
Model 1的基礎是JSP文件,它由一些相互獨立的JSP文件,和其餘一些Java Class組成(不是必須的)。這些JSP從HTTP Request中得到所須要的數據,處理業務邏輯,而後將結果經過Response返回前端瀏覽器。
Model 2
採用面向對象技術實現MVC模式從而擴展JSP/Servlet的模式被成爲是Model 2模式。Apache Jakarta項目中Struts是一個實現Model 2的很好的框架,它經過一些Custom Tag Lib處理表現層,用ActionFrom Bean表示數據,用本身提供的一個ActionServlet做爲控制器實現頁面的流轉的控制功能。
說的直白一些,model1即爲單純的jsp+java,沒有框架參與,經過response和request對象傳送值域,而model2,則使用較爲流行的struts2框架。
1.14 在Linux中,能夠使用命令(c)加掛計算機上的非Linux文件系統。
a) cat /proc/filesystems
b) ln
c) mount
d) df
1.15 下面關於Linux中 shell 的說法錯誤的是(d)。
a) shell 是解釋用戶在終端鍵入的命令的一種中間程序
b) shell 能夠讀取並執行腳本文件中的命令
c) 用戶能夠使用參數將命令行的參數傳遞給shell 腳本,從而實如今 Linux 中的交互式編程
d) 默認狀況下,Linux 中建立的全部文件都具備執行權限
1.16 在Oracle中,當須要使用顯式遊標更新或刪除遊標中的行時,UPDATE或DELETE語句必須使用(c)子句。
a) WHERE CURRENT OF
b) WHERE CURSOR OF
c) FOR UPDATE
d) FOR CURSOR OF
1.17 在Oracle中,使用下列的語句CREATE PUBLIC SYNONYM parts FOR Scott.inventory;完成的任務是(d)。
a) 將Scott.inventory對象的訪問權限賦予全部用戶
b) 指定了新的對象權限
c) 指定了新的系統權限
d) 給Scott.inventory對象建立一個公用同義詞parts
1.18 在Oracle中,執行以下PL/SQL語句後
CREATE TYPE car AS OBJECT ( id NUMBER, model VARCHAR2(25), color VARCHAR2(15) );
…
DECLARE
myvar car.model%TYPE;
BEGIN
…
END;
變量myvar的數據類型爲(c)。
a) NUMBER
b) car類型
c) VARCHAR2
d) OBJECT
解釋:該知識點屬於PL/SQL高級編程,中的對象類型(OBJECT TYPE)
2.1 List、Map、Set三個接口存儲元素時各有什麼特色?
2.2在項目中用過Spring的哪些方面?及用過哪些Ajax框架?
1.一、下列說法正確的有(C)
A、class中的constructor不可忽略
B、constructor能夠做爲普通方法被調用
C、constructor在一個對象被new時被調用
D、一個class只能定義一個constructor
1.二、下列運算符合法的是(D)
A、&&
B、<>
C、If
D、:=
1.三、下列哪一種說法不正確(ABC)
A、實例方法能夠直接調用超類的實例方法
B、實例方法能夠直接調用超類的類方法
C、實例方法能夠直接調用其餘類的實例方法
D、實例方法能夠直接調用本類的類方法
1.四、執行以下程序代碼後,c的值是(C)
a=0;
c=0;
do{
--c;
a=a-1;
}while(a>0);
A、0 B、1 C、-1 D、死循環
2.一、(×)constructor必須與class同名,可是方法不能與class同名
2.二、(×)constructor能夠被繼承,所以能夠重寫Overriding,也能夠被重載Overloading
2.三、(√)String類是final類 故不能夠被繼承
2.四、(×)數組的大小能夠任意改變,又稱動態數組
2.五、(×)try{}裏有一個return語句,則緊跟在try後面的finally{}裏的code將在return後執行
3.一、抽象類和接口有什麼不一樣點?
接口和抽象類的概念不同。接口是對動做的抽象,抽象類是對根源的抽象。
抽象類表示的是,這個對象是什麼。接口表示的是,這個對象能作什麼。好比,男人,女人,這兩個類(若是是類的話……),他們的抽象類是人。說明,他們都是人。
人能夠吃東西,狗也能夠吃東西,你能夠把「吃東西」定義成一個接口,而後讓這些類去實現它.
不一樣點:
參數抽象類接口
默認的方法實現它能夠有默認的方法實現接口徹底是抽象的。它根本不存在方法的實現
實現子類使用extends關鍵字來繼承抽象類。若是子類不是抽象類的話,它須要提供抽象類中全部聲明的方法的實現。子類使用關鍵字implements來實現接口。它須要提供接口中全部聲明的方法的實現
構造器抽象類能夠有構造器接口不能有構造器
與正常Java類的區別除了你不能實例化抽象類以外,它和普通Java類沒有任何區別接口是徹底不一樣的類型
訪問修飾符抽象方法能夠有public、protected和default這些修飾符接口方法默認修飾符是public。你不能夠使用其它修飾符。
main方法抽象方法能夠有main方法而且咱們能夠運行它接口沒有main方法,所以咱們不能運行它。
多繼承抽象方法能夠繼承一個類和實現多個接口接口只能夠繼承一個或多個其它接口
速度它比接口速度要快接口是稍微有點慢的,由於它須要時間去尋找在類中實現的方法。
添加新方法若是你往抽象類中添加新的方法,你能夠給它提供默認的實現。所以你不須要改變你如今的代碼。若是你往接口中添加方法,那麼你必須改變實現該接口的類。
3.1.1何時使用抽象類和接口
若是你擁有一些方法而且想讓它們中的一些有默認實現,那麼使用抽象類吧。
若是你想實現多重繼承,那麼你必須使用接口。因爲Java不支持多繼承,子類不可以繼承多個類,但能夠實現多個接口。所以你就能夠使用接口來解決它。
若是基本功能在不斷改變,那麼就須要使用抽象類。若是不斷改變基本功能而且使用接口,那麼就須要改變全部實現了該接口的類。
3.二、sleep()和wait()有什麼不一樣點?
主要區別在於:
sleep表示睡眠,wait表示等待 sleep須要手動喚醒線程,而wait不須要手動喚醒等待結束後自動執行。
原文連接:https://www.zhihu.com/question/23328075
3.三、TreeMap和HashMap有什麼不一樣點?
第一點:HashMap:基於哈希表實現。使用HashMap要求添加的鍵類明肯定義了hashCode()和equals()[能夠重寫hashCode()和equals()],爲了優化HashMap空間的使用,您能夠調優初始容量和負載因子。
TreeMap:基於紅黑樹實現。TreeMap沒有調優選項,由於該樹總處於平衡狀態。
第二點:HashMap經過hashcode對其內容進行快速查找。
TreeMap中全部的元素都保持着某種固定的順序,若是你須要獲得一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。
第三點:HashMap 非線程安全
TreeMap 線程安全
第四點:HashMap:適用於在Map中插入、刪除和定位元素。
Treemap:適用於按天然順序或自定義順序遍歷鍵(key)。
總結:HashMap一般比TreeMap快一點(樹和哈希表的數據結構使然),建議多使用HashMap,在須要排序的Map時候才用TreeMap
原文連接:https://www.cnblogs.com/ouwenkgwpf/p/4547509.html
3.四、throws和throw有什麼不一樣點?
throw是語句拋出一個異常。
語法:throw (異常對象);
throw e;
throws是方法可能拋出異常的聲明。(用在聲明方法時,表示該方法可能要拋出異常)
語法:[(修飾符)](返回值類型)(方法名)([參數列表])[throws(異常類)]{......}
public void doA(int a) throws Exception1,Exception3{......}
3.五、length()和length有什麼不一樣點?
java中的length屬性是針對數組說的,好比說你聲明瞭一個數組,想知道這個數組的長度則用到了length這個屬性。
java中的length()方法是針對字符串String說的,若是想看這個字符串的長度則用到length()這個方法。
4.一、請編寫一個jdbc查詢任意一個數據庫(如mysql、Oracle等),數據庫名爲test,代表爲user,只有一個字段。
1. public class MysqlDemo {
2. public static void main(String[] args) throws Exception {
3. Connection conn = null;
4. String sql;
5. // MySQL的JDBC URL編寫方式:jdbc:mysql://主機名稱:鏈接端口/數據庫的名稱?參數=值
6. //避免中文亂碼要指定useUnicode和characterEncoding
7. String url = "jdbc:mysql://localhost:3306/test?"
8. + "user=root&password=root&useUnicode=true&characterEncoding=UTF8";
9.
10. try {
11. //之因此要使用下面這條語句,是由於要使用MySQL的驅動,因此咱們要把它驅動起來,
12. //能夠經過Class.forName把它加載進去,也能夠經過初始化來驅動起來,下面三種形式均可以
13. Class.forName("com.mysql.jdbc.Driver");//動態加載mysql驅動
14. // or:
15. // com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver();
16. // or:
17. // new com.mysql.jdbc.Driver();
18.
19. System.out.println("成功加載MySQL驅動程序");
20. //一個Connection表明一個數據庫鏈接
21. conn = DriverManager.getConnection(url);
22. // Statement裏面帶有不少方法,好比executeUpdate能夠實現插入,更新和刪除等
23. Statement stmt = conn.createStatement();
24. sql = "select * from user";
25. ResultSet rs = stmt.executeQuery(sql);
26. while (rs.next()) {//遍歷user表中全部數據
27. String name = rs.getString("name") ;
28. System.out.println(「姓名是:」+name);//假如user表中的字段爲name
29. }
30. }catch (Exception e) {
31. e.printStackTrace();
32. } finally {
33. try {
34. if (rs != null)
35. rs.close(); //關閉結果數據集
36. if (stmt != null)
37. stmt.close(); //關閉執行環境
38. if (conn != null)
39. conn.close(); //關閉數據庫鏈接
40. } catch (SQLException e) {
41. e.printStackTrace();
42. }
43. }
44.
45. }
46.
47. }
注意:鏈接oracle與mysql不一樣點爲,url=jdbc:Oracle:thin:@localhost:1521:orcl
driver = oracle.jdbc.driver.OracleDriver
4.二、請編寫一socket的程序,客戶端向服務器端發送字符串,服務器端在返回相同的字符串。(相似echo功能)
1. public class Client { //客戶端
2. public static void main(String args[])throws Exception
3. {
4. String clientMessage;//來自用戶輸入的的信息
5. String serverMessage; //服務器端的信息
6. Socket ClientSocket=new Socket("127.0.0.0",5557);//參數是本機地址和端口,客戶端套接字,發起TCP鏈接
7. BufferedReader fromUser=new BufferedReader(new InputStreamReader(System.in));//字符讀取流,獲取從鍵盤輸入的字符
8. BufferedReader fromServer=new BufferedReader(new InputStreamReader(ClientSocket.getInputStream()));//獲取從服務器端的流,創建套接字輸入流
9. DataOutputStream toServer=new DataOutputStream(ClientSocket.getOutputStream());//創建套接字輸出流
10. clientMessage=fromUser.readLine();//讀取從用戶的輸入
11. toServer.writeBytes(clientMessage);//寫到服務器端
12. serverMessage=fromServer.readLine();//從服務器端讀取
13. ClientSocket.close();//關閉套接字鏈接
14. }
15. }
1. public class Server {//服務端
2. public static void main(String args[])throws Exception
3. {
4. String ClientMessage;
5. String ServerMessage;
6. ServerSocket serversocket=new ServerSocket(5557);//端口要和客戶端對應
7. System.out.println("正在監聽5555端口");//
8. while(true)
9. {
10. Socket collection=serversocket.accept();//調用accept()函數,創建TCP鏈接
11. DataInputStream fromClient=new DataInputStream(collection.getInputStream());
12. DataOutputStream toClient=new DataOutputStream(collection.getOutputStream());
13. ClientMessage=fromClient.readUTF();//接收來自客戶端的信息
14. toClient.writeBytes(ServerMessage);//寫到服務器端
15. System.out.println("成功創建TCP鏈接");
16. }
17. }
18. }
4.三、寫一個多線程程序,四個線程對一個int變量,2個加1,2個減1,輸出。
1. public class TMain {
2. int j=1;
3. public synchronized void inc(){
4. j++;
5. System.out.println(Thread.currentThread().getName()+"-inc:"+j);
6. }
7. class T1 implements Runnable{
8. public void run(){
9. inc();
10. }
11. }
12. public synchronized void dec(){
13. j--;
14. System.out.println(Thread.currentThread().getName()+"-dec:"+j);
15. }
16.
17.
18. class T11 implements Runnable{
19. public void run(){
20. dec();
21. }
22. }
23. public static void main(String[] args) {
24. TMain t = new TMain();
25. T1 t1 =t.new T1();
26. T11 t11 =t.new T11();
27. for(int i=0;i<2;i++){
28. Thread thread=new Thread(t1);
29. thread.start();
30.
31.
32. Thread thread1=new Thread(t11);
33. thread1.start();
34. }
35.
36.
37. }
38. }
5.一、說出經常使用的10個linux操做命令,至少5個,並簡述命令的做用。
LS命令
-做用:顯示目錄內容,相似DOS下的DIR
-格式:LS【options】【filename】
-經常使用參數:
>-a:all,不隱藏任何以"."字符開始的文件
>-l:使用較長的格式列出信息
>-r:按照文件名的逆序打印輸出
>-F:加上文件類型的指示符
ls -lF | grep / 過濾
man ls 查詢ls的幫助文件
cat命令
-做用:顯示文件內容,concatenate的縮寫,相似dos的type命令。
-格式:cat【options】【fielname】
-經常使用參數:
>-n:顯示文件內容的行號。
>-b:相似-n,可是不對空白行進行編號。
>-s:當遇到有連續兩行以上的空白行時,就代換爲一行的空白行。
mv命令
-做用:更改文件或者目錄的名字。
-格式:mv[options]source destination
-經常使用參數:
>-f:強制模式,覆蓋文件不提示。
>-i:交互模式,當要覆蓋文件的時候給提示。
rm命令
-做用:刪除文件命令,相似dos的del命令
-格式:rm【options】filenames
-經常使用參數:
>-f:強制模式,不給提示。
>-r,-R:刪除目錄,recursive
原文連接:http://blog.csdn.net/sinat_21903855/article/details/48936175
5.二、說出常見的5個linux系統日誌,至少3個並作簡述日誌的用途。
access-log 紀錄HTTP/web的傳輸
acct/pacct 紀錄用戶命令
aculog 紀錄MODEM的活動
btmp 紀錄失敗的紀錄
lastlog 紀錄最近幾回成功登陸的事件和最後一次不成功的登陸
messages 從syslog中記錄信息(有的連接到syslog文件)
sudolog 紀錄使用sudo發出的命令
sulog 紀錄使用su命令的使用
syslog 從syslog中記錄信息(一般連接到messages文件)
utmp 紀錄當前登陸的每一個用戶
wtmp 一個用戶每次登陸進入和退出時間的永久紀錄
xferlog 紀錄FTP會話
原文連接:http://os.51cto.com/art/200711/60313.htm
建立一張員工表,代表EMPLOYEES,有四個字段,EMPLOYEE_ID:員工表(主鍵)、DEPT_ID:部門號、EMPLOYEE_NAME:員工姓名、EMPLOYEE_SALARY:員工工資。
問題一、寫出建表語句
CREATE TABLE EMPLOYEES(
EMPLOYEE_ID int not null primary key,
DEPT_IDint,
EMPLOYEE_NAME char(40),
EMPLOYEE_SALARY double
);
問題二、檢索出員工工資最高的員工姓名和工資
select * from user where employee_salary= (select max(employee_salary) from user)
問題三、檢索出部門中員工最多的部門號和此部門員工數量
select dept_id,count(*) cno from user GROUP BY dept_id desc limit 1
7.一、j2ee中的應用服務器有哪些?
1)Weblogic
2)Tomcat
3)JBoss
4)WebSphere
5)IIS
各個服務器之間的區別請參考連接:http://blog.csdn.net/qq1175421841/article/details/52280652
三、請簡述什麼是集羣?---瞭解就能夠
(服務器集羣就是指將很多服務器集中起來一塊兒進行同一種服務,在客戶端看來就象是隻有一個服務器。集羣能夠利用多個計算機進行並行計算從而得到很高的計算速度,也能夠用多個計算機作備份,從而使得任何一個機器壞了整個系統仍是能正常運行。一旦在服務器上安裝並運行了羣集服務,該服務器便可加入羣集。羣集化操做能夠,而且實現了羣集化資源的高可用性。下述各節簡要介紹了羣集建立和羣集操做中的節點行爲。)
1. 字符串中有重複的內容去重例如:abbccccaaddaggb-->abvadagb
1. public class Test {
2. public static void main(String[] args) {
3. List list = new ArrayList();
4. list.add("測試1");
5. list.add("測試2");
6. list.add("測試3");
7. list.add("測試4");
8. list.add("測試4");
9. list.add("測試2");
10. list.add("測試5");
11. System.out.println("沒有去重前的數據爲>>>"+list.toString());
12. for(int i = 0;i
13. for(int j = list.size()-1;j>i;j--) {
14. if(list.get(j).equals(list.get(i))){
15. list.remove(j);
16. }
17. }
18. }
19. System.out.println("去重後的數據爲>>>"+list.toString());
2. javaScript字符串去重的方法
a) for遍歷
1. function quchong1(str){
2. var newStr="";
3. var flag;
4. for(var i=0;i
5. flag=1;
6. for(var j=0;j
7. if(str[i]==newStr[j]){
8. flag=0;
9. break;
10. }
11. }
12. if(flag) newStr+=str[i];
13. }
14. return newStr;
15. }
b) indexOf(無兼容問題)
1. function quchong2(str){
2. var newStr="";
3. for(var i=0;i
4. if(newStr.indexOf(str[i])==-1){
5. newStr+=str[i];
6. }
7. }
8. return newStr;
9. }
c) search()方法
1. function quchong3(str){
2. var newStr="";
3. for(var i=0;i
4. if(newStr.search(str[i])==-1)
5. newStr+=str[i];
6.
7. }
8. return newStr;
9. }
d) 對象屬性
1. function quchong4(str){
2. var obj={};
3. var newStr="";
4. for(var i=0;i
5. if(!obj[str[i]]){
6. newStr+=str[i];
7. obj[str[i]]=1;
8. }
9. }
10. return newStr;
11. }
3. 利用java面向對象的思路設計正方形.長方形.和圓的計算面積的類
圓:
12. Public class MianJi {
13. float r;
14. float pai = (float) 3.14;
15. //圓的面積
16. void gongShi(){
17. Float s = pai*r*r;
18. System.out.println(「圓的面積爲」+s);
19. }
20. //正方形的面積
21. void zhengFangXing(float bianChang){
22. System.out.println(「正方形的面積爲」+bianChang*bianChang);
23. }
24. //長方形的面積
25. void zhengFangXing(float chang,float kuan){
26. System.out.println(「長方形的面積爲」+chang*kuan);
27. }
28.
29. Public static void main(String[] arg){
30. MianJi c = new MianJi ();
31. System.out.println(「請輸入圓的半徑:」)
32. Scanner sc = new Scanner(System,in);
33. c.r = sc.nextFloat();
34. c.gongShi();
35.
36. System.out.println(「請輸入正方形的邊長:」)
37. Scanner sc = new Scanner(System,in);
38. float bian = sc.nextFloat();
39. c.zhengFangXing(bian)
40.
41. System.out.println(「請輸入長方形的長和寬:」)
42. Scanner sc = new Scanner(System,in);
43. float chang= sc.nextFloat();
44. float kuan= sc.nextFloat();
45. c.changFangXing(chang,kuan);
46.
47. }
48. }
4. 任何>=6的偶數均可以分解爲兩個質數之和,從鍵盤輸入一個偶數,輸出其分解的質數
1. public class D3hw8 {
2. //將偶數拆分紅倆質數的和
3. public static void main(String[] args) {
4. int num=inPut();
5. outPut(num);
6.
7. }
8.
9. public static int inPut(){
10. //輸入數字並經過檢驗肯定一個符合要求的數
11. Scanner sc=new Scanner(System.in);
12. System.out.println("請輸入大於6的偶數:");
13. int num=sc.nextInt();
14. if(num%2!=0||num<=6){
15. System.out.println("輸入錯誤,請從新輸入大於6的偶數:");
16. return inPut();//錯誤則返回inPut()繼續輸入
17. }
18. return num;//正確則返回num值
19. }
20.
21. public static boolean isPrim(int num){
22. //判斷是不是質數
23. for(int i=2;i<=Math.sqrt((double)num);i++){
24. if(num%i==0){
25. return false;
26. }
27. }
28. return true;
29. }
30.
31. public static void outPut(int num){
32. //輸出全部符合條件的質數對
33. for(int i=2;i<=num/2;i++){
34. if(isPrim(i)==true&&isPrim(num-i)==true){
35. System.out.println(i+" "+(num-i));
36. }
37. }
38. }
39.
40.
41. }
1. 筆試題
1.1 面向對象的特徵有哪些方面?
答案:封裝,多態,繼承。詳細內容見本文第二章內容:JavaSE基礎,第一節Java面向對象
1.2 int 和 Integer 有什麼區別?
答案:Int是整型,Integer是int的封裝類型。詳細內容:見本文第二章內容:JavaSE基礎,第六 節:Java的數據類型
1.三、JAVA語言如何進行異常處理,關鍵字:throws,throw,try,catch,finally分別表明什麼意義?在try塊中能夠拋出異常嗎? 答案:詳細內容:見本文第二章內容:JavaSE基礎,第四節: Java的異常處理。
1.四、java中實現多態的機制是什麼? 答案:詳細內容:見本文第二章內容:JavaSE基礎,第三節: Java中的多態。
1.五、說出一些經常使用的類,包,接口,請各舉5個。
答案:包:lang,util,awt,swing,io
類:System, Math,Date,RunTime,Test
2.1題目:僱員查詢系統
2.1.1. 語言和環境
A、實現語言:
Java
B、實現技術:
HTML
JavaScript
JSP
Servlet
C、環境要求:
Eclipse
Oracle 9i
JDK 1.6
Tomcat 6
火狐
2.1.2. 要求
XXX公司有一我的事管理系統,其中一個功能模塊是根據員工職位及員工姓名對員工明細進行查詢。
功能和頁面設計要求:
查詢頁面能夠顯示僱員的職位名稱,職位名稱的顯示內容來源於數據庫表POST。職位名稱不能重複。
查詢頁面可以完成客戶端校驗工做,可以對未選擇職位類別進行校驗提示。
結果顯示頁面可以根據輸入的僱員名稱和選擇的僱員職位查詢結果,並正確的使用表格顯示結果,若是查詢內容爲空,應正確顯示提示信息。
數據庫設計要求:
數據庫名稱是handson,數據塊下的用戶名是EMPSYS。
數據庫表POST的全部字段必須按(表1)內容設置。內容按照(表3)內容填充數據表
數據庫表EMPLOYEE的全部字段必須按(表2)內容設置。內容按照(表4)內容填充數據表。
2.1.3. 數據庫的設計
數據庫的名稱: handson
用戶:EMPSYS
表1:
表名POST
主鍵POST_ID
序號字段名稱字段說明類型位數屬性備註
1POST_ID職位編號int4必填,非空
2POST_NAME職位名稱nvarchar50必填,非空
3POST_DESC職位描述nvarchar100
表2:
表名EMPLOYEE
主鍵EMP_ID
序號字段名稱字段說明類型位數屬性備註
1EMP_ID僱員編號int4必填,非空
POST_ID職位編號int4必填,非空
2EMP_NAME僱員姓名nvarchar100
3EMP_SEX僱員性別int4 1:男
2:女
EMP_AGE僱員年齡int4
EMP_DEPART所屬部門nvarchar50
4EMP_YEAR僱員工齡int4
2.1.4. 推薦實現步驟
1. 創建數據庫
A、創建用戶名EMPSYS和密碼EMPSYS:(5分)
B、創建數據庫表,表的結構參見上述表的結構:(5分)
C、數據庫完成之後,錄入下面記錄測試數據,以下表:(5分)
表3:
POST_IDPOST_NAMEPOST_DESC
1000行政助理行政輔助人員
1001行政主管行政負責人
1002業務經理業務處主管
1003總經理公司負責人
表4:
EMP_IDPOST_IDEMP_NAMEEMP_SEXEMP_AGEEMP_DEPARTEMP_YEAR
000011000李曉明125行政部2
000021000楊偉林129行政部5
000031002尤志苗233業務部9
000041003牛曉飛140集團10
2.1.4 設計WEB頁面
建立項目:
A、在Eclipse中創建JAVA WEB 項目。(3分)
製做首頁:
A、設計僱員查詢的主頁面,命名爲default.jsp,頁面風格能夠參看圖1所示。(5分)
B、編寫程序獲取數據表POST中的職位編號和名稱,填充在頁面控件中,要求職位名稱不能出現重複記錄。(10分)
C、單擊「查詢」按鈕時,要驗證客戶是否選擇職位名稱,若是沒有選擇職位名稱提示錯誤信息。(10分)
D、當首頁的僱員名項爲空時,顯示全部記錄。(15分)
E、當「僱員名稱」項和「職位名稱」項同時做爲條件時,按要求找出查詢記錄。(15分)
圖1 查詢主界面
設計製做結果頁面:
A、客戶選填寫了僱員名稱或選擇僱員職位後,單擊「查詢」按鈕,開始根據要求查詢,顯示查詢結果頁面,如圖2所示:(10分)
圖2 僱員明細信息顯示頁面
2.1.5. 注意事項:
請注意代碼的軟件書寫,實體的命名規範(12分)。
答案:
一、修飾類
當用final修飾一個類時,代表這個類不能被繼承。也就是說,若是一個類你永遠不會讓他被繼承,就能夠用final進行修飾。final類中的成員變量能夠根據須要設爲final,可是要注意final類中的全部成員方法都會被隱式地指定爲final方法。
在使用final修飾類的時候,要注意謹慎選擇,除非這個類真的在之後不會用來繼承或者出於安全的考慮,儘可能不要將類設計爲final類。
二、修飾方法
被final修飾的方法將不能被子類覆蓋,主要用於1,把方法鎖定,以防任何繼承類修改它的含。2,在早期的Java實現版本中,會將final方法轉爲內嵌調用,因此效率可以提高
三、修飾變量
對於一個final變量,若是是基本數據類型的變量,則其數值一旦在初始化以後便不能更改;若是是引用類型的變量,則在對其初始化以後便不能再讓其指向另外一個對象。
當用final做用於類的成員變量時,成員變量(注意是類的成員變量,局部變量只須要保證在使用以前被初始化賦值便可)必須在定義時或者構造器中進行初始化賦值,並且final變量一旦被初始化賦值以後,就不能再被賦值了。
答案:修改/etc/profile文件當本機僅僅做爲開發使用時推薦使用這種方法,由於此種配置時全部用戶的shell都有權使用這些環境變量,可能會給系統帶來安全性問題。用文本編輯器打開/etc/profile,在profile文件末尾加入:JAVA_HOME=/usr/share/jdk1.5.0_05
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME
export PATH
export CLASSPATH從新登陸便可
5. 寫出5個你在JAVA開發中經常使用的包含(全名),並簡述其做用。
經常使用的五個:
1)java.lang.*
提供利用 Java 編程語言進行程序設計的基礎類。最重要的類是 Object(它是類層次結構的根)和 Class(它的實例表示正在運行的應用程序中的類)。
2)java.util.*
包含集合框架、遺留的 collection 類、事件模型、日期和時間設施、國際化和各類實用工具類(字符串標記生成器、隨機數生成器和位數組、日期Date類、堆棧Stack類、向量Vector類等)。集合類、時間處理模式、日期時間工具等各種經常使用工具包
3)java.io.*
Java的核心庫java.io提供了全面的IO接口。包括:文件讀寫、標準設備輸出等。Java中IO是以流爲基礎進行輸入輸出的,全部數據被串行化寫入輸出流,或者從輸入流讀入。
4)java.net.*
並不是全部系統都支持 IPv6 協議,而當 Java 網絡鏈接堆棧嘗試檢測它並在可用時透明地使用它時,還能夠利用系統屬性禁用它。在 IPv6 不可用或被顯式禁用的狀況下,Inet6Address 對大多數網絡鏈接操做都再也不是有效參數。雖然能夠保證在查找主機名時 java.net.InetAddress.getByName 之類的方法不返回 Inet6Address,但仍然可能經過傳遞字面值來建立此類對象。在此狀況下,大多數方法在使用 Inet6Address 調用時都將拋出異常。
5)java.sql.*
提供使用 JavaTM 編程語言訪問並處理存儲在數據源(一般是一個關係數據庫)中的數據的 API。此 API 包括一個框架,憑藉此框架能夠動態地安裝不一樣驅動程序來訪問不一樣數據源。
6. 寫出5個常見的運行時異常(RuntimeException)。
1)ClassCastException(類轉換異常)
2)IndexOutOfBoundsException(數組越界)
3)NullPointerException(空指針)
4)ArrayStoreException(數據存儲異常,操做數組時類型不一致)
5)IO操做的BufferOverflowException異常
優勢:
新的實現很容易,由於大部分是繼承而來的 。很容易修改和擴展已有的實現
缺點:
打破了封裝,由於基類向子類暴露了實現細節 ,白盒重用,由於基類的內部細節一般對子類是可見的 ,當父類的實現改變時可能要相應的對子類作出改變 ,不能在運行時改變由父類繼承來的實現。因而可知,組合比繼承具備更大的靈活性和更穩定的結構,通常狀況下應該優先考慮組合。只有當下列條件知足時才考慮使用繼承: 子類是一種特殊的類型,而不僅是父類的一個角色 ,子類的實例不須要變成另外一個類的對象子類擴展,而不是覆蓋或者使父類的功能失效。
Java接口和Java抽象類表明的就是抽象類型,就是咱們須要提出的抽象層的具體表現。OOP面向對象的編程,若是要提升程序的複用率,增長程序的可維護性,可擴展性,就必須是面向接口的編程,面向抽象的編程,正確地使用接口、抽象類這些太有用的抽象類型作爲你結構層次上的頂層。
一、Java接口和Java抽象類最大的一個區別,就在於Java抽象類能夠提供某些方法的部分實現,而Java接口不能夠,這大概就是Java抽象類惟一的優勢吧,但這個優勢很是有用。 若是向一個抽象類里加入一個新的具體方法時,那麼它全部的子類都一會兒都獲得了這個新方法,而Java接口作不到這一點,若是向一個Java接口裏加入一個新方法,全部實現這個接口的類就沒法成功經過編譯了,由於你必須讓每個類都再實現這個方法才行.
二、一個抽象類的實現只能由這個抽象類的子類給出,也就是說,這個實現處在抽象類所定義出的繼承的等級結構中,而因爲Java語言的單繼承性,因此抽象類做爲類型定義工具的效能大打折扣。 在這一點上,Java接口的優點就出來了,任何一個實現了一個Java接口所規定的方法的類均可以具備這個接口的類型,而一個類能夠實現任意多個Java接口,從而這個類就有了多種類型。
三、從第2點不難看出,Java接口是定義混合類型的理想工具,混合類代表一個類不只僅具備某個主類型的行爲,並且具備其餘的次要行爲。
四、結合一、2點中抽象類和Java接口的各自優點,具精典的設計模式就出來了:聲明類型的工做仍然由Java接口承擔,可是同時給出一個Java抽象類,且實現了這個接口,而其餘同屬於這個抽象類型的具體類能夠選擇實現這個Java接口,也能夠選擇繼承這個抽象類,也就是說在層次結構中,Java接口在最上面,而後緊跟着抽象類,哈,這下兩個的最大優勢都能發揮到極至了。這個模式就是「缺省適配模式」。 在Java語言API中用了這種模式,並且全都遵循必定的命名規範:Abstract +接口名。
Java接口和Java抽象類的存在就是爲了用於具體類的實現和繼承的,若是你準備寫一個具體類去繼承另外一個具體類的話,那你的設計就有很大問題了。Java抽象類就是爲了繼承而存在的,它的抽象方法就是爲了強制子類必須去實現的。
使用Java接口和抽象Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。而不要用具體Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。
我想,若是你編的代碼裏面連一個接口和抽象類都沒有的話,也許我能夠說你根本沒有用到任何設計模式,任何一個設計模式都是和抽象分不開的,而抽象與Java接口和抽象Java類又是分不開的。
接口的做用,一言以蔽之,就是標誌類的類別。把不一樣類型的類歸於不一樣的接口,能夠更好的管理他們。把一組看如不相關的類歸爲一個接口去調用.能夠用一個接口型的變量來引用一個對象,這是接口我認爲最大的做用.
參考:https://www.cnblogs.com/AlanLee/p/6104492.html
答案:見本文第二章,第八節:Java的集合
12. 什麼叫對象持久化(OBJect PERSIstence),爲何要進行對象持久化?
持久化的對象,是已經存儲到數據庫或保存到本地硬盤中的對象,咱們稱之爲持久化對象。爲了保存在內存中的各類對象的狀態(也就是實例變量,不是方法),而且能夠把保存的對象狀態再讀出來。雖然你能夠用你本身的各類各樣的方法來保存object states,可是Java給你提供一種應該比你本身好的保存對象狀態的機制,那就是序列化。
簡單說就是對象序列化是將對象狀態轉換爲可保持或傳輸的格式的過程。
什麼狀況下須要序列化:
a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;
c)當你想經過RMI傳輸對象的時候;
對象要實現序列化,是很是簡單的,只須要實現Serializable接口就能夠了。
public class Test implements Serializable
(1).javascript的優勢:
javascript減小網絡傳輸。
在javascript這樣的用戶端腳本語言出現以前,傳統的數據提交和驗證工做均由用戶端瀏覽器經過網絡傳輸到服務器開發上進行。若是數據量很大,這對於網絡和服務器開發的資源來講實在是一種無形的浪費。而使用javascript就能夠在客戶端進行數據驗證。
javascript方便操縱html對象。
javascript能夠方便地操縱各類頁面中的對象,用戶能夠使用javascript來控制頁面中各個元素的外觀、狀態甚至運行方式,javascript能夠根據用戶的須要「定製」瀏覽器,從而使網頁更加友好。
javascript支持分佈式應用運算。
javascript能夠使多種任務僅在用戶端就能夠完成,而不須要網絡和服務器開發的參與,從而支持分佈式應用 的運算和處理。
(2)javascript的侷限性:
各瀏覽器廠商對javascript支持程度不一樣。
目前在互聯網上有不少瀏覽器,如firefox、internet explorer、opera等,但每種瀏覽器支持javascript的程度是不同的,不一樣的瀏覽器在瀏覽一個帶有javascript腳本的主頁時,因爲對javascript的支持稍有不一樣,其效果會有必定的差距,有時甚至會顯示不出來。
「web安全性」對javascript一些功能犧牲。
當把javascript的一個設計目標設定爲「web安全性」時,就須要犧牲javascript的一些功能。所以,純粹的javascript將不能打開、讀寫和保存用戶計算機上的文件。其有權訪問的惟一信息就是該javascript所嵌入開發 的那個web主頁中的信息,簡言之,javascript將只存在於它本身的小小世界—web主頁裏。
JSP(Java Server Pages)是由Sun Microsystems公司倡導、許多公司參與一塊兒創建的一種動態網頁技術標準。 JSP技術是用JAVA語言做爲腳本語言的,JSP網頁爲整個服務器端的JAVA庫單元提供了一個接口來服務於HTTP的應用程序。
在傳統的網頁HTML文件(*.htm,*.html)中加入Java程序片斷(Scriptlet)和JSP標記(tag),就構成了JSP網頁(*.jsp)。Web服務器在遇到訪問JSP網頁的請求時,首先執行其中的程序片斷,而後將執行結果以HTML格式返回給客戶。程序片斷能夠操做數據庫、從新定向網頁以及發送 email 等等,這就是創建動態網站所須要的功能。全部程序操做都在服務器端執行,網絡上傳送給客戶端的僅是獲得的結果,對客戶瀏覽器的要求最低,能夠實現無Plugin,無ActiveX,無Java Applet,甚至無Frame。
JSP的優勢:
修改靜態內容不須要從新編譯jsp
能夠執行一些java 腳本代碼.
1)對於用戶界面的更新,其實就是由 Web Server進行的,因此給人的感受更新很快。
2)全部的應用都是基於服務器的,因此它們能夠時刻保持最新版本。
3)客戶端的接口不是很繁瑣,對於各類應用易於部署、維護和修改。
事務讀取到回滾以前 的數據. 叫髒數據
髒數據在臨時更新(髒讀)中產生。事務A更新了某個數據項X,可是因爲某種緣由,事務A出現了問題,因而要把A回滾。可是在回滾以前,另外一個事務B讀取了數據項X的值(A更新後),A回滾了事務,數據項恢復了原值。事務B讀取的就是數據項X的就是一個「臨時」的值,就是髒數據。
髒讀就是指當一個事務正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。由於這個數據是尚未提交的數據,那麼另一個事務讀到的這個數據是髒數據,依據髒數據所作的操做多是不正確的。
ERP系統是企業資源計劃(Enterprise Resource Planning )的簡稱。
BOSS(Business & Operation Support )指的是業務運營支撐系統。
BOS 是 ERP 的集成與應用平臺。 BOS 遵循面向服務的架構體系,是一個面向業務的可視化開發平臺;是一個 ERP 和第三方應用集成的技術平臺。它有效的解決了 ERP 應用的最主要矛盾---用戶需求個性化和傳統 ERP 軟件標準化之間的矛盾。
BOS 與 ERP 是什麼關係?
ERP 是企業管理信息化的全面解決方案, ERP 是基於 BOS 構建的。 ERP 知足企業全面業務的標準應用;BOS 確保了企業 ERP 應用中的個性化需求完美實現。基於 BOS 的 ERP,能夠爲不一樣行業不一樣發展階段的企業構建靈活的、可擴展的、全面集成的總體解決方案。
做者:王震陽
連接:https://www.jianshu.com/p/fb7d48083e5e
來源:簡書
簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。finalize