Java程序基本結(jié)構(gòu)
/**
* 可以用來(lái)自動(dòng)創(chuàng)建文檔的注釋
*/
public class Hello {
public static void main(String[] args) {
// 向屏幕輸出文本:
System.out.println("Hello, world!");
/* 多行注釋開(kāi)始
注釋內(nèi)容
注釋結(jié)束 */
}
} // class定義結(jié)束
-
class關(guān)鍵字用來(lái)創(chuàng)建類,類名必須以英文字母開(kāi)頭,習(xí)慣大寫,大駝峰命名。 -
public是訪問(wèn)修飾符,代表該class是公開(kāi)的 -
class內(nèi)部可以定義若干方法 -
main是方法名,void代表該方法沒(méi)有返回值,static是修飾符,表示該方法是靜態(tài)的,String[] args表示該方法的參數(shù)必須是String數(shù)組,args是入?yún)ⅲ椒ǖ拿?guī)范一般為小駝峰。 - 在方法內(nèi)部才是真正執(zhí)行的代碼,
java的每一行代碼必須以;結(jié)束。 -
System.out.println()是內(nèi)置的打印方法,結(jié)尾自帶換行,System.out.print()方法不換行顯示,\t可以添加若干空格。
數(shù)據(jù)類型和變量
- java的變量定義方式為
類型 變量名 = 值 - 在Java中,變量分為兩種:基本類型的變量和引用類型的變量。
-
基本數(shù)據(jù)類型
基本數(shù)據(jù)類型是CPU可以直接進(jìn)行運(yùn)算的類型。Java定義了以下幾種基本數(shù)據(jù)類型:
- 整數(shù)類型:
byte,short,int,long
| 類型 | 占用儲(chǔ)存空間 | 表數(shù)范圍 |
|---|---|---|
byte |
1 字節(jié)=8bit 位 |
-128~127 |
| short | 2 字節(jié) | -32768 ~ 32767 |
| int | 4 字節(jié) | -2147483648 ~ 2147483647 |
| long | 8 字節(jié) | -9223372036854775808 ~ 9223372036854775807 |
Java程序中變量通常聲明為int型,除非不足以表示較大的數(shù),才使用long
long n2 = 900L long型的結(jié)尾需要加L
- 浮點(diǎn)數(shù)類型:
float,double
| 類型 | 占用儲(chǔ)存空間 | 表數(shù)范圍 |
|---|---|---|
| 單精度 float | 4字節(jié) | -3.403E38~3.403E38 |
| 雙精度 double | 8 字節(jié) | -1.798E308~1.798E308 |
(1) float:單精度,尾數(shù)可以精確到7位有效數(shù)字。很多情況下,精度很難滿足需求。
(2) double:雙精度,精度是float的兩倍。通常采用此類型
(3) 對(duì)于float類型,需要加上f后綴。
- 字符類型:
char
- char 型數(shù)據(jù)用來(lái)表示通常意義上“字符”(一個(gè)字段=2字節(jié),相當(dāng)于16個(gè)bit)
- Java中的所有字符都使用Unicode編碼,故一個(gè)字符可以存儲(chǔ)一個(gè)字母,一個(gè)漢字,或其他書面語(yǔ)的一個(gè)字符
- 定義char變量,通常使用一對(duì) ' '(單引號(hào)),內(nèi)部只能寫一個(gè)字符
- 表達(dá)方式:(1)聲明一個(gè)字符;(2)轉(zhuǎn)義字符
- 直接使用Unicode值來(lái)表示字符型常量:"\uXXXX",其中,XXXX代表一個(gè)十六進(jìn)制整數(shù)。如:\u000a表示\n。
- 拓展(轉(zhuǎn)義字符):\b(退格符)、\n(換行符)、\r(回車符)、\t(制表符)、"(雙引號(hào))、'(單引號(hào))、\(反斜線)
- 布爾類型:
boolean
布爾類型boolean只有true和false兩個(gè)值
常量
定義變量的時(shí)候,如果加上final修飾符,這個(gè)變量就變成了常量:
final double PI = 3.14; // PI是一個(gè)常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!
- 常量在定義時(shí)進(jìn)行初始化后就不可再次賦值,再次賦值會(huì)導(dǎo)致編譯錯(cuò)誤。為了和變量區(qū)分開(kāi)來(lái),常量名通常全部大寫。
移位運(yùn)算
>>和 <<表示又位移和左位移,位移指的是會(huì)把十進(jìn)制數(shù)轉(zhuǎn)換成2進(jìn)制數(shù)進(jìn)行1的位移操作
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912
數(shù)組類型
- 數(shù)組定義:
類型[] 變量名 = new 類型[數(shù)量] - 數(shù)組所有元素初始化為默認(rèn)值,整型都是0,浮點(diǎn)型是0.0,布爾型是false;
- 數(shù)組一旦創(chuàng)建后,大小就不可改變。
條件判斷
-
>,<,>=,<=,==都可以用來(lái)作為判斷 - 浮點(diǎn)值不能直接用
==來(lái)判斷,正確的方法是利用差值小于某個(gè)臨界值來(lái)判斷:
// 條件判斷
public class Main {
public static void main(String[] args) {
double x = 1 - 9.0 / 10;
if (Math.abs(x - 0.1) < 0.00001) {
System.out.println("x is 0.1");
} else {
System.out.println("x is NOT 0.1");
}
}
}
- 判斷引用類型相等
在Java中,判斷值類型的變量是否相等,可以使用==運(yùn)算符。但是,判斷引用類型的變量是否相等,==表示“引用是否相等”,或者說(shuō),是否指向同一個(gè)對(duì)象。例如,下面的兩個(gè)String類型,它們的內(nèi)容是相同的,但是,分別指向不同的對(duì)象,用==判斷,結(jié)果為false:
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1);
System.out.println(s2);
if (s1 == s2) {
System.out.println("s1 == s2");
} else {
System.out.println("s1 != s2");
}
}
}
要判斷引用類型的變量?jī)?nèi)容是否相等,必須使用equals()方法:
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1);
System.out.println(s2);
if (s1.equals(s2)) {
System.out.println("s1 equals s2");
} else {
System.out.println("s1 not equals s2");
}
}
}
注意:執(zhí)行語(yǔ)句s1.equals(s2)時(shí),如果變量s1為null,會(huì)報(bào)NullPointerException
類
-
public定義公共變量, -
private定義私有變量 -
this始終指向當(dāng)前實(shí)例,如果沒(méi)有命名沖突可以省略
class Person {
private String name;
public String getName() {
return name; // 相當(dāng)于this.name
}
}
- 可變參數(shù)
類型...,可變參數(shù)相當(dāng)于數(shù)組類型:
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 傳入3個(gè)String
g.setNames("Xiao Ming", "Xiao Hong"); // 傳入2個(gè)String
g.setNames("Xiao Ming"); // 傳入1個(gè)String
g.setNames(); // 傳入0個(gè)String
可變參數(shù)改寫為String[]類型
但是,調(diào)用方需要自己先構(gòu)造String[],比較麻煩
另一個(gè)問(wèn)題是,調(diào)用方可以傳入null:
而可變參數(shù)可以保證無(wú)法傳入null,因?yàn)閭魅?個(gè)參數(shù)時(shí),接收到的實(shí)際值是一個(gè)空數(shù)組而不是null。
- 實(shí)例在創(chuàng)建時(shí)通過(guò)new操作符會(huì)調(diào)用其對(duì)應(yīng)的構(gòu)造方法,構(gòu)造方法用于初始化實(shí)例;
沒(méi)有定義構(gòu)造方法時(shí),編譯器會(huì)自動(dòng)創(chuàng)建一個(gè)默認(rèn)的無(wú)參數(shù)構(gòu)造方法;
可以定義多個(gè)構(gòu)造方法,編譯器根據(jù)參數(shù)自動(dòng)判斷;
可以在一個(gè)構(gòu)造方法內(nèi)部調(diào)用另一個(gè)構(gòu)造方法,便于代碼復(fù)用。 -
方法重載,在一個(gè)類中,我們可以定義多個(gè)方法。如果有一系列方法,它們的功能都是類似的,只有參數(shù)有所不同,那么,可以把這一組方法名做成同名方法。這種方法名相同,但各自的參數(shù)不同,稱為方法重載(Overload)。
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
注意:方法重載的返回值類型通常都是相同的。
繼承
- java使用
extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承 - 繼承是面向?qū)ο缶幊讨蟹浅?qiáng)大的一種機(jī)制,它首先可以復(fù)用代碼。當(dāng)我們讓Student從Person繼承時(shí),Student就獲得了Person的所有功能,我們只需要為Student編寫新增的功能。
class Person {
private String name;
private int age;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}
class Student extends Person {
// 不要重復(fù)name和age字段/方法,
// 只需要定義新增score字段/方法:
private int score;
public int getScore() { … }
public void setScore(int score) { … }
}
注意:子類自動(dòng)獲得了父類的所有字段,嚴(yán)禁定義與父類重名的字段!
- Java只允許一個(gè)class繼承自一個(gè)類,因此,一個(gè)類有且僅有一個(gè)父類。只有Object特殊,它沒(méi)有父類。
- 在Java中,沒(méi)有明確寫extends的類,編譯器會(huì)自動(dòng)加上extends Object。所以,任何類,除了Object,都會(huì)繼承自某個(gè)類。
protected
- 繼承有個(gè)特點(diǎn),就是子類無(wú)法訪問(wèn)父類的private字段或者private方法。例如,Student類就無(wú)法訪問(wèn)Person類的name和age字段:
class Person {
private String name;
private int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // 編譯錯(cuò)誤:無(wú)法訪問(wèn)name字段
}
}
這使得繼承的作用被削弱了。為了讓子類可以訪問(wèn)父類的字段,我們需要把private改為protected。用protected修飾的字段可以被子類訪問(wèn):
class Person {
protected String name;
protected int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // OK!
}
}
因此,protected關(guān)鍵字可以把字段和方法的訪問(wèn)權(quán)限控制在繼承樹(shù)內(nèi)部,一個(gè)protected字段和方法可以被其子類,以及子類的子類所訪問(wèn)
super
-
super關(guān)鍵字表示父類(超類)。子類引用父類的字段時(shí),可以用super.fieldName - 如果父類沒(méi)有默認(rèn)的構(gòu)造方法,子類就必須顯式調(diào)用super()并給出參數(shù)以便讓編譯器定位到父類的一個(gè)合適的構(gòu)造方法。
這里還順帶引出了另一個(gè)問(wèn)題:即子類不會(huì)繼承任何父類的構(gòu)造方法。子類默認(rèn)的構(gòu)造方法是編譯器自動(dòng)生成的,不是繼承的。
阻止繼承
- 正常情況下,只要某個(gè)
class沒(méi)有final修飾符,那么任何類都可以從該class繼承。
從Java 15開(kāi)始,允許使用sealed修飾class,并通過(guò)permits明確寫出能夠從該class繼承的子類名稱。
public sealed class Shape permits Rect, Circle, Triangle {
...
}
上述Shape類就是一個(gè)sealed類,它只允許指定的3個(gè)類繼承它
多態(tài)
多態(tài)是指,針對(duì)某個(gè)類型的方法調(diào)用,其真正執(zhí)行的方法取決于運(yùn)行時(shí)期實(shí)際類型的方法。- 在繼承關(guān)系中,子類如果定義了一個(gè)與父類方法簽名完全相同的方法,被稱為覆寫(Override)。
- 加上@Override可以讓編譯器幫助檢查是否進(jìn)行了正確的覆寫。希望進(jìn)行覆寫,但是不小心寫錯(cuò)了方法簽名,編譯器會(huì)報(bào)錯(cuò)。
抽象類
-
abstract關(guān)鍵字可以定義抽象類和抽象方法
abstract class Person {
public abstract void run();
}
抽象類是無(wú)法被實(shí)例化的,抽象方法也無(wú)法執(zhí)行
接口
-
interface用來(lái)聲明接口 - 接口是比抽象類還要抽象的純抽象接口,如果一個(gè)抽象類沒(méi)有字段全是抽象方法,就可以寫成接口