jndi step by step (5) 高级应用之Name

1、jndi 高级应用之 Name

    1.1、什么是 Name?
   
    这之前的文档中,我们所用的例子里,对象的名字都是 java.lang.String 类型的,也就是字符串类型。
    在这个文档里,我们则会介绍一些其他的对象名字类型,如 Name,以及他们的使用方法。
    我们首先会讲讲什么是字符串的名字和结构化的名字,以及它们的存在的必要性。
    然后,我们会介绍2个结构化名字的类:(复合名字)composite 和 (混合名字)compound。
    最后,我们介绍如何在对象名字中使用特殊字符,以及如何解析和构成一个复杂的对象的名字。
   
    1.2、字符串名字 vs 结构化名字
   
    在 Context 和 DirContext 接口里,每一个命名服务的方法都有2种方式,一个是接受字符串类型的名字,
    一个是接受结构化的名字(Name 类的对象)。例如:
   
lookup(java.lang.String)
lookup(javax.naming.Name)

    1.2.1、字符串名字
   
    使用字符串类型的名字,可以让你不必再生成一个CompositeName类的对象。例如下面的代码是相同的:

java 代码
  1. Object obj1 = ctx.lookup("cn=Ted Geisel, ou=People, o=JNDITutorial");   
  2.   
  3. CompositeName cname = new CompositeName(   
  4.     "cn=Ted Geisel, ou=People, o=JNDITutorial");   
  5. Object obj2 = ctx.lookup(cname);  

 

    1.2.1、结构化的名字
   
    结构化的名字的对象可以是 CompositeName 或 CompoundName 类的对象,或者是任何一个实现了 “Name ”
    接口的类。
    如果是 CompositeName 类的实例,那么就被认为是一个复合的名字(composite name)。所谓的复合名字就
    是可以在一个名字里使用多个命名服务系统,而不是仅仅一个。
    如果是 compound 类的实例,那么就被认为是混合的名字(compound name)。混合的名字只包含一个命名服务
    系统。
   
    1.2.2、那么该使用哪种名字呢?
   
    一般来说,如果用户可以提供字符串类型的名字,那么就使用字符串类型的名字;可是如果用户提供的是一
    个组合的名字,那么就应该使用结构化的名字了。
    例如一个应用如果会涉及多个命名服务系统,那么就应该使用结构化的名字了。
   
    1.3 复合名字(composite name)
   
    复合名字就是跨越多个命名系统的名字。例如:
   
cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt

    这个名字里包含了两个命名系统:一个 LDAP 系统 "cn=homedir,cn=Jon Ruiz,ou=People" 和一个文件系统
    "tutorial/report.txt"。当你把这个字符串传递给 Context 的 look 方法,那么就会从 LDAP 系统里查找
    那个文件,然后返回那个对象(一个文件对象的实例)。当然,这取决于特定的SPI。例如:

java 代码
  1. File f = (File)ctx.lookup(   
  2.     "cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt");   

 

    1.3.1、字符串的表现形式
   
    我们可以这样想像,一个复合的名字是由不同的“组件”组成的。所谓的“组件”,我们可以想象成是一个名字
    的一小部分,每一个组件表示一个命名服务系统。每一个组件都由正斜杠“/”分开。
    例如 :cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt
    包含三个组件:
   
cn=homedir,cn=Jon Ruiz,ou=People
tutorial
report.txt

    第一个组件属于LDAP命名服务系统,第2和第3个组件属于文件命名服务系统。正如我们从这个例子里看到的,
    多个组件(tutorial 和 report.txt)可以属于同一个命名服务系统,但是一个组件是不能跨越多个命名服务
    系统的。
   
    除了使用正斜杠“/”外,复合名字还允许使用其它三个特殊符号:反斜杠“\",单引号"'",双引号" " "。
    这三个符号是内置的符号,也就是说他们三个具有特殊的用途。
   
    反斜杠“\”的作用是转译字符。反斜杠“\”之后的字符,会被认为是普通字符。
    例如“a\/b”,其中的“/”是不会被认为是不同的命名服务系统的分隔符号。
   
    双引号和单引号可以让引号内的字符成为普通字符,例如下面的用法都是一样的:
   
a\/b\/c\/d
"a/b/c/d"
'a/b/b/d'

    可见,有的时候使用引号还是很方便的。
   
    复合的名字也可以是空的,空的复合名字意味着没有组件,一般用空的字符串表示。
   
    复合名字的组件也可以是空的,例如:
   
/abc
abc/
abc//xyz

    1.3.2、复合名字的类
   
    CompositeName 类是用来构成复合名字的类。你可以给它的构造函数传递一个复合名字的字符串。
    例如:

java 代码
  1. import javax.naming.CompositeName;   
  2. import javax.naming.InvalidNameException;   
  3.   
  4. /**  
  5.   * Demonstrates how to construct a composite name given its  
  6.   * string representation  
  7.   *  
  8.   * usage: java ConstructComposite   
  9.   */  
  10. class ConstructComposite {   
  11.     public static void main(String[] args) {   
  12.     if (args.length != 1) {   
  13.         System.out.println("usage: java ConstructComposite <string></string>");   
  14.         System.exit(-1);   
  15.     }   
  16.     String name = args[0];   
  17.     try {   
  18.         CompositeName cn = new CompositeName(name);   
  19.         System.out.println(cn + " has " + cn.size() + " components: ");   
  20.         for (int i = 0; i < cn.size(); i++) {   
  21.         System.out.println(cn.get(i));   
  22.         }   
  23.     } catch (InvalidNameException e) {   
  24.         System.out.println("Cannot parse name: " + name);   
  25.     }   
  26.     }   
  27. }   

 

    运行这个例子,输出的结果就是:
   
a/b/c has 3 components:
a
b
c

    CompositeName 类有许多方法,例如查看,修改,比较,以及得到一个复合名字的字符串表现形式。
   
    1.3.3、访问复合名字里的组件
   
    可以通过下列方法访问复和名字里的组件:
   
get(int posn)
getAll()
getPrefix(int posn)
getSuffix(int posn)
clone()

    如果你想得到特定位置的组件的名字,那么使用 get(int posn) 方法就非常合适。
   
    getAll() 方法可以返回全部的组件的名字。
    例如:

java 代码
  1. try {   
  2.     CompositeName cn = new CompositeName(name);   
  3.     System.out.println(cn + " has " + cn.size() + " components: ");   
  4.     for (Enumeration all = cn.getAll(); all.hasMoreElements();) {   
  5.     System.out.println(all.nextElement());   
  6.     }   
  7. catch (InvalidNameException e) {   
  8.     System.out.println("Cannot parse name: " + name);   
  9. }  

    你可以用 getPrefix(int posn) 和 getSuffix(int posn)来从前端或者后端查找组件的名字,如:

java 代码
  1. CompositeName cn = new CompositeName("one/two/three");   
  2. Name suffix = cn.getSuffix(1);  // 1 <= index < cn.size()   
  3. Name prefix = cn.getPrefix(1);  // 0 <= index < 1  

 

    运行结果:
   
two/three
one

    1.3.4、修改一个复合名字
   
    你可以通过下列方法修改一个复合名字:

add(String comp)
add(int posn, String comp)
addAll(Name comps)
addAll(Name suffix)
addAll(int posn, Name suffix)
remove(int posn)

    当你创建了一个复合名字实例后,你可以对它进行修改。看看下面的例子:

java 代码
  1. CompositeName cn = new CompositeName("1/2/3");   
  2. CompositeName cn2 = new CompositeName("4/5/6");   
  3. System.out.println(cn.addAll(cn2));           // 1/2/3/4/5/6   
  4. System.out.println(cn.add(0"abc"));         // abc/1/2/3/4/5/6   
  5. System.out.println(cn.add("xyz"));            // abc/1/2/3/4/5/6/xyz   
  6. System.out.println(cn.remove(1));             // 1   
  7. System.out.println(cn);               // abc/2/3/4/5/6/xyz   

 

    1.3.4、比较复合名字
   
    你可以通过以下的方法对复合名字进行比较:
   
compareTo(Object name)
equals(Object name)
endsWith(Name name)
startsWith(Name name)
isEmpty()

    你可以使用 compareTo(Object name) 方法对一个复合名字的列表进行排序。下面是一个例子:

java 代码
  1. import javax.naming.CompositeName;   
  2. import javax.naming.InvalidNameException;   
  3.   
  4. /**  
  5.   * Demonstrates how to sort a list of composite names.  
  6.   *  
  7.   * usage: java SortComposites [<name></name>]*  
  8.   */  
  9. class SortComposites {   
  10.     public static void main(String[] args) {   
  11.     if (args.length == 0) {   
  12.         System.out.println("usage: java SortComposites [<names></names>]*");   
  13.         System.exit(-1);   
  14.     }   
  15.     CompositeName[] names = new CompositeName[args.length];   
  16.     try {   
  17.         for (int i = 0; i < names.length; i++) {   
  18.         names[i] = new CompositeName(args[i]);   
  19.         }   
  20.   
  21.         sort(names);   
  22.   
  23.         for (int i = 0; i < names.length; i++) {   
  24.         System.out.println(names[i]);   
  25.         }   
  26.     } catch (InvalidNameException e) {   
  27.         System.out.println(e);   
  28.     }   
  29.     }   
  30.   
  31.     /**  
  32.      * Use bubble sort.  
  33.      */  
  34.     private static void sort(CompositeName[] names) {   
  35.     int bound = names.length-1;   
  36.     CompositeName tmp;   
  37.   
  38.     while (true) {   
  39.         int t = -1;   
  40.         for (int j=0; j < bound; j++) {   
  41.         int c = names[j].compareTo(names[j+1]);   
  42.         if (c > 0) {   
  43.             tmp = names[j];   
  44.             names[j] = names[j+1];   
  45.             names[j+1] = tmp;   
  46.             t = j;   
  47.         }   
  48.         }   
  49.         if (t == -1break;   
  50.         bound = t;   
  51.     }   
  52.     }   
  53. }  

    equals() 方法可以让你比较两个复合名字是否相同。只有两个复合名字有相同的组件,而且顺序一样,
    会返回 true。
   
    使用 startsWith() 和 endsWith()方法,你可以判断复合名字是以什么字符串开头和以什么字符串结尾。
   
    isEmpty() 方法可以让你知道一个复合名字是否为空。你也可以使用 size() == 0 来实现同样的功能。
   
    下面是一些例子:

java 代码
  1. CompositeName one = new CompositeName("cn=fs/o=JNDITutorial/tmp/a/b/c");   
  2. CompositeName two = new CompositeName("tmp/a/b/c");   
  3. CompositeName three = new CompositeName("cn=fs/o=JNDITutorial");   
  4. CompositeName four = new CompositeName();   
  5.   
  6. System.out.println(one.equals(two));        // false   
  7. System.out.println(one.startsWith(three));  // true   
  8. System.out.println(one.endsWith(two));      // true   
  9. System.out.println(one.startsWith(four));   // true   
  10. System.out.println(one.endsWith(four));     // true   
  11. System.out.println(one.endsWith(three));    // false   
  12. System.out.println(one.isEmpty());      // false   
  13. System.out.println(four.isEmpty());     // true   
  14. System.out.println(four.size() == 0);       // true  

    1.3.5、复合名字的字符串表现形式
   
    你可以使用 toString() 方法来实现这个功能。
    下面是一个例子:

java 代码
  1. import javax.naming.CompositeName;   
  2. import javax.naming.InvalidNameException;   
  3.   
  4. /**  
  5.   * Demonstrates how to get the string representation of a composite name.  
  6.   *  
  7.   * usage: java CompositeToString   
  8.   */  
  9. class CompositeToString {   
  10.     public static void main(String[] args) {   
  11.     if (args.length != 1) {   
  12.         System.out.println("usage: java CompositeToString <string></string>");   
  13.         System.exit(-1);   
  14.     }   
  15.     String name = args[0];   
  16.     try {   
  17.         CompositeName cn = new CompositeName(name);   
  18.         String str = cn.toString();   
  19.         System.out.println(str);   
  20.         CompositeName cn2 = new CompositeName(str);   
  21.         System.out.println(cn.equals(cn2));     // true   
  22.     } catch (InvalidNameException e) {   
  23.         System.out.println("Cannot parse name: " + name);   
  24.     }   
  25.     }   
  26. }  

    1.3.5、复合名字作为Context的参数
   
    直接看一个例子,非常简单:

java 代码
  1. // Create the initial context   
  2. Context ctx = new InitialContext(env);   
  3.        
  4. // Parse the string name into a CompositeName   
  5. Name cname = new CompositeName(   
  6.     "cn=homedir,cn=Jon Ruiz,ou=people/tutorial/report.txt");   
  7.   
  8. // Perform the lookup using the CompositeName   
  9. File f = (File) ctx.lookup(cname);  

 

    1.4、混合名字
   
    混合名字不是跨越命名系统的,例如:
   
    cn=homedir,cn=Jon Ruiz,ou=People
   
    它和复合名字有些类似,我们也可以把它看成是由不同的组件组成的,这个名字里包含了三个组件:
   
    ou=People
    cn=Jon Ruiz
    cn=homedir
   
    1.4.1、混合名字和复合名字的关系
   
    当你给 Context.lookup() 传递一个字符串的时候,首先 lookup 方法会把这个字符串作为复合名字来看
    待,这个复合名字可能只包含一个组件。但是一个组件可能会包含几个混合名字。
   
    1.4.2、混合名字的字符串表现方式
   
    正如上面所说的,一个混合名字是由很多的组件组成的。组件之间的分隔符号依赖于特定的命名服务系统。
    例如在 LDAP 里,分隔符好就是“,”,因此,下面的这个混合名字
   
ou=People
cn=Jon Ruiz
cn=homedir

    的字符串形式就是 cn=homedir,cn=Jon Ruiz,ou=People

    1.4.3、混合名字的类
   
    处理混合名字,我们可以使用 CompoundName 类。你可以向它的构造函数传递混合名字的字符串,并且还
    得设置一些必要的属性,这些属性一般都是特定的命名服务系统的一些规则。
   
    实际上,只有当你准备编写一个SPI的时候,才会去使用装个构造函数。作为一个开发者,一般你只是会
    涉及混合名字里的各个组件而已。
    下面是一个例子:

java 代码
  1. import javax.naming.*;   
  2. import java.util.Hashtable;   
  3.   
  4. /**  
  5.   * Demonstrates how to get a name parser and parse a name.  
  6.   *  
  7.   * usage: java ParseCompound  
  8.   */  
  9. class ParseCompound {   
  10.     public static void main(String[] args) {   
  11.     // Set up environment for creating initial context   
  12.     Hashtable env = new Hashtable(11);   
  13.     env.put(Context.INITIAL_CONTEXT_FACTORY,    
  14.         "com.sun.jndi.ldap.LdapCtxFactory");   
  15.     env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
  16.   
  17.     try {   
  18.         // Create the initial context   
  19.         Context ctx = new InitialContext(env);   
  20.   
  21.         // Get the parser for this namespace   
  22.         NameParser parser = ctx.getNameParser("");   
  23.   
  24.         // Parse name   
  25.         Name compoundName = parser.parse("cn=John,ou=People,ou=Marketing");   
  26.   
  27.         // List components in name   
  28.         for (int i = 0; i < compoundName.size(); i++) {   
  29.         System.out.println(compoundName.get(i));   
  30.         }   
  31.   
  32.         // Close ctx when done   
  33.         ctx.close();   
  34.     } catch (NamingException e) {   
  35.         System.out.println(e);   
  36.     }   
  37.     }   
  38. }   

    1.4.4、操作混合名字类
   
    注意,上面的例子中,我们使用 NameParser.parse() 来返回一个实现了Name接口的对象。这个接口可以
    是  CompositeName类 或 CompoundName类。这就意味着,你可以访问或者修改一个混合名字对象,就好
    像我们在复合名字里做的一样!
    下面是一个例子:

java 代码
  1. // Get the parser for this namespace   
  2. NameParser parser = ctx.getNameParser("");   
  3.   
  4. // Parse the name   
  5. Name cn = parser.parse("cn=John,ou=People,ou=Marketing");   
  6.   
  7. // Remove the second component from the head   
  8. System.out.println(cn.remove(1));          // ou=People   
  9.   
  10. // Add to the head (first)   
  11. System.out.println(cn.add(0"ou=East"));  // cn=John,ou=Marketing,ou=East   
  12.   
  13. // Add to the tail (last)   
  14. System.out.println(cn.add("cn=HomeDir"));  // cn=HomeDir,cn=John,ou=Marketing,ou=East  

 

    输出结果:
   
ou=People
cn=John,ou=Marketing,ou=East
cn=HomeDir,cn=John,ou=Marketing,ou=East

    需要的注意的是,LDAP系统里,组件的顺序是从右到左的。也就是右边是名字的开头,而左边是名字
    的结尾!
   
    下面这个例子是修改混合名字的:

java 代码
  1. import javax.naming.*;   
  2. import java.util.Hashtable;   
  3. import java.io.File;   
  4.   
  5. /**  
  6.   * Demonstrates how to modify a compound name by adding and removing components.  
  7.   * Uses file system syntax.  
  8.   *  
  9.   * usage: java ModifyCompoundFile  
  10.   */  
  11. class ModifyCompoundFile {   
  12.     public static void main(String[] args) {   
  13.     // Set up environment for creating initial context   
  14.     Hashtable env = new Hashtable(11);   
  15.     env.put(Context.INITIAL_CONTEXT_FACTORY,    
  16.         "com.sun.jndi.fscontext.RefFSContextFactory");   
  17.   
  18.     try {   
  19.         // Create the initial context   
  20.         Context ctx = new InitialContext(env);   
  21.   
  22.         // Get the parser for this namespace   
  23.         NameParser parser = ctx.getNameParser("");   
  24.   
  25.         // Parse name   
  26.         Name cn = parser.parse("Marketing" + File.separator +    
  27.         "People" + File.separator +   
  28.         "John");   
  29.   
  30.         // Remove 2nd component from head   
  31.         System.out.println(cn.remove(1));             // People   
  32.   
  33.         // Add to head (first)   
  34.         System.out.println(cn.add(0"East"));     
  35.                         // East/Marketing/John   
  36.   
  37.         // Add to tail (last)   
  38.         System.out.println(cn.add("HomeDir"));   
  39.                         // /East/Marketing/John/HomeDir   
  40.   
  41.         // Close ctx when done   
  42.         ctx.close();   
  43.     } catch (NamingException e) {   
  44.         System.out.println(e);   
  45.     }   
  46.     }   
  47. }  

 

    这个例子使用了文件系统,而不是LDAP系统,输出结果:
   
People
East/Marketing/John
East/Marketing/John/HomeDir

    1.4.5、混合名字作为Context的参数
   
    我们只用一个例子就可以了:

java 代码
  1. // Create the initial context   
  2. Context ctx = new InitialContext(env);   
  3.   
  4. // Get the parser for the namespace   
  5. NameParser parser = ctx.getNameParser("");   
  6.   
  7. // Parse the string name into a compound name   
  8. Name compound = parser.parse("cn=Jon Ruiz,ou=people");   
  9.   
  10. // Perform the lookup using the compound name   
  11. Object obj = ctx.lookup(compound);  

    1.4.6、取得完整的混合名字
   
    有的时候,你可能需要根据一个混合名字来的完整的名字。例如一个DNS,你只知道一部分如“www.abc.com”,
    但实际上它的完整的DNS是“www.abc.com.cn”。
   
    其实这个功能已经超出了jndi 的 api 所控制的范围了,因为 jndi 是不能决定完整的名字到底是什么样子,
    那必须依赖特定的命名服务系统。
   
    但是,jndi 仍然提供了一个接口 Context.getNameInNamespace()。这个方法的返回结果,依赖于你所使用
    的命名服务系统。
   
    下面是一个例子:
   

java 代码
  1. // Create the initial context   
  2. Context ctx = new InitialContext(env);   
  3.   
  4. // Perform the lookup    
  5. Context jon = (Context)ctx.lookup("cn=Jon Ruiz,ou=people");   
  6.   
  7. String fullname = jon.getNameInNamespace();  

 

    输出结果:
   
    cn=Jon Ruiz,ou=people,o=JNDItutorial

   
    1.5、名字解析
   
    名字解析的意思就是根据一个名字的字符串形式,把它解析成结构化的形式,如混合名字或复合名字。
    jndi API里包含了这样的接口,其接口的实现,依赖于特定的SPI。
   
    1.5.1、解析复合名字
   
    下面是一个例子:

java 代码
  1. // Parse the string name into a CompositeName   
  2. Name cname = new CompositeName(   
  3.     "cn=homedir,cn=Jon Ruiz,ou=people/tutorial/report.txt");  

    其实这个用法我们早就在前面看过了。
   
    1.5.2、解析混合名字
   
    为了解析混合名字,你必须使用 NameParser 接口,这个接口有一个方法:

java 代码
  1. Name parse(String name) throws InvalidNameException  

    首先,你必须从 SPI 里得到一个 NameParser 接口的实现:

java 代码
  1. // Create the initial context   
  2. Context ctx = new InitialContext();   
  3.   
  4. // Get the parser for LDAP   
  5. NameParser ldapParser =    
  6.     ctx.getNameParser("ldap://localhost:389/o=jnditutorial");   
  7.   
  8. // Get the parser for filenames   
  9. NameParser fsParser = ctx.getNameParser("file:/");  

    一旦你得到了 NameParser 实例,你就可以用它来把字符串形式的名字转换成结构化的名字。

java 代码
  1. // Parse the name using the LDAP parser   
  2. Name compoundName = ldapParser.parse("cn=John Smith, ou=People, o=JNDITutorial");   
  3.   
  4. // Parse the name using the LDAP parser   
  5. Name compoundName = fsParser.parse("tmp/tutorial/beyond/names/parse.html");  

    如果解析错误,那么会得到 InvalidNameException 。
   
    尽管 parse() 方法可以返回一个结构化的名字,但是我们还是建议仅仅用它来处理混合名字,而不要
    处理复合名字。
    parse()返回的对象未必一定是 CompoundName 类,只要返回的对象实现了 Name 接口就可以--这依赖
    于特定的SPI。
   
   
    1.6、动态生成复合名字
   
    之前我们介绍了访问,操作复合名字的方法。但是还有一些难以处理的情况需要我们去面对。
    例如,如果你要给一个复合名字增加一个组件的话,你是增加复合名字的组件还是混合名字的组件?
    如果是混合名字的组件,那么使用什么规则呢?
    例如我们有这样一个复合名字:
   
    cn=homedir,cn=Jon Ruiz/tutorial
   
    现在你需要增加一个文件名字,如果是windows系统,那么就是:
   
    cn=homedir,cn=Jon Ruiz/tutorial\report.txt
   
    然后我们再增加一个LDAP混合名字 ou=People,那么就需要使用LDAP的规则:
   
    cn=homedir,cn=Jon Ruiz,ou=People/tutorial\report.txt
   
    在这个例子中,我们使用了不同的命名服务系统,我们需要知道什么时候因该使用什么样的命名系统
    的规则,这真的很麻烦。
   
    为了解决这个问题,jndi API 提供了一个接口 Context.composeName(),它用来动态的组装名字,
    当然依赖于特定的SPI了。
    你需要给它提供两个参数:一个是需要追加的组件,一个是被追加的组件的名字。
   
    下面是一个例子:

// Create the initial context
Context ctx = new InitialContext(env);

// Compose a name within the LDAP namespace
Context ldapCtx = (Context)ctx.lookup("cn=Jon Ruiz,ou=people");
String ldapName = ldapCtx.composeName("cn=homedir", "cn=Jon Ruiz,ou=people");
System.out.println(ldapName);

// Compose a name when it crosses into the next naming system
Context homedirCtx = (Context)ctx.lookup(ldapName);
String compositeName = homedirCtx.composeName("tutorial", ldapName);
System.out.println(compositeName);

// Compose a name within the File namespace
Context fileCtx = (Context)ctx.lookup(compositeName);
String fileName = fileCtx.composeName("report.txt", compositeName);
System.out.printl

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值