java基础学习笔记

目录

一、Java基础

二、面向对象

三、重点  this、static

四、继承、多态

五、final,包,super

 六、接口、抽象类


一、Java基础

1、public class 和 class的区别:
    
    * 一个java源文件当中可以定义多个class

    * 一个java源文件当中public的class不是必须的

    * 一个class会定义生成一个xxx.class字节码文件

    * 一个java源文件当中定义公开的类的话,只能有一个,并且该类名称必须和java源文件名称一致。

    * 每一个class当中都可以编写main方法,都可以设定程序的入口,想执行B.class中的main方法:java B,
    想执行X.class当中的main方法:java X

    * 注意:当在命令窗口中执行java Hello,那么要求Hello.class当中必须有主方法。没有主方法会出现运行
    阶段的错误:
        D:\course\JavaProjects\02-JavaSE\day02>java Hello
        错误: 在类 B 中找不到主方法, 请将主方法定义为:
            public static void main(String[] args)


2、标识符可以随意编写吗,有命名规则吗?有
        
    规则1:标识符只能由数字、字母(包括中文)、下划线 _、美元符号$组成,
    不能含有其它符号。

    规则2:标识符不能以数字开头

    规则3:关键字不能做标识符。例如:public class static void 这些蓝色的字体
    都是关键字,关键字是不能做标识符的。

    规则4:标识符是严格区分大小写的。大写A和小写a不一样。

    规则5:标识符理论上是没有长度限制的。

3、综合的看一下,在类型转换的时候需要遵循哪些规则?

    第一条:八种基本数据类型中,除 boolean 类型不能转换,剩下七种类型之间都可以
    进行转换;

    第二条:如果整数型字面量没有超出 byte,short,char 的取值范围,可以直接将其赋
    值给byte,short,char 类型的变量;

    第三条:小容量向大容量转换称为自动类型转换,容量从小到大的排序为:
    byte < short(char) < int < long < float < double,其中 short和 char 
    都占用两个字节,但是char 可以表示更大的正整数;

    第四条:大容量转换成小容量,称为强制类型转换,编写时必须添加“强制类型转换符”,
    但运行时可能出现精度损失,谨慎使用;

    第五条:byte,short,char 类型混合运算时,先各自转换成 int 类型再做运算;

    第六条:多种数据类型混合运算,各自先转换成容量最大的那一种再做运算;


4、switch后面和case后面只能是int和String类型

5、方法定义: 
      [修饰符列表] 返回值类型 方法名(形式参数列表){方法体;}
          [修饰符列表],此项是可选项,不是必须的,目前大家统一写成 public static
          对于方法修饰符中有static关键字的,使用"类名.方法名(实参列表)"调用方法;方法在同类可省略"类名.",不同类不能省略


6、方法在执行过程中,在JVM的内存是如何分配的?内存是如何变化的?
      1)方法只定义,不调用,是不会执行的,JVM也不会分配其“运行所属”的内存空间,只有调用时才动态分配内存空间。
      2)在JVM内存划分中有三块主要的内存空间(还有其他内存空间)
           *方法区内存
           *栈内存
           *堆内存
      3)“栈”数据结构(先进后出原则)
      4)方法代码片段存在哪里?方法执行时执行过程的内存在哪里?
           *方法代码片段属于.class字节码文件一部分,字节码文件在类加载时将其放到方法区中。所以JVM三块主要内存空间方法区内存最先有数据,存放了代码片段。
           *代码片段在方法区内存中只有一份,但可被重复调用,每次调用时,需要给该方法分配独立的活动场所,在栈内存中分配。【栈内存中分配方法运行的所属内存空间】。
      5)方法在调用瞬间会给该方法分配内存空间,会在栈中发生压栈动作;方法结束后,释放内存空间,发生弹栈动作。
           *压栈(入栈)
           *弹栈(出栈)
      6)局部变量在“方法体”中声明。局部变量运行阶段内存在栈中分配。


7、
      *方法调用时,在参数传递时,实际上传递的是变量保存的“值”。
      *栈内存主要存储局部变量


8、方法重载
      1)什么时候用方法重载?
           *功能相相似尽可能让方法名相同;功能不同时,尽可能让方法名不同
      2)满足什么条件构成方法重载?
           *在同一类中;
           *方法名相同;
           *参数列表不同:参数数量不同/顺序不同/类型不同
      3)方法重载与什么有关?
           *方法重载与方法名+参数列表有关;
           *方法重载与返回值类型、修饰符列表无关;

9、递归
      1)递归很耗费栈内存,递归算法能不用就不用
      2)错误Error:java.lang.StackOverflowError  ——栈内存溢出错误。错误无法挽回,结果是JVM停止工作。
      3)递归必须有结束条件,没有结束条件一定发生栈内存溢出错误;
           递归即使有结束条件,也可能发生栈内存溢出错误,因为递归的太深了。

二、面向对象

1、面向对象包括三大特征
    封装
    继承
    多态
  
     ***类是现实世界当中具有共同特征的事物进行抽象形成的模板或概念。
     ***对象是实际存在的个体。


2、当我们采用面向对象的方式贯穿整个系统的话,涉及到三个术语:
    OOA:面向对象分析
    OOD:面向对象设计
    OOP:面向对象编程
    整个软件开发的过程,都是采用OO进行贯穿的。

    实现一个软件的过程:
        分析(A) --> 设计(D) --> 编程(P)


3、面向过程和面向对象有什么区别?
        *面向过程的开发方式主要的特点:注重实现这个功能的步骤以及因果关系

        *面向对象就是将现实世界分割成不同的单元,然后每一个单元都实现成对象,
        然后给一个环境驱动一下,让各个对象之间协作起来形成一个系统。


4、创建对象
        * java语言中所有的class都属于引用数据类型。
        *方法体外声明的变量叫做成员变量。(实例变量属于成员变量)
        *new运算符作用是创建对象,在JVM堆内存中开辟新的内存空间

        *栈内存(局部变量):方法代码片段执行时给该方法分配内存空间,在栈内存中压栈;
        *堆内存(实例变量):new的对象在堆内存中存储


5、对象与引用
        *对象:new运算符在堆内存中开辟的内存空间称为对象;
        *引用:引用是一个变量,但这个变量中保存了另一个java对象的内存地址;(引用数据类型定义的变量)

        **java语言中,程序员不能直接操作堆内存,java没有指针;只能通过“引用”去访问堆内存当中对象内部的实例变量。
        访问“实例变量”语法格式:
              //  读取数据:引用.变量名
              //  修改数据:引用.变量名=值


6、JVM内存管理
      1)JVM(java虚拟机)主要包括三块内存空间:栈内存,堆内存,方法区内存
      2)堆内存和方法区内存各有一个;一个线程一个栈内存;
      3)方法调用时所需内存空间在栈内存中分配,简称压栈。方法执行结束后,该内存空间释放,简称弹栈。
      4)栈内存主要存储方法体中的局部变量;
      5)方法的代码片段以及整个类的代码片段都被存储到方法区内存中,在类加载时载入;
      6)静态变量存储在方法区内存(重要)
      7)程序执行过程中,new运算符创建的java对象存储在堆内存中,对象内部有实例变量,所以实例变量存储在堆内存中。
      8)变量分类
                     -局部变量【方法体内声明】
                     -成员变量【方法体外声明】
                                             *实例变量【没有static修饰】
                                             *静态变量【static修饰】
      9)三块内存变化最频繁的是栈内存; 最先有数据的是方法区内存; 垃圾回收器主要针对堆内存;
      10)垃圾回收器【自动垃圾回收机制,GC机制】什么时候会考虑将某个java对象回收?
                    *当堆内存中的java对象成为垃圾数据时,会被垃圾回收器回收;
                    *什么时候堆内存中的java对象会成为垃圾数据?
                              没有更多引用指向它时;(这个对象无法被访问,因为对象只能通过引用方式访问。)
                          

7、空引用访问“实例”相关数据一定会出现空指针异常( java.lang.NullPointerException)
         **“实例”相关数据:这个数据访问的似乎必须有对象的参与;


8、关联关系:A has a B 关系 【A对象中含有B对象的引用】

三、重点  this、static

1、封装( 什么是封装?封装有什么好处?怎么封装,代码怎么写?)

     1)封装就是利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,
     数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
     2)封装后才会形成真正的“对象”,真正的“独立体”;
     3)封装意味着以后的程序可以在不同的环境中重复使用,可以降低程序的耦合度,提高程序的扩展性,以及重用性或复用性。
     4)封装后对于事物本身提高了安全性。
      


2、构造方法
      1)构造方法又称为构造函数/构造器/Constructor
      2)构造方法的“返回值类型”不需要指定, 也不能写void,写上就成了普通方法了
      3)构造方法的方法名必须与类名保持一致;
      4)构造方法作用:
                  *通过构造方法的调用,可以创建对象;
                  *创建对象的同时,初始化实例变量的内存空间【给实例变量赋值】
      5)构造方法怎么调用?
                  *new 构造方法名(实参列表)
      6)构造方法调用执行后,有返回值吗?
                  *每个构造方法实际执行结束后都有返回值,但“return 值”语句不需要写。构造方法结束时,java程序自动返回值;
                   并且返回值类型是构造方法所在类的类型。

      7)实例变量是系统默认赋值,是在构造方法执行过程中初始化赋值的。


3、方法调用时的参数传递问题
        *传递时,java只遵循一种语法机制,就是把变量中保存的“值”传递过去;
     这个值有时是字面值;有时是另一个java对象的内存地址0x1234;


4、this关键字
      1)this 是一个引用,是一个变量;this变量中保存了内存地址指向自身;this存储在JVM堆内存java对象内部。
      2)创建100个java对象,每个对象都有this,也就是有100个this;
      3)this可以出现在"实例方法"当中,this指向当前正在执行这个动作的对象。(this代表当前对象)
      4)多数情况this可省略不写。
      5)this不能在带有static关键字的方法中使用。(static的方法调用不需要对象直接使用类名,所以执行过程没有当前对象,也就不能使用this。)
      6)this什么时候不能省略?
              区分实例变量与局部变量时this不能省略。
      7)this可以用在哪里?
                *可以使用在实例方法中,代表当前对象【语法格式:this.】
                *可以使用在构造方法中,通过当前构造方法调用其他构造方法【语法格式:this(实参);】
           *** 重点【记忆】:this()这种语法只能出现在构造方法第一行
      8)输出“引用”,自动调用引用的toString()方法。
 
      
5、
     *没有static关键字的方法称为“实例方法”,实例方法怎么访问?“引用.”
     *没有static关键字的变量称为“实例变量”
     *注意:当一个行为/动作执行过程中需要对象参与,那么这个方法一定要定义为“实例方法”

     *带有static的方法,既可采用“类名.”方式访问,也可采用“引用.”方式访问,但即使用“引用.”访问,实际上与引用指向对象无关。(此时引用为null不会出现空指针异常)


6、static
      1)什么时候成员变量声明为实例变量?静态变量?
           -所以对象都有这个属性,但这个属性值会随对象变化而变化【不同对象的这个属性值可能不同】
            -所以对象都有这个属性,所有对象的这个属性值相同,建议定义为静态变量,节省内存开销。
       *静态变量在类加载时初始化,内存在方法区中开辟。访问时不需要创建对象,直接使用“类名.静态变量名”的方式访问。

      2)static修饰方法是静态方法,static修饰变量的静态变量;所以static修饰原始都是静态的,都可用“类名.”方式访问,(不建议用“引用.”)
      3)static修饰的所有元素都是类级别的,与具体对象无关
      4)静态方法中无法直接访问实例变量与实例方法;

      5)方法什么时候定义为静态的?
           方法描述的是动作,当所有对象执行这个动作时,产生的影响一样,那么这个动作不再属于某个对象的动作了,可以将其提升为类级别的动作(模板动作)。
      6)大多数方法都定义为实例方法,因为一般一个动作在发生时都需要对象参与,但也有例外,例如:大多数“工具类”中的方法都是静态方法,因为工具类是为了方便编程,为方便方法调用,不用new对象是最好的。


7、static定义的具体代码块
      1)语法格式:
               static{
                   java语句
               }
      2)静态代码块在类加载时执行,且只执行一次
      3)静态代码块在一个类中可以编写多个,遵循自上而下依次执行
      4)静态代码块作用?
            --与具体需求有关
            --静态代码块是java为程序员准备一个特殊时刻——类加载时刻,若行为在此刻执行一定特殊出现,这段程序可放到静态代码块中。
      5)通常在静态代码块中完成预备构造,先完成数据的准备工具。如:初始化连接池、解析XML配置文件……


8、实例代码块【了解】
      1)实例代码块可以编写多个,遵循自上而下依次执行
      2)实例代码块在构造方法执行之前执行,构造方法执行一次,实例代码块对应执行一次。
      3)实例代码块也是java为程序员准备的一个特殊时机——对象初始化时机

四、继承、多态

1、继承(面向对象三大特征之一)
      1)继承“基本”作用:代码复用。
       继承 最“重要”作用:有了继承才有了“方法覆盖”和“多态机制”
      2)继承语法格式:
               [修饰符列表] class 类名 extends 父类名{
                     类体=属性+方法 
               }
      3)java语言只支持单继承,一个类不能同时继承多个类。(c++支持多继承)
      4)关于继承的以下术语:
              B类继承A类,则:
                          A类称为:父类、基类、超类、superclass
                          B类称为:子类、派生类、subclass
      5)java语言中子类继承父类继承哪些数据?
               -私有的不支持继承;
               -构造方法不支持继承;
               -其他数据都可以被继承;
      6)虽然java语言只支持单继承,但一个类也可以间接继承其他类,如:
              C继承B,B继承A,A继承T,则C间接继承A、T类
      7)java语言中假设一个类没有显示继承任何类,该类默认继承JavaSE库中提供的java.lang.Object类,一个对象与生俱来就有 Object类型中所有的特征。


2、方法覆盖/方法重写
      1)方法覆盖又称为方法重写:override【官方的】/overwrite
      2)什么时候使用方法重写?
           *当父类中的方法无法满足当前子类的业务需求时,子类有必要将父类中继承过来的方法重新编写,即方法重写/方法覆盖;
      3)满足什么条件方法会发生重写?
           *方法重写发生在具有继承关系的父子类中
           *方法重写时,返回值类型相同,方法名相同,形参列表相同
           *方法重写时,访问权限不能更低,可以更高
           *方法重写时,抛出异常不能更多,可以更少
      4)注意:
           *私有方法不能继承,所以不能覆盖;
           *构造方法不能继承,所以不能覆盖;  
           *静态方法不存在覆盖;
           *覆盖只针对方法,不针对属性;


3、多态【基础语法】
      1)Animal、Cat、Bird三个类关系:
                Cat继承Animal;  Bird继承Animal;   Cat与Bird没有继承关系

      2)多态涉及的一些概念:
                 *向上转型(upcasting)  
                           子类型——>父类型
                           又被称为:自动类型转换
                 *向下转型(downcasting)  
                           父类型——>子类型
                           又被称为:强制类型转换【需加强制类型转换符】
                 *重点记忆:
                            无论向上转型还是向下转型,两种类型间必须要有继承关系。
                            没有继承关系,程序是无法编译通过的。

        3)向上转型
            向上转型只要编译通过,运行一定不会出问题。

        4)向下转型
            a.什么时候需要使用向下转型?
               *当调用的方法是子类型中特有的,在父类型当中不存在,必须进行向下转型。

            b.向下转型有风险吗?
               *向下转型存在隐患,编译通过,运行可能出错。
                    Animal b=new Bird();    Cat c=(Cat)b;        
               以上程序运行时会出现java.lang.ClassCastException(类型转换异常)——总是在”向下转型时“发生。

            c.怎么避免这个风险?
              instanceof运算符,可以在程序运行阶段动态的判断某个引用指向的对象
        
        5)instanceof
              语法: 引用  instanceof   数据类型名
              执行结果是布尔类型;
              例如:b instanceof  Animal 
                        true表示  b引用指向的对象是Animal类型       
            false表示 b引用指向的对象不是Animal类型    
        
        6)java规范要求在向下转型【强制类型转换】前,使用instanceof


4、多态在实际开发中的作用
              *降低程序耦合度,提高程序开展力;
              *能使用多态尽量使用多态;
              *父类型引用指向子类型对象;
    核心:面向抽象编程,尽量不要面向具体编程。 
    
          OCP原则:对扩展开放,对修改关闭

五、final,包,super

1、final
    1)final是一个关键字,表示最终的,不可变的。
    2)final修饰的类无法被继承
    3)final修饰的方法无法被覆盖
    4)final修饰的变量一旦被赋值后,无法再重新赋值【不可二次赋值】
    5)final修饰的实例变量必须手动赋值,不能采用系统默认值。
    6)final修饰的引用,一旦指向某个对象后,不能再指向其他对象,那么被指向的对象无法被垃圾回收器回收。
    final修饰的引用虽然指向某个对象后,不能再指向其他对象,但所指向的对象内部的内存是可以被修改的。

    
2、常量
    1)final修饰的实例变量是不可变的,这种变量一般和static联合使用,被称为“常量”。
    2)常量定义语法格式:
        public static final 类型 常量名=值;
    3)java规范要求所有常量名字全部大写,每个单词用下划线连接。

    
    
3、包机制
    1)包:package,方便重新的过来,不同功能的类被分门别类放到不同软件包中,查找、管理比较方便,易维护。
    2)包的定义:
        -在java源程序的第一行编写package语句
        -package只能编写一个语句
        -语法结构:  package 包名
    3)包名的命名规范:
        公司域名+项目名+模块名+功能名
    4)包名要求全部小写,包名也是标识符
    5)一个包对应一个目录
    6)使用package机制后,该怎么编译?怎么运行?
        -使用package机制后,Test类名变为:com.hfl.javase.Test
        -编译:javac java源文件路径(生成class文件)
        -手动创建目录,将class文件放到指定目录下——————》运行:java com.hfl.javase.Test
        -另一种方式(编译+运行)
          *编译:javac -d 编译后存放路径  java源文件路径
          如:javac -d . *.java  编译当前所有java文件到当前目录下
    7)import语句用来完成导入其它类,同一个包下不需要导入
       java.lang.*系统自动导入,不需要import;
       
    
4、访问控制权限修饰符
    1)访问控制权限修饰符来控制元素的访问范围
    2)访问控制权限修饰符包括:
        public           表示公开的,在任何位置都可以访问
        protected        同包,子类
        default(缺省)  同包
        private          表示私有的,只能在本类中访问
    3)访问控制权限修饰符可以修饰类、变量、方法……
    4)当某个数据只希望子类使用,使用protected进行修饰
    5)修饰符范围
        private<缺省<protected<public
    6)类只能使用public和缺省修饰符进行修饰。【内部类除外】
        

5、super
    1)super
    super能出现在实例方法和构造方法中。
    super的语法是:“super.”、“super()”
    super不能使用在静态方法中。
    super. 大部分情况下是可以省略的。
    super.什么时候不能省略呢?
        父类和子类中有同名属性,或者说有同样的方法,
        想在子类中访问父类的,super. 不能省略。
    super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中
    的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。

    2)super()
    表示通过子类的构造方法调用父类的构造方法

    3)当一个构造方法第一行既没有this()    又没有super()时,默认会有一个super();
    表示通过当前子类的构造方法调用父类的无参构造方法,所以必须保证父类无参构造方法存在。
    (最好手写父类的无参构造方法)
    
    4)this()与super()不能共存,都只能出现在构造方法第一行
    5)无论怎么折腾,父类构造方法一定会执行
    
    6)super(实参)作用:初始化当前对象的父类型特征,并不是创建新对象,实际上对象只创建一个。
    
    7)super关键字代表什么?
       super关键字代表的是“当前对象”的那部分父类型特征
       super不是引用。也不保存内存地址,不指向任何对象。
    8)super的使用:
        super.属性名                【访问父类的属性】
        super.方法名(实参)        【访问父类的方法】
        super(实参)                    【调用父类的构造方法】


6、
    *  system.exit()————(终止程序的运行,退出JVM虚拟机)        
    *  实例变量赋默认值是在执行构造方法时赋值;
    

 六、接口、抽象类

1、抽象类
      1)抽象类:
         类与类之间有共同特征,将这些具有共同特征的类进一步抽象形成抽象类;
      由于类本身不存在,所以抽象类无法创建对象。
      
      2)抽象类属于引用数据类型
      
      3)抽象类无法实例化,无法创建对象,用来被子类继承;
      
      4)final与abstract不能联合使用,两个关键字是对立的(final修饰的类不能被继承;抽象类主要用来被继承)
      
      5)抽象类的子类可以是抽象类
      
      6)抽象类虽然无法实例化,但抽象类有构造方法,这个构造方法是供子类使用的
      
      7)抽象方法:
          抽象方法本身没有实现的方法,没有方法体的方法。
              public abstract void display();
                 *没有方法体,以分号结尾
                 *有abstract修饰
                 
      8)抽象类不一定有抽象方法;有抽象方法的一定是抽象类;
      
      9)*****(重要)
          一个非抽象的类继承抽象类,必须实现抽象类中的抽象方法----覆盖/重写,
        这是java语法强行规定的,否则编译器报错。
        
    作用:降低接口实现类对接口实现过程的难度,
        将接口中不需要使用的方法交给抽象类完成,
        这样接口实现类只需要实现需使用的方法即可。
    
    
2、接口(基础语法)
    1)接口是一种"引用数据类型",编译后也是一个class文件
    2)接口是完全抽象的。(抽象类是半抽象的);也可以说接口是特殊的抽象类。
    3)接口定义语法:
        [修饰符列表] interface 接口名{}
    4)接口支持多继承,一个接口可以继承多个接口
    5)接口只包含两部分内容:常量 、 抽象方法;
    6)接口中的元素都是 public 修饰的(公开的)
        jdk1.8添加了默认方法
    7)接口中的抽象方法定义时 “public abstract” 可以省略,定义常量时的 “public static final” 可以省略
    8)接口中的方法都是抽象方法,没有方法体,以“;”结尾;
    
    9)<*****> 一个非抽象的类实现接口必须将接口中的所有方法加以实现(覆盖、重写)
    
    10)一个类可以实现多个接口; 
            弥补了java的缺陷:java中类与类只支持单继承。实际上单继承是为了简单而出现的。
    
    11)类与类之间叫继承,类与接口之间叫“实现”。
        *继承使用 extends 关键字,实现使用 implement 关键字
        *extends与implement可以共存,extend在前,implement在后
    
    12)使用接口写代码时,可以使用多态(父类型引用指向子类型对象)
           **调用其他接口中的方法需要转型(接口转型)

    13)接口与接口之间进行强制类型转变时,没有继承关系也可以强转。(编译不报错)
    但要注意,运行时可能会出现ClassCastException异常,需要加instanceof进行判断。

3、接口在开发中的作用    
        接口在开发中的作用类似于多态在开发中的作用
        多态:面向抽象编程,不要面向具体编程,降低抽象耦合度,提高程序扩展性
        
        (面向抽象编程————面向接口编程)
        
    
            总结一句话:三个字“解耦合”
                面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则。
                接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)

                接口可以解耦合,解开的是谁和谁的耦合!!!
                任何一个接口都有调用者和实现者。
                接口可以将调用者和实现者解耦合。
                调用者面向接口调用。
                实现者面向接口编写实现。

                以后进行大项目的开发,一般都是将项目分离成一个模块一个模块的,
                模块和模块之间采用接口衔接。降低耦合度。
    
      
4、类型与类型之间的关系
    is a(继承)、has a(关联)、like a(实现)
      is a:
          Cat is an Anmal(猫是一个动物)
          凡是满足 is a 的表示都可以设置为继承
          A extends B
      
      has a:
          I has a Pen(我有一支笔)
          凡是满足 has a 关系的表示“关联关系”
          关联关系通常以“属性”的形式存在
          A{
              B b;
          }
          
      like a:
          Cooker like a FoodMenu(厨师像一个菜单)
          凡是满足 like a 的表示“实现关系”
          实现关系通常是:类实现接口
          A implements B


5、抽象类与接口之间的区别
    抽象类是半抽象的;接口是完全抽象的;

    抽象类中有构造方法;接口中没有构造方法;
 
    接口与接口间支持多继承;类与类之间只能单继承;

    一个类可以同时实现多个接口
    一个类只能继承一个抽象类(单继承)

    接口中只允许出现常量和抽象方法;

    接口一般是对“行为”的抽象    
          
            
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      

    
    
    
    
    
    
    
    
    


版权声明:本文为huangfeilong1原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。