-
2018-04-05 18:18:14
概述
static关键字在c语言中比较常用,使用恰当能够大大提高程序的模块化特性,有利于扩展和维护。
但是对于c语言初学者,static由于使用灵活,并不容易掌握。本文就static在c语言中的应用进行总结,供参考使用。错漏之处,请不吝指正。
最后一节加入了c++面向对象中static的使用特性,当作拓展阅读。
在程序中使用static
变量
1. 局部变量
普通局部变量是再熟悉不过的变量了,在任何一个函数内部定义的变量(不加static修饰符)都属于这个范畴。编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值。
普通局部变量存储于进程栈空间,使用完毕会立即释放。
静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。
变量在全局数据区分配内存空间;编译器自动对其初始化
其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束小程序体会一下静态局部变量的威力:
#include <stdio.h> void fn(void) { int n = 10; printf("n=%d\n", n); n++; printf("n++=%d\n", n); } void fn_static(void) { static int n = 10; printf("static n=%d\n", n); n++; printf("n++=%d\n", n); } int main(void) { fn(); printf("--------------------\n"); fn_static(); printf("--------------------\n"); fn(); printf("--------------------\n"); fn_static(); return 0; }
运行结果如下:
-> % ./a.out n=10 n++=11 -------------------- static n=10 n++=11 -------------------- n=10 n++=11 -------------------- static n=11 n++=12
可见,静态局部变量的效果跟全局变量有一拼,但是位于函数体内部,就极有利于程序的模块化了。
2. 全局变量
全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。
普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。
函数
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
- 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
- 不同的文件可以使用相同名字的静态函数,互不影响
非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明
下面两个文件的例子说明使用static声明的函数不能被另一个文件引用:
/* file1.c */ #include <stdio.h> static void fun(void) { printf("hello from fun.\n"); } int main(void) { fun(); fun1(); return 0; } /* file2.c */ #include <stdio.h> static void fun1(void) { printf("hello from static fun1.\n"); }
使用
gcc file1.c file2.c
编译时,错误报告如下:/tmp/cc2VMzGR.o:在函数‘main’中: static_fun.c:(.text+0x20):对‘fun1’未定义的引用 collect2: error: ld returned 1 exit status
修改文件,不使用static修饰符,可在另一文件中引用该函数:
/* file1.c */ #include <stdio.h> void fun(void) { printf("hello from fun.\n"); } /* file2.c */ int main(void) { fun(); return 0; }
同样使用
gcc file1.c file2.c
编译,编译通过,运行结果如下:-> % ./a.out hello from fun.
面向对象
静态数据成员
在类内数据成员的声明前加上static关键字,该数据成员就是类内的静态数据成员。其特点如下:
- 静态数据成员存储在全局数据区,静态数据成员在定义时分配存储空间,所以不能在类声明中定义
- 静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。
- 由于上面的原因,静态数据成员不属于任何对象,在没有类的实例时其作用域就可见,在没有任何对象时,就可以进行操作
- 和普通数据成员一样,静态数据成员也遵从
public, protected, private
访问规则 - 静态数据成员的初始化格式:
<数据类型><类名>::<静态数据成员名>=<值>
- 类的静态数据成员有两种访问方式:
<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>
同全局变量相比,使用静态数据成员有两个优势:
- 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性
- 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能
静态成员函数
与静态数据成员类似,静态成员函数属于整个类,而不是某一个对象,其特性如下:
- 静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数
- 出现在类体外的函数定义不能指定关键字static
- 非静态成员函数可以任意地访问静态成员函数和静态数据成员
总结
static是一个很有用的关键字,使用得当可以使程序锦上添花。当然,有的公司编码规范明确规定只用于本文件的函数要全部使用static关键字声明,这是一个良好的编码风格。
无论如何,要在实际编码时注意自己的编码习惯,尽量体现出语言本身的优雅和编码者的编码素质。
更多相关内容 -
Java static关键字详解
2018-08-07 17:30:21static关键字 在类中,用static声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。 这里要强调一下: static修饰的成员变量和方法,从属于类 普通变量和...static关键字
在类中,用static声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
这里要强调一下:
static修饰的成员变量和方法,从属于类
普通变量和方法从属于对象
静态方法不能调用非静态成员,编译会报错
static关键字的用途
一句话描述就是:方便在没有创建对象的情况下进行调用(方法/变量)。
显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
static可以用来修饰类的成员方法、类的成员变量,另外也可以编写static代码块来优化程序性能
static方法
static方法也成为静态方法,由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有this的,因为不依附于任何对象,既然都没有对象,就谈不上this了,并且由于此特性,在静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用。
虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。
代码示例:
从上面代码里看出:
静态方法test2()中调用非静态成员变量address,编译失败。这是因为,在编译期并没有对象生成,address变量根本就不存在。
静态方法test2()中调用非静态方法test1(),编译失败。这是因为,编译器无法预知在非静态成员方法test1()中是否访问了非静态成员变量,所以也禁止在静态方法中调用非静态成员方法
非静态成员方法test1()访问静态成员方法test2()/变量name是没有限制的
所以,如果想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。最常见的静态方法就是main方法,这就是为什么main方法是静态方法就一目了然了,因为程序在执行main方法的时候没有创建任何对象,只有通过类名来访问。
特别说明:static方法是属于类的,非实例对象,在JVM加载类时,就已经存在内存中,不会被虚拟机GC回收掉,这样内存负荷会很大,但是非static方法会在运行完毕后被虚拟机GC掉,减轻内存压力
static变量
static变量也称为静态变量,静态变量和非静态变量的区别:
静态变量被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化
非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
static成员变量初始化顺序按照定义的顺序来进行初始化
static块
构造方法用于对象的初始化。静态初始化块,用于类的初始化操作。
在静态初始化块中不能直接访问非staic成员。
static块的作用
静态初始化块的作用就是:提升程序性能。
为什么说静态初始化块能提升程序性能,代码示例如下:
class Person{ private Date birthDate; public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { Date startDate = Date.valueOf("1946"); Date endDate = Date.valueOf("1964"); return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
isBornBoomer是用来这个人是否是1946-1964年出生的,而每次isBornBoomer被调用的时候,都会生成startDate和birthDate两个对象,造成了空间浪费,如果改成这样效率会更好:
class Person{ private Date birthDate; private static Date startDate,endDate; static{ startDate = Date.valueOf("1946"); endDate = Date.valueOf("1964"); } public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行
静态初始化块可以置于类中的任何地方,类中可以有多个静态初始化块。
在类初次被加载时,会按照静态初始化块的顺序来执行每个块,并且只会执行一次。static关键字的误区
static关键字会改变类中成员的访问权限吗?
有些初学的朋友会将java中的static与C/C++中的static关键字的功能混淆了。在这里只需要记住一点:与C/C++中的static不同,Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。看下面的例子就明白了:
public class Person { public String name = "李四"; private static String address = "中国"; }
说明static关键字不能改变变量和方法的访问权限
能通过this访问静态成员变量吗?
public class Main { static int value = 33; public static void main(String[] args) throws Exception{ new Main().printValue(); } private void printValue(){ int value = 3; System.out.println(this.value); } }
这段代码输出结果为:33
this代表什么?this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
static能作用于局部变量么?
static是不允许用来修饰局部变量。不要问为什么,这是Java语法的规定
static常见笔试面试题
1、下面这段代码的输出结果是什么?
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"); } }
输出结果为:
base static test static base constructor test constructor
分析下这段代码的执行过程:
找到main方法入口,main方法是程序入口,但在执行main方法之前,要先加载Test类
加载Test类的时候,发现Test类继承Base类,于是先去加载Base类
加载Base类的时候,发现Base类有static块,而是先执行static块,输出base static结果
Base类加载完成后,再去加载Test类,发现Test类也有static块,而是执行Test类中的static块,输出test static结果
Base类和Test类加载完成后,然后执行main方法中的
new Test()
,调用子类构造器之前会先调用父类构造器调用父类构造器,输出base constructor结果
然后再调用子类构造器,输出test constructor结果
2、这段代码的输出结果是什么?
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"); } }
输出结果为:
test static myclass static person static person Test test constructor person MyClass myclass constructor
为什么输出结果是这样的?我们来分析下这段代码的执行过程:
找到main方法入口,main方法是程序入口,但在执行main方法之前,要先加载Test类
加载Test类的时候,发现Test类有static块,而是先执行static块,输出test static结果
然后执行
new MyClass()
,执行此代码之前,先加载MyClass类,发现MyClass类继承Test类,而是要先加载Test类,Test类之前已加载加载MyClass类,发现MyClass类有static块,而是先执行static块,输出myclass static结果
然后调用MyClass类的构造器生成对象,在生成对象前,需要先初始化父类Test的成员变量,而是执行
Person person = new Person("Test")
代码,发现Person类没有加载加载Person类,发现Person类有static块,而是先执行static块,输出person static结果
接着执行Person构造器,输出person Test结果
然后调用父类Test构造器,输出test constructor结果,这样就完成了父类Test的初始化了
再初始化MyClass类成员变量,执行Person构造器,输出person MyClass结果
最后调用MyClass类构造器,输出myclass constructor结果,这样就完成了MyClass类的初始化了
3、这段代码的输出结果是什么?
public class Test { static{ System.out.println("test static 1"); } public static void main(String[] args) { } static{ System.out.println("test static 2"); } }
输出结果为:
test static 1 test static 2
虽然在main方法中没有任何语句,但是还是会输出,原因上面已经讲述过了。另外,static块可以出现类中的任何地方(只要不是方法内部,记住,任何方法内部都不行),并且执行是按照static块的顺序执行的。
-
深入理解static关键字
2019-10-25 09:08:13文章目录1、static存在的主要意义2、static的独特之处3、静态变量和实例变量的概念4、静态变量和实例变量【重点常用】5、static静态方法6、static代码块7、static应用场景 1、static存在的主要意义 static的主要.....提到static关键字,相信大家都不陌生,这是相对比较难以理解的一个关键字,相信各位也都能深深感受的到!本篇文章将好好总结一下static这个关键字。
文章目录
在开始讲static之前,我想让各位看一段有意思的代码:
public class Test { static{ System.out.println("test static 1"); } static{ System.out.println("test static 2"); } public static void main(String[] args) { } }
看完程序,小白童鞋发话了:啥玩意?main方法中啥都没有,能运行啥?博主你个星星星…
运行结果: test static 1 test static 2
小白童鞋:那啥…那啥…博主我说啥了,我啥都没说…
其实,上面的代码懂的自然懂,不懂的自然就不懂了,因为上面的代码涉及到JVM的类加载了!当然不在本篇博客文章的范畴内,如果有兴趣理解上面的程序,这篇文章可能会对你有所帮助
别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
1、static存在的主要意义
static的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法!
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
2、static的独特之处
1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。
怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩?
2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。
3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!
4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。
3、static应用场景
因为static是被类的实例对象所共享,因此如果某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量。
因此比较常见的static应用场景有:
1、修饰成员变量
2、修饰成员方法
3、静态代码块
4、修饰类【只能修饰内部类也就是静态内部类】
5、静态导包以上的应用场景将会在下文陆续讲到…
4、静态变量和实例变量的概念
静态变量:
static修饰的成员变量叫做静态变量【也叫做类变量】,静态变量是属于这个类,而不是属于是对象。实例变量:
没有被static修饰的成员变量叫做实例变量,实例变量是属于这个类的实例对象。还有一点需要注意的是:static是不允许用来修饰局部变量,不要问我问什么,因为java规定的!
5、静态变量和实例变量区别【重点常用】
静态变量:
静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。实例变量:
每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。我相信各位智商都比宜春智商要高,应该都能理解上面的话。下面举了例子完全出于娱乐,理解了大可不必看,下面的例子仅供参考,仅供娱乐一下下气氛,赶时间的熊dei大可略过!
怎么理解呢?打个比喻吧…就比方说程序员小王是一个比较温柔阳光的男孩子,这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
当然肯定有一些基础非常扎实的大佬会问为什么会有同学认为输出是
123
。里面定义的value=123
只是一个很普通的局部变量而已 。和成员变量没有半毛钱关系 。和静态变量也没有关系。是的!确实如这位大佬说的一样!只是博主我说的同学是小白同学,站在小白童鞋的角度上,所以还望大佬理解见谅!我举的这个StaticDemo
例子主要目的是说明一下this
也是可以访问static
的变量的!!!value=123只是一个跑龙套的配角,旨在让小白同学认清其中的关系。回过头再去品味一下上面的那段话,你就能非常客观明了了,这个思想概念要有只是这种用法不推荐!
因此小结一下访问静态变量和实例变量的两种方法:
静态变量:
类名.静态变量
对象.静态变量(不推荐)
静态方法:
类名.静态方法
对象.静态方法(不推荐)
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编程思想》
http://baijiahao.baidu.com/s?id=1601254463089390982&wfr=spider&for=pc
https://blog.csdn.net/qq_34337272/article/details/82766943
https://www.cnblogs.com/dolphin0520/p/3799052.html最后,欢迎各位关注我的公众号,一起探讨技术,向往技术,追求技术
-
glibc-static-2.17-55.el7.x86_64
2016-03-21 11:51:41安装此资源,可以解决/lib64/libstdc++.so.5: undefined reference to `memcpy@GLIBC_2.14' 这个问题 -
C# static的用法详解
2020-12-14 21:50:19目录 一、静态类 1、静态类的主要特性: 2、静态类与私有构造函数区别: 二、静态成员 三、静态方法 ...static静态的,不变的,在某个类中只有一个,不会因实例化对象的不同而不同。...static可以修饰...目录
static静态的,不变的,在某个类中只有一个,不会因实例化对象的不同而不同。
static可以修饰类、字段、属性、方法等。
如一个方法前加上static修饰后要对其进行调用可以直接通过类名点出来,不用再对类进行实例化。
一、静态类
静态类与非静态类的重要区别在于静态类不能实例化,也就是说,不能使用 new 关键字创建静态类类型的变量。
在声明一个类时使用static关键字,具有两个方面的意义:
- 首先,它防止程序员写代码来实例化该静态类;
- 其次,它防止在类的内部声明任何实例字段或方法。
1、静态类的主要特性:
- 仅包含静态成员。
- 无法实例化。
- 静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化。
- 不能包含实例构造函数。
- 如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类。
2、静态类与私有构造函数区别:
- 私有构造器方式仍然可以从类的内部对类进行实例化,而静态类禁止从任何地方实例化类,其中包括从类自身内部。
- 使用私有构造器的类中,是允许有实例成员的,编译器不允许静态类有任何实例成员。
- 使用静态类的优点在于,编译器能够执行检查以确保不致偶然地添加实例成员,编译器将保证不会创建此 类的实例。
- C#编译器会自动把它标记为sealed。这个关键字将类指定为不可扩展;换言之,不能从它派生出其他类。
二、静态成员
- 通过static关键字修饰,是属于类,实例成员属于对象,在这个类第一次加载的时候,这个类下面的所有静态成员会被加载。
- 静态成员只被创建一次,所以静态成员只有一份,实例成员有多少个对象,就有多少份。
- 类加载的时候,所有的静态成员就会被创建在“静态存储区”里面,一旦创建直到程序退出,才会被回收。
- 成员需要被共享的时候,方法需要被反复调用的时候,就可以把这些成员定义为静态成员。
- 在静态方法中,不能直接调用实例成员,因为静态方法被调用的时候,对象还有可能不存在。
- this/base 关键字在静态方法中不能使用,因为有可能对象还不存在。
- 可以创建这个类的对象,制定对象的成员在静态方法中操作。
- 在实例方法中,可以调用静态成员,因为这个时候静态成员肯定存在。
- 非静态类可以包含静态的方法、字段、属性或事件;
- 无论对一个类创建多少个实例,它的静态成员都只有一个副本;
- 静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例成员;
- 静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;
- 虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。
三、静态方法
- 静态方法是不属于特定对象的方法;
- 静态方法可以访问静态成员;
- 静态方法不可以直接访问实例成员,可以在实例函数调用的情况下,实例成员做为参数传给静态方法;
- 静态方法也不能直接调用实例方法,可以间接调用,首先要创建一个类的实例,然后通过这一特定对象来调用静态方法。
四、静态构造函数
- 静态类可以有静态构造函数,静态构造函数不可继承;
- 静态构造函数可以用于静态类,也可用于非静态类;
- 静态构造函数无访问修饰符、无参数,只有一个 static 标志;
- 静态构造函数不可被直接调用,当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。
class Program { public static int i =0; public Program() { i = 1; Console.Write("实例构造方法被调用"); } static Program() { i = 2; Console.Write("静态构造函数被执行"); } static void Main(string[] args) { Console.Write(Program.i);//结果为2,首先,类被加载,所有的静态成员被创建在静态存储区,i=0,接着调用了类的成员,这时候静态构造函数就会被调用,i=2 Program p = new Program(); Console.Write(Program.i);//结果为1,实力化后,调用了实例构造函数,i=1,因为静态构造函数只执行一次,所以不会再执行。 } }
五、静态成员的存储
使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员
static修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。
5.1 静态全局变量
定义:
在全局变量前,加上关键字 static 该变量就被定义成为了一个静态全局变量。
特点:
- 该变量在全局数据区分配内存。
- 初始化:如果不显式初始化,那么将被隐式初始化为0。
5.2 静态局部变量
定义:
在局部变量前加上static关键字时,就定义了静态局部变量。
特点:
- 该变量在全局数据区分配内存。
- 初始化:如果不显式初始化,那么将被隐式初始化为0。
- 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或 语句块结束时,其作用域随之结束。
5.3 静态数据成员
内存分配:
在程序的全局数据区分配。
初始化和定义:
- 静态数据成员定义时要分配空间,所以不能在类声明中定义。
- 为了避免在多个使用该类的源文件中,对其重复定义,所在,不能在类的头文件中定义。
- 静态数据成员因为程序一开始运行就必需存在,所以其初始化的最佳位置在类的内部实现。
特点:
- 对相于 public,protected,private 关键字的影响它和普通数据成员一样
- 因为其空间在全局数据区分配,属于所有本类的对象共享,所以,它不属于特定的类对象,在没产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它。
访问形式:
类对象名.静态数据成员名
作用:
静态数据成员,主要用在类的所有实例都拥有的属性上。
比如,对于一个存款类,帐号相对于每个实例都是不同的,但每个实例的利息是相同的。所以,应该把利息设为存款类的静态数据成员。
这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局区的内存,所以节省存贮空间。
第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了,因为它们实际上是共用一个东西。
5.4 静态成员函数
特点:
- 静态成员函数与类相联系,不与类的对象相联系。
- 静态成员函数不能访问非静态数据成员。原因很简单,非静态数据成员属于特定的类实例。
作用:
主要用于对静态数据成员的操作。
调用形式:
类对象名.静态成员函数名()
class Program { static int i = getNum(); int j = getNum(); static int num = 1; static int getNum() { return num; } static void Main(string[] args) { Console.WriteLine("i={0}", i); Console.WriteLine("j={0}", new Program().j); Console.Read(); } }
分析上面的代码:
Console.WriteLine("i={0}", i);
这里 i 是 static 变量,在类 Program 第一次被加载时,要先为 Program 里面所有的 static 变量分配内存。尽管现在有超线程技术,但是指令在逻辑上还是逐条的按顺序自上而下执行,所以先为 static int i 分配内存,并且在该内存中保持int的缺省值0,接着再为 static int num 变量分配内存,值当然也为0。
然后第二步,为变量赋值:先为 static int i 变量赋值,i=getNum(),看 getNum() 里面的代码,就是return num,这个时候 num 的值是 0 ,于是 i=0 。然后对变量num赋值,num=1;这行代码执行后,num就为1了。所以,j=1。
所以最后的结果为:
i=0
j=1
注意:
当类第一次被加载时,会对类中的静态变量先按顺序进行分配内存空间,当全部分配完内存空间之后,在对静态变量按顺序赋值。
首先分为两部分 寄存器和内存(包括缓存)
内存分为两部分 代码和数据
数据分为两部分 静态存储区和运行时存储
运行时存储分为 堆栈 和 堆
静态存储分为 全局静态存储 和 常量
-
static详解
2020-11-19 22:58:10static是什么? static是C/C++语言中常见的一个关键字,就像int,break,while,for等一样,都属于关键字。 static怎么用? 在C语言中,static被用来修饰变量与函数。其用法概括为以下三个方面: 1.修饰局部变量 static... -
c++中的static详解
2020-06-01 10:47:42C语言中的static关键字有三种用途: 1.静态局部变量:用于函数体的内部修饰变量,这种变量的生存期长于该函数。 int f() { static int i = 1; // note:1 //int i = 1; // note:2 i+=1; return i; } 要明白这个用法... -
static
2018-11-30 09:48:14static类成员变量: 1 static类的成员变量是和类进行绑定的,不是和对象绑定,如果一个类的static成员变量变成了1,另外的成员变量的该值也变成了1,。 2 static成员变量先于对象存在,因为它不与对象绑定,必须在类... -
static, const, static const 与 const static
2018-09-30 23:03:241. static const/ const static, 意义一样 namespace NdpiTransformersTest { class TestMain { public: ~TestMain(); static int run(int argc, const char** argv); private: static ... -
C语言 基础知识之static(static是什么,static的好处,static的使用和作用)
2021-05-14 00:18:40一、static是什么? static是C/C++中的修饰符,可以用来修饰变量,也可以用来修饰函数。 二、static的好处是什么? 1、隐藏变量或函数、隔离错误,有利于模块化程序 在编程中,难免会用到全局变量,全局变量... -
java中static修饰成员变量
2022-03-10 14:48:39static: 静态的, 它是成员修饰符,可以修饰成员内容,不可以修饰局部 修饰成员变量--> 静态变量+类变量; 修饰方法 --> 静态方法+类方法 修饰块 修饰类 static修饰的成员变量与成员方法使用方式: 1... -
无法从 static 上下文引用非 static 方法
2022-03-27 13:22:581.用static修饰的方法称为静态方法,修饰变量则为静态变量,又分别叫做类方法或者类变量。 静态方法中不能直接调用非静态方法。因为非静态方法不是独立存在的,它是依附于对象存在——即只有申明了对象,才能通过... -
面试|static 关键字有什么作用
2019-06-16 12:43:01今天主要学习下Java语言中的static关键字。 static关键字的含义及使用场景 static是Java50个关键字之一。static关键字可以用来修饰代码块表示静态代码块,修饰成员变量表示全局静态成员变量,修饰方法表示静态方法... -
C语言中static用法详解
2022-04-19 19:06:08文章目录前言一、static修饰变量1.修饰局部变量2.修饰全局变量二、static修饰函数总结 前言 static关键字不仅可以用来修饰变量,还可以用来修饰函数。在使用static关键字修饰变量时,我们称此变量为静态变量。静态... -
static什么意思c语言
2021-05-19 03:36:21Q1:C语言编程中“static”是什么意思?static 出现在不同的地方含义不抄同的。如果是在函数之外使用,表示该对像在此文件中是全局可访问的知,在文件之外是不可访问的。如果出现在函数内部,则表示该变量不是自动... -
java中static的用法
2021-04-17 19:54:16java中static的用法 -
static和const的作用与区别
2022-01-22 10:31:30我们先来说static. static主要有三个作用: 1.修饰局部变量,成为静态局部变量 2.修饰全局变量,成为静态全局变量 3.修饰函数,成为静态函数 我们一个一个来解释. 1.修饰局部变量。成为静态局部变量 我们先来... -
python static变量
2021-01-13 05:02:52python在函数中有static变量吗python函数怎么实现static变量?python函数实现static变量具体如下:def ask_ok(prompt, retries=4, complaint='Yes or no, please。'): while True: ok = raw_input(prompt) if ok in ... -
java中static关键字的使用说明
2019-06-01 23:01:291、static关键字的用途 在《Java编程思想》P86页有这样一段话: “static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类... -
Rust 全局静态变量(match、lazy_static! 、phf)
2020-11-11 10:01:10文章目录一、全局变量1.1 如何使用 lazy_static! 创建全局可变变量的例子二、创建全局静态变量的三种方式2.1 match2.2 lazy_static!宏2.2.1 lazy_static!的作用接下去使用 lazy_static! 消除上面的所有问题。2.2.2... -
史上最全的静态网站生成器Static Site Generators大集合
2019-07-14 17:20:20在以下两篇文章中我们介绍了一些常见的静态网站(博客)生成器,后来发现了一个国外的别人家的网址,整理的更加全面,可以号称是史上最全的Static Site Generators大集合,集合了超过463个项目,这里转载过来介绍给... -
wxpython之StaticText最全介绍(持续更新)
2020-05-12 15:40:59本文是作者学习wxpython过程中的随笔,主要是StaticText的相关操作,本文会持续更新。 目录 一、各个部分代码介绍 1、StaticText的建立 2、StaticText文本字体大小、颜色、风格、斜体、字体背景颜色等操作 3... -
final、static和static final区别和使用
2020-07-22 10:26:52final、static和static final用法 1.final关键字 final关键字修饰变量: 1.这个变量必须在构造对象时就能够初始化 2.如果是基本数据类型的变量,那么这个变量一旦初始化就不能更改,相当于一个常量 3.如果是引用数据... -
Java基础知识——static和final
2022-04-02 15:09:53一、static 1. static修饰变量: static修饰的变量属于静态变量,随着类加载直接分配内存,与实例对象无关,所有类共用静态变量(需要考虑作用域,private、public之类); 2. static修饰函数: static修饰的函数... -
static关键字
2019-05-04 22:22:35static关键字的核心:修饰的成员不属于对象了,而属于类本身。 它可以修饰字段,方法,内部类。 特点: 1、static修饰的内容,附着类的加载而加载——当JVM把class字节码加载到虚拟机时,static修饰的成员已经自动的... -
Java中的static的使用指南
2021-03-09 18:03:19一、Java中的static使用之静态变量1.Java 中被static修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享、且优先于对象存在。静态成员可以使用类名直接访问,也可以... -
glibc-static安装
2020-02-08 19:06:40glibc-static安装 如需转载请标明出处: http://blog.csdn.net/itas109 QQ技术交流群: 129518033 文章目录 glibc-static安装 @[toc] 前言 1.直接安装 2.源码编译 2.1 查看glibc版本 2.2 下载glibc源码 2.3 ... -
C语言面试题 - static的使用
2021-08-22 15:38:251)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。 2)全局静态变量... -
java中的Static、final、Static final各种用法
2019-10-22 21:38:13对Static、final、Static final这几个关键词熟悉又陌生?想说却又不知怎么准确说出口?好的,本篇博客文章将简短概要出他们之间的各自的使用,希望各位要是被你的面试官问到了,也能从容的回答… static 加载:... -
无法从 static 上下文引用非 static 字段
2020-12-14 15:02:53错误:无法从 static 上下文引用非 static 字段 ‘tagLocatorMapper’ 解决方式: @Component public class StaticTagLocatorMapper { @Autowired private TagLocatorMapper tagLocatorMapper; public static ...