主要內(nèi)容
1. Java基本功
2. Java面向?qū)ο?/strong>
3. Java核心技術(shù)
1. Java基本功
1.1 Java基礎(chǔ)概念與常識(shí)
1.1.1 JDK和JRE
JDK是Java Development Kit警儒,它是功能齊全的 Java SDK训裆。它擁有 JRE 所擁有的一切,還有編譯器(javac)和工具(如 javadoc 和 jdb)。它能夠創(chuàng)建和編譯程序边琉。
JRE是Java運(yùn)行時(shí)環(huán)境属百,它是運(yùn)行已編譯Java程序所需的所有內(nèi)容的集合,包括Java虛擬機(jī)(JVM)变姨,Java類庫诸老,Java命令和其他的一些基礎(chǔ)構(gòu)件,但是它不能用于創(chuàng)建新程序钳恕。
1.1.2 Java和C++的區(qū)別
相同點(diǎn):
- 都是面向?qū)ο蟊鸱С址庋b、繼承和多態(tài)
不同點(diǎn):
- Java沒有指針直接訪問內(nèi)存忧额,程序內(nèi)存更安全
- Java類是單繼承的厘肮,C++支持多繼承;java接口可以多繼承
- Java有自動(dòng)內(nèi)存管理機(jī)制睦番,不需要程序員手動(dòng)釋放無用內(nèi)存
- 在 C 語言中类茂,字符串或字符數(shù)組最后都會(huì)有一個(gè)額外的字符‘\0’來表示結(jié)束。但是托嚣,Java 語言中沒有結(jié)束符這一概念巩检。
1.1.3 什么是Java程序的主類,應(yīng)用程序和小程序的主類有何不同
Java程序的主類是含main方法的類示启。 主方法的修飾符必須是public static void的,但是主類的修飾符不一定是public的。
小程序的主類必須要求是public修飾的球订。
1.1.4 Java應(yīng)用程序與小程序之間的區(qū)別
應(yīng)用程序從主線程啟動(dòng)容燕,也就是從main()方法啟動(dòng);
applet小程序(Java編寫的小應(yīng)用程序,jsp中的java腳本)沒有main()方法,主要是嵌在瀏覽器頁面上運(yùn)行(調(diào)用init()或者run())來啟動(dòng)钦听。
1.1.5 為什么說Java語言“編譯與解釋并存”
編譯型:編譯型語言是指編譯器針對(duì)特定的操作系統(tǒng)將源代碼一次性翻譯成可被該平臺(tái)執(zhí)行的機(jī)器碼畔裕;
解釋型:解釋型語言是指解釋器對(duì)源程序逐行解析成特定平臺(tái)的機(jī)器碼并立即執(zhí)行。
Java預(yù)先編譯乖订,生成字節(jié)碼(.class 文件)-> 編譯型扮饶;
字節(jié)碼(.class 文件)需要用Java解釋器來解釋執(zhí)行 -> 解釋型;
Therefore, Java編譯與解釋并存乍构。
1.2 Java語法
1.2.1 Java泛型甜无,類型擦除,常用的通配符
Java泛型:泛型提供了編譯時(shí)類型安全檢測(cè)機(jī)制哥遮,該機(jī)制允許程序員在編譯時(shí)檢測(cè)到非法的類型岂丘。泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)眠饮。
類型擦除:Java的泛型是偽泛型奥帘,因?yàn)镴ava在編譯期間所有的泛型信息都會(huì)被擦掉。
泛型的使用方式:泛型類君仆;泛型接口翩概;泛型方法
泛型類
/**
* 泛型類
* 泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)
*/
//此處T可以隨便寫成任意標(biāo)識(shí)返咱,常見的如T,E,K,V等形式的參數(shù)常用于表示泛型
//但是在實(shí)例化泛型類時(shí)钥庇,必須指定T的具體類型
public class GenericClass<T> {
private T key;
public GenericClass(T key){
this.key = key;
System.out.println(key);
}
public T getKey(){
return key;
}
public static void main(String[] args) {
GenericClass<Integer> genericClassInteger = new GenericClass<>(1234);
}
}
泛型方法
public class GenericMethod {
public static <E> void printArray(E[] inputArray){
for (E element : inputArray){
System.out.printf("%s",element);
}
System.out.println();
}
public static void main(String[] args) {
//可以創(chuàng)建不同類型數(shù)組: Integer, Double 和 Character
Integer[] intArr = {1,2,3};
String[] stringArr = {"hello", " ","world"};
Character[] characterArr = {'c','h','e','e','r'};
printArray(intArr);
printArray(stringArr);
printArray(characterArr);
}
}
泛型接口
/**
* 泛型接口
* @param <T>
*/
public interface Generator<T> {
public T method();
}
/**
* 實(shí)現(xiàn)泛型接口,不指定類型
* @param <T>
*/
public class GeneratorImpl<T> implements Generator<T> {
@Override
public T method() {
return null;
}
}
/**
* 實(shí)現(xiàn)泛型接口咖摹,指定類型
*/
public class GeneratorImpl2 implements Generator<String> {
@Override
public String method() {
return "hi~~ cheer";
}
}
常用的通配符:
- 评姨?不確定的java類型
- T (type) 表示具體的一個(gè)java類型
- K V (key value) 分別表示java鍵值對(duì)中的鍵和值
- E (element) 表示Element元素
1.2.2 ==和equals的區(qū)別
==:判斷兩個(gè)對(duì)象是不是同一個(gè)對(duì)象,基本數(shù)據(jù)類型比較的是值萤晴, 引用數(shù)據(jù)類型比較的是內(nèi)存地址
equals:比較兩個(gè)對(duì)象是否相等吐句,不能用于比較基本數(shù)據(jù)類型;equals方法存在于Object類之中店读,Object類是所有類的直接或間接父類嗦枢。
類沒有覆蓋equals()方法,等價(jià)通過"=="比較兩個(gè)對(duì)象屯断,默認(rèn)使用Object類的equals()方法文虏。
類覆蓋了equals()方法,某些值或內(nèi)容相等默認(rèn)兩個(gè)對(duì)象相等殖演。例如String之中的equals()方法就是被重寫過的氧秘。
補(bǔ)充關(guān)于String類型的小知識(shí):當(dāng)創(chuàng)建 String 類型的對(duì)象時(shí),虛擬機(jī)會(huì)在常量池中查找有沒有已經(jīng)存在的值和要?jiǎng)?chuàng)建的值相同的對(duì)象趴久,如果有就把它賦給當(dāng)前引用丸相。如果沒有就在常量池中重新創(chuàng)建一個(gè) String 對(duì)象。
1.2.3 hashCode()與equals()
hashCode() 獲取哈希碼(散列碼)彼棍,實(shí)際是一個(gè)int型整數(shù)灭忠。這個(gè)哈希碼的作用是確定該對(duì)象在哈希表中的索引位置膳算,默認(rèn)對(duì)堆上的對(duì)象產(chǎn)生獨(dú)特的值。散列表中存儲(chǔ)的時(shí)鍵值對(duì)(key-value)更舞, 特點(diǎn)是:能根據(jù)“鍵”快速檢索對(duì)應(yīng)的值畦幢。HashCode方法的主要作用是為了配合基于散列的集合一起正常運(yùn)行,這樣的散列集合包括HashSet缆蝉、HashMap以及HashTable宇葱。
hashCode()存在的意義 提高執(zhí)行速度;當(dāng)你把對(duì)象加入 HashSet 時(shí)刊头,HashSet 會(huì)先計(jì)算對(duì)象的 hashcode 值來判斷對(duì)象加入的位置黍瞧,同時(shí)也會(huì)與其他已經(jīng)加入的對(duì)象的 hashcode 值作比較,如果沒有相符的 hashcode原杂,HashSet 會(huì)假設(shè)對(duì)象沒有重復(fù)出現(xiàn)印颤。但是如果發(fā)現(xiàn)有相同 hashcode 值的對(duì)象,這時(shí)會(huì)調(diào)用 equals()方法來檢查 hashcode 相等的對(duì)象是否真的相同穿肄。如果兩者相同年局,HashSet 就不會(huì)讓其加入操作成功。如果不同的話咸产,就會(huì)重新散列到其他位置矢否。
為什么重寫equals()時(shí)必須重寫hashCode方法
hashCode()和equals()的相關(guān)規(guī)定: 兩對(duì)象相等則hashcode一定相等;兩個(gè)對(duì)象相等脑溢,equals方法會(huì)返回true僵朗;兩對(duì)象hashcode值相等,它們不一定相等
重新equals是希望兩個(gè)對(duì)象在某些值相等的時(shí)候就默認(rèn)相等屑彻,hashCode()的默認(rèn)行為是對(duì)堆上的對(duì)象產(chǎn)生獨(dú)特值验庙。如果沒有重寫 hashCode(),則該 class 的兩個(gè)對(duì)象無論如何都不會(huì)相等(即使這兩個(gè)對(duì)象指向相同的數(shù)據(jù))社牲。
為什么兩個(gè)對(duì)象有相同的hashcode值粪薛,它們也不一定相等
因?yàn)?hashCode() 所使用的雜湊算法也許剛好會(huì)讓多個(gè)對(duì)象傳回相同的雜湊值。越糟糕的雜湊算法越容易碰撞搏恤,但這也與數(shù)據(jù)值域分布的特性有關(guān)(所謂碰撞也就是指的是不同的對(duì)象得到相同的 hashCode汗菜。-- hash碰撞問題
重寫equals和hashCode方法
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* equals() and hashCode(), which are used in various collections
*
* Summary:
* 1. If two objects are equal, they MUST have the same hashcode
* 2.
*/
public class equalAndHashCode {
private static class Person{
private String firstName;
private String lastName;
private int ssn;
public Person(String firstName, String lastName, int ssn) {
this.firstName = firstName;
this.lastName = lastName;
this.ssn = ssn;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getSsn() {
return ssn;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof Person)) return false;
Person p = (Person) obj;
return this.firstName.equals(p.getFirstName())
&&this.lastName.equals(p.getLastName())
&&this.ssn == p.getSsn();
}
@Override
public int hashCode() {
return Objects.hash(firstName,lastName,ssn);
}
}
public static void main(String[] args) {
Person p1 = new Person("John","Smith",111111);
Person p2 = new Person("John","Smith",111111);
Person p3 = new Person("Bob","Smith",111111);
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
Set<Person> set = new HashSet<>();
set.add(p1);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set.size());
}
}
1.3 基本數(shù)據(jù)類型
1.3.1 8種基本類型的包裝類和常量池
實(shí)現(xiàn)常量池的基本類型的包裝類
Java 基本類型的包裝類的大部分都實(shí)現(xiàn)了常量池技術(shù),即 Byte,Short,Integer,Long,Character,Boolean挑社;前面 4 種包裝類默認(rèn)創(chuàng)建了數(shù)值[-128,127] 的相應(yīng)類型的緩存數(shù)據(jù)巡揍,Character創(chuàng)建了數(shù)值在[0,127]范圍的緩存數(shù)據(jù)痛阻,Boolean 直接返回True Or False。如果超出對(duì)應(yīng)范圍仍然會(huì)去創(chuàng)建新的對(duì)象腮敌。-- 為相應(yīng)的包裝類創(chuàng)建對(duì)應(yīng)類型的緩存數(shù)據(jù)
兩種浮點(diǎn)數(shù)類型的包裝類Float和Double沒有實(shí)現(xiàn)常量池技術(shù)阱当。
應(yīng)用場(chǎng)景
- Integer i1=40俏扩;Java 在編譯的時(shí)候會(huì)直接將代碼封裝成 Integer i1=Integer.valueOf(40);,從而使用常量池中的對(duì)象弊添。
- Integer i1 = new Integer(40);這種情況下會(huì)創(chuàng)建新的對(duì)象录淡。
代碼示例
public class testConstantPool {
public static void main(String[] args) {
//對(duì)Integer線程池技術(shù)理解
/* Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2); //輸出為true,由于緩沖數(shù)據(jù)池的存在
Integer i11 = 333;
Integer i22 = 333;
System.out.println(i11 == i22); //輸出為false
Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4); //輸出為false, Float Double并沒有實(shí)現(xiàn)常量池技術(shù)*/
//Integer比較豐富的例子
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
System.out.println("i1=i2 " + (i1 == i2)); //true
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //true
System.out.println("i1=i4 " + (i1 == i4)); //false
System.out.println("i4=i5 " + (i4 == i5));//false
/**
*解釋:
* 語句 i4 == i5 + i6油坝,因?yàn)?這個(gè)操作符不適用于 Integer 對(duì)象嫉戚,+/-/*等運(yùn)算符都會(huì)將數(shù)據(jù)轉(zhuǎn)化成數(shù)值類型來進(jìn)行操作
* 首先 i5 和 i6 進(jìn)行自動(dòng)拆箱操作,進(jìn)行數(shù)值相加澈圈,即 i4 == 40彬檀。
* 然后 Integer 對(duì)象無法與數(shù)值進(jìn)行直接比較,
* 所以 i4 自動(dòng)拆箱轉(zhuǎn)為 int 值 40瞬女,最終這條語句轉(zhuǎn)為 40 == 40 進(jìn)行數(shù)值比較窍帝。
*/
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //true
System.out.println("40=i5+i6 " + (40 == i5 + i6)); //true
}
}
1.4 方法(函數(shù))
1.4.1 為什么Java中只有值傳遞
按值調(diào)用:表示方法接收的是調(diào)用者提供的值
按引用調(diào)用:表示方法接收的是調(diào)用者提供的變量地址。
一個(gè)方法可以改變傳遞引用所對(duì)應(yīng)的原變量的值诽偷,而不能修改傳遞值調(diào)用對(duì)應(yīng)的變量值坤学。
Java程序設(shè)計(jì)的特點(diǎn)
Java 程序設(shè)計(jì)語言總是采用按值調(diào)用。也就是說报慕,方法得到的是所有參數(shù)值的一個(gè)拷貝深浮,也就是說,方法不能修改傳遞給它的任何參數(shù)變量的內(nèi)容卖子。
Java中方法參數(shù)的使用情況
一個(gè)方法不能修改一個(gè)基本數(shù)據(jù)類型的參數(shù)(即數(shù)值型或布爾型)
/**
* 我們已經(jīng)知道了一個(gè)方法不能修改一個(gè)基本數(shù)據(jù)類型的參數(shù)
*/
public class primaryDataTypePara {
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
一個(gè)方法可以修改一個(gè)對(duì)象參數(shù)的狀態(tài)
/**
* 實(shí)現(xiàn)一個(gè)改變對(duì)象參數(shù)狀態(tài)的方法并不是一件難事略号。
* 理由很簡(jiǎn)單,方法得到的是對(duì)象引用的拷貝洋闽,對(duì)象引用及其他的拷貝同時(shí)引用同一個(gè)對(duì)象玄柠。
*/
public class referencePara {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
// 將數(shù)組的第一個(gè)元素變?yōu)?
array[0] = 0;
}
}
一個(gè)方法不能讓原本的對(duì)象參數(shù)引用一個(gè)新的對(duì)象
/**
* 方法并沒有改變存儲(chǔ)在變量 s1 和 s2 中的對(duì)象引用。
* swap 方法的參數(shù) x 和 y 被初始化為兩個(gè)對(duì)象引用的拷貝诫舅,
* 這個(gè)方法交換的是這兩個(gè)拷貝
*/
public class referenceParaSwap {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1 = new Student("小張");
Student s2 = new Student("小李");
referenceParaSwap.swap(s1, s2);
System.out.println("s1:" + s1.getName());
System.out.println("s2:" + s2.getName());
}
public static void swap(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x:" + x.getName());
System.out.println("y:" + y.getName());
}
static class Student{
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
1.4.2 重載和重寫方法的區(qū)別
從發(fā)生范圍羽利、參數(shù)列表、返回類型刊懈、異常这弧、訪問修飾符和發(fā)生階段來進(jìn)行說明。
1.4.3 深拷貝 vs 淺拷貝
淺拷貝: 對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞虚汛,對(duì)引用數(shù)據(jù)類型進(jìn)行傳遞般的拷貝匾浪,此為淺拷貝。
深拷貝: 對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞卷哩,對(duì)引用數(shù)據(jù)類型蛋辈,創(chuàng)建一個(gè)新對(duì)象,并復(fù)制其內(nèi)容,為深拷貝
2. Java面向?qū)ο?/h1>
2.1 類和對(duì)象
2.1.1 面向?qū)ο蠛兔嫦蜻^程的區(qū)別
面向過程: 大多數(shù)面向過程的語言大多數(shù)是直接編譯成機(jī)械碼在電腦上執(zhí)行冷溶,并且其他的一些面向過程的腳本語言的性能不一定比Java好渐白。不需要實(shí)例化對(duì)象。所以當(dāng)性能是最重要的考量因素的時(shí)候逞频,比如單片機(jī)纯衍、嵌入式開發(fā)、Linux/Unix 等一般采用面向過程開發(fā)苗胀。
面向?qū)ο? 面向?qū)ο笠拙S護(hù)襟诸、易復(fù)用、易擴(kuò)展柒巫。 因?yàn)槊嫦驅(qū)ο笥蟹庋b励堡、繼承、多態(tài)性的特性堡掏,所以可以設(shè)計(jì)出低耦合的系統(tǒng)祭饭,使系統(tǒng)更加靈活岂却、更加易于維護(hù)狰挡。Java 性能差的主要原因并不是因?yàn)樗敲嫦驅(qū)ο笳Z言吴趴,而是 Java 是半編譯語言,最終的執(zhí)行代碼并不是可以直接被 CPU 執(zhí)行的二進(jìn)制機(jī)械碼亭畜。
2.1.2 在Java定義一個(gè)不做事并且沒有參數(shù)的構(gòu)造函數(shù)的作用
為了子類好:Java 程序在執(zhí)行子類的構(gòu)造方法之前扮休,如果沒有用 super()來調(diào)用父類特定的構(gòu)造方法,則會(huì)調(diào)用父類中“沒有參數(shù)的構(gòu)造方法”拴鸵。因此玷坠,如果父類中只定義了有參數(shù)的構(gòu)造方法,而在子類的構(gòu)造方法中又沒有用 super()來調(diào)用父類中特定的構(gòu)造方法劲藐,則編譯時(shí)將發(fā)生錯(cuò)誤八堡,因?yàn)?Java 程序在父類中找不到?jīng)]有參數(shù)的構(gòu)造方法可供執(zhí)行。
子類實(shí)例化時(shí)聘芜,默認(rèn)調(diào)用父類的無參構(gòu)造方法(不管子類的構(gòu)造器有沒有參數(shù)兄渺,因?yàn)樽宇惱^承的是父類的屬性和方法,只調(diào)用父類的無參構(gòu)造器就可以繼承父類的屬性和方法汰现,因此不會(huì)調(diào)用父類的有參構(gòu)造器)挂谍,再調(diào)用子類的有參/無參構(gòu)造器。
2.1.3 創(chuàng)建對(duì)象所用的運(yùn)算符瞎饲;對(duì)象實(shí)體和對(duì)象引用的不同點(diǎn)
創(chuàng)建對(duì)象所用的運(yùn)算符: new
區(qū)別點(diǎn):對(duì)象實(shí)例存在堆內(nèi)存中口叙,對(duì)象引用存在棧內(nèi)存中;一個(gè)對(duì)象引用可以指向0個(gè)或1個(gè)對(duì)象嗅战,一個(gè)對(duì)象可以有n個(gè)引用指向它庐扫。
堆和棧的區(qū)別:
內(nèi)存大小:堆非常大;棧很小
工作效率:堆相對(duì)比較慢形庭,棧就會(huì)快一些
2.1.4 調(diào)用子類構(gòu)造方法之前會(huì)先調(diào)用父類無參的構(gòu)造方法,目的厌漂?
因?yàn)樽宇惱^承的是父類的屬性和方法萨醒,只調(diào)用父類的無參構(gòu)造器就可以繼承父類的屬性和方法,因此調(diào)用父類的無參構(gòu)造器是為了幫助子類做初始化工作苇倡。
2.2 面向?qū)ο蟮娜筇卣?/h3>
2.2.1 封裝
封裝是指把一個(gè)對(duì)象的狀態(tài)信息(也就是屬性)隱藏在對(duì)象內(nèi)部富纸,不允許外部對(duì)象直接訪問對(duì)象的內(nèi)部信息。但是可以提供一些可以被外界訪問的方法來操作屬性旨椒。
2.2.2 繼承
繼承是使用已存在的類的定義作為基礎(chǔ)建立新類的技術(shù)晓褪,新類的定義可以增加新的數(shù)據(jù)或新的功能,也可以用父類的功能综慎,但不能選擇性地繼承父類涣仿。通過使用繼承,可以快速地創(chuàng)建新的類示惊,可以提高代碼的重用好港,程序的可維護(hù)性,節(jié)省大量創(chuàng)建新類的時(shí)間 米罚,提高我們的開發(fā)效率钧汹。
- 子類擁有父類對(duì)象所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類是無法訪問录择,只是擁有拔莱。
- 子類可以擁有自己屬性和方法,即子類可以對(duì)父類進(jìn)行擴(kuò)展隘竭。
- 子類可以用自己的方式實(shí)現(xiàn)父類的方法塘秦。
2.2.3 多態(tài)
表示一個(gè)對(duì)象具有多種狀態(tài)。具體表現(xiàn)為父類的引用指向子類的實(shí)例货裹。
- 對(duì)象類型和引用類型之間具有繼承(類)/實(shí)現(xiàn)(接口)的關(guān)系嗤形;
- 對(duì)象類型不可變,引用類型可變
- 方法具有多態(tài)性弧圆,屬性不具有多態(tài)性
- 引用類型變量發(fā)出的方法調(diào)用的到底是哪個(gè)類中的方法赋兵,必須在程序運(yùn)行期間才能確定
- 多態(tài)不能調(diào)用“只在子類存在但在父類不存在”的方法
- 如果子類重寫了父類的方法,真正執(zhí)行的是子類覆蓋的方法搔预,如果子類沒有覆蓋父類的方法霹期,執(zhí)行的是父類的方法
2.3 修飾符
2.3.1 常見關(guān)鍵字總結(jié): static, final, this, super
final關(guān)鍵字(不可修改;修飾類拯田、方法和變量)
final關(guān)鍵字修飾的特點(diǎn):
- final修飾的類不能被繼承历造,final類中的所有成員方法都會(huì)被隱式的指定為final方法
- final修飾的方法不能被重寫
- final修飾的變量是常量,如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改吭产;如果是引用類型的變量侣监,則在對(duì)其初始化之后便不能讓其指向另一個(gè)對(duì)象。
使用final方法的原因:
- 把方法鎖定臣淤,以防任何繼承類修改它的含義
- 效率橄霉,在早期的Java實(shí)現(xiàn)版本中,會(huì)將final方法轉(zhuǎn)為內(nèi)嵌調(diào)用邑蒋。類中所有的private方法都隱式地指定為final姓蜂。
static關(guān)鍵字
static關(guān)鍵字使用場(chǎng)景
- 修飾成員變量和成員方法
被 static 修飾的成員屬于類,不屬于單個(gè)這個(gè)類的某個(gè)對(duì)象医吊,被類中所有對(duì)象共享(不屬于單個(gè)實(shí)例)钱慢,可以并且建議通過類名調(diào)用。被static 聲明的成員變量屬于靜態(tài)成員變量卿堂,調(diào)用格式:類名.靜態(tài)變量名 類名.靜態(tài)方法名()束莫。靜態(tài)變量 存放在 Java 內(nèi)存區(qū)域的方法區(qū)。
方法區(qū)與 Java 堆一樣御吞,是各個(gè)線程共享的內(nèi)存區(qū)域麦箍,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量陶珠、靜態(tài)變量挟裂、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分揍诽,但是它卻有一個(gè)別名叫做 Non-Heap(非堆)诀蓉,目的應(yīng)該是與 Java 堆區(qū)分開來。
/**
* static修飾的成員屬于類的例子
*/
public class StaticBean {
private String name;
//靜態(tài)變量
static int age;
public StaticBean(String name) {
this.name = name;
}
//靜態(tài)方法
static void SayHello() {
System.out.println("Hello i am java");
}
@Override
public String toString() {
return "StaticBean{"+
"name=" + name + ",age=" + age +
"}";
}
}
public class StaticDemo {
public static void main(String[] args) {
StaticBean staticBean = new StaticBean("1");
StaticBean staticBean2 = new StaticBean("2");
StaticBean staticBean3 = new StaticBean("3");
StaticBean staticBean4 = new StaticBean("4");
//static變量是屬于整個(gè)類的暑脆,所以賦值就使得該類的所有實(shí)例的age有了相應(yīng)的改變
StaticBean.age = 33;
System.out.println(staticBean + " " + staticBean2 + " " + staticBean3 + " " + staticBean4);
//StaticBean{name=1,age=33} StaticBean{name=2,age=33} StaticBean{name=3,age=33} StaticBean{name=4,age=33}
StaticBean.SayHello();//Hello i am java
}
}
- 靜態(tài)代碼塊
靜態(tài)代碼塊: 靜態(tài)代碼塊定義在類中方法外, 靜態(tài)代碼塊在非靜態(tài)代碼塊之前執(zhí)行(靜態(tài)代碼塊—>非靜態(tài)代碼塊—>構(gòu)造方法)渠啤。 該類不管創(chuàng)建多少對(duì)象,靜態(tài)代碼塊只執(zhí)行一次.
一個(gè)類中的靜態(tài)代碼塊可以有多個(gè)添吗,位置可以隨便放沥曹,它不在任何的方法體內(nèi),JVM加載類時(shí)會(huì)執(zhí)行這些靜態(tài)的代碼塊碟联,如果靜態(tài)代碼塊有多個(gè)妓美,JVM將按照它們?cè)陬愔谐霈F(xiàn)的先后順序依次執(zhí)行它們,每個(gè)代碼塊只會(huì)被執(zhí)行一次鲤孵。
/**
* static{}靜態(tài)代碼塊與{}非靜態(tài)代碼塊的執(zhí)行順序
*/
public class CodeBlock {
public CodeBlock(){
System.out.println("默認(rèn)構(gòu)造方法壶栋!--");
}
/**
* 每次實(shí)例化都會(huì)執(zhí)行
*/
//非靜態(tài)代碼塊
{
System.out.println("非靜態(tài)代碼塊");
}
/**
* 該類無論創(chuàng)建多少對(duì)象,靜態(tài)代碼塊只執(zhí)行一次
*/
//靜態(tài)代碼塊
static {
System.out.print("靜態(tài)代碼塊普监!--");
}
private static void test() {
System.out.print("靜態(tài)方法中的內(nèi)容! --");
{
System.out.print("靜態(tài)方法中的代碼塊贵试!--");
}
System.out.println();
}
public static void main(String[] args) {
CodeBlock.test();//靜態(tài)代碼塊琉兜!--靜態(tài)方法中的內(nèi)容! --靜態(tài)方法中的代碼塊!--
CodeBlock test = new CodeBlock();
}
}
- 靜態(tài)內(nèi)部類(static修飾類的花只能修飾內(nèi)部類)
靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類之間存在一個(gè)最大的區(qū)別: 非靜態(tài)內(nèi)部類在編譯完成之后會(huì)隱含地保存著一個(gè)引用毙玻,該引用是指向創(chuàng)建它的外圍類豌蟋,但是靜態(tài)內(nèi)部類卻沒有。沒有這個(gè)引用就意味著:1. 它的創(chuàng)建是不需要依賴外圍類的創(chuàng)建淆珊。2. 它不能使用任何外圍類的非static成員變量和方法夺饲。
public class Super {
private int number;
void showNumber(){
System.out.println(this);
System.out.println("number = " + number);
}
static void showNum(){
System.out.println(1111);
}
/**
* 測(cè)試靜態(tài)內(nèi)部類,靜態(tài)內(nèi)部類不可以引用外部的非
*/
static class Test{
void outerMethod(){
showNum();
}
}
/**
* 測(cè)試非靜態(tài)內(nèi)部類
*/
public class Test2{
void outerMethod(){
showNumber();
}
}
static class Sub extends Super{
/**
* 繼承就意味著子類擁有父類所有屬性和方法(構(gòu)造方法除外)施符。
* static方法同樣能被繼承,
* 只是在某些情況下(比如類向上轉(zhuǎn)型時(shí))調(diào)用時(shí)與非static方法出現(xiàn)差異擂找。
*/
void bar(){
showNumber();
super.number=10;
super.showNumber();
System.out.println(this);
System.out.println(super.number);
}
}
public static void main(String[] args) {
Sub sub = new Sub();
Sub sub1 = new Sub();
((Super)sub).showNumber();
sub.bar();
sub1.bar();
Super super1 = new Super();
super1.showNumber();
/**
* 測(cè)試非靜態(tài)內(nèi)部類
*/
Test2 test = new Super().new Test2();
test.outerMethod();
/**
* 測(cè)試靜態(tài)內(nèi)部類
*/
Test test1 = new Super.Test();
test1.outerMethod();
}
}
- 靜態(tài)導(dǎo)包(用來導(dǎo)入類中的靜態(tài)資源戳吝,1.5之后的新特性)
import static 這兩個(gè)關(guān)鍵字連用可以指定導(dǎo)入某個(gè)類中的指定靜態(tài)資源,并且不需要使用類名調(diào)用類中靜態(tài)成員贯涎,可以直接使用類中靜態(tài)成員變量和成員方法听哭。
import static java.lang.Math.*;
public class staticImportPackage {
public static void main(String[] args) {
System.out.println(max(1,4));
}
}
static method / non-static method
- 在外部調(diào)用靜態(tài)方法時(shí),可以使用”類名.方法名”的方式塘雳,也可以使用”對(duì)象名.方法名”的方式陆盘。而實(shí)例方法只有后面這種方式。也就是說败明,調(diào)用靜態(tài)方法可以無需創(chuàng)建對(duì)象隘马。
- 靜態(tài)方法在訪問本類的成員時(shí),只允許訪問靜態(tài)成員(即靜態(tài)成員變量和靜態(tài)方法)妻顶,而不允許訪問實(shí)例成員變量和實(shí)例方法酸员;實(shí)例方法則無此限制
- 靜態(tài)方法屬于類本身,非靜態(tài)方法屬于從該類生成的每個(gè)對(duì)象讳嘱。 如果您的方法執(zhí)行的操作不依賴于其類的各個(gè)變量和方法幔嗦,請(qǐng)將其設(shè)置為靜態(tài)(這將使程序的占用空間更小)沥潭。 否則邀泉,它應(yīng)該是非靜態(tài)的。
靜態(tài)代碼塊與非靜態(tài)代碼塊
- 相同點(diǎn): 都是在JVM加載類時(shí)且在構(gòu)造方法執(zhí)行之前執(zhí)行钝鸽,在類中都可以定義多個(gè)汇恤,定義多個(gè)時(shí)按定義的順序執(zhí)行,一般在代碼塊中對(duì)一些static變量進(jìn)行賦值寞埠。
- 不同點(diǎn): 靜態(tài)代碼塊在非靜態(tài)代碼塊之前執(zhí)行(靜態(tài)代碼塊—非靜態(tài)代碼塊—構(gòu)造方法)屁置。靜態(tài)代碼塊只在執(zhí)行一次,之后不再執(zhí)行仁连,而非靜態(tài)代碼塊在每new一次就執(zhí)行一次蓝角。 非靜態(tài)代碼塊可在普通方法中定義(不過作用不大)阱穗;而靜態(tài)代碼塊不行
- 非靜態(tài)代碼塊與構(gòu)造函數(shù)的區(qū)別: 非靜態(tài)代碼塊是給所有對(duì)象進(jìn)行統(tǒng)一初始化,而構(gòu)造函數(shù)是給對(duì)應(yīng)的對(duì)象初始化使鹅,因?yàn)闃?gòu)造函數(shù)是可以多個(gè)的揪阶,運(yùn)行哪個(gè)構(gòu)造函數(shù)就會(huì)建立什么樣的對(duì)象,但無論建立哪個(gè)對(duì)象患朱,都會(huì)先執(zhí)行相同的構(gòu)造代碼塊鲁僚。也就是說,非靜態(tài)代碼塊中定義的是不同對(duì)象共性的初始化內(nèi)容裁厅。
this和super關(guān)鍵字
this關(guān)鍵字
- this關(guān)鍵字用于引用類的當(dāng)前實(shí)例冰沙。可以訪問當(dāng)前實(shí)例的變量和方法执虹。
super關(guān)鍵字
- super關(guān)鍵字用于從子類訪問父類的變量和方法拓挥。
使用this和super要注意的問題
- 在構(gòu)造器中使用 super() 調(diào)用父類中的其他構(gòu)造方法時(shí),該語句必須處于構(gòu)造器的首行袋励,否則編譯器會(huì)報(bào)錯(cuò)侥啤。另外,this 調(diào)用本類中的其他構(gòu)造方法時(shí)茬故,也要放在首行盖灸。
- this、super不能用在static方法中磺芭。(this和super是屬于對(duì)象范疇的東西赁炎,而靜態(tài)方法是屬于類范疇的東西。)
2.4 接口和抽象類
2.4.1 接口和抽象類的區(qū)別
- 接口的方法默認(rèn)是 public徘跪,所有方法在接口中不能有實(shí)現(xiàn)(Java 8 開始接口方法可以有默認(rèn)實(shí)現(xiàn))甘邀,而抽象類可以有非抽象的方法。
- 接口中除了 static垮庐、final 變量松邪,不能有其他變量,而抽象類中則不一定哨查。
- 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口逗抑,但只能實(shí)現(xiàn)一個(gè)抽象類。接口自己本身可以通過 extends 關(guān)鍵字?jǐn)U展多個(gè)接口寒亥。
- 接口方法默認(rèn)修飾符是 public邮府,抽象方法可以有 public、protected 和 default 這些修飾符(抽象方法就是為了被重寫所以不能使用 private 關(guān)鍵字修飾8绒取)褂傀。
- 從設(shè)計(jì)層面來說,抽象是對(duì)類的抽象加勤,是一種模板設(shè)計(jì)仙辟,而接口是對(duì)行為的抽象同波,是一種行為的規(guī)范。
- 總結(jié)一下 jdk7~jdk9 Java 中接口概念的變化: 在 jdk 7 或更早版本中叠国,接口里面只能有常量變量和抽象方法未檩。這些接口方法必須由選擇實(shí)現(xiàn)接口的類實(shí)現(xiàn); jdk8 的時(shí)候接口可以有默認(rèn)方法和靜態(tài)方法功能粟焊; Jdk 9 在接口中引入了私有方法和私有靜態(tài)方法.
/**
* java8的時(shí)候接口有默認(rèn)方法和靜態(tài)方法功能
*/
public interface Test {
public void generalInterfaceMethod();
default void defaultMethod(){
System.out.println("test default method");
}
public static void test(){
System.out.println("hahahahha");
}
public static void main(String[] args) {
Test.test();
}
}
2.5 其它重要的知識(shí)點(diǎn)
2.5.1 String StringBuffer和StringBuilder的區(qū)別冤狡, String為什么不可變
String StringBuffer和StringBuilder
- String 類中使用 final 關(guān)鍵字修飾字符數(shù)組來保存字符串,private final char value[]项棠,所以 String 對(duì)象是不可變的悲雳。
- 而 StringBuilder 與 StringBuffer 都繼承自 AbstractStringBuilder 類,在 AbstractStringBuilder 中也是使用字符數(shù)組保存字符串char[]value 但是沒有用 final 關(guān)鍵字修飾香追,所以這兩種對(duì)象都是可變的怜奖。
線程安全性
- String 中的對(duì)象是不可變的,也就可以理解為常量翅阵,線程安全。AbstractStringBuilder 是 StringBuilder 與 StringBuffer 的公共父類迁央,定義了一些字符串的基本操作掷匠,如 expandCapacity、append岖圈、insert讹语、indexOf 等公共方法。StringBuffer 對(duì)方法加了同步鎖或者對(duì)調(diào)用的方法加了同步鎖蜂科,所以是線程安全的顽决。StringBuilder 并沒有對(duì)方法進(jìn)行加同步鎖,所以是非線程安全的导匣。
性能
- 每次對(duì) String 類型進(jìn)行改變的時(shí)候才菠,都會(huì)生成一個(gè)新的 String 對(duì)象,然后將指針指向新的 String 對(duì)象贡定。StringBuffer 每次都會(huì)對(duì) StringBuffer 對(duì)象本身進(jìn)行操作赋访,而不是生成新的對(duì)象并改變對(duì)象引用。相同情況下使用 StringBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升缓待,但卻要冒多線程不安全的風(fēng)險(xiǎn)
對(duì)三者使用的總結(jié)
- 操作少量的數(shù)據(jù): 適用 String
- 單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用 StringBuilder
- 多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用 StringBuffer
2.5.2 Object類的常見方法總結(jié)
Object 類是一個(gè)特殊的類蚓耽,是所有類的父類。它主要提供了以下 11 個(gè)方法
public final native Class<?> getClass()//native方法旋炒,用于返回當(dāng)前運(yùn)行時(shí)對(duì)象的Class對(duì)象步悠,使用了final關(guān)鍵字修飾,故不允許子類重寫瘫镇。
public native int hashCode() //native方法鼎兽,用于返回對(duì)象的哈希碼答姥,主要使用在哈希表中,比如JDK中的HashMap接奈。
public boolean equals(Object obj)//用于比較2個(gè)對(duì)象的內(nèi)存地址是否相等踢涌,String類對(duì)該方法進(jìn)行了重寫用戶比較字符串的值是否相等。
protected native Object clone() throws CloneNotSupportedException//naitive方法序宦,用于創(chuàng)建并返回當(dāng)前對(duì)象的一份拷貝睁壁。一般情況下,對(duì)于任何對(duì)象 x互捌,表達(dá)式 x.clone() != x 為true潘明,x.clone().getClass() == x.getClass() 為true。Object本身沒有實(shí)現(xiàn)Cloneable接口秕噪,所以不重寫clone方法并且進(jìn)行調(diào)用的話會(huì)發(fā)生CloneNotSupportedException異常钳降。
public String toString()//返回類的名字@實(shí)例的哈希碼的16進(jìn)制的字符串。建議Object所有的子類都重寫這個(gè)方法腌巾。
public final native void notify()//native方法遂填,并且不能重寫。喚醒一個(gè)在此對(duì)象監(jiān)視器上等待的線程(監(jiān)視器相當(dāng)于就是鎖的概念)澈蝙。如果有多個(gè)線程在等待只會(huì)任意喚醒一個(gè)吓坚。
public final native void notifyAll()//native方法,并且不能重寫灯荧。跟notify一樣礁击,唯一的區(qū)別就是會(huì)喚醒在此對(duì)象監(jiān)視器上等待的所有線程,而不是一個(gè)線程逗载。
public final native void wait(long timeout) throws InterruptedException//native方法哆窿,并且不能重寫。暫停線程的執(zhí)行厉斟。注意:sleep方法沒有釋放鎖挚躯,而wait方法釋放了鎖 。timeout是等待時(shí)間捏膨。
public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos參數(shù)秧均,這個(gè)參數(shù)表示額外時(shí)間(以毫微秒為單位号涯,范圍是 0-999999)目胡。 所以超時(shí)的時(shí)間還需要加上nanos毫秒誉己。
public final void wait() throws InterruptedException//跟之前的2個(gè)wait方法一樣,只不過該方法一直等待域蜗,沒有超時(shí)時(shí)間這個(gè)概念
protected void finalize() throws Throwable { }//實(shí)例被垃圾回收器回收的時(shí)候觸發(fā)的操作
2.5.3 Java序列化如果有些字段不想進(jìn)行序列化巨双,怎么辦
使用transient關(guān)鍵字噪猾,阻止實(shí)例中那些用此關(guān)鍵字修飾的的變量序列化;當(dāng)對(duì)象被反序列化時(shí)筑累,被 transient 修飾的變量值不會(huì)被持久化和恢復(fù)袱蜡。transient 只能修飾變量,不能修飾類和方法慢宗。
序列化和反序列化: Java 序列化是指把 Java 對(duì)象轉(zhuǎn)換為字節(jié)序列的過程便于保存在內(nèi)存坪蚁、文件、數(shù)據(jù)庫中镜沽,ObjectOutputStream類的 writeObject() 方法可以實(shí)現(xiàn)序列化敏晤。
Java 反序列化是指把字節(jié)序列恢復(fù)為 Java 對(duì)象的過程,ObjectInputStream 類的 readObject() 方法用于反序列化缅茉。
2.5.4 獲取用鍵盤輸入常用的兩種方法
- 通過Scanner
- 通過BufferReader
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
public class KeyboardInput {
public static void main(String[] args) throws IOException {
/*Scanner input = new Scanner(System.in);
String s = input.nextLine();
System.out.println(s);
input.close();*/
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
System.out.println(s);
}
}
3. Java核心技術(shù)
3.1 集合
3.1.1 Collections工具類和Arrays工具類常見方法總結(jié)
Collections工具類和Arrays工具類都是對(duì)原本數(shù)組直接進(jìn)行操作
Collections
- 排序操作
void reverse(List list)://反轉(zhuǎn)
void rotate(List list, int distance)//旋轉(zhuǎn)嘴脾。當(dāng)distance為正數(shù)時(shí),將list后distance個(gè)元素整體移到前面蔬墩。當(dāng)distance為負(fù)數(shù)時(shí)译打,將 list的前distance個(gè)元素整體移到后面。
void sort(List list) //按自然排序的升序排序
void shuffle(List list) //隨機(jī)排序
void swap(List list, int i , int j) //交換兩個(gè)索引位置的元素
//定制排序的用法
Collections.sort(arrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
具體的實(shí)例:
/**
* 排序的常用方法
*/
/* ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
System.out.println("原始數(shù)組:");
System.out.println(arrayList);
// void reverse(List list):反轉(zhuǎn)
Collections.reverse(arrayList);
System.out.println("Collections.reverse(arrayList):");
System.out.println(arrayList);
//void rotate(List list, int distance)//旋轉(zhuǎn)拇颅。當(dāng)distance為正數(shù)時(shí)扶平,
//將list后distance個(gè)元素整體移到前面。當(dāng)distance為負(fù)數(shù)時(shí)蔬蕊,將 list的前distance個(gè)元素整體移到后面。
Collections.rotate(arrayList,4);
System.out.println("Collections.rotate(arrayList, 4):");
System.out.println(arrayList);
//void sort(List list),按自然排序的升序排序
Collections.sort(arrayList);
System.out.println("Collections.sort(arrayList):");
System.out.println(arrayList);
//void shuffle(List list),隨機(jī)排序
Collections.shuffle(arrayList);
System.out.println("Collections.shuffle(arrayList):");
System.out.println(arrayList);
//void swap(List list, int i , int j),交換兩個(gè)索引位置的元素
Collections.swap(arrayList, 2, 5);
System.out.println("Collections.swap(arrayList, 2, 5):");
System.out.println(arrayList);
//定制排序的用法
Collections.sort(arrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
System.out.println("定制排序后: ");
System.out.println(arrayList);*/
- 查找/替換操作
int max(Collection coll)//根據(jù)元素的自然順序哥谷,返回最大的元素岸夯。 類比int min(Collection coll)
boolean replaceAll(List list, Object oldVal, Object newVal) //用新元素替換舊元素
int indexOfSubList(List list, List target) //統(tǒng)計(jì)target在list中第一次出現(xiàn)的索引,找不到則返回-1们妥,類比int lastIndexOfSubList(List source, list target).
Collections.binarySearch(arrayList, 7) //
具體實(shí)例:
/**
* 查找猜扮,替換操作
*/
/* ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
ArrayList<Integer> arrayList2 = new ArrayList<Integer>();
arrayList2.add(-3);
arrayList2.add(-5);
arrayList2.add(7);
System.out.println("原始數(shù)組:");
System.out.println(arrayList);
//int max(Collection coll)//根據(jù)元素的自然順序,返回最大的元素监婶。 類比int min(Collection coll)
System.out.println("Collections.max(arrayList):");
System.out.println(Collections.max(arrayList));
System.out.println("Collections.min(arrayList):");
System.out.println(Collections.min(arrayList));
//collections是對(duì)原本的數(shù)組直接進(jìn)行操作
//boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替換舊元素
System.out.println("Collections.replaceAll(arrayList, 3, -3):");
Collections.replaceAll(arrayList, 3, -3);
System.out.println(arrayList);
//int indexOfSubList(List list, List target)//統(tǒng)計(jì)target在list中第一次出現(xiàn)的索引旅赢,
//找不到則返回-1,類比int lastIndexOfSubList(List source, list target).
System.out.println("Collections.indexOfSubList(arrayList, arrayList2):");
System.out.println(Collections.indexOfSubList(arrayList, arrayList2));
System.out.println("Collections.binarySearch(arrayList, 7):");
// 對(duì)List進(jìn)行二分查找惑惶,返回索引煮盼,List必須是有序的
Collections.sort(arrayList);
System.out.println(Collections.binarySearch(arrayList, 7));*/
- 同步控制
- 提供了多個(gè)synchronizedXXX()方法
Collections提供了多個(gè)synchronizedXxx()方法·,該方法可以將指定集合包裝成線程同步的集合带污,從而解決多線程并發(fā)訪問集合時(shí)的線程安全問題僵控。
我們知道 HashSet,TreeSet鱼冀,ArrayList,LinkedList,HashMap,TreeMap 都是線程不安全的报破。Collections提供了多個(gè)靜態(tài)方法可以把他們包裝成線程同步的集合悠就。
最好不要用下面這些方法,效率非常低充易,需要線程安全的集合類型時(shí)請(qǐng)考慮使用 JUC 包下的并發(fā)集合梗脾。 - 設(shè)置不可變集合
emptyXxx(): 返回一個(gè)空的、不可變的集合對(duì)象盹靴,此處的集合可以是:List炸茧,Set,Map鹉究。
singletonXxx(): 返回一個(gè)只包含指定對(duì)象(只有一個(gè)或一個(gè)元素)的不可變的集合對(duì)象宇立,此處的集合可以是:List,Set自赔,Map妈嘹。
unmodifiableXxx(): 返回指定集合對(duì)象的不可變視圖,此處的集合可以是:List绍妨,Set润脸,Map。
上面三類方法的參數(shù)是原有的集合對(duì)象他去,返回值是該集合的”只讀“版本毙驯。
- 提供了多個(gè)synchronizedXXX()方法
/**
* collections設(shè)置不可變集合
*/
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
HashSet<Integer> integers1 = new HashSet<>();
integers1.add(1);
integers1.add(3);
integers1.add(2);
Map scores = new HashMap();
scores.put("語文" , 80);
scores.put("Java" , 82);
//Collections.emptyXXX();創(chuàng)建一個(gè)空的、不可改變的XXX對(duì)象
List<Object> list = Collections.emptyList();
System.out.println(list);//[]
Set<Object> objects = Collections.emptySet();
System.out.println(objects);//[]
Map<Object, Object> objectObjectMap = Collections.emptyMap();
System.out.println(objectObjectMap);//{}
//!!!返回的內(nèi)容只包含一個(gè)指定對(duì)象
//singletonXxx(): 返回一個(gè)只包含指定對(duì)象(只有一個(gè)或一個(gè)元素)的不可變的集合對(duì)象灾测,
//此處的集合可以是:List爆价,Set,Map媳搪。
List<Map<String, String>> mapList = Collections.singletonList(scores);
System.out.println(mapList);
List<ArrayList<Integer>> arrayLists = Collections.singletonList(arrayList);
System.out.println(arrayLists);//[[-1, 3, 3, -5, 7, 4, -9, -7]]
//創(chuàng)建一個(gè)只有一個(gè)元素铭段,且不可改變的Set對(duì)象
Set<ArrayList<Integer>> singleton = Collections.singleton(arrayList);
System.out.println(singleton);//[[-1, 3, 3, -5, 7, 4, -9, -7]]
Map<String, String> nihao = Collections.singletonMap("1", "nihao");
System.out.println(nihao);//{1=nihao}
//unmodifiableXXX(); 創(chuàng)建普通XXX對(duì)象對(duì)應(yīng)的不可變版本
List<Integer> integers = Collections.unmodifiableList(arrayList);
System.out.println(integers);//[-1, 3, 3, -5, 7, 4, -9, -7]
Set<Integer> integers2 = Collections.unmodifiableSet(integers1);
System.out.println(integers2);//[1, 2, 3]
Map<Object, Object> objectObjectMap2 = Collections.unmodifiableMap(scores);
System.out.println(objectObjectMap2);//{Java=82, 語文=80}
//添加會(huì)出現(xiàn)異常
/*list.add(1);
arrayLists.add(arrayList);
integers.add(1);*/
Arrays
- 排序: sort();查找: binarySearch()秦爆;比較: equals()序愚;填充: fill();轉(zhuǎn)列表: asList()等限;轉(zhuǎn)字符串: toString()爸吮;復(fù)制: copyOf()
具體實(shí)例
import java.util.Arrays;
import java.util.List;
/**
* arrays類的常見方法
*/
public class arraysMethods {
public static void main(String[] args) {
//1. 排序: sort()
// *************排序 sort****************
int a[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
// sort(int[] a)方法按照數(shù)字順序排列指定的數(shù)組。
Arrays.sort(a);
System.out.println("Arrays.sort(a):");
for (int i : a) {
System.out.print(i);
}
// 換行
System.out.println();
// sort(int[] a,int fromIndex,int toIndex)按升序排列數(shù)組的指定范圍
int b[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
Arrays.sort(b, 2, 6);
System.out.println("Arrays.sort(b, 2, 6):");
for (int i : b) {
System.out.print(i);
}
// 換行
System.out.println();
int c[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
// parallelSort(int[] a) 按照數(shù)字順序排列指定的數(shù)組(并行的)望门。同sort方法一樣也有按范圍的排序
Arrays.parallelSort(c);
System.out.println("Arrays.parallelSort(c):");
for (int i : c) {
System.out.print(i);
}
// 換行
System.out.println();
// parallelSort給字符數(shù)組排序形娇,sort也可以
char d[] = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.parallelSort(d);
System.out.println("Arrays.parallelSort(d):");
for (char d2 : d) {
System.out.print(d2);
}
// 換行
System.out.println();
//Collections可以對(duì)字符串進(jìn)行排序
String[] strs = { "abcdehg", "abcdefg", "abcdeag" };
Arrays.sort(strs);
System.out.println(Arrays.toString(strs));//[abcdeag, abcdefg, abcdehg]
/**
* 2.查找 binarySearch
*/
// *************查找 binarySearch()****************
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
// 排序后再進(jìn)行二分查找,否則找不到
Arrays.sort(e);
System.out.println("Arrays.sort(e)" + Arrays.toString(e));
System.out.println("Arrays.binarySearch(e, 'c'):");
int s = Arrays.binarySearch(e, 'c');
System.out.println("字符c在數(shù)組的位置:" + s);
/**
* 3. 比較: equals()
*/
// *************比較 equals****************
// *************比較 equals****************
char[] e1 = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
char[] f = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
/*
* 元素?cái)?shù)量相同筹误,并且相同位置的元素相同埂软。 另外,如果兩個(gè)數(shù)組引用都是null,則它們被認(rèn)為是相等的 勘畔。
*/
// 輸出true
System.out.println("Arrays.equals(e, f):" + Arrays.equals(e1, f));
/**
* 4.填充: fill(), 批量進(jìn)行初始化
*/
// *************填充fill(批量初始化)****************
int[] g = { 1, 2, 3, 3, 3, 3, 6, 6, 6 };
// 數(shù)組中所有元素重新分配值
Arrays.fill(g, 3);
System.out.println("Arrays.fill(g, 3):");
// 輸出結(jié)果:333333333
for (int i : g) {
System.out.print(i);
}
// 換行
System.out.println();
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
// 數(shù)組中指定范圍元素重新分配值
Arrays.fill(h, 0, 2, 9);
System.out.println("Arrays.fill(h, 0, 2, 9);:");
// 輸出結(jié)果:993333666
for (int i : h) {
System.out.print(i);
}
System.out.println();
/**
* 轉(zhuǎn)列表 asList()
*/
// *************轉(zhuǎn)列表 asList()****************
/*
* 返回由指定數(shù)組支持的固定大小的列表所灸。
* (將返回的列表更改為“寫入數(shù)組”。)該方法作為基于數(shù)組和基于集合的API之間的橋梁炫七,與Collection.toArray()相結(jié)合爬立。
* 返回的列表是可序列化的,并實(shí)現(xiàn)RandomAccess万哪。
* 此方法還提供了一種方便的方式來創(chuàng)建一個(gè)初始化為包含幾個(gè)元素的固定大小的列表如下:
*/
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
System.out.println(stooges);
/**
* 轉(zhuǎn)字符串 toString()
*/
// *************轉(zhuǎn)字符串 toString()****************
/*
* 返回指定數(shù)組的內(nèi)容的字符串表示形式侠驯。
*/
char[] k = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
System.out.println(Arrays.toString(k));// [a, f, b, c, e, A, C, B]
/**
* 復(fù)制 copyOf()
*/
// *************復(fù)制 copy****************
// copyOf 方法實(shí)現(xiàn)數(shù)組復(fù)制,h為數(shù)組,6為復(fù)制的長(zhǎng)度
int[] h2 = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
int i[] = Arrays.copyOf(h2, 6);
System.out.println("Arrays.copyOf(h2, 6);:");
// 輸出結(jié)果:123333
for (int j : i) {
System.out.print(j);
}
// 換行
System.out.println();
// copyOfRange將指定數(shù)組的指定范圍復(fù)制到新數(shù)組中
int j[] = Arrays.copyOfRange(h2, 6, 11);
System.out.println("Arrays.copyOfRange(h2, 6, 11):");
// 輸出結(jié)果66600(h數(shù)組只有9個(gè)元素這里是從索引6到索引11復(fù)制所以不足的就為0)
for (int j2 : j) {
System.out.print(j2);
}
// 換行
System.out.println();
}
}
3.2 異常
3.2.1 Java異常類層次結(jié)構(gòu)圖
所有的異常都有一個(gè)共同的祖先 java.lang 包中的 Throwable 類奕巍。Throwable: 有兩個(gè)重要的子類:Exception(異常) 和 Error(錯(cuò)誤) 吟策,二者都是 Java 異常處理的重要子類,各自都包含大量子類的止。異常和錯(cuò)誤的區(qū)別:異常能被程序本身處理檩坚,錯(cuò)誤是無法處理的。
- error:
Error(錯(cuò)誤):是程序無法處理的錯(cuò)誤诅福,表示運(yùn)行應(yīng)用程序中較嚴(yán)重問題匾委。大多數(shù)錯(cuò)誤與代碼編寫者執(zhí)行的操作無關(guān),而表示代碼運(yùn)行時(shí) JVM(Java 虛擬機(jī))出現(xiàn)的問題氓润。例如赂乐,Java 虛擬機(jī)運(yùn)行錯(cuò)誤(Virtual MachineError),當(dāng) JVM 不再有繼續(xù)執(zhí)行操作所需的內(nèi)存資源時(shí)咖气,將出現(xiàn) OutOfMemoryError挨措。這些異常發(fā)生時(shí),Java 虛擬機(jī)(JVM)一般會(huì)選擇線程終止崩溪。
這些錯(cuò)誤表示故障發(fā)生于虛擬機(jī)自身运嗜、或者發(fā)生在虛擬機(jī)試圖執(zhí)行應(yīng)用時(shí),如 Java 虛擬機(jī)運(yùn)行錯(cuò)誤(Virtual MachineError)悯舟、類定義錯(cuò)誤(NoClassDefFoundError)等。這些錯(cuò)誤是不可查的砸民,因?yàn)樗鼈冊(cè)趹?yīng)用程序的控制和處理能力之 外抵怎,而且絕大多數(shù)是程序運(yùn)行時(shí)不允許出現(xiàn)的狀況。對(duì)于設(shè)計(jì)合理的應(yīng)用程序來說岭参,即使確實(shí)發(fā)生了錯(cuò)誤反惕,本質(zhì)上也不應(yīng)該試圖去處理它所引起的異常狀況。在 Java 中演侯,錯(cuò)誤通過 Error 的子類描述姿染。 - exception:
Exception(異常):是程序本身可以處理的異常。Exception 類有一個(gè)重要的子類 RuntimeException。RuntimeException 異常由 Java 虛擬機(jī)拋出悬赏。NullPointerException(要訪問的變量沒有引用任何對(duì)象時(shí)狡汉,拋出該異常)、ArithmeticException(算術(shù)運(yùn)算異常闽颇,一個(gè)整數(shù)除以 0 時(shí)盾戴,拋出該異常)和 ArrayIndexOutOfBoundsException (下標(biāo)越界異常)。
3.2.2 Throwable類常用方法
- public string getMessage():返回異常發(fā)生時(shí)的簡(jiǎn)要描述
- public string toString():返回異常發(fā)生時(shí)的詳細(xì)信息
- public string getLocalizedMessage():返回異常對(duì)象的本地化信息兵多。使用 Throwable 的子類覆蓋這個(gè)方法尖啡,可以生成本地化信息。如果子類沒有覆蓋該方法剩膘,則該方法返回的信息與 getMessage()返回的結(jié)果相同
- public void printStackTrace():在控制臺(tái)上打印 Throwable 對(duì)象封裝的異常信息
3.2.3 try-catch-finally
- try塊:用于捕獲異常衅斩。其后可接零個(gè)或多個(gè) catch 塊,如果沒有 catch 塊怠褐,則必須跟一個(gè) finally 塊畏梆。
- catch塊: 用于處理 try 捕獲到的異常。
- finally塊:無論是否捕獲或處理異常惫搏,finally 塊里的語句都會(huì)被執(zhí)行具温。當(dāng)在 try 塊或 catch 塊中遇到 return 語句時(shí),finally 語句塊將在方法返回之前被執(zhí)行筐赔。
在以下四種特殊情況下铣猩,finally塊不會(huì)被執(zhí)行- 在 finally 語句塊第一行發(fā)生了異常。 因?yàn)樵谄渌熊罘幔琭inally 塊還是會(huì)得到執(zhí)行
- 在前面的代碼中用了 System.exit(int)已退出程序达皿。 exit 是帶參函數(shù) ;若該語句在異常語句之后贿肩,finally 會(huì)執(zhí)行
- 程序所在的線程死亡
- 關(guān)閉CPU
注意
當(dāng) try 語句和 finally 語句中都有 return 語句時(shí)峦椰,在方法返回之前,finally 語句的內(nèi)容將被執(zhí)行汰规,并且 finally 語句的返回值將會(huì)覆蓋原始的返回值汤功。
3.2.4 使用 try-with-resources
來代替try-catch-finally
- 面對(duì)必須要關(guān)閉的資源,我們總是應(yīng)該優(yōu)先使用try-with-resources而不是try-finally溜哮。隨之產(chǎn)生的代碼更簡(jiǎn)短滔金,更清晰,產(chǎn)生的異常對(duì)我們也更有用茂嗓。try-with-resources語句讓我們更容易編寫必須要關(guān)閉的資源的代碼餐茵。 Java 中類似于InputStream瓢湃、OutputStream 亮钦、Scanner 、PrintWriter等的資源都需要我們調(diào)用close()方法來手動(dòng)關(guān)閉驻啤。使用try-with-resources語句就無需手動(dòng)關(guān)閉資源了。
- 當(dāng)然多個(gè)資源需要關(guān)閉的時(shí)候道批,使用 try-with-resources 實(shí)現(xiàn)起來也非常簡(jiǎn)單错英,如果你還是用try-catch-finally可能會(huì)帶來很多問題。
通過使用分號(hào)分隔屹徘,可以在try-with-resources塊中聲明多個(gè)資源走趋。
示例
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
3.3 多線程
3.3.1 簡(jiǎn)述線程、程序噪伊、進(jìn)程的基本概念簿煌;以及他們之間的關(guān)系
線程
線程與進(jìn)程相似,但線程是一個(gè)比進(jìn)程更小的執(zhí)行單位鉴吹。一個(gè)進(jìn)程在其執(zhí)行的過程中可以產(chǎn)生多個(gè)線程姨伟。與進(jìn)程不同的是同類的多個(gè)線程共享同一塊內(nèi)存空間和一組系統(tǒng)資源,所以系統(tǒng)在產(chǎn)生一個(gè)線程豆励,或是在各個(gè)線程之間作切換工作時(shí)夺荒,負(fù)擔(dān)要比進(jìn)程小得多,線程也被稱為輕量級(jí)進(jìn)程良蒸。
程序
程序是含有指令和數(shù)據(jù)的文件技扼,被存儲(chǔ)在磁盤或其他的數(shù)據(jù)存儲(chǔ)設(shè)備中,也就是說程序是靜態(tài)的代碼嫩痰。
進(jìn)程
進(jìn)程是程序的一次執(zhí)行過程剿吻,是系統(tǒng)運(yùn)行程序的基本單位,因此進(jìn)程是動(dòng)態(tài)的串纺。系統(tǒng)運(yùn)行一個(gè)程序即是一個(gè)進(jìn)程從創(chuàng)建丽旅,運(yùn)行到消亡的過程。
一個(gè)進(jìn)程就是一個(gè)執(zhí)行中的程序纺棺,它在計(jì)算機(jī)中一個(gè)指令接著一個(gè)指令地執(zhí)行著榄笙,同時(shí),每個(gè)進(jìn)程還占有某些系統(tǒng)資源如 CPU 時(shí)間祷蝌,內(nèi)存空間茅撞,文件,輸入輸出設(shè)備的使用權(quán)等等巨朦。換句話說米丘,當(dāng)程序在執(zhí)行時(shí),將會(huì)被操作系統(tǒng)載入內(nèi)存中罪郊。
進(jìn)程和線程的區(qū)別
線程是進(jìn)程劃分成的更小的運(yùn)行單位。線程和進(jìn)程最大的不同在于基本上各進(jìn)程是獨(dú)立的尚洽,而各線程則不一定悔橄,因?yàn)橥贿M(jìn)程中的線程極有可能會(huì)相互影響。
從另一角度來說,進(jìn)程屬于操作系統(tǒng)的范疇癣疟,主要是同一段時(shí)間內(nèi)挣柬,可以同時(shí)執(zhí)行一個(gè)以上的程序,而線程則是在同一程序內(nèi)幾乎同時(shí)執(zhí)行一個(gè)以上的程序段睛挚。
3.3.2 線程的基本狀態(tài)
Java線程運(yùn)行的生命周期
Java線程在運(yùn)行的生命周期中的指定時(shí)刻只能處于6種狀態(tài)的其中一個(gè)狀態(tài)邪蛔。
線程之間的狀態(tài)變遷
線程在生命周期中并不是固定處于某一種狀態(tài),隨著代碼執(zhí)行在不同狀態(tài)之間切換扎狱,狀態(tài)變遷圖如下圖所示:
-
初始和運(yùn)行狀態(tài)
線程創(chuàng)建之后它將處于 NEW(新建) 狀態(tài)侧到,調(diào)用 start() 方法后開始運(yùn)行,線程這時(shí)候處于 READY(可運(yùn)行) 狀態(tài)淤击〗晨梗可運(yùn)行狀態(tài)的線程獲得了 cpu 時(shí)間片(timeslice)后就處于 RUNNING(運(yùn)行) 狀態(tài)。
操作系統(tǒng)隱藏 Java 虛擬機(jī)(JVM)中的 READY 和 RUNNING 狀態(tài)污抬,它只能看到 RUNNABLE 狀態(tài)汞贸,所以 Java 系統(tǒng)一般將這兩個(gè)狀態(tài)統(tǒng)稱為 RUNNABLE(運(yùn)行中) 狀態(tài) 。
線程運(yùn)行狀態(tài) 等待印机、超時(shí)等待矢腻、阻塞和終止?fàn)顟B(tài)
當(dāng)線程執(zhí)行 wait()方法之后,線程進(jìn)入 WAITING(等待)狀態(tài)射赛。進(jìn)入等待狀態(tài)的線程需要依靠其他線程的通知才能夠返回到運(yùn)行狀態(tài)多柑,而 TIME_WAITING(超時(shí)等待) 狀態(tài)相當(dāng)于在等待狀態(tài)的基礎(chǔ)上增加了超時(shí)限制,比如通過 sleep(long millis)方法或 wait(long millis)方法可以將 Java 線程置于 TIMED WAITING 狀態(tài)咒劲。當(dāng)超時(shí)時(shí)間到達(dá)后 Java 線程將會(huì)返回到 RUNNABLE 狀態(tài)顷蟆。當(dāng)線程調(diào)用同步方法時(shí),在沒有獲取到鎖的情況下腐魂,線程將會(huì)進(jìn)入到 BLOCKED(阻塞) 狀態(tài)帐偎。線程在執(zhí)行 Runnable 的run()方法之后將會(huì)進(jìn)入到 TERMINATED(終止) 狀態(tài)。
3.4 文件與I/O流
3.4.1 Java中的IO流分為幾種
Java中IO流分為幾種
- 按照流的流向分蛔屹,可以分為輸入流和輸出流
- 按照操作單元分削樊,可以分為字節(jié)流和字符流
- 按照流的角色分為節(jié)點(diǎn)流和處理流
節(jié)點(diǎn)流:可以從或向一個(gè)特定的地方(節(jié)點(diǎn))讀寫數(shù)據(jù),如FileReader兔毒。
處理流:是對(duì)一個(gè)已經(jīng)存在的流的連接和封裝漫贞,通過所封裝的流的功能調(diào)用實(shí)現(xiàn)數(shù)據(jù)讀寫。如BufferedReader育叁。處理流的構(gòu)造方法總是要帶一個(gè)其他的流對(duì)象做參數(shù)迅脐。
Java IO流的類是從下面的4個(gè)抽象類的基類中派生出來的。
- InputStream/Reader: 所有輸入流的基類豪嗽,前者是字節(jié)輸入流谴蔑,后者是字符輸入流
- OutputStream/Writer: 所有輸出流的基類豌骏,前者是字節(jié)輸出流,后者是字符輸出流隐锭。
按操作方式分類結(jié)構(gòu)圖
按操作對(duì)象分類結(jié)構(gòu)圖
既然有了字節(jié)流窃躲,為什么還要有字符流
字符流是由 Java 虛擬機(jī)將字節(jié)轉(zhuǎn)換得到的,問題就出在這個(gè)過程還算是非常耗時(shí)钦睡,并且蒂窒,如果我們不知道編碼類型就很容易出現(xiàn)亂碼問題。所以荞怒, I/O 流就干脆提供了一個(gè)直接操作字符的接口洒琢,方便我們平時(shí)對(duì)字符進(jìn)行流操作。如果音頻文件挣输、圖片等媒體文件用字節(jié)流比較好纬凤,如果涉及到字符的話使用字符流比較好。
BIO, NIO和AIO的區(qū)別