Loading... ## 黑马视频(7/1) ## Java SE ## day01 计算机基础知识 * JDK 全称 Java Development Kit * JRE 全称 JAVA RunTime Environment * java的运行依靠 JVM 全称 Java Virtual Machine * java 程序运行:源文件( .java ) -->编译器( compiler ) -->字节码文件( .class ) --> 解释器运行字节码文件 * java 之父 詹姆斯·高斯林( james Gosling ) ,Oak --> java,IBM --> Sun * write once , run anywhere * jdk 下的几个文件 1. bin: 可执行程序 2. db: 小型数据库,javaDB,纯java编写 3. include : 存放的头文件 * windows 下几个经常使用的快捷键 1. Tab 制表符 2. Shift 上当转换键 3. Ctrl 控制键 4. Alt “ Alter ” 的缩写,汉语 “ 改变 ” 5. PrScrn ( PrintScreen )摁下之后没反应,之后打开画图,Ctrl + V ,就可以捕获刚才的截图。 6. win+ D 快速显示桌面 7. win+ R 执行 运行 命令 8. win + L 切换用户 9. win + E 打开我的电脑 * 学习一门语言的第一步就是用这门语言书写出 HelloWord . * 注释会增加代码的可读性,所以要经常加注释 * 输出十进制,直接输出即可;输出整数,二进制,需要以数字0B开头;输出整数,八进制,以数字0开头,输出整数,十六进制,以数组开头 OX(0-9 A-F),只要有点都是浮点类型的数;字符常量,单引号包裹,只能写一个字符;字符串,双引号包裹。 --- ## day02 进制运算、数据类型等 * 计算机设备中存数数据是 0 和 1 ,又被称为 bit ( 位 ) ,是最小信息单元 ,一般用小写字母 b 表示 。 8个比特位表示一个字节 ( byte ),是计算中的最小存储单元。程序运行时,分配给程序的是一个字节而不是一个位。 1. 一个字节 通常被称为 1B , 2. 1024B 通常被称为 1KB , 3. 1204KB 通常被称为 1MB , 4. 1024MB 通常被称为 1GB , 5. 1024GB 通常被称为 1TB 6. 1024TB 通常称为 1PB * 对于买的硬盘或者U盘内存的计算 500G = 500*1000*1000*1000 / 1024 /1024/1024 约等于 465 G * 十六进制 4个二进制一组,八进制 3个二进制一组 * 任意进制转十进制:系数*基数的权次幂相加 1. 系数:每一位上的数据 2. 基数:x进制,基数就是x 3. 权:在右边,从0开始编号,对应位上的编号就是该权位。 * 8421码 快速转换法,都要先换位2进制才能用 * 1 1 1 1 1 1 1 1 * 128 64 32 16 8 4 2 1 * 11001100 = 4+8+68+128 = 204 * 十进制转8进制: 111 = 7 100 = 4 ,7就是八进制中最大的数,结果:74 * 十进制转十六进制:0011 = 3 1100 = C ,结果:3C * 十进制用快速转换法转变为2进制:60和128、64比较,没128大,所以第一位0,64比较,第二位也是0,和32比较,减去剩下28,第三位是1,和16比较,能减去,剩下12,所以第四位1,以此类推,第五位1,第六位1,剩下第7位,第8位也是0,所以结果:00111100 * 原码 * 二进制定点表示法,最高位符号位,0表示正,1表示负,其余表示数值大小 * -7和7的二进制表示: * +7 = 0 0000111 反码: 0 0000111 补码: 0 0000111 * -7 = 1 0000111 反码: 1 1111000 补码:1 1111001 * 反码 :正数的反码与原码相同,负数的反码是对其原码逐位取反,符号位除外。 * 补码:数的补码与其原码相同,负数的补码是在其反码的末位加1,求反码在补码的最后一位减一 * java 中进行运算的时候都是以补码进行运算的。 --- ### day03 运算符 * char可以存储汉字是因为使用的是 Unicode 编码,占两个字节,中文刚好也占两个字节,单引号放单个字符。 * 任何一个正整数 % 2 不是 0 就是 1 可以当做一个判断条件 ```java byte b = 10 ; b ++ ; // b = (byte) (b+1) b = b + 1 ; // b = b + 1 结果是 int 类型 , 强制转为 byte 类型,会出现精度损失 short s = 1; s+=1 ; // s = (short)(s+1) ; ``` * 改变两个变量的值,不需要第三方变量(有弊端,可能超过取值范围 ): ```java int x = 5, y = 10 ; x = x + y ; y = x -y ; x = x -y ; -------------- // 利用异或操作符运算 // 一个数据对另一个数据异或两次,本身不变 x = x ^ y ; y = x ^ y ; x = x ^ y ; ``` * 关于左移。向左移动几次,就是乘以 2 的 几次幂。 * 关于右移,向右移动几次,就是除以 2 的几次幂 * 最有效率的 2 * 8 ``` System.out.println(2 << 3) ;0 ``` --- ### day04 循环结构 * 有小括号不能有分号,有分号不能有小括号 * do...while() 至少执行一次循环,while()、for() 必须先判断,成立了再继续执行语句。 * return :结束方法,break:跳出循环,continue:终止本次循环继续执行下次循环。 * switch语句中,表达式的数据类型,可以是byte,short,int,char,enum(枚举),JDK7后可以接收字符串。 --- ### day05 数组 *** [I@19bb25a** * [ 代表一个数组,几个 [ 就是几维数组 * I 代表 int 类型 * @ 是固定的 * 19bb25a 代表的是十六进制的地址值 * 栈(FILO): 存储局部变量。局部变量:定义在方法声明上和方法中的变量 * 堆:存储 new 出来的数组或者对象 * 方法区: 相当于代码仓库,字节码文件加载到内存中,就是进入到了方法区中 * 本地方法区:和系统相关 * 寄存器:给 cpu 使用 * 数组初始化不允许动静结合 ```java int[] arr1 = new int[2] ; // 动态初始化 int[] arr = new int[5]{1,2,3,4,5} ; //不允许这样写,属于动静结合 int[] arr2 = {1,2,3,4,5} ; //静态初始化 int[] arr3 ; arr3 = new int[]{1,2,3,4,5}; //静态初始化 ``` * 访问数组中不存在的索引,会出现索引越界异常。 * 方法: * arr.length -->表示数组长度 ,取值的最大长度是 arr.length -1 (比如 arr.length 是5,那么可取的最大长度就是4) * 二维数组的定义: * 数据类型 数组名 [] [] =new 数据类型[m][n] ; * 数据类型 [] 数组名[] = new 数据类型[m][n] ; 例如: int[] y[] * int[] x,y[] ; x是一维数组,y是二维数组 * 基本数据类型的值传递,不改变原值,因为调用后就会弹栈,局部变量随之消失。 * 引用数据类性的值传递改变原值(例如:数组),因为即使方法弹栈,但是堆内存数组对象还在,可以通过地址继续访问。 * Java 中只有传值,因为地址也是值,这是Java之父所认可的。 --- ### day06 面向对象 基础 * java 中类是最基本的单位。 * 成员变量: 就是事物的属性 * 成员方法:就是事物的行为 * 类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体。 * 创建一个对象后,如果没有任何引用指向该对象,那么该对象就会变成垃圾,被垃圾回收机制回收。 * 创建对象后实际上是把地址值赋给那个类型的那个变量。 * 当这个对象使用完后,main 方法也弹出栈,所有的引用都将置为空。 * 成员变量: * 在类中方法外 * 在堆内存(成员变量属于对象,对象进入堆内存) * 随着对象的创建而存在,随着对象的消失而消失 * 有默认初始值 0 、 null 、 false * 局部变量: * 在方法定义中或者方法声明上 * 在栈内存(局部变量属于方法,方法进入栈内存) * 随着方法的调用而存在,随着方法的调用完毕而消失 * 没有默认初始值,必须定义,赋值,然后才能使用 * 局部变量的名称可以和成员变量的名称一样,但是在方法中使用的时候,采用的是就近原则。 * 基本数据类型变量包括:byte,short,int,long,float,double,boolean,char * 引用数据类型包括:数组,类,接口,枚举, * 引用数据类型当参数,给的是地址值。基本数据类型当参数,给的是值 * 匿名对象只适合对方法的一次调用,因为多次调用就会产生多个对象。 * 匿名对象可以调用属性,但是没有意义,因为调用后就变成了垃圾。如果需要赋值。还是要创建有名字的对象。 * 跟主函数在一起的方法都加静态。 * 匿名对象可以当做参数传递。实际是把地址值传了过去。 * 封装:指隐藏对象的属性和实现细节,,仅对外提供公共访问方式。把不需要对外提供的内容都隐藏起来,把属性隐藏,提供公共方法对其访问, * this 关键字代表当前对象的引用,用来区分局部变量和成员变量。对象中记录了什么地址,this 中就记录了什么地址。成员变量必须由对象来调用。 * get 方法中返回的变量,系统会自动加上 this 关键字。 --- ### day07 面向对象 构造方法等 * 构造方法:给对象的数据(属性)进行初始化: * 方法名与类名相同(大小写也要一样) * 没有返回类型 * 没有具体的返回值 * 如果没有给出构造方法,系统将自动提供一个默认的无参构造方法。 * 如果给出了构造方法,系统将不提供默认的构造方法,这个时候如果还想用无参构造方法,必须自己给出,建议永远给自己无参构造方法。 * 构造方法主要是给属性进行初始化,set 方法主要是修改属性值。 * 创建一个对象并调用的步骤: * xxx.class 加载进内存, * 声明一个 xxx 类型的引用, * 在堆内存创建对象 * 给堆对象中属性默认初始化值 * 属性进行显示初始化 * 构造方法进栈,对对象中的属性进行赋值,构造方法弹栈 * 将对象的地址赋值给引用 * static 关键字特点: *随着类的加载而加载 * 优先于对象存在 * 被类的所有对象所共享(如果某个成员变量是被所有对象所共享的,那么他应该定义为静态)(共性用静态,特性用费静态) * 可以通过类名调用 * 他本身也可以通过对象名调用 * 但是推荐使用类名调用,可以不创建对象,少占用内存 * 静态修饰的内容一般称为:与类相关的类成员,类变量 * static 注意事项 : * 在静态方法中没有 this 关键字,静态是随着类加载而加载的,this 是随着对象的创建而存在,静态比对象先存在 * 静态方法:只能访问静态的成员变量和静态的成员方法 * 非静态方法:可以访问静态或者非静态的成员变量,可以访问静态或者非静态的成员方法 * 即:静态只能访问静态 * 静态变量也叫类变量,成员变量也叫对象变量 * 静态变量属于类,成员变量属于对象,所以也被称为实例变量或者对象变量 * 静态变量存储于方法区的静态区,成员变量存储于堆内存, * 静态变量随着类加载和消失,成员变量随着对象得创建与消失 * 静态变量可以通过类名调用,也可以通过对象调用,成员变量只能通过对象调用。 * public :被 JVM 调用,所以权限要足够大 static : 被 JVM 调用,不需要创建对象,直接类名.对象即可调用 void:被 JVM 调用,不需要有人和的返回值 main:这样写才可以被 JVM 识别,但是不是关键字 String[] agrs :以前是用来接收键盘录入的,可以修改。现在也不用了,Scanner 可以代替 * 如果一个类中所有的方法都是静态的,需要再多做一步,创建私有构造方法。目的是不让其他类来创建对象调用静态方法。 * 文档注释:需要在 / * ** xxxx * */ 中书写 javadoc -d api -version -author xxx.java * @author 作者 * @version v1.0 版本 * @param arr 方法说明 * @return 对返回值类型的描述 * Math.Random() 生成随机的大于等于0.0 小于1.0的伪随机数 * 生成1-100随机数 * Math.random() 0.0000 - 0.9999 * Math.random() *100 ===> 0.000-99.999 * (int) ( Math.Random() * 100 ) ==> 0 - 99 * (int) ( Math.Random() * 100 ) + 1 ==> 1 - 99 --- ### day08 面向对象 代码块的分类 * 局部代码块 : 在方法中出现,限定变量生命周期,及早释放,提高内存利用率 * 构造代码块,在类中方法外出现,多个构造方法中相同的代码存放到一起,每次调用构造都执行,在构造方法前执行,每创建一次对象就会执行一次,优先于构造函数执行 * 静态代码块: * 在类中方法外出现,加了 static 修饰符 * 在类中方法外出现,加上static 修饰符,用于给类进行初始化,在类加载的时候就执行,并且只执行一次 * 一般用于加载驱动 * 主方法中的静态代码块 优先于主方法执行 * 关于继承: * java 中只支持单继承,不支持多继承 * java 支持多继承(继承体系) * 如果想用这个体系的所有功能用最底层的类创建对象(子类可以使用父类,父类不能够使用子类) * 如果想用这个体系的共性功能,看最顶层的类 * 子类只能继承父类的所有非私有的成员(成员方法和成员变量) * 子类不能继承父类的构造方法,但是可以通过 super 关键字访问父类的构造方法 * 不要为了部分功能而去使用继承 * 字父类出现同名变量,使用就近原则,使用子类中的值。但是开发中不会出现这种情况,因为子类继承父类就是为了使用父类的成员,如果定义了同名的就没有意义了。 * this 与 super * this: * 代表当前对象的引用,谁来调用我,我就代表谁 * this.成员变量 调用本类的成员变量,也可以调用父类的成员变量 * this(....) :调用本类的构造方法 * this.成员方法 调用本类的成员方法,也可以调用父类的成员方法 * super : * 代表当前对象父类的引用 * super.成员变量,调用父类的成员变量 * suoer(....) 调用父类的构造方法 * super.成员方法 调用父类的成员方法 * 子类中的构造方法,默认都会访问父类中的空参构造方法 * 因为子类会继承父类中的数据,可能还会使用父类的数据,所以子类初始化之前,一定要先完成父类数据的初始化 * 每个构造方法的第一条语句默认都是:super() Object类最顶层的父类,用来访问父类中的空参构造方法。 * 如果父类没有无参构造方法,子类可以通过super或者 this 解决, super(...) this(...) 必须出现在构造方法的第一条语句上 * 子类方法和父类方法相同,还有一个名字叫做:重写,复写,覆盖,返回值类型可以使子类或者父类。 * 当子类需要父类的功能,而功能主题子类有自己的内容时,可以重写父类中的方法,这样既继承了父类的功能,又定义了子类特有的功能。 * 父类中的私有方法不能被重写,因为私有方法不会被继承,子类重写父类方法的时候,访问权限不能更低,最好一致 * 父类静态方法子类必须通过静态方法进行重写,定义子类的目的是为了比父类更加强大 ,静态只能覆盖静态,算不上重写 * 子类重写父类方法的时候,最好声明一模一样。 * overload 和 override : * overload 可以改变返回值类型,只看参数列表 * 方法重写:子类中出现了和父类中方法声明一模一样的方法,与返回值类型欧冠,返回值一致(或者是子类或者是父类)的 * 方法重载:本类中出现的方法名一样,参数列表不同的方法,与返回值类型无关, * 子类对象调用方法的时候,先找子类本身,再找父类。 * 子类调用父类中的方法就是加 this 或者 super 。 * final : * 修饰类,类不能被继承 * 修饰变量,变量就成了常量,只能够被赋值一次 * 修饰方法,方法不能被重写 * 一般和 public static 共用 * 修饰基本类型:值不能被改变 * 修饰引用类型,是地址值不能被改变,对象中的属性可以改变。 * 初始化时机:显示初始化、在对象构造完毕前初始化即可。成员变量的默认初始化值是无效值 * 常量命名:如果是一个单词,所有字母都大写,如果多个字母,每个单词字母都大写,每个单词用下划线隔开 --- ### day09 面向对象 多态 * 多态( polymorphic ) : * 事物存在的多种状态, * 前提是要有继承关系,要有方法重写,要有父类引用指向 子类对象 * 成员变量: 编译看左边( 父类 ) ,运行看左边(父类) * 成员方法:编译看左边(父类),运行看右边(子类) * 多态又叫动态绑定,编译看的是左边(父类)中有没有该方法,运行时看的是右边(子类),运行子类中的方法,真正进入栈内存的是子类的方法。 * 静态方法:编译看左边(父类),运行看左边(父类),(静态和类相关,算不上重写,所以访问还是左边的) * 只有非静态的成员方法,编译看左边,运行看右边 * 基本数据类型自动类型提升和强制类型转换。 * 父类引用指向子类对象,就是向上转型, * 多态好处: * 提高了代码的维护性(继承保证) * 提高了代码的扩展性(由多态保证) * 可以当做形式参数,可以接收任意子类对象, * 开发的时候很少在创建对象的时候用父类引用指向子类对象,直接创建子类对象更方便,可以使用子类中特有属性和行为 * 当做参数的时候多态最好,因为扩展性强 * 多态的弊端: * 不嗯能够使用子类的特有属性和行为。 * 关键字 instanceof 用来判断前边的引用是否是后边的数据类型 * 抽象类: * 抽象类和抽象方法必须使用 abstract 来修饰 * 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者接口 * 抽象类不允许被实例化的, 按照多态的方式,需要对其具体的子类实例化,才能使用 * 抽象类的子类要么是抽象类,要么重写抽象类中的所有抽象方法。 * 抽象类的成员特点: * 成员变量:既可以是变量,也可以是常量,abstract 不能修饰成员变量 * 有构造方法,用于子类访问父类数据的初始化 * 成员方法:可以是抽象的也可以是非抽象的 * 抽象方法:强制要求子类做的事 * 非抽象方法:子类继承的事情,提高代码复用性 * 一个抽象类没有抽象方法,可以被定义为抽象类,这么做的目的只有一个,那就是不让其他类创建本类对象,交给子类完成。 * abstract 和 static 不能共存,被 abstract 修饰的方法没有方法体,被static 修饰 的可以用 类名. 调用 , 但是调用抽象方法是没有意义的。 * abstract 和 final 也不能共存。被 final 修饰的不让子类重写,被 abstract 修饰的 强制 子类重写,矛盾 * abstract 和 private 不能共存, 被 private 修饰的不让子类访问,被 abstract 修饰的 是为了让子类看到并强制重写,所以矛盾, * 接口 interface * 接口用关键字 interface 表示 : interface 接口名 { } * 类实现接口用 implements 表示 class 类名 implenments 接口名 { } * 接口不能实例化 * 接口中不能定义非抽象方法 * 接口的子类可以是抽象类也可以是具体类。推荐重写接口中的所有抽象方法。 * 成员变量只能是常量,并且只能是静态的公共的, * 成员变量默认修饰符:public static final ,建议手动给出 * 构造方法:接口没有构造方法 * 成员方法:只能是抽象方法。默认修饰符 public abstract ,建议手动给出 * 类与类只能继承,只能单继承,可以多层继承 * 类与接口:可以单实现也可以多实现,可以在继承一个类的同时实现多个接口 * 接口与接口:继承关系,可以单继承也可以多继承 * 抽象类: * 成员变量:可以是变量也可以是常量 * 构造方法:有 * 成员方法:可以抽象,也可以非重抽象 * 接口 * 成员方法:只能是抽象 * 成员变量:只能是常量 * 抽象类: 被继承体现的是 : is a 关系,抽象类中定义的是该继承体系的共性功能 * 接口: 被实现体现的是 like a 关系 , 接口中定义的是该继承体系的扩展功能 --- ### day10 面向对象,包,内部类 * 定义包的格式: * package 包名 * 多级包用 . 隔开 * package 语句必须是程序的第一条可执行的代码 * package 语句在一个 java 文件中只能有一个 * 如果没有package 默认表示无包名 * javac -d . xxx.java * import 导包,* 代表通配符,用 * 导入可能导致查找慢,开发中一般具体导入需要的类 |条件|本类|同一个包下(子类和无关类)|不同包下(子类)|不同包下(无关类)| |:--:|:--:||||||| |private|Y|N|N|N| |默认|Y|Y|N|N| |protected|Y|Y|Y|N| |public|Y|Y|Y|Y| * 面向对象(类及其组成所使用的常见修饰符) * A:修饰符: * 权限修饰符:private,默认的,protected,public * 状态修饰符:static,final * 抽象修饰符:abstract * B:类: * 权限修饰符:默认修饰符,public * 状态修饰符:final * 抽象修饰符:abstract * 用的最多的就是:public * C:成员变量: * 权限修饰符:private,默认的,protected,public * 状态修饰符:static,final * 用的最多的就是:private * D:构造方法: * 权限修饰符:private,默认的,protected,public * 用的最多的就是:public * E:成员方法: * 权限修饰符:private,默认的,protected,public * 状态修饰符:static,final * 抽象修饰符:abstract * 用的最多的就是:public * F:除此以外的组合规则: * 成员变量:public static final 接口 * 成员方法: * public static * public abstract * public final * 内部类访问特点: * 内部类可以直接访问外部类的成员,包括私有。 * 外部类要访问内部类的成员,必须创建对象。 * 外部类名.内部类名 对象名 = 外部类对象.内部类对象; ( 两个new ) * 访问 静态内部类 : 外部类名.内部类名 对象名 = 外部类名.内部类对象 (一个ne * 局部内部类访问局部变量必须用final修饰 * 当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用 * 匿名内部类:前提:存在一个类或者接口,类可以是具体类也可以是抽象类 * 本质:是一个继承了该类或者实现了该接口的子类匿名对象。 * 不能向下转型,因为没有子类类名,只有一个方法的时候使用 * new 类名或者接口名(){ //实现接口 重写方法; // 重写抽象方法 }; // 结束,整个代表的是一个对象 * 在开发中匿名内部类可以当做参数传递。本质是把匿名内部类看做一个对象 。父类引用指向子对象 * 链式编程:每次调用方法后还能继续调用方法,证明调用方法返回的是对象 --- ### day11 eclipse 使用和 Object 对象中方法 * eclipse: * 红色必须处理,黄色代表没使用 * F11 运行 * F4 打开父类 * Alt + / 提示符,内容辅助 * Ctrl + N 新建 * Ctrl + Shift + f 格式化 * Ctrl + Shift + o 整理包 * Ctrl + / 单行注释 * Ctrl +Shift + / 多行注释 , Ctrl +Shift + \ 取消多行注释 * Alt + 上下箭头 移动这一行 * F3 或者 Ctrl + 鼠标点击 --> 查看源码,选中类名 * Ctrl + Shift + t 查找具体类 * Ctrl + o 查找具体类的具体方法 * Ctrl + 1 给出建议,生成建议的代码 * Ctrl + D 删除 当前行或者选中的多行 * Alt + Shift + m 抽取方法 * Alt + Shift + r 改名 * Ctrl + Alt + 下 ,向下复制一行当前行 * Alt + Shift + S * o :生成有参构造 * c 生成空参构造 * r get set 方法 * s toString 方法 * jar 是多个 class 文件的压缩 ,主要是使用别人写好的东西,一般在 开发中都将用到的 jar 文件放在 lib 包中 * Debug调试: * 左键双击加上断点 * 进栈的地方就是加断点的地方 * 左上角: * variables : 查看程序中的变量值的变化 * breakpoints:删除或者查看断点 * ForDemo 被查看的源文件 * Console 控制台 * API Application Programming interface 应用程序编程编程接口 * Objext 类:是类层次结构的根类,每个类都使用Object作为超类,所有对象包括数组都实现这个类的方法, * hashCode() ; native 修饰,代表借助本地资源计算出的 一个 hashCode 值,返回一个 int 数 * getClass() ,返回此对象的运行时类,获取该对象的字节码文件, * getName() ,获取这个类的名称, 详细类名 * toString() , 返回该对象的字符串表示 getClass().getName + @ + Integer.toHexString(hashCode) , hashCode --> 换成了16进制返回 ,类名+@+hashCode 16进制表示方方式。没有实际意义,一般都选择将其进行重写。可以更方便的显示属性值。如果直接打印对象的引用,会默认调用toString方法 * Equals ,比较两个对象是否相等,比较的是两个对象的地址值,没有什么意义,开发中需要重写,因为开发中一般比较的对象的属性值,开发中认为相同属性的是一个对象 * == 和 euqals ,都可以作比较,返回的都是boolean , * == 是比较运算符,既可以比较基本数据类型,也可以比较引用数据类型,基本数据类型比较的是值,引用数据类型比较的是地址值 * equals 方法只能比较的是引用数据类型,equals 方法重写前,比较的是地址值,底层依赖的是 == , 但是比较地址值是么有意义的,所以需要重写 equals 方法,比较对象中的属性值, 最后修改:2020 年 11 月 02 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 哇卡哇卡