static关键字【详细】

About Me

欢迎各位读者访问,大家一起学习。

优秀是一种习惯♡♡♡

做更好的自己!

本人见识有限,写到博客难免有错误或疏忽的地方,还望各位大佬多多指点,在此表示感激不尽。♡♡♡


static关键字理解

1. 理解静态

问题:为什么是静态,有静是不是就有动啊?这个静是相对于什么来说?

C:\Users\Administrator\AppData\Local\TemputoolsDoutuPlugin/tempImage1635925058416.gif

  • 要想明白上面这个问题,首先得知道什么是类和对象

    1. :是具有共同特征事物的抽象【属性 — 方法】

    2. 对象:是根据类产生的具体实例

    举例:张三和李四都属于学生: 学生有自己的学号,姓名,年龄,等这些就称为属性张三和李四是具体实例对象,有具体数据。如:姓名:张三,年龄23,学号123456 | 姓名:李四,年龄66,学号888888,这些数据都是随着对象变化而变化随对象储存,这就是"动"。

  • 既然动是随对象变化而变化,那静就是不随对象变化而变化,而是这类事物共享的数据随类储存,也称为类变量。

    * 核心:静态是所属于类,非静态是所属对象。

2. 静态存在的意义

  1. 创建独立于具体对象的域变量或方法,即使没有对象也可以使用属性和方法

  2. 可以形成**静态代码块**以优化程序性能。

    静态代码块:Java中static{}代码块,主要用来初始化类,为类的静态变量赋予初始值

    2.1 静态代码块可以放到类中任意位置,不存在于任何方法体中。

    2.2 可以有多个static块。

    2.3 当类首次加载,会按照static块依次执行,并且只执行一次

3. 静态的使用

  1. 什么时候使用静态变量?

    中出现所有的对象共享数据,这个字段就可以使用static修饰。

  2. 什么时候使用静态方法?

    确定不会变化的工具行为,也就是方法中没有用到非静态数据时可以使用静态方法。常见的工具类

  3. 注意事项:

    3.1 静态方法只能访问静态成员(属性和方法)

    3.2 非静态方法可以访问静态成员也可以访问非静态成员(属性和方法)

    3.3 因为静态优先于对象的存在而存在,所以静态方法中不能使用this,super关键字

4. 静态的特点

​ 4.1 静态是从属于类的,随类的加载而加载。 【类】

​ 4.2 静态是优先于对象,所以说static不允许修饰局部变量

public class Demo02 {
    //定义成员变量使用static修饰
    static int a = 10;
    //定义一个方法
    public void m1(){
        //static int b = 20;  编译报错,因为static不允许修饰局部变量
    }
}

​ 4.4 静态修饰后可以被所有对象共享 【共享】

    static5个特点:
        1. static随着类的加载而加载
        2. static只会执行一次
        3. static类被所有的对象共享
        4. static中不能有this关键字
        5. static方法中不能使用非静态的方法  *****
  */
public class Demo01 {
    public static void main(String[] args) {
        //创建不同的对象,但是用static修饰的成员变量可以被该类所有的对象共享
        Demo02 d2 = new Demo02();
        System.out.println(d2.a);   // 10
        Demo02 d3 = new Demo02();
        System.out.println(d3.a);   // 10
        
    }
}

class Demo02 {
    //定义成员变量使用static修饰
    static int a = 10;

}

​ 4.5 可以直接通过类名调用【推荐】,也可以通过对象调用【不推荐】

public class Demo01 {
    public static void main(String[] args) {
        //调用成员变量,使用类名.调用
        System.out.println(Demo02.a); // 10
        //调用成员方法,使用类名.调用
        Demo02.method();
        //创建对象,使用对象的方式调用
        Demo02 d2 = new Demo02();
        d2.method();


    }
}

//创建一个类Demo02
class Demo02 {
    //定义成员变量使用static修饰
    static int a = 10;

    //定义一个成员方法使用static修饰
    public static void method() {
        System.out.println("Demo02方法");
    }

}

​ 4.6 静态只能随着类的加载只执行一次 【一次】

​ 4.7 静态中是不能thissuper的。 【this】

class A{
    static int a = 10;
    public static void method(){
      //  System.out.println(this.a);   编译报错,static中不能使用this关键字
    }
}
class B{
    int a = 10;
    public void method(){
        System.out.println(this.a);   //编译成功
    }
}

​ 4.8 静态中不能访问非静态 【 先后人】

//创建一个类A
class A{
    //创建一个成员变量
    int a = 10;
    static int b = 20;
     public static void method(){
        // System.out.println(a);  //编译报错,静态中不能使用非静态成员
         System.out.println(b);   // 编译成功,静态可以访问静态
     }
}

5. 静态的利弊

  • 利:节省空间,方便调用。

    因为使用静态变量修饰之后所有对象可以共享同一份数据,不必重新new对象,这样可以节省空间,调用的时候可以通过类名直接调用,方便调用。

  • 弊:生命周期长,强耦合,局限性

    因为静态是随着类的加载而存在,静态变量如果多次被引用赋值,可能会导致参数混乱,有一定的局限性,访问也只能访问静态成员。

    image-20211103173311061

6. 代码块

6.1:静态代码块

静态代码块是随着类的加载而加载,而且只能执行唯一一次。

//创建一个类A
class A{
    //创建一个静态代码块
    static {
        System.out.println("静态代码块");
    }
}

6.2:构造代码块

构造代码块每次在调用构造方法的时候,都会先执行构造代码块,而且都是比构造方法优先执行。

作用:所有构造方法里面相同的代码提取出来,放到构造代码块中。构造代码块每次创建对象就会执行一次。

public class Demo001 {
    public static void main(String[] args) {
        //创建对象,构造代码块没创建一次对象就会执行一次
        A a = new A(); //构造代码块
        A a1 = new A(); //构造代码块
    }
}
//创建一个类A
class A{
    //创建一个构造代码块
    {
        System.out.println("构造代码块");
    }
}

6.3:局部代码块

局部代码块是在方法中定义,目的是尽量的把变量从内存清理出去。

注意:局部代码块中的成员变量只在当前{}中有效

//创建一个类A
class A{
   //定义一个成员方法
    public void method(){
        //创建局部代码块
        {
            System.out.println("局部代码块");
        }
    }
}

7. 面试题

第一题:

public class Test {
    public static int count = 0;

    {
        count++;
        System.out.println("非静态代码块 count=" + count);
    }

    static {
        count++;
        System.out.println("静态代码块1 count=" + count);
    }

    static {
        count++;
        System.out.println("静态代码块2 count=" + count);
    }

    public static void main(String[] args) {
        System.out.println("*************** Static1 执行 ***************");
        Test sct1 = new Test();
        System.out.println("*************** Static2 执行 ***************");
        Test sct2 = new Test();
    }
}
1. 执行结果:
静态代码块1 count=1
静态代码块2 count=2
*************** Static1 执行 ***************
非静态代码块 count=3
*************** Static2 执行 ***************
非静态代码块 count=4

执行流程: static{…}为静态代码块会随着类的加载而依次执行,所以首先执行静态代码块1和2

​ {…}为非静态代码块会随着对象的创建而自动执行,所以就有非静态代码块3和4

第二题:

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");
    }
}
2. 执行结果
base static
test static
base constructor
test constructor

执行流程: 执行开始找main方法,因为main方法是执行程序的入口,但是在执行main方法之前必须先加载Test类,而在加载Test类的时候发现继承Base类,所以转去加载Base类,在Base类中有一个静态代码块会随着类的加载 而执行,所以先打印base static ,然后回到Test类中也有一个static块,接着打印test static,当加载完所有的类时,开始加载方法。然后到main方法中执行new Test(),会先去父类中调用父类的构造器也就是执行base constructor 最后执行本类构造器 test constructor 。

第三题:

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");
    }
}
3. 执行结果
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

执行过程:首先加载Test类然后执行static静态代码块打印test static,然后执行main方法中的new MyClass()类,而此时MyClass类还没有加载,在加载MyClass类之前发现继承Test类,又发现Test类已经被加载所以只需要执行MyClass中的静态代码块打印 myclass static,加载完后通过构造器生成对象,因为继承Test所以先执行父类的构造器,而执行之前必须先初始化父类的成员变量,所以执行Person person = new Person(“Test”);而此时Person类还没有加载,因此先会执行person类中的静态代码块打印person static,然后执行Person的构造方法打印person Test,接着执行Test类中构造器,打印test constructor, 最后初始化自身打印pereson MyClass, 执行MyClass的构造方法打印myClass constructor

第四题:

public class Test {

    static {
        System.out.println("test static 1");
    }

    public static void main(String[] args) {

    }

    static {
        System.out.println("test static 2");
    }
}
4. 执行结果
test static 1
test static 2

执行流程: 虽然main方法中没有任何语句,但是还是会输出,原因就是静态代码块是随着类的加载而执行。static可以写到类中的任意位置,但是不能写到方法的内部。

8. 思维导图

image-20211116102224611

不要在最能吃苦的年纪选择了安逸!!! — Tornado♥

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值