探索 Java 静态变量(static)的奥秘

目录

一、静态变量初印象

(一)什么是静态变量

(二)静态变量的特点

二、静态变量的使用场景

(一)共享数据

(二)全局常量

(三)工具类中的变量

三、静态变量的访问方式

(一)通过类名访问

(二)通过对象访问(不推荐)

四、静态变量的初始化

(一)声明时初始化

(二)静态代码块初始化

五、静态变量与线程安全

(一)潜在的线程安全问题

(二)解决线程安全问题的方法

六、静态变量的优缺点

(一)优点

(二)缺点

七、总结与最佳实践

(一)总结

(二)最佳实践

宝子们,今天咱要深入探讨一下 Java 中一个非常重要且有点神秘的概念 —— 静态变量(static)。在 Java 的世界里,静态变量就像是隐藏在幕后的魔法力量,一旦掌握,就能让你的代码更加高效、简洁且富有条理。不过别急,咱们一步一步来揭开它的神秘面纱。

一、静态变量初印象

(一)什么是静态变量

想象一下,你有一个班级,班级里有每个学生的个人成绩(非静态变量,每个学生对象都有自己的一份),但同时也有整个班级的平均分(静态变量)。这个平均分不属于任何一个特定的学生,而是整个班级共有的属性,而且无论你通过哪个学生对象去访问这个平均分,它的值都是一样的。这就是静态变量在 Java 中的概念,它是属于类的,而不是属于类的某个实例(对象)。

用代码来表示的话,大概是这样:

class Student {// 非静态变量,每个学生的成绩private int score;// 静态变量,班级的平均分public static double averageScore;
}

(二)静态变量的特点

  • 内存分配:静态变量在类加载的时候就会被分配内存空间,而且只会分配一次,不管你之后创建了多少个这个类的对象,它们都共享这同一个静态变量的内存空间。这就好比班级的平均分这个数据,只需要在内存中存在一份就够了,不需要每个学生都带着一份相同的平均分数据。
  • 生命周期:静态变量的生命周期从类加载开始,一直到整个程序结束才会销毁。这就意味着,只要类被加载了,静态变量就一直在那里,随时准备被使用,不像非静态变量,当对象被销毁时,它也就跟着消失了。

二、静态变量的使用场景

(一)共享数据

在很多实际应用中,我们需要在不同的对象之间共享一些数据,这时候静态变量就派上用场了。比如说,我们在开发一个电商系统,有一个 Product 类代表商品,可能需要记录所有商品的销售总量。这个销售总量不是某个特定商品的属性,而是所有商品共同的统计信息,所以可以用静态变量来表示:

class Product {private String name;private double price;// 静态变量,记录商品的销售总量public static int totalSales = 0;public Product(String name, double price) {this.name = name;this.price = price;}// 模拟商品销售的方法,每次销售会增加销售总量public void sell() {totalSales++;System.out.println(name + " 已售出,当前销售总量:" + totalSales);}
}public class StaticVariableExample1 {public static void main(String[] args) {Product product1 = new Product("手机", 5000);Product product2 = new Product("电脑", 8000);product1.sell();product1.sell();product2.sell();}
}

在这个例子中,totalSales 就是一个静态变量,无论创建了多少个 Product 对象,它们都共享这个 totalSales 的值,并且通过调用 sell 方法可以不断更新销售总量,实现了数据在不同对象间的共享。

(二)全局常量

有时候我们需要定义一些全局的常量,这些常量在整个程序的运行过程中都不会改变,而且多个地方都可能需要用到。比如数学中的圆周率 PI,在一个涉及到几何计算的程序中,很多地方都可能要用到这个值,就可以用静态变量来定义:

class MathUtils {// 静态常量,圆周率public static final double PI = 3.14159;
}public class StaticVariableExample2 {public static void main(String[] args) {double radius = 5;// 计算圆的面积,使用 MathUtils.PI 这个静态常量double area = MathUtils.PI * radius * radius;System.out.println("半径为 " + radius + " 的圆的面积:" + area);}
}

这里的 PI 被定义为 public static final,意味着它是一个公共的、静态的、不可修改的常量。其他类可以直接通过 MathUtils.PI 的方式来访问这个常量,既方便又保证了数据的一致性和安全性。

(三)工具类中的变量

在一些工具类中,我们也经常会用到静态变量。比如一个 StringUtils 工具类,用于处理字符串的各种操作,可能会有一个静态变量来记录某个操作的执行次数,以便进行性能统计或者调试:

class StringUtils {// 静态变量,记录字符串反转操作的执行次数public static int reverseCount = 0;public static String reverse(String str) {reverseCount++;return new StringBuilder(str).reverse().toString();}
}public class StaticVariableExample3 {public static void main(String[] args) {String str1 = "Hello";String reversedStr1 = StringUtils.reverse(str1);System.out.println("反转后的字符串:" + reversedStr1);String str2 = "World";String reversedStr2 = StringUtils.reverse(str2);System.out.println("反转后的字符串:" + reversedStr2);System.out.println("字符串反转操作执行次数:" + StringUtils.reverseCount);}
}

在这个例子中,reverseCount 作为静态变量,在每次调用 reverse 方法时都会递增,从而可以统计出这个方法被调用的次数,方便我们了解工具类的使用情况。

三、静态变量的访问方式

(一)通过类名访问

这是访问静态变量最常用的方式,因为静态变量是属于类的,所以直接用类名加上静态变量名就可以访问它,就像我们前面例子中访问 MathUtils.PI 和 StringUtils.reverseCount 那样:

public class StaticVariableAccess1 {public static void main(String[] args) {// 直接通过类名访问静态变量System.out.println("圆周率:" + MathUtils.PI);}
}

(二)通过对象访问(不推荐)

虽然也可以通过类的对象来访问静态变量,但这种方式不太好,因为它容易让人误解为静态变量是属于对象的,而且从语义上来说也不太准确。不过,Java 是允许这样做的:

public class StaticVariableAccess2 {public static void main(String[] args) {Product product = new Product("电视", 6000);// 通过对象访问静态变量(不推荐)product.totalSales++;System.out.println("销售总量:" + Product.totalSales);}
}

在这个例子中,我们通过 product 对象修改了 totalSales 的值,但实际上这个值是所有 Product 对象共享的,与具体的 product 对象无关。所以为了代码的清晰和可维护性,建议还是通过类名来访问静态变量。

四、静态变量的初始化

(一)声明时初始化

我们可以在声明静态变量的时候就给它赋初始值,就像前面例子中那样:

class SomeClass {// 声明时初始化静态变量public static int num = 10;
}

这种方式简单直接,适合那些初始值在编译时就确定的情况。

(二)静态代码块初始化

如果静态变量的初始化需要一些复杂的逻辑,比如读取配置文件、进行数据库连接等操作,就可以使用静态代码块来初始化静态变量:

class DatabaseConfig {// 静态变量,数据库连接 URLpublic static String url;// 静态变量,数据库用户名public static String username;// 静态变量,数据库密码public static String password;static {// 模拟从配置文件读取数据库配置信息url = "jdbc:mysql://localhost:3306/mydb";username = "root";password = "123456";System.out.println("数据库配置信息已初始化");}
}public class StaticVariableInitialization {public static void main(String[] args) {System.out.println("数据库连接 URL:" + DatabaseConfig.url);}
}

在这个例子中,我们使用静态代码块来初始化数据库连接的相关配置信息,这样可以在类加载时就完成这些初始化操作,保证在后续使用这些静态变量时它们已经被正确初始化。

五、静态变量与线程安全

(一)潜在的线程安全问题

由于静态变量是被所有类的实例共享的,在多线程环境下,如果多个线程同时访问和修改同一个静态变量,就可能会出现数据不一致的问题,也就是线程安全问题。比如我们有一个 Counter 类,其中有一个静态变量用于计数:

class Counter {public static int count = 0;public static void increment() {count++;}
}

如果有多个线程同时调用 increment 方法,就可能会出现问题。因为 count++ 这个操作实际上不是原子性的,它包含了读取 count 的值、将其加 1、再将新值写回内存这三个步骤,在多线程环境下,这些步骤可能会被交错执行,导致最终的结果不是我们预期的。

(二)解决线程安全问题的方法

  • 使用 synchronized 关键字:可以将 increment 方法加上 synchronized 关键字,这样就可以保证同一时间只有一个线程能够进入这个方法,从而避免了数据不一致的问题:

class SynchronizedCounter {public static int count = 0;public static synchronized void increment() {count++;}
}

  • 使用原子类:Java 提供了一些原子类,比如 AtomicInteger,可以用来替代普通的静态变量进行原子操作,保证在多线程环境下的线程安全:

import java.util.concurrent.atomic.AtomicInteger;class AtomicCounter {// 使用原子类来保证线程安全public static AtomicInteger count = new AtomicInteger(0);public static void increment() {count.incrementAndGet();}
}

在实际开发中,需要根据具体的场景选择合适的方法来保证静态变量在多线程环境下的线程安全。

六、静态变量的优缺点

(一)优点

  • 共享数据方便:能够在不同的对象之间方便地共享数据,避免了在每个对象中都存储相同的数据,节省了内存空间,并且可以方便地对共享数据进行统一的管理和操作。
  • 提高性能:由于静态变量在类加载时就被初始化,并且只初始化一次,所以在后续的访问中不需要再次进行初始化操作,相比于每次都创建新的对象和变量,能够提高一定的性能。

(二)缺点

  • 线程安全问题:如前所述,在多线程环境下,如果对静态变量的访问和修改不当,很容易出现线程安全问题,需要额外的措施来保证数据的一致性和正确性。
  • 增加代码的耦合度:过度使用静态变量可能会导致代码的耦合度增加,因为多个地方都可能直接依赖于这些静态变量,如果需要对静态变量的定义或者逻辑进行修改,可能会影响到很多相关的代码,使得代码的维护变得困难。

七、总结与最佳实践

(一)总结

静态变量在 Java 中是一个强大而又需要谨慎使用的特性。它为我们提供了共享数据、定义全局常量和在工具类中使用变量等方便的功能,但同时也带来了线程安全和代码耦合度等方面的挑战。

(二)最佳实践

  • 谨慎使用:不要滥用静态变量,只有在真正需要共享数据或者定义全局常量的情况下才使用,避免因为过度使用而导致代码难以维护和出现各种潜在问题。
  • 保证线程安全:如果静态变量会在多线程环境下被访问和修改,一定要采取合适的措施来保证线程安全,如使用 synchronized 关键字或者原子类。
  • 命名规范:给静态变量起一个有意义、清晰的名字,遵循 Java 的命名规范,以便其他开发人员能够容易地理解其含义和用途。
  • 封装访问:尽量将静态变量的访问封装在合适的方法中,而不是直接对外暴露,这样可以更好地控制对静态变量的操作,提高代码的安全性和可维护性。

宝子们,静态变量虽然有点复杂,但只要我们理解了它的原理和使用场景,并且遵循最佳实践,就能充分发挥它的优势,写出更加优秀的 Java 代码。希望这篇文章能帮助你对 Java 静态变量有一个更深入、更全面的理解,如果在学习过程中有任何问题,随时回来复习哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/497645.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

import org.springframework.data.jpa.repository.JpaRepository<T, ID>;

org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口中的 ID 类型参数。 理解 JpaRepository<T, ID> 中的 T 和 ID&#xff1a; T (Type): T 代表的是你想要操作的 实体类 的类型。例如&#xff0c;如果你有一个名为 User 的实体类&#xff0c;那…

docker项目部署流程(Vue+Spingboot)

文章目录 1.docker安装(Ubuntu)1.卸载原有的docker引擎和旧版本软件包(第一次使用跳过)2.使用存储库安装apt 2.安装nginx并运行3.安装Mysql4.构建jdk镜像1.准备条件(3样东西)拉取ubuntu镜像DockerfileDockerfile简洁版 2.构建java镜像3.创建并运行java容器 5.网络配置1.自定义网…

《童年-高尔基》阅读笔记

《童年-高尔基》阅读笔记 2024-12-29日读完&#xff0c;在图书馆的书架上&#xff0c;大批的厚厚的外国文学书&#xff0c;只有这本薄薄的&#xff0c;我就拿下来看了一下&#xff0c;发现这本书就是初中就在语文课本中提到的自传体三部曲之一&#xff0c;其他两本是《在人间》…

fpga系列 HDL:ModelSim显示模拟波形以及十进制格式数值

FPGA中使用数字滤波器时&#xff0c;可通过观察模拟波形更好地查看滤波效果。可以通过ModelSim中的波形格式设置来实现更直观的波形显示。 右键波形->Format-> Analog 效果 不同的数值格式显示&#xff1a;右键波形->Radix-> Decimal 效果 示例代码 ver…

AI大模型语音识别转文字

提取音频 本项目作用在于将常见的会议录音文件、各种语种音频文件进行转录成相应的文字&#xff0c;也可从特定视频中提取对应音频进行转录程文字保存在本地。最原始的从所给网址下载对应视频和音频进行处理。下载ffmpeg(https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-…

《计算机组成及汇编语言原理》阅读笔记:p128-p132

《计算机组成及汇编语言原理》学习第 10 天&#xff0c;p128-p132 总结&#xff0c;总计 5 页。 一、技术总结 1.8088 organization and architecture 8088处理器是16位电脑&#xff0c;寄存器是16位&#xff0c;数据总线(data bus)是8位&#xff0c;地址总线是20位。 (1)g…

【ArcGIS Pro】完整的nc文件整理表格模型构建流程及工具练习数据分享

学术科研啥的要用到很多数据&#xff0c;nc文件融合了时间空间数据是科研重要文件。之前分享过怎样将nc文件处理成栅格后整理成表格。小编的读者还是有跑不通整个流程的&#xff0c;再来做一篇总结篇&#xff0c;也分享下练习数据跟工具&#xff0c;如果还是弄不了的&#xff0…

安装了python,环境变量也设置了,但是输入python不报错也没反应是为什么?window的锅!

目录 问题 结论总结 衍生问题 1 第1步&#xff1a;小白python安装&#xff0c;不要埋头一直点下一步&#xff01;&#xff01;&#xff01; 2 第2步&#xff1a;可以选择删了之前的&#xff0c;重新安装python 3 第3步&#xff1a;如果你不想或不能删了重装python&#…

图像处理-Ch7-快速小波变换和小波包

个人博客&#xff01;无广告观看&#xff0c;因为这节内容太多了&#xff0c;有点放不下&#xff0c;分了三节 文章目录 快速小波变换(The Fast Wavelet Transform)与两频段子带编译码系统的关系例&#xff1a;计算一维小波变换 一维快速小波反变换例&#xff1a;计算一维小波…

【从零开始】11. LLaMA-Factory 微调 Qwen 模型(番外篇)

书接上回&#xff0c;在完成了 RAGChecker 测试后&#xff0c;离 RAG 应用真正发布还差最后一步 - 基础信息指令微调。考虑到模型还是需要具备一定程度的“自我认知”&#xff0c;因此需要将公司信息“嵌入”到模型里面的。为此&#xff0c;我选择了 LLaMA-Factory&#xff08;…

小程序配置文件 —— 15 页面配置

页面配置 小程序的页面配置&#xff0c;也称为局部配置&#xff0c;每一个小程序页面也可以使用自己的 .json 文件来对页面的窗口表现进行配置&#xff1b; 需要注意的是&#xff1a;页面配置文件的属性和全局配置文件中的 window 属性几乎一致&#xff0c;只不过这里不需要额…

Linux高级--2.4.5 靠协议头保证传输的 MAC/IP/TCP/UDP---协议帧格式

任何网络协议&#xff0c;都必须要用包头里面设置写特殊字段来标识自己&#xff0c;传输越复杂&#xff0c;越稳定&#xff0c;越高性能的协议&#xff0c;包头越复杂。我们理解这些包头中每个字段的作用要站在它们解决什么问题的角度来理解。因为没人愿意让包头那么复杂。 本…

算法基础一:冒泡排序

一、冒泡排序 1、定义 冒泡排序&#xff08;英语&#xff1a;Bubble Sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序&#xff08;如从大到小、首字母从A到Z&#xff09;错误就把他们交换过来。 …

Spring Cloud LoadBalancer (负载均衡)

目录 什么是负载均衡 服务端负载均衡 客户端负载均衡 Spring Cloud LoadBalancer快速上手 启动多个product-service实例 测试负载均衡 负载均衡策略 自定义负载均衡策略 什么是负载均衡 负载均衡(Load Balance&#xff0c;简称 LB) , 是高并发, 高可用系统必不可少的关…

OneNet平台2024版MQTT协议完整开发案例教程【MQTT数据流模式】

一、前言 此篇文章是以智慧路灯项目为例&#xff0c;完整的演示介绍 OneNet平台的开发。 OneNet平台 是2024年的新版本&#xff0c;采用数据流模版为例。 方便大家&#xff0c;腾讯云IOT 转 OneNet平台。 因为目前腾讯云IOT平台无法白嫖了&#xff0c;新用户已经无法免费使用…

一起学Git【第六节:查看版本差异】

git diff是 Git 版本控制系统中用于展示差异的强大工具。他可以用于查看文件在工作区、暂存区和版本库之间的差异、任意两个指定版本之间的差异和两个分支之间的差异等,接下来进行详细的介绍。 1.显示工作区与暂存区之间的差异 # 显示工作区和暂存区之间的差异,后面不加参数…

【小程序】wxss与rpx单位以及全局样式和局部样式

目录 WXSS 1. 什么是 WXSS 2. WXSS 和 CSS 的关系 rpx 1. 什么是 rpx 尺寸单位 2. rpx 的实现原理 3. rpx 与 px 之间的单位换算* 样式导入 1. 什么是样式导入 2. import 的语法格式 全局样式和局部样式 1. 全局样式 2. 局部样式 WXSS 1. 什么是 WXSS WXSS (We…

uniapp-vue3(下)

关联链接&#xff1a;uniapp-vue3&#xff08;上&#xff09; 文章目录 七、咸虾米壁纸项目实战7.1.咸虾米壁纸项目概述7.2.项目初始化公共目录和设计稿尺寸测量工具7.3.banner海报swiper轮播器7.4.使用swiper的纵向轮播做公告区域7.5.每日推荐滑动scroll-view布局7.6.组件具名…

大数据技术-Hadoop(一)Hadoop集群的安装与配置

目录 一、准备工作 1、安装jdk&#xff08;每个节点都执行&#xff09; 2、修改主机配置 &#xff08;每个节点都执行&#xff09; 3、配置ssh无密登录 &#xff08;每个节点都执行&#xff09; 二、安装Hadoop&#xff08;每个节点都执行&#xff09; 三、集群启动配置&a…

ipad如何直连主机(Moonlight Sunshine)

Windows 被连接主机&#xff08;Windows&#xff09; 要使用的话需要固定ip&#xff0c;不然ip会换来换去&#xff0c;固定ip方法本人博客有记载Github下载Sunshine Sunshine下载地址除了安装路径需要改一下&#xff0c;其他一路点安装完成后会打开Sunshine的Web UI&#xff…