>  기사  >  Java  >  정적 키워드에 대한 깊은 이해

정적 키워드에 대한 깊은 이해

青灯夜游
青灯夜游원래의
2019-11-27 16:15:293190검색

정적 키워드에 대한 깊은 이해

정적에 대해 이야기하기 전에 흥미로운 코드를 살펴보시기 바랍니다.

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

프로그램을 읽은 후 Xiaobai Tongxiao는 다음과 같이 말했습니다. 이것은 무엇입니까? main 메소드에는 아무것도 없는데, 무엇을 실행할 수 있나요? 블로거 당신은 스타입니다...

运行结果:
test static 1
test static 2

리틀화이트 아동화: 그럼 뭐...뭐라구요... 블로거님 저는 아무말도 안했는데...

사실 위 내용은 이해가 되네요 code 물론 이해하실 수도 있고, 이해하지 못하는 사람도 당연히 이해하지 못할 것입니다. 위의 코드에는 JVM 클래스 로딩이 포함되어 있기 때문입니다! 물론, 이는 이 블로그 기사의 범위에 포함되지 않습니다. 위 프로그램을 이해하는 데 관심이 있는 경우 이 기사가 도움이 될 수 있습니다. 1. 정적 존재의 주요 의미는 독립적인 도메인 변수 또는 메소드를 작성하는 것입니다. 특정 개체의.

객체를 생성하지 않고도 속성을 사용하고 메서드를 호출할 수 있습니다! 정적 키워드의 또 다른 핵심 역할은

프로그램 성능을 최적화하기 위해 정적 코드 블록을 형성하는 데 사용됩니다. 정적 블록은 클래스의 어느 위치에나 배치될 수 있으며, 클래스에는 여러 정적 블록이 있을 수 있습니다. 클래스가 처음 로드되면 각 정적 블록은 정적 블록의 순서대로 실행되며 한 번만 실행됩니다. 정적 블록을 사용하여 프로그램 성능을 최적화할 수 있는 이유는 그 특성 때문입니다. 클래스가 로드될 때 한 번만 실행됩니다. 따라서 한 번만 수행하면 되는 많은 초기화 작업이 정적 코드 블록에서 수행됩니다.

[추천 학습: java 비디오 튜토리얼]

2. static

1의 고유한 기능은 static에 의해 수정된 변수나 메소드는 클래스의 모든 객체와 독립적입니다. 변수와 메소드

는 어떤 인스턴스 객체에도 속하지 않지만 클래스의 인스턴스 객체에 의해 공유됩니다.

"클래스의 인스턴스 객체가 공유함"이라는 문장을 어떻게 이해하시나요? 즉, 클래스의 정적 멤버는 모든 사람에게 속합니다. [모든 사람은 이 클래스의 여러 개체 인스턴스를 참조합니다. 우리 모두는 클래스가 여러 인스턴스를 만들 수 있다는 것을 알고 있습니다. ], 개인용 멤버 변수와는 달리 모든 클래스 객체가 공유합니다 [self는 이 클래스의 단일 인스턴스 객체를 나타냅니다]... 아주 간단하게 만든 것 같습니다. 이해하셨나요?

2. 클래스가 처음 로드되면 static으로 수정된 부분이 로드되며, 클래스가 처음 사용될 때만 로드되고 초기화됩니다. 사용되며 초기화해야 하며 나중에 필요에 따라 다시 할당할 수 있습니다. 3. 정적 변수 값은 클래스가 로드될 때 공간이 할당되며 나중에 클래스 개체가 생성될 때 다시 할당되지 않습니다. 값을 지정하면 임의로 지정할 수 있습니다!

4. 정적에 의해 수정된 변수나 메서드는 객체보다 우선합니다. 즉, 클래스가 로드된 후에는 객체가 생성되지 않아도 액세스할 수 있습니다.

3. 정적 응용 시나리오

정적은 클래스의 인스턴스 개체에서 공유되므로

모든 개체에서 멤버 변수를 공유하는 경우 이 멤버 변수를 정적 변수로 정의해야 합니다. 더 일반적인 정적 애플리케이션 시나리오는 다음과 같습니다.

1. 멤버 변수 수정2. ​​멤버 메서드 수정3. 정적 코드 블록4. 내부 클래스만 수정할 수 있습니다. ]

5 , 정적 가져오기 패키지

위의 응용 시나리오는 다음에서 논의됩니다...




4 정적 변수와 인스턴스 변수의 개념

정적 변수:

정적으로 수정된 멤버 변수는 다음과 같습니다. 정적 변수(클래스 변수라고도 함)라고 하는 정적 변수는 개체가 아닌 이 클래스에 속합니다.

인스턴스 변수: 정적으로 수정되지 않는 멤버 변수를 인스턴스 변수라고 합니다. 이 클래스에 속하는 인스턴스 개체입니다. 또 다른 주의할 점은:

static은 지역 변수를 수정하는 데 사용할 수 없습니다

, 이유는 묻지 마세요. Java에 규정되어 있기 때문입니다!


5. 정적 변수와 인스턴스 변수의 차이점 [자주 사용하는 요점] 정적 변수:

정적 변수는 어떤 인스턴스 객체에도 속하지 않고 클래스에 속하므로 하나만 존재하게 됩니다. 로딩 프로세스 중에 JVM은 정적 변수에 대한 메모리 공간을 한 번만 할당합니다. 인스턴스 변수: 객체가 생성될 때마다 각 객체마다 멤버 변수 메모리 공간이 할당됩니다. 인스턴스 변수는 인스턴스 객체에 속하며, 메모리에는 객체가 여러 번 생성될 때 여러 개의 멤버 변수가 있게 됩니다. .

저는 모든 사람의 IQ가 이춘의 IQ보다 높다고 믿으며, 위의 말을 모두 이해하셔야 합니다. 아래의 예는 순전히 재미를 위한 것입니다. 아래 예는 단지 참고용이며 급한 경우에는 건너뛰셔도 됩니다.

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

위 내용은 정적 키워드에 대한 깊은 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.