Java

语言技能

JAVA基础

操作符、控制执行流程
JAVA的重要特性:自动内存管理机制、异常处理。

ArrayList的优缺点

因为ArrayList底层使用数组实现,所以优缺点与数组类似。
优点:
1、根据下标遍历元素效率较高。
2、根据下标访问元素效率较高。
3、在数组的基础上封装了对元素操作的方法。
4、可以自动扩容。

缺点:
1、插入和删除的效率比较低。
2、根据内容查找元素的效率较低。
扩容规则:每次扩容现有容量的50%。

ArrayList vs LinkedList

  1. 存储结构不同
  2. 操作不同
    ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

ArrayList:内部使用数组的形式实现了存储,实现了RandomAccess接口,利用数组的下面进行元素的访问,因此对元素的随机访问速度非常快。

因为是数组,所以ArrayList在初始化的时候,有初始大小10,插入新元素的时候,会判断是否需要扩容,扩容的步长是0.5倍原容量,扩容方式是利用数组的复制,因此有一定的开销;

ArrayList vs Vector

List接口下一共实现了三个类:ArrayList,Vector,LinkedList。LinkedList就不多说了,它一般主要用在保持数据的插入顺序的时候。ArrayList和Vector都是用数组实现的,主要有这么三个区别:

1、Vector是多线程安全的,而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;

2、两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同的,很多网友说Vector增加原来空间的一倍,ArrayList增加原来空间的50%,其实也差不多是这个意思,不过还有一点点问题可以从源码中看出,一会儿从源码中分析。

3、Vector可以设置增长因子,而ArrayList不可以,最开始看这个的时候,我没理解什么是增量因子,不过通过对比一下两个源码理解了这个,先看看两个类的构造方法:

如何决定使用 HashMap 还是 TreeMap?

TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。
HashMap<K,V>的Key值实现散列hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在Map中插入、删除和定位元素。
结论:如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。

ConcurrentHashMap

put
rehash
get

红黑树

红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。
性质1. 结点是红色或黑色。
性质2. 根结点是黑色。
性质3. 所有叶子都是黑色。(叶子是NIL结点)
性质4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
性质5. 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。

nio 和 io 的区别

1、IO基于字节流和字符流进行操作的
2、NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
3、NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO

IO模型

  1. 就IO而言:概念上有5中模型:blocking I/O,nonblocking I/O,I/O multiplexing (select and poll),signal driven I/O (SIGIO),asynchronous I/O (the POSIX aio_functions)。
  2. 然后呢 不同的操作系统对上述模型支持不同: unix支持io多路复用,不同系统叫法不同 :freebsd里面叫 kqueue;linux 是epoll。而windows: 2000的时候就诞生了IOCP支持最后一种异步I/O
    3.java是一种跨平台语言,为了支持异步IO,诞生了nio,Java1.4引入的NIO 1.0是基于I/O复用的。在各个平台上会选择不同的复用方式。Linux用的epoll,BSD上用kqueue,Windows上应该是重叠I/O(肯定不是IOCP)。
    4:基于jdk的nio ,不同公司出了一堆框架:apache mina ,jboss的netty,sun的grizzly。 这些都是直接封装传输层的tcp/udp。

nio直接使用比较难用,所以有了netty等针对网络io部分(tcp/udp-传输层)的封装(nio也有非网络io部分),为了使nio更易用。 netty等只是一个nio框架,不需要web容器的额外支持,也就是说不限定web容器。

java 中,直接缓冲区与非直接缓冲器有什么区别

非直接缓冲区:通过allocate()分配缓冲区,将缓冲区建立在JVM的内存中
直接缓冲区:通过allocateDirect()分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率。

深拷贝与浅拷贝的区别

深拷贝:除了对象本身被复制外,对象所包含的所有成员变量都会被复制,包括引用类型的成员对象

浅拷贝:只复制对象其中包含的值类型的成员变量,而引用类型的成员对象没有被复制

this 关键字

this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。编译器做了一些幕后工作,它暗自把“所使用对象的引用”作为第一个参数传递给被调用的方法。

Static关键字

通常来说,当创建类时,就是在描述那个类的对象的外观与行为。除非用new创建那个类的对象,否则,实际上并未获得任何对象。执行new来创建对象时,数据存储空间才被分配,其方法才供外界调用。
有两种情形用上述方法是无法解决的。一种情形是,只想为某特定域分配单一存储空间,而不去考虑究竟要创建多少对象,甚至根本就不创建任何对象。另一种情形是,希望某个方法不与包含它的类的任何对象关联在一起。也就是说,即使没有创建对象,也能够调用这个方法。
通过static关键字可以满足这两方面的需要。Static方法就是没有this的方法。

实例变量与静态变量有什么不同

区别一、定义不同
静态变量定义时候前面要加上static,实例变量不需要加。
区别二、初始化不同
静态变量随着类的加载而初始化,实例变量是new对象后才进行初始化。
区别三、内存位置不同
静态变量存储在静态变量区,实例变量存储在堆内存区
区别四、调用方式不同
静态变量通过类名调用,实例变量通过对象调用
区别五、生命周期不同
静态变量随着类的加载而加载,虚拟机停止运行时,静态变量周期结束。实例变量随着对象的产生而产生,随着对象的消失而失去引用,等待垃圾回收。

final关键字

final数据:一块数据是恒定不定的
final方法:把方法锁定,以防任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。
final类:当将某个类的整体定义为final时,就表明了你不打算继承该类,而且也不允许别人这样做。换句话说,出于某种考虑,你对该类的设计永不需要做任何变动,或者出于安全的考虑,你不希望它有子类。

finalize()方法

假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。它的工作原理“假定”是这样的:一时垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。
不同于C++中的析构函数,在C++中对象一定会被销毁,而Java里的对象却并非总是被垃圾回收。或者换句话说:

  1. 对象可能不被垃圾回收
  2. 垃圾回收并不等于“析构”

类的初始化顺序

初始化顺序:
父类–静态变量/父类–静态初始化块
子类–静态变量/子类–静态初始化块
父类–变量/父类–初始化块
父类–构造器
子类–变量/子类–初始化块
子类–构造器

结论:

  • 子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了;
  • 静态变量、静态初始化块顺序取决于它们在类中出现的先后顺序
  • 变量、初始化块初始化顺序取决于它们在类中出现的先后顺序

Java有哪些类型引用

  1. 强引用(Strong Reference):类似于Object object = new Object(),强引用存在,垃圾收集器就不会回收它所指向的对象
  2. 软引用(Soft Reference):内存足够的时候,软引用指向的对象存留,内存将要溢出的时候,回收软引用指向的对象
  3. 弱引用(Weak Reference):只要垃圾收集器开始工作,不管内存够不够,弱引用指向的对象都被回收
  4. 虚引用(Phantom Reference):虚引用对指向对象没有任何影响

ThreadLocal原理

每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。

应用场景:
每个线程需要有自己单独的实例
实例需要在多个方法中共享,但不希望被多线程共享

存在泄露问题:
在上面提到过,每个thread中都存在一个map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例. 这个Map的确使用了弱引用,不过弱引用只是针对key. 每个key都弱引用指向threadlocal. 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收. 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用. 只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收.
  所以得出一个结论就是只要这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。其实这是一个对概念理解的不一致,也没什么好争论的。最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用的。就可能出现内存泄露。

线程池的工作原理

image.png

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:(1)直接申请线程执行该任务;(2)缓冲到队列中等待线程执行;(3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。
线程池运行机制主要分为:
线程池如何维护自身状态。
线程池如何管理任务。
线程池如何管理线程。

线程池状态

image.png

image.png

任务执行机制

任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。

首先,所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:
首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。


image.png

接口和抽象类有什么区别

接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。

抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。
人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.
所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。

第一点. 接口是抽象类的变体,接口中所有的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类。
第二点. 接口可以多继承,抽象类不行
第三点. 接口定义方法,不能实现,而抽象类可以实现部分方法。
第四点. 接口中基本数据类型为static 而抽象类不是的。

当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的

所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度的。

servlet 生命周期详解

Servlet 生命周期可以归纳为:Servlet 加载--->实例化--->服务--->销毁

  1. 在正常情况下,Servlet只会初始化一次,而处理服务会调用多次,销毁也只会调用一次;但是如果一个Servlet长时间不使用的话,也会被容器自动销毁,而如果需要再次使用时会重新进行初始化的操作,即在特殊情况下初始化可能会进行多次,销毁也可能进行多次。

  2. 在servlet实例创建之后,在servlet能为客户请求提供服务之前,容器会在servlet实例上调用init()方法。如果你有初始化代码,就应该覆盖servlet类的init()方法,否则会调用GenericServlet的init()方法。而对应每个客户请求(无论是谁,无论是不是同一个人,只针对请求),容器都会创建一对新的请求和响应对象,创建一个新的线程/栈。任何servlet类都不会有多个实例,除非一种特殊情况(SingleThreadModel)。

  3. servlet生命周期的4个周期总结如下:
    a. 实例化以及加载servlet,new的过程

b. 初始化init(ServletConfig)。

c. 处理请求,调用servlet的service,doget,dopost方法将Request和Response,作为参数传递。

d. 退出服务,调用destory方法释放资源。

servlet3异步原理

](https://my.oschina.net/wangxindong/blog/1555194)

image.png

接收到request请求之后,由tomcat工作线程从HttpServletRequest中获得一个异步上下文AsyncContext对象,然后由tomcat工作线程把AsyncContext对象传递给业务处理线程,同时tomcat工作线程归还到工作线程池,这一步就是异步开始。在业务处理线程中完成业务逻辑的处理,生成response返回给客户端。在Servlet3.0中虽然处理请求可以实现异步,但是InputStream和OutputStream的IO操作还是阻塞的,当数据量大的request body 或者 response body的时候,就会导致不必要的等待。从Servlet3.1以后增加了非阻塞IO,需要tomcat8.x支持。
image.png

通讯模型中的NIO可以利用很少的线程处理大量的连接,提高了机器的吞吐量。Servlet的异步处理机制使得我们可以将请求异步到独立的业务线程去执行,使得我们能够将请求线程和业务线程分离。通讯模型的NIO跟Servlet3的异步没有直接关系。但是我们将两种技术同时使用就更增加了以tomcat为容器的系统的处理能力。自从Servlet3.1以后增加了非阻塞的IO,这里的非阻塞IO是面向inputstream和outputstream流,通过jdk的事件驱动模型来实现,更一步增强了Servlet异步的高性能,可以认为是一种增强版的异步机制。

OOP

OOP ,面向对象程序设计,一切皆对象。
面向对象的三个特性:封装、继承、多态

抽象/封装

隐藏具体实现:访问限制符,private、protected、public
复用具体实现:组合(composition)也叫聚合(aggregation), “has-a”(拥有)的关系。

继承

“是一个” 与“象是x一个” 关系,"is-a" and "is-like-a"关系。

继承如何确保合理性

可以用一个导出类对象完全来完全替代一个基类对象。
里氏替换原则:

多态

向上转型(upcasting):把导出类看做是它的基类的过程。
多态的作用是消除类型之间耦合的关系,多态方法调用允许一种类型表现出与其他相似类型之间的区别。
动态绑定(后期绑定),它的含义是在运行时根据对象的类型进行绑定。

对象的创建和生命周期

Java完全采用了动态内存分配方式。

对象生命周期

对象作用域,作用域之外依靠垃圾回收器

复用类的方式

  • 组合
  • 继承
  • 代理

内部类

内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类

异常处理

Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型:Error用来表示编译时和系统错误;Exception是可以抛出的基本类型,在Java类库,用户方法以及运行时故障中都可能抛出Exception型异常。

  • 受检异常和运行时异常(RuntimeException非受检异常)
  • finally的使用场景:把除内存之外的资源恢复到它们的初始状态,比如已经打开的文件或网络连接。

并发编程

想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为“并发”。

如何合理设置线程池大小

要想合理的配置线程池的大小,首先得分析任务的特性,可以从以下几个角度分析:

任务的性质:CPU密集型任务、IO密集型任务、混合型任务。
任务的优先级:高、中、低。
任务的执行时间:长、中、短。
任务的依赖性:是否依赖其他系统资源,如数据库连接等。
性质不同的任务可以交给不同规模的线程池执行。
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程

多线程

synchronized 和 Lock 有什么区别?

1、 首先synchronized是Java内置关键字,在JVM层面,Lock是个Java类;
2、 synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
3、 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
4、 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

reentrentlock

AQS

CountDownLatch

java对象的访问定位方式

1.句柄访问

  1. 直接指针访问

equals vs ==

1、 ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同
2、 ==是指对内存地址进行比较 equals()是对字符串的内容进行比较
3、==指引用是否相同 equals()指的是值是否相同

Java通过几种原子操作完成工作内存和主内存的交互

lock:作用于主内存,把变量标识为线程独占状态。
unlock:作用于主内存,解除独占状态。
read:作用主内存,把一个变量的值从主内存传输到线程的工作内存。
load:作用于工作内存,把read操作传过来的变量值放入工作内存的变量副本中。
use:作用工作内存,把工作内存当中的一个变量值传给执行引擎。
assign:作用工作内存,把一个从执行引擎接收到的值赋值给工作内存的变量。
store:作用于工作内存的变量,把工作内存的一个变量的值传送到主内存中。
write:作用于主内存的变量,把store操作传来的变量的值放入主内存的变量中。

volatile作用

volatile保持内存可见性和防止指令重排序
volatile的特殊规则就是:
read、load、use动作必须连续出现。
assign、store、write动作必须连续出现。

所以,使用volatile变量能够保证:
每次读取前必须先从主内存刷新最新的值。
每次写入后必须立即同步回主内存当中。
也就是说,volatile关键字修饰的变量看到的随时是自己的最新值。线程1中对变量v的最新修改,对线程2是可见的。

本地事务

InnoDB 是通过 日志和锁 来保证的事务的 ACID特性,具体如下:
(1)通过数据库锁的机制,保障事务的隔离性;
(2)通过 Redo Log(重做日志)来,保障事务的持久性;
(3)通过 Undo Log (撤销日志)来,保障事务的原子性;
(4)通过 Undo Log (撤销日志)来,保障事务的一致性;
Undo Log 如何保障事务的原子性呢?
具体的方式为:在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为 Undo Log),然后进行数据的修改。如果出现了错误或者用户执行了 Rollback 语句,系统可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态。
Redo Log如何保障事务的持久性呢?
具体的方式为:Redo Log 记录的是新数据的备份(和 Undo Log 相反)。在事务提交前,只要将 Redo Log 持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到崩溃之前的状态。

JMX

为什么要分库分表

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

推荐阅读更多精彩内容