• 不就是Java吗类和对象 Part II


    5、对象的构造及其初始化

    5.1 如何初始化对象?

    我们知道,在之前的Date这个类中,我们需要调用setDate这个方法来初始化。其实我们还有好几种方法,我们先给大家介绍一下构造方法

    5.2 构造方法

    5.2.1 概念

    1. ⽅法名与类名相同
    2. 没有返回值

    举个栗子:

    public Date() {
        
        }
    
    • 1
    • 2
    • 3

    这就是一个最简单的不带参数的构造方法。在创建对象的时候,由编译器自动调用,并且在整个对象的生命周期内只调用一次。

    当我们实例化一个对象的时候,必须有以下这两部,但不是一定只有这两步

    1. 为对象分配内存

    2. 调用合适的构造方法?

      这句话其实另有玄机,代表构造方法不一定只有一个

    public class Date {
        public int year;
        public int month;
        public int day;
    
        //不带参数的构造方法
        public Date() {
            System.out.println("调用不带参数的构造方法");
        }
    
        //带三个参数的构造方法
        public Date(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
            System.out.println("调用带三个参数的构造方法");
        }
    
        //打印日期
        public void printDate() {
            System.out.println(year + "年" + month + "月" + day + "日");
        }
    
        public static void main(String[] args) {
            //调用不带参数的构造方法
            Date date1 = new Date();
            //调用带三个参数的构造方法
            Date date2 = new Date(2022,05,24);
        }
    }
    
    
    • 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

    image-20220524223407516

    image-20220524223700120

    那么我们要注意:当程序没有构造方法的时候,编译器会默认帮我们生成一个不带参数的构造方法。如果你自己写了构造方法,那么编译器就不会再自动生成一个不带参数的构造方法了,我们可以通过下面的代码进行检验

    public class Date {
        public int year;
        public int month;
        public int day;
        
        //带三个参数的构造方法
        public Date(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
            System.out.println("调用带三个参数的构造方法");
        }
    
        //打印日期
        public void printDate() {
            System.out.println(year + "年" + month + "月" + day + "日");
        }
    
        public static void main(String[] args) {
            //调用不带参数的构造方法
            Date date1 = new Date();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    image-20220524224131954

    我们可以看见,这段代码中,我们自己写了一个带三个参数的构造函数,然后main方法中却调用了不带参数的构造函数,通过图片我们得知,这报错了,是因为我们自己生成构造函数的话,编译器就不会再帮我们生成不带参数的构造函数,所以在主函数调用不带参数的构造函数会报错

    5.2.2 特性

    1. 名字必须与类名相同

      image-20220524224600818

    2. 没有返回值类型,设置为void也不行

      image-20220524224640612

    3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次

    4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)

      重载:方法名相同,参数裂变不同,和返回值没有关系

      image-20220524224953676

    5. 如果用户没有自己定义构造函数,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。(所以一个类是一定至少含有一个构造函数)

    6. 编译器帮我们实现的构造函数:右键->generate->constructer->选中要操作的属性

      编译器帮我们实现show函数:右键->generate->toString()

    7. 构造方法中,可以通过this调用其他构造方法来简化代码

      class Student {
          public String name;
          public int age;
          public double score;
          public String sex;
      
          //不带参数的构造函数
          public Student() {
              this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数
          }
      
          //带三个参数的构造函数
          public Student(String name, int age, double score) {
              this.name = name;
              this.age = age;
              this.score = score;
          }
      
          //带四个参数的构造函数
          public Student(String name, int age, double score, String sex) {
              this.name = name;
              this.age = age;
              this.score = score;
              this.sex = sex;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", score=" + score +
                      ", sex='" + sex + '\'' +
                      '}';
          }
      }
      public class StudentDemo {
          public static void main(String[] args) {
              Student student1 = new Student();
              System.out.println(student1);
          }
      }
      
      
      • 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

      image-20220524225842892

      注意:

      1. this必须放在构造函数的第一行

        public Student() {
                System.out.println("this只能放在第一行");
                this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数
            }
        
        • 1
        • 2
        • 3
        • 4

        image-20220524230048690

      2. 一个构造函数里面只能有一个this

        public Student() {
                this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数
                this("宝娟"2577);
            }
        
        • 1
        • 2
        • 3
        • 4

        image-20220524230322145

      3. 不能成环

        //不带参数的构造函数
            public Student() {
                this("剪秋",22,98);//调用Student类中带4个参数的构造函数
            }
        
            //带三个参数的构造函数
            public Student(String name, int age, double score) {
                this();
                this.name = name;
                this.age = age;
                this.score = score;
            }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12

        image-20220524230438021

    5.3 默认初始化

    我们之前写过的代码就是默认初始化的

    public class Date {
        //默认初始化:初始化为对应的null
        public int year;
        public int month;
        public int day;
    
        public static void main(String[] args) {
           
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.4 就地初始化

    public class Date {
        //就地初始化:不太好
        public int year = 2022;
        public int month = 05;
        public int day = 24;
    
        public static void main(String[] args) {
            
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    代码编译完成后,编译器会将所有就地初始化的语句添加到各个构造函数中

    博客分隔符-笑脸

    就目前为止,我们针对目前所学习的知识,来创建一个猫的类吧

    class Cat {
        public String name;
        public int age;
        public String color;
    
        public Cat() {
            //可以在这里面进行初始化
            this.name = "哈哈";
            this.age = 3;
            this.color = "黑色";
            System.out.println("带一个参数的构造方法");
        }
    
        public Cat(String name, int age, String color) {
            this.name = name;
            this.age = age;
            this.color = color;
            System.out.println("带三个参数的构造方法");
        }
    
        public void sleep() {
            System.out.println(this.name + "正在睡觉");
        }
    
        public void eat() {
            System.out.println(this.name + "正在吃饭");
        }
    }
    public class CatDemo {
        public static void main(String[] args) {
            Cat cat1 = new Cat("啦啦",4,"白色");
            cat1.sleep();
            cat1.eat();
    
            Cat cat2 = new Cat();
            cat2.sleep();
            cat2.eat();
        }
    }
    
    • 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

    image-20220524232121581

    6、封装

    6.1 封装的概念

    面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。封装呢,简单来说,就是套壳屏蔽细节。

    比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

    对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
    封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互(公开的方法 和 私有的属性之间结合)

    封装还可以降低代码的耦合度(意思就是关联程度,比如:name改成myname影响不会特别大)

    6.2 封装实例:猫类

    我们刚刚完成了一个猫类,现在,我们给他改成封装的思考方式

    class Cat {
        //1.我们先把访问修饰限定符改成private
        //这些被private修饰的 属性 或者 方法,只能在当前Cat当中使用->安全,类外不课件
        //对类的实现细节 进行隐藏
        private String name;
        private int age;
        private String color;
    
        //2.我们可以生成一些get set 方法
        //右键->Generate->getter and setter
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        //之前写过的构造函数
        public Cat() {
            //可以在这里面进行初始化
            this.name = "哈哈";
            this.age = 3;
            this.color = "黑色";
            System.out.println("带一个参数的构造方法");
        }
    
        public Cat(String name, int age, String color) {
            this.name = name;
            this.age = age;
            this.color = color;
            System.out.println("带三个参数的构造方法");
        }
    
        //之前自己定义的方法
        public void sleep() {
            System.out.println(this.name + "正在睡觉");
        }
    
        public void eat() {
            System.out.println(this.name + "正在吃饭");
        }
    
        //生成一个show方法
        //右键->Generate->toString()
        //打印:System.out.println(cat);
    
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", color='" + color + '\'' +
                    '}';
        }
    }
    public class CatDemo {
        public static void main(String[] args) {
            Cat cat = new Cat();
            cat.setName("嘻嘻");//又可以通过刚刚生成的setter方法把名字传过去
            String name = cat.getName();//可以通过getter方法获取到名字
            System.out.println(name);
            System.out.println("--------------");
            //name age color是私有属性,类外访问不到
            //cat.name = "嘻嘻";
            //cat.age = 10;
            //cat.color = "⻩⾊";
            System.out.println(cat);
        }
    }
    
    • 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
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    我们可以看到,类里面的属性被设置成了私有,但是我们还生成了一系列gettersetter方法来给类的使用者去获取到相关信息,所以这就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

    那么现在,在主函数我们想要直接访问nameagecolor方法是访问不到的,只能通过gettersetter方法

    image-20220526152200757

    小猫分隔符

    我们接下来继续看,如果我们传入的对象是空的时候,是什么效果?

    public static void main(String[] args) {
            Cat cat = null;
            System.out.println(cat);//传过去null,打印的就是字符串null
        }
    
    • 1
    • 2
    • 3
    • 4

    我们看运行结果,是null

    image-20220526152618544

    博客分隔符-笑脸

    我们在输入数据的时候,同样可以采用构造方法

    public static void main(String[] args) {
            Cat cat1 = new Cat("华妃",4,"橘⻩⾊");
            cat1.show();
            cat1.sleep();
            Cat cat2 = new Cat("欣贵人",14,"红色");
            cat2.show();
            cat2.sleep();
            Cat cat3 = new Cat();
            cat3.sleep();
            cat3.show();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    image-20220526153118837

    但是奇怪的是,cat3我们并没有初始化啊,为什么它叫哈哈了?

    实际上,我们看上面的带一个参数的构造函数

    public Cat() {
         //可以在这里面进行初始化
         this.name = "哈哈";
         this.age = 3;
         this.color = "黑色";
         System.out.println("带一个参数的构造方法");
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们在这里面也进行过初始化,如果我们之后有传入其他的数据,那么就使用我们传过来的数据,要不就使用这里面构造的数据

    6.3 包

    为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。把多个类收集在一起成为一组,称为软件包,有点类似于目录。

    包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。

    6.3.1 导入包中的类

    Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入java.util这个包中的 Date类.

    public class TimeStamp {
        public static void main(String[] args) {
            java.util.Date date = new java.util.Date();
            //得到一个毫秒级别的时间戳
            System.out.println(date.getTime());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们可以通过import语句简化写法

    import java.util.Date;
    
    public class TimeStamp {
        public static void main(String[] args) {
            Date date = new Date();
            System.out.println(date.getTime());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果需要使用 java.util 中的其他类, 可以使用 import java.util.*

    import java.util.*;//导⼊util的包,但是只会访问需要的那个功能
    
    public class TimeStamp {
        public static void main(String[] args) {
            Date date = new Date();
            System.out.println(date.getTime());//时间戳需要用到util下面的功能
            System.out.println("---------------");
            int[] array = {1,3,5,7,9};
            System.out.println(Arrays.toString(array));//toString也需要用到util下面的功能
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    但是有可能,Date功能在util包和sql包下面都有,恰好我们都import了,那么就会报错

    import java.util.*;
    import java.sql.*;
    
    public class TimeStamp {
        public static void main(String[] args) {
            Date date = new Date();
            System.out.println(date.getTime());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20220526154846070

    那么我们需要显示指定才能解决,像下面这样

    import java.util.*;
    import java.sql.*;
    
    public class TimeStamp {
        public static void main(String[] args) {
            java.util.Date date = new java.util.Date();
            System.out.println(date.getTime());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    我们还可以可以使用import static导入包中静态的方法和字段。(不推荐)

    import static java.lang.Math.*;
    
    public class TimeStamp {
        public static void main(String[] args) {
            double x = 30;
            double y = 40;
            // 静态导入的方式写起来更方便一些.
            // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
            double result = sqrt(pow(x, 2) + pow(y, 2));
            System.out.println(result);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注意事项: import C++#include 差别很大。C++ 必须 #include 来引入其他文件内容,,但是 Java 不需要,import 只是为了写代码的时候更方便。import 更类似于 C++ namespaceusing

    6.3.2 自定义包

    ⼀般全部小写,公司域名逆序

    1. 基本规则

      • 在文件的最上方加上一个 package 语句指定该代码在哪个包中.

      • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.baidu.demo )

      • 包名要和代码路径相匹配. 例如创建 com.baidu.demo 的包, 那么会存在一个对应的路径 com/baidu/demo 来存储
        代码.

      • 如果一个类没有 package 语句, 则该类被放到一个默认包中

    2. 创建方法

      1. src->右键->New->Package

        image-20220526160928156

      2. 公司域名逆序,全部小写,中间用.分隔

        image-20220526162659903

      3. 这时候我们就可以看见文件夹创建了

        image-20220526162733596

    6.3.3 访问限定符

    image-20220526163323648

    public:公共的,公开的,哪里都能访问

    default:什么都不写,只能在同一个包里面访问

    private:只能在当前类访问

    具体的访问修饰限定符我们下一篇博客会详细讲解

    6.3.4 包的访问权限控制举例

    Computer类位于pack.test.demo包中,TestComputer位置pack.test.demo2包中:

    Computer:

    package pack.test.demo;
    
    public class Computer {
        private String cpu;//cpu
        private String memory;//内存
        public String screen;//屏幕
        String brand;//品牌
    
        public Computer(String cpu, String memory, String screen, String brand) {
            this.cpu = cpu;
            this.memory = memory;
            this.screen = screen;
            this.brand = brand;
        }
        
        public void PowerOn() {
            System.out.println("开机");
        }
        
        public void PowerOff() {
            System.out.println("关机");
        }
        
        public void SurfInternet() {
            System.out.println("上网");
        }
    }
    
    
    • 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

    TestDemo:

    package pack.test.demo2;
    
    import pack.test.demo.Computer;//引用了pack.test.demo里面的Computer这个类
    
    public class TestComputer {
        public static void main(String[] args) {
            Computer computer = new Computer("CPU","MEMORY","SCREEN","BRAND");
            System.out.println(computer.screen);//public 到处可以访问
            //System.out.println(computer.cpu);//private 只能在当前类访问
            //System.out.println(computer.brand);//默认权限 只能在同一个包下访问
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    image-20220526165201545

    6.3.5 常见的包

    1. java.lang:系统常用基础类(String,object)
    2. java.lang.reflect:Java反射基础包
    3. java.net:网络编程开发包
    4. java.sql:数据库开发支持包
    5. java.util:工具程序包(集合类等),非常重要
    6. java.io:I/O编程开发包

    7、static成员

    7.1 引入

    我们之前创建的Student类,里面有以下这四个属性

    public String name;
    public int age;
    public double score;
    public String sex;
    
    • 1
    • 2
    • 3
    • 4

    那么我们知道,每个人的姓名、年龄、分数和性别可能都是不同的,那么如果这几个同学都是一个班级的,那该怎么表示?

    我们仍然可以创建一个成员变量,然后分别赋值。我们也可以创建一个static修饰的成员变量,被static修饰的成员变量,称之为静态成员(类成员),他不属于某个具体的对象,使所有对象所共享的

    7.2 static修饰成员变量

    static修饰的成员变量,称为静态成员变量。

    它最大的特性就是:不属于某个具体的对象,是所有对象所共享的

    那么他的特性都有:

    1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中

    2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问,而且这样也不需要new对象了

      Student.class;//推荐
      student.class;//不推荐
      
      • 1
      • 2
    3. JDK7及以前,HotSpot(Java虚拟机)中存储在方法区,JDK8及之后,类变量存储在Java堆中

      其实方法区和堆逻辑上都属于同一个等级的关系,只不过在JVM当中实现这个东西的时候,把他放在了堆里

    4. 类变量存储在方法区当中

    5. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

      类在的话就在,不在就没了

    举个栗子吧:

    class Student {
        //成员变量(字段/属性)
        public String name;
        public int age;
        public double score;
        public String sex;
        public static String classRoom = "0301";//静态的成员变量
    
        public Student(String name, int age, double score, String sex) {
            this.name = name;
            this.age = age;
            this.score = score;
            this.sex = sex;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    ", sex='" + sex + '\'' +
                    ", class='" + classRoom +'\'' +
                    '}';
        }
    }
    public class StudentDemo {
        public static void main(String[] args) {
            Student student1 = new Student("甄嬛",20,98,"女");
            Student student2 = new Student("沈眉庄",21,97,"女");
            Student student3 = new Student("安陵容",22,99,"女");
            System.out.println(Student.classRoom);//推荐:类名.静态成员变量
            System.out.println("---------------");
            //也可以通过对象.静态成员变量访问
            System.out.println(student1.classRoom);
            System.out.println(student2.classRoom);
            System.out.println(student3.classRoom);
        }
    }
    
    
    • 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

    image-20220526205054097

    那么我们可以调试一下,看看静态的成员变量是否存在于某个具体的变量当中

    image-20220526205307435

    7.3 static修饰成员方法

    正常情况下,一个类当中成员变量是要设置成private ,成员方法是要设置成public的,但是private修饰之后,在类外是访问不到的。那么假如要是把静态成员变量设置成private修饰的,在类外怎么访问呢?

    class Student2 {
        //成员变量(字段/属性)
        public String name;
        public int age;
        public double score;
        public String sex;
        private static String classRoom = "0301";//静态的成员变量,被private修饰了
    }
    public class StudentDemo2 {
        public static void main(String[] args) {
            System.out.println(Student.classRoom);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    image-20220526205940985

    我们可以通过静态的成员方法,来实现在类外访问被private修饰的静态成员变量

    class Student2 {
        //成员变量(字段/属性)
        public String name;
        public int age;
        public double score;
        public String sex;
        private static String classRoom = "0301";//静态的成员变量,被private修饰了
    
        public static String getClassRoom() {
            return classRoom;
        }
    }
    public class StudentDemo2 {
        public static void main(String[] args) {
            System.out.println(Student2.getClassRoom());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    静态方法的特性:

    1. 不属于某个具体的对象,是类方法

    2. 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者

      Student.getClassRoom();
      student.getClassRoom();
      
      • 1
      • 2
    3. 静态方法没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态成员变量

      class Student2 {
          //成员变量(字段/属性)
          public String name;
          public int age;
          public double score;
          public String sex;
          private static String classRoom = "0301";//静态的成员变量,被private修饰了
      
          public Student2(String name, int age, double score, String sex) {
              this.name = name;
              this.age = age;
              this.score = score;
              this.sex = sex;
          }
      
          public static String getClassRoom() {
              this("齐妃",30,88,"女");//静态成员方法里面是不能调用this方法的
              age = age + 1;//静态成员方法里面是不能引用非静态变量的
              System.out.println(classRoom);//静态成员方法里面可以引用静态成员变量
              return classRoom;
          }
      }
      public class StudentDemo2 {
          public static void main(String[] args) {
              System.out.println(Student2.getClassRoom());
          }
      }
      
      • 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

      image-20220526211822652

    4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用

      class Student2 {
          //成员变量(字段/属性)
          public String name;
          public int age;
          public double score;
          public String sex;
          private static String classRoom = "0301";//静态的成员变量,被private修饰了
      
          public static String getClassRoom() {
              //doClass();//静态成员方法当中不能调用普通方法
              return classRoom;
          }
          
          public void doClass() {
              System.out.println(this.name + "正在上课");
          }
      }
      public class StudentDemo2 {
          public static void main(String[] args) {
              System.out.println(Student2.getClassRoom());
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

      image-20220526212147346

    5. 通过对象.成员变量可能会产生问题

      public static void main(String[] args) {
              Student student = null;//student不指向任何对象
              student.classRoom = "0302";//student不指向任何对象,所以student.classRoom不知道指向的是哪里,就被赋值成0302了
              System.out.println(student.classRoom);
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5

    7.4 static成员变量初始化

    我们要初始化静态的成员变量的时候,一般不会使用构造函数,构造方法中初始化的是与对象相关的实例属性

    静态成员变量的初始化分为两种:就地初始化和静态代码块初始化。

    就地初始化就是刚才那几个例子

    private static String classRoom = "0301";
    
    • 1

    静态代码块初始化,在学习他之前,还得学学代码块

    8、代码块

    8.1 分类

    8.1.1实例代码块/构造代码块

    实例代码块是优先于构造方法执行的。实际上从字节码的角度来看,是把实例代码块当中的内容,拷贝到了构造方法之前。一般用来初始化普通的成员变量

    当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行

    class Student {
        public String name;
        private int age;
        private double score;
        public static String classes;
    
        //构造方法
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
            System.out.println("构造方法:带三个参数");
        }
    
        //实例代码块
        {
            this.name = "瑛贵人";
            this.age = 22;
            this.score = 88.8;
            System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Student student1 = new Student("华妃",18,99.9);
            
        }
    }
    
    • 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

    image-20220526222227581

    8.1.2 静态代码块

    先执行静态代码块,再执行实例代码块。静态代码块只会执行一次。如果写了多个静态代码块,会根据书写的先后顺序来进行赋值(其实就是相当于之前的被复制了)

    class Student {
        public String name;
        private int age;
        private double score;
        public static String classes;
    
        //构造方法
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
            System.out.println("构造方法:带三个参数");
        }
    
        //实例代码块
        {
            this.name = "瑛贵人";
            this.age = 22;
            this.score = 88.8;
            System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
        }
    
        //静态代码块
        static {
            classes = "0301";
            System.out.println("静态代码块:最先执行,一般用来初始化静态成员变量");
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Student student1 = new Student("华妃",18,99.9);
        }
    }
    
    
    • 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

    image-20220526222746722

    静态代码块的注意事项:

    1. 静态代码块也是不依赖对象的,里面不能出现非静态成员

      //静态代码块
      static {
          this("富察贵人",78,11);//静态代码块里面不能出现this
          age = age + 1;//静态代码块里面不能出现非静态成员变量
          classes = "0301";
          System.out.println("静态代码块:最先执行,一般用来初始化静态成员变量");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      image-20220526223215736

    2. 出现两个静态代码块的时候,打印的结果是最后一个static的值(之前的被覆盖了)

      class Student {
          public String name;
          private int age;
          private double score;
          public static String classes;
      
          //构造方法
          public Student(String name, int age, double score) {
              this.name = name;
              this.age = age;
              this.score = score;
              System.out.println("构造方法:带三个参数");
          }
      
          //实例代码块
          {
              this.name = "瑛贵人";
              this.age = 22;
              this.score = 88.8;
              System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
          }
      
          //静态代码块
          static {
              classes = "0301";
          }
          static {
              classes = "0302";
          }
      }
      public class TestDemo {
          public static void main(String[] args) {
              System.out.println(Student.classes);
          }
      }
      
      • 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

      image-20220526223613168

    3. 在普通的成员方法里面是可以使用静态的成员变量的

      class Student {
          public String name;
          private int age;
          private double score;
          public static String classes;
      
          //构造方法
          public Student(String name, int age, double score) {
              this.name = name;
              this.age = age;
              this.score = score;
              System.out.println("构造方法:带三个参数");
          }
      
          public void doClass() {
              //在普通的成员方法里面是可以使用静态局部变量的
              System.out.println(classes);
          }
      }
      public class TestDemo {
          public static void main(String[] args) {
              Student student1 = new Student("华妃",18,99.9);
              student1.doClass();
          }
      }
      
      • 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

      image-20220526224105507

    4. 静态的成员方法中,不能访问非静态的成员

      public static void func() {
              //在静态成员函数里面不能访问非静态成员
              doclass();
              System.out.println(age);
              //在静态成员函数里面访问静态局部变量可以
              System.out.println(classes);
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      image-20220526224424288

    5. 静态代码块先执行,并且只执行一次,在类加载阶段执行

      class Person {
          public String name;
          public int age;
          public Person(String name,int age) {
              this.name = name;
              this.age = age;
              System.out.println("构造方法");
          }
      
          {
              System.out.println("实例代码块");
          }
      
          static {
              System.out.println("静态代码块");
          }
      }
      public class TestDemo2 {
          public static void main(String[] args) {
              Person person1 = new Person("瑛贵人",20);
              System.out.println("----------------------");
              Person person2 = new Person("浣碧",22);
          }
      }
      
      
      • 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

      image-20220527151043230

    6. 实例代码块只有在创建对象的时候执行

      class Student {
          public String name;
          private int age;
          private double score;
          public static String classes;
      
          //构造方法
          public Student(String name, int age, double score) {
              this.name = name;
              this.age = age;
              this.score = score;
              System.out.println("构造方法:带三个参数");
          }
      
          //实例代码块
          {
              this.name = "瑛贵人";
              this.age = 22;
              this.score = 88.8;
              System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
          }
      
          //静态代码块
          static {
              classes = "0301";
          }
          static {
              classes = "0302";
              System.out.println("打印静态代码块");
          }
      }
      public class TestDemo {
          public static void main(String[] args) {
              System.out.println(Student.classes);
          }
      }
      
      
      • 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

      image-20220526225555905

    9、内部类

    当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

    9.1 分类

    1. 实例内部类
    2. 静态内部类
    3. 匿名内部类

    9.2 实例内部类

    用法:

    class OuterClass {
        public int data1 = 10;
        private int data2 = 20;
        public static int data3 = 30;
    
        class InnerClass {
            //这里就是实例内部类
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 如何实例化内部类对象:

      1.外部类名.内部类名 变量 = new 外部类名().new 内部类名(); 
      2.外部类名 变量1 = new 外部类名();
        外部类名.内部类名 变量2 = 变量1.new 内部类名();
      
      • 1
      • 2
      • 3

      举例:

      方式一:

      OuterClass.InnerClass innerclass1 = new OuterClass().new InnerClass();
      
      • 1

      方式二:

      OuterClass outerclass = new OuterClass();
      OuterClass.InnerClass innerclass2 = outerclass.new InnerClass();
      
      • 1
      • 2
    2. 实例内部类当中不能定义静态的成员变量的。(非得要定义,可以这样:定义一个final属性的值,这样的话编译期间就确定值了,public static final int data = 10;)

      class OuterClass {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          class InnerClass {
              //这里就是实例内部类
              public static int data4 = 40;//必须要加final才不报错
              public static final int data5 = 50;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      image-20220527100321231

      1. 如果实力内部类当中,创建的变量跟外部类当中的变量重名了,优先使用自己的

        class OuterClass {
            public int data1 = 10;
            private int data2 = 20;
            public static int data3 = 30;
        
            class InnerClass {
                //这里就是实例内部类
                public int data1 = 40;
                private int data2 = 50;
        
                public void display() {
                    System.out.println("data1 = " + data1);
                    System.out.println("data2 = " + data2);
                }
            }
        }
        public class TestDemo1 {
            public static void main(String[] args) {
                OuterClass outerClass = new OuterClass();
                OuterClass.InnerClass innerClass = outerClass.new InnerClass();
                innerClass.display();
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23

        image-20220527100730446

        那么我们如果想要获取到外部类的成员变量呢?

        外部类类名.外部类成员变量

        class OuterClass {
            public int data1 = 10;
            private int data2 = 20;
            public static int data3 = 30;
        
            class InnerClass {
                //这里就是实例内部类
                public int data1 = 40;
                private int data2 = 50;
        
                public void display() {
                    System.out.println("内部类当中的data1 = " + this.data1);
                    System.out.println("内部类当中的data2 = " + this.data2);
                    System.out.println("外部类当中的data1 = " + OuterClass.this.data1);
                    System.out.println("外部类当中的data2 = " + OuterClass.this.data2);
                }
            }
        }
        public class TestDemo1 {
            public static void main(String[] args) {
                OuterClass outerClass = new OuterClass();
                OuterClass.InnerClass innerClass = outerClass.new InnerClass();
                innerClass.display();
            }
        }
        
        • 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

        image-20220527101047796

        注意:我们还使用了this,我们可以注意到:实力内部类当中,不仅有自己的this,也有外部类的this

      2. 不能包含静态的方法

        class OuterClass {
            public int data1 = 10;
            private int data2 = 20;
            public static int data3 = 30;
        
            class InnerClass {
                //这里就是实例内部类
                public int data1 = 40;
                private int data2 = 50;
        
                public static void func() {
                    System.out.println("实例内部类里面不能出现静态方法");
                }
                public void display() {
                    System.out.println("内部类当中的data1 = " + this.data1);
                    System.out.println("内部类当中的data2 = " + this.data2);
                    System.out.println("外部类当中的data1 = " + OuterClass.this.data1);
                    System.out.println("外部类当中的data2 = " + OuterClass.this.data2);
                }
            }
        }
        public class TestDemo1 {
            public static void main(String[] args) {
                OuterClass outerClass = new OuterClass();
                OuterClass.InnerClass innerClass = outerClass.new InnerClass();
                innerClass.display();
            }
        }
        
        
        • 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

        image-20220527101333035

      3. 在实力内部类里面的普通的成员方法里面,不能定义static final属性的值

        public void display() {
            static final int c = 90;
        }
        
        • 1
        • 2
        • 3

        image-20220527101522099

      4. 匿名对象:一般使用在只用一次的时候,但是每访问一次都要重新new一下

        public static void main(String[] args) {
                //匿名对象:new OuterClass()
                System.out.println(new OuterClass().data1);
                System.out.println(new OuterClass().data1);//每访问一次就需要重新new一下
            }
        
        • 1
        • 2
        • 3
        • 4
        • 5

      代码汇总:

      class OuterClass {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          class InnerClass {
              //这里就是实例内部类
              public int data1 = 40;
              private int data2 = 50;
              public static final int data4 = 60;
      
              /*public static void func() {
                  System.out.println("实例内部类里面不能出现静态方法");
              }*/
              public void display() {
                  //static final int c = 90;
                  System.out.println("内部类当中的data1 = " + this.data1);
                  System.out.println("内部类当中的data2 = " + this.data2);
                  System.out.println("--------------------------");
                  System.out.println("外部类当中的data1 = " + OuterClass.this.data1);
                  System.out.println("外部类当中的data2 = " + OuterClass.this.data2);
                  System.out.println("--------------------------");
                  System.out.println("外部类当中的data3 = " + OuterClass.this.data3);
                  System.out.println("内部类当中的data4 = " + this.data4);
              }
          }
      }
      public class TestDemo1 {
          public static void main2(String[] args) {
              //匿名对象:new OuterClass()
              System.out.println(new OuterClass().data1);
              System.out.println(new OuterClass().data1);//每访问一次就需要重新new一下
          }
          public static void main(String[] args) {
              OuterClass outerClass = new OuterClass();
              OuterClass.InnerClass innerClass = outerClass.new InnerClass();
              innerClass.display();
          }
      }
      
      • 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

    9.3 静态内部类

    用法:

    class OuterClass2 {
     public int data1 = 10;
     private int data2 = 20;
     public static int data3 = 30;
     
     static class InnerClass2 {
         
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 如何实例化静态内部类

      外部类.内部类 变量 = new 外部类.内部类();
      
      • 1

      举例:

      class OuterClass2 {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          static class InnerClass2 {
      
          }
      }
      public class TestDemo2 {
          public static void main(String[] args) {
              OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    2. 在静态内部类当中,只能访问外部类当中静态成员变量

      要想访问外部类非静态成员变量,就要提供外部类对象(即new一个外部类对象)

      class OuterClass2 {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          static class InnerClass2 {
              public int data4 = 40;
              private int data5 = 50;
              public static int data6 = 60;
              
              public void show() {
                  //这样是访问不了的
                  System.out.println(data1);
                  System.out.println(data2);
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      image-20220527105158924

      我们需要先new一个外部类对象,在通过外部类对象去调用

      class OuterClass2 {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          static class InnerClass2 {
              public int data4 = 40;
              private int data5 = 50;
              public static int data6 = 60;
      
              OuterClass2 outer = new OuterClass2();//new一个外部类对象
      
              public void show() {
                  System.out.println(outer.data1);//外部类对象.成员变量
                  System.out.println(outer.data2);//外部类对象.成员变量
                  System.out.println(data3);//外部类当中data3是静态的,可以直接访问
                  System.out.println(data4);
                  System.out.println(data5);
                  System.out.println(data6);
              }
          }
      }
      public class TestDemo2 {
          public static void main(String[] args) {
              OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
              innerClass2.show();
          }
      }
      
      • 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

      image-20220527105618541

    9.3 匿名内部类

    1. 匿名内部类可以创建变量
    2. 匿名内部类可以写方法
    3. 匿名内部类还可以对被匿名的类里面的方法进行重写

    写法:

    class Test {
    
    }
    public class TestDemo3 {
        public static void main(String[] args) {
            new Test() {
    
            }.test();
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码:

    class Test {
        public int a = 19;
    
        public void test() {
            System.out.println("test()");
        }
    }
    public class TestDemo3 {
        public static void main(String[] args) {
            new Test() {
                public int c = 10;//1.匿名内部类可以创建变量
                public void test01() {//2.匿名内部类可以写方法
                    System.out.println("test01()");
                }
    
                @Override//匿名内部类还可以对被匿名的类里面的方法进行重写
                public void test() {
                    System.out.println("对test函数进行重写");
                }
            }.test();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    Django CreateView视图
    算法时间复杂度
    基于WebGL、Cesium技术的三维空间可视化
    针对CSP-J/S的每日一练:Day7
    企业级环境部署:在 Linux 服务器上如何搭建和部署 Python 环境?
    【经验分享】如何使用VSCode对比两个文件
    软考中级软件设计师--4.数据库知识
    【云原生之Docker实战】使用Docker部署Homepage应用程序仪表盘
    怎么用电脑制作证件照?使用这个工具就可以了
    九科-模块化-创建目录_如果不存在
  • 原文地址:https://blog.csdn.net/m0_53117341/article/details/126091423