引入继承
Java中使用类对实体进行描述,类经过实例化之后的产物对象,就可以用来表示现实中的实体,描述的事物错综复杂,事物之间可能会存在一些关联,因此我们就需要将他们共性抽取,面向对象的思想中提出了继承的概念,专门用来共性抽取,实现代码复用。
继承的概念
继承--inheritance: 它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生的新的类,我们叫做派生类。
继承最重要的思想就是:共性的抽取,代码的复用
继承的语法
修饰符 class 子类 extends 父类{
...
}
dog类和cat类都继承了Animal类,其中:Animal类称为父类/基类或者超类,dog和cat称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类只需要在实现时关心自己新加的成员即可。
//Animal.java
public class Animal {
String name;
int age;
public void eat(){
System.out.println(name+"在吃饭");
}
public void sleep(){
System.out.println(name+"在睡觉");
}
}
//Dog.java
public class Dog extends Animal{
void bark(){
System.out.println(name+"在叫");
}
}
//Cat.java
public class Cat extends Animal{
void mew(){
System.out.println(name+"在叫");
}
}
//TestExtend.java
public class TestExtend {
public static void main(String[] args) {
Dog dog = new Dog();
//Dog类中没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
//dog访问eat()和sleep()也是从Animal中继承下来的
dog.eat();
dog.bark();
dog.sleep();
}
}
由于我没有放名字,执行结果也就是
总的来说就是两个方面
1.子类会将父类中的成员变量或者成员方法继承到子类中
2.子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没必要继承了
有时具体代码怎么实现,子类能否直接访问父类继承下来的呢?
子类中访问父类的成员变量
①.子类和父类不存在同名成员变量
public class Base {
int a;
int b;
}
class Derived extends Base{
int c;
public void method(){
a = 10;//访问从父类继承下来的a
b = 20;//访问从父类继承下来的b
c = 30;//访问子类自己的c
}
}
-
Base
类:Base
类定义了两个成员变量a
和b
,它们是int
类型。由于没有使用private
进行修饰,它们是默认的包访问权限(default
),可以在同一包内的其他类中访问。
-
Derived
类:Derived
类继承了Base
类,所以它自动拥有Base
类的成员变量a
和b
。同时,Derived
类还声明了一个额外的成员变量c
,类型为int
。
-
method()
方法:- 这是
Derived
类中的一个方法,方法内部访问了父类Base
的成员变量a
和b
,以及子类Derived
自己的成员变量c
。a = 10;
:通过继承,Derived
类可以访问并修改Base
类的a
变量。b = 20;
:同样,Derived
类可以访问并修改Base
类的b
变量。c = 30;
:这是对子类Derived
的成员变量c
赋值。
- 这是
②.子类和父类成员变量同名
public class Base {
int a;
int b;
int c;
}
class Derived extends Base{
int a; //与父类中成员a同名,且类型相同
char b;//与父类中成员b同名,但类型不同
public void method(){
a = 100;
b = 200;
c = 300;
}
}
在Derived
类中:
Derived
类继承自Base
类,因此Derived
类会自动继承Base
类中的a
、b
和c
成员变量。但在Derived
类中:int a;
:声明了一个与父类同名的成员变量a
,但它是在子类中重新定义的。这并不是覆盖父类的a
,而是隐藏了父类的a
,也就是说,子类Derived
会有一个自己的a
,与父类的a
不同。char b;
:声明了一个与父类同名但类型不同的成员变量b
,父类的b
是int
类型,子类的b
是char
类型。这也导致了父类的b
被子类的b
隐藏,但类型不兼容。
method()
方法:a = 100;
:这里的a
是指Derived
类中的a
(子类的成员变量),所以赋值给子类中的a
。b = 200;
:这里的b
是指Derived
类中的b
(子类的成员变量),因此赋值给子类的b
。由于b
的类型在父类和子类中不同(父类是int
,子类是char
),这在实际编译时会出现类型不匹配错误。
就是说
a
是一个int
类型的变量,在父类和子类中都存在,但是它们分别是独立的。b
是一个int
类型的变量在父类中,而在子类中是char
类型的,类型不同会导致赋值时出现问题。c
在子类中直接访问父类的c
,这是合法的,因为c
是从父类继承过来的。
所以,我们就会发现
成员变量访问遵循的是一种就近原则,自己子类有优先自己的,如果没有就向父类去找
子类中访问父类的成员方法
还是一样的讨论
- 名字相同
- 名字不同
成员方法名字不同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
class Derived extends Base{
public void methodB() {
System.out.println("Derived中的methodB()");
}
public void methodC(){
methodB();//访问子类自己的methodB
methodA();//访问父类继承的methodA
}
}
一样的,还是优先就近的
成员方法名字相同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
class Derived extends Base{
public void methodA(int a){
System.out.println("Derived中的methodA(int)");
}
public void methodB() {
System.out.println("Derived中的methodB()");
}
public void methodC(){
methodA();//没有传参,访问父类中的methodA()
methodA(20);//传递int参数,访问子类中的的methodA(int)
methodB();//直接访问,永远访问到的都是子类中的methodB(),父类的无法访问到
}
}
【说明】
- 通过子类对象访问父类与子类中不同方法时,优先在子类中找,找到则访问,否则在父类中去找。找到则访问,否则编译报错
- 通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数不同(也就是方法重载了),根据调用方法传递参数选择合适方法去访问,如果没有也报错
但是我们可以看到父类的methodB是访问不到的,如果我们要直接去访问,是做不到的,该如何操作呢???
super关键字
在子类方法中访问父类成员<可以理解为是一种引用,只能指代直接的父类,不能指父类的父类>
public class Base {
int a;
int b;
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
class Derived extends Base {
int a; //与父类中成员变量同名且类型相同
char b; //与父类中成员变量同名但类型不同
public void methodA(int a) {
System.out.println("Derived中的methodA(int)");
}
public void methodB() {
System.out.println("Derived中的methodB()");
}
public void methodC() {
a = 100; //等价于this.a = 100
b = 101; //等价于this.b = 101 this是当前对象的引用
//访问父类的成员变量时,需要借助super
//super是获取到子类对象中从父类继承下来的部分
super.a = 200;
super.b = 201;
//父类和子类中构成重载的方法,直接通过参数列表区分清楚访问父类还是子类
methodA();
methodA(20);
//如果在子类中要访问重写的父类方法,用到super
methodB();
super.methodB();
}
}
super、this都不能在静态方法中使用