Java核心技术卷I基础知识

Java API查询

1.Java程序设计概述

  • 1)简单性
    Java没有头文件、指针运算、结构、联合、操作符重载、虚基类等。
  • 2)面向对象
    Java没有多重继承,取而代之的是更简单的接口概念。Java提供了更丰富的运行时自省功能。
  • 3)分布式
    Java有一个丰富的例程库,用于处理HTTP和FTP之类的TCP/IP协议。
  • 4)健壮性
    Java采用的指针模型可以消除重写内存和损坏数据的可能性。
  • 5)安全性
    Java适用于网络/分布式环境。Java防范各种攻击:
    运行时堆栈溢出;
    破坏自己的进程空间外的内存;
    未经授权读写文件。
  • 6)体系结构中立
    Java编译器生成与特定的计算机体系结构无关的字节码指令,可以在任何机器上解释执行,也可以动态地翻译成本地机器代码。
  • 7)可移植性
    Java规范中没有“依赖具体实现”的地方。基本数据类型的大小以及有关运算都做了明确的说明。
    除了与用户界面有关的部件外,所有其他Java库都能很好地支持平台独立性。包括文件、正则表达式、XML、日期和时间、数据库、网络连接、线程等,而不用关心底层操作系统。
  • 8)解释型
  • 9)高性能
    字节码可以在运行时刻动态地翻译成对应运行这个应用的特定CPU的机器码。
    即时编译器的优势:可以监控经常执行哪些代码并优化这些代码以提高速度。更为复杂的优化是函数调用(内联)。必要时,还可以撤销优化。
  • 10)多线程
  • 11)动态性
    当需要将某些代码添加到正在运行的程序中时,动态性将是一个非常重要的特性。

  • 用户从Internet下载Java字节码,并在自己的机器上运行。在网页运行的Java程序称为applet。

2.Java程序设计环境

  • java术语



    JRE——包含虚拟机,但不包含编译器。

  • Windows和Linux上安装JDK时,需要将jdk/bin目录加入到执行路径中——执行路径是操作系统查找可执行文件时所遍历的目录列表
  • C:\Program Files\Java\jdk-10\lib\src.zip文件中包含了所有公共类库的源代码。新建一个目录javasrc,将其解压到这个新目录。
    更多的源代码,例如编译器、虚拟机、本地方法以及私有辅助类,访问http://jdk.java.net/10/
  • javadoc的安装
    javadoc的下载路径
  • 使用Windows命名行工具
    javac是一个java编译器,将文件Welcome.java编译成Welcome.class。java启动Java虚拟机,虚拟机执行编译器放在class文件中的字节码。
    有关问题可以查看java在线文档
E:
cd E:\IdeaProjects\corejava\v1ch02\Welcome
dir  //查看当前目录有哪些文件
javac Welcome.java //编译器编译成字节码
java Welcome   //虚拟机执行字节码

cd .. //返回到上一级目录
cd ImageViewer // 可以使用Tab键补全
javac ImageViewer.java
java ImageViewer
  • Java区分大小写

3.Java的基本程序设计结构

  • 源代码的文件名必须与公共类的名字相同,并用.java作为扩展名。
  • java ClassName
    运行已编译的程序时,Java虚拟机将从指定类中的main方法开始执行。
public class ClassName
{
    public static void main(String[] args)
    {
        program statements
    }
}
  • Java中所有函数都属于某个类的方法,因此,main方法必须有一个外壳类,并且main方法必须是静态的。main方法没有为操作系统返回“退出代码”。
  • Java使用的通用语法:
    object.method(parameters)

  • 注释
    // /* /
    /
    * */ 这种注释可以用来自动生成文档。

  • 数据类型
    1)四种整型



    Java没有任何无符号形式的int long short byte类型
    2)两种浮点型



    Double.POSITIVE_INFINITY正无穷大
    Double.NEGATIVE_INFINITY负无穷大
    Double.NaN(不是一个数字)

    3)表示Unicode编码的字符单元的字符类型char
    转义序列\u0000到\uffff
    特殊字符的转义序列:



    在Java中,char类型描述了UTF-16编码中的一个代码单元,强烈建议不要在程序中使用char类型,除非确实需要处理UTF-16代码单元。
    4)表示真值的boolean
    false和true,整型值和布尔值之间不能进行转换。这个跟C++不一样,因此会避免因为转换而带来的麻烦。
if (Double.isNaN(x)) //检查x是否不是一个数字

  • final指示常量
    static final设置一个类常量,可以在一个类中的多个方法中使用。

  • Math类中的sqrt方法处理的不是对象,这样的方法被称为静态方法。

>>>运算符会用0填充高位,>>用符号位填充高位。不存在<<<运算符。
移位运算符的右操作数要完成模32的运算。1 << 35等价于 1 << 3.


  • Java字符串是Unicode字符序列,Java没有内置的字符串类型,由标准类库中的String提供。
    Java API查询
    任何一个Java对象都可以转换成字符串。
    String类对象称为不可变字符串。不可变字符串虽然修改(需要重新创建)慢,但是可以让字符串共享。
    equals方法检测两个字符串是否相等;==只能确定两个字符串是否放置在同一位置上。
    Java字符串由char值序列组成。大多数常用的Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。length方法返回采用UTF-16编码表示的给定字符串所需的代码单元数量。要想得到实际的长度,即码点数量,可以使用codePointCount.
    遍历字符串,查看每一个码点(不要使用char,这个是查看代码单元)如下,另一种方法是使用codePoints
//方法1
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;

//方法2
int[] codePoints = str.codePoints().toArray();

//将码点数组转换为一个字符串
String str = new String(codePoints, 0, codePoints.length);
  • 构建字符串
    采用字符串连接的方式效率比较低,每次连接字符串,都会构建一个新的String对象,既耗时,又浪费空间。
    StringBuilder类可以避免这个问题。
StringBuilder builder = new StringBuilder();

builder.append(ch);
builder.append(str);

String completedString = builder.toString();

  • 标准输入输出
    Scanner类定义在java.util包中。
    当使用的类不是定义在基本java.lang包中,一定要使用import指示将相应的包加载进来。




    关于时间的格式化选项,在新代码中,应该使用java.time包方法。

//输入
import java.util.*;

Scanner in = new Scanner(System.in);
String name = in.nextLine();

//格式化输出
System.out.printf("Hello, %s. Next year, you'll be %d", name, age);
  • 文件输入输出
//读取
Scanner int = new Scanner(Paths.get("C:\\mydirectory\\myfile.txt"), "UTF-8");

//写入
PrintWriter out = new PrinterWriter("myfile.txt", "UTF-8"); //文件位于java虚拟机启动路径的相对位置

  • 不能在嵌套的两个块中声明同名的变量。与C++不同。
    在循环中,检测两个浮点数是否相等需要格外小心,由于舍入误差,最终可能得不到精确值,因此永远不会结束。
    Java提供一种带标签的break语句,类似于C++ goto。


  • 大数值
    如果基本的整数和浮点数精度不能够满足需求,可以使用java.math包中的两个很有用的类:BigInteger和BigDecimal。可以处理包含任意长度数字序列的数值。BigInteger实现了任意精度的整数运算,BigDecimal实现了任意精度的浮点数运算。
    Java没有提供运算符重载的功能。

  • 数组
    数组长度不要求是常量;
    越界访问数组后面的元素会引发异常而终止执行;
    一旦创建了数组,就不能再改变大小。
int[] a = new int[100];

// for each循环
for (variable: collection) statement

//更贱大的打印数组中所有值的方法
System.out.println(Arrays.toString(a));
  • Java数组与C++分配在堆上的数组指针一样,[]运算符被预定义为检查数组边界,没有指针运算。
  • 在Java应用程序的main方法中,程序名并没有存储在args数组中
java Message -h world
args[0] 是"-h"。
  • 对数组的一些操作可以使用java.util.Arrays
  • Java的多维数组被解释为“数组的数组”,Java只有一维数组。可以创建一个不规则数组,每一个子数组元素不一样多。

4.对象与类

  • 在Java中,所有类都源自Object
  • 对象的三个主要特性
    1)行为
    2)状态
    3)标识
  • OOP程序设计:首先从设计类开始,然后再往每个类中添加方法。
    识别类的简单规则是在分析问题的过程中寻找名词,而方法对应着动词。
  • 类之间的关系
    1)依赖("uses-a")
    如果一个类的方法操纵另一个类的对象,就说一个类依赖于另一个类。
    应该尽可能将相互依赖的类减至最少。
    2)聚合("has-a")
    3)继承("is-a")



  • Math类只封装了功能,它不需要也不必隐藏数据。
  • 一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。
    可以将Java对象变量看作C++对象指针。
    所有Java对象都存储在堆中。
Date deadline;//没有指向任何对象,不能使用
deadline = new Date(); //可以使用
  • 要想创建一个完整的程序,应该将若干类组合在一起,其中只有一个类有main方法。
  • 可以认为Java编译器内置了make功能
  • 构造器总是伴随着new操作符的执行被调用。
  • 在Java中,所有的方法都必须在类的内部定义。是否将某个方法设置为内联方法是Java虚拟机的任务。
  • 封装的优点
    1)可以改变内部实现,除了该类的方法之外,不会影响其他代码
    2)更改器可以执行错误检查,然而直接对域进行赋值将不会进行这些处理
  • 注意不要编写返回引用可变对象的访问器方法。如果需要返回一个可变对象的引用,应该首先对它进行克隆。
  • final修饰可变的类,有点类似于顶层const,表示final变量中的对象引用不能再指向其他的对象,但是这个对象中的内容可以更改。
    final修饰大都用于基本类型域或不可变类的域。(String类就是不可变的类。)表示在后面的操作中,不能够再对它进行修改。

  • 可以认为静态方法是没有this参数的方法。静态方法不能操作对象,不能访问实例域。但是可以访问自身类中的静态域。
    在下面两种情况下使用静态方法:
    1)一个方法不需要访问对象状态,其所需参数都是通过显式参数提供(如Math.pow)
    2)一个方法只需要访问类的静态域
  • main是静态的。在启动程序时还没有任何一个对象,静态的main方法将执行并创建程序所需要的对象。
    每一个类可以有一个main方法。这是一个常用于对类进行单元测试的技巧。
    独立测试类:java classname
    测试整个程序(类属于这个程序):java Application

  • Java总是采用按值调用。
  • 方法参数有两种类型
    1)基本数据类型(数字、布尔值)
    2)对象引用
    如下swap函数并不能实现对象的交换,在C++中要实现该交换,不能只交换指针,而是要交换指针所指向的内容。C++也有引用交换,实际上交换的也是内容。
//该函数并不能实现对象的交换
public static void swap(Employee x, Employee y) { //x y都是对象引用
    Employee temp = x;
    x = y;
    y = temp;
}

  • 方法的签名signature——方法名+参数类型。返回类型不是方法签名的一部分。Java基于签名进行重载。Java重载和C++重载一样,都是基于签名进行重载。

  • 仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器。
  • C++ 11规定,可以为数据成员提供一个类内初始值。没有初始值的成员将被默认初始化。这个跟Java的显式域初始化是一致的。
  • Java通常使用a+实例域作为参数;或者直接this.name = name使用this的形式。
    C++使用m或x。

  • 三种初始化数据域的方法
    1)在构造器中设置值
    2)在声明中赋值
    3)初始化块
    首先运行初始化块,然后运行构造器的主体部分。

  • 由于有自动垃圾回收器,所以Java不支持析构器。
    但是,当对象使用了内存之外的其他资源,比如文件,或使用了系统资源的另一个对象句柄。这时将其回收和再利用将显得十分重要。
    可以添加一个finalize方法,该方法将在垃圾回收器清除对象之前调用。

  • 所有标准的Java包都处于java和javax包层次中。
    使用包的主要原因是确保类名的唯一性。
  • 有两种方式访问另一个包中的公有类
    1)在每个类名之前添加完整的包名
    2)使用import语句(类似于C++的namespace和using)
    import语句是一种引用包含在包中的类的简明描述。一旦使用了import语句,就不必写出包的全名。
    如果两个包中有相同名字的类,再增加一个特定的import语句来表明使用哪个类。如果两个Date类都需要,则使用完整的包名即可。
java.time.LocalDate today = java.time.LocalDate.now();

import java.util.*;
import java.sql.*;
import java.util.Date; //sql中也有Date类
  • import还可以导入静态方法和静态域的功能
import static java.lang.System.*; //导入类System的静态方法和静态域
  • 将类放入包中的方法
    在源文件的开头,定义类代码之前,加入包的名字。
    如果没有package语句,会放在一个默认包中。
    将包中的文件放到与完整的包名匹配的子目录中。com.horstmann.corejava包中所有源文件放在com/horstmann/corejava。
    注意:编译器在编译源文件的时候不检查目录结构。但是如果包与目录不匹配,虚拟机就找不到类。
package com.horstmann.corejava;

public class Employee;
{
    ...
}
  • 没有指定public和private,这个部分(类、方法或变量)可以被同一个包中的所有方法访问。(这个默认的范围有点大)
  • 类加载器明确禁止加载用户自定义的、包名以“java.”开始的类。
    并且可以通过包密封机制来解决将各种包混在在一起的问题。如果将一个包密封起来,就不能再向这个包添加类了。

  • 类路径
    为了使类能够被多个程序共享,需要做到以下几点:



    如下,类路径包括:
    1)基目录/home/user/classdir或c:\classes;
    2)当前目录(.);
    3)JAR文件/home/user/archives/archive.jar后c:\archives\archive.jar
    运行时库文件(rt.jar和在jre/lib与jre/lib/ext目录下的一些其他的JAR文件)会被自动搜索,不必将它们显式地列在类路径中。
    javac编译器总是在当前的目录中查找文件,但Java虚拟机仅在类路径中有"."目录的时候才查看当前目录。

//UNIX
java -classpath /home/user/classdir:.:/home/user/archives/archive.jar

//Windows
java -classpath c:\classdir;.;c:\archives\archive.jar
  • 虚拟机查询的过程:
    假定要查找com.horstmann.corejava.Employee类
    1)jre/lib和jre/lib/ext目录下
    2)/home/user/classdir/com/horstmann/corejava/Employee.class
    3)com/horstmann/corejava/Employee.class从当前目录开始
    4)com/horstmann/corejava/Employee.class inside /home/user/archives/archive.jar

  • 编译器查询的过程
    假定源文件包含
    import java.util.;
    import com.hortstmann.corejava.
    ;
    并且源代码引用了Employee类。
    1)查找java.lang.Employee(默认导入的lang包)
    2)java.util.Employee
    3)com.horstmann.corejava.Employee
    4)当前包中的Employee
    5)编译器还要查看源文件是否比类文件新。如果是,则源文件就会被自动得重新编译。


  • javadoc可以由源文件生成一个HTML文档。

  • 类设计技巧
    1)一定要保证数据私有
    2)一定要对数据初始化
    3)不要在类中使用过多的基本类型
    4)不是所有的域都需要独立的域访问器和域更改器
    5)将职责过多的类进行分解
    6)类名和方法名要能够体现它们的职责
    7)优先使用不可变的类

5.继承

  • Java中所有的继承都是公有继承
  • 子类覆盖超类方法的注意点
    1)不能直接访问超类的私有域
    2)调用超类的访问器时,加上super.
public class Manager extends Employee
{
    public double getSalary()
    {
        double baseSalary = super.getSalary();
        return baseSalary + bonus;
    }
}
  • 子类构造器
    使用super调用超类构造器必须是子类构造器的第一条语句。如果子类的构造器没有显式地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。
  • 在Java中,不需要将方法声明为虚拟方法,动态绑定是默认的处理方式。如果不希望一个方法具有虚拟特征,可以将其标记为final。
  • 由一个公共超类派生出来的所有类的集合称为继承层次。
  • "is-a"规则,表明子类的每个对象也是超类的对象。
    另一种表述法是置换法则。表明程序中出现超类对象的任何地方都可以用子类置换。
    Java中,对象变量是多态的。一个Employee变量既可以引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象。
  • 同C++一样,反过来赋值不可以,且超类引用子类对象时,不能调用子类特有的方法,这是由声明类型决定的。
  • 一个超类数组和一个子类数组同时引用同一个子类对象,此时对超类存储超类引用,应该引发错误
Manager[] managers = new Manager[10];
Employeep[ staff = managers;
staff[0] = new Employee(...); //引发ArrayStoreException异常
  • 理解方法调用
    1)获取所有候选方法
    包括重载的超类中同名的属性为public的方法
    2)进行参数类型匹配
    3)如果是private、static、final方法或构造器,编译器静态绑定。如果依赖于隐式参数的实际类型,则在运行时实现动态绑定
  • 每次都搜索的开销太大。虚拟机为每个类创建了一个方法表,列出了所有方法的签名和实际调用的方法。
    此时的解析过程变为:
    1)虚拟机提取对象的实际类型的方法表
    2)虚拟机搜索定义指定方法签名的类
    3)虚拟机调用方法
  • 在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。
  • 阻止继承:final类和方法
    类中特定的方法也可以被声明为final,子类不能覆盖这个方法。
    一个类声明为final,其中的方法自动成为final,不包括域。
    虚拟机中的即时编译器如果发现:方法很简短、别频繁调用且没有真正被覆盖,就对其进行内联处理。
  • 强制类型转换,类似于C++ dynamic_cast
    1)只能在继承层次内进行类型转换
    2)在将超类转换为子类之前,应该使用instanceof进行检查
    一般情况下,应该尽量少用类型转换和instanceof运算符。
  • 抽象类——C++的抽象类(包含纯虚函数virtual xxx() = 0;)
    关键字abstract
    包含一个或多个抽象方法的类必须被声明为抽象的。
    抽象方法不需要实现。
    类即使不含抽象类,也可以声明为抽象类。抽象类不能被实例化。
    可以定义一个抽象类的对象变量(引用),它只能引用非抽象子类的对象。
  • 受保护访问
    总结:
    1)仅对本类可见——private
    2)对所有类可见——public
    3)对本包和所有子类可见——protected
    4)对本包可见——默认,不需要修饰符

  • Object:所有类的超类
    在Java中,只有基本类型不是对象(比如数值、字符和布尔类型)
    所有数组类型都扩展了Object类。
  • equals方法
    判断两个对象是否具有相同的引用。
    编写一个equals的建议:


  • hashCode方法
    散列码是由对象导出的一个整型值。hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。
    Equals与hasnCode的定义必须一直:如果x.equals(y)返回true,那么x.HashCode()必须与y.hashCode()具有相同的值。
  • toString方法
    返回表示对象值的字符串。
    如果x是任意一个对象,并调用System.out.println(x);会直接调用x.toString()。
    数组类型需要调用静态方法:Arrays.toString,多维数组调用Arrays.deepToString
    建议:为自定义的每一个类增加toString方法,可以让所有使用这个类的程序员从这个日志记录支持中受益。


  • 泛型数组列表(Java没有运算符重载)
    ArrayList是一个采用类型参数的泛型类,实现动态数组。(跟C++ vector是一样的)
    使用add方法为数组添加新元素,而不要使用set方法。set方法只能替换数组中已经存在的元素的内容。

  • 对象包装器与自动装箱
    所有的类型都有一个与之对应的类。这些类成为包装器(wrapper)。
    Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean。
    对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。
    自动装箱:可以将int类型自动装箱,自动变成Integer。
ArrayList<Integer> list = new ArrayList<>();
//自动装箱
list.add(3);
//自动拆箱
int n = list.get(i);
//将基本方法放置在包装器中
int x = Integer.parseInt(s); //静态方法,字符串转整型
  • Integer对象是不可变的:包含在包装器中的内容不会改变。
    修改数值参数值的方法,需要使用在org.omg.CORBA中定义的holder类型。
public static void triple(IntHolder x) 
{
    x.value = 3 * x.value;
}

  • Java的可变参数与数组是一样的

  • 枚举声明定义的类型是一个类,并且刚好有定义的数量个实例。
    比较两个枚举类的值时,不需要equals,直接使用==即可。
    所有枚举类都是Enum类的子类。重要的方法有toString和valueOf。

  • 反射库被大量用于JavaBeans,是Java组件的体系结构。
    能够分析类能力的程序称为反射(reflective)。反射机制用于:
    1)在运行时分析类的能力
    2)在运行时查看对象
    3)实现通用的数组操作diamante
    4)利用Method对象,这个对象很像C++的函数指针
  • Class类
    运行时,Java运行时系统为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
    保存这些信息的类被称为Class。Object的getClass()方法返回一个Class类型的实例。

6.接口、lambda表达式与内部类

7.异常、断言和日志

8.泛型程序设计

9.集合

10.图形程序设计

11.事件处理

12.Swing用户界面组件

13.部署Java应用程序

14.并发

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容