Java基礎文法(面向過程)
在學習面向對象之前,我們需要了解面向過程的程式設計思維,如果你學習過C語言和Python就會很輕松!
變量和關鍵字
變量
變量就是一個可變的量,例如定義一個int類型的變量(int就是整數類型):
int a = 10;
a = 20;
a = 30;
我們能夠随意更改它的值,也就是說它的值是随時可變的,我們稱為變量。變量可以是類的變量,也可以是方法内部的局部變量(我們現階段主要用局部變量,類變量在面向對象再講解)
變量和C語言中的變量不同,Java中的變量是存放在JVM管理的記憶體中,C語言的變量存放在記憶體(某些情況下需要手動釋放記憶體,而Java會自動幫助我們清理變量占據的記憶體)Java和C++很類似,但是沒有指針!Java也叫C+±-
Java是強類型語言,隻有明确定義了變量之後,你才能使用!一旦被指定某個資料類型,那麼它将始終被認為是對應的類型(和JS不一樣!)
定義一個變量的格式如下:
[類型] [辨別符(名字)] = [初始值(可選)]
int a = 10;
注意:辨別符不能為以下内容:
- 辨別符以由大小寫字母、數字、下劃線(_)和美元符号($)組成,但是不能以數字開頭。
- 大小寫敏感!
- 不能有空格、@、#、+、-、/ 等符号
- 應該使用有意義的名稱,達到見名知意的目的,最好以小寫字母開頭
- 不可以是 true 和 false
- 不能與Java語言的關鍵字重名
關鍵字
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLzEFRORTSU9UMJpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0UjZjFGNhhTY4YGZmFmY5ATZjRTM4gjNiRTOkFjZkRzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
包括基本資料類型、流程控制語句等,了解就行,不用去記,後面我們會一點一點帶大家認識!
常量
常量就是無法修改值的變量,常量的值,隻能定義一次:
final int a = 10;
a = 10; //報錯!
常量前面必須添加final關鍵字(C語言裡面是const,雖然Java也有,但是不能使用!)
這隻是final關鍵字的第一個用法,後面還會有更多的用法。
注釋
養成注釋的好習慣,不然以後自己都看不懂自己的代碼!注釋包括單行注釋和多行注釋:
//我是單行注釋
/**
* 我是
* 多行注釋
*/
//TODO 待做标記
基本資料類型
Java中的資料類型分為基本資料類型和引用類型兩大類,引用類型我們在面向對象時再提,基本資料類型是重點中的重點!首先我們需要了解有哪些類型。然後,我們需要知道的,并不是他們的精度如何,能夠表示的範圍有多大,而是為什麼Java會給我們定義這些類型,計算機是怎麼表示這些類型的,這樣我們才能夠更好的記憶他們的精度、表示的範圍大小。是以,我們從計算機原理的角度出發,帶領大家走進Java的基本資料類型。
這一部分稍微有點燒腦,但是是重中之重,如果你掌握了這些,任何相關的面試題都難不倒你!(如果你學習過計算機組成原理就很好了解了)
計算機中的二進制表示
在計算機中,所有的内容都是二進制形式表示。十進制是以10為進位,如9+1=10;二進制則是滿2進位(因為我們的計算機是電子的,電平信号隻有高位和低位,你也可以暫且了解為通電和不通電,高電平代表1,低電平代表0,由于隻有0和1,是以隻能使用2進制表示我們的數字!)比如1+1=10=2^1+0,一個位也叫一個bit,8個bit稱為1位元組,16個bit稱為一個字,32個bit稱為一個雙字,64個bit稱為一個四字,我們一般采用位元組來描述資料大小。
十進制的7 -> 在二進制中為 111 = 2^2 + 2^1 + 2^0
現在有4個bit位,最大能夠表示多大的數字呢?
- 最小:0000 => 0
- 最大:1111 => 23+22+21+20 => 8 + 4 + 2 + 1 = 15
在Java中,無論是小數還是整數,他們都要帶有符号(和C語言不同,C語言有無符号數)是以,首位就作為我們的符号位,還是以4個bit為例,首位現在作為符号位(1代表負數,0代表正數):
- 最小:1111 => -(22+21+2^0) => -7
- 最大:0111 => +(22+21+2^0) => +7 => 7
現在,我們4bit能夠表示的範圍變為了-7~+7,這樣的表示方式稱為原碼。
計算機中的加減法
原碼
雖然原碼表示簡單,但是原碼在做加減法的時候,很麻煩!以4bit位為例:
1+(-1) = 0001 + 1001 = 怎麼讓計算機去計算?(雖然我們知道該去怎麼算,但是計算機不知道!)
我們得創造一種更好的表示方式!于是我們引入了反碼:
反碼
- 正數的反碼是其本身
- 負數的反碼是在其原碼的基礎上, 符号位不變,其餘各個位取反
經過上面的定義,我們再來進行加減法:
1+(-1) = 0001 + 1110 = 1111 => -0 (直接相加,這樣就簡單多了!)
思考:1111代表-0,0000代表+0,在我們實數的範圍内,0有正負之分嗎?
- 0既不是正數也不是負數,那麼顯然這樣的表示依然不夠合理!
補碼
根據上面的問題,我們引入了最終的解決方案,那就是補碼,定義如下:
- 正數的補碼就是其本身 (不變!)
- 負數的補碼是在其原碼的基礎上, 符号位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1)
其實作在就已經能夠想通了,-0其實已經被消除了!我們再來看上面的運算:
1+(-1) = 0001 + 1111 = (1)0000 => +0 (現在無論你怎麼算,也不會有-0了!)
是以現在,4bit位能夠表示的範圍是:-8~+7(Java使用的就是補碼!)
以上内容是重點, 是一定要掌握的知識,這些知識是你在面試中的最終防線!有了這些理論基礎,無論面試題如何變換,都能夠通過理論知識來破解
整數類型
整數類型是最容易了解的類型!既然我們知道了計算機中的二進制數字是如何表示的,那麼我們就可以很輕松的以二進制的形式來表達我們十進制的内容了。
在Java中,整數類型包括以下幾個:
- byte 位元組型 (8個bit,也就是1個位元組)範圍:-128~+127
- short 短整形(16個bit,也就是2個位元組)範圍:-32768~+32767
- int 整形(32個bit,也就是4個位元組)最常用的類型!
- long 長整形(64個bit,也就是8個位元組)最後需要添加l或L
long都裝不下怎麼辦?BigInteger!
數字已經達到byte的最大值了,還能加嗎?為了便于了解,以4bit為例:
0111 + 0001 = 1000 => -8(你沒看錯,就是這樣!)
整數還能使用8進制、16進制表示:
- 十進制為15 = 八進制表示為017 = 十六進制表示為 0xF = 二進制表示 1111 (代碼裡面不能使用二進制!)
字元類型和字元串
在Java中,存在字元類型,它能夠代表一個字元:
- char 字元型(16個bit,也就是2位元組,它不帶符号!)範圍是0 ~ 65535
- 使用Unicode表示就是:\u0000 ~ \uffff
字元要用單引号擴起來!比如 char c = ‘淦’;
字元其實本質也是數字,但是這些數字通過編碼表進行映射,代表了不同的字元,比如字元
'A'
的ASCII碼就是數字
65
,是以,char類型其實可以轉換為上面的整數類型。
Java的char采用Unicode編碼表(不是ASCII編碼!),Unicode編碼表包含ASCII的所有内容,同時還包括了全世界的語言,ASCII隻有1位元組,而Unicode編碼是2位元組,能夠代表65536種文字,足以包含全世界的文字了!(我們編譯出來的位元組碼檔案也是使用Unicode編碼的,是以利用這種特性,其實Java支援中文變量名稱、方法名稱甚至是類名)
既然char隻能代表一個字元,那怎麼才能包含一句話呢?(關于數組,我們這裡先不了解,數組我們放在面向對象章節講解)
String就是Java中的字元串類型(注意,它是一個類,建立出來的字元串本質是一個對象,不是我們的基本類型)字元串就像它的名字一樣,代表一串字元,也就是一句完整的話。
字元串用雙引号括起來!比如:String str = “一日三餐沒煩惱”;
小數類型
小數類型比較難了解(比較難了解指的是原理,不是使用)首先來看看Java中的小數類型包含哪些:
- float 單精度浮點型 (32bit,4位元組)
- double 雙精度浮點型(64bit,8位元組)
思考:小數的範圍該怎麼定義呢?我們首先要了解的是小數在計算機裡面是如何存放的:
根據國際标準 IEEE 754,任意一個二進制浮點數 V 可以表示成下面的形式:
V = (-1)^S × M × 2^E
(1)(-1)^S 表示符号位,當 S=0,V 為正數;當 S=1,V 為負數。
(2)M 表示有效數字,大于等于 1,小于 2,但整數部分的 1 不變,是以可以省略。(例如尾數為1111010,那麼M實際上就是1.111010,尾數首位必須是1,1後面緊跟小數點,如果出現0001111這樣的情況,去掉前面的0,移動1到首位;題外話:随着時間的發展,IEEE 754标準預設第一位為1,故為了能夠存放更多資料,就舍去了第一位,比如儲存1.0101 的時候, 隻儲存 0101,這樣能夠多存儲一位資料)
(3)2^E 表示指數位。(用于移動小數點)
比如: 對于十進制的 5.25 對應的二進制為:101.01,相當于:1.0101*2^2。是以,S 為 0,M 為 1.0101,E 為 2。是以,對于浮點類型,最大值和最小值不僅取決于符号和尾數,還有它的階碼。我們在這裡就不去計算了,想了解的可以去搜尋相關資料。
思考:就算double有64bit位數,但是依然存在精度限制,如果我要進行高精度的計算,怎麼辦?BigDecimal!
布爾類型
布爾類型(boolean)隻有
true
和
false
兩種值,也就是要麼為真,要麼為假,布爾類型的變量通常用作流程控制判斷語句。(C語言一般使用0表示false,除0以外的所有數都表示true)布爾類型占據的空間大小并未明确定義,而是根據不同的JVM會有不同的實作。
類型轉換
隐式類型轉換
隐式類型轉換支援位元組數小的類型自動轉換為位元組數大的類型,整數類型自動轉換為小數類型,轉換規則如下:
- byte→short(char)→int→long→float→double
問題:為什麼long比float大,還能轉換為float呢?小數的存儲規則讓float的最大值比long還大,隻是可能會丢失某些位上的精度!
是以,如下的代碼就能夠正常運作:
byte b = 9;
short s = b;
int i = s;
long l = i;
float f = l;
double d = f;
System.out.println(d);
//輸出 9.0
顯示類型轉換
顯示類型轉換也叫做強制類型轉換,也就是說,違反隐式轉換的規則,犧牲精度強行進行類型轉換。
int i = 128;
byte b = (byte)i;
System.out.println(b);
//輸出 -128
為什麼結果是-128?精度丢失了!
- int 類型的128表示:00000000 00000000 00000000 10000000
- byte類型轉換後表示:xxxxxxxx xxxxxxxx xxxxxxxx 10000000 => -128
資料類型自動提升
在參與運算時(也可以位于表達式中時,自增自減除外),所有的byte型、short型和char的值将被提升到int型:
byte b = 105;
b = b + 1; //報錯!
System.out.println(b);
這個特性是由 Java虛拟機規範 定義的,也是為了提高運作的效率。其他的特性還有:
- 如果一個操作數是long型,計算結果就是long型
- 如果一個操作數是float型,計算結果就是float型
- 如果一個操作數是double型,計算結果就是double型
運算符
指派和算術運算符
指派運算符
=
是最常用的運算符,其實就是将我們等号右邊的結果,傳遞給等号左邊的變量,例如:
int a = 10;
int b = 1 + 8;
int c = 5 * 5;
算術運算符也就是我們在國小階段學習的
+
-
*
/
%
,分别代表加減乘除還有取餘,例如:
int a = 2;
int b = 3;
int c = a * b;
//結果為6
需要注意的是,
+
還可以用作字元串連接配接符使用:
當然,字元串可以直接連接配接其他類型,但是會全部當做字元串處理:
int a = 7, b = 15;
System.out.println("lbw" + a + b); //lbw715
算術運算符還包括
++
和
--
也就是自增和自減,以自增為例:
int a = 10;
a++;
System.out.println(a); //輸出為11
自增自減運算符放在變量的前後的傳回值是有差別的:
int a = 10;
System.out.println(a++); //10 (先傳回值,再自增)
System.out.println(a); //11
int a = 10;
System.out.println(++a); //11 (先自增,再傳回值)
System.out.println(a); //11
int a = 10;
int b = 2;
System.out.println(b+++a++); //猜猜看結果是多少
為了使得代碼更簡潔,你還可以使用擴充的指派運算符,包括
+=
、
-=
、
/=
、
*=
、
%=
,和自增自減類似,先執行運算,再傳回結果,同時自身改變:
int a = 10;
System.out.println(a += 2); //等價于 a = a + 2
關系運算符
關系運算符的結果隻能是布爾類型,也就是要麼為真要麼為假,關系運算符包括:
> < == //大于小于等于
>= <= != //大于等于,小于等于,不等于
關系運算符一般隻用于基本類型的比較,運算結果隻能是boolean:
int a = 10;
int b = 2;
boolean x = a > b;
System.out.println(x);
//結果為 true
邏輯運算符
邏輯運算符兩邊隻能是boolean類型或是關系/邏輯運算表達式,傳回值隻能是boolean類型!邏輯運算符包括:
&& //與運算,要求兩邊同時為true才能傳回true
|| //或運算,要求兩邊至少要有一個為true才能傳回true
! //非運算,一般放在表達式最前面,表達式用括号擴起來,表示對表達式的結果進行反轉
實際案例來看看:
int a = 10;
int b = 2;
boolean x = a > b && a < b; //怎麼可能同時滿足呢
System.out.println(x); //false
int a = 10;
int b = 2;
boolean x = a > b || a <= b; //一定有一個滿足!
System.out.println(x); //true
int a = 10;
int b = 2;
boolean x = !(a > b); //對結果進行反轉,本來應該是true
System.out.println(x); //false
位運算符
& //按位與,注意,傳回的是運算後的同類型值,不是boolean!
| //按位或
^ //按位異或 0 ^ 0 = 0
~ //按位非
按位運算實際上是根據值的二進制編碼來計算結果,例如按位與,以4bit為例:
0101 & 0100 = 0100 (隻有同時為1對應位才得1)
int a = 7, b = 15;
System.out.println(a & b); //結果為7
三目運算符
三目運算符其實是為了簡化代碼而生,可以根據條件是否滿足來決定傳回值,格式如下:
int a = 7, b = 15;
String str = a > b ? "行" : "不行"; // 判斷條件(隻能是boolean,或傳回boolean的表達式) ? 滿足的傳回值 : 不滿足的傳回值
System.out.println("漢堡做的行不行?"+str); //漢堡做的行不行?不行
了解三目運算符,就很容易了解後面的if-else語句了。
流程控制
我們的程式都是從上往下依次運作的,但是,僅僅是這樣還不夠,我們需要更加進階的控制語句來幫我進行更靈活的控制。比如,判斷使用者輸入的數字,大于1則輸出ok,小于1則輸出no,這時我們就需要用到選擇結構來幫助我們完成條件的判斷和程式的分支走向。學習過C語言就很輕松!
選擇結構
選擇結構包含if和switch類型,選擇結構能夠幫助我們根據條件判斷,再執行哪一塊代碼。
if語句
就像上面所說,判斷使用者輸入的數字,大于1則輸出ok,小于1則輸出no,要實作這種效果,我們首先可以采用if語句:
if(判斷條件){
//判斷成功執行的内容
}else{
//判斷失敗執行的内容
}
//if的内容執行完成後,後面的内容正常執行
其中,
else
語句不是必須的。
現在,又來了一個新的需求,使用者輸入的是1列印ok,輸入2,列印yes,其他列印no,那麼這樣就需要我們進行多種條件的判斷了,當然if能進行多分支判斷:
if(判斷條件1){
//判斷成功執行的内容
}else if(判斷條件2){
//再次判斷,如果判斷成功執行的内容
}else{
//上面的都沒成功,隻能走這裡
}
同樣,
else
語句不是必須的。
現在,又來了一個新的需求,使用者輸入1之後,在判斷使用者下一次輸入的是什麼,如果是1,列印yes,不是就列印no,這樣就可以用嵌套if了:
if(判斷條件1){
//前提是判斷條件1要成功才能進來!
if(判斷條件2){
//判斷成功執行的内容
}else{
//判斷失敗執行的内容
}
}
switch語句
我們不難發現,雖然
else-if
能解決多分支判斷的問題,但是效率實在是太低了,多分支if采用的是逐級向下判斷,顯然費時費力,那麼有沒有一直更專業的解決多分支判斷問題的東西呢?
switch(判斷主體){
case 值1:
//運作xxx
break; //break用于跳出switch語句,不添加會導緻程式繼續向下運作!
case 值2:
//運作xxx
break;
case 值3:
//運作xxx
break;
}
在上述語句中,隻有判斷主體等于case後面的值時,才會執行case中的語句,同時需要使用break來跳出switch語句,否則會繼續向下運作!
為什麼switch效率更高呢,因為switch采用二分思想進行查找(這也是為什麼switch隻能判斷值相等的原因),能夠更快地找到我們想要的結果!
循環結構
小明想向小紅表白,于是他在螢幕上列印了520個 “我愛你”,我們用Java該如何實作呢?
for語句
for語句是比較靈活的循環控制語句,一個for語句的定義如下:
for(初始條件;循環條件;更新){
//循環執行的内容
}
//循環結束後,繼續執行
- 初始條件:循環開始時的條件,一般用于定義控制循環的變量。
- 循環條件:每輪循環開始之前,進行一次判斷,如果滿足則繼續,不滿足則結束,要求為boolean變量或是boolean表達式。
- 更新:每輪循環結束後都會執行的内容,一般寫增量表達式。
初始條件、循環條件、更新條件不是缺一不可,甚至可以都缺!
for(int i = 0;i < 520;i++){
System.out.println("我愛你");
}
for(;;){
//這裡的内容将會永遠地進行下去!
}
增強for循環在數組時再講解!
while循環
while循環和for循環類似,但是它更加的簡單,隻需要添加維持循環的判斷條件即可!
while(循環條件){
//循環執行的内容
}
和for一樣,每次循環開始,當循環條件不滿足時,自動退出!那麼有時候我們希望先執行了我們的代碼再去判斷怎麼辦呢,我們可以使用do-while語句:
do{
//執行内容
}while(循環條件);
一定會先執行do裡面的内容,再做判斷!
思考:
for(;;){
}
while(true){
}
//它們的性能誰更高?
面向過程程式設計實戰(基礎+算法)
列印九九乘法表
簡單:将九九乘法表列印到控制台。
求1000以内的水仙花數
中等:列印1000以内所有滿足水仙花的數,“水仙花數”是指一個三位數其各位數字的立方和等于該數本身,例如153是“水仙花數”,因為:153 = 1^3 + 5^3 + 3^3
青蛙跳台階問題
困難:一共有n個台階,一隻青蛙每次隻能跳一階或是兩階,那麼一共有多少種跳到頂端的方案?例如n=2,那麼一共有兩種方案,一次性跳兩階或是每次跳一階。
動态規劃:其實,就是利用,上次得到的結果,給下一次作參考,下一次就能利用上次的結果快速得到結果,依次類推
不對啊,别的教程都講了數組、方法,怎麼我們還沒講就進入面向對象了呢?
- 數組在Java中,并非基本類型,數組是程式設計不可見的對象類型,學習了面向對象再來了解,會更加容易!
- 方法在Java中是類具有的屬性,是以,在了解了對象類型之後,再來了解方法,就更加簡單了!