Java面向对象高级

一、static关键字的使用:

1.关键字static:

如果想让一个成员变量被类的所有实例所共享,用static修饰即可,称为类变量(或类属性);

(1)类属性、类方法的设计思想:

当编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。此外,在类中声明的实例方法,在类的外面必须要先创建对象才能调用。但是有些方法的调用者和当前类的对象无关,这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。这里的类变量、类方法只需要使用static修饰即可。所以也称为静态变量、静态方法。

(2)static关键字:static可以用来修饰属性、方法、代码块和内部类

(3)static修饰属性(静态变量):

a.个数:静态变量在内存空间中只有一份,被类的多个对象共享;类的每一个实例都保存着一份实例变量

b.内存位置:静态变量JDK6之前存放在方法区,JDK7之后存放在堆空间;实例变量存放在堆空间的对象实体中

c.加载时机:静态变量随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份;实例变量随着对象的创建而加载,每个对象拥有一份实例变量

d.调用者:静态变量可以被类直接调用,也可以使用对象调用;实例变量只能使用对象进行调用

e.消亡时机:静态变量随着类的卸载而消亡;实例变量随着对象的消亡而消亡

(4)static修饰方法(类方法、静态方法):

静态方法随着类的加载而加载,可以通过"类名.静态方法名(实参列表)"的格式直接调用;

静态方法内可以调用静态的调用静态的属性或静态的方法,但是不可以调用非静态的属性和非静态的方法;在类的非静态方法中可以调用当前类的静态结构和非静态结构

static修饰的方法中不能使用关键字this

(5)属性声明为静态的时机:

a.判断当前类的多个实例是否能共享次成员变量,且此成员变量的值是相同的;

b.开发中常将一些常量声明为静态的

(6)方法声明为静态的时机:

a.方法内操作的变量如果都是静态变量的话需要将此方法声明为静态方法

b.在开发中常常将工具类中的方法声明为静态方法

二、单例设计模式:

1.设计模式:设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式

2.单例模式:所谓类的单例模式,就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象的实例,并且该类提供一个获得该对象实例的方法

3.实现单例模式的思路:

首先必须将类的构造器的访问权限设置private,这样就不能用new操作符在类的外部产生类的对象,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象, 只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以指向类内部产生的该类对象的变量也必须定义成静态的。

(1)懒汉式:

a.类的构造器私有化

b.声明当前类的实例

c.通过getXxx()获取当前类的实例,如果未创建对象,则在方法内部创建对象

d.将属性和方法都声明为static

(2)饿汉式:

a.类的构造器私有化

b.在类的内部创建当前类的实例

c.通过getXxx()获取当前类的实例,必须声明为static的

d.此属性也必须声明为static

(3)两种模式的对比:

a.饿汉式立即加载,随着类的加载当前唯一实例就创建了

b.懒汉式延迟加载,在需要使用的时候进行创建

c.饿汉式写法简单,由于内存中较早加载,使用更加方便、更快,但是在内存中占用时间较长,是线程安全的

d.懒汉式线程不安全,在需要时候进行创建,节省内存空间

三、代码块(初始化块)

1.代码块的作用:用来初始化类或对象的信息(即初始化类或对象的成员变量)

2.代码的修饰:只能使用static进行修饰

3.代码的分类:静态代码块(使用static进行修饰)和非静态代码块(不使用static进行修饰)

4.具体使用:

(1)静态代码块:随着类的加载而执行,由于类的加载只会执行一次,静态代码块只会执行一次,用来初始化类的信息,内部可以声明变量、调用属性或方法,编写输出语句等操作,静态代码块的执行先于非静态代码块的执行;在同一个类中可以存在多个静态代码块,如果有多个静态代码块则按照声明的先后顺序依次执行;静态代码块内只能调用静态的结构(属性和方法),不能调用非静态的结构(属性和方法)

(2)非静态代码块:随着对象的创建而执行,每创建当前类的一个实例,就会执行一次非静态代码块,用来初始化对象的信息,内部可以声明变量、调用属性或方法,编写输出语句等操作;在同一个类中可以存在多个非静态代码块,按照声明的先后顺序依次执行;非静态代码块内可以调用静态的结构(属性和方法)和非静态的结构(属性和方法)

非静态代码块的作用是给对象进行初始化;对象一建立就运行非静态代码块,而且优先于构造函数执行。有对象建立才会运行非静态代码块,类不能调用非静态代码块,而且非静态代码块与构造函数的执行顺序是前者先于后者执行。非静态代码块与构造函数的区别是在于非静态代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的非静态代码块。也就是说非静态代码块中定义的是不同对象共性的初始化内容。

四、类中属性赋值的位置和过程

1.给类中非静态的属性赋值的位置:

a.默认初始化

b.显式初始化

c.构造器中初始化

d.代码块中初始化

e.有了对象后通过"对象名.属性"或"对象名.getXXX()"进行赋值

2.执行的先后顺序:a、b或d、c、e

3.当涉及到继承关系时,按照如下顺序执行:

执行父类的静态代码块,并初始化父类静态成员变量

执行子类的静态代码块,并初始化子类静态成员变量

执行父类的非静态代码块,执行父类的构造函数,并初始化父类普通成员变量

执行子类的非静态代码块, 执行子类的构造函数,并初始化子类普通成员变量

五、final关键字的使用:

final的理解为最终的,可以用来修饰类、方法和变量

1.final修饰类:表示此类不能被继承,如果将某个类设置为final形式,则类中的所有方法都被隐式设置为final形式,但是final类中的成员变量可以被定义为final或非final的形式

2.final修饰方法:表示此方法不能被重写

3.final修饰变量:既可以修饰成员变量,也可以修饰局部变量,此时变量一旦赋值就不可以修改;

(1)final修饰的成员变量:可以显式赋值、在代码块中赋值以及在构造器中赋值,final修饰的成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。

(2)final修饰的局部变量:方法内声明的局部变量再调用局部变量前一定要赋值,而且一旦赋值就不可更改,方法的形参在调用此方法时给形参进行赋值,一旦赋值就不可更改

4.static与final修饰:修饰成员变量时此成员变量称为全局常量,比如Math.PI

六、抽象类与抽象方法

1.由来:

随着继承层次中一个个新子类的定义,类变得越来越具体,父类更加一般,更通用,类的设计应该保证父类和子类能够共享特征,有时将一个父类设计的非常抽象,以至于没有具体的实例,这样的类叫做抽象类。没有方法体的方法称为抽象方法,Java语法规定包含抽象方法的类必须是抽象类;

2.语法格式:

(1)抽象类:被abstract修饰的类,抽象类包含构造器,因为子类对象实例化时需要直接或间接的调用到父类的构造器,但是抽象类不能实例化;抽象类中可以没有抽象方法,但是抽象方法必须在抽象类中;

(2)抽象方法:被abstract修饰的方法;抽象方法只有方法的声明,没有具体的方法体(没有花括号);抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体);子类必须重写父类中的所有抽象方法之后才能实例化,否则子类依然是一个抽象类;

抽象方法必须用public修饰或者protected修饰(因为如果用private修饰则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;

3.abstract不能使用的场景:

(1)abstract不能修饰的结构:属性、构造器、代码块等

(2)abstract不能共同使用的关键字:私有方法、静态方法、final的方法、final的类

七、接口(interface):

接口就是规范,定义的是一组规则,本质是标准和规范;

1.接口的定义格式interface:

(1)声明的属性:必须使用public static final修饰

(2)声明的方法:JDK8之前只能声明抽象方法,修饰为public abstract;JDK8之后可以声明静态方法和默认方法;JDK9之后可以声明私有方法;

(3)不可以声明构造器

2.接口和类的关系:实现关系

class A extends SuperA implements B,C{
}
A相较于SuperA称为子类,A相较于B和C来说称为实现类

(1)类可以实现多个接口

(2)类针对于接口的多实现一定程度上弥补了类的单继承的局限性

(3)类必须将实现的接口中的所有抽象方法都重写(实现),方可实例化,否则此类必须声明为抽象类;

3.接口和接口的关系:继承关系,且可以多继承

4.接口的多态性: 接口名 变量名 = new 实现类对象;

5.抽象类和接口的区别:

(1)都可以声明抽象方法,都不能实例化;

(2)抽象类一定有构造器,接口没有构造器

(3)类与类之间是继承关系,类与接口之间是实现关系,接口与接口之间是继承关系

区别点抽象类接口
定义可以包含抽象方法的类主要是抽象方法和全局常量的集合
组成构造方法、抽象方法、普通方法、常量、变量常量、抽象方法
使用子类继承抽象类(extends)子类实现接口(implements)
关系抽象类可以实现多个接口接口不能继承抽象类,但允许继承多个接口
常见设计模式模板方法简单工厂、工厂方法、代理模式
对象都通过对象的多态性产生实例化对象
局限抽象类有单继承的局限接口没有此局限
实际作为一个模板作为一个标准或是表示一种能力
选择如果抽象类和接口都可以使用的话,优先使用接口,因为可以避免单继承的局限性

6.JDK8、JDK9之后的接口新特性:

(1)JDK8之后允许在接口中定义静态方法:该方法只能被此接口调用,而不能被其实现类调用

(2)JDK8之后允许在接口中定义默认方法:修饰为public default,该方法能被实现类调用,如果实现类重写了此方法,则调用的是自己重写的方法;

(3)接口冲突:类实现了两个接口而两个接口中定义了同名同参数的默认方法,则实现类在没有重写此方法的情况下会报错,要求此时实现类必须重写接口综合功能定义的同名同参数的方法

(4)类优先原则:子类(实现类)继承了父类并实现了接口,父类和接口中声明了同名同参数的方法,默认情况下子类(实现类)在没有重写此方法的情况下调用的是父类中的方法;

(5)在子类中调用父类或接口中重写的方法:

(6)JDK9中允许定义私有方法

七、类成员之五:内部类

1.内部类:

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类(InnerClass),类B则称为外部类(OuterClass)

2.声明内部类的原因:

具体来说,当一个事物的内部还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类,总的来说,遵循高内聚、低耦合的面向对象开发原则。

3.内部类的分类:

(1)成员内部类:直接声明在外部类的里面;分为静态成员内部类(使用static修饰)和非静态成员内部类(不使用static进行修饰)

(2)局部内部类:声明在方法内、构造器内、代码块内的内部类;分为匿名的局部内部类和非匿名的局部内部类

4.成员内部类:

(1)理解:从类的角度看,成员内部类内部可以声明属性、方法、构造器、代码块、内部类的结构;此内部类可以声明父类和实现接口,可以使用final、abstract修饰;从外部类的成员的角度看,成员内部类可以调用外部类的结构,可以使用public、缺省和private、protected权限修饰符修饰,同样可以使用static修饰符修饰;

(2)创建成员内部类的实例:

1.静态成员内部类对象:
外部类类名.静态成员内部类类名 对象名 = new 外部类类名.静态成员内部类类名();
2.非静态成员内部类对象:
外部类类名 外部类对象 = new 外部类类名();
外部类类名.非静态成员内部类类名 内部类对象名 = new 外部类对象.非静态成员内部类类名();

(3)在成员内部类中调用外部类的结构:

外部类类名.this.外部类成员属性;
外部类类名.this.外部类成员方法();

八、枚举类:

枚举类本质上也是一种类,只不过这个类的对象是有限的、固定的几个,不能让用户随意地创建;若枚举只有一个对象,则可以作为一种单例模式的实现方式;在开发中,如果针对于某个类其实例是确定个数的,则推荐将此类声明为枚举类;

1.枚举类的实现方式:

(1)自定义枚举类:

class 类名{
    //声明当前类的实例变量
    private final 实例变量类型 实例变量名;
    //私有化类的构造器
    private 类名(实例变量类型 实例变量名){
        this.实例变量名 = 实例变量名;
    }
    //提供实例变量的get方法,没有set方法
    public 实例变量类型 getXXX()
    {
        return this.实例变量名;
    }
    //创建当前枚举类的多个实例
    public static final 类名 枚举对象名1 = new 枚举类名(实际参数);
    public static final 类名 枚举对象名2 = new 枚举类名(实际参数);
    ...
    public static final 类名 枚举对象名n = new 枚举类名(实际参数);
    
}

 (2)enum定义枚举类:

enum 类名{
    //必须在枚举类的开头声明多个对象,对象之间使用逗号隔开
    枚举对象名1(实际参数),
    枚举对象名2(实际参数),
    枚举对象名3(实际参数),
    ...    
    //声明当前类的实例变量,使用private final修饰
    private final 实例变量类型 实例变量名;
    //私有化类的构造器
    private 类名(实例变量类型 实例变量名){
        this.实例变量名 = 实例变量名;
    }
    //提供实例变量的get方法,没有set方法
    public 实例变量类型 getXXX()
    {
        return this.实例变量名;
    }
    
    
}

2.Enum中的常用方法:

使用enum关键字定义的枚举类默认其父类是java.lang.Enum类,使用enum关键字定义的枚举类不要再显式的定义其父类,否则报错;

常用方法:
1.String toString():默认返回的是常量名,可以重写该方法
2.static 枚举类型[] values():返回枚举类型的对象数组,可以方便的遍历所有的枚举值,是一个静态的方法
3.static 枚举类型 valueOf(String name):可以把一个字符串转换为对应的枚举类对象,要求字符串必须是枚举类对象的值
4.String name():得到当前枚举常量的名称,建议优先使用toString();
5.int ordinal():返回当前枚举常量的次序,从0开始

3.枚举类实现接口的操作:

情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法,当通过不同的枚举类对象调用此方法时,执行的是同一个方法;

情况2:让枚举类的每一个对象重写接口中的抽象方法,当通过不同的枚举类对象调用此方法时执行的是不同的方法

九、注解(Annotation)

1.注解概述:

注解从JDK5.0引入,以"@注解名"在代码中存在,Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,还可以添加一些参数值,这些信息被保存在Annotation的"name=value"对中,注解可以在类编译、运行时进行加载,体现不同的功能;

在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等,在JavaEE中注解可用来代替JavaEE旧版中所遗留的冗余代码和XML配置等;

2.常见的Annotation作用:

(1)生成文档相关的注解:

@author:标明开发该类模块的作者,多个作者之间使用逗号分割
@version:标明该类模块的版本
@see:参考转向,也就是相关主题
@since:从哪个版本开始增加的
@param:对方法中某参数的说明,如果没有参数就不能写
@return:对方法返回值的说明,如果方法的返回值类型是void就不能写;
@exception:对方法可能抛出的异常进行说明,如果方法没有用throws显式抛出异常就不能写

(2)在编译时进行格式检查(JDK内置的三个基本注解)

1.@Override:限定重写父类方法该注解只能用于方法;
Java中@Override注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素;
它可以强制一个子类必须重写父类方法或者实现接口的方法;
@Override的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错,这样可以帮助程序员避免一些低级错误;
2.@Deprecated:用于表示所修饰的元素(类、方法等)已过时,通常是因为所修饰的结构危险或存在更好的选择;
Java中@Deprecated可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时;
当其他程序使用已过时的元素时,编译器将会给出警告;
Java9为@Deprecated注解增加了以下两个属性:
(1)forRemoval:boolean类型的属性,指定该API在将来是否会被删除;
(2)since:String类型的属性,指定该API从哪个版本被标记为过时;
例如:@Deprecated(since = "9", forRemoval = true)
3.@SuppressWarnings:抑制编译器警告
Java中的@SuppressWarnings注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素;
注解的使用有以下三种:
(1)抑制单类型的警告:@SuppressWarnings("unchecked");
(2)抑制多类型的警告:@SuppressWarnings("unchecked","rawtypes");
(3)抑制所有类型的警告:@SuppressWarnings("all");
抑制警告的关键字如下所示:
all:抑制所有警告
boxing:抑制装箱、拆箱操作时候的警告
cast:抑制映射相关的警告
dep-ann:抑制启用注释的警告
deprecation:抑制过期方法警告
fallthrough:抑制在switch中缺失breaks的警告
finally:抑制finally模块没有返回的警告
hiding:抑制相对于隐藏变量的局部变量的警告
incomplete-switch:忽略不完整的switch语句
nls:忽略非nls格式的字符
null:忽略对null的操作
rawtypes:使用generics时忽略没有指定相应的类型
restriction:抑制禁止使用劝阻或禁止引用的警告
serial:忽略在serializable类中没有声明serialVersionUID变量
static-access:抑制不正确的静态访问方式警告
synthetic-access:抑制子类没有按最优方法访问内部类的警告
unchecked:抑制没有进行类型检查操作的警告
unqualified-field-access:抑制没有权限访问的域的警告
unused:	抑制没被使用过的代码的警告
4.@SafeVarargs注解
5.@FunctionalInterface注解:
@FunctionalInterface就是用来指定某个接口必须是函数式接口,所以@FunInterface只能修饰接口,不能修饰其它程序元素;
@FunctionalInterface注解的作用只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错;

(3)跟踪代码的依赖性,实现替代配置文件的功能

Servelet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servelet的部署

3.自定义注解

权限修饰符 @interface 自定义注解名{
    成员变量类型1 成员变量名称1();
    成员变量类型2 成员变量名称2();
    ...
    成员变量类型n 成员变量名称n();
}
注解中的成员变量以方法的形式来定义:
成员变量类型 成员变量名称();
如果在注解里定义了成员变量,那么使用该注解时就应该为它的成员变量指定值:
@自定义注解名(成员变量名1 = 成员变量值1,成员变量名2 = 成员变量值2,....)
注解中的成员变量也可以有默认值可使用default关键字:
权限修饰符 @interface 自定义注解名{
    成员变量类型1 成员变量名1() default 默认值1;
    ....
}

4.元注解:

对现有的注解进行解释说明的注解,JDK1.5在java.lang.annotation包定义了4个标准的meta-annotation,它们被用来提供对其他annotation类型作说明:

(1)@Target:用于描述注解的使用范围
@Target注解用来指定一个注解的使用范围,即被@Target修饰的注解可以用在什么地方;
@Target注解有一个成员变量(value)用来设置适用目标,value是java.lang.annotation.ElementType枚举类型的数组,其中常用的枚举常量为:
CONSTRUCTOR:用于构造方法
FIELD:用于成员变量(包括枚举常量)
LOCAL_VARIABLE:用于局部变量
METHOD:用于方法
PACKAGE:用于包
PARAMETER:用于类型参数(JDK1.8新增)
TYPE:用于类、接口(包括注解类型)或enum声明
(2)@Retention:用于描述注解的生命周期,也就是该注解被保留的时间长短;@Retention注解中的成员变量value用来设置保留策略;
value可以通过枚举类型RetentionPolicy的3个常量对象来指定:
SOURCE(在源文件中有效),CLASS(在class文件中有效),RUNTIME(在运行时有效)
唯有RUNTIME阶段才能被反射读取到;
生命周期大小排序为SOURCE<CLASS<RUNTIME,前者能使用的地方后者一定也能使用;
如果需要在运行时去动态获取注解信息,那只能用RUNTIME注解;
如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如ButterKnife),就用CLASS注解;
如果只是做一些检查性的操作,比如@Override和@SuppressWarnings,则可选用SOURCE注解;
(3)@Documented:表明这个注解应该被javadoc工具记录
@Documented是一个标记注解,没有成员变量;
用@Documented注解修饰的注解类会被JavaDoc工具提取成文档;
JavaDoc是不包括注解的;但如果声明注解时指定了@Documented就会被JavaDoc之类的工具处理,所有注解类型信息就会被包括在生成的帮助文档中;
(4)@Inherited:允许子类继承父类中的注解
使用@Inherited注解的Class类,表示这个注解可以被用于该Class类的子类;
就是说如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解;

十、单元测试:

所测方法所在的类必须是public、非抽象的,无空参构造器的

@Test标记的方法本身必须是public,非抽象的,非静态的,void无返回值的,无参数的

十一、包装类的使用

1.使用包装类的原因:

Java提供了基本数据类型和引用数据类型,使用基本数据类型在于效率,但是有时希望其具有面向对象的特征,此时需要使用包装类

2.Java中的包装类:

基本类包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter

3.包装类与基本数据类型之间的转换

(1)转换的原因:

一方面,在有些场景下需要使用基本数据类型对应的包装类的对象,此时就需要将基本数据类型的变量转换为包装类的对象;另一方面,既然是包装类是对象,那么对象不能进行加减乘除的运算,为了能够进行这些运算,就需要将包装类的对象转换为基本数据类型的变量

(2)转换的方法:

1.(装箱)基本数据类型转换为包装类:
a.使用包装类的构造器
包装类类名 包装类对象 = new 包装类类名(基本数据类型变量);
b.调用包装类的valueOf方法(推荐使用)
包装类类名 包装类对象 = 包装类类名.valueOf(基本数据类型变量);
2.(拆箱)包装类转换为基本数据类型
基本数据类型名 基本数据类型变量 = 包装类对象.xxxValue();

JDK5.0引入了自动装箱和自动拆箱的特性,包装类和其对应的基本数据类型之间可以直接相互赋值:

自动装箱:
int i1 = 0;
Integer ii1 = i1;
Integer ii2 = i1 + 1;
自动拆箱:
int i2 = ii2;

4.String类型与基本数据类型和包装类之间的相互转换

1.基本数据类型、包装类转换为String类型:
a.调用String类中静态的重载valueOf方法:
int i1 = 0;
int ii1 = i1;
String s1 = String.valueOf(i1);
String ss1 = String.valueOf(ii1);
b.基本数据类型和连接符
2.String类型转换为基本数据类型和包装类:
调用包装类中的静态方法:parseXXX
String s1 = "123";
int i1 = Integer.parseInt(s1);

5.包装类对象特点:

(1)包装类缓存对象:

包装类缓存对象
Byte-128~127
Short-128~127
Integer-128~127
Long-128~127
Float
Double
Booleantrue和false
Character0~127
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值