• 18、Java的类变量、类方法;static 关键字;静态导入;初始化块;静态初始化块;单例模式


    一、static

    ☘️ static 可用来修饰类的成员:成员变量、方法、嵌套类

    public class Person {
        /* static 可以修饰成员变量 */
        private static int age;
    
        /* static 可以修饰成员方法 */
        public static void eat() {
    
        }
    
        /* static 可以修饰嵌套类 */
        static class Head {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (1) static 修饰成员变量

    ☘️ 被static修饰的成员变量可叫做:类变量、静态变量、静态字段
    ☘️ 类变量在程序运行过程中只占用一份固定的内存(存储在方法区
    ☘️ 可通过类名访问
    ☘️ 可通过引用变量名访问(不推荐)

    类名或引用变量名访问静态变量:

    public class VisitStaticField {
        public static void main(String[] args) {
            /* 通过类名访问静态变量 */
            // Book.bookNum = 1
            System.out.println("Book.bookNum = " + Book.bookNum);
    
            Book.bookNum = 11;
    
            /* 通过引用变量名访问静态变量 */
            // book 是引用变量名
            Book book = new Book();
            // book.bookNum = 11
            System.out.println("book.bookNum = " + book.bookNum);
        }
    }
    
    class Book {
        /* bookNum 是一个静态变量 (类变量) */
        public static int bookNum = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    统计 Person 对象的个数:

    public class CountPersonObjectNum {
        public static void main(String[] args) {
            Person p = new Person();
            Person pp = new Person();
            Person ppp = new Person();
            Person pppp = new Person();
            Person ppppp = new Person();
            Person pppppp = new Person();
            Person ppppppp = new Person();
            
            // 当前 Person 对象个数:7
            System.out.println("当前 Person 对象个数:" + Person.personObjNum);
        }
    }
    
    class Person {
        /* personObjNum 记录 Person 对象的个数 */
        public static int personObjNum = 0;
    
        public Person() {
            personObjNum++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    ☘️ 没有被static修饰的成员变量可叫做:实例变量
    ☘️ 实例变量在每个实例内部都有一份内存(存储在空间)
    ☘️ 只能通过引用变量名访问


    (2) static 修饰成员方法

    ☘️ 被 static 修饰的成员方法可叫做:类方法、静态方法
    ☘️ 类方法可通过类名调用
    ☘️ 类方法可通过引用变量名调用
    通过类名或对象名调用类方法:

    public class TestDemo {
        public static void main(String[] args) {
            /* 通过类名调用类方法 */
            String randStr1 = GqUtil.obtainRandomStr();
    
            /* 通过引用变量名(对象名)访问类方法(不推荐) */
            GqUtil util = new GqUtil();
            String randStr2 = util.obtainRandomStr();
    
            // randStr1 = 2185dffafc244193a9c056db2cf7ba93
            System.out.println("randStr1 = " + randStr1);
            // randStr2 = 9beb2ecd2a4842f18692b33354508c16
            System.out.println("randStr2 = " + randStr2);
        }
    }
    
    class GqUtil {
        /**
         * 被 static 修饰的方法叫做类方法(或静态方法)
         * 作用:返回随机字符串
         */
        public static String obtainRandomStr() {
            return UUID.randomUUID().toString().replace("-", "");
        }
    }
    
    • 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

    ☘️ 类方法内部坚决不能使用this

    ✏️ this
    ① 与实例挂钩
    ② 是一个隐藏的、位置最靠前的方法参数
    ③ 是当前对象(实例)的引用
    ④ 类变量、类方法可通过类名访问或调用,与实例没有瓜葛

    ☘️ 类方法中可直接访问类变量和调用其他类方法
    ☘️ 类方法中不可以直接访问实例变量,不可以直接调用实例方法

    public class TestDemo {
        public static void main(String[] args) {
            /* 通过类名调用类方法 */
            String randStr1 = GqUtil.getRandString();
    
            /* 通过引用变量名(对象名)访问类方法(不推荐) */
            GqUtil util = new GqUtil();
            String randStr2 = util.getRandString();
    
            System.out.println("randStr1 = " + randStr1);
            System.out.println("randStr2 = " + randStr2);
        }
    }
    
    class GqUtil {
        /* 一个类变量 */
        public static int count = 6;
    
        public String name = "庆医";
    
        /* 类方法 */
        public static String getRandString() {
            // 类方法中可直接访问类变量
            System.out.println("count = " + count);
    
            /*
                类方法中不能直接访问实例变量 name,
                需创建 GqUtil 的对象, 然后通过对象名访问 name
    
                类方法中不能直接调用实例方法 test2
                需创建 GqUtil 的对象, 然后通过对象名调用 test2
             */
            GqUtil util = new GqUtil();
            System.out.println("name = " + util.name);
            util.test2();
    
            // 类方法中可调用其他类方法
            test1();
            return UUID.randomUUID().toString().replace("-", "");
        }
    
        /* 类方法 */
        public static void test1() {
            System.out.println("类方法 test1()");
        }
    
        /* 实例方法 */
        public void test2() {
            System.out.println("实例方法 test2()");
        }
    }
    
    • 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

    ☘️ 没有被static修饰的方法叫做实例方法(只能通过对象名去调用)
    ☘️ 只能通过对象名去调用
    ☘️ 内部可以使用this
    ☘️ 可直接访问实例变量、实例方法
    ☘️ 可直接访问或调用类变量、类方法(类变量、类方法可通过对象名进行访问或调用)

    二、静态导入

    (1) 静态导入概念

    ☘️ 使用了静态导入后,就可以省略类名访问静态成员(成员变量、方法、嵌套类)

    public class TestClass {
        public static int age = 1;
    
        public static void show() {
            System.out.println("age is " + age);
        }
    
        public static class Other {
            public void other() {
                System.out.println("Other_other()");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    public class TestDemo {
        public static void main(String[] args) {
            // 1
            System.out.println(TestClass.age);
            // age is 1
            TestClass.show();
    
            TestClass.Other other = new TestClass.Other();
            // Other_other()
            other.other();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    静态导入后访问静态成员:

    import static com.gq.TestClass.*; // 静态导入
    
    public class TestDemo {
        public static void main(String[] args) {
            // 1
            System.out.println(age);
            // age is 1
            show();
    
            Other other = new Other();
            // Other_other()
            other.other();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (2) 静态导入使用场景

    import static java.lang.Math.PI;
    
    public class TestDemo {
        public static void main(String[] args) {
            // 62.83185307179586
            System.out.println(2 * PI * 10);
            // 125.66370614359172
            System.out.println(2 * PI * 20);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、如何给实例变量设置初始值

    ☘️ 声明的时候就赋值
    ☘️ 构造方法的时候赋值
    ☘️ 在初始化代码块中赋值

    ✏️ 编译器会将初始化代码块中的代码复制到每个构造方法的头部(每创建一个对象就会执行一次初始化代码块中的代码)
    ✏️ 先执行初始化代码块中的代码,后执行构造方法中的代码

    public class TestDemo {
        public static void main(String[] args) {
            Person person = new Person("庆医");
    
            // age = 17
            System.out.println("age = " + person.age);
            // name = 庆医
            System.out.println("name = " + person.name);
            // money = 67899.99
            System.out.println("money = " + person.money);
            // address = 日本神奈川县川崎市
            System.out.println("address = " + person.address);
        }
    }
    
    class Person {
        /* 声明 age 属性的时候就给 age 赋值 */
        public int age = 17;
        public String name;
        public double money;
        public String address;
    
        public Person(String name) {
            /* 在构造方法中给 name 属性赋值 */
            this.name = name;
        }
    
        /* 在初始化代码块中给 money 和 address 属性赋值 */ {
            money = 67899.99;
            address = "日本神奈川县川崎市";
        }
    }
    
    • 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

    先执行初始化代码块中的代码,后执行构造方法中的代码
    看下面代码,思考打印结果是什么:

    public class TestDemo {
        public static void main(String[] args) {
            Person p = new Person(11, "庆医", 10000);
            System.out.println("age = " + p.age); // age = 12
            System.out.println("name = " + p.name); // name = 庆医
            System.out.println("money = " + p.money); // money = 15000.0
        }
    }
    
    class Person {
        public int age;
        public String name;
        public double money;
    
        public Person(int age, String name, double money) {
            this.name = name;
            this.age = age + this.age;
            this.money = money + this.money;
        }
    
        /* 每次创建 Person 对象都会调用初始化代码块的代码 */
        {
            name = "张浩男";
            age = 1;
            money = 5000;
        }
    }
    
    • 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

    四、如何给类变量设置初始值

    ☘️ 声明的时候就赋值
    ☘️ 在静态初始化(代码)块

    ✏️ 当一个类被初始化的时候会执行静态初始化块中的代码
    ✏️ 当一个类第一次被主动使用的时候,JVM 会对类进行初始化(当一个类第一次被主动使用的时候会执行静态初始化块中的代码)
    ✏️ 静态初始化块中的代码只会被执行一次

    public class TestDemo {
        public static void main(String[] args) {
            new Person();
            new Person(12898);
        }
    }
    
    class Person {
        /* 静态初始化块(作用:为类变量赋初始值) */
        static {
            System.out.println("\n静态初始化块中的代码在类第一次被使用的时候会被执行一次(只执行一次)");
        }
    
        /* 初始化块(为实例变量赋初始值) */ 
        {
            System.out.println("初始化块中的代码会被复制到每个构造方法的头部, 每次创建对象都会执行一次初始化块中的代码");
        }
    
        public Person() {
    
        }
    
        public Person(int money) {
    
        }
    }
    
    • 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

    在这里插入图片描述

    ☘️ 初始化块和静态初始化块都可以有多个(按照它们在源码中出现的顺序依次执行)

    五、继承中出现(静态)初始化块 ☆

    public class TestDemo {
        public static void main(String[] args) {
            new Student();
            /*
                Person_静态初始化块
                Student_静态初始化块 
                Person_初始化块
                Person_Person()
                Student_初始化块
                Student_Person()
             */
        }
    }
    
    class Person {
        // 静态初始化块, 当类第一次被使用的时候会执行一次静态初始化块里面的代码
        static {
            System.out.println("Person_静态初始化块");
        }
    
        // 初始化块, 初始化块里面的代码会被拷贝到每个构造方法的头部
        // 每次创建 Person 对象的时候都会执行一次初始化块里面的代码
        {
            System.out.println("Person_初始化块");
        }
    
        public Person() {
            System.out.println("Person_Person()");
        }
    }
    
    class Student extends Person {
        // 静态初始化块, 当类第一次被使用的时候会执行一次静态初始化块里面的代码
        static {
            System.out.println("Student_静态初始化块");
        }
    
        // 初始化块, 初始化块里面的代码会被拷贝到每个构造方法的头部
        // 每次创建 Student 对象的时候都会执行一次初始化块里面的代码
        {
            System.out.println("Student_初始化块");
        }
    
        public Student() {
            System.out.println("Student_Person()");
        }
    }
    
    • 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

    六、单例模式

    ☘️ Singleton Pattern:如果一个类被设计成单例设计模式,则在整个应用程序运行过程中,该类只能存在一个实例。
    饿汉式:

    class Rocket { /* 饿汉式 */
        private static Rocket instance = new Rocket();
    
        // 构造方法私有化
        private Rocket() {
    
        }
    
        /**
         * 返回唯一的 Rocket 实例
         */
        public static Rocket getInstance() {
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    懒汉式:

    class Rocket { /* 懒汉式: 有线程安全问题 */
        private static Rocket instance = null;
    
        // 构造方法私有化
        private Rocket() {
    
        }
    
        /**
         * 返回唯一的 Rocket 实例
         */
        public static Rocket getInstance() {
            if (instance == null)
                instance = new Rocket();
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    再见!如有错误,请不吝赐教!

  • 相关阅读:
    Java中JVM虚拟机详解
    按关键字搜索lazada商品接口(支持高并发请求多个国家站数据),代码对接教程
    2023.10.10
    大数据(9f)Flink状态编程
    Flink学习6:编程模型
    互联网云厂商大转向:在海外重燃新「战事」
    数据结构----线性表之双向链表
    ALPHA开发板网络方案说明
    String详解及StringBuffer,StringBuilder常用方法简介(含字符常量池)巨详细
    22-06-25 西安 linux(02) 命令、shell脚本开发、linux安装软件
  • 原文地址:https://blog.csdn.net/m0_54189068/article/details/126809228