Java 語言支持的類型分為兩類:基本類型和引用類型。整型(byte 1, short 2, int 4, long 8)钦讳、字符型(char 2/4)囱桨、浮點型(float 4, double 8)和布爾型(boolean 1)都是基本類型膛檀。
Java 字符類型采用 UTF-16 編碼芒粹,UTF-16 以雙字節(jié)為編碼單位,一個字符占 2 字節(jié)或 4 字節(jié)废菱。中文和英文在 UTF-16 中都占 2 字節(jié)技矮。
char c = '\u9999';
注意,如果使用一個巨大的整數(shù)值(超出 int 的表示范圍)殊轴,Java 不會自動把這個整數(shù)當(dāng)成 long 來處理——除非顯式的加上 l 或 L:
long longValue = 99999999999999; // 錯誤
long longValue = 99999999999999L; // 正確
同理衰倦,也不能將不帶 f 或 F 的浮點型賦給 float 類型:
float f = 3.14; //錯誤
float f = 3.14F; //正確
Java 的 switch 語句原來支持 byte、short旁理、char樊零、int 四種類型,Java 7 增加了 String 類型。另驻襟,一直不支持 boolean 類型夺艰。
Java 中的 break 可以直接跳出外層循環(huán),有時候還是蠻有用的:
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if ( xxx ){
break outer;
}
}
}
Java 中沉衣,數(shù)組也是一種數(shù)據(jù)類型郁副,一種引用類型。
String[] ss = new String[]{"ab", "cd", "ef"}; // 靜態(tài)初始化
String[] ss = {"ab", "cd", "ef"}; // 簡化的靜態(tài)初始化
String[] ss = new String[3]; // 動態(tài)初始化豌习,系統(tǒng)賦類型默認值
Java 5 提供的 foreach 循環(huán):
for (String s : ss) {
System.out.println(s);
}
Java 的核心概念(區(qū)別于C/C++)——引用存谎。REMEMBER.
面向?qū)ο?OO
public, protected, private, default(無); abstract, final; static
.對象,引用肥隆;引用在棧區(qū)既荚,對象在堆區(qū);
this
.靜態(tài)(
static
)成員不能訪問非靜態(tài)成員巷屿;但非靜態(tài)成員可以訪問靜態(tài)成員;外部類只能用
public
修飾或無修飾墩虹;比如用
private
修飾的name
成員變量嘱巾,那么就不能p.name
來訪問。p.name
是要把name
屬性直接暴露出來诫钓!Java 引入包(package)制旬昭,提供了類的多層命名空間,用于解決類的命名沖突菌湃、類文件管理等問題问拘。
同一個類中一個構(gòu)造方法調(diào)用另一個構(gòu)造方法:
this(xx, xx);
調(diào)用父類構(gòu)造器:super(xxx, xxx);
子類構(gòu)造器執(zhí)行前會先調(diào)用父類構(gòu)造器,一直向上到
java.lang.Object
的構(gòu)造器惧所,這個過程必須完成骤坐,哪里被中斷哪里就錯了。(要么你顯式super
調(diào)用下愈,要么系統(tǒng)自動調(diào)用父無參構(gòu)造器)結(jié)果是纽绍,創(chuàng)建任何類的對象,最先執(zhí)行的都是java.lang.Object
的構(gòu)造器势似。
比如:子類中沒有用super
顯示調(diào)用父類構(gòu)造器拌夏,而父類也沒有無參構(gòu)造器,這時就會編譯出錯相同類型的變量履因,調(diào)用同一個方法時呈現(xiàn)出多種不同的行為特征障簿,這就是多態(tài);
-
Java
引用變量有兩個類型:編譯時類型和運行時類型栅迄。編譯時類型是變量聲明時的類型站故;運行時類型是實際賦給該變量的對象類型;編譯時類型和運行時類型不同毅舆,就可能出現(xiàn)多態(tài)世蔗。-
BaseClass b = new SubClass();
//編譯時類型和運行時類型不同端逼; -
b.selfIntroduce();
//會調(diào)用運行時類型的方法,即子類的方法污淋;
-
引用變量在編譯階段只能調(diào)用其編譯時類型所具有的方法顶滩,但在運行時則執(zhí)行它運行時類型所具有的方法;
因此寸爆,代碼中b.subClassMethod();
是編譯不通過的礁鲁。看起來像廢話。如果想把某些類設(shè)置成最終類赁豆,即不能被當(dāng)成父類仅醇,則可以使用
final
修飾這個類;除此之外魔种,使用private
修飾這個類的所有構(gòu)造器析二,從而保證子類無法調(diào)用該類的構(gòu)造器,也就無法繼承該類节预,也無法創(chuàng)建該類的實例叶摄。對于把所有構(gòu)造器都設(shè)為private
的類而言,可另外提供一個靜態(tài)方法安拟,用于創(chuàng)建該類的實例蛤吓。繼承與組合
is-a
vshas-a
Java 對實例變量進行初始化的順序是:先執(zhí)行 初始化塊 或 聲明實例變量 時指定的初始值,再執(zhí)行構(gòu)造器里指定的初始值糠赦。
當(dāng)
JVM
第一次使用某個類時会傲,系統(tǒng)會首先為該類的所有靜態(tài)成員變量分配內(nèi)存,接著對這些靜態(tài)成員變量進行初始化拙泽,即執(zhí)行靜態(tài)初始化塊代碼static{}
或 聲明靜態(tài)成員變量時指定的初始值淌山。final
變量不能重新賦值,子類不能復(fù)寫父類的final
方法顾瞻,final
類不能派生子類艾岂。通過使用朋其,不可變會讓系統(tǒng)更加安全王浴。final
關(guān)鍵字,可以實現(xiàn)不可變類
不可變類不是僅僅靠
final
來實現(xiàn)的!不可變類是用代碼控制的梅猿!看下面氓辣!
final
修飾的成員變量必須顯式的指定初始值,否則這些成員變量的值就一直是系統(tǒng)默認分配的0, '\u0000', false, null
袱蚓。這里的“顯式指定”可以在聲明變量時钞啸,可以在初始化塊里,可以在構(gòu)造器里。-
String
和其他類的相互轉(zhuǎn)化:-
String.valueOf()
或直接value + ""
体斩; -
Integer.parseInt(), Double.parseDouble()...
梭稚;
-
-
常量池:專門用于管理在編譯時被確定并被保存在已編譯的.class文件中的一些數(shù)據(jù)。它包括類絮吵、方法弧烤、接口中的常量,還包括字符串常量蹬敲。
常量池中的對象暇昂,系統(tǒng)會自動保留對他們的強引用——所以不會輕易地被垃圾回收; -
new String("hello");
會產(chǎn)生兩個字符串對象:一個字符串常量"hello"
對象在常量池中伴嗡;一個在運行時的內(nèi)存堆中急波; - 有抽象方法的類一定要定義為抽象類,抽象類不一定要有抽象方法瘪校。抽象方法不能有方法體澄暮,抽象類不能實例化,只能作為父類被其他子類繼承阱扬;
- 從語義的角度來看泣懊,抽象類是從多個具體類中抽象出來的父類,它具有更高層次的抽象价认,抽象類避免了子類設(shè)計的隨意性嗅定。
- 將抽象進行的跟徹底——接口——接口里所有方法都是抽象方法自娩。
-
匿名內(nèi)部類
- 被匿名內(nèi)部類訪問的局部變量自動變成
final
修飾用踩。 -
native
關(guān)鍵字的意思是該方法是由其他語言實現(xiàn)的; -
Lambda
表達式的類型是函數(shù)式接口忙迁; - 函數(shù)式接口:只包含一個抽象方法的接口——可以有其他方法脐彩,但只能有一個抽象方法;
Lambda
表達式也是對象姊扔; -
Lambda
表達式的本質(zhì):使用簡潔的語法來創(chuàng)建函數(shù)式接口的實例惠奸。 -
JAR
文件——Java Archive File
———— Java 檔案文件。
就是壓縮文件而已恰梢,只不過是 Java 平臺內(nèi)部處理的標(biāo)準佛南。和普通 zip 或 rar 格式的壓縮文件沒什么區(qū)別,只不過多一個MANIFEST.MF
清單文件嵌言。作用和壓縮文件是一樣的:打包壓縮在一起嗅回,便于傳輸和處理。
工具類
java.util.Arrays
-
asList(T... a)
:好像只有這一個方法可以把任意個元素組成 List 摧茴;否則你只有 new 一個 List 然后 一個個 add 了: - 或者绵载,后面的
Collections.addAll(Collection c, T... a)
; -
binarySearch(int[] a, int key)
:各種版本的二分查找(數(shù)組必須已有序); -
copyOf & copyOfRange
:復(fù)制一個數(shù)組娃豹; -
equals(int[] a, int[] a2)
:比較兩個數(shù)組(的每一個元素)焚虱; -
deepEquals(Object[] a1, Object[] a2)
:Returns true if the two specified arrays are deeply equal to one another. -
toString(int[] a)
:友好的輸出數(shù)組(元素);直接用System.out.println(int[] a)
輸出的是哈希值懂版; -
deepToString(Object[] a)
:Returns a string representation of the deep contents of the specified array. -
fill(int[] a, int val)
:填充數(shù)組內(nèi)容鹃栽; -
hashCode(int[] a)
:Returns a hash code based on the contents of the specified array. -
deepHashCode(Object[] a)
:Returns a hash code based on the "deep contents" of the specified array. -
parallelPrefix(int[] array, IntBinaryOperator op)
:并行,累積(cumulate)計算定续;Cumulates, in parallel, each element of the given array in place, using the supplied function. For example if the array initially holds [2, 1, 0, 3]
and the operation performs addition, then upon return the array holds [2, 3, 3, 6].
Parallel prefix computation is usually more efficient than sequential loops for large arrays. -
setAll(int[] array, IntUnaryOperator generator)
:和上面的fill
很像谍咆,但這里用的是生成函數(shù)generator
,而不是靜態(tài)值私股;
generator - a function accepting an index and producing the desired value for that position
可以用lambda
表達式摹察。
-
parallelSetAll(int[] array, IntUnaryOperator generator)
:并行版; -
sort(int[] a)
:排序倡鲸; -
sort(T[] a, Comparator<? super T> c)
:自定義比較函數(shù)供嚎; -
parallelSort(int[] a)
:并行的排序; -
spliterator(double[] array)
:spliterator, splitable iterator峭状,可分割迭代器克滴,Java 8 新增的用于 并 行 遍 歷 數(shù)據(jù)而設(shè)計的迭代器。見這里优床。
-
stream(double[] array)
:Java 8 流式編程接口劝赔,Returns a sequential DoubleStream with the specified array as its source.**
java.util.Scanner
一個文本掃描器,可以掃描File/Path(都指向一個文件), InputStream, String
等實現(xiàn)了Readable
接口的對象胆敞;
A simple text scanner which can parse primitive types and strings using regular expressions.
A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods.
String input = "1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*");
System.out.println(s.nextInt());
System.out.println(s.nextInt());
System.out.println(s.next());
System.out.println(s.next());
s.close();
1
2
red
blue
-
hasNext()
:有下一個字符串着帽;默認以空白符為Delimiter
; -
next()
:獲取下一個字符串移层; -
hasNextBoolean/Byte/Int/Long/Double/Float
:下一個仍翰。。观话。 -
hasNextLine()
:還有下一行予借;+nextLine()
; -
useDelimiter(String pattern)
:自定義分隔符(Delimiter
)频蛔; -
useRadix(int)
:自定義數(shù)字的進制:
當(dāng)讀到不符合要求的內(nèi)容時灵迫,hasNextXXX()
方法靜默退出,不拋出任何異常:
Scanner
還支持與正則表達式配合使用晦溪。
System 類
System 類代表當(dāng)前 Java 程序的運行平臺被丧,程序不能創(chuàng)建 System 類的對象握恳;System 類提供了代表標(biāo)準輸入啡捶、標(biāo)準輸出卸勺、標(biāo)準錯誤的類變量浆劲,并提供了一些靜態(tài)方法用于訪問環(huán)境變量、系統(tǒng)屬性哀澈,還提供了加載文件和動態(tài)鏈接庫的方法牌借。
-
System.exit(int status)
:退出虛擬機; -
System.getenv()
:獲取環(huán)境變量割按; -
System.getProperties()
:獲取系統(tǒng)屬性膨报; -
System.gc()
:通知系統(tǒng)進行垃圾清理; -
System.runFinalization()
:通知系統(tǒng)進行資源回收适荣;(貌似都沒什么用) System.currentTimeMillis()
:當(dāng)前時間毫秒现柠;-
System.in System.out System.err
:三個標(biāo)準流; -
System.setIn() setOut() setErr()
:重定向弛矛; -
System.identityHashCode(Object X)
:根據(jù)對象的內(nèi)存地址計算得到的 Hash 值够吩;當(dāng)某個類的hashCode()
方法被重寫后,該類實例的hashCode()
方法就不能唯一的標(biāo)識該對象丈氓;此時就可以用identityHashCode()
方法周循。
Runtime 類
Runtime 類代表 Java 程序的運行時環(huán)境,每個 Java 程序都有一個與之對應(yīng)的 Runtime 實例万俗,應(yīng)用程序通過該對象與其運行時環(huán)境相連湾笛。程序不能自己創(chuàng)建 Runtime 實例,但可以通過 getRuntime()
方法獲取與之關(guān)聯(lián)的 Runtime 對象闰歪;
- 與 System 類類似的是 Runtime 類也提供了
gc()
方法和runFinalization()
方法來通知系統(tǒng)進行垃圾回收嚎研、資源清理,并提供了load(String filename) 和 loadLibrary(String libname)
方法來加載文件和動態(tài)鏈接庫库倘。 -
runtime.exec("notepad.exe")
:運行CMD命令临扮!和 Python 一樣!
java.util.Objects
Java 7 新增了一個 Objects 工具類于樟,它提供了一些工具方法來操作對象公条,這些工具方法大多是“空指針”安全的拇囊。比如你不能確定一個引用變量是否為 null迂曲,如果貿(mào)然調(diào)用該變量的 toString() 方法,則可能引發(fā) NullPointerException 異常寥袭;但如果使用 Objects 類提供的 toString() 方法路捧,就不會引發(fā)空指針異常——當(dāng) o 為 null 時传黄,程序就返回一個 “null” 字符串杰扫。
-
Objects.requireNonNull(T obj)
:當(dāng) obj 為 null 時拋出 NullPointerException 異常。
java.lang.String
public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence
不可被繼承膘掰;章姓;
The class String includes methods for examining individual characters of the sequence, for comparing strings, for searching strings, for extracting substrings, and for creating a copy of a string with all characters translated to uppercase or to lowercase.
-
String(byte[] bytes, Charset charset)
:Constructs a new String by decoding the specified array of bytes using the specified charset. -
String(char[] value)
佳遣,String(String original)
,String(StringBuffer buffer)
凡伊,String(StringBuilder builder)
零渐; -
length()/isEmpty()/toLowerCase()/toUpperCase()
:基本操作; -
trim()
:去除收尾的空白符系忙; -
charAt(int index)
:獲取字符串中的單個字節(jié)诵盼; -
int compareTo(String anotherString)/compareToIgnoreCase(String str)
:比較字符串;返回第一個不同字符的差值银还; -
concat(String str)
:連接风宁,即加到尾部; -
boolean contains(CharSequence s)
:包含蛹疯;查找字符串戒财; -
equals(Object anObject)/equalsIgnoreCase(String anotherString)
:比較字符串; -
static String format(String format, Object... args)
:格式化字符串捺弦,類似于 C 語言的printf()
或 Python 的%
固翰; -
byte[] getBytes(Charset charset)/getBytes(String charsetName)
:Encodes this String into a sequence of bytes using the given charset, storing the result into a new byte array. -
getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
:字節(jié)數(shù)組; -
char[] toCharArray()
:返回一個 char 數(shù)組羹呵,包含相同內(nèi)容骂际; -
hashCode()
:String 類重寫了hashCode()
方法!記赘曰丁歉铝! -
int indexOf(int ch)/indexOf(String str[, int fromIndex])/lastIndexOf(int ch[, int fromIndex])/lastIndexOf(String str[, int fromIndex])/endsWith(String suffix)/startsWith(String prefix)
:檢索字符/字符串; -
static String join(CharSequence delimiter, CharSequence... elements)
:join
操作凑耻;String.join("xxx", "a", "b", "c");
-
String[] split(String regex[, int limit])
:split
操作——join
的逆操作太示; -
boolean matches(String regex)
:正則表達式;Tells whether or not this string matches the given regular expression. -
String replace(char oldChar, char newChar)/replace(CharSequence target, CharSequence replacement)/replaceAll(String regex, String replacement)/replaceFirst(String regex, String replacement)
:字符串操作香浩,替換类缤; -
substring(int beginIndex[, int endIndex])
:子串; -
static String valueOf(boolean b/char c/char[] data/double d/int i/long l/Object obj)
:轉(zhuǎn)換其他類型邻吭;
StringBuilder
:非線程安全餐弱,性能略高;StringBuffer
:線程安全囱晴;// 可變字符串類膏蚓;
sb.append(), insert(), replace(), delete(), reverse()
等原地操作;
java.lang.Math
稍復(fù)雜的計算畸写,如三角函數(shù)驮瞧、對數(shù)、指數(shù)枯芬;
-
Math.E/Math.PI
:2.718281828459045 / 3.141592653589793 -
abs(T t), toDegrees(1.57), toRadians(90), sin/cos/tan, max/min
:基本計算论笔; -
floor()/ceil()
:向下取整(小于目標(biāo)數(shù)的最大整數(shù))/向上取整采郎; -
round()
:四舍五入取整; -
sqrt()/cbrt
:平方根/立方根狂魔;(square root / cubic root) -
exp(n)
:e 的 n 次 冪尉剩; -
pow(x, y)
:x 的 y 次方; -
log(x)/log10(x)
:自然對數(shù)/底為 10 的對數(shù)毅臊; -
Math.random()
:返回 0.0 到 1.0 之間的偽隨機數(shù)理茎;
java.util.Random
一個功能更完善(相比如Math.random()
)的隨機數(shù)生成器;
如何生成 [5, 20)
之間的隨機數(shù)管嬉?r.nextInt(15) + 5
-
Random([long seed])
:種子皂林;默認為當(dāng)前系統(tǒng)時間; -
DoubleStream doubles([long streamSize[, double randomNumberOrigin, double randomNumberBound]])
:流式編程蚯撩,生成一個[ 0.0, 1.0 )(或[ randomNumberOrigin, randomNumberBound ))的 Double 流础倍; -
IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound)
:同上(不過返回的是任意一個合法整數(shù)或[ Origin, Bound ));longs(...)
胎挎; -
nextBoolean(), nextBytes(byte[] bytes), nextDouble(), nextFloat(), nextInt(), nextLong()
: -
nextInt(int bound)
:返回 [ 0, bound ) 之間的任意一個整數(shù)沟启; -
相同的種子會產(chǎn)生相同的偽隨機數(shù)序列:
java.util.Date & java.util.Calendar 時間日期類
Date
無法實現(xiàn)國際化,而且它對不同屬性也使用了前后矛盾的偏移量犹菇,比如月份和小時都是以 0 開始的德迹,月份中的天數(shù)則是從 1 開始的;
Date
從 JDK1.0 就開始存在了揭芍,它的大部分構(gòu)造器胳搞、方法都已經(jīng)過時,不再推薦使用称杨;
總體來說肌毅,
Date
是一個設(shè)計相當(dāng)糟糕的類,因此 Java 官方建議盡量少用Date
姑原;因為
Date
類在設(shè)計上存在一些缺陷悬而,所以 Java 提供了Calendar
類(since jdk1.1)來更好的處理日期和時間;而
Calendar
又顯得過于復(fù)雜锭汛;
java.time Java 8 新增的日期笨奠、時間包
參考:Java8 日期/時間(Date Time)API 指南
為什么我們需要新的 Java 日期/時間API?
在開始研究 Java 8日期/時間 API 之前店乐,讓我們先來看一下為什么我們需要這樣一個新的API艰躺。在Java中呻袭,現(xiàn)有的與日期和時間相關(guān)的類存在諸多問題眨八,其中有:
- Java 的日期/時間類的定義并不一致,在 java.util 和 java.sql 的包中都有日期類左电,此外用于格式化和解析的類在 java.text 包中定義廉侧。
- java.util.Date 同時包含日期和時間页响,而 java.sql.Date 僅包含日期,將其納入 java.sql 包并不合理段誊。另外這兩個類都有相同的名字闰蚕,這本身就是一個非常糟糕的設(shè)計。
- 對于時間连舍、時間戳没陡、格式化以及解析,并沒有一些明確定義的類索赏。對于格式化和解析的需求盼玄,我們有 java.text.DateFormat 抽象類,但通常情況下潜腻,SimpleDateFormat 類被用于此類需求埃儿。
- 所有的日期類都是可變的,因此他們都不是線程安全的融涣,這是 Java 日期類最大的問題之一童番。
- 日期類并不提供國際化,沒有時區(qū)支持威鹿,因此Java引入了 java.util.Calendar 和 java.util.TimeZone 類剃斧,但他們同樣存在上述所有的問題。
在現(xiàn)有的日期和日歷類中定義的方法還存在一些其他的問題忽你,但以上問題已經(jīng)很清晰地表明:Java 需要一個健壯的日期/時間類悯衬。這也是為什么 Joda Time 在 Java 日期/時間需求中扮演了高質(zhì)量替換的重要角色。
Java 8日期/時間 API
Java 8日期/時間 API 是 JSR-310 的實現(xiàn)檀夹,它的實現(xiàn)目標(biāo)是克服舊的日期時間實現(xiàn)中所有的缺陷筋粗,新的日期/時間API的一些設(shè)計原則是:
- 不變性:新的日期/時間 API 中,所有的類都是不可變的炸渡,這對多線程環(huán)境有好處娜亿。
- 關(guān)注點分離:新的 API 將人可讀的日期時間和機器時間(unix timestamp)明確分離,它為日期(Date)蚌堵、時間(Time)买决、日期時間(DateTime)、時間戳(unix timestamp)以及時區(qū)定義了不同的類吼畏。
- 清晰:在所有的類中督赤,方法都被明確定義用以完成相同的行為。舉個例子泻蚊,要拿到當(dāng)前實例我們可以使用 now() 方法躲舌,在所有的類中都定義了 format() 和 parse() 方法,而不是像以前那樣專門有一個獨立的類性雄。為了更好的處理問題没卸,所有的類都使用了工廠模式和策略模式羹奉,一旦你使用了其中某個類的方法,與其他類協(xié)同工作并不困難约计。
- 實用操作:所有新的日期/時間 API 類都實現(xiàn)了一系列方法用以完成通用的任務(wù)诀拭,如:加、減煤蚌、格式化耕挨、解析、從日期/時間中提取單獨部分尉桩,等等俗孝。
- 可擴展性:新的日期/時間 API 是工作在 ISO-8601 日歷系統(tǒng)上的,但我們也可以將其應(yīng)用在非 IOS 的日歷上魄健。
Java 日期/時間 API 包
Java 日期/時間 API 包含以下相應(yīng)的包赋铝。
- java.time 包:這是新的 Java 日期/時間 API 的基礎(chǔ)包,所有的主要基礎(chǔ)類都是這個包的一部分沽瘦,如:
LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration
等等革骨。所有這些類都是不可變的和線程安全的,在絕大多數(shù)情況下析恋,這些類能夠有效地處理一些公共的需求良哲。- java.time.chrono 包:這個包為非 ISO 的日歷系統(tǒng)定義了一些泛化的 API,我們可以擴展 AbstractChronology 類來創(chuàng)建自己的日歷系統(tǒng)助隧。
- java.time.format 包:這個包包含能夠格式化和解析日期時間對象的類筑凫,在絕大多數(shù)情況下,我們不應(yīng)該直接使用它們并村,因為 java.time 包中相應(yīng)的類已經(jīng)提供了格式化和解析的方法巍实。
- java.time.temporal 包:這個包包含一些時態(tài)對象,我們可以用其找出關(guān)于日期/時間對象的某個特定日期或時間哩牍,比如說棚潦,可以找到某月的第一天或最后一天。你可以非常容易地認出這些方法膝昆,因為它們都具有 “withXXX” 的格式丸边。
- java.time.zone 包:這個包包含支持不同時區(qū)以及相關(guān)規(guī)則的類。
System.out.println(LocalDateTime.now());
2017-01-29T21:19:52.802
java.util.集合
這是一個大主題荚孵,待發(fā)掘妹窖。這里只記錄一些細枝末節(jié)。
Collection
和Map
接口是Java
集合框架的根接口收叶。
Collection
接口(子接口為Set
和List
)中定義的方法包括:
add(Object o), addAll(Collection c), remove(Object o), removeAll(Collection c), retainAll(Collection c)(求交集),
removeIf(Predicate filter)(Java 8 新增骄呼,批量刪除符合條件的元素)
clear(), isEmpty(), size()
contains(Object o), containsAll(Collection c),
iterator(), spliterator()
toArray(),
stream()/parallelStream()
(流式編程)
forEach(Comsumer action)
:Java 8 為Iterable
接口新增的默認方法;
迭代Collection
集合過程中不要修改集合內(nèi)容,否則會引發(fā)ConcurrentModificationException
谒麦;(盡管在用Iterator
遍歷時可以用iterator.remove()
方法刪除上一次迭代的元素)
常用的類有:
-
HashSet
:Set
接口的 哈希表(Hash Table)實現(xiàn)俄讹;非線程安全哆致;根據(jù)hashCode()
值和equals()
方法來確定對象的存儲位置——先根據(jù)hashCode()
值計算桶位绕德,再用equals()
方法比較(如果桶位上已經(jīng)有對象);分析以下四種情況:-
hashCode()
不同摊阀,equals()
為true
——存在兩個不同的桶里耻蛇; -
hashCode()
相同,equals()
為false
——存儲在同一個桶里胞此,鏈接法鏈起來臣咖; -
hashCode()
不同,equals()
為false
——存在不同的桶里漱牵; -
hashCode()
相同夺蛇,equals()
為true
——后面一個存不進去,沖突了酣胀;
-
-
所以刁赦,當(dāng)我們試圖把某個類的對象存入
Set
中時,應(yīng)重寫該類的hashCode()
和equals()
方法闻镶,并使他們的返回值保持一致甚脉。Set.remove()
的邏輯也是一樣的:先通過hashCode()
方法計算桶位,在用equals()
比較铆农。 -
LinkedHashSet
:在HashSet
的基礎(chǔ)上牺氨,用 雙 鏈 表 維護元素添加的順序,可以獲得更快的遍歷速度墩剖,并且是按元素的添加順序遍歷猴凹;
-
SortedSet
接口; -
TreeSet
:紅黑樹實現(xiàn)岭皂;確保集合元素處于有序狀態(tài)精堕;TreeSet
會調(diào)用對象的compareTo()
方法來進行比較,從而確定元素的存儲位置蒲障;這里雖然不關(guān)equals()
方法什么事歹篓,但從邏輯上考慮還是應(yīng)該保證equals()
方法和compareTo()
的一致性。- Java 提供了一個
Comparable
接口揉阎,該接口里定義了一個compareTo()
方法庄撮; - 已實現(xiàn)了
Comparable
接口的類有:BigDecimal, BigInteger, 以及所有數(shù)值類型的包裝類(Double, Integer...), Character, Boolean(true > false), String, Date, Time
等;
- Java 提供了一個
-
EnumSet
:專為枚舉類設(shè)計的集合毙籽,EnumSet
中的元素必須是指定枚舉類型的枚舉值洞斯; -
List
是Collection
的子接口,是有序集合,因此新增了一些根據(jù)索引來操作集合元素的方法:-
add(int index, Object o), allAll(int index, Collection o), get(int index), indexOf(Object o), lastIndexOf(Object o), remove(int index), set(int index, Object element), subList(int fromIndexInclude, int toIndexExclude), replaceAll(UnaryOperator operator), sort(Comparator c)
烙如;
-
- 相比于
Set
么抗,List
接口還額外提供了listIterator()
方法來返回一個ListIterator
迭代器;ListIterator
繼承自Iterator
接口亚铁,新增了專門操作List
的方法:-
boolean hasPrevious(), Object previous(), add(Object o)
蝇刀;向前迭代和增加元素(Iterator
只能向后迭代和刪除元素);
-
-
ArrayList
:
ArrayList
和Vector
Vector
是一個非常古老的類(since jdk1.0)徘溢,有很多缺陷吞琐,不建議使用;
Stack
(棧)繼承自Vector
然爆,因此也不建議使用站粟;
若要使用“棧”這種數(shù)據(jù)結(jié)構(gòu)曾雕,用ArrayDeque
奴烙;
- 固定長度的
List
:Arrays.asList(T... a)
返回的是固定長度的List
集合,不支持增加剖张、刪除元素切诀; -
Queue
集合 -
Queue
接口:-
add(Object o)
:將指定元素加入隊列尾部舔箭; -
peek()
:獲取隊列頭部元素膳犹,但不刪除; -
poll()
:獲取隊列頭部元素傀广,并刪除肯污; - 還有
element(), offer(Object e), remove()
等方法翘单;
-
Queue
有一個PriorityQueue
實現(xiàn)類。除此之外蹦渣,Queue
還有一個Deque
接口哄芜,代表“雙端隊列”,兩端都可以添加柬唯、刪除元素认臊。Deque
的實現(xiàn)類有ArrayDeque
和LinkedList
。**
-
PriorityQueue
實現(xiàn)類:加入隊列中的元素會按大小排序锄奢; -
ArrayDeque
:和ArrayList
很像失晴,只是加入了在數(shù)組前端添加、刪除的功能拘央; -
LinkedList
:List
的鏈表實現(xiàn)涂屁,同時實現(xiàn)了Deque
接口,也是雙端隊列的實現(xiàn)類灰伟;
從 Java 源碼來看拆又,Java 是先實現(xiàn)了 Map ,然后通過包裝一個所有 value 都為 null 的 Map 就實現(xiàn)了 Set 集合。
-
HashMap
:Map
接口的 哈希表(Hash Table)實現(xiàn)帖族;-
IdentityHashMap
:當(dāng)且僅當(dāng)兩個 key 嚴格相等(key1 == key2)時才認為兩個 key 相等:
-
-
TreeMap
:SortedMap
接口實現(xiàn)類栈源,紅黑樹實現(xiàn); -
LinkedHashMap
:同LinkedHashSet
竖般; -
EnumMap
:同EnumSet
甚垦; -
Map
接口方法:
clear(), containsKey(Object key), containsValue(Object value), isEmpty(), size(),
Set entrySet()
:返回 Map 中所有 key-value 對組成的 Map.Entry 對象的 Set 集合;(Entry 是 Map 的內(nèi)部類捻激,有getKey(), getValue(), setValue(V value)
三種方法)
Set keySet()
:返回所有 key 組成的 Set 集合制轰;
Collection values()
:返回所有 value 組成的 Collection 集合前计;
get(Object key), put(Object key, Object value)(key 存在則覆蓋原 value胞谭,不存在則添加一個新的 key-value 對), remove(Object key)/remove(Object key, Object value),
putAll(Map m)
Java 8 為 Map 新增的方法:
compute(Object key, BiFunction remappingFunction)/computeIfAbsent/computeIfPresent
forEach(BiConsumer action)
:遍歷 key-value 對;
getOrDefault(Object key, V defaultValue)
:獲取指定 key 值對應(yīng)的 value男杈,key 不存在時返回 defaultValue丈屹;
putIfAbsent(Object key, V value)
replace(Object key, V value)
:與 put 不同的是,當(dāng) key 不存在時不會添加新的 key-value 對伶棒;
replaceAll(BiFunction function)
:用 BiFunction 重新計算所有的 key-value 對旺垒;
- 使用
Properties
讀寫屬性文件(Windows 平臺上的 ini 文件就是一種屬性文件) -
Properties
類可以把 Map 對象和屬性文件關(guān)聯(lián)起來,從而方便的讀寫屬性文件中的鍵值對肤无;
Properties
相當(dāng)于一個 key先蒋、value 都是 String 類型的 Map。
java.util.Collections 工具類
排序操作:
-
reverse(List list), shuffle(List list), sort(List list), sort(List list, Comparator c)
:顛倒順序宛渐,洗牌(打亂順序)竞漾,排序; -
swap(List list, int i, int j)
:交換元素窥翩; -
rotate(List, int distance)
:當(dāng) distance 為正時业岁,將 list 集合后 distance 個元素整體移動到前面;為負時將前 distance 個元素移動到后面寇蚊;
查找笔时、替換操作:
-
int binarySearch(List list, Object key)
:二分查找,前提有序仗岸; -
max(Collection c)/max(Collection, Comparator comp)/min/min
:根據(jù)自然順序/指定順序返回最大/最小元素允耿; -
fill(List list, Object o)
:填充; -
int frequency(Collection c, Object o)
:出現(xiàn)次數(shù)扒怖; -
replaceAll(List list, Object oldVal, Object newVal)
:替換所有舊值较锡;
同步控制:
-
synchronizedList/Set/Map(List/Set/Map c)
:將指定集合包裝成同步的,即線程安全的姚垃;Java 集合框架中常用的 ArrayList, ArrayDeque, LinkedList, HashSet, TreeSet, HashMap, TreeMap 都是線程不安全的念链;
設(shè)置不可變集合:
-
emptyXxx()
:返回一個空的,不可變的集合對象; -
singleton(T o)/singletonList(T o)/singletonMap(K k, V v)
:返回一個僅包含一個元素的 set掂墓,list谦纱,map; -
unmodifiableXxx(C c)
:返回指定集合對象的不可變視圖君编;只讀跨嘉,任何修改操作都不支持(包括修改 key-value 對的 value);
泛型
本章的內(nèi)容可以與前一章的內(nèi)容補充閱讀吃嘿,因為 JDK1.5 增加泛型支持在很大程度上都是為了讓集合能記住其元素的數(shù)據(jù)類型祠乃。
“菱形語法”:List<String> stringList = new ArrayList<>();
所謂泛型,就是允許在定義類兑燥、接口亮瓷、方法時使用 類 型 參 數(shù)。
上述菱形語法中的<String>
就是類型參數(shù)降瞳;
使用通配符來匹配任何泛型:
public void test(List<?> l){}
這里List<?>
表示所有泛型List
的父類嘱支。
List<? extends Shape>
:設(shè)定通配符的上限;
List<? super Shape>
:設(shè)定通配符的下限挣饥;
定義泛型方法:
<T> void fromArrayToCollection(T[] a, Collection c) {
for (T o : a){
c.add(a);
}
}
fromArrayToCollection(a, c);// 調(diào)用的時候無需顯式傳入類型參數(shù)除师,編譯器會根據(jù)實參類型自動推斷;
不支持泛型數(shù)組——
能夠聲明List<String>[]
類型扔枫,
但不能創(chuàng)建ArrayList<String>[10]
的實例汛聚。
無法實例化,也就不能用短荐。
異常處理
異常機制可以使程序中的異常處理代碼和正常業(yè)務(wù)邏輯分離倚舀,從而使程序代碼更加優(yōu)雅,并且更具健壯性搓侄。
try, catch, finally, throw, throws
-
try
塊用于放置可能拋出異常的代碼瞄桨; - 多個
catch
塊用于捕捉異常; -
finally
塊用于釋放在try
塊里打開的物理資源讶踪,異常機制會保證finally
塊總被執(zhí)行(只有用System.exit(1)
來退出虛擬機才可以避免 finally 的執(zhí)行芯侥;return 語句不能阻止。)乳讥; -
throw
:(在代碼中)拋出異常:throw new Exception("您正在闖紅燈柱查!請趕緊停下!");
-
throws
:(在定義方法時)聲明拋出異常:public static void main(String[] args) throws IOException {
Checked
異常和Runtime
異常:Checked
異常是可以在編譯階段被處理的異常云石,所以必須被手動唉工、顯式的處理;Runtime
異常是運行時異常汹忠,無需強制處理淋硝;
Error 錯誤雹熬,一般是指與虛擬機相關(guān)的問題,如系統(tǒng)崩潰谣膳、虛擬機錯誤竿报、動態(tài)鏈接失敗等,這種錯誤無法恢復(fù)或不可捕捉继谚,通常應(yīng)用程序無法處理烈菌,因此程序中不應(yīng)該試圖用 catch 捕獲 Error 對象。
先處理小異常花履,再處理大異常芽世。
總是把對應(yīng) Exception 的 catch 塊放在最后,因為如果把它放在前面诡壁,則后面的 catch 塊將永遠不會獲得執(zhí)行的機會济瓢。
Java 7 提供的多異常捕獲:catch (IndexOutOfBoundsException|NumberFormatException|ArithmeticException e){}
使用try-catch-finally
的正確姿勢:
FileInputStream fis = null;// 【塊】 作用域,所以在這里 【聲 明】欢峰;
try {
fis = new FileInputStream("a.txt");// 在 try 塊中 嘗 試 打開葬荷;
...
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {// 這里涨共,因為可能打開失敗纽帖,fis還是null;
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面代碼顯得有些臃腫举反。Java 7 提供了自動關(guān)閉資源的 try 語句懊直。
注意點:
- 資源的聲明和初始化都必須放在()里,不能像上面那樣在外面聲明里面初始化火鼻;
- fis 的作用域還是只有在 try 塊中室囊;catch 塊和外部都無法訪問;
- 這里的自動關(guān)閉特性只是省去了 finally 語句魁索,catch 捕捉異常還是不可省的融撞;
能自動關(guān)閉是因為資源類實現(xiàn)了AutoCloseable
或Closeable
接口;
Java 7 幾乎把所有的資源類(文件 IO粗蔚、JDBC 的 Connection尝偎、Statement 等接口)都實現(xiàn)了
AutoCloseable
或Closeable
接口;所以鹏控,任性的用吧致扯;
暫時不想處理或不能處理的異常,用throw
或throws
往上拋当辐;throw
在代碼中拋抖僵,throws
在方法聲明中拋;或者先在 catch 中部分處理異常缘揪,處理不了的部分再往上拋耍群;
自定義異常類
繼承Exception
或RuntimeException
類义桂;
public class MyException extends Exception {
public MyException(){
super();
}
public MyException(String msg) {
super(msg);
}
}
不要使用過于龐大的 try 塊,而是劃分為多個 try-catch 單元蹈垢。
MySQL 數(shù)據(jù)庫與 JDBC 編程
JDBC for Java Database Connectivity澡刹,Java 數(shù)據(jù)庫連接。
JDBC 為數(shù)據(jù)庫開發(fā)提供了標(biāo)準 API耘婚。使用 JDBC 開發(fā)的數(shù)據(jù)庫應(yīng)用可以跨平臺罢浇,甚至可以跨數(shù)據(jù)庫(如果全部使用標(biāo)準的 SQL 語句)。
開始之前首先要在項目中引入依賴:
基本過程:
Connection
:
-
Statement createStatement()
:返回一個Statement
對象沐祷; -
PreparedStatement prepareStatement(String sql)
:返回預(yù)編譯的Statement
對象嚷闭; -
CallableStatement prepareCall(String sql)
:返回CallableStatement
對象,用于調(diào)用存儲過程赖临;
除此之外胞锰,Connection
還有控制事務(wù)的方法:
setSavePoint([String name]), roolback([Savepoint savepoint]), setAutoCommit(boolean autoCommit)(關(guān)閉/打開自動提交,打開/關(guān)閉事務(wù)), commit()
Statement
:(PreparedStatement
和CallableStatement
都是其子類)
-
ResultSet executeQuery(String sql)
:執(zhí)行查詢語句兢榨; -
int executeUpdate(String sql)
:執(zhí)行 DML 語句和 DDL 語句嗅榕,并返回受影響的行數(shù); -
boolean execute(String sql)
:執(zhí)行任何 SQL 語句吵聪,如果執(zhí)行后的第一個結(jié)果為ResultSet
對象凌那,則返回 true;如果執(zhí)行后的第一個結(jié)果為受影響的行數(shù)或沒有任何結(jié)果吟逝,則返回 false帽蝶。 - 執(zhí)行了
execute
之后可以用Statement
對象的以下兩個方法來獲取執(zhí)行結(jié)果:getResultSet()
getUpdateCount()
使用PreparedStatement
執(zhí)行 SQL 語句
否則,就得用這種繁瑣的字符串拼接方式(注意單引號)块攒,不僅很容易出錯励稳,還容易遭到 SQL 注入攻擊。
String sql = "select * from jdbc_test where jdbc_name='" + userName + "' and jdbc_desc='" + userPass + "'";
還有內(nèi)容:使用CallableStatement
調(diào)用存儲過程囱井、管理結(jié)果集驹尼、處理 Blob 類型數(shù)據(jù)、使用ResultSetMetaData
分析結(jié)果集庞呕、離線RowSet
新翎、CachedRowSet
查詢分頁、事務(wù)千扶、分析數(shù)據(jù)庫信息料祠、數(shù)據(jù)庫連接池。
Annotation 注解
從 JDK 5 開始澎羞,Java 增加了對元數(shù)據(jù)(
MetaData
)的支持髓绽,也就是Annotation
(注解)。注解是代碼里的特殊標(biāo)記妆绞,這些標(biāo)記可以在編譯顺呕、類加載枫攀、運行時被讀取,并執(zhí)行相應(yīng)的處理株茶。通過使用注解来涨,開發(fā)人員可以在不改變原有邏輯的情況下,在源文件中嵌入一些補充的信息启盛。代碼分析工具蹦掐、開發(fā)工具和部署工具可以通過這些補充信息進行驗證或部署。
Annotation
就像修飾符一樣僵闯,可用于修飾包卧抗、類、構(gòu)造器鳖粟、方法社裆、成員變量、參數(shù)向图、局部變量的聲明泳秀,這些信息被存儲在Annotation
的"name=value"
中。
Annotation
是一個接口榄攀,程序可以通過 反 射 來獲取指定程序元素的Annotation
對象嗜傅,然后通過Annotation
對象來取得注解里的元數(shù)據(jù)。
Annotation
不影響代碼的執(zhí)行航攒。
IO 輸入輸出
java.io.File
An abstract representation of file and directory pathnames.
對文件和目錄路徑的抽象抽象的文件或目錄磺陡。
File 能創(chuàng)建、刪除漠畜、重命名文件和目錄——這是文件和目錄層面上的,沒有進入到文件里面的內(nèi)容——File 不 能 訪 問 文 件 內(nèi) 容 坞靶。訪問文件內(nèi)容需要 IO憔狞。
在 File 眼中,文件和目錄是一樣的彰阴;一種抽象的文件和目錄——實際存在與否毫不影響瘾敢。所以才有
exists()
方法來判斷是否存在。
File 類:
開始之前尿这,一個重要的注意點:File 既可以是相對路徑也可以是絕對路徑簇抵;在相對路徑的時候,F(xiàn)ile 內(nèi)部并不會自動的把它轉(zhuǎn)為絕對路徑射众;
絕對路徑并不是相對路徑的完成形式碟摆!
絕對路徑并不是相對路徑的完成形式!叨橱!
絕對路徑并不是相對路徑的完成形式5渫伞6鲜ⅰ!他們是平等的愉舔!
所以钢猛,
new File("a.ini").getParent()
為null
,new File("a/b/c").getParent()
為a\b
轩缤;
因為 父路徑 僅看/
前面的部分 && 相對路徑并不會自動補全為絕對路徑命迈,相對路徑和絕對路徑是平等的。
訪問文件/路徑名:
-
System.out.println(File f)//直接輸出File
:輸出 File 初始化時的路徑火的; -
String getPath()
:同直接輸出 File躺翻,輸出該 File 對應(yīng)的路徑; -
String getName()
:文件:文件名卫玖;路徑:最后一級路徑名公你; -
String getAbsolutePath()
:根據(jù)當(dāng)前工作路徑生成絕對路徑,然后輸出絕對路徑字符串假瞬; -
File getAbsoluteFile()
:同上陕靠,但這里返回的是轉(zhuǎn)換為絕對路徑后的 File 對象; -
String getParent()
:路徑中/
之前的內(nèi)容脱茉,如果沒有則返回null
剪芥; -
File getParentFile()
:同上,但返回的是父路徑 File琴许,若無返回null
税肪; -
boolean renameTo(File newNameFile)
:重命名;
File file = new File("a/b/c");
file: a\b\c
file.getPath(): a\b\c
file.getName(): c
file.getAbsolutePath(): D:\IntellijIDEAProjects\Empty_Project\a\b\c
file.getAbsoluteFile(): D:\IntellijIDEAProjects\Empty_Project\a\b\c
file.getParent(): a\b
file.getParentFile(): a\b
File file = new File("a.ini");
file: a.ini
file.getPath(): a.ini
file.getName(): a.ini
file.getAbsolutePath(): D:\IntellijIDEAProjects\Empty_Project\a.ini
file.getAbsoluteFile(): D:\IntellijIDEAProjects\Empty_Project\a.ini
file.getParent(): null
file.getParentFile(): null
文件/路徑檢測:
-
boolean exists()
:判斷是否 真 的 存在榜田; -
boolean canWrite()/canRead()
:是否可寫/可讀益兄; -
boolean isFile()/isDirectory()
:是否是文件/目錄; -
boolean isAbsolute()
:是否是絕對路徑箭券;
獲取常規(guī)文件信息:
-
long lastModified()
:最后修改時間净捅; -
long length()
:文件內(nèi)容長度;
文件/路徑操作:
-
boolean createNewFile()
:若文件已經(jīng)存在辩块,直接返回 false蛔六;若不存在,創(chuàng)建成功返回 true废亭,失敗返回 false国章;注意,這個方法只能用來創(chuàng)建文件豆村!new File("a").createNewFile()
則創(chuàng)建一個文件a
液兽;不能創(chuàng)建路徑! -
boolean mkdir()
:這才是創(chuàng)建目錄你画!但只能創(chuàng)建一層抵碟! -
boolean mkdirs()
:創(chuàng)建多級目錄桃漾;new File("a/b/c").mkdirs()
; -
boolean delete()
:刪除文件或路徑的最后一級拟逮;上面用mkdirs()
一次性創(chuàng)建a/b/c
三級目錄后撬统,用delete()
只能刪除最后一級的c
目錄;所以:
File
只能代表一個文件敦迄,或一個目錄恋追,即多級路徑的最后一級;盡管你用了一段長長的路徑來初始化它罚屋,它實際代表的只有最后一級路徑苦囱。
-
String[] list()
:列出所有子文件名和路徑名,用String
數(shù)組盛裝脾猛; -
File[] listFiles()
:用File
數(shù)組盛裝撕彤; -
static File[] listRoots()
:列出系統(tǒng)所有的根路徑;
File file = new File("xxx");
// 先絕對化猛拴,再獲取父路徑羹铅;直接獲取是null;
for (String s : file.getAbsoluteFile().getParentFile().list()) {
System.out.println(s);
}
在 Windows 平臺的路徑分隔符是反斜杠
'\'
愉昆,而 Java 中'\'
表示轉(zhuǎn)義职员,所以要這樣寫:"F:\\abc\\test.txt"
;或者直接用'/'
跛溉,Java 支持自動將斜線'/'
【當(dāng)為】平臺無關(guān)的路徑分隔符焊切。
一個思考:
這個應(yīng)該是在 HTML 中用的;在這里new File(String filename)
是字符串芳室,放進去沒效果专肪。
IO 流
輸入流/輸出流;字節(jié)流/字符流渤愁;節(jié)點流/處理流(or 包裝流)牵祟;
-
InputStream/Reader
:所有輸入流的基類; -
OutputStream/Writer
:所有輸出流的基類;
Java 使用處理流來包裝節(jié)點流是一種典型的裝飾器設(shè)計模式靖秩;處理流可以“嫁接”在任何已存在的流的基礎(chǔ)之上寺旺,這就允許 Java 應(yīng)用采用相同的代碼、透明的方式來訪問不同的輸入/輸出流艳狐。
InputStream 和 Reader
FileInputStream fis = new FileInputStream("a.txt");
byte[] bbuf = new byte[5];
int hasRead = 0;
while ((hasRead=fis.read(bbuf)) > 0){
System.out.println(new String(bbuf, 0, hasRead, "gb2312"));
System.out.println("--------");
}
fis.close();
output:
這一?
--------
校?確
--------
實和?
--------
?的父
--------
親梁?
--------
舫?有
--------
關(guān),?
...
// 將字節(jié)緩沖區(qū)大小改為1024后,完整輸出:
這一切滓玖,確實和他的父親梁啟超有關(guān),父親希望他做一個有趣的人质蕉,正如父親給他的信中說的那樣:
我愿意你趁畢業(yè)后一兩年势篡,分出點光陰翩肌,多學(xué)些常識,尤其是文學(xué)或人文科學(xué)中之某部門禁悠,稍為多用點功夫念祭。我怕你因所學(xué)太專之故,把生活也弄成近于單調(diào)碍侦,太單調(diào)的生活粱坤, 容易厭倦,厭倦即為苦惱瓷产,乃墮落之根源……不獨朋友而已站玄,即如在家庭里頭,像你有我這樣一位爹爹濒旦,也屬人生難逢的幸福株旷;若你的學(xué)問興味太過單調(diào),將來也會相對詞竭尔邓,不能領(lǐng)著我的教訓(xùn)晾剖,你在生活中本來應(yīng)享有的樂趣,也削減不少了铃拇。
gb2312
編碼下钞瀑,一個中文字符2個字節(jié),若不能讀入一個完整的字符就會出現(xiàn)亂碼慷荔。(即使將字節(jié)緩沖區(qū)大小設(shè)為偶數(shù)雕什,仍舊不能避免亂碼,因為英文字符和半角標(biāo)點符號還是占1個字節(jié))
FileReader fr = new FileReader("a.txt");
System.out.println(fr.getEncoding());
char[] cbuf = new char[1024];
int hasRead = 0;
while ((hasRead=fr.read(cbuf)) > 0) {
System.out.println(new String(cbuf, 0, hasRead));
}
fr.close();
output:
????У???????????????????й???????????????????????????縸????????????????????
??????????????????????????????Щ?????????????????????????????????????????????????????????????????ū????????????????????? ???????????????????????????????????????????????????????????????????λ????????????????????????????????ζ??????????????????????????????????????????????б???????е??????????????????
將gb2312
格式的a.txt
讀取成字符串显晶,是需要指定編碼的(除非與默認一致為utf-8
)贷岸;但FileReader
的構(gòu)造函數(shù)沒有接受字符編碼參數(shù)的版本,so磷雇。
讀入字節(jié)流的時候是不需要編碼的偿警,因為是字節(jié);而讀入字符流的時候唯笙,只有
InputStreamReader
這一個從字節(jié)流到字符流的轉(zhuǎn)換流提供了接受編碼的構(gòu)造方法螟蒸。
所以上述代碼可以改為:
InputStreamReader fr = new InputStreamReader(new FileInputStream("a.txt"), "gb2312");
OutputStream 和 Writer
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("acopy.txt");
byte[] bbuf = new byte[32];
int hasRead = 0;
while ((hasRead=fis.read(bbuf)) > 0) {
fos.write(bbuf, 0, hasRead);
}
fis.close();
fos.close();
// 字節(jié)流,不涉及編碼崩掘。
// FileWriter 略七嫌;
Windows 平臺的換行符是
\r\n
,Unix 是\n
苞慢;
輸入輸出流類圖
System.in
是InputStream
诵原,System.out
是處理流PrintStream
.
輸入輸出的內(nèi)容是文本,就應(yīng)考慮使用字符流;二進制內(nèi)容就考慮字節(jié)流绍赛。
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("a.txt"), "gb2312");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
while ((line=bufferedReader.readLine()) != null){
System.out.println(line);
}
// InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("a.txt"), "gb2312");
// BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// bufferedReader.lines().forEach(System.out::println);
Java 虛擬機讀寫其他進程的數(shù)據(jù)
前面介紹過蔓纠,Runtime
對象的exec()
方法可以運行平臺上的其他程序,該方法產(chǎn)生一個 Process 對象吗蚌,Process 對象代表由該 Java 程序啟動的子進程腿倚。Process 類提供了如下三個方法:
-
InputStream getErrorStream()
:獲取子程序的錯誤流; -
InputStream getInputStream()
:獲取子進程的輸出流(麻蛋褪测,這里居然是站在主進程的角度)猴誊; -
OutputStream getOutputStream()
:獲取子進程的輸入流;
Process p = Runtime.getRuntime().exec("ipconfig");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), "gbk"));
br.lines().forEach(System.out::println);
output:
Windows IP 配置
無線局域網(wǎng)適配器 無線網(wǎng)絡(luò)連接 2:
連接特定的 DNS 后綴 . . . . . . . :
本地鏈接 IPv6 地址. . . . . . . . : fe80::412e:cbae:8818:bef1%14
IPv4 地址 . . . . . . . . . . . . : 172.18.217.1
子網(wǎng)掩碼 . . . . . . . . . . . . : 255.255.0.0
默認網(wǎng)關(guān). . . . . . . . . . . . . :
... ...
序列化
對象序列化的目標(biāo)是將對象保存到磁盤中侮措,或在網(wǎng)絡(luò)中直接傳輸對象懈叹。對象序列化機制允許把內(nèi)存中的 Java 對象轉(zhuǎn)換為平臺無關(guān)的二進制流,并可將這種二進制流恢復(fù)成原來的 Java 對象分扎。
序列化:Serialize澄成,反序列化:Deserialize;
為了讓某個類是可序列化的畏吓,該類必須 實現(xiàn)以下兩個接口之一:Serializable
或Externalizable
墨状;
Serializable
是一個標(biāo)記接口,實現(xiàn)該接口無需實現(xiàn)任何方法菲饼,它只是表明該類的實例是可序列化的肾砂。
通常建議:程序創(chuàng)建的每個
JavaBean
類都實現(xiàn)Serializable
。
處理流ObjectOutputStream
和ObjectInputStream
public class ReviewJava {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("objectOutput.txt"));
oos.writeObject(new Person("孫悟空", 500));
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("objectOutput.txt"));
Person p = (Person) ois.readObject();
System.out.println(p);
ois.close();
}
}
class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
output:
Person{name='孫悟空', age=500}
反序列化機制無需通過構(gòu)造器來初始化 Java 對象宏悦。
當(dāng)一個可序列化類有多個父類時镐确,包括直接父類和間接父類,這些父類要么有無參數(shù)的構(gòu)造器饼煞,要么也是可序列化的——否則反序列化時將拋出
InvalidClassException
異常源葫。如果父類是不可序列化的,只是帶有無參數(shù)的構(gòu)造器砖瞧,則該父類中定義的成員變量值不會被序列化到二進制流中息堂。
對象引用的序列化
若某個類是可序列化的,則它的每一個成員變量都必須是可序列化的块促,否則荣堰,該類將不可序列化。
當(dāng)序列化一個
Teacher
對象時竭翠,如果該Teacher
對象持有一個Person
對象的引用持隧,為了在反序列化時可以正確恢復(fù)該Teacher
對象,程序會順帶將該Person
對象也序列化逃片,所以Person
類也必須是可序列化的,否則Teacher
類將不可序列化。回想上面褥实,
Person
類持有一個String
類型的name
屬性呀狼,序列化Person
時String name
當(dāng)然要被序列化。遞歸序列化
當(dāng)對某個對象進行序列化時损离,系統(tǒng)會自動把該對象的所有實例變量依次進行序列化哥艇,如果某個實例變量引用到另一個對象,則被引用的對象也會被序列化僻澎;這種情況被稱為“遞歸序列化”貌踏。
為了防止重復(fù)序列化同一個對象,序列化機制會對每一個序列化的對象進行編號窟勃;如果某個對象已經(jīng)被序列化過祖乳,程序?qū)⒅皇侵苯虞敵鲆粋€序列化編號,而不是再次序列化該對象秉氧。
這樣可能引起一個潛在的問題——某個對象在序列化之后的修改將不會再次被輸出眷昆。
使用transient
關(guān)鍵字避免對某個實例變量的序列化:
private transient int age;
serialVersionUID
根據(jù)前面的介紹可以知道,反序列化 Java 對象時必須提供該對象的 class 文件汁咏,現(xiàn)在的問題是亚斋,隨著項目的升級,系統(tǒng)的 class 文件也會升級攘滩,Java 如何保證兩個 class 文件的兼容性帅刊?
Java 序列化機制允許為序列化類提供一個 private static final 的 serialVersionUID 值,該值用于標(biāo)識該 Java 類的序列化版本漂问,也就是說赖瞒,如果一個類升級后,只要它的 serialVersionUID 保持不變级解,序列化機制就會把它們當(dāng)做同一個序列化版本冒黑。
NIO & NIO.2
略∏诨看印象筆記和書抡爹。
java.nio.file.Path/Paths/Files
早期的 Java 只提供了一個 File 類來訪問文件系統(tǒng),但 File 類的功能比較有限芒划,它不能利用特定文件系統(tǒng)的特性冬竟,F(xiàn)ile 類所提供的的方法的性能也不高。而且民逼,其大多數(shù)方法在出錯時僅返回失敗泵殴,并不會提供異常信息。
Java 7 的 NIO.2 為了彌補這種不足拼苍,引入了一個 Path 接口笑诅,代表一個平臺無關(guān)的路徑。除此之外,NIO.2 還提供了 Paths吆你、Files 兩個工具類弦叶,其中 Files 包含大量靜態(tài)的工具方法來操作文件;Paths 則包含兩個返回 Path 的靜態(tài)工廠方法妇多。
Path 接口也提供了一堆類似(or 增強)File 類的功能伤哺,為避免混淆,略者祖。
Paths 工具類只有兩個 get 方法立莉,用來生成 Path ,同略七问。
Files
Files 類是一個高度封裝的工具類蜓耻,它提供了大量的工具方法來完成文件復(fù)制、讀取文件內(nèi)容烂瘫、寫入文件內(nèi)容等功能——這些原本需要通過 IO 操作才能完成的功能媒熊,用 Files 工具類可以很方便、優(yōu)雅的完成坟比。
使用 Files 可以大大簡化文件 IO.
一個前提:Files 的工具方法都是與 Path 配合使用的芦鳍,好在 File 有個 toPath() 方法,這樣就可以將 Files 與 File 銜接起來用葛账。
-
copy(InputStream in, Path target, CopyOption... options)
:復(fù)制輸入流中的內(nèi)容到文件柠衅; -
copy(Path source, OutputStream out)
:復(fù)制文件的內(nèi)容到輸出流; -
copy(Path source, Path target, CopyOption... options)
:復(fù)制文件的內(nèi)容到另一個文件籍琳; -
createDirectories(Path dir, FileAttribute<?>... attrs)
:File.mkdirs() -
createDirectory(Path dir, FileAttribute<?>... attrs)
:File.mkdir() -
createFile(Path path, FileAttribute<?>... attrs)
:File.createNewFile() -
delete(Path path)/deleteIfExists(Path path)/exists(Path path, LinkOption... options)
:File.delete()/File.exists() -
static Stream<Path> find(Path start, int maxDepth, BiPredicate<Path,BasicFileAttributes> matcher, FileVisitOption... options)
:Return a Stream that is lazily populated with Path by searching for files in a file tree rooted at a given starting file.
但是用Files.find()
的時候遇到了問題:
遇到?jīng)]有權(quán)限的文件夾菲宴,直接拋出異常退出了,不能跳過繼續(xù)遍歷趋急。
最后的FileVisitOption
是一個枚舉值喝峦,但它總共只有一個FOLLOW_LINKS
啊N卮铩Rゴ馈!毛用沒有2榻C减狻!
-
getLastModifiedTime(Path path, LinkOption... options)
:File.lastModified() -
isDirectory(Path path, LinkOption... options)
:File.isDirectory() -
isExecutable(Path path), isHidden(Path path), isReadable(Path path), isRegularFile(Path path, LinkOption... options), isSameFile(Path path, Path path2), isSymbolicLink(Path path), isWritable(Path path)
: -
Stream<String> lines(Path path[, Charset cs])
:讀取文件內(nèi)所有的行霜威; -
List<String> readAllLines(Path path[, Charset cs])
:上面返回的是流谈喳,這里返回的是List<String>
; -
write(Path path, byte[] bytes, OpenOption... options)
:方便的寫數(shù)據(jù)戈泼; -
write(Path path, Iterable<? extends CharSequence> lines, [Charset cs, ]OpenOption... options)
:將多行數(shù)據(jù)寫入文件婿禽; -
Stream<Path> list(Path dir)
:File.listFiles() -
move(Path source, Path target, CopyOption... options)
:移動文件到新的路徑赏僧; -
BufferedReader newBufferedReader(Path path[, Charset cs])
:將文件打開為BufferedReader
流;等價于new BufferedReader(new InputStreamReader(new FileInputStream(File file, String cs)))
谈宛;只不過要快捷的多次哈! -
BufferedWriter newBufferedWriter(Path path[, Charset cs], OpenOption... options)
:恩,快捷吆录; -
InputStream newInputStream(Path path, OpenOption... options)
:同上; -
OutputStream newOutputStream(Path path, OpenOption... options)
: -
DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
:以 Path 流的方式返回目標(biāo)路徑的所有子路徑琼牧; -
String probeContentType(Path path)
:探測文件類型恢筝; -
long size(Path path)
:File.length()
接下來是遍歷路徑 walk/walkFileTree
-
Stream<Path> walk(Path start, [int maxDepth, ]FileVisitOption... options)
:這里用的也是FileVisitOption
,和上面find
的問題一樣:遇到?jīng)]有訪問權(quán)限的路徑就直接拋出異常退出巨坊,沒法讓它繼續(xù)下去撬槽; -
Path walkFileTree(Path start, [Set<FileVisitOption> options, int maxDepth, ]FileVisitor<? super Path> visitor)
:這里用的是FileVisitor
,它能對訪問成功/失敗趾撵、打開路徑前/后的情況做很細致的控制侄柔,遍 歷 路 徑 就 是 它 了 !
File root = new File("C:/");
final int[] pathCnt = {0};
Files.walkFileTree(root.toPath(), new SimpleFileVisitor<Path>(){
// 訪問一個目錄前調(diào)用占调;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println(dir);
++pathCnt[0];
return super.preVisitDirectory(dir, attrs);
}
// 訪問文件前調(diào)用暂题;
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
++pathCnt[0];
return super.visitFile(file, attrs);
}
// 訪 問 文 件 失 敗 ,或 者 目 錄 不 可 打 開 究珊,時調(diào)用薪者;所以改寫的關(guān)鍵地方在這里;
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 默認行為是簡單的拋出 exc剿涮;
// return super.visitFileFailed(file, exc);
exc.printStackTrace();
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
return FileVisitResult.CONTINUE;
}
// dir的所有 子 孫 路徑都被訪問完畢后言津,調(diào)用此方法;
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return super.postVisitDirectory(dir, exc);
}
});
System.out.println(pathCnt[0]);
===========================================================
public class SimpleFileVisitor<T> implements FileVisitor<T> {
/**
* Initializes a new instance of this class.
*/
protected SimpleFileVisitor() {
}
/**
* Invoked for a directory before entries in the directory are visited.
*
* <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
* CONTINUE}.
*/
@Override
public FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
throws IOException
{
Objects.requireNonNull(dir);
Objects.requireNonNull(attrs);
return FileVisitResult.CONTINUE;
}
/**
* Invoked for a file in a directory.
*
* <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
* CONTINUE}.
*/
@Override
public FileVisitResult visitFile(T file, BasicFileAttributes attrs)
throws IOException
{
Objects.requireNonNull(file);
Objects.requireNonNull(attrs);
return FileVisitResult.CONTINUE;
}
/**
* Invoked for a file that could not be visited.
*
* <p> Unless overridden, this method re-throws the I/O exception that prevented
* the file from being visited.
*/
@Override
public FileVisitResult visitFileFailed(T file, IOException exc)
throws IOException
{
Objects.requireNonNull(file);
throw exc;
}
/**
* Invoked for a directory after entries in the directory, and all of their
* descendants, have been visited.
*
* <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
* CONTINUE} if the directory iteration completes without an I/O exception;
* otherwise this method re-throws the I/O exception that caused the iteration
* of the directory to terminate prematurely.
*/
@Override
public FileVisitResult postVisitDirectory(T dir, IOException exc)
throws IOException
{
Objects.requireNonNull(dir);
if (exc != null)
throw exc;
return FileVisitResult.CONTINUE;
}
}
多線程
進程是資源分配的基本單位取试,線程是任務(wù)調(diào)度的基本單位悬槽。
線程共享進程的內(nèi)存、文件句柄等資源瞬浓。
使用多線程來實現(xiàn)并發(fā)要比使用多進程實現(xiàn)并發(fā)的性能高得多初婆。(?)
繼承 Thread 類創(chuàng)建線程
public class ReviewJava {
public static void main(String[] args) {
new MyThread().start();
System.out.println("開啟線程是瞬間的瑟蜈!不是阻塞到它執(zhí)行完烟逊!");
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("In Thread, println: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
java.lang.Thread:
-
Thread([ThreadGroup group,] [Runnable target,] [String name])
:構(gòu)造方法; -
static Thread currentThread()
:獲取當(dāng)前線程铺根; -
getName()
:線程名宪躯,默認是 main, Thread-0, Thread-1... -
int getPriority()/setPriority(int newPriority)
:線程優(yōu)先級; -
ThreadGroup getThreadGroup()
:獲取所屬的線程組位迂; -
setDaemon()/isDaemon()
:守護線程访雪; -
join([long millis])
:等待線程執(zhí)行完详瑞; -
static void sleep(long millis)
:睡覺;
實現(xiàn) Runnable 接口創(chuàng)建線程
- 實現(xiàn) Runnable 接口臣缀,重寫 run() 方法坝橡;
- 創(chuàng)建實例并作為 Thread 實例的 target:
new Thread(target).start()
多個線程可以共享同一個 target,該 target 的實例變量可以在多個線程間共享精置。But 這樣好嗎计寇?書上說好!你說呢脂倦?
public class ReviewJava {
public static void main(String[] args) {
// 兩個線程共享同一個target番宁;
MyThread target = new MyThread();
new Thread(target).start();
new Thread(target).start();
}
}
class MyThread implements Runnable {
private int i = 0;
@Override
public void run() {
for (; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
output:
Thread-1: 0 // 幾乎同時進入循環(huán)0,所以赖阻;
Thread-0: 0 // 幾乎同時進入循環(huán)0蝶押,所以;
Thread-0: 1
Thread-1: 2
Thread-1: 3
Thread-0: 4
Thread-1: 5
Thread-0: 6
Thread-0: 7
Thread-1: 8
Thread-0: 9
使用 Callable 和 Future 創(chuàng)建線程
可以有返回值火欧,可以拋出異常棋电,但比前兩種稍顯復(fù)雜。
線程以以下三種方法結(jié)束:
- run() 或 call()(Callable 接口) 方法執(zhí)行完苇侵,線程正常結(jié)束赶盔;
- 線程拋出一個未捕獲的異常;
- 直接調(diào)用 stop() 方法來結(jié)束線程——該方法容易導(dǎo)致死鎖衅檀,通常不推薦使用招刨。
通過調(diào)用一個線程對象的 join() 方法來等待其執(zhí)行完成。
Daemon Thread哀军,守護線程沉眶,當(dāng)所有前臺線程都死亡時,后臺線程(守護線程)會自動死亡杉适。JVM 的垃圾回收線程就是典型的守護線程谎倔。(setDaemon())
主線程默認是前臺線程,前臺線程創(chuàng)建的子線程默認是前臺線程猿推;后臺線程創(chuàng)建的子線程默認是后臺線程片习。
線程同步
同步監(jiān)視器/同步代碼塊
synchronized(obj) {
...
}
obj 就是同步監(jiān)視器;線程開始執(zhí)行同步代碼塊之前必須獲得對同步監(jiān)視器的鎖定蹬叭。
將上面的代碼修改為:
public void run() {
synchronized (i) {
for (; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
output:
Thread-0: 0
Thread-0: 1
Thread-0: 2
Thread-0: 3
Thread-0: 4
Thread-0: 5
Thread-0: 6
Thread-0: 7
Thread-0: 8
Thread-0: 9
就不會出現(xiàn)兩個線程同時進入循環(huán)0的情況了藕咏。
同步方法
同步方法的同步監(jiān)視器是 this。
public synchronized void run() {
同步鎖(Lock)
可以實現(xiàn)更靈活的鎖機制秽五,lock()/unlock()孽查,略。
死鎖
兩個線程相互等待對方釋放鎖時就會發(fā)生死鎖坦喘,Java 虛擬機沒有監(jiān)測盲再,也沒有采取措施來處理死鎖情況西设,所以多線程編程時只能避免死鎖出現(xiàn)。一旦出現(xiàn)死鎖答朋,整個程序既不會發(fā)生任何異常贷揽,也不會給出任何提示。
線程間通信
-
wait()/notify()/notifyAll()
梦碗,略禽绪。 - 使用 Lock 對象來同步時,要配合 Condition 來控制線程通信叉弦。
- 使用阻塞隊列(BlockingQueue)來控制線程間通信
阻塞隊列滿時丐一,添加操作會被阻塞;空時取操作被阻塞淹冰;通過向阻塞隊列的存取(put(E e)/take())來實現(xiàn)線程間通信巨柒。
線程組 ThreadGroup
用來對線程進行批量管理樱拴。默認情況下,子線程和創(chuàng)建它的父線程處于同一線程組洋满。
Thread([ThreadGroup group,] [Runnable target,] [String name])
-
int activeCount()
:返回此線程組中活動線程的數(shù)目晶乔;
線程池
略。
線程安全的集合類
- 通過包裝牺勾;
Map m = Collections.synchronizedMap(new HashMap())
- Java 5 提供的正罢,在 java.util.concurrent 包下的大量支持高效并發(fā)訪問的集合接口和實現(xiàn)類;
ConcurrentHashMap, CopyOnWriteArrayList...
網(wǎng)絡(luò)編程
書上講的是驻民,翻具,,額回还,裆泳,,還是略好了柠硕。
集合與數(shù)組的轉(zhuǎn)換
Collections.addAll(Collection c, T... elements)
一次性添加多個元素到集合工禾;
Collection.toArray()
返回一個重新分配空間的數(shù)組(看清楚是 Collection
接口);
Arrays.asList(T... a)
返回一個固定長度的List
蝗柔;
補充
stream 流式編程——未
不可變類
不可變類:當(dāng)你獲得這個類的一個實例引用時闻葵,你不可以改變這個實例的內(nèi)容。不可變類的實例一但創(chuàng)建癣丧,其內(nèi)在成員變量的值就不能被修改槽畔。
舉個例子:String 和 StringBuilder,String 是 immutable 的坎缭,每次對于 String 對象的修改都將產(chǎn)生一個新的 String 對象竟痰,而原來的對象保持不變签钩,而 StringBuilder 是 mutable,因為每次對于它的對象的修改都作用于該對象本身坏快,并沒有產(chǎn)生新的對象铅檩。
不可變類的好處:
- 線程安全,可以不用被 synchroinzed 就在并發(fā)環(huán)境中共享莽鸿,無需使用額外的鎖機制(簡化編程模型)昧旨;
- 提高性能,可以被緩存起來重復(fù)使用祥得;例如對字符串字面值的緩存兔沃;
immutable 也有一個缺點就是會制造大量垃圾,由于他們不能被重用而且對于它們的使用就是”用“然后”扔“级及,字符串就是一個典型的例子乒疏,它會創(chuàng)造很多的垃圾,給垃圾收集帶來很大的麻煩饮焦。當(dāng)然這只是個極端的例子怕吴,合理的使用 immutable 對象會創(chuàng)造很大的價值。
如何實現(xiàn)不可變類县踢?
- immutable對象的狀態(tài)在創(chuàng)建之后就不能發(fā)生改變转绷,任何對它的改變都應(yīng)該產(chǎn)生一個新的對象。
- Immutable類的所有的屬性都應(yīng)該是final的硼啤。
- 對象必須被正確的創(chuàng)建议经,比如:對象引用在對象創(chuàng)建過程中不能泄露(leak)。
- 對象應(yīng)該是final的谴返,以此來限制子類繼承父類煞肾,以避免子類改變了父類的immutable特性。
- 如果類中包含mutable類對象亏镰,那么返回給客戶端的時候扯旷,返回該對象的一個拷貝,而不是該對象本身(該條可以歸為第一條中的一個特例)
其他
-
java.math.BigDecimal
:高精度數(shù)字索抓,解決Double
不夠精確的問題钧忽;
注意點(坑)
作用域問題。Java 的作用域和 C/C++ 是很相似的逼肯,try 塊中的變量耸黑,在 finally 塊中訪問不到。
兩個new String("語文")
對象篮幢,雖然內(nèi)容是一樣的大刊,但是是不同的對象!地址不一樣三椿!理解缺菌!
Java 如何實現(xiàn)鏈表結(jié)構(gòu)葫辐?引用的概念還不夠刻骨銘心啊伴郁!
Collection.toArray()
返回的是Object[]
類型俊马,Object[]
是不能轉(zhuǎn)換為String[]
類型的簇秒。數(shù)組里面的每一個元素都是Object
俊啼,不是僅僅強轉(zhuǎn)一個引用就行的摊唇。
所有的 包 裝 類 也都是 不 可 變 類 !狐胎!
基本類型(如 int)的數(shù)值和包裝類(Integer)之間是可以自動“裝箱拆箱”的鸭栖,但在需要使用類型參數(shù) T 地方,int 是不能自動轉(zhuǎn)為 Integer 的握巢,必須手動修改晕鹊。
換為 Integer 后全都消停了: