Java是一門面向對象的高級編程語言,不只吸取了C++語言的各類優勢,好比繼承了C++語言面嚮對象的技術核心。還摒棄了C++裏難以理解的多繼承、指針等概念,,同時也增長了垃圾回收機制,釋放掉不被使用的內存空間,解決了管理內存空間的煩惱。java
所以Java語言具備功能強大和簡單易用兩個特徵。Java語言做爲靜態面向對象編程語言的表明,極好地實現了面向對象理論,容許程序員以優雅的思惟方式進行復雜的編程 。mysql
面試手冊PDF下載連接:https://shimo.im/docs/Wyj8QRpq39jkC3jD
面試手冊PDF下載連接:https://shimo.im/docs/Wyj8QRpq39jkC3jD
面試手冊PDF下載連接:https://shimo.im/docs/Wyj8QRpq39jkC3jD程序員
Java 語言是一種分佈式的面嚮對象語言,具備面向對象、平臺無關性、簡單性、解釋執行、多線程、安全性等不少特色,下面針對這些特色進行逐一介紹。web
1. 面向對象面試
Java 是一種面向對象的語言,它對對象中的類、對象、繼承、封裝、多態、接口、包等均有很好的支持。爲了簡單起見,Java 只支持類之間的單繼承,可是可使用接口來實現多繼承。使用 Java 語言開發程序,須要採用面向對象的思想設計程序和編寫代碼。算法
2. 平臺無關性sql
平臺無關性的具體表如今於,Java 是「一次編寫,處處運行(Write Once,Run any Where)」的語言,所以採用 Java 語言編寫的程序具備很好的可移植性,而保證這一點的正是 Java 的虛擬機機制。在引入虛擬機以後,Java 語言在不一樣的平臺上運行不須要從新編譯。編程
Java 語言使用 Java 虛擬機機制屏蔽了具體平臺的相關信息,使得 Java 語言編譯的程序只需生成虛擬機上的目標代碼,就能夠在多種平臺上不加修改地運行。數組
3. 簡單性緩存
Java 語言的語法與 C 語言和 C++ 語言很相近,使得不少程序員學起來很容易。對 Java 來講,它捨棄了不少 C++ 中難以理解的特性,如操做符的重載和多繼承等,並且 Java 語言不使用指針,加入了垃圾回收機制,解決了程序員須要管理內存的問題,使編程變得更加簡單。
4. 解釋執行
Java 程序在 Java 平臺運行時會被編譯成字節碼文件,而後能夠在有 Java 環境的操做系統上運行。在運行文件時,Java 的解釋器對這些字節碼進行解釋執行,執行過程當中須要加入的類在鏈接階段被載入到運行環境中。
5. 多線程
Java 語言是多線程的,這也是 Java 語言的一大特性,它必須由 Thread 類和它的子類來建立。Java 支持多個線程同時執行,並提供多線程之間的同步機制。任何一個線程都有本身的 run() 方法,要執行的方法就寫在 run() 方法體內。
6. 分佈式
Java 語言支持 Internet 應用的開發,在 Java 的基本應用編程接口中就有一個網絡應用編程接口,它提供了網絡應用編程的類庫,包括 URL、URLConnection、Socket 等。Java 的 RIM 機制也是開發分佈式應用的重要手段。
7. 健壯性
Java 的強類型機制、異常處理、垃圾回收機制等都是 Java 健壯性的重要保證。對指針的丟棄是 Java 的一大進步。另外,Java 的異常機制也是健壯性的一大致現。
8. 高性能
Java 的高性能主要是相對其餘高級腳本語言來講的,隨着 JIT(Just in Time)的發展,Java 的運行速度也愈來愈高。
9. 安全性
Java 一般被用在網絡環境中,爲此,Java 提供了一個安全機制以防止惡意代碼的攻擊。除了 Java 語言具備許多的安全特性之外,Java 還對經過網絡下載的類增長一個安全防範機制,分配不一樣的名字空間以防替代本地的同名類,幷包含安全管理機制。
Java 語言的衆多特性使其在衆多的編程語言中佔有較大的市場份額,Java 語言對對象的支持和強大的 API 使得編程工做變得更加容易和快捷,大大下降了程序的開發成本。Java 的「一次編寫,處處執行」正是它吸引衆多商家和編程人員的一大優點。
1. JDK
JDK(Java SE Development Kit),Java標準的開發包,提供了編譯、運行Java程序所須要的各類工具和資源,包括了Java編譯器、Java運行時環境、以及經常使用的Java類庫等。
2. JRE
JRE(Java Runtime Environment),Java運行時環境,用於解釋執行Java的字節碼文件。普通用戶只須要安裝JRE來運行Java程序便可,而做爲一名程序員必須安裝JDK,來編譯、調試程序。
3. JVM
JVM(Java Virtual Mechinal),Java虛擬機,是JRE的一部分。它是整個Java實現跨平臺的核心,負責解釋執行字節碼文件,是可運行Java字節碼文件的虛擬計算機。全部平臺上的JVM向編譯器提供相同的接口,而編譯器只須要面向虛擬機,生成虛擬機能識別的代碼,而後由虛擬機來解釋執行。
當使用Java編譯器編譯Java程序時,生成的是與平臺無關的字節碼,這些字節碼只面向JVM。也就是說JVM是運行Java字節碼的虛擬機。
不一樣平臺的JVM是不一樣的,可是他們都提供了相同的接口。JVM是Java程序跨平臺的關鍵部分,只要爲不一樣平臺實現了相同的虛擬機,編譯後的Java字節碼就能夠在該平臺上運行。
爲何要採用字節碼:
在 Java 中,JVM 能夠理解的代碼就叫作
字節碼
(即Java源代碼通過虛擬機編譯器編譯後擴展名爲.class
的文件),它不面向任何特定的處理器,只面向虛擬機。Java 語言經過字節碼的方式,在必定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特色。因此 Java 程序運行時比較高效,並且,因爲字節碼並不針對一種特定的機器,所以,Java 程序無須從新編譯即可在多種不一樣操做系統的計算機上運行。
什麼是跨平臺:
所謂跨平臺性,是指java語言編寫的程序,一次編譯後,能夠在多個系統平臺上運行。
實現原理:Java程序是經過java虛擬機在系統平臺上運行的,只要該系統能夠安裝相應的java虛擬機,該系統就能夠運行java程序。
Java 程序從源代碼到運行須要三步:
4. 總結
Oracle JDK版本將每三年發佈一次,而OpenJDK版本每三個月發佈一次;
OpenJDK 是一個參考模型而且是徹底開源的,而Oracle JDK是OpenJDK的一個實現,並非徹底開源的;
Oracle JDK 比 OpenJDK 更穩定。OpenJDK和Oracle JDK的代碼幾乎相同,但Oracle JDK有更多的類和一些錯誤修復。所以,若是您想開發企業/商業軟件,我建議您選擇Oracle JDK,由於它通過了完全的測試和穩定。某些狀況下,有些人提到在使用OpenJDK 可能會遇到了許多應用程序崩潰的問題,可是,只需切換到Oracle JDK就能夠解決問題;
在響應性和JVM性能方面,Oracle JDK與OpenJDK相比提供了更好的性能;
Oracle JDK不會爲即將發佈的版本提供長期支持,用戶每次都必須經過更新到最新版本得到支持來獲取最新版本;
Oracle JDK根據二進制代碼許可協議得到許可,而OpenJDK根據GPL v2許可得到許可。
Java中有 8 種基本數據類型,分別爲:
6 種數字類型 (四個整數形,兩個浮點型):byte、short、int、long、float、double
1 種字符類型:char
1 種布爾型:boolean。
byte:
short:
int:
long:
注意:Java 裏使用 long 類型的數據必定要在數值後面加上 L,不然將做爲整型解析
long 數據類型是 64 位、有符號的以二進制補碼錶示的整數;
最小值是 -9,223,372,036,854,775,808(-2^63);
最大值是 9,223,372,036,854,775,807(2^63 -1);
這種類型主要使用在須要比較大整數的系統上;
默認值是 0L;
例子: long a = 100000L,Long b = -200000L。
"L"理論上不分大小寫,可是若寫成"l"容易與數字"1"混淆,不容易分辯。因此最好大寫。
float:
double:
char:
boolean:
這八種基本類型都有對應的包裝類分別爲:Byte、Short、Integer、Long、Float、Double、Character、Boolean
類型名稱 | 字節、位數 | 最小值 | 最大值 | 默認值 | 例子 |
---|---|---|---|---|---|
byte字節 | 1字節,8位 | -128(-2^7) | 127(2^7-1) | 0 | byte a = 100,byte b = -50 |
short短整型 | 2字節,16位 | -32768(-2^15) | 32767(2^15 - 1) | 0 | short s = 1000,short r = -20000 |
int整形 | 4字節,32位 | -2,147,483,648(-2^31) | 2,147,483,647(2^31 - 1) | 0 | int a = 100000, int b = -200000 |
lang長整型 | 8字節,64位 | -9,223,372,036,854,775,808(-2^63) | 9,223,372,036,854,775,807(2^63 -1) | 0L | long a = 100000L,Long b = -200000L |
double雙精度 | 8字節,64位 | double類型一樣不能表示精確的值,如貨幣 | 0.0d | double d1 = 123.4 | |
float單精度 | 4字節,32位 | 在儲存大型浮點數組的時候可節省內存空間 | 不一樣統計精準的貨幣值 | 0.0f | float f1 = 234.5f |
char字符 | 2字節,16位 | \u0000(即爲0) | \uffff(即爲65,535) | 能夠儲存任何字符 | char letter = 'A'; |
boolean布爾 | 返回true和false兩個值 | 這種類型只做爲一種標誌來記錄 true/false 狀況; | 只有兩個取值:true 和 false; | false | boolean one = true |
引用數據類型分3種:類,接口,數組;
簡單來講,只要不是基本數據類型.都是引用數據類型。 那他們有什麼不一樣呢?
一、從概念方面來講
1,基本數據類型:變量名指向具體的數值
2,引用數據類型:變量名不是指向具體的數值,而是指向存數據的內存地址,.也及時hash值
二、從內存的構建方面來講(內存中,有堆內存和棧內存二者)
1,基本數據類型:被建立時,在棧內存中會被劃分出必定的內存,並將數值存儲在該內存中.
2,引用數據類型:被建立時,首先會在棧內存中分配一塊空間,而後在堆內存中也會分配一塊具體的空間用來存儲數據的具體信息,即hash值,而後由棧中引用指向堆中的對象地址.
舉個例子
//基本數據類型做爲方法參數被調用 public class Main{ public static void main(String[] args){ //基本數據類型 int i = 1; int j = 1; double d = 1.2; //引用數據類型 String str = "Hello"; String str1= "Hello"; } }
由上圖可知,基本數據類型中會存在兩個相同的1,而引用型類型就不會存在相同的數據。
假如"hello"的引用地址是xxxxx1,聲明str變量並其賦值"hello"實際上就是讓str變量引用了"hello"的內存地址,這個內存地址就存儲在堆內存中,是不會改變的,當再次聲明變量str1也是賦值爲"hello"時,此時就會在堆內存中查詢是否有"hello"這個地址,若是堆內存中已經存在這個地址了,就不會再次建立了,而是讓str1變量也指向xxxxx1這個地址,若是沒有的話,就會從新建立一個地址給str1變量。
1,基本數據類型:判斷數據是否相等,用和!=判斷。
2,引用數據類型:判斷數據是否相等,用equals()方法,和!=是比較數值的。而equals()方法是比較內存地址的。
補充:數據類型選擇的原則
若是要表示整數就使用int,表示小數就使用double;
若是要描述日期時間數字或者表示文件(或內存)大小用long;
若是要實現內容傳遞或者編碼轉換使用byte;
若是要實現邏輯的控制,可使用booleam;
若是要使用中文,使用char避免中文亂碼;
若是按照保存範圍:byte < int < long < double;
什麼是自動裝箱拆箱?
從下面的代碼中就能夠看到裝箱和拆箱的過程
//自動裝箱 Integer total = 99; //自定拆箱 int totalprim = total;
裝箱就是自動將基本數據類型轉換爲包裝器類型;拆箱就是自動將包裝器類型轉換爲基本數據類型。
在Java SE5以前,自動裝箱要這樣寫:Integer i =
new` `Integer(
10``);
對於Java的自動裝箱和拆箱,咱們看看源碼編譯後的class文件,其實裝箱調用包裝類的valueOf方法,拆箱調用的是Integer.Value方法,下面就是變編譯後的代碼:
常見面試一:
這段代碼輸出什麼?
public class Main { public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; Integer i3 = 200; Integer i4 = 200; System.out.println(i1==i2); System.out.println(i3==i4); } }
答案是:
true false
爲何會出現這樣的結果?輸出結果代表i1和i2指向的是同一個對象,而i3和i4指向的是不一樣的對象。此時只需一看源碼便知究竟,下面這段代碼是Integer的valueOf方法的具體實現:
public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
private static class IntegerCache { static final int high; static final Integer cache[]; static { final int low = -128; // high value may be configured by property int h = 127; if (integerCacheHighPropValue != null) { // Use Long.decode here to avoid invoking methods that // require Integer's autoboxing cache to be initialized int i = Long.decode(integerCacheHighPropValue).intValue(); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - -low); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }
從這2段代碼能夠看出,在經過valueOf方法建立Integer對象的時候,若是數值在[-128,127]之間,便返回指向IntegerCache.cache中已經存在的對象的引用;不然建立一個新的Integer對象。
上面的代碼中i1和i2的數值爲100,所以會直接從cache中取已經存在的對象,因此i1和i2指向的是同一個對象,而i3和i4則是分別指向不一樣的對象。
常見面試二:
public class Main { public static void main(String[] args) { Double i1 = 100.0; Double i2 = 100.0; Double i3 = 200.0; Double i4 = 200.0; System.out.println(i1==i2); System.out.println(i3==i4); } }
輸出結果爲:
false false
緣由很簡單,在某個範圍內的整型數值的個數是有限的,而浮點數卻不是。
讓基本數據類型也具備對象的特徵
基本類型 | 包裝器類型 |
---|---|
boolean | Boolean |
char | Character |
int | Integer |
byte | Byte |
short | Short |
long | Long |
float | Float |
double | Double |
爲了讓基本類型也具備對象的特徵,就出現了包裝類型(如咱們在使用集合類型Collection時就必定要使用包裝類型而非基本類型)由於容器都是裝object的,這是就須要這些基本類型的包裝器類了。
自動裝箱:new Integer(6);
,底層調用:Integer.valueOf(6)
自動拆箱: int i = new Integer(6);
,底層調用i.intValue();
方法實現。
Integer i = 6; Integer j = 6; System.out.println(i==j);
答案在下面這段代碼中找:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
兩者的區別:
+=
操做符會進行隱式自動類型轉換,此處a+=b隱式的將加操做的結果類型強制轉換爲持有結果的類型,而a=a+b則不會自動進行類型轉換.如:
byte a = 127; byte b = 127; b = a + b; // 報編譯錯誤:cannot convert from int to byte b += a;
如下代碼是否有錯,有的話怎麼改?
short s1= 1; s1 = s1 + 1;
有錯誤.short類型在進行運算時會自動提高爲int類型,也就是說s1+1
的運算結果是int類型,而s1是short類型,此時編譯器會報錯.
正確寫法:
short s1= 1; s1 += 1;
+=
操做符會對右邊的表達式結果強轉匹配左邊的數據類型,因此沒錯.
咱們能夠作強制轉換,可是 Java 中 int 是 32 位的,而 byte 是 8 位的,因此,若是強制轉化,int 類型的高 24 位將會被丟棄,由於byte 類型的範圍是從 -128 到 127
咱們平常的工做中都使用開發工具(IntelliJ IDEA 或 Eclipse 等)能夠很方便的調試程序,或者是經過打包工具把項目打包成 jar 包或者 war 包,放入 Tomcat 等 Web 容器中就能夠正常運行了,但你有沒有想過 Java 程序內部是如何執行的?其實不管是在開發工具中運行仍是在 Tomcat 中運行,Java 程序的執行流程基本都是相同的,它的執行流程以下:
final做爲Java中的關鍵字能夠用於三個地方。用於修飾類、類屬性和類方法。
特徵:凡是引用final關鍵字的地方皆不可修改!
(1)修飾類:表示該類不能被繼承;
(2)修飾方法:表示方法不能被重寫;
(3)修飾變量:表示變量只能一次賦值之後值不能被修改(常量)。
final也是不少面試喜歡問的地方,但我以爲這個問題很無聊,一般能回答下如下5點就不錯了:
除此以外,編譯器對final域要遵照的兩個重排序規則更好:
在構造函數內對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,這兩個操做之間不能重排序 初次讀一個包含final域的對象的引用,與隨後初次讀這個final域,這兩個操做之間不能重排序.
全部的人都知道static關鍵字這兩個基本的用法:靜態變量和靜態方法.也就是被static所修飾的變量/方法都屬於類的靜態資源,類實例所共享.
除了靜態變量和靜態方法以外,static也用於靜態塊,多用於初始化操做:
public calss PreCache{ static{ //執行相關操做 } }
此外static也多用於修飾內部類,此時稱之爲靜態內部類.
最後一種用法就是靜態導包,即import static
.import static是在JDK 1.5以後引入的新特性,能夠用來指定導入某個類中的靜態資源,而且不須要使用類名,能夠直接使用資源名,好比:
import static java.lang.Math.*; public class Test{ public static void main(String[] args){ //System.out.println(Math.sin(20));傳統作法 System.out.println(sin(20)); } }
關鍵詞 | 修飾物 | 影響 |
---|---|---|
final | 變量 | 分配到常量池中,程序不可改變其值 |
final | 方法 | 子類中將不能被重寫 |
final | 類 | 不能被繼承 |
static | 變量 | 分配在內存堆上,引用都會指向這一個地址而不會從新分配內存 |
static | 方法塊 | 虛擬機優先加載 |
static | 類 | 能夠直接經過類來調用而不須要new |
爲了網絡進行傳輸或者持久化
什麼是序列化
將對象的狀態信息轉換爲能夠存儲或傳輸的形式的過程
除了實現Serializable接口還有什麼序列化方式
咱們有時候將一個java對象變成字節流的形式傳出去或者從一個字節流中恢復成一個java對象,例如,要將java對象存儲到硬盤或者傳送給網絡上的其餘計算機,這個過程咱們能夠本身寫代碼去把一個java對象變成某個格式的字節流再傳輸。
可是,jre自己就提供了這種支持,咱們能夠調用OutputStream
的writeObject
方法來作,若是要讓java幫咱們作,要被傳輸的對象必須實現serializable
接口,這樣,javac編譯時就會進行特殊處理,編譯的類才能夠被writeObject
方法操做,這就是所謂的序列化。須要被序列化的類必須實現Serializable
接口,該接口是一個mini接口,其中沒有須要實現方法,implements Serializable只是爲了標註該對象是可被序列化的。
例如,在web開發中,若是對象被保存在了Session中,tomcat在重啓時要把Session對象序列化到硬盤,這個對象就必須實現Serializable接口。若是對象要通過分佈式系統進行網絡傳輸,被傳輸的對象就必須實現Serializable接口。
內部類的定義
將一個類定義在另外一個類裏面或者一個方法裏面,這樣的類稱爲內部類。
內部類的做用:
一、成員內部類 成員內部類能夠無條件訪問外部類的全部成員屬性和成員方法(包括private成員和靜態成員)。 當成員內部類擁有和外部類同名的成員變量或者方法時,會發生隱藏現象,即默認狀況下訪問的是成員內部類的成員。
二、局部內部類 局部內部類是定義在一個方法或者一個做用域裏面的類,它和成員內部類的區別在於局部內部類的訪問僅限於方法內或者該做用域內。
三、匿名內部類 匿名內部類就是沒有名字的內部類
四、靜態內部類 指被聲明爲static的內部類,他能夠不依賴內部類而實例,而一般的內部類須要實例化外部類,從而實例化。靜態內部類不能夠有與外部類有相同的類名。不能訪問外部類的普通成員變量,可是能夠訪問靜態成員變量和靜態方法(包括私有類型) 一個 靜態內部類去掉static 就是成員內部類,他能夠自由的引用外部類的屬性和方法,不管是靜態仍是非靜態。可是不能夠有靜態屬性和方法
Java可拋出(Throwable)的結構分爲三種類型:被檢查的異常(CheckedException),運行時異常(RuntimeException),錯誤(Error)。
一、運行時異常
定義:RuntimeException及其子類都被稱爲運行時異常。
特色:Java編譯器不會檢查它。也就是說,當程序中可能出現這類異常時,假若既"沒有經過throws聲明拋出它",也"沒有用try-catch語句捕獲它",仍是會編譯經過。例如,除數爲零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException異常,fail-fast機制產生的ConcurrentModificationException異常(java.util包下面的全部的集合類都是快速失敗的,「快速失敗」也就是fail-fast,它是Java集合的一種錯誤檢測機制。當多個線程對集合進行結構上的改變的操做時,有可能會產生fail-fast機制。記住是有可能,而不是必定。例如:假設存在兩個線程(線程一、線程2),線程1經過Iterator在遍歷集合A中的元素,在某個時候線程2修改了集合A的結構(是結構上面的修改,而不是簡單的修改集合元素的內容),那麼這個時候程序就會拋出 ConcurrentModificationException 異常,從而產生fail-fast機制,這個錯叫併發修改異常。Fail-safe,java.util.concurrent包下面的全部的類都是安全失敗的,在遍歷過程當中,若是已經遍歷的數組上的內容變化了,迭代器不會拋出ConcurrentModificationException異常。若是未遍歷的數組上的內容發生了變化,則有可能反映到迭代過程當中。這就是ConcurrentHashMap迭代器弱一致的表現。ConcurrentHashMap的弱一致性主要是爲了提高效率,是一致性與效率之間的一種權衡。要成爲強一致性,就獲得處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap同樣了。)等,都屬於運行時異常。
常見的五種運行時異常:
ClassCastException
(類轉換異常)
IndexOutOfBoundsException
(數組越界)
NullPointerException
(空指針異常)
ArrayStoreException
(數據存儲異常,操做數組是類型不一致)
BufferOverflowException
二、被檢查異常
定義:Exception類自己,以及Exception的子類中除了"運行時異常"以外的其它子類都屬於被檢查異常。
特色 : Java編譯器會檢查它。 此類異常,要麼經過throws進行聲明拋出,要麼經過try-catch進行捕獲處理,不然不能經過編譯。例如,CloneNotSupportedException就屬於被檢查異常。
當經過clone()接口去克隆一個對象,而該對象對應的類沒有實現Cloneable接口,就會拋出CloneNotSupportedException異常。被檢查異常一般都是能夠恢復的。 如:
IOException
FileNotFoundException
SQLException
被檢查的異常適用於那些不是因程序引發的錯誤狀況,好比:讀取文件時文件不存在引起的FileNotFoundException
。然而,不被檢查的異常一般都是因爲糟糕的編程引發的,好比:在對象引用時沒有確保對象非空而引發的NullPointerException
。
三、錯誤
定義 : Error類及其子類。
特色 : 和運行時異常同樣,編譯器也不會對錯誤進行檢查。
當資源不足、約束失敗、或是其它程序沒法繼續運行的條件發生時,就產生錯誤。程序自己沒法修復這些錯誤的。例如,VirtualMachineError就屬於錯誤。出現這種錯誤會致使程序終止運行。OutOfMemoryError、ThreadDeath。
Java虛擬機規範規定JVM的內存分爲了好幾塊,好比堆,棧,程序計數器,方法區等
咱們知道finally{}中的語句是必定會執行的,那麼這個可能正常脫口而出就是return以前,return以後可能就出了這個方法了,鬼知道跑哪裏去了,但更準確的應該是在return中間執行,請看下面程序代碼的運行結果:
public classTest { public static void main(String[]args) { System.out.println(newTest().test());; } static int test() { intx = 1; try { return x; } finally { ++x; } } }
執行結果以下:
1
運行結果是1,爲何呢?主函數調用子函數並獲得結果的過程,比如主函數準備一個空罐子,當子函數要返回結果時,先把結果放在罐子裏,而後再將程序邏輯返回到主函數。所謂返回,就是子函數說,我不運行了,你主函數繼續運行吧,這沒什麼結果可言,結果是在說這話以前放進罐子裏的。
異常表示程序運行過程當中可能出現的非正常狀態,運行時異常表示虛擬機的一般操做中可能遇到的異常,是一種常見運行錯誤。java編譯器要求方法必須聲明拋出可能發生的非運行時異常,可是並不要求必須聲明拋出未被捕獲的運行時異常。
error 表示恢復不是不可能但很困難的狀況下的一種嚴重問題。好比說內存溢出。不可能期望程序能處理這樣的狀況。exception表示一種設計或實現問題。也就是說,它表示若是程序運行正常,從不會發生的狀況。
異常是指java程序運行時(非編譯)所發生的非正常狀況或錯誤,與現實生活中的事件很類似,現實生活中的事件能夠包含事件發生的時間、地點、人物、情節等信息,能夠用一個對象來表示,Java使用面向對象的方式來處理異常,它把程序中發生的每一個異常也都分別封裝到一個對象來表示的,該對象中包含有異常的信息。
Java對異常進行了分類,不一樣類型的異常分別用不一樣的Java類表示,全部異常的根類爲java.lang.Throwable。
Throwable下面又派生了兩個子類:
Error和Exception,Error表示應用程序自己沒法克服和恢復的一種嚴重問題,程序只有奔潰了,例如,說內存溢出和線程死鎖等系統問題。
Exception表示程序還可以克服和恢復的問題,其中又分爲系統異常和普通異常:
系統異常是軟件自己缺陷所致使的問題,也就是軟件開發人員考慮不周所致使的問題,軟件使用者沒法克服和恢復這種問題,但在這種問題下還可讓軟件系統繼續運行或者讓軟件掛掉,例如,數組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉換異常(ClassCastException);
普通異常是運行環境的變化或異常所致使的問題,是用戶可以克服的問題,例如,網絡斷線,硬盤空間不夠,發生這樣的異常後,程序不該該死掉。
java爲系統異常和普通異常提供了不一樣的解決方案,編譯器強制普通異常必須try..catch處理或用throws聲明繼續拋給上層調用方法處理,因此普通異常也稱爲checked異常,而系統異常能夠處理也能夠不處理,因此,編譯器不強制用try..catch處理或用throws聲明,因此係統異常也稱爲unchecked異常。
"=="
對於基本類型和引用類型 == 的做用效果是不一樣的,以下所示:
String x = "string"; String y = "string"; String z = new String("string"); System.out.println(x==y); // true System.out.println(x==z); // false System.out.println(x.equals(y)); // true System.out.println(x.equals(z)); // true
由於 x 和 y 指向的是同一個引用,因此 == 也是 true,而 new String()方法則重寫開闢了內存空間,因此 == 結果爲 false,而 equals 比較的一直是值,因此結果都爲 true。
equals
equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面的代碼就明白了。
首先來看默認狀況下 equals 比較一個有相同值的對象,代碼以下:
class Cat { public Cat(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } Cat c1 = new Cat("葉痕秋"); Cat c2 = new Cat("葉痕秋"); System.out.println(c1.equals(c2)); // false
輸出結果出乎咱們的意料,居然是 false?這是怎麼回事,看了 equals 源碼就知道了,源碼以下:
public boolean equals(Object obj) { return (this == obj); }
原來 equals 本質上就是 ==。
那問題來了,兩個相同值的 String 對象,爲何返回的是 true?代碼以下:
String s1 = new String("葉子"); String s2 = new String("葉子"); System.out.println(s1.equals(s2)); // true
一樣的,當咱們進入 String 的 equals 方法,找到了答案,代碼以下:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
原來是 String 重寫了 Object 的 equals 方法,把引用比較改爲了值比較。
總結
== 對於基本類型來講是值比較,對於引用類型來講是比較的是引用;而 equals 默認狀況下是引用比較,只是不少類從新了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等。
java的集合有兩類,一類是List,還有一類是Set。前者有序可重複,後者無序不重複。當咱們在set中插入的時候怎麼判斷是否已經存在該元素呢,能夠經過equals方法。可是若是元素太多,用這樣的方法就會比較滿。
因而有人發明了哈希算法來提升集合中查找元素的效率。 這種方式將集合分紅若干個存儲區域,每一個對象能夠計算出一個哈希碼,能夠將哈希碼分組,每組分別對應某個存儲區域,根據一個對象的哈希碼就能夠肯定該對象應該存儲的那個區域。
hashCode方法能夠這樣理解:它返回的就是根據對象的內存地址換算出的一個值。這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了;若是這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。
不對,兩個對象的 hashCode() 相同,equals() 不必定 true。
代碼示例:
String str1 = "keep"; String str2 = "brother"; System. out. println(String. format("str1:%d | str2:%d", str1. hashCode(),str2. hashCode())); System. out. println(str1. equals(str2));
執行結果:
str1:1179395 | str2:1179395 false
代碼解讀:很顯然「keep」和「brother」的 hashCode() 相同,然而 equals() 則爲 false,由於在散列表中,hashCode() 相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不必定能得出鍵值對相等。
泛型是Java SE 1.5以後的特性, 《Java 核心技術》中對泛型的定義是:
「泛型」 意味着編寫的代碼能夠被不一樣類型的對象所重用。
「泛型」,顧名思義,「泛指的類型」。咱們提供了泛指的概念,但具體執行的時候卻能夠有具體的規則來約束,好比咱們用的很是多的ArrayList就是個泛型類,ArrayList做爲集合能夠存放各類元素,如Integer, String,自定義的各類類型等,但在咱們使用的時候經過具體的規則來約束,如咱們能夠約束集合中只存放Integer類型的元素,如
List<Integer> iniData = new ArrayList<>()
使用泛型的好處?
以集合來舉例,使用泛型的好處是咱們沒必要由於添加元素類型的不一樣而定義不一樣類型的集合,如整型集合類,浮點型集合類,字符串集合類,咱們能夠定義一個集合來存放整型、浮點型,字符串型數據,而這並非最重要的,由於咱們只要把底層存儲設置了Object便可,添加的數據所有均可向上轉型爲Object。 更重要的是咱們能夠經過規則按照本身的想法控制存儲的數據類型。
面向對象的編程語言有封裝、繼承 、抽象、多態等4個主要的特徵。
注意:在使用多態後的父類引用變量調用方法時,會調用子類重寫後的方法。
定義格式:父類類型 變量名=new 子類類型();
重寫(Override)
從字面上看,重寫就是 從新寫一遍的意思。其實就是在子類中把父類自己有的方法從新寫一遍。子類繼承了父類原有的方法,但有時子類並不想原封不動的繼承父類中的某個方法,因此在方法名,參數列表,返回類型(除過子類中方法的返回值是父類中方法返回值的子類時)都相同的狀況下, 對方法體進行修改或重寫,這就是重寫。但要注意子類函數的訪問修飾權限不能少於父類的。
public class Father { public static void main(String[] args) { // TODO Auto-generated method stub Son s = new Son(); s.sayHello(); } public void sayHello() { System.out.println("Hello"); } } class Son extends Father{ @Override public void sayHello() { // TODO Auto-generated method stub System.out.println("hello by "); } }
重寫 總結:
1.發生在父類與子類之間
2.方法名,參數列表,返回類型(除過子類中方法的返回類型是父類中返回類型的子類)必須相同
3.訪問修飾符的限制必定要大於被重寫方法的訪問修飾符(public>protected>default>private)
4.重寫方法必定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常
重載(Overload)
在一個類中,同名的方法若是有不一樣的參數列表(參數類型不一樣、參數個數不一樣甚至是參數順序不一樣)則視爲重載。同時,重載對返回類型沒有要求,能夠相同也能夠不一樣,但不能經過返回類型是否相同來判斷重載。
public class Father { public static void main(String[] args) { // TODO Auto-generated method stub Father s = new Father(); s.sayHello(); s.sayHello("wintershii"); } public void sayHello() { System.out.println("Hello"); } public void sayHello(String name) { System.out.println("Hello" + " " + name); } }
重載 總結:
1.重載Overload是一個類中多態性的一種表現
2.重載要求同名方法的參數列表不一樣(參數類型,參數個數甚至是參數順序)
3.重載的時候,返回值類型能夠相同也能夠不相同。沒法以返回型別做爲重載函數的區分標準
java中提供瞭如下四種建立對象的方式:
public class Test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(2); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ Integer integer = iterator.next(); if(integer==2) list.remove(integer); } } }
執行上段代碼是有問題的,會拋出ConcurrentModificationException
異常。
緣由:調用list.remove()
方法致使modCount
和expectedModCount
的值不一致。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
解決辦法:在迭代器中若是要刪除元素的話,須要調用Iterator
類的remove
方法。
public class Test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(2); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ Integer integer = iterator.next(); if(integer==2) iterator.remove(); //注意這個地方 } } }
相同點:
不一樣點:
什麼是fail-fast?
就是最快的時間能把錯誤拋出而不是讓程序執行。
Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。
ConcurrentHashMap將整個Map分爲N個segment(相似HashTable),能夠提供相同的線程安全,可是效率提高N倍,默認N爲16。
HashMap能夠經過下面的語句進行同步:
Map m = Collections.synchronizeMap(hashMap);
按功能來分:輸入流(input)、輸出流(output)。
按類型來分:字節流和字符流。
字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。
一、定義:
反射機制是在運行時,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意個對象,都可以調用它的任意一個方法。在java中,只要給定類的名字,就能夠經過反射機制來得到類的全部信息。
這種動態獲取的信息以及動態調用對象的方法的功能稱爲Java語言的反射機制。
二、哪裏會用到反射機制?
jdbc就是典型的反射
Class.forName('com.mysql.jdbc.Driver.class');//加載MySQL的驅動類
這就是反射。如hibernate,struts等框架使用反射實現的。
第一步:獲取Class對象,有4種方法: 1)Class.forName(「類的路徑」); 2)類名.class 3)對象名.getClass() 4)基本類型的包裝類,能夠調用包裝類的Type屬性來得到該包裝類的Class對象
1)Class:表示正在運行的Java應用程序中的類和接口 注意: 全部獲取對象的信息都須要Class類來實現。 2)Field:提供有關類和接口的屬性信息,以及對它的動態訪問權限。 3)Constructor:提供關於類的單個構造方法的信息以及它的訪問權限 4)Method:提供類或接口中某個方法的信息
優勢:
一、可以運行時動態獲取類的實例,提升靈活性;
二、與動態編譯結合
缺點:
一、使用反射性能較低,須要解析字節碼,將內存中的對象進行解析。
解決方案:
一、經過setAccessible(true)關閉JDK的安全檢查來提高反射速度;
二、屢次建立一個類的實例時,有緩存會快不少
三、ReflectASM工具類,經過字節碼生成的方式加快反射速度
二、相對不安全,破壞了封裝性(由於經過反射能夠得到私有方法和屬性)
Java Io 流共涉及 40 多個類,這些類看上去很雜亂,但實際上頗有規則,並且彼此之間存在很是緊密的聯繫, Java I0 流的 40 多個類都是從以下 4 個抽象類基類中派生出來的。
按操做方式分類結構圖: