Java面试题——基础篇

1. JDK和JRE有什么区别?

JRE:JRE是Java Runtime Environment的缩写,顾名思义是java运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的,还有所有的Java类库的class文件,都在lib目录下,并且都打包成了jar。

至于在Windows上的虚拟机是哪个文件呢?就是<JRE安装目录>/bin/client中的jvm.dll。

JDK:Jdk是Java Development Kit的缩写,顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。

如果你需要运行java程序,只需安装JRE就可以了。如果你需要编写java程序,需要安装JDK。

2.==和equals的区别是什么?

==:

对于基本类型和引用类型==的作用效果是不同的,如下所示:

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用是否相同。所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

代码解读:程序在运行的时候会创建一个字符串缓冲池,当使用y=”string"这样的表达式创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,由于x先被放到了缓冲池中,所以在y被创建的时候,程序找到了具有相同值的x,将x的引用交给了y。而z使用的new操作符,它告诉程序我需要一个新的,于是创建了一个新对象到内存中。

修改下程序:

String x = "string";
String z = new String("string");
z=z.intern();
System.out.println(x==z); //true
System.out.println(x.equals(z)); // true

java.lang.String的intern()方法"string".intern()方法的返回值还是字符串"string",表面上看起来好像这个方 法没什么用处。但实际上,它做了个小动作:检查字符串池里是否存在"string"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会 把"string"添加到字符串池中,然后再返回它的引用。

equals:

equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。

总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

3.两个对象的hashCode()相同,则equals()也一定为 true,对吗?

不对。

        String str1 = "通话";
        String str2 = "重地";
        System.out.println(String.format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
        System.out.println(str1.equals(str2));

执行结果:

str1:1179395 | str2:1179395
false

JDK中关于hashCode的规定:

hashCode
public int hashCode()返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 Java.util.Hashtable 提供的哈希表)的性能。
hashCode 的常规协定是:
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

JDK规定当你调用equals方法比较两个对象相等时,他们调用hashcode方法时,都应该返回相同的整数值,也就是hashcode相等。记住,是应该相同。为什么应该?下面这段红色字体说了,必须重写hashcode方法维护协定!如果你不重写,那么就不能保证hashcode返回相同结果。
换句话说:重写equals方法时请必须重写hashcode方法,以保证equals方法相等时两个对象hashcode返回相同的值。如果没有重写hashcode方法的话,hashcode的值就不一定相等了。

4.final在java中有什么作用?

  • final修饰的类叫最终类,该类不能被继承。
  • final修饰的方法不能被重写。
  • final修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

final的一些重点知识:
1.final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
2.不能够对final变量再次赋值。
3.在匿名类中所有变量都必须是final变量。
4.final和abstract这两个关键字是反相关的,final类就不可能是abstract的。
5.final方法在编译阶段绑定,称为静态绑定。
6.对于集合对象声明为final指定是引用不能被更改,但是你可以向其中增加,删除或者修改内容。

5.java 中的 Math.round(-1.5) 等于多少?

等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。
Math类的几个常见方法:

  • ceil():返回大于等于(>=)给定参数的最小整数。
  • floor():返回小于等于(<=)给定参数的最大整数。
  • rint():返回与参数最接近的整数。返回类型为double。
  • round():6它表示四舍五入,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整。

6.String 属于基础的数据类型吗?

Strng不属于基础类型,基础类型有8种:byte,boolean,char,short,int,float,long,double。String属于对象。

7.java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

8.String str="i"与 String str=new String("i")一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

9.接口和抽象类有什么区别?

  • 接口能够多实现,而抽象类只能单独被继承,其本质就是,一个类能继承多个接口,而只能继承一个抽象类。
  • 方法上,抽象类的方法可以用abstract和public或者protect修饰。而接口默认为public abstract修饰。
  • 抽象类的方法可以有需要子类实现的抽象方法,也可以有具体的方法。而接口在老版本的jdk中,只能有抽象方法,但是Java8版本的接口中,接口可以带有默认方法。
  • 属性上,抽象类可以用各种各样的修饰符修饰。而接口的属性是默认的public static final。
  • 抽象类中可以含有静态代码块和静态方法,而接口不能含有静态方法和静态代码块。
  • 抽象类可以含有构造方法,接口不能含有构造方法。

10.面向对象的特征?

  • 封装
  • 抽象
  • 继承
  • 多态

11.int 和 Integer 有什么区别?

Integer是int的包装类,int的初值为0,Integer的初值为null。

        int i = 128;
        Integer i2 = 128;
        Integer i3 = new Integer(128);
        //Integer会自动拆箱为int,所以为true
        System.out.println(i == i2);
        System.out.println(i == i3);
        System.out.println("**************");
        Integer i5 = 127;//java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);
        Integer i6 = 127;
        System.out.println(i5 == i6);//true
        /*Integer i5 = 128;
        Integer i6 = 128;
        System.out.println(i5 == i6);//false
*/        Integer ii5 = new Integer(127);
        System.out.println(i5 == ii5); //false
        Integer i7 = new Integer(128);
        Integer i8 = new Integer(123);
        System.out.println(i7 == i8);  //false

首先,i和i2,i3比较都会输出true,这是因为Integer和int比都会自动拆箱。
i5和i6第一次比较输出的是true,而第二次输出的就是false,这是为什么呢?在java编译的时候,Integer i5=127会被翻译为Integer.valueOf(127)。以下是valueOf方法的源码:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

从源代码可以看出-128到127之间的值,会直接返回缓存中对应的值,并不会new,所以第二次比较为false。

总结:

  • 无论如何,Integer和new Integer都不会相等。不会经历拆箱过程,类似i2和i3,i5和ii5的比较。
  • 如果两个都是非new出来的Integer,如果数在-128到127之间,则为true,否则为false。
  • int和Integer(无论是通过什么创建),都为true,因为会把Integer自动拆箱为int再去比。

12.重载和重写的区别?

重载是编译时的多态性,重写是运行时的多态性。对于重载而言,是发生在一个类中,同名的方法如果有不同的参数列表(类型,数量)则视为重载;重写发生在子类和父类之间,重写要求子类与父类被重写方法有相同的参数列表,兼容的返回类型,比父类方法更好访问,不能比父类声明更多的异常。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

13.session 与 cookie 区别?

  • session是保存在服务器端,理论上是没有大小限制,只要内存够大。
  • 浏览器第一次访问服务器的时候后创建一个session对象并返回一个JSESSIONID=ID的值,创建一个Cookie对象key为JISSIONID,value为ID的值是,将这个Cookie协会浏览器。
  • 浏览器在第二次访问服务器的时候携带Cooike信息JSESSIONID=ID的值,如果该JESSIONID的session已经销毁,那么会重新创建一个新的session再返回一个新的JSESSIONID通过Cookie返回到浏览器。
  • 针对一个web项目,一个浏览器是共享一个session,就算有两个web项目部署在同一个服务器上,针对两个项目的session是不同的。
  • session是基于Cookie技术实现,重启浏览器后再次访问原有的连接依然会创建一个新的session,因为Cookie在关闭浏览器后就会消失,但是原来服务器的Session还在,只有等到了销毁的时间会自动销毁。
  • 如果浏览器端禁用了Cookie,那么每次访问都会创建一个新的Session,但是我们可以通过服务器端程序重写URL即可,如果页面多连接多,会增加不必要的工作量,
    那可以强制让你用户开启接收Cookie后再让其访问即可。

说说Cookie和Session的区别?

1、Cookie和Session都是会话技术,Cookie是运行在客户端,Session是运行在服务器端。

2、Cookie有大小限制以及浏览器在存cookie的个数也有限制,Session是没有大小限制和服务器的内存大小有关。

3、Cookie有安全隐患,通过拦截或本地文件找得到你的cookie后可以进行攻击。

4、Session是保存在服务器端上会存在一段时间才会消失,如果session过多会增加服务器的压力。

14.HTTP 请求的 GET 与 POST 方式的区别?

  • GET在浏览器回退时是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被Bookmark,而POST不可以。
  • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • GET请求只能进行url编码,而POST支持多种编码方式。
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST么有。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中。

GET和POST是什么?HTTP协议中的两种发送请求的方法。
HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。
HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。

GET和POST还有一个重大区别,简单的说:

GET产生一个TCP数据包;POST产生两个TCP数据包。

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。

因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

  1. GET与POST都有自己的语义,不能随便混用。
  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
  3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

原文链接

15.Anonymous Inner Class(匿名内部类)是否可以继承其他类,是否可以实现接口?

匿名内部类是没有名字的内部类,不能继承其他类,但一个内部类可以作为一个接口,由另一个内部类实现。

16.Collection和Collections的区别?

Collection是集合类的接口,继承该接口的主要有Set和List。
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索,排序,线程安全化等操作。

17. += 和 = +的区别?

+=是复合赋值操作符,= +是简单赋值操作符。复合赋值表达式可以自动将所执行计算的结果I转型为其左侧变量的类型。如果左侧变量比右侧变量要窄,复合赋值操作符会自动进行类型转换,而简单赋值操作符需要强制类型转换。

18.jdk1.8和jdk1.7下的ConcurrentHashMap

jdk1.7

  • 将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
  • 由Segment数组结构和HashEntry数组结构组成。
  • 一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种数组和链表结构,一个Segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment的锁。

jdk1.8

  • 采用CAS和synchronized来保证并发安全
  • 数据结构是:数组+链表/红黑二叉树
  • synchronized只锁定当前链表或红黑二叉树的首节点。

19.Java对象的实例化顺序?

  • 父类的静态成员变量和静态代码块
  • 子类的静态成员变量和静态代码块
  • 父类成员变量和方法块加载
  • 父类构造函数加载
  • 子类成员变量和方法块加载
  • 子类的构造函数加载

20.不用final还可以用什么办法使得这个类不被继承?

构造函数私有化。

21.Exception与error的差别?

都继承于Throwable。
Error类一般指与虚拟机相关的问题,如系统崩溃、虚拟机错误、内存空间不足、方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception ),运行时异常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用try。。。catch捕获,要么用throws字句声明抛出,交给它的父类处理,否则编译不会通过。

22.string的最大长度?

编译时65535,运行时Integer.MAX_VALUE。(与堆内存大小有关)

23.lambad表达式和匿名类的区别?

  • 匿名类的this关键字指向匿名类,而lambda表达式的this关键字指向包围lambda表达式的类。
  • Java编译器将lambda编译成类的私有方法。

24.什么是函数式接口(@FunctionInterface)?

函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

25. static方法能不能被重写?

不能。重写是运行时多态,而static方法是在编译期绑定的,不存在多态。静态方法从程序开始运行后就已经分配了内存,所有引用到该方法的对象所指向的都是同一块内存中的数据,也就是该静态方法。子类中如果定义了相同名称的静态方法,并不会重写,而应该是在内存中又分配了一块给子类的静态方法。

26.多态

事物在运行过程中存在不同的状态。

27.为什么内部类实现的单例模式是线程安全的?

当第一次加载Singleton类时并不会初始化instance,只有在第一次调用getInstance方法时才会导致instance被初始化。

虚拟机会保证一个类的构造器<client>()方法在多线程环境中被正确的加载,同步,如果多个线程同时去初始化一个类,那么只有一个线程去执行这个类的构造器<client>()方法,其他线程都需要阻塞等待,直到活动线程执行<client>()方法完毕。

29.BIO NIO AIO

29.1 BIO

同步阻塞I/O模式,数据的读取写入阻塞在一个线程内等待完成。

Java中由一个独立的Acceptor线程负责监听客户端的连接。通过在while循环调用accept()方法等待客户端连接,请求一旦接收到一个连接请求,就通过线程池进行连接处理。

29.2 NIO

同步非阻塞I/O,通过多路复用器不断轮询各个连接的状态,当有读或写时,才会处理它。

29.3 AIO

异步非阻塞I/O,在相应的状态发生改变时,会通知对应的线程来处理。

30. 序列化

序列化就是将对象的状态(各个属性量)保存起来,以便存储在文件中或在网络上传输。

31. transient关键字的作用?

让成员变量不被初始化。

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

推荐阅读更多精彩内容

  • 下面的内容是对网上原有的Java面试题集及答案进行了全面修订之后给出的负责任的题目和答案,原来的题目中有很多重复题...
    独念白阅读 1,243评论 0 3
  • ❝面膜好不好用,膜布材质很重要❞ 膜布直接影响功效! 「粉瑟面膜」源于新西兰原始森林麦卢卡茶树纤维制成 *搭载更多...
    e6107628ebf0阅读 1,378评论 0 1
  • 1我的人生故事 每个人都可能经历一番波折,每个人都有一番大起大落,高考的落差让我不得不去选择一个本二的学校,上分...
    310涂涛阅读 250评论 0 0
  • 如果想学前端,那css3是最基础的了,也是最先学习的一门知识 废话不多说直接正题 什么是css3 css3形成页面...
    StevenTang阅读 226评论 1 1
  • 今天是圣诞节,也是奶奶的生日。我们一家人去酒店吃晚饭庆祝奶奶的生日。我看见妈妈给奶奶一个大红包。并说祝奶奶福如东海...
    温晓晴阅读 197评论 0 0