• 【Java进阶篇】第三章 常用类


    在这里插入图片描述

    一、String类

    1、String类概述

    • String表示字符串类型,属于引用数据类型
    • 在Java中,随便使用双引号括起来的都是String对象,如"abc"、"Hello world!"就是两个String对象
    • Java中,字符串是不可变的
    • JDK中,双引号括起来的字符串,如"abc"都是直接存储在方法区的字符串常量池中(因为字符串在实际开发中使用频繁,为了执行效率)

    2、String字符串的存储原理

    凡是双引号括起来的,都在字符串常量池中有一份
    new对象的时候一定在堆内存当中开辟空间

    String s1 = "abcdef";
    //实际在底层创建了三个字符串对象,都在字符串常量池中
    //这就是Java中的字符串一旦创建就不可变,即不可在abcdef后直接补xy
    String s2 = "abcdef"+"xy";
    String s3 = new String("xy");
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    3、有String型属性的对象

    class User{
    	int id;
    	String name;
    }
    ....
    User user = new User(110,"张三");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    注意:name中存的是指向字符串常量池中的字符串对象的内存地址

    4、两种字符串对象创建方式的区别

    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2);//true
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    String x = new String("xyz");
    String y = new String("xyz");
    System.out.println(x == y); //false
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    综上:String类的比较直接使用equals()方法:

    String k = new String("testString");
    //这么写更优,可以避免空指针异常
    System.out.println("testString".equals(k));
    
    //这么写k为null时空指针异常
    System.out.println(k.equals("testString"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    String的特殊性:

    • int i = 100; i中存的是100这个值

    • String s = “abc”; s中存的是"abc"这个字符串对象在字符串常量池中的内存地址!!!

    5、String类的特殊构造方法

    构造方法中传入数组

    //97即a,98是b,99是c
    byte[] bytes = {97,98,99}; 
    //传入一个数组
    String s3 = new String(bytes);
    //输出abc字符串
    System.out.println(s3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    构造方法中传入数组和偏移量

    //String(字节数组,数组元素起始下标。长度)
    String s4 = new String(bytes,1,2);
    //输出bc,即只将byte数组中的一部分转化为字符串
    System.out.println(s4);
    
    • 1
    • 2
    • 3
    • 4

    对于char数组char[] charArray以上构造方法同样可以用

    总结String类的构造方法:

    • String s = new String(“xxx”);
    • String s = “xxx”;
    • String s = new String(char数组);
    • String s = new String(char数组,起始下标,长度);
    • String s = new String(byte数组);
    • String s = new String(byte数组,起始下标,长度);

    6、String类中的方法

    1)charAt方法—char

    用法:

    字符串对象 . charAt(int下标);

    示例:

    //返回一个char
    char c = "中国人".charAt(1);
    System.out.println(c);           //国
    
    • 1
    • 2
    • 3

    2)compareTo方法—int

    作用:

    按字典顺序比较两个字符串,靠后的字母是大的

    示例:

    //0 ,代表前后一致
    int result1 = "abc".compareTo("abc");
    //-1;代表前小后大,类比8-9
    int result2 = "abcd".compareTo("abce");
    //1, 代表前大后小,类比9-8
    int result3 = "abce".compareTo("abcd");
    
    //第一位的比较已经得出结论,x < y,-1
    System.out.println("xyz".compareTo("yxz");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到:equals只能看到是否相等,但compareTo方法还能看到谁打谁小:

    //随便写个静态的自己编的equals方法协助理解
    
    public static boolean equals(byte[] value,byte[] other){
            if(value.length == other.length) {
                for(int i=0;i<value.length;i++){
                    if(value[i] != other[i]){
                        return false;
                    }
                }
                return true;
            }
            return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3)contains方法—boolean

    作用:

    判断前面的字符串是否包含后面的字符串

    示例:

    //true
    System.out.println("HelloWorld.java".contains(".java"));
    
    • 1
    • 2

    4)endsWith方法—boolean

    作用:

    判断当前字符串是否以某个字符串结尾

    示例:

    //false
    System.out.println("text.txt".endWith(".java"));
    
    • 1
    • 2

    类似的,也有startWith方法,用法与endWith方法相同

    5)equalsIgnoreCase方法—boolean

    作用:
    判断两个字符串是否相等,且忽略大小写

    示例:

    System.out.println("ABC".equalsIgnoreCase("abc"); //true
    
    • 1

    6)getBytes方法—byte[]

    作用:
    将字符串对象转换成字节数组,即返回一个byte数组

    示例:

    byte[] byteArray = "abcdef".getBytes();
    for(int i=0;i<byteArray.length;i++){
    	System.out.println(byteArray[i]);
    }
    
    • 1
    • 2
    • 3
    • 4

    运行:
    在这里插入图片描述

    7)indexOf()方法–int

    作用:
    判断某子字符串在当前字符串中第一次出现的索引下标

    示例:

    //6
    System.out.println("oraclejava++java#".indexOf("java"));
    
    //-1
    System.out.println("abc".indexOf("qwe"));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    判断最后一次出现的索引可以用lastIndexOf()

    8)isEmpty()方法–boolean

    作用:
    判断某个字符串是否为空

    示例:

    
    String e = "";
    System.out.println(e.isEmpty());  //true
    
    • 1
    • 2
    • 3
    //源码
    public boolean isEmpty(){
    	return value.length == 0;
    }
    //底层调用了字符串对象的length()方法
    //System.out.println("abc".length());   //3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    判断数组长度和字符串长度不一样,判断数组长度是length属性,判断字符串长度是length()方法

    9)replace()方法—String

    作用:
    完成字符串对象某部分的替换

    示例:

    String newString = "http://code-9527.com".replace("http://","https://");
    System.out.println(newString);
    System.out.println("name=9527&age=22".replace("=",":"));
    
    • 1
    • 2
    • 3

    10)split()方法----String[]

    作用:
    拆分字符串

    示例:

    String[] ymd = "2022-11-16".split("-");
    for(int i=0;i<ymd.length;i++){
        System.out.println(ymd[i]);
    }
    //输出结果:2022 11 16
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    11)subString()方法—String

    作用:
    截取字符串

    示例:

    //传入要截取的起始下标
    String a = "http://www.baidu.com".subString(7);
    
    //重载后可以:
    subString(int beginIndex,int endIndex)
    //注意是左闭右开
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    12)toCharArray()方法—char[]

    作用:
    将字符串转换成char数组

    示例:

    char[] chars = "中国人".toCharArray();
    for(int i=o;i<chars.length;i++){
    	System.out.println(char[i]);
    }
    
    //输出: 中 国 人
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    13)toLowerCase()方法—String

    作用:
    将字符串中的大写字母转化为小写

    示例:

    String str ="AbcDEF".toLowerCase();
    //即abcdef
    
    //相反的:
    toUpperCase()方法,将小写转化为大写
    
    • 1
    • 2
    • 3
    • 4
    • 5

    14)trim()方法—String

    作用:
    去除字符串前后的空白

    示例:

    String s = "    hello World   ".trim();
    
    //输出hello world ,注意去除的只是前后的空格
    
    • 1
    • 2
    • 3

    15)valueOf()方法—String

    作用:
    将非字符串转化成字符串。
    “String类中只有一个方法是静态的,不用new对象—valueOf()方法”

    示例:

    String s1 = String.valueOf(true);
    String s2 = String.valueOf(100);
    
    • 1
    • 2

    当静态方法valueOf传入参数是一个对象的时候,会自动调用该对象的toString()方法

    class Customer{
    //未重写toString方法
    }
    String s1 = String.valueOf(new Customer());
    //输出s1结果是Customer@10f7689
    
    • 1
    • 2
    • 3
    • 4
    • 5

    为什么println输出一个引用时,默认调用引用的toString()方法?

    println源码中:String s = String.valueOf(x); 而valueOf方法又调用了toString()

    二、StringBuffer类

    在实际的开发中,进行字符串频繁的拼接,又什么影响?
    😉
    Java中,字符串是不可变的,每一次拼接都会产生新的字符串,从而占用大量的方法区内存,造成空间浪费

    Strings = "abc";
    s += "hello";
    //以上,在方法区字符串常量池中共创建了三个对象
    
    • 1
    • 2
    • 3

    当进行大量的字符串拼接时,用JDK中自带的java.lang.StringBuffer和java.lang.StringBuilder(Buffer即缓冲)

    1、StringBuffer类的构造方法

    构造一个其中不带字符的字符串缓冲区,初始容量为16字符
    
    //源码
    public StringBuffer(){
    	super(16); //调用了父类的构造方法
    }
    
    //父类
    AbstractStringBuilder(int capacity) {
            if (COMPACT_STRINGS) {
                value = new byte[capacity];
                coder = LATIN1;
            } else {
                value = StringUTF16.newBytesFor(capacity);
                coder = UTF16;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    StringBuffer底层实际是一个byte[]数组,往StringBuffer中放字符串,实际上是放到byte数组中了

    //创建一个初始化容量为16的byte[]数组(字符串缓冲区对象)
    StringBuffer sb = new StringBuffer();
    //append方法拼接字符串
    sb.append("a");
    sb.append("b");
    //append方法和println相似,对各类型数据有重载
    sb.append(3.14);
    sb.append(true);
    System.out.println(sb);   //结果:ab3.14true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:

    append方法中调用了arrayCopy()方法,在进行字符串追加的时候,若byte数组满了,会自动扩容

    2、String类和StringBuffer类的区别

    • String类底层是一个private final byte[] value
    • StringBuffer类底层是一个byte[] value

    图示:

    在这里插入图片描述

    String和StringBuffer的底层虽然都是一个byte[]数组,但String的byte[]用了final修饰,而数组一旦创建长度不可变,且final修饰的引用一旦指向某个对象,就不可再指向其他对象,故String不可变
    在这里插入图片描述

    如何优化StringBuffer的性能?

    在创建StringBuffer的时候,尽可能给定一个初始化容量(即使用有参构造),以减少底层数组扩容的次数。(预估后,给一个大点的初始化容量,默认16)

    StringBuffer sb = new StringBuffer(100);
    
    • 1

    初始化容量太小,追加(拼接)没几次数组就满了,底层就得调用arrayCopy扩容,从而影响效率。

    3、StringBuffer和StringBuilder的区别

    StringBulider sb = new StringBulider();
    sb.append("code");
    sb.append(9527);
    ……
    
    • 1
    • 2
    • 3
    • 4

    不同之处:

    StringBuffer中的方法都有synchronized关键字修饰,表示StringBuffer在多线程环境下运行是安全的,相反,StringBuffer是非线程安全的

    三、基础类型对应的8个包装类

    1、包装类出现的背景

    Java中为8种基本数据类型对应准备了8种包装类型,8种包装类属于引用数据类型,父类是Object。包装类存在的意义如图:
    在这里插入图片描述

    //写段代码帮助理解包装类的实现
    
    public class MyInt{
        int value;
        public MyInt(){
            
        }
        public MyInt(int value){
            this.value = value;
        }
    }
    --------
    //通过构造方法把100包装成了对象
    MyInt mi = new MyInt(100);
    //此时图中的doSome()方法可传参了
    doSome(mi);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、八种包装类

    八种包装类
    其中,前六种的父类的Number类。Boolean和Character的父类是Object。Number类是一个抽象类,不能直接new对象。

    3、包装类的构造方法和常量

    Integer类:

    //Integer的两个构造方法:
    //Integer(int)
    //Integer("String")
    Integer x = new Integer(100);
    Integer y = new Integer("123");
    //Integer类已经重写了toString()方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Double类和Integer类相似:

    Double d1 = new Double(1.23);
    Double d2 = new Double("1.23");
    
    • 1
    • 2

    通过访问包装类常量,可以获取最大值和最小值

    System.out.println("int的最大值:" + Integer.MAX_VALUE);
    //Integer.MIN_VALUE
    //Byte.MIN_VALUE
    
    • 1
    • 2
    • 3

    4、自动装箱和自动拆箱

    了解自动装箱、拆箱前先看一下装箱、拆箱:

    装箱
    //通过构造方法,将基本数据类型转化为引用数据类型
    Integer i = new Integer(123);
    
    ------------
    拆箱
    //通过xxValue方法,将引用数据类型转化为基本数据类型
    int retValue = i.intValue();
    float f = i.floatValue();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    自动装箱与自动拆箱:

    //自动装箱
    Integer x = 100;
    //自动拆箱
    int y = x;
    
    • 1
    • 2
    • 3
    • 4
    Integer z = 1000;
    //+两边要求基本数据类型,故z自动拆箱
    System.out.println(z+1);
    
    • 1
    • 2
    • 3
    Integer a = 1000;
    Integer b = 1000;
    System.out.println(a == b);//,两对象的内存地址不同,false
    
    //虽然是自动装箱,但本质还是Integer a = new Integer(1000);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    比较坑爹的一个点:

    //这里是false
    Integer a = 128;
    Integer b = 128;
    System.out.println(a == b);
    ---------
    //这里是true
    Integer a = 127;
    Integer b = 127;
    System.out.println(a == b);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Java中,为了提高程序效率,将[-128,127]之间所有的包装对象提前创建好,放到了方法区的整数型常量池中,这个区间的数据不再需要new,直接从整数型常量池中取用,所哟后者是true

    示意图:

    Integer类加载的时候,会初始化整数型常量池,256个对象
    在这里插入图片描述

    5、Integer类的常用方法(拆箱)

    Integer x = new Integer("123");  //123
    Integer y = new Integer("string"); //error
    Integer z = new Integer("中文");  //error
    
    • 1
    • 2
    • 3

    不是一个数字的字符串时,不能包装成Integer类型,编译不报错(因为构造方法中允许传入一个字符串)但运行出错。
    在这里插入图片描述

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

    四、日期相关类

    import java.util.Date;
    
    • 1

    1、获取系统当前时间

    //调用Date类的无参构造,精确到毫秒
    
    Date nowTime = new Date();
    
    //Date类的toString方法已经被重写,英文格式的日期
    System.out.println(nowTime);
    //整理到这会儿突然有些emo,记录下此刻的时间吧。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    2、日期格式化

    import java.text.SimpleDateFormat;
    
    Date nowTime = new Date();
    //创建日期格式化对象
    //yMd HmsS字符不可变,中间的格式连接符随意
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
    //Date转成了String
    String nowTimeStr = sdf.format(nowTime);
    System.out.println(nowTimeStr); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以上格式化日期的同时,也将Date类型转化成了String类型,那String类型如何转成Date类型

    String time = "2022-11-17 22:36:26 666";
    
    //此处的格式要和上面字符串中的格式一致,否则后面会java.text.ParseException异常
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
    
    Date dateTime = sdf2.parse(time);
    
    System.out.println(dateTime); //Thu Nov 17 22:36:26 CST 2022
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、currentTimeMillis方法

    获取自1970年1月1日00:00:00起,到当前系统时间的毫秒数。

    long nowTimeMillis = System.currentTimeMillis();
    
    • 1

    该方法可用于统计某方法执行所需时长:

    long beginTime = System.currentTimeMillis();
    
    xxx方法执行
    
    long endTime = System.currentTimeMillis();
    //endTime-beginTime
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    除了这个方法,System类中的方法还有:

    • System.g() 建议启动垃圾回收器
    • System.exit(0)退出JVM

    4、Date类的有参构造方法

    
    Date time =  new Date(1); //参数单位是毫秒,即1970-01-01 00:00:00 01
    
    
    • 1
    • 2
    • 3
    //获取昨天此时的时间
    
    Date time2 = new Date(System.currentTimeMillis()-1000*24*60*60);
    
    • 1
    • 2
    • 3

    五、Random类

    1、数字格式化

    import java.text.DecimalFormat;
    
    //#代表任意数字,逗号代表4分位, 点.代表小数点,0代表不够时补0
    DecimalFormat df = new DecimalFormat("###,###.##");
    
    String s2 = df.format(1234.56789);  //1,234.57
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、BigDecimal

    发音big dai sei mao,属于大数据,精度极高,不属于基本数据类型,属于引用数据类型,常用于财务软件当中。

    BigDecimal v1 = new BigDecimal(100);
    BigDecimal v2 = new BigDecimal(200);
    
    //别使用v1+v2
    //v1和v2是引用,别用+
    BigDecimal v3 = v1.add(v2);
    
    //除
    BigDecimal v4 = v2.divide(v1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、产生随机数

    //创建random对象
    Random random = new Random();
    
    //产生一个随机数
    int num1 = random.nextInt();
    
    //netInt(101),即下一个是100
    //所以产生一个[0~100]之间的随机数
    int num2 = random.nexInt(101);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    练习:生成五个不重复的随机数

    /**
     * 先理清思路,梳理逻辑,再提取方法,最后填充代码实现
     * 可先写个长度为5的初始数组,再产生一个个随机数,放入数组
     * 为了保证不重复,进入数组前需要比较(此处需要一个比较是否相同,或元素是否包含于数组的方法)
     */
    
    import java.util.Arrays;
    import java.util.Random;
    public class RandomTest {
        public static void main(String[] args) {
            int[] array = new int[5];
            /**
             * 改一下数组中元素的初始值
             * 赋初值-1
             */
            for(int i=0;i<array.length;i++){
                array[i] = -1;
            }
            int index = 0;
            Random random = new Random();
            while(index<array.length){
                int element = random.nextInt(10001);
                //这里的if条件,一开始可以用汉字先占位表达逻辑
                //if(元素不包含于数组)
                if(!contains2(array,element)){
                    array[index] = element;
                    index++;
                }
    
            }
            //遍历输出即为5个不重复的随机数
            for(int i=0;i<array.length;i++){
                System.out.println(array[i]);
            }
    
        }
    
        /**
         * 判断元素是否包含于数组
         * “是否”即boolean返回类型
         * @param array
         * @param num
         * @return
         */
        public static boolean contains(int[] array,int num){
            Arrays.sort(array);
            //返回>=0的索引即找到了,也就是包含,反之不包含
            return Arrays.binarySearch(array,num) >=0;
        }
    
        /**
         * 不用Arrays类提供的排序和二分查找,我自己用遍历查找
         * @param array
         * @param num
         * @return
         */
        public static boolean contains2(int[] array, int num){
            for(int i=0;i<array.length;i++){
                if(array[i] == num){
                    return true;
                }
            }
            return false;
    
        }
    }
    
    //第一个使用排序会对每次循环中原数组下标发生重新洗牌,赋值出现错位,存在bug,使用contains2方法遍历
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    六、枚举

    就上上面例子中的contains方法,只有两种返回结果,这时使用boolean返回类型很合适,但当返回结果有两种以上,且都是一枚一枚的可以列举出来的时候,boolean就不再满足了=====>枚举诞生。

    1、枚举

    一枚一枚可以列举出来的,建议使用枚举类型,枚举类型属于引用数据类型。枚举编译之后也是生成class文件,枚举中的每一个值可以看作是常量

    //举个例子,先写两个结果的来举例
    
    public enum Result {
        SUCCESS,FAIL
    }
    class TestEnum{
        public static void main(String[] args) {
            Result r = divide(10,0);
            System.out.println(r==Result.SUCCESS ? "计算成功" : "计算失败");
        }
    	//注意这里返回类型是枚举类型
        public static Result divide(int a,int b){
            try{
                int c = a/b;
                return Result.SUCCESS;
            }catch(Exception e){
                return Result.FAIL;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2、枚举类型的定义

    enum 枚举类型名{       //引用数据类型
      枚举值1,枚举值2,枚举值3    //常量
    }
    
    • 1
    • 2
    • 3

    举例:

    public enum Color{
    	RED,BLUE,YELLOW,BLACK
    }
    
    • 1
    • 2
    • 3

    3、switch+枚举

    public enum Season {
        SPRING,SUMMER,AUTUMN,WINTER
    }
    
    • 1
    • 2
    • 3
    switch(){
                case Season.SPRING:       //case中的枚举名必须省略
                    System.out.println("春天");
                    break;
                case Season.SUMMER:
                    System.out.println("夏天");
                    break;
                case Season.AUTUMN:
                    System.out.println("秋天");
                    break;
                case Season.WINTER:
                    System.out.println("冬天");
                    break;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    Qt在Windows系统下检索U盘的插拔
    C++初识 - 引用
    什么是能力?
    【Try to Hack】ip地址
    欧洲云巨头OVHcloud收购边缘计算专家 gridscale
    DOS常用指令
    使用vue3 搭建一个H5手机端访问的项目
    Django项目引入NPM和gulp管理前端资源
    Word处理控件Aspose.Words功能演示:使用 Python 创建 MS Word 文档
    注册树模式
  • 原文地址:https://blog.csdn.net/llg___/article/details/127840680