一、static的用途
1.static修饰的变量和方法是属于类的;
2.static修饰的变量和对象,方法存储在方法区的静态常量池中;是共享的;
3.可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法
4.static修饰的代码块在main方法前执行,目的是修饰main方法
5.static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问
6.static方法是没有this的方法,在static方法内部不能调用非静态方法,反过来是可以的
static:方便在没有创建对象的情况下来进行调用方法和变量
二、static修饰符
重点:static属于类
1.static修饰变量
static变量也称作静态变量,static成
静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个地址,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个地址空间,各个对象拥有的地址空间互不影响。
2.static修饰方法
static方法称作静态方法,静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。
3.static修饰代码块
在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
在main方法运行所以能优化程序
三、static的内存加载
static关键字存储在我们的方法区当中的静态常量池当中,static修饰的方法、变量和代码块都是可以被用来共享的
输出:李四 王五
输出:王五
输出:李四
public class Person {private int age ;private String name;public static String from;public Person(int age, String name) {super();this.age = age;this.name = name;}@Overridepublic String toString() {return "Person [age=" + age + ", name=" + name + ", from=" + from + "]";}public static void main(String[] args) {Person person1 = new Person(20,"张三");Person person2 = new Person(21,"李四");Person.from = "中国";System.out.println(person1.toString());System.out.println(person2.toString());}}
Person [age=20, name=张三, from=中国]
Person [age=21, name=李四, from=中国]
四、static执行顺序
public class Test {static{System.out.println("test static");}public Test(){System.out.println("test constructor");}public static void main(String[] args) {new Test();}
}class Base{static{System.out.println("base static");}public Base(){System.out.println("base constructor");}
}
先来想一下这段代码具体的执行过程,在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。
public class Test {Person person = new Person("Test");static{System.out.println("test static");}public Test() {System.out.println("test constructor");}public static void main(String[] args) {new MyClass();}
}class Person{static{System.out.println("person static");}public Person(String str) {System.out.println("person "+str);}
}class MyClass extends Test {Person person = new Person("MyClass");static{System.out.println("myclass static");}public MyClass() {System.out.println("myclass constructor");}
}
test static myclass static person static person Test test constructor person MyClass myclass constructor
类似地,我们还是来想一下这段代码的具体执行过程。首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器。
再来看一道经典例题:涉及到类的加载顺序问题
结果分析:
1.main方法所在的类一定是优先加载进来的,main方法加载进来后,就开始从上往下给静态的域和静态的初始化方法进行执行(分为两个阶段,第一阶段给所有的静态域赋值为0或null,先全部引入进来,静态方法这时候不执行,第二阶段,开始从上往下执行静态初始化语句和静态初始化块)
这时候执行k=1,第二句new了一个新对象,这时从上到下执行一遍非静态的初始化语句和初始化块,找到了一个非静态初始化块,这时执行里面内容,调用print方法,输出第一句
2.接着执行非静态的初始化语句和初始化块,
到了这一句,同样,调用print方法,输出第二行
3.没有非静态的了,这时执行构造函数,导致输出第三行
4.然后执行第二个new语句,同理,会输出4.5.6句
7.接着执行
输出第七行
8.执行n=99语句,n就变成了99,然后执行static静态块的内容,输出第八行
9.然后在最底下main方法里有new一个新对象的语句,这时,从上到下执行一遍非静态的初始化语句和初始化块,依次找到非静态初始化块,和private int j =print ("j")语句,依次输出第9.10句,然后再打印构造函数,输出第11句。
本文参考:Java中的static关键字解析 - Matrix海子 - 博客园