黑马程序员-java关键字static详解

时间:2021-10-03 12:14:49

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


static表示“全局”或者“静态”的意思,是一个修饰符,用于修饰成员变量和成员方法,也可以形成静态代码块。

static修饰的成员变量和成员方法习惯上称为静态变量和静态方法。

被static修饰的成员变量和成员方法独立于该类的任何对象。只要这个类被加载,java虚拟机就能根据类名在方法区内找到它们。因此,引出了下列static的特点。

static的特点:

1、随着类的加载而加载。也就是说,静态会随着类的消失而消失。说明它的生命周期最长。

2、静态成员优先于对象存在,可以在任何对象创建前访问它。

3、不依赖类特定的对象,被所有对象所共享

4、静态成员多一个调用方式,除了可以被对象调用(不推荐),还可以直接被类名调用。格式:类名.静态成员

注意:

用public修饰的static成员本质上是全局变量和全局方法。

用private修饰static成员时,该成员可以被该类中其他的静态方法调用,或者出现在该类的静态代码块中,但不可以在其他类中通过类名直接调用。


下面分别介绍静态变量、静态方法和静态代码块。

1、静态变量

被static修饰的成员变量,叫做静态变量或类变量;未被static修饰的成员变量,叫做实例变量

两者的区别:

1)

静态变量在内存中只有一个拷贝(节省内存),所有对象共享这个拷贝。JVM只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配;

实例变量,每创建一个对象,就会为实例变量分配一次内存。实例变量可以在内存中有多个拷贝,互不影响(灵活)。

2)静态变量随着类的加载而存在于方法区中;实例变量随着对象的建立而存在于堆内存中。

3)静态变量生命周期最长,随着类的消失才消失;实例变量的生命周期随着对象的消失而消失。

4)静态变量既可以通过类名直接访问(方便),也可以通过对象访问(不推荐);实例变量只能通过对象访问。


2、静态方法

静态方法既可以直接通过类名调用,也可以通过对象调用;

因为静态成员先于对象存在,所以静态方法中不可以定义this\super关键字

静态方法只能访问静态成员,非静态方法既可以访问静态成员也可以访问非静态成员;

因为静态方法独立于任何对象,所以静态方法必须被实现,而不能是抽象的


3、静态代码块

静态代码块是类中独立于类成员的static语句块,可以有多个,位置可以随便放,但不在任何方法体内。JVM加载类时会按照它们在类中出现的先后顺序依次执行这些静态代码块,且每个代码块只加载一次。如下面的例子所示。

例1:

class StaticCode
{
static
{
System.out.println("StaticCode");
}
}
class StaticCodeDemo
{
static //静态代码块先于main方法执行
{
System.out.println("StaticCodeDemo1");
}
public static void main(String[] args)
{
new StaticCode();
new StaticCode(); //静态代码块只加载一次。
System.out.println("Over!");
}
static //静态代码块可以放在任意位置,JVM按照位置的先后顺序执行它们。
{
System.out.println("StaticCodeDemo2");
}
}

输出结果:

StaticCodeDemo1
StaticCodeDemo2
StaticCode
Over!

初始化时的优先顺序:静态代码块>构造代码块>构造函数;

静态代码块内不能访问非静态成员,构造代码块可以。


4、static与final一起用

我们先来了解一下final的作用。

final:作为一个修饰符,有“终态的”、“无法改变”的含义。它可以修饰非抽象类、非抽象成员方法和变量。

1、被final修饰的类不可以被继承。因此final类的成员方法不会被覆盖。设计类时,如果该类不需要子类,类的实现细节不允许改变,并且确信该类不会再被扩展,则可设计为final类。

2、被final修饰的方法不可以被复写。使用final方法的原因有二:1)把方法锁定,防止任何继承类修改它的意义和实现;2)高效。编译器在遇到调用final方法时会转入内嵌机制,大大提高执行效率。

3、被final修饰的变量是一个常量,只能赋值一次且一旦给定就不能改变。而且必须被赋值后才能使用。可以在定义时赋值,也可在构造方法中赋值(只要在构造方法结束前给赋值即可)。final既可以修饰成员变量,也可以修饰局部变量。

4、final不能用于修饰构造方法。

5、父类的private成员方法是不能被子类方法覆盖的,因此它其实默认是final类型的。

6、内部类定义在类中的局部位置时,只能访问该局部被final修饰的局部变量。


因此当static和final一起修饰成员时,

对于变量,表示一旦给值就不可以修改,并且在内存中只有一份拷贝,可以通过类名直接访问。如果前面再加上public修饰符,则就是“全局常量”。对于常量一般用大写字母命名,多个单词间用_连接。如

public static final double PI=3.14;

对于方法,表示不可以被覆盖,并且可以通过类名直接访问。

注意:

对于被static和final修饰的实例常量,对象本身不能再改变了。但对于一些容器类型(如ArrayList、HashSet等集合类型)的实例变量,容器变量本身不能改变,但可以修改容器中存放的对象。如下面的例子所示。

例2:

import java.util.*;

class StaticFinal
{
private static final String str1="aaa";
private static final String str2=null;
private static String str3=null;
private static final int i1=0;
private static final Integer i2=new Integer(1);
private static final ArrayList<String> al=new ArrayList<String>();

public static void change()
{
//str1="bbb"; //错误,final修饰,str1的值不能再改变。
//str2="bbb"; //错误,final修饰,str2的值不能再改变,哪怕值为null。
str3="bbb"; //正确,static只表示类变量,可以改变值。
//i1=1; //错误,final修饰,i1的值不能再改变,哪怕值为0。
//i2=new Integer(1); //错误,final修饰,i2的值不能再改变。
al.add("aaa"); //正确,容器类型变量本身未变化,但存放内容改变了。
System.out.println("---------after change--------");
}
public static void print()
{
System.out.println("str1="+str1);
System.out.println("str2="+str2);
System.out.println("str3="+str3);
System.out.println("i1="+i1);
System.out.println("i2="+i2);
System.out.println("al="+al);
}
}
class TestDemo
{
public static void main(String[] args)
{
StaticFinal.print();
StaticFinal.change();
StaticFinal.print();
}
}

输出结果:

str1=aaa
str2=null
str3=null
i1=0
i2=1
al=[]
---------after change--------
str1=aaa
str2=null
str3=bbb
i1=0
i2=1
al=[aaa]

5、什么时候使用静态

静态变量:当对象中出现共享数据时,该数据被static修饰;对象中的特有数据要定义成非静态存在于堆内存中。

静态方法:当功能内部没有访问到非静态数据(对象的特有数据)时,那么该方法可以定义为静态的。


6、静态的应用

1)工具类

class ArrayTool
{
private ArrayTool(){} //不需要建立对象,故将构造函数私有化。
public static int getMax(int[] arr) //方法静态后,方便于直接调用。
{
int max=0;
for (int x=1;x<arr.length ;x++ )
{
if (arr[x]>arr.[max])
max=x;
}
return arr[max];
}
}

java中有许多这样的工具类,如java中的Math类。


2)单例设计模式

单例设计模式用于保证一个类在内存只存在一个对象。

分为两种:懒汉式与饿汉式。

饿汉式:

/*Single类一进内存,就已经创建好了对象。*/
class Single
{
/*为了让其他程序可以访问该类对象,只好在本类中自定义一个对象;
private用于防止其他程序直接访问该类对象;static用于让本类方法调用*/
private static Single s=new Single();

//private用于防止其他程序建立该类对象
private Single(){}

//为了方便其他程序对自定义对象的访问,对外提供一些访问方式
public static Single getInstance()
{
return s;
}
}

懒汉式:

/*Single类进内存时,对象还不存在。对象是在方法被调用时才初始化。这叫做对象的延时加载*/
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null) //存在安全隐患
s=new Single();
return s;
}
}
懒汉式安全隐患的解决方案:

class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class) //用同步代码块的方式解决问题。
{
if(s==null)
s=new Single();
}
}
return s;
}
}

开发一般用饿汉式,方便安全。



---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com