一:继承的概念及语法
(1)什么是继承
继承:是指能够直接获得已有的性质和特征,而不必重复定义它们。在面向对象的软件技术中,继承是子类自动地共享父类中定义的数据和方法的机制。子类继承父类后,即实现了代码的复用,又使子类可以在父类的基础上进行扩展
例如下面有两个类,分别是Dog
和Cat
,用于描述自然界中的狗和猫
class Dog{
public String name;
public int age;
public float weight;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void bark(){
System.out.println(name + "正在吠叫");
}
}
//猫
class Cat{
public String name;
public int age;
public float weight;
public void eat() {
System.out.println(name + "正在吃饭");
}
public void meow(){
System.out.println(name + "正在喵喵");
}
}
从代码中可以看到,狗和猫之间存在一些共性
- 共同的属性:
name
、age
、weight
- 共同的行为:
eat()
所以,继承的作用就体现出来了,也即抽取共性。狗和猫本质都是动物,所以我们可以把它们共同的属性和方法给抽取出来,形成一个新的类Animal
,此时Dog
和Cat
之间就存在了父子类关系
- 父类(基类):
Animal
- 子类(派生类):
Dog
和Cat
(2)Java继承语法
继承语法:在Java中如果要表示类之间的继承关系,需要借助extends
关键字,格式如下
修饰符 class 子类 extends 父类{
....
}
接着,对(1)中所述情况用代码设计如下
class Animal{
public String name;
public int age;
public double weight;
public void eat(){
System.out.println(name + "正在吃饭");
}
}
//狗
class Dog extends Animal{
public void bark(){
System.out.println(name + "正在吠叫");
}
}
//猫
class Cat extends Animal{
public void meow(){
System.out.println(name + "正在喵喵");
}
}
public class TestDemo {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "狗蛋";
dog.age = 5;
dog.weight = 34.3;
Cat cat = new Cat();
cat.name = "花喵";
cat.age = 4;
cat.weight = 21.2;
System.out.println(dog.name);
dog.eat();
dog.bark();
System.out.println(cat.name);
cat.eat();
cat.meow();
}
}
二:成员访问
(1)子类访问父类的成员变量
子类和父类不存在同名成员变量:这种情况容易理解,没什么问题
class Base{
public int a = 1;
public int b = 2;
}
class Sub extends Base{
public int c = 3;
}
public class TestDemo2 {
public static void main(String[] args) {
Sub sub = new Sub();
System.out.println("sub_a form Base:" + sub.a);
System.out.println("sub_b form Base:" + sub.b);
System.out.println("sub_c:" + sub.c);
}
}
子类和父类存在同名成员变量:访问时遵循就近原则,自己有就访问自己的,没有则会在父类中找寻
class Base{
public int a = 1;
public int b = 2;
public int c = 3;
}
class Sub extends Base{
public int a = 11; //与父类中的a同名且类型相同
public String b = "Hello"; //与父类中成员b同名,但类型不同
}
public class TestDemo2 {
public static void main(String[] args) {
Sub sub = new Sub();
System.out.println(sub.a);//访问子类中的a
System.out.println(sub.b);//访问子类中的b
System.out.println(sub.c);//访问父类中的c
}
}
(2)子类访问父类的成员方法
子类和父类不存在同名成员变量:这种情况容易理解,没什么问题
class Base{
public void base_method(){
System.out.println("父类中的方法");
}
}
class Sub extends Base{
public void sub_method(){
System.out.println("子类中的方法");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Sub sub = new Sub();
sub.base_method();//从父类继承的方法
sub.sub_method();//子类自己的方法
}
}
子类和父类存在同名成员变量:遵循就近原则,子类有就访问子类的,没有就用父类的。同时如果父类和子类同名方法构成重载,那么它就会根据调用方法所传递的参数选择合适的方法调用
class Base{
public void method1(){
System.out.println("父类中的方法1");
}
public void method2(){
System.out.println("父类中的方法2");
}
}
class Sub extends Base{
//该方法与父类同名方法构成重载
public void method1(int a){
System.out.println("子类中的方法1");
}
public void method2(){
System.out.println("子类中的方法2");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Sub sub = new Sub();
sub.method1();//没有传参,访问父类方法
sub.method1(10);//传参,访问子类方法
sub.method2();//子类有,则直接访问子类的
}
}
三:super关键字
(1)super关键字
A:作用一:访问父类同名成员
从前文可以看到,子类是不能够直接访问父类的同名成员的。而在Java中,可以使用super
关键字解决
- 注意: 仅仅可以在非静态方法中使用
class Base{
int a;
int b;
public void method1(){
System.out.println("父类中的方法1");
}
public void method2(){
System.out.println("父类中的方法2");
}
}
class Sub extends Base{
int a;//与父类中的变量a同名且类型相同
String b;//与父类中的变量b同名但类型不同
//该方法与父类中的同名方法构成重载
public void method1(int a){
System.out.println("子类中的方法1");
}
//该方法与父类中的同名方法构成重写
public void method2(){
System.out.println("子类中的方法2");
}
public void test(){
//同名成员变量直接访问时,都是子类的
a = 100;
b = "Hello";
//访问父类成员变量是需要借助super关键字
super.a = 300;
super.b = 400;
//对于父类和子类中构成重载的方法,通过参数就可以区别他们
method1();//父类的
method1(10);//子类的
//如果要访问被重写的父类方法,则需要借助super关键字
method2();//由于被重写,所以访问的永远是子类的
super.method2();//父类的
}
}
public class TestDemo2 {
public static void main(String[] args) {
Sub sub = new Sub();
sub.test();
}
}
B:作用二:调用父类构造方法
有如下代码,子类Sub1
和Sub2
中的变量a和b
继承自父类Base
,同时它们各自拥有自己的变量c
。当给父类Base
加入构造函数后却发现编译器报错
这是因为:构造子类对象时,先会执行父类的构造方法,然后再执行子类的构造方法。所以对于子类对象,它必须先调用父类的构造方法,这正是super
关键字的第二个作用
class Base{
int a;
int b;
public Base(int a, int b){
this.a = a;
this.b = b;
}
}
class Sub1 extends Base{
int c;
public Sub1(int a, int b, int c){
super(a, b);
this.c = c;
}
}
class Sub2 extends Base{
int c;
public Sub2(int a, int b, int c){
super(a, b);
this.c = c;
}
}
public class TestDemo2 {
public static void main(String[] args) {
Sub1 sub1 = new Sub1(1, 2, 3);
Sub1 sub2 = new Sub1(4, 5, 3);
System.out.println(sub1.a);
System.out.println(sub1.b);
System.out.println(sub1.c);
System.out.println(sub2.a);
System.out.println(sub2.b);
System.out.println(sub2.c);
}
}
需要注意:
- 如果父类使用的是默认构造方法或显示定义了无参的构造方法,那么子类构造方法第一行会默认隐含super调用
super
只能在子类构造方法第一行出现,且只能出现一次,并且不可以和this同时出现
(2)super和this对比
相同点
- 都可以在成员方法(仅限于非静态方法)中用来访问成员变量或调用其他成员方法(非静态)
- 都可以作为构造方法的第一条语句(必须是),并且不能同时存在
不同点
this
是当前对象的引用,用来访问本类的成员;super
是子类从父类中继承下来的部分成员的引用,用来访问从父类的成员this
是非静态成员方法的一个隐藏参数;super
不是隐藏参数- 在构造方法中,
this()
用于调用本类构造方法;super()
用于调用父类构造方法。二者不能在构造方法中不能同时出现 - 构造方法中一定会存在
super()
调用,但this()
如果用户不写则不会默认存在
评论区