1.1泛型的使用

第一章.Java语言进阶

1.1 泛型的使用

为什么需要泛型❓
下面看一段代码:

public int addInt(int x,int y) {
	return x+y;
}
public float addFloat(float x,float y) {
	return x+y;
}

上面虽然传入的参数类型不一样,但是方法体都相同。如果项目需要增加一个double 类型那就需要新增1个方法,也就大大增加了开发时间。

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
    String item = (String)arrayList.get(i);
    Log.d("泛型测试","item = " + item);
}

程序的运行结果会以崩溃😟 结束:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。
我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。

List<String> arrayList = new ArrayList<String>();  //指定为String类型
//arrayList.add(100); 在编译阶段,编译器就会报错

Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

  • 泛型有三种使用方式:
泛型
泛型类
泛型方法
泛型接口
  • 泛型好处:
    1. 适用于多种数据类型执行相同的代码
    2. 泛型中的类型在使用时指定,不需要强制类型转换,在编译阶段进行类型检测。

1.1.1 泛型类

泛型类使用方式:

public class gener1<E> {//可以随便写成任意的E K T 都行,泛型中可以定义多个 
        public E key;
        public void setKey(E key) {
            this.key = key;
        }
        public E getKey() {
           return this.key;
        }
}
class Generclasss<E,K> {  //泛型类中可以写多个
    private E data1;
    private K data2;
    public void setData1(E data1) {
        data1 = data1;
    }
    public void  setData2(K data2) {
        data2 = data2;
    }
}

//定义方式1 明确类型
gener1<String> gener1 = new gener1<>();
gener1.setKey("hello world");
System.out.println(gener1.getKey());

//定义方式2 隐式转换
gener1 gener11 = new gener1();
gener11.setKey(100.90);
System.out.println(gener11.getKey());
  1. android 中泛型类的使用:
public abstract class Singleton<T> {
    private T mInstance;
    protected abstract T create();
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
                }
        return mInstance;
        }
    }
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
    new Singleton<IActivityManager>() {
        @Override
        protected IActivityManager create() {
        final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
        final IActivityManager am = IActivityManager.Stub.asInterface(b);
        return am;
    }
}

//使用 获取AMS
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

注意:

  • 泛型的类型参数只能是类类型,不能是简单类型。
  • 不能对泛型类型使用instanceof操作。编译时会出错。

1.1.2 泛型接口

泛型接口使用

public interface GenInter<E> {
        public abstract void add(E data);
}
实现分为两种方式:
//1) 没有明确类型
public class Generclass1<T> implements Generinter<T>{
    @Override
    public T next() {
        return null;
    }
}
//2) 明确类型
class Generclass2 implements Generinter<String> {
    @Override
    public String next() {
        return null;
    }        
}    

android中泛型接口的使用举例:

public interface Iterable<T> {
    Iterator<T> iterator();
    ...
}
public interface Collection<E> extends Iterable<E> {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
	...	
}
public interface List<E> extends Collection<E> {
    int size();
    boolean isEmpty();
    ...
}
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8683452581122892189L;
    private static final int DEFAULT_CAPACITY = 10;
    ...
}

1.1.3 泛型方法

泛型方法使用:

泛型方法不一定声明在泛型类中,普通的类中也可以声明泛型方法。
格式:public <E> 返回值  函数
例如1class A {
    public <E> E add (E name) { //注意一定要有<E>才代表泛型方法,只有E不代表是泛型
        return name;
    }
}
A a = new A();
System.out.println(a.add(800));  //System.out.println(a.<Integer>add(800));

例如2class B<E> {
    private E a;
    public E add(E _a) {   //不是泛型方法,public 后面没有<E>
        System.out.println("----");
        this.a=_a;
        return a;
    }
    publi<E> void fun(E e) { //泛型方法,参数类型既可以和B中类型相同,也可以不同
    }
}

泛型方法可变参数:T 可以传多个不同类型的值

static public<T> void ShowPrin(T ...args) {
    for (T t:args){
        System.out.println(t.toString());
        }
    }
}

泛型方法举例:

public class GenerFruit {
        static class Fruit {
              @Override
              public String toString() {
                  return "Fruit"}
        }
    
        static class Apple extends Fruit {
            @Override
            public String toString() {
                return "Apple";
            }
        }
    
        static class Person {
            @Override
            public String toString() {
                return "Person";
            }
        }

        static class GenerteTest<T> {   
            //接口1 普通方法
            public void Show(T to) {
                System.out.println(to.toString());
            }
            //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
            //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
            //接口2 定义泛型方法
            public <E> void Show1(E e) {
                System.out.println(e.toString());
            }
            //接口3 //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
            public <T> void Show2(T e){
                System.out.println(e.toString());
            }
            //接口4 泛型可变参数
            public <T> void priMag(T ...args) {//可以是不同类型的
                for (T t:args) {
                    System.out.println(t.toString());
                }
            }
            
}
public static void main(String[] args) {
    //类继承关系 Apple --- > Fruit
    //          Person
    Fruit fruit = new Fruit();
    Apple apple = new Apple();
    Person person = new Person();

    GenerteTest<Fruit> generteTest = new GenerteTest<>();
    System.out.println("----------------------接口1---------------");
    generteTest.Show(apple); //调用接口1 apple 继承Fruit所以不会报错
    generteTest.Show(person);//报错,必须要和Fruit类型相同

    System.out.println("----------------------接口2---------------");
    //调用的是接口2 E 泛型方法 不管传入什么类型都可以
    generteTest.Show1(fruit);
    generteTest.Show1(apple);

    System.out.println("----------------------接口3---------------");
    //调用接口3  泛型方法 不管传入什么类型都可以
    generteTest.Show2(person);
    generteTest.Show2(fruit);

    System.out.println("----------------------接口4---------------");
    //调用接口4 泛型可变参数,任何类型都可以
    generteTest.priMag(fruit,apple,person);
    System.out.println("-----------------使用外部------------------");
    //使用外部接口 
    ShowPrin("abc",1,person);
}
//泛型方法可变参数 可以传不同的类型    
static public<T> void ShowPrin(T ...args) {
    for (T t:args){
        System.out.println(t.toString());
        }
    }
}

泛型方法小结:
1.泛型类中的方法不一定是泛型方法,必须是public <E>
2.有泛型方法的类,不一定的泛型类
3.泛型类中定义的类型,泛型方法中也可以含有其他类型
4.泛型方法可变参数可以传不同的类型

1.1.4 限定类型变量

extends 左右都允许有多个,但是 extends 右边的类只能有1个,必须在最前面,但是接口可以有多个

package Generic;
import java.io.Serializable;
import java.lang.reflect.Constructor;
public class Generclass5 {
   //1)泛型方法 限定T,必须继承自Comparable
    public  <T extends Comparable<T>> T min(T a,T b) {
        if(a.compareTo(b)>0) {
            return a;
        }else {
            return b;
        }
    }
    //2)  extends 左右都允许有多个
    //注意 extends 右边的类只能有1个,必须在最前面,但是接口可以有多个
    public static <T extends Comparable & Serializable> void min1(T a, T b) {
    }
//--------------------------------------------------------------------
    //3 举例
    public  abstract class SystemService {
        public abstract void onCreate();
        public abstract void onStart();
    }
    public class ActivityManager extends SystemService {
        public ActivityManager() {
            System.out.println("ActivityManager");
        }
        @Override
        public void onCreate() {
            System.out.println("onCreate");
        }
        @Override
        public void onStart() {
            System.out.println("onStart");
        }
    }
    public<T extends SystemService> T startService(Class<T> clazz) {
        T instance = null;
        try {
            //内部类构造函数默认带有外部类的this,ActivityManager 属于Generclass5的内部类
            Constructor<T> constructor = clazz.getDeclaredConstructor(Generclass5.class);
            instance = (T) constructor.newInstance(this);
            instance.onCreate();
        }catch (Exception e) {
            System.out.println(e);
        }
        return instance;
    }
    //main
    public static void main(String[] args) {
        Generclass5 generclass5 = new Generclass5();
        SystemService ams = generclass5.startService(ActivityManager.class);
        ams.onStart();
    }
}
//打印:
//ActivityManager
//onCreate
//onStart

android中 extend 泛型方法的使用:

//用来在SystemServer中创建各个Service
//泛型方法
public <T extends SystemService> T startService(Class<T> serviceClass) {
       try {
           final String name = serviceClass.getName();//包名+类名
           ....
           final T service;
           try {
               Constructor<T> constructor = serviceClass.getConstructor(Context.class);
               service = constructor.newInstance(mContext);
           } catch (... ex) {
            ...
           return service;
       } finally {
         ...  
       }
    }

1.1.5 泛型使用约束

1.不能够 定义静态泛型变量,实例化泛型类型变量,数组
2.静态域 或者 静态方法里不能引用 泛型变量
3.如果静态方法本身就是一个泛型方法,就可以使用
4.泛型类 中的类型 <xxx> 使用注意点: 不能使用基本类型
5.不能使用 instance 去判断泛型的类型
6.泛型 不管是什么类型getClass() 都相等
7.泛型里的不同类型调用 类里面的静态变量,静态变量是唯一一份
8.不能catch(捕获)泛型类对象, 泛型类不能继承自Exception类

备注:
在java中泛型只是一个占位符,必须在传递类型后才能使用。就泛型类而言,类实例化时才能传递真正的类型参数,由于静态方法的加载先于类的实例化,也就是说类中的泛型还没有传递真正的类型参数时,静态方法就已经加载完成。显然,静态方法不能使用/访问泛型类中的泛型。

使用约束举例

public class Generclass6<E> {
    //0 不能定义静态泛型变量
    //由于静态变量在java程序一运行时就已经被载入内存,而此时它的类型无法确定,而开辟空间必须知道类型,两者矛盾
    /private static E instance;
    private E  e;
    
    //1 不能够实例化类型变量,因为在编译期无法确定泛型参数化类型
    public void create() {
        //e = new E();  
    }

    // 2> 静态域或者静态方法 不能引用类型变量,静态方法的加载先于类的实例化,也就是说类中的泛型还没有传递真正的类型参数时,静态方法就已经加载完成。显然,静态方法不能使用/访问泛型类中的泛型。
    //public static E instance() {
    //}

    //3>  如果静态方法本身是泛型方法,就看以引用泛型
    public static<T> void fun() {
    }

    public static void main(String[] args) {
        //4>  泛型定义中不能使用基本类型
        Generclass6<Double> generclass6 = new Generclass6<>();
        //Generclass6<double> generclass61 =new Generclass6<double>();  //泛型使用基本类型会报错

        //5 泛型不能使用 instanceof 判断类型
		//if (generclass6 instanceof Generclass6<Double>) {
		//}

        //6 泛型不管是什么类型getClass 都相等
        Generclass<Integer> generclass = new Generclass<>();
        Generclass<String> generclass1 = new Generclass<>();
        System.out.println(generclass.getClass() == generclass1.getClass());  //true

        //7 泛型数组不能new,否则会报错
        //Generclass<String>[] generclass2 = new Generclass<String>[10];
    }


    //8 泛型类不能继承及Exception,也不能捕获泛型类
	// class ThrowAble<T> extends Exception {
	//}
	// public<T extends Throwable> void fun() {
	//  try {
	//     }catch (T e) {
	//   }
	// }
    
    //9 不能够捕获泛型对象,但是可以抛出继承自Throwable的泛型对象
    public <T extends Throwable> void doWork1(T x) throws T{
        try {

        }catch (Throwable e) {
            throw x;
        }
    }
}

1.1.6 泛型使用继承规则

  • 泛型类中的 T 是继承关系,但是两个泛型不是继承关系,所以这泛型类之间没有任何继承关系
  • 泛型类内部接口调用fun(T) ,可以传入T 的子类。
  • 泛型类之间可以继承 ,但是 泛型中 父类和子类泛型之间的 T 要相同,必须要严格匹配
package Generic;
class Employee {
}
class Worker extends Employee{
}

class Pair<T> {
    private T t;
    public void setT(T t) {
        this.t = t;
    }
}
class ExtendPair<T> extends Pair<T> {
}

public class Generclass7 {
    public static void fun(Pair<Worker> pair) {
    }

    public static void main(String[] args) {
        //1 泛型中的 T 是继承关系,但是两个泛型不是继承关系,所以这两者之间没有任何继承关系
        Pair<Employee> pair = new Pair<>();
        Pair<Worker> pair1 = new Pair<>();
        //Pair<Employee> pair2 = new Pair<Worker>(); //这种方式会报错
        //fun(pair);  //这种方式会报错
        fun(pair1);

        //2.泛型类内部接口调用  由于Worker 继承自Employee  泛型中的类是Employee  Worker直接可以用
        Pair<Employee> pair2 = new Pair<>();
        Worker worker = new Worker();
        pair2.setT(worker);

        //3.泛型类之间可以继承   注意泛型中 父类和子类泛型之间的T要相同
        //多态实现
        Pair<Worker> pair3 = new ExtendPair<Worker>();
        
        Pair<Worker> pair4 = new Pair<>();
        ExtendPair<Worker> extendPair = new ExtendPair<>();
        fun(pair3);//多态
        fun(pair4);
        fun(extendPair);//多态
    }
}

1.1.7 泛型使用通配符

  • extends ( ? extends xxx 上限通配符)

    传入参数:

    ​ 只能是 xxx 的子类 和 xxx 本身
    访问数据:
    ​ 限定:只能get xxx 类型,返回值只能是 xxx 类型, 不能set。

  • super ( ?super xxx 下限通配符)
    传入参数:

    ​ 只能是 xxx 父类 和 xxx 本身
    设置数据
    ​ 限定:只能set xxx 类型获 xxx 的子类, 不能get。

举例
1.未使用通配符
2.使用 ? extends xxx
3.使用 ? super xxx

package Generic.GenericTest;
public class WildChar {
    //1.未使用通配符 传入参数只能是 GenericType<Fruit> 不是是Fruit 的子类
    public static void print(GenericType<Fruit> genericType) {
    }
    public static void use() {
        //未使用通配符,调用只能是固定类型
        GenericType<Fruit> fruitGenericType = new GenericType<>();
        GenericType<Apple> appleGenericType = new GenericType<>();
        print(fruitGenericType);
        //print(appleGenericType); //传参错误,虽然fruitGenericType 和 appleGenericType 是继承关系,但是两者之间的T 必须要严格匹配。
    }
    
    //2.使用通配符extends  传入参数可以是Fruit 和Fruit的子类
    public static void print2(GenericType<? extends Fruit> genericType) {
    }
    public static  void useExtends() {
        //使用1  ? extends xxx
        //传入本身
        GenericType<Fruit> genericType = new GenericType<>();
        print2(genericType);
        //传入子类
        GenericType<Apple> appleGenericType = new GenericType<>();
        print2(appleGenericType);

        //? extends xxx  的限定:只能get不能set
        GenericType<? extends Apple> fruit = new GenericType<>();
        Fruit fruit2 = fruit.getT();
        Apple apple = fruit.getT();
        //Apple apple1 = fruit.getT();

		//fruit.setT(new Apple());
		//fruit.setT(new HongFushi());
    }

    //3使用通配符super  传入参数可以是Apple 和Apple的父类
    public static void print3(GenericType<? super Apple> genericType) {
    }
    public static void userSuper() {
        //使用1  ? super xxx
        //传入本身
        GenericType<Apple> genericType = new GenericType<>();
        print3(genericType);
        //传入父类
        GenericType<Fruit> genericType1 = new GenericType<>();
        print3(genericType1);

        //? super xxx 的限定:只能set xxx  不能get
        GenericType<? super Apple> genericType2 =  new GenericType<>();
        genericType2.setT(new Apple());
        genericType2.setT(new HongFushi());
        //genericType2.setT(new Fruit());//出错

        //Apple apple1 = genericType2.getT();
    }
    public static void main(String[] args) {
    }
}

1.1.8 android 中泛型的使用

(1)RemoteCallbackList

主要用于aidl的跨进程通信,并且主要是服务端回调客户端的方法。

IInterface.java
package android.os;
public interface IInterface
{
    public IBinder asBinder();
}
/*-----------------------RemoteCallbackList.java-----------------------------------*/
package android.os;
import android.util.ArrayMap;
import android.util.Slog;
import java.io.PrintWriter;
import java.util.function.Consumer;
public class RemoteCallbackList<E extends IInterface> {
    private static final String TAG = "RemoteCallbackList";
    ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
    private Object[] mActiveBroadcast;
    private int mBroadcastCount = -1;
    private boolean mKilled = false;
    private StringBuilder mRecentCallers;
    //存放远程的Binder对象
    private final class Callback implements IBinder.DeathRecipient {
        final E mCallback; //远程代理对象
        final Object mCookie;
        Callback(E callback, Object cookie) {
            mCallback = callback;
            mCookie = cookie;
        }
        //远程对象死亡回调
        public void binderDied() {
            synchronized (mCallbacks) {
                mCallbacks.remove(mCallback.asBinder()); //远程代理对象转化成Binder
            }
            onCallbackDied(mCallback, mCookie);
        }
    }
    //将远程代理对象注册  xxx.Stub.asInterface(BinderProxy/Binder); 
    public boolean register(E callback) {
        return register(callback, null);
    }
    public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return false;
            }
            // Flag unusual case that could be caused by a leak. b/36778087
            logExcessiveCallbacks();
            IBinder binder = callback.asBinder(); //BinderProxy 或者 Binder
            try {
                Callback cb = new Callback(callback, cookie);
                binder.linkToDeath(cb, 0);//死亡监听
                mCallbacks.put(binder, cb); //远程Binder对象 和 死亡监听对象 存入
                return true;
            } catch (RemoteException e) {
                return false;
            }
        }
    }
	//反注册
    public boolean unregister(E callback) {
        synchronized (mCallbacks) {
            Callback cb = mCallbacks.remove(callback.asBinder());
            if (cb != null) {
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
                return true;
            }
            return false;
        }
    }
    public void kill() {
        synchronized (mCallbacks) {
            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
                Callback cb = mCallbacks.valueAt(cbi);
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
            }
            mCallbacks.clear();
            mKilled = true;
        }
    }
    //死亡回调
    public void onCallbackDied(E callback) {
    }
    //死亡回调
    public void onCallbackDied(E callback, Object cookie) {
        onCallbackDied(callback);
    }
    public int beginBroadcast() {
        synchronized (mCallbacks) {
            if (mBroadcastCount > 0) {
                throw new IllegalStateException(
                        "beginBroadcast() called while already in a broadcast");
            }
            final int N = mBroadcastCount = mCallbacks.size();
            if (N <= 0) {
                return 0;
            }
            Object[] active = mActiveBroadcast;
            if (active == null || active.length < N) {
                mActiveBroadcast = active = new Object[N];
            }
            for (int i=0; i<N; i++) {
                active[i] = mCallbacks.valueAt(i);
            }
            return N;
        }
    }
    public E getBroadcastItem(int index) {
        return ((Callback)mActiveBroadcast[index]).mCallback;
    }
    public Object getBroadcastCookie(int index) {
        return ((Callback)mActiveBroadcast[index]).mCookie;
    }
    public void finishBroadcast() {
        synchronized (mCallbacks) {
            if (mBroadcastCount < 0) {
                throw new IllegalStateException(
                        "finishBroadcast() called outside of a broadcast");
            }

            Object[] active = mActiveBroadcast;
            if (active != null) {
                final int N = mBroadcastCount;
                for (int i=0; i<N; i++) {
                    active[i] = null;
                }
            }
            mBroadcastCount = -1;
        }
    }
    public void broadcast(Consumer<E> action) {
        int itemCount = beginBroadcast();
        try {
            for (int i = 0; i < itemCount; i++) {
                action.accept(getBroadcastItem(i));
            }
        } finally {
            finishBroadcast();
        }
    }
    public <C> void broadcastForEachCookie(Consumer<C> action) {
        int itemCount = beginBroadcast();
        try {
            for (int i = 0; i < itemCount; i++) {
                action.accept((C) getBroadcastCookie(i));
            }
        } finally {
            finishBroadcast();
        }
    }
    public int getRegisteredCallbackCount() {
        synchronized (mCallbacks) {
            if (mKilled) {
                return 0;
            }
            return mCallbacks.size();
        }
    }
    public E getRegisteredCallbackItem(int index) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return null;
            }
            return mCallbacks.valueAt(index).mCallback;
        }
    }
    public Object getRegisteredCallbackCookie(int index) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return null;
            }
            return mCallbacks.valueAt(index).mCookie;
        }
    }
	....
}

/*----RemoteCallbackList 使用举例-------------*/
//IBluetooth.aidl
    void registerCallback(IBluetoothCallback cb)
    void unregisterCallback(IBluetoothCallback cb)
//IBluetoothCallback.aidl    
    void onBluetoothStateChange();

//Service端
private RemoteCallbackList<IBluetoothCallback> mCallbacks = new RemoteCallbackList<IBluetoothCallback>();

void registerCallback(IBluetoothCallback cb) {
      mCallbacks.register(cb);
}
void unregisterCallback(IBluetoothCallback cb) {
      mCallbacks.unregister(cb);
}
public void notifyCallbacck() {
	if (mCallbacks != null) {
    	int n = mCallbacks.beginBroadcast(); //获取注册的数量
    	for (int i = 0; i < n; i++) {
    	try {
        	mCallbacks.getBroadcastItem(i).onBluetoothStateChange(); //回调onBluetoothStateChange方法
        } catch (RemoteException e) {        
        }	
    	mCallbacks.finishBroadcast();//移除
		}    
}
//Client 端
1.获得IBluetooth Binder对象
2.注册IBluetoothCallback 对象    

(2)WeakReference 防止Handler 内存泄漏
    private static class MyHandler extends Handler {
        private WeakReference<MainActivity> mWeakReference;

        public MyHandler(MainActivity activity) {
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity mainActivity = mWeakReference.get();
            switch (msg.what) {
                case 1:
                    break;
                default:
                    break;
            }
        }
    }
(3) 泛型方法的使用
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.getConstructor(Application.class).newInstance(mApplication);
        } catch (NoSuchMethodException e) {
		。。。
        }
    }
    return super.create(modelClass);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值