• javaSE阶段重要知识点梳理


    JavaSE

    一、 基础语法

    1、        基本数据类型

      1、

    数据类型

    字节数

    二进制位数

    byte

    1

    8

    short

    2

    16

    int

    4

    32

    long

    8

    64

    float

    4

    32

    double

    8

    64

    char

    2

    16

    boolean

    1

    1

      boolean类型的值只有true和false两种逻辑值,在编译后会使用1和0来表示,这两个数在内存中按位算,仅需1位(bit)即可存储,位是计算机最小的存储单位。

      数据类型char在中文编码GBK-2312中占2个字节,在UTF-8中占3个字节。

    2、判断与循环

    逻辑运算符

    作用

    &

    与作用,左右两边两个判断结果都为true就是true,否则是false

    |

    或作用,左右两边两个判断结果只要有一边为true,结果就是true,否则就是false

    ^

    异或,你为true我就是false

    &&

    左右两边,左边如果为false,右边不执行,直接返回结果false

    ||

    同理:左边如果为true,右边不执行直接返回true

      第一种:

      if(条件表达式){

        语句块1

      }else{

        语句块2

      }

      第二种:

      if(条件表达式){

        语句块1

      }else if{

        语句块2

      } else {

      以上条件均不满足而执行的默认的逻辑

      }

      第三种:

      switch(被判断的变量)

      {

      case 条件1:

        执行条件1的逻辑

        break;

      case 条件2:

        执行条件2的逻辑

        break;

      case n:

        执行条件n的逻辑

        break;

      default:

        以上条件均不满足而执行的默认的逻辑

      }

      第四种:

               While(判断语句){

            循环体

       }

      Do{

          循环体

      }while(判断语句);

      While是先判断后执行,do while 是先执行循环体语句,然后进行判断语句,就是说无论判断是否为true都会至少执行一次循环体语句。

    3、数组

      1、 数组是对应的容器集合,需要存储同类的数据,在一个容器中。

      2、 数组使用中,需要规定数组的长度,长度从1开始,设置后长度不可变。

      3、 数组使用中,定义数据的类型为:Object,可以存储所有类型:,否则根据需要定义类型,可以是对象也可以是基本数据类型

      4、 数组要想遍历(一个个的拿到数据内的数据),我们需要for循环来完成,知道循环的次数,一个个的获取。数组获取中,如果超过角标,那么就会报错:角标越

        界异常:indexoutofboundsException,因为角标从0开始

      5、 排序算法:(选择和冒泡)

      1、冒泡排序算法

     

           2、选择排序算法

         

    二、 面向对象

    1、        封装

        1、封装属性:对属性的访问权限进行了封装,私有的只能自己用,公共的谁都可以用。

                  2、封装方法:普通方法也可以使用关键字进行封装,效果一样,私有的方法别人不能用。

                  3、对私有的属性,可以封装一套获取方法和设置方法,分别是set和get

                         Set别人设置我当前的私有属性的数据。

                         Get别人获取使用我当前的私有属性的数据。

                  4、在创建对象的时候进行封装:构造方法

                         创建对象的过程中,我需要创建出来的对象是空对象,那么就用空参构造方法。

                         创建对象的过程中,我需要创建出来的对象是带有数据的对象,不是一个空对象,那么我们就使用有参构造方法来创建。

                         如果对象中不写构造方法,默认就有空参,如果写了那么就使用写了的,如果只写有参不写空参,那么就无法使用空参创建对象。默认的空参在你写了有参的时候就会消失,需要手动再写空参才能正常使用。

                  5、构造方法和普通方法一样,可以方法名相同,但是参数数据类型与数量不能相同,必须在同一个对象中,我们称之为是方法的重载。

                         重载:方法名必须相同,参数类型不同。

                         重载:可以修改方法的访问修饰符,可以修改返回类型,无返回还是有返回。

        6、封装四个关键字使用的范围

    位置

    private

    默认的

    protected

    public

    同一个类

    同一个包内的类

    不同包内的子类

    不同包并且不是子类

             注意:当前属性如果需要完全给别人使用,我们就需要设置公共的:public

             注意:当前属性如果只能自己完成,别人不能使用,我们就需要设置私有的:private

             注意:private 使用:方法:get方法与set方法,可以被别人使用。

    2、        继承

      1、 继承:子类继承父类,子类可以使用父类的所有的非私有的属性和方法

           2、方法的重写,需要注意事项:

                         1、方法名必须相同。

                         2、必须是继承或者实现关系。

                         3、重写方法传递的形参必须与父类的方法保持一致,不能设置其他形参。

          4、重写的方法,访问权限封装关键字:子类的访问权限可以修改,但是权限的大小一定要比父类大。不能比父类小。

                  3、方法的重载和重写的区别

          1、方法的重写:必须有子父类的关系,子类重写父类的方法,提升方法的功能,重写方法中,修饰符子类需要比父类更大不能更小,也可以相同。方法名必须相

          同,参数和返回值也必须相同。

          2、方法的重载:在当前自身对象中,提供多种相同的方法,但是有区别的是,修饰符可以不一样、返回值可以不一样、但是方法名必须相同、小括号内参数,参

          数类型不同或者参数数量不同来区分。

                         一个是发生在自己当前对象的:方法重载:overload

                         一个是发生在子父类对象的:方法重写:override

    3、        多态

       1、多态创建出来的对象,可以执行调用父类的非私有的属性和方法。

       2、使用多态,如果要想调用子类的方法,那么子类的方法,必须是父类重写的方法。否则无法调用。

        当前使用多态创建出来的对象,如果当前的方法,是子类重写父类的方法,那么就优先调用执行。如果当前的方法,子类没有重写父类的方法,

        那么调用的就是父类的方法。

            3、多态调用执行:

                    1、多态优先调用子类重写父类的方法。

                    2、多态可以调用父类的非私有方法。

                    3、多态可以调用父类的非私有属性。

                    4、多态不能调用子类的非私有属性。

                    5、多态不能调用子类的非私有方法。

            4、解决多态不能调用子类的非私有属性和方法。

    如果使用多态想要调用子类的特有属性和方法的话。我们需要将多态创建出来的对象,进行强制类型转换。将父类转换成子类。

            5、多态使用的细节总结:

                  1、使用多态必须是有继承或者实现关系的。

        2、使用多态我们如果需要调用子类的方法,必须是重写方法,重写方法的权限最大。

        3、使用多态创建出来的对象,不能调用子类的特有属性和方法,如果真想调用,我们就需要使用到强转。

        注意:在强转的过程中,大家很有可能会出现一个异常:ClassCaseException:类型转换异常。

        4、注意:为了避免后期大家出现类型转换的错误,我们事先可以先测试判断一下类型转换是否正确:instanceof

        5、当前我们可以使用到,关键字进行判断,判断出来的结果,就是正确或者错误,我们判断当前转换的类型,是否合格,如果合格我们就转换,如果不合格我们

        就不转换,就能解决类型异常的问题。

    4、        抽象类

        1、抽象类:光定义方法但不实现方法。

        2、抽象方法,只能出现在抽象类或者是接口中,不能出现在普通方法中。抽象方法:当前只定义,不实现。例:父亲的愿望需要儿子去实现(俗称:负债子还)。

       

            3、创建一个抽象类:关键字:abstract。

    抽象类,其实和普通类类似,可以有属性、构造方法、普通方法、get方法set方法等等。但是,抽象类中还可以定义抽象方法。

            4、在抽象类中,定义抽象方法

     

      5、子类如果实现的是抽象类,那么必须实现抽象方法(一定要对抽象方法进行重写)

            如果子类不重写父类的抽象方法,就会一直报错。

            6、创建对象的过程中,抽象类是不允许被创建出来的

                抽象的对象,是不允许直接被new创建的。

                抽象类,如果要想创建,我们就需要使用对应的实现类(儿子创建)

      7、 抽象类的出现:抽象类当中存在抽象方法,如果要想实现,那么就需要子类完成。但是抽象类,又不能直接初始化,还是得用多态来初始化。

            多态的产生,其实有两部分的原因:

        1、 多态让创建对象,变的更加的灵活,根据用户的选择可以动态的进行创建。

        2、 抽象类与接口是不能直接被创建的,而是需要通过多态的创建方式,让子类具体实现,完成创建工作。

            8、抽象类总结:

                1、抽象类不能被final修饰,因为抽象类必须要有儿子。

           2、抽象类不能在方法中使用 final修饰,因为抽象类的抽象方法必须要被重写。

                3、抽象类可以继承抽象类,所有的抽象方法需要在实现类中一起实现。

                4、抽象类中可以有抽象方法,也可以有普通方法、属性、构造方法。

    5、        接口

      1、定义接口关键字:interface

           2、接口中方法都是抽象方法,不能有方法体。不能有普通方法,不能有构造方法。当前的接口中,只能有抽象方法,不能有实体方法。

           3、定义接口中的属性:接口中不能有变量,只能存在常量。当前的属性也可以使用静态来修饰。

           使用静态修饰的好处:接口对象名称调用属性可以直接调用。

        4、接口能否被final修饰?

      1、接口是不能被final修饰的。

      2、接口中的方法不能被final修饰。

      3、接口中的属性可以被final修饰,因为默认就是使用的final。

     5、使用接口:接口是不能直接被new出来的。所以当前必须先有实现类。

     6、Java的关系映射:单继承、多实现。

      单继承:对象与对象间,继承只能继承1次。

      多实现:对象与接口,一个对象可以实现多个接口。

     7、接口和抽象类的区别:

        1、抽象类可以有普通方法和抽象方法。

             2、抽象类中定义的属性是变量。

             3、抽象类可以继承抽象类或者普通类。

             4、抽象类可以实现接口,但是没有必要。

             5、抽象类不能直接创建,需要用多态的方法创建。

      1、接口中只能有抽象的方法。

      2、接口中属性是常量定义。

      3、接口可以继承接口,但不能实现。

      4、接口继承是多继承,抽象类继承是单继承。

      5、不管是接口还是抽象类,对象不能使用final修饰。方法不能使用final修饰。

        因为后期在Java中开发项目,项目后期肯定要维护升级的,我们如果使用抽象类,就只能单继承,不方便维护,让项目加大了局限性。而使用接口的话,实现的接口数

      量不限,可以多实现,解决了项目后期更新的局限性问题。

      理解:

             抽象类就相当于是领导,领导定义任务,员工完成。

             接口就相当于是规范,接口制定规范,所有员工需要遵守。

    6、        关键字

      1、 this关键字

      this对象:this关键字就是当前对象的引用关键字,用来区分传递形参与当前对象内的参数区别开来。

      2、 super关键字

      1、构造方法中使用super:

      Super必须放在前面。

      Super需要带有小括号。带有小括号就会调用父类对应的构造方法。

           2、super在普通方法中使用

      Super如果在普通方法中使用,那么直接使用super即可,不需要添加小括号,不添加小括号的super,是直接引用的父类对象,不是构造方法。

      3、 static关键字

      1、static关键字修饰方法,我们无需new对象,即可通过类名调用方法。

           2、static关键字修饰属性,我们无需new对象,即可通过类名调用属性。

           3、原因:static修饰的属性和方法,在生成.class文件的时候就会被初始化出来。

           4、static可以设置一个静态代码块:

    Static{    }:运行的方式,是程序执行的时候,第一次就调用的,运行权限比构造方法还要大。程序一运行,第一个就调用的是静态代码块。

                  使用场景:后期初始化数据的时候,我们会使用到。

           5、static缺点:如果是static修饰的属性和方法,声明周期就会拉长,内存数据回收就会变得不方便,有些占用内存,后期在项目中,如果数据庞大的情况下,还是尽量少使用。

      4、 final关键字

        1、final可以修饰class属性对象,被final所修饰的对象,将不能被继承。

                  2、final可以修饰属性,被修饰的属性,就从变量变成了常量,不可修改。

                  3、final修饰的方法,当前的方法只能使用,不能被重写。

    7、        内部类

      1、内部类:在当前的对象内,再创建一个对象,称之为内部类

      2、我们创建内部类对象规则:

      内部类,必须在类对象的括号内创建,命名不能带有小括号。

      3、初始化内部类。

      注意:因为当前的对象是内部对象,所以先要将外部对象创建出来,再来创建内部对象,外部对象创建内部对象中,使用到了调用,所以创建内部对象的时候,前面需要带

      上小点,进行调用。

      4、内部对象,是否会生成.class文件:

      会生成一个Animal$Cat.class文件,因为是否生成class文件,取决于是否使用了class关键字

      5、内部类一样和外部类使用,只是内部类是写在对象内的。一样也可以封装、继承、多态

      6、匿名对象:就是没有名字的对象,一般使用在创建对象的时候。

      注意:当前调用的方法与创建的对象,只会执行一次,它是一次性写法。因为没有给对象在创建的时候命名,所以方法执行完,对象也会随着消失销毁在内存中。所以如果

      需要再次调用方法,那么就需要重新再次创建对象。

      7、匿名内部类:在当前类中创建一个类,但是不给名称。(因为抽象类与接口不能直接被new,要先直接初始化其实也有方法,我们当前就需要使用匿名内部类)

      8、匿名内部类,也会生成一个class文件,虽然没有明文写出,生成的class文件还是会出来,命名使用的是1命名。

    三、 常用API

    1、引用数据类型:

                  1、基本数据类型:8种:

                         整数类型:byte、short、int、long

                         浮点类型:float、double

                         判断类型:boolean

                         字符类型:char

                  2、引用数据类型:9种:

                         整数类型:Byte、Short、Integer、Long

                         浮点类型:Float、Double

                         判断类型:Boolean

                         字符类型:Character

                         字符串:String

        3、字符和字符串的区别:

                         1、字符是基本数据类型,字符串是引用数据类型。

               2、字符定义的字符可以转换为对应的ascii表,对应的数字,可以进行加减乘除。

          3、字符串因为是常量,数据是在常量池中,只能做对应的加法,是拼接。其余的减乘除都是不行的,因为String是不支持的

                    4、String对象,创建的几种不同方式。

                         直接创建:从常量池就能直接拿到对应的常量数据

                         通过new关键字创建:常量池常量传递给堆,堆再给栈。

          5、String创建的字符串都是常量,不能修改,修改的话就是重新在来生成一个,原有的就会等待回收机制回收。

          6、String构造方法中,我们可以给空的、也可以给具体的String字符串、还可以给对应的数组让数组对应的字符或者数字来生成一个字符串。

          

          7、 自动装箱与自动拆箱

          基本数据类型,转换为引用数据类型,自动装箱。

          引用数据类型,转换为基本数据类型,自动拆箱。

    2、StringBuffer和StringBuilder

         1、String:字符串常量

        StringBuffer:字符串变量

        StringBuilder:字符串变量

           2、线程安全

        StringBuilder:线程非安全的

        StringBuffer:线程安全的

           3、三者使用:

        1.如果要操作少量的数据用 = String

        2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

        3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

           4、

                  1、StringBuffer对象,默认长度是:16

                  2、StringBuilder对象,默认长度是:16

    3、Date

      1、Date日期对象,我们就是通过它将对应的毫秒值转化为我们看的懂的日期对象。

      2、Date对象中,重写了toString方法,里面含有对应的返回日期格式。我们看完后,查看到了对应的英文字母,这些就是定义日期格式的关键字符。

      3、当前我们如果需要自己定义日期显示的样式,我们就需要对应设置自己所需要的字母关键字符,但是Date里面是没有给我们提供设置方法的,我们需要使用到日期的帮助类:SimpleDateFormat

      4、SimpleDateFormat对象,对Date日期对象进行了一个显示内容的封装,让当前的日期,可以根据用户自身的需求进行自定义格式。但是自定义格式中,我们需要通过字母将格式设置完成后,才能使用。

      5、当前Date与SimpleDateFormat对象两个相结合,最终就能得到我们想要的日期格式,日期格式显示出来的内容,就是根据毫秒值来转换的。

      6、日期为什么需要使用毫秒值,避免时间重复,时间计算是需要非常精准的。因为任何项目如果时间戳出了问题,就会产生不可逆的异常。

    四、 集合

    1、        三个集合的区别

     

     

    List

    ArrayList

    底层是数组

    查询速度快,增删改慢

    线程不安全

    效率高

    LinkedList

    底层是双向链表

    增删速度快,查询慢

    线程不安全

    效率高

    Vector

    底层是数组

    查询快,增删慢

    线程安全

    效率低

    ArrayList 初始容量为10,自增长度是(10*3/2)+ 1。

    Vector 内部数据数组的大小为10,其标准容量增量为零。

     

    Set

    HashSet

    底层是HashMap

    存取速度快,

    线程不安全

    无序,不可重复

    可以存储null,但只能放入一个null

    TreeSet

    底层是红黑树

    排序存储

    线程不安全

    有序,不可重复

    不可以存储null

    HashSet其底层HashMap实例的默认初始容量是16,加载因子是0.75

     

    Map

    HashMap

    底层是Hash表(数组+链表)

    效率高

    线程不安全

    键值可以存储null,但只能存储一次,不然会被覆盖

    HashTable

    底层是Hash表(数组+链表)

    效率低

    线程安全

    不可以存储null

    HashMap   默认初始容量是16,加载因子是0.75

    HashTable默认初始容量是11,加载因子是0.75

    2、        集合的关系图

     

      1、汇总:

                  Collection:是集合中的一个接口父类,它是一个分支,子类实现接口中:List和Set

                  Map:也是集合中的一个接口父类,子类中是:HashMap、HashTable

           2、Map接口集合,与Collection区别:

                  Map结构分为:k v,俗称键值对。

                  K:key:键

                  V:value:值

                  在map集合中,k就是键,是主键的意思,不能重复。V就是值,值可以重复。

                  在List集合中,角标就是键,里面的内容就是值。角标从0开始,没有重复的。

    五、 IO流

    1、我们所学习的所有IO:

                  FileWriter:文件的字符输出流,是Writer的子类。

                  FileReader:文件的字符输入流,是Reader的子类。

                  BufferedWriter:字符高效输出流,当前默认有缓冲区。

                  BufferedReader:字符高效输入流,当前默认有缓冲区。

                  FileOutputStream:文件字节输出流,当前是万能输出流。

                  FileInputStream:文件字节输入流,当前是万能输入流。

                  BufferedOutputStream:文件高效字节输出流,当前默认有缓冲区。

                  BufferedInputStream:文件高效字节输入流,当前默认有缓冲区。

          

                  ObjectOutputStream:对象输出流

                  ObjectInputStream:对象输入流

                  只能将支持 java.io.Serializable 接口的对象写入和读取到流中。

    2、文件的嵌套复制,递归

      在文件夹复制的工作中,文件夹中还有存在子文件夹,嵌套存在深度不一致多,有的多有的少。我们需要复制过程中考虑全面,就需要将代码嵌套比较的多,效率极低。

                  当前解决的办法:使用的是递归来解决文件夹嵌套创建的问题。

                  递归的好处:不断调用自身,进行文件夹创建。

      递归使用的注意事项:

        需要递归的过程中,设置出口,停止定义,否则就会一直调用导致堆栈溢出。

                  递归的算法:当前重复调用方法,直到拿到预期的目标跳出递归,进入出口。

                  递归的使用中:

                         入口:调用自身。

                         出口:得到预期的目标,跳出进入出口,结束调用自己。

    3、        IO流,分为字节流和字符流

      1、字节流和字符流

      字符流:只能对文本文档操作的流对象,因为当前读写设置的是char类型的容器。

      字节流:是万能流,不仅可以对文本文档操作,还能对其他类型的文档操作,比如图片、视频、音频等等,因为当前读写设置的容器类型为byte类型,根据字符的编码存储编码的序号数字,进行转译。

      2、字节流与字符流,当前的效率字节流要快于字符流。

      字节流复制文件,一个需要800多毫秒即可,但是字符流复制文件,一个需要7秒,同样的大小200多MB的文件。

        1、 在公司中开发,我们常用的是字节流,因为字节流对应功能局限性小,效率高。

        2、 当前我们学习字符流和字节流中,我们还学习了对应的高效输出输入流。

        3、 字节高效的输入输出流,要快于普通字节流,快的速度800毫秒可以缩短到300毫秒。

        字符流也是同理,高效的要高于普通的,因为高效的可以读取一行,直接写入,需要换行写入的时候可以插入换行。

      3、为什么高效的要快:因为当前底层设置了缓冲区,我们可以创建高效流的同时使用默认缓冲区,或者自定义缓冲区,我们一般使用默认即可,因为默认已经足够我们使用了。

                  默认缓冲区大小:8192字节。

    六、 多线程

    1、        多线程的开启方式

      1、方法一:自己创建一个对象,继承多线程对象,进行创建多线程。

     

                  2、方法二:通过实现一个接口Runnable接口,让多线程对象使用。

     

    2、        多线程设置同步锁

      1、方式一:使用同步代码块:关键步骤上锁

      注意:锁对象必须是一致的,一般使用class文件,保持一致。或者使用单例设计模式创建对象。

           2、方式二:同步方法:将整个方法进行上锁

      注意:当前线程可以开启多个,但是runnable实现类必须只有一个,不能实现多个实现类,否则同步方法无效

      方法三:静态同步方法,所有的对象都上锁,一锁锁所有

      注意:当前如果要实现多个runnable实现类,不同的对象间使用方法锁的话,可以使用静态同步方法,这样的话就能够解决方法二不能锁住的问题。

           3、方法三:静态同步方法,所有的对象都上锁,一锁锁所有

      注意:当前如果要实现多个runnable实现类,不同的对象间使用方法锁的话,可以使用静态同步方法,这样的话就能够解决方法二不能锁住的问题。

    3、        两种设计模式

      1、 单例设计模式

      懒汉式:

     

      饿汉式:

     

      2、 生产者与消费者模式

      1、生产者生产数据到缓冲区中,消费者从缓冲区中取数据。

      如果缓冲区已经满了,则生产者线程阻塞;

      如果缓冲区为空,那么消费者线程阻塞。

      2、wait:等待方法

      被等待的线程是自身唤醒的,必须得依靠别的线程或者对象唤醒,唤醒通过锁对象调用唤醒方法。

      3、notify:唤醒方法(唤醒单个)

            唤醒单个:随机唤醒等待的线程中的一个线程

           

      4、notifyAll:唤醒方法(唤醒全部)

      唤醒全部:将当前所有等待的线程进行唤醒,唤醒后不一定所有线程都执行,具体谁执行还需要看CPU给谁分配了资源抢占了锁对象。

      注意:当前锁对象如果不是同一把锁,就无法唤醒,同时也可能产生一直等待的状态,线程堵塞

      5、sleep方法,当前休息睡眠一定的时间后,自己就能够清醒过来,进行继续运行。

      区别:等待唤醒机制,如果当前处于等待的状态,就会释放锁对象,丢给其他对象使用并且执行,当前自身处于等待不执行的状态。 睡眠方法,则不同,当前线程如果是睡眠方法,那么在睡眠的过程中是不会释放锁资源的,等清醒后,继续操作执行,直到执行结束,结束操作,释放锁资源,提供给其他对象使用。

    七、 网络编程

      1、网络编程的三要素:IP地址、端口号、网络协议。

      2、网络协议:底层协议:UDP、TCP

                  UDP:直接连接的网络协议,一般使用的场景:视频、语音、聊天室。

                  TCP:三次握手连接,一般使用的场景:传输图片、发送聊天文字等等。

                  区别:

                         UDP:效率快,但是数据丢失无法找回,数据传输不稳定。

                         TCP:效率相对慢,传输的数据,稳定丢失比较低。

                  使用网络协议传输的过程中,我们基本上使用的都是流对象传输。

    八、 反射与正则表达式

    1、反射

      1、对象反射的方式一共有:三种

                  第一种:创建出来对象,通过对象的引用获取。

                  第二种:通过类的包名 + 类名直接寻找。

                  第三种:直接通过类名调用class关键字。

           2、暴力访问,将setAccessible的状态设为true,然后就可以获取私有的属性和方法。

          

      2、正则表达式

           1、使用一个正则对象

    static Pattern

    compile(String regex)
              将给定的正则表达式编译到模式中。

    Matcher

    matcher(CharSequence input)
              创匹配给定输入与此模式的匹配器。

    boolean

    matches()
              尝试将整个区域与模式匹配。

       使用示例:

     

  • 相关阅读:
    在FreeBSD中pkg包管理器使用实例
    租了一台华为云耀云服务器,却直接被封公网ip,而且是官方的bug导致!
    Java8 Stream对集合的一些应用
    idea 编码UTF-8 设置
    Java RSA非对称加密算法实现
    分库分表 策略 临时
    springboot+dubbo + zookeeper 案例
    跟着华为,学数字化转型(6):一把手工程
    跟着华为,学数字化转型(5):数据保护和业务决策
    跟着华为,学数字化转型(4):数据采集
  • 原文地址:https://www.cnblogs.com/qjc-hll-520/p/12194042.html
Copyright © 2020-2023  润新知