【西行 - 计收猪八戒】 面向对象之成员变量和局部变量

简介: Java 语言中,根据定义变量位置不同,可以将变量分为两大类:成员变量和局部变量。成员变量和局部变量的运行机制存在较大差异,本篇会详细介绍这两种变量,废话不多说,开始了。


1、成员变量和局部变量


定义:


成员变量:在类范围里定义的变量,也就是前面几篇介绍的 Field。


局部变量:在方法里定义的变量。


注意:在变量命名规范上,我们应该做到知名识意,且变量名要首字母小写,后面每个单词首字母大写。


成员变量又可根据是否有 static 关键字修饰分为:类 Field 和 实例 Field 两种。


类 Field 被 static 关键字修饰,它从这个类准备阶段就开始存在,直到系统完全销毁这个类时消亡。作用域上,类 Field 和这个类的生存范围相同。


实例 Field 未被 static 关键字修饰,它从这个类的实例被创建起开始存在,直到系统完全销毁这个实例。作用域上,实例 Field 和这个实例的生存范围相同。


类 Field 访问形式:

类名.类Field
实例.类Field


只要类存在,我们就可以通过类名访问其类成员变量。虽说也可以通过实例访问类 Field ,但由于这个实例并不拥有 类 Field ,所以本质上还是操作的类 field(修改值的时候,改的是类值而不是实例值)。


实例 Field 访问形式:

实例.实例Field


下面看一下具体代码了解其基本使用:

public class VariableTest {
    // 定义类 field
    public String name;
    // 定义实例 field
    public static int age;
    public static void main(String[] args) {
        // VariableTest 类已经初始化了,则 age 变量起作用了,输出 0
        System.out.println("VariableTest 的 age 类 Field 值:" + VariableTest.age);
        // 创建 VariableTest 对象
        VariableTest v = new VariableTest();
        // 通过实例访问类field 和实例 field
        System.out.println("v 对象的 name field 值是:" + v.name + "v 对象的 age field 值是:" + v.age);
        // 为 v 实例变量赋值
        v.name = "J3";
        // 通过实例访问类 field,依然是访问 VariableTest 的 age 类 field
        v.age = 18;
        System.out.println("v 对象的 name field 值是:" + v.name + "v 对象的 age field 值是:" + v.age);
        System.out.println("VariableTest 的 age 的类 field 值:" + VariableTest.age);
        VariableTest v2 = new VariableTest();
        System.out.println("v2 对象的 age 类 field 值:" + v2.age);
    }
}


由程序可知,成员变量无须显示初始化,系统会自动为成员变量进行默认初始化,初始化规则与数组动态初始化元素赋值规则完全相同。


从运行结果可以看出,类 Field 的作用于比实例 Field 的作用于更大:实例 Field 随实例的存在而存在,而类 Field 则随类的存在而存在。


虽然实例可以访问类 Field ,但其本质还是访问的类 Field 指向的都是同一片内存空间(不提倡实例调用类 Field,可读性不好)。


局部变量根据定义形式不同,可以分为如下三种:


形参:在定义方法签名时定义的变量,形参的作用域在整个方法内有效。


方法局部变量:在方法体内定义的局部变量,它的作用域是从定义该变量的地方生效,到该方法结束时失效。


代码块局部变量:在代码块中定义的局部变量,这个局部变量的作用域从定义该变量的地方生效,到改代码块结束时失效。


局部变量与成员变量不同,必须显示初始化。也就是说,局部变量定义的时候必须制定默认值,否则不可访问它们。


下面演示局部变量基本使用:

public class VariableTest {
    public static void main(String[] args) {
        // ===================代码块======================
        {
            // 定义一个代码块局部变量 a
            int a;
            // 下面代码将出现错误,因为 a 变量还未初始化
            // System.out.println("代码库局部变量 a 的值:" + a);
            a = 5;
            System.out.println("代码块局部变量 a 的值:" + a);
        }
        // 下面试图访问的 a 变量并不存在
        // System.out.println(a);
        // ===================方法变量======================
        // 定义一个方法局部变量 b
        int b;
        // 下面代码将出现错误,因为 b 变量还未初始化
        // System.out.println("代码库局部变量 b 的值:" + b);
        b = 5;
        System.out.println("方法局部变量 b 的值:" + b);
        // ===================形参变量======================
        VariableTest variableTest = new VariableTest();
        variableTest.sum(2, 3);
    }
    public void sum(int c, int d) {
        System.out.println("形参 c 和 d 相加的值:" + (c + d));
    }
}


对于上面程序中的形参是真个方法体内有效,初始化是在调用方法时由系统完成,形参的值由方法的调用者负责指定。


在同一个类里,成员变量的作用范围是整个类内有效,一个类不能定义两个同名成员变量,即使一个是类 Field ,一个是实例 Field 也不行;一个方法不能定义两个同名的局部变量,即使一个是方法局部变量,一个是代码块局部变量或者形参也不行。


Java 运行局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果需要在这个方法里引用被覆盖的成员变量,则可使用 this 或类名作为调用者来限定访问成员变量。


同名变量案例:

public class VariableTest {
    // 定义 name 实例 field
    private String name = "J3";
    // 定义 age 类 field
    private static int age = 18;
    public static void main(String[] args) {
        // 方法里的局部变量,局部变量覆盖成员变量
        int age = 28;
        // 直接访问 age ,将输出局部变量的值
        System.out.println(age);
        // 使用类名调用
        System.out.println(VariableTest.age);
    }
    public void info() {
        // 方法里的局部变量,局部变量覆盖成员变量
        String name = "西行";
        // 直接访问 name 输出局部变量的值
        System.out.println(name);
        // 使用 this ,限定调用成员变量
        System.out.println(this.name);
    }
}


2、成员变量的初始化和内存中的运行机制


当系统加载类或创建该类的实例时,系统自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值。


看下面代码,根据 VariableTest 类创建了两个实例,配合示意图来说明 Java 成员变量的初始化和内存中的运行机制。

public class VariableTest {
    // 定义 name 实例 field
    private String name;
    // 定义 age 类 field
    private static int age;
    public static void main(String[] args) {
        // 创建两个对象
        VariableTest v1 = new VariableTest();
        VariableTest v2 = new VariableTest();
        // 给实例 field 赋值
        v1.name = "J3";
        v2.name = "西行";
        // 给类 field 赋值
        v1.age = 28;
        v2.age = 38;
        System.out.println("v1 中实例变量值:" + v1.name + ",v1 中类变量值:" + v1.age);
        System.out.println("v2 中实例变量值:" + v2.name + ",v2 中类变量值:" + v2.age);
    }
}


当程序执行到第 9 行代码时,如果这行代码是第一次使用 VariableTest 类,则系统会在第一次使用 VariableTest 类时加载这个类,并初始化这个类。再类的准备阶段,系统将会为该类的类 Field 分配内存空间,并指定默认初始值。当 VariableTest 类初始化完成后,系统内存中的存储示意图如下:


image.png


当 VariableTest 类初始化完成后,系统将在堆内存中为 VariableTest 类分配一块内存区,在这块内存区里包含了保存 age 类 Field 的内存,并设置 age 的默认初始值:0。


系统接着创建了一个 VariableTest 对象,并把这个 VariableTest 对象赋给 v1 变量,VariableTest 对象里包含了名为 name 的实例 Field ,实例 Field 是在创建实例时分配内存空间并指定初始值的。当创建了第一个 VariableTest 对象后,系统内存的存储的示意图如下:


image.png


有图可知,age 类 Field 并不属于 VariableTest 对象,它时属于 VariableTest 类的,所以创建第一个对象时并不需要为 age 类 Field 分配内存,系统只是为 name 实例 Field 分配了内存空间,并指定默认初始值:null。


接着执行 10 行代码创建第二个 VariableTest 对象,此时因为 VariableTest 类已经存在于堆内存中了,所以不需要对 VariableTest 类进行初始化。并且创建第二个 VariableTest 对象与创建第一个 VariableTest 对象并没有什么不同。


当程序执行 12、13 代码时,将为 v1 的 name 实例 Field 赋值,也就是下图中 name 值向 “J3” 字符串。指向完后,两个 VariableTest 对象在内存中的存储示意图如下:


image.png


上图可以看出,name 实例 Field 是属于单个 VariableTest 实例的,因此修改第一个 VariableTest 对象的 name 实例 Field 时仅仅与该对象有关,同样修改第二个对象的 name 实例 Field 时,也与 VariableTest 类和其他 VariableTest 对象无关。


最后当程序执行到 15、16 行代码时,此时通过 VariableTest 对象来修改 VariableTest 的类 Field,结合图和运行结果不难看出,v1、v2 对象修改的都是统一内存中的值。修改成功后,内存中的存储示意图如下:


image.png


从上图可以看出,通过 v1 和 v2 来访问类 Field 时,实际上访问的时 VariableTest 类的 age 。


实际上,不管通过那个 VariableTest 实例来访问 age Field ,本质上还是通过 VariableTest 类来访问 age FIeld,他们所访问的时同一块内存。所以以后建议,当程序需要访问类 Field 时,尽量使用类作为主调,而不是使用对象作为主调,避免歧义,题号程序可读性。


3、局部变量的初始化和内中的运行机制


局部变量定义后,必须经过显示初始化后才能使用,系统不会为局部变量执行初始化。因为定义局部变量后,系统并未为这个变量分配内存空间,知道等到程序为这个变量赋初始值时,系统才会为局部变量分配内存,并将初始值保存到这块内存中。


与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在的方法的栈内存中。如果局部变量是基本类型的变量,则直接把这个变量的值保存在改变量对应的内存中;如果局部变量是一个引用类型的变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组。


栈内存中的变量无需系统垃圾回收,往往随方法或代码块的运行结束而结束。因此,局部变量的作用域是从初始化该变量开始,直到该方法或改代码快运行完成而结束。因为局部变量只保存基本类型的值或对象的引用,因此局部变量所占的内存通常比较小。


好了,今天的内容到这里就结束了


目录
相关文章
|
机器学习/深度学习 算法 数据可视化
智能扑克牌识别软件(Python+YOLOv5深度学习模型+清新界面)
智能扑克牌识别软件(Python+YOLOv5深度学习模型+清新界面)
2438 0
|
7月前
|
人工智能 自然语言处理 数据可视化
阿里云OpenClaw(Clawdbot)一键部署,得到一个7x24小时全天候待命的员工!还不用交社保!
OpenClaw(原Clawdbot、Moltbot)是一款能以自然语言完成文件处理、信息提取、自动化工作流等实操任务的开源自托管AI助手,2026年阿里云推出轻量应用服务器一键部署方案,通过专属镜像实现零依赖快速落地,全程可视化操作,零基础也能3分钟内完成部署。以下结合官方实操指南,详细拆解前置准备、部署流程、配置验证及问题排查,助力高效搭建专属AI助手。
432 2
|
8月前
|
人工智能 监控 安全
一封“来自自己邮箱”的钓鱼邮件,如何绕过所有安全防线?微软揭示企业邮件配置盲区正成攻击温床
2025年,华南某金融科技公司遭遇“内部域名伪造”钓鱼攻击:员工收到来自自己邮箱的MFA更新邮件,实为攻击者利用SPF、DKIM、DMARC配置疏漏伪造。邮件显示“内部发送”,极具迷惑性,险致资金损失。微软披露,此类攻击全球频发,根源在于邮件认证链断裂。专家呼吁企业收紧DMARC策略、审计邮件路由,并建立“零信任邮件”文化,筑牢基础安全防线。
466 6
|
8月前
|
弹性计算 运维 安全
2026年阿里云 ECS 云服务器的定义、购买流程、功能优势与使用指南说明
阿里云 ECS(Elastic Compute Service)是阿里云提供的 IaaS(基础设施即服务)级云计算服务,用户无需提前采购物理服务器,就能像使用水电一样便捷地获取计算资源,实现 “即开即用” 和弹性伸缩,适配从个人开发到企业核心业务的全场景需求。以下从核心定义、购买方式、产品优势、使用途径及部署建议等维度,用通俗语言展开解析,帮助用户全面了解 ECS。
|
11月前
|
人工智能 供应链 算法
AI 产业服务平台:打造产业智能化的“加速器”与“连接器”
AI产业服务平台整合技术、数据、算力与人才,为中小企业提供低门槛、一站式AI赋能服务,覆盖研发、生产、营销、管理全链条,助力产业智能化转型。
391 0
|
8月前
|
人工智能 弹性计算 安全
2026年阿里云OpenClaw(原Clawdbot)部署教程:构建钉钉AI机器人24小时为你工作为例
OpenClaw(原Moltbot、Clawdbot)是一款开源的本地优先AI代理与自动化平台,可将多渠道通信能力与大语言模型深度集成,打造具备持久记忆与主动执行能力的定制化AI助理。本文基于阿里云轻量应用服务器,详细拆解OpenClaw镜像部署及钉钉AI机器人集成流程,同时解答常见问题,全程无营销表述,确保不改变原意,助力用户合规、稳定地启用服务。
1731 1
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
742 1
|
11月前
|
存储 API 数据库
按图搜索1688商品的API接口
本文介绍如何利用阿里云ImageSearch服务实现1688商品的按图搜索功能。通过提取图像特征向量并计算相似度,结合Flask搭建API接口,可快速构建基于图片的商品检索系统,提升电商用户体验。
1094 0
|
机器学习/深度学习 人工智能 安全
AI大模型安全风险和应对方案
AI大模型面临核心安全问题,包括模型内在风险(如欺骗性对齐、不可解释性和模型幻觉)、外部攻击面扩大(如API漏洞、数据泄露和对抗性攻击)及生成内容滥用(如深度伪造和虚假信息)。应对方案涵盖技术防御与优化、全生命周期管理、治理与行业协同及用户教育。未来需关注动态风险适应、跨领域协同和量子安全预研,构建“技术+管理+法律”三位一体的防护体系,推动AI安全发展。
4202 1
|
机器学习/深度学习 人工智能 前端开发
【AI系统】AI编译器前瞻
本文基于《The Deep Learning Compiler: A Comprehensive Survey》调研,对比了TVM、nGraph、TC、Glow和XLA五个热门AI编译器,介绍了它们的特点和应用场景。文章分析了AI编译器面临的挑战,包括动态Shape问题、Python编译静态化、发挥硬件性能、特殊优化方法及易用性与性能兼顾问题,并展望了AI编译器的未来,探讨了编译器形态、IR形态、自动并行、自动微分及Kernel 自动生成等方面的发展趋势。
1277 1

热门文章

最新文章