Java泛型(泛型类、反射、类型通配符)-黑马视频笔记

泛型

学习参考视频:B站黑马

本文结构:

一、什么是泛型

二、泛型类、接口

三、泛型方法

四、类型通配符

五、类型擦除

六、泛型和数组

七、 泛型和反射

一、什么是泛型

背景

JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。

泛型的概念

参数化类型

好处

  • 编译时检查类型
  • 减少了类型转换

MainClass.java

通过以下程序理解泛型的好处

import java.util.ArrayList;

public class MainClass {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a");
        list.add(1);

        // 类型转换错误
//        for (Object o : list) {
//            String tmp = (String) o;
//            System.out.println(tmp);
//        }

        ArrayList<String> ll = new ArrayList<>();
        ll.add("Java");
        ll.add("Python");
        ll.add("C++");
//        ll.add(1);    // 编译错误
        for (String s : ll) {
            System.out.println(s);
        }
    }
}

二、泛型类、接口

2.1 泛型类的定义

泛型类的定义

class 类名 <泛型标识,泛型标识> {
  private 泛型标识 变量名;
}

常见的泛型标识:T、E、K、V

使用

  • 语法:类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
  • java1.7之后,后面的<>内部可省略:类名<具体的数据类型> 对象名 = new 类名<>();

Generic.java

泛型的定义

public class Generic<T> {
    private T key;

    public Generic() {
    }

    public Generic(T key) {
        this.key = key;
    }

    @Override
    public String toString() {
        return "Generic{" +
                "key=" + key +
                '}';
    }

    public void setKey(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }

    public static void main(String[] args) {

        // 不指定类型时,默认Object型
        Generic ge1 = new Generic();
        Object key1 = ge1.getKey();
        System.out.println("key1-->" + key1);

        // 不能是基本数据类型
//        new Generic<int>();

        // 泛型类型实际上都是相同类型
        Generic<String> strGe = new Generic<>("abc");
        Generic<Integer> intGe = new Generic<>(1);
        String strGeKey = strGe.getKey();
        Integer intGeKey = intGe.getKey();
        System.out.println("strKey-->" + strGeKey);
        System.out.println("intGe-->" + intGeKey);
        System.out.println(strGe.getClass() == intGe.getClass());
    }
}

2.2 泛型类的使用

定义一个奖品抽奖的泛型类,getProduct方法获取随机奖品,addProduct向奖品池添加奖品。

import java.util.ArrayList;
import java.util.Random;

public class ProductGetter<T> {

    Random r = new Random();
    // 奖品
    private T product;
    // 奖品池
    ArrayList<T> list = new ArrayList<>();

    public ProductGetter() {
    }

    public ProductGetter(ArrayList<T> list) {
        this.list = list;
    }

    // 抽奖
    public T getProduct() {
        int i = r.nextInt(list.size());
        return list.get(i);
    }

    // 添加奖品
    public void addProduct(T product) {
        list.add(product);
    }

    public static void main(String[] args) {
        // 1. str型
        ProductGetter<String> strProductGetter = new ProductGetter<>();
        // 加入奖池
        String[] strProducts = new String[]{"手枪", "充电器", "弓箭"};
        for (int i = 0; i < strProducts.length; i++) {
            strProductGetter.addProduct(strProducts[i]);
        }
        // 抽奖
        String product = strProductGetter.getProduct();
        System.out.println("恭喜你抽中了" + product);

        // 2. int
        ProductGetter<Integer> intProductGetter = new ProductGetter<>();
        // 加入奖池
        int[] intProducts = new int[]{100000, 20000, 80000};
        for (int i = 0; i < intProducts.length; i++) {
            intProductGetter.addProduct(intProducts[i]);
        }
        // 抽奖
        Integer intProduct = intProductGetter.getProduct();
        System.out.println("恭喜你抽中了" + intProduct);

    }

}

输出:

恭喜你抽中了手枪
恭喜你抽中了100000

2.3 泛型类的派生子类

规则

  • 子类也是泛型类,子类和父类的泛型类型要一致。class ChildGeneric extends Generic

  • 子类不是泛型类,父类要明确泛型的数据类型。class ChildGeneric extends Generic


Parent.java

泛型父类

public class Parent<T> {
    private T value;

    public Parent(T value) {
        this.value = value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

FirstChild.java

子类也是泛型

public class FirstChild<T> extends Parent<T> {

    public FirstChild(T value) {
        super(value);
    }

    public static void main(String[] args) {
        FirstChild<String> firstChild = new FirstChild<>("aaa");
        String value = firstChild.getValue();
        System.out.println("value-->" + value);
    }
}

SecondChild.java

子类不是泛型,需指定父类的泛型类型

public class SecondChild extends Parent<Integer> {

    public SecondChild(Integer value) {
        super(value);
    }

    public static void main(String[] args) {
        SecondChild secondChild = new SecondChild(111);
        Integer value = secondChild.getValue();
        System.out.println("value-->" + value);
    }
}

2.4 泛型接口

规则

  • 实现类不是泛型类,接口要明确数据类型
  • 实现类是泛型类,实现类和接口的泛型类型要一致

Generator.java

/**
 * 泛型接口
 * @param <T>
 */
public interface Generator<T> {
    T getKey();
}

Apple.java

public class Apple implements Generator<String> {

    @Override
    public String getKey() {
        return "hello apple";
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        System.out.println(apple.getKey());
    }
}

Pair.java

public class Pair<E, T> implements Generator<T> {

    private T key;
    private E value;

    public Pair(E value, T key) {
        this.key = key;
        this.value = value;
    }

    @Override
    public T getKey() {
        return key;
    }

    public E getValue() {
        return value;
    }

    public static void main(String[] args) {
        Pair<String, Integer> pair = new Pair<>("爱马仕", 100);
        Integer key = pair.getKey();
        String value = pair.getValue();
        System.out.println("key-->" + key + ", value-->" + value);
    }
}

三、泛型方法

使用了泛型的类的成员方法不属于泛型方法。声明了的方法才是

语法

修饰符 <T, E, ...> 返回值类型 方法名(形参列表) {
  
}

可变参数在范型表示后面加…,参数为可遍历的对象。

public <E> void print(E... e){
  for(e1: e) {
    System.out.println(e1)
  }
}

ProductGetter.java

import java.util.ArrayList;
import java.util.Random;

public class ProductGetter<T> {

    Random r = new Random();
    // 奖品
    private T product;
    // 奖品池
    ArrayList<T> list = new ArrayList<>();

    public ProductGetter() {
    }

    public ProductGetter(ArrayList<T> list) {
        this.list = list;
    }

    // 抽奖
    public T getProduct() {
        int i = r.nextInt(list.size());
        return list.get(i);
    }

    // 泛型方法。参数传递奖品池,然后从传入的奖品池随机获取奖品
    /**
       *
       * @param list 奖品池
       * @param <E> 奖品的类型
       * @return
       */
    public <E> E getProduct(ArrayList<E> list) {
        int i = r.nextInt(list.size());
        return list.get(i);
    }
  
    /**
       * 定义了静态的泛型方法
       * @param e
       * @param t
       * @param k
       * @param <E>
       * @param <T>
       * @param <K>
       */
    public static <E, T, K> void printType(E e, T t, K k) {
        System.out.println(e + "--" + e.getClass().getSimpleName());
        System.out.println(t + "--" + t.getClass().getSimpleName());
        System.out.println(k + "--" + k.getClass().getSimpleName());
    }

    /**
     * 可变参数的泛型方法
     * @param e
     * @param <E>
     */
    public static <E> void print(E... e) {
        for (E e1 : e) {
            System.out.println(e1 + "--" + e1.getClass().getSimpleName());
        }
    }

    // 添加奖品
    public void addProduct(T product) {
        list.add(product);
    }

}

泛型方法的类型与类的泛型不冲突,名称相同时方法的泛型类型优先级更高。

所以,泛型方法getProduct也可写为:

public <T> T getProduct(ArrayList<T> list) {
    int i = r.nextInt(list.size());
    return list.get(i);
}

MethodTest.java

对泛型方法进行测试

泛型方法可以是静态的,泛型类的成员方法不能是静态的。 可以尝试更改成员方法为static修饰,会出现编译错误。
import java.util.ArrayList;

public class MethodTest {
    public static void main(String[] args) {
        ProductGetter<Integer> productGetter =  new ProductGetter<>();
        // 奖品池
        ArrayList<String> list =  new ArrayList<>();
        list.add("华为电脑");
        list.add("苹果电脑");
        list.add("小米手机");

        String product = productGetter.getProduct(list);
        System.out.println(product + "\t" + product.getClass().getSimpleName());

        System.out.println("<------>");

        // Integer型
        ArrayList<Integer> intList =  new ArrayList<>();
        intList.add(10000);
        intList.add(20000);
        intList.add(40000);

        Integer intProduct = productGetter.getProduct(intList);
        System.out.println(intProduct + "\t" + intProduct.getClass().getSimpleName());

        System.out.println("<------>");

        // 范型参数
        productGetter.printType(111, "aaa", true);

        System.out.println("<------>");

        // 可变参数
        productGetter.print(111, 222, 333);

    }
}

输出:
苹果电脑 String
<------>
20000 Integer
<------>
111–Integer
aaa–String
true–Boolean
<------>
111–Integer
222–Integer
333–Integer

四、类型通配符

4.1 类型通配符

什么是类型通配符

  • ?代替具体的类型实参
  • 所以,类型通配符是类型实参,不是类型形参

Box.java

public class Box <E>{
    private E first;

    public void setFirst(E first) {
        this.first = first;
    }

    public E getFirst() {
        return first;
    }
}

Test.java

测试

public class Test {
    public static void main(String[] args) {
        Box<Integer> box1 = new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<String> box2 = new Box<>();
        box2.setFirst("aaa");
        showBox(box2);
    }

//    public static <T> void showBox(Box<T> box) {
//        T first = box.getFirst();
//        System.out.println(first);
//    }

    public static void showBox(Box<?> box) {
        Object first = box.getFirst();
        System.out.println(first);
    }
}

4.2 类型通配符的上限

要求该泛型的类型,必须是实参类型或者其子类

语法

/接口<? extends 实参类型>

Test.java

public class Test {
    public static void main(String[] args) {
        Box<Integer> box1 = new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<Number> box2 = new Box<>();
        box2.setFirst(200);
        showBox(box2);
    }

//    public static <T> void showBox(Box<T> box) {
//        T first = box.getFirst();
//        System.out.println(first);
//    }

    public static void showBox(Box<? extends Number> box) {
        Object first = box.getFirst();
        System.out.println(first);
    }
}

案例

三个类,继承关系:Animal<–Cat<–MiniCat

Test01.java

import java.util.ArrayList;

public class Test01 {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> miniCats = new ArrayList<>();

//        showAnimal(animals);  // 编译错误,因为只能接受Cat及其子类
        showAnimal(cats);
        showAnimal(miniCats);

    }

    public static void showAnimal(ArrayList<? extends Cat> list) {
        // 不确定参数的类型,不能填充元素
//        list.add(new Animal());
//        list.add(new Cat());
//        list.add(new MiniCat());
        for (Cat cat : list) {
            System.out.println(cat.getClass().getSimpleName());
        }
    }

}

4.3 类型通配符的下限

语法

要求该泛型的类型,只能是实参类型,或实参类型的父类类型。

/接口 <? super 实参类型>

TestDown.java

import java.util.ArrayList;
import java.util.List;

public class TestDown {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> miniCats = new ArrayList<>();

        showAnimal(animals);
        showAnimal(cats);
//        showAnimal(miniCats); // 编译不通过

    }

    /**
     * 类型通配符的下限,要求list只能是Cat或Cat的父类
     * @param list
     */
    public static void showAnimal(List<? super Cat> list) {
        // 编译正确但是不保证正确
//        list.add(new Cat());
//        list.add(new MiniCat());
        for (Object o : list) {
            System.out.println(o);
        }
    }

}

案例

三个实体类

Animal.java

public class Animal {
    public String name;

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }

    public Animal(String name) {
        this.name = name;
    }
}

Cat.java

public class Cat extends Animal{
    public int age;


    public int getAge() {
        return age;
    }

    public Cat(String name, int age) {
        super(name);
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

MiniCat.java

public class MiniCat extends Cat{
    public int level;


    public MiniCat(String name, int age, int level) {
        super(name, age);
        this.level = level;
    }

    public int getLevel() {
        return level;
    }

    @Override
    public String toString() {
        return "MiniCat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", level=" + level +
                '}';
    }
}

Test02.java

import java.util.Comparator;
import java.util.TreeSet;

public class Test02 {
    public static void main(String[] args) {
        TreeSet<Cat> treeSet = new TreeSet<>(new Comparator2());
//        TreeSet<Cat> treeSet = new TreeSet<>(new Comparator1());
//        TreeSet<Cat> treeSet = new TreeSet<>(new Comparator3());
        treeSet.add(new Cat("jerry", 20));
        treeSet.add(new Cat("amy", 22));
        treeSet.add(new Cat("frank", 35));
        treeSet.add(new Cat("jim", 15));

        //
        for (Cat cat : treeSet) {
            System.out.println(cat);
        }

    }
}

class Comparator1 implements Comparator<Animal> {

    @Override
    public int compare(Animal o1, Animal o2) {
        return o1.getName().compareTo(o2.getName());
    }
}

/**
 * 按年龄升序排列
 */
class Comparator2 implements Comparator<Cat> {

    @Override
    public int compare(Cat o1, Cat o2) {
        return o1.getAge() - o2.getAge();
    }
}

class Comparator3 implements Comparator<MiniCat> {

    @Override
    public int compare(MiniCat o1, MiniCat o2) {
        return o1.getLevel() - o2.getLevel();
    }
}

TreeSet比较器的定义:

此时E为Cat,比较器的泛型必须是Animal或Cat。而比较器Comparator3的泛型类型为MiniCat,第二行注释的代码编译会出现错误。

public TreeSet(Comparator<? super E> comparator)

五、类型擦除

概念

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为——类型擦除。

泛型信息只存在编译阶段!!!

5.1 无限制类型擦除

泛型类信息无条件擦除,为Object

在这里插入图片描述

Erasure.java

public class Erasure <T> {
    private T key;

    @Override
    public String toString() {
        return "Erasure{" +
                "key=" + key +
                '}';
    }

    public Erasure() {
    }

    public Erasure(T key) {
        this.key = key;
    }

    public void setKey(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }
}

Test09.java

public class Test09 {

    public static void main(String[] args) {
        // 泛型类型不同,最后会进行类型擦除
        ArrayList<Integer> intList = new ArrayList<>();
        ArrayList<String> strList = new ArrayList<>();

        System.out.println(intList.getClass().getSimpleName());
        System.out.println(strList.getClass().getSimpleName());
        System.out.println(strList.getClass() == intList.getClass());

        // 无限制的擦除为Object
        Erasure<Integer> erasure = new Erasure<>();
        Class<? extends Erasure> cls = erasure.getClass();
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field.getName() + "--" + field.getType().getSimpleName());
        }

    }
}

5.2 有限制的类型擦除

有限制的擦除,擦除为指定类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Muapi8YO-1690871344412)(/Users/xuhanqi/Library/Application Support/typora-user-images/image-20230729164820346.png)]

5.3 擦除方法中类型定义的参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPchbcLX-1690871344413)(/Users/xuhanqi/Library/Application Support/typora-user-images/image-20230729165120324.png)]

Erasure.java

public class Erasure <T extends Number> {
    private T key;

		// 泛型方法,上限为Number
    public <T extends Number> T getValue(T value) {
        return value;
    }

    @Override
    public String toString() {
        return "Erasure{" +
                "key=" + key +
                '}';
    }

    public Erasure() {
    }

    public Erasure(T key) {
        this.key = key;
    }

    public void setKey(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }
}

Test10.java

public class Test10 {
    public static void main(String[] args) {
        Erasure<Integer> er1 = new Erasure<>();
        Erasure<Float> er2 = new Erasure<>();

        Class<? extends Erasure> cls = er1.getClass();
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method method : declaredMethods) {
          // 类型擦除为Number
            System.out.println(method.getName() + "--" + method.getReturnType().getSimpleName());
        }
    }
}

输出:

toString–String
getValue–Number
getKey–Number
setKey–void

5.4 桥接方法

在这里插入图片描述

Info.java

/**
 * 泛型接口
 * @param <T>
 */
public interface Info <T> {
    T info(T t);
}

InfoImpl.java

public class InfoImpl implements Info<Integer> {

    @Override
    public Integer info(Integer value) {
        return value;
    }
}

InfoTest.java

import java.lang.reflect.Method;

public class InfoTest {
    public static void main(String[] args) {
        Class<InfoImpl> cls = InfoImpl.class;
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method.getName() + "--" + method.getReturnType().getSimpleName());
        }
    }
}

输出:

info–Integer
info–Object

六、泛型和数组

规则

  • 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
  • 可以通过java.lang.reflect.Array的newInstance(Class, int)创建T[]数组

推荐:ArrayList[] arr = new ArrayList();

Test10.java

public class Test10 {
    public static void main(String[] args) {
        // 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
//        ArrayList<String>[] listArr = new ArrayList<>[5];   // 编译不通过

        // 先声明引用,不推荐
        ArrayList[] list = new ArrayList[5];
        ArrayList<String>[] listArr = list;

        ArrayList<Integer> intList = new ArrayList<>();
        intList.add(100);

        // 编译通过但是 ClassCastException异常
        list[0] = intList;
        String s = listArr[0].get(0);
        System.out.println(s);
    }
}

Test11.java

import java.util.ArrayList;

public class Test11 {
    public static void main(String[] args) {
        // 莫有问题
        ArrayList<String>[] listArr = new ArrayList[5];

        ArrayList<Integer> intList = new ArrayList<>();
        intList.add(100);

        // 编译不通过
        listArr[0] = intList;
    }
}

案例

Fruit.java

import java.lang.reflect.Array;

public class Fruit <T> {
    // 不知道具体类型,无法new
//    private T[] array = new T[3];
    private T[] array;

    public Fruit(Class<T> cls, int len) {
        // 通过Array.newInstance创建泛型数组
        array = (T[]) Array.newInstance(cls, len);
    }

    // 填充数组
    public void put(T item, int index) {
        array[index] = item;
    }

    // 获取数组元素
    public T get(int index) {
        return array[index];
    }

    // 获取数组
    public T[] getArray() {
        return array;
    }
}

Test12.java

import java.util.Arrays;

public class Test12 {
    public static void main(String[] args) {
        Fruit<String> fruit = new Fruit(String.class, 3);

        fruit.put("apple", 0);
        fruit.put("lemon", 1);
        fruit.put("peach", 2);

        String[] arr = fruit.getArray();
        System.out.println(Arrays.toString(arr));
    }
}

七、 泛型和反射

使用泛型的反射会更加方便

/**
 * 泛型和反射
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 指定泛型类型
        Class<Person> cls = Person.class;
        Constructor<Person> declaredConstructor = cls.getDeclaredConstructor();
        Person p1 = declaredConstructor.newInstance();

      	// 不指定泛型类型
        Class clz = Person.class;
        Constructor con = clz.getDeclaredConstructor();
        Object p2 = con.newInstance();

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
泛型的产生条件 泛型是为了解决在编译期间无法确定类型而引入的,其产生条件包括: 1. 在代码中需要使用到泛型类型,但是这个泛型类型的具体类型在编译期间是不确定的; 2. 在代码中需要对泛型类型进行操作,比如调用其方法或者获取其属性值。 泛型的概念 泛型是指在编写代码时,不需要指定数据类型,而是在使用时再指定具体的数据类型。这样就可以提高代码的复用性和灵活性。 泛型类 泛型类是指使用泛型定义的,其中泛型参数可以用在的成员变量、成员方法、构造方法中。 泛型类派生 泛型类派生是指使用泛型定义的的子,其中子可以继续使用父中定义的泛型类型。 泛型 泛型是指在继承泛型类时,子也要使用泛型类型。 不泛型泛型是指在继承泛型类时,子不使用泛型类型。 泛型接口 泛型接口是指使用泛型定义的接口,其中泛型参数可以用在接口的方法中。 泛型方法 泛型方法是指使用泛型定义的方法,其中泛型参数可以用在方法的参数列表、返回值、方法体中。 类型通配符 类型通配符是指在定义泛型时使用的一种特殊符号,用于表示不确定的类型。 引出类型通配符 类型通配符可以用于引出泛型类型参数的上限或者下限。 类型通配符的上限 类型通配符的上限是指使用 extends 关键字限制泛型类型参数的范围,表示泛型类型参数必须是某个类型的子或者实现类型通配符的下限 类型通配符的下限是指使用 super 关键字限制泛型类型参数的范围,表示泛型类型参数必须是某个类型的父或者超类型擦除 类型擦除是指在编译期间,将泛型类型参数替换为其上限或者 Object 类型的过程。 无限制类型的擦除 无限制类型的擦除是指在泛型类型参数没有明确指定上限或者下限时,将其擦除为 Object 类型。 有限制类型擦除 有限制类型擦除是指在泛型类型参数有明确指定上限或者下限时,将其擦除为上限或者下限。 擦除泛型方法中类型定义的参数 在泛型方法中,如果定义了泛型类型参数,则在编译期间也会进行类型擦除。 桥接方法 在泛型类或者泛型接口中,如果有泛型方法,则在编译期间会自动生成桥接方法来确保类型安全。 泛型数组 泛型数组是指使用泛型定义的数组,其中数组元素的类型泛型类型参数。 泛型反射 泛型反射的结合可以实现动态创建泛型类型对象、获取泛型类型信息等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值