继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承是面向对象最显著的一个特征
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并扩展新的能力.
Java继承是会用已存在的类的定义作为基础建立新类的技术
新类的定义可以增加新的数据或者新的功能,也可以使用父类的功能,但不能选择性的继承父类(超类/基类)
这种继承使得复用以前的代码非常容易,能够大大的缩短开发的周期,降低开发费用.
继承有什么用?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
被继承的类叫父类,继承的类叫子类,比如类A继承类B,则B为父类,A为子类
继承的关键字为extends
语法:
class A extends B{
…
}
类的继承格式:
class 父类 { }
class 子类 extends 父类 { }
继承的类型:需要注意的是 Java 不支持多继承,但支持多重继承。
继承的好处:
(1)提高类代码的复用性
(2)提高了代码的维护性
(3)使得类和类产生了关系,是多态的前提(它也是继承的一个弊端,类的耦合性提高了)
继承的特性
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法,即重写父类方法。
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
特点
使用extends关键字来表示继承关系
相当于子类把父类的功能复制了一份
Java只支持单继承
继承可以传递(爷爷/儿子/孙子这样的关系)
父类的私有成员也会被继承,但由于是私有的不可见,所以子类不能使用父类的私有资源
继承多用于功能的修改,子类可以在拥有父类功能的同时,进行功能拓展
像是is a的关系
super
可以通过这个关键字使用父类的内容,Super代表的是父类的一个引用对象
注意:在构造方法里,出现的调用位置必须是第一行
描述
有父类Base,内部定义了x、y属性。有子类Sub,继承自父类Base。子类新增了一个z属性,并且定义了calculate方法,在此方法内计算了父类和子类中x、y、z属性三者的乘积。请补全子类构造方法的初始化逻辑,使得该计算逻辑能够正确执行。
输入描述:
三个整数:x, y, z
输出描述:
三个整数的乘积:xyz
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
int x = scanner.nextInt();
int y = scanner.nextInt();
int z = scanner.nextInt();
Sub sub = new Sub(x, y, z);
System.out.println(sub.calculate());
}
}
}
class Base {
private int x;
private int y;
public Base(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
class Sub extends Base {
private int z;
public Sub(int x, int y, int z) {
//write your code here
}
public int getZ() {
return z;
}
public int calculate() {
return super.getX() * super.getY() * this.getZ();
}
答案解析:
我们的任务只是完善子类的构造方法。首先因为父类的成员变量都是private类型的,无法直接访问,因此赋初值只能通过super调用父类的构造方法,然后变量z是子类特有的,因此可以自己给自己赋值。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
int x = scanner.nextInt();
int y = scanner.nextInt();
int z = scanner.nextInt();
Sub sub = new Sub(x, y, z);
System.out.println(sub.calculate());
}
}
}
class Base {
private int x;
private int y;
public Base(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
/**
* 我们的任务只是完善子类的构造方法。首先因为父类的成员变量都是private类型的,无法直接访问,
* 因此赋初值只能通过super调用父类的构造方法,然后变量z是子类特有的,因此可以自己给自己赋值。
*/
class Sub extends Base {
private int z;
public Sub(int x, int y, int z) { //子类构造方法
super(x,y); //调用父类构造方法
this.z=z;
//write your code here
}
public int getZ() {
return z;
}
public int calculate() {
return super.getX() * super.getY() * this.getZ();
}
运行结果是
1
2
3
6
描述
在父类Base中定义了计算方法calculate(),该方法用于计算两个数的乘积(X*Y)。请在子类Sub中重写该方法,将计算逻辑由乘法改为除法(X/Y)。注意,当分母为0时输出“Error”。
输入描述:
两个整数
输出描述:
两个整数的商(int类型,不考虑小数情况)
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
int x = scanner.nextInt();
int y = scanner.nextInt();
Sub sub = new Sub(x, y);
sub.calculate();
}
}
}
class Base {
private int x;
private int y;
public Base(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void calculate() {
System.out.println(getX() * getY());
}
}
class Sub extends Base {
//write your code here......
}
解析答案
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
int x = scanner.nextInt();
int y = scanner.nextInt();
System.out.println("除法");
Sub sub = new Sub(x, y);
sub.calculate();
System.out.println("乘法");
Base base= new Base(x, y);
base.calculate();
}
}
}
class Base {
private int x;
private int y;
public Base(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void calculate() {
System.out.println(getX() * getY());
}
}
class Sub extends Base {
public Sub(int x, int y) {
super(x, y);
}
//write your code here......
public void calculate() {
if (getY() == 0) {
System.out.println("Error");
} else {
System.out.println(getX() / getY());
}
}
结果为
5
10
除法
0
乘法
50
注意:若同时用到 extends 和 implements 的时候,extends 必须放在 implements 关键字之前。
class 子类名 extends 父类名 implenments 接口名
{...
}
Java只能单继承,接口为了弥补单继承的不足就出现了。
继承解决的是代码复用以及对象的关系,接口解决的是解耦.
1.在类的声明中,通过关键字extends来创建一个类的子类。一个类通过关键字implements声明自己使用一个或者多个接口。 extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法; implements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用 |
2.extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,JAVA中不支持多重继承,但是可以用接口 来实现,这样就要用到implements,继承只能继承一个类,但implements可以实现多个接口,用逗号分开就行了 |
比如:EG
class A extends B implements C,D,E
implements是一个类实现一个接口用的关键字,他是用来实现接口中定义的抽象方法。比如:people是一个接口,他里面有say这个方法。
public interface people(){ public say();}
但是接口没有方法体。只能通过一个具体的类去实现其中的方法体。比如chinese这个类,就实现了people这个接口。
public class chinese implements people{ public say() {System.out.println("你好!");}}
接口实现的注意点:
a.实现一个接口就是要实现该接口的所有的方法(抽象类除外)。
b.接口中的方法都是抽象的。
c.多个无关的类可以实现同一个接口,一个类可以实现多个无关的接口。
DEMO
这里有一个游戏,人猿泰山。 主角是一个单独的类,这里我们主要用怪物说明接口的用法: 怪物有很多种, 按地域分:有的在天上飞,有的在地上跑,有的在水里游 按攻击方式分:有的能近距离物理攻击,有的能远距离射击
假设游戏里需要这样的几 种怪——
野狗:地上移动,近距离攻击
黑熊:地上移动,近/远距离攻击
秃鹫:地上/天上移动,远距离攻击
食人鱼: 水中移动,近距离攻击
鳄鱼:地上/水中移动,近距离攻击
显然,如果我们将每一种怪物定义为一个类,那就不是面向对象的程序开 发了,我们应当使用接口:
interface OnEarth{//陆地接口
int earthSpeed;//陆地移动速度
void earthMove();//陆地移动方法
}
interface OnWater{//水中接口
int waterSpeed;//水中移动速度
void waterMove();//水中移动方法
}
interface OnAir{//空中接口
int airSpeed;//水中移动速度
void airMove();//水中移动方法
}
interface NearAttack{//近距离攻击接口
int nearAttackPower;//近距离攻击力
void nearAttack();//近距离攻击方法
}
interface FarAttack{//远距离攻击接口
int farAttackPower;//远距离攻击力
void farAttack();//远距离攻击方法
}
这样一来,根据需求,我们可以选择性的继承接口:
class Tyke implements OnEarth, NearAttack{//野狗类
void earthMove(){//实现继承的方法1
}
void nearAttack(){//实现继承的方法2
}
}
class BlackBear implements OnEarth, NearAttack, FarAttack{//黑熊类
void earthMove(){//实现继承的方法1
}
void nearAttack(){//实现继承的方法2
}
void farAttack(){//实现继承的方法3
}
}
class Vulture implements OnEarth, OnAir, FarAttack{//秃鹫类
void earthMove(){//实现继承的方法1
}
void airMove(){//实现继承的方法2
}
void farAttack(){//实现继承的方法3
}
}
class ManeatFish implements OnWater, NearAttack{//食人鱼类
void waterMove(){//实现继承的方法1
}
void nearAttack(){//实现继承的方法2
}
}
class Crocodile implements OnEarth, OnWater, NearAttack{//鳄鱼类
void earthMove(){//实现继承的方法1
}
void waterMove(){//实现继承的方法2
}
void nearAttack(){//实现继承的方法3
}
}
重写的前提:需要有继承关系,比如B类继承A类,则B类可以重写A类中的方法!(子类重写父类中的方法)
注意点:
子类所要重写的方法名必须与父类的方法名相同
方法的参数列表也必须一致
方法体不同(就是方法内部的功能不同,相同了就不用重写了)
如A类中有一个无参的方法print()
然后有一个B类继承了A类,并且重写了A类的方法,那么B类所重写方法的方法名也必须为print(),也必须无参
子类可以继承父类的所有属性和方法,子类可以通过super关键字来调用父类的属性和方法
Demo:
class Person{
protected int id;
protected int age;
public Person(){}
public Person(int id, int age) {
this.id = id;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Teacher extends Person{
public Teacher(){}
public Teacher(int id, int age)
{
super(id,age); //调用父类构造器
}
}
public class Demo{
public static void main(String[] args) {
Teacher teacher = new Teacher(1,2);
//子类可以直接继承父类的方法
System.out.println(teacher.getId());
System.out.println(teacher.getAge());
}
}
运行结果:
运行结果:
1
2
注意:
在java中一个父类可以有多个子类,但一个子类只可以有一个父类
父类类型的引用变量可以指向子类对象,即以下这条语句是合法的
Person person = new Teacher();
子类类型的引用变量不能执行父类对象,即以下这条语句是非法的:
//本条语句是非法的
Teacher teacher = new Person();
Jdk提供了一个类叫Object
Java里所有类默认继承Object类
方法重写:
子类可以重写(也叫覆盖)父类的方法,重写是指在子类中有一个和父类方法名相同且参数表也完全相同的方法,当子类重写父类方法后,子类对象调用方法时会调用自己类里重写的方法
class Person{
protected int id;
protected int age;
public Person(){}
public Person(int id, int age) {
this.id = id;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Teacher extends Person{
public Teacher(){}
public Teacher(int id, int age)
{
super(id,age); //调用父类构造器
}
@Override
public int getAge() {
return 18;
}
}
public class Demo{
public static void main(String[] args) {
Person teacher = new Teacher(1,2);
//子类可以直接继承父类的方法
System.out.println(teacher.getId());
System.out.println(teacher.getAge());
}
}
解释:
在上面的例子中,Teacher类重写了父类Person的getAge方法,导致Teacher的getAge方法永远返回18(永远18岁,yeah!),然后在main方法中Perosn类型的引用变量teacher指向了Teacher类型的对象,而在调用teacher.getAge()时返回的是18,由此可见在调用方法时不是看引用变量是什么类型,而是看引用变量引用的对象是什么类型
部分内容来源:1