Java 基礎(chǔ)

一嘹裂、數(shù)據(jù)類(lèi)型

基本類(lèi)型

  • byte/8
  • char/16
  • short/16
  • int/32
  • float/32
  • long/64
  • double/64
  • boolean/~

boolean 只有兩個(gè)值:true妄壶、false,可以使用 1 bit 來(lái)存儲(chǔ)焦蘑,但是具體大小沒(méi)有明確規(guī)定。JVM 會(huì)在編譯時(shí)期將 boolean 類(lèi)型的數(shù)據(jù)轉(zhuǎn)換為 int盒发,使用 1 來(lái)表示 true,0 表示 false。JVM 支持 boolean 數(shù)組颠锉,但是是通過(guò)讀寫(xiě) byte 數(shù)組來(lái)實(shí)現(xiàn)的蓖议。

包裝類(lèi)型

基本類(lèi)型都有對(duì)應(yīng)的包裝類(lèi)型,基本類(lèi)型與其對(duì)應(yīng)的包裝類(lèi)型之間的賦值使用自動(dòng)裝箱與拆箱完成蛮艰。

Integer x = 2;     // 裝箱
int y = x;         // 拆箱

緩存池

new Integer(123) 與 Integer.valueOf(123) 的區(qū)別在于:

  • new Integer(123) 每次都會(huì)新建一個(gè)對(duì)象腋腮;
  • Integer.valueOf(123) 會(huì)使用緩存池中的對(duì)象,多次調(diào)用會(huì)取得同一個(gè)對(duì)象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);    // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k);   // true

valueOf() 方法的實(shí)現(xiàn)比較簡(jiǎn)單即寡,就是先判斷值是否在緩存池中徊哑,如果在的話就直接返回緩存池的內(nèi)容。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

在 Java 8 中聪富,Integer 緩存池的大小默認(rèn)為 -128~127莺丑。

static final int low = -128;
static final int high;
static final Integer cache[];

static {
    // high value may be configured by property
    int h = 127;
    String integerCacheHighPropValue =
        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    if (integerCacheHighPropValue != null) {
        try {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        } catch( NumberFormatException nfe) {
            // If the property cannot be parsed into an int, ignore it.
        }
    }
    high = h;

    cache = new Integer[(high - low) + 1];
    int j = low;
    for(int k = 0; k < cache.length; k++)
        cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
}

編譯器會(huì)在自動(dòng)裝箱過(guò)程調(diào)用 valueOf() 方法,因此多個(gè)值相同且值在緩存池范圍內(nèi)的 Integer 實(shí)例使用自動(dòng)裝箱來(lái)創(chuàng)建墩蔓,那么就會(huì)引用相同的對(duì)象梢莽。

Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true

基本類(lèi)型對(duì)應(yīng)的緩沖池如下:

  • boolean values true and false
  • all byte values
  • short values between -128 and 127
  • int values between -128 and 127
  • char in the range \u0000 to \u007F

在使用這些基本類(lèi)型對(duì)應(yīng)的包裝類(lèi)型時(shí),如果該數(shù)值范圍在緩沖池范圍內(nèi)奸披,就可以直接使用緩沖池中的對(duì)象昏名。

在 jdk 1.8 所有的數(shù)值類(lèi)緩沖池中,Integer 的緩沖池 IntegerCache 很特殊阵面,這個(gè)緩沖池的下界是 - 128轻局,上界默認(rèn)是 127,但是這個(gè)上界是可調(diào)的膜钓,在啟動(dòng) jvm 的時(shí)候嗽交,通過(guò) -XX:AutoBoxCacheMax=<size> 來(lái)指定這個(gè)緩沖池的大小,該選項(xiàng)在 JVM 初始化的時(shí)候會(huì)設(shè)定一個(gè)名為 java.lang.IntegerCache.high 系統(tǒng)屬性颂斜,然后 IntegerCache 初始化的時(shí)候就會(huì)讀取該系統(tǒng)屬性來(lái)決定上界夫壁。

StackOverflow : Differences between new Integer(123), Integer.valueOf(123) and just 123

二、String

概覽

String 被聲明為 final沃疮,因此它不可被繼承盒让。

在 Java 8 中,String 內(nèi)部使用 char 數(shù)組存儲(chǔ)數(shù)據(jù)司蔬。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}

在 Java 9 之后邑茄,String 類(lèi)的實(shí)現(xiàn)改用 byte 數(shù)組存儲(chǔ)字符串,同時(shí)使用 coder 來(lái)標(biāo)識(shí)使用了哪種編碼俊啼。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final byte[] value;

    /** The identifier of the encoding used to encode the bytes in {@code value}. */
    private final byte coder;
}

value 數(shù)組被聲明為 final肺缕,這意味著 value 數(shù)組初始化之后就不能再引用其它數(shù)組。并且 String 內(nèi)部沒(méi)有改變 value 數(shù)組的方法授帕,因此可以保證 String 不可變同木。

不可變的好處

1. 可以緩存 hash 值

因?yàn)?String 的 hash 值經(jīng)常被使用,例如 String 用做 HashMap 的 key跛十。不可變的特性可以使得 hash 值也不可變彤路,因此只需要進(jìn)行一次計(jì)算。

2. String Pool 的需要

如果一個(gè) String 對(duì)象已經(jīng)被創(chuàng)建過(guò)了芥映,那么就會(huì)從 String Pool 中取得引用洲尊。只有 String 是不可變的远豺,才可能使用 String Pool。

<div align="center"> <img src="pics/9112288f-23f5-4e53-b222-a46fdbca1603.png" width="300px"> </div>

3. 安全性

String 經(jīng)常作為參數(shù)坞嘀,String 不可變性可以保證參數(shù)不可變躯护。例如在作為網(wǎng)絡(luò)連接參數(shù)的情況下如果 String 是可變的,那么在網(wǎng)絡(luò)連接過(guò)程中姆吭,String 被改變榛做,改變 String 對(duì)象的那一方以為現(xiàn)在連接的是其它主機(jī),而實(shí)際情況卻不一定是内狸。

4. 線程安全

String 不可變性天生具備線程安全检眯,可以在多個(gè)線程中安全地使用。

Program Creek : Why String is immutable in Java?

String, StringBuffer and StringBuilder

1. 可變性

  • String 不可變
  • StringBuffer 和 StringBuilder 可變

2. 線程安全

  • String 不可變昆淡,因此是線程安全的
  • StringBuilder 不是線程安全的
  • StringBuffer 是線程安全的锰瘸,內(nèi)部使用 synchronized 進(jìn)行同步

StackOverflow : String, StringBuffer, and StringBuilder

String Pool

字符串常量池(String Pool)保存著所有字符串字面量(literal strings),這些字面量在編譯時(shí)期就確定昂灵。不僅如此避凝,還可以使用 String 的 intern() 方法在運(yùn)行過(guò)程中將字符串添加到 String Pool 中。

當(dāng)一個(gè)字符串調(diào)用 intern() 方法時(shí)眨补,如果 String Pool 中已經(jīng)存在一個(gè)字符串和該字符串值相等(使用 equals() 方法進(jìn)行確定)管削,那么就會(huì)返回 String Pool 中字符串的引用;否則撑螺,就會(huì)在 String Pool 中添加一個(gè)新的字符串含思,并返回這個(gè)新字符串的引用。

下面示例中甘晤,s1 和 s2 采用 new String() 的方式新建了兩個(gè)不同字符串含潘,而 s3 和 s4 是通過(guò) s1.intern() 方法取得一個(gè)字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中线婚,然后返回這個(gè)字符串引用遏弱。因此 s3 和 s4 引用的是同一個(gè)字符串。

String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2);           // false
String s3 = s1.intern();
String s4 = s1.intern();
System.out.println(s3 == s4);           // true

如果是采用 "bbb" 這種字面量的形式創(chuàng)建字符串塞弊,會(huì)自動(dòng)地將字符串放入 String Pool 中漱逸。

String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6);  // true

在 Java 7 之前,String Pool 被放在運(yùn)行時(shí)常量池中游沿,它屬于永久代饰抒。而在 Java 7,String Pool 被移到堆中奏候。這是因?yàn)橛谰么目臻g有限循集,在大量使用字符串的場(chǎng)景下會(huì)導(dǎo)致 OutOfMemoryError 錯(cuò)誤唇敞。

new String("abc")

使用這種方式一共會(huì)創(chuàng)建兩個(gè)字符串對(duì)象(前提是 String Pool 中還沒(méi)有 "abc" 字符串對(duì)象)蔗草。

  • "abc" 屬于字符串字面量咒彤,因此編譯時(shí)期會(huì)在 String Pool 中創(chuàng)建一個(gè)字符串對(duì)象,指向這個(gè) "abc" 字符串字面量咒精;
  • 而使用 new 的方式會(huì)在堆中創(chuàng)建一個(gè)字符串對(duì)象镶柱。

創(chuàng)建一個(gè)測(cè)試類(lèi),其 main 方法中使用這種方式來(lái)創(chuàng)建字符串對(duì)象模叙。

public class NewStringTest {
    public static void main(String[] args) {
        String s = new String("abc");
    }
}

使用 javap -verbose 進(jìn)行反編譯歇拆,得到以下內(nèi)容:

// ...
Constant pool:
// ...
   #2 = Class              #18            // java/lang/String
   #3 = String             #19            // abc
// ...
  #18 = Utf8               java/lang/String
  #19 = Utf8               abc
// ...

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String abc
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
// ...

在 Constant Pool 中,#19 存儲(chǔ)這字符串字面量 "abc"范咨,#3 是 String Pool 的字符串對(duì)象故觅,它指向 #19 這個(gè)字符串字面量。在 main 方法中渠啊,0: 行使用 new #2 在堆中創(chuàng)建一個(gè)字符串對(duì)象输吏,并且使用 ldc #3 將 String Pool 中的字符串對(duì)象作為 String 構(gòu)造函數(shù)的參數(shù)。

以下是 String 構(gòu)造函數(shù)的源碼替蛉,可以看到贯溅,在將一個(gè)字符串對(duì)象作為另一個(gè)字符串對(duì)象的構(gòu)造函數(shù)參數(shù)時(shí),并不會(huì)完全復(fù)制 value 數(shù)組內(nèi)容躲查,而是都會(huì)指向同一個(gè) value 數(shù)組它浅。

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

三、運(yùn)算

參數(shù)傳遞

Java 的參數(shù)是以值傳遞的形式傳入方法中镣煮,而不是引用傳遞姐霍。

以下代碼中 Dog dog 的 dog 是一個(gè)指針,存儲(chǔ)的是對(duì)象的地址怎静。在將一個(gè)參數(shù)傳入一個(gè)方法時(shí)邮弹,本質(zhì)上是將對(duì)象的地址以值的方式傳遞到形參中。因此在方法中使指針引用其它對(duì)象蚓聘,那么這兩個(gè)指針此時(shí)指向的是完全不同的對(duì)象腌乡,在一方改變其所指向?qū)ο蟮膬?nèi)容時(shí)對(duì)另一方?jīng)]有影響。

public class Dog {

    String name;

    Dog(String name) {
        this.name = name;
    }

    String getName() {
        return this.name;
    }

    void setName(String name) {
        this.name = name;
    }

    String getObjectAddress() {
        return super.toString();
    }
}
public class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        func(dog);
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        System.out.println(dog.getName());          // A
    }

    private static void func(Dog dog) {
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        dog = new Dog("B");
        System.out.println(dog.getObjectAddress()); // Dog@74a14482
        System.out.println(dog.getName());          // B
    }
}

如果在方法中改變對(duì)象的字段值會(huì)改變?cè)瓕?duì)象該字段值夜牡,因?yàn)楦淖兊氖峭粋€(gè)地址指向的內(nèi)容与纽。

class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        func(dog);
        System.out.println(dog.getName());          // B
    }

    private static void func(Dog dog) {
        dog.setName("B");
    }
}

StackOverflow: Is Java “pass-by-reference” or “pass-by-value”?

float 與 double

Java 不能隱式執(zhí)行向下轉(zhuǎn)型,因?yàn)檫@會(huì)使得精度降低塘装。

1.1 字面量屬于 double 類(lèi)型急迂,不能直接將 1.1 直接賦值給 float 變量,因?yàn)檫@是向下轉(zhuǎn)型蹦肴。

// float f = 1.1;

1.1f 字面量才是 float 類(lèi)型僚碎。

float f = 1.1f;

隱式類(lèi)型轉(zhuǎn)換

因?yàn)樽置媪?1 是 int 類(lèi)型,它比 short 類(lèi)型精度要高阴幌,因此不能隱式地將 int 類(lèi)型下轉(zhuǎn)型為 short 類(lèi)型勺阐。

short s1 = 1;
// s1 = s1 + 1;

但是使用 += 或者 ++ 運(yùn)算符可以執(zhí)行隱式類(lèi)型轉(zhuǎn)換卷中。

s1 += 1;
// s1++;

上面的語(yǔ)句相當(dāng)于將 s1 + 1 的計(jì)算結(jié)果進(jìn)行了向下轉(zhuǎn)型:

s1 = (short) (s1 + 1);

StackOverflow : Why don't Java's +=, -=, *=, /= compound assignment operators require casting?

switch

從 Java 7 開(kāi)始,可以在 switch 條件判斷語(yǔ)句中使用 String 對(duì)象渊抽。

String s = "a";
switch (s) {
    case "a":
        System.out.println("aaa");
        break;
    case "b":
        System.out.println("bbb");
        break;
}

switch 不支持 long蟆豫,是因?yàn)?switch 的設(shè)計(jì)初衷是對(duì)那些只有少數(shù)的幾個(gè)值進(jìn)行等值判斷,如果值過(guò)于復(fù)雜懒闷,那么還是用 if 比較合適十减。

// long x = 111;
// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'
//     case 111:
//         System.out.println(111);
//         break;
//     case 222:
//         System.out.println(222);
//         break;
// }

StackOverflow : Why can't your switch statement data type be long, Java?

四、繼承

訪問(wèn)權(quán)限

Java 中有三個(gè)訪問(wèn)權(quán)限修飾符:private愤估、protected 以及 public帮辟,如果不加訪問(wèn)修飾符,表示包級(jí)可見(jiàn)玩焰。

可以對(duì)類(lèi)或類(lèi)中的成員(字段以及方法)加上訪問(wèn)修飾符织阅。

  • 類(lèi)可見(jiàn)表示其它類(lèi)可以用這個(gè)類(lèi)創(chuàng)建實(shí)例對(duì)象。
  • 成員可見(jiàn)表示其它類(lèi)可以用這個(gè)類(lèi)的實(shí)例對(duì)象訪問(wèn)到該成員震捣;

protected 用于修飾成員荔棉,表示在繼承體系中成員對(duì)于子類(lèi)可見(jiàn),但是這個(gè)訪問(wèn)修飾符對(duì)于類(lèi)沒(méi)有意義蒿赢。

設(shè)計(jì)良好的模塊會(huì)隱藏所有的實(shí)現(xiàn)細(xì)節(jié)润樱,把它的 API 與它的實(shí)現(xiàn)清晰地隔離開(kāi)來(lái)。模塊之間只通過(guò)它們的 API 進(jìn)行通信羡棵,一個(gè)模塊不需要知道其他模塊的內(nèi)部工作情況壹若,這個(gè)概念被稱為信息隱藏或封裝。因此訪問(wèn)權(quán)限應(yīng)當(dāng)盡可能地使每個(gè)類(lèi)或者成員不被外界訪問(wèn)皂冰。

如果子類(lèi)的方法重寫(xiě)了父類(lèi)的方法店展,那么子類(lèi)中該方法的訪問(wèn)級(jí)別不允許低于父類(lèi)的訪問(wèn)級(jí)別。這是為了確蓖毫鳎可以使用父類(lèi)實(shí)例的地方都可以使用子類(lèi)實(shí)例赂蕴,也就是確保滿足里氏替換原則。

字段決不能是公有的舶胀,因?yàn)檫@么做的話就失去了對(duì)這個(gè)字段修改行為的控制概说,客戶端可以對(duì)其隨意修改。例如下面的例子中嚣伐,AccessExample 擁有 id 公有字段糖赔,如果在某個(gè)時(shí)刻,我們想要使用 int 存儲(chǔ) id 字段轩端,那么就需要修改所有的客戶端代碼放典。

public class AccessExample {
    public String id;
}

可以使用公有的 getter 和 setter 方法來(lái)替換公有字段,這樣的話就可以控制對(duì)字段的修改行為。

public class AccessExample {

    private int id;

    public String getId() {
        return id + "";
    }

    public void setId(String id) {
        this.id = Integer.valueOf(id);
    }
}

但是也有例外奋构,如果是包級(jí)私有的類(lèi)或者私有的嵌套類(lèi)骨田,那么直接暴露成員不會(huì)有特別大的影響。

public class AccessWithInnerClassExample {

    private class InnerClass {
        int x;
    }

    private InnerClass innerClass;

    public AccessWithInnerClassExample() {
        innerClass = new InnerClass();
    }

    public int getValue() {
        return innerClass.x;  // 直接訪問(wèn)
    }
}

抽象類(lèi)與接口

1. 抽象類(lèi)

抽象類(lèi)和抽象方法都使用 abstract 關(guān)鍵字進(jìn)行聲明声怔。如果一個(gè)類(lèi)中包含抽象方法,那么這個(gè)類(lèi)必須聲明為抽象類(lèi)舱呻。

抽象類(lèi)和普通類(lèi)最大的區(qū)別是醋火,抽象類(lèi)不能被實(shí)例化,需要繼承抽象類(lèi)才能實(shí)例化其子類(lèi)箱吕。

public abstract class AbstractClassExample {

    protected int x;
    private int y;

    public abstract void func1();

    public void func2() {
        System.out.println("func2");
    }
}
public class AbstractExtendClassExample extends AbstractClassExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}
// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated
AbstractClassExample ac2 = new AbstractExtendClassExample();
ac2.func1();

2. 接口

接口是抽象類(lèi)的延伸芥驳,在 Java 8 之前,它可以看成是一個(gè)完全抽象的類(lèi)茬高,也就是說(shuō)它不能有任何的方法實(shí)現(xiàn)兆旬。

從 Java 8 開(kāi)始,接口也可以擁有默認(rèn)的方法實(shí)現(xiàn)怎栽,這是因?yàn)椴恢С帜J(rèn)方法的接口的維護(hù)成本太高了丽猬。在 Java 8 之前,如果一個(gè)接口想要添加新的方法熏瞄,那么要修改所有實(shí)現(xiàn)了該接口的類(lèi)脚祟。

接口的成員(字段 + 方法)默認(rèn)都是 public 的,并且不允許定義為 private 或者 protected强饮。

接口的字段默認(rèn)都是 static 和 final 的由桌。

public interface InterfaceExample {

    void func1();

    default void func2(){
        System.out.println("func2");
    }

    int x = 123;
    // int y;               // Variable 'y' might not have been initialized
    public int z = 0;       // Modifier 'public' is redundant for interface fields
    // private int k = 0;   // Modifier 'private' not allowed here
    // protected int l = 0; // Modifier 'protected' not allowed here
    // private void fun3(); // Modifier 'private' not allowed here
}
public class InterfaceImplementExample implements InterfaceExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}
// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);

3. 比較

  • 從設(shè)計(jì)層面上看,抽象類(lèi)提供了一種 IS-A 關(guān)系邮丰,那么就必須滿足里式替換原則行您,即子類(lèi)對(duì)象必須能夠替換掉所有父類(lèi)對(duì)象。而接口更像是一種 LIKE-A 關(guān)系剪廉,它只是提供一種方法實(shí)現(xiàn)契約娃循,并不要求接口和實(shí)現(xiàn)接口的類(lèi)具有 IS-A 關(guān)系。
  • 從使用上來(lái)看斗蒋,一個(gè)類(lèi)可以實(shí)現(xiàn)多個(gè)接口淮野,但是不能繼承多個(gè)抽象類(lèi)。
  • 接口的字段只能是 static 和 final 類(lèi)型的吹泡,而抽象類(lèi)的字段沒(méi)有這種限制骤星。
  • 接口的成員只能是 public 的,而抽象類(lèi)的成員可以有多種訪問(wèn)權(quán)限爆哑。

4. 使用選擇

使用接口:

  • 需要讓不相關(guān)的類(lèi)都實(shí)現(xiàn)一個(gè)方法洞难,例如不相關(guān)的類(lèi)都可以實(shí)現(xiàn) Compareable 接口中的 compareTo() 方法;
  • 需要使用多重繼承揭朝。

使用抽象類(lèi):

  • 需要在幾個(gè)相關(guān)的類(lèi)中共享代碼队贱。
  • 需要能控制繼承來(lái)的成員的訪問(wèn)權(quán)限色冀,而不是都為 public。
  • 需要繼承非靜態(tài)和非常量字段柱嫌。

在很多情況下锋恬,接口優(yōu)先于抽象類(lèi)。因?yàn)榻涌跊](méi)有抽象類(lèi)嚴(yán)格的類(lèi)層次結(jié)構(gòu)要求编丘,可以靈活地為一個(gè)類(lèi)添加行為与学。并且從 Java 8 開(kāi)始,接口也可以有默認(rèn)的方法實(shí)現(xiàn)嘉抓,使得修改接口的成本也變的很低索守。

super

  • 訪問(wèn)父類(lèi)的構(gòu)造函數(shù):可以使用 super() 函數(shù)訪問(wèn)父類(lèi)的構(gòu)造函數(shù),從而委托父類(lèi)完成一些初始化的工作抑片。
  • 訪問(wèn)父類(lèi)的成員:如果子類(lèi)重寫(xiě)了父類(lèi)的某個(gè)方法卵佛,可以通過(guò)使用 super 關(guān)鍵字來(lái)引用父類(lèi)的方法實(shí)現(xiàn)。
public class SuperExample {

    protected int x;
    protected int y;

    public SuperExample(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void func() {
        System.out.println("SuperExample.func()");
    }
}
public class SuperExtendExample extends SuperExample {

    private int z;

    public SuperExtendExample(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public void func() {
        super.func();
        System.out.println("SuperExtendExample.func()");
    }
}
SuperExample e = new SuperExtendExample(1, 2, 3);
e.func();
SuperExample.func()
SuperExtendExample.func()

Using the Keyword super

重寫(xiě)與重載

1. 重寫(xiě)(Override)

存在于繼承體系中敞斋,指子類(lèi)實(shí)現(xiàn)了一個(gè)與父類(lèi)在方法聲明上完全相同的一個(gè)方法截汪。

為了滿足里式替換原則,重寫(xiě)有以下三個(gè)限制:

  • 子類(lèi)方法的訪問(wèn)權(quán)限必須大于等于父類(lèi)方法植捎;
  • 子類(lèi)方法的返回類(lèi)型必須是父類(lèi)方法返回類(lèi)型或?yàn)槠渥宇?lèi)型挫鸽。
  • 子類(lèi)方法拋出的異常類(lèi)型必須是父類(lèi)拋出異常類(lèi)型或?yàn)槠渥宇?lèi)型。

使用 @Override 注解鸥跟,可以讓編譯器幫忙檢查是否滿足上面的三個(gè)限制條件丢郊。

下面的示例中,SubClass 為 SuperClass 的子類(lèi)医咨,SubClass 重寫(xiě)了 SuperClass 的 func() 方法枫匾。其中:

  • 子類(lèi)方法訪問(wèn)權(quán)限為 public,大于父類(lèi)的 protected拟淮。
  • 子類(lèi)的返回類(lèi)型為 ArrayList<Integer>干茉,是父類(lèi)返回類(lèi)型 List<Integer> 的子類(lèi)。
  • 子類(lèi)拋出的異常類(lèi)型為 Exception很泊,是父類(lèi)拋出異常 Throwable 的子類(lèi)角虫。
  • 子類(lèi)重寫(xiě)方法使用 @Override 注解,從而讓編譯器自動(dòng)檢查是否滿足限制條件委造。
class SuperClass {
    protected List<Integer> func() throws Throwable {
        return new ArrayList<>();
    }
}

class SubClass extends SuperClass {
    @Override
    public ArrayList<Integer> func() throws Exception {
        return new ArrayList<>();
    }
}

在調(diào)用一個(gè)方法時(shí)戳鹅,先從本類(lèi)中查找看是否有對(duì)應(yīng)的方法,如果沒(méi)有查找到再到父類(lèi)中查看昏兆,看是否有繼承來(lái)的方法枫虏。否則就要對(duì)參數(shù)進(jìn)行轉(zhuǎn)型,轉(zhuǎn)成父類(lèi)之后看是否有對(duì)應(yīng)的方法×フ總的來(lái)說(shuō)腾它,方法調(diào)用的優(yōu)先級(jí)為:

  • this.func(this)
  • super.func(this)
  • this.func(super)
  • super.func(super)
/*
    A
    |
    B
    |
    C
    |
    D
 */


class A {

    public void show(A obj) {
        System.out.println("A.show(A)");
    }

    public void show(C obj) {
        System.out.println("A.show(C)");
    }
}

class B extends A {

    @Override
    public void show(A obj) {
        System.out.println("B.show(A)");
    }
}

class C extends B {
}

class D extends C {
}
public static void main(String[] args) {

    A a = new A();
    B b = new B();
    C c = new C();
    D d = new D();

    // 在 A 中存在 show(A obj),直接調(diào)用
    a.show(a); // A.show(A)
    // 在 A 中不存在 show(B obj)死讹,將 B 轉(zhuǎn)型成其父類(lèi) A
    a.show(b); // A.show(A)
    // 在 B 中存在從 A 繼承來(lái)的 show(C obj)瞒滴,直接調(diào)用
    b.show(c); // A.show(C)
    // 在 B 中不存在 show(D obj),但是存在從 A 繼承來(lái)的 show(C obj)赞警,將 D 轉(zhuǎn)型成其父類(lèi) C
    b.show(d); // A.show(C)

    // 引用的還是 B 對(duì)象妓忍,所以 ba 和 b 的調(diào)用結(jié)果一樣
    A ba = new B();
    ba.show(c); // A.show(C)
    ba.show(d); // A.show(C)
}

2. 重載(Overload)

存在于同一個(gè)類(lèi)中,指一個(gè)方法與已經(jīng)存在的方法名稱上相同仅颇,但是參數(shù)類(lèi)型、個(gè)數(shù)碘举、順序至少有一個(gè)不同忘瓦。

應(yīng)該注意的是,返回值不同引颈,其它都相同不算是重載耕皮。

五、Object 通用方法

概覽


public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

equals()

1. 等價(jià)關(guān)系

Ⅰ 自反性

x.equals(x); // true

Ⅱ 對(duì)稱性

x.equals(y) == y.equals(x); // true

Ⅲ 傳遞性

if (x.equals(y) && y.equals(z))
    x.equals(z); // true;

Ⅳ 一致性

多次調(diào)用 equals() 方法結(jié)果不變

x.equals(y) == x.equals(y); // true

Ⅴ 與 null 的比較

對(duì)任何不是 null 的對(duì)象 x 調(diào)用 x.equals(null) 結(jié)果都為 false

x.equals(null); // false;

2. 等價(jià)與相等

  • 對(duì)于基本類(lèi)型蝙场,== 判斷兩個(gè)值是否相等凌停,基本類(lèi)型沒(méi)有 equals() 方法。
  • 對(duì)于引用類(lèi)型售滤,== 判斷兩個(gè)變量是否引用同一個(gè)對(duì)象罚拟,而 equals() 判斷引用的對(duì)象是否等價(jià)。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

3. 實(shí)現(xiàn)

  • 檢查是否為同一個(gè)對(duì)象的引用完箩,如果是直接返回 true赐俗;
  • 檢查是否是同一個(gè)類(lèi)型,如果不是弊知,直接返回 false阻逮;
  • 將 Object 對(duì)象進(jìn)行轉(zhuǎn)型;
  • 判斷每個(gè)關(guān)鍵域是否相等秩彤。
public class EqualExample {

    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        EqualExample that = (EqualExample) o;

        if (x != that.x) return false;
        if (y != that.y) return false;
        return z == that.z;
    }
}

hashCode()

hashCode() 返回散列值叔扼,而 equals() 是用來(lái)判斷兩個(gè)對(duì)象是否等價(jià)。等價(jià)的兩個(gè)對(duì)象散列值一定相同漫雷,但是散列值相同的兩個(gè)對(duì)象不一定等價(jià)瓜富。

在覆蓋 equals() 方法時(shí)應(yīng)當(dāng)總是覆蓋 hashCode() 方法,保證等價(jià)的兩個(gè)對(duì)象散列值也相等降盹。

下面的代碼中食呻,新建了兩個(gè)等價(jià)的對(duì)象,并將它們添加到 HashSet 中。我們希望將這兩個(gè)對(duì)象當(dāng)成一樣的仅胞,只在集合中添加一個(gè)對(duì)象每辟,但是因?yàn)?EqualExample 沒(méi)有實(shí)現(xiàn) hashCode() 方法,因此這兩個(gè)對(duì)象的散列值是不同的干旧,最終導(dǎo)致集合添加了兩個(gè)等價(jià)的對(duì)象渠欺。

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

理想的散列函數(shù)應(yīng)當(dāng)具有均勻性,即不相等的對(duì)象應(yīng)當(dāng)均勻分布到所有可能的散列值上椎眯。這就要求了散列函數(shù)要把所有域的值都考慮進(jìn)來(lái)挠将。可以將每個(gè)域都當(dāng)成 R 進(jìn)制的某一位编整,然后組成一個(gè) R 進(jìn)制的整數(shù)舔稀。R 一般取 31,因?yàn)樗且粋€(gè)奇素?cái)?shù)掌测,如果是偶數(shù)的話内贮,當(dāng)出現(xiàn)乘法溢出,信息就會(huì)丟失汞斧,因?yàn)榕c 2 相乘相當(dāng)于向左移一位夜郁。

一個(gè)數(shù)與 31 相乘可以轉(zhuǎn)換成移位和減法:31*x == (x<<5)-x,編譯器會(huì)自動(dòng)進(jìn)行這個(gè)優(yōu)化粘勒。

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}

toString()

默認(rèn)返回 ToStringExample@4554617c 這種形式竞端,其中 @ 后面的數(shù)值為散列碼的無(wú)符號(hào)十六進(jìn)制表示。

public class ToStringExample {

    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}
ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());
ToStringExample@4554617c

clone()

1. cloneable

clone() 是 Object 的 protected 方法庙睡,它不是 public事富,一個(gè)類(lèi)不顯式去重寫(xiě) clone(),其它類(lèi)就不能直接去調(diào)用該類(lèi)實(shí)例的 clone() 方法乘陪。

public class CloneExample {
    private int a;
    private int b;
}
CloneExample e1 = new CloneExample();
// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object'

重寫(xiě) clone() 得到以下實(shí)現(xiàn):

public class CloneExample {
    private int a;
    private int b;

    @Override
    public CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}
CloneExample e1 = new CloneExample();
try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
java.lang.CloneNotSupportedException: CloneExample

以上拋出了 CloneNotSupportedException赵颅,這是因?yàn)?CloneExample 沒(méi)有實(shí)現(xiàn) Cloneable 接口。

應(yīng)該注意的是暂刘,clone() 方法并不是 Cloneable 接口的方法饺谬,而是 Object 的一個(gè) protected 方法。Cloneable 接口只是規(guī)定谣拣,如果一個(gè)類(lèi)沒(méi)有實(shí)現(xiàn) Cloneable 接口又調(diào)用了 clone() 方法募寨,就會(huì)拋出 CloneNotSupportedException。

public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2. 淺拷貝

拷貝對(duì)象和原始對(duì)象的引用類(lèi)型引用同一個(gè)對(duì)象森缠。

public class ShallowCloneExample implements Cloneable {

    private int[] arr;

    public ShallowCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ShallowCloneExample clone() throws CloneNotSupportedException {
        return (ShallowCloneExample) super.clone();
    }
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222

3. 深拷貝

拷貝對(duì)象和原始對(duì)象的引用類(lèi)型引用不同對(duì)象拔鹰。

public class DeepCloneExample implements Cloneable {

    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

4. clone() 的替代方案

使用 clone() 方法來(lái)拷貝一個(gè)對(duì)象即復(fù)雜又有風(fēng)險(xiǎn),它會(huì)拋出異常贵涵,并且還需要類(lèi)型轉(zhuǎn)換列肢。Effective Java 書(shū)上講到恰画,最好不要去使用 clone(),可以使用拷貝構(gòu)造函數(shù)或者拷貝工廠來(lái)拷貝一個(gè)對(duì)象瓷马。

public class CloneConstructorExample {

    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

六拴还、關(guān)鍵字

final

1. 數(shù)據(jù)

聲明數(shù)據(jù)為常量,可以是編譯時(shí)常量欧聘,也可以是在運(yùn)行時(shí)被初始化后不能被改變的常量片林。

  • 對(duì)于基本類(lèi)型,final 使數(shù)值不變怀骤;
  • 對(duì)于引用類(lèi)型费封,final 使引用不變,也就不能引用其它對(duì)象蒋伦,但是被引用的對(duì)象本身是可以修改的弓摘。
final int x = 1;
// x = 2;  // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;

2. 方法

聲明方法不能被子類(lèi)重寫(xiě)。

private 方法隱式地被指定為 final痕届,如果在子類(lèi)中定義的方法和基類(lèi)中的一個(gè) private 方法簽名相同韧献,此時(shí)子類(lèi)的方法不是重寫(xiě)基類(lèi)方法,而是在子類(lèi)中定義了一個(gè)新的方法爷抓。

3. 類(lèi)

聲明類(lèi)不允許被繼承势决。

static

1. 靜態(tài)變量

  • 靜態(tài)變量:又稱為類(lèi)變量阻塑,也就是說(shuō)這個(gè)變量屬于類(lèi)的蓝撇,類(lèi)所有的實(shí)例都共享靜態(tài)變量,可以直接通過(guò)類(lèi)名來(lái)訪問(wèn)它陈莽。靜態(tài)變量在內(nèi)存中只存在一份渤昌。
  • 實(shí)例變量:每創(chuàng)建一個(gè)實(shí)例就會(huì)產(chǎn)生一個(gè)實(shí)例變量,它與該實(shí)例同生共死走搁。
public class A {

    private int x;         // 實(shí)例變量
    private static int y;  // 靜態(tài)變量

    public static void main(String[] args) {
        // int x = A.x;  // Non-static field 'x' cannot be referenced from a static context
        A a = new A();
        int x = a.x;
        int y = A.y;
    }
}

2. 靜態(tài)方法

靜態(tài)方法在類(lèi)加載的時(shí)候就存在了独柑,它不依賴于任何實(shí)例。所以靜態(tài)方法必須有實(shí)現(xiàn)私植,也就是說(shuō)它不能是抽象方法忌栅。

public abstract class A {
    public static void func1(){
    }
    // public abstract static void func2();  // Illegal combination of modifiers: 'abstract' and 'static'
}

只能訪問(wèn)所屬類(lèi)的靜態(tài)字段和靜態(tài)方法,方法中不能有 this 和 super 關(guān)鍵字曲稼。

public class A {

    private static int x;
    private int y;

    public static void func1(){
        int a = x;
        // int b = y;  // Non-static field 'y' cannot be referenced from a static context
        // int b = this.y;     // 'A.this' cannot be referenced from a static context
    }
}

3. 靜態(tài)語(yǔ)句塊

靜態(tài)語(yǔ)句塊在類(lèi)初始化時(shí)運(yùn)行一次索绪。

public class A {
    static {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
    }
}
123

4. 靜態(tài)內(nèi)部類(lèi)

非靜態(tài)內(nèi)部類(lèi)依賴于外部類(lèi)的實(shí)例,而靜態(tài)內(nèi)部類(lèi)不需要贫悄。

public class OuterClass {

    class InnerClass {
    }

    static class StaticInnerClass {
    }

    public static void main(String[] args) {
        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

靜態(tài)內(nèi)部類(lèi)不能訪問(wèn)外部類(lèi)的非靜態(tài)的變量和方法瑞驱。

5. 靜態(tài)導(dǎo)包

在使用靜態(tài)變量和方法時(shí)不用再指明 ClassName,從而簡(jiǎn)化代碼窄坦,但可讀性大大降低唤反。

import static com.xxx.ClassName.*

6. 初始化順序

靜態(tài)變量和靜態(tài)語(yǔ)句塊優(yōu)先于實(shí)例變量和普通語(yǔ)句塊凳寺,靜態(tài)變量和靜態(tài)語(yǔ)句塊的初始化順序取決于它們?cè)诖a中的順序。

public static String staticField = "靜態(tài)變量";
static {
    System.out.println("靜態(tài)語(yǔ)句塊");
}
public String field = "實(shí)例變量";
{
    System.out.println("普通語(yǔ)句塊");
}

最后才是構(gòu)造函數(shù)的初始化彤侍。

public InitialOrderTest() {
    System.out.println("構(gòu)造函數(shù)");
}

存在繼承的情況下肠缨,初始化順序?yàn)椋?/p>

  • 父類(lèi)(靜態(tài)變量、靜態(tài)語(yǔ)句塊)
  • 子類(lèi)(靜態(tài)變量拥刻、靜態(tài)語(yǔ)句塊)
  • 父類(lèi)(實(shí)例變量怜瞒、普通語(yǔ)句塊)
  • 父類(lèi)(構(gòu)造函數(shù))
  • 子類(lèi)(實(shí)例變量、普通語(yǔ)句塊)
  • 子類(lèi)(構(gòu)造函數(shù))

七般哼、反射

每個(gè)類(lèi)都有一個(gè) Class 對(duì)象吴汪,包含了與類(lèi)有關(guān)的信息。當(dāng)編譯一個(gè)新類(lèi)時(shí)蒸眠,會(huì)產(chǎn)生一個(gè)同名的 .class 文件漾橙,該文件內(nèi)容保存著 Class 對(duì)象。

類(lèi)加載相當(dāng)于 Class 對(duì)象的加載楞卡,類(lèi)在第一次使用時(shí)才動(dòng)態(tài)加載到 JVM 中霜运。也可以使用 Class.forName("com.mysql.jdbc.Driver") 這種方式來(lái)控制類(lèi)的加載,該方法會(huì)返回一個(gè) Class 對(duì)象蒋腮。

反射可以提供運(yùn)行時(shí)的類(lèi)信息淘捡,并且這個(gè)類(lèi)可以在運(yùn)行時(shí)才加載進(jìn)來(lái),甚至在編譯時(shí)期該類(lèi)的 .class 不存在也可以加載進(jìn)來(lái)池摧。

Class 和 java.lang.reflect 一起對(duì)反射提供了支持焦除,java.lang.reflect 類(lèi)庫(kù)主要包含了以下三個(gè)類(lèi):

  • Field :可以使用 get() 和 set() 方法讀取和修改 Field 對(duì)象關(guān)聯(lián)的字段;
  • Method :可以使用 invoke() 方法調(diào)用與 Method 對(duì)象關(guān)聯(lián)的方法作彤;
  • Constructor :可以用 Constructor 的 newInstance() 創(chuàng)建新的對(duì)象膘魄。

反射的優(yōu)點(diǎn):

  • 可擴(kuò)展性 :應(yīng)用程序可以利用全限定名創(chuàng)建可擴(kuò)展對(duì)象的實(shí)例,來(lái)使用來(lái)自外部的用戶自定義類(lèi)竭讳。
  • 類(lèi)瀏覽器和可視化開(kāi)發(fā)環(huán)境 :一個(gè)類(lèi)瀏覽器需要可以枚舉類(lèi)的成員创葡。可視化開(kāi)發(fā)環(huán)境(如 IDE)可以從利用反射中可用的類(lèi)型信息中受益绢慢,以幫助程序員編寫(xiě)正確的代碼灿渴。
  • 調(diào)試器和測(cè)試工具 : 調(diào)試器需要能夠檢查一個(gè)類(lèi)里的私有成員。測(cè)試工具可以利用反射來(lái)自動(dòng)地調(diào)用類(lèi)里定義的可被發(fā)現(xiàn)的 API 定義胰舆,以確保一組測(cè)試中有較高的代碼覆蓋率骚露。

反射的缺點(diǎn):

盡管反射非常強(qiáng)大,但也不能濫用思瘟。如果一個(gè)功能可以不用反射完成荸百,那么最好就不用。在我們使用反射技術(shù)時(shí)滨攻,下面幾條內(nèi)容應(yīng)該牢記于心够话。

  • 性能開(kāi)銷(xiāo) :反射涉及了動(dòng)態(tài)類(lèi)型的解析蓝翰,所以 JVM 無(wú)法對(duì)這些代碼進(jìn)行優(yōu)化。因此女嘲,反射操作的效率要比那些非反射操作低得多畜份。我們應(yīng)該避免在經(jīng)常被執(zhí)行的代碼或?qū)π阅芤蠛芨叩某绦蛑惺褂梅瓷洹?/p>

  • 安全限制 :使用反射技術(shù)要求程序必須在一個(gè)沒(méi)有安全限制的環(huán)境中運(yùn)行。如果一個(gè)程序必須在有安全限制的環(huán)境中運(yùn)行欣尼,如 Applet爆雹,那么這就是個(gè)問(wèn)題了。

  • 內(nèi)部暴露 :由于反射允許代碼執(zhí)行一些在正常情況下不被允許的操作(比如訪問(wèn)私有的屬性和方法)愕鼓,所以使用反射可能會(huì)導(dǎo)致意料之外的副作用钙态,這可能導(dǎo)致代碼功能失調(diào)并破壞可移植性。反射代碼破壞了抽象性菇晃,因此當(dāng)平臺(tái)發(fā)生改變的時(shí)候册倒,代碼的行為就有可能也隨著變化。

八磺送、異常

Throwable 可以用來(lái)表示任何可以作為異常拋出的類(lèi)驻子,分為兩種: ErrorException。其中 Error 用來(lái)表示 JVM 無(wú)法處理的錯(cuò)誤估灿,Exception 分為兩種:

  • 受檢異常 :需要用 try...catch... 語(yǔ)句捕獲并進(jìn)行處理崇呵,并且可以從異常中恢復(fù);
  • 非受檢異常 :是程序運(yùn)行時(shí)錯(cuò)誤馅袁,例如除 0 會(huì)引發(fā) Arithmetic Exception域慷,此時(shí)程序崩潰并且無(wú)法恢復(fù)。

<div align="center"> <img src="pics/PPjwP.png" width="600"/> </div>

九司顿、泛型

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

十芒粹、注解

Java 注解是附加在代碼中的一些元信息兄纺,用于一些工具在編譯大溜、運(yùn)行時(shí)進(jìn)行解析和使用,起到說(shuō)明估脆、配置的功能钦奋。注解不會(huì)也不能影響代碼的實(shí)際邏輯,僅僅起到輔助性的作用疙赠。

注解 Annotation 實(shí)現(xiàn)原理與自定義注解例子

十一付材、特性

Java 各版本的新特性

New highlights in Java SE 8

  1. Lambda Expressions
  2. Pipelines and Streams
  3. Date and Time API
  4. Default Methods
  5. Type Annotations
  6. Nashhorn JavaScript Engine
  7. Concurrent Accumulators
  8. Parallel operations
  9. PermGen Error Removed

New highlights in Java SE 7

  1. Strings in Switch Statement
  2. Type Inference for Generic Instance Creation
  3. Multiple Exception Handling
  4. Support for Dynamic Languages
  5. Try with Resources
  6. Java nio Package
  7. Binary Literals, Underscore in literals
  8. Diamond Syntax

Java 與 C++ 的區(qū)別

  • Java 是純粹的面向?qū)ο笳Z(yǔ)言,所有的對(duì)象都繼承自 java.lang.Object圃阳,C++ 為了兼容 C 即支持面向?qū)ο笠仓С置嫦蜻^(guò)程厌衔。
  • Java 通過(guò)虛擬機(jī)從而實(shí)現(xiàn)跨平臺(tái)特性,但是 C++ 依賴于特定的平臺(tái)捍岳。
  • Java 沒(méi)有指針富寿,它的引用可以理解為安全指針睬隶,而 C++ 具有和 C 一樣的指針。
  • Java 支持自動(dòng)垃圾回收页徐,而 C++ 需要手動(dòng)回收苏潜。
  • Java 不支持多重繼承,只能通過(guò)實(shí)現(xiàn)多個(gè)接口來(lái)達(dá)到相同目的变勇,而 C++ 支持多重繼承恤左。
  • Java 不支持操作符重載,雖然可以對(duì)兩個(gè) String 對(duì)象執(zhí)行加法運(yùn)算搀绣,但是這是語(yǔ)言內(nèi)置支持的操作飞袋,不屬于操作符重載,而 C++ 可以链患。
  • Java 的 goto 是保留字授嘀,但是不可用,C++ 可以使用 goto锣险。
  • Java 不支持條件編譯蹄皱,C++ 通過(guò) #ifdef #ifndef 等預(yù)處理命令從而實(shí)現(xiàn)條件編譯。

What are the main differences between Java and C++?

JRE or JDK

  • JRE is the JVM program, Java application need to run on JRE.
  • JDK is a superset of JRE, JRE + tools for developing java programs. e.g, it provides the compiler "javac"

參考資料

  • Eckel B. Java 編程思想[M]. 機(jī)械工業(yè)出版社, 2002.
  • Bloch J. Effective java[M]. Addison-Wesley Professional, 2017.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芯肤,一起剝皮案震驚了整個(gè)濱河市巷折,隨后出現(xiàn)的幾起案子大磺,更是在濱河造成了極大的恐慌炭懊,老刑警劉巖画切,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栋盹,死亡現(xiàn)場(chǎng)離奇詭異弱恒,居然都是意外死亡裁替,警方通過(guò)查閱死者的電腦和手機(jī)铁瞒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)势似,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)歌豺,“玉大人推穷,你說(shuō)我怎么就攤上這事±噙郑” “怎么了馒铃?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)痕惋。 經(jīng)常有香客問(wèn)我区宇,道長(zhǎng),這世上最難降的妖魔是什么值戳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任议谷,我火速辦了婚禮,結(jié)果婚禮上堕虹,老公的妹妹穿的比我還像新娘卧晓。我一直安慰自己叶洞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布禀崖。 她就那樣靜靜地躺著衩辟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪波附。 梳的紋絲不亂的頭發(fā)上艺晴,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音掸屡,去河邊找鬼封寞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仅财,可吹牛的內(nèi)容都是我干的狈究。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盏求,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抖锥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起碎罚,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤磅废,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后荆烈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拯勉,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年憔购,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宫峦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玫鸟,死狀恐怖导绷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鞋邑,我是刑警寧澤诵次,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布账蓉,位于F島的核電站枚碗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏铸本。R本人自食惡果不足惜肮雨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望箱玷。 院中可真熱鬧怨规,春花似錦陌宿、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掰烟,卻和暖如春爽蝴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纫骑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工蝎亚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人先馆。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓发框,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親煤墙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梅惯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348