一文看懂java-static关键字

带有静态方法的类通常是不打算要被初始化的。我们都知道抽象类是不能被初始化的。但当你想让一些非抽象类也不能被初始化,你可以使用私有的构造函数加以限制。Math类就是这样防止自己被初始化的。它让构造函数标记为私有,所以你无法创建Math的实例。

但这并不能说有静态方法的类就不能被初始化了。事实上,只要有main()的类都算是有静态的方法的!因此你可以随意在类中组合静态和非静态方法。

静态的方法不能调用非静态的变量和方法。

比如下面的代码:
public class StaticTest { int i=0; public int geti(){ return i; } public
static void main(String[] args) { System.out.println(i);//错, int o=geti();//错,
} }
静态变量被同类的所有实例共享。

我们来看下面这个例子:
public class StaticTest { int i=0; public int geti(){ return i; } public
static void main(String[] args) { Song song01=new Song("lalala"); Song
song02=new Song("hahaha"); Song song03=new Song("wawawa");
System.out.println(song01.toString()+"\n"+song02.toString()+"\n"+song03.toString());
} } class Song { private String name; private int num=0; Song(String name) {
this.name=name; num++; } @Override public String toString() { return name+"
"+num; } }
输出结果为:

lalala 1

hahaha 1

wawawa 1

我们可以发现song01、song02、song03各自拥有一个name,但这些实例只有一个num变量。所以说,静态变量是共享的。同一类的所有实例共享一份静态变量。

静态变量在类被加载时就会被初始化。类似下面的代码:

 
public class StaticTest { public static void main(String[] args) {
//静态变量会在楼类的任何对象刨应立前就完成初始化 //份态变量会在民类的任何 静态方法执行之前就初始化
System.out.println(Song.num); Song song01=new Song("lalala");
System.out.println(Song.num); } } class Song { private String name; public
static int num=0;//song类被载入时num被初始化为0 Song(String name) { this.name=name;
num++; } }
 

程序运行后输出:

0

1

 

类属性中被static所引用的变量,会被作为GC的root根节点。作为根节点就意味着,这一类变量是基本上不会被回收的。因此,static很容易引入内存泄漏的风险。

当一个面试官让你解释static关键字,如果你说
static可以修饰属性、方法和内部类,以及修饰之后又有什么效果的话,那么面试官基本上不会记住你这个回答,整个印象就是普通。

但是如果你说完以后,补充一下说道,你曾经遇到过一个内存泄漏的问题,就是因为static修饰的一个Map类型的变量导致的,最后排查了堆栈信息找到了问题的所在,并且解决了这个问题。那么,面试官这个时候内心中对你的印象,就会不自然的提升几分。

对于static,更深入的理解是,static会将所引用的属性、方法以及内部类,与类直接产生引用关系,而非与类的实例。这就是为什么,你可以使用类名.属性、类名.方法以及类名.内部类名,来直接引用一个被static所修饰的属性、方法或者内部类。
如果你没有用static修饰,那么你就必须使用实例才能引用这些方法、属性或者是内部类,最典型的就是内部类。

我们来看下下面这段简短的代码:
public class StaticTest { public static void main(String[] args) { Song.get();
//Song.set(); Song song=new Song(); song.set("lili"); } } class Song { private
String name; public void set(String name){ this.name=name; } public static void
get(){ System.out.println("name"); } }

我们定义了一个Song类,并在其中定义了一个非静态方法set()和一个静态方法get(),当我们想要执行static修饰的静态方法的时候我们可以直接使用[类.方法名]来调用这个方法,而当我们想要调用set()方法时,我们必须要new一个Song的实例,然后用[实例名.方法名]来调用这个方法,这就是static的基本作用。

 

让我们简单修改下代码:
public class StaticTest { public static void main(String[] args) { Song.get();
//Song.set(); Song song=new Song(); song.set("lili"); Song.Singer singer=new
Song().new Singer(); singer.set("fengfeng"); } } class Song { private String
name; public void set(String name){ this.name=name; } public static void get(){
System.out.println("name"); } class Singer{ private String singername; public
void set(String singername){ this.singername=singername; } } }
 

 

相信很多程序员都好奇过,为什么一个没有被static修饰的内部类,必须要这么声明:

OutterClass.InnerClass innerClass = new OutterClass().new InnerClass();

也就是我们上面程序的:

Song.Singer singer=new Song().new Singer();

解释一下,这是
因为你没有使用static修饰InnerClass,所以你必须new出来一个OutterClass的实例,才能在此基础上new出内部类的实例,因为内部类只能通过外部类的实例才能引用。如果你使用了static修饰,那么你就可以这样使用内部类。

OutterClass.StaticInnerClass staticInnerClass = new
OutterClass.StaticInnerClass();

类似这样:

 
public class StaticTest { public static void main(String[] args) {
Song.get(); //Song.set(); Song song=new Song(); song.set("lili"); //Song.Singer
singer=new Song().new Singer(); //singer.set("fengfeng"); Song.Singer
singer=new Song.Singer(); } } class Song { private String name; public void
set(String name){ this.name=name; } public static void get(){
System.out.println("name"); } static class Singer{ private String singername;
public void setSinger(String singername){ this.singername=singername; } } }

这两种方式最大的区别就是,第一种方式,如果你想要获得InnerClass的实例,你必须有一个OutterClass的实例,其实这种方式你创建了两个实例,所以有两个new关键字。而第二种方式就好理解一些,静态内部类不依赖于外部类的实例存在,因此只需要直接创建内部类的实例就可以了,所以只有一个new关键字。

技术
©2019-2020 Toolsou All rights reserved,
过拟合和欠拟合的形象解释2021 美赛时间安排表Springboot之JPA常用查询方法中国月球车“月兔二号”在月球发现一块奇怪岩石PS制作登录界面C语言程序设计课程设计 之《学生成绩管理系统》消息质量平台系列文章|全链路排查篇年薪20万属于什么水平?答案让人扎心!王者荣耀背景故事整合Jsp+Ajax+Servlet+Mysql实现增删改查(一)