静的について話し始める前に、興味深いコードを見ていただきたいと思います。
public class Test { static{ System.out.println("test static 1"); } static{ System.out.println("test static 2"); } public static void main(String[] args) { } }
プログラムを読んだ後、Xiaobi Tongxiao はこう言いました。「これは何ですか?」 main メソッドには何もないので、何が実行できるのでしょうか?ブロガー、あなたはスターです...
运行结果: test static 1 test static 2
小さな白い子供用の靴: それは...それは...何か言った、ブロガー、私は何も言っていません...
実際、上記のコードは JVM クラスのロードを伴うため、理解できる人には自然に理解できますが、理解できない人には理解できないのが自然です。もちろん、これはこのブログ記事の範囲内ではありません。上記のプログラムを理解することに興味がある場合は、この記事が役に立つかもしれません。
1. static の主な意味存在
静的の主な意味は、特定のオブジェクトから独立したドメイン変数またはメソッドを作成することです。 オブジェクトが作成されていなくても、プロパティを使用してメソッドを呼び出すことができます!
静的キーワードは、プログラムのパフォーマンスを最適化するための静的コード ブロックの形成に使用される においても重要な役割を果たします。静的ブロックはクラス内のどこにでも配置でき、クラス内に複数の静的ブロックが存在する可能性があります。クラスが初めてロードされるとき、各静的ブロックは静的ブロックの順序で 1 回だけ実行されます。 静的ブロックを使用してプログラムのパフォーマンスを最適化できる理由は、クラスのロード時に 1 回だけ実行されるというその特性によるものです。したがって、一度だけ実行する必要がある多くの初期化操作は、静的コード ブロックで実行されます。
#[推奨学習:Java ビデオ チュートリアル
]2. static の一意性1. static によって変更された変数またはメソッドは、クラスのどのオブジェクトからも独立しています。つまり、これらの変数とメソッド
はどのインスタンス オブジェクトにも属さず、クラスのインスタンス オブジェクトによって共有されます# ####。「クラスのインスタンス オブジェクトによって共有される」という文をどのように理解しますか?つまり、クラスの静的メンバーは全員に属します [全員がこのクラスの複数のオブジェクト インスタンスを参照します。クラスが複数のインスタンスを作成できることは誰もが知っています。 ]、個人的なメンバー変数とは異なり、すべてのクラス オブジェクトによって共有されます [self は、このクラスの単一のインスタンス オブジェクトを参照します]...非常に単純に説明したと思いますが、理解できますか?
2. クラスの初回ロード時には static で変更された部分がロードされ、クラスの初回使用時にのみロードおよび初期化されます。クラスがロードされるのはこれが初めてです。クラスは使用後に初期化する必要があり、後で必要に応じて再度割り当てることができます。4. static によって変更された変数またはメソッドはオブジェクトよりも優先されます。つまり、クラスがロードされた後は、オブジェクトが作成されていなくてもアクセスできます。 3. 静的アプリケーションのシナリオ3. 静的変数値は、クラスがロードされるときに領域が割り当てられ、後でクラス オブジェクトが作成されるときに再割り当てされません。値を割り当てる場合は任意に割り当てることができます。
静的はクラスのインスタンス オブジェクトによって共有されるため、 特定のメンバーの場合変数はすべてのオブジェクトで共有されるため、このメンバー変数は静的変数
として定義する必要があります。したがって、より一般的な静的アプリケーションのシナリオは次のとおりです: 1. メンバー変数の変更
2. メンバー メソッドの変更3. 静的コード ブロック
4.変更されたクラス [内部クラス、つまり静的内部クラスのみを変更できます]5. 静的ガイド パッケージ4. 静的変数とインスタンス変数の概念
上記のアプリケーション シナリオについては、以下で順番に説明します...
静的変数: 静的に変更されたメンバー変数は静的変数と呼ばれます [静的変数とも呼ばれます]クラス変数]、静的変数はオブジェクトではなく、このクラスに属します。 インスタンス変数:
static によって変更されないメンバー変数をインスタンス変数と呼びます。インスタンス変数は、このクラスに属するインスタンス オブジェクトです。
もう 1 つ注意すべき点は、
static をローカル変数の変更に使用することは許可されていません。
5. 静的変数とインスタンス変数の違い [よく使われるポイント]
静的変数: 静的変数はクラスに属していない インスタンス オブジェクトはクラスに属しているため、メモリにはコピーが 1 つだけ存在します クラスのロード プロセス中に、JVM は静的変数にメモリ領域を 1 回だけ割り当てます。
インスタンス変数: オブジェクトが作成されるたびに、各オブジェクトにメンバ変数のメモリ領域が割り当てられます。インスタンス変数は、インスタンス オブジェクトに属します。メモリ上にオブジェクトは複数作成されます。複数のメンバー変数。皆さんはイーチュンよりも高い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.INSTANCE
时 SingletonHolder
才会被加载,此时初始化 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 中国語 Web サイトの他の関連記事を参照してください。