静态和成员的特点(Java)

本文通过两个问题深入探讨Java中静态函数、变量和成员函数、变量的特点。问题一阐述了非静态成员在类内部创建导致的栈溢出异常及其解决办法。问题二展示了静态成员如何在类加载时初始化,以及静态成员之间的调用机制。这两个例子有助于理解静态与成员在Java中的行为和加载过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了更加能方便的理解我们的静态函数、变量和成员函数、变量,通过两个问题来详细描述一下我们静态和成员的特点。

问题一:

class Demo03{
    public static void main(String[] args){

        A a=new A();
    }
}

class A{
  
    A a=new A();    
}

我们发现平时在创建对象时,我们都是在实体类的外面创建该实体类的对象,现在是在该实体类的内部创建了一个对象,而且还是在该类的函数外面,这里就相当于创建的对象用作该类的成员变量,我们先来看一下运行后的结果:

我们发现运行后,出现了了StackOverflowError这个错误,这叫栈溢出异常,之前在介绍递归的时候,提到过这个错误,就是因为不断调用自身而没有函数的弹栈导致运行函数的栈内存满了,就产生了这样的错误。那么在这里是为什么会出现这样的异常呢?我们在前面介绍构造函数的时候,提到对象的加载的流程,在主类中创建一个对象就相当于调用了该实体类A相应的构造函数,但是我们在该实体类A的构造函数外面创建的对象,当然在创建完对象之后,还要在堆内存中对相应的成员变量进行默认的初始化,而这里在实体类A中创建的对象相当于该类的成员变量,因此在默认初始化的时候不断的去创建对象再进行初始化,而导致的构造函数不断进栈出现栈溢出错误。那么如何避免这样的错误出现呢,我们可以用init,因为在此函数只会在对象创建的时候只调用一次。

package Excise2;

public class Demo02 {

	public static void main(String[] args) {

	A a = new A();
     }
}
class A{

   A a;
  public void init(){   
  this.a=new A();
 }
}

问题二:

class Demo03{
    public static void main(String[] args){

        B b=new B();
        System.out.println(b==b.b);
        System.out.println(b.b==b.b.b);
        System.out.println(b.b.b.b.b.b.b==b.b.b.b.b.b.b.b.b.b.b.b.b);
    }
}

class B{
    static B b=new B();
}

这里与上面的问题不同之处在于将实体类B中创建的对象用static修饰了,那么就是这个区别,就与上面产生了不同的运行结果:

通过运行发现,这里不仅没有出现错误还运行出了结果,原因是该实体类B在创建自己的对象时将创建的对象用static修饰了,那么表明此对象是全局静态的变量,在前面讲到过静态变量和函数是随着类加载的,那么在类加载到方法区后就已经创建了一个对象,在主类里面创建对象后,是不用对静态的变量进行默认初始化的,所以就不会存在一直调用构造函数使栈溢出的结果,那后面打印的结果是如何得来的呢?如图:

首先在方法区的静态区里会创建一个B的对象b且是全局的,然后主函数进栈再创建一个同名的对象b,此时两个对象的地址和存储位置是不同的,b.b是用主类中的对象去调用其他地方的对象,查找三部曲:1.该对象会先去它所在的函数里面找有没有要调用的东西,如果没有的话;2.去堆内存找相应的要调用的东西,如果还没有;3,就去该项目下的静态方法区中查找,如果还没有的话那就是真的没有了。

所以这里先在自己所在的函数中找除了自己叫b的其他叫b的对象,没有就去堆内存中找b的对象,还是没有就去静态区找,此时找到了并拿到了该b的地址0x123,然后当前主类的b拿到的地址是0x234,b==b.b比较的是两个对象的地址,不同则结果为false;而b.b==b.b.b和b.b.b.b.b.b.b==b.b.b.b.b.b.b.b.b.b.b.b.b两边所拿到的地址都是一样的都是0x123,因此都是true。

这就是静态和成员的特点,希望通过这两个例子,能对你理解静态变量和函数的加载有更好的帮助。

<think>好的,我现在需要回答用户关于Java静态成员特点、作用使用场景的问题。首先,我得回忆一下Java静态成员的基本概念。静态成员包括静态变量静态方法,它们属于类本身,而不是类的实例。这意味着无论创建多少个对象静态成员都只有一份副本。 接下来,我需要整理静态成员的主要特点。根据引用[1],静态方法不能直接访问非静态成员,这应该是静态成员的一个特点。另外,引用[3]提到普通内部类不能声明static的方法变量,但内部静态类可以,这可能涉及到静态成员的使用限制。静态成员在类加载时初始化,生命周期与类一致,这也是一个重要特点。 然后是作用。静态变量常用于共享数据,比如配置信息或计数器,这在引用[2]的例子中,使用静态Map来存储枚举值,应该属于共享数据的作用。静态方法则用于工具类,比如Math类中的方法,不需要实例化即可调用,方便使用。 使用场景方面,工具类方法、单例模式、常量定义都是常见的场景。引用[2]中的静态Map用于缓存数据,这也是一个典型的使用场景。同时,引用[3]提到内部静态类,可能用于封装仅与外部类相关的逻辑,比如Builder模式。 需要注意的是用户提供的引用内容中,引用[1]提到了静态方法不能直接访问非静态成员,而引用[2]展示了静态Map在枚举类中的应用,引用[3]则讨论了内部静态类的限制。这些都需要整合到回答中,并正确标注引用来源。 另外,用户可能对静态成员的线程安全性有疑问,但提供的引用中没有提到这点,所以可能需要简要提及,但避免深入。同时,要确保回答结构清晰,分点列出特点、作用、使用场景注意事项,并生成相关问题。 最后,检查是否符合系统级指令,比如数学公式的格式是否正确,是否有使用中文回答,引用的标识是否自然添加。确保没有使用任何Markdown格式,语言口语化,逻辑连贯。</think>### Java静态成员详解 #### 一、核心特点 1. **类级别共享** 静态成员(变量/方法)属于类而非实例对象,所有实例共享同一份存储空间。例如使用静态计数器统计实例数量时,所有对象操作的都是同一个计数器[^1]。 2. **初始化时机** 静态成员在类加载阶段初始化,早于对象创建,生命周期持续到JVM卸载类为止。 3. **访问限制** 静态方法只能直接访问静态成员,若需访问非静态成员必须先创建实例[^1]: ```java public class Demo { static int count; int id; public static void printCount() { System.out.println(count); // ✅直接访问静态变量 // System.out.println(id); ❌无法直接访问非静态变量 } } ``` 4. **内存效率优势** 单个静态成员副本可被所有实例共享,减少内存消耗,适用于存储全局配置、工具方法等场景[^2]。 #### 二、主要作用 1. **共享数据管理** 通过静态变量实现跨实例数据共享,如引用[2]中在枚举类使用静态Map存储映射关系: ```java public enum Status { PENDING(1), APPROVED(2); private static final Map<Integer, Status> cache = new HashMap<>(); static { for (Status s : values()) { cache.put(s.code, s); } } // 通过静态方法访问缓存 public static Status getByCode(int code) { return cache.get(code); } } ``` 2. **工具类封装** 静态方法常用于工具类,如`Math.sqrt()`无需实例化即可调用。 3. **单例模式实现** 结合私有构造器静态变量实现线程安全单例: ```java public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } ``` #### 三、典型使用场景 | 场景类型 | 示例说明 | 代码示例片段 | |-------------------|-----------------------------------|----------------------------------| | 全局配置参数 | 数据库连接配置 | `public static final String DB_URL = "jdbc:mysql://localhost:3306";` | | 工具类方法 | 字符串处理工具 | `StringUtils.isEmpty(str)` | | 对象创建控制 | 工厂模式/单例模式 | 见上文单例模式代码 | | 内部静态类 | 实现与外部类解耦的逻辑封装[^3] | `OuterClass.InnerStaticClass obj = new OuterClass.InnerStaticClass();` | #### 四、注意事项 1. **线程安全问题** 静态变量的共享特性可能导致多线程环境下的竞态条件,需通过`synchronized`或`ThreadLocal`等机制保证线程安全。 2. **过度使用风险** 滥用静态成员会破坏面向对象特性,增加代码耦合度。如非必要,优先使用实例成员。 3. **初始化顺序** 静态代码块按声明顺序执行,需注意依赖关系: ```java public class InitOrder { static int a = initA(); // 先执行 static { System.out.println("静态代码块"); // 后执行 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值