浅谈JAVA中的日志文件

      log4的使用方法:log4是具有日志记录功能,主要通过一个配置文件来对程序进行监测 
  有两种配置方式:一种程序配置,一种文件配置

  有三个主要单元要了解,Logger,appender,layout.

  logger是进行记录的主要类,appender是记录的方式,layout是记录的格式

  logger七种日志级别:debug,info,warn,error,fatal,all,off

  最常用的应该是debug()和info();而warn()、error()、fatal()仅在相应事件发生后才使用

  appender主要分三类,终端输出,文件输出,流输出

  ConsoleAppender
  FileAppender
  WriterAppender

  layout也分三类:简单格式,html格式,匹配格式

  SimpleLayout
  HTMLLayout
  PatternLayout

  输出简写:(用在PatternLayout)

  %m 输出代码中指定的消息
  %p 输出优先级
  %r 输出自应用启动到输出该log信息耗费的毫秒数
  %c 输出所属的类目,通常就是所在类的全名
  %t 输出产生该日志事件的线程名
  %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
  %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
  %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。一般使用了它就不需使用%c了

  [按照下面步骤来写吧!就可以得到我们所想要的日志文件哦!]

  [按照顺序就可以将日志文件读出来了!]作用实例:

  程序配置一般过程:

  1.得到特定名称的logger
  2.得到其中一个layout
  3.根据layout得到appender
  4.logger添加appender
  5.logger设置级别
  6.logger输出有级别的日志信息

  程序:

Logger logger = Logger.getLogger(Test.class.getName())     //取得logger
SimpleLayout simpleLayut = new SimpleLayout()              //取得layout
FileAppender fa = new FileAppender(simpleLayout,"test.log") //取得ppender
logger.addAppender(fa)                                     //添加appender
logger.set((Level)Level.DEBUG)                             //设置级别
logger.debug("hihihihih")                                  //记录信息 


  文件配置过程:

  1.得到特定名称的logger
  2.加载配置文件      (配置文件做了2-5的工作)
  3.logger输出有级别的日志信息

  文件配置程序:

  1.Logger logger = Logger.getLogger(Test.class.getName())     //取得logger
  2.使用下面的配置加载
  3.输出信息

  文件配置分properties,xml

  读取properties:PropertyConfigurator.configure("logTest.properties");

  读取xml:DOMConfigurator.configure("src/logTest.xml");

  告别System.out.print()—J2SDK1.4新增Java日志框架 (一) 

  作为一名Java 程序员,最熟悉的、使用最多的调用恐怕莫过于System.out.print(“…”)。当你没有调试工具而要跟踪一个变量的值得时候;当你需要显示捕获的Exception、Error的时候;当你想知道程序在运行的时候究竟发生了什么的时候,通常的做法就是调用System.out.print把他们在终端、控制台上打印出来。这种方式对于输出信息的分类、格式化及永久保存带来诸多不便。虽然我们可以把它写入一个文件然后进行分析,但是这要需要编写额外的程序代码,其成本不可忽视!而由此给目标系统本身增加的复杂程度不可避免的使开发、调试陷入一个深深的迷潭。

  [要知道一点:如果说能不写的代码最好不要写!]

  JDK1.4的推出,使得这一切即将成为历史。让我们向System.out.print()告别吧,使用Java Logging API为自己的程序构建一个完整的日志记录系统!

  一、第一个实例先看一个简单的实例:

1  import java.util.logging.*;
2 public class SimpleLoggingTest {
3     public static void main(String args[]) {
4          //程序的其它处理
5          //使用Logger的静态方法获得一个匿名          //Logger
6          Logger logger1 = Logger.getAnonymousLogger();
7          //记录消息
8          logger1.log(Level.INFO,"第一条日志记录");
9          //程序的其它处理
10      }
11 } 


  实例1

  注意:编译、执行该程序需要JDK1.4及以上版本的支持。

  运行该程序,可以在控制台看到程序运行结果:

  2003-1-14 15:09:40 SimpleLoggingTest main信息: 第一条日志记录首先,程序引用java.util.Logging包(第1行)。接着,在适当的时候获得一个Logger(记录器)类的实例(第6行,获取一个匿名的Logger)。最后,在程序需要记录信息的地方调用Logger类的log方法进行记录(第8行,记录一个INFO级别的消息)。

  二、Java Logging API 

  Java Logging API封装在JDK1.4.0的java.util.Logging 包中。它通过产生便于最终用户、系统管理员、故障维护工程师以及软件开发团队(工程师)进行分析的日志记录为软件的开发调试和维护提供便利的手段。它捕获操作系统平台和执行程序的安全故障、配置错误、执行瓶颈和(或)Bug等数据信息,以纯文本、XML或程序员自定的某种方式将其格式化成日志记录,然后传递给内存、系统输出流、控制台、文件、Sockets等多种系统资源进行缓存和输出。

  (一)、该软件包中的关键类。

  n        Logger:  应用程序进行日志记录调用的主要实体。 Logger对象用于记录特定系统或应用程序的消息。

  n        LogRecord:  用于在日志框架和单个记录处理程序之间传递记录请求。

  n        Handler:  日志数据的最终输出处理器。它将LogRecord对象导出到各种目标,包括内存、输出流、控制台、文件和套接字。多种Handler子类可供用于这种用途。

  图一

  n        Level:  定义一组标准的记录级别,可用于控制记录的输出。可以把程序配置为只输出某些级别的记录,而忽略其他级别的输出。

  n        Filter:  精细过滤、控制记录的内容,比记录级别所提供的控制准确得多。记录API支持通用的过滤器机制,这种机制允许应用程序代码添加任意过滤器以便控制记录的输出。

  n        Formatter:  为LogRecord对象的格式化提供支持。

  图二

  n        LogManager: Java Logging框架中唯一的、全局的对象,用于维护与Logger记录器及日志服务的一系列共享的数据结构及状态。它负责整个日志框架的初始化、维护一组全局性的Handle对象、维护一个树形结构的Logger的名字空间、诊测日志框架配置文件的改变从而重新读入并应用相关的参数以及负责程序停止运行时整个日志框架的清理工作。

  (二)Logger

  1、Logger的命名空间

  在SimpleLoggingTest.java实例中,我们使用了一个匿名的(没有命名的)Logger对象。在Java Logging 框架中,Logger是可以命名的。Logger的名字空间与java类的名字空间相同的结构相同:使用“。”间隔的字符串。Logger的名字空间体现了Logger的层次结构。例如:命名为“a.b”的Logger是命名为“a.b.c”的“父”(上一级)Logger记录器。Logger的命名可以是任意的字符串,一般情况下,使用包或类的名字为Logger 进行命名。

  Logger的名字空间由全局单列类LogManager的实例进行创建、维护。

  [貌似在处理数据库的时候的DriverManage来管理数据库驱动一样了!]

  匿名Logger不被存储在命名空间中。

  2、创建Logger实例

  Logger对象可以通过调用工厂方法getLogger或getAnonymousLogger获取。

  //获取一个名为“A”的Logger对象

  Logger loggerA= Logger.getLogger(“A”);

  // 获取一个名为“A.B”的Logger对象,其上级记录器为loggerA.

  Logger loggerAB= Logger.getLogger(“A.B”);

  //获取一个匿名Logger对象

  Logger loggerTmp = Logger.getAnonymousLogger();

  对非匿名Logger,getLogger先在命名空间中查找同名的Logger对象,如果有,则返回该Logger对象;如果不存在,则在命名空间中创建注册一个新的Logger对象,并与其上级Logger对象相关联。

  匿名Logger对象属于创建它的对象的私有对象,只能由创建它的对象使用,记录一些临时性的日志信息。而命名Logger对象使全局性的,在日志框架的生存期内,除了创建它的对象外还,可由其它对象用于记录日志信息。

  匿名的Logger对象由一个全局的的root Logger “” 对象(root Logger的名字为空)。这意味着所有匿名Logger对象将从root Logger “”中继承行为。

  匿名Logger对象通常用于java Applet应用中。它去掉了在运行过程中的一班性的安全检查,允许其创建类对象对Logger的控制、状态信息进行修改,如:setLevel设置Logger的日志消息记录级别;addHandle增加Logger的Handle(处理器)对象等。

  一个Logger对象可以拥有有零个到多个Handler实例。当没有Handler时,如不禁止日志记录沿名字空间向上传递,那该Logger对象的日志消息记录将有其拥有Handler实例的上级Logger进行处理。当一个Logger对象拥有多个 Handler实例对象时,其记录的日志数据将被所有的Handler逐一进行处理。

  (三)、Handler

  Handler对象接收传来的日志消息将其输出。Handler可以把日志消息输出到多种目标资源,如:输出到控制台进行显示、写入日志文件、传送到网络上的远程日志服务进行处理、写入系统日志等任何物理资源

  告别System.out.print()—J2SDK1.4新增Java日志框架(二) 

  Handler对象在创建时使用LogManager对象的相关属性的默认值(如Handler的Filter、Formatter、Level等对象属性)进行初始化。

  Handler对象可通过调用setLevel(Level.OFF)暂停工作;通过调用setLevel设置适当的记录日志消息级别恢复工作。

  Handler是一个抽象类。在J2SDK1.4中,其子类及它们之间的关系见图一。

  1、   MemoryHandler Handler的子类,在内存中的一个循环缓冲区用于缓存日志记录请求。通常MemoryHandler只简单的把传入的LogRecords存储到它的内存中。这种缓存的开销非常低廉,它去掉了格式化所产生的系统消耗。当某个触发条件满足时,MemoryHandler将其缓冲的数据push(发布)到目标Handler,由后者执行实际的输出。有三种模式触发MemoryHandler进行push操作:a、传入的LogRecords的级别高于 MemoryHandler预先定义的push级别;b、有其他对象显式的调用其push方法;c、其子类重载了log方法,逐一检索每个传入的 LogRecords,若符合特定的标准则进行push操作。

  实例:假设我们需要跟踪一个生产环境中的一个很少出现的Bug.在大多数场合,系统化产生大量的日志记录,而我们仅只关心记录中最近的几条,那么我们只需要使用MemoryHandler对日志记录进行缓存,当且仅当某个事件发生时将最近的几条记录从内存中 dump到制定的文件中。

//MemoryHandlerTest.java 
import java.util.logging.*;
import java.io.*;
public class MemoryHandlerTest {
 FileHandler fhandler;
 Logger logger;
 MemoryHandler mhandler;

 MemoryHandlerTest() {
     try {
         //构造名为my.log的日志记录文件
         fhandler = new FileHandler("my.log");
         int numRec = 5;
         //构造一个5个日志记录的MemoryHandler,
//其目标Handler为一个FileHandler
         mhandler = new MemoryHandler (fhandler, numRec, Level.OFF) ;
            //构造一个记录器
            logger = Logger.getLogger("com.mycompany");
            //为记录器添加一个MemoryHandler
         logger.addHandler(mhandler);
    } catch (IOException e) {

     }
 }

 public static void main(String args[]) {
     MemoryHandlerTest mt = new MemoryHandlerTest();
     int trigger = (int)(Math.random()*100);
    for (int i=1;i<100;i++) {
        //在MemoryHandler中缓存日志记录
         mt.logger.log(Level.INFO,"日志记录"+i);
         if (i==trigger) {
            //触发事件成立,显式调用MemoryHandler的
//push方法触发目标Handler输出日志记录到
//my.log文件中
//当且仅当这个事件成立的时候就触发这个事件了!

            mt.mhandler.push(); 
            break;
         }
     }
 }


  实例2

  2、FileHandler 文件处理器。

  StreamHandler流处理器将日志记录以流的形式输出。FileHandler、ConsoleHandler、SocketHandler为StreamHandler的子类。 ConsoleHandler将日志记录输出到控制终端。前面的实例(实例2除外)都将日记记录数据输出到控制台。

  FileHandler将日志记录输出到特定的文件,或循环的几个日志文件中。日志文件可以设置容量大小。当日志文件达到限定的容量时将被自动清空,重头开始写入新的日志记录数据。

  例:创建一个容量为1Mb的文件处理器

int limit = 1000000; // 1 Mb 

FileHandler fh = new FileHandler("my.log", limit, 1); 


  对于循环的日志文件,每个文件将被指定容量限制。当当前的日志文件的长度达到制定值后该文件被关闭,新的日志文件被创建,旧的文件将在文件名模板后追加序号。如此产生多个顺序编号的日志记录文件。

  例:

try {    // 创建一个拥有3个日志文件,每个容量为1Mb的文件处理器


        String pattern = "my%g.log";
int limit = 1000000; // 1 Mb
 int numLogFiles = 3;
 FileHandler fh = new FileHandler(pattern, limit, numLogFiles);
        …
 } catch (IOException e) {
 } 


  (四)、Level 
  Level对象定义了一组日志消息的级别,用于控制日志消息的输出。一个级别对应一个整型值。日志消息级别按照其整数值的大小排定优先级。在Logger对象中设定一个级别,则大于等于该级别的日志消息将会被传递到某个Handler对象进行输出处理。

  J2SDK1.4的Java Logging框架中定义了以下消息级别:

级别名称 int值(Windows 2000环境) 
OFF 2147483647 
SEVERE 1000 
WARNING 900 
INFO 800 
CONFIG 700 
FINE 500         
FINER 400 
FINEST 300 
ALL         -2147483648 


表一

  Level.OFF具有最高的级别。将Logger的Level级别设置成Level.OFF

  让我们看看消息级别是怎样工作的:LoggingLevelTest.java

import java.util.logging.*;
public class LoggingLevelTest {
 public static void main(String args[]) {
     //使用Logger的静态方法获得一个匿名Logger
     Logger logger1 = Logger.getAnonymousLogger();
     //设置Logger对象记录的最低日志消息级别
     logger1.setLevel(Level.FINER);
     //记录消息
     logger1.severe("SEVERE级消息");
     logger1.warning("WARNING级消息");
     logger1.config("CONFIG级消息");
     logger1.info("INFO级消息");
     logger1.fine("FINE级消息");
     logger1.finer("FINER级消息");
     logger1.finest("FINEST级消息");
 }


  实例3

  运行结果:2003-1-15 7:02:03 LoggingLevelTest main服务器: SEVERE级消息2003-1-15 7:02:04 LoggingLevelTest main警告: WARNING级消息2003-1-15 7:02:04 LoggingLevelTest main配置: CONFIG级消息

  告别System.out.print()—J2SDK1.4新增Java日志框架(三) 

  2003-1-15 7:02:04 LoggingLevelTest main信息: INFO级消息可以看出,优先级低于INFO的日志消息不被记录。

  Level的构造函数为protected便于程序员开发自己的消息级别类。

import java.util.logging.*;
//自定义消息级别
class myLevel extends Level{
//定义自己的消息级别SYSE
public static final Level SYSE = new myLevel("SYSE", Level.SEVERE.intValue()+10);
      public myLevel(String ln,int v) {
         super(ln,v);
 }

}
public class MyLevelTest {

    public static void main(String args[]) {
      Logger logger1 = Logger.getAnonymousLogger();    
      //设置消息级别
      logger1.setLevel(myLevel.SYSE);

       //记录消息
       logger1.log(myLevel.SYSE,"SYSE消息");
       logger1.severe("SVERE消息");
 }


  实例4

  运行结果:

  2003-1-15 15:40:04 MyLevelTest main

  SYSE: SYSE消息

  只有SYSE消息被记录,SVERE消息不被记录,因为自定义级别SYSE高于SEVERE.

  (五)Formatter 

  Formatter负责对LogRecords进行格式化。每个记录处理器Handler同一个Formatter对象相关联。Formatter对象接收从Handler传来的LogRecord,将其格式化成字符串后返回给Handler进行输出。

  Formatter是一个抽象类。在J2SDK1.4中,其子类及它们之间的关系见图二。

  自定义扩展Formatter类。实例:MyFormatterTest.java

import java.util.Date;
import java.util.logging.*;

//创建每条日志记录以行的日志格式:
//时间<空格>消息级别<空格>消息ID<空格>日志信息内容<换行>
class MyFormatter extends Formatter {
        
    public String format(LogRecord rec) {
       StringBuffer buf = new StringBuffer(1000);
        buf.append(new Date().toLocaleString()); //时间
        buf.append('' '');
        buf.append(rec.getLevel()); //消息级别
        buf.append('' '');
        buf.append(rec.getMillis()); //作为消息ID
        buf.append('' '');
        buf.append(formatMessage(rec));//格式化日志记录数据
        buf.append(''\n'');    //换行
        return buf.toString();
    }       
}
public class MyFormatterTest {
    public static void main(String args[]){
       //创建记录器
Logger log1 = Logger.getLogger("MyLogger");
       //创建记录处理器
Handler mh = new ConsoleHandler();
//为记录处理器设置Formatter
       mh.setFormatter(new MyFormatter());
       //为记录器添加记录处理器
log1.addHandler(mh);
//禁止消息处理将日志消息上传给父级处理器
       log1.setUseParentHandlers(false);
       //记录消息
       log1.severe("消息1");
       log1.warning("消息2");
       log1.info("消息3");
       log1.config("消息4");
    }


  实例5

  程序运行结果:

  2003-1-15 16:59:38 SEVERE 1042621178968 消息1 2003-1-15 16:59:40 WARNING 1042621178985 消息2 2003-1-15 16:59:41 INFO 1042621179105 消息3

  三、配置文件 

  J2SDK1.4的Java Logging框架的配置文件(Windows):%J2SDK1.4_HOME%/jre/lig/logging.properties从配置文件可以看到:(一)自定义日志配置文件:java -Djava.util.logging.config.file=myfile(二)全局Handler在Java VM启动时被加载。

  (二)全局Handler默认为java.util.logging.ConsoleHandler. handlers= java.util.logging.ConsoleHandler所以我们的任何日志记录动作都会在控制台进行显示。

  (三)缺省的消息记录级别为:INFO。level= INFO在缺省情况下我们在控制台看不见低于INFO级别的日志消息。

  (四)缺省的Handler消息格式为java.util.logging.SimpleFormatter

  四、日志框架在程序测试中的应用

  Logger类提供了两个的方法:Logger.entering()  Logger.exiting() .这对我们调试自己的方法调用提供了便利的方式。

  例子:记录方法调用的输入参数和输出参数 方法myMethod将一个int 追加在一个对象之后。

  运行该程序应将logging.properties的

  java.util.logging.ConsoleHandler.level = INFO改为:java.util.logging.ConsoleHandler.level = ALL

import java.util.logging.*;
public class MyClass {
        public String myMethod(int p1, Object p2) {
           Logger logger = Logger.getLogger("com.mycompany.MyClass");
            if (logger.isLoggable(Level.FINER)) {
                logger.entering(this.getClass().getName(), "myMethod",
                                new Object[]{new Integer(p1), p2});
            }
            String tmp = p2.toString() + p1;
            if (logger.isLoggable(Level.FINER)) {
                logger.exiting(this.getClass().getName(), "myMethod", tmp);
            }
            return tmp;
        }
        public static void main(String args[]) {
          MyClass mc = new MyClass();
          String rslt = mc.myMethod(123,"Hello");
        }


  后记 
  J2SDK1.4引入的日志记录框架为构建简易的日志记录系统提供了便利的解决方案。虽然还有期它的一些专用日志包如Log4j,但从简单的打印输出到严密的、可扩展的日志记录框架,J2SDK1.4的日志系统已经足以满足一般的系统开发的要求

  Log4j基本使用方法

  Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、 INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显示内容。

  3.1.定义配置文件

  Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:

  1.配置根Logger,其语法为:

  log4j.rootLogger = [ level ] , appenderName, appenderName, … 其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG.通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。

  appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。

  2.配置日志信息输出目的地Appender,其语法为

log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1

log4j.appender.appenderName.option = valueN 


  其中,Log4j提供的appender有以下几种:org.apache.log4j.ConsoleAppender(控制台),org.apache.log4j.FileAppender(文件),org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

  3.配置日志信息的格式(布局),其语法为:

log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1

log4j.appender.appenderName.layout.option = valueN 


  其中,Log4j提供的layout有以下几种:org.apache.log4j.HTMLLayout(以HTML表格形式布局),org.apache.log4j.PatternLayout(可以灵活地指定布局模式),org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

  3.2.在代码中使用Log4j

  下面将讲述在程序代码中怎样使用Log4j.

  3.2.1.得到记录器

  使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:

  public static Logger getLogger( String name),

  通过指定的名字获得记录器,如果必要的话,则为这个名字创建一个新的记录器。Name一般取本类的名字,比如:

  static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () ) ;

  3.2.2.读取配置文件

  当获得了日志记录器之后,第二步将配置Log4j环境,其语法为:BasicConfigurator.configure (): 自动快速地使用缺省Log4j环境。

  PropertyConfigurator.configure ( String configFilename) :读取使用Java的特性文件编写的配置文件。

  DOMConfigurator.con

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java的BorderLayout是一种常用的布局管理器,它将容器分为5个区域:北、南、东、西和间。在使用BorderLayout时,我们可以通过设置组件的位置来确定它们在容器的位置。 下面是一个简单的例子,演示如何使用BorderLayout: ``` import java.awt.BorderLayout; import javax.swing.JButton; import javax.swing.JFrame; public class BorderLayoutExample { public static void main(String[] args) { JFrame frame = new JFrame("BorderLayout Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button1 = new JButton("North"); JButton button2 = new JButton("South"); JButton button3 = new JButton("East"); JButton button4 = new JButton("West"); JButton button5 = new JButton("Center"); frame.add(button1, BorderLayout.NORTH); frame.add(button2, BorderLayout.SOUTH); frame.add(button3, BorderLayout.EAST); frame.add(button4, BorderLayout.WEST); frame.add(button5, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } } ``` 在这个例子,我们创建了一个JFrame,并在其添加了5个JButton。使用BorderLayout将它们放置在窗口的不同位置。在上面的代码,我们将button1放置在北面,button2放置在南面,button3放置在东面,button4放置在西面,button5放置在心。 需要注意的是,当我们在使用BorderLayout时,如果某个位置没有被占用,那么它将会自动被设置为空。另外,如果我们在同一个位置添加了多个组件,那么后添加的组件将会覆盖先添加的组件。 总之,BorderLayout是一个非常常用的布局管理器,可以帮助我们轻松地将组件放置在容器的不同位置。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值