java学习笔记第四周(二)

目录

一、异常

1、异常机制

2、异常(Exception)的概念

 2.1 异常的分析

3、异常的分类 

 3.1 Error

3.2 Exception

3.3 RuntimeException

3.4 CheckedException

4、异常的处理(重点)

4.1 异常处理方式之一:捕获异常(try-catch-finally)

4.1 异常处理方式之二:声明异常(声明异常抛出throws)

5、try-with-resource自动关闭Closable接口的资源

6、自定义异常

7、IDEA开发环境的调试试图

7.1 断点 breakpoint

二、项目:飞机大战

1、构建游戏窗口

2、使用paint方法画图形

3、自建GameUtil类:加载图片代码

4、双缓冲解决屏幕闪烁问题

5、游戏物体根类的实现

6、面向对象思想重构飞机类

7、键盘控制飞机移动

8、面向对象优化键盘控制

9、炮弹类设计

9.1 炮弹类的设计

9.2 炮弹任意角度飞行

9.3 容器对象存储多发炮弹

10、炮弹和飞机的碰撞检测

11、爆炸类

12、记录时间

13、飞机大战全代码

13.1 MyGameFrame类

13.2 Plane类

13.3 GameObject类

13.4 GameUtil类

13.5 Shell类

13.6 Explode类

13.7 Constant类


一、异常

1、异常机制

异常机制本质:

        当程序出现异常,程序安全的退出、处理完后继续执行的机制

2、异常(Exception)的概念

 2.1 异常的分析

package studyweek4;

public class 异常 {
    public static void main(String[] args) {
        System.out.println("step1");
        try {//如果没有try—catch则不会打印step2,有的话就会处理异常,并且继续往下执行程序
            int i = 1 / 0;
        }catch (Exception e){
            e.printStackTrace();//打印错误信息
        }
        System.out.println("step2");
    }
}

3、异常的分类 

 3.1 Error

Error是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时JVM(Java虚拟机)出现的问题。

例如,Java虚拟机运行错误(VirtualMachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。Error表明系统JVM已经处于不可恢复的崩溃状态中。我们不需要管他。

3.2 Exception

Exception是程序本身能够处理的异常,如:空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsException)、类型转换异常(ClassCastException)、算术异常(ArithmeticException)等。

Exception类是所有异常类的父类,其子类对应了各种各样可能出现的异常事件。通常Java的异常可分为:

1.RuntimeException运行时异常

2.CheckedException已检查异常

3.3 RuntimeException

派生于RuntimeException的异常,如被0除、数组下标越界、空指针等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。

这类异常通常是由编程错误导致的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,经常需要通过增加“逻辑处理来避免这些异常”。

最常见的异常——空指针异常:

package studyweek4;

public class 异常 {    
    public static void main(String[] args){
        String str=null;
        //遇到空指针加个判断:if(str!=null)
        System.out.println(str.charAt(0));
    }
}

3.4 CheckedException

4、异常的处理(重点)

4.1 异常处理方式之一:捕获异常(try-catch-finally)

注意:有父子关系的子类放前面 

上面过程详细解析: 

         try:try语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理。

        一个try语句必须带有至少一个catch语句块或一个finally语句块。

        catch:每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。

        常用方法,这些方法均继承自Throwable类。

                toString()方法,显示异常的类名和产生异常的原因

                getMessage()方法,只显示产生异常的原因,但不显示类名。

                printStackTrace()方法,用来跟踪异常事件发生时堆栈的内容。

        catch捕获异常时的捕获顺序:

                如果异常类之间有继承关系,在顺序安排上需注意。越是顶层的类,越放在下面,再不然就直接把多余的catch省略掉。也就是先捕获子类异常再捕获父类异常。

         finally:有些语句,不管是否发生了异常,都必须要执行,那么就可以把这样的语句放到finally语句块中。

        通常在finally中关闭程序块已打开的资源,比如:关闭文件流、释放数据库连接等。

//异常处理的经典代码(捕获异常)a.txt中内容:abc
        FileReader reader =null;
        try {
            reader =new FileReader("D:/a.txt");
            char c=(char)reader.read();//读取文件中的一个字符
            char c2=(char)reader.read();
            System.out.println(""+c+c2);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try{
                if(reader!=null){
                    reader.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}//输出ab

4.1 异常处理方式之二:声明异常(声明异常抛出throws)

注意:当父类没有进行throw抛出异常的时候,那么子类是不能抛出的,只能try-catch,因为子类声明的异常范围不能超过父类声明的范围。

5、try-with-resource自动关闭Closable接口的资源

        JAVA中,JVM的垃圾回收机制可以对内部资源实现自动回收,给开发者带来了极大的便利。但是JVM对外部资源(调用了底层操作系统的资源)的引用却无法自动回收,例如数据库连接,网络连接以及输入输出IO流等。这些连接就需要我们手动去关闭,不然会导致外部资源泄露,连接池溢出以及文件被异常占用等。

        JDK7之后,新增了“try-with-reasource”。它可以自动关闭实现了AutoClosable接口的类,实现类需要实现close()方法。”try-with-resources声明”,将try-catch-finally简化为try-catch,这其实是一种语法糖,在编译时仍然会进行转化为try-catch-finally语句。

把要打开的资源放在try()中写,就不用使用finally去关闭,编译器会自动关闭资源

6、自定义异常

在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类。

自定义异常类只需从Exception类或者它的子类派生一个子类即可。

自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。

习惯上,自定义异常类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。

7、IDEA开发环境的调试试图

进行调试的核心是设置断点。程序执行到断点时,暂时挂起,停止执行。就像看视频按下停止一样,我们可以详细的观看停止处的每一个细节。

7.1 断点 breakpoint

        程序运行到此处,暂时挂起,停止执行。我们可以详细在此时观察程序的运行情况,方便做出进一步的判断。

二、项目:飞机大战

1、构建游戏窗口

package game1;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

//Frame是awt里面的一个类,因此要import导入
public class MygameFrame extends Frame {
    public void launchFrame(){
        //初始化窗口
        this.setTitle("飞机大战");//窗口标题
        setVisible(true);//窗口是否可见
        setSize(500,500);//窗口大小
        setLocation(500,300);//窗口打开位置

        //增加关闭窗口的动作
        this.addWindowListener(new WindowAdapter() {//匿名内部类
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);//-1是异常推出程序,0是正常关闭
            }
        });

    }

    public static void main(String[] args) {
        MygameFrame mygameFrame=new MygameFrame();
        mygameFrame.launchFrame();
    }

}

2、使用paint方法画图形

@override
    public void paint(Graphics g){
        g.setColor(Color.red);
        g.drawLine(100,100,400,200);

        g.setColor(Color.green);
        g.drawRect(100,100,300,200);

        g.setColor(Color.YELLOW);
        g.drawOval(100,100,300,200);
}

3、自建GameUtil类:加载图片代码

package game1;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

/**
 * 游戏的工具类
 * */
public class GameUtil {
    //构造器私有,防止访问
    private GameUtil(){

    }

    public static Image getImage(String path) {
        BufferedImage img=null;
        URL u =GameUtil.class.getClassLoader().getResource(path);

        try {
            img= ImageIO.read(u);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return img;
    }

    public static void main(String[] args) {
        Image img=GameUtil.getImage("images/plane.png");
        System.out.println(img);
    }
}

4、双缓冲解决屏幕闪烁问题

//做了解即可,使用直接复制
    private Image offScreenImage=null;
    public void update(Graphics g){
        if(offScreenImage==null)
            offScreenImage=this.createImage(500,500);//这是游戏窗口的宽度和高度
        Graphics gOff=offScreenImage.getGraphics();
        paint(gOff);
        g.drawImage(offScreenImage,0,0,null);
    }

5、游戏物体根类的实现

package game1;

import java.awt.*;

/**
 *游戏物体的跟类
 */
public class GameObject {

    Image img;//图片
    double x,y;
    int speed;//物体移动的速度
    int width,height;//宽度和高度

    public GameObject(Image img, double x, double y, int speed, int width, int height) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.speed = speed;
        this.width = width;
        this.height = height;
    }

    public void drawMyself(Graphics g){
        g.drawImage(img,(int)x,(int)y,width,height,null);

    }
    public GameObject(){

    }

    //所有的物体都是矩形,当你获得对应的矩形的时候,我们就可以做一些相关的判断操作
    public Rectangle geiRec(){
        return new Rectangle((int)x,(int)y,width,height);
    }
}
//在paint外部声明和调用
 GameObject plane2=new GameObject(plane,100,100,3,22,33);

//在paint内部使用
    plane2.drawMyself(g);

6、面向对象思想重构飞机类

package game1;

import java.awt.*;

public class Plane extends GameObject{
    @Override
    public void drawMyself(Graphics g){
        super.drawMyself(g);

        //飞机飞行的算法可以自行设定
        x+=speed;

    }

    public Plane(Image img, double x, double y, int speed) {
        super(img, x, y, speed);
    }
}

7、键盘控制飞机移动

//prant中定义且判断
        if(left){
            p1.x-=p1.speed;
        }
        if(right){
            p1.x+=p1.speed;
        }
        if(up){
            p1.y-=p1.speed;
        }
        if(down){
            p1.y+=p1.speed;
        }
//创建内部类实现
class KeyMonitor extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {//获取按下按键
            System.out.println("按下"+e.getKeyCode());
         switch (e.getKeyCode()){
             case KeyEvent.VK_LEFT:
                 left=true;
                 break;
             case KeyEvent.VK_RIGHT:
                 right=true;
                 break;
             case KeyEvent.VK_UP:
                 up=true;
                 break;
             case KeyEvent.VK_DOWN:
                 down=true;
                 break;

         }

        }

        @Override
        public void keyReleased(KeyEvent e) {//松开按键
            System.out.println("抬起"+e.getKeyCode());
            switch (e.getKeyCode()){
                case KeyEvent.VK_LEFT:
                    left=false;
                    break;
                case KeyEvent.VK_RIGHT:
                    right=false;
                    break;
                case KeyEvent.VK_UP:
                    up=false;
                    break;
                case KeyEvent.VK_DOWN:
                    down=false;
                    break;

            }
        }
    }

8、面向对象优化键盘控制

把上述代码放在plane类中,在内部类中直接调用即可。

9、炮弹类设计

9.1 炮弹类的设计

package game1;

import java.awt.*;

/**
 * 炮弹类
 */
public class Shell extends GameObject{
    double degree;//炮弹沿着指定的角度飞行。
    public Shell(){
        x=200;
        y=200;

        degree=Math.random()*Math.PI*2;//0到2Π中随机生成角度

        width=10;
        height=10;

        speed=8;

    }

    //接下来画自己
    @Override
    public void drawMyself(Graphics g) {
        Color c=g.getColor();//记录原来颜色
        g.setColor(Color.yellow);

        g.fillOval((int)x,(int)y,width,height);//实心圆形

        g.setColor(c);//用完只后还回去颜色


        //根据算法指定移动路径
        x+=speed*Math.cos(degree);
        y+=speed*Math.sin(degree);

        //碰到边界改变方向
        if(y> Constant.GAME_HEIGHT-this.height||y<30){
            degree=-degree;
        }

        if(x<0||x>Constant.GAME_WIDTH-this.width){
            degree=Math.PI-degree;
        }
    }
}

9.2 炮弹任意角度飞行

9.3 容器对象存储多发炮弹

为了存储多发炮弹,我们通过定义一个容器ArrayList来管理这些对象。在paint方法中遍历容器中所有对象,并画出这些炮弹。

(由于现在没学,先使用数组来创建)

prant类里面调用 
for(int i=0;i<shells.length;i++){
            shells[i].drawMyself(g);
        }

//launchFrame中创建

        for(int i=0;i<50;i++){
            shells [i]=new Shell();//初始化创建50个对象
        }

10、炮弹和飞机的碰撞检测

        之前提到过,每一个物体都是一个矩形,因此碰撞检测就是判断每一个炮弹矩形和飞机矩形是否相交。

      

//画炮弹
        for(int i=0;i<shells.length;i++){
            shells[i].drawMyself(g);
            //实现碰撞检测,将所有的炮弹和飞机进行检测,看有没有碰撞
            boolean peng=shells[i].getRec().intersects(p1.getRec());
            if(peng){
                p1.live=false;
            }

11、爆炸类

package game1;

import java.awt.*;

/**
 * 爆炸类
 */
public class Explode {
    //位置
    double x, y;

    int count=1;

    static Image[] imgs = new Image[16];

    static {
        for (int i = 0; i < 16; i++) {
            imgs[i] = GameUtil.getImage("images/explode/"+(i+1)+".gif");
            imgs[i].getWidth(null);//解决懒加载问题
        }
    }

    public void drawMySelf(Graphics g) {
        if (count < 16) {
            g.drawImage(imgs[count], (int)x, (int)y, null);
            count++;
        }
    }

    public Explode(){}

    public Explode(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

12、记录时间

 //记时
    public void drawTime(Graphics g){

        Color c=g.getColor();
        Font f=g.getFont();
        g.setColor(Color.green);
        if(p1.live) {
            period = (System.currentTimeMillis()-start.getTime())/1000;
            g.drawString("坚持:1", 30, 50);
        } else{
            if(end==null) {
                end = new Date();
                period=(end.getTime()-start.getTime())/1000;
            }
            g.setFont(new Font("微软雅黑",Font.BOLD,30));
            g.drawString("最终时间"+period,190,250);
        }
        g.setColor(c);
        g.setFont(f);
    }

13、飞机大战全代码

利用数组创建炮弹目前会有报错,但是不影响运行,以后如果能改进再说  

13.1 MyGameFrame类

package game1;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;


//Frame是awt里面的一个类,因此要import导入
public class MyGameFrame extends Frame {
    Image planeImg=GameUtil.getImage("images/plane.png");//获取图片
    Image bg=GameUtil.getImage("images/bg.jpg");

    Plane p1=new Plane(planeImg,100,100,7);

    Shell[] shells = new Shell[50];

    Explode explode;
    Date start=new Date();//游戏开始的时间
    Date end;//游戏结束的时间
    long period=0;//玩了多少秒


    @Override
    public void paint(Graphics g){//g就当作是一支画笔
        //画背景
        g.drawImage(bg,0,0,500,500,null);//把图片画出来

        //画飞机
        p1.drawMyself(g);

        //画时间
        drawTime(g);


        //画炮弹
        for(int i=0;i<shells.length;i++){
            if(shells[i]!=null) {
                shells[i].drawMyself(g);
            }
            //实现碰撞检测,将所有的炮弹和飞机进行检测,看有没有碰撞
            boolean peng=shells[i].getRec().intersects(p1.getRec());
            if(peng){
                p1.live=false;

                //处理爆炸效果
                if(explode==null) {
                    explode = new Explode((int)p1.x,(int)p1.y);
                }
                explode.draw(g);
            }
        }

    }

    public void launchFrame(){
        //初始化窗口
        this.setTitle("飞机大战");//窗口标题
        setVisible(true);//窗口是否可见
        setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);//窗口大小
        setLocation(400,400);//窗口打开位置

        //增加关闭窗口的动作
        this.addWindowListener(new WindowAdapter() {//匿名内部类
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);//-1是异常推出程序,0是正常关闭
            }
        });

        new PaintThread().start(); //启动重画窗口的线程
        this.addKeyListener(new KeyMonitor());

        //初始化创建50个对象
        for(int i=0;i<shells.length;i++){
            shells[i]=new Shell();
        }

    }

/**
 * 定义了一个重画窗口的线程类。
 * 定义成内部类是为了方便直接使用窗口类的相关方法、
 */

    //使用线程内部类实现动画效果
    class PaintThread extends Thread{//继承Thread类
        @Override
        public void run(){
            while(true){
                repaint();  //内部类可以直接使用外部类的成员
                try {
                    Thread.sleep(50);  //1s=1000ms,系统睡眠50ms,1s画20次
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 内部类实现键盘的监听处理
     */
   class KeyMonitor extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            //System.out.println("按下"+e.getKeyCode());

            p1.addDirection(e);
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // System.out.println("抬起"+e.getKeyCode());
            p1.minusDirection(e);
        }
    }

    //记时
    public void drawTime(Graphics g){

        Color c=g.getColor();
        Font f=g.getFont();
        g.setColor(Color.green);
        if(p1.live) {
            period = (System.currentTimeMillis()-start.getTime())/1000;
            g.drawString("坚持:1", 30, 50);
        } else{
            if(end==null) {
                end = new Date();
                period=(end.getTime()-start.getTime())/1000;
            }
            g.setFont(new Font("微软雅黑",Font.BOLD,30));
            g.drawString("最终时间"+period,190,250);
        }
        g.setColor(c);
        g.setFont(f);
    }

    private Image offScreenImage=null;//清除屏幕闪烁问题
    public void update(Graphics g){
        if(offScreenImage==null)
            offScreenImage=this.createImage(500,500);//这是游戏窗口的宽度和高度
        Graphics gOff=offScreenImage.getGraphics();
        paint(gOff);
        g.drawImage(offScreenImage,0,0,null);
    }


    public static void main(String[] args) {
        MyGameFrame mygameFrame=new MyGameFrame();
        mygameFrame.launchFrame();
    }

13.2 Plane类

package game1;

import java.awt.*;
import java.awt.event.KeyEvent;

public class Plane extends GameObject {
    boolean left, right, up, down;//飞机方向控制

    boolean live=true;

    @Override
    public void drawMyself(Graphics g) {
        if(live) {
            super.drawMyself(g);

            //飞机飞行的算法可以自行设定
            //x+=speed;

            if (left) {
                x -= speed;
            }
            if (right) {
                x += speed;
            }
            if (up) {
                y -= speed;
            }
            if (down) {
                y += speed;
            }
        }
    }

    public Plane(Image img, double x, double y, int speed) {
        super(img, x, y, speed);
    }

    public void addDirection(KeyEvent e) {

        switch (e.getKeyCode()) {
            case KeyEvent.VK_LEFT:
                left = true;
                break;
            case KeyEvent.VK_RIGHT:
                right = true;
                break;
            case KeyEvent.VK_UP:
                up = true;
                break;
            case KeyEvent.VK_DOWN:
                down = true;
                break;
        }
    }
    public void minusDirection(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_LEFT:
                left = false;
                break;
            case KeyEvent.VK_RIGHT:
                right = false;
                break;
            case KeyEvent.VK_UP:
                up = false;
                break;
            case KeyEvent.VK_DOWN:
                down = false;
                break;
        }
    }
}

13.3 GameObject类

package game1;

import java.awt.*;

/**
 *游戏物体的跟类
 */
public class GameObject {

    Image img;//图片
    double x,y;
    int speed;//物体移动的速度
    int width,height;//宽度和高度

    public GameObject(Image img, double x, double y, int speed, int width, int height) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.speed = speed;
        this.width = width;
        this.height = height;
    }

    public GameObject(Image img, double x, double y, int speed) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.speed = speed;

        this.width=img.getWidth(null);//直接获得图片的宽和高,对比上面的方法更加方便
        this.height=img.getHeight(null);
    }

    public void drawMyself(Graphics g){//将物体在窗口中画出
        g.drawImage(img,(int)x,(int)y,width,height,null);
    }

    public GameObject(){//空构造器
    }

    //所有的物体都是矩形,当你获得对应的矩形的时候,我们就可以做一些相关的判断操作
    public Rectangle getRec(){
        return new Rectangle((int)x,(int)y,width,height);
    }
}

13.4 GameUtil类

package game1;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

/**
 * 游戏的工具类
 * */
public class GameUtil {
    //构造器私有,防止访问
    private GameUtil(){//用来被子类继承时super的

    }

    public static Image getImage(String path) {
        BufferedImage img=null;

        try {
            URL u =GameUtil.class.getClassLoader().getResource(path);
            img= ImageIO.read(u);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return img;
    }

}

13.5 Shell类

package game1;

import java.awt.*;

/**
 * 炮弹类
 */
public class Shell extends GameObject{
    double degree;//炮弹沿着指定的角度飞行。
    public Shell(){
        x=200;
        y=200;

        degree=Math.random()*Math.PI*2;//0到2Π中随机生成角度

        width=5;
        height=5;

        speed=5;

    }

    //接下来画自己
    @Override
    public void drawMyself(Graphics g) {
        Color c=g.getColor();//记录原来颜色
        g.setColor(Color.yellow);

        g.fillOval((int)x,(int)y,width,height);//实心圆形

        g.setColor(c);//用完只后还回去颜色


        //根据算法指定移动路径
        x+=speed*Math.cos(degree);
        y+=speed*Math.sin(degree);

        //碰到边界改变方向
        if(y> Constant.GAME_HEIGHT-this.height||y<40){
            degree=-degree;
        }

        if(x<0||x>Constant.GAME_WIDTH-this.width){
            degree=Math.PI-degree;
        }
    }
}

13.6 Explode类

package game1;

import java.awt.*;

/**
 * 爆炸类
 */
public class Explode {
    //位置
    double x, y;

    int count=0;

    static Image[] imgs = new Image[16];

    static {
        for (int i = 0; i < 16; i++) {
            imgs[i] = GameUtil.getImage("images/explode/"+(i+1)+".gif");
            imgs[i].getWidth(null);//解决懒加载问题
        }
    }

    public void draw(Graphics g) {
        if (count < 16) {
            g.drawImage(imgs[count], (int)x, (int)y, null);
            count++;
        }
    }

    public Explode(){}

    public Explode(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

13.7 Constant类

package game1;

/**
 * 存放相关的常量
 */
public class Constant {
    //游戏窗口的宽度
    public static final int GAME_WIDTH = 500;
    //游戏窗口的高度
    public static final int GAME_HEIGHT = 500;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值