Java編程思想重點(diǎn)筆記

1. Java中的多態(tài)性理解(注意與C++區(qū)分)

  • Java中除了static方法和final方法(private方法本質(zhì)上屬于final方法博脑,因?yàn)椴荒鼙蛔宇愒L問)之外驻谆,其它所有的方法都是動(dòng)態(tài)綁定,這意味著通常情況下奈嘿,我們不必判定是否應(yīng)該進(jìn)行動(dòng)態(tài)綁定—它會(huì)自動(dòng)發(fā)生喝噪。
    • final方法會(huì)使編譯器生成更有效的代碼,這也是為什么說聲明為final方法能在一定程度上提高性能(效果不明顯)指么。
    • 如果某個(gè)方法是靜態(tài)的酝惧,它的行為就不具有多態(tài)性:
    class StaticSuper {
        public static String staticGet() {
            return "Base staticGet()";
        }
        
        public String dynamicGet() {
            return "Base dynamicGet()";
        }
    }
    
    class StaticSub extends StaticSuper {
        public static String staticGet() {
            return "Derived staticGet()";
        }
        
        public String dynamicGet() {
            return "Derived dynamicGet()";
        }
    }
    
    public class StaticPolymorphism {
        
        public static void main(String[] args) {
            StaticSuper sup = new StaticSub();
            System.out.println(sup.staticGet());
            System.out.println(sup.dynamicGet());
        }
    
    }
    
      輸出:
    

Base staticGet()
Derived dynamicGet()

  • 構(gòu)造函數(shù)并不具有多態(tài)性榴鼎,它們實(shí)際上是static方法,只不過該static聲明是隱式的晚唇。因此巫财,構(gòu)造函數(shù)不能夠被override。

  • 在父類構(gòu)造函數(shù)內(nèi)部調(diào)用具有多態(tài)行為的函數(shù)將導(dǎo)致無法預(yù)測(cè)的結(jié)果哩陕,因?yàn)榇藭r(shí)子類對(duì)象還沒初始化平项,此時(shí)調(diào)用子類方法不會(huì)得到我們想要的結(jié)果。

    class Glyph {
        void draw() {
            System.out.println("Glyph.draw()");
        }
        Glyph() {
            System.out.println("Glyph() before draw()");
            draw();
            System.out.println("Glyph() after draw()");
        }
    }
    
    class RoundGlyph extends Glyph {
        private int radius = 1;
        
        RoundGlyph(int r) {
            radius = r;
            System.out.println("RoundGlyph.RoundGlyph(). radius = " + radius);
        }
        
        void draw() {
            System.out.println("RoundGlyph.draw(). radius = " + radius);
        }
    }
    
    public class PolyConstructors {
    
        public static void main(String[] args) {
            new RoundGlyph(5);
    
        }
    
    }
    

輸出:

Glyph() before draw()
RoundGlyph.draw(). radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(). radius = 5

為什么會(huì)這樣輸出悍及?這就要明確掌握Java中構(gòu)造函數(shù)的調(diào)用順序

(1)在其他任何事物發(fā)生之前闽瓢,將分配給對(duì)象的存儲(chǔ)空間初始化成二進(jìn)制0;
(2)調(diào)用基類構(gòu)造函數(shù)心赶。從根開始遞歸下去扣讼,因?yàn)槎鄳B(tài)性此時(shí)調(diào)用子類覆蓋后的draw()方法(要在調(diào)用RoundGlyph構(gòu)造函數(shù)之前調(diào)用),由于步驟1的緣故缨叫,我們此時(shí)會(huì)發(fā)現(xiàn)radius的值為0椭符;
(3)按聲明順序調(diào)用成員的初始化方法;
(4)最后調(diào)用子類的構(gòu)造函數(shù)耻姥。

  • 只有非private方法才可以被覆蓋销钝,但是還需要密切注意覆蓋private方法的現(xiàn)象,這時(shí)雖然編譯器不會(huì)報(bào)錯(cuò)琐簇,但是也不會(huì)按照我們所期望的來執(zhí)行蒸健,即覆蓋private方法對(duì)子類來說是一個(gè)新的方法而非重載方法。因此婉商,在子類中似忧,新方法名最好不要與基類的private方法采取同一名字(雖然沒關(guān)系,但容易誤解据某,以為能夠覆蓋基類的private方法)橡娄。

  • Java類中屬性域的訪問操作都由編譯器解析,因此不是多態(tài)的癣籽。父類和子類的同名屬性都會(huì)分配不同的存儲(chǔ)空間挽唉,如下:

    // Direct field access is determined at compile time.
    class Super {
        public int field = 0;
        public int getField() {
            return field;
        }
    }
    
    class Sub extends Super {
        public int field = 1;
        public int getField() {
            return field;
        }
        public int getSuperField() {
            return super.field;
        }
    }
    
    public class FieldAccess {
    
        public static void main(String[] args) {
            Super sup = new Sub();
            System.out.println("sup.filed = " + sup.field + 
                    ", sup.getField() = " + sup.getField());
            Sub sub = new Sub();
            System.out.println("sub.filed = " + sub.field + 
                    ", sub.getField() = " + sub.getField() + 
                    ", sub.getSuperField() = " + sub.getSuperField());
        }
    
    }
    

    輸出:

sup.filed = 0, sup.getField() = 1
sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0

 Sub子類實(shí)際上**包含了兩個(gè)稱為field的域**,然而在引用Sub中的field時(shí)所產(chǎn)生的默認(rèn)域并非Super版本的field域筷狼,因此為了得到Super.field瓶籽,必須**顯式地指明**super.field。

2. is-a關(guān)系和is-like-a關(guān)系

  • is-a關(guān)系屬于純繼承埂材,即只有在基類中已經(jīng)建立的方法才可以在子類中被覆蓋塑顺,如下圖所示:

    is-a.png

    基類和子類有著完全相同的接口,這樣向上轉(zhuǎn)型時(shí)永遠(yuǎn)不需要知道正在處理的對(duì)象的確切類型,這通過多態(tài)來實(shí)現(xiàn)严拒。

  • is-like-a關(guān)系:子類擴(kuò)展了基類接口扬绪。它有著相同的基本接口,但是他還具有由額外方法實(shí)現(xiàn)的其他特性裤唠。

    has-a.png

    缺點(diǎn)就是子類中接口的擴(kuò)展部分不能被基類訪問挤牛,因此一旦向上轉(zhuǎn)型,就不能調(diào)用那些新方法种蘸。

3. 運(yùn)行時(shí)類型信息(RTTI + 反射)

  • 概念
    RTTI:運(yùn)行時(shí)類型信息使得你可以在程序運(yùn)行時(shí)發(fā)現(xiàn)和使用類型信息墓赴。

  • 使用方式
    Java是如何讓我們?cè)谶\(yùn)行時(shí)識(shí)別對(duì)象和類的信息的,主要有兩種方式(還有輔助的第三種方式航瞭,見下描述):

    • 一種是“傳統(tǒng)的”RTTI诫硕,它假定我們?cè)诰幾g時(shí)已經(jīng)知道了所有的類型,比如Shape s = (Shape)s1刊侯;
    • 另一種是“反射”機(jī)制章办,它運(yùn)行我們?cè)谶\(yùn)行時(shí)發(fā)現(xiàn)和使用類的信息,即使用Class.forName()滔吠。
    • 其實(shí)還有第三種形式纲菌,就是關(guān)鍵字instanceof挠日,它返回一個(gè)bool值疮绷,它保持了類型的概念,它指的是“你是這個(gè)類嗎嚣潜?或者你是這個(gè)類的派生類嗎冬骚?”。而如果用==或equals比較實(shí)際的Class對(duì)象懂算,就沒有考慮繼承—它或者是這個(gè)確切的類型只冻,或者不是。
  • 工作原理
    要理解RTTI在Java中的工作原理计技,首先必須知道類型信息在運(yùn)行時(shí)是如何表示的,這項(xiàng)工作是由稱為Class對(duì)象的特殊對(duì)象完成的垮媒,它包含了與類有關(guān)的信息睡雇。Java送Class對(duì)象來執(zhí)行其RTTI秕豫,使用類加載器的子系統(tǒng)實(shí)現(xiàn)混移。

無論何時(shí)歌径,只要你想在運(yùn)行時(shí)使用類型信息沮脖,就必須首先獲得對(duì)恰當(dāng)?shù)腃lass對(duì)象的引用勺届,獲取方式有三種:
(1)如果你沒有持有該類型的對(duì)象免姿,則Class.forName()就是實(shí)現(xiàn)此功能的便捷途故俐,因?yàn)樗恍枰獙?duì)象信息药版;
(2)如果你已經(jīng)擁有了一個(gè)感興趣的類型的對(duì)象槽片,那就可以通過調(diào)用getClass()方法來獲取Class引用了还栓,它將返回表示該對(duì)象的實(shí)際類型的Class引用。Class包含很有有用的方法辽聊,比如:
```java
package rtti;
interface HasBatteries{}
interface WaterProof{}
interface Shoots{}

class Toy {
    Toy() {}
    Toy(int i) {}
}

class FancyToy extends Toy
implements HasBatteries, WaterProof, Shoots {
    FancyToy() {
        super(1);
    }
}

public class RTTITest {
    
    static void printInfo(Class cc) {
        System.out.println("Class name: " + cc.getName() + 
                ", is interface? [" + cc.isInterface() + "]");
        System.out.println("Simple name: " + cc.getSimpleName());
        System.out.println("Canonical name: " + cc.getCanonicalName());
    }
    
    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("rtti.FancyToy"); // 必須是全限定名(包名+類名)
        } catch(ClassNotFoundException e) {
            System.out.println("Can't find FancyToy");
            System.exit(1);
        }
        printInfo(c);
        
        for(Class face : c.getInterfaces()) {
            printInfo(face);
        }
        
        Class up = c.getSuperclass();
        Object obj = null;
        try {
            // Requires default constructor.
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Can't Instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Can't access");
            System.exit(1);
        }
        printInfo(obj.getClass());
    }

}
```

輸出:

Class name: rtti.FancyToy, is interface? [false]
Simple name: FancyToy
Canonical name: rtti.FancyToy
Class name: rtti.HasBatteries, is interface? [true]
Simple name: HasBatteries
Canonical name: rtti.HasBatteries
Class name: rtti.WaterProof, is interface? [true]
Simple name: WaterProof

Canonical name: rtti.WaterProof
Class name: rtti.Shoots, is interface? [true]
Simple name: Shoots
Canonical name: rtti.Shoots
Class name: rtti.Toy, is interface? [false]
Simple name: Toy
Canonical name: rtti.Toy

(3)Java還提供了另一種方法來生成對(duì)Class對(duì)象的引用唯灵,即使用類字面常量。比如上面的就像這樣:FancyToy.class;來引用。
這樣做不僅更簡(jiǎn)單斑匪,而且更安全蚀瘸,因?yàn)樗?strong>在編譯時(shí)就會(huì)受到檢查(因此不需要置于try語句塊中)贮勃,并且它根除了對(duì)forName方法的引用,所以也更高效泉孩。類字面常量不僅可以應(yīng)用于普通的類寓搬,也可以應(yīng)用于接口曼尊、數(shù)組以及基本數(shù)據(jù)類型骆撇。


注意:當(dāng)使用“.class”來創(chuàng)建對(duì)Class對(duì)象的引用時(shí)肴裙,不會(huì)自動(dòng)地初始化該Class對(duì)象蜻懦,初始化被延遲到了對(duì)靜態(tài)方法(構(gòu)造器隱式的是靜態(tài)的)或者非final靜態(tài)域(注意final靜態(tài)域不會(huì)觸發(fā)初始化操作)進(jìn)行首次引用時(shí)才執(zhí)行:宛乃。而使用Class.forName時(shí)會(huì)自動(dòng)的初始化析既。

為了使用類而做的準(zhǔn)備工作實(shí)際包含三個(gè)步驟:

  • 加載:由類加載器執(zhí)行。查找字節(jié)碼宰译,并從這些字節(jié)碼中創(chuàng)建一個(gè)Class對(duì)象
  • 鏈接:驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間肋坚,并且如果必需的話,將解析這個(gè)類創(chuàng)建的對(duì)其他類的所有引用铣鹏。
  • 初始化:如果該類具有超類,則對(duì)其初始化合溺,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。

這一點(diǎn)非常重要睛约,下面通過一個(gè)實(shí)例來說明這兩者的區(qū)別:

package rtti;
import java.util.Random;
class Initable {
        static final int staticFinal = 47;
        static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);
    
        static {
            System.out.println("Initializing Initable");
        }
}
class Initable2 {
        static int staticNonFinal = 147;
        
        static {
            System.out.println("Initializing Initable2");
        }
}
class Initable3 {
        static int staticNonFinal = 74;
        
        static {
            System.out.println("Initializing Initable3");
        }
}
public class ClassInitialization {

        public static Random rand = new Random(47);
        
        public static void main(String[] args) {
            // Does not trigger initialization
            Class initable = Initable.class;
            System.out.println("After creating Initable ref");
            // Does not trigger initialization
            System.out.println(Initable.staticFinal);
            // Does trigger initialization(rand() is static method)
            System.out.println(Initable.staticFinal2);
            
            // Does trigger initialization(not final)
            System.out.println(Initable2.staticNonFinal);
            
            try {
                Class initable3 = Class.forName("rtti.Initable3");
            } catch (ClassNotFoundException e) {
                System.out.println("Can't find Initable3");
                System.exit(1);
            }
            System.out.println("After creating Initable3 ref");
            System.out.println(Initable3.staticNonFinal);
        }
}

輸出:

After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74


  • RTTI的限制怔揩?如何突破? — 反射機(jī)制
    如果不知道某個(gè)對(duì)象的確切類型秘案,RTTI可以告訴你茬缩,但是有一個(gè)限制:這個(gè)類型在編譯時(shí)必須已知未舟,這樣才能使用RTTI識(shí)別它勇哗,也就是在編譯時(shí),編譯器必須知道所有要通過RTTI來處理的類扰法。

    可以突破這個(gè)限制嗎蛹含?是的,突破它的就是反射機(jī)制迹恐。
    Class類與java.lang.reflect類庫(kù)一起對(duì)反射的概念進(jìn)行了支持挣惰,該類庫(kù)包含了FieldMethod以及Constructor類(每個(gè)類都實(shí)現(xiàn)了Member接口)殴边。這些類型的對(duì)象是由JVM在運(yùn)行時(shí)創(chuàng)建的,用以表示未知類里對(duì)應(yīng)的成員珍语。這樣你就可以使用Constructor創(chuàng)建新的對(duì)象锤岸,用get()/set()方法讀取和修改與Field對(duì)象關(guān)聯(lián)的字段,用invoke()方法調(diào)用與Method對(duì)象關(guān)聯(lián)的方法板乙。另外是偷,還可以調(diào)用getFields()拳氢、getMethods()和getConstructors()等很便利的方法,以返回表示字段蛋铆、方法以及構(gòu)造器的對(duì)象的數(shù)組馋评。這樣,匿名對(duì)象的類信息就能在運(yùn)行時(shí)被完全確定下來刺啦,而在編譯時(shí)不需要知道任何事情留特。


反射與RTTI的區(qū)別

當(dāng)通過反射與一個(gè)未知類型的對(duì)象打交道時(shí),JVM只是簡(jiǎn)單地檢查這個(gè)對(duì)象玛瘸,看它屬于哪個(gè)特定的類(就像RTTI那樣),在用它做其他事情之前必須先加載那個(gè)類的Class對(duì)象,因此蜡豹,那個(gè)類的.class文件對(duì)于JVM來說必須是可獲取的:要么在本地機(jī)器上销凑,要么可以通過網(wǎng)絡(luò)取得。所以RTTI與反射之間真正的區(qū)別只在于:對(duì)RTTI來說渺绒,編譯器在編譯時(shí)打開和檢查.class文件(也就是可以用普通方法調(diào)用對(duì)象的所有方法)贺喝;而對(duì)于反射機(jī)制來說,.class文件在編譯時(shí)是不可獲取的宗兼,所以是在運(yùn)行時(shí)打開和檢查.class文件搜变。

下面的例子是用反射機(jī)制打印出一個(gè)類的所有方法(包括在基類中定義的方法):

```java
package typeinfo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

// Using reflection to show all the methods of a class.
// even if the methods are defined in the base class.
public class ShowMethods {
    private static String usage = 
        "usage: \n" + 
        "ShowMethods qualified.class.name\n" +
        "To show all methods in class or: \n" +
        "ShowMethods qualified.class.name word\n" +
        "To search for methods involving 'word'";
    
    private static Pattern p = Pattern.compile("\\w+\\.");

    public static void main(String[] args) {
        if(args.length < 1) {
            System.out.println(usage);
            System.exit(0);
        }
        int lines = 0;
        try {
            Class<?> c = Class.forName(args[0]);
            Method[] methods = c.getMethods();
            Constructor[] ctors = c.getConstructors();
            if(args.length == 1) {
                for(Method method : methods) {
                    System.out.println(p.matcher(method.toString()).replaceAll(""));
                }
                for(Constructor ctor : ctors) {
                    System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                }
                lines = methods.length + ctors.length;
            } else {
                for(Method method : methods) {
                    if(method.toString().indexOf(args[1]) != -1) {
                        System.out.println(p.matcher(method.toString()).replaceAll(""));
                        lines++;
                    }
                }
                for(Constructor ctor : ctors) {
                    if(ctor.toString().indexOf(args[1]) != -1) {
                        System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                        lines++;
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            System.out.println("No such Class: " + e);
        }

    }
}
```

輸出:

public static void main(String[])
public final native void wait(long) throws InterruptedException
public final void wait() throws InterruptedException
public final void wait(long,int) throws InterruptedException
public boolean equals(Object)
public String toString()
public native int hashCode()
public final native Class getClass()
public final native void notify()
public final native void notifyAll()
public ShowMethods()

4. 代理模式與Java中的動(dòng)態(tài)代理

  • 代理模式
    在任何時(shí)刻,只要你想要將額外的操作從“實(shí)際”對(duì)象中分離到不同的地方针炉,特別是當(dāng)你希望能夠很容易地做出修改挠他,從沒有使用額外操作轉(zhuǎn)為使用這些操作,或者反過來時(shí)篡帕,代理就顯得很有用(設(shè)計(jì)模式的關(guān)鍵是封裝修改)殖侵。例如,如果你希望跟蹤對(duì)某個(gè)類中方法的調(diào)用镰烧,或者希望度量這些調(diào)用的開銷拢军,那么你應(yīng)該怎樣做呢?這些代碼肯定是你不希望將其合并到應(yīng)用中的代碼怔鳖,因此代理使得你可以很容易地添加或移除它們茉唉。
    interface Interface {
        void doSomething();
        void somethingElse(String arg);
    }
    
    class RealObject implements Interface {
    
        @Override
        public void doSomething() {
            System.out.println("doSomething.");
        }
    
        @Override
        public void somethingElse(String arg) {
            System.out.println("somethingElse " + arg);
        }
    }
    
    class SimpleProxy implements Interface {
        
        private Interface proxy;
        
        public SimpleProxy(Interface proxy) {
            this.proxy = proxy;
        }
    
        @Override
        public void doSomething() {
            System.out.println("SimpleProxy doSomething.");
            proxy.doSomething();
        }
    
        @Override
        public void somethingElse(String arg) {
            System.out.println("SimpleProxy somethingElse " + arg);
            proxy.somethingElse(arg);
        }
    }
    
    public class SimpleProxyDemo {
        
        public static void consumer(Interface iface) {
            iface.doSomething();
            iface.somethingElse("bonobo");
        }
    
        public static void main(String[] args) {
            consumer(new RealObject());
            consumer(new SimpleProxy(new RealObject()));
        }
    
    }
    

輸出:

doSomething.
somethingElse bonobo
SimpleProxy doSomething.
doSomething.
SimpleProxy somethingElse bonobo
somethingElse bonobo

  • 動(dòng)態(tài)代理
    Java的動(dòng)態(tài)代理比代理的思想更向前邁進(jìn)了一步,因?yàn)樗梢?strong>動(dòng)態(tài)地創(chuàng)建代理并動(dòng)態(tài)地處理對(duì)所代理方法的調(diào)用结执。
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    class DynamicProxyHandler implements InvocationHandler {
        
        private Object proxy;
        
        public DynamicProxyHandler(Object proxy) {
            this.proxy = proxy;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("*** proxy: " + proxy.getClass() +
                    ". method: " + method + ". args: " + args);
            if(args != null) {
                for(Object arg : args)
                    System.out.println(" " + arg);
            }
            return method.invoke(this.proxy, args);
        }
    }
    
    public class SimpleDynamicProxy {
        
        public static void consumer(Interface iface) {
            iface.doSomething();
            iface.somethingElse("bonobo");
        }
    
        public static void main(String[] args) {
            RealObject real = new RealObject();
            consumer(real);
            // insert a proxy and call again:
            Interface proxy = (Interface)Proxy.newProxyInstance(
                    Interface.class.getClassLoader(), 
                    new Class[]{ Interface.class },
                    new DynamicProxyHandler(real));
            
            consumer(proxy);
        }
    
    }
    

輸出:

doSomething.
somethingElse bonobo
*** proxy: class typeinfo.$Proxy0. method: public abstract void typeinfo.Interface.doSomething(). args: null
doSomething.
*** proxy: class typeinfo.$Proxy0. method: public abstract void typeinfo.Interface.somethingElse(java.lang.String). args: [Ljava.lang.Object;@6a8814e9
bonobo
somethingElse bonobo

5. 即時(shí)編譯器技術(shù) — JIT

Java虛擬機(jī)中有許多附加技術(shù)用以提升速度度陆,尤其是與加載器操作相關(guān)的,被稱為“即時(shí)”(Just-In-Time献幔,JIT)編譯器的技術(shù)懂傀。這種技術(shù)可以把程序全部或部分翻譯成本地機(jī)器碼(這本來是JVM的工作),程序運(yùn)行速度因此得以提升蜡感。當(dāng)需要裝載某個(gè)類時(shí)蹬蚁,編譯器會(huì)先找到其.class文件恃泪,然后將該類的字節(jié)碼裝入內(nèi)存。此時(shí)犀斋,有兩種方案可供選擇:
(1)一種就是讓即時(shí)編譯器編譯所有代碼贝乎。但這種做法有兩個(gè)缺陷:這種加載動(dòng)作散落在整個(gè)程序生命周期內(nèi),累加起來要花更多時(shí)間叽粹;并且會(huì)增加可執(zhí)行代碼的長(zhǎng)度(字節(jié)碼要比即時(shí)編譯器展開后的本地機(jī)器碼小很多)览效,這將導(dǎo)致頁面調(diào)度,從而降低程序速度球榆。
(2)另一種做法稱為惰性評(píng)估(lazy evaluation)朽肥,意思是即時(shí)編譯器只在必要的時(shí)候才編譯代碼,這樣持钉,從不會(huì)被執(zhí)行的代碼也許就壓根不會(huì)被JIT所編譯衡招。新版JDK中的Java HotSpot技術(shù)就采用了類似方法,代碼每次被執(zhí)行的時(shí)候都會(huì)做一些優(yōu)化每强,所以執(zhí)行的次數(shù)越多始腾,它的速度就越快。

6. 訪問控制權(quán)限

  • Java訪問權(quán)限修飾詞:public空执、protected浪箭、包訪問權(quán)限(默認(rèn)訪問權(quán)限,有時(shí)也稱friendly)和private辨绊。
  • 包訪問權(quán)限:當(dāng)前包中的所有其他類對(duì)那個(gè)成員具有訪問權(quán)限奶栖,但對(duì)于這個(gè)包之外的所有類,這個(gè)成員卻是private门坷。
  • protected:繼承訪問權(quán)限宣鄙。有時(shí)基類的創(chuàng)建者會(huì)希望有某個(gè)特定成員,把對(duì)它的訪問權(quán)限賦予派生類而不是所有類默蚌。這就需要protected來完成這一工作冻晤。protected也提供包訪問權(quán)限,也就是說绸吸,相同包內(nèi)的其他類都可以訪問protected元素鼻弧。protected指明“就類用戶而言,這是private的锦茁,但對(duì)于任何繼承于此類的導(dǎo)出類或其他任何位于同一個(gè)包內(nèi)的類來說攘轩,它卻是可以訪問的”。比如:
    基類:
    package access.cookie;
    public class Cookie {
        public Cookie() {
            System.out.println("Cookie Constructor");
        }
     
        void bite() {  // 包訪問權(quán)限蜻势,其它包即使是子類也不能訪問它
            System.out.println("bite");
        }
    }
    

子類:
```java
package access.dessert;
import access.cookie.Cookie;

public class ChocolateChip extends Cookie {

    public ChocolateChip() {
        System.out.println("ChocolateChip constructor");
    }
    
    public void chomp() {
        bite();  // error, the method bite() from the type Cookie is not visible
    }
}
```

可以發(fā)現(xiàn)子類并不能訪問基類的包訪問權(quán)限方法撑刺。此時(shí)可以將Cookie中的bite指定為public,但這樣做所有的人就都有了訪問權(quán)限握玛,為了只允許子類訪問够傍,可以將bite指定為protected即可。

7. 組合和繼承之間的選擇

  • 組合和繼承都允許在新的類中放置子對(duì)象挠铲,組合是顯式的這樣做冕屯,而繼承則是隱式的做。
  • 組合技術(shù)通常用于想在新類中使用現(xiàn)有類的功能而非它的接口這種情形拂苹。即在新類中嵌入某個(gè)對(duì)象安聘,讓其實(shí)現(xiàn)所需要的功能,但新類的用戶看到的只是為新類所定義的接口瓢棒,而非所嵌入對(duì)象的接口浴韭。為取得此效果,需要在新類中嵌入一個(gè)現(xiàn)有類的private對(duì)象脯宿。但有時(shí)念颈,允許類的用戶直接訪問新類中的組合成分是極具意義的,即將成員對(duì)象聲明為public连霉。如果成員對(duì)象自身都隱藏了具體實(shí)現(xiàn)榴芳,那么這種做法是安全的。當(dāng)用戶能夠了解到你正在組裝一組部件時(shí)跺撼,會(huì)使得端口更加易于理解窟感。比如Car對(duì)象可由public的Engine對(duì)象、Wheel對(duì)象歉井、Window對(duì)象和Door對(duì)象組合柿祈。但務(wù)必要記得這僅僅是一個(gè)特例,一般情況下應(yīng)該使域成為private哩至。
  • 在繼承的時(shí)候躏嚎,使用某個(gè)現(xiàn)有類,并開發(fā)一個(gè)它的特殊版本憨募。通常紧索,這意味著你在使用一個(gè)通用類,并為了某種特殊需要而將其特殊化菜谣。稍微思考一下就會(huì)發(fā)現(xiàn)珠漂,用一個(gè)“交通工具”對(duì)象來構(gòu)成一部“車子”是毫無意義的,因?yàn)椤败囎印辈⒉话敖煌üぞ摺蔽膊玻鼉H是一種交通工具(is-a關(guān)系)媳危。
  • “is-a”(是一個(gè))的關(guān)系是用繼承來表達(dá)的,而“has-a”(有一個(gè))的關(guān)系則是用組合來表達(dá)的冈敛。
  • 到底是該用組合還是繼承待笑,一個(gè)最清晰的判斷方法就是問一問自己是否需要從新類向基類進(jìn)行向上轉(zhuǎn)型,需要的話就用繼承抓谴,不需要的話就用組合方式暮蹂。

8. final關(guān)鍵字

  • 對(duì)final關(guān)鍵字的誤解
    當(dāng)final修飾的是基本數(shù)據(jù)類型時(shí)寞缝,它指的是數(shù)值恒定不變(就是編譯期常量,如果是static final修飾仰泻,則強(qiáng)調(diào)只有一份)荆陆,而對(duì)對(duì)象引用而不是基本類型運(yùn)用final時(shí),其含義會(huì)有一點(diǎn)令人迷惑集侯,因?yàn)橛糜趯?duì)象引用時(shí)被啼,final使引用恒定不變,一旦引用被初始化指向一個(gè)對(duì)象棠枉,就無法再把它指向另一個(gè)對(duì)象浓体。然而,對(duì)象其自身卻是可以被修改的辈讶,Java并未提供使任何對(duì)象恒定不變的途徑(但可以自己編寫類以取得使對(duì)象恒定不變的效果)命浴,這一限制同樣適用數(shù)組,它也是對(duì)象荞估。
  • 使用final方法真的可以提高程序效率嗎咳促?
    將一個(gè)方法設(shè)成final后,編譯器就可以把對(duì)那個(gè)方法的所有調(diào)用都置入“嵌入”調(diào)用里勘伺。只要編譯器發(fā)現(xiàn)一個(gè)final方法調(diào)用跪腹,就會(huì)(根據(jù)它自己的判斷)忽略為執(zhí)行方法調(diào)用機(jī)制而采取的常規(guī)代碼插入方法(將自變量壓入堆棧;跳至方法代碼并執(zhí)行它飞醉;跳回來冲茸;清除堆棧自變量;最后對(duì)返回值進(jìn)行處理)缅帘。相反轴术,它會(huì)用方法主體內(nèi)實(shí)際代碼的一個(gè)副本來替換方法調(diào)用。這樣做可避免方法調(diào)用時(shí)的系統(tǒng)開銷钦无。當(dāng)然逗栽,若方法體積太大,那么程序也會(huì)變得雍腫失暂,可能受到到不到嵌入代碼所帶來的任何性能提升彼宠。因?yàn)槿魏翁嵘急换ㄔ诜椒▋?nèi)部的時(shí)間抵消了。

在最近的Java版本中弟塞,虛擬機(jī)(特別是hotspot技術(shù))能自動(dòng)偵測(cè)這些情況凭峡,并頗為“明智”地決定是否嵌入一個(gè)final 方法。然而决记,最好還是不要完全相信編譯器能正確地作出所有判斷摧冀。通常,只有在方法的代碼量非常少,或者想明確禁止方法被覆蓋的時(shí)候索昂,才應(yīng)考慮將一個(gè)方法設(shè)為final建车。

類內(nèi)所有private 方法都自動(dòng)成為final。由于我們不能訪問一個(gè)private 方法楼镐,所以它絕對(duì)不會(huì)被其他方法覆蓋(若強(qiáng)行這樣做癞志,編譯器會(huì)給出錯(cuò)誤提示)往枷】虿可為一個(gè)private方法添加final指示符,但卻不能為那個(gè)方法提供任何額外的含義错洁。

9. 策略設(shè)計(jì)模式與適配器模式的區(qū)別

  • 策略設(shè)計(jì)模式
    創(chuàng)建一個(gè)能夠根據(jù)所傳遞的參數(shù)對(duì)象的不同而具有不同行為的方法秉宿,被稱為策略設(shè)計(jì)模式,這類方法包含所要執(zhí)行的算法中固定不變的部分屯碴,而“策略”包含變化的部分描睦。策略就是傳遞進(jìn)去的參數(shù)對(duì)象,它包含要執(zhí)行的代碼导而。
  • 適配器模式
    在你無法修改你想要使用的類時(shí)忱叭,可以使用適配器模式,適配器中的代碼將接受你所擁有的接口今艺,并產(chǎn)生你所需要的接口韵丑。

10. 內(nèi)部類

  • 內(nèi)部類與組合是完全不同的概念,這一點(diǎn)很重要虚缎。

  • 為什么需要內(nèi)部類撵彻? — 主要是解決了多繼承的問題,繼承具體或抽象類

    • 一般來說实牡,內(nèi)部類繼承自某個(gè)類或?qū)崿F(xiàn)某個(gè)接口陌僵,內(nèi)部類的代碼操作創(chuàng)建它的外圍類的對(duì)象。所以可以認(rèn)為內(nèi)部類提供了某種進(jìn)入其外圍類的窗口创坞。
    • 內(nèi)部類最吸引人的原因是:每個(gè)內(nèi)部類都能獨(dú)立地繼承自一個(gè)(接口的)實(shí)現(xiàn)碗短,所以無論外圍類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對(duì)于內(nèi)部類都沒有影響题涨。
    • 如果沒有內(nèi)部類提供的偎谁、可以繼承多個(gè)具體的或抽象的類的能力,一些設(shè)計(jì)與編程問題就很難解決携栋。從這個(gè)角度看搭盾,內(nèi)部類使得多重繼承的解決方案變得完整。接口解決了部分問題婉支,而內(nèi)部類有效的實(shí)現(xiàn)了“多重繼承”鸯隅。也就是說,內(nèi)部類允許繼承多個(gè)非接口類型。

    考慮這樣一種情形:如果必須在一個(gè)類中以某種方式實(shí)現(xiàn)兩個(gè)接口蝌以。由于接口的靈活性炕舵,你有兩種選擇:使用單一類或者使用內(nèi)部類。但如果擁有的是抽象的類或具體的類跟畅,而不是接口咽筋,那就只能使用內(nèi)部類才能實(shí)現(xiàn)多重繼承。

使用內(nèi)部類徊件,還可以獲得其他一些特性:
- 內(nèi)部類可以有多個(gè)實(shí)例奸攻,每個(gè)實(shí)例都有自己的狀態(tài)信息,并且與其外圍類對(duì)象的信息相互獨(dú)立虱痕。
- 在單個(gè)外圍類中睹耐,可以讓多個(gè)內(nèi)部類以不同的方式實(shí)現(xiàn)同一個(gè)接口或繼承同一個(gè)類。
- 創(chuàng)建內(nèi)部類對(duì)象的時(shí)刻并不依賴于外圍類對(duì)象的創(chuàng)建部翘。
- 內(nèi)部類并沒有令人迷惑的is-a關(guān)系硝训,它就是一個(gè)獨(dú)立的實(shí)體。

11. String類型 — 不可變

  • 用于String的“+”與“+=”是Java中僅有的兩個(gè)重載過的操作符新思,而Java并不允許程序員重載任何操作符窖梁。
  • 考慮到效率因素,編譯器會(huì)對(duì)String的多次+操作進(jìn)行優(yōu)化夹囚,優(yōu)化使用StringBuilder操作(javap -c class字節(jié)碼文件名 命令查看具體優(yōu)化過程)纵刘。這讓你覺得可以隨意使用String對(duì)象,反正編譯器會(huì)為你自動(dòng)地優(yōu)化性能崔兴。但編譯器能優(yōu)化到什么程度還不好說彰导,不一定能優(yōu)化到使用StringBuilder代替String相同的效果。比如:
    public class WitherStringBuilder {
        public String implicit(String[] fields) {
            String result = "";
            for(int i = 0; i < fields.length; i++)
                result += fields[i];
            return result;
        }
        
        public String explicit(String[] fields) {
            StringBuilder result = new StringBuilder();
            for(int i = 0; i < fields.length; i++)
                result.append(fields[i]);
            return result.toString();
        }
    }
    

運(yùn)行javap -c WitherStringBuilder敲茄,可以看到兩個(gè)方法對(duì)應(yīng)的字節(jié)碼位谋。
implicit方法:

public java.lang.String implicit(java.lang.String[]);
Code:
0: ldc #16 // String
2: astore_2
3: iconst_0
4: istore_3
5: goto 32
8: new #18 // class java/lang/StringBuilder

11: dup
12: aload_2
13: invokestatic #20 // Method java/lang/String.valueOf:(
Ljava/lang/Object;)Ljava/lang/String;
16: invokespecial #26 // Method java/lang/StringBuilder."<

init>":(Ljava/lang/String;)V
19: aload_1
20: iload_3
21: aaload
22: invokevirtual #29 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: invokevirtual #33 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
28: astore_2
29: iinc 3, 1
32: iload_3
33: aload_1
34: arraylength
35: if_icmplt 8
38: aload_2
39: areturn
public java.lang.String implicit(java.lang.String[]);
Code:
0: ldc #16 // String
2: astore_2
3: iconst_0
4: istore_3
5: goto 32
8: new #18 // class java/lang/StringBuilder
11: dup
12: aload_2
13: invokestatic #20 // Method java/lang/String.valueOf:(
Ljava/lang/Object;)Ljava/lang/String;
16: invokespecial #26 // Method java/lang/StringBuilder."<
init>":(Ljava/lang/String;)V
19: aload_1
20: iload_3
21: aaload
22: invokevirtual #29 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: invokevirtual #33 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
28: astore_2
29: iinc 3, 1
32: iload_3
33: aload_1
34: arraylength
35: if_icmplt 8
38: aload_2
39: areturn

可以發(fā)現(xiàn),StringBuilder是在循環(huán)之內(nèi)構(gòu)造的堰燎,這意味著每經(jīng)過循環(huán)一次掏父,就會(huì)創(chuàng)建一個(gè)新的StringBuilder對(duì)象。

explicit方法:

public java.lang.String explicit(java.lang.String[]);
Code:
0: new #18 // class java/lang/StringBuilder
3: dup
4: invokespecial #45 // Method java/lang/StringBuilder."<
init>":()V
7: astore_2
8: iconst_0
9: istore_3
10: goto 24
13: aload_2
14: aload_1
15: iload_3
16: aaload
17: invokevirtual #29 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: pop
21: iinc 3, 1
24: iload_3
25: aload_1
26: arraylength
27: if_icmplt 13
30: aload_2
31: invokevirtual #33 // Method java/lang/StringBuilder.to

String:()Ljava/lang/String;
34: areturn
}

可以看到秆剪,不僅循環(huán)部分的代碼更簡(jiǎn)短赊淑、更簡(jiǎn)單,而且它只生成了一個(gè)StringBuilder對(duì)象仅讽。顯式的創(chuàng)建StringBuilder還允許你預(yù)先為其指定大小陶缺。如果你已經(jīng)知道最終的字符串大概有多長(zhǎng),那預(yù)先指定StringBuilder的大小可以避免多次重新分配緩沖洁灵。


總結(jié)

因此饱岸,當(dāng)你為一個(gè)類重寫toString()方法時(shí)掺出,如果字符串操作比較簡(jiǎn)單,那就可以信賴編譯器苫费,它會(huì)為你合理地構(gòu)造最終的字符串結(jié)果汤锨。但是,如果你要在toString()方法中使用循環(huán)百框,那么最好自己創(chuàng)建一個(gè)StringBuilder對(duì)象闲礼,用它來構(gòu)造最終的結(jié)果。


  • System.out.printf()System.out.format()方法模仿自C的printf铐维,可以格式化字符串柬泽,兩者是完全等價(jià)的。

Java中方椎,所有新的格式化功能都由java.util.Formatter類處理聂抢。
String.format()方法參考了C中的sprintf()方法,以生成格式化的String對(duì)象棠众,是一個(gè)static方法,它接受與Formatter.format()方法一樣的參數(shù)有决,但返回一個(gè)String對(duì)象闸拿。當(dāng)你只需使用format()方法一次的時(shí)候,該方法很方便书幕。
```java
import java.util.Arrays;
import java.util.Formatter;

public class SimpleFormat {

    public static void main(String[] args) {
        int x = 5;
        double y = 5.324667;
        System.out.printf("Row 1: [%d %f]\n", x, y);
        System.out.format("Row 1: [%d %f]\n", x, y);
        
        Formatter f = new Formatter(System.out);
        f.format("Row 1: [%d %f]\n", x, y);
        
        String str = String.format("Row 1: [%d %f]\n", x, y);
        System.out.println(str);
        
        Integer[][] a = {
            {1, 2, 3}, {4, 5, 6},
            {7, 8, 3}, {9, 10, 6}
        };
        System.out.println(Arrays.deepToString(a));
    }
}
```

12. 序列化控制

  • 當(dāng)我們對(duì)序列化進(jìn)行控制時(shí)新荤,可能某個(gè)特定子對(duì)象不想讓Java序列化機(jī)制自動(dòng)保存與恢復(fù)。如果子對(duì)象表示的是我們不希望將其序列化的敏感信息(如密碼)台汇,通常會(huì)面臨這種情況苛骨。即使對(duì)象中的這些信息是private屬性,一經(jīng)序列化處理苟呐,人們就可以通過讀取文件或者攔截網(wǎng)絡(luò)傳輸?shù)姆绞絹碓L問到它痒芝。有兩種辦法可以防止對(duì)象的敏感部分被序列化:

    • 實(shí)現(xiàn)Externalizable代替實(shí)現(xiàn)Serializable接口來對(duì)序列化過程進(jìn)行控制,Externalizable繼承了Serializable接口牵素,同時(shí)增添了兩個(gè)方法:writeExternal()readExternal()严衬。

    兩者在反序列化時(shí)的區(qū)別

    • 對(duì)Serializable對(duì)象反序列化時(shí),由于Serializable對(duì)象完全以它存儲(chǔ)的二進(jìn)制位為基礎(chǔ)來構(gòu)造笆呆,因此并不會(huì)調(diào)用任何構(gòu)造函數(shù)请琳,因此Serializable類無需默認(rèn)構(gòu)造函數(shù),但是當(dāng)Serializable類的父類沒有實(shí)現(xiàn)Serializable接口時(shí)赠幕,反序列化過程會(huì)調(diào)用父類的默認(rèn)構(gòu)造函數(shù)俄精,因此該父類必需有默認(rèn)構(gòu)造函數(shù),否則會(huì)拋異常榕堰。
    • 對(duì)Externalizable對(duì)象反序列化時(shí)竖慧,會(huì)先調(diào)用類的不帶參數(shù)的構(gòu)造方法,這是有別于默認(rèn)反序列方式的。如果把類的不帶參數(shù)的構(gòu)造方法刪除测蘑,或者把該構(gòu)造方法的訪問權(quán)限設(shè)置為private灌危、默認(rèn)或protected級(jí)別,會(huì)拋出java.io.InvalidException: no valid constructor異常碳胳,因此Externalizable對(duì)象必須有默認(rèn)構(gòu)造函數(shù)勇蝙,而且必需是public的。
  • Externalizable的替代方法:如果不是特別堅(jiān)持實(shí)現(xiàn)Externalizable接口挨约,那么還有另一種方法味混。我們可以實(shí)現(xiàn)Serializable接口,并添加writeObject()readObject()的方法诫惭。一旦對(duì)象被序列化或者重新裝配翁锡,就會(huì)分別調(diào)用那兩個(gè)方法。也就是說夕土,只要提供了這兩個(gè)方法馆衔,就會(huì)優(yōu)先使用它們,而不考慮默認(rèn)的序列化機(jī)制怨绣。

    這些方法必須含有下列準(zhǔn)確的簽名:

    private void writeObject(ObjectOutputStream stream) 
            throws IOException;
    private void readObject(ObjectInputStream stream)
            throws IOException, ClassNotFoundException
    
  • 可以用transient關(guān)鍵字逐個(gè)字段地關(guān)閉序列化角溃,它的意思是“不用麻煩你保存或恢復(fù)數(shù)據(jù)—我自己會(huì)處理的”。由于Externalizable對(duì)象在默認(rèn)情況下不保存它們的任何字段篮撑,所以transient關(guān)鍵字只能和Serializable對(duì)象一起使用减细。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赢笨,隨后出現(xiàn)的幾起案子未蝌,更是在濱河造成了極大的恐慌,老刑警劉巖茧妒,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萧吠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嘶伟,警方通過查閱死者的電腦和手機(jī)怎憋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來九昧,“玉大人绊袋,你說我怎么就攤上這事≈ィ” “怎么了癌别?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蹋笼。 經(jīng)常有香客問我展姐,道長(zhǎng)躁垛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任圾笨,我火速辦了婚禮教馆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘擂达。我一直安慰自己土铺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布板鬓。 她就那樣靜靜地躺著悲敷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俭令。 梳的紋絲不亂的頭發(fā)上后德,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音抄腔,去河邊找鬼瓢湃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妓柜,可吹牛的內(nèi)容都是我干的箱季。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼棍掐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了拷况?” 一聲冷哼從身側(cè)響起作煌,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赚瘦,沒想到半個(gè)月后粟誓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡起意,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年鹰服,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揽咕。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悲酷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亲善,到底是詐尸還是另有隱情设易,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布蛹头,位于F島的核電站顿肺,受9級(jí)特大地震影響戏溺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屠尊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一旷祸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讼昆,春花似錦托享、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掺炭,卻和暖如春辫诅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涧狮。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工炕矮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人者冤。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓肤视,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涉枫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子邢滑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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

  • 多態(tài) 任何域的訪問操作都將有編譯器解析,如果某個(gè)方法是靜態(tài)的愿汰,它的行為就不具有多態(tài)性 java默認(rèn)對(duì)象的銷毀順序與...
    yueyue_projects閱讀 944評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法困后,類相關(guān)的語法,內(nèi)部類的語法衬廷,繼承相關(guān)的語法摇予,異常的語法,線程的語...
    子非魚_t_閱讀 31,644評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理吗跋,服務(wù)發(fā)現(xiàn)侧戴,斷路器,智...
    卡卡羅2017閱讀 134,672評(píng)論 18 139
  • 我開始注意母親“異常”行為是在一個(gè)非常普通的晚上秩冈。那時(shí)候我癱在床上本缠,手里拿著手機(jī),耳朵里插著耳機(jī)入问。明明已經(jīng)不小...
    淼川姑娘閱讀 331評(píng)論 0 0
  • 一只無聊的龜閱讀 233評(píng)論 2 3