类的继承
在Java中,类文件是带有。Java后缀的代码文件。每个类文件最多允许有一个公共类。类文件的名称可以是任何名称(当然,不允许以数字开头的名称)。
在类内部,对于成员变量,如果在定义时没有执行显式的赋值初始化,Java会确保类的每个成员变量都正确初始化:
1) Char、short、byte、int、long、float、double等默认初始化为0 (Boolean默认初始化为false);
2)对于引用类型的变量,默认初始化为NULL。
如果构造函数没有显式定义,编译器会自动创建一个无参数构造函数,但请记住,如果构造函数是显式定义的,编译器不会自动添加构造函数。
让我们关注初始化序列:
当一个程序被执行并且需要生成某个类的对象时,Java执行引擎会检查这个类是否被加载。如果没有,则Java执行引擎将执行类的加载以生成对象。如果装入类,则Java执行引擎直接生成对象。
在装入类期间,初始化类的静态成员变量,如果类中有静态块,则执行静态块。静态成员变量和静态语句块的执行顺序与代码中相同。请记住,在Java中,类是按需加载的,而且只在需要类时才加载,而且只加载一次。看看这个例子:
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Bread bread1 = new Bread();
Bread bread2 = new Bread();
}
}
class Bread {
static{
System.out.println("Bread is loaded");
}
public Bread() {
System.out.println("bread");
}
}
如果您运行这段代码,您将看到“Bread is loaded”只打印一次。
在对象生成期间,在执行构造函数之前初始化对象的成员变量。也就是说,在调用任何方法(包括构造函数)之前,类中的变量都是初始化的,即使变量在方法定义之间遍历。
public class Test {
public static void main(String[] args) {
new Meal();
}
}
class Meal {
public Meal() {
System.out.println("meal");
}
Bread bread = new Bread();
}
class Bread {
public Bread() {
System.out.println("bread");
}
}
输出结果为:
bread
meal
继承是所有OOP语言中不可分割的一部分,Java中使用extends关键字来表示继承关系。创建类时,总是继承它,如果没有显式指示要继承的类,则从根类Object隐式继承它。以下面的代码为例:
class Person {
public Person() {
}
}
class Man extends Person {
public Man() {
}
}
Man类继承自Person类,因此Person类称为父类(基类),Man类称为子类(派生类)。如果两个类之间存在继承关系,子类自动继承父类的方法和变量,并可以在子类中调用父类的方法和变量。在Java中,只允许单继承,这意味着一个类最多可以显式地从一个父类继承。但是一个类可以被多个类继承,这意味着一个类可以有多个子类。
1. 子类从父类继承成员变量
当子类继承一个类时,它可以使用父类的成员变量,但不能使用父类的所有成员变量。具体原则如下:
1) Public和protected成员变量可以从父类继承;不能从父类继承私有成员变量;
2)对于父类的包访问成员变量,如果子类和父类在同一个包中,则子类可以继承;否则,子类不能继承;
3)对于子类可以继承的父类成员变量,如果子类中存在同名的成员变量,则会出现隐藏的现象,即子类的成员变量将掩码同名的父类成员变量。如果想从子类访问父类中同名的成员变量,需要使用super关键字来引用它。
2. 子类从它们的父类继承方法
类似地,子类并不继承父类的所有方法。
1)继承自父类的Public和protected成员方法;不能继承父类的私有成员方法;
2)对于父类的包访问成员方法,如果子类和父类在同一个包中,则子类可以继承;否则,子类不能继承;
3)如果一个同名的成员方法出现在子类中,它被称为覆盖。也就是说,子类的成员方法将覆盖具有相同名称的父类的成员方法。如果想从子类访问父类中同名的成员方法,需要使用super关键字来引用它。
注意:隐藏和覆盖是不同的。隐藏用于成员变量和静态方法,而覆盖用于普通方法。(稍后再详细介绍)
3.构造函数
子类不能继承其父类的构造函数,但请注意,如果父类的构造函数接受参数,则必须在子类的构造函数中用super关键字和适当的参数列表显式调用父类的构造函数。如果父类没有参数构造函数,就没有必要在子类的构造函数中调用super关键字。如果super关键字没有被使用,系统会自动调用父类的无参数构造函数。看看这个例子:
class Shape {
protected String name;
public Shape(){
name = "shape";
}
public Shape(String name) {
this.name = name;
}
}
class Circle extends Shape {
private double radius;
public Circle() {
radius = 0;
}
public Circle(double radius) {
this.radius = radius;
}
public Circle(double radius,String name) {
this.radius = radius;
this.name = name;
}
}
4.super
super有两个主要用途:
1)超级。成员变量/超级。成员方法;
2) super (parameter1, parameter2…)。
第一个用于调用子类中父类的成员变量或方法。第二个方法主要用于子类构造函数中,以显式调用父类的构造函数。注意,如果在子类构造函数中使用,它必须是子类构造函数中的第一条语句。