- 博客(104)
- 收藏
- 关注
原创 Java基础学习笔记-抽象类和抽象方法
当一个类被定义为抽象类时,它可以包含各种类型的成员,包括属性、方法等,其中方法有可以分为普通方法和抽象方法。继承抽象类的特征和注意事项:一个类如果继承了抽象类,那么这个类必须重写抽象类的全部抽象方法,否则这个类也必须定义成抽象类。·抽象方法只能定义在抽象类中。但是抽象类中可以包括抽象方法,也可以包含普通方法,还可以包含普通类包含的一切成员。如果继承的类是抽象类,不一定要实现抽象父类的所有抽象方法。如果继承的类是普通类,一定要实现抽象父类的所有抽象方法。抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
2025-02-10 17:37:01
478
原创 Java基础学习笔记-枚举
枚举类都是继承了枚举类型:java.lang.Enumn。枚举的构造器都是私有的,枚举对外不能创建对象。枚举的作用:是为了左信息的标志和信息的分类。枚举类的第一行默认都是罗列枚举对象的名称的。第一行都是罗列枚举类实例的名称。枚举是Java中的一种特殊类型。枚举都是最终类,不可以被继承。修饰符 enum 枚举名称{枚举类相当于是多例模式。
2025-02-10 17:04:28
162
原创 Java基础学习笔记-多态
而在程序设计的术语中,它意味着一个特定类型的变量可以引用不同类型的对象,并且能自动地调用引用的对象的方法,也就是根据作用到的不同对象类型,响应不同的操作。>将一个指向子类对象的父类引用赋给一个子类的引用,即将父类类型转换为子类类型,称为向下转型,此时必须进行强制类型转换。定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。判断关键字左边的变量指向的对象的真实类型,是否是右边类型或者是其子类类型,是则返回true,反之。
2025-02-09 23:13:50
508
原创 Java基础学习笔记-包与权限修饰符
可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。导包 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!如果这个类中使用不同包下的相同的类名,此时默认只能导入一个类的包,另一个类要使用全名访问。·如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。包是用来分门别类的管理不同类的,类似于文件夹,建包利于程序的管理和维护。·如果该成员只希望本类访问,使用private修饰。·权限修饰符:是用来控制一个成员能够被访问的范围。
2025-02-08 23:48:04
162
原创 Java基础学习笔记-super关键字与final关键字
(1)如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。·子类通过this(...)去调用本类的其他构造器,本类其他构造器会通过super去手动调用父类的构造器,最终还是会调用父类构造器的。(2)如果子类的构造方法中通过super显式地调用了父类的有参构造方法,那么将执行父类相应的构造方法,而不是执行父类的无参构造方法。子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己的构造器。
2025-02-08 23:31:56
412
原创 Java基础学习笔记-方法重写
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。>重写涉及的是子类和父类之间的同名方法,要求方法名相同、参数列表相同、返回值类型相同。(继承)重写:子类对父类方法进行重写,要求方法名、参数列表、返回类型全部一致。>重写方法的返回值类型必须和被重写方法的返回值类型相同或是其子类。·重写的方法名称、参数列表必须与被重写方法的名称和参数列表一致。>重写方法和被重写方法必须具有相同的参数列表。>重写方法和被重写方法必须具有相同的方法名。>重写方法不能缩小被重写方法的访问权限。
2025-02-08 20:29:19
446
原创 Java基础学习笔记-继承
Java中所有类,要么直接继承了Object,要么默认继承了Object,要么间接继承了Object,Object是祖宗类。继承的设计规范:子类们相同(共性属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。1.在现有类(父亲)的基础上创建一个新的类(子类),子类继承父类一部分的属性和方法(私有成员不能被继承)。继承是面向对象的三大特性之一,继承可以解决编程中代码冗余的问题,是实现代码重用的重要手段之一。1.子类可以继承父类的属性和行为,但是子类不能继承父类的构造器,
2025-02-08 18:17:37
311
原创 Java基础学习笔记-Java的参数传递机制
对于引用数据类型,赋值是把原对象的引用(可以理解为内存地址)传递给另一个引用。对于数组而言,当用一个数组名直接给另外一个数组名赋值时,相当于传递了一个引用,此时,这两个引用指向同一个数组,也就是指向同以内存空间。对于基本数据类型,不同的变量会分配不同的存储空间,并且存储空间中存储的是该变量的值。赋值操作传递的是变量的值,改变一个变量的值不会影响另一个变量的值。在传数实参给方法的形参的时候,并不是传输实参变量本身,而是传输实参变量中存储的值,这就是值传递。·引用类型的参数传输存储的地址值。
2025-02-08 00:10:13
259
原创 Java基础学习笔记-static关键字
Java中,一般情况调用类的成员都需要先创建类的对象,然后通过对象进行调用。使用static关键字可以实现通过类名加“.”直接调用类的成员,不需要创建类的对象。由于工具里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有。用static修饰的属性称为静态变量或者类变量,没有使用static修饰的属性称为实例变量。用static修饰的方法称为静态方法或者类方法,不用static修饰的方法称为实例方法。·实例方法可以访问静态的成员,也可以访问实例的成员。
2025-02-07 23:57:16
291
原创 Java基础学习笔记-封装
封装反映了事物的相对独立性,有效避免了外部错误对此对象的影响,并且能对对象使用由于大意产生的错误操作起到预防作用。特征的含义:所谓特征指的是已经称为Java设计代码的基本特点,即使毫无意义,通常也要满足这样的设计要求来编写程序。为每一个成员变量提供配套public修饰的getter、setter()方法暴露其取值和赋值。>Java中封装的实质就是将类的状态信息隐藏在类内部,不允许外部程序直接访问,而是通过。封装的原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为。有参构造器是可写可不写的。
2025-02-07 17:30:32
316
原创 Java基础学习笔记-成员方法
方法重载其实是对原有方法的一种升级,可以根据参数的不同,采用不同的实现方法,而且不需要编写多个名称,简化了类调用方法的代码。成员变量的作用域是在整个类内部都是可见的,所有成员方法都可以使用它,如果访问权限允许,还可以在类外部使用成员变量。方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数类型或参数个数。在不同的方法中,可以有同名的局部变量。类中的属性,也就是直接在类中定义的变量称为成员变量,它定义在方法的外部。局部变量的作用域仅限于定义它的方法,在该方法外无法访问它。
2025-02-07 16:42:20
296
原创 Java基础学习笔记-构造方法
2.用this来调用成员方法:this.成员方法,this可省略。当我们没有定义构造方法的时候,系统会自动创建一个无参的构造方法。c.在构造方法中调用构造方法必须放在第一行,并且只能出现一次。2.格式:[访问修饰符] 方法名称([参数列表]){ }当我们定义以后,系统不会自动创建一个无参的构造方法。(即使连void也不行)。b.不能在两个构造方法中互相调用。this([参数1,参数2])1.实例化时自动调用:初始化。1.用this来调用属性。b.方法名与类名一致。a.只能在构造方法中。
2025-02-06 23:54:52
137
原创 Java基础学习笔记-创建和使用对象
对象数组:数组的元素是对象。对象数组的数据类型就是具体的类名,对象数组存储的就是这个类的对象,每个数组元素就是一个对象,当根据下标找到某个元素时,可以按照对象的使用方法来使用该元素。类是一类事物的集合和抽象,代表这类事物共有的属性和行为。一个对象称为类的一个实例,是类一次实例化的结果。在Java中,要引用对象的属性和方法,需要使用“.”。类对象可以调用类中的成员,如属性和方法等。对象名.方法名() //引用对象的方法。>右边的类名()称为类的构造方法。>左边的类名为对象的数据类型。
2025-02-06 23:40:34
120
原创 Java基础学习笔记-面向对象的基本概念
返回类型可以是void,在定义方法时,返回类型为void时表明没有返回值,方法体中不必使用“teturn”关键字返回具体数据,但是可以使用“return”关键字退出方法。方法的返回类型为void(无返回值),方法内则不能使用return返回数据,如果方法的返回类型写了具体类型,方法内部则必须使用return返回对应类型的数据。>当需要在方法执行的时候为方法传递参数时才需要参数列表,如果不需要传递参数就可以省略,不过小括号不可以省略,传递多个参数时,以半角的逗号进行分隔。对象执行操作的行为称为类的方法。
2025-02-06 23:24:39
470
原创 Java基础学习笔记-数组
boolean类型数组元素的默认值是false、String类型数组元素的默认值是null。4.布尔类型数组的元素默认值都是false----------boolean。5.引用类型数组的元素默认值都是null----------String。3.浮点型数组的元素默认值都是0.0----------double。数组的最大索引-数组名.length-1//前提元素个数大于0。2.字符数组的元素默认值都是0----------char。1.整型数组的元素默认值都是0----------int。
2025-02-06 18:13:41
491
原创 Java基础学习笔记-跳转语句
在while和do-while循环中,continue执行完毕后,程序将直接判断循环条件,如果为true,则继续下一次循环;Java语言支持3种类型的跳转语句:break语句、continue语句、return语句。continue语句的作用是强制循环提前返回,也就是让循环跳过本次循环中的剩余代码,然后开始下一次循环。break语句在循环中的作用是终止当前循环,在switch语句中的作用是终止switch。return语句的作用是结束当前方法的执行并退出返回到调用该方法的语句处。2.continue语句。
2025-02-05 22:28:39
296
原创 Java基础学习笔记-循环结构
其中while循环称为外层循环,for循环称为内层循环,因为是两层嵌套,所以称为二重循环。3)for循环:先执行变量初始化部分,再判断循环条件,然后执行循环体,最后进行循环变量的计算。(4)循环语句执行完毕后执行表达式3,改变循环变量的值,再次执行表达式2,如果结果为真,继续执行。2)do-while循环:先执行循环体,再判断循环条件,循环体至少执行一次。·功能上是完全一样的,for能解决的,while也能解决,反之亦然。(1)首先对循环条件的结果进行判断,如果结果为真,则执行循环语句。
2025-02-05 22:12:50
512
原创 Java基础学习笔记-顺序结构、选择结构
JDK1.7后,switch后的表达式可以是int、short、byte、char、枚举类型、String类型的表达式,不支持double、float、long。1.表达式类型只能是byte、short、int、char、JDK5开始支持枚举,JDK7开始支持String、不支持double、float、long。嵌套if控制语句可以通过外层语句和内层语句的协作,来增强程序的灵活性。1.在上述3种if控制语句中,条件表达式的值必须是布尔类型,这是Java语言与C、C++语言的不同之处。
2025-02-05 19:51:21
1070
原创 Java基础学习笔记-常用运算符
运算符就是告诉程序执行特定的运算操作的符号。Java提供了6类运算符,分别是赋值运算符、算术运算符、关系运算符、逻辑运算符、位运算符和条件运算符。### 1.赋值运算符赋值运算符“=”用于给变量指定变量值,并可以和算术运算符结合,组成复合赋值运算符。复合赋值运算符主要包括“+=” “-=” “*=” “/=” “%=”。int i = 5;int j = 15;i = i + j;//可以替换为i += j;分析:推荐使用复合赋值运算符,将“i = i + j”换为"i += j",此写法便于程
2025-02-05 00:11:22
448
原创 Java基础学习笔记-强制类型转换
不同基本数据类型之间进行运算时需要进行类型转换。除布尔类型外,所有基本数据类型进行运算时都要考虑类型转换,主要应用在算术运算时和赋值运算时。int类型强制转换为byte时,int的低位第一字节的数据00000011在强制类型转换中会丢失。不同类型的操作数,首先自动转换为表达式中最高级别的数据类型然后进行运算,运算的结果是最高级别的数据类型,简称低级别自动转换为高级别。将高级别的类型赋值给低级别类型时,必须进行强制类型转换。将低级别的类型赋值给高级别类型时将进行自动类型转换。存储位数越多,类型的级别越高。
2025-02-04 18:09:06
202
原创 Java基础学习笔记-标识符、变量、常量、关键字
Java自己保留的一些单词,作为特殊功能的,例如:public、class、byte、short、int、long、double...我们不能用来作为类名或者变量名称、否则报错。数据类型:强制限制盒子中存储数据的形式,例如:int(整数类型)、double(小数类型)。2.变量的有效范围是从定义开始到"}"截止,且同一个范围内部不能定义2个同名的变量。在Java中,标识符用来为程序中的常量、变量、方法、类、接口和包命名。2.标识符的首字母以字母、下划线、或美元符号开头,不能以数字开头。
2025-02-04 17:56:51
597
原创 Java学习前提了解
计算机最小的组成单元是:使用8个进制位一组,来保存数据,我们称之为一个字节(byte,简称B)。注意:随便写一个整数字面量默认是int类型的,132223244244虽然没有超过long的范围,但是它超过了本身int类型的表示范围。字符串 黑马,hello "黑马" ,"hello" 程序中必须使用双引号,内容可有可无。数据在计算机底层采用二进制:使用0、1,按照逢2进1的规则来表示数据的存储。我们的程序只需要开发一次,就可以在各种安装了JVM的系统平台上运行。
2025-02-04 17:50:48
312
原创 《苍穹外卖》项目学习记录-Day12导出运营数据
最终我们这个表格需要下载到客户端浏览器,下载到客户端浏览器需要有一个输出流,这个输出流我们一般通过response对象获得。我们使用这个输出流就可以把我们这个Excel文件,下载到客户端浏览器。this.getClass().getClassLoader().getResourceAsStream()从我们这个类路径下面读取资源,最终也可以返回一个输入流对象。this.getClass().getClassLoader()获得类加载器。1.查询数据库,获取营业数据---查询最近30天的运营数据。
2025-02-04 00:13:30
627
原创 《苍穹外卖》项目学习记录-Day12Apache POI介绍
我们操作excel文件主要有两种操作方式,一个是可以从一个已有的文件里面来读取文件的内容,还有一个就是可以向一个excel文件当中来写入内容。·入门案例2 通过POI读取Excel文件中的内容。·入门案例1 通过POI创建一个Excel文件。
2025-02-03 23:18:58
336
原创 《苍穹外卖》项目学习记录-Day11销量排名统计
所以这条sql语句应该为查询order_detail表和orders表,查询菜品或套餐的名字以及对应的销售的份数,当order_detail.order_id =orders.id的时候并且orders表中的status字段=5,也就是已完成的订单,而且是在指定的时间段内,进行分组查询,根据销量降序排序,查询销量前10个的商品。转换后元素会形成一个新的流。public static <T> Stream<T> of(T...values) 获取当前接收数据的Stream流。
2025-02-03 18:24:59
1355
原创 《苍穹外卖》项目学习记录-Day11订单统计
2.有初始值:T reduce(T identity, BinaryOperator<T> accumulator)。public static <T> Stream<T> of(T...values) 获取当前接收数据的Stream流。static <T> Stream<T> concat(Stream a,Stream b) 合并a和b两个流为一个流。public static <T> Stream<T> stream(T[] array) 获取当前数组的Stream流。
2025-02-02 23:28:19
1601
原创 《苍穹外卖》项目学习记录-Day11用户统计
StringUtils.join()是Apache Commons Lang库中的一个方法,用于将数组、集合或迭代器中的元素连接成一个字符串,并通过指定的分隔符分隔这些元素。返回的数据由前端这个折线图来约定的,也就是说折线图对这个数据有这样的要求,我们去适应前端的这个数据格式,也就是前端需要什么样的数据,我们后端给它什么样的数据。统计指定时间区间之内的每一天的用户数量,每一天的用户数量包含两部分,一个是用户的总量,一个是新增用户数量。·array:要连接的数组或集合。·数组:可以是任何类型的数组。
2025-02-02 19:08:54
797
原创 《苍穹外卖》项目学习记录-Day11营业额统计
请求方式为GET方式,因为营业额统计本质上是一个查询类的操作, 查询相应的数据,最终把数据整合好,然后返回给前端。接口的请求参数有哪些,我们要统计营业额具体是统计哪个时间段之内的呢,应该通过参数传过来比较合理,比如说想统计近七日的,那近七日是几月几号到几月几号呢,这就需要通过参数传过来,然后后端根据它提交过来的这个参数,来查询这个时间段之内的统计数据。统计这一天的营业额,假设这一天一个订单也没有,这个营业额统计出来是null,所以它如果返回的是null的话我们把它转成0。静态方法,根据当前时间创建对象。
2025-02-02 18:02:18
1577
原创 《苍穹外卖》项目学习记录-Day10来单提醒
它是先请求到来nginx,由nginx反向代理转发到了我们后端,也就是说WebSocket这一次请求通过nginx进行了一次转发,才转发到了后端。前提是我们提前在nginx配置好了这个路径,这样客户端跟服务端握好手了,这个长连接就建立好了。是用户下了某个单或者催促某个订单,这个时候才会有相应的提醒,具体当前提醒的是哪个订单,我们也需要把这个订单id给提交过来。content:表示具体的提醒内容,提示框会显示文字,这些文字就是我们具体推过来的这个内容content。
2025-02-01 21:35:15
1115
原创 《苍穹外卖》项目学习记录-Day10WebSocket介绍
它们是双向的数据交互,浏览器可以给客户端发送消息,客户端可以给浏览器发送消息。服务器跟浏览器建立连接的过程,这个连接其实就是一个WebSocket的长连接。建立连接,服务端给客户端发送消息。客户端给服务端发送消息。
2025-02-01 19:28:47
259
原创 《苍穹外卖》项目学习记录-Day10订单状态定时处理
查询订单表把派送中的订单查询出来,也就是订单的状态为派送中,我们是查上一个工作日也就是当前时间减去一个小时,因为它是凌晨一点触发的。遍历集合把数据库查出来的超时订单状态修改成已取消状态,设置取消原因为订单超时,自动取消,设置取消时间为当前时间。查询订单表把超时的订单查询出来,也就是订单的状态为待付款,下单的时间已经超过了15分钟。遍历集合把数据库查出来的派送中订单状态修改成已完成状态。利用Cron表达式生成器生成Cron表达式。
2025-02-01 17:50:20
616
原创 《苍穹外卖》项目学习记录-Day10Spring Task
还有一类订单需要处理,比如说订单已经派送出去了,用户已经收到货了,这个时候我们需要去点击完成按钮来修改这个订单的状态,订单的状态就更新成已完成这种状态。如果派送中的订单一直不点击完成,那么它就会一直处于这种派送中的状态,而线下用户早已收到货了,那这个订单一直处于派送中,这显然是不合理的。我们的业务规则就是,如果这个订单超时我们就要把这个订单取消掉,这个过程由程序自动去完成,所以这个业务功能就是超时订单的定时处理。如果指定了是几号,周几这个位置就需要写一个问号,如果指定了是周几,日这个位置就需要写一个问号。
2025-02-01 16:38:29
709
原创 《苍穹外卖》项目学习记录-Day8订单支付
不仅仅是这一次接口调用,支付成功之后微信后台会给我们推送消息,也就是说微信后台会调用我们这个商户系统,这个交互过程当中也会涉及到一些数据的交互,数据的传输,那这个数据如何保证安全。2.微信后台会调用我们的商户系统给我们推送支付的结果,那这个地方我们就会遇到一个问题,就是微信后台它怎么就能调用到我们的商户系统,它这个调用过程本质上也是一个Http请求,我们当前商户系统的ip地址就是自己的笔记本的ip地址,而我们自己的笔记本ip地址只是一个局域网内的一个ip地址,微信后台其实是调用不到的。
2025-01-31 23:09:21
2124
3
原创 《苍穹外卖》项目学习记录-Day7用户下单
涉及两张表的操作,要保证数据的一致性(出现异常订单表里面插入了数据,订单明细表插入数据失败,导致数据不一致),所以要加@Transactional注解,要么全成功要么全失败,当执行完所有的操作才会提交事务,如果出现异常会进行事务回滚。如果查上来购物车数据,说明该用户的购物车不为空可以下单,如果查不上来购物车数据,说明该用户的购物车是空的不能下单。每个商品数量是多少?这些数据来自于用户的购物车,因为点餐的时候用户是先将商品加到购物车当中,然后才能提交订单,所以用户购买的商品种类和数量是由购物车数据来决定的。
2025-01-30 20:34:10
1574
原创 《苍穹外卖》项目学习记录-Day7导入地址簿模块功能代码
一个用户可以有多个收货地址,但是只能有一个默认地址,这个默认地址的作用就是当用户下单的时候默认使用这个地址。用户也可以点击新增收货地址,在弹出来的页面可以填写一个新的地址,填写完后点击保存地址向数据库插入一条新增地址的信息。用户可以对已经填写好的收货地址进行修改,如果这个地址不用了也可以点击删除地址按钮把这个地址删除掉。因为对于同一个用户他的所有地址来说,只能有一个是默认地址,现在提交过来一个地址设置成默认地址,那么在设置这个默认地址之前可能用户已经有了一个默认地址。·查询当前用户所有的地址信息。
2025-01-30 15:55:44
563
原创 《苍穹外卖》项目学习记录-Day7清空购物车
这个功能实现比较简单,只需要向数据库发送一条根据用户的id也就是user_id,去删除对应的用户购物表数据的SQL就可以。user_id都是4代表的是同一个用户。
2025-01-30 14:59:19
305
原创 《苍穹外卖》项目学习记录-Day7查看购物车
这是一个查询类的操作所以请求方式是GET方式,我们不需要提交任何参数,因为我们是查询当前用户的所有的购物车数据,每一次业务操作的时候都会在请求头里面携带一个token(我们前面设计了一个拦截器来拦截令牌token,然后把user_id封装到BaseContext里面去),后端通过解析这个token就能拿到user_id。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
2025-01-29 23:10:22
273
原创 《苍穹外卖》项目学习记录-Day7添加购物车
我们设计这些冗余字段就可以提高我们的查询速度,我们在购物车里面要展示商品的名称、图片、价格,如果没有这些冗余字段,我们在查询数据的时候,除了要查询购物车表还需要联合去查询菜品表或者是套餐表,相当于两张表的连接查询,有了冗余字段之后就变成了单表查询,直接查询购物车表就可以了,所以它的查询速度会快很多。要将商品添加到购物车,具体添加的是哪个商品呢?选的是那些商品,这就需要有商品的id字段,我们的商品分成了两种,一个是菜品,一个是套餐,为了更清晰我们设计了两个字段,一个菜品id,一个是套餐id。
2025-01-29 21:33:09
1352
原创 《苍穹外卖》项目学习记录-Day7缓存套餐
它会拿到这个key到Redis当中来查询有没有相应的数据,如果查到了它就会直接使用这个缓存数据,就不会再去调用getById方法,如果没有查询到,它就会通过反射来调这个方法,这个方法里面就是去查数据库,把这个数据返回,这个时候它就会把返回结果缓存起来,等下一次再查询相同数据的时候,缓存中就有了。因为id是自增的所以只输入age和name就可以了,在插入完之后它会把主键值赋值到user对象上,它是先插入到数据库,插入到数据库之后id就有值了,然后再去操作缓存数据,这个时候它就能取到id的值。
2025-01-29 16:33:17
1416
原创 《苍穹外卖》项目学习记录-Day7缓存菜品
修改菜品如果是修改了分类,那就影响到了两份缓存数据,也就是原先的分类会少一个,修改后的分类会多一个,如果修改的是菜品名称,价格这些属性则只会影响一份缓存数据。数据库中菜品数据有变更时清理缓存数据,如果不清理会造成我们这个数据的不一致,比如说在商家管理端把某一个菜品的价格改了,如果不清理对应的缓存,因为管理端改了菜品价格修改的是MySQL数据库里面数据,但是小程序展示出来的是读取缓存Redis里面的数据,Redis里面的数据没有改,所以小程序里面展示的还是原来的价格。2.如果存在,直接返回,无须查询数据库。
2025-01-28 19:21:54
1664
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅