为什么不能在静态的方法里面调用非静态方法或变量?

原文链接: https://blog.csdn.net/zhouwubin123/article/details/6623308#commentBox

tips:

  • 静态:static
    用法:是一个修饰符;用于修饰成员(成员变量和成员函数)
    当成员被静态修饰后,就多了一种调用方式,除了可以被对象调用外,还可以直接被类名调用格式:类名.静态成员

  • 静态的特点:

    • 随着类的加载而加载,也就是,说静态会随着类的消失而消失,说明静态的生命周期最长
    • 优先于对象的存在,明确一点:静态是先存在的对象是后存在的
    • 被所有对象共享
    • 可以直接被类名多调用
  • 实例变量和类变量的区别

    • 存放位置:类变量随着类的加载存在于方法区中,实例变量随着对象的对象的建立存在于堆内存里
    • 生命周期:类变量生命周期最长,随着“类”的加载而加载,随着类的消失而消失;实例变量随着“对象”的消失而消失
  • 静态的使用注意事项:

    • 静态方法只能访问静态成员(包括成员变量和成员方法);非静态方法可以访问静态也可以访问非静态
    • 静态方法中不可以定义this,super关键字;因为静态优先于对象存在,所以静态方法中不可以出现this,super关键字
    • 主函数是静态的。
  • 静态的利弊

    • 利:对 对象的共享数据进行单独空间的存储,节省空间,没有必要没一个对象中都存储一份
      可以直接被类名所调用
    • 弊:生命周期过长,访问出现局限性(只能访问静态)

所以得出:

因为静态是随着类的加载而加载的 也就是说类只要存在了,那么静态就存在了,而静态是优先于对象存在的 ,你要访问非静态的东西,也就是非静态的成员(包括变量和方法),非静态的东西还没存在你怎么访问的到吗??

对上面结论做出解释案例:

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

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

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

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的字段加载完,一个类就加载完成了,以后就无需再加载了; 这里就已经对A1类进行了加载,后面就不会加载它了,用到了也只会到方法区加载字节码信息

  • (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()方法执行完,整个程序退出。

扩展

这里可以研究一下类的加载顺序?什么情况下会触发类的加载机制?jvm的加载机制又是怎样的?等问题

参考:
为什么静态方法不能调用非静态的变量和方法
为什么不能从静态的方法里面调用非静态方法,或变量?
(转载)为什么不能从static方法调用非static的方法或变量?

猜你喜欢

转载自blog.csdn.net/weixin_43719015/article/details/102718905