继承是面向对象的第二个基本特征,通过继承可以实现父子关系,以及代码的复用。
一、继承
Java的继承通过extends关键字来实现,实现继承的类称为子类,被继承的类被称为父类,有的也称为基类(超类)父类和子类的关系是一种特殊的关系。也可以认为父类是大类,而子类是小类。不过一般也可以称为子类是父类的扩展、延伸。
1、Java子类继承父类的语法格式为:
修饰符 class 子类的名字 extends 父类的名字{
//类体
}
例如:猫类继承动物类。
//动物类
public class Animal {
public void shout(){
System.out.println("会叫");
}
}
//猫类
public class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵");
}
}
2、继承的作用
(1)、描述类和类之间的关系
(2)、降低类和类之间的重复代码
3、extends关键字
Java中只支持单继承,每个Java类最多只能有一个父类。Java类体系中的根类是java.lang.Object,所有类都是Object的直接子类或间接子类。如果定义类时没有extends字句,则该类的父类默认为Object。
我们可以使用instanceof关键字来判断一个对象是否是指定类所创建的实例
例如,接着上面的例子
boolean flag = cat instanceof Cat;
System.out.println(flag); //true
boolean flag1 = animal instanceof Cat;
System.out.println(flag1); //false
二、方法重写
在继承中,子类可以定义和父类相同的名称且参数列表一致的函数,将这种函数称之为函数的重写,也就是子类中重新定义父类中已有的办法。
1、重写的要求
(1)、子类重写父类的方法时,方法的名称和参数列表必须和父类保持一致
(2)、子类重写方法的访问权限必须大于等于父类中被重写方法的访问权限,否则会报错。
(3)、子类中重写方法的返回值必须是父类中方法的返回值类型或该类型的子类。
(4)、子类中重写方法不能比父类中被重写方法产生更多的异常类型。
(5)、父类的改造函数是不能被子类继承的,因此不能在子类中重写。但是可以使用super关键字来访问父类中的改造函数
2、重写方法的调用顺序
(1)、子类中重写后的方法在方法体的第一行默认调用父类被重写的方法
(2)、子类的对象调用重写方法时依据就近原则优先调用子类重写后的方法
动物类(父类)
public class Animal {
public void shout(){
System.out.println("动物在叫");
}
public void eat(String food){
System.out.println("在吃"+food);
}
}
狗类(子类)
public class Dog extends Animal{
@Override
public void shout() {
//使用super关键字调用父类的方法,默认调用,可删除
super.shout();
System.out.println("汪汪汪");
}
@Override
public void eat(String food) {
System.out.println("小狗爱吃骨头,不爱吃"+food);
}
}
2、方法重写和重载的区别
(1)、重写用于继承关系的父子类中,不能用于同一个类中。而重载通常用于同一个类中。
(2)、重写用于更改父类方法中的行为,或者实现接口中的方法。而重载用于为一个行为提供多种实现方式。
(3)、重写特点:函数名必须相同、参数列表必须相同。子类的返回值类型要等于或者小于父类的返回值等,子类的作用域不能小于父类中方法的作用域,子类抛出的异常类型不能大于父类中方法的异常类型。
(4)、重载的特点:函数名相同,参数列表不同,与访问控制符、返回值类型等无关。
三、继承特性
- Java只支持单继承,不允许多重继承;但是Java支持多层继承
(1)、一个子类只能有一个父类;只有唯一的亲生父亲
(2)、一个父类可以派生出多个子类;子孙满堂
(3)、Java支持多层继承;代代相传
(4)、被继承的类称之为父类(基类),继承的类称之为子类 - 子类可以继承父类的非私有属性和非私有方法
- 子类可以扩展父类没有的属性和方法
- 子类不会继承父类的构造方法。
- 子类一定会调用父类的构造器。因为在创建子类对象时,一定会先为从父类继承的属性进行初始化,所以要调用父类的构造器
- 在子类中无论写不写super()都会调用父类的无参构造。如果要写,则必须在子类构造器首行。如果父类没有无参构造,必须在子类的构造器首行使用super(实参列表)显式调用父类的有参构造,否则编译报错;
- 不要为了使用继承而继承。例如:让工人继承学生。
四、final关键字
final关键字主要用于修饰类、属性、方法,以及方法的形参。final在类之前,该类不能被继承;final在方法之前,该方法不能被重写。final在变量之前,该变量不能被修改,因为它已经是一个常量了。
- final修饰类
public final class FinalClass {
}
//报错,不能继承
public class SubClass extends FinalClass{
}
- 修饰成员属性
public class SubClass {
//常量必须要初始化,它没有默认值
//public final static double PI; //报错,必须初始化
public final static double PI = 3.14;
public static void main(String[] args) {
//PI = 3.25; //报错,不能被修改
}
}
- 修饰引用类型
public static void main(String[] args) {
final Person person = new Person("张三");
//person = new Person(); //报错,变量被不能被修改
person.setName("李四"); //正确,
/*
因为final表示变量本身是不能修改,
但是person所引用的对象的内部状态是可以修改的。
*/
}
- 修饰方法
//父类
public class FinalClass {
public final void test(){
System.out.println("父类方法");
}
}
//子类
public class SubFinalClass extends FinalClass{
@Override
public final void test(){ //报错,不能重写test方法
System.out.println("父类方法");
}
}
- 修饰形参
public void test1(final int x){
x = 10; //报错,不能修改
}
五、接口
因为Java不像c++支持多继承,只支持单继承。为了实现类似于多重继承的效果,Java提供了接口来拓展类的功能,弥补Java中单继承的缺点。
- 定义
接口就是抽象方法和常量值定义的集合,一般来讲这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现
[public] interface 接口名 [extends SuperInterfaceList] {
常量属性
抽象方法
}
例如
public interface MyInterface {
double E = 2.21; //属性前面默认的修饰符为public static final
public abstract void test04(); //方法前面默认的修饰符为public abstract
void test05(){}; //报错,不能有方法体
}
- 接口实现
使用关键字implements,例如实现单接口
//接口
public interface MyIterface {
void test01();
void test02();
}
//实现接口
//实现接口的类的命名规范:接口的类名后面接Impl
public class MyIterfaceImpl implements MyIterface{
// 要实现重写接口里面的所有方法
@Override
public void test01() {
System.out.println("方法一");
}
@Override
public void test02() {
System.out.println("方法二");
}
}
- 多接口实现
public class MyIterfaceImpl1 extends MyClass implements MyIterface,MyInterface2{
@Override
public void test01() {
System.out.println("重写方法1");
}
@Override
public void test02() {
System.out.println("重写方法2");
}
@Override
public void test04() {
System.out.println("重写方法4");
}
@Override
public void test05() {
System.out.println("重写方法5");
}
}
最后测试
public class Test {
public static void main(String[] args) {
MyIterfaceImpl1 myInterfaceImpl1 =new MyIterfaceImpl1();
myInterfaceImpl1.test01();
myInterfaceImpl1.test02();
myInterfaceImpl1.tes03();
myInterfaceImpl1.test04();
myInterfaceImpl1.test05();
System.out.println(MyIterfaceImpl.PI);
}
}
结果
- 总结
(1)、接口没有构造方法。
(2)、接口中定义的所有属性默认是 public static final的,即静态常量。
(3)、接口中定义的所有方法默认是public abstract的,即抽象方法。
(4)、由于接口中的定义的所有方法默认都是抽象的,所以接口不能被实例化。
(5)、有抽象函数的不一定是抽象类,也可以是接口。
(6)、类可以通过implements实现接口,一个类可以实现多个接口。
(7)、如果类没有实现接口中的所有方法,那么类必须是抽象类。
(8)、如果在类中访问接口中的属性,不能使用super关键字,因为两者之间没有显示的继承关系。可以使用“接口名.属性名”直接访问。
(9)、接口是支持多重继承的,即一个接口可以有多个父接口
抽象类
抽象类不能被直接实例化。因此它作为其他类的父类,与final正好相反。
使用abstract关键字来修饰的类就是抽象类。
public abstract class AbstractClass { //抽象类
//抽象方法只能在抽象类中定义,它只有声明,没有方法体
public abstract void test(); //抽象方法,木有方法体
}
特点
1、抽象类中不一定有抽象函数。
2、但是抽象方法只能在抽象类中定义。也就是说:抽象类中可以有普通方法也可以有抽象
方法,但是抽象方法只能出现在抽象类中。
3、抽象类不能直接创建实例,只能通过子类创建实例。
4、编译器强制子类实现抽象父类中的抽象方法。如果子类也是抽象类则可以不实现。