JavaSE:继承

在谈继承之前,我们先观察下面这个代码: 

//定义一个猫类
class Cat {
    public String name;
    public int age;
    public float weigth;

    public void eat(){
        System.out.println(this.name+"正在吃饭");
    }
    public void mimi(){
        System.out.println(this.name+"正在咪咪叫");
    }
}
//定义一个狗类
class Dog {
    public String name;
    public int age;
    public float weigth;

    public void eat(){
        System.out.println(this.name+"正在吃饭");
    }
    public void bark(){
        System.out.println(this.name+"正在狗叫");
    }

}
//测试类
public class Test{
 public static void main(String[] args) {
  Dog dog=new Dog();//定义一个狗对象
  dog.name="旺财";
  dog.eat();
  dog.bark();
  Cat cat=new Cat();//定义一个猫对象
  cat.name="咪咪";
  cat.eat();
  cat.mimi();
 }
}

上面代码中发现,在猫类和狗类中,有大量代码存在重复:

 public String name;
    public int age;
    public float weigth;

    public void eat(){
        System.out.println(this.name+"正在吃饭");
    }

那能不能将这些重复的部分进行抽取呢?面向对象的思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。

继承概念:

继承(inheritance):是面向对象程序设计使代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展和增加新功能。一个类继承另一个类,这样产生的类叫子类/派生类,被继承的类叫父类/基类/超类,继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用

例如:上面的猫类和狗类中,可以将共性进行抽取。然后采用继承的思想来达到共用。

//定义一个Animal类
class Animal(){
 public String name;
    public int age;
    public float weigth;

    public void eat(){
        System.out.println(this.name+"正在吃饭");
    }
}

继承的语法:

在猫类和狗类中继承Animal类,在java中要表示继承的关系,需要借助extends关键字,具体如下:

修饰符 class 子类 extends 父类{

}

 对猫类和狗类重新进行设计:让猫类和狗类都继承Animal类

//猫类
class Cat extends Animal{
    public void mimi(){
        System.out.println(this.name+"正在咪咪叫");
    }
}
//狗类
class Dog extends Animals{ 
    public void bark(){
        System.out.println(this.name+"正在狗叫");
    }

}

 1.子类会将父类中的成员变量或者成员方法继承到子类中了

 2.子类继承父类之后,必须要新添加自己特有的成员,体现出与父类的不同,否则就没有必要继承

父类成员访问:

1.子类中访问父类的成员变量:

1.1子类和父类不存在同名字的成员变量:
public class B {
    public int a=1;
    public int b=2;
}

public class C extends B{
    public int c=3;
    public void func(){
        System.out.println(a);//访问从父类继承下来的a
        System.out.println(b);//访问从父类继承下来的b
        System.out.println(c);//访问子类自己有的c
    }
}

在子类中也可以通过this调用来访问父类的成员变量。

public class B {
    public int a=1;
    public int b=2;
}

public class C extends B{
    public int c=3;
    public void func(){
        System.out.println(this.a);//访问从父类继承下来的a
        System.out.println(this.b);//访问从父类继承下来的b
        System.out.println(this.c);//访问子类自己有的c
    }
}

这样写代码和上面效果一样。 

1.2子类和父类存在同名的成员变量:
public class B {
    public int a=1;
    public int b=2;
}

public class C extends B{
    public int c=3;
    public int a=100;//定义一个与父类同名的成员变量
    public void func(){
        System.out.println(a);//不知道是调用哪一个a
        System.out.println(b);//访问从父类继承下来的b
        System.out.println(c);//访问子类自己有的c
    }
}

我们在子类访问变量a时,不知道调用的哪一个a,但从运行结果:来看,这里调用的a是子类自己的a。

所以当父类和子类有同名的成员变量时:优先访问子类的。

但有的人想问,如果想要先调用父类的a呢?这里就要用到一个关键字super

格式:super . 父类的成员变量

super可以用来访问子类继承过来父类的成员

具体下面会讲到。

 

 成员变量访问遵循就近原则,子类有优先访问自己的,如果没有则在父类中找。

 2.子类中访问父类的成员方法:

2.1子类和父类不存在同名的成员方法:
//父类
public class B {
   public void methodA(){
       System.out.println("父类的methodA方法...");
   }
}
//子类
public class C extends B{
    public void methodB(){
        System.out.println("子类的methodB方法...");
    }
}
//测试类
class Test {
  public static void main(String[] args) {
   C c=new C();
   c.methodA();
   c.methodB();
  }
 }

 调用成员方法与调用成员变量的操作一样。

 子类和父类成员方法没有同名的情况下,在子类方法中同对象引用访问方法时,优先调用子类自己的,若子类没有则在父类中寻找,若父类也没有则会报错

2.2子类和父类存在同名的成员方法:
2.1子类和父类存在同名的成员方法(方法里的参数不一样):
//父类
public class B {
   public void methodA(char a){
       System.out.println("父类的methodA(char a)方法...");
   }
}
//子类
public class C extends B{
    public void methodA(){
        System.out.println("子类的methodA()方法...");
    }
}
//测试类
 class Test {
  public static void main(String[] args) {
   C c=new C();
   c.methodA();
   c.methodA('a');
  }
 }

父类的methodA(char a)方法和 子类的methodA( )方法,因为方法名相同,方法参数不同。所以构成了方法的重载

根据调用的方法以及传递的参数访问合适的方法,若没有找到该方法,则会报错。

2.2子类和父类存在同名的成员方法(方法里的参数一样):
//父类
public class B {
   public void methodA(){
       System.out.println("父类的methodA()方法...");
   }
}
//子类
public class C extends B{
    public void methodA(){
        System.out.println("子类的methodA()方法...");
    }
}
//测试类
 class Test {
  public static void main(String[] args) {
   C c=new C();
   c.methodA();
  }
 }

在测试类中调用methodA的方法,默认调用的是子类的methodA的方法。

如果想调用父类的methodA的方法就要用super关键字。super.methodA()

//父类
public class B {
   public void methodA(){
       System.out.println("父类的methodA()方法...");
   }
}
//子类
public class C extends B{
    public void methodA(){
        System.out.println("子类的methodA()方法...");
    }
//通过super来调用父类的methodA的方法
 public void methoda(){
        super.methodA();
}
//测试类
 class Test {
  public static void main(String[] args) {
   C c=new C();
   c.methodA();
  }
 }

 super关键字:

有时候在一些场景下,子类和父类可能存在同名的成员变量或者成员方法,如果想从子类中访问父类中同名的成员变量或者成员方法,直接访问是无法做到的,所以Java中提供了super关键字,它主要的作用:在子类中访问父类的成员变量或者成员方法。

在子类中:

1.super.父类成员变量

2.super.父类成员方法

可以通过上面两种方式来访问父类中的成员。

super只能指向子类的父类,不能指向子类的父类的父类。

super还有第三种方法:

如果在父类中有构造方法,当子类继承父类之后,子类需要显示的调用父类的构造方法,要先帮助父类的成员进行初始化。可以用super调用父类的构造方法进而对父类进行初始化

//父类
public class B {
    public String name;
    public int age;
//父类的构造方法
    public B(String name,int age){
        this.name=name;
        this.age=age;
    }
//子类
public class C extends B{
//子类显示调用父类的构造方法
   public C(String name,int age){
       super(name,age);//
    }
}
//测试类
class Test {
  public static void main(String[] args) {
   C c=new C("酷酷的森",19);
  }
 }

在使用super的时候,要注意:super在调用父类构造方法时,一定要在方法中的第一条语句,不然会出错。

1.super调用父类的构造方法时,必须在第一行。前面我们讲过this()调用构造方法时,也只能在第一行,所以super()和this()是不能共存的。

2.当没有提供任何的构造方法的时候,java中会提供默认的构造方法,如下:

//父类
public class B {
//默认提供的构造方法
    public B(){
}
    }
//子类
public class C extends B{
/默认提供的构造方法
    public C(){
    super();
}
}

子类对象中成员是由两部分组成,父类继承下来的以及子类新增加的部分,父子父子,肯定是先有父再有子,所以在构造子类的对象时,要先调用父类的构造方法,将父类继承下来的成员初始化完整,然后调用子类的构造方法,将子类自己新增加的成员初始化完整。 

 注意:

1.在public C方法中,你也可以省略super(),因为在若父类显式定义无参或者默认的构造方法时,在子类构造方法第一行默认有隐式的super()调用。调用基类的构造方法。

2.如果父类构造方法带有参数,此时需要用户显式定义构造方法,并在子类构造方法中选用合适的父类构造方法调用,否则编译出错。

3.在子类构造方法中调用父类构造方法,super()必须在子类构造函数第一条语句

4.this()和super()不能同时出现在同一个构造方法里面。

但是只要你写了一个构造方法,这个默认构造方法就不会提供。

 super和this:

相同点:

1.都是java中的关键字

2.都只能在非静态方法中使用,用来访问非静态的成员或方法

3.在构造方法中使用时,都只能在该方法中的第一条语句,且super()和this()不能同时存在。

不同点:

1.this是指当前对象的引用, 当前对象即调用实例方法的对象。super是在子类对象中调用从父类继承下来的那部分成员的引用

2.在非静态成员方法中,this调用的是本类的方法和属性,而super用来访问从父类继承下来的方法和属性。

3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现

4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

再谈代码块:

之前讲过静态代码块,实例代码块,构造方法在没有谈继承的时候所执行的顺序。可以在(javaSE:对象和类(2))中复习

现在讲了继承,它们又是按照什么顺序进行执行的呢?

//父类
public class B {
    public String name;
    public int age;
    //父类构造方法
    public B(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("父类构造方法执行了....");
    }
    //父类静态代码块
    static{
        System.out.println("父类静态代码块执行了....");
    }
    //父类实例代码块
    {
        System.out.println("父类实例代码块执行了....");
    }
}
//子类
public class C extends B{
    //实例代码块
    {
        System.out.println("子类实例代码块执行了.....");
    }
    //构造方法
    public C(String name,int age){
        super(name,age);
        System.out.println("子类构造方法执行了.....");
    }
    //子类静态代码块
    static{
        System.out.println("子类静态代码块执行了....");
    }
//测试类
class Test {
     public static void main(String[] args) {
         C c=new C("酷酷的森",19);
     }
 }

运行结果:

可知:先执行父类和子类的静态,再执行父类的实例和构造,最后执行子类的实例和构造

 如果改一下代码,增加一个对象,又会有什么不同呢?

//父类
public class B {
    public String name;
    public int age;
    //父类构造方法
    public B(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("父类构造方法执行了....");
    }
    //父类静态代码块
    static{
        System.out.println("父类静态代码块执行了....");
    }
    //父类实例代码块
    {
        System.out.println("父类实例代码块执行了....");
    }
}
//子类
public class C extends B{
    //实例代码块
    {
        System.out.println("子类实例代码块执行了.....");
    }
    //构造方法
    public C(String name,int age){
        super(name,age);
        System.out.println("子类构造方法执行了.....");
    }
    //子类静态代码块
    static{
        System.out.println("子类静态代码块执行了....");
    }
//测试类
 class Test {
     public static void main(String[] args) {
         C c=new C("酷酷的森",19);
         System.out.println("============");
         C cc=new C("111111",19);
     }
 }

 运行结果:

可知:第二次实例化对象,父类和子类的静态代码块都不会执行。

访问限定符:

为了实现封装特性,java引入了限定访问操作符,主要限定:类或者类中的成员能否在类外或者其他包中使用。

 private修饰的成员,只能在当前类访问

default修饰的成员,只能在同一个包低下的类中访问

public修饰的成员,公共的,任何地方都能使用

protected修饰的成员,同一个包低下可以使用,但是不同包下,只有子类才能使用。

 继承方式:

1.单继承:

public class A{
....
}
public B extends A{
....
}

2.多层继承:

public class A{
....
}
public class B extends A{
....
}
public class C extends B{
....
}

但是多继承,也不能无限继承下去。一般不超过3层的继承关系

3.不同类继承同一个类:

public class A{
....
}
public class B extends A{
....
}
public class C extends A{
....
}

4.java是不支持多继承的:一个类继承有多个父类是不允许的

java不支持多继承,但是可以通过接口的修形式,支持多继承

 final关键字:

1.final 关键可以用来修饰变量、成员方法以及类。
2.final修饰的变量是常量,此时这个变量的值就不能被修改了

3.如果一个类不想被其他类继承,此时可以用关键字final来,修饰这个类,此时这个类就叫密封类,不允许被其他类继承。

final public class  Animal {
}
这个类因为被final修饰,所以不能被继承。

4.final修饰方法,这个方法就不能被重写。

在Java中,final关键字可以放在public关键字的前面或后面。它们的顺序可以互换,但是对于变量、方法和类的修饰,final关键字通常放在访问修饰符(如publicprivateprotected)的前面。

继承和组合:

组合和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段

继承是is-a的关系:比如:猫是动物,狗也是动物

组合是has-a的关系或者是 a part of (一部分)的关系:比如:汽车有发动机,轮子还有车载系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值