反射机制java.lang.reflect.*

一、什么是反射:

                  见网盘--javaAndWeb---202x年--java--java基础讲解项目--项目‘ReflextTest’

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

二、反射的原理:

下图是类的正常加载过程、反射原理与class对象:
Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。


image.png

三、反射的优缺点:

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

2、缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

四、反射的用途:

1、反编译:.class-->.java

2、通过反射机制访问java对象的属性,方法,构造方法等

3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
例如,在使用Strut2框架的开发过程中,我们一般会在struts.xml里去配置Action,比如

<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">   
    <result>/shop/shop-index.jsp</result>           
    <result name="error">login.jsp</result>       
</action>

比如我们请求login.action时,那么StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,从action中查找出name为login的Action,并根据class属性创建SimpleLoginAction实例,并用Invoke方法来调用execute方法,这个过程离不开反射。配置文件与Action建立了一种映射关系,当View层发出请求时,请求会被StrutsPrepareAndExecuteFilter拦截,然后StrutsPrepareAndExecuteFilter会去动态地创建Action实例。
比如,加载数据库驱动的,用到的也是反射。
Class.forName("com.mysql.jdbc.Driver"); // 动态加载mysql驱动

五、反射机制常用的类:

Java.lang.Class;
java.lang.reflect.Type;
Java.lang.reflect.Constructor;

Java.lang.reflect.Field;

Java.lang.reflect.Method;

Java.lang.reflect.Modifier;

六、反射的基本使用:

1、获得Class:主要有三种方法:

(1)Object-->getClass stu1.getClass()
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性,如:Student.class
(3)通过class类的静态方法:Class.forName(String className)(最常用)

Class类的主要方法有:

  .getName()  返回原类的名字。

3、创建实例:通过反射来生成对象主要有两种方法:

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

Class<?> classStu = Student.class;
Object stu = classStu.newInstance()

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。

//获取Student的Class对象
Class<?> struClass= Student.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=stuClass.getConstructor(Student.class);
//根据构造器创建实例:
Object obj = constructor.newInstance(“熊少文”,2,"男");

例:Student.java fanshe.java
Student.java

public class Student {
       public Sting address;
    private String name;
    private int age;
    private String sex;
        public List<String> list;   //泛型类型为String ,List是一个泛型类
       public Map.Entry<Integer,Long> map;     //泛型类型有两个int,long
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }
    
    

}

Fanshe.java 该方法有main函数,可执行看效果。

public class Fanshe {
    public static void main(String[] args) {
        //第一种方式获取Class对象  
        Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
        Class stuClass = stu1.getClass();//获取Class对象
        System.out.println(stuClass.getName());
           //第二种方式获取Class对象
        Class stuClass2 = Student.class;
        System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
        //第三种方式获取Class对象
        try {
            Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

一般的,我们使用instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断时候为某个类的实例,他是一个native方法。

public native boolean isInstance(Object obj);

4、通过反射获取构造方法并使用:

(1)批量获取的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

(2)单个获取的方法,并调用:
public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

(3) 调用构造方法:

Constructor-->newInstance(Object... initargs)

newInstance是 Constructor类的方法(管理构造函数的类)

api的解释为:newInstance(Object... initargs) ,使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象,并为之调用。
例子:Student2.java定义了6个公有,私有,保护的构造函数,Constructors.java中反射获取,并创建对象。
见网盘--javaAndWeb---202x年--java--java基础讲解项目--项目‘ReflextTest’

5.获取字段。

获取成员变量并调用:

  • 1.批量的
  •  1).Field[] getFields():获取所有的"公有字段"
    
  •  2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
    
  • 2.获取单个的:
  •  1).public Field getField(String fieldName):获取某个"公有的"字段;
    
  •  2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
    
  • 设置字段的值:
  •  Field --> public void set(Object obj,Object value):
    
  •              参数说明:
    
  •              1.obj:要设置的字段所在的对象;
    
  •              2.value:要为字段设置的值;
    

见百度网盘--javaAndWeb---202x年--java---项目‘ReflextTest’
getField.java

6.获取成员方法

/*
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
):
*/

、反射方法的其他使用--通过反射运行配置文件内容:

我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
我们只需要将新类发送给客户端,并修改配置文件即可

1.配置文件以txt文件为例子:

className = bean.Student
methodName = show

show为Student类中的一个方法
测试类:getFileData.java,该类用到了文件流FileReader类
具体代码见项目'ReflectTest'

反射方法的其他使用--通过反射越过泛型检查:

------泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的
通过反射越过泛型检查

  • 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
public class Demo {
    public static void main(String[] args) throws Exception{
        ArrayList<String> strList = new ArrayList<>();
        strList.add("aaa");
        strList.add("bbb");
        
    //  strList.add(100);
        //获取ArrayList的Class对象,反向的调用add()方法,添加数据
        Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
        //获取add()方法
        Method m = listClass.getMethod("add", Object.class);
        //调用add()方法
        m.invoke(strList, 100);
        
        //遍历集合
        for(Object obj : strList){
            System.out.println(obj);
        }
    }

java.lang.reflect.Type与java.lang.reflect.ParameterizedType;

Type是Java反射中框架中的一个核心接口,用于获取类型信息,它的定义很简单

package java.lang.reflect;
    
    public interface Type {
    
        default String getTypeName() {
            return this.toString();
        }
    }

Type则是一个非常重要的接口,我们常见的Class类就实现了Type接口,Type接口的另一个重要实现类ParameterizedType,Class类只保存了当前类的基本类型信息。而ParameterizedType则保存了泛型,外部类等额外类型信息,Type没有这个能力。

测试Type,ParameterizedType:

准备:

1.接口StudentInterface ,让Student实现它,这里只做测试用,没有定义接口方法。

package bean;
public interface StudentInterface {
}

2.Student.java

3.测试类:

3.1TypeTest.java

public class TypeTest {
    public static void main(String args[]) {
        Student student = new Student("熊少文",29,"男");
    Class<? extends Student> clazz = student.getClass();

    System.out.println("-----------------------------获取接口类型信息-----------------------------------------");
    Type[] genericInterfaces = clazz.getGenericInterfaces();
    for (Type genericInterface : genericInterfaces)
        System.out.println("Generic Interface:" + genericInterface.getTypeName());
    System.out.println("-----------------------------打印接口Type的具体类型-----------------------------------------");

    for (Type genericInterface : genericInterfaces)
        System.out.println("Type Class:" + genericInterface.getClass().getTypeName());
    

    
    System.out.println("-----------------------------   //获取父类类型信息-----------------------------------------");
    Type genericSuperclass = clazz.getGenericSuperclass();
    System.out.println("Generic Superclass:" + genericSuperclass.getTypeName());
    
    //Type来获取字段基本数据类型。
    Field[] fields=clazz.getDeclaredFields();
    System.out.println("-----------------------获取字段的基本类型address,name,age,sex,list,map-------------");
    for(Field field:fields) {
        Class<?> type =field.getType();
        System.out.println("Basic Type: " + type.getName());
    }
    
    //获取字段的泛型类型
    System.out.println("-----------------------获取字段的获取字段的泛型类型address,name,age,sex,list,map-------------");
    for (Field field : fields) {
        Type genericType = field.getGenericType();
        System.out.println("Generic Type: " + genericType.getTypeName());
    }
     //获取Type的实际类型
    System.out.println("-----------------------获取字段的的实际类型address,name,age,sex,list,map------------");
    for (Field field : fields) {
        Type genericType = field.getGenericType();
        System.out.println("Type Class: " + genericType.getClass().getName());
    }

    }
}

测试结果:


image.png

3.2 ParameterizedType测试例子:

例1:ParameterizedTypeTest.java ,只对泛型类的参数字段进行测试,本例为Map.Entry<Integer,Long> map

public class ParameterizedTypeTest {
    public static void main(String args[]) throws Exception{
        Field field = Student.class.getDeclaredField("map");//若此处为"name/address/age",则发出java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType异常。
        ParameterizedType type = (ParameterizedType) field.getGenericType();
    
        System.out.println("-----------------------获取字段的基本类型Map.Entry<Integer,Long> map-------------");
        Type rawType = type.getRawType();
        System.out.println("Raw Type: " + rawType.getTypeName());

   
        System.out.println("-----------------------获取字段泛型参数的类型Map.Entry<Integer,Long> map------------");
        Type[] argumentTypes = type.getActualTypeArguments();
        for (Type argumentType : argumentTypes)
            System.out.println("Argument Type: " + argumentType.getTypeName());

     
        System.out.println("-----------------------如果字段类型是个内部类,则获取字段所在外部类的类型Entry是Map的内部类------------");
        Type ownerType = type.getOwnerType();
        System.out.println("Owner Type: " + ownerType.getTypeName());

    }
}
测试结果:
-----------------------获取字段的基本类型Map.Entry<Integer,Long> map-------------
Raw Type: java.util.Map$Entry
-----------------------获取字段泛型参数的类型Map.Entry<Integer,Long> map------------
Argument Type: java.lang.Integer
Argument Type: java.lang.Long
-----------------------如果字段类型是个内部类,则获取字段所在外部类的类型Entry是Map的内部类------------
Owner Type: java.util.Map

例2:见百度网盘--javaANDweb---202x年---可运行的web项目---mvcproject---->BaseDao.java<--->UserDaoImpl----UserDaoImplTest,该功能是查询数据库表。

invoke,就是通过函数名反射调用相应的函数。

以下代码简单地介绍了java反射中invoke方法

import java.lang.reflect.Method;

public class InvokeMethods {
    public static void main(String[] args) {
        Employee emp = new Employee();
        Class cl = emp.getClass();// 是Class,而不是class
        try {
            
            Method sAge = cl.getMethod("setAge", new Class[] { int.class });
            Method gAge = cl.getMethod("getAge", null);
            Method pName = cl.getMethod("printName", new Class[] { String.class });
            Object[] args1 = { new Integer(25) };
            // invoke方法中,第二个参数为参数列表,该参数列表是一个object[]数组
            // emp为隐式参数该方法不是静态方法必须指定
            sAge.invoke(emp, args1);// 通过setter方法赋值
            Integer AGE = (Integer) gAge.invoke(emp, null);// 通过getter方法返回值
            int age = AGE.intValue();// Integer转换成int
            System.out.println("The Employee Age is: " + age);
            Object[] args3 = { new String("Jack") };
            pName.invoke(emp, args3);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.exit(0);
    }
}

class Employee {

    private int age;
    private String name;
    // 定义一个员工类
    public Employee() {
        age = 0;
        name = null;
    }

    // 将要被调用的方法
    public void setAge(int a) {
        age = a;
    }

    // 将要被调用的方法
    public int getAge() {
        return age;
    }

    // 将要被调用的方法
    public void printName(String n) {
        name = n;
        System.out.println("The Employee Name is: " + name);
    }

}

反射常用的地方(本人)

  1. 获取Class对象,常用于项目的dao(mvc和hibernate)或service(mybatis)层。
public abstract class BaseServiceImpl<T> implements BaseService<T>{
public Class<?> clazz;
 public BaseServiceImpl() {
       Type type=this.getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType)type;
        Type[] types = pt.getActualTypeArguments();
         clazz = (Class<?>)types[0];
         
    }
@Override
    public T select(int id) {
        Map<Object,Object> rsMap =getBaseDao().select(tableName, id);
        //我们需要把Map转型T--Map里有很多个字段名:字段值的键值对
        @SuppressWarnings("unchecked")
        T t =(T) MapToEntityTool.map2entity(rsMap, clazz);
        return t;
    }
}
  1. 把mybatis查询获取的Map对象转化为实体对象。
    ---利用动态获取对象反射过来的字段,方法来处理
public class MapToEntityTool {
    /**
     * 缓存类的属性信息
     */
    private static Map<String, EntityCacheItem> convertItemCache = new HashMap<>();

    /**
     * map to entry 的泛型方法 功能:把map对象转化为entity实体对象(entityClass对应的)
     * 原理:首先获取参数entityClass的所有字段,与方法,所有字段名放在fieldNameList集合中,所有方法放在Map集合中
     * 其次:获取mybatis传递过来的Map集合中的所有键值(键名与entityClass传递过来的字段名一样,不一样不做处理) 所有发生的异常,return
     * null;这样不留垃圾
     * 
     * @return
     */
    public static <T> T map2entity(Map<Object, Object> map, Class<T> entityClass) {
        EntityCacheItem entityCacheItem = convertItemCache.get(entityClass.getName());
        if(entityCacheItem ==null) {
            entityCacheItem = EntityCacheItem.createEntityCacheItem(entityClass);
            convertItemCache.put(entityClass.getName(),entityCacheItem);
        }
        //entityClass拿到参数传来的对象的属性名秒称(List)集合
        List<String> fieldNameList = entityCacheItem.getFieldNameList();
        //通过entityClass参数,获取类型里边的set方法的map集合
        Map<String,Method> setMethodMap=entityCacheItem.getSetMethodMap();
        System.out.println("数据库中查询的结果集:"+map);
        System.out.println("实体类对象中的属性名字:"+fieldNameList);
        Map<Object, Object> targetMap = new HashMap<>();
        String key;
        String key1;
        String key2;
        for(Map.Entry<Object, Object> entry:map.entrySet()) {
            key = entry.getKey().toString();
            while(key.contains("_")) {
                //add_date  a_b_c_d
                key1 = key.substring(0,key.indexOf("_")); //add
                key2 = key.substring(key.indexOf("_")+1); //date
                key = key1 + key2.substring(0,1).toUpperCase()+key2.substring(1); //addDate
            }
            targetMap.put(key, entry.getValue());
        }
        T entity = null;
        try {
            entity = entityClass.newInstance(); // 反射方式创建一个对象(实例entity),此时它还是空的,下面要注入内容
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } // 通过反射方式,获取这个类型的对象

        Object mapFieldValue = null;
        Method setMethod1 = null;
        Class<?>[] parameterTypes = null;
        for (String fieldName1 : fieldNameList) { // 循环拿到Map集合(调用时参数传过来的map)中的键:循环把map所有的键值注入到实体中(如:User会对应很多setXXX设置字段的方法)
            mapFieldValue = targetMap.get(fieldName1);
            if (mapFieldValue == null)
                continue; // 如果键不存在,就没有做本次循环的必要,继续进行一下次循环
            setMethod1 = setMethodMap.get(fieldName1); // 如果方法不存在,也没有。。。。
            if (setMethod1 == null)
                continue;
            parameterTypes = setMethod1.getParameterTypes(); // 获取方法中的所有类型的参数数组
            if (parameterTypes == null || parameterTypes.length > 1) { // 如果setxxx方法中参数没有或大于1个时也不做本次循环,因为setUsername(xx)中只有一个参数
                continue;
            }
            if (parameterTypes[0].isAssignableFrom(mapFieldValue.getClass())) {
                // 若map传来的属性值的类型和set方法中参数的类型一致
                // 如:setUsername(Object)的object,与mapFieldValue的类型是否一致
                try {
                    setMethod1.invoke(entity, mapFieldValue);// 调用是对象的set方法把属性值注入到实体里(entity--不是键值对的)
                                                                // 如:setUsername("xiongshaowen");
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            } else {
                //这里输出让我们看到,封装时封装的数据类型,基本数据类型为它的包装类,特别是模型类中int id;要定为Integer id;
                System.out.println(
                        "不同类型:set方法中的参数类型:" + parameterTypes[0] + "======数据库中查询的结果集中数据类型:" + mapFieldValue.getClass());
            }
        }

        return entity;
    }
    // 定义一个缓存(静态存储)内部类,实例化后有把传来的类封装字段和方法到List,Map集合中的功能。把map转换实例时,会非常频繁的造访下面代码,这样我们定义一个缓存减少系统消耗
        static class EntityCacheItem {
            private EntityCacheItem() {
            }; // 私有化构造(无参)方法,让该类不可在外部实例化,要通过内部方法来创建实例对象

            private List<String> fieldNameList = new ArrayList<String>();
            private Map<String, Method> setMethodMap = new HashMap<>();

            public List<String> getFieldNameList() {
                return fieldNameList;
            }

            public Map<String, Method> getSetMethodMap() {
                return setMethodMap;
            }

            public void parseEntity(Class<?> entityClass) {
                Field[] fields = entityClass.getDeclaredFields(); // 获取所有字段,不管私有还是公有
                String fieldName;
                String setMethodName;
                Method setMethod = null;
                setMethodMap = new HashMap<>();
                for (Field field : fields) {
                    field.setAccessible(true); // 获取可修改字段的权限
                    fieldName = field.getName(); // 属性字段的名字
                    fieldNameList.add(fieldName);
                    setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // 设置类似javabean方法,如:setUsername(Username)
                    try {
                        setMethod = entityClass.getDeclaredMethod(setMethodName, field.getType()); // 拿到实体对应的类中的setUsername(Object
                                                                                                    // param)方法
                    } catch (Exception e) {
                        e.printStackTrace();

                    }
                    setMethodMap.put(fieldName, setMethod); // 如:Map集合中的一个元素{username,setUsername(Object param)}
                }
            }

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

推荐阅读更多精彩内容