• Java入门笔记 06-常用类


    介绍:本章将介绍Java的一些常用类,内容不完整,会在后续使用过程中逐步完善。

    一、 字符串相关类

      1. String类介绍

    |--- String类声明为final的,不能被继承;

    |--- 实现了Serializable接口:表示字符串是支持序列化的;

    |--- 实现了Comparable接口:表示String可以比较大小;

    |--- String内部定义了final char[] value用于存储字符串数据;

    |--- Sring类字符串具有不可变性:当对字符串重新赋值、对现有的字符串进行连接操作、调用String的replace()方法时,需要重新指定内存区域赋值,不能使用原有的value进行赋值。

      2. String的实例化方式

    • 通过字面量的方式:
    //通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
    String s1 = "javaEE";
    String s2 = "javaEE";
    • 通过构造器来构造:
    //通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
    String s3 = new String("javaEE");
    String s4 = new String("javaEE");
    System.out.println(s1 == s2);//true
    System.out.println(s1 == s3);//false
    System.out.println(s1 == s4);//false
    System.out.println(s3 == s4);//false

      3. String的拼接操作

    • 常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量。(final的变量当做常量)
    String s3 = "javaEEhadoop";
    String s4 = "javaEE" + "hadoop";
    System.out.println(s3 == s4);//true
    • 只要其中有一个是变量,结果就在堆中。
    String s5 = s1 + "hadoop";
    System.out.println(s3 == s5);//false
    
    String s6 = "javaEE" + s2;
    System.out.println(s3 == s6);//false
    
    String s7 = s1 + s2;
    System.out.println(s3 == s7);//false
    • 如果拼接的结果调用intern()方法,返回值就在常量池中。
    String s8 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
    System.out.println(s3 == s8);//true

      一道题目

     1 String str = new String("good");
     2 char[] ch = { 't', 'e', 's', 't' };
     3 
     4 public void change(String str, char ch[]) {
     5     str = "test ok";//String的不可变性
     6     ch[0] = 'b';
     7 }
     8 @Test
     9 public void test(){
    10     StringClass ex = new StringClass();
    11     ex.change(ex.str, ex.ch);
    12     System.out.println(ex.str);//good
    13     System.out.println(ex.ch);//best
    14 }

      4. String类常用的方法

    • 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 join:将多个字符串放在一起,用一定界符分割。
    String all = String.join("/","A"/,"B","C");//输出A/B/C
     1 @Test
     2     public void test1(){
     3         String s1 = "  12345678  9aBcdEfg9  ";
     4         String s2 = "  12345678  9aBcdEfG9  ";
     5         System.out.println(s1);
     6 
     7         System.out.println("s1.length: " + s1.length());
     8         System.out.println("s1.charAt(1): " + s1.charAt(1));
     9         System.out.println("s1.isEmpty(): " + s1.isEmpty());
    10         System.out.println("s1.toLowerCase(): " + s1.toLowerCase());
    11         System.out.println("s1.toUpperCase(): " + s1.toUpperCase());
    12         System.out.println("s1.trim(): " + s1.trim());
    13         System.out.println("s1.equals(s2): " + s1.equals(s2));
    14         System.out.println("s1.equalsIgnoreCase(s2): " + s1.equalsIgnoreCase(s2));
    15         System.out.println("s1.concat("demo"): " + s1.concat("demo ") + "s1: " + s1);
    16         System.out.println("s1.compareTo(s2): " + s1.compareTo(s2));
    17         System.out.println("s1.substring(3): " + s1.substring(3));
    18         System.out.println("s1.substring(3,4): " + s1.substring(3,4));
    19         System.out.println("s1.endsWith("g"): " + s1.endsWith("g"));
    20         System.out.println("s1.endsWith("g  "): " + s1.endsWith("g  "));
    21         System.out.println("s1.startsWith("  "): " + s1.startsWith("  "));
    22         System.out.println("s1.startsWith("78",8): " + s1.startsWith("78",8));
    23         System.out.println("s1.contains("12"): " + s1.contains("12"));
    24         System.out.println("s1.indexOf("12"): " + s1.indexOf("12"));
    25         System.out.println("s1.indexOf("g",3): " + s1.indexOf("g",3));
    26         System.out.println("s1.lastIndexOf("9"): " + s1.lastIndexOf("9"));
    27         System.out.println("s1.lastIndexOf("9",10): " + s1.lastIndexOf("9",22));
    28         System.out.println("s1.replace("678","666"): " + s1.replace("678","666"));
    29     }

    输出:
      12345678  9aBcdEfg9  
    s1.length: 23
    s1.charAt(1):  
    s1.isEmpty(): false
    s1.toLowerCase():   12345678  9abcdefg9  
    s1.toUpperCase():   12345678  9ABCDEFG9  
    s1.trim(): 12345678  9aBcdEfg9
    s1.equals(s2): false
    s1.equalsIgnoreCase(s2): true
    s1.concat("demo"):   12345678  9aBcdEfg9  demo s1:   12345678  9aBcdEfg9  
    s1.compareTo(s2): 32
    s1.substring(3): 2345678  9aBcdEfg9  
    s1.substring(3,4): 2
    s1.endsWith("g"): false
    s1.endsWith("g  "): false
    s1.startsWith("  "): true
    s1.startsWith("78",8): true
    s1.contains("12"): true
    s1.indexOf("12"): 2
    s1.indexOf("g",3): 19
    s1.lastIndexOf("9"): 20
    s1.lastIndexOf("9",10): 20
    s1.replace("678","666"):   12345666  9aBcdEfg9

      5. String与char[]之间的转换

    • String --> char[]:调用String的toCharArray()
    • char[] --> String:调用String的构造器
     1     @Test
     2     public void test2(){
     3         String str1 = "abc123";  //题目: a21cb3
     4 
     5         char[] charArray = str1.toCharArray();
     6         for (int i = 0; i < charArray.length; i++) {
     7             System.out.println(charArray[i]);
     8         }
     9 
    10         char[] arr = new char[]{'h','e','l','l','o'};
    11         String str2 = new String(arr);
    12         System.out.println(str2);
    13     }

      6.  String 与 byte[]之间的转换

    • 编码:String --> byte[]:调用String的getBytes()
    • 解码:byte[] --> String:调用String的构造器

      

     1 @Test
     2     public void test3() throws UnsupportedEncodingException {
     3         String str1 = "abc123中国";
     4         byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。
     5         System.out.println(Arrays.toString(bytes));//Arrays.toString遍历数组
     6 
     7         byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。
     8         System.out.println(Arrays.toString(gbks));
     9 
    10         System.out.println("******************");
    11 
    12         String str2 = new String(bytes);//使用默认的字符集,进行解码。
    13         System.out.println(str2);
    14 
    15         String str3 = new String(gbks);
    16         System.out.println(str3);//出现乱码。原因:编码集和解码集不一致!
    17 
    18 
    19         String str4 = new String(gbks, "gbk");
    20         System.out.println(str4);//没有出现乱码。原因:编码集和解码集一致!
    21    
    22 
    23     }

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

      7. StringBuffer和StringBuilder的介绍

    • String:不可变的字符序列;底层使用char[]存储
    • StringBuffer: 可变的字符序列;线程安全的,效率低;底层使用char[]存储
    • StringBuilder: 可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
    1 @Test
    2     public void test1(){
    3         StringBuffer sb1 = new StringBuffer("abc");
    4         sb1.setCharAt(0,'m');
    5         System.out.println(sb1);//mbc
    6     }

      StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器:

    • StringBuffer():初始容量为16的字符串缓冲区
    • StringBuffer(int size):构造指定容量的字符串缓冲区
    • StringBuffer(String str):将内容初始化为指定字符串内容

       StringBuilder的构造器与StringBuffer一致。

      8. StringBuffer和StringBuilder的常用方法: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)

      总结

    • 增:append(xxx)
    • 删:delete(int start,int end)
    • 改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
    • 查:charAt(int n )
    • 插:insert(int offset, xxx)
    • 长度:length();
    • *遍历:for() + charAt() / toString()

      9. String、StringBuffer和StringBuilder的效率对比:StringBuilder > StringBuffer > String(从高到低排列)

    二、 时间相关API

    1. java.lang.System类:System类提供currentTimeMillis()方法,返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差,称为时间戳。

    @Test
     public void test1(){
         long time = System.currentTimeMillis();
         System.out.println(time);
    }

    2. java.util.Date类

    • 构造器一:Date():创建一个对应当前时间的Date对象
    • 构造器二:Date(long date)创建指定毫秒数的Date对象
    Date date1 = new Date();
    Date date2 = new Date(176030620410L);
    • toString():显示当前的年、月、日、时、分、秒
    • getTime():获取当前Date对象对应的毫秒数。(时间戳)
    Date date1 = new Date();
    System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019
    System.out.println(date1.getTime());//1550306204104

    3. java.text.SimpleDateFormat类:可以非常灵活地格式化Date,也可以用于解析各种格式的日期字符串。

    //实例化SimpleDateFormat:使用默认的构造器
    SimpleDateFormat sdf = new SimpleDateFormat();
    • 格式化:日期 --->字符串
    //格式化:日期 --->字符串
    Date date = new Date();
    String format = sdf.format(date);
    System.out.println(format);//20-2-12 下午5:44
    • 解析:字符串 ---> 日期
    //解析:格式化的逆过程,字符串 ---> 日期
    String str = "19-12-18 上午11:43";
    Date date1 = sdf.parse(str);
    System.out.println(date1);//Wed Dec 18 11:43:00 CST 2019
    • 按照指定的方式格式化和解析:调用带参的构造器
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");//调用带参的构造器
    
    //格式化
    String format1 = sdf1.format(date);
    System.out.println(format1);//2019-02-18 11:48:27
    
    //解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),
            //否则,抛异常
    Date date2 = sdf1.parse("2020-02-18 11:48:27");
    System.out.println(date2);//Tue Feb 18 11:48:27 CST 2020

      4. java.util.Calendar(日历)类:Calendar是一个抽象基类,主要用于完成日期字段之间相互操作的功能。

      获取Calendar实例的方法

    • 使用Calendar.getInstance()方法
    Calendar calendar1 = Calendar.getInstance();
    • 调用它的子类GregorianCalendar的构造器
    GregorianCalendar calendar2 = new GregorianCalendar();

      常用方法

       5. JDK8中新日期时间API

    三、 Java比较器:Java中的对象,正常情况下,只能进行比较:==  或  != 。不能使用 > 或 < 的,但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。如何实现?使用两个接口中的任何一个:Comparable 或 Comparator

      1. 自然排序:Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。实现Comparable接口的对象列表(和数组)可以通过 Collections.sort 或 Arrays.sort进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

    1 @Test
    2     public void test1(){
    3         String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
    4 
    5         Arrays.sort(arr);
    6         System.out.println(Arrays.toString(arr));
    7     }
    • String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式;
    • String、包装类重写compareTo()方法以后,进行了从小到大的排列;
    • 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo的方法。
    • Comparable 的典型实现:String、Character、数值类型对应的包装类以及BigInteger、BigDecimal、Boolean、Date、Time等。(默认都是从小到大排列的)

      对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo的方法。

    public class Goods implements  Comparable{
    
        private String name;
        private double price;
    
        public Goods() {
        }
    
        public Goods(String name, double price) {
            this.name = name;
            this.price = price;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
        }
    
        @Override
        public String toString() {
            return "name: " + this.name + ", price: " + price + "
    ";
        }
    
        @Override
        //方式一:按照价格从低到高排序
        public int compareTo(Object o) {
            if (o instanceof Goods) {
                Goods goods = (Goods)o;
                return Double.compare(this.price, goods.price);
            }
            throw new RuntimeException("传入的数据类型不一致!");
        }
    
        /*
        @Override
        //方式二:按照价格从低到高排序,并且按照产品名称从低到高排序.
        public int compareTo(Object o) {
            if(o instanceof Goods){
                Goods goods = (Goods)o;
                if(this.price > goods.price){
                    return 1;
                }else if(this.price < goods.price){
                    return -1;
                }else{
    //                return 0;//仅按照价格从低到高排序
                    return this.name.compareTo(goods.name);
                }
            }
            throw new RuntimeException("传入的数据类型不一致!");
        }*/
    }
    
    @Test
    public void test2(){
        Goods[] arr = new Goods[5];
        arr[0] = new Goods("lenovoMouse",34);
        arr[1] = new Goods("dellMouse",12);
        arr[2] = new Goods("xiaomiMouse",12);
        arr[3] = new Goods("huaweiMouse",65);
        arr[4] = new Goods("microsoftMouse",43);
    
        Arrays.sort(arr);
    
        System.out.println(Arrays.toString(arr));
    }

      重写compareTo(obj)的规则

    •     如果当前对象this大于形参对象obj,则返回正整数,
    •     如果当前对象this小于形参对象obj,则返回负整数,
    •     如果当前对象this等于形参对象obj,则返回零。

      2. 定制排序:当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 接口的对象来排序。

      重写compare(Object o1,Object o2)方法,比较o1和o2的大小:

    • 如果方法返回正整数,则表示o1大于o2;
    • 如果返回0,表示相等;
    • 返回负整数,表示o1小于o2。
     1 public void test3(){
     2     String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};
     3     Arrays.sort(arr,new Comparator(){
     4 
     5         //按照字符串从大到小的顺序排列
     6         @Override
     7         public int compare(Object o1, Object o2) {
     8             if(o1 instanceof String && o2 instanceof  String){
     9                 String s1 = (String) o1;
    10                 String s2 = (String) o2;
    11                 return -s1.compareTo(s2);
    12             }
    13             throw new RuntimeException("输入的数据类型不一致");
    14         }
    15     });
    16     System.out.println(Arrays.toString(arr));
    17 }
     1 @Test
     2 public void test4(){
     3     Goods[] arr = new Goods[6];
     4     arr[0] = new Goods("lenovoMouse",34);
     5     arr[1] = new Goods("dellMouse",43);
     6     arr[2] = new Goods("xiaomiMouse",12);
     7     arr[3] = new Goods("huaweiMouse",65);
     8     arr[4] = new Goods("huaweiMouse",224);
     9     arr[5] = new Goods("microsoftMouse",43);
    10 
    11     Arrays.sort(arr, new Comparator() {
    12         //指明商品比较大小的方式:按照产品名称从低到高排序,再按照价格从高到低排序
    13         @Override
    14         public int compare(Object o1, Object o2) {
    15             if(o1 instanceof Goods && o2 instanceof Goods){
    16                 Goods g1 = (Goods)o1;
    17                 Goods g2 = (Goods)o2;
    18                 if(g1.getName().equals(g2.getName())){//名字一样,按照价格排序
    19                     return -Double.compare(g1.getPrice(),g2.getPrice());
    20                 }else{
    21                     return g1.getName().compareTo(g2.getName());
    22                 }
    23             }
    24             throw new RuntimeException("输入的数据类型不一致");
    25         }
    26     });
    27     System.out.println(Arrays.toString(arr));
    28 }

    3. Comparable接口与Comparator的使用的对比

    • Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
    • Comparator接口属于临时性的比较。

    四、 System类

      1. 常用方法

    • native long currentTimeMillis():该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。
    •  void exit(int status):该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。
    • void gc():该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。
    • String getProperty(String key):该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示:

    五、 Math类

      1. 常用方法

    • xxxValue():将 Number 对象转换为xxx数据类型的值并返回。
    • compareTo():将number对象与参数比较。
    • equals():判断number对象是否与参数相等。
    • valueOf():返回一个 Number 对象指定的内置数据类型
    • toString():以字符串形式返回值。
    • parseInt():将字符串解析为int类型。
    • abs():返回参数的绝对值。
    • ceil():返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型。
    • floor():返回小于等于(<=)给定参数的最大整数 。
    • rint():返回与参数最接近的整数。返回类型为double。
    • round():它表示四舍五入,算法为 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以Math.round(11.5) 的结果为12,Math.round(-11.5) 的结果为-11。
    • min():返回两个参数中的最小值。
    • max():返回两个参数中的最大值。
    • exp():返回自然数底数e的参数次方。
    • log():返回参数的自然数底数的对数值。
    • pow():返回第一个参数的第二个参数次方。
    • sqrt():求参数的算术平方根。
    • sin():求指定double类型参数的正弦值。
    • cos():求指定double类型参数的余弦值。
    • tan():求指定double类型参数的正切值。
    • asin():求指定double类型参数的反正弦值。
    • acos():求指定double类型参数的反余弦值。
    • atan():求指定double类型参数的反正切值。
    • atan2():将笛卡尔坐标转换为极坐标,并返回极坐标的角度值。
    • toDegrees():将参数转化为角度。
    • toRadians():将角度转换为弧度。
    • random():返回一个随机数。

    六、 BigInteger和BigDecimal

     1 @Test
     2 public void test4(){
     3     Goods[] arr = new Goods[6];
     4     arr[0] = new Goods("lenovoMouse",34);
     5     arr[1] = new Goods("dellMouse",43);
     6     arr[2] = new Goods("xiaomiMouse",12);
     7     arr[3] = new Goods("huaweiMouse",65);
     8     arr[4] = new Goods("huaweiMouse",224);
     9     arr[5] = new Goods("microsoftMouse",43);
    10 
    11     Arrays.sort(arr, new Comparator() {
    12         //指明商品比较大小的方式:按照产品名称从低到高排序,再按照价格从高到低排序
    13 @Override
    14 public int compare(Object o1, Object o2) {
    15             if(o1 instanceof Goods && o2 instanceof Goods){
    16                 Goods g1 = (Goods)o1;
    17                 Goods g2 = (Goods)o2;
    18                 if(g1.getName().equals(g2.getName())){
    19                     return -Double.compare(g1.getPrice(),g2.getPrice());
    20                 }else{
    21                     return g1.getName().compareTo(g2.getName());
    22                 }
    23             }
    24             throw new RuntimeException("输入的数据类型不一致");
    25         }
    26     });
    27     System.out.println(Arrays.toString(arr));
    28 }

    七、Scanner类:获取用户的键盘输入,主要提供两个方法:

    • hasNextXxx:判断是否还有下一项Int、Boolean等代表基本数据类型的字符串,若只是判断是否有下一行字符串,只用hasNext
    • nextXxx:获取下一项输入

      默认情况下,Scanner使用空白(空格、Tab、回车)作为分隔符,另外,Java也提供了两个简单的方法来进行逐行输入:

    • hasNextLine:判断是否有下一行
    • nextLine:获取下一行
  • 相关阅读:
    OCP-1Z0-053-V13.02-252题
    Java中list.get(index)报错
    OCP-1Z0-053-V13.02-103题
    Hash unique和Sort unique
    如何解决mysql数据库8小时无连接自动关闭
    OCP-1Z0-053-V13.02-538题
    OCP-1Z0-053-V13.02-537题
    OCP-1Z0-053-V13.02-518题
    用绘本回忆青春创业经历——leo鉴书46
    OCP-1Z0-053-V13.02-502题
  • 原文地址:https://www.cnblogs.com/dailymatters/p/12293785.html
Copyright © 2020-2023  润新知