23static 和final关键词 之static 和运行顺序

这两个关键词相当于修饰符

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)后对象级别(父类和子类的构造代码块和构造代码)  

参考:https://www.cnblogs.com/dolphin0520/p/3799052.html

posted @ 2019-06-26 20:28  三十六烦恼风x  阅读(1202)  评论(1编辑  收藏  举报