java----设计模式--创建型模式(GOF23)

学习网址:https://refactoringguru.cn/design-patterns

 单例设计模式:

1、饿汉式(线程安全,调用效率高,不能延时加载)

public class Demo {
    public static void main(String[] args){
    	Singleton1 singleton1 = Singleton1.getSingleton1();  //通过类调用接口
    	singleton1.print();
    }
}
class Singleton1{
	private Singleton1(){}; //将构造方法私有化,外部不能new对象
	private static Singleton1 singlenton1 = new Singleton1();  //使用静态存储方式,只存一个对象,当类被加载时,singleton1对象就被创建,一直放在内存中,直到程序结束;
	public static Singleton1 getSingleton1(){                  //将getSingleton1()方法设置为静态方法,让类可以调用,返回实例
		return singlenton1;
	}
	public void print(){
		System.out.println("测试");
	}
	
}

2、懒汉式(线程安全,调用效率没有饿汉式高,但是可以延时加载,比如我调用该类中的其他静态字段,只要没有调用这个获取示例对象的方法,该对象就不会被创建

已经优化过DCL(double check lock)(https://blog.csdn.net/qiyei2009/article/details/71813069);

import java.io.Serializable;

public class Demo {
    public static void main(String[] args){
        Singleton1 singleton1 = Singleton1.getSingleton1();  //通过类调用接口
        singleton1.print();
    }
}
//让单例可以实现被序列化;
class Singleton1 implements Serializable {
    //volatile 保证变量的一直性,没有volatile线程可能会访问到一个没有初始化的对象
    private static volatile Singleton1 singleton1;  //volatile必须使用,防止指令重排(不然可能会 出现问题),double check
    //为了防止通过反射强制访问私有方法;,如果singleton1为空的话,就可以实例,如果程序已经运行了,不为空,就设置报错;
    private Singleton1(){
        if (singleton1!=null){
            throw new RuntimeException("此类已经被实例化;单例模式");
        }
    }; //将构造方法私有化,外部不能new对象

    public static Singleton1 getSingleton1(){
        if(singleton1==null){ //解决多线程性能问题,
            synchronized (Singleton1.class){
                if(singleton1==null){ //若果没有这个判断,当程序刚刚启动,如果两个多线程同时进入第一个if判断,那么之后,就会生成两个多个实例对象了
                    singleton1 = new Singleton1(); //创建实例,在getSingleton()方法调用时创建,程序结束后释放,多线程存在问题
                }
            }
        }
        return singleton1;
    }
    public void print(){
        System.out.println("测试");
    }
}

3、静态内部类实现(懒加载。线程安全)

class Singleton1 implements Serializable {
    public static class SingletonClassInstance{
        public static final Singleton1 singleton1 = new Singleton1();
    }
    public static Singleton1 getSingleton1(){
        //返回静态内部类的属性
        return SingletonClassInstance.singleton1;
    }
}
public class Demo {
    public static void main(String[] args){
        Singleton1 singleton1 = Singleton1.getSingleton1();
        Singleton1 singleton2 = Singleton1.getSingleton1();
        System.out.println(singleton1==singleton2);
        //当然如果将静态内部类和静态内部类的方法访问修饰符都设置public我们也可以直接通过.来访问
        Singleton1 singleton3 = Singleton1.SingletonClassInstance.singleton1;
        System.out.println(singleton1==singleton3);
    }
}

4、使用枚举实现单利(线程安全,不能延时加载。可以避免反射和反序列的漏洞)

enum  EnumSingleton {
    INSTANCE;
    public void test(){
        System.out.println(this);
    };
}
public class Demo {
    public static void main(String[] args){
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        System.out.println(instance1==instance2);
        instance1.test();
    }
}

补充1:我们还可以通过单例缓存池来管理单例(比如spring中的singletonObjects,使用Map做数据结果,首先判断map有有没有数据没有new object(),将这个对象返回,并将对象添加到Map中,所以第二次判断map是否有值的时候就直接返回该对象了

补充2:通过使用构造方法私有化,加静态方法代替单例,例如工具类中的Math

不需要创建对象,静态方法一直加载在内存中,直到程序退出

public class Demo {
    public static void main(String[] args){
    	Singleton1.fun1();       //直接通过类调用方法
    }
}
class Singleton1{
	private Singleton1(){}; //将构造方法私有化,外部不能new对象
	public static void fun1(){
		System.out.println("方法一");
	}
	public static void fun2(){
		System.out.println("方法二");
	}
	public static void fun3(){
		System.out.println("方法三");
	}
}

单例和使用静态方法代替单例对比

1、使用单例,当执行方法时,如果有定义的变量,方法进栈,执行完毕后,出栈,变量销毁,优点:省内存,缺点:需要反复的调用时,耗时

2、静态方法代替单例:优点:调用的时间快,缺点:一直加载内存中,不能被销毁,比较占内存。

 

使用反射和反序列化破解单利

对于反射就不举例了,防止反射的方法就是在私有构造方法调用的时候抛出异常

对于下面案例中利用反序列化来破解单利,防止反序列的方式加 readResolve()方法;

class Singleton implements Serializable{
    public static Singleton singleton = new Singleton();
    private Singleton(){}

    //反序列化时,如果定义了readResolve(),则直接返回此方法指定的对象。而不需要单独再创建新对象!
    private Object readResolve(){
        return singleton;
    }
}

public class Demo {
    public static void main(String[] args){
        Singleton singleton = Singleton.singleton;
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("singleton.obj");
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(singleton);

            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("singleton.obj"));
            Singleton object = (Singleton) objectInputStream.readObject();
            //如果没有加这个readResolve方法,结果为false
            System.out.println(object==singleton);

        } catch (IOException e) {

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

 

 工厂设计模式:

所谓工厂设计模式,就是用工厂方法替代new操作,实现创建者和调用者分离

  简单工厂模式

    问题在于假如新建了一个类,如果需要该工厂可以创建该类,那么就需要对之前的工厂进行修改,就会违反了面向对象设计原则(对修改关闭)

public class Demo {
    public static void main(String[] args){    	
    	Product product = ProductFactory.getProduct("phone");
    	product.work();
    }
}

class ProductFactory{
	static Product getProduct(String name){ //也可以直接为每一个javabean设计一个方法,返回该对象;
		if("phone".equals(name)){
			return new Phone();
		}else if("compulter".equals(name)){
			return new Computer();
		}
		return null;
	}
}

interface Product{
	void work();
}
class Phone implements Product{
	public void work(){
		System.out.println("手机开始工作");
	}
}
class Computer implements Product{
	public void work(){
		System.out.println("手机开始工作");
	}
}

  工厂方法模式(工厂bean)

    如果新建了一个类,我们只需要重新写一个工厂,这样对其他的工厂就没有修改了。弊端:需要额外创建许多类(实际使用并不多)

public class Demo {
    public static void main(String[] args){
        Product produce = new PhoneFactory().createProduce();
        produce.work();
    }
}
interface ProduceFactoryInterface{
    Product createProduce();
}
class PhoneFactory implements ProduceFactoryInterface{
    @Override
    public Product  createProduce() {
        return new Phone();
    }
}
class ComputerFactory implements ProduceFactoryInterface{
    @Override
    public Product createProduce() {
        return new Computer();
    }
}
interface Product{
    void work();
}
class Phone implements Product{
    public void work(){
        System.out.println("手机开始工作");
    }
}
class Computer implements Product{
    public void work(){
        System.out.println("手机开始工作");
    }
}

  抽象工厂模式

    假如我们有不同的javabean接口,这些接口下又有不同的实现,那么简单工厂模式和方法工厂模式就无能为力了

    用来生产不同产品族的全部产品

public interface FurnitureInterface {
    public void run();
}

class refrigeratorFurniture implements FurnitureInterface{
    @Override
    public void run() {
        System.out.println("冰箱已经购买");
    }
}

class TvFurniture implements FurnitureInterface{
    @Override
    public void run() {
        System.out.println("电视已经购买");
    }
}
public interface ToyInterface {
    void run();
}
class BearToy implements ToyInterface{
    @Override
    public void run() {
        System.out.println("小熊已经购买");
    }
}
class DogToy implements ToyInterface{
    @Override
    public void run() {
        System.out.println("小狗已经购买");
    }
}
public interface SupermarketInterface {
    ToyInterface buyToy();
    FurnitureInterface buyFurniture();
}

class Supermarket1 implements SupermarketInterface{
    @Override
    public ToyInterface buyToy() {
        return new BearToy();
    }
    @Override
    public FurnitureInterface buyFurniture() {
        return new TvFurniture();
    }
}
class Supermarket2 implements SupermarketInterface{
    @Override
    public ToyInterface buyToy() {
        return new DogToy();
    }
    @Override
    public FurnitureInterface buyFurniture() {
        return new refrigeratorFurniture(); //可以任意定制相关产品
    }
}

  生产一系列的javabean的工厂,在工厂(超市)中可以任意定制产品

public class Demo {
    public static void main(String[] args) {
        //选择超市1
        SupermarketInterface supermarketInterface = new Supermarket1();
        supermarketInterface.buyFurniture().run();
        supermarketInterface.buyToy().run();
    }
}

 

 建造者设计模式:

  场景:我们需要构建的对象非常复杂,比如构建一个房屋,需要非常多的组件(灯泡,桌子等)

  建造者模式本质:分离对象的子组件单独构造(由Build来负责)和装配(由Director来负责),从而构建复杂的对象

  Build类中的每一个build 方法都是为了构建一个小组件;

  Director类中每一个director方法,都是为了组装成不同的对象;

  由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。

  示例

    小组件

public interface Chair {
}
class BigChair implements Chair{
}
public interface Light {
}
class BigLight implements Light{
}

    需要构建的对象

public class Home {
    public Chair chair;
    public Light light;
}

    build

public interface HomeBuilder {
    Light builderlight();
    Chair builderchair();

}

class HomeBuilderImpl implements HomeBuilder{
    @Override
    public Light builderlight() {
        System.out.println("构建一个大的灯泡");
        return new BigLight();
    }
    @Override
    public Chair builderchair() {
        System.out.println("构建一个大的椅子");
        return new BigChair();
    }
}

    director

public interface HomeDirector {
    Home directorHome();
}
class HomeDirectorImpl implements HomeDirector{
    @Override
    public Home directorHome() {
        HomeBuilder homeBuilder = new HomeBuilderImpl();
        Light light = homeBuilder.builderlight();
        Chair chair = homeBuilder.builderchair();
        Home home = new Home();
        home.chair = chair;
        home.light = light;
        return home;
    }
}

    demo

public class Demo {
    public static void main(String[] args) {
        HomeDirectorImpl homeDirector = new HomeDirectorImpl();
        Home home = homeDirector.directorHome();
        System.out.println(home);
    }
}

  

 原型模式:

prototype

  java克隆技术:以某一个对象为原型,复制出新的对象,显然,新的对象具备原型对象的特点,优势:效率高,避免了重新执行构造过程步骤。

  克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后再修改克隆对象的值。

原型模式实现:
  -Cloneable接口和重新Object的clone方法

  -Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了clone()方法替我们做了绝大部分事情。

  浅copy

    浅copy中的引用类型copy的也是引用类型的内存地址。

    对于String引用类型,由于String类型不可以修改的,所以改变值的时候会改变变量名的内存地址;

    对于其他对象引用类型,改变对象的值,并不会改变原来的指向这个对象的内存地址。所以dog1和dog2共享A,任何一个对象对A修改都会影响其他对象。

public class Demo {
    public static void main(String[] args) {
        Dog dog1 = new Dog("花花",new A("a"));
        try {
            Dog dog2 = (Dog) dog1.clone();
            //dog2和dog1对象公用了一个A对象
            dog2.a.name="b";
            System.out.println(dog1.a.name);
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class A{
    public String name;
    public A(String name) {
        this.name = name;
    }
}
class Dog extends Object implements Cloneable{
    public String name;
    public A a;
    public Dog(String name, A a) {
        this.name = name;
        this.a = a;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

  深copy(方式1)

public class Demo {
    public static void main(String[] args) {
        Dog dog1 = new Dog("花花",new A("a"));
        try {
            Dog dog2 = (Dog) dog1.clone();
            dog2.a.name="b";
            System.out.println(dog1.a.name); //结果是a
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class A implements Cloneable{
    public String name;
    public A(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Dog extends Object implements Cloneable{
    public String name;
    public A a;
    public Dog(String name, A a) {
        this.name = name;
        this.a = a;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object clone = super.clone();
        Dog clone1 = (Dog) clone;
        clone1.a = (A) this.a.clone();
        return clone;
    }
}

  深copy(方式2,使用序列化的方式)

public class Demo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog1 = new Dog("花花",new A("a"));
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(dog1);
        byte[] bytes = byteArrayOutputStream.toByteArray();
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Dog dog2 = (Dog) objectInputStream.readObject();

        dog1.a.name = "b";
        System.out.println(dog2.a.name); //结果是a
    }
}

class A implements Serializable{
    public String name;
    public A(String name) {
        this.name = name;
    }
}
class Dog implements Serializable{
    public String name;
    public A a;
    public Dog(String name, A a) {
        this.name = name;
        this.a = a;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object clone = super.clone();
        return clone;
    }
}

 

 模板方法设计模式:

1、定义一个操作算法的骨架(每一个对象都必须实现的),而将一些可变部分的实现(可以根据自己的需求自定义)延迟到子类中

2、模板方法模式使子类可以不改变一个算法的结构即可重新定义该算法的某些特征

子类调用父类的方法,父类在调用子类的方法;(本质上在调用子类方法之前,先执行父类的方法(所有的子类都有一样的需求))

/**
 	需求在进行数据的增删改查的时候,每次都要判断用户是不是为管理员,如果为管理员,就可以执行操作
 	例如学校老师(管理员)对学生的管理。企业管理员对员工的管理,
*/
public class Demo {
    public static void main(String[] args){    	
    	TeacherandStudent user = new TeacherandStudent();
    	user.action("Admin","add");   //用户执行命令
    	user.action("Test","add");
    }
}

abstract class Manage{    //所有的对象都需要执行的操作
	public void action(String user,String method){
		if("Admin".equals(user)){
			excute(method);
			return;
		}else{
			System.out.print("你没有权限,请您联系管理员");
			return;
		}
	}
	abstract void excute(String method);
}
class TeacherandStudent extends Manage{        //老师和学生的页面都是一样的,只是老师这个用户有对学生进行修改的权限
	public void excute(String method){     //老师和学生的业务逻辑不需要判断用户有没有添加和删除的权限了
		if("add".equals(method)){
			System.out.println("正在执行添加操作");
		}else if("delete".equals(method)){
			System.out.println("正在执行删除操作");
		}
	}
}
class BossManage extends Manage{ 
	public void excute(String method){
		
	}
}

另一种思路比较(静态代理模式),调用代理的方法,代理中调用需要实现类的方法

public class Demo {
    public static void main(String[] args) {
        Father father = new Father();
        father.excute(new Son(),"add");
    }
}

interface action{
    void action(String option);
}
class Father{
    public void excute(action obj,String option){
        //判断该对象是否有权限(省略)
        obj.action(option);
    }
}
class Son implements action{
    public void action(String option) {
        if ("add".equals(option)){
            System.out.println("添加数据成功");
            return;
        }if ("delete".equals(option)){
            System.out.println("删除数据成功");
            return;
        }
    }
}

  

 策略设计模式:

功能:对if else进行解耦

接口应用:

定义一系列算法,并将每一种算法封装起来可以相互替换使用;

策略模式让算法独立于使用它的客户,可以独立变化;

把可变的行为抽象出来,定义一系列算法,进行封装,让代码方便扩展,修改,易于维护

实例1:

/**
 	不同的文件保存到不同的地方
*/
public class Demo {
    public static void main(String[] args){    	
    	User save = new User();
    	save.setISave(new FileSava());
    	save.loadfile("数据");
    }
}

interface ISave{
	public abstract void save();
}

class FileSava implements ISave{
	public void save(){
		System.out.println("文件保存到本地完毕");
	};
}
class NetSava implements ISave{
	public void save(){
		System.out.println("文件保存到网络完毕");
	};
}

abstract class BaseSava{
	private ISave isave;   //使用组合的方式,将BaseSava和ISava组合起来
	public void setISave(ISave isave){  
		this.isave = isave;
	}
	public void loadfile(String data){
		System.out.println("文件合法.....");
		this.isave.save();
	}
}
class User extends BaseSava{
	
}

实例2

  在项目中,有一些需要过期处理的东西,比如,订单未支付过期处理,发红包未领取自动退还,代金券未支付过期处理等。在存入redis的时候,会通过key来区分是什么业务,比如订单的key为AA开头,红包的key为BB开头,代金券的key为CC开头。那么,如果监听到redis中有过期的键值对的话,程序会得到键值对的key,通过key来判断属于哪部分业务,然后进行相应的处理。传统做法为,将所有的处理业务都写在一个类,或者一个方法中,通过多个if判断执行不同的逻辑,比如,如果是AA开头,那么执行订单的过期处理逻辑等等。

  实现效果,就是将所有的处理类放到一个list中。请求到了,循环遍历这个list。循环遍历这个链就类似于if else判断,然后让链中的某一个类可以处理这个请求。

和责任链模式的区别:

  责任链中的各个处理类之间有关联,A类有包含B类,B类包含C类,形成一个链

  策略模式,各个处理类没有联系,相互独立。

定义一个抽象类,或者接口

public abstract class AbstractRedisExpireHandle {
    //key的前缀,用于区分是什么业务
    private String prefix;
    //构造,子类必须实现
    public AbstractRedisExpireHandle(){}
    /**
     * 操作前缀属性的公开方法
     */
    public String getPrefix() {
        return prefix;
    }
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    //抽象的,所有具体处理者应该实现的处理逻辑
    abstract void expireHandle(String redisKey);
}

实现接口或者抽象类,每一个处理类都有相同的结构

/**
 * 商品订单过期
 */
@Component
public class GoodsOrderExpireHandle extends AbstractRedisExpireHandle {
 
    //设置该业务的前缀
    private static final String PREFIX = "dd";
 
    public GoodsOrderExpireHandle(){
        setPrefix(PREFIX);
    }
 
    @Reference(version = "1.0.0")
    private TaskService taskService;
 
    //实现具体处理逻辑
    @Override
    void expireHandle(String redisKey) {
        taskService.goodsOrderExpireHandle(redisKey);
    }
/**
 * 红包过期
 */
@Component
public class RedPacketExpireHandle extends AbstractRedisExpireHandle {
 
    //设置业务前缀
    private static final String PREFIX = "kb";
 
    @Reference(version = "1.0.0")
    private TaskService taskService;
 
    public RedPacketExpireHandle(){
        setPrefix(PREFIX);
    }
 
    //实现具体处理逻辑
    @Override
    void expireHandle(String redisKey) {
        taskService.redPacketExpireHandle(redisKey);
    }
}

基于spring完成链的创建

@Component
public class ExecuteHandle implements ApplicationContextAware,InitializingBean {
 
    //spring容器
    private ApplicationContext context;
 
    //具体处理者的集合
    private List<AbstractRedisExpireHandle> expireHandles = new ArrayList<>();
 
    //该方法会在容器启动的时候被调用
    @Override
    public void afterPropertiesSet() throws Exception {
        //从容器中找到所有继承了抽象处理者的类,并加入到集合中,从而形成处理链
        String[] beanNames =  BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,AbstractRedisExpireHandle.class);
        for(int i=0;i<beanNames.length;i++){
            expireHandles.add((AbstractRedisExpireHandle)context.getBean(beanNames[i]));
        }
    }
 
    //该方法会将spring的当前容器传递进来
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
 
 
    //遍历处理链,通过前缀来判断,能否处理逻辑,如果不能则继续遍历(在这将if else进行解耦,不光光是解饿,程序后期的代码添加,更方便维护,如果增加一个hander,只需要写一个handel实现接口即可)
    public void handle(String redisKey){
        if(expireHandles.size() > 0){
            for(AbstractRedisExpireHandle abstractRedisExpireHandle : expireHandles){
                if(redisKey.startsWith(abstractRedisExpireHandle.getPrefix())){
                    abstractRedisExpireHandle.expireHandle(redisKey);
                }
            }
        }
    }
 
}
  • ApplicationContextAware为容器感知接口,实现这个接口需要实现setApplicationContext方法,spring会将容器当做参数传递到这个方法中,我们就可以使用当前容器了。
  • InitializingBean接口为初始化Bean的接口,实现这个接口需要实现afterPropertiesSet方法,该方法会在spring实例化这个类的时候被调用,可以做一些初始化工作,我就是在这个方法中将所有的具体处理者形成处理链的。

使用

@Component
public class RedisExpireListener implements MessageListener {
 
    @Autowired
    private ExecuteHandle executeHandle;
 
    @Override
    public void onMessage(Message message, byte[] bytes) {
        byte[] body = message.getBody();
        String redisKey = new String(body);
        executeHandle.handle(redisKey);
    }

  

 迭代器设计模式:

 省略

 观察者设计模式:

观察者模式定义了一个一对多的依赖关系,让一个或者多个观察者对象同时监听一个主题对象;这一个主题对象在状态上发生变化,就会通知所有依赖此对象的所欲的观察者对象;

import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        MessageSubject messageSubject = new Subject();
        messageSubject.registerObserver(new User("小明"));
        User u = new User("小芳");
        messageSubject.registerObserver(u);
//        messageSubject.notifyObserver();
        messageSubject.setMessage("开炮...");
        messageSubject.removeObserver(u);
        messageSubject.setMessage("继续开炮...");
    }
}

interface MessageSubject{
    //注册观察者
    public void registerObserver(Observer o);
    //移除观察者
    public void removeObserver(Observer o);
    //通知所有的观察者
    public void notifyObserver();
    public void setMessage(String message);
}

//观察者接口
interface Observer{
    public void print(String message);
}

class Subject implements MessageSubject{
    private Observer observer;
    private String message;
    private List<Observer> observerList = new ArrayList<Observer>();
    @Override
    public void registerObserver(Observer o) {
        observerList.add(o);
    }
    @Override
    public void removeObserver(Observer o) {
        observerList.remove(o);
    }

    public void setMessage(String message) {
        this.message = message;
        notifyObserver();
    }

    @Override
    public void notifyObserver() {
        for (int i = 0; i < observerList.size(); i++) {
            observerList.get(i).print(message);
        }
    }
}
class User implements Observer{
    private String name;
    public User(String name) {
        this.name = name;
    }
    @Override
    public void print(String message) {
        System.out.println(name+"已经接受消息:"+message+",准备处理");
    }
}

  

posted @ 2019-04-06 10:28  小名的同学  阅读(247)  评论(0编辑  收藏  举报