目录
1 为什么需要继承
设想一下,我们要写一个狗类,我们会这样写:
public class Dog{
public String name;
public String color;
public int age;
public void eat() {
System.out.println(name+"正在吃狗粮...");
}
public void bark() {
System.out.println(name+"正在汪汪叫~");
}
}
如果再让我们写一个猫类呢,我们也许会这样写:
public class Cat {
public String name;
public String color;
public int age;
public void eat() {
System.out.println(name+"正在吃猫粮...");
}
public void mew() {
System.out.println(name+"正在喵喵叫~");
}
}
但是这里有一个问题,狗类和猫类的name、color、age、eat这些成员重复了,这样会显得代码
冗余。
只写一个狗类和猫类可能不是很明显,那么如果再写一个鸟类、鱼类....等等。
每写一个这样的类我们就需要重复写这样一段代码,那么有没有办法能够省略呢?
有!这就需要用到我今天讲的继承。
继承的意义即:达到代码的复用。
2 继承概念
继承的类被称为子类,或者派生类。
被继承的类被称为父类/超类 或者基类。
子类会继承父类的特性,也能够派生出父类没有的特性。
例如:
3 继承的语法
3.1 extends关键字
在Java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下:
修饰符 class 子类 extends 父类 {// ...}
用上面的动物类和猫类狗类为例的话,就是这样:
// Animal.javapublic class Animal {String name ;String color;int age ;public void eat (){System . out . println ( name + " 正在吃饭 " );}public void sleep (){System . out . println ( name + " 正在睡觉 " );}}// Dog.javapublic class Dog extends Animal {void bark (){System . out . println ( name + " 汪汪汪 ~~~" );}}// Cat.Javapublic class Cat extends Animal {void mew (){System . out . println ( name + " 喵喵喵 ~~~" );}}
public class Test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.name="大黄";
dog.color="黄色";
dog.age=3;
dog.bark();
Cat cat=new Cat();
cat.name="小黑";
cat.color="黑色";
cat.age=2;
cat.mew();
}
}
运行结果:
Cat类和Dog类中并未定义name、color、age等变量,但是实例化后的对象却可以访问到这些成员
说明子类Cat和子类Dog成功继承了父类Animal的属性。
【注意事项】
1. 子类会将父类中的成员变量或者成员方法继承到子类中了2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必 要继承了
3.2 父类成员访问
3.2.1 子类中访问父类的成员变量
1. 子类和父类不存在同名成员变量
public class Base {int a ;int b ;}public class Derived extends Base {int c ;public void method (){a = 10 ; // 访问从父类中继承下来的 ab = 20 ; // 访问从父类中继承下来的 bc = 30 ; // 访问子类自己的 c}}
2. 子类和父类成员变量同名
这是一个父类:
public class Base {
public int a=100;
public int b=200;
}
public class Derived extends Base{
public int a=1000;
public char b = 'A';
public void method(){
System.out.println(a);
System.out.println(b);
}
public static void main(String[] args) {
Derived derived=new Derived();
derived.method();
}
}
① 如果访问的成员变量子类中有,优先访问自己的成员变量。② 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。③ 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
3.2.2 子类中访问父类的成员方法
1. 成员方法名字不同
public class Base {public void methodA (){System . out . println ( "Base 中的 methodA()" );}}public class Derived extends Base {public void methodB (){System . out . println ( "Derived 中的 methodB() 方法 " );}public void methodC (){methodB (); // 访问子类自己的 methodB()methodA (); // 访问父类继承的 methodA()// methodD(); // 编译失败,在整个继承体系中没有发现方法 methodD()}}
2. 成员方法名字相同
public class Base {public void methodA (){System . out . println ( "Base 中的 methodA()" );}public void methodB (){System . out . println ( "Base 中的 methodB()" );}}public class Derived extends Base {public void methodA ( int a ) {System . out . println ( "Derived 中的 method(int) 方法 " );}public void methodB (){System . out . println ( "Derived 中的 methodB() 方法 " );}public void methodC (){methodA (); // 没有传参,访问父类中的 methodA()methodA ( 20 ); // 传递 int 参数,访问子类中的 methodA(int)methodB (); // 直接访问,则永远访问到的都是子类中的 methodB() ,基类的无法访问到}}
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同 ( 重载 ) ,根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
4 super关键字
对于以下的基类和派生类:
public class Base {
public int a=100;
public int b=200;
public void methodA(){
System.out.println("这是Base中的methodA");
}
}
public class Derived extends Base{
public int a=1000;//变量名和类型名相同
public char b = 'A';//变量名相同 类型名不同
public void methodA(){
System.out.println("这是Derived中的methodA");
}
public void method(){
System.out.println(a);
System.out.println(b);
methodA();
}
public static void main(String[] args) {
Derived derived=new Derived();
derived.method();
}
}
根据我们上面学的知识,当我调用Derived类中的method方法时,输出的应当是Derived中的属性
和方法,运行结果如下:
如果要让method输出父类的属性和methodA方法,就要用到super关键字,就像下面这样:
public void method(){
System.out.println(super.a);
System.out.println(super.b);
super.methodA();
}
总结:在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。
【注意】:只能在非静态方法中使用
5 子类构造方法
父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
以刚才的父类Base和子类Derived为例:
public class Base {
public int a=100;
public int b=200;
public Base(){
System.out.println("这是Base的构造方法");
}
}
public class Derived extends Base{
public int a=1000;//变量名和类型名相同
public char b = 'A';//变量名相同 类型名不同
public Derived(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("这是Derived的构造方法");
}
public static void main(String[] args) {
Derived derived=new Derived();
}
}
运行结果如下:
6 super和this
7 再谈初始化
在讲接下来的内容之前,我们先回顾一下之前的代码块相关的知识。
构造代码块,静态代码块,以及构造方法的执行顺序...来看以下代码:
public class Derived {
public Derived(int data) {
System.out.println("这是Derived的构造方法");
}
{
System.out.println("这是Derived的实例代码块");
}
static{
System.out.println("这是Derived的静态代码块");
}
}
运行结果:
结论:
1. 静态代码块先执行,并且只执行一次,在类加载阶段执行2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
上述的内容是在没有继承关系的情况下的结论。
如果存在继承关系的执行顺序呢?
public class Base {
public int Data;
public Base(int data) {
Data = data;
System.out.println("这是Base的构造方法");
}
{
System.out.println("这是Base的实例代码块");
}
static{
System.out.println("这是Base的静态代码块");
}
}
public class Derived extends Base{
public Derived(int data) {
super(data);
System.out.println("这是Derived的构造方法");
}
{
System.out.println("这是Derived的实例代码块");
}
static{
System.out.println("这是Derived的静态代码块");
}
}
对于上面继承了父类的子类,我们另外用一个Test类来运行:
public class Test {
public static void main(String[] args) {
Derived derived=new Derived(100);
}
}
运行结果如下:
通过分析结果,得出以下结论:
8 protected 关键字
来看一下代码:
//父类
public class B {
private int a;
protected int b;
public int c;
int d;
}
//同一包内的子类
public class D extends B{
public void method(){
//super.a=10;//编译报错,父类中private成员在不同包子类中不可见
super.b=20;
super.c=30;
super.d=40;
}
}
//不同包的子类
public class C extends B {
public void method(){
//super.a=10;//编译报错,父类中private成员在不同包子类中不可见
super.b=20;
super.c=30;
//super.d=40;//编译报错 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
}
}
//不同包中的类
public class TestC {
public static void main(String[] args) {
C c=new C();
c.a=10;// 编译报错,父类中private成员在不同包其他类中不可见
c.b=20;// 父类中protected成员在不同包其他类中不能直接访问
c.c=30;// 父类中public成员在不同包其他类中可以直接访问
c.d=40;// 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
}
}
什么时候下用哪一种呢?我们希望类要尽量做到 "封装", 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者.因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要用 public.另外, 还有一种 简单粗暴 的做法: 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是对访问权限的滥用, 还是更希望同学们能写代码的时候认真思考, 该类提供的字段方法到底给 "谁" 使用(是类内部自己用, 还是类的调用者使用, 还是子类使用)
9 继承方式
10 final 关键字
final关键可以用来修饰变量、成员方法以及类。
1. 修饰变量或字段,表示常量(即不能修改)
final int a = 10 ;a = 20 ; // 编译出错
2. 修饰类:表示此类不能被继承
final public class Animal {...}public class Bird extends Animal {...}// 编译出错Error :( 3 , 27 ) java : 无法从最终 com . bit . Animal 进行继承
3. 修饰方法:表示该方法不能被重写
11 继承与组合
// 轮胎类class Tire {// ...}// 发动机类class Engine {// ...}// 车载系统类class VehicleSystem {// ...}class Car {private Tire tire ; // 可以复用轮胎中的属性和方法private Engine engine ; // 可以复用发动机中的属性和方法private VehicleSystem vs ; // 可以复用车载系统中的属性和方法// ...}// 奔驰是汽车class Benz extend Car {// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来}
完
看完了觉得有用的姥爷们求求您们点个免费的赞和关注,这对我真的很重要!非常感谢大家!