013 关键字Static和Final

 

static关键字:可以用于修饰属性,方法,类。
 
在Java中,可以将一些成员限制为和类相关的,而不是和实例相关的。实现这个目的的方法是,在类的成员如方法、属性乃至自由代码块前面加上“static”这个修饰符。

实例变量用于描述单个实例的状态,而类变量用于描述类的状态,和具体的实例无关。
 实例方法用于操作实例变量并维护特定实例的状态,而类方法并不了解单个实例的状态。

在Java中,static最常用的地方是用在Java应用程序的入口方法main()前面用于修饰main()方法,表示main()方法是一个类方法。
 
我们在执行一个应用程序的时候,解析器将会去寻找这个main()方法,之所以将它设置为类方法,是因为,在调用这个main()方法之前,还没有任何对象存在,所以,如果不将它设置为类相关的,则没有办法得到任何的对象,也就无法调用这个main()方法。

我们来看另外一个static的例子:
public class TestStatic {
 public static int count=0;
 
 public TestStatic() {
  count++;
 }
 
 public static int getCount() {
  return count;
 }
 
 public static void main(String args[]) {
 
  TestStatic ts1=new TestStatic();
  System.out.println(ts1.count);
 
  TestStatic ts2=new TestStatic();
  System.out.println(ts2.count);
  //System.out.println(TestStatic.count); 
  //System.out.println(TestStatic.getCount());
 }
}

在这个程序中,定义了一个静态变量count,并定义了一个静态方法来获取。每次在构造这个类的实例的时候,都将这个count加上1。
 
因为这个变量是类变量,所以,每次在调用构造方法来构造类的实例的时候,都会将这个变量加上1。因为它是和类相关的,所以,它会在原来的基础上加上1,而不是每次都对它进行初始化成0。
 
运行这个程序,将得到如下的结果:
1
2
如果将这个程序的静态变量count和静态方法getCount()都改成非静态的(只需将变量和方法前的static都去掉),重新编译运行,则可以得到下面的结果:
1
1

这是因为,如果将静态变量count前的static去掉,则这个变量变成了非静态变量,也就是实例变量,这样,每次调用构造器进行类的实例化时,将重新对这个变量进行初始化,因此,每次它都是从0开始计算,然后在构造器中对其加1,这样,打印出来的结果就是1了。

在这个程序中,也可以不通过实例名来访问变量count或方法getCount(),因为这两个成员都是类成员,可以直接通过类名加“.”来调用它们。将程序中的两个打印语句的注释去掉,就可以向控制台输出如下的结果:
1
2
2
2

最后需要指出的是,static不可用于修饰构造器。

注意:虽然static修饰的成员是类成员,但是,同样可以通过实例名来调用static修饰的类成员,就像调用其他的实例成员一样。

接下来讲讲static自由块。
 
前面提到了自由块(游离块),那么作为Java源代码特殊的组成部分,是可以用static来修饰的。修饰后的结果我们来分析下。
 
自由块可以看成是一种特殊的方法,这个方法没有方法名、没有输入参数、没有返回值,不能进行方法调用。从这个角度来看,在自由块前面也可以加上static关键字来修饰。这样,这个自由块就成了静态自由块。静态自由块通常用于初始化静态变量。
public class Count {
 private int serialNumber;
 public static int counter;
 static {
  System.out.println("static自由块被执行");
  counter = 1;
 }
 public static int getTotalCount() {
  return counter;
 }
 public Count() {
  counter++;
  serialNumber = counter;
 }
 public static void main(String[] args) {
  System.out.println("main() invoked");
  System.out.println("counter = " + Count.counter);
 }
}

在这个类中,定义了一个静态的int类型变量counter,然后在static自由块中初始化这个变量。编译并运行这个程序,可以得到如下的输出:
static自由块被执行
main() invoked
counter = 1

因为static自由块是类相关而不是实例相关的,所以,即使没有实例化对象,它也会被执行(在main()方法中没有实例化这个类)----它将向控制台输出“static自由块被执行”并将静态变量“counter”初始化成1。


static修饰属性:无论一个类生成了多少个对象,所有这些对象共同使用唯一一份静态的成员变量;一个对象对该静态成员变量进行了修改,其他对象的该静态成员变量的值也会随之发生变化。如果一个成员变量是static的,那么我们可以通过“类名.成员变量名”的方式来使用它(推荐使用这种方式)。[Class012/StaticVariable.java]
 
static修饰方法:static修饰的方法叫做静态方法。对于静态方法来说,可以使用类名.方法名的方式来访问。[Class012/StaticMethod.java]
 
静态方法只能继承,不能重写(Override)。【难点】
[Class012/StaticMethodOverwrite.java]
 
静态方法可以通过二种形式来调用,一种是类名加方法名,另一种是类引用加方法名.通过第二种方式来调用静态方法,其实质是检查引用的类型来调用静态方法(即类名加方法名的方式).
静态成员(方法和属性)属于类而不是属于对象,静态方法,静态属性,动态属性早在编译期就已经确定(相关地址数据存储在虚拟机的方法区类数据中).
 
1. 静态方法可以被继承
程序一:
import static java.lang.System.out;
class SuperClass
{
       publicstatic void display()
       {
              out.println("inthe super class.");
       }
}
public class SubClass extends SuperClass
{
       publicstatic void main(String[] args)
       {
              SubClasssub = new SubClass();
              sub.display();  // 调用子类继承父类的静态方法
       }
}
根据输出结果:
in the super class.可知 sub.display() 调用子类继承了父类的静态方法.由此可见,子类可以继承父类的静态方法.
 
 
 
2. 静态方法被隐藏不能被重写
程序二:
import static java.lang.System.out;
class SuperClass
{
       publicstatic void display()
       {
              out.println("inthe super class.");
       }
}
public class SubClass extends SuperClass
{
       publicstatic void main(String[] args)
       {
              SubClasssub = new SubClass();           // 上转型对象
              sub.display();  // 调用子类自己的静态方法
 
              SuperClasssup = new SubClass(); // 子类对象
              sup.display();  // 用被隐藏的父类静态方法
       }
       publicstatic void display()
       {
              out.println("inthe sub class.");
       }
}
运行结果:
----------------------------
in the sub class.
in the super class.
----------------------------
对于动态方法,上转型对象调用的将会是子类中重写父类的动态方法.而对于此处的静态方法,父类子类中都含有静态方法display()的情况,上转型对象却调用的父类的静态方法display(),而非子类中定义的静态方法display().
由此可见:子类并没有重写父类的静态方法,而是将父类的静态方法隐藏,隐藏的父类方法可以通过父类类名在子类中被调用.当子类中重写的方法也是静态的时候,其实质是将父类的
静态方法隐藏,而并非是将其重写.
隐藏和重写的区别在于:隐藏是根据引用的类型来进行调用.重写是根据对象的类型来进行调用.
 
3. 动态方法不能覆盖静态方法
上述程序二,如果去掉子类方法display()的static修饰,则会报如下错误:
---------- 编译Java ----------
报错:
SubClass 中的 display() 无法覆盖 SuperClass 中的 display();
被覆盖的方法为 staticpublic void display()
说明: 子类中的动态方法display()无法覆盖父类中的静态方法display().
 
4. 静态方法不能覆盖动态方法
 
上述程序二,如果去掉父类方法display()的static修饰,则会报如下错误:
---------- 编译Java ----------
报错:
SubClass 中的 display() 无法覆盖 SuperClass 中的 display();
覆盖的方法为静态public static void display()
说明: 子类中的静态方法display()无法覆盖父类中的动态方法display().
 
 

 

2. final关键字:final可以修饰属性、方法、类。
 
final修饰类:当一个类被final所修饰时,表示该类是一个终态类,即不能被继承。
[Class012/FinalClass.java]
final修饰方法:当一个方法被final所修饰时,表示该方法是一个终态方法,即不能被重写(Override)。[Class012/FinalMethod.java]
 
final修饰属性:当一个属性被final所修饰时,表示该属性不能被改写。当final修饰一个原生数据类型时,表示该原生数据类型的值不能发生变化(比如说不能从10变为20);
如果final修饰一个引用类型时,表示该引用类型不能再指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。[Class012/FinalProperty.java]
 
对于final类型成员变量,一般来说有两种赋初值方式:
a) 在声明final类型的成员变量时就赋上初值
b) 在声明final类型的成员变量时不赋初值,但在类的所有构造方法中都为其赋上初值。
[Class012/FinalData.java]
 
3.static代码块:静态代码块。静态代码块的作用也是完成一些初始化工作。
 
首先执行静态代码块,然后执行构造方法。静态代码块在类被加载的时候执行,而构造方法是在生成对象的时候执行;要想调用某个类来生成对象,首先需要将类加载到Java虚拟机上(JVM),然后由JVM加载这个类来生成对象。
 
类的静态代码块只会执行一次,是在类被加载的时候执行的,因为每个类只会被加载一次,所以静态代码块也只会被执行一次;而构造方法则不然,每次生成一个对象的时候都会调用类的构造方法,所以new一次就会调用构造方法一次。
 
如果继承体系中既有构造方法,又有静态代码块,那么首先执行最顶层的类的静态代码块,一直执行到最底层类的静态代码块,然后再去执行最顶层类的构造方法,一直执行到最底层类的构造方法。注意:静态代码块只会执行一次。
[Class012/StaticBlock.java]
[Class012/StaticBlockInherit.java]
 
4.不能在静态方法中访问非静态成员变量;可以在静态方法中访问静态的成员变量。可以在非静态方法中访问静态的成员变量。
[Class012/StaticProperty.java]
 
5. 总结:静态的只能访问静态的;非静态的可以访问一切。
6. 不能在静态方法中使用this关键字。
[Class012/ StaticProperty.java]

你可能感兴趣的:(static)