一. 輸入輸出
- 輸入的作用,就是由使用者告訴程序要操作的數(shù)據(jù)
- 例如,我要通過餓了么訂餐,你得告訴程序你要吃什么,送貨地址是什么吧
- 輸出的作用,就是由程序向使用者展現(xiàn)執(zhí)行的結(jié)果
- 還是訂餐的例子,程序向你展示騎手接單沒有,送到哪里了
- 將來輸入輸出來源會(huì)有多種,比如 app,網(wǎng)頁(yè),終端程序等等
1. System.out
之前已經(jīng)介紹過【標(biāo)準(zhǔn)輸出】:System.out
打開 jshell 用一下,回憶下對(duì)象和方法使用格式
對(duì)象.方法(參數(shù));
套用一下,對(duì)象是 System.out,方法是 println,參數(shù)是 “你好”
jshell> System.out.println("你好");
你好
小技巧
- jshell 中用
Tab
鍵可以提示對(duì)象有哪些方法 - jshell 中省略
;
也不會(huì)報(bào)錯(cuò)
2. System.in
再來看看輸入,對(duì)象是 System.in,方法叫 read,沒有參數(shù)
jshell> System.in.read();
運(yùn)行后,可以看到光標(biāo)一閃一閃,表示正在等待用戶的輸入,這時(shí)輸入小 a
jshell> System.in.read();
a
$1 ==> 97
-
會(huì)顯示 97,稱之為返回值,代表 read() 讀入的結(jié)果
- 因?yàn)橛?jì)算機(jī)中所有符號(hào),都是用數(shù)字表示,參看下表
-
前面的 $1 是一個(gè)【變量】,將來它就代表 97,也就是剛才輸入的小 a
-
【變量】可以反復(fù)被使用
\ | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0000 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 |
0016 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 | 無 |
0032 | ! | " | # | $ | % | & | ’ | ( | ) | * | + | , | - | . | / | |
0048 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? |
0064 | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
0080 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \ | ] | ^ | _ |
0096 | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o |
0112 | p | q | r | s | t | u | v | w | x | y | z | { | | | } | ~ | 無 |
System.in 的缺點(diǎn)
- 字符被轉(zhuǎn)成的數(shù)字,不便人類閱讀
- 只能輸入一個(gè)字符
3. Scanner
用 Scanner 改進(jìn),System.in 是 java 為我們提供好的對(duì)象,而 Scanner 需要我們自己創(chuàng)建,語(yǔ)法是
jshell> new Scanner(System.in);
$2 ==> java.util.Scanner...
將來這個(gè) $2 就代表剛才的 Scanner 對(duì)象,我們稱之為【變量】
Scanner 對(duì)象里面最常用的方法是 nextLine,用法如下
jshell> $2.nextLine();
你好啊
$3 ==> "你好啊"
4. 變量名
$2
,$3
這樣作為變量名雖然也可以,但如果用更有意義的名稱來表示,更方便人類閱讀、記憶。例如
jshell> var scanner = new Scanner(System.in)
scanner ==> java.util.Scanner[delimiters=\p{javaWhitespace}+] ... \E][infinity string=\Q∞\E]
jshell> var line = scanner.nextLine()
hello
line ==> "hello"
- scanner 就代指輸入對(duì)象
- line 就代指用 nextLine() 讀取到的字符串值
- var 是關(guān)鍵字,代表某種類型,具體有哪些類型后面再展開
5. 關(guān)鍵字
變量取名時(shí)要注意兩個(gè)規(guī)則,不能以數(shù)字開頭,不能是關(guān)鍵字
什么是關(guān)鍵字呢?關(guān)鍵字就是 java 中有特殊意義的單詞,例如見過的有 class,var,new 等等,如果用 idea 中可以通過特殊顏色強(qiáng)調(diào)哪些單詞是關(guān)鍵字,可以看到這些藍(lán)色的單詞都屬于關(guān)鍵字
至java 17 為止,共有 67 個(gè)關(guān)鍵字,參看這兩份表格,這些關(guān)鍵字,都會(huì)在今后的課程中陸續(xù)學(xué)到
二. 類型、變量、運(yùn)算符
1. 字符與字符串
字符值與字符串值
像這樣用雙引號(hào)引起來的值,在 Java 里稱為字符串,字符串顧名思義,由多個(gè)字符組成,單個(gè)字符用單引號(hào)表示,例如
jshell> 'a'
$4 ==> 'a'
jshell> "abc"
$5 ==> "abc"
- 單引號(hào)里必須由一個(gè)字符
- 雙引號(hào)里可以有零個(gè)、一個(gè)、多個(gè)字符
轉(zhuǎn)義字符
比如我需要輸出一個(gè)單引號(hào)字符值,'''
這樣寫行不行?本意是想表示中間的單引號(hào),但遺憾的是java把前兩個(gè)單引號(hào)當(dāng)成了一對(duì),把它當(dāng)作了那個(gè)空字符了
怎么辦呢
為了把真正的單引號(hào)跟語(yǔ)法的單引號(hào)區(qū)分開,需要給它加一個(gè)反斜杠標(biāo)記,告訴java,我想表示真正的單引號(hào),而不是語(yǔ)法中的單引號(hào)。試一下。
jshell> System.out.println("\'")
'
這種結(jié)合了反斜杠的具有特殊含義的字符,稱之為轉(zhuǎn)義字符(Escape Character)
常見的有七個(gè):\' \" \\ \n \t \b \r
剛才已經(jīng)講過單引號(hào)轉(zhuǎn)義了
繼續(xù)來看幾個(gè)例子
jshell> System.out.println("\"") // 雙引號(hào)轉(zhuǎn)義
"
jshell> System.out.println("\\") // 反斜杠本身轉(zhuǎn)義
\
jshell> System.out.println("1\n2") // 換行
1
2
jshell> System.out.println("123\t4") // 縮進(jìn)
123 4
jshell> System.out.println("123\b4") // 退格
124
jshell> System.out.println("123\r4") // 回車,退格是光標(biāo)退一格,回車是退到頭
423
文本塊
最后再再來看看文本塊,如果有一段文字內(nèi),其中需要有很多的轉(zhuǎn)義字符,那么可讀性會(huì)變得很差,例如
jshell> System.out.println("床前\"明月\"光,\n疑是地上霜。")
因此在 java 14 這個(gè)版本引入了文本塊來進(jìn)行改善。
jshell> System.out.println("""
床前"明月"光,
疑是地上霜。""")
文本塊本質(zhì)上還是屬于字符串值,由一對(duì) 三個(gè)雙引號(hào)作為起始和結(jié)束標(biāo)記,中間如果想表示雙引號(hào)、換行這兩個(gè)特殊字符,無需再轉(zhuǎn)義表示
- 一個(gè)注意事項(xiàng)是 “”" 后需要換個(gè)行,不要緊接著寫字符。
2. 類型
何為類型
現(xiàn)在讓用戶輸入兩個(gè)數(shù),求得相加結(jié)果
jshell> scanner.nextLine()
1
$22 ==> "1"
jshell> scanner.nextLine()
2
$23 ==> "2"
jshell> $22 + $23
$24 ==> "12"
顯然,這并不是我們想要的結(jié)果,它是輸入的值當(dāng)作了字符串,+ 號(hào)執(zhí)行的是字符串連接操作,解決辦法如下
jshell> scanner.nextInt()
1
$25 ==> 1
jshell> scanner.nextInt()
2
$26 ==> 2
jshell> $25 + $26
$27 ==> 3
nextLine() 和 nextInt() 返回的類型是不同的
- 前者返回的是字符串,類型為 String,+ 表示兩個(gè)字符串連接
- 后者返回的是整數(shù),類型為 int,+ 表示兩個(gè)整數(shù)相加
數(shù)字類型
類型名 | 說明 | 數(shù)字范圍 | 類型后綴 |
---|---|---|---|
byte | 整數(shù)類型,用1個(gè)字節(jié)表示 | [ ? 2 7 , 2 7 ) [-2^7,2^7) [?27,27) 即 [ ? 128 , 128 ) [-128,128) [?128,128) | |
short | 整數(shù)類型,用2個(gè)字節(jié)表示 | [ ? 2 15 , 2 15 ) [-2^{15},2^{15}) [?215,215) | |
int | 整數(shù)類型,用4個(gè)字節(jié)表示 | [ ? 2 31 , 2 31 ) [-2^{31},2^{31}) [?231,231) | |
long | 整數(shù)類型,用8個(gè)字節(jié)表示 | [ ? 2 63 , 2 63 ) [-2^{63},2^{63}) [?263,263) | L |
float | 浮點(diǎn)小數(shù),用4個(gè)字節(jié)表示 | [ ? 1.9999999 ? 2 127 , 1.9999999 ? 2 127 ] [-1.9999999 * 2^{127},1.9999999 * 2^{127}] [?1.9999999?2127,1.9999999?2127] | F |
double | 浮點(diǎn)小數(shù),用8個(gè)字節(jié)表示 | [ ? 1.9999999 ? 2 1023 , 1.9999999 ? 2 1023 ] [-1.9999999 * 2^{1023},1.9999999 * 2^{1023}] [?1.9999999?21023,1.9999999?21023] | D |
-
[]
包含等于,()
不包含等于 -
類型后綴
- 不區(qū)分大小寫,但建議用大寫,因?yàn)樾懙?L 與 1 容易混淆
- 尾符號(hào) D 可以省略
-
float 和 double 精度不同,即小數(shù)點(diǎn)后的位數(shù)
- float 的精度二進(jìn)制是 23,換算成十進(jìn)制是 6~7
- double 的精度二進(jìn)制是 52,換算成十進(jìn)制是 15~16
字符類型
類型名 | 說明 | 范圍 |
---|---|---|
char | 字符類型,配合單引號(hào) | [ 0 , 2 16 ) [0,2^{16}) [0,216) 即 [ 0 , 65536 ) [0, 65536) [0,65536) |
String | 字符串類型,配合雙引號(hào)或文本塊 | - |
3. 變量與運(yùn)算符
變量
變量的定義格式為
類型 變量名 = 值;
- 從語(yǔ)法可以看到,變量由類型和名稱組成,類型決定了變量能存儲(chǔ)的數(shù)據(jù)大小與數(shù)據(jù)格式,名字用來代表后面的值
- 這個(gè)語(yǔ)法其實(shí)咱們前面見過類似的,var scanner = new Scanner(System.in),這里 var 是類型,因?yàn)橹拔覀冞€沒有學(xué)習(xí)具體有哪些類型,因此使用了 var 來代表某種類型,scanner是變量名,后面這一串就是值,也是 Scanner 對(duì)象
= 稱之為賦值運(yùn)算符,可以用來更新變量代表的值。
例如:
int a = 10
這行代碼的意思是,定義了整型變量a,更新它的初始值為10
再來一句:
a = 20
這時(shí)候?yàn)樯恫挥脤懬懊娴念愋土四兀驗(yàn)樽兞慷x只需一次,定義好之后變量就可以反復(fù)使用了,這行代碼的意思是,將 a 所代表的值更新為 20
變量可以用來保存運(yùn)算的結(jié)果,它自身也能參與運(yùn)算
運(yùn)算符
int a = 5 + 3
結(jié)果為 8
int a = 5 - 3
結(jié)果為 2
int a = 5 * 3
結(jié)果為 15
int a = 5 / 3
結(jié)果為 1,整數(shù)除法有兩個(gè)注意點(diǎn)
- 整數(shù)除法,只會(huì)保留商,而小數(shù)部分會(huì)被舍棄,并不考慮四舍五入
- 除0是不合法的,會(huì)出錯(cuò)
int a = 5 % 3
結(jié)果為 2
小數(shù)加減乘除與整數(shù)類似,只是小數(shù)除法可以保留小數(shù)點(diǎn)后的數(shù)字,而且可以除零,例如
jshell> 5.0 / 3.0
$40 ==> 1.6666666666666667
jshell> 5.0 / 0.0
$41 ==> Infinity
增強(qiáng)賦值運(yùn)算符
int a = 20;
a = a + 10;
意思是,獲取 a 的原有值 20,執(zhí)行加 10 運(yùn)算,將新的結(jié)果重新賦值給 a,a 代表的值被更新成了 30。可以用 += 增強(qiáng)賦值運(yùn)算符達(dá)到類似的效果
a += 10;
先不要看 = 號(hào),獲取 a 的原有值 30,執(zhí)行加 10 運(yùn)算,運(yùn)算的結(jié)果 40 賦值給 a
a -= 10;
先拿 a 的原有值 40 與后面的 10 做減法,再將結(jié)果 30 賦值給 a
自增自減運(yùn)算符
兩個(gè)特殊運(yùn)算符 ++ 與 –
++ 是讓變量自增1,-- 是讓變量自減1,舉個(gè)例子
int a = 10;
a++;
結(jié)果為 11,++ 和 – 既可以寫在變量之前,也可以寫在變量之后,這兩種寫法的區(qū)別,我們到高級(jí)篇再講,目前暫不用去了解
4. 練習(xí) - 房貸計(jì)算器
【等額本息還款】法計(jì)算房貸
術(shù)語(yǔ)
等額本息是指一種貸款的還款方式,是在還款期內(nèi),每月償還同等數(shù)額的貸款(包括本金和利息)
每月償還的貸款可以通過下述公式計(jì)算
p ? r ? ( 1 + r ) m / ( ( 1 + r ) m ? 1 ) p * r * (1 + r)^m / ((1 + r)^m - 1) p?r?(1+r)m/((1+r)m?1)
- p 為貸款本金 principal
- r 為月利率 monthlyInterestRate
- m 為還款月數(shù) months
公式中這些都是什么意思呢
例如:貸款 200 萬(wàn)元 ,對(duì)應(yīng)公式中的 p,200 萬(wàn)就是貸款本金,年利率 6%,月利率 mr 就是 6% / 12 = 0.5%,假設(shè) 10 年還清,這時(shí)還款月數(shù)就是 360 個(gè)月。套入公式計(jì)算即可得到每月還多少錢
要完成這個(gè)計(jì)算,有一點(diǎn)沒學(xué)過的是求這里的 1+ mr 的 m 次方,計(jì)算它需要用一個(gè)求冪方法,這個(gè)方法是 jdk 核心類庫(kù)中 Math 這個(gè)類提供的,Math的字面意思是數(shù)學(xué),Math 類中提供了很多與數(shù)學(xué)計(jì)算相關(guān)的方法,如果你以后有這方面需求,就找它。
Math.pow()
pow 是 static 方法,語(yǔ)法為 類名.方法名(參數(shù)值)
,它需要兩個(gè)參數(shù),參數(shù)1是底數(shù),參數(shù)2是指數(shù)
jshell> Math.pow(2.0, 1)
$42 ==> 2.0
jshell> Math.pow(2.0, 2)
$43 ==> 4.0
jshell> Math.pow(2.0, 3)
$44 ==> 8.0
解答
打開 idea,編寫 Calculator 類
public class Calculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入貸款本金 p");
double p = scanner.nextDouble();
System.out.println("請(qǐng)輸入年利率 r%");
double yr = scanner.nextDouble();
double mr = yr / 100.0 / 12.0;
System.out.println("請(qǐng)輸入貸款月數(shù) m");
int m = scanner.nextInt();
System.out.println(p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1));
}
}
數(shù)字格式化
對(duì)結(jié)果的數(shù)字進(jìn)行格式化,讓它以貨幣的格式來顯示
需要借助核心類庫(kù)中一個(gè) NumberFormat 對(duì)象,字面意思是數(shù)字格式化,使用它的 getCurrencyInstance 方法來獲取一個(gè)貨幣格式化對(duì)象,再使用它的 format 方法把 double 小數(shù)格式化為貨幣格式,格式化時(shí)也會(huì)保留兩位小數(shù)
例子
System.out.println(NumberFormat.getCurrencyInstance(Locale.CHINA).format(1000000.00)); System.out.println(NumberFormat.getCurrencyInstance(Locale.US).format(1000000.00));
System.out.println(NumberFormat.getCurrencyInstance(Locale.GERMANY).format(1000000.00));
System.out.println(NumberFormat.getCurrencyInstance(Locale.KOREA).format(1000000.00));
輸出
¥1,000,000.00
$1,000,000.00
1.000.000,00 €
?1,000,000
如果 Locale 省略不寫,默認(rèn)為中國(guó)
房貸計(jì)算器可以改寫為
double payment = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1); System.out.println(NumberFormat.getCurrencyInstance().format(payment));
查閱 Javadoc
練習(xí)做完了,大家學(xué)到了什么呢?
有同學(xué)說,學(xué)會(huì)怎么算每月還款了,是嗎?并不是,通過這個(gè)例子,要認(rèn)識(shí)到 java 核心類庫(kù)能幫我們解決很多問題,比如說求冪運(yùn)算,比如說數(shù)字格式化,包括之前學(xué)過的 Scanner 都是核心類庫(kù)提供的功能,這就提醒我們,要解決某個(gè)編程問題之前,要先想一想核心類庫(kù)有沒有這方面的功能,如果有拿來用就行了,不必自己重新實(shí)現(xiàn)某項(xiàng)功能的代碼,我們是站在巨人的肩膀上進(jìn)行編程的。
當(dāng)然,同學(xué)們并不是一開始就知道核心類庫(kù)都提供了哪些功能,因此,這就需要我們不斷去熟悉核心類庫(kù),熟悉它都有哪些類,哪些方法,比較重要的功能都在后續(xù)的課程中都會(huì)陸續(xù)介紹到,當(dāng)然呢,同學(xué)們也不能總等著老師來喂知識(shí),也可以自己查閱 javadoc 來擴(kuò)展自己的知識(shí)面
javadoc 就是 java documentation 的縮寫,我們下載的 jdk 中已經(jīng)自帶了,無需額外再下載。那怎么查閱 javadoc 呢,如果大家用的是 idea,那么可以通過一些快捷鍵來查閱java文檔
-
比如想看看類的文檔,這時(shí)先按 Ctrl + N 查找類,假設(shè)我想看 Math 類的文檔,輸入要查閱的類名 Math,回車,可以跳轉(zhuǎn)到這個(gè)類
-
接下來我想看看方法的文檔怎么辦呢,按一下 ctrl + f12,列出當(dāng)前類的所有方法,綠色表示可以使用方法,橙色帶鎖的,表示是該類一種特殊的私有方法,不能直接使用。找感興趣的方法時(shí),如果你懂一些英文單詞,那么會(huì)有一定優(yōu)勢(shì),例如你想找一個(gè)平方根方法,它對(duì)應(yīng)的英文是 sqrt,這時(shí)敲入這幾個(gè)字母,就會(huì)定位到方法,同樣可以用翻譯查看該方法的功能
-
可以查到它的作用:返回一個(gè)數(shù)的平方根,這是方法名,查看后面括號(hào)內(nèi)可以得知,需要一個(gè)參數(shù),代表要求平方根的那個(gè)數(shù)字,是一個(gè)double 小數(shù),方法名稱前還有個(gè) double 表示它的結(jié)果類型也是一個(gè) double 小數(shù)
-
Math 中的方法大部分都是 static 方法,也就是配合類名使用的方法,之前也說過,用法為 類名.方法名(參數(shù))
在平時(shí)寫代碼時(shí),如果忘記了某個(gè)方法的作用,可以光標(biāo)定位到該方法,按 Ctrl + Q 進(jìn)行查閱,效果是類似的
三. 條件語(yǔ)句
編程時(shí)有一種重要的語(yǔ)句叫做條件語(yǔ)句,之前我們學(xué)過的都屬于順序語(yǔ)句,也就是從上至下,依次要執(zhí)行每一行代碼。
但是有的情況下,我們并不希望所有代碼都執(zhí)行,而是希望滿足條件的代碼才執(zhí)行
例如:要對(duì)用戶輸入數(shù)據(jù)的合法性進(jìn)行檢查:
- 貸款本金必須大于0
- 貸款月數(shù)范圍在 1 ~ 360 之間
- 年利率范圍在 1% ~ 36% 之間
如果你輸入的值連這些條件都不滿足,有必要去計(jì)算每月還款金額嗎?
這種情況下,就要用到條件語(yǔ)句了,它的語(yǔ)法格式為
if(條件) {
// 條件為 true 執(zhí)行這里
} else {
// 條件為 false 執(zhí)行這里
}
什么意思呢,if 本意是如果,如果條件成立,執(zhí)行代碼1,else 本意是否則,即條件不成立,執(zhí)行代碼2,其中 else { }
語(yǔ)句塊不是必須的,可以省略
那么條件這部分怎么寫呢?對(duì)于數(shù)字類型可以借助比較運(yùn)算符的運(yùn)算結(jié)果來充當(dāng)條件,參考下面的表格,這種表格列出了所有比較運(yùn)算符
比較運(yùn)算符 | 含義 | |
---|---|---|
a == b | 判斷 a 與 b 是否相等 | |
a > b | 判斷 a 是否 > b | |
a >= b | 判斷 a 是否 >= b | |
a < b | 判斷 a 是否 < b | |
a <= b | 判斷 a 是否 <= b | |
a != b | 判斷 a 與 b 是否不相等 |
1. boolean 類型
判斷的結(jié)果是布爾類型,可以充當(dāng)條件,它的取值非真即假,真用 true 表示,假用 false 表示
2. 單條件
jshell> int a = 1000;
a ==> 1000
jshell> if(a > 0){
...> System.out.println("ok");
...> } else {
...> System.out.println("必須>0");
...> }
ok
jshell> a = -1000;
a ==> -1000
jshell> if(a > 0){
...> System.out.println("ok");
...> } else {
...> System.out.println("必須>0");
...> }
必須>0
房貸計(jì)算器改寫如下
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入貸款本金 p");
double p = scanner.nextDouble();
if(p > 0) {
System.out.println("請(qǐng)輸入年利率 r%");
double yr = scanner.nextDouble();
double mr = yr / 100.0 / 12.0;
System.out.println("請(qǐng)輸入貸款月數(shù) m");
int m = scanner.nextInt();
double payment = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
System.out.println(NumberFormat.getCurrencyInstance().format(payment));
} else {
System.out.println("貸款金額必須大于0");
}
}
3. 多條件
剛才我們?cè)谂袛噘J款本金的時(shí)候,只需要有一個(gè)大于 0 的條件就可以了,但是接下來我們要去檢查年利率的時(shí)候,他是在一個(gè)范圍之間,這就必須有兩個(gè)條件,一個(gè)條件呢是要讓年利率大于等于 1%,第二個(gè)條件呢,是讓上年利率必須小于等于 36%,而且呢這兩個(gè)條件你必須同時(shí)成立
多個(gè)條件可以用邏輯運(yùn)算符連接
邏輯運(yùn)算符 | 含義 | |
---|---|---|
條件1 && 條件2 | && 意思是并且,兩個(gè)條件必須同時(shí)成立,結(jié)果為 true | |
條件1 || 條件2 | || 意思是或者,兩個(gè)條件有一個(gè)成立,結(jié)果為 true | |
! 條件 | ! 意思是取反 |
舉例
jshell> int b = 120;
b ==> 120
jshell> if(b >= 1 && b <= 360) {
...> System.out.println("ok");
...> } else {
...> System.out.println("必須在1~360之間");
...> }
ok
jshell> b = 0
b ==> 0
jshell> if(b >= 1 && b <= 360) {
...> System.out.println("ok");
...> } else {
...> System.out.println("必須在1~360之間");
...> }
必須在1~360之間
jshell> b = 361
b ==> 361
jshell> if(b >= 1 && b <= 360) {
...> System.out.println("ok");
...> } else {
...> System.out.println("必須在1~360之間");
...> }
必須在1~360之間
房貸計(jì)算器改寫如下
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入貸款本金 p");
double p = scanner.nextDouble();
if(p > 0.0) {
System.out.println("請(qǐng)輸入年利率 r%");
double yr = scanner.nextDouble();
if (yr >= 1.0 && yr <= 36.0) {
double mr = yr / 100.0 / 12.0;
System.out.println("請(qǐng)輸入貸款月數(shù) m");
int m = scanner.nextInt();
if (m >= 1 && m <= 360) {
double payment =
p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
System.out.println(NumberFormat.getCurrencyInstance()
.format(payment));
} else {
System.out.println("貸款月數(shù)范圍在 1 ~ 360 之間");
}
} else {
System.out.println("年利率范圍在 1% ~ 36% 之間");
}
} else {
System.out.println("貸款金額必須大于0");
}
}
4. 相反條件
我們這段代碼,邏輯上沒錯(cuò),但你會(huì)發(fā)現(xiàn)不容易閱讀
多層 if 嵌套導(dǎo)致代碼的可讀性變得很差,一旦大家寫代碼時(shí)出現(xiàn)了兩層以上的 if 語(yǔ)句,就要小心了。如何改進(jìn)呢?
這里給同學(xué)們介紹一種方法:可以去除 else 提高代碼可讀性
比如,現(xiàn)在有兩個(gè)分支,c 是一個(gè)條件,要么走分支1,要么走分支2,用下面的 if else 可以表示
if(c) {
// 分支1
} else {
// 分支2
}
等價(jià)于
if(!c) {
// 分支2
} else {
// 分支1
}
能不能不寫 else 呢?假設(shè)進(jìn)入了分支2,分支2的代碼執(zhí)行后,程序還會(huì)繼續(xù)向下執(zhí)行,導(dǎo)致分支1也被執(zhí)行
if(!c) {
// 分支2
}
// 分支1
可以改寫為下面的形式,這樣就避免了 else 出現(xiàn)
if(!c) {
// 分支2
return;
}
// 分支1
總結(jié)一下,以上代碼的等價(jià)轉(zhuǎn)換,有一句口訣:條件取反,if else 倒置,return 一加, else 可去
變換有一定的規(guī)律:
原條件 | 相反條件1 | 相反條件2 |
---|---|---|
p > 0.0 | !(p > 0.0) | p <= 0.0 |
yr >= 1.0 && yr <= 36.0 | !(yr >= 1.0 && yr <= 36.0) | yr < 1.0 || yr > 36.0 |
m < 1 || m > 360 | !(m < 1 || m > 360) | m >= 1 && m <= 360 |
-
用邏輯與連接的兩個(gè)條件,可以兩個(gè)條件分別取反,然后&&變||
- 年利率>=1.0 并且<=36.0 的相反條件是:年利率<1.0 或者 年利率>36.0
-
用邏輯或連接的兩個(gè)條件,可以兩個(gè)條件分別取反,然后||變&&
這種邏輯變換規(guī)律稱之為反演規(guī)則,公式記不住沒關(guān)系,簡(jiǎn)單的反演我們自己就能想出來,復(fù)雜的變換才用公式保證正確性
房貸計(jì)算器改寫如下
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入貸款本金 p");
double p = scanner.nextDouble();
if (p <= 0.0) {
System.out.println("貸款金額必須大于0");
return;
}
System.out.println("請(qǐng)輸入年利率 r%");
double yr = scanner.nextDouble();
if (yr < 1.0 || yr > 36.0) {
System.out.println("年利率范圍在 1% ~ 36% 之間");
return;
}
double mr = yr / 100.0 / 12.0;
System.out.println("請(qǐng)輸入貸款月數(shù) m");
int m = scanner.nextInt();
if (m < 1 || m > 360) {
System.out.println("貸款月數(shù)范圍在 1 ~ 360 之間");
return;
}
double payment = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
System.out.println(NumberFormat.getCurrencyInstance().format(payment));
}
四. 循環(huán)語(yǔ)句
1. 循環(huán)語(yǔ)句語(yǔ)法
for(初始化變量; 終止條件; 更新變量) {
// 循環(huán)體代碼
}
- 初始化部分僅執(zhí)行一次
- 執(zhí)行終止條件,如果為 true 繼續(xù)循環(huán),如果為 false 退出循環(huán)
- 執(zhí)行循環(huán)體代碼
- 執(zhí)行迭代部分,從 2 開始執(zhí)行
例如
for(int i = 0; i < 3; i++) {
System.out.println(i);
}
執(zhí)行流程如下
- 聲明一個(gè)變量 i,初始值為 0,用來控制循環(huán)次數(shù)
- 判斷 i < 3,此時(shí) i 取值是 0,條件為 true,繼續(xù)循環(huán)
- System.out.println(0)
- i++,自增為 1
- 判斷 i < 3,此時(shí) i 取值是 1,條件為 true,繼續(xù)循環(huán)
- System.out.println(1)
- i++,自增為 2
- 判斷 i < 3,此時(shí) i 取值是 2,條件為 true,繼續(xù)循環(huán)
- System.out.println(2)
- i++,自增為 3
- 判斷 i < 3,此時(shí) i 取值是 3,條件為 false,退出循環(huán)
2. 變量的作用范圍
上例中 i 的作用范圍,僅在循環(huán)語(yǔ)句的 {} 內(nèi)有效,現(xiàn)在要求求 1~100 的整數(shù)和,則需要把 sum 這個(gè)變量定義在 {} 外層
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println(sum);
這里能否這樣寫
for (int i = 1; i <= 100; i++) {
int sum = 0;
sum += i;
}
System.out.println(sum);
不行,變量有它們各自的作用范圍,從變量的定義開始,找到包圍它的,右 } 括號(hào)為止。
- 在它上面用它
- 在右括號(hào)外用它
3. 練習(xí) - 貸款計(jì)算詳情
現(xiàn)在需要計(jì)算每月償還的利息、償還的本金、剩余的本金
例如,借款 1000 元,利息 100%,兩月還清,根據(jù)公式計(jì)算出來每月還款 1333.33
月份 | 本月還款 | 償還本金 | 償還利息 | 剩余本金 |
---|---|---|---|---|
1 | 1333.33 | 333.33 | 1000.00 | 666.67 |
2 | 1333.33 | 666.67 | 666.67 | 0 |
總額 | 2666.67 | 1000.00 | 1666.67 |
可以看到
- 償還利息 = 剩余本金 ? 月利率 償還利息 = 剩余本金 * 月利率 償還利息=剩余本金?月利率
- 償還本金 = 每月還款 ? 償還利息 償還本金 = 每月還款 - 償還利息 償還本金=每月還款?償還利息
房貸計(jì)算器改寫如下
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入貸款本金 p");
double p = scanner.nextDouble();
if (p <= 0.0) {
System.out.println("貸款金額必須大于0");
return;
}
System.out.println("請(qǐng)輸入年利率 r%");
double yr = scanner.nextDouble();
if (yr < 1.0 || yr > 36.0) {
System.out.println("年利率范圍在 1% ~ 36% 之間");
return;
}
double mr = yr / 100.0 / 12.0;
System.out.println("請(qǐng)輸入貸款月數(shù) m");
int m = scanner.nextInt();
if (m < 1 || m > 360) {
System.out.println("貸款月數(shù)范圍在 1 ~ 360 之間");
return;
}
double payment = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
System.out.println(NumberFormat.getCurrencyInstance().format(payment));
double totalInterest = 0.0; // 總利息
for (int i = 1; i <= m; i++) {
double interest = p * mr; // 每月償還利息
double principal = payment - interest; // 每月償還本金
p -= principal; // 剩余本金
totalInterest += interest;
System.out.print(i);
System.out.print("\t本月還款:" +
NumberFormat.getCurrencyInstance().format(payment));
System.out.print("\t償還本金:" +
NumberFormat.getCurrencyInstance().format(principal));
System.out.print("\t償還利息:" +
NumberFormat.getCurrencyInstance().format(interest));
System.out.println("\t剩余本金:" + NumberFormat.getCurrencyInstance().format(p));
}
System.out.print("總還款額:" +
NumberFormat.getCurrencyInstance().format(payment * m));
System.out.println("\t總利息:" +
NumberFormat.getCurrencyInstance().format(totalInterest));
}
五. 方法
將來代碼多了,全部寫在 main 方法里,會(huì)顯得非常凌亂,難于閱讀。這節(jié)課的目標(biāo)是使用方法來改寫前面的代碼。
1. 定義方法
先來了解一下定義方法的語(yǔ)法
[訪問修飾符] [static] 返回結(jié)果類型 方法名([參數(shù)類型1 參數(shù)名1, 參數(shù)類型2 參數(shù)名2, ...]) {
// 方法體
return 返回結(jié)果
}
- 其中用 [] 的([] 不是語(yǔ)法的一部分)是可選部分
- 訪問修飾符先省略不寫,后面再講
- static 這里先寫上,這個(gè)前面提過,用 static 修飾的方法不屬于對(duì)象
- 方法執(zhí)行完畢后可能會(huì)有返回結(jié)果
- 如果沒有返回結(jié)果,返回值類型填寫 void
- 如果有返回結(jié)果,填寫實(shí)際的類型,例如
- 返回整數(shù),填寫 int
- 返回小數(shù),填寫 double
- 方法名隨便起,但應(yīng)當(dāng)做到望文生義
- 參數(shù)是方法執(zhí)行時(shí)需要傳入的數(shù)據(jù),可以有零到多個(gè),格式為【參數(shù)類型 參數(shù)名】,多個(gè)參數(shù)之間用逗號(hào)分隔
- {} 內(nèi)寫方法的具體代碼
- 最后 return 用來結(jié)束方法的運(yùn)行,如果方法有返回結(jié)果,也需要配合 return 把結(jié)果傳遞給調(diào)用者
例如,想計(jì)算一下兩個(gè)整數(shù)的和
class Test {
static int add(int a, int b) {
return a + b;
}
}
2. 調(diào)用方法
回憶一下 Math.pow(2.0, 2) 就是一個(gè)由 java 提供好的 static 方法,它怎么用呢,Math 是類名,pow 是方法名,括號(hào)內(nèi)是參數(shù),對(duì)于我們自己寫的 static 方法,用法是類似的:
類名.方法名([參數(shù)值1, 參數(shù)值2, ...])
即
Test.add(100,200);
怎么拿到返回結(jié)果呢?
int c = Test.add(100,200);
如果是調(diào)用本類 static 方法,可以省略前面的類名
3. 方法的意義
學(xué)完了方法的定義、調(diào)用流程,再來看看方法的意義
方法的一個(gè)意義在于隱藏實(shí)現(xiàn)細(xì)節(jié):
例如,對(duì)于前面例子中的【等額本息】方式計(jì)算房貸,如果沒有方法,那就要求編程者必須非常清楚計(jì)算公式
double payment = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
假設(shè)有一位資深程序員(例如你)提供了計(jì)算房貸方法,那么編程者就只需要知道:
計(jì)算等額本金還款,需要一個(gè)名字叫calculate的方法
它需要三個(gè)參數(shù),… ,至于具體的計(jì)算過程,被隱藏在了方法內(nèi)部
double payment = calculate(p, mr, m);
- p 貸款本金
- mr 月利率
- m 貸款月數(shù)
對(duì)于使用它的小白程序員來講,無需了解它的實(shí)現(xiàn)細(xì)節(jié),直接拿來用就可以了。小白程序員是站在你的肩膀上編程
方法的另一個(gè)意義在于減少重復(fù)代碼、提高代碼的可維護(hù)性:
對(duì)比以下代碼,第一段是沒用方法,如果有一處寫錯(cuò)了,所有重復(fù)的地方都得修改
double p1 = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
double p2 = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
double p3 = p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
用了方法,萬(wàn)一寫錯(cuò),只需要一個(gè)地方的代碼需要修改
static double calculate(double p, double mr, int m) {
return p * mr * Math.pow((1 + mr), m) / (Math.pow((1 + mr), m) - 1);
}
4. 練習(xí) - 貸款計(jì)算 - 方法改寫
public class Calculator3 {
public static void main(String[] args) {
double p = inputAndCheckP();
double mr = inputAndCheckMr();
int m = inputAndCheckM();
double payment = Calculator3.calculate(p, mr, m);
printDetails(p, mr, m, payment);
}
static double inputAndCheckP() {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入本金");
double p = scanner.nextDouble();
if(p <= 0) {
// System.out.println("貸款金額必須 > 0");
// throw new 異常類型("提示信息")
throw new IllegalArgumentException("貸款金額必須 > 0");
}
return p;
}
static double inputAndCheckMr() {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入年利率");
double yr = scanner.nextDouble();
if(yr < 1.0 || yr > 36.0) {
throw new IllegalArgumentException("年利率必須是 1 ~ 36");
}
return yr / 12.0 / 100;
}
static int inputAndCheckM() {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入還款月數(shù)");
int m = scanner.nextInt();
if(m < 1 || m > 360) {
throw new IllegalArgumentException("貸款月數(shù)必須是 1 ~ 360");
}
return m;
}
static void printDetails(double p, double mr, int m, double payment) {
for (int i = 0; i < m; i++) {
double payInterest = p * mr; // 償還利息
double payPrincipal = payment - payInterest; // 償還本金
p -= payPrincipal; // 剩余本金
System.out.print ("月份:" + (i + 1));
System.out.print("\t本月還款:" + NumberFormat.getCurrencyInstance().format(payment));
System.out.print("\t償還本金:" + NumberFormat.getCurrencyInstance().format(payPrincipal));
System.out.print("\t償還利息:" + NumberFormat.getCurrencyInstance().format(payInterest));
System.out.println("\t剩余本金:" + NumberFormat.getCurrencyInstance().format(p));
}
System.out.println("總還款額:" + NumberFormat.getCurrencyInstance().format(payment * m));
}
/**
* 以等額本息方式計(jì)算每月還款金額
* @param p 本金
* @param mr 月利率
* @param m 還款月數(shù)
* @return 每月還款金額
*/
static double calculate(double p, double mr, int m) {
double pow = Math.pow(1 + mr, m);
return p * mr * pow / (pow - 1);
}
}
說明1
大家抽取方法時(shí)有一個(gè)原則,就是把一組完整功能,所對(duì)應(yīng)的多行代碼抽取為一個(gè)方法,這里我們把計(jì)算還款總額和計(jì)算還款詳情,分別抽取了兩個(gè)方法
- calculate
- printDetails
抽取時(shí),要點(diǎn)如下
- 方法名要見文知義
- 返回值不著急寫,看看方法的外部需不需要用到這個(gè)方法內(nèi)的變量
- 需要,返回值定義為結(jié)果類型
- 不需要,返回值定義為 void
- 參數(shù)也不著急寫,看方法內(nèi)缺哪些變量定義,以它們?yōu)榉椒▍?shù)
說明2
對(duì)于 calculate 這種比較重要的方法定義,最好給它加一個(gè)文檔,你得告訴將來這個(gè)方法的使用者,怎么用這個(gè)方法,每個(gè)參數(shù)是什么意思。
先寫斜杠兩個(gè)星號(hào)的開始,不用著急寫它的結(jié)束,直接一回車。idea 就會(huì)自動(dòng)生成一段 javadoc 文檔,你可以在這里介紹方法的作用
- 在 @param 這里對(duì)每個(gè)參數(shù)進(jìn)行說明
- 在 @return 這里對(duì)返回值進(jìn)行說明。
說明3
如果在某些驗(yàn)證不通過,想讓剩余代碼不要運(yùn)行,可以利用 throw 語(yǔ)法
- return 這種語(yǔ)法叫正常返回,也就是當(dāng)方法調(diào)用后,代碼還會(huì)繼續(xù)運(yùn)行
- 還有就是這種throw 語(yǔ)法,它稱為異常返回,如果沒有額外處理,代碼從方法調(diào)用后就中斷運(yùn)行
六. package 與 jar
1. package
隨著我們寫的類越來越多,把他們都放在一塊兒來管理,感覺比較的不規(guī)范,因此,我們要引入一個(gè)新的package語(yǔ)法,對(duì)源文件進(jìn)行一個(gè)更好的管理。
其實(shí)這個(gè)package說白了就是Java中一種目錄結(jié)構(gòu)
|- 包1
|- 從屬于包1 的類
|- 包2
|- 從屬于包2 的類
語(yǔ)法:
package 包名; // 告訴下面的類從屬于此包
class 類 {
}
包的命名一般都是域名的倒置,如
- baidu.com 域名的倒置就是 com.baidu
- bilibilicom 域名的倒置就是 com.bilibili
2. import
與 package 關(guān)系非常密切的一個(gè)語(yǔ)法:import,如果你的類想使用另外一個(gè)類,而兩個(gè)類不同包,這時(shí)就必須用 import,把另一個(gè)類導(dǎo)入進(jìn)來才能使用
package com.itheima.a;
import java.util.Scanner;
class Calulator {
public static void main(String[] args) {
// 要用到 Scanner, 這時(shí)就用用到上面的 import 語(yǔ)句
}
}
- 有一種特殊情況不需要 import 導(dǎo)入,即 java.lang 包下的類使用時(shí),都不需要 import
3. jar
代碼寫完了,我們最終要發(fā)布成品吧,那是把源文件直接給使用者嗎?顯然不是吧。最終交給 jvm 運(yùn)行的是 class 類文件,我們會(huì)把這多個(gè) class 類文件打成一個(gè)壓縮包,交付給用戶。
用 idea 可以方便地打包
步驟1:創(chuàng)建工件(artifact)
步驟2:構(gòu)建工件文章來源:http://www.zghlxwxcb.cn/news/detail-797557.html
步驟3:運(yùn)行 jar 包,需要客戶電腦上安裝好 jdk文章來源地址http://www.zghlxwxcb.cn/news/detail-797557.html
java -jar jar包文件名
到了這里,關(guān)于基礎(chǔ)篇_開發(fā)命令行程序(輸入輸出,類型、變量、運(yùn)算符,條件語(yǔ)句,循環(huán)語(yǔ)句,方法,package與jar)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!