Day25Java基础学习(多线程,GUI)

Day25

  1. 多线程(单例设计模式)
    (1)单例设计模式:保证类在内存中只有一个对象。
    (2)如何保证类在内存中只有一个对象呢?
    控制类的创建,不让其他类来创建本类的对象。Private
    在本类中定义一个本类的对象。Singleton s;
    提供公共的访问方式。public static Singleton getInstance(){return s}

单例写法两种:
饿汉式和懒汉式的区别:
1:饿汉式是空间转换时间,懒汉式是时间换空间。
2:在多线程访问时,饿汉式不会创建多个对象,懒汉式有可能创建多个对象
(1) 饿汉式 开发用这种方式

public class thread_Demo1_Singleton {
    /*
    单例设计模式:保证类在内存中只有一个对象。
     */
    public static void main(String[] args) {
       // Singlenton s1 = new Singlenton();

        /*Singlenton s1 = Singlenton.s;  //成员变量被私有,不能通过类名.调用
        Singlenton.s = null;
        Singlenton s2 = Singlenton.s;

        System.out.println(s1 == s2);*/

        Singlenton s1 = Singlenton.getInstance();
        Singlenton s2 = Singlenton.getInstance();
        System.out.println(s1 == s2);


    }
}
/*
  饿汉式
 */
class Singlenton{
    //1:私有构造方法,其他类不能访问该构造方法了
    private Singlenton(){}
    //2:创建本类对象
    private   static Singlenton s = new Singlenton();
    //3:对外提供公共的访问方法
    public static Singlenton getInstance(){ //获取实例
        return s;
    }

}

(2) 懒汉式 ,单例的延迟加载模式,面试写这种方式 多线程的问题?

class Singlenton {
    //1:私有构造方法,其他类不能访问该构造方法了
    private Singlenton(){}
    //2:创建本类对象
    private   static Singlenton s = new Singlenton();
    //3:对外提供公共的访问方法
    public static Singlenton getInstance(){
        if(s == null) {
            s = new Singlenton();
        }
        return s;
    }
}

(3) 第三种格式

class Singlenton {
    //1:私有构造方法,其他类不能访问该构造方法了
    private Singlenton() {
    }

    //2:创建本类对象
    public static final Singlenton s = new Singlenton();
}
  1. 多线程(Runtime类)
    Runtime类式一个单例类

public class Demo2_RunTime {
    public static void main(String[] args) throws IOException {
        Runtime r = Runtime.getRuntime(); //获取运行时对象
        //r.exec("shutdown -s -t 300");  //命令电脑五分钟后关闭
        r.exec("shutdown -a");  //取消关机命令
    }
}

3.	多线程(Timer)
Timer类:计时器
public class Demo3_Timer {
    public static void main(String[] args) throws InterruptedException {
        Timer t = new Timer();
       //在指定时间安排指定任务
        //第一个参数时安排的任务
        //第二个参数时执行的时间;第三个参数时过多长时间再重复运行
        t.schedule(new MyTimerTask(),new Date(120,3,20,22,17),3000);

        while (true){
            Thread.sleep(1000);
            System.out.println(new Date());
        }

    }
}
class MyTimerTask extends TimerTask {

    @Override
    public void run() {
        System.out.println("起床背单词");
    }
}

  1. 多线程(两个线程间的通信)
    (1) 什么时候需要通信
    多个线程并发执行时,在默认情况下cpu时随机切换线程的。
    如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印。
    (2) 怎么通信
    如果希望线程等待,就调用wait()
    如果希望唤醒等待的线程,就调用notify();
    这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。
public class Demo1_Notify {
    //等待唤醒机制
    public static void main(String[] args) {
        final Printer p = new Printer();

        new Thread() {
            public void run (){
                while (true) {
                    try {
                        p.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new Thread() {
            public void run (){
                while (true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
//等待唤醒机制

class Printer {
    private int flag = 1;
    public  void print1() throws InterruptedException {
        synchronized (this) {
            if(flag != 1){
                this.wait();    //当前线程等待
            }
            System.out.print("黑");
            System.out.print("马");
            System.out.print("程");
            System.out.print("序");
            System.out.print("员");
            System.out.print("\r\n");
            flag = 2;
            this.notify();   //随机唤醒单个等待线程
        }
    }

    public void print2() throws InterruptedException {
        synchronized (this) {
            if(flag != 2){
                this.wait();
            }
            System.out.print("传");
            System.out.print("智");
            System.out.print("播");
            System.out.print("客");
            System.out.print("\r\n");
            flag = 1;
            this.notify();
        }
    }
}
  1. 多线程(两个或三个以上间的线程通信)
    多个线程通信的问题
    (1) notify()方法是随机唤醒一个线程
    (2) notifyAll()方法是唤醒所有线程
    (3) JDK5之前无法唤醒指定的一个线程
    (4) 如果多个线程之间通信,需要使用notifyAll通知所有线程,用while来反复判断条件。
    (5) 注意事项:
    1:在同步代码块中,用那个对象锁,就用那个对象调用wait方法
    2:为什么wait方法和notify方法定义在Object类中?
    因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法需要定义在Object这个类中
    3:sleep方法和wait方法的区别?
    1)sleep方法必须传入参数,参数就是时间,时间到了就自动醒来。
    wait方法可以传入参数,也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待。
    2)sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡。
    wait方法在同步函数或同步代码块中,释放锁。
public class Demo2_NotifyAll {
    public static void main(String[] args) {
       final Printer2 p = new Printer2();
        new Thread(){
             public void run(){
                 while (true){
                     try {
                         p.print1();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
        }.start();

        new Thread(){
            public void run(){
                while (true){
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new Thread(){
            public void run(){
                while (true){
                    try {
                        p.print3();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

    }
}
/*
1:在同步代码块中,用那个对象锁,就用那个对象调用wait方法
2:为什么wait方法和notify方法定义在Object类中?
因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法需要定义在Object这个类中
3:sleep方法和wait方法的区别?
  1)sleep方法必须传入参数,参数就是时间,时间到了就自动醒来。
    wait方法可以传入参数,也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待。
  2)sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡。
  wait方法在同步函数或同步代码块中,释放锁。

 */
class Printer2 {
    private int flag = 1;
    public  void print1() throws InterruptedException {
        synchronized (this) {
            while (flag != 1){
                this.wait();    //当前线程等待
            }
            System.out.print("黑");
            System.out.print("马");
            System.out.print("程");
            System.out.print("序");
            System.out.print("员");
            System.out.print("\r\n");
            flag = 2;
           // this.notify();   //随机唤醒单个等待线程
            this.notifyAll();
        }
    }

    public void print2() throws InterruptedException {
        synchronized (this) {
            while (flag != 2){
                this.wait();    //线程2在此等待
            }
            System.out.print("传");
            System.out.print("智");
            System.out.print("播");
            System.out.print("客");
            System.out.print("\r\n");
            flag = 3;
           // this.notify();  不适合
            this.notifyAll();
        }
    }
    public void print3() throws InterruptedException {
        synchronized (this) {
            while (flag != 3){
                this.wait();  线程3在此等待,if语句在哪里等待,就在那里起来
                              //while循环是循环判断,每次都会判断标记
            }
            System.out.print("i");
            System.out.print("t");
            System.out.print("c");
            System.out.print("a");
            System.out.print("s");
            System.out.print("t");
            System.out.print("\r\n");
            flag = 1;
            //this.notify();
            this.notifyAll();
        }
    }
}
  1. 多线程(JDK1.5的新特性互斥锁)
    (1) 同步
    使用ReentrantLock类的lock()和unlock()方法进行同步
    (2) 通信
    使用ReentrantLock类的newCondition()方法可以获取Condition对象
    需要等待的时候用Condition的await()方法,唤醒的时候用signal()方法
    不同的线程使用不同的Condition,这样就能区分唤醒时候找那个线程了
public class Demo3_ReentratLock {
    public static void main(String[] args) {
       final Printer3 p = new Printer3();
       new  Thread(){
           public void run(){
               while (true){
                   try {
                       p.print1();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       }.start();

        new  Thread(){
            public void run(){
                while (true){
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new  Thread(){
            public void run(){
                while (true){
                    try {
                        p.print3();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

    }
}
class Printer3 {
   private ReentrantLock r = new ReentrantLock();
   private Condition c1 = r.newCondition();
   private Condition c2 = r.newCondition();
   private Condition c3 = r.newCondition();

    private int flag = 1;
    public  void print1() throws InterruptedException {
       r.lock();     //获取锁
           if(flag != 1){
               c1.await();    //当前线程等待
            }
            System.out.print("黑");
            System.out.print("马");
            System.out.print("程");
            System.out.print("序");
            System.out.print("员");
            System.out.print("\r\n");
            flag = 2;
            c2.signal();
            r.unlock(); //释放锁
        }


    public void print2() throws InterruptedException {
       r.lock();
           if (flag != 2){
                c2.await();   //线程2在此等待
            }
            System.out.print("传");
            System.out.print("智");
            System.out.print("播");
            System.out.print("客");
            System.out.print("\r\n");
            flag = 3;
           c3.signal();
            r.unlock();
        }

    public void print3() throws InterruptedException {
       r.lock();
            if (flag != 3){
                c3.await(); 线程3在此等待,if语句在哪里等待,就在那里起来
                //while循环是循环判断,每次都会判断标记
            }
            System.out.print("i");
            System.out.print("t");
            System.out.print("c");
            System.out.print("a");
            System.out.print("s");
            System.out.print("t");
            System.out.print("\r\n");
            flag = 1;
           c3.signal();
            r.unlock();
        }
    }

  1. 多线程(线程组的概述和使用)
    (1) 线程组概述
    java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java运行程序直接对线程组进行控制。
    默认情况下,所有的线程都属于主线程组:
    public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
    public final String getName()//通过线程组对象获取他组的名字
    我们也可以给线程设置分组
    ThreadGroup(String name)创建线程组对象并给其赋值名字。
    创建线程对象。
    Thread(ThreadGroup?group,Runnable?target,String?name)。
    设置整组的优先级或者守护线程。
    (2) 案例演示
    线程组的使用,默认是主线程组
public class Demo4_ThreadGroup {
    public static void main(String[] args) {
        //demo1();
        ThreadGroup tg = new ThreadGroup("我是一个新的线程组"); //创建新的线程组
        MyRunnable mr = new MyRunnable();   //创建Runnable()的子类对象
        
        Thread t1 = new Thread(tg,mr,"张三"); //将线程t1放在组中
        Thread t2 = new Thread(tg,mr,"李四");//将线程t2放在组中

        System.out.println(t1.getThreadGroup().getName()); //获取组名
        System.out.println(t2.getThreadGroup().getName());


    }

    private static void demo1() {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr,"张三");
        Thread t2 = new Thread(mr,"李四");

        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();

        System.out.println(tg1.getName());  //默认是主线程
        System.out.println(tg2.getName());
    }
}
class MyRunnable implements Runnable {

    @Override
    public void run() {
        for(int i = 0; i < 1000 ; i++){
            System.out.println(Thread.currentThread().getName() + "..." + i);
        }
    }
}

  1. 多线程(线程的五种状态)
    (1) 新建
    (2) 就绪状态
    (3) 阻塞
    (4) 运行
    (5) 死亡
    在这里插入图片描述
  2. 多线程(线程池的概述和使用)
    (1) 线程池的概述
    程序启动一个新线程成本是比较高的,因为它涉及到要操作系统进行交互,而使用线程池也可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池,线程池里的每一个线性代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。
    (2) 内置线程池的使用概述
    *JDK5新增了一个Executors工厂类产生线程池,有如下几个方法
    public static ExecutorService newFixedThreadPool(int nThreads)
    public static ExecutorService newSingleThreadExecutor()
    这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程,它提供了如下方法:
    Future<?> submit(Runnable task)
    Future submit(Callable task)
    *使用步骤:
    创建线程池对象
    创建Runnable实例
    提交Runnable实例
    关闭线程池
    (3) 案例演示
    提交的是Runnable
public class Demo5_Executors {
    /*
    public static ExecutorService newFixedThreadPool(int nThreads)
    public static ExecutorService newSingleThreadExecutor()

     */
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池
        pool.submit(new MyRunnable());  //将线程放进池子里并执行
        pool.submit(new MyRunnable());
        
        pool.shutdown();        //关闭线程池
        
    }
}

  1. 多线程(多线程程序实现的方式3)
    提交的是Callable
  public class Demo6_Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池
       Future<Integer> f1 =  pool.submit(new MyCallable(100));  //将线程放进池子里并执行
        Future<Integer> f2 =   pool.submit(new MyCallable(50));

        System.out.println(f1.get());
        System.out.println(f2.get());

        pool.shutdown();        //关闭线程池
    }
}
  class MyCallable implements Callable<Integer> {
     private int num;
     public MyCallable(int num){
         this.num = num;
     }
      @Override
      public Integer call() throws Exception {
          int sum = 0;
          for(int i = 1; i <= num; i ++){
              sum += i;
          }
          return sum;
      }
  }

多线程程序实现的方式3的好处和弊端
好处:可以有返回值,可以抛出异常
弊端:代码比较复杂,所以一般不用
11. 设计模式(简单工厂模式概述和使用)
(1) 简单工厂模式概述
又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
(2) 优点
客户端不需要在负责对象的创建,从而明确了各个类的职责
(3) 缺点
这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护。
(4) 案例
动物抽象类:public abstract Animal {public abstract void eat();}
具体狗类public class Dog extends Animal {}
具体猫类public class Cat extends Animal {}
开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。

public class AnimalFactory {
   /* public static Dog creatDog() {
        return new Dog();
    }
    public static Cat createCat(){
        return new Cat();
    }*/

    //发现方法需要定义很多,复用性太差
    //改进
    public  static Animal creatAnimal(String name){
        if("dog".equals(name)) {
            return new Dog();
        }else if("cat".equals(name)) {
            return new Cat();
        }else {
            return null;
        }
    }
}

  1. 设计模式(工厂方法模式的概述和使用)
    (1) 工厂方法模式概述
    工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象创建工作由继承抽象工厂的具体类实现。
    (2) 优点
    客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期容易维护,增强系统的扩展性。
    (3) 缺点
    需要额外的编写代码,增加了工作量
    (4) 案例演示

  2. GUI(如何创建一个窗口并显示)

Graphical User Interface(图形用户接口)
public class Demo1_Frame {
    public static void main(String[] args) {
        Frame f = new Frame("我的第一个窗口");
        f.setSize(400,600);  //设置窗体大小
        f.setLocation(300,50);  //设置窗体位置
        f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        f.setVisible(true); //设置窗体可见
    }
}
  1. GUI(布局管理器)
    (1) FlowLayout(流式布局管理器)
    从左到右的顺利排列。
    Panel默认的布局管理器
    (2) BorderLayout(边界布局管理器)
    东、南、西、北、中
    Frame默认布局管理器
    (3) GridLayout(网格布局管理器)
    规则的矩阵
    (4) CardLayout(卡片布局管理器)
    选项卡
    (5) GridBagLayout(网络包布局管理器)
    非规则的矩阵
public class Demo1_Frame {
    public static void main(String[] args) {
        Frame f = new Frame("我的第一个窗口");
        f.setSize(400,600);  //设置窗体大小
        f.setLocation(300,50);  //设置窗体位置
       //f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        Button b1 = new Button("按钮1");
        f.add(b1);
        f.setLayout(new FlowLayout());  //设置布局管理器
        f.setVisible(true); //设置窗体可见
    }
}
  1. GUI(窗体监听)
public class Demo1_Frame {
    public static void main(String[] args) {
        Frame f = new Frame("我的第一个窗口");
        f.setSize(400,600);  //设置窗体大小
        f.setLocation(300,50);  //设置窗体位置
       //f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        Button b1 = new Button("按钮1");
        f.add(b1);
        f.setLayout(new FlowLayout());  //设置布局管理器
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                super.windowOpened(e);
            }
        });
        f.setVisible(true); //设置窗体可见

    }
}
  1. GUI(鼠标监听)
public class Demo1_Frame {
    public static void main(String[] args) {
        Frame f = new Frame("我的第一个窗口");
        f.setSize(400,600);  //设置窗体大小
        f.setLocation(300,50);  //设置窗体位置
       //f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        Button b1 = new Button("按钮1");
        f.add(b1);
        f.setLayout(new FlowLayout());  //设置布局管理器
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                super.windowOpened(e);
            }
        });            //窗体监听

        b1.addMouseListener(new MouseAdapter() {
            /*@Override
            public void mouseClicked(MouseEvent e) {  //单击
              System.exit(0);
            }*/
            public void mouseReleased(MouseEvent e) {  //释放
                System.exit(0);
            }
        });
        f.setVisible(true); //设置窗体可见

    }
}
  1. GUI(键盘监听和键盘事件)
public class Demo1_Frame {
    public static void main(String[] args) {
        Frame f = new Frame("我的第一个窗口");
        f.setSize(400,600);  //设置窗体大小
        f.setLocation(300,50);  //设置窗体位置
       //f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        Button b1 = new Button("按钮1");
        f.add(b1);
        f.setLayout(new FlowLayout());  //设置布局管理器
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                super.windowOpened(e);
            }
        });            //窗体监听

        b1.addMouseListener(new MouseAdapter() {
            /*@Override
            public void mouseClicked(MouseEvent e) {  //单击
              System.exit(0);
            }*/
            public void mouseReleased(MouseEvent e) {  //释放
                System.exit(0);
            }
        });
        b1.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
               // System.exit(0);
               // System.out.println(e.getKeyCode());
               // if (e.getKeyCode() == 32) {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    System.exit(0);
                }
            }
        });
        f.setVisible(true); //设置窗体可见

    }
}
  1. GUI(动作监听)
public class Demo1_Frame {
    public static void main(String[] args) {
        Frame f = new Frame("我的第一个窗口");
        f.setSize(400,600);  //设置窗体大小
        f.setLocation(300,50);  //设置窗体位置
       //f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        Button b1 = new Button("按钮1");
        Button b2 = new Button("按钮2");
        f.add(b1);
        f.add(b2);
        f.setLayout(new FlowLayout());  //设置布局管理器
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                super.windowOpened(e);
            }
        });            //窗体监听


        b1.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
               // System.exit(0);
               // System.out.println(e.getKeyCode());
               // if (e.getKeyCode() == 32) {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    System.exit(0);
                }
            }
        });

        b2.addActionListener(new ActionListener() {  //添加动作监听,应用场景是暂停视频和播放视频
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        f.setVisible(true); //设置窗体可见

    }
}
  1. 设计模式(适配器设计模式)
    (1) 什么是适配器
    在使用监听器的时候,需要定义一个类事件监听器接口。
    通常接口中有多个方法,而程序中不一定所有的都用到,但又必须重写,这很繁琐。
    适配器简化了这些操作,我们定义监听器时都要继承适配器,然后重写需要的方法即可。
    (2) 适配器原理
    适配器就是一个类,实现了监听器接口,所有抽象方法都重写了,但是方法全是空的。
    适配器类需要定义成抽象的,因为创建该类对象,调用空方法没有意义。
    目的就是为了简化程序员的操作,定义监听器时继承适配器,只重写需要的方法就可以了。
public static void main(String[] args) {
        
    }
}
interface 和尚 {
    public  void 打坐();
    public void 念经();
    public void z撞钟();
    public void 习武();
    
}
  abstract  class 天罡星 implements 和尚 { //声明成抽象的原因是不想让其他类创建本类对象,因为创建也没有意义

    @Override
    public void 打坐() {
        
    }

    @Override
    public void 念经() {

    }

    @Override
    public void z撞钟() {

    }

    @Override
    public void 习武() {

    }
}
class 鲁智深 extends 天罡星{
    public void 习武(){
        System.out.println("1");
        System.out.println("2");
    }
}

  1. GUI(需要知道的)
    (1) 事件处理
    事件:用户的一个操作
    事件源:被操作的组件
    监听器:一个自定义类的对象,实现了监听器接口,包含事件处理方法,把监听器添加在事件源上,当事件发生的时候虚拟机就会自动调用监听器中的事件处理方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值