• Java面向对象进阶


    Java面向对象

    基本类型包装类

    包装类介绍

    Java提供的基本类型包装类,使得Java能够更好的体现面向对象的思想,同时也使得基本类型能够支持对象操作

    image-20231012211835165

    包装类实际上就是将我们的基本数据类型,封装成一个类(运用了封装的思想)

    自动装箱/拆箱:

    Integer i = 10;
        int a = i;
    
    • 1
    • 2

    本质上就是:

    Integer i =  Integer.valueOf(10);//自动装箱
        int a = i.intValue();  //自动拆箱
    
    • 1
    • 2

    包装类对象比较:

    public static void main(String[] args) {
        Integer a = new Integer(10);
        Integer b = new Integer(10);
        System.out.println(a == b);    //虽然a和b的值相同,但是并不是同一个对象,所以说==判断为假
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public static void main(String[] args) {
        Integer a = 10, b = 10;
        System.out.println(a == b);//true
    }
    
    • 1
    • 2
    • 3
    • 4

    通过自动装箱转换的Integer对象,如果值相同,得到的会是同一个对象:IntegerCache会默认缓存-128~127之间的所有值,将这些值提前做成包装类放在数组中存放,这是为了提升效率,因为小的数使用频率非常高,有些时候并不需要创建那么多对象,创建对象越多,内存也会消耗更多。同样的,Long、Short、Byte类型的包装类也有类似的机制。

    字符串常用方法:

    Integer i = Integer.valueOf("5555");//字符串转Integer
    Integer i = Integer.decode("0xA6");//十六进制和八进制的字符串进行解码
     System.out.println(Integer.toHexString(166));//十进制的整数转换为8进制
    
    • 1
    • 2
    • 3

    特殊包装类

    用于计算超大数字的BigInteger

    BigInteger i = BigInteger.valueOf(Long.MAX_VALUE);
    i = i.multiply(BigInteger.valueOf(Long.MAX_VALUE));   //即使是long的最大值乘以long的最大值,也能给你算出来
    i = i.pow(100);   //long的最大值来个100次方吧
    i = i.divide(BigDecimal.valueOf(3), 100, RoundingMode.CEILING);
    //计算10/3的结果,精确到小数点后100位
    //RoundingMode是舍入模式,就是精确到最后一位时,该怎么处理,这里CEILING表示向上取整
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    数组

    一维数组

    数组是相同类型数据的有序集合,数组可以代表任何相同类型的一组内容(包括引用类型和基本类型)其中存放的每一个数据称为数组的一个元素

    数组类型比较特殊,它本身也是类,但是编程不可见(底层C++写的,在运行时动态创建)即使是基本类型的数组,也是以对象的形式存在的,并不是基本数据类型。

    public static void main(String[] args) {
        int[] array = new int[10];   //在创建数组时,需要指定数组长度,也就是可以容纳多个int变量的值
      	Object obj = array;   //因为同样是类,肯定是继承自Object的,所以说可以直接向上转型
    }
    
    • 1
    • 2
    • 3
    • 4

    数组每个位置上都有默认值,如果是引用类型,就是null,如果是基本数据类型,就是0,或者是false

    数组的下标是从0开始的,不是从1开始的

    int[] array = new int[10];
    array[0] = 888;   //就像使用变量一样,是可以放在赋值运算符左边的,我们可以直接给对应下标位置的元素赋值
    
    • 1
    • 2

    数组本身也是一个对象,数组对象也是具有属性的

    int[] array = new int[10];
    System.out.println("当前数组长度为:"+array.length);   //length属性是int类型的值,表示当前数组长度,长度是在一开始创建数组的时候就确定好的
    
    • 1
    • 2

    由于基本数据类型和引用类型不同,所以说int类型的数组时不能被Object类型的数组变量接收的;如果是引用类型的话,是可以的

    String[] arr = new String[10];
    Object[] array = arr;    //数组同样支持向上转型	
    Object[] arr = new Object[10];
    String[] array = (String[]) arr;   //也支持向下转型
    
    • 1
    • 2
    • 3
    • 4

    可变长参数

    可变长参数本质就是一个数组:

    public void test(String... strings){   //strings这个变量就是一个String[]类型的
        for (String string : strings) {
            System.out.println(string);   //遍历打印数组中每一个元素
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    字符串

    String类

    每个用双引号括起来的字符串,都是String类型的一个实例对象

    String str = "Hello World!";
    String str = new String("Hello World!");  //这种方式就是创建一个新的对象
    
    • 1
    • 2

    直接使用双引号创建的字符串,如果内容相同,为了优化效率,那么始终都是同一个对象:

    String str1 = "Hello World";
    String str2 = "Hello World";
    System.out.println(str1 == str2);
    
    • 1
    • 2
    • 3

    如果我们使用构造方法主动创建两个新的对象,那么就是不同的对象了:

    String str1 = new String("Hello World");
    String str2 = new String("Hello World");
    System.out.println(str1 == str2);
    
    • 1
    • 2
    • 3

    String常用方法:

    System.out.println(str.length());   //length方法可以求字符串长度,这个长度是字符的数量
    //双引号括起来的字符串本身就是一个实例对象
    System.out.println("Hello World".length());   //虽然看起来挺奇怪的,但是确实支持这种写法
    String sub = str.substring(0, 3);   //分割字符串,并返回一个新的子串对象
    String[] strings = str.split(" ");   //使用split方法进行字符串分割,比如这里就是通过空格分隔,得到一个字符串数组
    char[] chars = str.toCharArray();  //字符数组和字符串之间转换
    char[] chars = new char[]{'奥', '利', '给'};
    String str = new String(chars);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    StringBuilder

    StringBuilder类型,实际上是专门用于构造字符串的,我们可以使用它来对字符串进行拼接、裁剪等操作,弥补了字符串不能修改的不足:

    StringBuilder builder = new StringBuilder();   //一开始创建时,内部什么都没有
    builder.append("AAA");   //我们可以使用append方法来讲字符串拼接到后面
    builder.append("BBB");
    System.out.println(builder.toString());   //当我们字符串编辑完成之后,就可以使用toString转换为字符串了
    builder.delete(2, 4);   //删除2到4这个范围内的字符
    
    • 1
    • 2
    • 3
    • 4
    • 5

    正则表达式

    正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等

    限定符表如下:

    字符描述
    *匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。***** 等价于 {0,}
    +匹配前面的子表达式一次或多次。例如,zo+ 能匹配 “zo” 以及 "zoo",但不能匹配 “z”+ 等价于 {1,}
    ?匹配前面的子表达式零次或一次。例如,do(es)? 可以匹配 “do”“does”“doxy” 中的 “do”? 等价于 {0,1}
    {n}n 是一个非负整数。匹配确定的 n 次。例如,o{2} 不能匹配 “Bob” 中的 o,但是能匹配 “food” 中的两个 o
    {n,}n 是一个非负整数。至少匹配n 次。例如,o{2,} 不能匹配 “Bob” 中的 o,但能匹配 “foooood” 中的所有 oo{1,} 等价于 o+o{0,} 则等价于 o*
    {n,m}m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。例如,o{1,3} 将匹配 “fooooood” 中的前三个 oo{0,1} 等价于 o?。请注意在逗号和两个数之间不能有空格。

    多种字符匹配:

    字符描述
    [ABC]匹配 […] 中的所有字符,例如 [aeiou] 匹配字符串 “google runoob taobao” 中所有的 e o u a 字母。
    [^ABC]匹配除了 […] 中字符的所有字符,例如 [^aeiou] 匹配字符串 “google runoob taobao” 中除了 e o u a 字母的所有字母。
    [A-Z][A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母。
    .匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\n\r]
    [\s\S]匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行。
    \w匹配字母、数字、下划线。等价于 [A-Za-z0-9_]

    内部类

    成员内部类

    成员内部类和成员方法、成员变量一样,是对象所有的,而不是类所有的

    成员内部类也可以使用访问权限控制

    public class Test {
        public class Inner {   //内部类也是类,所以说里面也可以有成员变量、方法等,甚至还可以继续套娃一个成员内部类
            public void test(){
                System.out.println("我是成员内部类!");
            }
        }
    }
    public static void main(String[] args) {
        Test test = new Test();
        Test.Inner inner = test.new Inner();
        inner.test();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在成员内部类中,是可以访问到外层的变量的:

    public class Test {
        private final String name;
        
        public Test(String name){
            this.name = name;
        }
        public class Inner {
            public void test(){
                System.out.println("我是成员内部类:"+name);
             		//成员内部类可以访问到外部的成员变量
              	//因为成员内部类本身就是某个对象所有的,每个对象都有这样的一个类定义,这里的name是其所依附对象的
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    静态内部类

    静态内部类就像静态方法和静态变量一样,是属于类的

    不需要依附任何对象,我们可以直接创建静态内部类的对象

    静态内部类由于是静态的,所以相对外部来说,整个内部类中都处于静态上下文(注意只是相当于外部来说)是无法访问到外部类的非静态内容的

    public class Test {
        private final String name;
    
        public Test(String name){
            this.name = name;
        }
    
        public static class Inner {
            public void test(){
                System.out.println("我是静态内部类!");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    局部内部类

    局部内部类就像局部变量一样,可以在方法中定义。

    既然是在方法中声明的类,那作用范围也就只能在方法中了。

    public class Test {
        public void hello(){
            class Inner{   //局部内部类跟局部变量一样,先声明后使用
                public void test(){
                    System.out.println("我是局部内部类");
                }
            }
            
            Inner inner = new Inner();   //局部内部类直接使用类名就行
            inner.test();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    匿名内部类

    匿名内部类是我们使用频率非常高的一种内部类,它是局部内部类的简化版。

    在抽象类和接口中都会含有某些抽象方法需要子类去实现,不能直接通过new的方式去创建一个抽象类或是接口对象,但是我们可以使用匿名内部类。

    在方法中使用匿名内部类,将其中的抽象方法实现,并直接创建实例对象。

    public static void main(String[] args) {
        Student student = new Student() {   //在new的时候,后面加上花括号,把未实现的方法实现了
            @Override
            public void test() {
                System.out.println("我是匿名内部类的实现!");
            }
        };
        student.test();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    匿名内部类中同样可以使用类中的属性(因为它本质上就相当于是对应类型的子类)

    接口也可以通过这种匿名内部类的形式,直接创建一个匿名的接口实现类

    public static void main(String[] args) {
        Study study = new Study() {
            @Override
            public void study() {
                System.out.println("我是学习方法!");
            }
        };
        study.study();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Lambda表达式

    如果一个接口中有且只有一个待实现的抽象方法,那么我们可以将匿名内部类简写为Lambda表达式

    public static void main(String[] args) {
        Study study = () -> System.out.println("我是学习方法!");   //是不是感觉非常简洁!
      	study.study();
    }
    
    • 1
    • 2
    • 3
    • 4

    Lambda表达式的具体规范:

    • 标准格式为:([参数类型 参数名称,]...) ‐> { 代码语句,包括返回值 }
    • 和匿名内部类不同,Lambda仅支持接口,不支持抽象类
    • 接口内部必须有且仅有一个抽象方法(可以有多个方法,但是必须保证其他方法有默认实现,必须留一个抽象方法出来)

    方法引用

    方法引用就是将一个已实现的方法,直接作为接口中抽象方法的实现(当然前提是方法定义得一样才行)

    public interface Study {
        int sum(int a, int b);   //待实现的求和方法
    }
    public static void main(String[] args) {
        Study study = (a, b) -> a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Integer.sum的参数和返回值,跟我们在Study中定义的完全一样,所以说我们可以直接使用方法引用:

    public static void main(String[] args) {
        Study study = Integer::sum;    //使用双冒号来进行方法引用,静态方法使用 类名::方法名 的形式
        System.out.println(study.sum(10, 20));
    }
    
    • 1
    • 2
    • 3
    • 4

    如果是普通从成员方法,我们同样需要使用对象来进行方法引用:

    public static void main(String[] args) {
        Main main = new Main();
        Study study = main::lbwnb;   //成员方法因为需要具体对象使用,所以说只能使用 对象::方法名 的形式
    }
    
    public String lbwnb(){
        return "卡布奇诺今犹在,不见当年倒茶人。";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public static void main(String[] args) {
        Study study = String::new;    //没错,构造方法也可以被引用,使用new表示
    }
    
    • 1
    • 2
    • 3

    异常机制

    程序运行出现我们没有考虑到的情况时,就有可能出现异常或是错误

    异常的类型

    每一个异常也是一个类,他们都继承自Exception

    异常类型本质依然类的对象,但是异常类型支持在程序运行出现问题时抛出也可以提前声明,告知使用者需要处理可能会出现的异常

    运行时异常:在编译阶段无法感知代码是否会出现问题,只有在运行的时候才知道会不会出错

    编译时异常:编译时异常明确指出可能会出现的异常,在编译阶段就需要进行处理(捕获异常)必须要考虑到出现异常的情况

    还有一种类型是错误,错误比异常更严重:比如OutOfMemoryError就是内存溢出错误

    自定义异常

    异常其实就两大类,一个是编译时异常,一个是运行时异常

    编译时异常只需要继承Exception就行了

    运行时异常只需要继承RuntimeException就行了

    还有一种类型是Error,它是所有错误的父类,同样是继承自Throwable的

    抛出异常

    手动抛出一个异常来终止程序继续运行下去,同时告知上一级方法执行出现了问题

    public static int test(int a, int b) {
        if(b == 0)
            throw new RuntimeException("被除数不能为0");  //使用throw关键字来抛出异常
        return a / b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    异常对象携带了我们抛出异常时的一些信息,比如是因为什么原因导致的异常,在RuntimeException的构造方法中我们可以写入原因

    如果我们在方法中抛出了一个非运行时异常,那么必须告知函数的调用方我们会抛出某个异常,函数调用方必须要对抛出的这个异常进行对应的处理才可以

    异常的处理

    出现异常时默认会交给JVM来处理,JVM发现任何异常都会立即终止程序运行,并在控制台打印栈追踪信息

    己处理出现的问题,让程序继续运行下去,就需要对异常进行捕获

    将代码编写到try语句块中,只要是在这个范围内发生的异常,都可以被捕获,使用catch关键字对指定的异常进行捕获

    catch中捕获的类型只能是Throwable的子类,也就是说要么是抛出的异常,要么是错误,不能是其他的任何类型

    public static void main(String[] args) {
        try {    //使用try-catch语句进行异常捕获
            Object object = null;
            object.toString();
        } catch (NullPointerException e){   //因为异常本身也是一个对象,catch中实际上就是用一个局部变量去接收异常
    
        }
        System.out.println("程序继续正常运行!");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果某个方法明确指出会抛出哪些异常,除非抛出的异常是一个运行时异常,否则我们必须要使用try-catch语句块进行异常的捕获,不然就无法通过编译

    如果我们确实不想在当前这个方法中进行处理,那么我们可以抛给上一级

    public static void main(String[] args) throws IOException {  //继续编写throws往上一级抛
        test(10);
    }
    private static void test(int a) throws IOException {
        throw new IOException();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果已经是主方法了,那么就相当于到顶层了,此时发生异常再往上抛出的话,就会直接交给JVM进行处理,默认会让整个程序终止并打印栈追踪信息。

    当代码可能出现多种类型的异常时,我们希望能够分不同情况处理不同类型的异常,就可以使用多重异常捕获:

    try {
      //....
    } catch (RuntimeException e){  //父类型在前,会将子类的也捕获
    
    } catch (NullPointerException e) {   //永远都不会被捕获
    
    } catch (IndexOutOfBoundsException e){   //永远都不会被捕获
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    程序运行时,无论是否出现异常,都会在最后执行任务,可以交给finally语句块来处理:

    try {
        //....
    }catch (Exception e){
                
    }finally {
      	System.out.println("lbwnb");   //无论是否出现异常,都会在最后执行
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    try语句块至少要配合catchfinally中的一个

    断言表达式

    可以使用断言表达式来对某些东西进行判断,如果判断失败会抛出错误,只不过默认情况下没有开启断言,我们需要在虚拟机参数中手动开启一下:

    image-20231014114123210

    断言表达式需要使用到assert关键字,如果assert后面的表达式判断结果为false,将抛出AssertionError错误。

    可以在表达式的后面添加错误信息:

    public static void main(String[] args) {
        int a = 10;
        assert a > 10 : "我是自定义的错误信息";
    }
    
    • 1
    • 2
    • 3
    • 4

    image-20231014114217901

    常用工具类介绍

    数学工具类

    public static void main(String[] args) {
      	//Math也是java.lang包下的类,所以说默认就可以直接使用
        System.out.println(Math.pow(5, 3));   //我们可以使用pow方法直接计算a的b次方
      
      	Math.abs(-1);    //abs方法可以求绝对值
      	Math.max(19, 20);    //快速取最大值
      	Math.min(2, 4);   //快速取最小值
      	Math.sqrt(9);    //求一个数的算术平方根
        Math.sin(Math.PI / 2);     //求π/2的正弦值,这里我们可以使用预置的PI进行计算
        Math.cos(Math.PI);       //求π的余弦值
        Math.tan(Math.PI / 4);    //求π/4的正切值
    
        Math.asin(1);     //三角函数的反函数也是有的,这里是求arcsin1的值
        Math.acos(1);
        Math.atan(0);
        Math.log(Math.E);    //e为底的对数函数,其实就是ln,我们可以直接使用Math中定义好的e
        Math.log10(100);     //10为底的对数函数
        //利用换底公式,我们可以弄出来任何我们想求的对数函数
        double a = Math.log(4) / Math.log(2);   //这里是求以2为底4的对数,log(2)4 = ln4 / ln2
       	ath.ceil(4.5);    //通过使用ceil来向上取整
        Math.floor(5.6);   //通过使用floor来向下取整
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    随机数的生成:

    public static void main(String[] args) {
        Random random = new Random();   //创建Random对象
        for (int i = 0; i < 30; i++) {
            System.out.print(random.nextInt(100)+" ");  //nextInt方法可以指定创建0 - x之内的随机数
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    数组工具类

    打印数组,可以直接通过toString方法转换字符串:

    public static void main(String[] args) {
        int[] arr = new int[]{1, 4, 5, 8, 2, 0, 9, 7, 3, 6};
        System.out.println(Arrays.toString(arr));
    }
    
    • 1
    • 2
    • 3
    • 4

    支持将数组进行排序:

    public static void main(String[] args) {
        int[] arr = new int[]{1, 4, 5, 8, 2, 0, 9, 7, 3, 6};
        Arrays.sort(arr);    //可以对数组进行排序,将所有的元素按照从小到大的顺序排放
        System.out.println(Arrays.toString(arr));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    数组中的内容也可以快速进行填充:

    public static void main(String[] args) {
        int[] arr = new int[10];
        Arrays.fill(arr, 66);
        System.out.println(Arrays.toString(arr));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    快速地对一个数组进行拷贝:

    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4, 5};
        int[] target = Arrays.copyOfRange(arr, 3, 5);   //也可以只拷贝某个范围内的内容
        System.out.println(Arrays.toString(target));
        System.out.println(arr == target);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    将一个数组中的内容拷贝到其他数组中:

    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4, 5};
        int[] target = new int[10];
        System.arraycopy(arr, 0, target, 0, 5);   //使用System.arraycopy进行搬运
        System.out.println(Arrays.toString(target));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    有序的数可以使用二分搜索:

    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4, 5};
        System.out.println(Arrays.binarySearch(arr, 5));   //二分搜索仅适用于有序数组
    }
    
    • 1
    • 2
    • 3
    • 4

    多维数组打印:

    public static void main(String[] args) {
        int[][] array = new int[][]{{2, 8, 4, 1}, {9, 2, 0, 3}};
        System.out.println(Arrays.deepToString(array));    //deepToString方法可以对多维数组进行打印
    }
    
    • 1
    • 2
    • 3
    • 4

    Arrays也为一维数组和多维数组提供了相等判断的方法:

    public static void main(String[] args) {
        int[][] a = new int[][]{{2, 8, 4, 1}, {9, 2, 0, 3}};
        int[][] b = new int[][]{{2, 8, 4, 1}, {9, 2, 0, 3}};
        System.out.println(Arrays.equals(a, b));   //equals仅适用于一维数组
        System.out.println(Arrays.deepEquals(a, b));   //对于多维数组,需要使用deepEquals来进行深层次判断
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    binarySearch(arr, 5)); //二分搜索仅适用于有序数组
    }

    
    多维数组打印:
    
    ~~~java
    public static void main(String[] args) {
        int[][] array = new int[][]{{2, 8, 4, 1}, {9, 2, 0, 3}};
        System.out.println(Arrays.deepToString(array));    //deepToString方法可以对多维数组进行打印
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Arrays也为一维数组和多维数组提供了相等判断的方法:

    public static void main(String[] args) {
        int[][] a = new int[][]{{2, 8, 4, 1}, {9, 2, 0, 3}};
        int[][] b = new int[][]{{2, 8, 4, 1}, {9, 2, 0, 3}};
        System.out.println(Arrays.equals(a, b));   //equals仅适用于一维数组
        System.out.println(Arrays.deepEquals(a, b));   //对于多维数组,需要使用deepEquals来进行深层次判断
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    Java常用类String
    java-net-php-python-ssm二手商品交易平台的设计与实现(2)计算机毕业设计程序
    MYSQL查询已数组形式返回 将查询结果组装成数组、逗号连接或者对象
    如何写A2 经济学大作文?
    RSA中PCKS1, PSS and OAEP的区别
    前端本地存储方案-localForage-vue3中使用
    03、爬取资料---但是失败,仅作为记录
    ARM32开发——GPIO输入
    SkiaSharp 之 WPF 自绘 投篮小游戏(案例版)
    ASP.NET超市便利店在线购物商城源码,针对周边配送系统
  • 原文地址:https://blog.csdn.net/CS_z_jun/article/details/133938435