Java常用知识点汇总(基础:数组,类与对象,接口与继承)

 

目录

数组

1.引用

2.增强型for循环

3.复制数组 arraycopy()

4.针对数组的工具类 Arrays

5.选择法排序

6.冒泡排序

7.练习-二维数组中的查找(剑指offer)

类与对象

1.方法重载

2.构造函数

3.JAVA中的this

4.包

5.访问修饰符

6.类属性/静态属性

7.类方法/静态方法

8.属性初始化

9.饿汉式与懒汉式单例模式

10.枚举类型

接口与继承

1.接口

2.对象转型

3.重写方法

4.多态

5.隐藏

6.super

7.Object

8.final

9.抽象类

​10.内部类

11.默认方法

类、抽象类、接口  简化概括

StringBuffer


数组

1.引用

创建数组的时候,要指明数组的长度。 如:new int[5] 
引用:如果变量代表一个数组,比如a,我们把a叫做引用 
           与基本类型不同 
               int c = 5; 这叫给c赋值为5 
           声明一个引用 int[] a; 
               a = new int[5]; 
               让a这个引用,指向数组

public class HelloWorld {
    public static void main(String[] args) {
        //声明一个引用
        int[] a;
        //创建一个长度是5的数组,并且使用引用a指向该数组
        a = new int[5];
         
        int[] b = new int[5]; //声明的同时,指向一个数组
         
    }
}

2.增强型for循环

只能用来取值,不能用来修改数组里的值

//增强型for循环遍历
int values [] = new int[]{18,62,68,82,65,9};
for (int each : values) {
    System.out.println(each);
}

3.复制数组 arraycopy()

把一个数组的值,复制到另一个数组中

System.arraycopy(src, srcPos, dest, destPos, length)
src: 源数组
srcPos: 从源数组复制数据的起始位置
dest: 目标数组
destPos: 复制到目标数组的起始位置
length: 复制的长度

public class Test{
    public static void main(String[] args) {
        int a [] = new int[]{18,62,68,82,65,9};
        int b[] = new int[3];//分配了长度是3的空间,但是没有赋值
         
        //通过数组赋值把,a数组的前3位赋值到b数组
         
        //方法一: for循环
        for (int i = 0; i < b.length; i++) {
            b[i] = a[i];
        }
        
        //方法二: System.arraycopy(src, srcPos, dest, destPos, length)     
        System.arraycopy(a, 0, b, 0, 3);
    }
}

4.针对数组的工具类 Arrays

import java.util.Arrays;
 
public class Test {
	public static void main(String[] args) {
        int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
        int b[] = Arrays.copyOfRange(a, 1, 3);
        int c[] = new int[10];
        
        System.out.println("b数组:"+Arrays.toString(b));
        Arrays.sort(a);
        System.out.println("排序后的a数组:"+Arrays.toString(a));
        System.out.println("数字 62出现的位置:"+Arrays.binarySearch(a, 62));
        System.out.println("数组a和数组b是否相同:"+Arrays.equals(a, b));
        Arrays.fill(c, 5);
        System.out.println("用5填充后的数组c:"+Arrays.toString(c));
        
    }
}
  • 数组复制 copyOfRange

与使用System.arraycopy进行数组复制类似的, Arrays提供了一个copyOfRange方法进行数组复制。
不同的是System.arraycopy,需要事先准备好目标数组,并分配长度。 copyOfRange 只需要源数组就就可以了,通过返回值,就能够得到目标数组了。
除此之外,需要注意的是 copyOfRange 的第3个参数,表示源数组的结束位置,该位置的值是取不到的。

  • 转换为字符串 toString

如果要打印一个数组的内容,就需要通过for循环来挨个遍历逐一打印,很麻烦。

但是Arrays提供了一个toString()方法,直接把一个数组,转换为字符串,这样方便观察数组的内容

Arrays.toString(a);

  • 排序 sort

Arrays.sort(a);

  • 搜索 binarySearch

binarySearch用于查询元素出现的位置。

需要注意的是,使用binarySearch进行查找之前,必须使用sort进行排序
如果数组中有多个相同的元素,查找结果是不确定的

Arrays.sort(a);

Arrays.binarySearch(a, 62);

  • 判断是否相同 equals

比较两个数组的内容是否一样
第二个数组的最后一个元素是8,和第一个数组不一样,所以比较结果是false

Arrays.equals(a, b);

  • 填充 fill

使用同一个值,填充整个数组

Arrays.fill(a, 5);

5.选择法排序

public class Test {
    public static void main(String[] args) {
        for(int i = 0; i < a.length-1; i++){
        	for(int j = i+1; j < a.length; j++){
        		if(a[j] < a[i]){
        			int temp = a[j];
        			a[j] = a[i];
        			a[i] = temp;
        		}
        	}
        }
    }
}

6.冒泡排序

public class Test {
    public static void main(String[] args) {
        for(int i = 0; i < a.length; i++){
        	for(int j = 0; j < a.length-i-1; j++){
        		if(a[j+1] < a[j]){
        			int temp = a[j+1];
        			a[j+1] = a[j];
        			a[j] = temp;
        		}
        	}
        }
    }
}

7.练习-二维数组中的查找(剑指offer)

题目链接

题目描述:

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解决思路:
矩阵是有序的,从左下角来看,向上数字递减,向右数字递增。
因此从左下角开始查找,当要查找数字比左下角数字大时,右移;
要查找数字比左下角数字小时,上移。
public class Solution {
    public boolean Find(int target, int [][] array) {
        int rowCount = array.length;
        int colCount = array[0].length;
        int i,j;
        for(i=rowCount-1,j=0;i>=0&&j<colCount;)
        {
            if(target == array[i][j])
                return true;
            if(target < array[i][j])
            {
                i--;
                continue;
            }
            if(target > array[i][j])
            {
                j++;
                continue;
            }
        }
        return false;
    }
}

类与对象

1.方法重载

  • 概念

方法的重载指的是方法名一样,但是参数类型不一样。在调用同名方法的时候,会根据传递的参数类型以及数量,自动调用对应的方法

有一种英雄,叫做物理攻击英雄 ADHero,为ADHero 提供三种方法

public void attack()

public void attack(Hero h1)

public void attack(Hero h1, Hero h2)

  • 可变数量的参数

如果要攻击更多的英雄,就需要设计更多的方法,这样类会显得很累赘
这时,可以采用可变数量的参数,只需要设计一个方法

public void attack(Hero ...heros)

在方法里,用操作数组的方式处理参数heros即可。

2.构造函数

  • 概念

方法名和类名一样(包括大小写),没有返回类型,实例化一个对象的时候,必然调用构造方法。

无参的构造方法,如果不写,就会默认提供一个

一旦提供了一个有参的构造方法,同时又没有显式的提供一个无参的构造方法 ,那么默认的无参的构造方法,就失效了

3.JAVA中的this

  •  通过this关键字访问对象的属性

  • 通过this调用其他的构造方法

如果要在一个构造方法中,调用另一个构造方法,可以使用this()

4.包

  • 把比较接近的类,规划在同一个包下

 

  • 使用其他包下的类,必须import

使用同一个包下的其他类,直接使用即可 
但是要使用其他包下的类,必须import

5.访问修饰符

  • 类之间的关系

  • 什么情况该用什么修饰符?

从作用域来看,public能够使用所有的情况。 但是大家在工作的时候,又不会真正全部都使用public,那么到底什么情况该用什么修饰符呢?

1. 属性通常使用private封装起来
2. 方法一般使用public用于被调用
3. 会被子类继承的方法,通常使用protected
4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

再就是作用范围最小原则
简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来了

6.类属性/静态属性

  • 定义

类属性: 又叫做静态属性 
对象属性: 又叫实例属性,非静态属性 

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性 
当一个属性被声明成类属性,那么所有的对象,都共享一个值 
与对象属性对比: 
不同对象的 对象属性 的值都可能不一样,比如盖伦的hp 和 提莫的hp 是不一样的。 
但是所有对象的类属性的值,都是一样的

给英雄设置一个类属性叫做“版权" (copyright), 无论有多少个具体的英雄,所有的英雄的版权都属于 Riot Games公司。

  • 访问类属性

7.类方法/静态方法

  • 定义

  • 调用类方法

  • 对象方法和类方法的使用场景

8.属性初始化

  • 对象属性初始化

对象属性初始化有3种
1. 声明该属性的时候初始化 
2. 构造方法中初始化
3. 初始化块

  • 类属性初始化

类属性初始化有2种
1. 声明该属性的时候初始化
2. 静态初始化块

只有一个类时,执行顺序为:属性声明-->初始化块-->构造方法

9.饿汉式与懒汉式单例模式

LOL里有一个怪叫大龙GiantDragon,只有一只,所以该类,只能被实例化一次

  • 单例模式

单例模式又叫做 Singleton模式,指的是一个类,在一个JVM里,只有一个实例存在。

(面试题)什么是单例模式?

回答的时候,要答到三元素

  1. 构造方法私有化
  2. 引用字段属性私有化并且共用唯一
  3. 通过对外提供公共的get方法调用,返回这个唯一的对象
  • 饿汉式单例模式

GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new 得到新的实例。
GiantDragon 提供了一个public static的getInstance方法,外部调用者通过该方法获取12行定义的对象,而且每一次都是获取同一个对象。 从而达到单例的目的。
这种单例模式又叫做饿汉式单例模式,无论如何都会创建一个实例

  • 懒汉式单例模式

懒汉式单例模式与饿汉式单例模式不同,只有在调用getInstance的时候,才会创建实例

  • 饿汉式和懒汉式的使用场景

10.枚举类型

  • 预先定义的常量

枚举enum是一种特殊的(还是类),使用枚举可以很方便的定义常量
比如设计一个枚举类型 季节,里面有4种常量

public enum Season {

    SPRING,SUMMER,AUTUMN,WINTER

}

一个常用的场合就是switch语句中,使用枚举来进行判断

注:因为是常量,所以一般都是全大写

  • 使用枚举的好处

假设在使用switch的时候,不是使用枚举,而是使用int,而int的取值范围就不只是1-4,有可能取一个超出1-4之间的值,这样判断结果就似是而非了。(因为只有4个季节)
但是使用枚举,就能把范围死死的限定在这四个当中

  • 遍历枚举

借助增强型for循环,可以很方便的遍历一个枚举都有哪些常量

接口与继承

1.接口

在设计LOL的时候,进攻类英雄有两种,一种是进行物理系攻击,一种是进行魔法系攻击 ,这时候,就可以使用接口来实现这个效果。 
接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击。

  • 设计一类英雄,能够使用物理攻击

创建一个接口 File->New->Interface 
AD ,声明一个方法 physicAttack 物理攻击,但是没有方法体,是一个“空”方法

  • 设计一类英雄,能够使用物理攻击

同理设计一类英雄,只能使用魔法攻击,这类英雄在LOL中被叫做AP 

  • 设计一类英雄,既能进行物理攻击,又能进行魔法攻击

一类英雄,能够同时进行物理攻击和魔法攻击 ,比如伊泽瑞尔,皮城女警凯特琳

2.对象转型

  • 引用类型与对象类型的概念   

在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad
对象是有类型的, 是ADHero
引用也是有类型的,是ADHero
通常情况下,引用类型和对象类型是一样的
接下来要讨论的类型转换的问题,指的是引用类型和对象类型不一致的情况下的转换问题

  • 子类转父类(向上转型)

所谓的转型,是指当引用类型对象类型不一致的时候,才需要进行类型转换
类型转换有时候会成功,有时候会失败(参考基本类型的类型转换)
到底能否转换成功? 很简单的判别办法:把右边的当做左边来用,看说得通不

Hero h = new Hero();

ADHero ad = new ADHero();

h = ad;

右边ad引用所指向的对象的类型是 物理攻击英雄
左边h引用的类型是 普通英雄
把物理攻击英雄 当做 普通英雄,说不说得通? 说得通,就可以转

所有的子类转换为父类,都是说得通的。比如:
苹果手机 继承了 手机,把苹果手机当做普通手机使用

  • 父类转子类(向下转型)

  • 没有继承关系的两个类,互相转换

没有继承关系的两个类,互相转换,一定会失败
虽然ADHero和APHero都继承了Hero,但是彼此没有互相继承关系
"把魔法英雄当做物理英雄来用",在语义上也是说不通的

  • 实现类转换成接口(向上转型)

引用ad指向的对象是ADHero类型,这个类型实现了AD接口
10行: 把一个ADHero类型转换为AD接口
从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。

  • 接口转换成实现类(向下转型)

10行: ad引用指向ADHero, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功
12行: adi实际上是指向一个ADHero的,所以能够转换成功
14行: adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。 

假设能够转换成功,那么就可以使用magicAttack方法,而adi引用所指向的对象ADHero是没有magicAttack方法的。

  • instanceof

instanceof Hero 判断一个引用所指向的对象,是否是Hero类型,或者Hero的子类

3.重写方法

子类可以继承父类的对象方法 ,在继承后,重复提供该方法,就叫做方法的重写 ,又叫覆盖 override

  • 概念

父类Item有一个方法,叫做effect

子类LifePotion继承Item,同时也提供了方法effect

  • 调用重写的方法

调用就会执行重写的方法,而不是从父类的方法。所以LifePotion的effect会打印:"血瓶使用后,可以回血"

  • 如果没有重写这样的机制怎么样?

如果没有重写这样的机制,也就是说LifePotion这个类一旦继承了Item,所有方法都不能修改了
但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动,这样就增加了开发时间和维护成本

4.多态

  • 概念

操作符的多态 

类的多态 
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接

父类引用指向子类对象

(都是同一个类型,调用同一个方法,却能呈现不同的状态)

观察类的多态现象:
1. i1和i2都是Item类型
2. 都调用effect方法
3. 输出不同的结果

  • 类的多态条件

要实现类的多态,需要如下条件
1. 父类(接口)引用指向子类对象
2. 调用的方法有重写

  • 类的多态-不使用多态

如果不使用多态,假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion
useMagicPotion


除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion 净化药水
useGuard 守卫
useInvisiblePotion 使用隐形药水,等等等等

如果物品的种类特别多,那么就需要设计很多的方法

这个时候采用多态来解决这个问题。设计一个方法叫做useItem,其参数类型是Item
如果是使用血瓶,调用该方法,如果是使用魔瓶,还是调用该方法
无论英雄要使用什么样的物品,只需要一个方法即可

5.隐藏

  • 概念

重写:子类覆盖父类的对象方法

隐藏:子类覆盖父类的类方法

父类有一个类方法 :battleWin

子类隐藏父类的类方法

  • 练习

Hero h =new ADHero();

h.battleWin(); //battleWin是一个类方法

h是父类类型的引用但是指向一个子类对象,h.battleWin(); 会调用父类的方法?还是子类的方法?

答:一个指向子类对象的父类引用变量来调用父子同名的静态方法时,只会调用父类的静态方法

6.super

  •  子类显式调用父类带参构造方法 

父类显式提供两个构造方法,分别是无参的构造方法和带一个参数的构造方法

子类显式调用父类带参构造方法,使用关键字 super 显式调用父类带参的构造方法

  • 调用父类属性

通过super调用父类的moveSpeed属性,ADHero也提供了属性moveSpeed

  • 调用父类方法

ADHero重写了useItem方法,并且在useItem中通过super调用父类的useItem方法

7.Object

Object类是所有类的父类。声明一个类的时候,默认是继承了Object :

public class Hero extends Object

  • toString()

Object类提供一个toString方法,所以所有的类都有toString方法。toString()的意思是返回当前对象的字符串表达
通过 System.out.println 打印对象就是打印该对象的toString()返回值

  • finalize()

当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件。当它被垃圾回收的时候,它的finalize() 方法就会被调用。
finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。

  • equals()

equals() 用于判断两个对象的内容是否相同

假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

  • ==

这不是Object的方法,但是用于判断两个对象是否相同,更准确的讲,用于判断两个引用,是否指向了同一个对象

  • hashCode()

hashCode方法返回一个对象的哈希值

  • 线程同步相关方法

Object还提供线程同步相关方法
wait(),notify(),notifyAll()

  • getClass()

getClass()会返回一个对象的类对象,关于类对象的详细内容请参考反射机制

8.final

  • final修饰类    

当Hero被修饰成final的时候,表示Hero不能够被继承,其子类会出现编译错误

  • final修饰方法    

Hero的useItem方法被修饰成final,那么该方法在ADHero中,不能够被重写

  • final修饰基本类型变量    

final修饰基本类型变量,表示该变量只有一次赋值机会 。16行进行了赋值,17行就不可以再进行赋值了

  •  final修饰引用    

h引用被修饰成final,表示该引用只有1次指向对象的机会,所以17行会出现编译错误
但是,依然可以通过h引用修改对象的属性值hp,因为hp并没有final修饰

  • 常量    

常量指的是可以公开,直接访问,不会变化的值 。比如 itemTotalNumber 物品栏的数量是6个

9.抽象类

在类中声明一个方法,这个方法没有实现体,是一个“空”方法,这样的方法就叫抽象方法,使用修饰符“abstract" 
当一个类有抽象方法的时候,该类必须被声明为抽象类

  • 概念

为Hero增加一个抽象方法 attack,并且把Hero声明为abstract的。
APHero,ADHero,ADAPHero是Hero的子类,继承了Hero的属性和方法。
但是各自的攻击手段是不一样的,所以继承Hero类后,这些子类就必须提供不一样的attack方法实现。

  • 抽象类可以没有抽象方法

Hero类可以在不提供抽象方法的前提下,声明为抽象类 
一旦一个类被声明为抽象类,就不能够被直接实例化

  • 抽象类和接口的区别
抽象类接口
子类只能继承一个抽象类,不能继承多个子类可以实现多个接口
抽象类可以定义
   public,protected,package,private
   静态和非静态属性
   final和非final属性

但是接口中声明的属性,只能是

   public static final
即便没有显式的声明,依然默认为public static final

注: 抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法


10.内部类

内部类分为四种: 
非静态内部类、 静态内部类、 匿名类、本地类

  • 非静态内部类

非静态内部类 BattleScore “战斗成绩”
非静态内部类可以直接在一个类里面定义

比如:
战斗成绩只有在一个英雄对象存在的时候才有意义
所以实例化BattleScore 的时候,必须建立在一个存在的英雄的基础上
语法: new 外部类().new 内部类()
作为Hero的非静态内部类,是可以直接访问外部类的private实例属性name的

  • 静态内部类

在一个类里面声明一个静态内部类
比如敌方水晶,当敌方水晶没有血的时候,己方所有英雄都取得胜利,而不只是某一个具体的英雄取得胜利。
与非静态内部类不同,静态内部类水晶类的实例化 不需要一个外部类的实例为基础,可以直接实例化
语法:new 外部类.静态内部类();
因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别

  • 匿名类

匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类

有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
这样的类,叫做匿名类

  • 本地类

本地类可以理解为有名字的匿名类
内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方

  • 在匿名类中使用外部的局部变量

在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final
为什么要声明为final,请参考第二个Hero代码中的解释
注:在jdk8中,已经不需要强制修饰成final了,如果没有写final,不会报错,因为编译器偷偷的帮你加上了看不见的final

11.默认方法

  • 什么是默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法
Mortal 这个接口,增加了一个默认方法 revive,这个方法有实现体,并且被声明为了default

  • 为什么会有默认方法

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。
但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法
通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

  • 练习-默认方法

为AD接口增加一个默认方法 attack()
为AP接口也增加一个默认方法 attack() 
: ADAPHero同时实现了AD,AP接口,那么 ADAPHero 对象调用attack()的时候,是调用哪个接口的attack()?

:接口有默认方法的话, 那么实现类,就可以说继承了(默认方法可以理解为继承)这个方法 既然是AD,AP两个接口内的都是同样的默认方法, 那么实现类,就必须重写这个同名的方法,不然不知道调用的是谁的方法(本身就会有冲突报错,必须重写这个方法) 所以根本不需要考虑这个方法是从哪个接口获取的(因为同名的必须重写)

类、抽象类、接口  简化概括

类继承--->子类完全继承父类特点

抽象类继承--->继承时抽象的部分不同的子类可以有不同的实现

接口继承--->所有成员在子类都可以有不同的实现

 

至于为什么要用接口而不是抽象类 这要看二者适用的情况

当个性大于共性时,适合接口,如鸟和飞机,适合抽象出一个飞的接口

当共性大于个性时,适合抽象类,如老鹰和麻雀,适合抽象出一个鸟的父类

另外接口可以实现多重继承,这也是一个特点

StringBuffer

  • 概念

StringBuffer是可变长的字符串,为什么StringBuffer可以变长?
和String内部是一个字符数组一样,StringBuffer也维护了一个字符数组。 但是,这个字符数组,留有冗余长度
比如说new StringBuffer("the"),其内部的字符数组的长度,是19,而不是3,这样调用插入和追加,在现成的数组的基础上就可以完成了。
如果追加的长度超过了19,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中,看上去 数组长度就变长了
length: “the”的长度 3
capacity: 分配的总空间 19

: 19这个数量,不同的JDK数量是不一样的

  • 方法
append()追加 
delete()删除 
insert()插入 
reverse()反转

  • String与StringBuffer的性能区别

StringBuffer的效率要大大的优于字符串拼接

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangjun0708

你的打赏将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值