Java常用类笔记

字符串相关的类

String类及常用方法

String的特性

  • String类:代表字符串。Java 程序中的所有字符串字面值(如"abc")都作为此类的实例实现。
  • String是一个final类,代表不可变的字符序列。
  • 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
  • String对象的字符内容是存储在一个final修饰的字符数组value[]中。
  • String实现了Serializable接口,表示字符串支持序列化。
  • String实现了Comparable接口,表示String可以比较大小。

常用API

  • int length():返回字符串的长度: return value.length

  • char charAt(int index):返回某索引处的字符return value[index]

  • boolean isEmpty():判断是否是空字符串:return value.length == 0

  • String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写

  • String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写

  • String trim():返回字符串的副本,忽略前导空白和尾部空白

  • boolean equals(Object obj):比较字符串的内容是否相同

  • boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写

  • String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”

  • int compareTo(String anotherString):比较两个字符串的大小

  • String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。

  • String substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。

  • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束

  • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始

  • boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始

  • boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true

  • int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引

  • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始

  • int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引

  • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。

    注:indexOf和lastIndexOf方法如果未找到都是返回-1

  • String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。

  • String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。

  • String replaceAll(String regex, String replacement):使 用 给 定 的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。

  • String replaceFirst(String regex, String replacement):使用给定的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。

  • boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。

  • String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。

  • String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。

String与基本数据类型转换

字符串-->基本数据类型、包装类

  • Integer包装类的public static int parseInt(String s):可以将由“数字”字符组成的字符串转换为整型。
  • 类似地,使用java.lang包中的Byte、Short、Long、Float、Double类调相应的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。

基本数据类型、包装类-->字符串

  • 调用String类的public String valueOf(int n)可将int型转换为字符串
  • 相应的valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可由参数的相应类型到字符串的转换

String与字符数组转换

字符数组-->字符串

  • String类的构造器:String(char[])String(char[] ,int offset ,int length)分别用字符数组中的全部字符和部分字符创建字符串对象。

字符串-->字符数组

  • public char[] toCharArray():将字符串中的全部字符存放在一个字符数组中的方法。
  • public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):提供了将指定索引范围内的字符串存放到数组中的方法。

String与字节数组转换

字节数组-->字符串

  • String(byte[]):通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。

  • String(byte[] ,int offset ,int length):用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一个字符串对象。

字符串-->字节数组

  • public byte[] getBytes():使用平台的默认字符集将此 String 编码为byte 序列,并将结果存储到一个新的 byte 数组中。
  • public byte[] getBytes(String charsetName):使用指定的字符集将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

编码:字符串-->字节(看得懂-->看不懂的二进制数据)

解码:编码的逆过程,字节-->字符串(看不懂的二进制数据-->看得懂)

说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。

StringBuffer、StringBuilder类

对比String、StringBuffer、StringBuilder

String:不可变字符序列,底层使用char[]存储。

StringBuffer:可变字符序列、效率低、线程安全,底层使用char[]存储

StringBuilder(JDK 5.0):可变字符序列、效率高、线程不安全,底层使用char[]存储

注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。

执行效率:StringBuilder > StringBuffer > String

StringBuffer类的常用方法(StringBuilder与StringBuffer常用方法类似)

  • StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接

  • StringBuffer delete(int start,int end):删除指定位置的内容

  • StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str

  • StringBuffer insert(int offset, xxx):在指定位置插入xxx

  • StringBuffer reverse() :把当前字符序列逆转

  • public int indexOf(String str)

  • public String substring(int start,int end)

  • public int length()

  • public char charAt(int n )

  • public void setCharAt(int n ,char ch)

日期时间API(JDK8之前)

基本关系

java.lang.System类

System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。

  • 此方法适于计算时间差。

java.util.Date类

表示特定的瞬间,精确到毫秒

  • 构造器:

    • Date():使用无参构造器创建的对象可以获取本地当前时间。
    • Date(long date):使用有参构造建创建指定毫秒数的Date对象。
  • 常用方法

    • getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

    • toString():把此Date对象转换为以下形式的String:dow mon dd hh:mm:ss zzz yyyy

      其中:dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat),zzz是时间标准。

java.text.SimpleDateFormat类

作用:对日期Date类的格式化和解析

两个操作:

  1. 格式化:日期-->字符串

    • SimpleDateFormat() :默认的模式和语言环境创建对象
    • public SimpleDateFormat(String pattern) :该构造方法可以用参数pattern指定的格式创建一个对象。
    • public String format(Date date) :格式化时间对象date
    SimpleDateFormat sdf = new SimpleDateFormat();
    Date date = new Date();
    System.out.println(date);
    System.out.println(sdf.format(date));
    

    输出:

    Thu Apr 08 20:26:57 CST 2021
    21-4-8 下午8:26
    

    指定输出格式:yyyy-MM-dd hh:mm:ss

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    Date date = new Date();
    System.out.println(dateFormat.format(date));
    

    输出:

    2021-04-08 08:36:42
    
  1. 解析:字符串-->日期

    要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),否则抛异常

    默认构造器格式

    String str = "2021-4-8 上午11:40";
    SimpleDateFormat format = new SimpleDateFormat();
    try {
      System.out.println(format.parse(str));
    } catch (ParseException e) {
      e.printStackTrace();
    }
    

    指定格式的构造器

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    try {
      System.out.println(dateFormat.parse("2021-04-08 08:35:49"));
    } catch (ParseException e) {
      e.printStackTrace();
    }
    

可以使用的格式:

java.util.Calendar(日历)类

Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。

获取Calendar实例的方法

  • 使用Calendar.getInstance()方法,返回一个子类GregorianCalendar的实例
  • 使用它的子类GregorianCalendar的构造器

常用方法:

  1. public void set(int field,int value)

    Calendar calendar = Calendar.getInstance();
    calendar.get(Calendar.DAY_OF_YEAR)
    
  2. public int get(int field)

    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.DAY_OF_YEAR,1);
    
  3. public void add(int field,int amount)

    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DAY_OF_YEAR, 10);
    
  4. public final Date getTime()

    Calendar calendar = Calendar.getInstance();
    calendar.getTime()
    
  5. public final void setTime(Date date)

    Calendar calendar = Calendar.getInstance();
    Date date = new Date();
    calendar.setTime(date)
    

注意:

  • 获取月份时:一月是0,二月是1,以此类推,12月是11
  • 获取星期时:周日是1,周二是2 ,....,周六是7

JDK8中新日期时间API

1.LocalDate 、LocalTime 、LocalDateTime

LocalDate、LocalTime、LocalDateTime 类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。

  • LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
  • LocalTime表示一个时间,而不是日期。
  • LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历。

常用方法:

2. 瞬时:Instant

  • Instant:时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。
  • 在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
  • java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。
  • 概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
  • (1ns = 10-9s) 1秒 = 1000毫秒 =106 微秒=109纳秒

常用方法:

3. java.time.format.DateTimeFormatter类

该类提供了三种格式化方法:

  • 预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
  • 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG)
  • 自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)

Java比较器

在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。

ava实现对象排序的方式有两种:

  • 自然排序:java.lang.Comparable
  • 定制排序:java.util.Comparator

自然排序:Comparable接口

  • Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
  • 实现 Comparable的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。
    • 如果当前对象this大于形参对象obj,则返回正整数;
    • 如果当前对象this小于形参对象obj,则返回负整数;
    • 如果当前对象this等于形参对象obj,则返回零;
  • 实现Comparable接口的对象列表(和数组)可以通过 Collections.sortArrays.sort 进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
  • 对于类C的每一个e1e2来说,当且仅当 e1.compareTo(e2) == 0 与e1.equals(e2) 具有相同的boolean值时,类C的自然排序才叫做与equals一致。建议(虽然不是必需的)最好使自然排序与equals一致。

例:

自定义的类,实现Comparable接口:

class Goods implements Comparable {
  private String name;
  private double price;
  //按照价格,比较商品的大小
  @Override
  public int compareTo(Object o) {
    if(o instanceof Goods) {
      Goods other = (Goods) o;
      if (this.price > other.price) {
        return 1;
      } else if (this.price < other.price) {
        return -1;
      }
      return 0;
    }
    throw new RuntimeException("输入的数据类型不一致");
  }
  //构造器、getter、setter、toString()方法略
}

对自定义的类进行排序,依据价格进行降序排列:

Goods[] all = new Goods[4];
all[0] = new Goods("《红楼梦》", 100);
all[1] = new Goods("《西游记》", 80);
all[2] = new Goods("《三国演义》", 140);
all[3] = new Goods("《水浒传》", 120);
Arrays.sort(all);
System.out.println(Arrays.toString(all));

定制排序:java.util.Comparator接口

  • 当元素的类型没有实现java.lang.Comparable 接口而又不方便修改代码,或者实现了java.lang.Comparable 接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来 排序,强行对多个对象进行整体排序的比较。
  • 重写compare(Object o1,Object o2)方法,比较o1o2的大小: 如果方法返回正整数,则表示o1大于o2;如果返回0表示相等;返回负整数,表示o1小于o2
  • 可以将Comparator传递给sort方法(如Collections.sortArrays.sort),从而允许在排序顺序上实现精确控制。
  • 还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

例:参考Comparable接口中的Goods类,取消对Comparable接口的实现:

class Goods implements Comparable {
  private String name;
  private double price;
  //构造器、getter、setter、toString()方法略
}

在调用Arrays.sort()方法时,传入一个Comparator接口的匿名实现类,实现按价格的降序排序

Goods[] all = new Goods[4];

all[0] = new Goods("《红楼梦》", 100);
all[1] = new Goods("《西游记》", 80);
all[2] = new Goods("《三国演义》", 140);
all[3] = new Goods("《水浒传》", 120);

// 这里创建一个Comparator接口的匿名实现类,用来进行比较价格
Arrays.sort(all, new Comparator<Goods>() {
  @Override
  public int compare(Goods o1, Goods o2) {
    if (o1.getPrice() == o2.getPrice()) {
      return 0;
    } else if (o1.getPrice() < o2.getPrice()) {
      return 1;
    } else {
      return -1;
    }
  }
});

System.out.println(Arrays.toString(all));

Comparable接口与Comparator接口对比:

  • Comparable接口的方式一旦指定,保证Comparable接口实现类的对象在任何位置都可以比较大小;

  • Comparator接口属于临时性的比较;

System类

  • System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。

  • 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。

  • 成员变量

    • System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
  • 成员方法

    • native long currentTimeMillis():
      该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。

    • void exit(int status):
      该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。 使用该方法可以在图形界面编程中实现程序的退出功能等。

    • void gc():
      该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。

    • String getProperty(String key):
      该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示:

例:

String javaVersion = System.getProperty("java.version");
System.out.println("java的version:" + javaVersion);

String javaHome = System.getProperty("java.home");
System.out.println("java的home:" + javaHome);

String osName = System.getProperty("os.name");
System.out.println("os的name:" + osName);

String osVersion = System.getProperty("os.version");
System.out.println("os的version:" + osVersion);

String userName = System.getProperty("user.name");
System.out.println("user的name:" + userName);

String userHome = System.getProperty("user.home");
System.out.println("user的home:" + userHome);

String userDir = System.getProperty("user.dir");
System.out.println("user的dir:" + userDir);

Math类

java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回值类型一般为double型。

  • abs:绝对值
  • acos,asin,atan,cos,sin,tan:三角函数
  • sqrt:平方根
  • pow(double a,doble b):a的b次幂
  • log:自然对数
  • exp:e为底的指数
  • max(double a,double b)/min(double a,double b):最大/最小值
  • random():返回0.0到1.0的随机数
  • long round(double a):double型数据a转换为long型(四舍五入)
  • toDegrees(double angrad):弧度—>角度
  • toRadians(double angdeg):角度—>弧度

BigInteger与BigDecimal

BigInteger类

  • Integer类作为int的包装类,能存储的最大整型值为231-1,Long类也是有限的,最大为263-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。
  • java.math包的BigInteger可以表示不可变的任意精度的整数。BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。
  • 构造器
    BigInteger(String val):根据字符串构建BigInteger对象

常用方法

  • public BigInteger abs():返回此 BigInteger 的绝对值的 BigInteger。
  • BigInteger add(BigInteger val):返回其值为 (this + val) 的 BigInteger
  • BigInteger subtract(BigInteger val):返回其值为 (this - val) 的 BigInteger
  • BigInteger multiply(BigInteger val):返回其值为 (this * val) 的 BigInteger
  • BigInteger divide(BigInteger val):返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。
  • BigInteger remainder(BigInteger val):返回其值为 (this % val) 的 BigInteger。
  • BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个 BigInteger 的数组。
  • BigInteger pow(int exponent):返回其值为(thisexponent) 的BigInteger。

BigDecimal类

  • 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。

  • BigDecimal类支持不可变的、任意精度的有符号十进制定点数

  • 构造器

    • public BigDecimal(double val)
    • public BigDecimal(String val)

常用方法

  • public BigDecimal add(BigDecimal augend)
  • public BigDecimal subtract(BigDecimal subtrahend)
  • public BigDecimal multiply(BigDecimal multiplicand)
  • public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

例:

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

推荐阅读更多精彩内容