1.什麼是Java虛擬機?爲何Java被稱做是「平臺無關的編程語言」?java
一、是麼是平臺node
Java是能夠跨平臺的編程語言,那咱們首先得知道什麼是平臺,咱們把CPU處理器與操做系統的總體叫平臺。linux
CPU你們都知道,若是計算機是人,那CPU就是人的大腦,它既負責思惟運算,又負責身體各部件的命令控制。CPU的種類不少,除去咱們熟知的Intel與AMD外,還有好比上面說到的SUN的Sparc,好比IBM的PowerPC等等,這些各個公司生產的CPU使用或相同或不一樣的指令集。指令集就是cpu中用來計算和控制計算機系統的一套指令的集合。指令集又分爲精簡指令集(RISC)與複雜指令集(CISC),每種cpu都有其特定的指令集。開發程序,首先要知道該程序在什麼CPU上運行,也就是要知道CPU所使用的指令集。android
下面說操做系統,操做系統是充當用戶和計算機之間交互的界面軟件,不一樣的操做系統支持不一樣的CPU,嚴格意義上說是不一樣的操做系統支持不一樣CPU的指令集。例如 windows和liunx都支持Intel和AMD的複雜指令集,但並不支持PowerPC所使用的精簡指令集,而早期的MAC電腦使用的是PowerPC處理器,因此也就沒法在MAC下直接安裝windows,直到05年MAC改用了Intel的CPU,才使在MAC下安裝windows成爲可能。但問題來了,原來的MAC 操做系統也只支持PowerPC,在Intel上也不能安裝,怎麼辦?因此蘋果公司也得重寫本身的MAC操做系統以支持這種變化。最後總結下,咱們要知道,不一樣的操做系統支持不一樣的CPU指令集,如今的windows,liunx,mac,solaris都支持Intel與AMD的CPU指令集。程序員
有了上面的鋪墊,旺旺老師就要告訴你們,若是您要開發程序,首先應該肯定:1,CPU類型,也就是指令集類型;2,操做系統;咱們把這種軟硬件的結合叫平臺。也能夠說「平臺= CPU+OS」。又由於如今主流的操做系統都支持主流的CPU,因此有時也把操做系統稱爲平臺。web
知道什麼是平臺,咱們看Java跨平臺原理。ajax
二、Java跨平臺原理算法
首先看一張與C語言有關的圖: spring
若是您有過C的開發經歷,這張圖看起來將很是輕鬆。咱們知道,只要是用標準C開發的程序,使用不一樣的編譯器編譯後的可執行文件是能夠在對應平臺運行的,好比windows能夠使用VC編譯,那編譯後的exe文件就能夠在windows下運行;liunx下能夠使用GCC編譯,生成的可執行文件就能夠在Liunx上運行。sql
到這裏請你們思考一個問題:「VC編譯的exe能在Liunx上運行嗎?」
答案確定是否認的。使用特定編譯器編譯的程序只能在對應的平臺運行,這裏也能夠說編譯器是與平臺相關的,編譯後的文件也是與平臺相關的。咱們說的語言跨平臺是編譯後的文件跨平臺,而不是源程序跨平臺,若是是源程序,任何一門語言都是跨平臺的語言了。這個若是您不明白,看下面一個案例:
比 如火星真的有外星人(而且毋庸置疑,火星是韓國人的,火星文也必定是韓國人發明的),就像咱們觀察螞蟻同樣,火星人默默的觀察着咱們,有一天,當人類作的 什麼事情讓火星人實在是看不下去了(好比旺旺老師的書出版了你不買,哈哈,嘔吐中,不要緊,吐啊吐啊就吐習慣了),因此決定來地球教育咱們,但有一個問 題,火星人只會說火星文,地球人理解不了,怎麼辦啊?找翻譯唄(也許非主流能夠幫忙,玩笑)!由中文翻譯把火星文翻譯爲中文,英文翻譯把火星文翻譯爲英文 等等等等,但這樣問題來了,中文翻譯翻譯的東西只有中國人能聽懂,美國人法國人根本不明白,英文翻譯翻譯的文章中國人也不明白,也就是語言不能跨平臺。
那上例中,火星文就是C語言,各個國家是平臺,中文翻譯英文翻譯就是對應平臺的編譯器,編譯後的文章就是可執行文件。雖然源文章火星文是與平臺無關的,但翻譯器是與特定國家相關的,翻譯後的文章也是與特定國家相關的。
接下來思考另外一個問題「怎麼讓火星文跨平臺呢?」
火星人想到了地球上有世界語,因而首先把本身的文章翻譯爲世界語;世界語各國人固然看不懂,不要緊,火星人又給每一個國家配備了一個世界語到本地語的翻譯,這 樣火星文只要翻譯一次(翻譯爲世界語),就能夠到各個國家運行了。還要記住,這個過程火星人要提供兩個組件,第一是火星文到世界語的翻譯,第二是世界語到 對應本地語言的翻譯。以下圖:
有了上面案例的積累,咱們也知道了語言跨平臺原理:「不能編譯成機器語言,由於那樣就與平臺相關了,編譯爲中間語言,再由解釋器二次編譯,解釋執行。」以下是Java跨平臺原理表示圖:
上圖中的.java就是源程序,相似於c語言的.c,生成的中間碼是.class,這個既是咱們上文中說的中間語,各個平臺解釋器就是各類國家翻譯。
接下來咱們再比較下兩種方式的差別:第一,C語言是編譯執行的,編譯器與平臺相關,編譯生成的可執行文件與平臺相關;第二,Java是解釋執行的,編譯爲中間碼的編譯器與平臺無關,編譯生成的中間碼也與平臺無關(一次編譯,處處運行),中間碼再由解釋器解釋執行,解釋器是與平臺相關的,也就是不一樣的平臺須要不一樣的解釋器.
這裏再說下語言根據執行方式的不一樣分類:第一是編譯執行,如上文中說到的C,它把源程序由特定平臺的編譯器一次性編譯爲平臺相關的機器碼,它的優勢是執行速度快,缺點是沒法跨平臺;第二是解釋執行,如HTML,JavaScript,它使用特定的解釋器,把代碼一行行解釋爲機器碼,相似於同聲翻譯,它的優勢是能夠跨平臺,缺點是執行速度慢,暴露源程序;第三種是從Java開始引入的「中間碼+虛擬機」的方式,它既整合了編譯語言與解釋語言的優勢,同時如虛擬機又能夠解決如垃圾回收,安全性檢查等這些傳統語言頭疼的問題,因此其後微軟的.NET平臺也使用的這種方式。
Java先編譯後解釋
同一個.class文件在不一樣的虛擬機會獲得不一樣的機器指令(Windows和Linux的機器指令不一樣),可是最終執行的結果倒是相同的
最後再請你們思考一個問題:「開發Java程序須要什麼?運行Java程序須要什麼?」
答案:開發Java須要由源文件到中間文件的解釋器;運行Java須要對應平臺的解釋器。與火星人要提供兩個組件同樣,SUN也得提供兩個組件:第一,把源程序翻譯爲中間碼的編譯器;第二,相應平臺的解釋器。SUN把這兩個組件包含在一個工具包中,咱們把它叫作JDK(Java Developent ToolKit),接下來咱們學習JDK的安裝與使用。
2.JDK、JRE、JVM關係是什麼?
JDK(Java Development Kit)
JRE(Java Runtime Environment)
JVM(Java Virtual Machines)
JDK是 Java 語言的軟件開發工具包(SDK)。在JDK的安裝目錄下有一個jre目錄,裏面有兩個文件夾bin和lib,在這裏能夠認爲bin裏的就是jvm,lib中則是jvm工做所須要的類庫,而jvm和 lib合起來就稱爲jre。
1、JDK
JDK(Java Development Kit) 是整個JAVA的核心,包括了Java運行環境(Java Runtime Envirnment),一堆Java工具(javac/java/jdb等)和Java基礎的類庫(即Java API 包括rt.jar)。
JDK是java開發工具包,基本上每一個學java的人都會先在機器 上裝一個JDK,那他都包含哪幾部分呢?在目錄下面有 六個文件夾、一個src類庫源碼壓縮包、和其餘幾個聲明文件。其中,真正在運行java時起做用的 是如下四個文件夾:bin、include、lib、 jre。有這樣一個關係,JDK包含JRE,而JRE包 含JVM。
bin:最主要的是編譯器(javac.exe)
include:java和JVM交互用的頭文件
lib:類庫
jre:java運行環境
1234
(注意:這裏的bin、lib文件夾和jre裏的bin、lib是 不一樣的)
總的來講JDK是用於java程序的開發,而jre則是隻能運行class而沒有編譯的功能。
2、JRE
JRE(Java Runtime Environment,Java運行環境),包含JVM標準實現及Java核心類庫。JRE是Java運行環境,並非一個開發環境,因此沒有包含任何開發工具(如編譯器和調試器)
JRE是指java運行環境。光有JVM還不能成class的 執行,由於在解釋class的時候JVM須要調用解釋所須要的類庫lib。 (jre裏有運行.class的java.exe)
JRE ( Java Runtime Environment ),是運行 Java 程序必不可少的(除非用其餘一些編譯環境編譯成.exe可執行文件……),JRE的 地位就象一臺PC機同樣,咱們寫好的Win64應用程序須要操做系統幫 咱們運行,一樣的,咱們編寫的Java程序也必需要JRE才能運行。
3、JVM
JVM(Java Virtual Machine),即java虛擬機, java運行時的環境,JVM是一種用於計算設備的規範,它是一個虛構出來的計算機,是經過在實際的計算機上仿真模擬各類計算機功能來實現的。針對java用戶,也就是擁有可運行的.class文件包(jar或者war)的用戶。裏面主要包含了jvm和java運行時基本類庫(rt.jar)。rt.jar能夠簡單粗暴地理解爲:它就是java源碼編譯成的jar包。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。這就是Java的可以「一次編譯,處處運行」的緣由。
4、JDK、JRE、JVM三者的聯繫與區別
1.三者聯繫:
JVM不能單獨搞定class的執行,解釋class的時候JVM須要調用解釋所須要的類庫lib。在JDK下面的的jre目錄裏面有兩個文件夾bin和lib,在這裏能夠認爲bin裏的就是jvm,lib中則是jvm工做所須要的類庫,而jvm和 lib和起來就稱爲jre。JVM+Lib=JRE。整體來講就是,咱們利用JDK(調用JAVA API)開發了屬於咱們本身的JAVA程序後,經過JDK中的編譯程序(javac)將咱們的文本java文件編譯成JAVA字節碼,在JRE上運行這些JAVA字節碼,JVM解析這些字節碼,映射到CPU指令集或OS的系統調用。
2.三者區別:
a.JDK和JRE區別:在bin文件夾下會發現,JDK有javac.exe而JRE裏面沒有,javac指令是用來將java文件編譯成class文件的,這是開發者須要的,而用戶(只須要運行的人)是不須要的。JDK還有jar.exe, javadoc.exe等等用於開發的可執行指令文件。這也證明了一個是開發環境,一個是運行環境。
b.JRE和JVM區別:JVM並不表明就能夠執行class了,JVM執行.class還須要JRE下的lib類庫的支持,尤爲是rt.jar。
參考文獻:http://playkid.blog.163.com/blog/static/56287260201372113842153/
https://baike.baidu.com/item/JVM/2902369?fr=aladdin
---------------------
版權聲明:本文爲CSDN博主「Ancientear」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/ancientear/article/details/79483592
3.Java支持的數據類型有哪些?什麼是自動拆裝箱?
基本數據類型:
整數值型:
字符型:
浮點類型:
布爾型:
Java有8種基本數據類型:
整數型:byte(8)、short(16)、int(32)、long(64)
浮點類型:float(32)、double(64)
字符型:char(16位的Unicode字符)
布爾型:boolean
jdk1.5以後支持自動拆裝箱
自動裝箱就是Java編譯器在基本數據類型和對應的對象包裝類型之間作的一個轉化。自動拆箱反之。
4.面向對象是什麼?
面向對象是一種編程風格,Python一切皆對象,把一切東西當作是一個個對象,好比人、耳機、鼠標、水杯等,他們各自都有屬性,好比:耳機是白色的,鼠標是黑色的,水杯是圓柱形的等等,把這些對象擁有的屬性變量和操做這些屬性變量的函數打包成一個類來表示
面向對象有三大特性:封裝,繼承,多態。
封裝:
將一類事物的屬性和行爲抽象成一個類,使其屬性私有化,行爲公開化,提升了數據的隱祕性的同時,使代碼模塊化。這樣作使得代碼的複用性更高。
意義:
將屬性和方法放到一塊兒作爲一個總體,而後經過實例化對象來處理;
隱藏內部實現細節,只須要和對象及其屬性和方法交互就能夠了;
對類的屬性和方法增長 訪問權限控制。
繼承:
在程序中,繼承描述的是多個類之間的所屬關係,若是一個類A裏面的屬性和方法能夠複用,則能夠經過繼承的方式,傳遞到類B裏,那麼類A就是基類,也叫作父類;類B就是派生類,也叫作子類。繼承進一步提升了代碼的複用性。
多態:
所謂多態:定義時的類型和運行時的類型不同,此時就成爲多態 ,多態的概念是應用於Java和C#這一類強類型語言中,而Python崇尚「鴨子類型」。
鴨子類型:雖然我想要一隻"鴨子",可是你給了我一隻鳥。 可是隻要這隻鳥走路像鴨子,叫起來像鴨子,游泳也像鴨子,我就認爲這是鴨子。
Python的多態,就是弱化類型,重點在於對象參數是否有指定的屬性和方法,若是有就認定合適,而不關心對象的類型是否正確。
面向過程:根據業務邏輯從上到下寫代碼
面向對象:將數據與函數綁定到一塊兒,進行封裝,這樣可以更快速的開發程序,減小了重複代碼的重寫過程
舉個例子:吃鴨子
第一種方式(面向過程):
1)養鴨子
2)鴨子長成
3)殺
4)做料
5)烹飪
6)吃
7)卒
第二種方式(面向對象):
1)找個賣啤酒鴨的人
2)給錢 交易
3)吃
4)胖6斤
---------------------
版權聲明:本文爲CSDN博主「愛吃蘿蔔的喵」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/zzd864582451/article/details/85335748
5.什麼是值傳遞和引用傳遞?
1、基本類型和引用類型的理解Java中的數據類型分爲兩種爲基本類型和引用類型。
一、基本類型的變量保存原始值,因此變量就是數據自己。
常見的基本類型:byte,short,int,long,char,float,double,Boolean,returnAddress。
二、引用類型的變量保存引用值,所謂的引用值就是對象所在內存空間的「首地址值」,經過對這個引用值來操做對象。
常見的引用類型:類類型,接口類型和數組。
2、值傳遞和引用傳遞的理解
一、值傳遞 在方法的調用過程當中,實參把它的實際值傳遞給形參,此傳遞過程就是將實參的值複製一份傳遞到函數中,這樣若是在函數中對該值(形參的值)進行了操做將不會影響實參的值。由於是直接複製,因此這種方式在傳遞大量數據時,運行效率會特別低下。
二、引用傳遞 引用傳遞彌補了值傳遞的不足,若是傳遞的數據量很大,直接復過去的話,會佔用大量的內存空間,而引用傳遞就是將對象的地址值傳遞過去,函數接收的是原始值的首地址值。在方法的執行過程當中,形參和實參的內容相同,指向同一塊內存地址,也就是說操做的其實都是源數據,因此方法的執行將會影響到實際對象。
舉例說明:public class Example {
String str = new String("hello"); char[] ch = {'a', 'b'};
public static void main(String[] args) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.println(ex.str + " and");
System.out.println(ex.ch); }
public void change(String str, char[] ch) {
str = "ok";
ch[0] = 'c';
}}
輸出是:hello andcb
過程分析:
一、爲對象分配空間
二、執行change()方法執行前實參(黑色)和形參(紅色)的指向以下:
由於String是不可變類且爲值傳遞,而ch[]是引用傳遞,因此方法中的str = "ok",至關於從新建立一個對象並無改變實參str的值,數組是引用傳遞,直接改變,因此執行完方法後,指向關係以下:
3.結論經過上面的分析咱們能夠得出如下結論:基本數據類型傳值,對形參的修改不會影響實參;引用類型傳引用,形參和實參指向同一個內存地址(同一個對象),因此對參數的修改會影響到實際的對象。
String, Integer, Double等immutable的類型特殊處理,能夠理解爲傳值,最後的操做不會修改實參對象。
---------------------
版權聲明:本文爲CSDN博主「Norte_L」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/Norte_L/article/details/80250057
6.Java中的方法覆蓋(Overriding)和方法重載(Overloading)是什麼意思?
java中的方法重載發生在同一個類裏面兩個或者多個方法的方法名相同可是參數不一樣的狀況。與此相對,方法覆蓋是說子類從新定義了父類的方法。方法覆蓋必須有相同的方法名,參數列表和返回類型。
覆蓋者可能不會限制它所覆蓋的方法的訪問。
重載(Overloading)
(1)方法重載是讓類以統一的方法處理不一樣類型數據的一種手段。多個同名函數同時存在,具備不一樣的參數個數(類型)。重載Override是一個類中多態性的一種表現。
(2)java的方法重載,就是在類中能夠建立多個方法,他們具備相同的名字,但具備不一樣參數和不一樣的定義。調用方法時經過傳遞給他們不一樣的參數個數和參數類型來決定具體使用那個方法,這就是多態性。
(3)重載的時候,方法名要同樣,可是參數類型和個數不同,返回值類型能夠相同也能夠不一樣。沒法以返回類型來做爲重載函數的區分標準。
重寫(Overriding)
(1)父類與子類的多態性,對父類的函數進行從新定義。若是在子類中定義某方法與其父類有相同的名稱和參數,咱們說該方法被重寫。在java中,子類可繼承父類的方法,則不須要從新編寫相同的方法。但有時子類並不想原封不動繼承父類的方法,而是想作必定的修改,這就採用方法重寫。方法重寫又稱方法覆蓋。
(2)若子類中的方法與父類的中的某一方法具備相同的方法名、返回類型和參數表,則新方法覆蓋原有的方法。如須要父類的原有方法,能夠使用super關鍵字,該關鍵字引用房錢類的父類。
(3)子類函數訪問權限大於父類。
7.Java中,什麼是構造方法?什麼是構造方法重載?什麼是複製構造方法?
當新對象被建立的時候,構造方法會被調用。每個類都有構造方法。在程序員沒有給類提供構造方法的狀況下,Java編譯器會爲這個類建立一個默認的構造方法。
Java中構造方法重載和方法重載很類似。能夠爲一個類建立多個構造方法。每個構造方法必須有它本身惟一的參數列表。
Java不支持像C++中那樣的複製構造方法,這個不一樣點是由於若是你不本身寫構造方法的狀況下,Java不會建立默認的複製構造方法。
---------------------
版權聲明:本文爲CSDN博主「never瘋」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/qq_40574571/article/details/90600081
8.Java支持多繼承麼?
不支持,Java中每一個類都只能繼承一個類,但能夠實現多個接口。。。
9.String和StringBuilder、StringBuffer的區別?
1、Java String 類——String字符串常量
字符串普遍應用 在Java 編程中,在 Java 中字符串屬於對象,Java 提供了String 類來建立和操做字符串。
須要注意的是,String的值是不可變的,這就致使每次對String的操做都會生成新的String對象,這樣不只效率低下,並且大量浪費有限的內存空間。咱們來看一下這張對String操做時內存變化的圖:
咱們能夠看到,初始String值爲「hello」,而後在這個字符串後面加上新的字符串「world」,這個過程是須要從新在棧堆內存中開闢內存空間的,最終獲得了「hello world」字符串也相應的須要開闢內存空間,這樣短短的兩個字符串,卻須要開闢三次內存空間,不得不說這是對內存空間的極大浪費。爲了應對常常性的字符串相關的操做,谷歌引入了兩個新的類——StringBuffer類和StringBuild類來對此種變化字符串進行處理。
2、Java StringBuffer 和 StringBuilder 類——StringBuffer字符串變量、StringBuilder字符串變量
當對字符串進行修改的時候,須要使用 StringBuffer 和 StringBuilder 類。
和 String 類不一樣的是,StringBuffer 和 StringBuilder 類的對象可以被屢次的修改,而且不產生新的未使用對象。
StringBuilder 類在 Java 5 中被提出,它和 StringBuffer 之間的最大不一樣在於 StringBuilder 的方法不是線程安全的(不能同步訪問)。
因爲 StringBuilder 相較於 StringBuffer 有速度優點,因此多數狀況下建議使用 StringBuilder 類。然而在應用程序要求線程安全的狀況下,則必須使用 StringBuffer 類。
三者的繼承結構
三者的區別:
(1)字符修改上的區別(主要,見上面分析)
(2)初始化上的區別,String能夠空賦值,後者不行,報錯
①String
String s = null;
String s = 「abc」;
②StringBuffer
StringBuffer s = null; //結果警告:Null pointer access: The variable result can only be null at this location
StringBuffer s = new StringBuffer();//StringBuffer對象是一個空的對象
StringBuffer s = new StringBuffer(「abc」);//建立帶有內容的StringBuffer對象,對象的內容就是字符串」
小結:(1)若是要操做少許的數據用 String;
(2)多線程操做字符串緩衝區下操做大量數據 StringBuffer;
(3)單線程操做字符串緩衝區下操做大量數據 StringBuilder。
十、內部類與外部類的調用
部類:
①靜態內部類中能夠有非靜態的方法
②當內部類中有靜態方法或者靜態成員變量時,必定是靜態內部類
通常內部類在外部類的成員變量位置,像這樣:
1 1 public class Outer { 2 class Inner{ 3 4 } 5 } 2 3 一、外部類訪問內部類: 4 5 內部類被static修飾:能夠直接new 6 7 Inner in = new Inner(); 8 9 內部類沒有被static修飾:得先new出來外部類的實例,再new內部類的 10 11 Inner in = new Outer().new Inner(); 12 13 二、內部類訪問外部類:(外部類.this.變量) 14
1 2 3 1 public class Outer {
2 int x = 9;
3 class Inner{
4 int x = 8;
5 public void test(){
6 int x = 7;
7 System.out.println(x);
8 System.out.println(this.x);
9 System.out.println(Outer.this.x);
10 test1();
11 }
12 }
13
14 private void test1(){
15 System.out.println("test");
16 }
17 public static void main(String[] args) {
18 Inner in = new Outer().new Inner();
19 in.test();
20 }
21 } 4 5 複製代碼 6
輸出爲:7,8,9,test
分析:第七行第八行相信你們都沒有什麼問題,第九行輸出的是9,說明訪問到了外部類的變量,而輸出的test說明內部類訪問到了外部類的test1方法
總結:
輸出是9的緣由:由於內部類持有一個外部類的引用,格式:外部類名.this
能夠調用private方法緣由是:由於他們在一個類Outer中因此能夠調用
三、外部類和內部類中的方法相互訪問:
①外部類的靜態方法test和非靜態內部類的非靜態方法voice的相互訪問:
! test----->voice 先new外類再new內類,再調方法
1 1 public class Outerclass { 2 3 2 class Inner{ 4 5 3 public void voice(){ 6 7 4 System.out.println("voice()"); 8 9 5 } 10 11 6 } 12 13 7 public static void test(){ 14 15 8 new Outerclass().new Inner().voice(); 16 17 9 } 18 19 10 public static void main(String[] args) { 20 //主函數調用test方法 11 test(); 21 22 13 } 23 24 14 } 25
輸出:voice();
!! voice----->test 外類.this.方法(持有的外部類的引用)
1 1 public class Outerclass {
2 class Inner{
3 public void voice(){
4 Outerclass.this.test();
5 }
6 }
7 public static void test(){
8 System.out.println("test()");
9 }
10 public static void main(String[] args) { 2 //主函數調用voice()
11 Inner in = new Outerclass().new Inner();
12 in.voice();
13 }
14 } 3 4 複製代碼 5
輸出:test();
②外部類的非靜態方法test和靜態內部類中的非靜態方法voice之間的相互調用
! voice------>test
1 1 public class Outerclass { 2 2 static class Inner{ 3 3 public void voice(){ 4 4 new Outerclass().test(); 5 5 } 6 6 } 7 7 public void test(){ 8 8 System.out.println("test()"); 9 9 } 10 10 public static void main(String[] args) { 11 //主函數調用voice()方法 11 new Outerclass.Inner().voice(); 12 12 } 13 13 }
輸出:test();
!! test----->voice
1 public class Outerclass { 2 static class Inner{ 3 public void voice(){ 4 System.out.println("voice()"); 5 } 6 } 7 public void test(){ //一、其餘類訪問外部類中的靜態內部類的非靜態方法 8 // new Outerclass.Inner().voice(); //二、此處的Outerclass中的test方法訪問靜態內部類中的非靜態方法 new Inner().voice(); 9 } 10 public static void main(String[] args) { //主函數調用test方法 11 new Outerclass().test(); 12 } 13 } 複製代碼
輸出:voice();
四、總結:
外部類訪問內部類:必須創建內部類的對象
內部類訪問外部類:內部類能夠直接訪問外部類的成員包括私有成員,由於外部類持有內部類的引用
特例:內部類寫在外部類的方法中(即局部變量的位置)
一、內部來外部類都可定義變量/常量
二、只能被final/abstract修飾
三、只能訪問被final/abstract修飾的變量
四、能夠直接訪問外部類中的成員,由於還持有外部類的引用
十一、多線程
a)線程與進程的區別,舉例說明
進程是資源分配和管理的基本單位,進程中包含的一個或多個執行單元叫作線程。一個程序至少有一個進程,一個進程至少有一個線程
理解線程與進程
若是把CPU比喻成工廠,單核CPU就是指工廠的電力有限,一次只能給一個車間供應電力,當CPU給某個車間供電的時候,其餘車間都要暫時停工。
若是是多核CPU,就至關於這個工廠的電力能夠一次供應多個車間
這時車間就至關於進程
一個車間裏,能夠有不少個機器人,他們一塊兒完成這個車間的任務
機器人就至關於線程
一個車間能夠有多個機器人,一個進程能夠有多個線程
可是機器人須要供電才能運做,單核CPU就至關於一次只能給一個機器人供電
因此在實際生產中,工廠一次只能給一個車間的一個機器人供電,下一次給另一個機器人供電(能夠是相同車間或者不一樣車間),就這樣交替供電來完成工廠中全部的任務
在系統中也同樣,單核CPU一次只能給一個進程的一個線程調度,而後不一樣的線程交替執行,來完成系統中全部的任務
車間的空間是這個車間的機器人們共享的,相應地,一個進程中的內存空間能夠給這個進程中的全部線程共享
但不一樣車間的空間不能共享,因此不一樣進程不能共享內存空間
---------------------
版權聲明:本文爲CSDN博主「sun_ljz」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/zhao18933/article/details/46699629
b)爲何要使用多線程
一、避免阻塞(異步調用)
單個線程中的程序,是順序執行的。若是前面的操做發生了阻塞,那麼就會影響到後面的操做。這時候能夠採用多線程,我感受就等因而異步調用。這樣的例子有不少:
ajax調用,就是瀏覽器會啓一個新的線程,不阻塞當前頁面的正常操做;
流程在某個環節調用web service,若是是同步調用,則須要等待web service調用結果,能夠啓動新線程來調用,不影響主流程;
android裏,不要在ui thread裏執行耗時操做,不然容易引起ANR;
建立工單時,須要級聯往其餘表中插入數據,能夠將級聯插入的動做放到新線程中,先返回工單建立的結果……
二、避免CPU空轉
以http server爲例,若是隻用單線程響應HTTP請求,即處理完一條請求,再處理下一條請求的話,CPU會存在大量的閒置時間
由於處理一條請求,常常涉及到RPC、數據庫訪問、磁盤IO等操做,這些操做的速度比CPU慢不少,而在等待這些響應的時候,CPU卻不能去處理新的請求,所以http server的性能就不好
因此不少web容器,都採用對每一個請求建立新線程來響應的方式實現,這樣在等待請求A的IO操做的等待時間裏,就能夠去繼續處理請求B,對併發的響應性就行了不少
固然,這種設計方式並非絕對的,如今像node.js、Nginx等新一代http server,採用了事件驅動的實現方式,用單線程來響應多個請求也是沒問題的。甚至實現了更高的性能,由於多線程是一把雙刃劍,在提高了響應性的同時,建立銷燬線程都是須要開銷的,另外CPU在線程之間切換,也會帶來額外的開銷。避免了這些額外開銷,多是node.js等http server性能優秀的緣由之一吧
三、提高性能
在知足條件的前提下,多線程確實能提高性能
打一個比方,多線程就至關於,把要炒的菜放到了不一樣的鍋裏,而後用不一樣的爐來炒,固然速度會比較快。原本須要先炒西紅柿,10分鐘;再炒白菜10分鐘;加起來就須要20分鐘。用了多線程之後,分別放在2個鍋裏炒,10分鐘就都炒好了
基本上,須要知足3個條件:
第1,任務具備併發性,也就是能夠拆分紅多個子任務。並非什麼任務都能拆分的,條件還比較苛刻
子任務之間不能有前後順序的依賴,必須是容許並行的
好比
這個是能夠並行的;
這個就沒法並行了,第2步計算須要依賴第1步的計算結果,即便分紅2個線程,也不會帶來任何性能提高
另外,還不能有資源競爭。好比2個線程都須要寫一個文件,第1個線程將文件鎖定了,第2個線程只能等着,這樣的2個子任務,也不具有併發性;執行sychronized代碼,也是一樣的狀況
第2,只有在CPU是性能瓶頸的狀況下,多線程才能實現提高性能的目的。好比一段程序,瓶頸在於IO操做,那麼把這個程序拆分到2個線程中執行,也是沒法提高性能的
第3,有點像廢話,就是須要有多核CPU才行。不然的話,雖然拆分紅了多個可並行的子任務,可是沒有足夠的CPU,仍是隻有一個CPU在多個線程中切換來切換去,不但達不到提高性能的效果,反而因爲增長了額外的開銷,而下降了性能。相似於雖然把菜放到了2個鍋裏,可是隻有1個爐子同樣
若是上述條件都知足,有一個經驗公式能夠計算性能提高的比例,叫阿姆達爾定律:
速度提高比例 = 1/[(1-P)+(P/N)],其中P是可並行任務的比例,N是CPU核心數量
假設CPU核心是無限的,則公式簡化爲1/(1-P)
假設P達到了80%(已經很是高了),那麼速度提高比例也只能達到5倍而已
c)用戶線程和守護線程(系統線程),舉例
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程)
Java平臺把操做系統的底層進行了屏蔽,在JVM虛擬平臺裏面構造出對本身有利的機制,這就是守護線程的由來。Daemon的做用是爲其餘線程的運行提供服務,好比說GC線程。
User Thread線程和Daemon Thread惟一的區別之處就在虛擬機的離開,若是User Thread所有撤離,那麼Daemon Thread也就沒啥線程好服務的了,因此虛擬機也就退出了。
守護線程用戶也能夠自行設定,方法:public final void setDaemon(boolean flag)
注意點:
正在運行的常規線程不能設置爲守護線程。
thread.setDaemon(true)必須在thread.start()以前設置,不然會跑出一個IllegalThreadStateException異常。
在Daemon線程中產生的新線程也是Daemon的(這裏要和linux的區分,linux中守護進程fork()出來的子進程再也不是守護進程)
根據本身的場景使用(在應用中,有可能你的Daemon Thread還沒來的及進行操做時,虛擬機可能已經退出了)
非守護線程案例:
1 2 3 複製代碼 4 5 public class DaemonTest { 6 public static void main(String[] args) { 7 Thread thread = new Thread(new Runnable() { 8 @Overridepublic void run() { 9 try {Thread.sleep(2000);} 10 catch (InterruptedException e) { 11 e.printStackTrace();} 12 System.out.println("thread 線程結束....");}}); 13 thread.start(); 14 System.out.println("main線程 結束...."); 15 } 16 } 17 18 複製代碼 19
結果輸出:
main線程 結束....
thread 線程結束....
守護線程案例:
1 package cn.kafka.t; 2 public class DaemonTest { 3 public static void main(String[] args) { 4 Thread thread = new Thread(new Runnable() { 5 @Overridepublic void run() {t 6 ry { 7 Thread.sleep(2000);} 8 catch (InterruptedException e) { 9 e.printStackTrace();} 10 System.out.println("thread 線程結束...."); 11 } 12 } 13 ); 14 //設置爲守護線程 thread.setDaemon(true);thread.start(); 15 System.out.println("main線程 結束....") 16 ;} 17 } 18 19 複製代碼 20
結果輸出:
main線程 結束....
結論:主線程結束,JVM直接退出,守護線程無論是否運行結束都要伴隨着JVM的退出而退出
d)線程的優先級別:
每一個進程都有相應的優先級,優先級決定它什麼時候運行和接收多少CPU時間。最終的優先級共32級,是從0到31的數值,稱爲基本優先級別(Base Priority LeveL)。系統按照不一樣的優先級調度進程的運行,0-15級是普通優先級,進程的優先級能夠動態變化,高優先級進程優先運行,只有高優先級進程不運行時,才調度低優先級進程運行,優先級相同的進程按照時間片輪流運行。16-31級是實時優先級,實時優先級與普通優先級的最大區別在於相同優先級進程的運行不按照時間片輪轉,而是先運行的進程就先控制CPU,若是它不主動放棄控制,同級或低優先級的進程就沒法運行。這就是進(線)程的推動問題。
e)死鎖:
死鎖是指在倆個或多個併發的進程中,若是每一個進程持有某種資源而又等待別的進程釋放它們現保持着的資源,不然就不能向前推動。此時,每一個進程都佔用了必定的資源可是又不能向前推動,則稱這一組進程產生了死鎖。
簡單的說:就是倆個或多個進程無止境的等候,永遠不會成立的條件的一種系統狀態。、
產生死鎖的根本緣由是系統可以提供的資源個數要求比該資源的進程數少,具體緣由:系統資源不足,進程推動順序非法。
死鎖的四個必要條件:
互斥條件:一個資源每次只能被一個進程使用。
請求和保持條件:一個進程因請求而阻塞時,對以得到的資源保持不放
不可剝奪條件:進程以得到的資源,在沒有使用完以前,不能強行剝奪。
循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源的關係
死鎖的處理方式?
檢測死鎖並恢復。
仔細對資源進行動態分配,以免死鎖。
經過破壞死鎖產生的四個必要條件之一,來防止死鎖的產生。
鴕鳥算法忽略該問題。(鴕鳥算法,一種計算機操做系統算法,用於當死鎖真正發生且影響系統正常運行時,手動干預—從新啓動。)
---------------------
版權聲明:本文爲CSDN博主「zhaoxaun666」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/zhaoxaun666/article/details/81076790
F)線程的同步方法
即有synchronized關鍵字修飾的方法。 因爲java的每一個對象都有一個內置鎖,當用此關鍵字修飾方法時, 內置鎖會保護整個方法。在調用該方法前,須要得到內置鎖,不然就處於阻塞狀態。
注: synchronized關鍵字也能夠修飾靜態方法,此時若是調用該靜態方法,將會鎖住整個類。
即有synchronized關鍵字修飾的語句塊。 被該關鍵字修飾的語句塊會自動被加上內置鎖,從而實現同步
代碼如:
synchronized(object){ }
wait():使一個線程處於等待狀態,而且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM肯定喚醒哪一個線程,並且不是按優先級。
notifyAll():喚醒全部處入等待狀態的線程,注意並非給全部喚醒線程一個對象的鎖,而是讓它們競爭。
在JavaSE5.0中新增了一個java.util.concurrent包來支持同步。
ReentrantLock類是可重入、互斥、實現了Lock接口的鎖,它與使用synchronized方法和快具備相同的基本行爲和語義,而且擴展了其能力。
ReenreantLock類的經常使用方法有:
ReentrantLock() : 建立一個ReentrantLock實例 lock() : 得到鎖 unlock() : 釋放鎖
注:ReentrantLock()還有一個能夠建立公平鎖的構造方法,但因爲能大幅度下降程序運行效率,不推薦使用
若是使用ThreadLocal管理變量,則每個使用該變量的線程都得到該變量的副本,副本之間相互獨立,這樣每個線程均可以隨意修改本身的變量副本,而不會對其餘線程產生影響。
ThreadLocal 類的經常使用方法
ThreadLocal() : 建立一個線程本地變量 get() : 返回此線程局部變量的當前線程副本中的值 initialValue() : 返回此線程局部變量的當前線程的"初始值" set(T value) : 將此線程局部變量的當前線程副本中的值設置爲value
12、類加載的過程
Java虛擬機中類加載的全過程,即 加載
、驗證
、準備
、解析
和初始化
一個Java文件從編碼完成到最終執行,通常主要包括兩個過程
編譯
運行
編譯,即把咱們寫好的java文件,經過javac命令編譯成字節碼,也就是咱們常說的.class文件。
運行,則是把編譯聲稱的.class文件交給Java虛擬機(JVM)執行。
而咱們所說的類加載過程便是指JVM虛擬機把.class文件中類信息加載進內存,並進行解析生成對應的class對象的過程。
舉個通俗點的例子來講,JVM在執行某段代碼時,遇到了class A, 然而此時內存中並無class A的相關信息,因而JVM就會到相應的class文件中去尋找class A的類信息,並加載進內存中,這就是咱們所說的類加載過程。
因而可知,JVM不是一開始就把全部的類都加載進內存中,而是隻有第一次遇到某個須要運行的類時纔會加載,且只加載一次。
類加載
類加載的過程主要分爲三個部分:
加載
連接
初始化
而連接又能夠細分爲三個小部分:
驗證
準備
解析
加載
簡單來講,加載指的是把class字節碼文件從各個來源經過類加載器裝載入內存中。
這裏有兩個重點:
字節碼來源。通常的加載來源包括從本地路徑下編譯生成的.class文件,從jar包中的.class文件,從遠程網絡,以及動態代理實時編譯
類加載器。通常包括啓動類加載器,擴展類加載器,應用類加載器,以及用戶的自定義類加載器。
注:爲何會有自定義類加載器?
一方面是因爲java代碼很容易被反編譯,若是須要對本身的代碼加密的話,能夠對編譯後的代碼進行加密,而後再經過實現本身的自定義類加載器進行解密,最後再加載。
另外一方面也有可能從非標準的來源加載代碼,好比從網絡來源,那就須要本身實現一個類加載器,從指定源進行加載。
驗證
主要是爲了保證加載進來的字節流符合虛擬機規範,不會形成安全錯誤。
包括對於文件格式的驗證,好比常量中是否有不被支持的常量?文件中是否有不規範的或者附加的其餘信息?
對於元數據的驗證,好比該類是否繼承了被final修飾的類?類中的字段,方法是否與父類衝突?是否出現了不合理的重載?
對於字節碼的驗證,保證程序語義的合理性,好比要保證類型轉換的合理性。
對於符號引用的驗證,好比校驗符號引用中經過全限定名是否可以找到對應的類?校驗符號引用中的訪問性(private,public等)是否可被當前類訪問?
準備
主要是爲類變量(注意,不是實例變量)分配內存,而且賦予初值。
特別須要注意,初值,不是代碼中具體寫的初始化的值,而是Java虛擬機根據不一樣變量類型的默認初始值。
好比8種基本類型的初值,默認爲0;引用類型的初值則爲null;常量的初值即爲代碼中設置的值,final static tmp = 456, 那麼該階段tmp的初值就是456
解析
將常量池內的符號引用替換爲直接引用的過程。
兩個重點:
符號引用。即一個字符串,可是這個字符串給出了一些可以惟一性識別一個方法,一個變量,一個類的相關信息。
直接引用。能夠理解爲一個內存地址,或者一個偏移量。好比類方法,類變量的直接引用是指向方法區的指針;而實例方法,實例變量的直接引用則是從實例的頭指針開始算起到這個實例變量位置的偏移量
舉個例子來講,如今調用方法hello(),這個方法的地址是1234567,那麼hello就是符號引用,1234567就是直接引用。
在解析階段,虛擬機會把全部的類名,方法名,字段名這些符號引用替換爲具體的內存地址或偏移量,也就是直接引用。
初始化
這個階段主要是對類變量初始化,是執行類構造器的過程。
換句話說,只對static修飾的變量或語句進行初始化。
若是初始化一個類的時候,其父類還沒有初始化,則優先初始化其父類。
若是同時包含多個靜態變量和靜態代碼塊,則按照自上而下的順序依次執行。
總結
類加載過程只是一個類生命週期的一部分,在其前,有編譯的過程,只有對源代碼編譯以後,才能得到可以被虛擬機加載的字節碼文件;在其後還有具體的類使用過程,當使用完成以後,還會在方法區垃圾回收的過程當中進行卸載。若是想要了解Java類整個生命週期的話,能夠自行上網查閱相關資料,這裏再也不多作贅述。
---------------------
版權聲明:本文爲CSDN博主「公衆號-IT程序猿進化史」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/ln152315/article/details/79223441
13、對象的建立
使用new關鍵字
經過new關鍵字直接在堆內存上建立對象,這樣很方便的調用對象的有參和無參的構造函數.
Student stu1 = new Student("lihua");
1
Class反射調用
使用Java中反射特性,來進行對象的建立。使用Class類的newInstance方法能夠調用無參的構造器來建立對象,若是是有參構造器,則須要使用Class的forName方法和Constructor來進行對象的建立。
1 Class stuClass = Class.forName("Student"); 2 Constructor constructor = stuClass.getConstructor(String.class); 3 Student stu2 = (Student) constructor.newInstance("李四"); 4 123
使用Clone方法
使用Clone的方法:不管什麼時候咱們調用一個對象的clone方法,JVM就會建立一個新的對象,將前面的對象的內容所有拷貝進去,用clone方法建立對象並不會調用任何構造函數。要使用clone方法,咱們必須先實現Cloneable接口並實現其定義的clone方法。
1 try 2 { 3 Student stu3 = (Student) stu1.clone(); 4 System.out.println(stu3); 5 } 6 catch (CloneNotSupportedException e) 7 { 8 e.printStackTrace(); 9 } 10 123456789 11 12 13
使用序列化
一個對象實現了Serializable接口,就能夠把對象寫入到文件中,並經過讀取文件來建立對象。
1 String path = Test.class.getClassLoader().getResource("").getPath(); 2 String objectFilePath = path + "out.txt"; 3 4 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFilePath)); 5 objectOutputStream.writeObject(stu2); 6 7 ObjectInput objectInput = new ObjectInputStream(new FileInputStream(objectFilePath)); 8 Student stu4 = (Student) objectInput.readObject(); 9 12345678 10
示例代碼
Student對象,實現Cloneable和Serializable接口
1 import java.io.Serializable; 2 3 /** 4 * Created by wzj on 2017/11/3. 5 */ 6 public class Student implements Cloneable,Serializable 7 { 8 private String name; 9 10 public Student(String name) 11 { 12 this.name = name; 13 } 14 15 /** 16 * @return a clone of this instance. 17 * @throws CloneNotSupportedException if the object's class does not 18 * support the {@code Cloneable} interface. Subclasses 19 * that override the {@code clone} method can also 20 * throw this exception to indicate that an instance cannot 21 * be cloned. 22 * @see Cloneable 23 */ 24 @Override 25 protected Object clone() throws CloneNotSupportedException 26 { 27 return super.clone(); 28 } 29 } 30 1234567891011121314151617181920212223242526272829 31 32 測試類 33 34 import java.io.*; 35 import java.lang.reflect.Constructor; 36 import java.lang.reflect.InvocationTargetException; 37 38 /** 39 * Created by wzj on 2017/11/2. 40 */ 41 public class Test 42 { 43 public static void main(String[] args) throws Exception 44 { 45 //一、第一種方式是經過new 46 Student stu1 = new Student("lihua"); 47 System.out.println(stu1); 48 49 //二、經過java反射,靜態方式 50 Class stuClass = Class.forName("Student"); 51 Constructor constructor = stuClass.getConstructor(String.class); 52 Student stu2 = (Student) constructor.newInstance("李四"); 53 54 System.out.println(stu2); 55 56 //三、經過clone實現,必須實現Cloneable接口 57 try 58 { 59 Student stu3 = (Student) stu1.clone(); 60 System.out.println(stu3); 61 } 62 catch (CloneNotSupportedException e) 63 { 64 e.printStackTrace(); 65 } 66 67 //四、經過對象流,必須實現Serializable 68 String path = Test.class.getClassLoader().getResource("").getPath(); 69 String objectFilePath = path + "out.txt"; 70 71 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFilePath)); 72 objectOutputStream.writeObject(stu2); 73 74 ObjectInput objectInput = new ObjectInputStream(new FileInputStream(objectFilePath)); 75 Student stu4 = (Student) objectInput.readObject(); 76 System.out.println(stu4); 77 } 78 79 80 } 81
---------------------
版權聲明:本文爲CSDN博主「dmfrm」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/u010889616/article/details/78946580
14.Java集合框架的基礎接口有哪些?
總共有兩大接口:Collection 和Map ,一個元素集合,一個是鍵值對集合; 其中List和Set接口繼承了Collection接口,一個是有序元素集合,一個是無序元素集合; 而ArrayList和 LinkedList 實現了List接口,HashSet實現了Set接口,這幾個都比較經常使用; HashMap 和HashTable實現了Map接口,而且HashTable是線程安全的,可是HashMap性能更好
15.HashMap和Hashtable有什麼區別?
HashMap是基於哈希表實現的,每個元素是一個key-value對,其內部經過單鏈表解決衝突問題,容量不足(超過了閥值)時,一樣會自動增加。
HashMap是非線程安全的,只是用於單線程環境下,多線程環境下能夠採用concurrent併發包下的concurrentHashMap。
HashMap 實現了Serializable接口,所以它支持序列化,實現了Cloneable接口,能被克隆。
HashMap存數據的過程是:
HashMap內部維護了一個存儲數據的Entry數組,HashMap採用鏈表解決衝突,每個Entry本質上是一個單向鏈表。當準備添加一個key-value對時,首先經過hash(key)方法計算hash值,而後經過indexFor(hash,length)求該key-value對的存儲位置,計算方法是先用hash&0x7FFFFFFF後,再對length取模,這就保證每個key-value對都能存入HashMap中,當計算出的位置相同時,因爲存入位置是一個鏈表,則把這個key-value對插入鏈表頭。
HashMap中key和value都容許爲null。key爲null的鍵值對永遠都放在以table[0]爲頭結點的鏈表中。
瞭解了數據的存儲,那麼數據的讀取也就很容易就明白了。
HashMap的存儲結構,以下圖所示:
圖中,紫色部分即表明哈希表,也稱爲哈希數組,數組的每一個元素都是一個單鏈表的頭節點,鏈表是用來解決衝突的,若是不一樣的key映射到了數組的同一位置處,就將其放入單鏈表中。
HashMap內存儲數據的Entry數組默認是16,若是沒有對Entry擴容機制的話,當存儲的數據一多,Entry內部的鏈表會很長,這就失去了HashMap的存儲意義了。因此HasnMap內部有本身的擴容機制。HashMap內部有:
變量size,它記錄HashMap的底層數組中已用槽的數量;
變量threshold,它是HashMap的閾值,用於判斷是否須要調整HashMap的容量(threshold = 容量*加載因子)
變量DEFAULT_LOAD_FACTOR = 0.75f,默認加載因子爲0.75
HashMap擴容的條件是:當size大於threshold時,對HashMap進行擴容
擴容是是新建了一個HashMap的底層數組,然後調用transfer方法,將就HashMap的所有元素添加到新的HashMap中(要從新計算元素在新的數組中的索引位置)。 很明顯,擴容是一個至關耗時的操做,由於它須要從新計算這些元素在新的數組中的位置並進行復制處理。所以,咱們在用HashMap的時,最好能提早預估下HashMap中元素的個數,這樣有助於提升HashMap的性能。
HashMap共有四個構造方法。構造方法中提到了兩個很重要的參數:初始容量和加載因子。這兩個參數是影響HashMap性能的重要參數,其中容量表示哈希表中槽的數量(即哈希數組的長度),初始容量是建立哈希表時的容量(從構造函數中能夠看出,若是不指明,則默認爲16),加載因子是哈希表在其容量自動增長以前能夠達到多滿的一種尺度,當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 resize 操做(即擴容)。
下面說下加載因子,若是加載因子越大,對空間的利用更充分,可是查找效率會下降(鏈表長度會愈來愈長);若是加載因子過小,那麼表中的數據將過於稀疏(不少空間還沒用,就開始擴容了),對空間形成嚴重浪費。若是咱們在構造方法中不指定,則系統默認加載因子爲0.75,這是一個比較理想的值,通常狀況下咱們是無需修改的。
另外,不管咱們指定的容量爲多少,構造方法都會將實際容量設爲不小於指定容量的2的次方的一個數,且最大值不能超過2的30次方
對HashMap想進一步深刻了解的朋友推薦看一下HashMap源碼剖析:http://blog.csdn.net/ns_code/article/details/36034955
Hashtable一樣是基於哈希表實現的,一樣每一個元素是一個key-value對,其內部也是經過單鏈表解決衝突問題,容量不足(超過了閥值)時,一樣會自動增加。
Hashtable也是JDK1.0引入的類,是線程安全的,能用於多線程環境中。
Hashtable一樣實現了Serializable接口,它支持序列化,實現了Cloneable接口,能被克隆。
Hashtable和HashMap比較類似,感興趣的朋友能夠看「Hashtable源碼剖析」這篇博客:http://blog.csdn.net/ns_code/article/details/36191279
下面主要介紹一下HashTable和HashMap區別
Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但兩者都實現了Map接口。
javadoc中關於hashmap的一段描述以下:此實現不是同步的。若是多個線程同時訪問一個哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須保持外部同步。
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省狀況下是非Synchronize的。在多線程併發的環境下,能夠直接使用Hashtable,不須要本身爲它的方法實現同步,但使用HashMap時就必需要本身增長同步處理。(結構上的修改是指添加或刪除一個或多個映射關係的任何操做;僅改變與實例已經包含的鍵關聯的值不是結構上的修改。)這通常經過對天然封裝該映射的對象進行同步操做來完成。若是不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來「包裝」該映射。最好在建立時完成這一操做,以防止對映射進行意外的非同步訪問,以下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
Hashtable 線程安全很好理解,由於它每一個方法中都加入了Synchronize。這裏咱們分析一下HashMap爲何是線程不安全的:
HashMap底層是一個Entry數組,當發生hash衝突的時候,hashmap是採用鏈表的方式來解決的,在對應的數組位置存放鏈表的頭結點。對鏈表而言,新加入的節點會從頭結點加入。
咱們來分析一下多線程訪問:
(1)在hashmap作put操做的時候會調用下面方法:
1 [java] view plaincopy 2 1.// 新增Entry。將「key-value」插入指定位置,bucketIndex是位置索引。 3 2.void addEntry(int hash, K key, V value, int bucketIndex) { 4 3. // 保存「bucketIndex」位置的值到「e」中 5 4. Entry<K,V> e = table[bucketIndex]; 6 5. // 設置「bucketIndex」位置的元素爲「新Entry」, 7 6. // 設置「e」爲「新Entry的下一個節點」 8 7. table[bucketIndex] = new Entry<K,V>(hash, key, value, e); 9 8. // 若HashMap的實際大小 不小於 「閾值」,則調整HashMap的大小 10 9.if (size++ >= threshold) 11 10. resize(2 * table.length); 12 11. } 13
在hashmap作put操做的時候會調用到以上的方法。如今假如A線程和B線程同時對同一個數組位置調用addEntry,兩個線程會同時獲得如今的頭結點,而後A寫入新的頭結點以後,B也寫入新的頭結點,那B的寫入操做就會覆蓋A的寫入操做形成A的寫入操做丟失
( 2)刪除鍵值對的代碼
1 [java] view plaincopy 2 1.<span style="font-size: 18px;"> </span>// 刪除「鍵爲key」的元素 3 2.final Entry<K,V> removeEntryForKey(Object key) { 4 3. // 獲取哈希值。若key爲null,則哈希值爲0;不然調用hash()進行計算 5 4.int hash = (key == null) ? 0 : hash(key.hashCode()); 6 5. int i = indexFor(hash, table.length); 7 6. Entry<K,V> prev = table[i]; 8 7. Entry<K,V> e = prev; 9 8. // 刪除鏈表中「鍵爲key」的元素 10 9. // 本質是「刪除單向鏈表中的節點」 11 10.while (e != null) { 12 11. Entry<K,V> next = e.next; 13 12. Object k; 14 13. if (e.hash == hash && 15 14. ((k = e.key) == key || (key != null && key.equals(k)))) { 16 15. modCount++; 17 16. size--; 18 17. if (prev == e) 19 18. table[i] = next; 20 19. else 21 20. prev.next = next; 22 21. e.recordRemoval(this); 23 22. return e; 24 23. } 25 24. prev = e; 26 25. e = next; 27 26. } 28 27. return e; 29 28. } 30
當多個線程同時操做同一個數組位置的時候,也都會先取得如今狀態下該位置存儲的頭結點,而後各自去進行計算操做,以後再把結果寫會到該數組位置去,其實寫回的時候可能其餘的線程已經就把這個位置給修改過了,就會覆蓋其餘線程的修改
(3)addEntry中當加入新的鍵值對後鍵值對總數量超過門限值的時候會調用一個resize操做,代碼以下:
[java] view plaincopy 1.// 從新調整HashMap的大小,newCapacity是調整後的容量 2.void resize(int newCapacity) { 3. Entry[] oldTable = table; 4. int oldCapacity = oldTable.length; 5. //若是就容量已經達到了最大值,則不能再擴容,直接返回 6.if (oldCapacity == MAXIMUM_CAPACITY) { 7. threshold = Integer.MAX_VALUE; 8. return; 9. } 10. // 新建一個HashMap,將「舊HashMap」的所有元素添加到「新HashMap」中, 11. // 而後,將「新HashMap」賦值給「舊HashMap」。 12. Entry[] newTable = new Entry[newCapacity]; 13. transfer(newTable); 14. table = newTable; 15. threshold = (int)(newCapacity * loadFactor); 16. }
這個操做會新生成一個新的容量的數組,而後對原數組的全部鍵值對從新進行計算和寫入新的數組,以後指向新生成的數組。
當多個線程同時檢測到總數量超過門限值的時候就會同時調用resize操做,各自生成新的數組並rehash後賦給該map底層的數組table,結果最終只有最後一個線程生成的新數組被賦給table變量,其餘線程的均會丟失。並且當某些線程已經完成賦值而其餘線程剛開始的時候,就會用已經被賦值的table做爲原始數組,這樣也會有問題。
HashMap把Hashtable的contains方法去掉了,改爲containsValue和containsKey,由於contains方法容易讓人引發誤解。
Hashtable則保留了contains,containsValue和containsKey三個方法,其中contains和containsValue功能相同。
咱們看一下Hashtable的ContainsKey方法和ContainsValue的源碼:
[java] view plaincopy 1.public boolean containsValue(Object value) { 2. return contains(value); 3. } [java] view plaincopy 1.// 判斷Hashtable是否包含「值(value)」 2.public synchronized boolean contains(Object value) { 3. //注意,Hashtable中的value不能是null, 4. // 如果null的話,拋出異常! 5.if (value == null) { 6. throw new NullPointerException(); 7. } 8. // 從後向前遍歷table數組中的元素(Entry) 9. // 對於每一個Entry(單向鏈表),逐個遍歷,判斷節點的值是否等於value 10. Entry tab[] = table; 11. for (int i = tab.length ; i-- > 0 ;) { 12. for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) { 13. if (e.value.equals(value)) { 14. return true; 15. } 16. } 17. } 18. return false; 19. }
[java] view plaincopy 1.// 判斷Hashtable是否包含key 2. public synchronized boolean containsKey(Object key) { 3. Entry tab[] = table; 4./計算hash值,直接用key的hashCode代替 5.int hash = key.hashCode(); 6. // 計算在數組中的索引值 7.int index = (hash & 0x7FFFFFFF) % tab.length; 8. // 找到「key對應的Entry(鏈表)」,而後在鏈表中找出「哈希值」和「鍵值」與key都相等的元素 9.for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { 10. if ((e.hash == hash) && e.key.equals(key)) { 11. return true; 12. } 13. } 14. return false; 15. }
下面咱們看一下HashMap的ContainsKey方法和ContainsValue的源碼:
[java] view plaincopy 1.// HashMap是否包含key 2. public boolean containsKey(Object key) { 3. return getEntry(key) != null; 4. }
[java] view plaincopy
// 返回「鍵爲key」的鍵值對 2.final Entry<K,V> getEntry(Object key) { 3. // 獲取哈希值 4. // HashMap將「key爲null」的元素存儲在table[0]位置,「key不爲null」的則調用hash()計算哈希值 5.int hash = (key == null) ? 0 : hash(key.hashCode()); 6. // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素 7.for (Entry<K,V> e = table[indexFor(hash, table.length)]; 8. e != null; 9. e = e.next) { 10. Object k; 11. if (e.hash == hash && 12. ((k = e.key) == key || (key != null && key.equals(k)))) 13. return e; 14. } 15. return null; 16. }
[java] view plaincopy
// 是否包含「值爲value」的元素 2.public boolean containsValue(Object value) { 3. // 若「value爲null」,則調用containsNullValue()查找 4.if (value == null) 5. return containsNullValue(); 6. // 若「value不爲null」,則查找HashMap中是否有值爲value的節點。 7. Entry[] tab = table; 8. for (int i = 0; i < tab.length ; i++) 9. for (Entry e = tab[i] ; e != null ; e = e.next) 10. if (value.equals(e.value)) 11. return true; 12. return false; 13. }
經過上面源碼的比較,咱們能夠獲得第四個不一樣的地方
其中key和value都是對象,而且不能包含重複key,但能夠包含重複的value。
經過上面的ContainsKey方法和ContainsValue的源碼咱們能夠很明顯的看出:
Hashtable中,key和value都不容許出現null值。可是若是在Hashtable中有相似put(null,null)的操做,編譯一樣能夠經過,由於key和value都是Object類型,但運行時會拋出NullPointerException異常,這是JDK的規範規定的。
HashMap中,null能夠做爲鍵,這樣的鍵只有一個;能夠有一個或多個鍵所對應的值爲null。當get()方法返回null值時,多是 HashMap中沒有該鍵,也可能使該鍵所對應的值爲null。所以,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。
Hashtable、HashMap都使用了 Iterator。而因爲歷史緣由,Hashtable還使用了Enumeration的方式 。
哈希值的使用不一樣,HashTable直接使用對象的hashCode。而HashMap從新計算hash值。
hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值。
Hashtable計算hash值,直接用key的hashCode(),而HashMap從新計算了key的hash值,Hashtable在求hash值對應的位置索引時,用取模運算,而HashMap在求位置索引時,則用與運算,且這裏通常先用hash&0x7FFFFFFF後,再對length取模,&0x7FFFFFFF的目的是爲了將負的hash值轉化爲正值,由於hash值有可能爲負數,而&0x7FFFFFFF後,只有符號外改變,然後面的位都不變。
HashTable在不指定容量的狀況下的默認容量爲11,而HashMap爲16,Hashtable不要求底層數組的容量必定要爲2的整數次冪,而HashMap則要求必定爲2的整數次冪。
Hashtable擴容時,將容量變爲原來的2倍加1,而HashMap擴容時,將容量變爲原來的2倍。
Hashtable和HashMap它們兩個內部實現方式的數組的初始大小和擴容的方式。HashTable中hash數組默認大小是11,增長的方式是 old*2+1。
16.String能被繼承嗎?爲何?
不能被繼承,由於String類有final修飾符,而final修飾的類是不能被繼承的。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { // 省略... }
1.修飾類
當用final修飾一個類時,代表這個類不能被繼承。final類中的成員變量能夠根據須要設爲final,可是要注意final類中的全部成員方法都會被隱式地指定爲final方法。
2.修飾方法
使用final修飾方法的緣由有兩個。第一個緣由是把方法鎖定,以防任何繼承類修改它的含義;第二個緣由是效率。在早期的Java實現版本中,會將final方法轉爲內嵌調用。可是若是方法過於龐大,可能看不到內嵌調用帶來的任何性能提高。在最近的Java版本中,不須要使用final方法進行這些優化了。
所以,只有在想明確禁止該方法在子類中被覆蓋的狀況下才將方法設置爲final。
注:一個類中的private方法會隱式地被指定爲final方法。
3.修飾變量
對於被final修飾的變量,若是是基本數據類型的變量,則其數值一旦在初始化以後便不能更改;若是是引用類型的變量,則在對其初始化以後便不能再讓其指向另外一個對象。雖然不能再指向其餘對象,可是它指向的對象的內容是可變的。
不少時候會容易把static和final關鍵字混淆,static做用於成員變量用來表示只保存一份副本,而final的做用是用來保證變量不可變。看下面這個例子:
1 public class Demo1 { 2 public static void main(String[] args) { 3 MyClass myClass1 = new MyClass(); 4 MyClass myClass2 = new MyClass(); 5 System.out.println(myClass1.i); 6 System.out.println(myClass2.i); 7 System.out.println(myClass1.j); 8 System.out.println(myClass2.j); 9 10 } 11 } 12 13 class MyClass { 14 public final double i = Math.random(); 15 public static double j = Math.random(); 16 }
運行結果:
0.3222977275463088 0.2565532218939688 0.36856868882926397 0.36856868882926397
每次打印的兩個j值都是同樣的,而i的值倒是不一樣的。從這裏就能夠知道final和static變量的區別了。
17.ArrayList 和 LinkedList 有什麼區別。
ArrayList和LinkedList都實現了List接口,他們有如下的不一樣點:
ArrayList是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。
相對於ArrayList,LinkedList的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。
LinkedList比ArrayList更佔內存,由於LinkedList爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
也能夠參考ArrayList vs. LinkedList。
1) 由於 Array 是基於索引 (index) 的數據結構,它使用索引在數組中搜索和讀取數據是很快的。 Array 獲取數據的時間複雜度是 O(1), 可是要刪除數據倒是開銷很大的,由於這須要重排數組中的全部數據。
2) 相對於 ArrayList , LinkedList 插入是更快的。由於 LinkedList 不像 ArrayList 同樣,不須要改變數組的大小,也不須要在數組裝滿的時候要將全部的數據從新裝入一個新的數組,這是 ArrayList 最壞的一種狀況,時間複雜度是 O(n) ,而 LinkedList 中插入或刪除的時間複雜度僅爲 O(1) 。 ArrayList 在插入數據時還須要更新索引(除了插入數組的尾部)。
3) 相似於插入數據,刪除數據時, LinkedList 也優於 ArrayList 。
4) LinkedList 須要更多的內存,由於 ArrayList 的每一個索引的位置是實際的數據,而 LinkedList 中的每一個節點中存儲的是實際的數據和先後節點的位置 ( 一個 LinkedList 實例存儲了兩個值: Node<E> first 和 Node<E> last 分別表示鏈表的其實節點和尾節點,每一個 Node 實例存儲了三個值: E item,Node next,Node pre) 。
什麼場景下更適宜使用 LinkedList,而不用ArrayList
1) 你的應用不會隨機訪問數據 。由於若是你須要LinkedList中的第n個元素的時候,你須要從第一個元素順序數到第n個數據,而後讀取數據。
2) 你的應用更多的插入和刪除元素,更少的讀取數據 。由於插入和刪除元素不涉及重排數據,因此它要比ArrayList要快。
換句話說,ArrayList的實現用的是數組,LinkedList是基於鏈表,ArrayList適合查找,LinkedList適合增刪
以上就是關於 ArrayList和LinkedList的差異。你須要一個不一樣步的基於索引的數據訪問時,請儘可能使用ArrayList。ArrayList很快,也很容易使用。可是要記得要給定一個合適的初始大小,儘量的減小更改數組的大小。
18.抽象類和接口的區別,類能夠繼承多個類麼,接口能夠繼承多個接口麼,類能夠實現多個接口麼。
類不能繼承多個類
接口能夠繼承多個接口
類能夠實現多個接口
抽象類
1.抽象類中能夠構造方法
2.抽象類中能夠存在普通屬性,方法,靜態屬性和方法。
3.抽象類中能夠存在抽象方法。
4.若是一個類中有一個抽象方法,那麼當前類必定是抽象類;抽象類中不必定有抽象方法。
5.抽象類中的抽象方法,須要有子類實現,若是子類不實現,則子類也須要定義爲抽象的。
接口
1.在接口中只有方法的聲明,沒有方法體。 (Java8 接口能夠有實例方法)
2.在接口中只有常量,由於定義的變量,在編譯的時候都會默認加上 public static final (必須被初始化,不能改變)
3.在接口中的方法,永遠都被public來修飾(只能)。
4.接口中沒有構造方法,也不能實例化接口的對象。
5.接口能夠實現多繼承
6.接口中定義的方法都須要有實現類來實現,若是實現類不能實現接口中的全部方法則實現類定義爲抽象類。
PS.
一、靜態方法不能被重寫,不能被抽象
本地方法是本地代碼實現的方法,不能是抽象
synchronize與方法的實現細節有關,抽象方法不能被synchronize修飾
二、抽象類中能夠定義一些子類的公共方法,子類只須要增長新的功能,不須要重複寫已經存在的方法;
而接口中只是對方法的申明和常量的定義。
區別總結:
類能夠實現不少個接口,可是隻能繼承一個抽象類
接口中全部的方法隱含的都是抽象的。而抽象類則能夠同時包含抽象和非抽象的方法。(Java8 接口能夠有實例方法 須要關鍵字default)
Java接口中聲明的變量默認是public static final(必須賦初始值)。抽象類能夠包含非final的變量。
Java接口中的成員函數默認是public abstract的。抽象類的成員函數能夠是private,protected或者是public。
接口可繼承接口,不能繼承類(抽象類和普通類) 抽象類可繼承接口也可繼承具體類(繼承接口時可只實現部分方法)
非抽象類若是要實現一個接口,它必需要實現接口聲明的全部方法。類能夠不實現抽象類或接口聲明的全部方法,固然,在這種狀況下,類也必須得聲明成是抽象的。
接口是絕對抽象的,不能夠被實例化。抽象類也不能夠被實例化,可是,若是它包含main方法的話是能夠被調用的。
從設計角度來看抽象類和接口:
一、抽象類就是is a,是實例必需要有的,好比Door 必須有開和關。
而接口就是has a,能夠有也能夠沒有,好比Door能夠有報警器,可是報警器不是門必須有的,是可擴展的行爲。
二、抽象類強調的是同類事物的抽象,接口強調的是同類方法的抽象。
類能夠繼承多個類麼?
不能。
一個類不能直接繼承多個類,java是單繼承語言。
好比說這樣:class A extends B,C 不能這樣寫,由於java不支持多繼承。
可是能夠像下面這樣實現繼承多個類:class A extends B,class C extends A,這樣C就同時繼承了B和A兩個類了。
19.String類的經常使用方法有那些?
charAt:返回指定索引處的字符
indexOf():返回指定字符的索引
replace():字符串替換
trim():去除字符串兩端空白
split():分割字符串,返回一個分割後的字符串數組
getBytes():返回字符串的byte類型數組
length():返回字符串長度
toLowerCase():將字符串轉成小寫字母
toUpperCase():將字符串轉成大寫字符
substring():截取字符串
format():格式化字符串
equals():字符串比較
20.String str=」aaa」,與String str=new String(「aaa」)同樣嗎?
不同的。由於內存分配的方式不同。
第一種,建立的」aaa」是常量,jvm都將其分配在常量池中。
第二種建立的是一個對象,jvm將其值分配在堆內存中。
21.Final在java中的做用
Final能夠修飾類,修飾方法,修飾變量。
修飾的類叫最終類。該類不能被繼承。
修飾的方法不能被重寫。
修飾的變量叫常量,常量必須初始化,一旦初始化後,常量的值不能發生改變。
22.Static關鍵字有什麼做用?
Static能夠修飾內部類、方法、變量、代碼塊
Static修飾的類是靜態內部類
Static修飾的方法是靜態方法,表示該方法屬於當前類的,而不屬於某個對象的,靜態方法也不能被重寫,能夠直接使用類名來調用。在static方法中不能使用this或者super關鍵字。
Static修飾變量是靜態變量或者叫類變量,靜態變量被全部實例所共享,不會依賴於對象。靜態變量在內存中只有一份拷貝,在JVM加載類的時候,只爲靜態分配一次內存。
Static修飾的代碼塊叫靜態代碼塊,一般用來作程序優化的。靜態代碼塊中的代碼在整個類加載的時候只會執行一次。靜態代碼塊能夠有多個,若是有多個,按照前後順序依次執行。
23.內部類與靜態內部類的區別?
靜態內部類相對與外部類是獨立存在的,在靜態內部類中沒法直接訪問外部類中變量、方法。若是要訪問的話,必需要new一個外部類的對象,使用new出來的對象來訪問。可是能夠直接訪問靜態的變量、調用靜態的方法;
普通內部類做爲外部類一個成員而存在,在普通內部類中能夠直接訪問外部類屬性,調用外部類的方法。
若是外部類要訪問內部類的屬性或者調用內部類的方法,必需要建立一個內部類的對象,使用該對象訪問屬性或者調用方法。
若是其餘的類要訪問普通內部類的屬性或者調用普通內部類的方法,必需要在外部類中建立一個普通內部類的對象做爲一個屬性,外同類能夠經過該屬性調用普通內部類的方法或者訪問普通內部類的屬性
若是其餘的類要訪問靜態內部類的屬性或者調用靜態內部類的方法,直接建立一個靜態內部類對象便可。
24.Java經常使用包有那些?
Java.lang
Java.io
Java.sql
Java.util
Java.awt
Java.math
25.Object類經常使用方法有那些?
Equals
Hashcode
toString
wait
notify
clone
getClass
26.一個java類中包含那些內容?
屬性、方法、內部類、構造方法、代碼塊。
27.什麼是JVM?java虛擬機包括什麼?
JVM:java虛擬機,運用硬件或軟件手段實現的虛擬的計算機,Java虛擬機包括:寄存器,堆棧,處理器
28.Java的數據結構有那些?
線性表(ArrayList)
鏈表(LinkedList)
棧(Stack)
隊列(Queue)
圖(Map)
樹(Tree)
29.Char類型能不能轉成int類型?能不能轉化成string類型,能不能轉成double類型
Char在java中也是比較特殊的類型,它的int值從1開始,一共有2的16次方個數據;Char<int<long<float<double;Char類型能夠隱式轉成int,double類型,可是不能隱式轉換成string;若是char類型轉成byte,short類型的時候,須要強轉。
30.什麼是拆裝箱?
拆箱:把包裝類型轉成基本數據類型?????
裝箱:把基本數據類型轉成包裝類型?????
3一、"=="和 equals 方法究竟有什麼區別?
==操做符專門用來比較兩個變量的值是否相等,也就是用於比較變量所對應的內存中所存 儲的數值是否相同,要比較兩個基本類型的數據或兩個引用變量是否相等,只能用==操做符。
equals 方法是用於比較兩個獨立對象的內容是否相同
3二、靜態變量和實例變量的區別?
在語法定義上的區別:靜態變量前要加 static 關鍵字,而實例變量前則不加。
在程序運行時的區別:實例變量屬於某個對象的屬性,必須建立了實例對象,其中的實例變量纔會被分配空間,才能使用這個實例變量。
靜態變量不屬於某個實例對象,而是屬於類,因此也稱爲類變量,只要程序加載了類的字節碼,不用建立任何實例對象,靜態變量就會被分配空間,靜態變量就能夠被使用了。總之,實例變量必須建立對象後才能夠經過這個對象 來使用,靜態變量則能夠直接使用類名來引用。
3三、是否能夠從一個 static 方法內部發出對非 static方法的調用?
不能夠。由於非 static 方法是要與對象關聯在一塊兒的,必須建立一個對象後,才能夠在該對 象上進行方法調用,而 static 方法調用時不須要建立對象,能夠直接調用。也就是說,當一 個 static 方法被調用時,可能尚未建立任何實例對象,若是從一個 static 方法中發出對非 static 方法的調用,那個非 static 方法是關聯到哪一個對象上的呢?這個邏輯沒法成立,因此, 一個 static 方法內部發出對非 static 方法的調用。
3四、Integer 與 int 的區別
int是java提供的8種原始數據類型之一。Java爲每一個原始類型提供了封裝類,Integer是java 爲 int 提供的封裝類。int 的默認值爲0,而 Integer的默認值爲 null,即 Integer 能夠區分出 未賦值和值爲0的區別,int 則沒法表達出未賦值的狀況
35、請說出做用域 public,private,protected,以及不寫時的區別
做用域 當前類 同一包(package) 子孫類 其餘包(package
(
1九、構造器 Constructor 是否可被 override?
構造器 Constructor
不能被繼承,所以不能重寫 Override ,但能夠被重載 Overload 。)
36、接口是否可繼承接口?抽象類是否可實現(implements)接口?抽象類是否可 繼承具體類(concrete class)?抽象類中是否能夠有靜態的 main方法?
接口能夠繼承接口。抽象類能夠實現(implements)接口,抽象類能夠繼承具體類。抽象類中 能夠有靜態的 main 方法。
37、java 中實現多態的機制是什麼?
靠的是父類或接口定義的引用變量能夠指向子類或具體實現類的實例對象,而程序調用的方
法在運行期才動態綁定,就是引用變量所指向的具體實例對象的方法,也就是內存里正在運 行的那個對象的方法,而不是引用變量的類型中定義的方法。
38、abstract class和 interface 有什麼區別?
含有 abstract 修飾符的 class 即爲抽象類,abstract 類不能建立的實例對象。含有 abstract 方法的類必須定義爲abstract class,abstract class類中的方法沒必要是抽象的。
abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,因此,不能有抽象構造方法或抽象靜 態方法。若是的子類沒有實現抽象父類中的全部抽象方法,那麼子類也必須定義爲 abstract 類型。 接口(interface)能夠說成是抽象類的一種特例,接口中的全部方法都必須是抽象的。接口 中的方法定義默認爲 public abstract類型,接口中的成員變量類型默認爲 public static final。
下面比較一下二者的語法區別:
1.抽象類能夠有構造方法,接口中不能有構造方法。
2.抽象類中能夠有普通成員變量,接口中沒有普通成員變量
3.抽象類中能夠包含非抽象的普通方法,接口中的全部方法必須都是抽象的,不能有非抽象 的普通方法。
4. 抽象類中的抽象方法的訪問類型能夠是 public,protected 和(默認類型,雖然 eclipse 下不報錯,但應該也不行),但接口中的抽象方法只能是 public 類型的,而且默認即 爲 public abstract 類型。
5. 抽象類中能夠包含靜態方法,接口中不能包含靜態方法
6. 抽象類和接口中均可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型能夠任 意,但接口中定義的變量只能是 public static final 類型,而且默認即爲 public static final 類 型。
7. 一個類能夠實現多個接口,但只能繼承一個抽象類。
39、數組有沒有 length()這個方法? String有沒有 length()這個方法?
數組沒有 length()這個方法,有 length 的屬性。String 有有 length()這個方法。
40、請寫出你最多見到的 幾個 runtime exception。
NullPointerException、ArrayIndexOutOfBoundsException、 ClassCastException。
41、JAVA 語言如何進行異常處理,關鍵字:throws,throw,try,catch,finally分 別表明什麼意義?在 try塊中能夠拋出異常嗎?
throws 捕獲並向外拋出異常 throw拋出異常 try catch 是內部捕獲異常並作自定義處理 finally 是不管是否有異常都會被處理的語句,除非在 finally 前存在被執行的 System.exit(int i)時除外
42、多線程有幾種實現方法?用什麼關鍵字修飾同步方法?
有兩種實現方法,分別是繼承 Thread 類與實現 Runnable 接口
43、List 和 Map 區別?
一個是存儲單列數據的集合,另外一個是存儲鍵和值這樣的雙列數據的集合,List 中存儲的數 據是有順序,而且容許重複;Map 中存儲的數據是沒有順序的,其鍵是不能重複的,它的 值是能夠有重複的。
44、說出一些經常使用的類,包,接口,請各舉 5 個
經常使用的類:BufferedReader BufferedWriter FileReader FileWirter String Integer java.util.Date,System,Class,List,HashMap
經常使用的包:java.lang java.io java.util java.sql,javax.servlet,org.apache.strtuts.action,org.hibernate
經常使用的接口:Remote List Map Document NodeList,Servlet,HttpServletRequest,HttpServletResponse,Transaction(Hibernate)、 Session(Hibernate),HttpSession
45、java中有幾種類型的流?JDK爲每種類型的流提供了一些抽象類以供繼承, 請說出他們分別是哪些類?
字節流,字符流。字節流繼承於 InputStream OutputStream,字符流繼承於 InputStreamReaderOutputStreamWriter。在 java.io 包中還有許多其餘的流,主要是爲了提 高性能和使用方便。
46、描述一下 JVM加載 class文件的原理機制?
JVM中類的裝載是由 ClassLoader 和它的子類來實現的,Java ClassLoader是一個重要的 Java 運行時系統組件。它負責在運行時查找和裝入類文件的類。
47、說一說 Servlet的生命週期?
servlet 有良好的生存期的定義,包括加載和實例化、初始化、處理請求以及服務結束。 這個生存期由 javax.servlet.Servlet 接口的 init,service 和 destroy 方法表達。 Servlet 被服務器實例化後,容器運行其 init 方法,請求到達時運行其 service 方法,service 方法自動派遣運行與請求對應的 doXXX 方法(doGet,doPost)等,當服務器決定將實例 銷燬的時候調用其 destroy 方法。 web 容器加載 servlet,生命週期開始。經過調用 servlet 的 init()方法進行 servlet 的初始化。 經過調用 service()方法實現,根據請求的不一樣調用不一樣的 do***()方法。結束服務,web 容 器調用 servlet的 destroy()方法。
48、什麼狀況下調用 doGet()和 doPost()?
Jsp頁面中的FORM標籤裏的method屬性爲get時調用doGet(),爲 post時調用doPost()。
4九、forward 和 redirect的區別
forward 是服務器請求資源,服務器直接訪問目標地址的 URL,把那個 URL 的響應內容讀 取過來,而後把這些內容再發給瀏覽器,瀏覽器根本不知道服務器發送的內容是從哪兒來的, 因此它的地址欄中仍是原來的地址。
redirect 就是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器從新去請求那個地址,通常來 說瀏覽器會用剛纔請求的全部參數從新請求,因此 session,request參數均可以獲取。
50、頁面間對象傳遞的方法
request,session,application,cookie 等
51、MVC的各個部分都有那些技術來實現?如何實現?
MVC 是 Model-View-Controller的簡寫。Model 表明的是應用的業務邏輯(經過 JavaBean,EJB 組件實現),View 是應用的表示面(由 JSP 頁面產生),Controller是提供 應用的處理過程控制(通常是一個 Servlet),經過這種設計模型把應用邏輯,處理過程和顯 示邏輯分紅不一樣的組件實現。這些組件能夠進行交互和重用。
52、數據庫三範式是什麼?
第一範式(1NF):字段具備原子性,不可再分。
第二範式(2NF):要求實體的屬性徹底依賴於主關鍵字。
第三範式(3NF):屬性值不可傳遞
5三、Java 中的23種設計模式:
Factory(工廠模式), Builder(建造模式), Factory Method(工廠方法模式),
Prototype(原始模型模式),Singleton(單例模式), Facade(門面模式), Adapter(適配器模式), Bridge(橋樑模式), Composite(合成模式),
Decorator(裝飾模式), Flyweight(享元模式), Proxy(代理模式), Command(命令模式), Interpreter(解釋器模式), Visitor(訪問者模式), Iterator(迭代子模式), Mediator(調停者模式), Memento(備忘錄模式), Observer(觀察者模式), State(狀態模式), Strategy(策略模式), Template Method(模板方法模式), Chain Of Responsibleity(責任鏈模式)
5四、頁面之間傳遞參數的幾種方法
1:經過URL連接地址傳遞
2:經過post方式
3:經過session
4: 經過Application
5:經過Servlet.Transfer
5五、Java集合類框架的基本接口有哪些?
Java集合類提供了一套設計良好的支持對一組對象進行操做的接口和類。Java集合類裏面最基本的接口有:
Collection:表明一組對象,每個對象都是它的子元素。
Set:不包含重複元素的Collection。
List:有順序的collection,而且能夠包含重複元素。
Map:能夠把鍵(key)映射到值(value)的對象,鍵不能重複
5六、spring注入方法:
屬性注入(set方法) 構造注入(無參構造) P標籤注入
57、主要學習的框架是什麼?並說下spring有哪幾種注入方式。
一、學習框架有struts、hibernate、Mybatis、spring
補充:
1.什麼是Java虛擬機?爲何Java被稱做是「平臺無關的編程語言」?
Java虛擬機是一個能夠執行Java字節碼的虛擬機進程。Java源文件被編譯成能被Java虛擬機執行的字節碼文件。
Java被設計成容許應用程序能夠運行在任意的平臺,而不須要程序員爲每個平臺單獨重寫或者是從新編譯。Java虛擬機讓這個變爲可能,由於它知道底層硬件平臺的指令長度和其餘特性。
2.JDK、JRE、JVM關係是什麼?
JDK(Java Development Kit)即爲Java開發工具包,包含編寫Java程序所必須的編譯、運行等開發工具以及JRE。開發工具如:用於編譯java程序的javac命令、用於啓動JVM運行java程序的java命令、用於生成文檔的javadoc命令以及用於打包的jar命令等等。
JRE(Java Runtime Environment)即爲Java運行環境,提供了運行Java應用程序所必須的軟件環境,包含有Java虛擬機(JVM)和豐富的系統類庫。系統類庫即爲java提早封裝好的功能類,只需拿來直接使用便可,能夠大大的提升開發效率。
JVM(Java Virtual Machines)即爲Java虛擬機,提供了字節碼文件(.class)的運行環境支持。
簡單說,就是JDK包含JRE包含JVM。
3.Java支持的數據類型有哪些?什麼是自動拆裝箱?
基本數據類型:
整數值型:byte,short,int,long,
字符型:char
浮點類型:float,double
布爾型:boolean
整數默認int型,小數默認是double型。Float和long類型的必須加後綴。
首先知道String是引用類型不是基本類型,引用類型聲明的變量是指該變量在內存中實際存儲的是一個引用地址,實體在堆中。引用類型包括類、接口、數組等。String類仍是final修飾的。
而包裝類就屬於引用類型,自動裝箱和拆箱就是基本類型和引用類型之間的轉換,至於爲何要轉換,由於基本類型轉換爲引用類型後,就能夠new對象,從而調用包裝類中封裝好的方法進行基本類型之間的轉換或者toString(固然用類名直接調用也能夠,便於一眼看出該方法是靜態的),還有就是若是集合中想存放基本類型,泛型的限定類型只能是對應的包裝類型。
5.面向對象是什麼?
面向對象是一種思想,世間萬物均可以看作一個對象,這裏只討論面向對象編程(OOP),Java是一個支持併發、基於類和麪向對象的計算機編程語言,面向對象軟件開發的優勢:
代碼開發模塊化,更易維護和修改;
代碼複用性強;
加強代碼的可靠性和靈活性;
增長代碼的可讀性。
面向對象的四大基本特性:
抽象:提取現實世界中某事物的關鍵特性,爲該事物構建模型的過程。對同一事物在不一樣的需求下,須要提取的特性可能不同。獲得的抽象模型中通常包含:屬性(數據)和操做(行爲)。這個抽象模型咱們稱之爲類。對類進行實例化獲得對象。
封裝:封裝能夠使類具備獨立性和隔離性;保證類的高內聚。只暴露給類外部或者子類必須的屬性和操做。類封裝的實現依賴類的修飾符(public、protected和private等)
繼承:對現有類的一種複用機制。一個類若是繼承現有的類,則這個類將擁有被繼承類的全部非私有特性(屬性和操做)。這裏指的繼承包含:類的繼承和接口的實現。
多態:多態是在繼承的基礎上實現的。多態的三個要素:繼承、重寫和父類引用指向子類對象。父類引用指向不一樣的子類對象時,調用相同的方法,呈現出不一樣的行爲;就是類多態特性。多態能夠分紅編譯時多態和運行時多態。
8.什麼是值傳遞和引用傳遞?
值傳遞是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響原變量.
引用傳遞通常是對於對象型變量而言的,傳遞的是該對象地址的一個副本, 並非原對象自己 。
通常認爲,java內的傳遞都是值傳遞. java中實例對象的傳遞是引用傳遞 。
10.Java中的方法覆蓋(Overriding)和方法重載(Overloading)是什麼意思?
Java中的方法重載發生在同一個類裏面兩個或者是多個方法的方法名相同可是參數不一樣的狀況。與此相對,方法覆蓋是說子類從新定義了父類的方法。方法覆蓋必須有相同的方法名,參數列表和返回類型。覆蓋者可能不會限制它所覆蓋的方法的訪問。
11.Java中,什麼是構造方法?什麼是構造方法重載?什麼是複製構造方法?
當新對象被建立的時候,構造方法會被調用。每個類都有構造方法。在程序員沒有給類提供構造方法的狀況下,Java編譯器會爲這個類建立一個默認的構造方法。
Java中構造方法重載和方法重載很類似。能夠爲一個類建立多個構造方法。每個構造方法必須有它本身惟一的參數列表。
12.Java支持多繼承麼?
Java中類不支持多繼承,只支持單繼承(即一個類只有一個父類)。 可是java中的接口支持多繼承,,即一個子接口能夠有多個父接口。(接口的做用是用來擴展對象的功能,一個子接口繼承多個父接口,說明子接口擴展了多個功能,當類實現接口時,類就擴展了相應的功能)。
17.String和StringBuilder、StringBuffer的區別?
Java平臺提供了兩種類型的字符串:String和StringBuffer/StringBuilder,它們能夠儲存和操做字符串。其中String是隻讀字符串,也就意味着String引用的字符串內容是不能被改變的。而StringBuffer/StringBuilder類表示的字符串對象能夠直接進行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法徹底相同,區別在於它是在單線程環境下使用的,由於它的全部方面都沒有被synchronized修飾,所以它的效率也比StringBuffer要高。
十、內部類與外部類的調用
a) 內部類能夠直接調用外部類包括private的成員變量,使用外部類引用的this.關鍵字調用便可
b) 而外部類調用內部類須要創建內部類對象
十一、多線程
a)一個進程是一個獨立的運行環境,能夠看作是一個程序,而線程能夠看作是進程的一個任務,好比QQ是一個進程,而一個QQ窗口是一個線程。
b)在多線程程序中,多線程併發能夠提升程序的效率,cpu不會由於某個線程等待資源而進入空閒狀態,它會把資源讓給其餘的線程。
c)用戶線程就是咱們開發程序是建立的線程,而守護線程爲系統線程,如JVM虛擬中的GC
d)線程的優先級別:每個線程都有優先級別,有限級別高的能夠先獲取CPU資源使該線程從就緒狀態轉爲運行狀態。也能夠自定義線程的有限級別
e)死鎖:至少兩個以上線程爭取兩個以上cpu資源,避免死鎖就避免使用嵌套鎖,只須要在他們須要同步的地方加鎖和避免無限等待
2三、類加載的過程
a) 遇到一個新的類時,首先會到方法區去找class文件,若是沒有找到就會去硬盤中找class文件,找到後會返回,將class文件加載到方法區中,在類加載的時候,靜態成員變量會被分配到方法區的靜態區域,非靜態成員變量分配到非靜態區域,而後開始給靜態成員變量初始化,賦默認值,賦完默認值後,會根據靜態成員變量書寫的位置賦顯示值,而後執行靜態代碼。當全部的靜態代碼執行完,類加載纔算完成。
2四、對象的建立
a) 遇到一個新類時,會進行類的加載,定位到class文件
b) 對全部靜態成員變量初始化,靜態代碼塊也會執行,並且只在類加載的時候執行一次
c) New 對象時,jvm會在堆中分配一個足夠大的存儲空間
d) 存儲空間清空,爲全部的變量賦默認值,全部的對象引用賦值爲null
e) 根據書寫的位置給字段一些初始化操做
f) 調用構造器方法(沒有繼承)
20.Java集合框架的基礎接口有哪些?
Collection爲集合層級的根接口。一個集合表明一組對象,這些對象即爲它的元素。Java平臺不提供這個接口任何直接的實現。## 標題 ##
Set是一個不能包含重複元素的集合。這個接口對數學集合抽象進行建模,被用來表明集合,就如一副牌。
List是一個有序集合,能夠包含重複元素。你能夠經過它的索引來訪問任何元素。List更像長度動態變換的數組。
Map是一個將key映射到value的對象.一個Map不能包含重複的key:每一個key最多隻能映射一個value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
30.HashMap和Hashtable有什麼區別?
一、HashMap是非線程安全的,HashTable是線程安全的。
二、HashMap的鍵和值都容許有null值存在,而HashTable則不行。
三、由於線程安全的問題,HashMap效率比HashTable的要高。
四、Hashtable是同步的,而HashMap不是。所以,HashMap更適合於單線程環境,而Hashtable適合於多線程環境。
通常如今不建議用HashTable, ①是HashTable是遺留類,內部實現不少沒優化和冗餘。②即便在多線程環境下,如今也有同步的ConcurrentHashMap替代,沒有必要由於是多線程而用HashTable。
2.String能被繼承嗎?爲何?
不能夠,由於String類有final修飾符,而final修飾的類是不能被繼承的,
3.ArrayList 和 LinkedList 有什麼區別。
ArrayList和LinkedList都實現了List接口,有如下的不一樣點:
>一、ArrayList是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。
>二、相對於ArrayList,LinkedList的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。
>三、LinkedList比ArrayList更佔內存,由於LinkedList爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
11.抽象類和接口的區別,類能夠繼承多個類麼,接口能夠繼承多個接口麼,類能夠實現多個接口麼。
一、抽象類和接口都不能直接實例化,若是要實例化,抽象類變量必須指向實現全部抽象方法的子類對象,接口變量必須指向實現全部接口方法的類對象。
二、抽象類要被子類繼承,接口要被類實現。
三、接口只能作方法申明,抽象類中能夠作方法申明,也能夠作方法實現
四、接口裏定義的變量只能是公共的靜態的常量,抽象類中的變量是普通變量。
五、抽象類裏的抽象方法必須所有被子類所實現,若是子類不能所有實現父類抽象方法,那麼該子類只能是抽象類。一樣,一個實現接口的時候,如不能所有實現接口方法,那麼該類也只能爲抽象類。
六、抽象方法只能申明,不能實現。abstract void abc();不能寫成abstract void abc(){}。
七、抽象類裏能夠沒有抽象方法
八、若是一個類裏有抽象方法,那麼這個類只能是抽象類
九、抽象方法要被實現,因此不能是靜態的,也不能是私有的。
十、接口可繼承接口,並可多繼承接口,但類只能單根繼承。
50.什麼是重寫?什麼是重載?
重載和重寫都是java多態的表現。
重載叫override,在同一個類中多態的表現。當一個類中出現了多個相同名稱的方法,但參數個數和參數類型不一樣,方法重載與返回值無關
重寫叫overwrite,是字符類中多態的表現。當子類出現與父類相同的方法,那麼這就是方法重寫。方法重寫時,子類的返回值必須與父類的一致。若是父類方法拋出一個異常,子類重寫的方法拋出的異常類型不能小於父類拋出的異常類型