一、Java
Java誕生於1995年,原屬於SUN公司,2009年4月20日,美國數據軟件巨頭甲骨文公司(Oracle)宣佈以74億美元收購SUN公司。Java是最受歡迎的開發語言,已經火了20年,並將繼續引領着IT的編程語言。Java的LOGO是一杯熱氣騰騰的咖啡,真的是令人回味無窮
1.1 為什麼Java是平台無關性(可以跨平台)
傳統語言
Java語言
我們可以對Java程序進行編譯操作,編譯後生成一種和平台系統無關的文件——字節碼文件(.class)。但是此時Windows、Linux均不能執行字節碼文件,只有Java虛擬機(JVM)可以識別字節碼文件,那麼為了在Windows系統上運行該Java程序,我們就只能在Windows平台上安裝Windows版本的JVM,如果要在Mac系統上運行,那麼得安裝Mac版本的JVM,總結來説就兩點:
- Java編譯後生成和平台無關的.class文件
- jvm是平台相關的
在這裏進行編譯操作的工具叫做javac,啓動JVM並把字節碼加載進JVM的工具叫做java
二、Java環境
2.1、JRE
JRE(Java Runtime Environment):Java運行環境,如果要運行Java程序,就需要JRE的支持,JRE裏包含JVM,一般在只運行程序而不開發程序的服務器中安裝
2.2、JDK
JDK(Java Development Kit):Java開發工具,包含開發Java程序的所有工具如javac和java等,JDK包含JRE,如果已經安裝了JDK就不必安裝JRE
2.3、JVM
JVM(Java Virtual Machine):Java虛擬機,它是運行所有Java程序的虛擬計算機。JVM是不跨平台的,在Windows下裝Windows版的JVM,在Linux下裝Linux版的JVM
三、Java編譯和運行的機制
- 編寫源文件(Java文件)
- 使用Java c工具對源文件進行編譯操作(java c 源文件.java),生成.class文件
- 生成字節碼文件(.class文件)之後,使用Java工具啓動JVM,運行程序(java 主方法的類名)
四、Java基礎
4.1、語法規則
- Java語言嚴格區分大小寫,大寫和小寫是不同的概念
- 一個Java源文件裏可以定義多個Java類,但其中最多隻能有一個類被定義為public類,但如果源文件中包含了public類,源文件必須和public類同名
- 一個源文件中包含了N個Java類時,成功編譯後會生成N份字節碼文件,每一個類都會生成單獨的.class文件,且字節碼的文件名和其對應的類名相同
- 若一個類想要運行,則必須擁有主方法(main),因為main方法是程序的入口
4.2、註釋
Java提供3種註釋類型:
- 單行註釋
//這是單行註釋
//快捷鍵:Ctrl+/
- 多行註釋
/*
我是多行註釋
快捷鍵為:打/* 再按回車
*/
- 文檔註釋
/*
*
* 我是文檔類型
* 快捷鍵為:打/*然後按幾下tab鍵
*/
多行註釋之間彼此都不能交叉嵌套,因為/會找舉例自己最近的/符號,組成一個註釋語句塊,上圖中單獨的*/符號就不能被編譯器識別了
4.3、關鍵字和保留字
關鍵字
關鍵字:在編程語言中事先定義的,有着特殊含義和用途的單詞
保留字
保留字:和關鍵字一樣是編程語言事先定義好的,只是説現在暫時沒有特殊的功能,但説不定以後某天會突然被賦予功能,因此被保留下來的單詞。比如goto和const就是保留字
4.4、分隔符和標識符
4.4.1、分隔符
- 分號(;):語句的分割,表示一句話結束,好比咱們使用的句號。
- 花括號({}):表示一個代碼塊,是一個整體,花括號要成對使用。
- 方括號([]):定義數組和訪問數組元素時使用。
- 圓括號(()):使用很廣泛,具體用到細講。
- 圓點(.):類和對象訪問它的成員時使用。
- 空格( ):把一整條語句分割成幾段,空格的次數不限制,好比一句英文裏單詞都要分開寫一樣。
4.4.2、標識符
在寫代碼的時候為了增強代碼的閲讀性,我們會自定義很多名字如:類名、方法名、變量名等。在編程的裏,我們把這種為了增強程序閲讀性而自定義的名稱,稱為標識符
標識符的命名規則:
- 由字母、數字、下劃線、$組成,但不能以數字開頭(注:此處的字母可以是中文、日文等)
- 大小寫敏感
- 不能使用Java中的關鍵字和保留字
- 不能用Java中內置的類名
4.5、數據類型
注意:Java只有8大數據類型,String不屬於基本數據類型,他屬於引用數據類型
常用的整數類型是int和long,byte和short基本不用,常用的小數類型是double,float基本不用。因為double是不精確的,在實際開發中表示精確的小數我們使用BigDecimal類
- 整數類型默認是int類型,小數類型默認是double類型
- 表示long類型常量,要加L或者l,建議加L
- 表示float類型常量,要加F或者f,建議加F
五、變量
變量是內存中一塊區域,可以往該區域存儲數據,修改裏面的數據,也可以獲取裏面的數據,一個變量如果沒有被初始化,就意味着沒有分配內存空間來存儲,就不能使用
定義變量的語法格式如下:
- String,表示類型,這裏可以寫任何的類型
- name:變量名,和我們的姓名一樣理解, 沒有為什麼
- =:賦值運算符,後面會講,意思是將右邊的值賦值給左邊的變量
- “xiaolin”:一個字符串類型的值,如果是其他類型,不要加引號
變量的幾個特點:
- 佔據着內存中某一塊存儲區域
- 該區域有自己的變量名和數據類型
- 可以被重複使用
- 該區域的變量值可以在同一個類型的範圍內不斷地變化
5.1、變量的定義以及賦值
public class VarDemo{
public static void main(String[] args) {
// 方式一,先定義變量,再賦值
// 數據類型 變量名;如:int age;
// 變量名 = 常量值;
// 定義一個int類型變量,初始值為17
int name;
//修改age變量的值為17
age = xiaolin;
System.out.println(age);
// 方式二,在聲明時同時賦值(推薦)
// 數據類型 變量名 = 初始化值;
// 定義一個String類型的變量,初始值為zs
String name = "zs";
}
}
變量的使用注意如下幾點:
- 變量必須是先聲明再使用,並且初始化後才可以使用(定義包裝類除外)
- 定義變量必須有數據類型
- 變量從開始定義到所在的作用域內可以使用,出了作用域之外就不可以使用了,且在同一作用域內,變量名不可以重複
常見的幾種變量類型的定義:
public class VarDemo{
public static void main(String[] args) {
//byte類型變量
byte b = 20;
System.out.println(b);
//short類型變量
short s = 20;
System.out.println(s);
//int類型變量
int i = 20;
System.out.println(i);
//long類型變量,使用L後綴
long l = 20L;
System.out.println(l);
//float類型變量,使用F後綴
float f = 3.14F;
System.out.println(f);
//double類型變量
double d = 3.14;
System.out.println(d);
//char類型變量
char c = 'A';
System.out.println(c);
//boolean類型變量
boolean bb = true;
System.out.println(bb);
//String類型變量
String str = "你好";
System.out.println(str);
}
}
5.2、交換兩個變量值
思路
- 把num1的值存儲到臨時變量temp中去
- 把num2的值賦給num1變量
- 把temp存儲的值賦給num2變量
實現代碼
public class ChangVarDemo{
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
System.out.println("num1=" + num1);
System.out.println("num2=" + num2);
//--------------------------------
//交互操作
int temp = num1;
num1 = num2;
num2 = temp;
//--------------------------------
System.out.println("num1=" + num1);
System.out.println("num2=" + num2);
}
}
六、表達式
表達式(expression),是由數字、運算符、括號、常量、變量等以能求得結果的組合
七、數據類型的轉換
在8大基本數據類型中,boolean不屬於數值類型,所以不參與轉換,其他類型的轉換規則如下圖。一般的,byte、short、char三種類型相互之間一般不參與轉換操作,按照轉換方式,有兩種(注意:boolean類型不參與類型轉換):
- 自動類型轉換:範圍小的數據類型直接轉換成範圍大的數據類型,小->大
- 強制類型轉換:範圍大的數據類型強制轉換成範圍小的數據類型,大->小
7.1、自動類型轉換與提升
7.1.1、自動類型轉換
自動類型轉換,也稱為“隱式類型轉換,就是把範圍小的數據類型直接轉換成範圍大的數據類型
轉換規則:byte、short、char—>int—>long—>float—>double
注意事項:byte、short、char相互之間不轉換,他們參與運算首先轉換為int類型
語法格式:範圍大的數據類型 變量 = 範圍小的數據類型值
public class TypeConvertDemo1{
public static void main(String[] args) {
//把int類型變量轉為long類型
long longNumber = 17;//整數默認為int類型
//把long類型轉換為float類型
float f1 = longNumber;
//把float類型轉換為double類型
double d = f1;
//定義兩個int類型的變量
int a1 = 2;
int b1 = 3;
int c1 = a1 + b1;
//定義一個byte類型,一個int類型
byte b2 = 2;
int c2 = 3;
System.out.println(b2 + c2);
//byte、short、char類型參與運算時把自己提升為int類型
//byte d1 = b2 + c2;//編譯報錯
int d3 = b2 + c2;//編譯通過
}
}
7.1.2、自動類型提升
當一個算術表達式中,包含多個基本數據類型的常量或變量(boolean除外)時,整個算術表達式的結果類型將出現自動提升,其規則是:
- 所有的byte、short、char類型被自動提升到int類型,再參與運算
- 整個表達式的最終結果類型,被提升到表達式中類型最高的類型
System.out.println('a' + 1);//98
byte b = 22;
b = b + 11;//編譯出錯,此時結果類型應該是int
double d1 = 123 + 1.1F + 3.14 + 99L ;
結論:算數表達式結果的類型就是其中範圍最大的數據類型。
7.2、強制類型轉換
強制類型轉換,也稱為“顯式類型轉換”,就是把範圍大的數據類型強制轉換成範圍小的數據類型
# 語法格式:
# 範圍小的數據類型 變量 = (範圍小的數據類型)範圍大的數據類型值;
注意:一般情況下不建議使用強轉,因為強轉有可能損失精度
public class TypeConvertDemo2{
public static void main(String[] args) {
int a = 2;
byte b = 3;
//自動類型轉換
int c = a + b;
//強制類型轉換
byte d = (byte) (a + b);
//把double轉換為int類型
int i = (int)3.14;
System.out.println(i);//3
}
}
八、運算符
對常量和變量進行操作的符號稱為運算符
常見的運算符分為:算術運算符、賦值運算符、比較運算符、邏輯運算符、三元運算符
8.1、算數運算符
| 運算符 | 運算規則1 | 示例 | 結果 |
|---|---|---|---|
| + | 正號 | +3 | 3 |
| + | 加 | 52+3 | 5 |
| + | 拼接字符串 | "中"+"國" | “中國” |
| - | 符號 | int a = 3
-a |
-3 |
| - | 減 | 3-1 | 2 |
| * | 乘 | 2*3 | 6 |
| / | 除 | 5/2 | 2 |
| % | 取模 | 5%2 | 1 |
| ++ | 自增 | int a = 1<br/>a++(++a) | 2 |
| -- | 自減 | int b =2 <br/>b--(--b) | 1 |
8.2、自增和自減
自增:++,遞增操作符,使變量值增加1,有前置和後置之分,只能操作變量。
自減:-- ,遞減操作符,使變量值減去1,有前置和後置之分,只能操作變量。
以++為例:
a++和++a結果都是讓a的值自增1,如果只是需要自增的話,使用哪一種都可以
但是他們倆有唯一的區別:
- 前置(++a): 表示對a加1之後的結果進行運算,先加後用
- 後置(a++):表示對a變量加1之前的值(原始值)進行運算,先用後加
public class ArithmeticOperatorsDemo2{
public static void main(String[] args) {
int a1 = 5;
int b1 = ++ a1;
System.out.println("a1=" + a1 + ",b1=" + b1);//a1=6,b1=6
int a2 = 5;
int b2 = a2 ++;
System.out.println("a2=" + a2 + ",b2=" + b2);//a2=6,b2=5
}
}
比較底層的解釋
++a表示取a的地址,增加它的內容,然後把值放在寄存器中
a++表示取a的地址,把它的值裝入寄存器,然後增加內存中的a的值
8.3、賦值運算符
| 運算符 | 運算規則 | 示例 | 結果 |
|---|---|---|---|
| == | 是否相等於 | 4==3 | false |
| != | 是否不等於 | !4=3 | true |
| < | 小於 | 4<3 | false |
| > | 大於 | 4>3 | true |
| <= | 小於等於 | 4<=3 | false |
| >= | 大於等於 | 4>=3 | true |
8.4、三元運算符
三元運算符,表示有三個元素參與的表達式,所以又稱為三目運算符,其語義表示if-else(如果什麼情況就做什麼,否則做什麼)
語法格式:數據類型 變量 = boolean表達式 ? 結果A :結果B
表達的意思,boolean表達式結果:
- 為true,則三元運算符的結果是結果A
- 為false,則三元運算符的結果是結果B
注意:
- 三元運算符必須定義變量接受運算的結果,否則報錯
- 三元運算符結果的類型由結果A和結果B來決定的,結果A和結果B的類型是相同的
8.5、邏輯運算符
邏輯運算符用於連接兩個boolean表達式,結果也是boolean類型的
| 運算符 | 運算規則 | 示範 | 結果 | ||||
|---|---|---|---|---|---|---|---|
| & | 與 | false & true | false | ||||
| \ | 或 | false \ | true | true | |||
| ^ | 異或 | true ^ false | true | ||||
| ! | 非 | !true | false | ||||
| && | 短路與 | false && false | false | ||||
| \ | \ | 短路或 | false \ | \ | false | true |
規律:
- 非:取反,!true則false,!false則true
- 與:有false則false
- 或:有true則true
- 異或:^ 相同則false,不同則true
8.5.1、&和&&( | 和 | |)的區別
& :&左邊表達式無論真假,&右邊表達式都進行運算
&& :如果&&左邊表達式為真,&&右邊表達式參與運算,否則&&右邊表達式不參與運算,故稱短路與
| 和 || 的區別同理,||,左邊為真,右邊不參與運算
public class LogicalOperatorDemo2 {
public static void main(String[] args) {
System.out.println(false & 1 / 0 == 1);//報錯,説明右邊執行了
System.out.println(false && 1 / 0 == 1);//不報錯,説明右邊沒有執行
System.out.println(true | 1 / 0 == 1);//報錯,説明右邊執行了
System.out.println(true | 1 / 0 == 1);//不報錯,説明右邊沒有執行
}
}
九、數組
9.1、JVM初探究
- 程序計數器:當前線程所執行的字節碼的行號的指示器
- 本地方法棧:為虛擬機使用的native方法服務
- 方法區:線程共享的內存區域,存儲已經被虛擬機加載的類的信息、常量、靜態變量,這個區域的內存回收的目標主要是針對常量池的回收和對類型的卸載
- Java虛擬機棧:簡稱棧,每個方法被執行的同時會創建一個棧幀用於存儲該方法的局部變量、操作棧、動態鏈接、方法出口等信息
- Java堆:被所有線程共享的一塊內存區域,在虛擬機創建的時候啓動,
所有對象的實例(new出來的對象)以及數組都要在堆上分配內存空間,所以堆佔的內存空間遠比棧大 - 每當調用一個方法時,創建一個棧幀,存放了當前方法的局部變量,當方法調用完畢時,該方法的棧幀就被銷燬了
- 每次new一個對象的時候,就表示在內存中開闢了一塊新的存儲空間
9.2、數組
9.2.1、什麼是數組
具有相同類型的多個常量值有序組織起來的一種數據形式,數組中使用索引來表示元素存放的位置,索引從0開始,步長是1,有點像Excel表格的行號
9.2.2、定義語法
數組元素類型[] 數組名;
int [] nums;
注意:
- 可以把int[] 看成一種數據類型,int類型的數組類型
- int[]數組表示,這個數組中的元素都是int類型的,同理
9.2.3、數組的初始化
數組在定義後,必須初始化才能使用。所謂初始化,就是在堆內存中給數組分配存儲空間,併為每一個
元素賦上初始值,有兩種方式:靜態初始化和動態初始化,數組的長度是固定的,無論以哪種,一旦初始化完成,數組的長度(元素的個數)就固定了,不能改變,除非重新對該初始化,初始化時,如果我們明確了具體的元素就用靜態初始化,如果還不知道具體放哪些元素,只知道個數,用動態初始化
9.2.3.1、靜態初始化
我們直接為每一個數組元素設置初始化值,而數組的長度由系統(JVM)決定
語法:數組元素類型[] 數組名 = new 數組元素類型[]{元素1,元素2,元素3,.......};
int[] nums = new int[]{1,3,5,7,9};
//簡單寫法:
int[] nums = {1,3,5,7,9};//簡單寫法,定義和初始化必須同時寫出來
9.2.3.2、靜態初始化內存分析
public class ArrayDemo1{
public static void main(String[] args) {
//定義並初始化數組
int[] nums = new int[] { 1, 3, 5, 7 };
System.out.println("數組長度=" + nums.length);
//重新初始化數組
nums = new int[] { 2, 4, 8 };
System.out.println("數組長度=" + nums.length);
}
}
若 num = null,則null表示不再引用堆中的內存空間,那麼此時nums就好比是沒有初始化的,不能使用
9.2.3.4、動態初始化
程序員只設置數組元素個數,而數組的元素的初始值由系統(JVM)決定
語法:數組元素類型[] 數組名 = new 數組元素類型[length];
int[] nums = new int[5];
注意:不能同時指定元素值和數組長度,int[] nums = new int[5]{1,3,5,7,9} 是錯誤的
內存圖與靜態初始化一樣,只是擁有默認值
9.2.4、數組中對元素的操作
9.2.4.1、獲取元素的個數
int size = 數組名.length;
9.2.4.2、設置元素
nums[1] = 30;
9.2.4.3、獲取元素
元素類型 變量名 = 數組名[index];
9.2.5、數組中常見的異常
- NullPointerException:空指針異常(空引用異常)
操作了一個尚未初始化或者沒有分配內存空間的數組
- ArrayIndexOutOfBoundsException:數組的索引越界異常
操作的數組的索引不在[0,數組名.length-1]範圍內
9.2.6、數組遍歷
9.2.6.1、for循環
int[] nums = new int[] { 1, 3, 5, 7 };
for (int index = 0; index < nums.length; index++) {
int ele = nums[index];//index依次是 0、1、2、3
System.out.println(ele);
}
9.2.6.2、for-each(增強for循環)
for(數組元素類型 變量: 數組){
//TODO
}
int[] nums = new int[] { 1, 3, 5, 7 };
for (int ele : nums) {
System.out.println(ele);
}
使用for-each操作數組更簡單,因為可以不關心索引,其底層原理依然是上述的for循環操作數組
9.2.7、二維數組
在之前,數組的每一個元素就是一個個的值,這種數組我們稱之為一維數組。二維數組,就是數組中的每一個元素是另一個一維數組
9.2.7.1、二維數組的定義和初始化
靜態
public class ArrayInArrayDemo1 {
public static void main(String[] args) {
//定義三個一維數組
int[] arr1 = { 1, 2, 3 };
int[] arr2 = { 4, 5 };
int[] arr3 = { 6 };
//把三個一維數組存儲到另一個數組中,那麼該數組就是二維數組
int[][] arr = new int[][] { arr1, arr2, arr3 };
}
}
二維數組中的元素類型是一維數組,把數組元素類型[]看成一個整體,表示數據類型
動態
數組元素類型[][] 數組名 = new 數組元素類型[x][y];
x表示二維數組中有幾個一維數組
y表示每一個一維數組中有幾個元素。
int[][] arr = new int[3][5];
9.2.7.2、獲取二維數組的元素
for循環
for (int index = 0; index < arr.length; index++) {
//取出每一個一維數組
int[] arr2= arr[index];
//迭代一維數組
for (int j = 0; j < arr2.length; j++) {
int ele = arr2[j];
System.out.println(ele);
}
System.out.println("-----");
}
for-each
for (int[] arr2 : arr) {
//arr2為每次遍歷出來的一維數組
for (int ele : arr2) {
//ele為從arr2一維數組中遍歷出來的元素
System.out.println(ele);
}
System.out.println("-----");
}
十、方法
10.1、方法的定義
方法:為了完成某一特定功能(如:求和,統計數量等)的代碼塊
語法格式:
修飾符] 返回值類型 方法名稱(參數類型 參數名1,參數類型 參數名2,…)
{
方法體;
[return 返回值;]
}
格式分析:
- 修飾符:public、static等,static修飾的方法直接使用類名調用即可,用static修飾的方法都屬於類的
-
返回值類型:限定返回值的類型,方法在完成一個功能後,是否需要給調用者返回一個結果?
- 如果需要給調用者返回結果,就寫上返回數據的類型
- 如果不需要給調用者返回結果,就用關鍵字
void,表示沒有返回結果
- 方法名稱:用於調用方法,遵循標識符規範,首字母小寫,採用駝峯命名法,見名知意
- 形式參數:方法中圓括號中的變量,可以有多個形式參數
- 方法體:編寫如何完成該功能的代碼
-
return關鍵字的作用
- 把返回值給方法的調用者
- 結束該方法,在return後不可以再學任何語句
- 當方法體中沒有return時,方法的返回值類型必須為void
- 實際參數:在調用某一個具體方法1時,實際傳遞的參數值
- 如果一個需要返回值,那麼一定要保證在任何條件下都必須得有返回值
注意事項
- 方法必須定義到類中,在java中最小的程序單元是類
- 一個類中可以定義多個方法
- 方法和方法之間是平行的,不能在一個方法中定義另一個方法(相當於不可以在一個房子裏面建房子)
- 方法定義沒有先後順序
10.2、方法的調用
如果方法有static修飾,可以直接用方法所在的類的類名調用,如果沒有static修飾,那麼必須使用實例化對象來調用
10.3、方法的重載
參數列表:參數的類型+參數的個數+參數的順序
方法簽名:方法名稱+方法的參數列表
在同一個類中,方法簽名是唯一的,否則編譯報錯
方法的重載:在同一個類中,允許某方法存在一個或者多個同名的方法,但是必須參數列表不同
10.3.1、如何是否是方法重載
方法重載判斷的原則:兩同一不同
兩同:在同一個類中,方法名是相同的
一不同:方法的參數列表不同(參數類型、參數個數、參數順序),只要參數類型、參數個數、參數順序其中有一個不同,都成為參數列表不同
方法的重載和返回值類型無關,只是一般都要求返回值類型相同
10.3.2、方法重載的作用
解決了同一功能的方法由於參數不同所造成的方法名稱不同而需要重複命名的問題