多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。
其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。
继承描述的是事物之间的所属关系,这种关系是: is -a 的关系。
定义继承 :就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问从父类中继承过来的属性和行为。
好处- 提高代码的复用性。
- 类与类之间产生了关系,是多态的前提。
通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
重点
多态与动态绑定的概念
多态
一个对象变量可以指示多种实际类型的现象称为多态。
在运行时能够自动地选择调用哪个方法的现象称为动态绑定。
父类中的属性和方法能不能被子类继承,和访问权限修饰符有关
网上一搜索Java访问权限修饰符,很容易找到类似的一张图:
表格简单易懂,也容易记忆,大部分情况下也没什么问题,像我这种懒人以前也是靠着这张图来学习继承的。不过最近重新研究了下继承方面的知识,发现这张图还是有着部分缺漏。
- private
- 可见性:private修饰的方法和属性只能被本类访问,而不能被其他类访问
- 继承性:private 修饰的属性和方法 子类不能继承
- default
- 可见性:默认的方法和属性可以在当前类中访问,也可以被与当前类在同一包下的所有其他类访问
- 继承性:本包中的子类可以继承,其他包中的子类不能继承
- protected
- 可见性:protected修饰的方法和属性可以被当前类、与当前类在同一包下的其他类、所有子类访问(注意:protected修饰的属性和方法很特殊,在当前类与同一包中的类可以随意访问由protected修饰的方法和属性。但是,如果子类与父类不在同一个包下,子类仍然可以访问父类的protected修饰的方法和属性,却只能访问自己继承的属性和方法,而不能访问父类的其他子类继承的属性和方法)
- 继承性:所有子类可以继承
- public
- 可见性:可以被所有类访问
- 继承性:所有子类可以继承
理解protected:属性和方法的可见性应该结合继承性一起理解,这样更容易记住。看下面一段代码
package com.lmy.test.po;
public class Person {
protected void say() {
System.out.println("Person");
}
}
=========================
package com.lmy.test.po;
public class Son1 extends Person {
public static void main(String[] args) {
new Son1().say();//ok
}
}
=========================
package com.lmy.test.po;
public class Son2 extends Person {
public static void main(String[] args) {
new Son2().say();//ok
new Son1().say();//ok
}
}
=========================
package com.lmy.test.vo;
import com.lmy.test.po.Person;
import com.lmy.test.po.Son1;
public class Son3 extends Person {
public static void main(String[] args) {
new Son3().say();//ok
new Son1().say();//error
new Person.say();//error
}
public void test() {
super.say();//ok
}
}
可以看到,与Person类在同一个package下的子类Son2中,new Son1().say()是可以正确执行的,而与Person不在同一个包下的Son3中,new Son1().say()是错误的语法,甚至是new Person().say()也是被拒绝的。也许可以理解为protected修饰的属性和方法的可见性实际上是本类和同一个包下的其他类,同时子类可以继承protected属性,所以在于Person不同包下的子类只能访问自己继承的protected属性和方法,而不能访问其他子类继承的protected属性和方法,这一点在可以调用super.say()却不能调用new Person().say()体现得及其明显。(所以Object虽然定义了clone方法,但是由protected修饰,所有类只能调用自己的clone方法,无法调用其他类对象的clone方法)
注意:private修饰的方法不能被继承,构造函数不能被继承,初始化块也不能被继承,子类对象中是有父类private修饰的属性, 但是含有 不等于 继承(有但是不能访问)
ps:初始化块也没有继承的必要,在子类的构造函数中,第一行一定是调用父类的构造函数(隐式创建并初始化一个父类对象)。其实换句话说,子类能继承父类的一切,但是受到访问权限的影响,不是所有的都可以访问。
继承后的特点——成员方法 成员变量不重名如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
成员变量重名如果子类父类中出现重名的成员变量,这时的访问是有影响的,子类的属性覆盖了父类的属性。在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字。
使用格式:
super.父类成员变量名
- 小贴士:父类中的成员变量是非私有的,子类中可以直接访问。若父类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
成员方法重名 ——重写(Override)如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
-
方法重写 :子类中出现与父类一模一样的方法时(方法签名相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。需要注意的是,重写的方法返回值必须与父类的兼容(相同或者是其子类),异常也必须兼容(相同或者是其子类),且方法的访问权限必须大于或者等于父类方法。
例如:父类方法public double sayHell() throws IOException{}; 在子类中重写为:private String sayHello() throws Exception(); 是完全错误的,完全违反三个规定,无法通过编译。
所谓方法重写,就是子类中定义与父类方法标签相同的方法(方法名、方法参数)。重写提现的是多态性,动态绑定。即将子类对象传递给父类变量,调用方法时将会调用子类重写的方法。
并不是所有方法都可以重写,只有动态绑定的方法能够重写。
动态绑定支持运行时判断对象实际类型,从而调用对应的方法
public class Animal {
public String like() throws IOException {
System.out.println("animal");
return "animal";
}
public final String look() throws IOException{
System.out.println("animal");
return "animal";
}
public static String speak() throws IOException {
System.out.println("animal");
return "animal";
}
private String say() throws IOException{
System.out.println("animal");
return "animal";
}
}
public class Cat extends Animal {
public String like() throws IOException {
System.out.println("cat");
return "cat";
}
public static String speak() throws IOException {
System.out.println("cat");
return "cat";
}
public void say() throws Exception{
System.out.println("cat");
}
}
public class Dog extends Animal {
public String like() throws IOException {
System.out.println("dog");
return "dog";
}
}
// 动态绑定 提现多态
Animal animal = new Cat();
animal.like();// cat
animal = new Dog();
animal.like();//dog
静态绑定
只会调用当前对象类型下的方法
Animal animal = new Cat(); animal.say();// animal静态绑定的方法
-
static 方法
-
static方法允许子类存在标签相同的静态方法,子类不能定义标签相同的非静态方法。即子类不能存在标签与父类static方法相同的动态绑定的方法。
-
在调用static方法时,只会调用自身的static方法,即
... Animal animal = new Cat(); animal.say(); // aniaml
-
-
final 方法
- final方法不允许子类中存在标签相同的方法,任何方法都不行(final设计的初衷就是限制子类的行为,由父类规定行为方式)
-
private 方法
-
private方法允许子类中存在标签相同的方法,没有任何限制。但由于是静态绑定的方法,所以父类对象只会调用自身的该方法。
-
在Animal类中添加main方法:
public static void main(String[] args) throws Exception{ Animal animal = new Cat(); animal.say();// animal }
-
方法重写遵循一同两小一大原则:
- 一同:方法签名相同(方法名、参数列表相同)
- 两小:返回值类型、抛出异常类型 要与父类相同或者是其子类
- 一大:访问权限 要大于父类访问权限
将Animal类中的like()方法改为protected String like() throws IOException
Cat类中重写Animal中的like()方法
// 错误:访问权限变小
private String like() throws IOException{...}
// 错误:抛出异常与父类不兼容(非父类异常类或其子类)
protected String like() throws Exception{...}
// 错误:返回值与父类不兼容(非父类返回值类型或其子类)
protected Integer like() throws IOException{...}
// 正确写法之一:
public String like() throws IOException{...}
另一个角度理解一同两小一大原则:继承讲究一个置换法则,即任何父类出现的地方,都可以由其子类置换。例如
// 如果有 代码一:
try{
String likeStr = new Animal().like();
}catch(IOException e){
e.printStackTrace();
}
// 必须可以有 代码二:
try {
String likeStr = new Cat().like();
}catch(IOException e) {
e.printStackTrace();
}
假设上面的代码所在类所处的包与Animal、Cat所在包不同,那么在上面的代码二中,如果Cat类的like()方法返回值不是String或者String的子类,抛出的异常类型不是IOException类对象或其子类对象,访问权限如果比public低,就会出现异常,违反了置换法则。
继承后的特点——构造方法子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。**所以子类的初始化过程中,必须先执行父类的初始化动作(执行父类的构造方法)。**子类的构
造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
public class Parent {
private String name;
private String sex;
public Parent(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
public class Son extends Parent {
public Son(){
//由于父类没有无参构造方法,所以子类必须显示声明给父类属性赋值
super("zhangsan","男");
}
}
继承中的对象初始化过程
创建子类对象的时候,会隐式的先创建一个父类对象
-
父类对象的初始化
- 给父类对象的实例变量分配空间、默认初始化
- 声明时初始化、初始化块初始化
- 调用父类的构造函数进行初始化
-
子类对象的初始化
- 给子类对象的实例变量分配空间、默认初始化
- 声明时初始化、初始化块初始化
- 调用子类的构造函数进行初始化
(图片来源于网络,来源太久远忘了,水印csdn自己加的,反正不是我自己画的,侵权请联系删除,十分抱歉)
-
Java只支持单继承,不支持多继承。
-
Java支持多层继承(继承体系)。
class A{} class B extends A{} class C extends B{}- 顶层父类是Object类。所有的类默认继承Object,作为父类。
-
子类和父类是一种相对的概念。
编程入门菜鸟一位。以上说法如果有不对的地方,欢迎各路大佬留言指正。



