Maison  >  Article  >  Java  >  Compréhension approfondie du mot-clé statique

Compréhension approfondie du mot-clé statique

青灯夜游
青灯夜游original
2019-11-27 16:15:293215parcourir

Compréhension approfondie du mot-clé statique

Avant de commencer à parler de statique, je veux que vous voyiez un morceau de code intéressant :

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

Après avoir lu le programme, Xiaobai Tongxiao a dit : Qu'est-ce que c'est ? Il n’y a rien dans la méthode main, alors que peut-elle exécuter ? Blogueuse, tu es une star...

运行结果:
test static 1
test static 2

Petites chaussures blanches pour enfants : Et alors... Alors quoi... Blogueuse, j'ai dit quelque chose, je n'ai rien dit...

En fait, ceux qui comprennent le code ci-dessus le comprendront naturellement, et ceux qui ne le comprennent pas ne le comprendront naturellement pas, car le code ci-dessus implique le chargement de classes JVM ! Bien sûr, cela n'entre pas dans le cadre de cet article de blog. Si vous souhaitez comprendre le programme ci-dessus, cet article peut vous être utile

1 La principale signification de l'existence statique.

La signification principale de static est de créer des variables de domaine ou des méthodes indépendantes d'objets spécifiques. Pour que vous puissiez utiliser des propriétés et appeler des méthodes même sans créer d'objet !

Le mot-clé static joue également un rôle clé dans étant utilisé pour former des blocs de code statiques afin d'optimiser les performances du programme . Un bloc statique peut être placé n’importe où dans une classe et il peut y avoir plusieurs blocs statiques dans une classe. Lorsqu'une classe est chargée pour la première fois, chaque bloc statique sera exécuté dans l'ordre des blocs statiques, et ne sera exécuté qu'une seule fois.

La raison pour laquelle le bloc statique peut être utilisé pour optimiser les performances du programme est due à ses caractéristiques : il ne sera exécuté qu'une seule fois lorsque la classe est chargée. Par conséquent, de nombreuses opérations d’initialisation qui ne doivent être effectuées qu’une seule fois sont effectuées dans des blocs de code statiques.

[Apprentissage recommandé : Tutoriel vidéo Java]

2.Le caractère unique de la statique

1. Les variables ou méthodes modifiées par static sont indépendantes de tout objet de la classe C'est-à-dire que ces variables et méthodes n'appartiennent à aucun objet instance, mais sont partagées par les objets instances de la classe<.>.

Comment comprenez-vous la phrase « partagé par les objets d'instance d'une classe » ? C'est-à-dire qu'un membre statique d'une classe appartient à tout le monde [Tout le monde fait référence à plusieurs instances d'objet de cette classe. Nous savons tous qu'une classe peut créer plusieurs instances ! ], partagé par tous les objets de classe, contrairement aux variables membres qui sont personnelles [self fait référence à une seule instance d'objet de cette classe]... Je pense avoir rendu les choses très simples, vous comprenez ?

2. Lorsque la classe est chargée pour la première fois, la partie modifiée par static sera chargée, et elle ne sera chargée et initialisée que lorsque la classe sera utilisée pour la première fois. c'est la première fois que la classe est chargée. Elle doit être initialisée une fois utilisée et peut être réaffectée si nécessaire ultérieurement.

3. La valeur de la variable statique se voit attribuer de l'espace lorsque la classe est chargée et ne sera pas réaffectée lors de la création future de l'objet de classe. Si vous attribuez une valeur, vous pouvez l'attribuer arbitrairement !

4. Les variables ou méthodes modifiées par static ont priorité sur les objets, ce qui signifie qu'une fois chargée, une classe est accessible même si aucun objet n'est créé.

3. Scénarios d'application statiques

Parce que la statique est partagée par les objets d'instance d'une classe, si

une variable membre sont partagés par tous les objets, cette variable membre doit donc être définie comme une variable statique .

Les scénarios d'application statiques les plus courants sont donc :

1. Modification des variables membres

2. Modification des méthodes membres
3. Classes modifiées [ne peuvent modifier que les classes internes, c'est-à-dire les classes internes statiques]
5. Package d'importation statique

Les scénarios d'application ci-dessus seront discutés les uns après les autres ci-dessous...

4. Les notions de variables statiques et de variables d'instanceVariables statiques :

Les variables membres modifiées par static sont appelées variables statiques [également appelées variables de classe], les variables statiques appartiennent à cette classe, pas aux objets.


Variables d'instance :

Les variables membres qui ne sont pas modifiées par static sont appelées variables d'instance. Les variables d'instance sont des objets d'instance appartenant à cette classe.


Une autre chose à noter est :

static n'est pas autorisé à modifier les variables locales , ne me demandez pas ce que je demande, car c'est stipulé par Java !

5. La différence entre les variables statiques et les variables d'instance [Points clés couramment utilisés]Variables statiques :

Statiques les variables n'appartiennent pas à Tout objet d'instance appartient à une classe, il n'y aura donc qu'une seule copie dans la mémoire. Pendant le processus de chargement de la classe, la JVM n'alloue qu'une seule fois de l'espace mémoire pour les variables statiques.

Variables d'instance :

Chaque fois qu'un objet est créé, un espace mémoire de variable membre sera alloué pour chaque objet. Les variables d'instance appartiennent à l'objet d'instance. Dans la mémoire, l'objet est créé plusieurs fois. fois. Plusieurs variables membres.

Je crois que tout le monde a un QI plus élevé que Yichun, vous devriez donc tous comprendre ce qui précède. Les exemples donnés ci-dessous sont uniquement destinés au divertissement. Vous n'avez pas besoin de les lire si vous les comprenez. Les exemples ci-dessous sont uniquement à titre de référence et sont uniquement destinés au divertissement et à l'ambiance. Si vous êtes pressé, vous pouvez les ignorer !

怎么理解呢?打个比喻吧...就比方说程序员小王是一个比较温柔阳光的男孩子,这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入门 栏目,欢迎学习!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn