Java两周半速成之路(第十六天)

一、网络编程

1.概述:

                 就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换

2.网络模型

 

3.网络参考模型图 

 

4.网络通信三要素

 

4.1IP地址 

InetAddress类的使用:

注意:通过API查看,此类没有构造方法,如果想要创建该类的对象,只能借助于其静态成员方法,因为其静态成员方法的返回值为InetAddress类的对象。

public static InetAddress getByName(String host)         

 确定主机名称的IP地址,传入的是ip地址,将ip地址封装成一个InetAddress对象

public String getHostName()                                            获取此IP地址的主机名

public String getHostAddress()                                        返回文本显示中的IP地址字符串

演示:

import java.net.InetAddress;

/*
    网络编程的三要素:ip地址 端口号 协议
 */
public class InetAddressDemo {
    public static void main(String[] args) throws Exception {
       // public static InetAddress getByName(String host) 确定主机名称的IP地址。
        //传入的是ip地址,将ip地址封装成一个InetAddress对象
        InetAddress inetAddress = InetAddress.getByName("192.168.1.13");
        System.out.println(inetAddress);     

        //public String getHostName()获取此IP地址的主机名。
        System.out.println(inetAddress.getHostName());

        //public String getHostAddress()返回文本显示中的IP地址字符串。
        System.out.println(inetAddress.getHostAddress());

    }
}

 

4.2端口号

 

4.3协议UDP和TCP 

 

4.4Socket 

 

 

4.5Socket机制图解

 5.UDP传输

 (1) UDP传输-发送端思路:

 

UDP传输-发送端代码演示:

package com.shujia.day18.udpdemo;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
    1:建立udp的socket服务
    2:将要发送的数据封装成数据包
    3:通过udp的socket服务,将数据包发送出
    4:关闭资源
    从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。

 */
public class SendDemo1 {
    public static void main(String[] args) throws Exception {
        //1:建立udp的socket服务
        //public DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。
        DatagramSocket ds = new DatagramSocket();

        //2:将要发送的数据封装成数据包 DatagramPacket
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        //构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
        //byte[] buf  要发送的数据的字节数组表现形式
        byte[] bytes = "我陈平安,唯有一剑,可搬山,倒海......".getBytes();
        //int length  要发送数据字节数组的长度
        int length = bytes.length;
        //InetAddress address  目标发送的ip地址的InetAddress形式对象
        InetAddress inetAddress = InetAddress.getByName("192.168.1.13");
        //int port 目标机器上应用程序的端口号
        int port = 12345;
        //创建数据包
        DatagramPacket dp = new DatagramPacket(bytes, length, inetAddress, port);

        //3:通过udp的socket服务,将数据包发送出
        //public void send(DatagramPacket p) 从此套接字发送数据报包。
        //将上面创建好的数据包进行发送
        ds.send(dp);

        //4:关闭资源
        ds.close();
    }
}

(2)UDP传输-接收端思路

UDP传输-接收端代码演示:

import java.lang.String;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
    1:建立udp的socket服务.
    2:通过receive方法接收数据
    3:将收到的数据存储到数据包对象中
    4:通过数据包对象的功能来完成对接收到数据进行解析.
    5:可以对资源进行关闭

 */
public class ReceiveDemo1 {
    public static void main(String[] args) throws Exception {
        //public DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。
        //创建Socket对象并绑定一个端口号
        DatagramSocket ds = new DatagramSocket(10086);

        //接收端需要创建一个空的数据包,接收过来的数据
        //DatagramPacket(byte[] buf, int length)
        //构造一个 DatagramPacket用于接收长度的数据包 length 。
        //byte[] buf 将来用于接收数据的字节数组
        byte[] bytes = new byte[1024];
        int length = bytes.length;       //int length  所准备的字节数组的长度
        DatagramPacket dp = new DatagramPacket(bytes, length);

        //2:通过receive方法接收数据
        //receive(DatagramPacket p)
        //程序走到这一步,会发生阻塞等待数据过来
        ds.receive(dp);//3:将收到的数据存储到数据包对象中

        //4:通过数据包对象的功能来完成对接收到数据进行解析.
        byte[] data = dp.getData(); // 用于解析数据包中接收到的数据
        int dataLength = dp.getLength(); // 获取真正接收到字节数组长度
        //将接收到的字节数组转字符串
        String info = new String(data, 0, dataLength);


        InetAddress inetAddress = dp.getAddress();
        String ip = inetAddress.getHostAddress();//获取发送段的ip地址
        String hostName = inetAddress.getHostName();//获取发送端的主机名
        System.out.println("===================================");
        System.out.println(hostName + " ip地址为:" + ip + ", 发来数据:" + info);
        System.out.println("===================================");

        //5:可以对资源进行关闭
        ds.close();
    }
}

注意:先执行接收端代码在执行输出端代码~

效果演示:

(3)练习:

      从键盘录入数据进行发送,如果输入的是886那么发送端就结束输入数据

发送端:


import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/*
    1:建立udp的socket服务
    2:将要发送的数据封装成数据包
    3:通过udp的socket服务,将数据包发送出
    4:关闭资源
    从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。

 */
public class SendDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立udp的socket服务
        //public DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。
        DatagramSocket ds = new DatagramSocket();
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);

        while (true){
            System.out.print("请输入要发送的数据: ");
            String info = sc.next();


            //2:将要发送的数据封装成数据包 DatagramPacket
            //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
            //构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
            //byte[] buf  要发送的数据的字节数组表现形式
            byte[] bytes = info.getBytes();
            //int length  要发送数据字节数组的长度
            int length = bytes.length;
            //InetAddress address  目标发送的ip地址的InetAddress形式对象
            InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
            //int port 目标机器上应用程序的端口号
            int port = 12345;
            //创建数据包
            DatagramPacket datagramPacket = new DatagramPacket(bytes, length, inetAddress, port);

            //3:通过udp的socket服务,将数据包发送出
            //public void send(DatagramPacket p) 从此套接字发送数据报包。
            //将上面创建好的数据包进行发送
            ds.send(datagramPacket);

            if("886".equals(info)){
                break;
            }
        }

        //4:关闭资源
        ds.close();
    }
}

 

接收端:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
    1:建立udp的socket服务.
    2:通过receive方法接收数据
    3:将收到的数据存储到数据包对象中
    4:通过数据包对象的功能来完成对接收到数据进行解析.
    5:可以对资源进行关闭

 */
public class ReceiveDemo1 {
    public static void main(String[] args) throws Exception {

        //public DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。
        //创建Socket对象并绑定一个端口号
        DatagramSocket ds = new DatagramSocket(12345);

        //接收端需要创建一个空的数据包,接收过来的数据
        //DatagramPacket(byte[] buf, int length)
        //构造一个 DatagramPacket用于接收长度的数据包 length 。
        //byte[] buf 将来用于接收数据的字节数组
        byte[] bytes = new byte[1024];
        //int length  所准备的字节数组的长度
        int length = bytes.length;
        DatagramPacket dp = new DatagramPacket(bytes, length);


        while (true){
            //2:通过receive方法接收数据
            //receive(DatagramPacket p)
            //程序走到这一步,会发生阻塞等待数据过来
            ds.receive(dp); //3:将收到的数据存储到数据包对象中

            //4:通过数据包对象的功能来完成对接收到数据进行解析.
            byte[] data = dp.getData(); // 用于解析数据包中接收到的数据
            int dataLength = dp.getLength(); // 获取真正接收到字节数组长度
            //将接收到的字节数组转字符串
            String info = new String(data, 0, dataLength);

            InetAddress inetAddress = dp.getAddress();
            String ip = inetAddress.getHostAddress(); //获取发送段的ip地址
            String hostName = inetAddress.getHostName(); //获取发送端的主机名

            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("================="+time+"=================");
            if("886".equals(info)){
                System.out.println("******"+hostName + " ip地址为:" + ip + "发送端停止发送了!!!!!!!******");
//                break;
            }else {
                System.out.println(hostName + " ip地址为:" + ip + ", 发来数据:" + info);
            }

        }

        //5:可以对资源进行关闭
//        ds.close();

    }
}

 

6.TCP传输

 (1)TCP传输-客户端思路

TCP传输-客户端思路代码演示:


import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

/*
    1:建立客户端的Socket服务,并明确要连接的服务器。
    2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
    3:通过Socket对象的方法,可以获取这两个流
    4:通过流的对象可以对数据进行传输
    5:如果传输数据完毕,关闭资源

 */

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
    1:建立客户端的Socket服务,并明确要连接的服务器。
    2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
    3:通过Socket对象的方法,可以获取这两个流
    4:通过流的对象可以对数据进行传输
    5:如果传输数据完毕,关闭资源

 */
public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        //构造方法:要传输服务器目标的ip地址和端口号
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        //InetAddress address 服务器的ip地址的InetAddress表现形式
//        InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
//        //int port 服务器程序所占用的端口号
//        int port = 10086;
//        Socket socket = new Socket(inetAddress, port);
        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        //该Socket对象如果成功创建,就说明已经与服务器建立连接成功!
        Socket socket = new Socket("192.168.1.13", 10086);
        System.out.println("与服务器段连接成功!" + socket);

        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        //4:通过流的对象可以对数据进行传输
        outputStream.write("真相只有一个".getBytes());
        //告诉服务器没有数据
        socket.shutdownOutput();


        //获取通道中的输入流对象
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int length = inputStream.read(bytes);
        String info = new String(bytes, 0, length);
        System.out.println("服务器给我的反馈是:" + info);

        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}

(2) TCP传输-服务器端思路

 TCP传输-服务器端思路代码演示:


import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/*
    1:建立服务器端的socket服务(ServerSocket),需要一个端口
    2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
    3:通过客户端的获取流对象的方法,读取数据或者写入数据
    4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

 */
public class ServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1:创建服务器段的Socket对象,
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);

        //2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
        //程序运行到这一步的时候,发生阻塞,会监听客户端的连接
        //返回的Socket对象,实际上可以理解为是与某一个客户端的连接通道
        //将来可以使用Socket对象中的输入流获取客户端发送的信息,或者使用输出流向客户端发送反馈
        Socket socket = ss.accept();

        //获取通道中的输入流对象
        InputStream is = socket.getInputStream();
        InetAddress inetAddress = socket.getInetAddress();
        String hostName = inetAddress.getHostName();
        String hostAddress = inetAddress.getHostAddress();
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = is.read(bytes))!=-1){
            String info = new String(bytes, 0, length);
            System.out.println(hostName + " ip地址为:" + hostAddress + ", 发来数据:" + info);
        }

        //获取通道中的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("服务器已收到! ".getBytes());

        socket.close();
        ss.close();
    }
}

 

(3)练习:

使用多台客户端与服务端建立联系,从键盘录入数据进行发送,如果输入的是886那么发送端就结束输入数据 (使用多线程)

客户端:

import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/*
    1:建立客户端的Socket服务,并明确要连接的服务器。
    2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
    3:通过Socket对象的方法,可以获取这两个流
    4:通过流的对象可以对数据进行传输
    5:如果传输数据完毕,关闭资源

 */
public class ClientDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        //构造方法:要传输服务器目标的ip地址和端口号
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        //InetAddress address 服务器的ip地址的InetAddress表现形式
//        InetAddress inetAddress = InetAddress.getByName("192.168.1.13");
//        //int port 服务器程序所占用的端口号
//        int port = 10086;
//        Socket socket = new Socket(inetAddress, port);
        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        //该Socket对象如果成功创建,就说明已经与服务器建立连接成功!
        Socket socket = new Socket("192.168.1.13", 10086);
        System.out.println("与服务器段连接成功!" + socket);
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);
        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        while (true){
            System.out.println("请输入要发送的数据: ");
            String info = sc.next();



            //4:通过流的对象可以对数据进行传输
            outputStream.write(info.getBytes());
            if("886".equals(info)){
                break;
            }
        }


        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}

 

服务端:


import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
    1:建立服务器端的socket服务(ServerSocket),需要一个端口
    2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
    3:通过客户端的获取流对象的方法,读取数据或者写入数据
    4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

 */
public class ServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1:创建服务器段的Socket对象,
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);

        //死循环监听客户端的连接,将每个客户端封装成一个线程运行
        while (true){
            Socket socket = ss.accept();
            new OneClient(socket).start();
        }
    }
}

class OneClient extends Thread{
    private Socket socket;
    private String hostName;
    private String hostAddress;
    OneClient(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //获取通道中的输入流对象
            InputStream is = socket.getInputStream();
            InetAddress inetAddress = socket.getInetAddress();
            hostName = inetAddress.getHostName();
            hostAddress = inetAddress.getHostAddress();
            System.out.println("=====用户: "+hostName+"已上线!====");
            byte[] bytes = new byte[1024];
            int length = 0;
            while ((length = is.read(bytes))!=-1){
                String info = new String(bytes, 0, length);
                if("886".equals(info)){
                    System.out.println("=====用户: "+hostName+"已离线!====");
                }else {
                    String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                    System.out.println(time);
                    System.out.println(hostName + " ip地址为:" + hostAddress + ", 发来数据:" + info);
                }

            }
        }catch (Exception e){
            System.out.println("--------------------------------------------");
            System.out.println(hostName + " ip地址为:" + hostAddress + "异常下线............");
            System.out.println("--------------------------------------------");
        }
    }
}

 

 二.类加载器

1.类的加载

 

2.类初始化时机 

 

3.类加载器概述

 

4.类加载器的作用 

 

 三.反射的基本使用

1.概述:

 

2.通过反射获取构造方法并使用

演示:


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Student {
    public int id;
    private int age;
    String name;

    public Student() {
    }

    Student(int age) {
        this.age = age;
    }

    private Student(String name) {
        this.name = name;
    }


    public void fun1() {
        System.out.println("这是公共的成员方法");
    }

    private void fun1(String name) {
        System.out.println("这是私有的成员方法" + name);
    }

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

public class StudentDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //如何获取一个类对用的Class对象
        //1、在有对象的前提下,使用getClass()方法获取
        Student student = new Student();
        Class<? extends Student> studentClass1 = student.getClass();

        //2、没有对象,只有类,通过类的属性进行获取
        Class<Student> studentClass2 = Student.class;

        //3、最常用的方式,Class类中有一个静态方法
        //public static Class<?> forName(String className) 返回与给定字符串名称的类或接口相关联的类对象。
        Class<?> studentClass = Class.forName("com.shujia.day18.FanShe.Student");

        System.out.println("===============================================================================");

/*
一个类中有三种成员:
    1、成员变量 Field
    2、构造方法 Constructor
    3、成员方法 Method
*/
        //TODO:通过反射获取构造方法并使用
        //Class类中有一个getConstructor方法
        //public Constructor<T> getConstructor(Class<?>... parameterTypes)
        //返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数。
        Constructor<?> c1 = studentClass.getConstructor();        //表示获取Student类中的无参构造方法
        System.out.println(c1);


        //获取Student类中的有参构造方法
        //获取不是public修饰的构造方法
        //getDeclaredConstructor()     可以获取所有权限构造方法
        Constructor<?> c2 = studentClass.getDeclaredConstructor(int.class);
        System.out.println(c2);


        /*
        如何使用将获取到的构造方法来创建对象呢?
        Constructor中有一个newInstance方法:
        newInstance(Object... initargs)
        使用由此Constructor对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。
         */
        Object o1 = c1.newInstance();
        System.out.println(o1);        //Student{id=0, age=0, name='null'}

        Constructor<?> c3 = studentClass.getDeclaredConstructor(String.class);
        //c3的构造方法是被private修饰的所以无法直接访问,所以要使用暴力访问,绕过检测机制
        c3.setAccessible(true);       //暴力访问,绕过检测机制
        Object  o3 = c3.newInstance("小明");
        System.out.println(o3);
        


    }
}

 

 3.通过反射获取成员变量并使用

演示:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;

public class StudentDemo1 {
    public static void main(String[] args) throws Exception {
        Class<?> studentClass = Class.forName("com.shujia.day18.FanShe.Student");

        //TODO:通过反射获取成员变量并使用
        //Class类中的getField方法          根据属性的名字获取对应的被public修饰的成员变量的对象
        Field id = studentClass.getField("id");
        System.out.println(id);
        //getDeclaredField 获取所有权限的成员变量
        Field name = studentClass.getDeclaredField("name");
        System.out.println(name);


        //getFields获取类中所有的被public修饰的成员变量
        Field[] fields = studentClass.getFields();
        System.out.println(Arrays.toString(fields));

//        //使用获取到的成员变量
        Constructor<?> c1 = studentClass.getDeclaredConstructor();      //表示获取Student类中的无参构造方法
        Object o = c1.newInstance();
        System.out.println(o);           //Student{id=0, age=0, name='null'}

        //需求:给对象o中的成员变量name进行赋值
//        public void set(Object obj, Object value)
        //将指定对象参数上的此Field对象表示的字段设置为指定的新值。
        name.set(o, "小明");
        System.out.println(o);       //Student{id=0, age=0, name='小明'}
        //如果要访问被private修饰的成员变量,依旧是暴力访问
        id.setAccessible(true);
        id.set(o,1001);
        System.out.println(o);       //Student{id=18, age=0, name='小明'}

        Field age = studentClass.getDeclaredField("age");
        age.setAccessible(true);
        age.set(o,18);
        System.out.println(o);


    }
}

 

4.通过反射获取成员方法并使用 

演示:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class StudentDemo2 {
    public static void main(String[] args) throws Exception {

        //TODO:通过反射获取成员方法并使用
        //public Method getMethod(String name, Class<?>... parameterTypes)
        //获取类中被public所修饰的成员方法
        //根据方法的名字和参数类型获取
        Class<?> studentClass = Class.forName("com.shujia.day18.FanShe.Student");
        Method fun1 = studentClass.getMethod("fun1");
        System.out.println(fun1);

        //getDeclaredMethod获取任意一个成员方法,包括私有的
        Method fun11 = studentClass.getDeclaredMethod("fun1", String.class);
        System.out.println(fun11);
        System.out.println("==================================================");

        //getMethods可以获取类中及其直接父类中的所有被public修饰的成员方法
        Method[] methods = studentClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("==================================================");

        //getDeclaredMethods可以获取本类中的所有的成员方法,包括私有的
        Method[] declaredMethods = studentClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("========================================");

        //如何调用获取到的成员方法
        Constructor<?> c1 = studentClass.getConstructor();        //表示获取Student类中的无参构造方法
        Object o = c1.newInstance();
        //public Object invoke(Object obj, Object... args)
        //在具有指定参数的方法对象上调用此方法对象表示的基础方法
        fun1.invoke(o);
        //如果是调用私有的方法,则需要暴力访问
        fun11.setAccessible(true);
        fun11.invoke(o, "张三");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值