Home  >  Article  >  Java  >  Deep understanding of the static keyword

Deep understanding of the static keyword

青灯夜游
青灯夜游Original
2019-11-27 16:15:293214browse

Deep understanding of the static keyword

Before I start talking about static, I want you to see an interesting piece of code:

public class Test {
     
    static{
        System.out.println("test static 1");
    }
  
    static{
        System.out.println("test static 2");
    }
    
    public static void main(String[] args) {
         
    }
}

After reading the program, Xiaobai Tongxiao said: What is this? There is nothing in the main method, so what can it run? Blogger, you are a star...

运行结果:
test static 1
test static 2

小白children's shoes: That what...that what...I said something, blogger, I didn't say anything...

In fact, those who understand the above code will naturally understand it, and those who do not understand will naturally not understand it, because the above code involves JVM class loading! Of course, it is not within the scope of this blog article. If you are interested in understanding the above program, this article may be helpful to you

1. The main significance of static existence

The main meaning of static is to create domain variables or methods that are independent of specific objects. So that even if the object is not created, you can still use properties and call methods!

The static keyword also plays a key role in being used to form static code blocks to optimize program performance. A static block can be placed anywhere in a class, and there can be multiple static blocks in a class. When a class is loaded for the first time, each static block will be executed in the order of the static blocks, and will only be executed once.

Why the static block can be used to optimize program performance is because of its characteristics: it will only be executed once when the class is loaded. Therefore, many initialization operations that only need to be performed once are performed in static code blocks.

[Recommended learning: java video tutorial]

2. The uniqueness of static

1. Variables or methods modified by static are independent of any object of the class. That is to say, these variables and methods do not belong to any instance object, but are shared by instance objects of the class.

How do you understand the sentence "shared by instance objects of a class"? That is to say, a static member of a class belongs to everyone [Everyone refers to multiple object instances of this class. We all know that a class can create multiple instances! ], shared by all class objects, unlike member variables which are personal [self refers to a single instance object of this class]... I think I have made it very simple, do you understand?

2. When the class is loaded for the first time, the part modified by static will be loaded, and it will only be loaded and initialized when the class is used for the first time. Note that this is the first time the class is loaded. It must be initialized once used, and can be assigned again as needed later.

3. The static variable value is allocated space when the class is loaded and will not be reallocated when the class object is created later. If you assign a value, you can assign it arbitrarily!

4. Variables or methods modified by static take precedence over objects, which means that after a class is loaded, it can be accessed even if no object is created.

3. Static application scenario

Because static is shared by instance objects of the class, if a certain Member variables are shared by all objects, so this member variable should be defined as a static variable .

So the more common static application scenarios are:

1. Modifying member variables
2. Modifying member methods
3. Static code blocks
4. Modified classes [can only modify internal classes, that is, static internal classes]
5. Static guide package

The above application scenarios will be discussed one after another below...

4. The concept of static variables and instance variables

Static variables:
The member variables modified by static are called static variables [also called class variables], static Variables belong to this class, not to objects.

Instance variables:
Member variables that are not modified by static are called instance variables. Instance variables are instance objects belonging to this class.

Another thing to note is: static is not allowed to be used to modify local variables, don’t ask me what I am asking, because it is stipulated by java!

5. The difference between static variables and instance variables [Key points commonly used]

Static variables:

Static variables do not belong to Any instance object belongs to a class, so there will only be one copy in the memory. During the loading process of the class, the JVM only allocates memory space once for static variables.

Instance variables:

Every time an object is created, member variable memory space will be allocated to each object. Instance variables belong to the instance object. In the memory, the object is created several times. Several member variables.

I believe that all of you have higher IQs than Yichun, and you should all understand the above. The examples given below are purely for entertainment. You don’t need to read them if you understand them. The examples below are for reference only and are just for entertainment and atmosphere. If you are in a hurry, you can skip them!

怎么理解呢?打个比喻吧...就比方说程序员小王是一个比较温柔阳光的男孩子,这1024的这一天,老板闲的没事,非要拉着程序员小王来玩耍,怎么个玩法呢?老板和小王一人拿着一把菜刀,规则很简单,互相伤害,一人一刀,你一刀,我一刀....游戏一开始,老板二话不说,跳起来就是一刀,程序员小王二话也没说反手就是一菜刀回去,这个时候老板发飙了,双眼瞪得忒大,跳起来又是一刀,这个时候程序员小王不敢还手了,就没动手。没想到老板越来越生猛,左一刀右一刀全程下来差不多砍个半个时....程序员小王一直没有还过手,因为小王知道他是老板...

这个程序员小王只会在老板第一次挥刀的时候,回老板一刀,之后就不还手了,这个时候我们把程序员小王看做是静态变量,把老板第一次向小王挥刀看做是类加载,把小王回老板一刀看出是分配内存空间,而一人一刀这个回合过程看成是类加载的过程,之后老板的每一刀都看成是创建一次对象。

连贯起来就是static变量值在类第一次加载的时候分配空间,以后创建类对象的时候不会重新分配

之后这个老板挨了一刀之后躺医院了一年,一出院回到公司第一件事就是拉程序员宜春出来玩耍,老板殊不知其然,这个博主程序员宜春性格异常暴躁,老板递给程序员宜春一把菜刀,博主宜春一接过菜刀,猝不及防的被老板跳起来就是一刀,程序员宜春痛的嗷了一声,暴躁的程序员宜春还没嗷完,在嗷的同时跳起来就是给老板一刀,接着老板跳起来又是一刀,程序员宜春嗷的一声又是回一刀,老板跳起来又一刀,程序员宜春嗷的一声又是回一刀,只要老板没停程序员宜春就没停,因为程序员宜春知道,就自己这曝脾气,暴躁起来si都敢摸,肯定有几个老铁知道....

程序员宜春就类似实例变量,每次创建对象,都会为每个对象分配成员变量内存空间,就像老板来一刀,程序员宜春都会回一刀这样子的...

6、访问静态变量和实例变量的两种方式

我们都知道静态变量是属于这个类,而不是属于是对象,static独立于对象。

但是各位有木有想过:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问【只要访问权限足够允许就行】,不理解没关系,来个代码就理解了

public class StaticDemo {

        static int value = 666;

        public static void main(String[] args) throws Exception{
            new StaticDemo().method();
        }

        private void method(){
            int value = 123;
            System.out.println(this.value);
        }

}

猜想一下结果,我猜你的结果是123,哈哈是咩?其实

运行结果: 666

回过头再去品味一下上面的那段话,你就能非常客观明了了,这个思想概念要有只是这种用法不推荐!

因此小结一下访问静态变量和实例变量的两种方法:

静态变量:

类名.静态变量

对象.静态变量(不推荐)

静态方法:

类名.静态方法

对象.静态方法(不推荐)

7、static静态方法

static修饰的方法也叫做静态方法,不知道各位发现咩有,其实我们最熟悉的static静态方法就是main方法了~小白童鞋:喔好像真的是哦~。由于对于静态方法来说是不属于任何实例对象的,this指的是当前对象,因为static静态方法不属于任何对象,所以就谈不上this了。

还有一点就是:构造方法不是静态方法

8、static静态代码块

先看个程序吧,看看自个是否掌握了static代码块,下面程序代码继承关系为 BaseThree——> BaseTwo——> BaseOne

BaseOne类

package com.gx.initializationblock;

public class BaseOne {

    public BaseOne() {
        System.out.println("BaseOne构造器");
    }

    {
        System.out.println("BaseOne初始化块");
        System.out.println();
    }

    static {
        System.out.println("BaseOne静态初始化块");

    }

}

BaseTwo类

package com.gx.initializationblock;

public class BaseTwo extends BaseOne {
    public BaseTwo() {
        System.out.println("BaseTwo构造器");
    }

    {
        System.out.println("BaseTwo初始化块");
    }

    static {
        System.out.println("BaseTwo静态初始化块");
    }
}

BaseThree 类

package com.gx.initializationblock;

public class BaseThree extends BaseTwo {
    public BaseThree() {
        System.out.println("BaseThree构造器");
    }

    {
        System.out.println("BaseThree初始化块");
    }

    static {
        System.out.println("BaseThree静态初始化块");
    }
}

测试demo2类

package com.gx.initializationblock;

/*
     注:这里的ABC对应BaseOne、BaseTwo、BaseThree 
 * 多个类的继承中初始化块、静态初始化块、构造器的执行顺序
     在继承中,先后执行父类A的静态块,父类B的静态块,最后子类的静态块,
     然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器
 */
public class Demo2 {
    public static void main(String[] args) {
        BaseThree baseThree = new BaseThree();
        System.out.println("-----");
        BaseThree baseThree2 = new BaseThree();

    }
}

运行结果

BaseOne静态初始化块
BaseTwo静态初始化块
BaseThree静态初始化块
BaseOne初始化块

BaseOne构造器
BaseTwo初始化块
BaseTwo构造器
BaseThree初始化块
BaseThree构造器
-----
BaseOne初始化块

BaseOne构造器
BaseTwo初始化块
BaseTwo构造器
BaseThree初始化块
BaseThree构造器

至于static代码块运行结果不是很清晰的童鞋,详细讲解请看这篇Static静态代码块以及各代码块之间的执行顺序

以上仅仅是让各位明确代码块之间的运行顺序,显然还是不够的,静态代码块通常用来对静态变量进行一些初始化操作,比如定义枚举类,代码如下:

public enum WeekDayEnum {
    MONDAY(1,"周一"),
    TUESDAY(2, "周二"),
    WEDNESDAY(3, "周三"),
    THURSDAY(4, "周四"),
    FRIDAY(5, "周五"),
    SATURDAY(6, "周六"),
    SUNDAY(7, "周日");
 
    private int code;
    private String desc;
 
    WeekDayEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
 
    private static final Map<Integer, WeekDayEnum> WEEK_ENUM_MAP = new HashMap<Integer, WeekDayEnum>();
 
    // 对map进行初始化
    static {
        for (WeekDayEnum weekDay : WeekDayEnum.values()) {
            WEEK_ENUM_MAP.put(weekDay.getCode(), weekDay);
        }
    }
 
    public static WeekDayEnum findByCode(int code) {
        return WEEK_ENUM_MAP.get(code);
    }
 
    public int getCode() {
        return code;
    }
 
    public void setCode(int code) {
        this.code = code;
    }
 
    public String getDesc() {
        return desc;
    }
 
    public void setDesc(String desc) {
        this.desc = desc;
    }
} 

当然不仅仅是枚举这一方面,还有我们熟悉的单例模式同样也用到了静态代码块,如下:

public class Singleton {
    private static Singleton instance;
 
    static {
        instance = new Singleton();
    }
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return instance;
    }
}

9、static变量与普通变量区别

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。

10、静态内部类

静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

1、它的创建是不需要依赖外围类的创建。
2、它不能使用任何外围类的非static成员变量和方法。

代码举例(静态内部类实现单例模式)

public class Singleton {
    
   // 声明为 private 避免调用默认构造方法创建对象
    private Singleton() {
    }
    
   // 声明为 private 表明静态内部该类只能在该 Singleton 类中被访问
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance()方法从而触发 SingletonHolder.INSTANCESingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

11、静态导包

静态导包格式:import static

这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法

//  Math. --- 将Math中的所有静态资源导入,这时候可以直接使用里面的静态方法,而不用通过类名进行调用
//  如果只想导入单一某个静态方法,只需要将换成对应的方法名即可
 
import static java.lang.Math.;
//  换成import static java.lang.Math.max;具有一样的效果
 
public class Demo {
    public static void main(String[] args) {
 
        int max = max(1,2);
        System.out.println(max);
    }
}

静态导包在书写代码的时候确实能省一点代码,可以直接调用里面的静态成员,但是会影响代码可读性,所以开发中一般情况下不建议这么使用。

12、static注意事项

1、静态只能访问静态。
2、非静态既可以访问非静态的,也可以访问静态的。

13、final与static的藕断丝连

到这里文章本该结束了的,但是static的使用始终离不开final字眼,二者可谓藕断丝连,常常繁见,我觉得还是很有必要讲讲,那么一起来看看下面这个程序吧。

package Demo;

class FinalDemo {
    public final double i = Math.random();
    public static double t = Math.random();
}

public class DemoDemo {
    public static void main(String[] args) {

        FinalDemo demo1 = new FinalDemo();
        FinalDemo demo2 = new FinalDemo();
        System.out.println("final修饰的  i=" + demo1.i);
        System.out.println("static修饰的 t=" + demo1.t);
        System.out.println("final修饰的  i=" + demo2.i);
        System.out.println("static修饰的 t=" + demo2.t);

        System.out.println("t+1= "+ ++demo2.t );
//      System.out.println( ++demo2.i );//编译失败
      }
}
运行结果:
    final修饰的  i=0.7282093281367935
    static修饰的 t=0.30720545678577604
    final修饰的  i=0.8106990945706758
    static修饰的 t=0.30720545678577604
    t+1= 1.307205456785776

static修饰的变量没有发生变化是因为static作用于成员变量只是用来表示保存一份副本,其不会发生变化。怎么理解这个副本呢?其实static修饰的在类加载的时候就加载完成了(初始化),而且只会加载一次也就是说初始化一次,所以不会发生变化!

至于final修饰的反而发生变化了?是不是巅覆你对final的看法?关于final详细讲解博主也准备好了一篇文章程序员你真的理解final关键字吗?

ok,文章就先到这里了,希望这篇文章能够帮助到你对static的认识,若有不足或者不正之处,希望谅解并欢迎批评指正!

本文来自 java入门 栏目,欢迎学习!

The above is the detailed content of Deep understanding of the static keyword. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn