Java——静态成员

news2024/11/8 11:48:09

一、静态变量(Static Variables)

1、静态变量是什么

静态变量,又被称为类变量,它使用 static 关键字修饰。静态变量是类级别的变量,在类加载时初始化。

2、静态变量的特点

类级别共享:静态变量在所有实例间共享,所有对象访问的是同一个静态变量。

内存管理:生命周期与类一致,从类加载到类卸载。静态变量在类加载时分配内存,直到程序结束才释放。

可以通过类名直接访问:静态变量是属于整个类的,所以它不需要实例就可以访问,直接使用类名进行访问。例如 ClassName.staticVariable

类级别共享:静态变量在所有实例间共享,所有对象访问的是同一个静态变量。

3、静态变量的定义与声明

public class MyClass {
    public static int staticVar = 0; // 静态变量
    public int instanceVar = 0;      // 实例变量
}

4、静态变量的使用场景

1)计数器

用于记录类被实例化的次数。

public class Counter {
    public static int count = 0;

    public Counter() {
        count++;
    }
}

2)共享资源

例如在工具类中共享某些资源或配置。

public class Utility {
	private static Scanner input = new Scanner(System.in);
    // 静态变量,用于工具类的共享

	public static char readAChar(char[] validChars) {
        //...
	}
}

3)定义常量

常用static final组合来定义常量。定义全局常量,如数学常数、配置信息等。

public class Constants {
    public static final double PI = 3.141592653589793;
}

5、静态变量的存储位置

在JDK 8,Java对内存管理进行了变更。

方法区被移除,取而代之的是元空间 (Metaspace)。对于方法区是在JVM中的,而对于元空间,则是位于本地内存中的。

因此,静态变量和常量池不再存储在永久代(永久代是方法区的实现)中,而是迁移到了堆内存中。这些改变是为了改善内存管理,避免永久代的内存限制问题。

所以说:

  1. JDK 8 之前

    静态变量存储在方法区中。
  2. JDK 8 及之后

    永久代被元空间取代。静态变量迁移到堆内存中。元空间用于存储类的元数据,位于本地内存中而非JVM堆中。

二、静态方法(Static Methods)

1、静态方法是什么

静态方法(又叫类方法)是使用static关键字声明的方法。它们属于类本身,可以在无需创建类实例的情况下调用。

Math.random();

我们可以想到调用 Math 类中的 random() 方法,直接使用类名就可以调用这个方法,所以这个方法是静态方法。

2、静态方法特点

1)特点

无需实例化:可以通过类名直接调用静态方法。

访问限制:静态方法只能直接访问其他静态成员(变量和方法),不能访问实例成员。

不能使用thissuper关键字:因为静态方法不属于任何实例。

多态性限制:静态方法不支持运行时多态,只能进行静态绑定。静态方法不能被重写,因为静态方法是与类相关联的,不依赖于具体实例。因此,不能通过子类的实例来覆盖父类的静态方法。

2)补充

如果在子类中定义了与父类静态方法同名的方法,这不是重写,而称为方法隐藏。调用时,方法的选择取决于引用变量的类型(也就是说取决于编译时类型,所以这就是静态绑定),而不是对象的实际类型(运行时类型)。

public class Example {
	public static void main(String[] args) {
		SuperClass sc = new SubClass();
		sc.staticMethod();
	}
}

class SuperClass {
	public static void staticMethod() {
		System.out.println("SuperClass staticMethod~");
	}
}

class SubClass extends SuperClass{
	public static void staticMethod() {
		System.out.println("SubClass staticMethod~");
	}
}

运行结果:

3、静态方法的最佳实践

1)工具类方法

java.lang.Math类的方法,提供数学运算功能。

double result = Math.sqrt(25.0); // 调用静态方法

不仅是Math这种标准库中提供的方法类,我们自己实现的方法类中的方法一般也是使用静态方法。

2)工厂方法

返回类的实例的静态方法。

public class Factory {
    public static MyClass createInstance() {
        return new MyClass();
    }
}

3)主方法

Java程序的入口点是静态方法main

public static void main(String[] args) {
    // 程序入口
}

三、main 方法详解

1、main 方法介绍

在Java中,main方法是程序的入口点。它是一个特殊的方法,Java虚拟机(JVM)在运行程序时会首先调用它。

2、main 方法的定义格式

public static void main(String[] args)

这是 main 方法的定义格式,但是,它的定义格式为什么是这样的呢?

  1. public: 访问修饰符,表示该方法可以被任何地方调用。因为JVM需要调用main方法,因此必须是public

  2. static: 静态修饰符,意味着该方法属于类而不是类的实例。JVM无需创建类的对象即可调用main方法。

  3. void: 返回类型,main方法不返回任何值。

  4. main: 方法名,必须是main,因为这是JVM识别的入口点。

  5. String[] args: 参数,args是一个String数组,用于接收命令行参数。用户可以在运行程序时向main方法传递参数。

3、main 方法的参数

我们可以通过命令行传递参数给 main 方法,我们先写一段程序:

public class Arguments {
    public static void main(String[] args) {
        for(String arg : args) {
            System.out.println("Argument: " + arg);
        }
    }
}

然后我们在命令行中编译这个代码:

然后运行程序,同时传入几个参数,可以看到运行结果是:

这里我们给 main 方法传入了三个参数:

Hello World !

三个字符串。然后这三个字符串通过我们在 main 方法中写的遍历参数数组的语句被打印出来。

4、在 IDEA 中给 main 方法传参

运行结果:

三、代码块(Block)

1、代码块介绍

1)代码块是什么

在Java中,代码块又称为初始化块,有些情况下初始化比较复杂,需要将其封装在代码块中。他没有名字,不能通过对象或者类名调用。一般是加载类时或者创建对象时隐式调用。

代码块分为实例代码块和静态代码块。它们用于在特定情况下执行特定的初始化操作。

2)代码块定义格式

    // 前面可以有修饰符可选,但也仅限 static 修饰符
    [static]{
        // 语句
    }

2、实例代码块(Instance block)

1)介绍

实例代码块在创建对象时执行。每次创建对象时,都会执行实例代码块的内容。如果只是使用类的静态成员,实例代码块并不会执行。

实例代码块中可以访问实例成员,也可以访问静态成员。

  1. 执行时机:在每次创建对象时执行,优先于构造函数执行。
  2. 使用场景:可以用于所有构造函数中共享的初始化代码,避免代码重复。所以可以将实例代码块理解成构造器的补充机制,用来辅助构造器来初始化对象。

2)示例

public class Block {
	public static void main(String[] args) {
		new Test();
		new Test(1);
	}
}

class Test {
	private int x;

	{
		System.out.print("实例代码块:x被初始化为 ");
	}

	public Test() {
		this.x = 0;
		System.out.println(this.x);
		System.out.println("Test()构造器");
	}

	public Test(int x) {
		this.x = x;
		System.out.println(this.x);
		System.out.println("Test(int x)构造器");
	}
}

运行结果:

3、静态代码块(Static block)

1)介绍

静态代码块用于初始化静态变量,只在类加载时执行一次。可以用于复杂的静态变量初始化或执行需要的配置信息。

静态代码块只能访问静态成员。

特点:

  • 类加载时执行:静态代码块在类首次加载到JVM时执行,且只执行一次。

  • 顺序执行:如果有多个静态代码块,按照在类中出现的顺序依次执行。

用途:

  • 静态变量复杂初始化:当静态变量的初始化不只是简单的赋值时,可以使用静态代码块。

  • 加载资源或配置:在类加载时执行一次性操作,如加载配置文件。

2)类的加载

在Java中,一个类在一次程序运行中通常只会被加载一次。类加载的具体机制由类加载器管理,确保每个类在运行期间仅加载一次。

类的加载会由以下行为触发:

  1. 创建类的实例:使用 new 关键字创建对象时。

  2. 访问类的静态成员(静态变量或静态方法):此时类会被加载,以便访问这些静态成员。例如包含 main 方法的类在程序启动时加载。因为程序启动时,JVM 会调用静态方法 main 方法。

  3. 通过反射机制:使用 Class.forName() 或其他反射API。

  4. 初始化子类:当子类被初始化时,父类也会被加载和初始化。

如果程序中没有触发这些行为,类就不会被加载。

3)示例

public class Block {
	public static void main(String[] args) {
		System.out.println("Example.staticVar = " + Example.staticVar);
	}
}

class Example {
	public static int staticVar;

	static {
		System.out.println("静态代码块");
	}
}

运行结果:

4、代码块的执行顺序

1)静态、实例代码块和构造器的执行顺序

在一个类没有加载时,创建此类的第一个对象的调用顺序是:

  1. 调用静态代码块和静态属性初始化。(静态代码块和静态属性初始化执行的优先级是一样的,如果有多个静态代码块和多个静态属性初始化,则按照它们定义的顺序执行)
  2. 调用实例代码块和实例属性初始化。(普通代码块和普通属性初始化的执行优先级也是一样的,如果有多个实例代码块和多个实例属性初始化,则按照它们定义的顺序执行)
  3. 调用构造方法。

示例:

public class Example {
	public static void main(String[] args) {
		new Test();
	}
}

class Test {
	private static int staticVar1 = initStaticVar1();

	static {
		System.out.println("静态代码块 1");
	}

	static {
		System.out.println("静态代码块 2");
	}

	private static int staticVar2 = initStaticVar2();

	private int instanceVar1 = initInstanceVar1();

	{
		System.out.println("实例代码块 1");
	}

	{
		System.out.println("实例代码块 2");
	}
	
	private int instanceVar2 = initInstanceVar2();
	
	public Test() {
		System.out.println("构造器");
	}

	public static int initStaticVar1() {
		System.out.println("staticVar1 被初始化");
		return 10;
	}

	public static int initStaticVar2() {
		System.out.println("staticVar2 被初始化");
		return 10;
	}

	public int initInstanceVar1() {
		System.out.println("instanceVar1 被初始化");
		return 20;
	}

	public int initInstanceVar2() {
		System.out.println("instanceVar2 被初始化");
		return 20;
	}
}

运行结果:

可以看到执行顺序与我们前面提到的一致,优先级一样的执行顺序由它的定义的顺序决定。

2)构造器中的执行顺序

对于一个类的构造器,它首应当是显示或者隐式调用 super() 也就是父类的构造器,然后就是执行子类的实例代码块,然后才是子类构造器的主体。

  1. 调用父类构造器:创建子类对象时,首先调用父类构造器(通过隐式或显式的 super() 调用)。

  2. 执行实例代码块:在父类构造器执行完成后,子类的实例代码块被执行。实例代码块的执行并不依赖于构造器的显式调用,而是自动插入在父类构造器调用后和子类构造器主体执行前。

  3. 执行构造器:最后,执行子类的构造器主体。

示例:

public class Example {
	public static void main(String[] args) {
		new SubClass();
	}
}

class SuperClass {
	public SuperClass() {
		System.out.println("父类构造器");
	}
}

class SubClass extends SuperClass{
	{
		System.out.println("实例代码块");
	}

	public SubClass() {
		// super(); 首先调用父类构造器
		// 执行实例代码块
		System.out.println("子类构造器");
	}
}

运行结果:

3)创建一个子类的执行顺序

在子类和父类都没有加载时,创建第一个子类对象,执行顺序为:

  1. 父类的静态代码块和静态属性初始化。(优先级一样,按定义的顺序执行)
  2. 子类的静态代码块和静态属性初始化。(优先级一样,按定义的顺序执行)
  3. 父类的实例代码块和实例属性初始化。(优先级一样,按定义的顺序执行)
  4. 父类的构造器。(取决于子类调用的是哪一个)
  5. 子类的实例代码块和实例属性初始化。(优先级一样,按定义的顺序执行)
  6. 子类的构造器。

示例:

public class Example {
	public static void main(String[] args) {
		new SubClass();
	}
}

class SuperClass {
	static {
		System.out.println("父类的静态代码块");
	}

	{
		System.out.println("父类的实例代码块");
	}

	public SuperClass() {
		System.out.println("父类的构造器");
	}
}

class SubClass extends SuperClass {
	static {
		System.out.println("子类的静态代码块");
	}

	{
		System.out.println("子类的实例代码块");
	}

	public SubClass() {
		System.out.println("子类的构造器");
	}
}

运行结果:

可以看到运行结果与我们上面提到的执行顺序一致。

参考资料:

Java学习笔记9---类静态成员变量的存储位置及JVM的内存划分_jdk8静态变量放在jvm哪里-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java | Leetcode Java题解之第542题01矩阵

题目: 题解: class Solution {static int[][] dirs {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};public int[][] updateMatrix(int[][] matrix) {int m matrix.length, n matrix[0].length;// 初始化动态规划的数组,所有的距离值都设置为一个很大…

996引擎 - 活捉NPC

996引擎 - 活捉NPC 引擎触发 - 引擎事件(QF)事件处理模块 GameEvent测试文件参考资料 引擎触发 - 引擎事件(QF) cfg_game_data 配置 ShareNpc1 可以将QM和机器人的触发事件全部转到 QF 引擎触发是通用的,TXT的所有触发转换成小写后在LUA中就可使用,如说明书中缺省可反馈至对接群…

【Linux系统编程】第四十三弹---多线程编程指南:线程终止方式与C++11中的thread

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、线程终止 1.1、pthread_exit() 1.2、pthread_cancel() 1.3、pthread_detach() 2、C11中thread 1、线程终止 问题7 …

【Mac】PD报错:无法为“Windows” 完成操作,虚拟机ID无效的解决办法

Parallels Desktop是Mac上一款非常常用的虚拟机软件,但是在使用过程中,可能会遇到一些问题不知道如何处理。比如有时会遇到PD报错:无法为“Windows 11”完成操作,虚拟机ID无效。 错误原因 电脑上安装过虚拟机,虚拟机被…

25-RVIZ CARLA插件

RVIZ插件(RVIZ plugin)提供了一个基于RVIZ(RVIZ) ROS包的可视化工具。 用RVIZ运行ROS桥接 RVIZ插件需要一个名为ego_vehicle的自车。要查看ROS-bridge使用RVIZ的示例,请在运行CARLA服务器的情况下执行以下命令: 1. 启用RVIZ启动ROS桥接: # …

spark-本地模式的配置和简单使用

python环境的安装 在虚拟机中,只能安装一个python的版本,若想要安装别的版本,则需要卸载之前的版本——解决方式,安装Anaconda 通过百度网盘分享的文件:Anaconda3-2021.05-Linux-x86_64.sh 链接:https://…

【算法】递归+回溯+剪枝:78.子集

目录 1、题目链接 2、题目 3、解法(回溯剪枝) 4、代码 1、题目链接 78.子集(LeetCode) 2、题目 3、解法(回溯剪枝) 思路: 枚举子集(答案)的第一个数选谁,第二个数选谁,第三个数选谁&#x…

Aop+自定义注解实现数据字典映射

数据字典 Web项目开发中,字典表的一般都会存在,主要用来给整个系统提供基础服务。 比如男女性别的类型可以使用0和1来进行表示,在存储数据和查询数据的时候,就可以使用字典表中的数据进行翻译处理。 再比如之前做的一个项目中宠物…

【NLP自然语言处理】深入探索Self-Attention:自注意力机制详解

目录 🍔 Self-attention的特点 🍔 Self-attention中的归一化概述 🍔 softmax的梯度变化 3.1 softmax函数的输入分布是如何影响输出的 3.2 softmax函数在反向传播的过程中是如何梯度求导的 3.3 softmax函数出现梯度消失现象的原因 &…

华为eNSP:QinQ

一、什么是QinQ? QinQ是一种网络技术,全称为"Quantum Insertion",也被称为"Q-in-Q"、"Double Tagging"或"VLAN stacking"。它是一种在现有的VLAN(Virtual Local Area Network&#xff0…

细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的另一种方法

目录 一、工程目的 1、目标 2、通讯协议及应对错误指令的处理目标 二、工程设置 三、程序改进 四、下载与调试 1、合规的指令 2、不以#开头,但以;结束,长度不限 3、以#开头,不以;结束,也不包含;,长…

Redis设计与实现 学习笔记 第十六章 Sentinel

Sentinel(哨岗、哨兵)是Redis的高可用性(high availability)解决方案:由一个或多个Sentinel实例(instance)组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的从服…

前端三件套-css

一、元素选择器 元素选择器&#xff1a;利用标签名称。p,h1-h6...... 行内样式&#xff08;内联样式&#xff09;&#xff1a;例如<p style"color:red;font-size:50px"> id选择器&#xff1a;针对某一个特定的标签来使用。以#定义。 class&#xff08;类&a…

MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!

2024-11-04 MoonBit更新 增加了编译期常量的支持。常量的名字以大写字母开头&#xff0c;用语法 const C ... 声明。常量的类型必须是内建的数字类型或 String。常量可以当作普通的值使用&#xff0c;也可以用于模式匹配。常量的值目前只能是字面量&#xff1a; const MIN_…

新疆高校大数据实验室案例分享

高校大数据实验室建设&#xff0c;企业可以提供技术支持、实训平台和项目案例&#xff0c;高校则提供科研和教学资源&#xff0c;实现产学研一体化。不仅有利于大数据技术的应用和人才培养也有利于区域发展。 泰迪与新疆合作的院校包括新疆大学、昌吉学院等 新疆大…

Capcut,更适合做TikTok运营的“剪映”

剪映这一次的更新&#xff0c;可谓是引来许多视频创作者的怒火。原本免费的功能&#xff0c;更新之后需要vip才能使用了&#xff1b;原本的vip功能&#xff0c;则需要升级至svip&#xff0c;甚至一些功能&#xff0c;需要会员积分才能使用。 许多运营TikTok的小伙伴一直在用剪映…

linux centos 安装redis

安装 wget https://download.redis.io/releases/redis-7.4.0.tar.gz解压redis-7.4.0.tar.gz文件 tar -zxvf redis-7.4.0.tar.gz进入redis安装目录 cd redis-7.4.0make时报错&#xff0c;因为需要安装gcc&#xff0c;gcc安装需要联网安装 修改端口 编辑文件用vi。nano命令cen…

第一个纯血鸿蒙应用(Napi开发-ArtTS调用C/C++)

1.行业背景 纯血鸿蒙&#xff0c;即鸿蒙Next版已于2014年1月正式发版&#xff0c;鸿蒙生态设备数量已经突破10亿台&#xff0c;已经有超过15000个应用和元服务上架。鸿蒙生态不只是移动设备这么简单&#xff0c;他打造的是一个18n的全场景战略&#xff0c;真正做到了“万物互联…

2.ARM_ARM是什么

CPU工作原理 CPU与内存中的内容&#xff1a; 内存中存放了指令&#xff0c;每一个指令存放的地址不一样&#xff0c;所需的内存空间也不一样。 运算器能够进行算数运算和逻辑运算&#xff0c;这些运算在CPU中都是以运算电路的形式存在&#xff0c;一个运算功能对应一种运算电…

【Ant.designpro】上传图片

文章目录 一、前端二、后端 一、前端 fieldProps:可以监听并且获取到组件输入的内容 action{“/api/upload_image”} 直接调用后端接口 <ProFormUploadButtonlabel{"上传手续图片"}name{"imgs"}action{"/api/upload_image"}max{5} fieldPro…