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

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

一、static

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

public class Person {
    /* static 可以修饰成员变量 */
    private static int age;

    /* static 可以修饰成员方法 */
    public static void eat() {

    }

    /* static 可以修饰嵌套类 */
    static class Head {

    }
}

(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;
}

统计 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++;
    }
}

☘️ 没有被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("-", "");
    }
}

☘️ 类方法内部坚决不能使用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()");
    }
}

☘️ 没有被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()");
        }
    }
}
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();
    }
}

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

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();
    }
}

(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);
    }
}

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

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

✏️ 编译器会将初始化代码块中的代码复制到每个 构造方法的头部(每创建一个对象就会执行一次初始化代码块中的代码)
✏️ 先执行初始化代码块中的代码,后执行构造方法中的代码
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 = "日本神奈川县川崎市";
    }
}

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

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;
    }
}

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

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

✏️ 当一个 类被初始化的时候会执行 静态初始化块中的代码
✏️ 当一个类第一次被主动使用的时候,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) {

    }
}

在这里插入图片描述

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

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

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()");
    }
}

六、单例模式

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

class Rocket { /* 饿汉式 */
    private static Rocket instance = new Rocket();

    // 构造方法私有化
    private Rocket() {

    }

    /**
     * 返回唯一的 Rocket 实例
     */
    public static Rocket getInstance() {
        return instance;
    }
}

懒汉式:

class Rocket { /* 懒汉式: 有线程安全问题 */
    private static Rocket instance = null;

    // 构造方法私有化
    private Rocket() {

    }

    /**
     * 返回唯一的 Rocket 实例
     */
    public static Rocket getInstance() {
        if (instance == null)
            instance = new Rocket();
        return instance;
    }
}

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

相关文章
|
7天前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
2月前
|
算法 Java
Java语言实现链表反转的方法
这种反转方法不需要使用额外的存储空间,因此空间复杂度为,它只需要遍历一次链表,所以时间复杂度为,其中为链表的长度。这使得这种反转链表的方法既高效又实用。
187 0
|
2月前
|
存储 Java 数据处理
Java映射操作:深入Map.getOrDefault与MapUtils方法
结合 `getOrDefault`方法的简洁性及 `MapUtils`的丰富功能,Java的映射操作变得既灵活又高效。合理地使用这些工具能够显著提高数据处理的速度和质量。开发人员可以根据具体的应用场景选择适宜的方法,以求在性能和可读性之间找到最佳平衡。
101 0
|
2月前
|
缓存 人工智能 NoSQL
Java中实现Token设置过期时间的方法
本文介绍了在Java应用中实现Token设置过期时间的多种方法,包括使用JWT和Redis缓存,并结合定时任务清理过期Token,以提升系统安全性与用户隐私保护。
264 0
|
2月前
|
算法 Java 开发者
Java 项目实战数字华容道与石头迷阵游戏开发详解及实战方法
本文介绍了使用Java实现数字华容道和石头迷阵游戏的技术方案与应用实例,涵盖GUI界面设计、二维数组操作、游戏逻辑控制及自动解法算法(如A*),适合Java开发者学习游戏开发技巧。
194 46
|
3月前
|
安全 Java API
Java 集合高级应用与实战技巧之高效运用方法及实战案例解析
本课程深入讲解Java集合的高级应用与实战技巧,涵盖Stream API、并行处理、Optional类、现代化Map操作、不可变集合、异步处理及高级排序等核心内容,结合丰富示例,助你掌握Java集合的高效运用,提升代码质量与开发效率。
203 0
|
3月前
|
算法 搜索推荐 Java
Java中的Collections.shuffle()方法及示例
`Collections.shuffle()` 是 Java 中用于随机打乱列表顺序的方法,基于 Fisher-Yates 算法实现,支持原地修改。可选传入自定义 `Random` 对象以实现结果可重复,适用于抽奖、游戏、随机抽样等场景。
126 0
|
16天前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
49 0
|
28天前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
64 16