这两个关键词相当于修饰符
static 修饰变量,方法,代码块,内部类
static 修饰的信息都只加载一次。
1.变量----静态变量
可以被所有对象共享
用static修饰变量就成为了类变量,随着类加载(方法区静态常量池)而加载到方法区静态区,静态区会对静态变量赋予系统默认初始值,静态变量与类同级 他是先于对象存在的,所以是用通过类名点去调用静态变量,当然也可以通过对象点出来。
静态变量存储在方法区静态区,它会对外提供了一个地址,后续创建的所有对象都共享这一个地址,静态变量是唯一的。当这个属性或者变量想让所有的对象都能共享,就设置成静态
作用:如果属性需要被共享就可以用stastic 修饰。
方法中可以定义静态变量?静态变量与类同级,因为方法里的内容被调用的时候才执行 ----不可以
构造代码块可以定义静态变量吗? 不可以 静态变量与类同级,而构造代码块与对象同级。
2.静态方法
静态修饰的方法随着类加载(方法区静态常量池)而加载到方法区静态区。不给静态方法赋值,静态区不会对静态方法赋予系统默认初始值。可以通过类名点来调用也可以通过对象来调用。静态方法执行放在栈内存执行。
静态方法中可以定义静态变量吗?方法的内容需要方法被调用的时候才执行,所以是不可以。
由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的因为它不依赖于任何对象,既然没有任何对象,就谈不上this了,并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能被调用。
静态信息是无法直接调用非静态信息 要通过对象访问
非静态的信息可以直接调用到静态信息 :因为加载到非静态信息的时候就已经是开始加载对象了,而类信息和静态信息已经加载完成了,所以可以调用到静态信息
但值得注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
静态方法能不能重载 -----静态方法可以重载 ,静态方法不能被重写
重写是运行时期多态,运行时期绑定代码---针对的是对象
静态方法---与类同级----在类加载的时候绑定代码
静态方法针对的是类进行绑定的,重写是针对对象进行绑定的
所以两者的级别不一样,在静态方法加载的时候都没有对象,所以在静态方法加载完成后,他都没有重写这形式。
java中方法签名一致的方法要么都是静态方法,要么都不是静态方法
所以以后写代码就子类与父类要么都不出现static要么全是static
父子类中可以存在方法签名一致的静态方法但不是方法的重写,父类中要么都是静态方法要么都不是静态方法,就像下面的如果重写成功的话应该执行的是B中的m方法,执行看子类。
Class A{
static final i;//静态区的初始值仅仅只是标记是初始值,没有实际意义,所以静态常量需要重新赋值。 但是随着版本的更迭,静态常量不在方法区的静态区了,会有初始值。
}
public class Error { public static void main(String[] args) { A c=new B(); c.m(); } } class A{ static void m(){ System.out.println("A类的m方法"); } } class B extends A{ static void m(){ System.out.println("B类的m方法"); } }
结果是:A类的m方法,所以A和B不构成多态,他们没有方法的重写
如果加上重载注释会报错
下面是有关静态方法运行时间的案例
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.下面这段代码的输出结果是什么?
public class Test extends Base{ 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"); } }
结果:
base static test static base constructor test constructor
至于为什么是这个结果,我们先不讨论,先来想一下这段代码具体的执行过程,在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。
3.静态代码块
在方法外类内用static修饰的{}---随着类的加载而加载,与类同级
作用:1.给静态属性赋初值
2.static代码块随着类的加载而加载且只加载一次,两个对象和一个对象的创建都只是加载一次
3.预先加载某些重要信息 IO流的时候
public class Error { static { //静态代码块 System.out.println("静态代码块1"); } public static void main(String[] args) { A c=new A(); A c2=new A(); System.out.println(c.age); } } class A{ static int age; static { //静态代码块 age=10; System.out.println("静态代码块2"); } { System.out.println("构造代码块"); } public A() { System.out.println("构造方法"); } }
结果
静态代码块1
静态代码块2
构造代码块
构造方法
构造代码块
构造方法
10
运行顺序是 静态->对象(构造代码块,构造函数) ,父类->子类 具体讲就是 父类的静态(属性,代码块,方法(加载))静态信息的加载看代码的顺序-->子类的静态-->父类的对象(构造方法、构造代码块、属性(在构造代码块前面就先执行))--->子类对象 属性优先于构造方法,先类级别(父类和子类的static)后对象级别(父类和子类的构造代码块和构造代码)