www, 萬維網, 全部信息用連接鏈接起來html
Java, 靜態網頁 -> 動態網頁java
JDK, Java Development Kit, Java開發工具包git
1995, JDK 1.0 1998, JDK 1.2, Java2 2000, JDK 1.3 2002, JDK 1.4, assert, logging, re 2004, JDK 1.5, 語法增長 2006, JDK 1.6, 普遍, Compiler API(動態編譯), 腳本語言支持, WebService支持 2010, Oracle併購SUN 2011, JDK 1.7, 帶資源的try, 重拋異常 2014, JDK 1.8, 大改進, lambda表達式 注: 從 JDK 1.5 以後, JDK 1.x 也被稱爲 JDK x, 如 JDK 1.8 也被叫作 Java 8
JCP, Java Community Process, 社區程序員
JSR, Java Specification Requests, 規範segmentfault
與 C++ 的區別api
編寫 Main.java數組
package javanote; public class Main{ public static void main(String args[]) { System.out.println("hello world"); } }
進入新建文件夾 ./javanote
, 而後新建源程序文件 Main.java
, 注意文件名和 public class
後面的類名一致安全
public class Main { // 注意 String[] args 不能省略 public static void main(String[] args){ System.out.println("hello world"); } }
編譯, 將會獲得 Main.class
目標文件(obj), 字節碼 bytecode
, 擴展名 class
.它不是實際機器的最終執行代碼bash
# c 表明 compiler $ javac Main.java $ ls Main.class Main.java
運行網絡
# 注意不是 java Main.class java Main
經過 JVM
讀取並處理 class
文件, 最終轉化成 CPU
的指令. JVM for Win/Unix/
模擬了一個操做系統/接口
源程序(.java
後綴) ---javac--- 字節碼(bytecode, .class
後綴) ---java--- 在 JVM 上運行
JVM 規定了虛擬的CPU和內存, 包含如下內容: 寄存器集, 類文件結構, 堆棧, 垃圾收集堆, 內存區域
Java 程序不能依賴於垃圾回收的時間或者順序
GC
是徹底自動的, 不能被強制執行, 程序員最多隻能用 System.gc()
來建議執行垃圾回收器回收內存, 可是具體的回收時間, 是不可知的。當對象的引用變量被賦值爲 null
, 可能被當成垃圾
GC
自動回收內存, 程序員不須要要沒法精確控制回收過程, 也就是說只有 new
, 沒有 delete
系統級線程會跟蹤存儲空間的分配狀況
JVM 空閒時, 檢查和釋放那些能夠釋放的空間
JRE
JRE = JVM + API(Lib)
JRE
運行程序時的三項主要功能加載代碼(class loader), 校驗代碼(bytecode verifier), 執行代碼(runtime interpreter, 所以虛擬機有時候也簡單稱爲一個解釋器)
小結: Java運行環境(JRE) 首先由虛擬機 JVM 來裝載程序, 而後調用相應的指令來執行
JDK = JRE + Tools
(編譯工具 javac
, 打包工具 jar
, 調試工具 jdb
, 執行器 java
, 文檔生成器 javadoc
)JRE = JVM + API
開發程序須要 JDK
, 若是隻是運行程序則 JRE
夠了
類 = 屬性(變量, 字段, field) + 行爲(函數, 方法, method)
Application 的基本結構
package javanote; publicc class Main{ public static void main(String args[]){ System.out.println("Hello"); } } // 一個文件能夠有多個 class, 可是隻能有一個 public class, 且與文件同名 // main 方法必須是 public static // main 方法是一個特殊的方法, 它是程序運行的入口 // main 還可用於測試, 直接運行該類的時候會調用 main, 若是是其餘類調用這個類的方法, 則不會運行 main // package, 包, 文件路徑 // import, 導入其餘的類
輸入 Scanner, 輸出 System.out.print()
package javanote; import java.util.InputMismatchException; import java.util.Scanner; public class Main{ public static void main(String args[]) { try { // 注意, 若是輸入的不是整數, 而是小數/字符串, 則會報錯. 所以須要try...catch... Scanner in = new Scanner(System.in); int a = in.nextInt(); System.out.println(a); } catch (InputMismatchException e) { // TODO: handle exception } } }
byte, 1 字節
short, 2 字節
int, 4 字節
long, 8 字節
Java 中沒有 無符號數
float, 4 字節
double, 8 字節, 浮點數默認是 double
邏輯型
boolean, 1 bit, true/false
Java 中不能夠用 if(1)
或 if(0)
, 也不能 if( a = 5 )
boolean 不能比較大小, 不能進行算術運算
2 + true // error true + true // error, 就算換成 boolean a, b; 而後 a + b; 仍然是error true > false // error 1 > false // error true == 6; // error a == b > false // error, ==, != 優先級低, 先作 b > false, 這裏也會出錯 ( a == b ) > false // error, 相似於 true > false 的error
邏輯運算的 !, &&, ||
只能用於 boolean, 做用到其餘類型上面會出錯, 這點也和 C
不同
char, 2 字節, 統一使用 Unicode 編碼, 跨平臺
強制類型轉換
double a = 10.3; int b = (int)a; int c = (int) (10/3.0); // (10/3.0)要加括號, 由於(int)是單目運算, 優先級高
數組是一種容器, 全部元素有相同的數據類型, 一旦建立, 則不能改變大小. 數組必須用 new
來分配空間, 數組元素默認初始化.
// 注意方括號的位置 // a和b都是數組, 其中 a 未分配空間, b 分配了 4 個 int 的空間 int[] a, b = new int[4]; // c 是數組, d 是 int 變量 int c[], d;
數組是引用類型, 元素個數能夠用變量定義,
// 數組是引用類型 int[] a1 = new int[3]; int b = 10; // 元素個數能夠用變量定義, 和c99相似 int[] a2 = new int[b]; // error, 數組是引用類型, 理解爲指針, 不能直接給它分配空間, 分配的空間在堆, 必須 new 一塊空間而後指向那裏 int a3[5];
若是沒有對數組進行顯式初始化, 則會隱式初始化爲 0 或 null, 比 C 安全
// 若是沒有對數組進行顯式初始化, 則會隱式初始化爲 0 或 null int[] a4 = {3, 1, 2}; int[] a5 = new int[]{3, 1, 2}; // 注意, 數組元素會默認初始化, 可是基本數據類型聲明後不會默認初始化 int a; a++; // error
與指針的相似之處, 數組變量只是數組的管理者, 而不是擁有者
int[] a = new int[10]; int[] b = a; b[0] = 5; // 此時 a[0] 也變成了 5 // a == b 的結果是 true
複製數組, 不能用 b = a
, 而須要遍歷, 除此之外還有 a.clone()
和 System.arraycopy(src, int srcPos, dst, int dstPos, int length)
方法
int[] a = {1, 2, 3, 4}; // 方法1, a.clone() int[] b = a.clone(); int[] c = new int[4]; // 方法2, System.arraycopy() System.arraycopy(a, 0, c, 0, a.length);
每一個數組, 都有一個 .length
屬性, 比 C
安全
int[] a = new int[10]; for( int i = 0; i < a.length; i++ ) { } // 也能夠這樣遍歷, 由於數組是個 Iterable 對象 for( int n : a ) { }
二維數組
int[][] a = new int[3][5]; for( int i = 0; i < a.length; i++ ) { for( int j = 0; j < a[0].length; j++ ) { a[i][j] = i * 5 + j; } } // 初始化 int[][] a = { {1, 2, 3, 4}, {1, 2, 3}, } // 注意最後能夠多一個逗號
字符串, String, 第一個字母大寫, 說明是一個類, 是管理者
String s1 = new String("123"); String s2 = "abc"; // 自動轉換 String s3 = s1 + s2 + 12 + 24; // "123abc1224" String s4 = s1 + s2 + (12 + 24); // "123abc36" // 注意 String s5 = null; System.out.println( s5 + "a" ); // nulla
讀取輸入
next --- 至關於 C++ 中的 cin nextLine --- 至關於 C++ 中的 getline
字符串的操做. 字符串是對象, 對它的全部操做都要經過 .
這個運算符
s.length()
注意要加括號 ()
, 是字符串的 length()
方法, 和數組的 length
屬性不同
並且在調用 .length()
時, s
必須指向一塊字符串, 不能是未初始化區域(null)
s.charAt(index)
, 注意這是隻讀操做, read-only 遍歷, 注意字符串沒法使用 for(char ch : s)
, 由於字符串不是 Iterable
對象
for( int i = 0; i < s.length(); i++ ){ s.charAt(i); }
子串, .substring()
// [n:-1] s.substring(n); // [n, n + len) s.substring(n, len);
內容是否相同 .equals()
if( s.equals("Hello") ) { }
比較大小, .compareTo()
, unicode 編碼相減
s1.compareTo(s2); // unicode 編碼相減
其餘操做
int loc = s.indexOf('a'); s.indexOf('a', loc + 1); s.indexOf("abc"); s.lastIndexOf('a'); s.startsWith(ch); s.endsWith(ch); s.trim(); // 去掉兩端空格 s.replace(c1, c2); s.toLowerCase(); s.toUpperCase();
基本數據類型對應的包裹類型
boolean --- Boolean byte --- Byte short --- Short char --- Character int --- Integer long --- Long float --- Float double --- Double
基本數據類型 + 更多方法和字段
int a = Integer.MAX_VALUE; // 2^31 - 1 boolean b = Character.isDigit('a'); // false char c = Character.toLowerCase('A'); // a
數學類, Math
Math.abs() Math.round() Math.random() Math.pow()
<=
形參類型的寬度(大小)好比形參 double 實參 int 是能夠的, 可是反過來就不行
因爲沒有 C 的指針, 也沒有 C++ 的引用 &
, 因此通常方法沒法實現 swap
, 須要使用數組或者對象
static
不屬於某個實例, 而是屬於整個類的(屬性). static
變量, 至關於該類一個全局變量, 有點像 C
中的 extern
全局變量, 是全部該類的對象實例所共享的. 這樣看來, Java
的類是一個樹的結構, 根結點中存儲着公有信息, 而對象實例是衍生出來的子結點. 也就是說, static
是一個公共的路燈, 只有一盞, 每一個人能夠去開關路燈. 可是若是你要去關掉某一戶人家裏的燈, 就要明確指明他家的門牌號
類就至關於計網中的協議, 好比網絡層協議, 規定了每一個數據包應該有什麼樣的格式. 而對象則是一個個具體的實際的數據包.
類變量, 至關於協議頭部的一個字段, 全部這個類的對象實例都有相同的頭部信息. 相對的, 成員變量則是數據部分.
static
變量單獨劃分一塊存儲空間, 不與具體的對象綁定在一塊兒, 該存儲空間被類的各個對象所共享.
static
變量值在方法區加載一次, 而 非 static
變量在建立對象時會加載不少次, 每次建立都會拷貝一份
static
方法再內存中有專用的代碼段
static
的訪問
static
方法能夠訪問 static
變量 (類變量)
static
方法不能訪問無 static
前綴的 普通成員變量
, 不能訪問實例變量, 也就是不能使用 this
或 super
, 調用類方法時, 能夠經過 `類名.
普通成員函數
能夠訪問 static 變量
能夠經過 類名.類變量
訪問類變量, 也能夠經過 對象名.類變量
訪問類變量. 同理, 調用類方法時能夠用 類名.類方法
, 也能夠用 類名.類方法
class A{ static int var = 1; public static void main(String args[]){ A.var++; // OK, 類名.類變量 A a = new A(); a.var++; // OK, 對象名.類變量 var++; // OK, 直接訪問 } }
final
, 不可改變的, 最終的類前面加上 final
, 表示這個類不能被繼承, 有利於 Java 的優化
方法前面加上 final
, 則這個方法不能被子類覆蓋 Override
字段前面加上 final
, 能且只能被賦值一次, 而後就不能被改變了. 一般用 static final
表示常量
成員變量的生存期 ---> 該對象的生存期, new 開始, GC 收集
成員變量的做用域 ---> 類的內部
構造方法, 構造函數, new 一個新的對象會調用構造方法, 能夠重載 overload (參數表不一樣)
初始化順序
1.new 2.先跳到構造函數(但不進入) 3.調到父類的構造函數(若是有父類) 4.重複3, 一直到最高的父類 5.跳到構造函數外面的 定義初始化 int price = 80, balance = 0; 6.進入構造函數, 完成構造函數裏面的語句 7.回到子類, 進行子類的定義初始化, 再完成子類的構造函數 8.把構造出來的對象交給對象實例 vm 管理
若是有構造方法的重載, 並且若是其中一個要調用另外一個構造方法, 那麼要藉助 this
class Person{ private int age, id; public Person(){ this(1);// 必須放在最開始, 調用另一個構造方法 age = 0; } public Person(int pid){ id = pid; } }
析構函數使用的是 finalize()
方法, 可是通常不用去定義, 交給 JVM 就行了
必須 new, 纔會分配具體的空間, 每一個對象的 id 不一樣
若是是用另外一個對象對其賦值, 則至關於兩我的管理同一個對象實例, 和指針同樣
Student s1 = new Student(); Student s2 = s1; s1.setAge(5); // 5 s1.getAge(); // 5 s2.setAge(10); // s2 更新 age s1.getAge(); // 10, s1也會修改, 由於二者指向同一塊區域
一個對象實例是內存中的一塊區域, 只能經過同類型的引用去訪問, 不能隨便拿一個指針亂指到一個實例(內存區域)
private
是對類的限制, 而不是對對象的限制
同一個類的內部, 該類的不一樣對象實例之間, 能夠互相訪問private.
可是出了這個類,就沒法訪問private了。舉例以下
class Lower{ private int val = 0; public void test(){ Lower a = new Lower(); Lower b = new Lower(); // 類的內部, 該類的不一樣對象實例之間, 能夠互相訪問private a.val = b.val; } } class Higher{ public void test2(){ Lower l1 = new Lower(); Lower l2 = new Lower(); // error, not visible. 超出了Lower類的內部, private字段就變成了不可見 l1.val = l2.val; } }
private 方法和字段不能繼承
public
default
(friendly)既沒有前綴 public, 也沒有前綴 private, 那麼這個成員是 default
那麼和它位於同一個包(同一個文件夾)的其餘類能夠訪問.
注意, 不須要顯式聲明爲 default
protected
同一包內的類可見
全部子類可見
父類 public, 子類也必須爲 public
父類 protected, 子類 protected, public, private
父類 private 不可以被繼承
信息隱蔽, 隱藏類的細節(private), 用戶只能經過受保護的接口訪問某個類
class Person{ private int age; public int getAge(){ return age; } }
繼承的例子 (extends
)
class Person{ int age; String name; } // 注意關鍵字 extends 表示繼承(擴展) class Student extends Person{ String school; } // 若是沒有 extends xxx, 那麼默認爲 extends java.lang.Object 的子類 // 所以全部的類都是直接或間接地繼承了 Object
JDK 1.5 以後引入了 @Override
, 表示覆蓋 (修改) 了父類的方法.
能夠當作註釋, 同時編譯器會幫你檢查, 若是父類中沒有相同的 方法名
+ 參數表
, 就會報錯
父類方法 func()
被覆蓋後, 仍然能夠用 super.func()
調用父類的方法
方法名相同, 參數表不一樣. 重載是新的方法.
構造方法是不能繼承的, 由於構造方法是和類名同名的
子類構造的過程當中須要用 super
來調用父類的構造方法
例題, 問如下代碼的結果是什麼
class Person { String name = ""; public Person(String n) { name = n; } } class Employee extends Person { String empID = ""; public Employee(String id) { empID = id; } } public class Test { public static void main(String args[]) { Employee e = new Employee("123"); System.out.println(e.empID); } }
結果是編譯報錯, 由於父類 Person
沒有無參數的構造函數, 解決方法
// 方法1, 給父類 Person 增長無參數的構造函數(子類會默認調用) class Person { public Person() {} } // 方法2, 子類構造函數中用 super 顯式調用父類已有的構造函數 class Employee extends Person { public Employee(String id) { super("Bob");// 注意 super 必須放在第一句 empID = id; } }
子類對象和父類對象的轉換
Student
是一個 Person
(子類對象能夠被視爲父類對象)
Person
不必定是 Student
, 具體狀況看下面的例子
// 假設 Student extends Person Person p1 = new Student(); // 編譯正常, 運行正常 Student s1 = (Student) p1; // 編譯正常, 運行正常 Person p2 = new Person(); Student s2 = (Student) p2; s2.xxx(); // 【編譯正常, 運行錯誤】
abstract
) 和接口(interface
)abstract
, 抽象
abstract class Person{ // 含有抽象方法的類必須聲明爲抽象類 // 注意抽象方法是分號;結尾, 而不是花括號 // 也就是說抽象方法只有聲明, 沒有實現 abstract void speak(); void eat() { // 抽象類能夠有非抽象方法 } } class Student extends Person{ @Override void speak() { // 子類 extends 父類後 // 要麼仍然保持 abstract 方法 // 要麼 Override 覆蓋(這裏也能夠稱爲實現)父類的 abstract 方法 } } public class Main{ public static void main(String args[]) { // 錯誤, 抽象類不能被實例化 `instantiate` // Person p1 = new Person(); // 可是抽象類能夠用於定義變量 Person p2 = new Student(); } }
interface
, 接口, 約定某種特徵
interface 是純抽象類, 全部方法都是 abstract
, 也就是 C++
中的純虛類
全部的成員變量都是 public static final
(至關於 const
常量)
static
代表它屬於這個類而不是某個具體對象, final
代表它一旦初始化就不會被改變, 是一個編譯時刻已經肯定的常量
// 注意直接用 interface 修飾, 不須要 class interface Flyable{ // 接口類的全部方法都是 `public abstract`, 不須要顯式寫出 // public abstract void fly(); void fly(); } interface Runnable{ // 若是使用 static 修飾符, 則代表這是默認實現, 不須要每次 implements 都去實現 run // java8 以上的新特性, 可是這就又回到父子繼承了 static void run() { } } // implements 多個接口用逗號分隔 class Superman extends Person implements Flyable, Runnable{ @Override public void fly() { } } class Airplane implements Flyable{ @Override public void fly() { } }
接口能夠繼承接口,可是不能繼承(extends)類
接口不能實現接口,只有類可以實現(implements)接口. 一個類能夠實現(implements)多個接口(多繼承), 也就是說, 經過接口能夠實現不相關類的相同行爲(fly()
), 而不須要考慮層次關係(``)
類 class
, 2.接口 interface
, 3.數組
case1, 編譯時多態, 靜態
重載 Overload, 多個方法同名但參數表不一樣
p.say(); p.say("Hi");
case2, 運行時多態, 動態
覆蓋 Override, 子類方法覆蓋父類同名方法. 動態綁定 dynamic binding, 也稱爲虛方法調用, 運行的時候根據實例對象來調用方法, 舉例以下
class Person{ void say(){ System.out.println("hi"); } } class Student extends Person{ @Override void say(){ System.out.println("hi, I'm a student"); } } public class Main{ static void func(Person p){ p.say(); } public static void main(String args[]) { Person p = new Person(); Student s = new Student(); Person ps = new Student(); // func() 的參數表說明了咱們須要一個 Person 對象 func(p); // 可是咱們也能夠把 Student 傳進去 // 這時候不會報錯, 並且會正確調用子類的 say() func(s); // 更進一步, 若是咱們用向上造型, func() 能接受 ps, 且會正確調用子類的 say() func(ps); } }
能夠被 Override
的方法均可以稱做虛方法, 虛方法不須要特殊的聲明
非 static、final、private
修飾的全部方法都是虛方法
父類 obj = new 子類();
子類的對象, 能夠被當成父類的對象來使用, 能夠賦值給父類的變量, 能夠傳遞給須要父類對象的函數, 若是一個容器存放的是父類對象, 那麼子類對象也能夠放進去
// 爲何? 由於繼承了父類, 對外的接口是同樣的
子類的對象能夠賦值給父類的變量. 注意, Java
不存在 對象
對 對象
的賦值, 而是讓兩個管理員共同管理一個對象. 類比指針, 不是複製內容, 而是給地址.
container
, 是放東西的東西, 數組能夠看作一種容器, 可是數組的長度一旦肯定就沒法改變, 而容器通常能夠改變其容量基本數據類型數組
與 對象數組
// 基本數據類型數組, 每個元素都是基本數據類型 int[] ai = new int[32]; // 每一個元素被初始化爲0 // String[] 數組, 對象數組, 每個元素只是一個管理者, 一個指針, 而不是保存實際內容 // 元素初始化爲 null, 實際內容還不存在, 須要 for 循環去建立和賦值(指向) String[] as = new String[10];
ArrayList
容器類
// import java.util.ArrayList; ArrayList<String> noteList = new ArrayList<String>(); // 用法參考 https://www.w3schools.com/java/java_arraylist.asp noteList.add( Integer.toString(0) ); noteList.add( "1" ); noteList.get(0); noteList.set(0, "00"); // import java.util.Collections; // 排序 Collections.sort(noteList); noteList.remove(0); noteList.clear(); // 若是要構建一個數組, 能夠調用 toArray() 方法 int size = noteList.size(); String[] mylist = new String[size]; noteList.toArray(mylist);
Set
去重
HashSet<String> s = new HashSet<String>(); s.add("first"); s.add("second"); s.add("first"); sysout(s); // [first, second] s.contains("1"); // false
Map
鍵值映射關係
/* 映射關係 1 penny 5 nickel 10 dime 25 quarter 50 half-dollar */ HashMap<Integer, String> dollarMap = new HashMap<Integer, String>(); dollarMap.put(1, "penny"); // 注意其餘容器都是 add, map 是 put dollarMap.put(5, "nickel"); dollarMap.put(10, "dime"); dollarMap.put(25, "quarter"); dollarMap.put(50, "half"); dollarMap.replace(50, "half-dollar"); dollarMap.containsKey(30); // false dollarMap.get(30); // null dollarMap.get(50);