(转载)为什么不能从static方法调用非static的方法或变量?

本文详细解释了Java中静态成员与非静态成员的区别,包括它们在内存中的分配时机及如何正确访问。同时,通过具体示例展示了类加载过程及其对成员访问的影响。

http://blog.youkuaiyun.com/zhouwubin123/article/details/6623308

Java开发人员一定遇到过,在eclipse开发环境中,在包括main方法在内的static方法中调用某非静态变量,会编译出错。

public class StaticDemo {
	int x;
	void fun() {
		System.out.println("this is fun()");
	}
	public static void main(String[] args) {
		x = 5;		//	报错,不能被访问
		fun();		//	报错,不能被访问
	}
}

在static方法中调用非静态变量x和非静态方法fun()的出错信息如下图所示:



究其原因,我们来做如下分析:

任何程序最终都是在内存中执行的,变量只有在内存中占有一席之地时才能被访问,不在内存中的变量就不能被访问。类的静态成员(变量和方法)都属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;

非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。

public class StaticDemo {
	static int x;
	static void fun() {
		System.out.println("this is fun()");
	}

	public static void main(String[] args) {
		StaticDemo.x = 5;		//	可以用类名直接访问静态成员
		fun();				//	在类体内也可以直接访问
	}
}

由于静态成员在类加载的时候就会被分配内存,而非静态成员则不会。因此,在一个类的静态成员中去访问其非静态成员会出错,是因为在类的非静态成员不存在的时候,类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。

而类又是在什么时候加载的呢?核心类(比如String类)在 JVM 启动时(main 方法开始执行前)就会被加载,其它类在使用前(实例化对象、调用其静态方法、访问静态域等前)会被动态加载。

需注意:子类被加载前,它的所有超类要根据由父到子的顺序被逐一加载。

如下例子:

class A1 {
	public static int a = 5;
}

class B1 extends A1 {
	public static int a = 8;

	void print() {
		System.out.println(super.a);
		System.out.println(a);
	}
}

使用一个测试类:

public class TestStatic {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("b.a="+B1.a);
                System.out.println("b.a="+A1.a);
                new B1().print();
	}
}

最终的运行结果如下:

b.a=8
b.a=5
5
8
代码解析: 
当你在dos环境下输入:java TestStatic时, 

(1)JVM会首先加载TestStatic类,此时JVM会先看看TestStatic类是否存在static字段,没有,直接执行main方法; 

(2)main方法第一句是打印B1.a, JVM便会去找类B1,找到B1时,发现B1的父类是A1,于是父亲A1被优先加载;

(3)在加载A1时,会扫描A1中是否存在static的成员(静态变量、静态方法),发现存在静态变量a(static int a = 5) ,先加载,当把所有static的字段加载完,一个类就加载完成了,以后就无需再加载了; 

(4)父亲加载完成了,才轮到子类B1的加载,同样先扫描B1中是否存在static的字段,发现存在静态变量a(static int a = 8),加载a,此时B1也加载完毕了; 

第一条print语句到此时也执行完毕了。 

轮到下一条print语句了(new B1().print()),当执行这句时,会发生动态绑定,此时会有一个代表B1对象的this对象传递给print()方法,所以print()方法中的 System.out.println(a); 其实是System.out.println(this.a),会打印出一个8。至于super.a就是打印父类中的a,结果是5。
到此,main()方法执行完,整个程序退出。
<think>在Java中,静态方法static method)不能直接访问静态变量(实例变量)。原因如下: 1. **生命周期和加载顺序**: - 静态方法在类加载时就已经存在,而静态变量只有在创建类的实例时才会被初始化。 - 因此,在静态方法调用时,可能还没有任何实例存在,也就无法访问静态变量。 2. **调用方式**: - 静态方法可以通过类名直接调用,而不需要创建对象实例。 - 静态变量属于具体的对象实例,必须通过对象来访问。 3. **设计原则**: - 静态方法属于类,而静态变量属于对象实例。在没有对象实例的情况下,静态方法无法知道要操作哪个对象的变量。 ### 示例代码说明 ```java public class Example { private int nonStaticVar = 10; // 静态变量 public static void staticMethod() { // 尝试访问静态变量会导致编译错误 System.out.println(nonStaticVar); // 错误:无法从静态上下文中引用静态变量 } public static void main(String[] args) { staticMethod(); // 直接调用静态方法,没有创建实例 } } ``` ### 解决方法 如果需要从静态方法中访问静态变量,必须通过对象实例来访问: ```java public class Example { private int nonStaticVar = 10; public static void staticMethod(Example example) { System.out.println(example.nonStaticVar); // 通过传入的对象实例访问 } public static void main(String[] args) { Example obj = new Example(); staticMethod(obj); // 输出: 10 } } ``` ### 特殊情况:静态方法中访问当前类的实例 在静态方法中,不能使用`this`关键字,因为`this`代表当前对象实例,而静态方法调用时可能没有实例存在。 ### 总结 - **静态方法**:只能直接访问静态变量(类变量)和调用其他静态方法。 - **静态方法**:可以访问静态变量静态变量,也可以调用静态方法静态方法。 ### 相关问题 §§相关问题§§: 1. 为什么Java的main方法必须是静态的? 2. 静态方法中能否使用this关键字?为什么? 3. 如何理解静态变量在内存中的存储位置? 4. 设计一个工具类时,应该全部使用静态方法吗?有什么优缺点?</think>### 静态方法能否使用静态变量? **不可以**,静态方法**绝对不能直接访问静态变量(实例变量)**。以下是详细解释和代码示例: #### 原因分析: 1. **生命周期不同**: - 静态方法在类加载时初始化,生命周期与类相同 - 静态变量在对象实例化时创建,生命周期与对象实例绑定 2. **内存分配差异**: - 静态方法存在于方法区 - 静态变量存在于堆内存的对象实例中 3. **调用方式矛盾**: ```java // 静态方法可直接通过类调用 MyClass.staticMethod(); // 静态变量必须通过对象实例访问 MyClass obj = new MyClass(); obj.nonStaticVar = 10; ``` #### 代码验证: ```java public class StaticExample { // 静态变量(实例变量) private int instanceVar = 10; // 静态变量(类变量) private static int staticVar = 20; // 静态方法 public static void tryAccess() { System.out.println("访问静态变量: " + staticVar); // 允许 // 以下两行会导致编译错误 System.out.println("尝试访问实例变量: " + instanceVar); // 错误! System.out.println("通过this访问: " + this.instanceVar); // 错误! } public static void main(String[] args) { StaticExample.tryAccess(); // 无需创建实例即可调用 } } ``` #### 编译错误信息: ``` StaticExample.java:12: 错误: 无法从静态上下文中引用静态变量 instanceVar System.out.println("尝试访问实例变量: " + instanceVar); ^ StaticExample.java:13: 错误: 无法从静态上下文中引用 this System.out.println("通过this访问: " + this.instanceVar); ^ 2 个错误 ``` ### 正确访问方式 若静态方法需要访问静态变量,必须通过对象实例: ```java public class CorrectAccess { private int instanceVar = 30; public static void accessViaObject() { // 创建对象实例 CorrectAccess obj = new CorrectAccess(); System.out.println("通过对象访问: " + obj.instanceVar); // 允许 } public static void main(String[] args) { CorrectAccess.accessViaObject(); // 输出: 通过对象访问: 30 } } ``` ### 关键区别总结 | 特性 | 静态方法 | 静态方法 | |---------------------|-----------------------------|-------------------------| | 访问静态变量 | ✅ 直接访问 | ✅ 直接访问 | | 访问静态变量 | ❌ 禁止直接访问 | ✅ 直接访问 | | 使用`this`关键字 | ❌ 禁止使用 | ✅ 可以使用 | | 调用方式 | `ClassName.methodName()` | `object.methodName()` | | 内存位置 | 方法区 | 堆内存(对象关联) | ### 根本原因 Java虚拟机规范(JVM Specification)要求: 1. 静态方法调用时没有隐式的`this`参数传递 2. 静态变量必须通过`aload_0`(对象引用)访问 3. 静态上下文不存在对象引用,因此无法解析静态变量地址
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值