java日常记录

java的lambda表达式和双冒号用法

双冒号:
类名::方法名

注意是方法名哦,后面没有括号“()”哒。为啥不要括号,因为这样的是式子并不代表一定会调用这个方法。这种式子一般是用作Lambda表达式,Lambda有所谓懒加载嘛,不要括号就是说,看情况调用方法。

例如

表达式:

person -> person.getAge();

可以替换成

Person::getAge

表达式

() -> new HashMap<>();

可以替换成

HashMap::new

这种[方法引用]或者说[双冒号运算]对应的参数类型是Function<T,R> T表示传入类型,R表示返回类型。比如表达式person -> person.getAge(); 传入参数是person,返回值是person.getAge(),那么方法引用Person::getAge就对应着Function<Person,Integer>类型。

容器Collection和Map

在这里插入图片描述
在这里插入图片描述

静态代码区 static{}

public class hello {
    public hello(){
        System.out.println("我是一个构造函数代码区");
    }

    {
        System.out.println("我是构造代码区");
    }
    static {
        System.out.println("我是静态的代码区");
    }
    public static void main(String[] arg){

        new hello();
        new hello();
    }
}

看输出
我是静态的代码区
我是构造代码区
我是一个构造函数代码区
我是构造代码区
我是一个构造函数代码区
有结果知道,执行顺序:静态代码区-》构造代码区-》构造函数代码区;并且静态代码只会执行次,

native关键字-本地方法

被native关键字修饰的方法叫做本地方法,本地方法和其它方法不一样,本地方法意味着和平台有关,因此使用了native的程序可移植性都不太高。另外native方法在JVM中运行时数据区也和其它方法不一样,它有专门的本地方法栈。native方法主要用于加载文件和动态链接库,由于Java语言无法访问操作系统底层信息(比如:底层硬件设备等),这时候就需要借助C语言来完成了。被native修饰的方法可以被C语言重写。

public class nativeClass {
    public native void hh();//和abstract关键字一样只有签名,没有实现
    static {
        System.loadLibrary("nativeClass");
    }
    public static void main(String[] mrg){
        new nativeClass().hh();
    }

}

在这里插入图片描述
用javac nativeClass .java之后就会出现nativeClass .class文件,java -jni nativeClass 之后就会出现一个nativeClass .h文件,表明已经被c语言重写了。
hh()方法被写成了JNICALL Java_nativeClass_hh()方法
.h文件是就是 c++里面的header file 头文件。其中定义了对函数的声明,全局变量,数据类型等

在这里插入图片描述

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class nativeClass */

#ifndef _Included_nativeClass
#define _Included_nativeClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     nativeClass
 * Method:    hh
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_nativeClass_hh
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

标识符native可以与所有其它的java标识符连用,但是abstract除外。这是合理的,因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。native与其它java标识符连用时,其意义同非Native Method并无差别,比如native static表明这个方法可以在不产生类的实例时直接调用,这非常方便,比如当你想用一个native method去调用一个C的类库时。上面的第三个方法用到了native synchronized,JVM在进入这个方法的实现体之前会执行同步锁机制(就像java的多线程。)
一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制。这些方法的实现体可以制一个异常并且将其抛出,这一点与java的方法非常相似。当一个native method接收到一些非基本类型时如Object或一个整型数组时,这个方法可以访问这非些基本型的内部

jps指令

jps 命令类似与 linux 的 ps 命令,但是它只列出系统中所有的 Java 应用程序。 通过 jps 命令可以方便地查看 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息。

如果在 linux 中想查看 java 的进程,一般我们都需要 ps -ef | grep java 来获取进程 ID。
如果只想获取 Java 程序的进程,可以直接使用 jps 命令来直接查看。
参数说明
-q:只输出进程 ID
-m:输出传入 main 方法的参数
-l:输出完全的包名,应用主类名,jar的完全路径名
-v:输出jvm参数
-V:输出通过flag文件传递到JVM中的参数
在这里插入图片描述
SuanfaStudyApplication是一个进程名字,是idea刚启动的一个web项目

jconsole +进程ID查看进程的情况

在这里插入图片描述

装箱和拆箱:

//装箱拆箱

/*
装箱和拆箱的针对对象:

基本类型:byte,int,short,long ,boolean,char,float,double
*     装箱:用基本类型对象的引用类型包装基本类型,使其具有对象的性质,比喻把int包装成Integer,

   拆箱:拆箱装箱是相反的操作,就是把类类型转化为基本类型,比喻把Integer转化为int

比喻:Integer i=2;     //装箱,此时会自动调用valueOf方法,即和 Integer i=Integer.valueOf(2)等同

           int j=i;      //拆箱  ,此时把类类型转化为基本类型,此时调用了intValue方法。注意int j=100不是拆箱

           类似的拆箱还有i++;因为类类型不能直接参与加减乘除运算,必须转化为基本类型才可以

有两种自动装箱过程
第一种 (-128~127)之内 调用相应包装类的valueOf()
例如:Integer i = 12; Integer a = 23;
这些过程由JDK 编译器自动装箱完成的 即 会自动调用 Integer.valueOf() 方法
上面实际为: Integer i = Integer.valueOf(12); Integer a = Integer.value(23);
其实在该范围之内,底层中是通过数组存的值,当在该范围内再一次定义相同大小的值,如12,JDk它会在相应的数组里找到该值是不是存在,如存在则数组里存有12,则把它的引用给该对象,所以它们的地址是相等的.
例如:Integer a = 20; Integer b = 20;
System.out.println(ba);//true
第二种 不在(-128~127)之内 直接 new 对象
例如: Integer i = 200; Integer a = 200;
实际为: Integer i = new Integer(200); Integer a = new Integer(200);
System.out.println(i
a);//false
注意:只要他们的实际值相同(基本类型数值) ,则它们的hashCode相等
二.自动拆箱的过程
自动拆箱是指将包装类的类型直接赋值给对应的基本数据类型的变量;
例如: Integer i = 100; int a = i;
实际上底层调用了相应的xxxValue()方法: Integer i = 100; int a = i.intValue();
在包装类进行与基本数据类型比较时 ,会存在自动拆箱的操作。所以int和Integer只要值相同那么就相等true;
Integer in = 400;
int i = 400;
// 当包装类和对应的基本类型在运算的时候会进行自动拆箱
System.out.println(in == i);//true
*/

  @Test
    public void zhuang_xiang_chai_xiang(){
        Integer t1 = 100;
        Integer t2 = new Integer(100);//new操作不涉及到装箱的操作
        Integer t3 = 100;
        int t4=100;

        Integer s1 =200;
        Integer s2 = new Integer(200);
        Integer s3 = 200;
        int s4 = 200;
        int s5=200;
        String string1="abc";
        String string2=new String("abc");
        String string3="abc";
        System.out.println(t1==t2);//false
        System.out.println(t1==t3);//true
        System.out.println(t3==t2);//false
        System.out.println(t2==t4);//true
        System.out.println(t4==t1);//true
        System.out.println(s1==s2);//false
        System.out.println(s3==s1);//false
        System.out.println(s3==s2);//false
        System.out.println(s3==s4);//true
        System.out.println(string1==string2);//false
        System.out.println(string1==string3);//true

        System.out.println("=="+s1.equals(s3));//true
        System.out.println(s4==s5);//true
    }
}

看了深入理解Java的P42后由符号引用和直接引用衍生的问题有java类生命周期和常量池技术

首先运行常量池是方法区的一部分,(方法区存储已被虚拟机加载的类信息,静态变量,常量,即时编译器编译后的代码等数据),一个class文件有很多信息的,版本信息,字段,方法,接口等描述信息。。那么运行常量池当然存储的是常量。
符号引用就是用符号字符来引用的,直接引用的就是不用符号引用的,比如:System.out.println(“hello”);hello就是一个直接引用;
比如:
public interface inter{
public static string s=“abc”;
public static int n=new Random().nextInt();
}

public class T{
private string str=inter.s;
private int n=inter.n;
}

对于T这个类他有两个常量,第一个常量str是在运行前就知道的常量,还有一个是在运行后才知道的常量n,(这就是编译时常量和运行时常量);inter.s对于T类的str来说就是一个符号引用,但是,str还是存储在T类的一个虚拟机栈的,而inter.n算是一个“直接引用”,在编译时就把“hello”字符串放入了常量池(参考博客

java类的生命周期:在JVM中,类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。其中验证 ,准备 , 解析 统称为连接,而解析阶段即是虚拟机将常量池内的符号引用替换为直接引用的过程;

1.符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

2.直接引用:
直接引用可以是
(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
(3)一个能间接定位到目标的句柄
直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。

主意啊::在这里一定要和虚拟机栈存储的引用类型(reference)类型区别开来(纠结了很久),两者虽说都是引用,但是一个是常量引用,也就是上面解析了一大堆的符号引用,例如一个类里面的private string name;这个name就是一个存在虚拟机栈的。上面的T类str,n也是存在虚拟机栈的。

引用和常量池技术的应用以及.intern()函数

public class Test{
public static void main(String[] args){
//s1,s2分别位于堆中不同空间
String s1=new String(“hello”);
String s2=new String(“hello”);
System.out.println(s1s2);//输出false
//s3,s4位于池中同一空间
String s3=“hello” String s4=“hello”;
System.out.println(s3
s4);//输出true
}
}

个人理解:new String(“hello”);这句话编译-》运行执行的操作:编译时把hello字符串放入到常量池里面,并且在虚拟机栈开辟一个指向String堆空间的名字为s1的栈空间名,运行时就在Java堆里面new一个空间对象,从常量池里面把hello字符串赋值到这个开辟的堆空间里面,s1指向这里,同理s2也是如此,那么s2和s1就是不一样的,指向的空间不一样,s3和s4都是指向常量池里面的"hello";所以是一样相等的。这个和包装类Integer n1=127,Integer n2=127,n1==n2输出是一样的,都是指向常量池的东西,但是如果超出了范围,如Integer n3=128,Integer n4=128;那就是不一样的了,涉及到装箱的操作,自动执行new操作;(谨记只有基本类型才有拆箱装箱。。。)

在拆箱和装箱的时候之前有一个问题:
Integer n1=127;
Integer n2=127;
System.out.println(n1==n2)输出为什么是false,而当改为128的时候就是错误的;这个是因为 Java默认把-128~127的值放入了常量池,所以在n1,n2装箱的时候会首先到常量池里面找,找到了就只向他就指向他。所以在这个范围内的都是指向常量池的同一个地方;所以相等true,而当超出这个范围后的装箱就要到Java堆上new了,那为什么无论什么时候int 和Integer只要值相等都是true,这个是因为Integer会自动拆箱转换为int再去比较。。所以一直都是相等的。(Java里面除了八大基本数据类型外其他都是引用类型,八大基本数据类型的封装类也是引用类型)
java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。。。这些类对象的值如果在-128~127则存在常量池

String对象.intern()函数是把这个字符串的值复制到常量池里面然后返回这个常量池的引用。
在同包同类下,引用自同一String对象.
在同包不同类下,引用自同一String对象.
在不同包不同类下,依然引用自同一String对象.
在编译成.class时能够识别为同一字符串的,自动优化成常量,所以也引用自同一String对象.
在运行时创建的字符串具有独立的内存地址,所以不引用自同一String对象.
String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,
如果有则返回一个引用,没有则添加自己的字符串进入常量池,注意:只是字符串部分。
所以这时会存在2份拷贝,常量池的部分被String类私有并管理,自己的那份按对象生命周期继续使用。
返回字符串对象的规范化表示形式
一个初始值为空的字符串池,它由类 String 私有地维护。
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串引用。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。
它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
所有字面值字符串和字符串赋值常量表达式都是内部的。
------------------------------------代码演示补充-------------------------------------
String s0= “java”;
String s1=new String(“java”);
String s2=new String(“java”);
s1.intern();
s2=s2.intern(); //把常量池中"java"的引用赋给s2
System.out.println( s0s1);//false “ intern返回的引用没有引用变量接收~ s1.intern();等于废代码.”
System.out.println( s0
s1.intern() );//true
System.out.println( s0s2 );//true
------------------------------------代码演示补充-------------------------------------
String s1=new String(“java”);
String s2=s1.intern();//s1 检查常量池,发现没有就拷贝自己的字符串进去
//s2 引用该字符串常量池的地址
System.out.println(s2 == s1);//false
System.out.println( s2
s1.intern());//true
System.out.println( s1==s1.intern());// false

更多常量池技术和引用的详细参考

Jdk自带的反编译器(javap反编译指令)

javap -c -l -private 反编译的文件名称.class

-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath 指定查找用户类文件的位置
-bootclasspath 覆盖引导类文件的位置

C:\Users\10792\Desktop\java编译>javap -p T.class
Compiled from "T.java"
public class T {
  public static int tint;
  public T();
  static {};
}

C:\Users\10792\Desktop\java编译>javap -l -p -c T.class
Compiled from "T.java"
public class T {
  public static int tint;

  public T();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 1: 0

  static {};
    Code:
       0: iconst_0
       1: putstatic     #2                  // Field tint:I
       4: return
    LineNumberTable:
      line 2: 0
}

C:\Users\10792\Desktop\java编译>

Java迭代器iterator

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能单向移动:

(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

(2) 使用next()获得序列中的下一个元素。

(3) 使用hasNext()检查序列中是否还有元素。

(4) 使用remove()将迭代器新返回的元素删除。

例如
  
在这里插入图片描述

public static void main(String[] args) {
        List<String> lst = new ArrayList<String>();
        lst.add("aaa");
        lst.add("bbb");
        lst.add("ccc");
        lst.add("ddd");
        lst.add("eee");
        lst.add("fff");
        Iterator<String> iterator = lst.iterator();
        //iterator.hasNext()如果存在元素的话返回true
        while(iterator.hasNext()) {
            //iterator.next()返回迭代的下一个元素
            System.out.println(iterator.next());
        }
    }

异常错误继承的关系图

Throwable类又继承Object类
在这里插入图片描述

Java方法参数的最后一个参数类型后加三个点

java1.5引入。"…“必须是方法的最后一个形参,表示多个(0,1,2,…)参数,类似数组参数,使用数组传实参。
但是与数组参数又有区别,”…"表示可变长参数(多个参数),数组参数只是一个参数。详见下面举例:

public class Test {
	void t1(String... a) {
		System.out.println("t1");
		for (String s : a)
			System.out.printf(" " + s);
		System.out.println();
	}
 
	void t2(String[] a) {
		System.out.println("t2");
		for (String s : a)
			System.out.printf(" " + s);
		System.out.println();
	}
 
	public static void main(String[] args) {
		String a[] = { "a", "b", "d", "e", "f", "g" };
		Test t = new Test();
		t.t1(a);
		t.t2(a);
		// 区别
		t.t1();// 可不传
		// t.t2();//必须传参数,否则报错
		t.t1("1", "2", "3", "4");// 也可以一个一个的传,t2则不可以
	}
}

t1
 a b d e f g
t2
 a b d e f g
t1

t1
 1 2 3 4

字节类型和序列化和反序列化

序列化: 就是将内存中的对象转换为字节序列,方便持久化到磁盘或者网络传输。
对象序列化过程可以分为两步:
第一: 将对象转换为字节数组
第二: 将字节数组存储到磁盘

反序列化 就是将字节序列转换为内存中的对象
可以是文件中的,也可以是网络传输过来的

作用:
对象的序列化和反序列化主要就是使用ObjectOutputStream 和 ObjectInputStream

字节类型:
基本类型:byte 二进制位数:8
2、包装类:java.lang.Byte
3、最小值:Byte.MIN_VALUE=-128
4、最大值:Byte.MAX_VALUE=127

(char 和 short是16位的,int 和float是32位的,double和long是64位的)

byte a = 127;
a+=1;
System.out.println(a);
结果正好是-128

由此我们可以看出来二进制从 00000000 到01111111到10000000到 11111111

即 十进制从 0 到 127 到 -128 到 -1。

private static final long serialVersionUID=。。。。。的作用

这是在类加载阶段的准备阶段进行赋值的,因为是final…

实现java.io.Serializable这个接口是为序列化,serialVersionUID 用来表明实现序列化类的不同版本间的兼容性。如果你修改了此类, 要修改此值。否则以前用老版本的类序列化的类恢复时会出错。

通配符?和泛型(类,接口,方法)

通配符常用的有:T,E,K,V,?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:

?表示不确定的 java 类型,? 无界通配符

T (type) 表示具体的一个java类型

K V (key value) 分别代表java键值中的Key Value

E (element) 代表Element

在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义

  1. <? extends Object>代表上边界限定通配符,?是Object的一个子类,
  2. <? super Object>代表下边界限定通配符。?是object的一个父类

泛型类:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;

public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
    this.key = key;
}

public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
    return key;
}

}

泛型接口:

//定义一个泛型接口
public interface Generator<T> {
    public T next();
}

实现泛型接口的时候:
/**

  • 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
  • 即:class FruitGenerator implements Generator{
  • 如果不声明泛型,如:class FruitGenerator implements Generator,编译器会报错:“Unknown class”
    */
    class FruitGenerator implements Generator{
    @Override
    public T next() {
    return null;
    }
    }

泛型方法的定义有点特殊:这个方法的所在类可以没有通配符,但是这个泛型方法的返回类型前面不许有一个这个泛型的声明才可能被方法体使用:这个通配符还可以作为参数列表使用
2 泛型方法的语法格式为:

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

{

//方法体...

}

public class PageUtil {

    public static <T extends List> PageResultDto getPageResultDto(T list) {
        PageInfo pageInfo = new PageInfo(list);
        PageResultDto pageResultDto = new PageResultDto();
        pageResultDto.setList(pageInfo.getList());
        pageResultDto.setPages(pageInfo.getPages());
        pageResultDto.setNextPage(pageInfo.getNextPage());
        pageResultDto.setPrePage(pageInfo.getPrePage());
        pageResultDto.setTotal(pageInfo.getTotal());
        return pageResultDto;

    }

}

4种访问方式

java的访问权限有下面四种:
public–都可访问(公有)
protected–包内和子类可访问(保护)
不写(default)–包内可访问 (默认)
private–类内可访问(私有)
不写任何的类型默认的就是default;

StringBuider和StringBuffer,String的区别

为什么要StringBuffer(在大量的字符串拼接的时候大量兰妃内存)
在这里插入图片描述
为什么要StringBuider?
StringBuffer的拼接方法是加了syschronized的,但是又因为字符串拼接一般不会跨线程,因此加syschrinized是多余的,所以有了StringBuilder类,没有加syschronized,降低开销,但是不保证线程安全

package com.lqh.javastudy.StringBuilderAndStringBuffer;

/**
 * 2 * @Author: 刘钦华
 * 3 * @Date: 2019/11/11 15:53
 * 3 * @Dscription
 */
public class main {

    public static void main(String[] strings){
        StringBuffer stringBuffer=new StringBuffer("dddd");
        String string="abc";
        String string02="abc";
        /*
        * 采用字面值的方式创建一个字符串时,
        * JVM首先会去字符串池中查找是否存在"abc"这个对象,如果不存在,
        * 则在字符串常量池中创建"abc"这个对象,
        * 然后将池中"abc"这个对象的引用地址返回给"abc"对象的引用s1,
        * 这样s1会指向字符串常量池中"abc"这个字符串对象;
        * 如果存在,则不创建任何对象,直接将池中"abc"这个对象的地址返回,
        * 赋给引用s2。因为s1、s2都是指向同一个字符串池中的"abc"对象,
        * 所以结果为true。
         */
        System.out.println(string==string02);//true
        
        //这个相加之后这个string在堆(常量池)的地址变了,在另一块空间存储相加的结果
        string=string+"andHi";
        System.out.println();
        //相加之后堆的地址空间没有变,在原来的空间连续存储
        stringBuffer=stringBuffer.append("Andhi");

        //这个和StringBuffer的区别:stringBuilder类的拼接函数没有加sychronized
        //所以StringBuilder是线程不安全的
        StringBuilder stringBuilder=new StringBuilder("dddd");


    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值