Java-面对对象

1、面向对象思想

面向对象(Object Oriented)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,是一种对现 实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。

面向对象是相对于面向过程来讲的,指的是把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。

面向过程到面向对象思想层面的转变:

向过程关注的是执行的过程,面向对象关注的是具备功能的对象。

面向过程到面向对象,是程序员思想上 从执行者到指挥者的转变。

1.1、面向对象思想分类

  1. OOA:面向对象分析(Object Oriented Analysis)
  2. OOD:面向对象设计(Object Oriented Design)
  3. OOP:面向对象程序(Object Oriented Programming)

1.2、三大特征

  1. 封装性:所有的内容对外部不可见。对所有属性进行封装,并为其提供setter及getter方法进行设置和取得操作。
  2. 继承性:将其他的功能继承下来继续发展
  3. 多态性:方法的重载本身就是一个多态性的体现

2、类

2.1、概述

类由属性和方法组成:

  1. 属性:就相当于人的一个个的特征
  2. 方法:就相当于人的一个个的行为。例如:说话、吃饭、唱歌、睡觉
class 类名称{

成员属性

成员方法

}

 

属性定义格式: 数据类型 属性名;

属性定义并赋值的格式: 数据类型 属性名 = 初始化值;

方法定义格式:权限修饰符 返回值类型 方法名(形式参数列表){ //方法体 return 返回值; }

2.2、方法

2.2.1、方法的重载(overload)

方法名称相同, 参数类型或参数长度或参数类型顺序不同, 可以完成方法的重载。

方法的重载与返回值无关。

方法的重载 ,可以让我们在不同的需求下, 通过传递不同的参数调用方法来完成具体的功能。

2.2.2、方法的重写(override)

  1. 参数列表必须完全与被重写方法相同
  2. 返回类型必须完全与被重写方法的返回类型相同
  3. 访问权限不能比父类中被重写的方法的访问权限更低
  4. 父类的成员方法只能被它的子类重写
  5. 声明为statc和private的方法不能被重写,但是可以被再次声明

2.2.4、重写与重载的区别

 

重载

重写

发生的位置

一个类中

子父类中

参数列表限制

必须不用的

必须相同的

返回值类型

与返回类型无关

返回类型必须一致

访问权限

与访问权限无关

子的方法权限必须不能小于父的方法权限

异常处理

与异常无关

异常范围可以更小,但是不能抛出新的异常类型

2.2.5、main方法

public class StaticDemo10{

    public static void main(String args[]){

        for(int i=0;i<args.length;i++){

            System.out.println(args[i]);

        }

    }

}

 

所有的参数在执行类的时候以空格进行分割。 java StaticDemo10 1 2 3 4 5 6 7

但是,如果要输入的是以下几种参数“hello world”、“hello vince”、“hello mjw”。 则参数需要使用“"”括起来。

2.2.6、可变参数

一个方法中定义完了参数,则在调用的时候必须传入与其一一对应的参数,但是在JDK 1.5之后提供了新的 功能,可以根据需要自动传入任意个数的参数。 可变参数只能出现在参数列表的最后。

返回值类型 方法名称(数据类型…参数名称){

    //参数在方法内部 , 以数组的形式来接收

}

 

2.3、构造方法(构造器)

2.3.1、概述

作用: 用于对象初始化。

执行时机: 在创建对象时,自动调用

特点: 所有的Java类中都会至少存在一个构造方法。如果一个类中没有明确的编写构造方法, 则编译器会自动生成一个无参的构造方法, 构造方法中没有任何的代码。

注意:如果自行编写了任意一个构造器, 则编译器不会再自动生成无参的构造方法。

2.3.2、定义格式

定义的格式: 与普通方法基本相同。

区别在于: 方法名称必须与类名相同, 没有返回值类型的声明

2.3.3、构造方法设计

建议自定义无参构造方法,不要对编译器形成依赖,避免错误发生。

当类中有非常量成员变量时,建议提供两个版本的构造方法,一个是无参构造方法,一个是全属性做参数的构造方法。

当类中所有成员变量都是常量或者没有成员变量时,建议不提供任何版本的构造。

2.3.4、构造方法的重载

一个类, 可以存在多个构造方法 : 参数列表的长度或类型不同即可完成构造方法的重载。

构造方法的重载 ,可以让我们在不同的创建对象的需求下, 调用不同的方法来完成对象的初始化。

2.4、Object类

2.4.1、概述

Object类是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承bject 类。

2.4.2、Object的多态

使用Object可以接收任意的引用数据类型

2.4.3、toString

建议重写Object中的toString方法。 此方法的作用:返回对象的字符串表示形式。

Object的toString方法, 返回对象的内存地址

2.4.4、equals

建议重写Object中的equals(Object obj)方法。此方法的作用:指示某个其他对象是否“等于”此对 象。

Object的equals方法:实现了对象上最具区别的可能等价关系; 也就是说,对于任何非空引用值x和 y ,当且仅当x和y引用同一对象( x == y具有值true )时,此方法返回true 。

equals方法重写时的五个特性

  1. 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
  2. 对称性 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报 true 。
  3. 传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true ,然后x.equals(z)应该返回true 。
  4. 一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,前 提是未修改对象上的equals比较中使用的信息。
  5. 非空性 :对于任何非空的参考值x , x.equals(null)应该返回false 。

2.5、内部类(日常不会怎么使用到)

2.5.1、概述

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

广泛意义上的内部类一般来说包括这四种: 1、成员内部类 2、局部内部类 3、匿名内部类 4、静态内部类

2.5.2、成员内部类

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:

class Outer {
    private double x = 0;
    public Outer(double x) {
        this.x = x;
    }  
    class Inner {
        //内部类
        public void say() {
            System.out.println("x="+x);
        }
    }
}

 

特点:成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。

不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。

如果要访问外部类的同名成员,需要以下面的形式进行访问:

外部类.this.成员变量

外部类.this.成员方法

外部使用成员内部

Outter outter = new Outter();

Outter.Inner inner = outter.new Inner();

2.5.3、局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限 于方法内或者该作用域内。

class Person{
    public Person() { }
}
class Man{
    public Man(){ }
    public People getPerson(){
        class Student extends People{ //局部内部类
            int age =0;
        }
        return new Student();
    }
}

 

注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及 static修饰符的。

2.5.4、匿名内部类

匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:

new 父类构造器(参数列表)|实现接口() {
    //匿名内部类的类体部分
}

 

在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类 或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的 引用。当然这个引用是隐式的。

在使用匿名内部类的过程中,我们需要注意如下几点:

  1. 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能 继承一个类或者实现一个接口。
  2. 匿名内部类中是不能定义构造函数的。
  3. 匿名内部类中不能存在任何的静态成员变量和静态方法。
  4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
  5. 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
  6. 只能访问final型的局部变量

2.5.5、静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。

静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非 static成员变量或者方法.

public class Test {
    public static void main(String[] args) {
        Outter.Inner inner = new Outter.Inner();
    }
}
class Outter {
    public Outter() { }
    static class Inner {
        public Inner() { }
    }
}

 

2.6、包装类

2.6.1、概述

在Java中有一个设计的原则“一切皆对象”,那么这样一来Java中的一些基本的数据类型,就完全不符合于 这种设计思想,因为Java中的八种基本数据类型并不是引用数据类型,所以Java中为了解决这样的问题, 引入了八种基本数据类型的包装类。

基本数据类型

包装类

int

Integer

char

Character

float

Float

double

Double

boolean

Boolean

byte

Byte

short

Short

long

Long

Integer、Short、Long、Double、Float、Byte都是Number的子类,表示是一个 数字。

Character、Boolean都是Object的直接子类

2.6.2、装箱和拆箱操作

将一个基本数据类型变为包装类,那么这样的操作称为装箱操作。

将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作。

拆箱

方法

描述

 

public byte byteValue()

用于Byte->byte

 

public abstract double doubleValue()

用于Double->double

 

public abstract float floatValue()

用于Float->float

 

public abstract int intValue()

用于Integer->int

 

public abstract long longValue()

用于Long->long

 

public short shortValue()

用于Short->short

装箱操作

在JDK1.4之前 ,如果要想装箱,直接使用各个包装类的构造方法即可,例如:

int temp = 10 ; // 基本数据类型

Integer x = new Integer(temp) ; // 将基本数据类型变为包装类

在JDK1.5,Java新增了自动装箱和自动拆箱,而且可以直接通过包装类进行四则运算和自增自建操作。例 如:

Float f = 10.3f ; // 自动装箱

float x = f ; // 自动拆箱

System.out.println(f * f) ; // 直接利用包装类完成

System.out.println(x * x) ; // 直接利用包装类完成

2.6.4、字符串转换

使用包装类还有一个很优秀的地方在于:可以将一个字符串变为指定的基本数据类型,此点一般在接收输入 数据上使用较多。

在Integer类中提供了以下的操作方法:

public static int parseInt(String s) :将String变为int型数据

在Float类中提供了以下的操作方法:

public static float parseFloat(String s) :将String变为Float

3、对象

3.1、对象的创建与使用

对象的定义格式:类名称 对象名称 = new 类名称() ;

访问类中的属性: 对象.属性 ;

调用类中的方法: 对象.方法(实际参数列表) ;

3.2、创建对象内存分析

3.2.1、栈

栈存储的特点是, 先进后出

Java栈的区域很小 , 大概2m左右 , 特点是存取的速度特别快

存储速度快的原因: 栈内存, 通过 '栈指针' 来创建空间与释放空间 ! 指针向下移动, 会创建新的内存, 向上移动, 会释放这些内存 ! 这种方式速度特别快 , 仅次于PC寄存器 ! 但是这种移动的方式, 必须要明确移动的大小与范围 , 明确大小与范围是为了方便指针的移动 , 这是一个对于数据存储的限制, 存储的数据大小是固定的 , 影响了程序的灵活性。所以我们把更大部分的数据 存储到了堆内存中

栈存储的是: 基本数据类型的数据以及引用数据类型的引用(地址)。

3.2.2、堆

堆存放的是类的对象

Java是一个纯面向对象语言, 限制了对象的创建方式: 所有类的对象都是通过new关键字创建

new关键字, 是指告诉JVM , 需要明确的去创建一个新的对象 , 去开辟一块新的堆内存空间:

堆内存与栈内存不同, 优点在于我们创建对象时 , 不必关注堆内存中需要开辟多少存储空间 , 也不需要关注内存占用时长。

堆内存中内存的释放是由GC(垃圾回收器)完成的 。

垃圾回收器 回收堆内存的规则: 当栈内存中不存在此对象的引用时,则视其为垃圾 , 等待垃圾回收器回收。

3.2.3、方法区

方法区中包含了一个特殊的区域 ( 常量池 )(存储的是使用static修饰的成员)

存放的是

  1. 类信息
  2. 静态的变量
  3. 常量
  4. 成员方法

3.2.4、PC寄存器

在Java程序中, 每个线程启动时, 都会创建一个PC寄存器。

PC寄存器保存的是 当前正在执行的JVM指令的地址。

3.2.5、本地方法栈

保存本地(native)方法的地址

3.3、匿名对象

没有对象名称的对象 就是匿名对象。

匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,等待被GC回收。

3.3、抽象类与抽象方法

3.3.1、抽象类

抽象类必须使用abstract class声明

一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。

abstract class 类名{ // 抽象类 }

抽象类的使用原则:

>抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。

>一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。

>抽象类不能使用final声明,因为final属修饰的类是不能有子类的 , 而抽象类必须有子类才有意义。

>抽象类能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的构造方法(默认是无参的),之后再调用子类自己的构造方法。

3.3.2、抽象类和普通类的区别

  1. 抽象类必须用public或procted 修饰(如果为private修饰,那么子类则无法继承,也就无法实现其 抽象方法)。默认缺省为 public
  2. 抽象类不可以使用new关键字创建对象, 但是在子类创建对象时, 抽象父类也会被JVM实例化。
  3. 如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也须定义为 abstract类

3.3.3、抽象方法

只声明而未实现的方法称为抽象方法(未实现指的是:没有“{}”方法体),抽象方法必须使用abstract关键字声明。

abstract class 类名{// 抽象类

public abstract void 方法名(); // 抽象方法,只声明而未实现

}

4、类与对象的关系

类表示一个共性的产物,是一个综合的特征,而对象,是一个个性的产物,是一个个体的特征。 (类似生活中的图纸与实物的概念。)

类必须通过对象才可以使用,对象的所有操作都在类中定义。

5、关键字

5.1、this

使用this关键字可以完成以下的操作:

>调用类中的属性

>调用类中的方法或构造方法

>表示当前对象

5.2、static

5.2.1、概述

static表示“静态”的意思,可以用来修饰成员变量和成员方法。

static的主要作用在于创建独立于具体对象的域变量或者方法。

简单理解: 被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。 并且不会因为对象的多次创建而在内存中建立多份数据

5.2.2、特性

  1. 静态成员在类加载时加载并初始化。
  2. 无论一个类存在多少个对象 , 静态的属性, 永远在内存中只有一份( 可以理解为所有对象公用 )
  3. 在访问时: 静态不能访问非静态 , 非静态可以访问静态

5.3、final

  1. 修饰属性、变量

变量就变成了常量,无法对其进行再次赋值。

final修饰的局部变量,只能赋值一次(可以先声明后再赋值)

final修饰的成员属性,必须在声明时赋值

全局常量(public static final)

  1. 修饰类

不可被继承

  1. 修饰方法

不能被子类重写

常量的命名规范:由一个或多个单词组成,单词与单词之间必须使用下划线分割,单词中所有字母均为大写

5.4、extend(继承)

class A{}

class B extend A{}

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域与方法,或子类从父类中继承方法,使得子类具有父类相同的行为。

Java只有单继承,多重继承(爷爷、爸爸、儿子),没有多继承。

5.5、super

与extend同步使用

通过super,可以访问父类的构造方法、方法、属性。

通过super调用父类的构造方法时,必须放在子类的构造方法的第一行。

5.6、import(导入)

import 包名.类名;

5.7、instanceof

作用: 判断某个对象是否是指定类的实例

格式: 实例化对象 instanceof 类 //此操作返回boolean类型的数据

6、包

6.1、包介绍

  1. 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
  2. 包如同文件夹一样,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名 加以区别。因此,包可以避免名字冲突。
  3. 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类

6.2、包的使用规则

包中java文件的定义: 在.java文件的首部, 必须编写类所属哪个包, 格式: package 包名;

包的定义: 通常由多个单词组成, 所有单词的字母小写, 单词与单词之间使用.隔开 ,一般命名为“com.公司名.项目 名.模块名....”。

7、设计模式

7.1、单例设计模式

单例设计模式:保证程序在内存中只有一个对象存在(被程序所共享)

7.1.1、单例设计模式的两种实现方式:

一、懒汉式:随着类的加载在内存中对象为null,当调用 getInstance 方法时才创建对象(延迟加载) 二、饿汉式:随着类的加载直接创建对象(推荐开发中使用)

7.1.2、单例设计模式的实现步骤

  1. 保证一个类只有一个实例,实现方式:构造方法私有化
  2. 必须要自己创建这个实例,实现方式:在本类中维护一个本类对象(私有,静态)
  3. 必须向整个程序提供这个实例,实现方式:对外提供公共的访问方式(getInstance方法,静态)

7.1.3、懒汉式实现代码

class Single{
    private Single(){}    
    private static Single s1 = null;
    public static Single getInstance(){
        if(s1 == null){
            s1 = new Single();
        }
        return s1;
    }
}

7.1.4、饿汉式实现代码
 

class Single2{
    private Single2(){}
    private static Single2 s = new Single2();
    public static Single getInstance(){
        return s;
    }
    void print(){
        System.out.println("Hello World!");
    }    
}

8、接口

8.1、定义

如果一个类中的全部方法都是抽象方法,全部属性都是全局常量,那么此时就可以将这个类定义成一个接口。

interface 接口名称{
    全局常量 ;
    抽象方法 ;
}

因为接口本身都是由全局常量和抽象方法组成 , 所以接口中的成员定义可以简写:

  1. 全局常量编写时,可以省略public static final 关键字

例如: public static final String INFO = "内容" ;

简写后: String INFO = "内容" ;

  1. 抽象方法编写时,可以省略 public abstract 关键字

例如: public abstract void print() ;

简写后: void print() ;

8.2、面向接口编程思想

是接口是定义(规范,约束)与实现(名实分离的原则)的分离

优点:

  1. 降低程序的耦合性
  2. 易于程序的扩展
  3. 有利于程序的维护

8.3、接口的实现 implements

接口可以多实现

class 子类 implements 父接口1,父接口2...{ }

8.4、接口的继承

接口因为都是抽象部分, 不存在具体的实现, 所以允许多继承

interface C extends A,B{ }

一个接口要想使用,必须依靠子类。 子类(如果不是抽象类的话)要实现接口中的所有抽象方法。

8.5、接口和抽象类的区别

  1. 抽象类要被子类继承,接口要被类实现。
  2. 接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
  3. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  4. 抽象类使用继承来使用, 无法多继承。 接口使用实现来使用, 可以多实现
  5. 抽象类中可以包含static方法 ,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
  6. 接口不能有构造方法,但是抽象类可以有

9、多态

多态:就是对象的多种表现形式,(多种体现形态)

9.1、多态的体现

对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态 ,对象多态性就从此而来。

方法的重载 和 重写 也是多态的一种, 不过是方法的多态(相同方法名的多种形态)。

重载: 一个类中方法的多态性体现

重写: 子父类中方法的多态性体现。

9.2、多态的使用:对象的类型转换

类似于基本数据类型的转换

  1. 向上转型:将子类实例变为父类实例

格式:父类 父类对象 = 子类实例 ;

  1. 向下转型:将父类实例变为子类实例

格式:子类 子类对象 = (子类)父类实例 ;

10、异常处理

异常(Exception)是在程序中导致程序中断运行的一种指令流。

10.1、异常体系结构

在Java中存在一个父类Throwable。Throwable存在两个子类:

  1. Error:表示的是错误,是JVM发出的错误操作,只能尽量避免,无法用代码处理。
  2. Exception:一般表示所有程序中的错误,所以一般在程序中将进行try…catch的处理。

处理格式语法:

try{
    // 有可能发生异常的代码段
}catch(异常类型1 对象名1){
    // 异常的处理操作
}catch(异常类型2 对象名2){
    // 异常的处理操作
} ...
finally{
    // 异常的统一出口
}

 

多异常捕获的注意点:

  1. 捕获更粗的异常不能放在捕获更细的异常之前。
  2. 如果为了方便,则可以将所有的异常都使用Exception进行捕获。

特殊的多异常捕获写法:

catch(异常类型1 |异常类型2 对象名){ //表示此块用于处理异常类型1 和 异常类型2 的异常信息 }

RuntimeExcepion与Exception的区别

只要是 RuntimeException的子类,则表示程序在操作的时候可以不必使用try…catch进行处理,如果有异常发生,则由JVM进行处理。当然,也可以通过try catch处理。

10.2、try+catch的处理流程

1、 一旦产生异常,则系统会自动产生一个异常类的实例化对象。

2、 那么,此时如果异常发生在try语句,则会自动找到匹配的catch语句执行,如果没有在try语句中,则会将异 常抛出.

3、 所有的catch根据方法的参数匹配异常类的实例化对象,如果匹配成功,则表示由此catch进行处理。

10.3、finally

在进行异常的处理之后,在异常的处理格式中还有一个finally语句,那么此语句将作为异常的统一出口,不管是否产生了异常,最终都要执行此段代码。

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行。

执行流程:

  1. 先计算返回值, 并将返回值存储起来, 等待返回。
  2. 执行finally代码块。
  3. 将之前存储的返回值, 返回出去。

需注意:

  1. 返回值是在finally运算之前就确定了,并且缓存了,不管finally对该值做任何的改变,返回的值都不 会改变。
  2. finally代码中不建议包含return,因为程序会在上述的流程中提前退出,也就是说返回的值不是try或 catch中的值。
  3. 如果在try或catch中停止了JVM,则finally不会执行.例如停电- -, 或通过如下代码退出JVM:System.exit(0) 。

10.4、throws关键字

此关键字主要在方法的声明上使用,表示方法中不处理异常,而交给调用处处理。

格式: 返回值 方法名称()throws Exception{ }

10.5、throw关键字

throw关键字表示在程序中人为的抛出一个异常,因为从异常处理机制来看,所有的异常一旦产生之后,实际上抛出的就是一个异常类的实例化对象,那么此对象也可以由throw直接抛出。

代码: throw new Exception("抛着玩的。") ;

10.6、自定义异常类

编写一个类, 继承Exception,并重写一参构造方法 即可完成自定义受检异常类型。

编写一个类, 继承RuntimeExcepion,并重写一参构造方法 即可完成自定义运行时异常类型。

class MyException extends Exception{
    // 继承Exception,表示一个自定义异常类
    public MyException(String msg){
        super(msg) ; // 调用Exception中有一个参数的构造
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值