Core Java(第1-3章)

第一章 概述

1996年,jdk1發佈。

Java並不只是一種語言,還是一個完整的平臺,有一個龐大的庫,其中包含了很多可重用的代碼,還提供了一個諸如安全性、跨操作系統的可移植性、垃圾自動收集等服務的執行環境。

簡單性、面向對象、分佈式、健壯性、安全性、體系結構中立、可移植性、解釋型、高性能、多線程、動態

可移植性:java中沒有“依賴具體實現”的地方,在java中int永遠都是4個字節,而c++中,int可能是2字節,也可能是4字節。在java中,處理文件、線程、網絡連接、數據庫、日期時間等,完全不用操心底層操作系統。

高性能 :JIT即時編譯器,可以對java中的熱點代碼做即時編譯執行(解釋器是解釋一句執行一句,每次執行都要解釋一次,即時編譯是直接翻譯成機器碼)更爲複雜的優化是消除函數調用的內聯優化。

1.2.6 體系結構中立 / 前端編譯器javac

jdk【編譯器, JRE【 JVM 【 即時編譯器、解釋器 】 】】

編譯器生成一個體繫結構中立的目標文件格式,即字節碼。只要有Java運行時系統(JRE,包括了JVM-即java解釋器),這些字節碼就可以在許多處理器上運行。精心設計的字節碼不僅可以很容易地在任何機器上通過JVM解釋執行,而且還可以動態地翻譯成本地機器代碼。
虛擬機有一個選項,可以將執行最頻繁的字節碼序列翻譯成機器碼,這被稱爲即時編譯。

1.2.8 解釋型

java解釋器可以在任何移植了java解釋器(JVM)的機器上執行java字節碼。由於鏈接是一個增量式且輕量級的過程? ,所以,開發過程也變得更加快捷,更加有探索性。?(什麼是鏈接?類加載後的連接?如何使開發變得更有探索性?)

2.9 即時編譯和內聯 / 後端編譯器 / JIT(Just In Time Compilation)

“Hot Spot Code”(熱點代碼):當虛擬機發現某個方法或代碼塊運行特別頻繁時,就會把這些代碼認定爲“Hot Spot Code”(熱點代碼)。

Hot Spot Code有兩種:一種是被多次調用的方法,一種是被多次調用的循環體。

爲了提高熱點代碼的執行效率,在運行時,虛擬機將會把熱點代碼(字節碼)編譯成與本地平臺相關的機器碼,並進行各層次的優化,完成這項任務的正是JIT編譯器。

JIT編譯器與解釋器的區別是:即時編譯生成機器相關的中間碼,可重複執行緩存效率高。解釋執行直接執行字節碼,重複執行需要重複解釋。

目前主流的HotSpot虛擬機中默認是採用解釋器與其中一個編譯器直接配合的方式工作。

更復雜的優化是消除函數調用(即“內聯”)。即時編譯器知道哪些類已經加載,基於當前加載的類集,如果特定的函數不會被覆蓋,就可以使用內聯。必要時還可以撤銷優化。

方法內聯後,減少了線程中方法棧的棧幀,也減少了創建、銷燬棧及方法調用的耗時,對時間和空間都有優化。

但是如果方法雖然是熱點方法(被執行了1500次或者10000次),但是方法體比較大(大於325字節),也不會被內聯優化。

此外,即使對方法做了內聯優化,還有可能因爲方法被繼承,導致需要類型檢查而沒能達到優化的效果。

因此,要想使得JIT的內聯優化發揮作用,需要注意以下幾點:

  1. 拆分大的方法,減小每個方法的方法體。
  2. 儘量使用final、private、static修飾符
  3. 使用+printInlining檢查效果
  4. 可參考Netty庫上使用內聯優化的例子

關於前端編譯器(Javac)和後端編譯器(JIT)
JIT的幾種優化JVM性能的手段

第二章 java程序設計環境

2.1.1 下載安裝JDK

術語

JDK : 編寫java程序的程序員使用的軟件
JRE : 運行java程序的用戶使用的軟件,包含虛擬機但不包含javac編譯器
java SE:Standard Edition,用於桌面或者簡單服務器應用的java平臺
Java EE: Enterprise Edition ,用於複雜服務器應用的java平臺
Open JDK:java SE的一個免費開源實現
SDK:software dev kit , 一個過時的術語,98-06年之間的JDK
update:Oracle的術語,表示bug修正版本。8u31是1.8.0_31
NetBeans:Oracle的集成開發環境

版本選擇

對於linux,建議選擇 .tar.gz,而非RPM。
.tar.gz可以選擇在任何位置解壓縮。
RPM安裝,需要反覆檢查是否裝在了/usr/java/jdk1.8.0_version

安裝

windows操作系統上,注意安裝路徑不要有空格和中文。
linux系統上,裝到/opt/jdk1.8.0_31

Linux系統目錄詳解
目錄 全稱 用途
/root root root用戶即超級管理員用戶的主目錄
/sbin super bin super User使用的命令,引導、修復或者恢復系統的命令。即超級管理員用戶使用的管理系統命令的文件夾
/etc and so on 等等,其它文件的意思。存放系統管理的配置文件
/home - 普通用戶主目錄的根目錄,在此目錄下,每個用戶都有一個自己的目錄,一般該目錄名是以用戶的賬號命名的
/opt optional Optional application software packages。這裏主要存放那些可選的程序。你想嘗試最新的firefox測試版嗎?那就裝到/opt目錄下吧,這樣,當你嘗試完,想刪掉firefox的時候,你就可 以直接刪除它,而不影響系統其他任何設置。安裝到/opt目錄下的程序,它所有的數據、庫文件等等都是放在同個目錄下面。主機額外安裝軟件的安裝目錄。比如安裝一個ORACLE數據庫則就可以放到這個目錄下。默認是空的。
/var variable-可變的 存放着在不斷擴充着的東西,那些經常被修改的目錄放在這個目錄下。包括各種日誌文件。非常重要的目錄,系統上跑了很多程序,那麼每個程序都會有相應的日誌產生,而這些日誌就被記錄到這個目錄下
/lib - 存放着系統最基本的動態連接共享庫,類似於Windows裏的DLL文件。幾乎所有的應用程序都需要用到這些共享庫
/usr Unix Software Resource Unix操作系統軟件資源所放置的目錄,所有系統默認的軟件都會放置到/usr, 系統安裝完時,這個目錄會佔用最多的硬盤容量
/usr/bin - 普通用戶的可使用指令,絕大部分都放在這裏
/usr/sbin - 非系統正常運作所需要的指令,常見的是某些網絡服務器軟件的服務指令
/bin Binary 命令
/media 自動識別的一些設備,例如U盤、光驅等等,會被掛載到這個目錄下
/proc :所有正在運行進程的映像
/tmp 每次重新引導就消失的臨時文件
將java的命令路徑設置到系統的執行路徑變量中

用戶在命令行窗口中鍵入某個命令並鍵入enter後,系統會遍歷執行路徑變量中配置的所有目錄,在這些目錄中找到要執行的命令,然後運行之,才能執行用戶發出命令。

因此我們要想在命令行窗口中運行 javac.exe 或者java.exe或者jdk/bin下的其它命令,就必須把jdk/bin的路徑配置到執行路徑變量中去,並使之永久生效。

linux的環境變量配置中,要先刪除~/.bash_profile中的三行關於~/.bashrc的 定義,然後把環境變量配置在~/.bashrc中
2.選擇要使用的java環境:update-alternatives –config java
3.要使得剛修改的環境變量生效:source .bashrc
4.查看環境變量:env
也可以放到/etc/bash/bashrc,這樣就是系統級的

安裝源文件和文檔
cd ~        //changge direcotry to ~ :進入用戶主目錄
mkdir javasrc  //make directory :創建javasrc文件夾
cd javasrc   
jar xvf jdk/src.zip  

關於tar jar命令詳見:
linux常用操作

第三章 java的基本程序結構

類名

必須大寫字母開頭,後跟字母和數字的任意組合,大小寫遵循駝峯規則。

注意

在java中“字母”和“數字”的範圍更大,字母包括a-z A-Z _ $,或者在某種語言中表示字母的任何Unicode字符。例如希臘人可以用π。數字除了0-9外,某種語言中表示數字的任何Unicode字符都可以。

如果想知道某個字符是否屬於JAVA中的字母,可以調用Character.isJavaIndetifierStart(char c),來檢測字符是否可以作爲首字母
Character.isJavaIndetifierPart(int codePoint)方法來檢測字符是否可以出現在類名中

每個應用程序必須有一個 public static void main(String [] args){}方法作爲程序的入口。

3.3.2浮點類型

浮點類型的字面量會被JAVA自動識別爲double,如果要標識爲float,可以在數字後邊加f。

99%的浮點數(小數)無法用二進制精確表示,正如十進制無法精確地表示分數1/3一樣。

任意實數,整數部分用普通的二進制便可以表示,對於小數部分,將該數字乘以2,取出整數部分作爲二進制表示的第1位;然後再將小數部分乘以2,將得到的整數部分作爲二進制表示的第2位;以此類推,知道小數部分爲0。

0.6 * 2 = 1.2, 取 1餘0.2,
0.2 * 2 = 0.4,取0餘0.4,
0.4 * 2 = 0.8,取0餘0.8,
0.8 * 2 = 1.6, 取 1餘0.6,
0.6 * 2 = 1.2,取1餘0.2,進入循環 …
形成了一個無限循環的二進制:0.1001 1001 1001 1001…

因此只能截取一部分,存儲浮點數的近似值。
參考:
小數的二進制表示與轉換

浮點數在計算機中如何存儲

double型精度是float型的兩倍。

如果在數值計算中不允許有任何的舍入誤差,可以用BigDecimal這個類來處理。

下面是用於表示溢出和出錯情況 的三個特殊的浮點數值:

正無窮大 Double.POSITIVE_INFINITY
負無窮大 Double.NEGATIVE_INFINITY
NaN 不是一個數字,可以用Double.isNan(x)來判斷

3.3.3 char類型

char類型的字面量值要用單引號括起來的單個字符來表示’A’。

碼點(code point)與代碼單元(code unit)

碼點(code point)是指Unicode字符集中的每個字符對應的代碼值。

比如,在Unicode字符集中,'A’的代碼值是56,十六進制值是\u0037。

起初,Unicode字符集中的總字符數是不超過65536個的,也即其碼點即代碼值不會大於65535,那麼在計算機中用兩個字節16位就足夠存儲這些碼值。

在java語言中,最初是是用char類型來表示Unicode字符集中的字符,用utf-16來編碼存儲。即一個char類型的字符,分配兩個字節來存儲。

但是隨着Unicode字符集的逐漸擴充,字符個數遠超65536,那麼有些碼點即代碼值也大於65536了。

這時,char類型,兩個字節的存儲空間,是無法存儲大於65535的碼點的。

因此jdk5開始,將char類型的定義更新成了“char類型描述了Ut-16編碼中的一個代碼單元(code unit)”。

綜上,char類型是一個代碼單元

字符集與編碼實現

針對Unicode字符集中字符數超過65536的問題,UTF-16編碼方式採用如下方式解決:

首先,將Unicode的碼點(code point ,每個字符的碼值)分成17個代碼級別。

第一個代碼級別-基本的多語言級別,包括經典的Unicode碼點,碼點從U+0000到U+FFFF。

其餘的16個代碼級別,碼點從U+10000到U+10FFFF。

但是,第一個代碼級別的碼點雖然是從U+0000到U+FFFF,但是在設計上,並沒有完全佔用U+0000到U+FFFF中的所有碼值,而是空閒出了U+D800 ~ U+DFFF這段碼值。

0~0xD7FF 15+(15*16)+(16^2)*7+((16^3)*13)=55295個
0xE000~0xFFFF (2^13)+ (2^14) + (2^15) ~ (2^16-1) //共8191個

其餘的16個代碼級別的字符,需要用兩個代碼單元來表示。就是利用了這段空閒的碼值,將其分爲U+D800~U+DBFF以及U+DC00~U+DFFF兩部分,前一部分用來作爲字符的第一個代碼單元的前綴,後一部分用來作爲字符的第二個代碼單元的前綴。

據此,如果一個代碼單元的值小於U+D800,或者大於U+DFFF,那麼它就是第一代碼級別的字符,也就是說這一個代碼單元就表示一個字符。
而如果,一個代碼單元的值處在U+D800~U+DFFF之間,那麼這個代碼單元就是需要兩個代碼單元的字符的其中一個代碼單元。

使用下面公式編碼:

  1. 計算 U’= U – 0x10000

  2. 將U’寫成二進制形式:yyyy yyyy yyxx xxxx xxxx

  3. 加上標誌位,1101 10yy yyyy yyyy 1101 11xx xxxx xxxx

可見,這是4個字節表示,2個6位標誌位,20位有效位。因爲U最大是0x10FFFF,所以U’最大是0xFFFFF,20位足夠表示。
參考:字符集和字符編碼4.9節UTF-16
windows上默認的Unicode編碼方式就是UTF-16,使用wchar_t表示。

3.5 運算符

整數除以0會產生異常。
浮點數除以0會得到無窮大或者NaN。

3.5.1 數學函數與常量

直接在源文件頭導入 Math.*即可:import static java.lang.Math.*;

floorMode()
由於java最初定的取餘運算的規則是,負數餘除於正數的結果爲負,大部分場景下的業務需求都不是這樣的,這也不符合“歐幾里得”規則:餘數總是>=0;因此專門提供了一個工具方法:
floorMode(x,n),x%n,可以保證餘數爲正數。
在java中:-5%2=-1,但floorMode(-5,2)=1

乘方:pow(a,n) a^n
開方:sqrt(a),平方根

正弦:sin
餘弦:cos
正切:tan

無理數e的近似值:Math.E
π的近似值:Math.PI

自然對數:log(數學中用ln)
以10爲底的對數:log10(數學中用lg)

3.5.3 類型轉換

在一些數值運算中,java可能會進行自動類型轉換,規則是:

  • 如果兩個操作數中有一個是 double 類型, 另一個操作數就會轉換爲 double 類型。
  • 否則,如果其中一個操作數是 float 類型,另一個操作數將會轉換爲 float 類型。
  • 否則, 如果其中一個操作數是 long 類型, 另一個操作數將會轉換爲 long 類型。
  • 否則, 兩個操作數都將被轉換爲 int 類型。

當將位數多的類型直接賦值給位數少的數值類型的時候,需要用戶顯式地進行強制類型轉換cast。
int a=0; a=(cast)0.5;

擴展賦值運算符會默認對計算結果進行強轉,因此無需用戶顯式地強轉。
int a =0; a+=0.5;

3.5.7 位運算符

&(按位與) |(按位或) ^(按位異或) ~(按位取反) <<(帶符號左移)>>(帶符號右移)>>>(無符號右移)

&:利用&運算符可以得到二進制整數的各個位上的數字,即保留要獲取的位上的數字,把其它位全掩掉:

例如任何二進制數x,進行如下運算後:x & 0b1000後,都只剩下從右邊數的第4位有效。如果 x & 100,則可以獲得第3位上的數字。

<<

帶符號左移,x<<n,意思是 連帶着符號位一起向左移出n位並捨棄,左移後低位空出來的n位,全部用0補上。相當於x*(2^n)
例如:圖片來自知乎

>>

帶符號右移,意思是帶着符號位一起向右移走n位,移走後空出的n位高位上要補上數值,全都用原符號位上的數值。
在這裏插入圖片描述

>>>

高位全部補0,無論原本的符號位是1還是0

3.6 字符串

s.substring(m,n); //從m開始複製,到n就不復制了。子串長度=n-m
‘+’ 字符串拼接符,當將一個字符串與一個非字符串的值進行拼接時,後者被轉換成字符串(任何一個 Java對象都可以轉換成字符串)

3.6.6 碼點與代碼單元

java字符串由char字符序列組成。

Char類型字符是一個採用UTF-16編碼表示Unicode碼點的代碼單元,大多數Unicode字符使用一個代碼單元就可以表示,但是輔助字符需要一對代碼單元來表示。

string.length()方法返回的就是使用UTF-16編碼表示給定的字符串時需要的代碼單元的數量。

string.codePointCount(0,string.length())返回的是實際的碼點數,即string中Unicode字符的數量。

string.charAt(i)返回的是第i個代碼單元,如果string中有一個輔助字符,即佔了兩個代碼單元的字符,那麼返回的第i個代碼單元就有可能是輔助字符的第2個代碼單元,而非string中的一個字符。

因此,爲了預防開發人員使用char類型時出現代碼單元和實際字符數量的混淆,代碼中最好不要有對char類型的操作。

而是,通過string.codePoints()獲得一個int流,然後.toArray()獲得每個字符的碼值數組int []。

反之,要用一個碼點數組構造一個字符串,可以用
new String(codePoints,0,codePoints.length)

3.6.7常用String API

Java 中的 String類包含了 50 多個方法。令人驚訝的是絕大多數都很有用, 可以設想使 用的頻繁非常高。

int compareTo(String other);-1,0,1

boolean equals(String other) / equalsIgnoreCase(String other)

boolean startWith(String prefix) / endsWith(String suffix)

int indexOf(String sub) / indexOf(String sub, int from)
int indexOf(int codePoints) / indexOf(int codePoints, int from)
從索引位置0或者from開始查找是否有匹配sub或則cp的子串,如果有,返回第一個子串的位置

int lastIndexOf()…返回最後一個子串的位置

3.6.8 字符串連接

用’+'拼接字符串比較浪費內存,因此推薦使用StringBuilder,在多線程情況下用StringBuffer,但其效率相對較低。
StringBuilder的常用API:
append(String s)
insert(int offset, String str)
delete(int start, int end)
length()
toString()

3.7 輸入輸出

java.util.Scanner**

首先構造一個Scanner對象,然後將之與System.in關聯。
參考:類Scanner

Scanner scanner = new Scanner( System.in );

useDelimiter(String pattern / Pattern pattern);

skip(String pattern / Pattern pattern);

useRadix(int radix); //設置數值顯示時的進位制

hasNext() / hasNextLine()

hasNextByte / Short / Int / Long / Float / Double / Boolean / BigInteger / BigDecimal()

文件輸入輸出

讀文件:new Scanner(Paths.get(“myfile.txt”, “UTF-8”));
寫文件:new PrintWriter(“myfile.txt”, “UTF-8”);

java.io.Console

首先使用System類中的一個靜態方法獲取一個Console的實例:

static Console System.console();

然後使用Console進行交互:

static char[] readPassword( String prompt, Object … args );
static String readLine(String prompt,Object … args);

顯示字符串prompt並讀取用戶輸入,直到輸入行結束。args參數可以用來提供輸入格式。

格式化輸出

System.out.printf( formatString , argvalues…);

在formatString中有三個概念,一個是格式說明符,一個是轉換字符,最後是控制格式化輸出的各種標誌。

以%字符開始的都是格式說明符,格式說明符尾部的是轉換符。

格式說明符都要用相應的參數實際值argvalue替換,轉換符則指示被格式化的數值的類型。

格式轉換符 數值類型 舉例
d 十進制整數
o 八進制整數
x 十六進制整數
s 字符串
c 單個字符
b 布爾類型
% 百分號 由於%自身的特殊意義導致的

另外還有 f (定點浮點數,15.9),e(指數浮點數,1.59e+01),g(通用浮點數)

標誌 目的 舉例
+ 打印正數和負數的符號 +3333.33
( 如果是負數,用括號括起來 (-3333.33)
空格 在正數之前添加空格
0 數字前面補0 003333.33)
( 如果是負數,用括號括起來 (-3333.33)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章