JAVA面试宝典

面向过程与面向对象的区别:

面向过程性能比面向对象性能高,但是不容易维护,不容易调用,也不容易扩展

面向对象性能比较低,但是易维护,易复用,也易扩展,有三大特性:多态,继承,封装,所以能够降低耦合

JVM

JVM是运行java字节码的虚拟机,在不同的操作系统都会都各自的虚拟机,所以java之所以这么强大就是因为可以一次编译处处运行,什么是字节码呢,字节码就是计算机能都读懂的代码

程序代码到运行的步骤

java代码通过jdk的javac编译获得字节码文件,通过虚拟机加载字节码文件执行程序

JDK是java开发工具,内含jre可以创建和编辑程序,jre是Java运行环境,jre包含jvmjava类库等,不能创建新程序

java面向对象编程三大特征

封装,继承,多态

封装:把一个类的属性私有化,同时提供外面访问属性的方法

继承:拥有父类所有的属性和方法,但是只能访问父类允许的方法和属性

多态:

String、StringBuilder、StringBuffer

String是不可变字符串,每次操作String都会创建新的string对象,StringBuilder和StringBuffer都继承abstractStringBuilder,每次操作上面两个对象都是在自身基础上操作的,且StringBuffer对方法加了同步锁,线程安全,StringBuilder线程不安全,所以StringBuilder性能会不StringBuffer性能高. String是由final修饰的字符数组来保存字符串的,所以String不可变,StringBuffer和StringBuilder是由字符数组来保存字符串的所以两种对象是可变的


装箱:将基本类型用对应的引用类型包装起来

拆箱:将包装类型转换成基本数据类型

为什么要定义一个无参构造器

在执行子类构造方法之前如果没调用super(来调用父类特定的构造方法),则会调用无参构造方法,所以如果没有定义一个无参构造器的话且没有用super来调用特定的构造方法就会编译错误


接口和抽象类的区别是什么

接口默认是public 所有方法在接口中不能有实现方法,抽象方法可以有其他非抽象方法

一个类可以继承多个接口,但是只能实现一个抽象类

接口默认public修饰,抽象类除了private不能修饰其他都可以修饰


成员变量和局部变量有什么区别

成员变量定义在类中方法外,局部变量是定义在方法中,在内存空间中,成员变量是存在堆中,局部变量是存在栈中.成员变量的生命周期是伴随着对象的创建而存在的,伴随对象销毁而销毁,而局部变量是在方法被调用时而存在的,方法调用完毕而销毁


一个类的构造方法的作用

构造方法是初始化对象,每个类都会有一个默认的无参构造器

构造方法有哪些特征

构造名与类名相同,没有返回值但是不能用void来修饰


hashCode和equals

就比如往hashSet集合中存放对象时,会先调用hashcode方法来计算hash值,如果不相等的就说明不存在可以存,如果相同的话就调用equals方法比较两个对象是否相等,如果相等不存,不相等就存


线程的基本状态

初始状态,线程被创建,但是没有调用start()方法

运行状态

阻塞状态

等待状态

超时等待状态

终止状态

当线程被创建后调用start()方法后就进入就绪状态,等待获取cpu的时间片,然后进入运行状态,调用wait()方法进入等待状态,调用sleep()进入超时等待状态,当超时时间达到后就会重新进入运行状态,当线程调用同步方法且没有获得锁就会进入阻塞状态,只有执行完当前县城后才会重新回到运行状态


final关键字

final修饰的变量必须要初始化后才能使用,且初始化后不能再改变

final修饰的方法不能被重载

final修饰的类不能被继承


IO流常见的流

字符流/字节流

BIO和NIO的区别

BIO是阻塞IO,当一个请求过来,要接收数据,都会先开启Socket,然后开启一个线程,读取数据,返回结果,只有业务完成后线程才能释放线程,当大量的请求进来后都会进去阻塞状态

NIO是非阻塞IO,当每一个请求进来后,都会创建一个通道,一个selector会监听多个通道的方式,当通道的状态为就绪状态时就会开启一个线程,执行多路复用操作


final、finally、finalize

finally是异常处理的一部分,只能用在try/cath语句中,表示最后一句要执行

finalize,对象被回收的时候会被调用,在垃圾收集器删除对象之前会被调用


ArraysList扩容机制

ArraysList的默认长度为10,当调用add方法的时候,会对ArrayList的长度进行改变,当往list存储的元素长度大于默认长度时就会对原素组的长度后进行扩容1.5倍,然后在判断新数组的长度是否足够,够了就用这个长度创建一个新的数组,不够将数组长度设置为需要的长度,再将原数组复制到新数组


ArraysList,LinkedList,Vector的区别

ArrayList和Vector都是底层通过数组实现的,可以通过索引查询元素,但是修改数组后需要重新创建新的数组并且将修改后的数组复制到新数组,比较麻烦,LinkList底层是通过链表来实现的,提供了双向链表存储功能,所以查询元素是需要遍历,但是插入数据只需要记录前后元素即可,插入数据比较方便

ArraysList和Vector:ArrayList线程不安全,Vector方法加了同步锁所以线程安全,但是性能就会降低,ArrayList

ArraysList的扩容是在原数组长度后的1.5倍,Vector是在原数组长度后的2倍


Set List Map分析

set和list都是Collection的子接口,map是接口

set插入元素是无序的,且不可重复

list插入元素是有序的,也可以重复

map是以键值对的形式存储元素的,见不能为重复,值可以重复

set的查询效率比较低,增删改效率比较高,插入和删除不会发生元素位置的改变

list的查询效率比较高,但是增删改的效率比较低,删除和修元素改会发生位置的改变


深拷贝和浅拷贝的区别

浅拷贝:复制内存地址,被复制的对象发生改变,复制的对象也会跟着改变

深拷贝:在内存中新开辟一个新的空间用于存储拷贝的对象


序列化和反序列化

序列化就是将对象转换成字节序列也就是比较常见的变成文件

反序列化就是将字节序列化转变成对象


怎么实现序列化

实现Serializable接口即可


如果不想字段被序列化怎么处理

使用transient关键字修饰即可


HashMap底层原理

HashMap是基于Map接口实现的,以键值对的形式存储数据,底层是由数组,链表和黑红树数据结构实现的,三种结构结合使用可以取其精华,数组查询效率高,链表增删改效率高,当链表元素超过8个的时候就会用红黑树代替链表,因为红黑树是二叉平衡数,所以查询效率会比链表高,HashMAp的初始容量为16,扩容是原长度的2倍


HashMap,HashTable和ConcurrentHashMap的区别

HashMap的线程不安全,接收一个空的key和空的value,异步执行,快速失败迭代器,在遍历的过程中有其他线程操作增删操作的时候会抛出异常,因为迭代器操作的是集合本身

HashTable线程安全,key和value不能为空,同步执行,在遍历是其他线程进行增删操作的时候不会抛出异常,因为迭代器操作的是原集合的拷贝

HashMap在jdk1.8后当链表长度大于8个是红黑树就会替换链表结构

HashTable和ConcurrentHashMap的区别,ConcurrentHashMap是在HashTable上优化的,优化了链表的查询修改的效率,因为HashTable的是锁住整个Map,而ConcurrentHashMap只锁了一部分Map,所以效率有所提高


为什么HashMap存储数据的容量会采用二进制

因为为了HashMap的存取效率高,要经量减少碰撞,就要经量把数据分配均匀,而取模运算就很好符合这个需求,所以存储容量会采用二进制


HashMap集合中put是如何实现的

调用put方法是首先判断数组是否为空,如果为空的话先进行扩容,然后再根据key计算出hash值得到插入的数组索引,如果索引所在的元素是空的话就直接插入元素然后判断已存的键值对数是否超过最大容量,如果超过就要进行扩容.如果索引所在的元素不为空的话比较数组的元素与key是否相同,如果相同直接覆盖,不相同的话判断元素是否为红黑树,如果数红黑树的话直接插入数据即可,如果不是的话,判断链表长度是否大于8,如果没大于的话直接遍历链表插入,如果遍历过程中发现key已经存在就直接覆盖,如果是红黑数的话直接树中插入键值对


Servlet接口中有哪些方法及Servlet生命周期

接口中有init()初始化方法,service()方法,destroy()方法

生命周期:web容器加载servlet并且实例化后,容器就会运行init方法进行servlet的初始化,当有请求进来时就会调用service方法,当服务器正常关闭后就是执行destroy方法,init和destory方法只会执行一次,而客户端每次请求都会调用service方法,如果需要初始化资源的方法可以放在init方法中


Mybatis

#{}和${}的区别

${}是变量占位符,用于xml的标签属性中和sql内部,用于字符串替换,也就是用户传入非法参数进来也会原样拼接到sql,所以sql内部会造成sql注入,非常不安全不建议使用于sql内部

#{}是参数占位符,用于sql内部,mybatis会将#{}变成?,在执行sql之前会使用预编译会话的参数设置方法按顺序给?占位符进行设置参数,避免了sql注入


Statement与preparedStatement区别

预编译会话的效率会比普通会话效率高,因为预编译会先将语句进行编译,且编译后的语句可以重复使用

预编译可以避免sql注入比较安全


什么是sql注入

用户输入非法参数,服务端在构造sql语句的时候也能构造成功,会让数据泄露等问题


一级缓存和二级缓存

一级缓存:当我们执行查询操作的时候,会将查询结果放到sqlsession中并且是用Map结构来保存的,当下一次执行查询操作的时候会先去sqlsession查询是否有,如果有的话就会直接拿来用,没有的话就去数据库查.当sqlsession消失是一级缓存一会随之消失,sqlsession调用增加修改删除close()commit()方法后就会清空缓存

二级缓存是存在sqlsessionFactory中的,且他的作用域是namespace,是多个sqlsession共享的,当不同的sqlsession操作相同的namespace且想sql传递的参数相同的话最终执行相同的sql语句,第一次执行会从数据库中查询数据并放到缓存中,第二次执行会从缓存中获取,提高查询效率

一级缓存是默认开启的,二级缓存是默认不打开的


如果查询的数据在多张表中  那么MyBatis是怎么实现的

多表联查的会导致实体类的字段与查询结果的属性不一致,所以实体类会用对应的类封装数据,在Mybatis会配置resultMap来映射不同的属性和字段


MP和Mybatis的区别

MP只在Mybatis的基础上做增强,不改变任何东西,主要差别在于MP提供了BaseMapper接口,写单表的增删查改不需要在写xml了


Spring

Spring框架中用到的设计模式

1.工厂设计模式:BeanFactory ,ApplicationContext, 创建Bean对象

2.代理设计模式:实现Aop功能

3.单例设计模式:Spring所有的bean都是单例模式

4.模板方法模式:jdbcTemplate,用于操作数据库的模板类

5.包装器设计模式:

6适配器模式

7:观察者模式


Ioc和Aop

什么是Ioc

Ioc是控制反转,不需要我们手动创建对象,只需要配置一下配置文件和贴注解spring容器就会帮我们创建对象


什么是Spring AOP

AOP是面向切面编程,基于动态代理,对重复代码或则非核心业务代码(比如事务管理,日志功能,权限控制)进行抽取,降低模块间的耦合度有利于维护和扩展



多线程会带来什么问题

使用多线程会出现内存泄露,上下文切换,死锁问题等


线程的生命周期

线程被创建,进入初始化状态

线程调用start方法进入就绪状态等待获取cpu时间片

当线程获取到时间片后,进入运行状态

在运行状态调用wait方法,线程就会进入等待状态,需要其他线程调用notify方法或notifyAll方法就会重新进入就绪状态

在运行状态调用sleep方法时,线程进入超时等待状态,等到时间到达后该线程就会进入就绪状态

在线程在运行状态需要调用synchronize同步锁时,而其他线程真用同步锁,该线程就会进入同步阻塞状态

当运行状态的线程调用yield让步方法时,该线程会由运行状态进入就绪状态

当线程执行完任务后就会进入终止状态


什么是上下文切换

当线程在执行任务没有完成但是cpu时间片到了切换下一个任务是,该线程会保存当前线程的任务完成度,然后进入就绪状态,当该线程再次进入运行状态时会在之前记录的任务进度上继续执行任务


什么线程死锁

两个或两个以上的线程相互持有对方所需要的资源,因为线程要执行完任务后才会释放资源,所以线程在循环等待对方所持有的资源,造成线程死锁


造成死锁的四个条件

互斥条件:资源在任意时刻只能被一个线程占用

请求和保持条件:线程在请求资源时阻塞,已持有的资源不会释放

不剥夺条件:线程不会强行抢夺其他线程的资源

循环等待条件:两个或两个以上的线程持有对方所需的资源

避免线程的方式

互斥条件无法破坏,因为枷锁就是为了让他们互斥的

破坏请求和保持条件:一次性请求资源

破坏剥夺条件:占用部分资源的线程进一步申请,如果没有申请成功就主动释放资源

破坏循环等待条件:按某一顺序进行申请资源


并行和并发的区别

并发:在某一时段内,多个线程在执行

并行:在单位时间内,多个任务同时执行


sleep和wait方法的区别

sleep和wait方法都会让线程进入阻塞状态

1.sleep是属于Thread类,wait是属于Object

2.sleep不会释放锁,wait会释放锁

3.sleep会自动苏醒,到时间后会从就绪状态转到运行状态

wait不会自动苏醒,需要其他线程通知,即其他线程调用notify方法或则notifyAll方法


synchronize关键字的作用

确保synchronize修饰的方法或则代码块能够任意时刻只执行一个线程

怎么使用synchronize关键字的

修饰实例方法:给当前对象加锁,在进入同步方法前要获取对象实例的锁

修饰静态方法:给当前的类加锁,在进入同步方法前获取当前类的锁

修饰代码块:给给定对象加锁,在进入同步方法前获取给定对象的锁

两个线程分别调用实例方法和静态方法不会发生互斥,因为访问静态方法占用的是类的锁,访问非静态方法的是对象实例的锁


什么是双重检查锁实现单例?怎么实现的

创建单列对象是进行两次判断实例是否存在,第一次加锁检查第二次不加锁检查

定义一个volatile修饰的变量接收创建的实例,使用volatile是为了禁止jvm的指令重排,定义无参构造器,然后判断当前的实例是否为空,目的是防止已创建的实例的对象进入,然后进行线程加锁,在判断实例是否为空,为空进行创建实例

创建实例不是原子性的,分为三步,1.分配内存空间,2.初始化实例,3.将实例指向分配的内存地址,因为jvm指令重排的原因,可以能创建实例的顺序为132


synchronized 关键字底层做了哪些优化

JDK1.6后synchronize引入了,偏向锁,轻量锁,自旋锁,锁消除,锁粗化等技术为了减少锁的操作开销

锁有四种状态,他们会随着竞争越激烈而升级,锁只能升级不会降级,提高对获得锁和释放锁的效果

1:无锁状态

2:偏向锁状态

3:自旋锁状态

4:重量级锁状态


synchronized和ReentrantLock区别是什么?

synchronize和ReentrantLock都是可重入锁,就是只要对象没有释放锁还是可以获取对象的

synchronize是JVM层面的,ReentrantLock是JDK层面的

synchronize和ReentrantLock控制等待和唤醒也是不同的,synchronize需要调用wait方法进入等待状态,需要其他线程调用notify和notifyall方法才能唤醒,ReentrantLock进入等待状态要调用await方法,唤醒要调用signal和signalAll方法

synchronize在竞争资源时会一直等待,ReentrantLock可以尝试获取锁,并得到获取结果

synchronize加锁代码在执行完任务或则出现异常后会释放锁,ReentrantLock不会释放锁,需要在finally代码块显示释放

synchronize是无法实现公平锁的,ReentrantLock可以满足公平锁的


volatile关键字

volatile是确保变量的可见性,禁止jvm指令重排

并发编程的三个特性

原子性:一个操作要么都执行,要么都不执行

可见性:当一个变量发生改变时,其他线程都可以看到最新修改的值,volatile可以确保变量的可见性

有序性:代码执行都有先后顺序,但是jvn内部会有优化,执行顺序会发生改变,volatile可以禁止jvm的指令重排


volatile和synchronize的区别

volatile和synchronize是互补的

①volatile关键字是同步线程的轻量级实现,所以性能会比synchronize的性能高,volatile关键字修饰变量,synchronize修饰方法和代码块,所以我们用synchronize比较多②volatile能确保数据的可见性,不能确保数据的原子性,synchronize可以两者的都可以确保③volatile解决的是线程中可变变量的可见性,synchronize解决的是多线程之间的访问资源同步性④volatile关键字不是发生线程阻塞,synchronize可能会发生线程阻塞


ThreadLocal有什么作用

ThreadLocal是线程本地存储,每个线程都会创建一个ThreadLocalMap对像,每个线程可以访问自己的ThreadLocalMap对像中的value,这样可以避免资源在多线程之间的共享


什么是线程池

创建多个线程放到一个池子中,有任务需要处理时,就会到任务队列中,等任务完成后都会线程不会销毁而是回到线程池中等待下一次任务


实现Runnable接口和Callable接口的区别

callable是对runnable的补充

Runnable接口不能抛出检查时异常,且没有返回值,callable的call方法可以捕捉异常,且有返回值

执行execute()方法和submit()方法的区别是什么呢?

execute用于提交不需要返回值的任务,所以无法判断任务是否成功在线程池中执行完成

submit方法用于提交需要返回值的任务,线程池会返回一个Future对象,通过这个对象来判断任务是否完成


如何创建线程池

有两种方式

1.通过ThreadPoolExecutor构造器来实现

2.通过Executor的工具类Executors来实现,可以创建三种池

FixedThreadPool:创建指定返回线程数量的线程池

SingleThreadExecutor:创建只返回一个线程对象的线程池

CacheThreadPool:创建可以根据实际情况创建线程数量的线程池


线程池的原理分析

当有一个任务要提交的时候,会到线程池中判断核心线程数量是否为最大值,如果不是的话就创建线程,如果是的话就到任务队列中判断是否已满,如果没满的话就会在队列中排队,如果满了就到线程池中判断最大线程数量是否为最大值,如果不为最大值的话创建线程,如果为最大值就会拒绝此次任务


线程池的七个参数

1.线程池最大核心线程数

2.线程池最大线程数

3.空闲线程的存活时间

4.空闲线程的存活时间单位

5.任务队列

6.线程工厂

7拒绝策略


Atomic原子类

Atomic具有原子特性,任务一旦执行就不会被打断,就算多个线程在同时执行,一旦任务开始就不会受其他线程的影响,


什么是JUC

JUC是java.util.concurrent包的简称,他是一个工具包,里面包含多个多个原子类,这个原子类可以提高线程的高并发,以及在多线程执行的过程避免线程死锁


什么是乐观锁和悲观锁

乐观锁:他总是很乐观,在读取数据的时候觉得不会有其他线程来修改数据,所以不加锁,但是在修改数据的时候他会先判断在此之前是否有其他线程对数据进行修改,通常是通过CAS和版本号来操作的

悲观锁:他总是很悲观,在读取数据的时候总是会觉得其他线程会修改数据,所以总是加锁,其他线程要获取该数据的时候会出现阻塞状态,等到锁的时候才会读取数据


spring中的bean的作用域有哪些

singleton:唯一的bean实例,且spring中的bean都时单例

prototype:每一次请求都会创建一个新的bean实例

request:每一次发送http请求都会创建一个新的bean,这个bean只作用于http的request中

session:每一次http请求都会创建一个新的bean,这个bean只作用于http中的session中

global-session:全局session作用域


Spring 中的单例 bean 的线程安全问题了解吗?

因为bean默认是单例的,在多线程操作统一个对象的时候会对这个对象的非静态成员变量的写操作会存在线程安全

解决方案

在类中定义一个ThreadLocal成员变量,将需要的成员变量保存到ThreadLocal对象中


@Component和@Bean的区别

1.@Component作用于类,@Bean作用于方法

2.@Component注解会通过扫描类所在的路径进行自动检测和自动装配到spring容器中,贴有@Bean注解的方法会创建一个bean,然后告诉spring这个方法是属于哪个类的,下次要用到的话就可以直接拿来用

3.@Bean注解的自定义更高,在需要使用到第三方库的类时,只能使用@Bean注解将这个类注入到spring容器中


Spring Aop和AspectJ Aop的区别

spring Aop是运行时增强,AspectJ Aop是编译时增强,Spring aop是基于动态代理的,AspectJ Aop是基于字节码操作的

AspectJ功能比springaop强大,所以aop相对来说比较简单


Spring中bean的生命周期

spring启动后会创建bean实例,bean实例创建后会对bean设置属性,如果bean实现了BeanNameAware接口的话,spring就会将bean的id传递给setBeanName方法

如果bean实现了BeanFactoryAware接口的话,spring会调用setBeanFactory方法将BeanFactory容器实例传入

如果bean实现了ApplicationContexAware接口的话,spring会调用bean的setApplicationContex方法,将bean所在的引用上下文传入进来

如果bean实现了BeanPostProcessor接口的话,spring会调用postProcessBeforInitialization方法进行预初始化

如果bean实现了initializingBean方法的话,spring会调用afterPropertiesSet方法

如果bean调用了init-method声明初始化方法,则该方法会被调用

如果实现BeanPostProcessor接口的话,spring会调用postProcessAffectInitialization方法

如果bean实现了DisposableBea你接口的话,spring会调用


SpringMVC的执行流程

1.当用户发送一个请求后会到前端控制器

2.前端控制器会到处理器映射器中查找处理器

3.返回处理器执行链到前端控制器

4.前端控制器会到控制器适配器中根据xml配置文件找到对应的controller

5.执行controller对应的方法,并返回ModelAndView对象到控制器适配器

6.控制器适配器会返回ModelAndView对象到前端控制器

7.前端控制器会将ModelAndView对象传到视图解析器进行解析

8试图解析器会返回View对象到前端控制器

9.前端控制器会根据model对View进行渲染视图,并将渲染结果响应给客户端

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

推荐阅读更多精彩内容