JavaSE进阶-01-面向对象

1.搭建环境

1)新建空工程

2)新建module



小技巧
快速生成main方法:psvm
快速生成输出语句:sout


final关键字

1.final时Java语言中的一个关键字
2.final表示最终的、不可变的。
3.final可以修饰变量以及方法,还有类等。

final修饰的变量一般会加static变为静态的,存在方法区
static final 联合修饰的变量成为“常量”,常量名建议大写,单词之间用‘_’衔接
常量和静态变量都是存储在方法去,并且都是在类加载时初始化。

final修饰的变量一旦赋值,就无法再分配值。
(final修饰的变量,只能赋一次值)

    public static void main(String[] args) {
        int i = 100;
        //可以为局部变量i重新赋值
        i=200;

        final int k=100;
        //无法为局部变量k重新赋值
        k=200;

        final int m;
        m=200;
        //一旦赋值,无法再分配值
        m=300;
    }


new一个对象,就会产生新的内存地址。使用final修饰该引用变量后,就不能再重新new一个对象,与后面的传值无关。并且在该方法执行过程中,该引用指向对象后,该对象不会被垃圾回收器回收。直到当前方法结束,才会释放空间。
(虽然final的引用只能指向1个对象,并且它永远都只能指向该对象,但是对象内部的数据可以被修改)

    public static void main(String[] args) {
        //可行
        Person p = new Person(20);
        
        //报错
        final Person p1= new Person(30);
        p1 = new Person(30);//不能指向其他地址
        p1.age=40;//可以重新赋值
    }

    static class Person{
        int age;

        public Person() {
        }

        public Person(int age) {
            this.age = age;
        }
    }


final修饰的实例变量,系统不负责赋默认值,要求必须手动赋值。手动赋值,在定义变量时赋值可以,在构造方法中赋值也可以。

    //用final修饰的实例变量必须手动赋值
    static class User{
        final int age;
    }

    /*因为系统会调用无参构造默认赋0,
     但是final修饰的方法必须手动赋值,
     因此只要在系统自动赋值前,
     手动赋值便不会报错*/
    static class User{
        final int age;

        public User() {
            this.age = 80;
        }
    }

final修饰的方法
被final修饰的方法无法被覆盖、被重写。

//可行
    class C{
        public void doSome(){
            System.out.println("C.doSome....");
        }
    }

    class D extends C{
        public void doSome(){
            System.out.println("D.doSome....");
        }
    }

//报错
    class C{
        public final void doSome(){
            System.out.println("C.doSome....");
        }
    }

    class D extends C{
        public void doSome(){
            System.out.println("D.doSome....");
        }
    }

final修饰的类
B类继承A类,相当于对 A类的功能进行扩展。如果不希望别人对A类型进行扩展。可以给A类加final关键字,这样的话A类就无法被继承了。

//可行
    class A{

    }
    class B extends A{

    }

//报错
    final class A{

    }
    class B extends A{

    }
public static void main(String[] args) {
        //多态
        C c= new D();

        //报错!编译器认为c是c类,c类没有doTher方法
        c.doTher();

        //可行!调用子类特有的方法时,需要向下转型。
        if(c instanceof D){//如果c确实指向D类型
            D d1=(D)c;
            d1.doTher();
        }
    }
    class C{
        public  void doSome(){}
    }
     class D extends C{
        public void doTher(){}
    }

抽象类

1.什么是抽象类?


抽象类:将类与类之间的共同特征进一步抽象,形成抽象类由于类本身是不存在的,所以抽象类无法创建对象(无法实例化)抽象类是用来被子类继承的

public class AbstractTest {
    public static void main(String[] args) {
        new Account();
    }

    abstract static class Account{}
}

final和abstract不能联合使用

public class AbstractTest {
    public static void main(String[] args) {

    }

    final abstract static class Account{}
}

子类继承抽象类,抽象类的子类可以是抽象类

public class AbstractTest {
    public static void main(String[] args) {
    }

     abstract static class Account{}

     class CreaditAccount extends Account{}
     abstract class BankAccount extends  Account{}
}

抽象类无法实例化,但存在构造方法,供子类使用。(子类的对象在创建的时候,通常会先调用父类的无参构造方法,供子类创建对象。)
父类定义了有参构造而子类又没用显示调用,系统默认子类调用父类无参构造时会报错。(当子类new对象的时候,super()如果找不到父类无参构造就会报错。)

public class AbstractTest {
    public static void main(String[] args) {
    }

     abstract static class Account{
         public Account(String s) {
         }
     }

     class CreaditAccount extends Account{}
}

2.抽象类属于什么类型?
抽象类也属于引用数据类型

3.抽象类怎么定义?
[修饰符列表] abstract class 类名{
类体;
}

抽象方法:表示没有实现的方法,没有方法体的方法。

例如:public abstract void doSome();
抽象方法特点是:
特点1:方法体,以分号结尾。
特点2:前面修饰符列表中有abstract关键字。
抽象类不一定有抽象方法,但抽象方法必须出现在抽象类,可以有非抽象方法。

public class AbstractTest {
    public static void main(String[] args) {
    }
      static class Account{
         public abstract void withdraw();

     }
}

抽象类有抽象方法时,非抽象的子类继承时需要重写/覆盖从父类继承过来的抽象方法

Java语言中没有方法体的方法不一定是抽象方法。

object类中有很多方法没有方法体,都是以“;”结尾。但是他们都不是抽象方法,例如:


这个方法底层调用了C++写的动态链接库程序。(native关键字表示调用JVM本地程序)


接口

1.接口的基础语法

①接口也是一种引用数据类型
接口是完全抽象的。(抽象类是半抽象的)👇

    interface MyMath{
        void doSome(){};
    }

③接口的定义👇
[修饰符列表] interface 接口名{}

public class Test01 {
    public static void main(String[] args) {
        
    }
    interface A{
        
    }
}

接口与接口之间支持多继承👇

public class Test01 {
    public static void main(String[] args) {}
    interface A{}
    interface B{}
    interface C extends A,B{}
}

接口中只包含两部分内容,一部分是:常量;一部分是抽象方法。(接口中没有其他内容)
接口中所有元素都是public修饰的
⑥接口中的抽象方法定义时,public abstract修饰符可以省略。👇

    interface MyMath{
        int sum(int a,int b);
    }

⑦接口中的常量定义时,public static final修饰符可以省略。👇

    interface MyMath{
        double PI=3.1415926535;
    }

类继承类为extend,类实现接口为implement👇

    interface MyMath{}
    class MyMathImpl implements MyMath{}

⑨实现类必须重写/覆盖/实现接口的方法(因为接口是抽象类,非抽象类不能有抽象方法)👇

    interface MyMath{
        int sum(int a, int b);
        int sub(int a ,int b);
    }
    class MyMathImpl implements MyMath{}

实现类的访问权限只能比接口更高👇

   interface MyMath{
        int sum(int a, int b);
        int sub(int a ,int b);
    }
    class MyMathImpl implements MyMath{
         int sum(int a,int b){
            return a+b;
        }
         int sub(int a,int b){
            return a-b;
        }
    }
    interface MyMath{
        int sum(int a, int b);
        int sub(int a ,int b);
    }
    class MyMathImpl implements MyMath{
        public int sum(int a,int b){
            return a+b;
        }
        public int sub(int a,int b){
            return a-b;
        }
    }

可以使用多态(父类型的引用指向子类型的对象)👇

public class Test01 {
    public static void main(String[] args) {
        MyMath mm= new MyMathImpl();
        //调用接口的方法(面向接口编程)
        int result1 = mm.sub(10,20);
        System.out.println("sub="+result1);
        int result2 = mm.sum(10,20);
        System.out.println("sum="+result2);
    }
    interface MyMath{
        int sum(int a, int b);
        int sub(int a ,int b);
    }
    static class MyMathImpl implements MyMath{
        public int sum(int a,int b){
            return a+b;
        }
        public int sub(int a,int b){
            return a-b;
        }
    }
}

重点:一个类可以实现多个接口,而Java中类和类之间支持单继承。现实中存在多继承,接口弥补了单继承带来的缺陷。(必须把接口的所有方法都实现了)👇

    interface A{
        void m1();
    }
    interface B{
        void m2();
    }
    interface C{
        void m3();
    }
    class D implements A,B,C{
        public void m1(){}
        public void m2(){}
        public void m3(){}
    }

接口和接口之间在进行强制类型转换的时候,没有继承关系也可以强转,但是编译没问题,运行时可能出现CLassCastException异常。向下转型养成if+instanceof判断的好习惯。

    public static void main(String[] args) {
        A a=new D();
        B b=new D();
        C c=new D();
        //有继承关系可以向下或向上转型
        B b2=(B)a;
        //无继承关系,编译没问题,运行有问题
        M m=new E();
        if(m instanceof K){
            K k=(K)m; 
        }
    }

继承和实现都存在时
语法:extends关键字在前,implements关键字在后。

    class Animal{}
    interface  Flyable{}
    class cat extends  Animal implements Flyable{}

2.接口在开发中的作用

面向接口编程,可以降低程序的耦合度,提高程序的扩展力,符合OCP开闭原则。
接口+多态才能达到降低耦合度
接口可以将调用者和实现者解耦合:
调用者面向接口调用,
实现者面向接口编写实现。

👇测试

   public static void main(String[] args) {
       //创建厨师对象
       FoodMenu cooder1= new AmericCooker();
       FoodMenu cooder2= new ChinaCooker();
       //创建顾客对象,并传入厨师对象
       Customer customer= new Customer(cooder1);
       //顾客点餐
       customer.order();
   }

👇接口:菜单类

       public interface FoodMenu{
       //西红柿炒蛋
       void shiZiChaoJiDan();
       //鱼香肉丝
       void yuXiangRouSi();
   }

👇顾客,手里有一个菜单

  public static class Customer{
       private FoodMenu foodMenu;
       //提供一个点菜的方法
       public void order(){
           //先拿到菜单才能点菜,调用get方法拿菜单
           FoodMenu fm=this.getFoodMenu();
           fm.shiZiChaoJiDan();
           fm.yuXiangRouSi();
           //也可以不调用get方法,因为子啊奔雷中私有的属性是可以访问
           /*foodMenu.shiZiChaoJiDan();
           foodMenu.yuXiangRouSi();*/
       }

       public Customer() {
       }
       public Customer(FoodMenu foodMenu) {
          this.foodMenu = foodMenu;
      }
      public FoodMenu getFoodMenu() {
           return foodMenu;
       }
       public void setFoodMenu(FoodMenu foodMenu) {
           this.foodMenu = foodMenu;
       }
   }

👇中餐厨师,实现菜单上的菜

 public static class ChinaCooker implements FoodMenu{
   @Override
   public void shiZiChaoJiDan() {
       System.out.println("中餐厨师做的西红柿炒鸡蛋!");
   }
   @Override
   public void yuXiangRouSi() {
       System.out.println("中餐厨师做的鱼香肉丝!");
   }

👇西餐厨师,实现菜单上的菜

public static class AmericCooker implements FoodMenu{
   @Override
   public void shiZiChaoJiDan() {
       System.out.println("西餐厨师做的西红柿炒鸡蛋!");
   }
   @Override
   public void yuXiangRouSi() {
       System.out.println("西餐厨师做的鱼香肉丝!");
   }

is a、has a、like a

is a:Cat is a Animal(猫是一个动物)
凡是能满足is a的表示“继承关系

has a:I has a Pen(我有一支笔)
凡是能满足has a的表示“关联关系”(关联关系通常以“属性”的形式存在。)

like a:Cooker like a FoodMenu(厨师像一个菜单一样)
凡是能满足like a关系的表示“实现关系”(实现关系通常是“类实现接口。”)


接口类与接口的区别

抽象类是半抽象
接口是完全抽象的

抽象类中有构造方法
接口中没有构造方法

接口和接口之间支持多继承
类和类之间只能单继承

一个类可以同时实现多个接口
一个抽象类只能继承一个类(单继承)

接口中只允许出现常量和抽象方法


package和import

1.为什么要用package?
package是Java中包机制。包机制的作用是为了方便程序的管理,不同的功能的类分别存放在不同的包下。

2.package包怎么用?
package是一个关键字,后面加包名,例如:
package com.bjpowernode.javase.chapter17;
注意:package语句只允许出现在Java源代码的第一行

3.包名命名规范
一般都采用公司域名倒叙的方式(因为公司域名具有全球唯一性)
:公司域名倒叙+项目名+模块名+功能名

对于带有package的Java程序用cmd怎么编译、运行?
类名:com.bjpowernode.javase.chapter17.HelloWorld(不再是HelloWorld了)

编译:javac -d . HelloWorld.java
javac:负责编译的命令
-d:带包编译
.:代表编译之后生成的东西放到当前目录下(.代表当前目录)
HelloWorld.java:被编译的java文件名

运行:java com.bjpowernode.javase.chapter17.HelloWorld

4.import的使用

java.lang不需要
同一个包下,不需要import。
不在同一个包下,需要import。

5.import的使用位置
import语句只能出现在package语句之下,class声明之上。


访问权限控制

1.private(私有的)
:只能在本类中访问

2.protected(受保护的)
:只能在本类、同包或子类中访问

3.public(公开的)
:在任何位置都可以访问

4.default(默认的)
:只能在本类以及同包下访问


范围大小:public>protected>默认的>private


Object类

API:应用程序编程接口
整个JDK的类库就是一个javase的API
每个API都会配置一套API帮助文档

1.toString()
:将对象转换成字符串形式

/*
源代码上toString()的默认实现:
    类名@对象的内存地址转换为十六进制的形式
*/
public String toString(){
    return this.getClass().getName() + '@' + Integer.toHexString(hashCode())
}

toString()设计目的:可以通过该方法将一个”java对象“转换成”字符串“的形式

没有重写toString()方法前,程序👇

public class Test01 {
   public static void main(String[] args) {
       MyTime t1=new MyTime(1970,1,1);
       String s1=t1.toString();
       System.out.println(s1);
       System.out.println(t1);//输出引用时,会自动调用该引用的toString()
   }
   static class MyTime{
       int year;
       int month;
       int day;
       public MyTime() {}
       public MyTime(int year, int month, int day) {
           this.year = year;
           this.month = month;
           this.day = day;
       }
   }
}

结果👇
Test01$MyTime@1b6d3586

Test01$MyTime@1b6d3586

重写toString()方法后,程序👇

public class Test01 {
   public static void main(String[] args) {
       MyTime t1=new MyTime(1970,1,1);
       String s1=t1.toString();
       System.out.println(s1);
       System.out.println(t1);//输出引用时,会自动调用该引用的toString()
   }
   static class MyTime{
       int year;
       int month;
       int day;
       public MyTime() {}
       public MyTime(int year, int month, int day) {
           this.year = year;
           this.month = month;
           this.day = day;
       }
       @Override
       public String toString() {
           return year +"年" 
                   + month +"月" 
                   + day+"日";
       }
   }
}

结果👇
1970年1月1日
1970年1月1日

2.finalize
:垃圾回收器负责调用的方法(只需重写,不需要程序员手动调)

/*
源代码上finalize()的默认实现:
    当一个Java镀锡即将被垃圾回收器回收的时候,垃圾回收器负责调用 finalize()。
*/
protected void finalize()  throws Throwable{}

静态代码块:

static{
 ......
}

静态代码块在类加载时刻执行,并且只执行一次。这是一个SUN准备的类加载时机。

finalize()方法同样也是SUN为程序员准备的时机。(垃圾回收时机)

System.gc();:建议启动垃圾回收器(因为只有垃圾到达一定数量才会启动)

   public static void main(String[] args) {
       for(int i=0;i<1000;i++) {
           Person p = new Person();
           p = null;
           System.gc();//建议启动垃圾回收器
       }
   }
   static class Person{
       protected void finalize() throws Throwable{
           System.out.println(this+"即将被回收!");
       }
   }

3.==与equals()
:判断两个对象是否相等

/*
源代码上equals()的默认实现:
    当且仅当x和y引用相同的对象( x == y具有值true )时,该方法返回true 。
*/
public boolean equals(Object obj){
    return (this == obj);
}

重写equals()后👇

public class Test02 {
   public static void main(String[] args) {
       //判断两个基本数据类型的数据是否相等使用”==“
       int a=100;
       int b=100;
       System.out.print("a==b:");
       System.out.println(a==b);
       //判断两个java对象是否相等使用”equals“
       MyTime t1=new MyTime(2001,7,8);
       MyTime t2=new MyTime(2001,7,8);
       //这里的”==“判断的是:t1中保存的对象内存地址和t2中保存的对象内存地址是否相等
       System.out.print("t1==t2:");
       System.out.println(t1==t2);
       System.out.print("t1.equals(t2):");
       System.out.println(t1.equals(t2));
   }
   static class MyTime{
       int year;
       int month;
       int day;
       public MyTime() {}
       public MyTime(int year, int month, int day) {
           this.year = year;
           this.month = month;
           this.day = day;
       }
       //重写equals
       @Override
       public boolean equals(Object o) {
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
           if(o instanceof MyTime) {
               MyTime myTime = (MyTime) o;
               return year == myTime.year &&
                       month == myTime.month &&
                       day == myTime.day;
           }
           return false;
       }
       @Override
       public int hashCode() {
           return Objects.hash(year, month, day);
       }
   }
}

重写equals()程序执行结果👇
a==b:true
t1==t2:false
t1.equals(t2):false
因为equals()默认实现是”==“比较,想要判断两个对象的内容是否相等,需要重写
重写equals()程序执行结果👇
a==b:true
t1==t2:false
t1.equals(t2):true

注意:string类重写了equals()、toString()方法

例题:比较两个用户

public class Test03 {
   public static void main(String[] args) {
       User u1=new User("zhangsan",new Address("北京","大兴区","1111111"));
       User u2=new User("zhangsan",new Address("北京","大兴区","1111111"));
       System.out.println(u1.equals(u2));
   }
   static class User{
       //用户名
       String name;
       //用户住址
       Address addr;

       public User() {}
       public User(String name, Address addr) {
           this.name = name;
           this.addr = addr;
       }
       @Override
       public boolean equals(Object o) {
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
           User user = (User) o;
           return Objects.equals(name, user.name) &&
                   Objects.equals(addr, user.addr);
       }
   }
   static class Address{
       String city;
       String street;
       String zipcode;

       public Address() {}
       public Address(String city, String street, String zipcode) {
           this.city = city;
           this.street = street;
           this.zipcode = zipcode;
       }
       @Override
       public boolean equals(Object o) {
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
           Address address = (Address) o;
           return Objects.equals(city, address.city) &&
                   Objects.equals(street, address.street) &&
                   Objects.equals(zipcode, address.zipcode);
       }
   }
}

运行结果:true

4.clone()
:负责对象克隆

protected Object clone(){
                throws CloneNotSupportedException
}

5.hashCode()
:获取对象哈希值

/*
源代码上hashCode()的默认实现:
    实际上就是一个Java对象的内存地址,经过哈希算法,得出的一个值。(带有native关键字,底层调用C++)
*/
public native int hashCode()
public class Test05 {
   public static void main(String[] args) {
       Object o = new Object();
       int hashCodeValue=o.hashCode();
       System.out.println(hashCodeValue);//460141958

       MyClass m= new MyClass();
       int hashCodeValue2=m.hashCode();
       System.out.println(hashCodeValue2);//1163157884
   }
   static class MyClass{}
}

内部类

:在类的内部又定义了一个新的类。(使用内部类编写的代码,可读性很差,能不用尽量不用)

1.实例内部类
:类似于实例变量


2.静态内部类
:类似于静态变量


3.局部内部类
:类似局部变量



3.1匿名内部类

使用匿名内部类前👇(还要定义接口的实现类)

public class Test01 {
   public static void main(String[] args) {
       MyMath mm=new MyMath();
       mm.mySum(new ComputerImp(),100,90);
       System.out.println();
   }

  interface Computer{
      int sum(int a,int b);
  }
   static class ComputerImp implements Computer{
       public int sum(int a,int b){
           return a+b;
       }
   }

   static class MyMath{
       public void mySum(Computer c,int x,int y){
           int retValue= c.sum(x,y);
           System.out.println(retValue);
       }
   }
}

使用匿名内部类后👇(不需要定义接口的实现类)

public class Test01 {
   public static void main(String[] args) {
       MyMath mm=new MyMath();
       mm.mySum(new Computer(){
           public int sum(int a,int b) {
               return a + b;
           }
       },200,300);
       System.out.println();
   }

   interface Computer{
       int sum(int a,int b);
   }

    static class MyMath{
       public void mySum(Computer c,int x,int y){
           int retValue= c.sum(x,y);
           System.out.println(retValue);
       }
   }
}

注意:不建议使用匿名内部类,因为一个类没有名字,没办法重复使用。并且代码太乱,可读性差。


笔记来源:B站动力节点Java零基础教程视频

视频链接:https://www.bilibili.com/video/BV1Rx411876f

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

推荐阅读更多精彩内容