Java 拾遺

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 vs has-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類不能派生子類艾岂。通過使用final關(guān)鍵字,可以實現(xiàn)不可變類朋其,不可變會讓系統(tǒng)更加安全王浴。

不可變類不是僅僅靠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)的類存在諸多問題眨八,其中有:

  1. Java 的日期/時間類的定義并不一致,在 java.util 和 java.sql 的包中都有日期類左电,此外用于格式化和解析的類在 java.text 包中定義廉侧。
  2. java.util.Date 同時包含日期和時間页响,而 java.sql.Date 僅包含日期,將其納入 java.sql 包并不合理段誊。另外這兩個類都有相同的名字闰蚕,這本身就是一個非常糟糕的設(shè)計。
  3. 對于時間连舍、時間戳没陡、格式化以及解析,并沒有一些明確定義的類索赏。對于格式化和解析的需求盼玄,我們有 java.text.DateFormat 抽象類,但通常情況下潜腻,SimpleDateFormat 類被用于此類需求埃儿。
  4. 所有的日期類都是可變的,因此他們都不是線程安全的融涣,這是 Java 日期類最大的問題之一童番。
  5. 日期類并不提供國際化,沒有時區(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è)計原則是:

  1. 不變性:新的日期/時間 API 中,所有的類都是不可變的炸渡,這對多線程環(huán)境有好處娜亿。
  2. 關(guān)注點分離:新的 API 將人可讀的日期時間和機器時間(unix timestamp)明確分離,它為日期(Date)蚌堵、時間(Time)买决、日期時間(DateTime)、時間戳(unix timestamp)以及時區(qū)定義了不同的類吼畏。
  3. 清晰:在所有的類中督赤,方法都被明確定義用以完成相同的行為。舉個例子泻蚊,要拿到當(dāng)前實例我們可以使用 now() 方法躲舌,在所有的類中都定義了 format() 和 parse() 方法,而不是像以前那樣專門有一個獨立的類性雄。為了更好的處理問題没卸,所有的類都使用了工廠模式和策略模式羹奉,一旦你使用了其中某個類的方法,與其他類協(xié)同工作并不困難约计。
  4. 實用操作:所有新的日期/時間 API 類都實現(xiàn)了一系列方法用以完成通用的任務(wù)诀拭,如:加、減煤蚌、格式化耕挨、解析、從日期/時間中提取單獨部分尉桩,等等俗孝。
  5. 可擴展性:新的日期/時間 API 是工作在 ISO-8601 日歷系統(tǒng)上的,但我們也可以將其應(yīng)用在非 IOS 的日歷上魄健。
Java 日期/時間 API 包

Java 日期/時間 API 包含以下相應(yīng)的包赋铝。

  1. java.time 包:這是新的 Java 日期/時間 API 的基礎(chǔ)包,所有的主要基礎(chǔ)類都是這個包的一部分沽瘦,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration 等等革骨。所有這些類都是不可變的和線程安全的,在絕大多數(shù)情況下析恋,這些類能夠有效地處理一些公共的需求良哲。
  2. java.time.chrono 包:這個包為非 ISO 的日歷系統(tǒng)定義了一些泛化的 API,我們可以擴展 AbstractChronology 類來創(chuàng)建自己的日歷系統(tǒng)助隧。
  3. java.time.format 包:這個包包含能夠格式化和解析日期時間對象的類筑凫,在絕大多數(shù)情況下,我們不應(yīng)該直接使用它們并村,因為 java.time 包中相應(yīng)的類已經(jīng)提供了格式化和解析的方法巍实。
  4. java.time.temporal 包:這個包包含一些時態(tài)對象,我們可以用其找出關(guān)于日期/時間對象的某個特定日期或時間哩牍,比如說棚潦,可以找到某月的第一天或最后一天。你可以非常容易地認出這些方法膝昆,因為它們都具有 “withXXX” 的格式丸边。
  5. java.time.zone 包:這個包包含支持不同時區(qū)以及相關(guān)規(guī)則的類。
System.out.println(LocalDateTime.now());

2017-01-29T21:19:52.802

java.util.集合

這是一個大主題荚孵,待發(fā)掘妹窖。這里只記錄一些細枝末節(jié)。

CollectionMap接口是Java集合框架的根接口收叶。

Collection接口(子接口為SetList)中定義的方法包括:

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()方法刪除上一次迭代的元素)

常用的類有:

  • HashSetSet接口的 哈希表(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等;
  • EnumSet:專為枚舉類設(shè)計的集合毙籽,EnumSet中的元素必須是指定枚舉類型的枚舉值洞斯;
  • ListCollection的子接口,是有序集合,因此新增了一些根據(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

ArrayListVector

Vector 是一個非常古老的類(since jdk1.0)徘溢,有很多缺陷吞琐,不建議使用;
Stack(棧)繼承自 Vector然爆,因此也不建議使用站粟;
若要使用“棧”這種數(shù)據(jù)結(jié)構(gòu)曾雕,用ArrayDeque奴烙;

  • 固定長度的ListArrays.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)類有ArrayDequeLinkedList。**


  • PriorityQueue實現(xiàn)類:加入隊列中的元素會按大小排序锄奢;
  • ArrayDeque:和ArrayList很像失晴,只是加入了在數(shù)組前端添加、刪除的功能拘央;
  • LinkedListList的鏈表實現(xiàn)涂屁,同時實現(xiàn)了Deque接口,也是雙端隊列的實現(xiàn)類灰伟;

從 Java 源碼來看拆又,Java 是先實現(xiàn)了 Map ,然后通過包裝一個所有 value 都為 null 的 Map 就實現(xiàn)了 Set 集合。

  • HashMapMap接口的 哈希表(Hash Table)實現(xiàn)帖族;
    • IdentityHashMap:當(dāng)且僅當(dāng)兩個 key 嚴格相等(key1 == key2)時才認為兩個 key 相等:
  • TreeMapSortedMap接口實現(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 語句懊直。

注意點:

  1. 資源的聲明和初始化都必須放在()里,不能像上面那樣在外面聲明里面初始化火鼻;
  2. fis 的作用域還是只有在 try 塊中室囊;catch 塊和外部都無法訪問;
  3. 這里的自動關(guān)閉特性只是省去了 finally 語句魁索,catch 捕捉異常還是不可省的融撞;

能自動關(guān)閉是因為資源類實現(xiàn)了AutoCloseableCloseable接口;

Java 7 幾乎把所有的資源類(文件 IO粗蔚、JDBC 的 Connection尝偎、Statement 等接口)都實現(xiàn)了AutoCloseableCloseable接口;所以鹏控,任性的用吧致扯;

暫時不想處理或不能處理的異常,用throwthrows往上拋当辐;throw在代碼中拋抖僵,throws在方法聲明中拋;或者先在 catch 中部分處理異常缘揪,處理不了的部分再往上拋耍群;

自定義異常類

繼承ExceptionRuntimeException類义桂;

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:(PreparedStatementCallableStatement都是其子類)

  • 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()nullnew 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苞慢;

輸入輸出流類圖
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png

System.inInputStream诵原,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)以下兩個接口之一:SerializableExternalizable墨状;
Serializable是一個標(biāo)記接口,實現(xiàn)該接口無需實現(xiàn)任何方法菲饼,它只是表明該類的實例是可序列化的肾砂。

通常建議:程序創(chuàng)建的每個JavaBean類都實現(xiàn)Serializable

處理流ObjectOutputStreamObjectInputStream
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屬性呀狼,序列化PersonString 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)建線程
  1. 實現(xiàn) Runnable 接口臣缀,重寫 run() 方法坝橡;
  2. 創(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é)束:

  1. run() 或 call()(Callable 接口) 方法執(zhí)行完苇侵,線程正常結(jié)束赶盔;
  2. 線程拋出一個未捕獲的異常;
  3. 直接調(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ā)生任何異常贷揽,也不會給出任何提示。

線程間通信
  1. wait()/notify()/notifyAll()梦碗,略禽绪。
  2. 使用 Lock 對象來同步時,要配合 Condition 來控制線程通信叉弦。
  3. 使用阻塞隊列(BlockingQueue)來控制線程間通信

阻塞隊列滿時丐一,添加操作會被阻塞;空時取操作被阻塞淹冰;通過向阻塞隊列的存取(put(E e)/take())來實現(xiàn)線程間通信巨柒。

線程組 ThreadGroup

用來對線程進行批量管理樱拴。默認情況下,子線程和創(chuàng)建它的父線程處于同一線程組洋满。

Thread([ThreadGroup group,] [Runnable target,] [String name])

  • int activeCount():返回此線程組中活動線程的數(shù)目晶乔;
線程池

略。

線程安全的集合類
  1. 通過包裝牺勾;
    Map m = Collections.synchronizedMap(new HashMap())
  2. 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)生新的對象铅檩。

不可變類的好處:

  1. 線程安全,可以不用被 synchroinzed 就在并發(fā)環(huán)境中共享莽鸿,無需使用額外的鎖機制(簡化編程模型)昧旨;
  2. 提高性能,可以被緩存起來重復(fù)使用祥得;例如對字符串字面值的緩存兔沃;

immutable 也有一個缺點就是會制造大量垃圾,由于他們不能被重用而且對于它們的使用就是”用“然后”扔“级及,字符串就是一個典型的例子乒疏,它會創(chuàng)造很多的垃圾,給垃圾收集帶來很大的麻煩饮焦。當(dāng)然這只是個極端的例子怕吴,合理的使用 immutable 對象會創(chuàng)造很大的價值。

如何實現(xiàn)不可變類县踢?

  1. immutable對象的狀態(tài)在創(chuàng)建之后就不能發(fā)生改變转绷,任何對它的改變都應(yīng)該產(chǎn)生一個新的對象
  2. Immutable類的所有的屬性都應(yīng)該是final的硼啤。
  3. 對象必須被正確的創(chuàng)建议经,比如:對象引用在對象創(chuàng)建過程中不能泄露(leak)。
  4. 對象應(yīng)該是final的谴返,以此來限制子類繼承父類煞肾,以避免子類改變了父類的immutable特性。
  5. 如果類中包含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 后全都消停了:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市暴浦,隨后出現(xiàn)的幾起案子捏题,更是在濱河造成了極大的恐慌,老刑警劉巖肉渴,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異带射,居然都是意外死亡同规,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門窟社,熙熙樓的掌柜王于貴愁眉苦臉地迎上來券勺,“玉大人,你說我怎么就攤上這事灿里」亓叮” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵匣吊,是天一觀的道長儒拂。 經(jīng)常有香客問我,道長色鸳,這世上最難降的妖魔是什么社痛? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮命雀,結(jié)果婚禮上蒜哀,老公的妹妹穿的比我還像新娘。我一直安慰自己吏砂,他們只是感情好撵儿,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布乘客。 她就那樣靜靜地躺著,像睡著了一般淀歇。 火紅的嫁衣襯著肌膚如雪易核。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天房匆,我揣著相機與錄音耸成,去河邊找鬼。 笑死浴鸿,一個胖子當(dāng)著我的面吹牛井氢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播岳链,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼花竞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掸哑?” 一聲冷哼從身側(cè)響起约急,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苗分,沒想到半個月后厌蔽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡摔癣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年奴饮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片择浊。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡戴卜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琢岩,到底是詐尸還是另有隱情投剥,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布担孔,位于F島的核電站江锨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏攒磨。R本人自食惡果不足惜泳桦,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娩缰。 院中可真熱鬧灸撰,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至债蓝,卻和暖如春壳鹤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饰迹。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工芳誓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啊鸭。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓锹淌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赠制。 傳聞我的和親對象是個殘疾皇子赂摆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法钟些,內(nèi)部類的語法烟号,繼承相關(guān)的語法,異常的語法政恍,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理汪拥,服務(wù)發(fā)現(xiàn),斷路器篙耗,智...
    卡卡羅2017閱讀 134,600評論 18 139
  • (一)Java部分 1喷楣、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,071評論 0 62
  • 我精神和神經(jīng)都不太好,最近特別嚴重鹤树,睡覺時經(jīng)常不知道是真是假,無法分辨現(xiàn)實和夢境逊朽,醒來需要很久才恢復(fù)罕伯,由于天氣溫度...
    小蔻子閱讀 273評論 0 1
  • 讓它下就是: 似海聲 似河流聲。 似火焰燃燒胸膛 沉默發(fā)出的聲叽讳。 似一朵花陷進春天 短暫而狂歡的聲追他。 似遇見她的...
    卓別臧閱讀 168評論 2 1