1.Java中实现继承的关键字extends
extends 本意为拓展、扩展、扩大;在java 中作为关键字,表示继承的意思。可以理解为在父类的基础上进一步完善,进一步发扬光大。编程世界里的继承和现实世界的继承非常相似,编程世界里的继承不能继承父类的私有属性和方法,同样的现实世界里我们能够继承父母的财富,但不能继承他们的私有之物,比如生活用品……
1.1 关键字使用语法
语法1:访问修饰符 class 子类名 extends 父类名{
注意:1.此处的修饰符只能是public或默认的(即不写修饰符)
2.java类的继承只有单继承,类不存在多继承
3.同包中,子类中能使用父类中除了private修饰的所有属性和方法,不同包时,能够使用protected和public修饰的方法和属性,具体可以看Java中访问权限修饰符。
4.除了Object类以外,任何类至少有一个父类,即Object类。一个类没有明确extends方式继承一个类时,编译器会自动给该类添加一个Object类。如果明确指定一个继承父类,则通过父类间接继承Object类。
//属性
//方法
}
1.1.1 代码实例
父类的代码如下:
public class Parent {
//父类中存在的属性
private String name = "父类属性";
public String getName() {
return name;
}
}
子类代码如下:
public class Child extends Parent {
//子类中存在与父类中的同名属性
public String name= "child的属性";
@Override
public String getName() {
return super.getName();
}
}
语法2:权限访问修饰符 interface 接口名 extends 父接口1,父接口2……{
注意:java中类不存在多继承,但是接口是存在多继承的,可以通过关键字extends实现多继承。
}
1.1.2 代码实例
父接口:
//父接口1
public interface ParentInterface1 {
//抽象方法定义
}
//父接口2
public interface ParentInterface2 {
//抽象方法定义
}
子接口:
//子接口定义
public interface ChildInterface extends ParentInterface1 , ParentInterface2 {
//内部接口定义
}
2 继承中属性和方法的调用过程
在上文已经介绍过,Java中的继承是继承了父类除了private属性修饰以外的所有属性和方法。那如果父类的属性和方法与子类的属性和方法重名之后,应该调用哪一个呢?下面将分别进行分析:
2.1 方法同名
聊继承,必然会谈重写(Override),何为重写?重写就是将父类的方法,无论是普通方法还是抽象方法都在子类中重写实现,实现的代码根据自己业务需求进行。
2.1.1 父类有方法和属性,子类无方法和属性
父类代码:
public class Parent {
/*
此处为方便调用使用public修饰
测试属性
*/
public String name = "父类的名称Parent";
/*
测试方法
*/
public void testMethod(){
System.out.println("父类的测试方法");
}
}
子类代码:
public class Child extends Parent {
}
测试代码:
public class Test {
public static void main(String[] args) {
/*
第一种:测试子类是否真正实现了父类的所有属性和方法
*/
Child child = new Child();
//子类是否继承父类属性
System.out.println(child.name);
//子类是否继承父类方法
child.testMethod();
}
}
运行结果:
可以看出,子类是继承了父类的所有方法和属性。
2.1.2 父类有方法和属性,子类有相同方法
子类代码:
public class Child extends Parent {
/**
* 重写父类的方法
* 如果@Override去掉之后,不叫方法重写,只是在子类中定义了一个和父类方法同名的方法,参数也相同而已。当我们通常也会认为此方法是重写方法。
* @Override是告诉编译器此方法是重写父类的方法,如果方法的名和参数列表修改会报编译时异常
* 返回权限必须大于或等于父类的访问权限
* 返回值类型必须小于或等于父类的访问权限
*
*/
@Override
public void testMethod() {
//这是在调用父类的同名方法
//super.testMethod();
System.out.println("子类的方法");
}
}
执行结果:
public class Test {
public static void main(String[] args) {
/*
第二种:测试子类是调用父类还是自身的方法
*/
Child child = new Child();
child.testMethod();//结果:子类的方法
}
}
从执行结果来看,子类执行的是子类自身的方法,而没有执行父类的方法。其内部到底如何调用的呢?
2.1.3 代码底层执行过程
下图是执行代码的真实过程:
从上图可知,子类对象先在自己的空间查找是否有此方法或属性,如果没有才到父类对象的存储空间查找,而且JVM采用的是就近原则查找,因此最终结果是调用的是子类对象的方法。
2.2 属性同名
父类代码:
public class Parent {
//父类中存在的属性
private String name = "父类属性";
public String getName() {
return name;
}
}
子类代码:
public class Child extends Parent {
//子类中存在与父类中的同名属性
public String name= "child的属性";
@Override
public String getName() {
return super.getName();
}
}
测试代码:
public class TestInherit {
public static void main(String[] args) {
/**
* 当父类和子类属性同名时,父类提供访问父类属性的方法,而子类未重写该方法时,通过多态的方式调用的是父类的属性
* 本质:父类方法在调用父类属性语法是:this.父类属性,而非子类属性,子类属性在父类中是无法获取到的
*/
Parent p = new Child();
System.out.println(p.getName());//父类属性
/**
* 父类和子类属性同名时,父类提供访问父类属性的方法,而子类重写该方法时,通过多态的方式调用的是子类的属性
*子类重写该方法时,两种情况:
* 1.子类直接调用自己的属性
* 2.子类通过super关键字调用父类的属性,这些属性依然遵守访问权限修饰符的约束
*/
//第一种情况
System.out.println(p.getName());//child的属性
//第二种情况
System.out.println(p.getName());//父类属性
/**
* 当父类拥有属性,子类没有该属性,当重写了父类方法
*/
System.out.println(p.getName());//父类属性
}
}
3.总结:
属性同名的调用过程和调用方法的过程基本相同,都是先在子类存储空间中查找是否有该属性,如果有,直接返回,不在继续寻找,如果没有才会继续查找父类,直到所有父类都没找到的时候,才会报异常。