• Java学习笔记(一)面向对象(OOP)


    面向对象:

    面向过程编程:一堆方法,调来调去
    面向对象编程:以对象为核心,围绕着对象做操作
    面向接口编程:面向对象的基础之上,抽接口
        好的代码:复用性好、可维护性好、可扩展性好、移植性好......

    面向过程:实在  

        缺陷一:缺乏对数据的封装
        缺陷二:数据和方法分离状态


    面向对象:抽象

      1.现实世界是由很多对象组成的
      2.现实世界是先有对象,再抽出类。代码中先创建类,再创建对象
      3.一个类可以创建多个对象,同一个类的多个对象,结构相同,数据不同
      4.类是一种数据类型 只能包含:
        1)描述对象所共有的特征:------变量。属性-----静的

        2)对象所共有的行为:----------方法。行为-----动的

      5.new后,成员变量有默认值

      6.创建对象语法:
        类名 引用 = new 类名();
        其中:new 类名()是在创建对象
        因对象为数据,所有声明引用来指代数据
      7.访问成员变量、访问方法     ----通过点来操作,语法:
        引用.成员变量
        引用.方法名();
      8.基本类型之间画等号----再赋值------身份证复印件
       引用类型之间画等号----指向同一个对象------房子钥匙
      9.null:空,表示没有指向对象
        若引用的值为null,则不能再进行点操作,
        否则会出现NullPointerException异常

    8种基本类型:可以直接赋值
    除了8种之外:
    类、接口、数组----引用类型    都是通过new出的

    1.基本:
      直接赋值
      变量装的就是确切的值
      画等号----赋值
      int num = 5;
    2.引用:
      new
      变量装的是地址
      画等号----指向同一个对象

    3.引用类型数组
      int[] arr = new int[4];    arr指向 一个int[]类型的地址, 这个地址的大小是4个int类型的大小。
      Cell[] cells = new Cell[4];  cells指向一个Cell[]类型的地址,这个地址的大小是4个Cell类型的大小

    java建议:
      1个文件只包含1个类

    java规定:
      java中一个文件可以包含多个类,
      但是,public的类只能有1个,
      并且,public的类必须与文件名相同

    Java中的类

    类可以看成是创建Java对象的模板。

    public class Dog{
      String breed;
      int age;
      String color;
      void barking(){
      }
     
      void hungry(){
      }
     
      void sleeping(){
      }
    }
    

      

    一个类可以包含以下类型变量:

    • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
    • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
    • 类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。

    一个类可以拥有多个方法,在上面的例子中:barking()、hungry()和sleeping()都是Dog类的方法。

    构造方法

      1.构造方法常常用于给成员变量初始化
      2.与类同名,没有返回值类型
      3.构造方法是在创建对象时被自动调用
      4.若自己不写构造方法,则编译器默认给一个无参构造,若自己写了,则不再默认提供无参构造
      5.构造方法可以重载

    public class Puppy{
        public Puppy(){
        }
     
        public Puppy(String name){
            // 这个构造器仅有一个参数:name
        }
    }
    

    创建对象

    对象是根据类创建的。在Java中,使用关键字new来创建一个新的对象。创建对象需要以下三步:

    • 声明:声明一个对象,包括对象名称和对象类型。
    • 实例化:使用关键字new来创建一个对象。
    • 初始化:使用new创建对象时,会调用构造方法初始化对象。
      public class Puppy{
         public Puppy(String name){
            //这个构造器仅有一个参数:name
            System.out.println("小狗的名字是 : " + name ); 
         }
         public static void main(String []args){
            // 下面的语句将创建一个Puppy对象
            Puppy myPuppy = new Puppy( "tommy" );
         }
      }  
    /* 实例化对象 */
    ObjectReference = new Constructor();
    /* 访问类中的变量 */
    ObjectReference.variableName;
    /* 访问类中的方法 */
    ObjectReference.MethodName();
    

    实例

    下面的例子展示如何访问实例变量和调用成员方法:

    public class Puppy{
       int puppyAge;
       public Puppy(String name){
          // 这个构造器仅有一个参数:name
          System.out.println("小狗的名字是 : " + name ); 
       }
     
       public void setAge( int age ){
           puppyAge = age;
       }
     
       public int getAge( ){
           System.out.println("小狗的年龄为 : " + puppyAge ); 
           return puppyAge;
       }
     
       public static void main(String []args){
          /* 创建对象 */
          Puppy myPuppy = new Puppy( "tommy" );
          /* 通过方法来设定age */
          myPuppy.setAge( 2 );
          /* 调用另一个方法获取age */
          myPuppy.getAge( );
          /*你也可以像下面这样访问成员变量 */
          System.out.println("变量值 : " + myPuppy.puppyAge ); 
       }
    }
    

      

    当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。

    • 一个源文件中只能有一个public类
    • 一个源文件可以有多个非public类
    • 源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。
    • 如果一个类定义在某个包中,那么package语句应该在源文件的首行。
    • 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
    • import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

    类有若干种访问级别,并且类也分不同的类型:抽象类和final类等。

      

    this:
      1.this指代当前对象,谁调指的就是谁
      2.用法:
        this.成员变量---访问成员变量
        this.方法()-----访问方法
        this()--------调构造方法

    继承:

      避免代码重复
      父类中包含所有子类公有的数据
      子类中包含子类所特有的数据

    企鹅类:

    public class Penguin { 
        private String name; 
        private int id; 
        public Penguin(String myName, int  myid) { 
            name = myName; 
            id = myid; 
        } 
        public void eat(){ 
            System.out.println(name+"正在吃"); 
        }
        public void sleep(){
            System.out.println(name+"正在睡");
        }
        public void introduction() { 
            System.out.println("大家好!我是"         + id + "号" + name + "."); 
        } 
    }
    

     老鼠类:

    public class Mouse { 
        private String name; 
        private int id; 
        public Mouse(String myName, int  myid) { 
            name = myName; 
            id = myid; 
        } 
        public void eat(){ 
            System.out.println(name+"正在吃"); 
        }
        public void sleep(){
            System.out.println(name+"正在睡");
        }
        public void introduction() { 
            System.out.println("大家好!我是"         + id + "号" + name + "."); 
        } 
    }
    

      从这两段代码可以看出来,代码存在重复了,导致后果就是代码量大且臃肿,而且维护性不高(维护性主要是后期需要修改的时候,就需要修改很多的代码,容易出错),所以要从根本上解决这两段代码的问题,就需要继承,将两段代码中相同的部分提取出来组成 一个父类:

    public class Animal { 
        private String name;  
        private int id; 
        public Animal(String myName, int myid) { 
            name = myName; 
            id = myid;
        } 
        public void eat(){ 
            System.out.println(name+"正在吃"); 
        }
        public void sleep(){
            System.out.println(name+"正在睡");
        }
        public void introduction() { 
            System.out.println("大家好!我是"         + id + "号" + name + "."); 
        } 
    }
    

      企鹅类:

    public class Penguin extends Animal { 
        public Penguin(String myName, int myid) { 
            super(myName, myid); 
        } 
    }
    

      老鼠类:

    public class Mouse extends Animal { 
        public Mouse(String myName, int myid) { 
            super(myName, myid); 
        } 
    }
    

      

    继承的特性

    • 子类拥有父类非private的属性,方法。

    • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

    • 子类可以用自己的方式实现父类的方法。

    • Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。

    • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。

    extends关键字

    在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

    public class Animal { 
        private String name;   
        private int id; 
        public Animal(String myName, String myid) { 
            //初始化属性值
        } 
        public void eat() {  //吃东西方法的具体实现  } 
        public void sleep() { //睡觉方法的具体实现  } 
    } 
     
    public class Penguin  extends  Animal{ 
    }
    

    super 与 this 关键字

    super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

    this关键字:指向自己的引用。

    class Animal {
      void eat() {
        System.out.println("animal : eat");
      }
    }
     
    class Dog extends Animal {
      void eat() {
        System.out.println("dog : eat");
      }
      void eatTest() {
        this.eat();   // this 调用自己的方法
        super.eat();  // super 调用父类方法
      }
    }
     
    public class Test {
      public static void main(String[] args) {
        Animal a = new Animal();
        a.eat();
        Dog d = new Dog();
        d.eatTest();
      }
    }
    

      

    输出结果为:

    animal : eat
    dog : eat
    animal : eat

    父类类型引用指向子类的对象---向上造型

    动物是老鼠--------------语义不通
    Mouse m = new Animal(); //错误

    Person p = new Student(); //向上造型
    Person p = new Teacher();
    Person p = new Doctor();

    public class Test {
    	public static void main(String[] args) {
    		Animal a = new Animal("动物");
    		Dog d = new Dog("小明");
    		sleep(a);
    		sleep(d);
    	}
    	
    	public static void sleep(Animal a){//可以接受任何Animal类型与Animal子类类型。
    		a.sleep();		//调用对应的方法
    		
    	}
    }
    
    class Animal{
    	String name;
    	public Animal(String name){
    		this.name = name;
    	}
    	public void sleep(){
    		System.out.println(name+"正在睡觉");
    	}
    }
    
    class Dog extends Animal{
    	
    	public Dog(String name) {
    		super(name);
    	}
    
    	public void sleep(){
    		System.out.println(name+"是条狗,"+"正在睡觉。");
    	}
    }
    

      输出:

        动物正在睡觉
        小明是条狗,正在睡觉。

    向上造型:父类引用指向子类对象
      注意:能点出来什么,看类型

    重写(override)和重载(overload)的区别------常见面试题
    重载:
      在一个类中,方法名相同,参数列表不同
    重写:
      在两个类中,并且是子类和父类的关系,签名相同

    重载:编译时----.java到.class的过程
      内存没东西---只看语法对不对

    重写:运行时----jvm加载.class并运行.class的过程内存才有东西

    堆、栈、方法区------jvm分配的内存

    重载时调用看类型,重写时调用看对象

    方法的重写:发生在两个类中,并且两个类是继承关系 。子类方法与父类方法的签名相同时     ----  子类重写了父类的方法。

    public class Test {
    	public static void main(String[] args) {
    		Coo c = new Coo();
    		Aoo a = new Boo();
    		c.print(a);
    	}
    	
    }
    
    class Aoo{
    	public void show(){
    		System.out.println("from Aoo");
    	}
    }
    class Boo extends Aoo{
    	public void show(){
    		System.out.println("from Boo");
    	}
    }
    class Coo{
    	public void print(Aoo a){
    		System.out.println("from Coo print(Aoo a)");
    		a.show();
    	}
    	public void print(Boo b){
    		System.out.println("from Coo print(Boo b)");
    		b.show();
    	}
    }
    

      输出:

    from Coo print(Aoo a)
    from Boo
    

      

    package 关键字

    建议
    域名反写 项目名称 模块名称
    com.xxx.studentmanager.course.类名

    package a.b.c.d.e; //声明包
    public class Test{
    }
    
    //a.Test----全局限定名
    a.b.c.d.e.Test o = new a.b.c.d.e.Test();
    
    import a.b.c.d.e.Test; //声明类、引入类
    Test t = new Test(); //创建对象
    
    
    import java.util.Arrays;
    import java.util.Random;
    import java.util.Scanner;
    
    import java.util.*;    //---------不建议导入所有类
    
      
    
    Scanner scan = new Scanner(System.in);
    
    java.util.Scanner scan = new java.util.Scanner(System.in);//也可以用全称。
    

      同一个包中的类不需要import

    访问修饰符:public,private,protected,默认

         访问权限   类    包  子类  其他包

            public     ∨     ∨     ∨     ∨          (对任何人都是可用的)

            protect    ∨     ∨    ∨     ×    (继承的类可以访问以及和private一样的权限)

            default    ∨     ∨     ×     ×    (包访问权限,即在整个包内均可被访问)

            private    ∨     ×     ×     ×    (除类型创建者和类型的内部方法之外的任何人都不能访问的元素)

    static:

    成员变量:
      1)实例变量-----不用static修饰的
      2)静态变量-----static修饰的

    何时用静态变量,何时用实例变量
    class Customer{ //帐户类
      String customerName; //帐户名称
      String customerPwd; //帐户密码
      static double poir; //利率
    }
    实例变量:属于对象,一个对象有一份
    静态变量:属于类,所有对象公用这一份

    class Aoo{
       int a;-------------属于对象
       static int b;------属于类
       void show(){
         b++;
       }
    }
    

    类的方法中,常常需要对对象的实例变量操作
    类的非静态方法,默认有个隐式的this
    类的静态方法,没有隐式的this的

    class Cell{
       int row;  //属于对象---实例变量
       int col;  //属于对象---实例变量
       static int num;
       static void show(){
          row++;   //错误的
          num++;   //正确的
    
          Cell c = new Cell();
          c.row = 5;
       }
       void drop(){
          this.row++;
       }
       void moveLeft(){
          this.col--;
       }
    
    }
    

    非静态方法:---有隐式this
      可以直接访问静态变量和实例变量
      需要访问实例变量时用

    静态方法:-----没有隐式this
      只能直接访问静态变量,不能直接访问实例变量
      不需要访问实例变量时,只需对参数操作即可

    何时用静态方法,何时用非静态方法:

      静态方法,只与参数相关,与实例变量有关
      Arrays.sort(arr);
      Math.random();
      Math.sqrt(25);

    何时用静态代码块:
    一般用于加载静态资源(图片、音频、视频)

    static{
         //静态代码块  
    }
    

    final关键字

    final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:

    • 声明类:

      final class 类名 {//类体}
    • 声明方法:

      修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
    • Java中常量的设置:

      public static final 数据类型 变量名; 

    :实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final

    abstract

    1.抽象方法:由abstract修饰
      只有方法的定义,没有方法体的
    2.抽象类:由abstract修饰
      可以包含抽象方法,也可以包含普通方法
    3.包含抽象方法的类,必须是抽象类
      类中没有抽象方法,也可以将类声明为抽象类
    4.抽象类不能被实例化 Shape s = new Shape();//错误
    5.抽象类一般需要被继承:
      1)子类也声明为抽象类
      2)子类重写抽象类中所有抽象方法---首选
    6.抽象类的意义:
      1)封装子类公用的成员  为子类提供一个公共的类型
      2)定义抽象方法,由子类来做不同的实现,但入口(方法名)是一样的

    abstract class Shape{   //抽象类(不完整)
       int c;   //周长
       abstract double area();  //抽象方法(不完整)
    }
    class Square extends Shape{   //方形类
       Square(double c){
          this.c = c;
       }
       double area(){  //重写
          return 0.0625*c*c;
       }
    }
    class Circle extends Shape{   //圆形
       Circle(double c){
          this.c = c;
       }
       double area(){  //重写
          return 0.0796*c*c;
       }
    }
    

      

    interface:

    1.接口就是一个标准、一个规范
    2.接口中只能包含常量和抽象方法
    3.接口不能被实例化
      接口 引用 = new 实现类(); //向上造型
    4.类实现接口,必须将所有抽象方法都实现
    5.类可以实现多个接口,用逗号分隔
      若类又继承父类又实现接口,需先继承后实现
    6.接口与接口之间可以继承

    接口与类相似点:

    • 一个接口可以有多个方法。
    • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
    • 接口的字节码文件保存在 .class 结尾的文件中。
    • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

    接口与类的区别:

    • 接口不能用于实例化对象。
    • 接口没有构造方法。
    • 接口中所有的方法必须是抽象方法。
    • 接口不能包含成员变量,除了 static 和 final 变量。
    • 接口不是被类继承了,而是要被类实现。
    • 接口支持多继承。

    抽象类和接口的区别

    • 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    • 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    • 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    接口特性

    • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
    • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
    • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
    interface 接口名称 [extends 其他的类名] {
            // 声明变量
            // 抽象方法
    }
    

    接口有以下特性:

    • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
    • 接口中的方法都是公有的。
    • 接口中的方法默认带有修饰符 public abstract 常量默认带有修饰符 public static final

    接口的实现

    当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。

    类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。

    实现一个接口的语法,可以使用这个公式:

    ...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...

    interface Animal {
       public void eat();
       public void travel();
    }
    

      

    public class MammalInt implements Animal{
     
       public void eat(){
          System.out.println("Mammal eats");
       }
     
       public void travel(){
          System.out.println("Mammal travels");
       } 
     
       public int noOfLegs(){
          return 0;
       }
     
       public static void main(String args[]){
          MammalInt m = new MammalInt();
          m.eat();
          m.travel();
       }
    }
    

      

    伪代码:

    想开一个工商银行(对象)
    工商银行类---------遵守标准(工行接口)
    
    1.制定标准---接口
      interface UnionPay{   //银联接口
         void 存钱();
         void 取钱();
         void 改密码();
         void 查余额();
      }
      interface ICBC extends UnionPay{   //工行接口
         void 在线支付();
      }
      interface ABC extends UnionPay{    //农行接口
         void 支付电话费();
      }
    2.遵守标准----类
      class ICBCImpl implements ICBC{  //工行类
         public void 存钱(){}
         public void 取钱(){}
         public void 改密码(){}
         public void 查余额(){}
         public void 在线支付(){}
         
      }
      class ABCImpl implements ABC{    //农行类
         public void 存钱(){}
         public void 取钱(){}
         public void 改密码(){}
         public void 查余额(){}
         public void 支付电话费(){}
      }
    3.main(){
         ICBCImpl icbc1 = new ICBCImpl();//开了1个工行
         icbc1.存钱()/取钱()/改密码()/查余额()/在线支付()
    
         ABCImpl abc1 = new ABCImpl();
    
         ICBC i = new ICBCImpl(); //向上造型
      }
    

      

    重写接口中声明的方法时,需要注意以下规则:

    • 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
    • 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
    • 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。

    在实现接口的时候,也要注意一些规则:

    • 一个类可以同时实现多个接口。
    • 一个类只能继承一个类,但是能实现多个接口。
    • 一个接口能继承另一个接口,这和类之间的继承比较相似。

    接口的继承

    一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

    // 文件名: Sports.java
    public interface Sports
    {
       public void setHomeTeam(String name);
       public void setVisitingTeam(String name);
    }
     
    // 文件名: Football.java
    public interface Football extends Sports
    {
       public void homeTeamScored(int points);
       public void visitingTeamScored(int points);
       public void endOfQuarter(int quarter);
    }
     
    // 文件名: Hockey.java
    public interface Hockey extends Sports
    {
       public void homeGoalScored();
       public void visitingGoalScored();
       public void endOfPeriod(int period);
       public void overtimePeriod(int ot);
    }
    

      

    Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。

    相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。

    接口的多继承

    在Java中,类的多继承是不合法,但接口允许多继承。

    在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:

    public interface Hockey extends Sports, Event
    

    以上的程序片段是合法定义的子接口,与类不同的是,接口允许多继承,而 Sports及 Event 可能定义或是继承相同的方法

    标记接口

    最常用的继承接口是没有包含任何方法的接口。

    标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。

    标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。

    例如:java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:

    package java.util; public interface EventListener {}

    没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:

    • 建立一个公共的父接口:

      正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。

    • 向一个类添加数据类型:

      这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。

    什么时候使用抽象类和接口

    • 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
    • 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
    • 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

    在 JDK1.8,允许我们给接口添加两种非抽象的方法实现:

      1、默认方法,添加 default 修饰即可;

      2、静态方法,使用 static 修饰;示例如下:

    interface Test{
        //这个是默认方法
        default String get(String aa){
            System.out.println("我是jdk1.8默认实现方法...");
            return "";
        }   
        //这个是静态方法    
        static void staticmethod(){
            System.out.println("我是静态方法");
        }
    }
    

      

  • 相关阅读:
    java 静态和非静态代码块执行顺序
    spring boot 学习
    js中的jQuery Validate增加手机号码验证
    JAVA验证手机号码是否正确
    redis启动报错 var/run/redis_6379.pid exists, process is already running or crashed
    移动端点击a标签拨打电话
    js计算两个日期之间的天数
    JS根据日期获取判断星期几
    JAVA生成订单编号工具类
    JAVA微信支付——微信公众号内支付 代码
  • 原文地址:https://www.cnblogs.com/zbuter/p/9172178.html
Copyright © 2020-2023  润新知