static 关键字

JAVA 代码由类组成,而类由属性 (即变量)和方法组成,没任何方法和属性能够在类外面;而属性和方法又分为静态或非静态,静态与非静态的生存期、级别、生成顺序都是有区别的;所谓静态就是以 static 所修饰的属性或方法;

特点:

  • 与方法中的局部变量不一样,由 static 所修饰的成员的生存期和类是相同的;

    这一点很好验证:
public class test
{
	public static void main (String[] args)
	{
		System.out.println (book.name);
	}
}
class book
{
	static String name = "JAVA program";
}

在这里插入图片描述
之前我们说过要引用不同类中的引用数据类型需要用 new 开辟一块内存空间之后才能引用,但这里我们没有用 new 而是直接引用,因为加了 static 关键字之后成员属性 String name 与方法同时产生,当还没有执行主方法的时候就已经存在了,因此并不需要再用 new 来开辟空间;
假如我们把关键字 static 去掉就会出问题:

public class test
{
	public static void main (String[] args)
	{
		System.out.println (book.name);
	}
}
class book
{
	String name = "JAVA program";
}

在这里插入图片描述
可以看到系统报错说无法引用费静态变量book.name,因为当主方法压栈之后并未对引用数据类型 book 开辟内存空间,系统无法找到相应的地址,当我们用了 new 之后又可以了:

public class test
{		
	public static void main (String[] args)
	{
		book one = new book ();
		System.out.println (one.name);
	}
}
class book
{
	String name = "JAVA program";
}

在这里插入图片描述
这里需要注意,如果说把 book one = new book ();放在了主方法的外面则又会出现刚才一样的报错;
我们都知道 JAVA 是顺序向下执行的,并且内部的 GC 机制会自动帮我们回收没有用到的垃圾,当 test.class 文件加载到方法区后在 main 方法压栈之前就直接通过 new 生成了引用数据类型,然而当 book one = new book ();执行完毕后并未对其进行操作也没有用 static 关键字进行保留,因此 GC 为了保留内存将其判定为垃圾自动回收,之后 main 方法压栈,这时 one 已经被回收了,相当于没有,所以还是找不到;

  • 静态方法只能访问静态成员变量,无法访问非静态成员变量,但可以通过创建类来访问非静态成员变量;

    这一点其实在上面已经验证过了,无论是访问同一个类中的成员变还是访问不同类中的成员变量,在未加 static 关键字的时候 main 方法 (main 方法是 static 修饰的) 都不可访问,而通过在方法中用创建类来调用就可以访问;
  • 静态方法可以调用静态方法,无法调用非静态方法,但可以通过创建类来调用非静态方法;

public class test
{
	public static void main (String[] args)
	{
		hello ();
	}
	public static void hello ()
	{
		System.out.print ("hello");
		world ();
	}
	public static void world ()
	{
		System.out.println (" world");
	}
}

在这里插入图片描述
这时候是可以正常执行的,但当我们把 world 中的 static 关键字去掉之后:

public class test
{
	public static void main (String[] args)
	{
		hello ();
	}
	public static void hello ()
	{
		System.out.print ("hello");
		world ();
	}
	public void world ()
	{
		System.out.println (" world");
	}
}

在这里插入图片描述
我们可以看到在编译是报了跟之前一样的错误;

我们知道 JVM 中的方法区存放所有以 static 修饰的成员,以 static 所修饰的变量或者方法与 .class 文件同时加载到方法区中,可以说他们是同时出生的,形象的可以看作类多少岁,以 static 所修饰的变量或者方法就多少岁,他们是同级的;而没有被 static 所修饰的方法并不会与 .class 文件一起加载到方法区中,当调用它的时候需要通过引用数据类型用 new 来开辟一块空间来得到一个首地址,再由首地址找到该方法的地址,再进行调用;如此看来非静态方法的使用过程其实和成员属性的使用过程是一模一样的,因此可以得出结论非静态方法其实是属于对象级别的;

既然如此就很好解释了,类级别的是最先产生的,而对象级别的则需要在静态方法压栈后在栈中进行应用才产生,那么当没有引用时对象级别的就不存在,因此在静态方法中直接调用非静态方法是不可以的;就好比你满世界的去找一个还没有出生的人,怎么可能找得到呢?

对于上面这个代码,当在 hello 方法中调用 world 方法时并未对 test 类开辟空间,因此无法得知 world 方法的地址,我们像前面一样做:

public class test
{
	public static void main (String[] args)
	{
		hello ();
	}
	public static void hello ()
	{
		test wor = new test ();
		System.out.print ("hello");
		wor.world ();
	}
	public void world ()
	{
		System.out.println (" world");
	}
}

在这里插入图片描述
这里又可以了,说明以上推断是没有问题的;

这里引申出一个新的东西,叫做静态代码块;

  • static 代码块:

所谓静态代码块就是以 static { } 的形式编写的代码块, static 代码块中的执行语句无需调用直接就可以执行,同时与用 static 修饰的成员一样与类是同一级别的,但不一样的是,它只能在类初始化的时候执行一次,执行完毕就销毁,并且优先于 main 方法执行。从这里我们可以推断它是在方法区执行;

通过一个简单的代码来验证一下:

public class test
{
	public static void main (String[] args)
	{
		System.out.println ("SiChuan");
	}
	static
	{
		System.out.println ("China");
	}
}

在这里插入图片描述
从结果可以看到确实是优先于 main 方法执行的;

但我们知道 main 方法是整个程序的入口,除了类体中定义的成员属性之外,它一定是第一个压栈的,但 static 代码块却优先于 main 方法,要满足以上条件就只能是 static 代码块是在方法区执行;

方法区的内存比栈区要大我们可以通过 static 代码块在方法区直接完成一些操作来提高程序的运行效率,一般用于初始化,但也不限于初始化。

  • 实例代码块:

    实例代码块就是直接用 { } 所编写的代码块,它优先于 main 方法,但落后于 static 方法被执行,可以执行多次,在构造方法执行之前自动执行一次;

    同样我们来验证一下:

public class test
{
	public test ()
	{
		System.out.println ("ChengDu");
	}
	public static void main (String[] args)
	{
		new test ();
		new test ();
	}
	static
	{
		System.out.println ("China");
	}
	{
		System.out.println ("SiChuan");
	}
}

在这里插入图片描述

  • 总结:

    1、static 所修饰的变量或者方法与类是同一级别的;
    2、访问 static 所修饰的变量无需用类创建对象,可以直接用类名 . 变量名来访问;
    3、静态方法不可直接访问非静态变量,但可以通过创建对象来访问;
    4、静态方法不可直接调用非静态方法,也可以通过创建对象来调用;
    5、静态变量或方法可以直接被调用或访问;
    6、static 代码块优先于 main 方法被执行,且只执行一次;
    7、实例代码块优先于 main 方法,但落后于 static 代码块被执行,且在构造方法执行之前自动指定一次;

以上全属 JAVA 小白的自我感悟,如有错误,还望纠正!!!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页