文章目录
继承
1.1继承定义
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及 追加属性和方法(子类可以使用父类中非私有的成员。)
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
eg:我有动物类,猫类,狗类(首先这些符合is a继承关系)猫类和狗类有相同特征:名字,年龄,要吃食物等。然后我们就可以将这相同特征部分抽象到动物类里面
下面是实现代码:
Animal实现了抽象的相同特征,以便子类调用
/**
* 父类
*/
public class Animal {
//成员变量
String name; //动物名字
int age; //动物年龄
//eat方法 成员方法
public void eat(String food){
System.out.println("一只"+this.age+"岁的"+this.name+"要吃"+food);
}
//构造方法
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
//构造方法 空构造,建议加上
public Animal() {
}
//get set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
俩个子类,Cat,Dog
子类的name age相同属性以及eat相同方法共同组成的相同特征在父类实现
/**
* 子类
**/
public class Cat extends Animal{
public void eat(String name,int age,String food){
Animal animal = new Animal(name,age);
animal.eat(food);
}
}
/**
* 子类
**/
public class Dog extends Animal{
public void eat(String name,int age,String food){
Animal animal = new Animal(name,age);
animal.eat(food);
}
}
测试类 创建对象,调用eat方法就能得到下面的结果
/**
测试方法
**/
public class Text {
public static void main(String[] args) {
Cat cat = new Cat();//Cat对象引用
//调用eat方法
cat.eat("Tom猫",2,"鱼");
System.out.println("----------------------");
Dog dog = new Dog();//Dog对象引用
//调用eat方法
dog.eat("旺财",5,"chouchou");
}
}
一只2岁的Tom猫要吃鱼
----------------------
一只5岁的旺财要吃chouchou
看完大致用法,下面就里面细节进行介绍
1.2继承格式
public class 子类名 externs 父类名{}
externs可以理解为扩展,即父类是子类的扩展,子类就可以调用父类的方法
1.3继承的好处与弊端
好处:提高代码的复用性(创建一个父类存放多个子类相同的部分)
提高代码的维护性(由于相同的部分移至到一个类里,方便修改)
弊端:继承实现代码的复用,增加了子类与父类之间的耦合性,使得更改父类,子类一定会跟着变化 ------引用黑马程序员
1.4继承使用原则
要满足is a关系,即A是B的一部分或B是A的一部分时可以考虑使用继承
1.5继承中访问变量的特点
子类方法中访问一个变量
- 首先在子类局部范围找
- 其次在子类成员范围找
- 最后再父类成员范围找(包括父类的父类)
- 如果都没有就报错
public class Jicheng{
public String weight = "50kg";//父类的父类找的变量
}
class Fu extends Jicheng{
public String height = "170cm";//父类成员找的变量
}
class Zi extends Fu {
int age = 18;//子类成员范围找的变量
public void show(){
String color = "yellow";//子类局部范围找的变量
System.out.println(height);
System.out.println(weight);
System.out.println(age);
System.out.println(color);
}
/**
* 内部测试类
* @param args
*/
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
}
}
1.6继承中构造方法的访问特点
子类中的构造方法都会默认在前面加一个supper(); 然后就会调用到父类的构造方法;要是想解除,则要在子类构造方法中定义一个显示的有参构造;这个子类就会不调用父类无参构造而调用有参构造; 从这里不难看出,在自己添加有参构造时,最好在加上无参构造;
/**
* Zi
* @author 祥瑞
*/
public class Zi extends Fu{
int age;
public Zi(){
System.out.println("Zi类无参构造");
}
public Zi(int age) {
this.age = age;
System.out.println("Zi类有参构造");
}
}
/**
* Fu
* @author 祥瑞
*/
public class Fu {
int age;
public Fu(){
System.out.println("Fu类无参构造");
}
public Fu(int age) {
this.age = age;
System.out.println("Fu类有参构造");
}
}
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
Zi zi2 = new Zi(18);
}
}
运行结果(原因如上)
Fu类无参构造
Zi类无参构造
Fu类无参构造
Zi类有参构造
1.7继承中成员方法访问的特点
和成员变量访问特点差不多,访问成员方法时,也是先从子类里找成员方法,没有再去父类找,再没有的话就报错
1.8suppr内存图
/**
* Zi
* @author 祥瑞
*/
public class Zi extends Fu{
int age = 30;
public void method(){
int age = 20;
System.out.println("Zi类ziMethod方法");
System.out.println(age);
System.out.println(this.age);
System.out.println(super.age);
}
}
/**
* Fu
* @author 祥瑞
*/
public class Fu {
int age = 40;
public void show(){
System.out.println("Fu类show方法");
}
}
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
zi.show();
}
}
运行结果:
Zi类ziMethod方法
20
30
40
Fu类show方法
下面详细介绍机器堆栈的运行情况
运行到public static void main(String[] args)这句时情况如下,main方法进入栈中
运行到 Zi zi = new Zi(); 时,先把zi动作加载到main方法里面,然后是在堆中创建一个内存,情况图如下:
但是子类构造方法会默认访问父类的构造方法,然后就进入到父类中,存放了父类的age,如图:
运行到 zi.method(); 时,method方法会进入栈内存中,如图
此时输出的
age = 20
this.age = 30 直接找this的地址
supper.age = 40 先找Zi的地址,然后找到supper空间
运行到 zi.show(); 时,就会在栈中弹出method方法,然后show方法进入栈内存,由于Zi里面没有,然后就会从Fu里找,最后存到栈中的是Fu的show方法,如图所示
最后依次弹出show方法和main方法。展示结束
1.9方法重写及其注意事项
优点:方法重写可以既包含父类的内容,还可以添加子类自己独特的内容;
注意事项:子类重写的方法的访问权限要高于父类,父类的私有方法不可以被重写,可以用@Override注释进行验证。
1.10继承注意事项
java支持单继承,可以多层继承;
即一个类只可以继承一个类,但父类还可以继承别的类;
拓展:java接口可以实现多实现在以后碰到选择继承还是实现时,一般选择实现接口,因为实现可以多实现,没有单继承这方面的限制。