欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(九)static和fianl

程序员文章站 2022-05-28 23:25:34
...

静态(static)和最终(final)

静态(static)

static本身是一个修饰符,可以修饰变量、方法、内部类、代码块。
静态是从类的层面来看的,已经超越了对象。静态是依附于类的存在而存在,所以静态修饰的变量、方法、代码块也可以叫类变量、类方法、类代码块。

1、特点

标记一个方法为static,意味着这个方法被所在类的所有实例公用,在类装载时初始化,被所有该类的实例共享,同时意味着:
1.static方法内部不能引用/声明static变量。
2.static方法不能被子类重写为非static方法。
3.父类的非static方法不能被子类重写为static方法。
4.static代码块可以用static{}来完成,在类被第一次装载时执行初始化,先于静态方法和其他方法的执行。

2、静态内存流程

(九)static和fianl
图片详解:
程序启动的时候先通过类加载器(ClassLoader)将基本核心类库(java.long包)加载到方法区,其次是主函数所在的类加载到方法区。
加载主函数所在的类。首先将Java源文件编译为class文件,然后class文件通过类加载器(ClassLoader)将class文件加载到方法区当中。将StaticDemo1.class存放到方法区中,将主函数main存放到方法区的静态区中。
当执行语句Person p1 = new Person(); 时,会将Person类的java源文件编译成class文件,然后将class文件通过类加载器(ClassLoader)将class文件加载到方法区中。在静态常量池中开辟空间,将Person.class以及其包含的内容存放在静态常量池,然后在静态区中开辟空间,将Person.class中的静态属性以及静态方法存放到静态区中,并给静态属性变量赋予默认值(gender = ‘\u0000’)和方法区空间地址(0x02da)。以上工作完成之后,会在栈内存中声明一个对象p1。然后执行new Person,在堆内存中开辟空间存储p1对象的一系列属性(name,age,gender),并赋予默认值(name = null;age=0;gender = 0x02da;因为gender为静态变量所以gender的默认值是方法区中对应静态变量的空间地址)和堆内存空间地址(0x6f8d),然后将堆空间地址赋值给栈内存中对应的对象。
当执行p1.name = “张无忌”;时,对象p1会沿着p1对象中存储的堆内存空间地址找到自己的属性,给自己的name属性赋值为:张无忌,将默认值覆盖。p1.age = 81;也是如此。
当执行p1.gender = ‘男’;时,对象p1会沿着p1对象中存储的堆内存空间地址找到自己的属性gender,然后沿着gender中存储的方法区空间地址找到方法区中静态区里的gender,然后给静态区里的gender赋值为:男。
当执行语句Person p2 = new Person();时,因为所有的类已经加载过了,所以可以直接进行声明和初始化操作。栈内存和堆内存中的操作和语句Person p1 = new Person();是一样的。
当执行语句p2.name = “赵敏”; p2.age = 18 ; 过程和p1.name = “张无忌”; p1.age = 81 ; 是一样的。
当执行p2.gender = ‘女’;时,对象p2会沿着p2对象中存储的堆空间地址找到堆内存中p2对应的gender属性,然后在根据gender属性中存放的地址找到方法区中静态区里的gender变量并赋值为:女,将原来的“男”值替换掉。由于p1和p2中gender属性存储的方法区空间地址是一样的,所以我们可以认为p1.gender = p2.gender = ‘女’;。
当执行语句p1.eat();时,p1对象会调用方法区中静态常量池里Person.class的eat();方法,Person.class会给出eat()方法的地址,然后入栈,在栈内存中开辟空间执行eat();方法,eat();方法中会自动产生this关键字代表p1对象,此时this 中存储的使p1中存储的堆内存空间地址,然后输出方法的结果。

3、静态/类变量(static variable)

1.定义

static修饰变量那么这个变量我们就称之为静态变量,又称为成员变量或者类变量。

2.特点

静态变量随着类的加载而加载到了方法区中的静态区,并且在静态区中自动赋予了一个默认值。静态变量先于对象而存在,所以静态变量可以通过类名来调用,也可以通过对象来调用。该类产生的所有的对象实际上存的是该静态变量在静态区中的地址,静态变量是被所有对象所共享的。
例如:System.in; System.out; 都是静态变量。
1.静态变量有默认值。
2.静态变量可以被类调用,也可以被对象调用。
3.静态变量是被所有对象共享的。
4.静态变量可以使用this来调用。
5.静态变量不能定义到构造代码块中,但可以在构造代码块中对静态变量赋值。
6.静态变量不能定义到函数/方法中。静态变量在加载的时候就需要分配空间,而方法在加载的时候不需要空间,在执行的时候才需要内存空间。
注意:实际开发中,static能少用就少用。

3.静态变量与实际变量的区别

1.在语法定义上的区别。静态变量前要加static关键字,而实例变量前则不加。
2.在程序运行时的区别。实例变量属于某个对象,必须创建对象才能使用。静态变量则可以直接使用类名引用。

4、静态/类方法(static method)

1.定义

static修饰方法就叫静态方法,也叫类方法。
之前我们接触过的Arrays.sort()、Arrays.toString()、Arrays.copyOf()、System.arraycopy()都是静态方法。

2.特点

在类加载的时候加载到了方法区中的静态区,只是存储在静态区,在方法被调用的时候到栈内存中执行。静态区中的元素不归属于某一个对象而是归属于类。静态方法先于对象而存在的,所以静态方法可以通过类名来调用,也可以通过对象来调用。

1.静态方法中不能定义静态变量,但可以对静态变量赋值。
2.不能在静态方法中直接调用本类中的非静态方法。可以通过创建对象来调用。所有非静态的方法和变量都需要对象来调用。
3.静态方法中不能使用this或者super。(主函数是静态方法)
4.静态方法可以重载。
5.静态方法不可以重写。但父子类中可以出现方法签名一致的静态方法,比如:main主函数。
6.静态方法可以被继承。

父子类中可以存在方法签名一致的静态方法。如果父子类中出现了两个方法签名的一致的方法要么全是静态,要么全是非静态。
静态不属于对象。而是属于类。不依赖某个对象而存在,而是依赖于类存在的。类没有多态,多态针对的是方法。
注意:类只加载一次,是在第一次使用的时候才加载到方法区,而且加载到方法区中之后就不再移除了。

5、静态/类代码块(static code block)

1.定义

在类中成员的位置用static修饰用{}括起来的代码块。
静态/类代码块针对的是类。
实际上静态代码块使随着类的加载而加载到静态区,在类创建对象或者执行方法之前执行一次,终其一生只执行一次。

2.作用

上面也提到了,静态代码块只在类执行的时候执行一次,所以静态代码块一般是对类进行初始化。

3.执行顺序

在这个类第一次被真正使用(第一次创建对象/调用方法)的时候执行一次。如果一个类包含多个静态/类代码块,则按照书写顺序执行。由于类只在第一次使用的时候加载,所以静态代码块也只执行一次。
代码执行顺序:先父类后子类,先静态后动态。(先父子类的静态,后父子类的动态)静态优先,父类优先。
代码是从上到下,从左到右依次编译执行。
创建子类对象的时候需要先创建父类对象->加载父类->执行父类静态代码块->执行子类代码块->父类构造代码块->父类构造函数->子类构造代码块->子类构造函数
(九)static和fianl
其实随着学习的深入,对静态的了解也更深入,可以说,所有的静态都和类的成员属性是平级的,但这个属性有些特殊,不能嵌套到别的成员里面声明,但可以被调用。

final(最终的)

final可以修饰数据,方法以及类。

1、常量(final variable)

1.定义

当final修饰数据(基本类型和引用类型)的时候,表示这个变量的值不可变,称之为常量。终其一生只能赋值一次。
在Java中所说的常量往往是指静态常量。因为实质上只有静态常量才是独有的一个。
我们之前接触过的System.in/out,in和out都是静态常量。NaN和infinity也是静态常量。arr.length,length是个常量。

2.特点

1.常量在定义好之后不可改变。
我自己的理解final固定的其实是栈内存中的数值。比如:就相当于常量在栈中的空间被锁死,只允许一次进入。这一次进入不确定是在什么时候。
2.常量可以作为参数传递。
3.对引用类型而言,final固定的是其在栈中的地址不可变。
数组在栈内存中存储的是地址,用final修饰,是不能改变数组的地址,但数组的值可以改变。
4.成员常量只要是在对象创建完成之前(构造方法/函数执行结束之前)赋初始值即可。
5.静态成员常量(static final)只要在类加载完成之前给值即可,而且只能在静态代码块中赋值。

public class Demo {
    //定义一个常量
    private final int i;
    //定义一个静态常量
    private fianl static int x;
    //初始化静态常量
    static{
        x=3;
    }
    //在构造函数中初始化常量
    public Demo(){
        this(5);
    }
    public Demo(int i){
        this.i = i;
    }
    pbulic static void m(int i){
        int y = i;
    }
    public static void main(String[] args){
        Demo d = new Demo(5);//对象创建的时候赋值
        m(t.i);//作为参数传递
        m(x);//作为参数传递
    }
}

2、最终方法(final method)

1.定义

final修饰方法的时候,这个方法就是最终方法。

2.特点

1.最终方法不可以被重写,可以重载,可以被继承。
2.静态方法可以被final修饰。

public class Demo{
    //最终方法
    public final void m(){}
    //重载最终方法
    public int m(int i){return i;}
    //修饰静态并重载
    public final static void m(double d){}
}
class A extends Demo{
    //此时只能重写m(int i)方法
    public int m(int i){
        int y = i;
        return y;
    }
}

3、最终类

1.定义

final修饰类那么这个类就是最终类。

2.特点

1.最终类不可以被继承,没有匿名内部类形式。
2.现阶段最终类的方法不能被重写。
我们之前接触过的System 、String是最终类。一般java中好多工具类都是最终类。

public final class Demo{}
相关标签: 对象