提高開發(fā)效率的奇技(一)lombok

閑話

“程序猿”、“碼農(nóng)”、“軟件攻城獅”拳亿,程序員這個職業(yè)現(xiàn)在已經(jīng)被這些網(wǎng)絡(luò)流行語給玩壞了。由于程序員門檻越來越低风瘦,由于“copy+改”的開發(fā)模式在業(yè)界里的流行公般,“程序員”這個曾經(jīng)神圣的詞語也似乎越來越廉價了。在人們的印象中官帘,程序員似乎就是整天在噼哩啪啦敲鍵盤的職業(yè)昧谊。我想說,噼哩啪啦敲鍵盤的那群人呢诬,其實(shí)叫打字員。程序員起碼應(yīng)該是一個動腦子的打字員尚镰。一個典型的程序員(或者叫合格的程序員)狗唉,應(yīng)該至少有80%的時間在思考和學(xué)習(xí)肾筐,最多有20%的時間用于碼字杏节。也就是說,一個不加班的程序員(假設(shè)存在)蹋半,一天應(yīng)該最多有1.6個小時在敲代碼。1.6小時能寫多少代碼份企?對于Java程序員降宅,有效的代碼量應(yīng)該差不多是100行腰根。Java恰恰是一個無效代碼很多的語言瘸恼,Java哆嗦的表達(dá)方式被無數(shù)業(yè)內(nèi)人數(shù)所詬病。因此冰啃,如何降低無效代碼量阎毅,提高開發(fā)效率抢肛,是值得每個Java程序員應(yīng)該思考的問題福稳。

開這一個系列旨在幫助大家提高開發(fā)效率半火,主要是介紹一些第三方包酌住、插件等的使用娱节。在開始之前,這里有幾個優(yōu)先級更高的通用性建議:

一是選擇一款合適的IDE(Integrate Development Environment质涛,集成開發(fā)環(huán)境)掰担。Java本身的繁瑣使得它不適合使用記事本類輕型開發(fā)工具進(jìn)行開發(fā)带饱,因此推薦使用相對重量級的IDE進(jìn)行開發(fā)勺疼。合適的開發(fā)工具可以幫助我們完成很多重復(fù)性工作,如生成常用的代碼执庐、自動導(dǎo)包轨淌、自動整理代碼格式递鹉,大幅節(jié)省碼字時間。目前市面上流行的Java IDE主要有三款:eclipse(包括在此基礎(chǔ)上衍生的MyEclipse北专、STS等)旬陡、intellij idea驶睦、netbeans匿醒。這三款產(chǎn)品各有所長,但綜合比較下來廉羔,強(qiáng)烈推薦使用intellij idea,它的智能提示效果和對各種文件類型的支持是其他兩款I(lǐng)DE所難以啟及的憋他。順便一提,它是捷克的一家叫做JetBrains的公司出品的IDE镀娶,這家公司出品的其他語言的IDE也很優(yōu)秀,如用于寫python的PyCharm揪罕、寫php的PhpStorm梯码、寫前端的WebStorm等。當(dāng)然好啰,idea的專業(yè)版是收費(fèi)的轩娶,大家都懂的,有條件的同學(xué)請支持正版坎怪。

二是建立一個自己的常用代碼庫罢坝,將一些新項(xiàng)目中常使用的代碼保存進(jìn)去,隨取隨用搅窿,可以使用github的gist功能幫助自己維護(hù)這個代碼庫嘁酿,idea有直接向github上提交gist的快捷功能,大家可以自行研究一下男应,后續(xù)文章中會有詳細(xì)介紹闹司。

三是使用新版本的JDK。在當(dāng)前語境下沐飘,建議使用JDK8以上的版本牲迫。Java8的lambda表達(dá)式和Stream功能可以大幅提高開發(fā)效率借卧。每個版本的JDK設(shè)計時铐刘,都會考慮簡化開發(fā)、提高效率檩禾,比如java7的diamond語法(Map<String, Object> map = new HashMap<>()盼产,等號后面的尖括號內(nèi)不用再指定類型)戏售,再比如后續(xù)java10中的var使得java向動態(tài)類型語言的方向發(fā)展谓传。因此续挟,掌握和使用新版本的JDK幾乎總能提升你的開發(fā)效率诗祸。

四是學(xué)習(xí)一門腳本語言直颅,比如python功偿。一方面平時常用的一些小的功能可以用腳本語言快速開發(fā)出來械荷,另一方面可以使用腳本語言結(jié)合模板開發(fā)一些代碼生成器虑灰。有人說過穆咐,超過90秒的重復(fù)性工作就應(yīng)該寫腳本來完成,試想你的代碼能代替你工作崖叫,你是不就可以坐享其成了心傀?

這個系列的第一部分講lombok剧包,它是一個旨在減少重復(fù)性代碼的第三方包,它的設(shè)計思路是通過一系列注解來自動生成代碼一铅。

引入方式

在maven的pom.xml文件中添加lombok的坐標(biāo)潘飘,如:

    <dependency> 
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
    </dependency>

注意在idea中使用lombok注解需要安裝lombok插件卜录,如下圖:


idea_lombok_plugin.png

API干貨

Data艰毒、Value

這兩個注解是“一站式”的注解丑瞧,設(shè)計的目的是想要取代一個實(shí)體類中除成員變量聲明以外的其他所有代碼绊汹。這兩個注解都用在類的上面西乖,二者的區(qū)別在于@Data注解是按照可變類的方式生成代碼获雕,@Value注解是按照不可變類的方式生成代碼轿偎。按照文檔上的說法坏晦,@Data注解相當(dāng)于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode注解的“五合一”合集嫁乘,它會根據(jù)成員變量自動生成相應(yīng)的get方法蜓斧、set方法挎春、構(gòu)造器直奋、toString方法以及equals和hashCode方法脚线。注意final修飾的成員變量不會生成相應(yīng)的set方法邮绿,也不會參與構(gòu)造器的生成船逮,transient修飾的成員變量則不會參與equals和hashCode方法的生成挖胃。@Data注解有一個可選項(xiàng)staticConstructor冠骄,可以通過將該選項(xiàng)的值設(shè)置為of來生成一個靜態(tài)的構(gòu)造器。@Value注解相當(dāng)于@Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @AllArgsConstructor @ToString @EqualsAndHashCode的“五合一”合集职烧,它會按照不可變類的方式去生成代碼蚀之。它會將所有成員變量聲明為private final變量足删,生成包含所有變量的構(gòu)造器以及getter失受、toString、equals和hashCode方法痪署。下面看一下實(shí)際效果兄旬,假設(shè)有一個Student類领铐,包含id绪撵、name、age汹来、grade字段:

@Data
public class Student{
    private String id;
    private final String name;
    private transient int age;
    private int grade;
}

等價于

import java.beans.ConstructorProperties;

public class Student {
    private String id;
    private final String name;
    private transient int age;
    private int grade;

    @ConstructorProperties({"name"})
    public Student(String name) {
        this.name = name;
    }

    public String getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    public int getGrade() {
        return this.grade;
    }

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

    public void setAge(int age) {
        this.age = age;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Student)) {
            return false;
        } else {
            Student other = (Student)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$id = this.getId();
                    Object other$id = other.getId();
                    if (this$id == null) {
                        if (other$id == null) {
                            break label39;
                        }
                    } else if (this$id.equals(other$id)) {
                        break label39;
                    }

                    return false;
                }

                Object this$name = this.getName();
                Object other$name = other.getName();
                if (this$name == null) {
                    if (other$name != null) {
                        return false;
                    }
                } else if (!this$name.equals(other$name)) {
                    return false;
                }

                if (this.getGrade() != other.getGrade()) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof Student;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        result = result * 59 + this.getGrade();
        return result;
    }

    public String toString() {
        return "Student(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ", grade=" + this.getGrade() + ")";
    }
}
@Value
public class Student {
    private String id;
    private String name;
    private transient int age;
    private int grade;
}

則等價于

import java.beans.ConstructorProperties;

public final class Student {
    private final String id;
    private final String name;
    private final transient int age;
    private final int grade;

    @ConstructorProperties({"id", "name", "age", "grade"})
    public Student(String id, String name, int age, int grade) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    public String getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    public int getGrade() {
        return this.grade;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Student)) {
            return false;
        } else {
            Student other = (Student)o;
            Object this$id = this.getId();
            Object other$id = other.getId();
            if (this$id == null) {
                if (other$id != null) {
                    return false;
                }
            } else if (!this$id.equals(other$id)) {
                return false;
            }

            label29: {
                Object this$name = this.getName();
                Object other$name = other.getName();
                if (this$name == null) {
                    if (other$name == null) {
                        break label29;
                    }
                } else if (this$name.equals(other$name)) {
                    break label29;
                }

                return false;
            }

            if (this.getGrade() != other.getGrade()) {
                return false;
            } else {
                return true;
            }
        }
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        result = result * 59 + this.getGrade();
        return result;
    }

    public String toString() {
        return "Student(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ", grade=" + this.getGrade() + ")";
    }
}

注意使用@Value注解時摔桦,成員變量不能直接聲明為final類型邻耕,否則會有編譯錯誤兄世。

Builder

@Builder用于實(shí)現(xiàn)23種設(shè)計模式中的構(gòu)建器模式,該模式通常用于構(gòu)造包含多個成員變量的類党远。如果一個類擁有多個成員變量沟娱,創(chuàng)造包含全部成員變量的構(gòu)造器不夠靈活济似,都使用setter方法賦值又過于麻煩,這時構(gòu)建器模式就發(fā)揮作用了蛾找。構(gòu)建器模式聲明一個公有的靜態(tài)構(gòu)建器打毛,然后聲明所有成員變量的同名方法幻枉,返回構(gòu)建器熬甫,這樣就可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,最后通過build()方法調(diào)用私有構(gòu)造器豺谈,完成對象的創(chuàng)建,從而簡化代碼厂榛。下面看一下官方文檔里的例子:

@Builder
class Example {
    private int foo;
    private final String bar;
}

等價于

class Example {
    private int foo;
    private final String bar;

    Example(int foo, String bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public static Example.ExampleBuilder builder() {
        return new Example.ExampleBuilder();
    }

    public static class ExampleBuilder {
        private int foo;
        private String bar;

        ExampleBuilder() {
        }

        public Example.ExampleBuilder foo(int foo) {
            this.foo = foo;
            return this;
        }

        public Example.ExampleBuilder bar(String bar) {
            this.bar = bar;
            return this;
        }

        public Example build() {
            return new Example(this.foo, this.bar);
        }

        public String toString() {
            return "Example.ExampleBuilder(foo=" + this.foo + ", bar=" + this.bar + ")";
        }
    }
}

@Builder注解可以用于類、方法和構(gòu)造器上柜砾,如果@Builder注解作用于方法上局义,生成的build()方法會調(diào)用該方法,不可以有兩個方法同時使用@Builder注解术幔。

Getter四敞、Setter忿危、ToString铺厨、EqualsAndHashCode

@Getter解滓、@Setter洼裤、@ToString和@EqualsAndHashCode注解分別用于給類的成員變量生成get方法腮鞍、set方法移国、給類生成toString()桥狡、equals和hashCode()方法。
其中@Getter和@Setter注解可以作用于類上或者成員變量上嫂易,如果作用于類上怜械,所有的成員變量均自動生成get和set方法(final變量無set方法)缕允。注意如果變量是boolean類型障本,生成的get方法叫做isXXX案训,如boolean good的get方法為isGood()强霎。如果想生成非public的get或set方法,可以將注解的value屬性進(jìn)行設(shè)置椿争,如:@Getter(value=lombok.AccessLevel.PROTECTED)
@ToString注解用于生成類的toString()方法,僅能作用于類上椅邓【澳伲可用的屬性值包括of、exclude透葛、includeFieldNames以及callSuper。of用于指定包含哪些字段萨蚕,exclude用于指定排除哪些字段岳遥,of和exclude只能存在一個。includeFieldNames是一個boolean值妻往,默認(rèn)值為true,用于指定是否包含成員變量名好渠,callSuper也是一個boolean值,默認(rèn)值為false霍掺,如果設(shè)置為true,在生成的toString方法中將包含父類的toString結(jié)果兔魂。
@EqualsAndHashCode方法用于生成類的equals()和hashCode()方法,只能作用于類上智玻,可用的屬性值類似于@ToString,也包括of事甜、exclude以及callSuper。transient修飾的成員變量不參與生成equals()和hashCode()方法邦马。
下面看一些例子:


@ToString(includeFieldNames=false, of={"id", "grade"})
@EqualsAndHashCode(exclude={"name"})
public class Student {
    @Getter(value=lombok.AccessLevel.PROTECTED)
    private String id;
    @Setter
    private String name;
    private transient int age;
    private int grade;
}

等價于

public class Student {
    private String id;
    private String name;
    private transient int age;
    private int grade;

    public Student() {
    }

    public String toString() {
        return "Student(" + this.getId() + ", " + this.grade + ")";
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Student)) {
            return false;
        } else {
            Student other = (Student)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                Object this$id = this.getId();
                Object other$id = other.getId();
                if (this$id == null) {
                    if (other$id == null) {
                        return this.grade == other.grade;
                    }
                } else if (this$id.equals(other$id)) {
                    return this.grade == other.grade;
                }

                return false;
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof Student;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        result = result * 59 + this.grade;
        return result;
    }

    protected String getId() {
        return this.id;
    }

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

NoArgsConstructor父丰、RequiredArgsConstructor、AllArgsConstructor

@NoArgsConstructor镀首、@RequiredArgsConstructor和@AllArgsConstructor都作用于類上,分別用于生成無參構(gòu)造器竖瘾、指定參數(shù)構(gòu)造器和全參構(gòu)造器。@RequiredArgsConstructor指定的參數(shù)是指final修飾的成員變量以及有約束條件(如用@NonNull修飾)的成員變量庸论。

Cleanup

@Cleanup注解用在局部變量上聂示,用于關(guān)閉指定的資源,如輸入輸出流。@Cleanup注解的底層實(shí)現(xiàn)方式是在finally塊中調(diào)用資源的close方法编曼,如果關(guān)閉資源的方法名不為close往扔,可以使用注解的value屬性指定方法名,注意指定的方法必須是沒有參數(shù)的。看下官方文檔中的例子:

public void copyFile(String in, String out) throws IOException {
    @Cleanup FileInputStream inStream = new FileInputStream(in);
    @Cleanup FileOutputStream outStream = new FileOutputStream(out);
    byte[] b = new byte[65536];
    while (true) {
        int r = inStream.read(b);
        if (r == -1) break;
        outStream.write(b, 0, r);
    }
}

等價于

public void copyFile(String in, String out) throws IOException {
   @Cleanup FileInputStream inStream = new FileInputStream(in);
   try {
       @Cleanup FileOutputStream outStream = new FileOutputStream(out);
       try {
           byte[] b = new byte[65536];
           while (true) {
               int r = inStream.read(b);
               if (r == -1) break;
               outStream.write(b, 0, r);
           }
       } finally {
           if (out != null) out.close();
       }
   } finally {
       if (in != null) in.close();
   }

NonNull

@NonNull注解作用于類的成員變量刺桃、方法桃移、參數(shù)以及局部變量上。如果放在參數(shù)上,lombok將在方法/構(gòu)造器方法體內(nèi)最開始的位置插入空值檢測的語句绞惦,如果變量值為null,將拋出空指針異常。如果放在成員變量上淑仆,任何為該變量賦值的方法(如set方法和構(gòu)造器)中將生成空值檢測語句墩弯。在方法和局部變量上加入該注解似乎沒有什么作用桥温。

SneakyThrows

@SneakyThrows注解作用于方法或構(gòu)造器上,用于將受檢異常轉(zhuǎn)換為非受檢異常区端,該方法可能導(dǎo)致程序暗藏殺機(jī),不推薦使用沥邻。

Synchronized

@Synchronized注解作用于方法上迁沫,類似于synchronized關(guān)鍵字,它生成一個私有的變量祷愉,將同步鎖加在私有變量上媒怯,這樣可以避免其他不受你控制的代碼干擾你的線程管理欺殿。借用一個別人整理的代碼:

public class SynchronizedExample {
  private final Object readLock = new Object();
  
  @Synchronized
  public static void hello() {
    System.out.println("world");
  }
  
  @Synchronized
  public int answerToLife() {
    return 42;
  }
  
  @Synchronized("readLock")
  public void foo() {
    System.out.println("bar");
  }
}

等價于

public class SynchronizedExample {
  private static final Object $LOCK = new Object[0];
  private final Object $lock = new Object[0];
  private final Object readLock = new Object();
  
  public static void hello() {
    synchronized($LOCK) {
      System.out.println("world");
    }
  }
  
  public int answerToLife() {
    synchronized($lock) {
      return 42;
    }
  }
  
  public void foo() {
    synchronized(readLock) {
      System.out.println("bar");
    }
  }
}

val

@val應(yīng)該是所有注解里最特殊的一個了棍潘。首先它長得就不太一樣,首字母小寫讓它看上去不那么像注解。再者它的用法也不太一樣,它使用的時候不需要加“@”符號,直接

val s = "code";

即可。這個注解用于局部變量聲明時自動推斷變量的類型晌姚,如上述代碼將s推斷為String類型焕议。它是一項(xiàng)“未來的”java特性,是對java10中var關(guān)鍵字的一種補(bǔ)充。val關(guān)鍵字聲明的局部變量都是不可變的嗤瞎,var關(guān)鍵字修飾的變量則為可變的跛锌。
注意本質(zhì)上val還是一個注解,即

val x = 10;

相當(dāng)于

@val final int x = 10;

設(shè)計思路

有的童鞋可能會問:你上面說的這些玩意,IDE基本都可以自動生成啊,lombok到底有意義么?
下面幾點(diǎn)可以證明簡化的比生成的好:

  • 可讀性。lombok去掉了getter、setter、toString胎围、equals、hashCode等一系列冗長的代碼垮抗,使得實(shí)體類看上去更加清爽冒版。
  • 便于修改辞嗡。雖然IDE可以自動生成getter栋烤、setter和構(gòu)造器薯定,但是對于修改來說簡直是噩夢话侄。比如你要把一個字段從String改成int類型,你需要把對應(yīng)的getter、setter乙嘀、構(gòu)造器末购、toString、equals虎谢、hashCode通通刪掉盟榴,再重新生成。使用lombok的話這些工作都不用做了婴噩,直接修改就可以了擎场。

最佳實(shí)踐

總結(jié)一下羽德,lombok主要用于簡化Java重復(fù)代碼,提高開發(fā)效率:

  • @Data迅办、@Value這兩個合集用于簡化實(shí)體類的重復(fù)代碼宅静,一個可變一個不可變,在一般情況下這倆注解就夠用了站欺。但是如果要訂制getter姨夹、setter等方法,就需要使用各個注解了镊绪。
  • @Builder注解用于實(shí)現(xiàn)構(gòu)造器模式匀伏,方便多成員變量的實(shí)體類的構(gòu)造。
  • @Cleanup注解用于關(guān)閉資源蝴韭,功能類似于try-with-resources特性够颠。
  • @NonNull注解用于對參數(shù)和成員變量進(jìn)行空值檢測。
  • @Synchronized注解用于更優(yōu)雅地實(shí)現(xiàn)同步鎖榄鉴。
  • @val注解用于體驗(yàn)java未來的功能——動態(tài)變量類型履磨。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市庆尘,隨后出現(xiàn)的幾起案子剃诅,更是在濱河造成了極大的恐慌,老刑警劉巖驶忌,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛辕,死亡現(xiàn)場離奇詭異,居然都是意外死亡付魔,警方通過查閱死者的電腦和手機(jī)聊品,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來几苍,“玉大人翻屈,你說我怎么就攤上這事∑薨樱” “怎么了伸眶?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刽宪。 經(jīng)常有香客問我厘贼,道長,這世上最難降的妖魔是什么圣拄? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任涂臣,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赁遗。我一直安慰自己署辉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布岩四。 她就那樣靜靜地躺著哭尝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剖煌。 梳的紋絲不亂的頭發(fā)上材鹦,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音耕姊,去河邊找鬼桶唐。 笑死,一個胖子當(dāng)著我的面吹牛茉兰,可吹牛的內(nèi)容都是我干的尤泽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼规脸,長吁一口氣:“原來是場噩夢啊……” “哼坯约!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起莫鸭,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤闹丐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后被因,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卿拴,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年梨与,在試婚紗的時候發(fā)現(xiàn)自己被綠了堕花。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛋欣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出如贷,到底是詐尸還是另有隱情陷虎,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布杠袱,位于F島的核電站尚猿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏楣富。R本人自食惡果不足惜凿掂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庄萎,春花似錦踪少、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忍捡。三九已至集漾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砸脊,已是汗流浹背具篇。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凌埂,地道東北人驱显。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像侨舆,于是被迫代替她去往敵國和親秒紧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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

  • 在面向?qū)ο缶幊讨斜夭豢缮傩枰诖a中定義對象模型挨下,而在基于Java的業(yè)務(wù)平臺開發(fā)實(shí)踐中尤其如此熔恢。相信大家在平時開發(fā)...
    消失er閱讀 12,726評論 15 150
  • 前言: 逛開源社區(qū)的時候無意發(fā)現(xiàn)的,用了一段時間臭笆,覺得還可以叙淌,特此推薦一下。 lombok 提供了簡單的注解的形式...
    OzanShareing閱讀 4,215評論 0 7
  • 【打卡接龍】:請家人安順序接下去 公司:浙江省東陽市東元食品有限公司 姓名:許亮亮 【日精進(jìn)打卡第18天】 【知~...
    許亮亮_2a75閱讀 82評論 0 0
  • 在勝間和代的書中愁铺,談了個水平思考力鹰霍,還談了一個垂直思考力,這兩個組合起來茵乱,就是立體思考力茂洒。垂直思考力就是多問為什么...
    鴨梨山大哎閱讀 370評論 0 1
  • 猝不及防,我邂逅了你----靜好書院瓶竭。 我親昵地給你起了一個中意的別名:心中良人督勺。 我悄悄地愛著你,你也默默地喜歡...
    涂糊蟲閱讀 1,511評論 1 3