当前位置:课程学习>>第四章 面向对象基础>>文本学习>>知识点四


知识点四  Java常用关键字




一、静态修饰符static

static被称为静态修饰符,它可以用来修饰类中的成员和类中的方法。

1. 类变量和实例变量

被static修饰的成员变量称为静态变量,也叫类变量(class variable)。没有被static修饰的成员变量叫实例变量(instance variable)。

类变量是所有该类对象的一个公共存储区,因此不需要创建对象就可以访问类变量。访问和使用类变量时,可以使用对象名来引用,也可以直接使用类名来引用。

类变量的初始化优先于任何其它非static变量,如果某个变量被声明为static,意味着在类初始化时就必须为它分配内存空间,而实例变量则是在创建实例后才可以用。

【例4.16】类变量和实例变量的用法。

1 //StaticVariable.java

2 public class StaticVariable

3 {

4    static int sa=3;

5    int b=5;

6    public static void main(String[] args)

7    {

8       System.out.println("Static variable sa ="+sa);

9       StaticVariable one =new StaticVariable();

10      System.out.println(one.sa);

11     System.out.println(one.b);

12   }

13}

二、访问控制符

Java中的访问权限即访问控制符,它是用来限定外界对其的访问存取程度,访问控制符可以修饰类、也可以修饰类中的成员和方法。一共有四种级别:缺省为空(包可访问)、public、protected、private。

1. 缺省情况(包可访问)

类、类中成员和方法的修饰符中若没有访问控制符修饰,那么就称为默认(或缺省)访问控制,也称包可访问,即该类、成员以及方法只能被与它同处一个包内的其他类所访问和引用,而不能被包外其他的类访问,即使其子类也不可以。由于包实际上就是目录,因此同一个包的含义也就是在同一个目录下或者就是同一个文件中。

【例4.17】缺省访问控制符的使用。

1 //PackageAccess.java

2 //package packageA;

3 class PackageAccess

4 {

5    int a=10;

6    void prt()

7    {

8       System.out.println("In the PackageAccess class");

9    }

三、继承extends

继承性是面向对象程序设计语言的一个基本特征,通过继承可以实现代码的复用。继承而得到的类称为子类(subclass),被继承的类称为父类(或叫超类superclass)。子类可以直接继承父类中访问控制符为public、protected的成员和方法,如果和父类在同一个包内,还可以继承缺省访问控制符的成员和方法。但是子类不能继承父类中访问控制符为private的成员和方法。

Java中是通过关键字extends来声明类的继承。此时需要注意的是父类必须是存在而且是可访问的,另外在编译子类时会自动编译父类。

继承的时候,如果子类中声明了和父类同名的成员变量时,父类的成员变量就不会被继承,而是被子类的成员所隐藏,称为成员变量的隐藏(overriding),同样如果子类中定义了和父类相同的方法(方法名相同、参数列表相同而且返回值类型也相同),这就是方法的重写(overriding)。

对于方法的重写需要注意的是子类中不能重写父类中final修饰的方法,如果不是抽象(abstract修饰的)类,子类就必须重写父类中的abstract修饰的(抽象)方法。此外重写后的方法不能比父类中的方法有更严格的访问控制符。

【例4.18】继承的实现、成员变量的隐藏以及方法的重写。

1 //ClassExtends.java

2 class SuperClass

3 {

4    int a=3;

5    String name="super class";

6    private int b;

7    void f1()

8    {

9       System.out.println("super class f1()");

10    }

11    public void f2()

12    {

13       b=3;

14       System.out.println("super class f2()");

15    }

16 }

17 public class ClassExtends extends SuperClass

18 {

19    String name="sub class";

20    public void f2()

21    {

22       a=4;

23       System.out.println("sub class f2()");

24    }

25    public static void main(String[] args)

26    {

27       ClassExtends ce=new ClassExtends();

28       ce.f1();

29       ce.f2();

30       System.out.println(ce.a);

31       System.out.println(ce.name);

32    }

33 }

程序运行结果为:

super class f1()

sub class f2()

4

sub class

这里有成员的隐藏(第19行),也有方法的重写(第20行的f2()),子类对象在访问成员和调用方法时都是子类中重写后的成员和方法,而不会访问和调用父类的成员和方法。需要注意的是第20行与第11行都有public修饰,如果去掉第20行的public,编译的时候就会出现图4.10所示的错误提示:

14

图4.10 重写后的方法不能比原来方法的访问权限更小

子类在继承父类后,子类不能继承父类的构造方法,但是用子类创建对象时却会自动调用父类中相应的构造方法。不管子类在创建对象时使用哪种形式(有参、无参)构造方法,都只会自动调用父类的默认构造方法(无参数的构造方法),此外如果父类中没有定义默认构造方法,那么在子类中也不能定义它的默认构造方法。当然子类中可以通过super关键字来调用父类有参数的构造方法(下一小节内容)。

【例4.19】继承时构造方法的调用。

1 class A

2 {

3    A()

4    {

5       System.out.println("in the A construction");

6    }

7    A(int a)

8    {

9       System.out.println("in the A construction,a="+a);

10    }

11 }

12 class B extends A

13 {

14    B()

15    {

16       System.out.println("in the B construction");

17    }

18    B(int b)

19    {

20       System.out.println("in the B construction,b="+b);

21    }

22 }

23 public class ConstructionExtends extends B

24 {

25    ConstructionExtends()

26    {

27       System.out.println("in the ConstructionExtends construction");

28    }

29    ConstructionExtends(int c)

30    {

31       System.out.println("in the ConstructionExtends construction,c="+c);

32    }

33    public static void main(String[] args)

34    {

35       System.out.println("create one object");

36       ConstructionExtends one=new ConstructionExtends();

37       System.out.println("create two object");

38       ConstructionExtends two=new ConstructionExtends(3);

39       System.out.println("main end");

40    }

41 }

程序运行结果如图4.11所示。

15

图4.11 继承时创建对象会依次调用父类的构造方法

如果把父类A或B中的默认构造方法删除掉,那么编译源程序时就会出现图4.12所示的错误(这里假设去掉B中的默认构造方法B())。

16

图4.12 父类没有默认构造方法时的错误提示

在一个子类中,既有类变量成员,又有实例成员,那么用这个子类创建对象时它们被初始化是有先后顺序的。首先是类中的类变量最先被初始化,然后是该类的父类的构造方法从高到低被依次调用,然后才是实例成员的初始化,最后才调用该类本身的构造方法,完成对象的创建工作。

【例4.20】类成员、实例成员以及构造方法的调用顺序。

1 //CallOrder.java

2 class TestClass

3 {

4    TestClass(int a)

5    {

6       System.out.println("in the TestClass,a="+a);

7    }

8 }

9 class SuperParentClass

10 {

11    SuperParentClass()

12    {

13       System.out.println("in the SuperParentClass");

14    }

15 }

16 class SuperClass extends SuperParentClass

17 {

18    SuperClass()

19    {

20       System.out.println("in the SuperClass");

21    }

22 }

23 public class CallOrder

24 {

25    TestClass tc1=new TestClass(1);

26    static TestClass tc2=new TestClass(2);

27    CallOrder(int a)

28    {

29       System.out.println("in the CallOrder,a="+a);

30    }

31    public static void main(String[] args)

32    {

33       System.out.println("main begin");

34       CallOrder one=new CallOrder(3);

35       System.out.println("create the second object");

36       CallOrder two=new CallOrder(4);

37    }

38 }

程序的运行结果如图4.13所示,注意类变量、实例变量、父类的构造方法、类本身的构造方法的执行顺序。

17
图4.13 类变量、实例变量、父类构造方法、本身构造方法的调用顺序

四、thissuper

1. this

this关键字表示当前类或当前对象,可通过this来引用类中被隐藏的成员变量,也可以利用this在一个构造方法中调用另外一个构造方法。

【例4.21】this的用法。

1 //ThisUsage.java

2 public class ThisUsage

3 {

4    int a;

5    int b;

6    ThisUsage()

7    {

8       a=0;

9       b=0;

10    }

11    ThisUsage(int a,int b)

12    {

13       this.a=a;

14       this.b=b;

15       //a=a;

16       //b=b;

17    }

18    ThisUsage(int a)

19    {

20       //System.out.println("a="+a);

21       this(a,10);

22    }

23    void prt()

24    {

25       System.out.println("a="+a+",b="+b);

26    }

27    public static void main(String[] args)

28    {

29       ThisUsage tu1=new ThisUsage(3,4);

30       tu1.prt();

31       ThisUsage tu2=new ThisUsage(5);

32       tu2.prt();

33    }

34 }

编译运行该程序后,其结果为:

a=3,b=4

a=5,b=10

如果第13行、第14行注释掉更换为第15行、第16行,编译运行后其结果为:

a=0,b=0

a=0,b=0

这是由于在ThisUsage(int a,int b)方法中的参数和成员变量名完全相同,无法区分,因此必须使用this。此外如果把第20行的注释符去掉,编译时就会出现如下提示:对this的调用必须是构造函数中的第一个语句。因此不能看出调用其他构造方法的语句必须是第一条语句,这样在一个构造方法中最多只能调用一次其他的构造方法,而且this和稍后的super一样都只能用在非静态方法中,而不能用在static修饰的方法中。

2. super

super关键字表示的是当前类的父类,super的使用有两种用法。

(1) 用super来访问父类中被隐藏的成员变量和父类中被重写的方法,格式为:

  super.成员名 super.方法名( [ 实参列表 ] )

【例4.22】super引用父类中被隐藏的成员和方法。

1 //SuperUsage1.java

2 class SuperClass

3 {

4    int x=3;

5    void prt()

6    {

7       System.out.println("in SuperClass.prt()");

8    }

9 };

10 class SuperUsage1 extends SuperClass

11 {

12    int x=10;

13    void prt()

14    {

15       super.prt();

16       System.out.println("in SubClasss.prt()");

17       System.out.println("super.x = "+super.x+", sub.x = "+x);

18    }

19    public static void main(String[] args)

20    {

21       SuperUsage1 su=new SuperUsage1();

22       su.prt();

23    }

24 };

里第15行是调用父类中被重写的prt()方法,在第17行访问了父类中被隐藏的成员变量x。程序运行结果为:

in SuperClass.prt()

in SubClass.prt()

super.x = 3,sub.x= 10

(2) 用super调用父类的构造方法,格式为: super( [ 实参列表 ] )

用super调用父类构造方法的语句和this用法相同也必须是构造方法的第一条语句,如果没有Java编译器会自动调用一个super()语句,而调用父类默认构造方法。

【例4.23】super调用父类构造方法。

1 //SuperUsage2.java

2 class SuperClass

3 {

4    int a;

5    SuperClass()

6    {

7       System.out.println("in the SuperClass");

8    }

9    SuperClass(int a)

10    {

11       this.a=a;

12       System.out.println("in the SuperClass,a="+a);

13    }

14 }

15    public class SuperUsage2 extends SuperClass

16    {

17       String str;

18       SuperUsage2()

19       {

20          //super();

21          System.out.println("in the SubClass");

22       }

23       SuperUsage2(int a,String str)

24       {

25          super(a);

26          this.str=str;

27       }

28       public static void main(String[] args)

29       {

30          SuperUsage2 one=new SuperUsage2();

31          SuperUsage2 two=new SuperUsage2(3,"test");

32          System.out.println(two.str);

33       }

34    }

程序运行结果为:

in the SuperClass

in the SubClass

in the SuperClass,a=3

test

这里如果把父类SuperClass中的默认构造方法去掉,即第5行到第8行去掉,那么在编译的时候就会出现错误,这是因为默认情况下,如果没有使用super语句,Java都会自动调用一个super()语句,即第20行有或没有的效果都一样。

五、常量final

final关键字表示最终或常量的含义,它可以用来修饰成员变量和类中的方法,也可以修饰类。

用final修饰的成员,不管其数据类型是基本数据类型还是引用类型,它的值一旦赋值后就不能被改变(即常量)。格式为:final 数据类型 成员名=初始化值;

【例4.24】常量final的用法。

1 //FinalTest.java

2 class FClass

3 {

4    int i=1;

5 };

6 public class FinalTest

7 {

8    final int fi=1;

9    FClass fc1=new FClass();

10    final FClass fc2=new FClass();

11    public static void main(String[] args)

12    {

13       FinalTest test=new FinalTest();

14       FClass fc3=new FClass();

15       test.fc1=fc3;

16       test.fi=4;

17       test.fc2=fc3;

18       test.fc1.i=10;

19       test.fc2.i=20;

20       System.out.println("test over");

21    }

22 };

编译该程序就会出现如图4.14所示。

18
图4.14 不能更改常量的值

这里是因为第16行对常量fi重新赋值了,第17行对常量fc2重新赋值了,只要把这两行注释掉,程序就可以正常运行了(至于第19行对fc2.i进行重新赋值是可以的,尽管fc2是常量,但它的成员i不是常量)。运行结果为:test over。

对于类中的常量成员,如果在声明时没有赋值,那么在创建对象时Java系统不会对它进行默认赋值(赋值为对应的数据类型的默认值),必须在使用前进行赋值,而且只能在构造方法中进行赋值。

【例4.25】利用构造方法对常量进行赋值。

1 //FinalVariable.java

2 class FinalVariable

3 {

4    int a;

5    final int fi1=1;

6    //final int fi1;

7    final int fi2;

8    FinalVariable()

9    {

10       fi2=3;

11    }

12    FinalVariable(int x)

13    {

14       fi2=x;

15    }

16    /*

17    void init(int x)

18    {

19       fi2=x;

20    }

21    */

22    public static void main(String[] args)

23    {

24       FinalVariable one=new FinalVariable();

25       FinalVariable two=new FinalVariable(10);

26       System.out.println("final variable one.a="+one.a);

27       System.out.println("final variable one.fi1="+one.fi1);

28       System.out.println("final variable one.fi2="+one.fi2);

29       System.out.println("final variable two.fi2="+two.fi2);

30    }

31 };

程序结果如图4.15所示。如果把第5行换为第6行或者把第16行到第21行的注释去掉,编译的时候都会出现错误。

19
图4.15 程序运行结果

用final修饰的方法是不能在它的子类中被重写,用final修饰的类是不能被继承。此外final修饰的类中所有的方法均为final方法(不管有无final关键字声明)。

六、抽象abstract

abstract表示抽象,可以用来修饰类和类中的方法。用abstract修饰的类叫抽象类,抽象类是不能直接用来创建对象,抽象类必须被继承,通过它的子类来访问抽象类中的成员和方法。

用abstract修饰的方法叫抽象方法,定义抽象方法只需方法的声明,不需要方法体(也不能有{}括号)。

【例4.26】抽象方法的定义。

1 //AbstractMethod.java

2 public abstract class AbstractMethod

3 {

4    abstract void f1();

5    abstract void f2()

6    {

7

8    }

9 }

编译该程序就会提示抽象方法不能有方法体,如图4.16所示。图4.16 抽象方法不能有方法体

20
图4.16 抽象方法不能有方法体

抽象类中不一定有抽象方法,但若有抽象方法,该类必须声明为抽象类。

【例4.27】抽象类中可以没有抽象方法。

1 //AbstractMethod.java

2 public abstract class AbstractMethod

3 {

4    void f1()

5    {

6      

7    }

8    void f2()

9    {

10

11    }

12 }

抽象类中可以没有抽象方法,编译以上程序没有任何问题。

【例4.28】抽象方法必须在抽象类中。

1 //AbstractMethod.java

2 public class AbstractMethod

3 {

4    abstract void f1();

5 }

编译以上程序,会出现错误提示,如图4.17所示的错误。

21
图4.17 抽象方法在非抽象类中的编译错误

对于抽象类的子类(直接子类)必须在类中重写父类中所有的抽象方法(给出方法体),否则除非该子类也是抽象类。

【例4.29】抽象类的子类。

1 //ExtendsAbstractClass.java

2 abstract class AbstractClass

3 {

4    abstract void f1();

5    abstract void f2();

6 }

7 public class ExtendsAbstractClass extends AbstractClass

8 {

9    void f1()

10    {

11      

12    }

13    void f2()

14    {

15       System.out.println("test");

16    }

17 }

编译该程序没有任何问题,但是如果把第9行到第12行或第13行到第16行注释掉,这里假设注释掉第9行到第12行,保存后重新编译,就会出现图4.18所示的错误。

22

图4.18 子类中未全部重写父类中的抽象方法出现的编译错误

更改的方法有两种:一是在子类中重写f1()方法,二是把当前类也声明为abstract类。

 

进入知识点五学习