是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
在日常写代码的过程中,有时会敲写很多重复的代码,让自己的代码冗长无味,降低了可读性。当我们使用继承时,代码就变得简洁明了,更加美观。减少无用代码,提高开发效率。
我们举一个例子来说明:
我们在编写程序时,遇到一类相同的物时,通常需要挨个定义,如下
编写狗类时,我们需要狗拥有名字,年龄,毛色等的属性,也需要eat()等成员方法,以便后续使用
编写猫类时,我们也需要猫拥有名字,年龄,毛色等属性,也同样需要eat()等方法
这时我们发现狗和猫有许多重复的定义(共性)–名字,年龄,eat()等…
那我们可不可以使用一种语法来减少重复呢?这时就发明了继承。
修饰符 class 子类 extends 父类 {
派生类 基类
超类
}
使用关键字extends来构写继承。
以猫和狗为例子,它们都属于动物,所以我将共性的类名称作Animal
//动物类——共性
class Animal{
public String name;
public int age;
public String color;
public void eat(){
System.out.println(this.name+"正在吃饭");
}
}
//狗类
class Dog extends Animal{
public float weight;
public void barks(){
System.out.println(super.name+" 汪汪汪");
}
}
//猫类
class Cat extends Animal{
public void sleep(){
System.out.println(super.name+ " 正在睡觉");
}
}
观察如下代码,我们可以看到在父类中有变量a,而子类中也定义了a。我们在
class Base{
public int a = 10;
public int b = 3;
}
class Derived extends Base{
public int a = 20;
public int c = 30;
public void menthod(){
System.out.println(a);
}
}
public class menthod {
public static void main(String[] args) {
Derived derived = new Derived();
System.out.println(derived.a);
}
}
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
1.如果访问的成员变量子类中有,优先访问自己的成员变量。
2.如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
3.如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
可以不可以打印出父类a的值? 这时就引出来我们的super和this
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
该关键字主要作用:在子类方法中访问父类的成员。
1.super不能出现在static修饰的方法。
2.子类在继承父类之后,要使用构造方法将父类成员进行初始化。
3.在子类构造方法中,会默认调用父类的无参构造方法,这是编译器自动添加的。默认是super()。
4.如果父类的构造方法是有参数的,则子类也需要是有参数的
在调用父类时,要先将父类的成员变量初始化
相同点
1:都是关键字。
2:都不能出现在静态构造方法中
3:this()和super()都需要位于构造方法中的第一行,也就意味着this()和super()不能同时出现
不同点
1:this是对当前对象的引用;
2:this符合就近原则访问成员变量,super访问父类成员变量。
3:this()用来调用子类构造方法,super()调用父类构造方法;
4:若存在继承,在子类的构造方法中,一定有super(),若自己不自定义,编译器也会也会自动添加,而this()则没有。
在子类构造方法中,会默认调用父类的无参构造方法,这是编译器自动添加的。默认是super();
我们在实例化对象后,该怎么写就怎么写
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "小黄";
dog.age = 1;
}
}
这种情况就比较复杂了。蓝色背景的代码为新加入的构造方法。
观察代码,父类中自己定义了带参数的构造方法,这时我们在定义子类时,一定要先对父类的构造方法进行处理,这种处理通常是在自己的构造方法中默默使用super对父类构造方法进行涵盖,如下:
这里我们发现,在父类中使用this,在子类中使用super,这就跟上面解释的this和super的原理有关。
定义好之后,我们该如何使用呢?代码如下
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("小黄",1);
Cat cat = new Cat("喵喵",2);
dog.eat();
cat.sleep();
}
}
先说结论
1.父类的实例,
2.父类的构造,
3.子类的实例
4.子类的构造。
class Person{
public String name;
public int age;
public Person(String name,int age){
this.name = name;
this.age = age;
System.out.println("构造方法执行");
}
{
System.out.println("实例化代码块执行");
}
static {
System.out.println("静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class testdemo {
public static void main(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("gaobo",20);
}
}
通过分析执行结果,得出以下结论:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多. 类之间的关系也会
更加复杂.
但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了
如果想从语法上进行限制继承, 就可以使用 final 关键字
1:修饰变量,则该变量的值不能再被改变。
2:修饰类,则表示该类不能被继承
3:修饰方法,则该方法不能被重写
和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法
(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。
通俗来说就是继承反转过来。
轮胎类
class Tire{
// ...
}
// 发动机类
class Engine{
// ...
}
// 车载系统类
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
private VehicleSystem vs; // 可以复用车载系统中的属性和方法
// ...
}
码字不易,感谢观看
如果对你有帮助的话,记得点赞评论+关注吧