介绍
在本实验中,你将学习访问修饰符和继承。通过不同的修饰符,访问级别会有所不同。Java 中的继承类似于生物学中的继承,子类可以保留父类的特性,并在某些方面表现出不同的行为。
在本实验中,你将学习访问修饰符和继承。通过不同的修饰符,访问级别会有所不同。Java 中的继承类似于生物学中的继承,子类可以保留父类的特性,并在某些方面表现出不同的行为。
到目前为止,我们已经编写了一些代码。在之前的实验中,我们编写了一个类。其中使用了一些修饰符,例如 public
和 private
。那么,这些词的含义是什么呢?
Java 提供了多种访问修饰符来为类、变量、方法和构造函数设置访问级别。四种访问级别如下:
Java 还提供了多种非访问修饰符,以实现其他行为微调方式:
static
变量的副本。final
变量只能显式初始化一次。final
方法不能被任何子类重写。声明为 final
的类不能被继承。abstract
类永远不能被实例化。abstract
方法是没有实现的方法声明。synchronized
和 volatile
修饰符与线程相关。示例:
在 /home/labex/project/modifierTest.java
文件中编写以下代码:
public class modifierTest {
// static 变量在类加载时初始化。
public static int i = 10;
public static final int NUM = 5;
// 非 static 变量在对象创建时初始化。
public int j = 1;
/*
* 静态代码块,类加载时执行
* 创建新对象不会再次执行该代码块,仅执行一次。
*/
static{
System.out.println("this is a class static block.");
}
public static void main(String[] args)
{
System.out.println("this is in main method");
// 你可以访问并修改 i
modifierTest.i = 20; // 等同于 obj.i = 20
System.out.println("Class variable i = " + modifierTest.i);
// 你可以访问 NUM,但不能修改它
// HelloWorld.NUM = 10; 这会导致错误,NUM 是 final 的,不可变
System.out.println("Class variable NUM = " + modifierTest.NUM);
// 创建新对象
modifierTest obj = new modifierTest();
// 我们可以使用类或对象访问静态方法和静态属性
obj.staticMethod(); // 等同于 modifierTest.staticMethod()
// 你不能这样访问 j: modifierTest.j
System.out.println("Object variable j = " + obj.j);
}
// 构造函数,只有在创建新对象时才会调用。
public modifierTest(){
System.out.println("this is in object's constructor.");
}
public static void staticMethod(){
System.out.println("this is a static method");
}
}
输出:
使用以下命令运行 modifierTest.java
文件:
javac /home/labex/project/modifierTest.java
java modifierTest
查看输出:
this is a class static block.
this is in main method
Class variable i = 20
Class variable NUM = 5
this is in object's constructor.
this is a static method
Object variable j = 1
在许多情况下,我们已经编写了一个类。然后,我们需要编写一个新类,只是为了对前一个类的代码进行少量修改,并且它们在逻辑上存在某种关系。这时我们可以使用继承。我们使用关键字 extends
来实现继承。子类通过继承获得父类所有可访问的属性和方法,同时子类也可以拥有自己特有的属性和方法。在子类中,我们可以访问父类中声明为 public
或 protected
的成员,但不能直接访问 private
成员。请看一个示例:继承结构如下图所示。你可以实现多级继承(水平)或层次继承(垂直):
示例:
在 /home/labex/project/inheritanceTest.java
文件中编写以下代码:
class Animal{
// 我是什么种类的动物。
private String species;
private int age = 8;
public Animal(){
System.out.println("Animal's constructor");
}
public void grow(){
// 在这个类中,我们可以直接访问私有属性。
System.out.println("I'm "+ this.age + " years old, " +"I grow up.");
}
}
class Dog extends Animal{
private String color;
// 在这个类中,我们不能访问父类的私有属性,但可以访问 grow() 方法。
public Dog(){
System.out.println("Dog's constructor");
}
public void run(){
this.grow();
System.out.println("I'm dog, I can run.");
}
}
class Bird extends Animal{
private double weight;
public Bird(){
// 如果显式调用父类的构造函数,必须在这里的第一行。
// super();
System.out.println("Bird's constructor");
}
public void fly(){
this.grow();
System.out.println("I'm bird, I can fly.");
}
}
public class inheritanceTest{
public static void main(String[] args){
Dog dog = new Dog();
dog.run();
Bird bird = new Bird();
bird.fly();
}
}
输出:
使用以下命令运行 inheritanceTest.java
文件:
javac /home/labex/project/inheritanceTest.java
java inheritanceTest
查看输出:
Animal's constructor
Dog's constructor
I'm 8 years old, I grow up.
I'm dog, I can run.
Animal's constructor
Bird's constructor
I'm 8 years old, I grow up.
I'm bird, I can fly.
当你看到这个输出时,可能会感到困惑。别担心,我们会解释原因。当我们使用 new
创建子类的对象时,默认情况下会首先从继承树结构的“顶部”开始调用父类的默认构造函数,最后执行自己的构造函数。可以使用 super
关键字显式调用父类的构造函数,但它必须是构造函数中的第一条语句(如果存在)。super
关键字指向调用类在层次结构中紧邻的父类。
通过访问修饰符,我们可以编写安全的代码,隐藏细节,实现访问控制。其他用户无需了解我们如何实现方法的细节。我们为其他调用者提供了一个接口。对于访问修饰符,你可以阅读 Java 源代码库来理解它们之间的区别。继承,记住层次结构,是类之间的关系。