static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。
static关键字主要有两种作用:
第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关。
第二,实现某个方法或属性与类而不是对象关联在一起
具体而言,在Java语言中,static主要有4中使用情况:成员变量、成员方法、代码块和内部类
1.static成员变量
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。
两者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
所以一般在需要实现以下两个功能时使用静态变量:
1).在对象之间共享值时
2).方便访问变量时
我们来看一个例子:
package jav;
public class Sum
{
//创建两个静态变量
static int a = 50;
static int b = 50;
int d = 30;
public int sum()
{
//调用静态变量
return a+b;
}
public static void main(String[] args)
{
Sum c = new Sum();
//直接使用静态变量
System.out.println(a);
System.out.println(c.sum());
//下面语句将会报错
//System.out.println(d);
}
}
程序运行结果为:
50
100
从上面程序可以看出,用static修饰的变量可以在直接被sum方法、main方法中直接调用,而没有用static修饰的变量只能通过实例对象间接调用,这就相当于弥补了java中没有全局变量。
除此之外,还要注意,普通变量存储在栈内存中,而static变量存储静态内存中;还得注意一点:我们先看一段代码
二.static方法
除了使用static定义变量之外,方法上也可以使用static进行定义,那么很明显,使用static定义的方法也可以在没有实例化对象产生的情况下由类名称直接进行调用。
我们来看这个例子:
package jav;
class test
{
//创建静态方法
public static int text()
{
return 50;
}
}
public class Sum
{
//创建静态变量
static int a = 50;
static int b = 50;
public static int sum()
{
//调用静态变量
return a+b;
}
public static void main(String[] args)
{
//直接调用静方法
System.out.println(test.text());
System.out.println(sum());
}
}
上面程序运行结果为:
50
100
通过这个程序,我们可以知道static方法与static变量类似,可以直接被调用,而不用static修饰的方法只能被实例化对象间接调用
此外我们还得注意,static修饰的方法能直接调用static方法,而非static方法既可以直接调用static方法,也可以直接调用static方法。
三.static代码块
static代码块又叫静态代码块,static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
package jav;
class test
{
//创建静态方法
public static int text()
{
return 50;
}
}
public class Sum
{
//创建静态变量
static int a = 50;
static int b = 50;
public static int sum()
{
//调用静态变量
return a+b;
}
public static void main(String[] args)
{
//直接调用静方法
System.out.println(test.text());
System.out.println(sum());
}
}
一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的。静态方法在类加载的时候就已经加载可以用类名直接调用
比如main方法就必须是静态的这是程序入口
两者的区别就是:静态代码块是自动执行的;
静态方法是被调用的时候才执行的。
我们来看一个例子:
package jav;
class Parent {
static String name = "parent";
{
System.out.println("parent block");
}
static {
System.out.println("parent static block");
}
public Parent() {
System.out.println("parent way");
}
}
class Child extends Parent {
static String childName = "child";
{
System.out.println("child block");
}
static {
System.out.println("child static block");
}
public Child() {
System.out.println("child way");
}
}
public class test{
public static void main(String[] args) {
new Child();// 语句(*)
}
}
上面程序运行结果:
parent static block
child static block
parent block
parent way
child block
child way
通过这个程序我们就可以知道static代码块会在类被加载的时候执行,并且只执行一次。
对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
四.static内部类
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,有的地方也称为静态内部类。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。下面程序就演示了这条规则。
public class StaticInnerClassTest
{
private int prop1= 5;
private static int prop2 = 9;
static class StaticInnerClass
{
//静态内部类里可以包含静态成员
private static int age;
public void accessOuterProp()
{
//下面代码出现错误
//静态内部类无法访问外部类的实例变量
System.out.println(prop1);
//下面代码正常
System.out.println(prop2);
}
}
}
上面程序中粗体字代码行定义了一个静态成员变量,因为这个静态成员变量处于静态内部类中,所以完全没有问题。StaticInnerClass 类里定义了一个accessOuterProp()方法,这是一个实例方法,但依然不能访问外部类的prop1成员变量,因为这是实例变量; 但可以访问prop2,因为它是静态成员变量。
静态内部类是外部类的一个静态成员,因此外部类的所有方法、所有初始化块中可以使用静态内部类来定义变量、创建对象等。
外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。下面程序示范了这条规则。
public class AccessStaticInnerClass
{
static class StaticInnerClass
{
private static int propl = 5;
private int prop2 = 9;
}
public void accessInnerProp()
{
//System.out.println (propl) ;
//上面代码出现错误,应改为如下形式
//通过类名访问静态内部类的类成员
System.out.println(StaticInnerclass.propl);
//System.out.printin (prop2);
//上面代码出现错误,应改为如下形式
//通过实例访问静态内部类的实例成员
System.out.println(new StaticInnerClass().prop2);
}
}
除此之外, Java还允许在接口里定义内部类,接口里定义的内部类默认使用public static修饰,也就是说,接口内部类只能是静态内部类。如果为接口内部类指定访问控制符,则只能指定public访问控制符; 如果定义接口内部类时省略访问控制符, 则该内部类默认是public访问控制权限。