Java系列笔记第二章:面向对象(OOP)

本文深入解析面向对象编程(OOP)的核心概念,包括成员变量与局部变量的区别、封装、继承和多态的特性,以及Java中类、接口、抽象类、多态、final关键字和内部类的详细使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二章 : 面向对象(OOP)

成员变量和局部变量的区别

  • 作用域不同:局部变量只能在方法体内使用,成员变量能在整个类内使用。
  • 定义的位置不同:局部变量在方法体内定义,成员变量在类内定义。
  • 初始值不同:局部变量使用必须手动初始化,成员变量如果不手动初始化,会被自动初始化为默认值。
  • 内存中的位置不一样:局部变量在JVM栈中,成员变量在堆中。
  • 生命周期不同:局部变量随方法结束被回收,成员变量随对象被回收而结束。

OOP三大特征:封装继承和多态

1. 封装

1.1 什么是封装

两种封装性的体现:

  • 方法 : 对实现细节的隐藏
  • private : 对成员变量的保护

tips:boolean类型的get方法名字应该是isXxx(),而不是getXxx()。

1.2 什么是一个标准的类
  • 所有成员变量均是private修饰。
  • 所有成员变量均有一个get和set方法。
  • 有一个无参构造方法。
  • 有一个全参数构造方法。

2. 常用API

2.1 Scanner类

import java.util.Scanner;
// System.in 表示从键盘,也就是标准输入流获取输入
Scanner sc = new Scanner(System.in);
sc.close();
  • next() 获取下一个单词,空格或者空白字符区分。

  • hasNext() 下面是否还有输入,空格或者空白字符分开。

  • nextLine() 获取下一行。用回车区分。

  • hasNextLine() 下面是否还有一行输入。用回车区分。

  • nextInt() 获取下一个整数。

  • hasNextInt() 下一个是否可以转成int。

2.2 Random类

用法:生成随机数

import java.util.Random;
Random rd = new Random;
//输出一个int范围内的整数
sout(rd.nextInt());
//输出一个[0,14)内的整数
sout(rd.nextInt(15))

2.3 ArrayList

动态数组,类似于vector。

2.3.1 常用方法
  • 初始化 : ArrayList<E> list = new ArrayList<>();
  • 输出 : 直接sout的话,输出的是内容。而不是ArrayList的内存地址。
  • size() : 返回长度。
  • add(E) : 添加内容,类似于vector的push_back()。
  • add(index, E) : 在指定的位置插入元素。
  • remove(index) : 根据index删除元素,返回元素值。如果index越界,会抛出异常。
  • remove(O) : 删除第一次出现的O,返回布尔值。如果O不存在,返回false,不会抛出异常。
  • get(index) : 根据index取对应位置的元素。
  • indexOf(O) : 根据元素内容找第一次出现的位置。

Tips: ArrayList中只能存储引用类型,不能存储基本类型。

基本类型包装类
intInteger
charCharacter
byteByte
shortShort
longLong
floatFloat
doubleDouble
2.3.2 ArrayList<int[]> 转二维数组
ArrayList<int[]> list = new ArrayList<>();
list.add(new int[]{1,2});
list.add(new int[]{5,6});
list.add(new int[]{9,9});

//ArrayList<int[]>转二维数组。
int[][] r = list.toArray(new int[0][]);

//Arrays.deepToString()可以将二维数组转为字符串。
System.out.println(Arrays.deepToString(r));

2.4 字符串String

字符串的特点:

  • 字符串是常量,不可变。
  • 字符串是共享的,只要字符串内容一样,就是同一个字符串。

字符串的构造方法:

  • String(), 创建一个空白字符串。
  • String(char[] array), 从字符数组中创建。
  • String(byte[] array), 从字节数组中创建。比如ASCII码,{97,98,99},对应创建出来的字符串就是"abc"。
  • 直接创建。String str = "hello"。

字符串的常量池:

  • 只要是双引号直接写出来的字符串,就在字符串常量池中。字符串常量池是堆中的一块空间。
  • 自己new的String对象,在池外,堆里。
  • 注意,对于引用类型,==是进行地址比较。
String s1 = "abc";
String s2 = "abc";
sout(s1 == s2);
//结果是true。
String s3 = new String("abc");
System.out.println(s2==s3);
//结果是false

字符串常用方法:

  • equals(O),只有O是内容相同的字符串,才会返回true。

如果是字符串常量和变量相比较,推荐"abc".equals(str)。因为str可能是null,为了防止空指针异常。

  • equalsIgnoreCase(O),同上,忽略大小写。
  • length(), 获取长度。
  • concat(str), 返回拼接的字符串。
  • charAt(index), 返回索引位置的char字符
  • indexOf(str/char), 返回子串首次出现的位置,如果没有,返回-1。
  • substring(beginIndex, endIndex),返回[begin,end)的子串。左闭右开。
  • toCharArray(), 返回字符串拆出来的字符数组。
  • getBytes(), 返回字符串对应的字节数组。
  • replace(oldstr,newstr), 返回将oldstr全部替换为newstr的字符串。
  • replaceFirst(oldstr,newstr), 返回将第一个oldstr替换为newstr的字符串。
  • endWith(str), 返回是否以给定的字符串结尾。

字符串分割:

  • split(regex), 按照给定的规则"正则表达式"将字符串分割为多个字符串,返回字符串数组String[]。

2.5 static 修饰符

  • 修饰成员变量 : 使用static修饰成员变量后,变量就不属于某个对象独有了,全部对象共享同一个数据。

  • 修饰成员方法 : 使用static修饰成员方法后,就变成了静态方法。静态方法不需要创建对象就可以直接通过类名称来调用。就算用对象名来调用静态方法,也会被javac编译为通过类名称来调用。

  • 静态方法只能访问静态变量/方法,不能访问非静态变量/方法。

  • 静态方法中不能使用this。

  • 内存中先初始化静态对象与静态方法。

  • 静态对象存储在方法区中。

静态代码块

首次创建类对象时,执行唯一一次静态代码块。第二次创建对象时就不执行了。

经典用法:一次性地对静态成员变量赋值。

public class MyClass{
    static{
        //静态代码块
        //静态代码块比其他方法执行优先度都高。
    }
}

2.6 Arrays类

数组相关的工具类,里面提供了大量的静态方法,用来实现数组的常见操作。

  • toString(数组),按照默认格式,将数组输出为字符串。
  • sort(数组,[lambda表达式]),使用lambda表达式对数组进行排序。默认升序/字典序升序。
    如果是自定义类型,需要实现Comparable或者Comparator接口

2.7 Math类

  • abs(), 返回double类型。
  • ceil(), 向上取整。返回double。
  • floor(), 向下取整。返回double。
  • round(), 四舍五入。返回long。
  • Math.PI, 就是π。

2.8. 匿名对象

匿名对象只能使用一次。

使用建议:如果确定某个对象只需要使用唯一的一次,那么就可以使用匿名对象。

//只想使用一次标准输入的情况下,可以使用匿名Scanner对象
int number = new Scanner(System.in).nextInt();
sout(number);

//使用匿名对象传参
fun(new Scanner(System.in));
//被调函数
public static void fun(Scanner sc){
    sout(sc.nextInt());
}

3. 继承

继承是多态的前提,没有继承就没有多态。

  • this.val: 本类的变量
  • super.val: 父类的变量

3.1 @Override重写

子类中方法名一样,参数列表也一样

  • 注意:子类方法的返回值必须小于等于父类方法的返回值类型
//父类返回值为Object
public Object method(){};
//子类返回值为String
public String method(){};
  • 注意:子类方法的权限必须大于等于父类方法的权限修饰符
//default是什么都不写,留空。
public>protected>(default)>private
  • 注意:重写时,如果要用到父类方法的功能,不用重写一遍,写super.method()即可

3.2 继承里的构造函数

  • 先执行父类构造函数,再执行子类构造函数。
  • 子类构造函数里会默认包含一个super()函数,即父类无参构造函数。
  • 只有子类构造函数才能调用super(),而且super()必须写在子类构造函数第一行。

3.3 super的三类用法

  • 子类的成员方法中访问父类的成员变量。
  • 子类的成员方法中访问父类的成员方法。
  • 子类的构造方法中访问父类的构造方法。

3.4 this的三类用法

  • 本类成员方法中访问本类成员变量。
  • 本类成员方法中访问另一个成员方法。
  • 本类的构造方法中访问本类的另一个构造方法。this();
    注意:this()和super()不能同时使用。

3.5 内存中的子类

在内存中子类包含了一个完整的父类内容。

3.6 继承的三大特征

  • Java继承是单继承,C++支持多继承。
  • Java可以多级继承。A->B->C。
  • 一个父类可以有多个子类。

4. 抽象类Abstract

4.1 基本概念

  • 抽象方法
public abstract void fun();
  • 抽象方法所在的类必须是抽象类
public abstract class A(){

}
  • 抽象类可以有普通方法。

4.2 抽象类的使用

  • 抽象类不能直接new对象。
  • 抽象类必须用一个子类来继承。
  • 抽象类必须被实现父类的所有的抽象方法。
  • 抽象类不一定有抽象方法。

注意:没有抽象方法的抽象类是在一些特殊场景下有用途,比如适配器模式。

5. 接口

接口是一种公共的规范标准。

5.1 接口定义的基本格式

  • 接口是一种引用数据类型。最重要的内容就是其中的抽象方法。
  • 接口没有构造方法
  • 接口没有静态代码块
public interface A{

}

5.2 接口中可以包含的内容

  • 常量
  • 抽象方法
  • 默认方法(Java 8)
  • 静态方法(Java 8)
  • 私有方法(Java 9)
5.2.1 接口中的抽象方法
  • 抽象方法必须是public abstract修饰,也可以忽略不写。
public interface MyInterfaceAbstract {
    public abstract void printHello1();
    public void printHello2();
    abstract void printHello3();
    void printHello4();
}
5.2.2 接口的实现
  • implements关键字来实现接口,并实现接口中的所有抽象类。
public class ImplMyInterfaceAbs  implements MyInterfaceAbstract{
    @Override
    public void printHello() {
        System.out.println("Hello world!");
    }
}
  • 如果没实现所有的抽象方法,那么这个类就必须是抽象类。
5.2.3 默认方法(Java 8)
  • 默认方法可以有方法体。
  • 默认方法是为了解决接口升级的问题。
  • public可以省略,default不能省略。
  • 默认方法会被接口的实现类继承下去。
  • 默认方法也可以被接口的实现类Override。
public interface MyInterfaceAbstract {
    void printHello();
    public default void print2(){
        System.out.println("我是接口的默认方法");
    }
}

public class ImplMyInterfaceAbs  implements MyInterfaceAbstract{
    @Override
    public void printHello() {
        System.out.println("Hello world!");
    }
}

public class test {
    public static void main(String[] args) {
        ImplMyInterfaceAbs inter = new ImplMyInterfaceAbs();
        inter.printHello();
        inter.print2();
    }
}
//=========================
/* 输出如下:
Hello world!
我是接口的默认方法
*/
5.2.4 接口的静态方法(Java 8)
  • 接口的静态方法只能通过接口来直接调用
  • 接口的静态方法不能通过接口的实现类来调用
public interface MyInterfaceAbstract {
    void printHello();
    public default void print2(){
        System.out.println("我是接口的默认方法");
    }
    public static void static_fun(){
        System.out.println("我是接口的静态方法");
    }
}

public class ImplMyInterfaceAbs  implements MyInterfaceAbstract{
    @Override
    public void printHello() {
        System.out.println("Hello world!");
    }
}

public class test {
    public static void main(String[] args) {
        ImplMyInterfaceAbs inter = new ImplMyInterfaceAbs();
        inter.printHello();
        inter.print2();
        //inter.static_fun(); 不能这么用,会报错。
        MyInterfaceAbstract.static_fun();
    }
}
//=========================
/* 输出如下:
Hello world!
我是接口的默认方法
我是接口的静态方法
*/
5.2.5 接口的私有方法(Java 9)

为了解决默认方法中的重复代码问题

  • 普通私有方法,解决多个默认方法的代码重复问题。
    private void fun1(){
     //TODO
     }
  • 静态私有方法,解决多个静态方法的代码重复问题。
5.2.6 接口的常量

使用public static final来修饰

  • 一旦使用final修饰,变量就不可变。
  • 接口中的常量默认就是public static final
  • 接口中的常量必须显式赋值。
  • 常量名字要全部大写。
public static final int NUM = 10;
5.3 接口实现注意事项
  1. 一个类可以实现多个接口。
  2. 如果实现的两个接口的抽象方法重名了,只需@Override一次即可。
  3. 如果实现的两个接口的默认方法重名了,则必须@Override。
  4. 如果继承的类的方法和实现的接口的默认方法重名了,继承优先于接口实现。
5.4 接口之间的多继承
  1. 类与类之间是单继承的。一个类只能继承一个类。
  2. 类与接口是多实现的。一个类可以实现多个接口。
  3. 接口与接口之间是多继承的。
package interfaceDemo;

public interface MyInterfaceA {
    public abstract void methodA();
    public abstract void methodCommon();

}

public interface MyInterfaceB {
    public abstract void methodB();
    public abstract void methodCommon();
}

public interface MyInterface extends MyInterfaceA,MyInterfaceB {
    public abstract void method();
}

/*
此时MyInterface一共有4个抽象方法。分别为:
public abstract void method();
public abstract void methodA();
public abstract void methodB();
public abstract void methodCommon();
*/

注意:

1. 多个父接口的抽象方法重复了没关系,因为都是抽象方法,并没有被实现。
2. 多个父接口的默认方法如果重复,则子接口必须实现这个默认方法,还得带着default关键字。

6. 多态

6.1 多态的定义

  • 多态性的前提,是extends或者implements。
  • 一个对象拥有多种形态,就是对象的多态性。

6.2 代码中体现多态性

  • 代码中的多态性,就是 父类引用指向子类对象。

    父类名字 对象名 = new 子类名字();

    接口名称 对象名 = new 实现类的名字();

  • 代码演示如下,可见父类引用指向了子类对象。

package multiDemo;

public class Fu {
    int num = 10;
    public void fun(){
        System.out.println("父类名称");
    }
}

public class Zi extends Fu{
    int num = 20;
    int age = 30;
    @Override
    public void fun() {
        System.out.println("子类方法");
    }
}

public class test {
    public static void main(String[] args) {
        //new的谁就是谁的方法。
        //如果子类没@Override这个方法,就会往上找。
        Fu ins = new Zi();
        ins.fun();
    }
}

/*输出如下:
子类方法
*/

6.3 多态中成员变量的使用方法

  • 直接通过对象名称使用成员变量,等号左边是谁就是谁。没有就向上找。
  • 间接通过成员方法来使用成员变量。该方法属于谁就用谁,没有就向上找。
  • 编译看左,运行看左。
//代码同上
Fu ins = new Zi()
System.out.println(ins.num);
//下面这句会报错,因为Fu类没有age成员变量。
//System.out.println(ins.age);
//输出结果为:10

6.4 多态中成员方法的使用方法

  • new的谁,就优先用谁,没有就向上找。
  • 编译看左,运行看右。

6.5 多态有什么用

无论右边new的时候换成哪个子类对象,等号左边调用方法都不会变。

6.6 对象的向上转型

  • 多态写法,就是对象的向上转型。

    父类引用指向子类对象

    父类名称 对象名 = new 子类名称();
    含义:创建一个子类对象,当做父类使用。

  • 向上转型一定是安全的。
    小范围转向大范围,比如int转向Integer;比如int转向double。

  • 向上转型的弊端:对象无法调用子类特有方法。

6.7 对象的向下转型(还原)

  • 子类名称 子类对象 = (子类名称) 父类对象
  • 如果父类对象之前不是子类对象向上转型的,就不能转换下来。
  • 错误的类型转换编译不会报错,运行会报错,java.lang.ClassCastException。

6.8 怎么知道父类引用指向的子类名字呢?

  • 对象名 Instanceof 类名称
  • 返回boolean类型。
  • 注意: 如果Instanceof两边的类不存在继承关系,那么不能使用。

7. final关键字

7.1 用来修饰一个类

  • 表示当前类不能有任何子类。

    public final class MyClass{
    
    }
    

7.2 用来修饰一个方法

  • 子类无法@Override被final修饰的父类方法。
public final void fun(){}
  • abstract和final无法同时使用

7.3 用来修饰一个局部变量

  • 被final修饰的局部变量不可变。
  • 要保证被final修饰的局部变量只有唯一的一次赋值行为。
final int a = 10;
final int b;
b = 12;
  • 不可变:

    对于基本数据类型,指的是数据不可变。

    对于引用类型,指的是变量当中的内存地址不可变。但是地址指向的对象的内容可变。比如学生类的实例,实例的学生名字可变,但是不能指向别的学生。

7.4 用来修饰一个成员变量

  • 因为成员变量具有默认值,所以final修饰的成员变量必须手动赋值。
  • 赋值方法:直接赋值;构造函数赋值。
  • 必须保证类的所有重载的构造函数都对final修饰的成员变量进行赋值。
public class student {
    private final String name;

    //默认无参构造函数赋值默认的名字
    public student() {
        name = "没名字";
    }

    //带参构造函数对name进行赋值
    public student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

8. 内部类

一个类内部包含另一个类。

比如汽车类和发动机类,汽车包含发动机,所以发动机是汽车的内部类。发动机无法单独存在,必须在汽车内部才有用。

8.1 成员内部类

8.1.1 定义格式
修饰符 class 外部类名称{
    修饰符 class 内部类名称{

    }
}
8.1.2 使用方式
  • 内部类用外部类成员变量,可以随意使用,外用内,一定需要借助内部类对象。

  • 内部类的两种使用方式:

    1. 间接使用。在外部类的方法中调用内部类,main函数中调用外部方法。
    2. 直接使用。外部类.内部类 内部类对象名 = new 外部类().new 内部类()
//演示
package InnerClassDemo;

public class Body {
    public class Heart {
        private int heartWeight;

        public int getHeartWeight() {
            return heartWeight;
        }

        public void setHeartWeight(int heartWeight) {
            this.heartWeight = heartWeight;
        }

        public Heart(int heartWeight) {
            this.heartWeight = heartWeight;
        }

        public void printBodyWeight() {
            System.out.println("心脏内部类中查看外部类的体重:" + weight);
        }

        public void printHeartWeight() {
            System.out.println("心脏内部类中查看自己的心脏重量:" + heartWeight);
        }

        public void methodHeart() {
            System.out.println("内部类方法");
        }
    }

    private int weight;
    private final Heart myheart = new Heart(1);

    public Body(int weight) {
        this.weight = weight;
    }

    public void getWeight() {
        System.out.println("外部身体类中查看身体重量:" + this.weight);
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public void methodBody() {
        System.out.println("外部类方法");
    }

    public void printHeartWeight() {
        System.out.println("外部身体类中打印心脏内部类的重量:" + myheart.heartWeight);
    }

    public void printHeartMethod() {
        System.out.println("=======集中调用心脏内部类的方法=======");
        myheart.methodHeart();
        myheart.printBodyWeight();
        myheart.printHeartWeight();
    }
}


//====================//
package InnerClassDemo;

public class test {
    public static void main(String[] args) {
        Body mybody = new Body(50);
        mybody.methodBody();
        mybody.getWeight();
        mybody.printHeartWeight();
        mybody.printHeartMethod();
        System.out.println("============");
        Body.Heart myHeart = new Body(40).new Heart(1);
        myHeart.printHeartWeight();
    }
}


//============输出结果============//
外部类方法
外部身体类中查看身体重量:50
外部身体类中打印心脏内部类的重量:1
=======集中调用心脏内部类的方法=======
内部类方法
心脏内部类中查看外部类的体重:50
心脏内部类中查看自己的心脏重量:1
============
心脏内部类中查看自己的心脏重量:1
8.1.3 同名变量的访问
  • 如果内部类成员变量和外部类成员变量名字重复了,通过外部类.this.成员变量来访问。
package InnerClassDemo;

public class Outer {
    //外部类的成员变量
    private int num = 10;

    public class Inner {
        //内部类的成员变量
        private int num = 20;

        public void methodInner() {
            //内部类的成员方法内局部变量
            int num = 30;
            System.out.println(num); // 局部变量:30
            System.out.println(this.num); //内部类的num:20
            System.out.println(Outer.this.num); //外部类的成员变量:10
        }
    }
}


package InnerClassDemo;

public class test {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer().new Inner();
        inner.methodInner();
    }
}

//============输出结果============//
30
20
10

8.2 局部内部类(包含匿名内部类)

  • 如果一个内部类是定义在方法内部的,就是局部内部类。
  • 局部内部类什么修饰符都不用加
public void fun(){
    class Inner{
        // ...
    }
}

8.3 局部内部类的final问题

局部内部类内如果想访问方法内的局部变量,这个变量必须是有效的final变量

原因分析:

  1. 局部内部类对象是new出来的,存在堆里。
  2. 方法的局部变量储存在栈上。
  3. 一旦方法执行结束,局部变量会被立刻清空。
  4. 局部内部类对象会等待垃圾回收,生命周期比局部变量长。
  5. 所以局部内部类使用的方法内的局部变量需要是有效final,储存在常量池。
public class LocalInnerClass {
    public void method(){
        int num = 10;
        final int i = 30;
        /*
        Variable 'num' is accessed from within inner class, needs to be final or effectively final
        */
        //错误写法,因为num被局部内部类使用,不能更改。
        //num = 20;
        class Inner{
            public void innerMethod(){
                System.out.println(num);
                System.out.println(i);
            }
        }
    }
}

8.4 匿名内部类

如果接口的实现类,或者父类的子类只需要使用一次,那么就可以通过匿名内部类的方式来创建对象。

定义方式
接口名称 对象名称 = new 接口名称() {//...};

package AnonymousInnerClassDemo;

public interface MyInterface {
    public abstract void method();
}


public class test {
    public static void main(String[] args) {
        MyInterface obj = new MyInterface() {
            @Override
            public void method() {
                System.out.println("匿名内部类实现接口");
            }
        };
        obj.method();
    }

}
//==========输出============//
匿名内部类实现接口

匿名内部类的匿名对象

package AnonymousInnerClassDemo;

public interface MyInterface {
    public abstract void method();
}


public class test {
    public static void main(String[] args) {
        new MyInterface() {
            @Override
            public void method() {
                System.out.println("匿名内部类的匿名对象实现接口");
            }
        }.method();
    }
}

//==========输出============//
匿名内部类的匿名对象实现接口

9 类和接口作为各种参数

9.1 类作为成员变量类型

比如String,就是类,String对象可以作为成员变量。

9.2 接口作为成员变量类型

  • 权限修饰符 接口名 对象名;
  • 传参数进去时,可以是实现一下接口的类的对象,也可以使用匿名内部类,也可以不新建对象,使用匿名对象。
package InterfaceAsParam;

//接口定义
public interface MyInterface {
    void printMessage();
}

//接口的实现类
public class ImplMyInterface implements MyInterface{
    @Override
    public void printMessage() {
        System.out.println("我是通过类实现了接口。");
    }
}

//使用接口作为成员变量的类
public class InterfaceAsMemberVar {
    private MyInterface myInterface;

    public void method(){
        //调用接口的抽象方法
        myInterface.printMessage();
    }

    public InterfaceAsMemberVar(MyInterface myInterface) {
        this.myInterface = myInterface;
    }
}

//测试函数 
public class test {
    public static void main(String[] args) {
        InterfaceAsMemberVar obj_1 = new InterfaceAsMemberVar(new ImplMyInterface());
        obj_1.method();

        MyInterface myInterface = new MyInterface() {
            @Override
            public void printMessage() {
                System.out.println("我通过匿名内部类实现了接口");
            }
        };
        InterfaceAsMemberVar obj_2 = new InterfaceAsMemberVar(myInterface);
        obj_2.method();

        InterfaceAsMemberVar obj_3 = new InterfaceAsMemberVar(new MyInterface() {
            @Override
            public void printMessage() {
                System.out.println("我通过匿名内部类的匿名对象实现了接口");
            }
        });
        obj_3.method();
    }
}

//==========输出==========//
我是通过类实现了接口。
我通过匿名内部类实现了接口
我通过匿名内部类的匿名对象实现了接口

9.3 接口作为方法的参数或者返回值。

  • 比如List这个接口

    List<String> l = new ArrayList<>();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值