- 静态变量(静态域)
- 静态代码块
- 静态方法
- 静态类
1、静态变量
- 无论创建多少个对象,该类的所有实例都共享同一个static变量,静态数据都只占用一份存储区域。也就是说在类加载的时候分配一块存储空间,所有此类的对象都可以操作此块存储空间。
- static关键字不能作用于局部变量,因此它只能作用于域(字段)。
- 如果一个域是静态的基本类型,且也没有对它进行初始化,那么他就会获得基本类型的标准初始值,如果它是一个对象引用,那么它的默认初始值为null。
- 静态变量存放在方法区中。
- 非静态变量存放在堆或栈空间里。
静态数据的初始化问题:
import staticcom.utils.Print.*;
class Bowl{
Bowl(int marker)
{
println("Bowl(" + marker + ")");
}
void f1(int marker)
{
println("f1(" + marker + ")");
}
}
class Table{
static Bowl bowl1 = new Bowl(1);
Table()
{
println("Table()");
bowl2.f1(1);
}
void f2(int marker)
{
println("f2(" + marker + ")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupborad{
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
public Cupborad() {
println("Cupborad()");
bowl4.f1(2);
}
void f3(int marker)
{
println("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {
static Table table = new Table();
static Cupborad cupborad = new Cupborad();
public static void main(String[] args) {
println("Creating new Cupborad in main");
new Cupborad();
println("Creating new Cupborad in main");
new Cupborad();
table.f2(1);
cupborad.f3(1);
}
}
/*
代码解析:
初始化顺序: 先静态对象,后"非静态"对象。(静态对象中按照定义时书写顺序初始化)
从输出结果中分析,要执行main()(静态方法),必须加载StaticInitialization类,然后其静态域table和cupborad被初始化,
这将导致它们对应的类也被加载,并且由于它们也都包含静态的Bowl对象,因此Bowl虽有也被加载。
总结创建过程:,假设有个名为Dog的类:
1、即使没有显示地使用static关键字,构造器实际上也是静态方法。因此当首次创建类型为Dog的对象时(构造器可以看成静态方法),
或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
2、然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只是在Class对象首次加载的时候进行一次。
3、当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4、这块存储空间会被清零,这就自动地将Dog对象中所有的基本类型数据都设置成了默认值(对数字来说是0...)——这是通过将对象内存设置为二进制零二一举生成的,而引用则被设置成了null。
5、执行所有出现于字段定义出的初始化动作。
6、执行构造器。
注意:
1、static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用
(当然也可以在非静态成员方法中使用),但是不能在其他类中通过类名来直接引用。
2、每new一个对象,就会创建一个该类的实例,但无论创建多少个对象,static变量只创建一次。
*/
2、静态块的使用
static代码块也叫静态代码块,是在类中独立于类成员的
static语句块,可以有多个,位置可以随便放
,它不在任何的方法体内,
JVM加载类时会执行这些静态的代码块
,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。
java 静态代码块 静态方法区别
一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,
在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用
比如main方法就必须是静态的 这是程序入口
两者的区别就是:
- 静态代码块是自动执行的;
- 静态方法是被调用的时候才执行的.
/*
类装载步骤
在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
装载:查找和导入类或接口的二进制数据;
链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
校验:检查导入类或接口的二进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引用转成直接引用;
初始化:激活类的静态变量的初始化Java代码和静态Java代码块。
*/
静态代码块的初始化顺序:
class Parent {
static String name = "hello";
{
System.out.println("parent block");
}
static
{
System.out.println("parent static block");
}
public Parent()
{
System.out.println("parent constructor");
}
}
class Child extends Parent {
static String childName = "hello";
{
System.out.println("child block");
}
static
{
System.out.println("child static block");
}
public Child()
{
System.out.println("child constructor");
}
}
public class StaticIniBlockOrderTest {
public static void main(String[] args) {
new Child();//语句(*)
}
}
/*
总结:
对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,
当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;
父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。
总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
*/
静态代码块与非静态代码块的区别:
1、静态代码块是在类加载时自动执行的,非静态代码块在创建对象自动执行的代码,不创建对象不执行该类的非静态代码块。 顺序: 静态代码块--》非静态代码块--》类构造方法。
2、在静态方法里面只能直接调用同类中其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。因为对于非静态的方法和变量,需要先创建类的实例对象后方可使用,而静态方法在使用前不用创建任何对象。
3、如果某些代码必须要在项目启动时候就执行的时候,我们可以采用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,此时代码是被动执行的。
3、静态方法
- 静态方法可以直接通过类名调用,任何的实例也都可以调用
- 因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
- 声明为static的方法一般有如下几个限制
- 它们仅能调用其他的static 方法。
- 它们只能访问static数据。
- 它们不能以任何方式引用this 或super。
4、静态类
通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。
import static com.utils.Print.*;
public class StaticCls {
public static void main(String[] args)
{
oinew OuterCls.InnerCls();
}
}
class OuterCls {
//声明一个静态类,只有内部类才能声明为静态类
public static class InnerCls {
InnerCls()
{
println("InnerCls");
}
}
}