06-Java基础-File & I\O

异常、File类、I\O流

异常

异常就是Java程序在运行过程中出现的错误。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。



从大体来分异常为两块:
1、error---错误 : 是指程序无法处理的错误,表示应用程序运行时出现的重大错误。例如jvm运行时出现的OutOfMemoryError以及Socket编程时出现的端口占用等程序无法处理的错误。
2、Exception --- 异常 :异常可分为运行时异常跟编译异常
1)运行时异常:即RuntimeException及其子类的异常。这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,但是程序员也可以根据需要进行捕获抛出。
2)编译异常:RuntimeException以外的异常。这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。

  • 捕获异常
    使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try
{
   // 程序代码
}catch(ExceptionName e1)
{
   //Catch 块
}

Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。

public static void main(String args[]){
      try{
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      }catch(ArrayIndexOutOfBoundsException e){
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
/*
以上代码编译运行输出结果如下:
Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
*/
  • 多重捕获块
    一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。
try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}
  • finally关键字
    finally 关键字用来创建在 try 代码块后面执行的代码块。
    无论是否发生异常,finally 代码块中的代码总会被执行。
    在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}

注意下面事项:
1.catch 不能独立于 try 存在。
2.在 try/catch 后面添加 finally 块并非强制性要求的。
3.try 代码后不能既没 catch 块也没 finally 块。
4.try, catch, finally 块之间不能添加任何代码。

  • throws/throw 关键字
    如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
    也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
    throws的方式处理异常:
    定义功能方法时,需要把出现的问题暴露出来让调用者去处理。
    那么就通过throws在方法上标识.
    throw的方式处理异常:
    在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
    throws和throw的区别:
    1.throws
    用在方法声明后面,跟的是异常类名
    可以跟多个异常类名,用逗号隔开
    表示抛出异常,由该方法的调用者来处理
    2.throw
    用在方法体内,跟的是异常对象名
    只能抛出一个异常对象名
    表示抛出异常,由方法体内的语句处理
    public static void main(String[] args){
        try {
            str2int("a");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void str2int(String str) throws Exception { //这里将得到的异常向外抛出
        try {
            System.out.println(Integer.parseInt(str));

        } catch(NumberFormatException e) {
            //TODO 这里可以做一些处理,处理完成后将异常报出,让外层可以得到异常信息
            throw new Exception("格式化异常");
        }
    }
/*
输出结果
java.lang.Exception: 格式化异常
    at Exception.ExceptionDemo.str2int(ExceptionDemo.java:37)
    at Exception.ExceptionDemo.main(ExceptionDemo.java:25)
*/
  • 声明自定义异常
    所有异常都必须是 Throwable 的子类。
    如果希望写一个检查性异常类,则需要继承 Exception 类。
    如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
public class Person {
    private int age;
 
    public int getAge() {
        return age;
    }

    public void setAge(int age) throws AgeOutOfBoundsException{
        if (age <= 150 && age > 0){
            this.age = age;
        }else {
            throw new AgeOutOfBoundsException("年龄非法");
        }
    }
}


public class ExceptionDemo {

    public static void main(String[] args){
        customExceptionDemo();
    }

    private static void customExceptionDemo(){

        Person person = new Person();
        try {
            person.setAge(-1);
        } catch (AgeOutOfBoundsException e) {
            e.printStackTrace();
        }
    }
}

class AgeOutOfBoundsException extends Exception{
    public AgeOutOfBoundsException() {
    }

    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}
File类
  • 构造方法
    File(String pathname):根据一个路径得到File对象
    File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
    File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象(更强大)

  • 创建功能
    public boolean createNewFile(): 创建文件 如果存在这样的文件,就不创建了
    public boolean mkdir(): 创建文件夹 如果存在这样的文件夹,就不创建了
    public boolean mkdirs(): 创建文件夹,如果父文件夹不存在,会帮你创建出来

  • 重命名和删除功能
    public boolean renameTo(File dest):把文件重命名为指定的文件路径
    public boolean delete():删除文件或者文件夹
    重命名注意事项:
    1.如果路径名相同,就是改名。
    2.如果路径名不同,就是改名并剪切。
    删除注意事项:
    Java中的删除不走回收站。
    要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹.

  • 判断功能
    public boolean isDirectory()  判断是否是目录
    public boolean isFile()     判断是否是文件
    public boolean exists()    判断是否存在
    public boolean canRead()  判断是否可读
    public boolean canWrite()  判断是否可写
    public boolean isHidden()  判断是否隐藏

  • 获取功能
    public String getAbsolutePath()  获取绝对路径
    public String getPath()      获取路径(构造方法中的路径)
    public String getName()      获取名称
    public long length()        获取长度。字节数
    public long lastModified()      获取最后一次的修改时间,毫秒值
    public String[] list()        获取指定目录下的所有文件或者文件夹的名称数组
    public File[] listFiles()       获取指定目录下的所有文件或者文件夹的File数组

  • 文件名称过滤器
    public String[] list(FilenameFilter filter)
    public File[] listFiles(FileFilter filter)

I/O流

IO流用来处理设备之间的数据传输。
Java对数据的操作是通过流的方式。
流按流向分为两种:输入流,输出流。

流按操作类型分为两种:
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。

IO流常用父类
字节流的抽象父类:
InputStream
OutputStream
字符流的抽象父类:
Reader
Writer

  • FileInputStream
    read()一次读入一个字节
FileInputStream fileInputStream = new FileInputStream("rr.txt");
int read;
// 每次读一个字节  -1表示结束
while ((read = fileInputStream.read()) != -1){
       System.out.println(read);
}
fileInputStream.close(); // 关闭流释放资源
  • FileOutputStream
    write()一次写出一个字节
// 拷贝方式一 不推荐使用
// 如果t.txt文件不存在,就创建一个,否则,在将原文件清空
// FileOutputStream fileOutputStream = new FileOutputStream("/Users/zhangxiaohan/Desktop/t.txt");
// 如果t.txt文件不存在,就创建一个,否则,在原文件基础上增加内容
FileOutputStream fileOutputStream = new FileOutputStream("/Users/zhangxiaohan/Desktop/t.txt", true); 
fileOutputStream.write(65);
fileOutputStream.write(66);
fileOutputStream.write(67);
fileOutputStream.close();

字节数组拷贝之available()方法:
int read(byte[] b)  一次读取一个字节数组
write(byte[] b)    一次写出一个字节数组
available()      获取读的文件所有的字节个数
弊端:有可能会内存溢出 不推荐

// 拷贝方式二  不推荐使用
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
byte[] arr = new byte[fis.available()]; //根据文件大小做一个字节数组
fis.read(arr);          //将文件上的所有字节读取到数组中
fos.write(arr);         //将数组中的所有字节一次写到了文件上
fis.close();
fos.close();
// 第三种拷贝
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
int len;
byte[] arr = new byte[1024 * 8];        //自定义字节数组
        
while((len = fis.read(arr)) != -1) {
    fos.write(arr, 0, len);             //写出字节数组写出有效个字节个数
}
        
fis.close();
fos.close();
  • BufferedInputStream和BufferOutputStream拷贝
    BufferedInputStream
    BufferedInputStream内置了一个缓冲区(数组)
    从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个,程序再次读取时, 就不用找文件了, 直接从缓冲区中获取,直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个。

BufferedOutputStream
BufferedOutputStream也内置了一个缓冲区(数组)
程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中,直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。

FileInputStream fis = new FileInputStream("致青春.mp3");     //创建文件输入流对象,关联致青春.mp3
BufferedInputStream bis = new BufferedInputStream(fis);    
 //创建缓冲区对fis 包装
FileOutputStream fos = new FileOutputStream("copy.mp3");    //创建输出流对象,关联copy.mp3
BufferedOutputStream bos = new BufferedOutputStream(fos);   //创建缓冲区对fos 包装
        
int b;
while((b = bis.read()) != -1) {     
    bos.write(b);
}
        
bis.close();    //只关包装后的对象即可
bos.close();
  • flush和close方法的区别
    flush()方法:
    用来刷新缓冲区的,刷新后可以再次写出
    需要实时写出的用flush刷新
    close()方法:
    用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出 。
字符流

字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
读取的时候是按照字符的大小读取的,不会出现半个中文
写出的时候可以直接将字符串写出,不用转换为字节数组

  • FileReader
    FileReader类的read()方法可以按照字符大小读取。
FileReader fr = new FileReader("aaa.txt");
int ch;
while((ch = fr.read()) != -1) { //将读到的字符赋值给ch
    System.out.println((char)ch);   //将读到的字符强转后打印
}
        
fr.close();     
  • FileWriter
    FileWriter类的write()方法可以自动把字符转为字节写出.
FileWriter fw = new FileWriter("aaa.txt");
fw.write("aaa");
fw.close();
  • 字符流的拷贝
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
    
int ch;
while((ch = fr.read()) != -1) {
    fw.write(ch);
}
    
fr.close();
fw.close();
  • 带缓冲的字符流
    BufferedReader 的 read() 方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率。
    BufferedWriter 的 write() 方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率。
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));  //创建字符输入流对象,关联aaa.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));  //创建字符输出流对象,关联bbb.txt
        
int ch;             
while((ch = br.read()) != -1) {     //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch
    bw.write(ch);                   //write一次,是将数据装到字符数组,装满后再一起写出去
}
        
br.close();                         //关流
bw.close();  
  • readLine() 和 newLine() 方法
    BufferedReader 的readLine() 方法可以读取一行字符(不包含换行符号)
    BufferedWriter 的newLine() 可以输出一个跨平台的换行符号"\r\n"
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));
String line;
while((line = br.readLine()) != null) {
    bw.write(line);
    //bw.write("\r\n");                 //只支持windows系统
    bw.newLine();                       //跨平台的
}
        
br.close();
bw.close(); 
  • LineNumberReader
    LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
    调用getLineNumber()方法可以获取当前行号
    调用setLineNumber()方法可以设置当前行号
LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt"));
lnr.setLineNumber(100);             //设置行号,行号从101开始,默认从0开始
while((line = lnr.readLine()) != null) {
    System.out.println(lnr.getLineNumber() + ":" + line);//获取行号
}   
lnr.close(); 
  • 装饰设计模式
    好处:耦合性不强,被装饰的类的变化与装饰类的变化无关。
interface Coder {
    public void code();
}
        
class Student implements Coder {
        
    @Override
    public void code() {
        System.out.println("javase");
        System.out.println("javaweb");
    }   
}
        
class SuperStudent implements Coder {
    private Student s;                      //获取到被包装的类的引用
    public ItcastStudent(Student s) {       //通过构造函数创建对象的时候,传入被包装的对象
        this.s = s;
    }
    @Override
    public void code() {                    //对其原有功能进行升级
        s.code();
        System.out.println("数据库");
        System.out.println("ssh");
        System.out.println(".....");
    }   
} 
  • 使用指定的码表读写字符
    FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)。
    FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)。
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8"));  //高效的用指定的编码表读
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK"));  //高效的用指定的编码表写
int ch;
while((ch = br.read()) != -1) {
    bw.write(ch);
}       
br.close();
bw.close();
递归

方法自己调用自己。
构造方法不能使用递归调用。
好处:不用知道循环次数。
弊端:不能调研次数过多,容易导致栈内存溢出。

private static int test(int num){

   if (num > 1){
        return num * test(num - 1) ;
    }else {
        return num;
    }
}
  • 序列流(了解)
    序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.
SequenceInputStream(InputStream, InputStream)
FileInputStream fis1 = new FileInputStream("a.txt");            //创建输入流对象,关联a.txt
FileInputStream fis2 = new FileInputStream("b.txt");            //创建输入流对象,关联b.txt
SequenceInputStream sis = new SequenceInputStream(fis1, fis2);  //将两个流整合成一个流
FileOutputStream fos = new FileOutputStream("c.txt");           //创建输出流对象,关联c.txt
            
int b;
while((b = sis.read()) != -1) {                                 //用整合后的读
    fos.write(b);                                               //写到指定文件上
}           
sis.close();
fos.close(); 
  • 序列流整合多个 (了解)
SequenceInputStream(Enumeration)
FileInputStream fis1 = new FileInputStream("a.txt");    //创建输入流对象,关联a.txt
FileInputStream fis2 = new FileInputStream("b.txt");    //创建输入流对象,关联b.txt
FileInputStream fis3 = new FileInputStream("c.txt");    //创建输入流对象,关联c.txt
Vector<InputStream> v = new Vector<>();                 //创建vector集合对象
v.add(fis1);                                            //将流对象添加
v.add(fis2);
v.add(fis3);
Enumeration<InputStream> en = v.elements();             //获取枚举引用
SequenceInputStream sis = new SequenceInputStream(en);  //传递给SequenceInputStream构造
FileOutputStream fos = new FileOutputStream("d.txt");
int b;
while((b = sis.read()) != -1) {
    fos.write(b);
}
sis.close();
fos.close();
  • 内存输出流
    该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据。
    创建对象: new ByteArrayOutputStream()
    写出数据: write(int), write(byte[])
    获取数据: toByteArray()
FileInputStream fis = new FileInputStream("a.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
while((b = fis.read()) != -1) {
    baos.write(b);
}
            
//byte[] newArr = baos.toByteArray();               //将内存缓冲区中所有的字节存储在newArr中
//System.out.println(new String(newArr));
System.out.println(baos);
fis.close();
  • 对象操作流ObjecOutputStream)(了解)
    该流可以将一个对象写出, 或者读取一个对象到程序中. 也就是执行了序列化和反序列化的操作.
// Person
public class Person implements Serializable {
   
}


public class Demo3_ObjectOutputStream {
    /**
    * @param args
    * @throws IOException 
    * 将对象写出,序列化
    */
     public static void main(String[] args) throws IOException {
        Person p1 = new Person("张三", 23);
        Person p2 = new Person("李四", 24);
    //      FileOutputStream fos = new FileOutputStream("e.txt");
    //      fos.write(p1);
    //      FileWriter fw = new FileWriter("e.txt");
    //      fw.write(p1);
    //无论是字节输出流,还是字符输出流都不能直接写出对象
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));//创建对象输出流
    oos.writeObject(p1);
    oos.writeObject(p2);
    oos.close();
    }           
}
  • 对象操作流ObjectInputStream)(了解)
public class Demo3_ObjectInputStream {
    /**
    * @param args
    * @throws IOException 
    * @throws ClassNotFoundException 
    * @throws FileNotFoundException 
    * 读取对象,反序列化
    */
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
    Person p1 = (Person) ois.readObject();
    Person p2 = (Person) ois.readObject();
    System.out.println(p1);
    System.out.println(p2);
    ois.close();
    }           
}
  • 对象操作流优化(了解)
// 将对象存储在集合中写出
Person p1 = new Person("张三", 23);
Person p2 = new Person("李四", 24);
Person p3 = new Person("马哥", 18);
Person p4 = new Person("辉哥", 20);
    
ArrayList<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
    
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f.txt"));
oos.writeObject(list);                                  //写出集合对象
    
oos.close();
// 读取到的是一个集合对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f.txt"));
ArrayList<Person> list = (ArrayList<Person>)ois.readObject();   //泛型在运行期会被擦除,索引运行期相当于没有泛型
                                                                            //想去掉黄色可以加注解                    @SuppressWarnings("unchecked")
for (Person person : list) {
    System.out.println(person);
}
        
ois.close();
  • 打印流的概述和特点
    该流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式。
    System.out就是一个PrintStream, 其默认向控制台输出信息。
PrintStream ps = System.out;
ps.println(97);                 //其实底层用的是Integer.toString(x),将x转换为数字字符串打印
ps.println("xxx");
ps.println(new Person("张三", 23));
Person p = null;
ps.println(p);                  //如果是null,就返回null,如果不是null,就调用对象的toString()
  • 标准输入输出流概述和输出语句
    标准输入输出流:
    System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据。
    System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据。
    修改标准输入输出流(了解):
    修改输入流: System.setIn(InputStream)
    修改输出流: System.setOut(PrintStream)
System.setIn(new FileInputStream("a.txt"));             //修改标准输入流
System.setOut(new PrintStream("b.txt"));                //修改标准输出流
            
InputStream in = System.in;                             //获取标准输入流
PrintStream ps = System.out;                            //获取标准输出流
int b;
while((b = in.read()) != -1) {                          //从a.txt上读取数据
ps.write(b);                                        //将数据写到b.txt上
}
            
in.close();
ps.close();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容