继承是面向对象的核心特性,是面向对象的学习重点。
继承是代码复用的重要方式,是类与类之间的一种关系。
从类与类之间的设计关系来看,子类必须属于父类的一种时,才会继承。
父类抽取出了共性的内容,子类可以在父类基础上扩展新的属性与行为。
子类拥有父类的所有属性与方法,无需重新定义。并且可以直接使用非私有的父类成员。
关键字 extends
父类
package com.igeek.javase.ch02.extendss; /** * @version 1.0 * @Description TODO * * 继承: is-a * 1.关键字 extends * 2.继承本质上是设计层面的思想,本意是提高代码的复用性 * 3.子类继承父类后,父类中的成员变量和成员方法子类都具备,并且子类还可以拓展新的属性和新的方法 * 4.创建的父类对象,可以使用父类中的成员属性和成员方法,但是不能使用子类拓展的属性及方法 * 5.Java中类与类之间是单继承 */ public class Animal { private String name; private String color; private int age; public Animal() { } public Animal(String name, String color, int age) { this.name = name; this.color = color; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //吃 public Animal eat(/*Animal a*/) /*throws Exception*/{ System.out.println("一只"+color+name+"在吃东西..."); return null; } }
子类
package com.igeek.javase.ch02.extendss; /** * @version 1.0 * @Description TODO */ public class Dog extends Animal{ // private String player; public Dog() { } public Dog(String name, String color, int age, String player) { super(name, color, age); this.player = player; } public String getPlayer() { return player; } public void setPlayer(String player) { this.player = player; } // public void kanjia(){ System.out.println("一只"+this.getAge()+"的"+this.getName()+"在看家..."); } }
当父类的成员变量用private修饰后,子类无法访问父类的成员变量,但是如果父类提供了public修饰的get/set方法,则子类可以通过get/set方法,正常访问父类的成员变量。
当子类继承父类后,拥有了父类的成员并可以直接调用父类非私有方法。如果子类认为父类提供的方法不够强大,子类可以按照子类自身的逻辑重新定义继承过来的父类方法,这个重新定义父类方法的过程叫做方法重写。
方法重写后,调用该方法时不再调用父类的方法,而调用子类重写后的方法。
package com.igeek.javase.ch02.extendss; /** * @version 1.0 * @Description TODO * * 方法重载 Overload * 1.发生在一个类中 * 2.方法名相同 * 3.参数列表不同(参数个数、参数顺序、参数数量不同) * 4.与返回值无关 * * 方法重写 Override * 1.发生在继承中,子类继承父类,子类重写父类的方法 * 2.方法名相同 * 3.形参必须一致 * 4.返回值:若是基本数据类型则返回值一致即可; * 若是引用数据类型,则子类的返回值类型不可以放大,只可以是<=父类的返回值类型 Object>Animal>Cat 父类>子类 * 5.访问权限修饰符:子类是不可以缩小范围的 public>protected>(default)>private * 6.抛出异常:子类抛出异常比父类小 Throwable>Exception>NullPointerException * * 注意: * 1.@Override一旦标注在方法上,检测当前方法是否符合重写要求 * 2.子类重写父类的方法后,调用执行时会执行子类重写后的逻辑 */ public class Cat extends Animal{ public Cat() { } public Cat(String name, String color, int age) { super(name, color, age); } //捕捉方法 public void zhua(){ System.out.println("一只"+this.getAge()+"岁的,小"+this.getName()+"在抓鱼..."); } // Alt+Insert -> Override 或者 Ctrl+O @Override public Cat eat(/*Animal a*/) /*throws NullPointerException*/{ Cat cat = new Cat(); //默认调用父类的逻辑 //super.eat(); System.out.println("一只"+getName()+"在吃鱼..."); return cat; } }
Test
package com.igeek.javase.ch02.extendss; /** * @version 1.0 * @Description TODO */ public class Test { public static void main(String[] args) { Cat cat = new Cat("小猫咪","花色",2); cat.eat(); //Cat中方法重写eat(),执行时会按照子类的逻辑进行执行 cat.zhua(); System.out.println("------------------"); Tiger tiger = new Tiger(); tiger.setName("东北虎"); tiger.setColor("白色"); tiger.setAge(3); tiger.eat(); tiger.hunt(); System.out.println("------------------"); Dog dog = new Dog("哈士奇","灰色",5,"彩钢儿"); dog.eat(); dog.kanjia(); System.out.println("------------------"); Animal animal = new Animal("动物","无色",1); animal.eat(); //animal.hunt(); //animal.kanjia(); //animal.zhua(); } }
输出
一只小猫咪在吃鱼... 一只2岁的,小小猫咪在抓鱼... ------------------ 一只白色东北虎在吃东西... 东北虎在狩猎中... ------------------ 一只灰色哈士奇在吃东西... 一只5的哈士奇在看家... ------------------ 一只无色动物在吃东西...
1.成员变量、成员方法 可以继承
2.私有的属性 可以继承
3.静态的成员 不可以继承,静态成员是所有实例共享
4.构造方法 不可以继承
1.成员变量不存在重写的问题
2.构造方法 不可以重写
3.静态方法 不可以重写
4.私有方法 不可以重写
在每次创建子类对象时,我们均会先初始化父类内容,再初始化其子类本身内容。目的在于子类对象中包含了其对应的父类存储空间,便可以包含了其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。
代码体现在子类的构造方法调用时,一定先调用父类的构造方法。
调用普通成员:
this.成员变量 可以访问本类对象的成员变量
super.成员变量 可以访问父类的成员变量
this.成员方法() 可以访问本类对象的成员方法
super.成员方法() 可以访问父类的成员方法
子类方法中
访问子类自身的成员用this.
访问父类的成员super.
就近原则:
局部 > 本类成员 > 父类成员
调用构造方法:
this(其他参数) 可以访问本类其他的构造方法
super(其他参数) 可以访问父类其他的构造方法
默认子类调用父类构造方法:
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super();
在子类构造中使用this() 或 this(参数类型 参数值…)的方法可以调用本类中的其他构造方法。但是最终都是要调用父类的构造方法,完成父类成员的初始化。
对象和对象之间的连接。在Java中,关联关系的代码表现形式为一个类做为另一个类的属性类型存在。即“有”的关系:”has-a”。
1.代码层面:一个类作为另外一个类的属性类型存在
2.has-a 有
3.一对一 、 一对多Phone[] phones、List<Phone> list
4.强关联(组合关系)、弱关联(聚合关系)
5.单向关联、双向关联
package com.igeek.javase.ch02.has; import java.util.Arrays; /** * @version 1.0 * @Description 关联关系 */ public class ClassTest { public static void main(String[] args) { Student stu1 = new Student("小红",100); Student stu2 = new Student("小黑",101); Student stu3 = new Student("小白",102); Student[] stus = {stu1,stu2,stu3}; Classes classes = new Classes("Java","班级1",stus); // classes.setStudents(stus); stu1.setClasses(classes); stu2.setClasses(classes); stu3.setClasses(classes); //双向关联中 , toString()会互相调用的问题,造成堆栈溢出错误 , 一定要注意 System.out.println(classes); System.out.println("-----------"+classes.getName()+"的学生信息如下------------"); for (Student student : classes.getStudents()) { System.out.println(student); } } } class Classes{ private String label; private String name; //一对多 强关联 双向关联 private Student[] students = new Student[3]; public Classes() { } public Classes(String label, String name) { this.label = label; this.name = name; } public Classes(String label, String name, Student[] students) { this.label = label; this.name = name; this.students = students; } /** * 获取 * @return label */ public String getLabel() { return label; } /** * 设置 * @param label */ public void setLabel(String label) { this.label = label; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return students */ public Student[] getStudents() { return students; } /** * 设置 * @param students */ public void setStudents(Student[] students) { this.students = students; } /*public String toString() { return "Classes{label = " + label + ", name = " + name + ", students = " + Arrays.toString(students) + "}"; //StackOverflowError 堆栈溢出 }*/ public String toString() { return "Classes{label = " + label + ", name = " + name + "}"; } } class Student{ private String name; private int num; //一对一 弱关联 双向关联 private Classes classes; public Student() { } public Student(String name, int num) { this.name = name; this.num = num; } public Student(String name, int num, Classes classes) { this.name = name; this.num = num; this.classes = classes; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return num */ public int getNum() { return num; } /** * 设置 * @param num */ public void setNum(int num) { this.num = num; } /** * 获取 * @return classes */ public Classes getClasses() { return classes; } /** * 设置 * @param classes */ public void setClasses(Classes classes) { this.classes = classes; } public String toString() { return "Student{name = " + name + ", num = " + num + ", classes = " + classes + "}"; } }
依赖关系(use-a):指一个类A使用到了另一个类B。依赖关系的特性:这种关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。
1.代码层面 一个类作为另外一个类的方法形参类型存在
2.use-a 用
3.具备临时性,偶然性
public class Person { private String name; //上班的方法 类A使用到了另一个类B public void work(Bus bus){ System.out.println(this.name+"乘坐"+bus.getName()+"去上班"); } public Person() { } public Person(String name) { this.name = name; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } public String toString() { return "Person{name = " + name + "}"; } }