1 static
static 修饰符也被称为静态修饰符。这个修饰符能够修饰三种程序组件:属性、方法、初始化代码块。
1.1 静态属性
static 修饰属性,则该属性就成为静态属性。静态属性是全类公有的属性。
class MyValue{ int a; static int b; } public class TestStatic{ public static void main(String args[]){ MyValue mv1 = new MyValue(); MyValue mv2 = new MyValue(); mv1.a = 100; mv1.b = 200; mv2.a = 300; mv2.b = 400; System.out.println(mv1.a); System.out.println(mv1.b); System.out.println(mv2.a); System.out.println(mv2.b); } } /* 100 400 300 400 */
要注意,400 这个数字出现了两次,200 没有出现。原因在于:b 属性是一个静态属性。MyValue 类的所有对象,都公用一个 b 属性,每一个对象的 b 属性,都指向同一块内存。如下图所示:
1.2 静态方法
用 static 修饰的方法称之为静态方法
class TestStatic{ int a = 10; //非静态属性 static int b = 20; //静态属性 public void ma(){} //非静态方法 public static void mb(){} //静态方法 public void fa(){ //fa 是一个非静态方法 System.out.println(a); //非静态方法能够访问非静态属性 System.out.println(b); //非静态方法能够访问静态属性 ma(); //非静态方法中,能够调用非静态方法 mb(); //非静态方法中,能够调用静态方法 } public static void fb(){ //fb 是一个静态方法 System.out.println(a); //编译错误 静态方法中不能访问非静态属性 System.out.println(b); //静态方法中可以访问静态属性 ma(); //编译错误, 静态方法中调用非静态方法 mb(); //静态方法中可以调用静态方法 } }
在非静态方法中,无论方法或属性是否是静态的,都能够访问;而在静态方法中,只能访问静态属性和方法。静态方法和静态属性统称为静态成员。以上的规律可以记成:静态方法中只能访问静态成员。需要注明的是,在静态方法中不能使用 this 关键字。除此之外,静态方法与静态属性一样,也能够用类名直接调用。
静态方法是属于全类公有的方法。从概念上来理解,调用静态方法时,并不针对某个特定的对象,这个方法是全类共同的方法。
class Super{ public void m1(){ System.out.println(“m1 in Super”); } public static void m2(){ System.out.println(“m2 in Super”); } } class Sub1 extends Super{ public void m1(){ //非静态方法覆盖非静态方法,编译通过 System.out.println(“m1 in Sub”); } public static void m2(){ //静态方法覆盖静态方法,编译通过 System.out.println(“m2 in Sub”); } } class Sub2 extends Super{ public static void m1(){} // 静态方法覆盖非静态方法,编译出错! public void m2(){} // 非静态方法覆盖静态方法,编译出错! }
静态方法只能被静态方法覆盖,非静态方法只能被非静态方法覆盖。
静态方法没有多态。当我们对一个引用调用静态方法的时候,等同于对这个引用的引用类型调用静态方法。
1.3 静态初始化代码块
public class MyClass{ static { //此处为静态初始化代码块 System.out.println(“In MyClass Static”); } int a; public MyClass(){ System.out.println(“MyClass()”); } }
1.3.1 类加载
类加载就是把.class 文件读入 JVM 的过程。也就是说,当 JVM 第一次遇到某个类时,会通过 CLASSPATH 找到相应的.class 文件,读入这个文件并把类的信息保存起来,这个过程叫做类加载。
2 final
final 属性能够修饰变量、方法和类。注意,所谓变量,既包括属性,又包括局部变量。也就是说,final 能够修饰属性、局部变量、方法参数(注意方法参数也是特殊的局部变量)。
2.1 常量
用 final 修饰的变量则称为常量。怎么来定义常量呢?可以把常量理解为:一旦赋值,其值不能改变的变量。
要注意的是,由于 static 属性是在类加载的时候分配空间的,因此静态的 final 属性不能在构造方法中赋值。我们可以选择在定义这个属性的时候赋值,或是在静态初始代码块中为这个属性赋值。
2.2 final 方法
相对 final 属性,final 方法的含义相对简单。final 修饰方法,表示该方法不能被子类覆盖。
2.3 final 类
final 修饰符也可以用来修饰类。final 类表示这个类不能被继承。
要注意区分 final 修饰方法和 final 修饰类的区别。一个类中有 final 方法,这个类能够被继承,但是无法覆盖 final 方法;而一个类如果本身就是 final 的,则这个类无法被继承,这样的话,它所有方法都无法被覆盖。
3 abstract
abstract 可以用来修饰类和方法。abstract 单词本身表示“抽象”,是 java 中一个很重要的修饰符。
3.1 抽象类
abstract 修饰类,则这个类就成为一个抽象类。抽象类的特点是:抽象类只能用来声明引用,不能用来创建对象。
虽然抽象类不能创建对象,但是抽象类能够声明引用,并让这个引用指向子类对象。从某种意义上说,写抽象类的目的就是为了能够让子类继承。
3.2 抽象方法
用 abstract 修饰的方法称为抽象方法。
抽象方法指的是:一个只有声明,没有实现的方法。对于抽象方法来说,方法的实现部分用一个分号来代替。
class MyAbstract{ public abstract void m1(); public void m2(){} }
因此,如果一个类中有抽象方法,这个类就必须是抽象类。
abstract class MyAbstract{ public abstract void m1(); public void m2(){} }
注意:有抽象方法的类必须是抽象类,但是反过来,抽象类中未必有抽象方法。
由此看出,抽象的语法和多态是紧密配合的。
总结一下:抽象方法的语法特征有如下几条:
1. 抽象方法只有声明,没有实现。实现的部分用分号表示。
2. 一个拥有抽象方法的类必须是抽象类。
3. 子类继承抽象类,要么也成为抽象类,要么就必须实现抽象类中的所有抽象方法。
3.3 抽象的作用
因此,使用抽象方法,就可以让我们把方法的声明放在父类中,把方法的实现留在子类中。这样一方面,能够很好的体现出“共性放在父类”这个基本的原则,另一方面,用父类类型的引用又可以调用这些方法,并不影响多态语法的正常使用。