**继承语法的存在是为了提高代码的复用性。**假设我们想创建以下几个类:Dog
、Cat
、Fish
。
那么就会有以下代码:
class Dog{
public String name;
public int age;
public String color;
public void eat(){
System.out.println(this.name + "在吃。");
};
public void run(){
System.out.println("狗在跑。");
}
}
class Cat{
public String name;
public int age;
public void eat(){
System.out.println(this.name + "在吃。");
};
public void jump(){
System.out.println("猫在跳。");
}
}
class Fish{
public String name;
public int age;
public void eat(){
System.out.println(this.name + "在吃。");
};
public void swim(){
System.out.println("鱼在游泳。");
}
}
我们发现上面的代码太冗余了,代码重复度太高了。这种代码的可读性是很低的。那么怎么解决这个问题呢?
不难发现这些都是动物,既然都是动物,就说明他们存在很多共同点。比如他们都有姓名、年龄等,同时他们都会有吃这个行为。
因此,我们可以将这些共有属性直接抽离出来构成一个单独的类Animal
。然后让上面的三个类继承Animal
类。
继承的结果就是子类中拥有了父类的变量和方法。
语法如下所示:
class 子类名 extends 父类名{
}
那么刚刚的代码就能够改为下面的形式:
class Animal {
public String name;
public int age;
public void eat(){
System.out.println(this.name + "在吃。");
};
}
class Dog extends Animal{
public String color;
public void run(){
System.out.println("狗在跑。");
}
}
class Cat extends Animal{
public void jump(){
System.out.println("猫在跳。");
}
}
class Fish extends Animal{
public void swim(){
System.out.println("鱼在游泳。");
}
}
作出上述改动后,我们的代码就变得更加简洁清楚了,同时代码的复用性也大大提高了。
class A{
public int a = 111;
}
class B extends A{
public int a = 222;
public void test(){
System.out.println(a);
}
public static void main(String[] args) {
B b1 = new B();
b1.test();
}
}
当我们的子类和父类出现了相同的变量和方法的时候,当我们直接调用这些重复的变量时,是调用子类还是父类呢?
我们运行上述代码:
最终打印的结果是子类中的变量。
因此,我们的变量(方法)的调用是符合就近原则的。
子类中的方法肯定优先找子类的变量和方法,子类中没有再去父类中找。
如果我们不想要就近原则,我们就想在子类方法中调用父类中的变量,此时我们就需要用super
关键字了。
我们只需要在方法调用和变量名前面加上super.
即可。
因此,观察下面的代码:
class A{
public int a = 111;
}
class B extends A{
public int a = 222;
public void test(){
System.out.println(super.a);
}
public static void main(String[] args) {
B b1 = new B();
b1.test();
}
}
观察终端中的结果,我们就会发现成功打印出了父类中的变量。
在父类和子类没有重名的情况下,我们能不能用this去访问父类中的变量呢?如果没看懂这句话的话,我们可以看下面这段代码:
class A{
public int a = 1;
public int b = 2;
}
class B extends A{
public int c = 3;
public int d = 4;
public void test(){
System.out.println(this.a);
System.out.println(this.b);
System.out.println(this.c);
System.out.println(this.d);
}
public static void main(String[] args) {
B b1 = new B();
b1.test();
}
}
在上面的代码中,我们发现,a
和b
是父类中的变量,但我们却用了关键字this
。这样写会不会报错呢?
答案是没有报错,并且成功引用了。通过这个例子向大家介绍一下this
和super
的使用范围。
因此,当某个变量(方法)父类子类中没有重复时,this也可以调用父类中的变量(方法)。当某个变量(方法)父类子类重复时,this就只能调用子类中的了。
父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
当我们不继承的时候,编译器提供的构造方法如下:
public B(){
}
但是当我们继承了父类A后,编译器提供的构造方法如下:
public B(){
super();
}
如果我们重写构造方法的话,我们也必须在子类构造方法中的第一行调用父类构造方法,否则会出现报错。比如我们可以这样写:
class A{
public int a = 1;
public int b = 2;
public A(int a, int b){
this.a = a;
this.b = b;
}
}
class B extends A{
public int c = 3;
public int d = 4;
public B(int a, int b, int c, int d){
super(a, b);
this.b = b;
this.d = d;
}
}
我们在类中,直接写一个{}
,此时就构成了实例代码块,如下图所示:
class A{
public int a = 1;
public int b = 2;
{
//实例代码块
}
public A(int a, int b){
this.a = a;
this.b = b;
}
}
实例代码块在对象创建时执行,并且在构造方法执行之前执行。
静态代码块就是在实例代码块前面写一个static
关键字。
class A{
public int a = 1;
public int b = 2;
static{
//静态代码块
}
public A(int a, int b){
this.a = a;
this.b = b;
}
}
静态代码块只会执行一次,并且执行顺序在实例代码块、构造方法之前。
如果我们父类和子类中都包含代码块,那此时的执行顺序又是怎样的呢?
class A{
public int a = 1;
public int b = 2;
static{
System.out.println("父类静态代码块。");
}
{
System.out.println("父类实例代码块。");
}
public A(){
System.out.println("父类构造方法。");
}
}
class B extends A{
public int c = 3;
public int d = 4;
{
System.out.println("子类实例代码块。");
}
static{
System.out.println("子类静态代码块。");
}
public B(){
super();
System.out.println("子类构造方法。");
}
public static void main(String[] args) {
B b = new B();
}
}
上述代码的执行结果如下:
说明此时我们是先执行父类静态方法,再执行子类静态方法。然后执行父类的实例代码块和构造方法,最终执行子类的实例代码块和构造方法。
final关键字修饰变量后,该变量就变成了常量
。变成常量后,说明这个常量的数值无法在后续的调用过程中修改。
例如:
public final int a;
final修饰的方法是无法重写的。因此,当父类方法加了final
关键字后,子类继承后,无法重写该方法。
public final void test(){
}
final修饰的类无法被继承。
final class A{
}