Java学习笔记(10月汇总):

Java学习笔记(10月汇总):

十月份忙着赶各种宣讲会,三天打鱼两天晒网地写了五百道JAVA笔试题,粗略总结了一点笔记,如下:

JAVAWeb:

  • ·J2EE中,当把来自客户机的HTTP请求委托给servlet时,会调用HttpServlet的service方法,其中根据method判断使用doGet还是doPost。Init方法则是在初始化阶段就调用。
  • ·Servlet是线程不安全的,在其中可能会定义共享的类变量,在并发多线程访问的情况下可能会造成成员变量的修改而引起错误。
    ·Socket相关:
  • ServletSocket是服务端绑定port接口,绑定在特定端口上,用来监听端口,构造时只需提供端口号而不需提供ip(默认已经是本地)。Socket是创建客户端连接访问主机的socket流,构造时需要提供ip+端口号。
  • ·JSP的9大内置对象与对应获取方式:
    request -> Service方法中的req参数
    response -> Service方法中的resp参数
    session -> request.getSession
    out -> response.getWritter
    page -> this
    application -> getServletContext
    exception -> Thorwable
    pageContext -> PageContext
    config -> getServletConfig
    一对请求响应,一个输出流,四个上下文环境,一个错误,一个配置信息
  • ·Servlet的生命周期:
    1.初始化阶段init(),仅执行一次,负责在装载servlet时初始化servlet对象、servletConfig对象
    2.运行阶段service(),核心方法,根据method分配doGet与doPost,同时在调用两者时构造servletRequset和servletResponse作为请求与响应参数
    3.销毁阶段destroy(),停止servlet,释放资源

异常:

  • ·运行时异常(RuntimeError)表示程序在运行时出现的错误,在编译时无法发现,程序员也无法事先利用try-catch块进行捕获。
  • ·JAVA中try\catch\finally\return的执行顺序:
    1.当只有try、catch、finally时:
    ①当try块中代码无误时,执行try块中代码,跳过catch块中代码,最后执行finally块中代码;
    ②当try块中出现异常时,执行异常前代码,跳过异常后代码,执行catch块中代码。
    2.当try、catch、finally、return语句同时出现时:
    ①当try中出现了return语句:
    程序先执行完毕return前代码,而后程序保存当前状态,执行finally块中代码,而后再返回原状态进行返回。
    注:这时候的保存状态类似于JAVA的形参传入机制,程序保存当前对象的引用,当finally块中对对象引用进行更改时,最后的返回结果不会改变,如:
Int i = 0;
try{
	i=1;
	return i;
}catch(Exception e){
...
}finally{
	i=2;
}

如上代码最后return的i的值为1,因为在finally中是对于对象引用的更改,而当finally块中执行的是对于对象属性的更改,如list.add(1)时,返回值会随着finally块中的更改而发生变动。
②当catch块中出现了return语句:
与try块中的情况类似,程序会先执行return前的代码,然后保存此时的对象状态,执行finally中的代码,最后再return先前保存的结果。
③当finally块中出现了return语句:
当在finally块中有return的时候,try\catch中的return会失效,在执行完前部分的return后,跳到finally块执行其代码,并在执行完了finally块中代码后,不跳回原状态返回而是结束。这种写法会导致编译器警告,因为会破坏程序的完整性,并不推荐在finally中写return

  • ·如果finally块中有return语句的话,它将覆盖掉函数中其他return语句。
    Eg:
public class Demo{
 public static void main(String args[]){
   int num = 10;
   System.out.println(test(num));
}
public static int test(int b){
   try
   {
    b += 10;
    return b;
   }
   catch(RuntimeException e)
   {
   }
   catch(Exception e2)
   {
   }
   finally
   {
    b += 10;
    return b;
   }
  }
}

如上代码输出的结果为30而不是20。

  • ·System.err.println:
    System.err.prinln是指错误定向输出,只能输出在屏幕中,不能进行重定向,不具有缓存(与System.Out.println不同),立刻输出,故当与标准输出搭配时,总输出结果可能与逻辑代码顺序不符。err的输出在eclipse中是红色的。

运算符:

  • ·JAVA中的运算排序:
    口诀:淡云一笔安洛三幅 单目运算>算术运算>移位运算>比较关系>按位运算>逻辑运算>三目运算>赋值运算
    具体:
    单目运算:+、-、++、–
    算术运算:+、-、*、/、%
    移位运算:>>、<<
    比较运算: >、<、==、!=
    按位运算:&、|、~、^
    逻辑运算:&&、||
    三目运算:expression?expression?expression
    赋值运算:=

  • · ++、- -运算符优先级大于+、-:
    Eg:
    a- - -b应理解为(a- -)-b

  • · &&与||运算符在判断完前部分为false后不会进行后半部分的判断,而&与|会。

  • ·++x与x++的差别在于,++x的优先度更高,会在本行运算前完成增值,而x++则是在运算后进行增值。
    关键字:

  • ·JAVA8中新增的lambda表达式:
    为了简化匿名类繁杂写法而引入,原本需要多行代码的程序现在只需一行含有lambda表达式的代码即可实现功能。最简单的例子是继承runnable接口新增线程。
    在JAVA8——出现lambda表达式之前,代码如下:

new Thread(new Runnable(){
@Override
	public void run(){
		System.Out.println(“i’m a new thread before java 8);
	} 
}).start();

在有了lambda表达式后,如上代码可以改为:

new Thread( () -> System.Out.println(“i’m a new thread after java 8);).start();

同理,对于GUI中的事件监听,也可以将代码量缩减,先前代码为:

jbutton.addActionListener(new ActionListener(){
	@Override
	public void actionPerformed(ActionEvernt e){
		System.Out.println(“i’m listening before java8”);
	}
});

如上代码在java8后可以缩减为:

jbutton.addActionListener( () -> System.Out.println(“i’m listening after java8”););

或者:

jbutton.addActionListener( () -> {
System.Out.println(“i’m listening after java8”);
});

JAVA8中的lambda表达式由三个部分组成:1)一个括号和其内部用于分割参数的逗号;2)一个箭头->;3)方法体,可以使表达式和语法块。语法如下:

(parameters) -> expression
(parameters) -> {statements;}`
  • ·内联函数:
    普通函数的调用过程:存放函数的内存地址往往与当前执行的代码块地址不同,调用时程序会将执行顺序转移到函数的内存地址,待执行完毕后再跳回先前执行的地址,这样的跳转操作要求在转移前保存当前状态并记忆当前地址,转回后要恢复现场,即常说的出入栈,这样的设计让程序在调用函数的时候出现了一定的时间和空间上的开销,于是,对于代码量不大而又需要频繁调用的函数来说,这样的性价比很低。
    解决这个问题的办法就是内联函数,在JAVA程序中用final关键字修饰函数使其成为内联函数,这样一来在程序编译时,编译器就会自动将内联函数的调用表达式替换为内联函数的函数体,这样减少了执行块跳转消耗的问题,但是增加了代码量,出现了空间开销,所以适合函数代码少且须频繁调用的函数。
    Eg:
public final void method1() {     
   //TODO something     
} 

因此,内联函数也被称为在线函数或编译时展开函数,特点有:①提升效率。②占用更多空间,编译器直接将内联函数扩展开,调用多复制品就多,因此更占用内存。③JAVA中不需要额外关注,JVM会进行自动优化。

  • ·synchronized与volatile:
    1.volatile是线程同步的轻量级实现,性能优于synchronized关键字,但是volatile只能修饰变量而synchronized关键字可以修饰方法及代码块。synchronize在java1.6之后进行了性能优化,执行效率有所提升,实际是开发中使用synchronized关键字的频率会高些。
    2.多线程访问volatile时不会发生阻塞,而synchronized关键字可能会发生。
    3.volatile关键字能保证数据可见性,但不能保证数据的原子性;synchronized关键字则两者都能保证。
    4.volatile关键字主要用于解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。
    5.synchronized: 具有原子性,有序性和可见性(三个都有);volatile:具有有序性和可见性(缺一个原子性)
    ·JAVA访问控制符:

  • java类访问控制关键字
    作用域|当前类|同包|子类|其他|
    ---------|---------|------|------|-------
    |Public|√|√|√|√|
    |Protected|√|√|√×|
    |Default|√ |√|×|×|
    |Private|√ ×|×|×|

  • ·JAVA中的switch语句中每个分支如果不加上break语句则不会停止,会继续执行当前分支后的每一分支,与golang不同。

  • ·transient是一个空接口,起标记作用,被transient修饰的变量不能被序列化,可以用来阻止变量持久化。

  • ·final修饰符的作用:
    1.修饰变量,变量的引用地址不可变,但是地址中的内容可变
    2.修饰方法,方法不可以被重写,但是可以被重载
    3.修饰类,类不可以被继承
    ·do-while循环不一定可以写成while循环的格式,因为前者一定会执行一次循环体,而后者不一定

  • ·null:
    null本身是一个关键字,可以用来表示一个不确定的对象,因此可以将null赋值给一个引用类型的变量,但不可以赋值给一个基本类型变量(int)。null本身不是对象,也不是object的实例。null可以被强制转换为任何类型(注意,是转换为任意类型而不是任意类型的对象),于是可以通过其来执行类静态方法。

public class TestClass {
   private static void testMethod(){
        System.out.println("testMethod");
   }
   public static void main(String[] args) {
        ((TestClass)null).testMethod();
   }
}

如上代码可以被正确编译并执行,因为testMethod为TestClass内部静态方法,属于类而非对象,null被强转为TestClass类(!),因此可以执行其静态方法。

  • ·for循环结构:
    for(初始化语句; 布尔表达式; 更新语句) {
    正文过程;
    }

继承:

  • ·接口与接口之间关系为继承,接口之间支持多继承
    在这里插入图片描述
  • ·当父类没有无参的构造函数时,子类需要在自己的构造函数中显式调用父类的构造函数,否则编译错误。
    Eg:
class Person {
    String name = "No name";
    public Person(String nm) {
        name = nm;
    }
}
class Employee extends Person {
    String empID = "0000";
    public Employee(String id) {
        empID = id;
    }
}
public class Test {
    public static void main(String args[]) {
        Employee e = new Employee("123");
        System.out.println(e.empID);
    }
}`

如上代码编译无法通过,应在public Employee(String id)中加上super(“nm”);
如果子类构造器没有显示地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。如果超类没有不带参数的构造器,并且在子类的构造器中有没有显示地调用超类的其他构造器,则Java编译器将报告错误。使用super调用构造器的语句必须是子类构造器的第一条语句。

  • ·java.lang包中不能被继承的类:
    public final class Byte
    public final class Character
    public static final class Character.UnicodeBlock
    public final class Class
    public final class Compile
    public final class Double
    public final class Float
    public final class Integer
    public final class Long
    public final class Math
    public final class ProcessBuilder
    public final class RuntimePermission
    public final class Short
    public final class StackTraceElement
    public final class StrictMath
    public final class String
    public final class StringBuffer
    public final class StringBuilder
    public final class System
    public final class Void

多态:

  • ·java中可以有多个重载的main方法,只有public static void main(String[] args){}是函数入口
  • ·重载指一个类中可以同时定义许多同名的方法,这些方法参数类型、个数各不相同,传回值也可以各不相同。
  • ·多态中,super()字段表示父类的对象,所以在子类中用super访问无法直接使用的父类成员与方法

抽象:

  • ·JAVA中接口与抽象类差别(必考):
    1.抽象类可以有构造方法,接口中不能有构造方法
    2.接口中的方法,永远只能被public修饰,而抽象类中可以存在私有方法,但同时接口中也可以有default、static方法,不必须是抽象的
    3.单继承多实现
    4.抽象类可以存在普通属性、方法;而接口中只能存在常量,对于定义的变量,在编译的时候后自动加上public static final
    5.接口可以多继承接口(!)

初始化:

  • ·类中的变量(全局变量)会初始化赋值,方法内的变量(局部变量)不会进行初始化赋值
    Eg:
public class Test {
		public static void main(String args[]) {
			int a = 10;
			int b;
			int c;
			if (a > 50) {
				b = 9;
			}
			c = b + a;
		}
	}`

结果:编译错误

  • ·JAVA代码中出现双引号”XXX”的,除非常量池中已经存在,否则都会被创建对象并放进字符串常量池中。
    Eg:
String s = new String("xyz");

如上代码可能创建一个或两个StringObject,一个创建在常量池中(除非已经存在),另一个创建在堆中。

  • ·JAVA中类的初始化执行步骤(必考):
    父类的静态变量、静态初始化块(static {})按照代码顺序依次执行->子类静态变量、静态初始化块按照代码顺序依次执行->父类非静态变量、非静态初始化块({})按照代码顺序依次执行->父类构造方法->子类非静态变量、非静态初始化块按照代码顺序依次执行->子类构造方法。

基础类型:

  • ·char < short < int < float < double 不同类型运算结果类型向右边靠齐。
  • ·Java中涉及到byte、short和char类型都可以强制转化为int,其中(int)char强转为ASCII码
  • ·对于byte、short、char型,被赋值为整数时,默认情况下会被提升转型为int,而被final修饰的则不会:
    Eg:
byte b1=1,b2=2,b3,b6,b8;
final byte b4=4,b5=6,b7;
b3=(b1+b2);  /*语句1*/
b6=b4+b5;    /*语句2*/
b8=(b1+b4);  /*语句3*/
b7=(b2+b5);  /*语句4*/

如上代码中,语句1、3、4将会报编译错误,原因:
语句1:b1、b2被自动转型为int,而b3没有被赋值,仍为byte,所以应该强转型b3=(byte)(b1+b2)
语句3:b4加了final关键字,不会被转型,而b1被转为int,因此需要改为b8=(byte)(b1+b4)
语句4:理由同上,同时因为b7被final修饰,不能被赋值。

  • ·在String的toLowerCase()中,最后会返回new String,所以(”ADMIN”.toLowerCase()==”admin”)==false。
  • ·JAVA中基础类型占用字节数:
    byte:1 byte 8 bit
    char:2 byte 16 bit
    int:4 byte 32 bit
    short:2 byte 16 bit
    long:8 byte 64 bit
    double:8 byte 64 bit
    float:4 byte 32 bit

数据结构:

  • ·ArrayList 在无参初始化时,默认创建长度为10的列表,而后每次扩容大小为1.5倍;在指定长度初始化时,创建时直接分配大小,不涉及扩容过程。
  • ·HashMap:
    HashMap的存储是无序的,其底层是用数组+链表所实现的,对于每一个key,需要计算其哈希值,通过比对哈希值确定顺序,因此HashMap中的元素是根据key的哈希值排序而不是根据插入顺序,一般情况下认为其是无序的。
    HashMap中允许且仅允许一条null键,当HashMap接收到了null键,其将当前键值对存放在链表的表头。而Hashtable(注意table是小写)中即不允许null键也不允许null值,否则运行时会报NullPointer错误。
  • ·HashMap与Hashtable:
    1.两者在底层都实现了Map接口,但在继承时,HashMap继承了AbstractMap而Hashtable继承了Dictionary
    2.HashMap在缺省状态下是非同步的,而Hashtable是synchronized的
    3.Hashtable中null不允许出现在键或值中;而在HashMap中,允许出现唯一null键与多个null值
    4.两者遍历方式在底层实现上方式不同,尽管HashMap和Hashtable都使用了Iterator,但是后者由于历史原因在遍历时还是会使用Enumeration方式
    ·Arrays.asList()返回的不是java.util.ArrayList,而是java.util.Arrays.ArrayList

多线程:

  • ·线程堵塞方法:
    1.sleep()方法:
    在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。传入参数为时间长度。
    2.wait()方法:
    在其他线程调用notify或notifyAll方法前,该线程保持等待状态。调用时,线程会释放其所占有的“锁标志”,从而使别的线程有机会抢占该锁。当前线程必须拥有当前对象锁,否则会抛出IllegalMonitorStateException异常,同理,唤醒时调用notify或notifyALL方法的线程也应该拥有当前对象锁。
    3.yield()方法:
    Yield方法暂停当前正在执行的线程对象,并使其回到可执行状态,意味着执行了yield()的线程很可能马上又被执行。所以其一般作用是使同优先级或更高优先级线程拥有执行机会。
    4.join()方法:
    使当前运行的线程等待,释放当前运行程序的锁,其底层调用的是wait()。
  • ·守护线程(Daemon Thread):
    守护线程又被称为后台线程,指在程序运行时在后台提供通用服务的线程,最常见的守护线程是GC垃圾回收线程。其与用户线程相对应,当程序中所有的用户线程都结束之后,程序也就终止了,同时会终止所有进程中的守护线程,因为没有需要被提供服务的对象了,守护线程也没有存在意义。
    注意点:
    1.thread.setDaemon(boolean)方法可以设置当前线程作为守护线程,但必须在thread.start()之前设置,否则会报IllegalThreadStateException,因此不能将正在运行的常规线程设置为守护线程。
    2.在Daemon线程中产生的新线程也将会是守护线程。
    3.守护线程永远不会去访问固有资源,如文件、数据库,因为它会在任何时候甚至一个操作的中间发生中断。
  • ·在多线程中,一个线程调用start(),则会先执行主线程,并将当前线程加入队列,即开启线程;而直接调用run()相当于函数调用,程序将根据代码顺序执行
    ·非静态方法的使用数据保存在栈中,是线程私有的。
    ·使用wait()一定要用try/catch捕获或抛出InterruptedException,且调用wait()\notify()\notifyAll()的对象一定要被synchronized。
    ·会抛出InterruptedException的方法有:
    1.Java.lang.Object的wait()方法
    2.Java.lang.Thread的sleep()方法
    3.Java.lang.Thread的join()方法

JVM:

  • ·JVM在编译.java文件时,首先会产生类名.class,如果类中有内部类,会产生$+内部类名+.class,如果有匿名类,则生成类名+$1.class。故一个.java文件不一定只会生成一个.class文件。
  • ·JVM内存分区:
    1.堆区(heap),用于存放所有对象,是线程共享的(数组也算是对象)
    2.栈区(stack),用于存放基本数据类型和对象的引用,是线程私有的(分为虚拟机栈和本地方法栈)
    3.方法区(method),用于存放类信息、常量、静态变量、编译后的字节码等,是线程共享的(也被称为非堆——None-Heap)
    其中JAVA的垃圾回收器GC主要针对堆区,方法调用的释放由栈区负责相应出入栈。
  • ·JAVA命令:
    java用来运行一个.class文件;javadoc用来生成api文档;jar用来打包生成jar包;javac用来把.java编译为.class文件

数据库索引:

  • ·索引是什么?
    数据在硬盘中的存储是零散的,两个逻辑上相靠的数据行可能在物理上相距甚远,数据间的存储形式类似于链表,每个数据拥有存有上下数据物理位置的指针,因此数据的查询速率常常会使人感到不满。倘若查找表中指针的最后一条数据,从指针的第一条数据开始检索,这样等到最后查找到所需数据的时候就需要遍历整张表格中的数据行,时间复杂度过高。
    于是为了优化查询,可使用索引,其是以某数据表中的某列作为单位的,一列对应着一张索引。索引将这一列的数据与物理位置单独抽取出来,对这一列的数据进行逻辑排序,这样在查找的时候可根据二分查找快速确定目标行的位置,加快查找速度。
  • ·索引的优缺点:
    优点:
    1.大大加快数据的检索速度
    2.创建唯一性索引,保证数据表中每一行的唯一性
    3.加速表与表之间的连接
    4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
    缺点:
    1.加大了数据表以外的物理存储空间消耗
    2.创建和维护索引加大了时间损耗
    3.当对数据表进行插入、修改操作的时候,由于要重建索引,降低了数据维护速度。
    注意:修改性能与检索性能是相矛盾的,就像链表与队列之间的优劣一般。
  • ·什么情况下应使用索引:
    1.经常搜索的列
    2.作为主键的列
    3.经常作为外键链接其他表的列
    4.经常作为范围搜索根据的或经常需要排序的列
    5.经常使用WHERE语句进行查询的列
  • ·什么时候不应该使用索引:
    1.在查询中很少参与到的列
    2.具有很少数据值的列,例如gender、type,列的取值太少,增加索引并不能加快检索速度,反而徒增空间、时间需求
    3.对于存储大数据量的列,例如text、image,由于数据量太大,创建索引时的空间需求也增大
    4.当修改性能远大于检索性能时,不应该使用索引。
  • ·索引的实现:
    索引的常见实现方式有B+树与哈希表。
    B+树是一颗多叉树,每个节点通常有多个孩子,呈扁平状,一颗B+树包含根节点、内部节点和叶子节点,其能保证数据排列有序。B+树自底向上插入。B+树是二叉查找树的改版,每个节点包含了索引值和只想对应数据物理存储地址的指针,这样就可以运用二分查找法log2n的复杂度获取到相应数据。
  • ·查看索引的方式:
    在数据库中使用explain语句实现对已建立的索引的查看。使用explain + select语句进行查看,其结果中table、id、rows都很好理解,最重要的是type,其表明了当前连接时哪种类型,从最好到最差的连接类型为:const、eq_reg、ref、range、indexhe和ALL。Possible_keys指明了当前表中可能使用的索引,key指明实际使用的索引。
    当在进行查询的时候,如果在where语句后使用了!=或<>,以及多个where语句间用or相连,以及like模糊查询以%开头,则系统会放弃索引而是用全局查询,这时候上述数据中的key=null。
  • ·创建索引的方式
    1.建表时创建:
CREATE TABLE tablename(
       Row1 INT NOT NULL, 
       Row2 VARCHAR(16) NOT NULL, 
       INDEX [indexName] (row2(length))
 );`

删除索引时:
DROP INDEX [indexname] on tablename;
2.向已有表添加索引

Create index [indexname] on tablename (row2(length))
  • ·组合索引:
    数据库中创建组合索引时,采用最左优先原则,例如建立组合索引(a,b,c)后,会建立三个索引(a),(a,b),(a,b,c),所以这样情况下只有当where语句中出现了关于a的查询之后才会使用到索引。

其他:

  • ·取整函数:
    ceil():天花板,向上取整 floor():地板,向下取整
    对于ceil函数,当其传入参数是(-1.0,0)时,结果为-0.0。
  • ·File类理解为对文件和目录路径名的抽象形式,不能用来执行对文件的读写操作。JAVA认知中的文件分为文本文件和二进制文件,而计算机认知中只有二进制文件,所以JAVA中对于文本文件和二进制文件都可以当做二进制文件处理。
  • ·Object类中的方法:
    Object()(构造方法)、clone()、equals(Object)、finalize()、getClass()、hashCode()、notifly()、notiflyAll()、toString()、wait()
  • ·clone仅是一种复制拷贝对象,不涉及到调用构造方法。
  • ·8进制为了与10进制分隔开,会在前面加上一个0,如八进制的8表示为010,16进制为了分隔会以0x开头。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值