1、继承概述
- 继承:解决代码复用,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义相同的属性和方法,所有子类不需要重新定义这些属性和方法,只需要通过extends来生命继承父类
(1)继承语法
class 子类 extends 父类{
}
/*
1、子类就会自动拥有父类定义的属性和方法;
2、父类又叫超类,基类;
3、子类又叫派生类
*/
- 继承示意图
(2)继承入门范例
- 目录如下:
①创建父类
package com.taobao;
public class Person {
public String name; //名字公开
private int age; //age私有化
private double salary; //..
//alt+insert
public Person() {
}
public Person(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
//将set方法写在构造器中,这样仍然可以验证
setSalary(salary);
setName(name);
setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
//1、加入对数据的校验,相当于增加了默认逻辑
if(name.length() >=2 && name.length()<=6) {
this.name = name;
} else {
System.out.println("名字的长度不对,需要2到6字符,默认名字");
this.name = "青春重新输入";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>=1 && age <= 120) {
this.age = age;
}else{
System.out.println("设置年龄不对,需要在(1-120),给默认年龄18");
this.age = 18; //给一个默认年龄
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//写一个方法,返回属性信息
public String info(){
return "信息为 name =" + name + "age = " + age + "薪水为 =" + salary;
}
}
②创建子类
package com.taobao;
public class Man extends Person {
public void testing(){
System.out.println(name);
}
}
③main方法调用子类
package com.taobao;
public class hello {
public static void main(String[] args) {
Man man = new Man();
man.name = "张三";
man.testing();
}
}
- 运行结果
2、继承细节
2.1、子类继承了所有的属性和方法
①非私有的属性和方法可以在子类中直接访问;
②私有属性和方法不能在子类直接访问,要通过公共方法访问;
- 范例:目录结构如下
(1)父类Person创建
public class Person {
public String name;
private double salary; //..
//alt+insert
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//写一个公共方法,返回属性信息
public String info(){
return "薪水为 =" + salary;
}
//写一个私有方法
private void info2(){
System.out.println("姓名:" + name);
}
//callTest
public void callInfo2(){
info2();
}
}
(2)子类Man继承Person父类
public class Man extends Person {
public void testing(){
System.out.println(name);
}
}
(3)main方法使用子类
public class hello {
public static void main(String[] args) {
Man man = new Man();
//1、子类继承非私有属性 - 可以直接访问
man.name = "张三";
//2、子类继承私有属性 - 通过父类提供公共方法(get)访问属性
man.setSalary(1000);
//3、之类继承父类公共方法 - 可以直接访问
System.out.println(man.info());
//4、子类继承父类的私有方法 - 通过在父类提供公共方法访问私有方法
man.callInfo2();
//5、直接访问子类的方法
man.testing();
}
}
- 运行结果
2.2、子类必须调用父类构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器
- 子类的无参构造器中默认有个 super(),代表调用父类的无参构造器;
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下都会调用父类的无参构造器
-
范例:目录结构如下
①创建父类
package com.taobao;
public class Person {
//父类构造器
public Person() {
System.out.println("调用父类的构造器...");
}
}
②创建子类
package com.taobao;
public class Man extends Person {
//子类构造器
public Man() {
//super(); //默认调用父类的无参构造器
System.out.println("子类的无参构造器...");
}
public Man(String name){
//super();
System.out.println("参数"+name);
}
}
③main方法创建子类对象
package com.taobao;
public class hello {
public static void main(String[] args) {
Man man = new Man();
}
}
- 运行结果:先创建父类,后创建子类
2.3、父类没有提供无参构造器
-
注意事项:必须在子类的构造器中用super去指定使用哪个父类的构造器完成对父类的初始化工作,否则编译不会通过;
-
范例:目录结构如下
①创建父类
package com.taobao;
public class Person {
public Person(String name) {
System.out.println("调用父类的构造器...");
}
}
②创建子类
package com.taobao;
public class Man extends Person {
public Man() {
super("张三");
System.out.println("子类的无参构造器...");;
}
public Man(String name) {
super("张三");
System.out.println("子类的有参构造器...");;
}
}
③创建对象,分别调用子类的无参构造器和带参构造器
package com.taobao;
public class hello {
public static void main(String[] args) {
System.out.println("调用子类无参构造器!");
Man man = new Man();
System.out.println("调用子类的带参构造器!");
Man man1 = new Man("lizhi");
}
}
- 运行结果
- 注意事项
- ①如果希望指定去调用父类的某个构造器,则显式的调用一下 :super(参数列表)
- ②super在使用时,需要放在构造器的第一行
- ③super()和this()都只能放在构造器第一行,因此两个方法不能存在同一个构造器
- ④java所有类都是object的父类,object是所有类的基类;
- ⑤父类构造器的调用不限于直接父类!将一直往上追溯知道Object类(顶级父类)
- ⑥子类最多继承一个父类,即java中是单继承方式:如何让A类继承B和C,先让B类继承C,再让A类继承B
- ⑦不能滥用继承,子类和父类之间必须满足is-a的逻辑关系;
3、继承的本质
- 范例
(1)新建类GradPa
package com.taobao;
public class GrandPa {
String name = "爷爷";
String hobby = "旅游";
}
(2)新建子类Father继承GradPa类
package com.taobao;
public class Father extends GrandPa {
String name = "大头爸爸";
int age = 39;
}
(3)新建子类Son继承Father
package com.taobao;
public class Son extends Father{
String name = "大头儿子";
}
(4)main方法创建子类对象
package com.taobao;
public class hello {
public static void main(String[] args) {
Son son = new Son();
System.out.println("调用Son子类name:"+son.name);
System.out.println("调用Father父类age:"+son.age);
System.out.println("调用GrandPa父类hobby:"+son.hobby);
}
}
- 运行结果
- 属性查找顺序 : 按照查找关系来返回信息
- ①首先看子类是否有该属性;
- ②如果子类有这个属性,并且可以访问,则返回信息;
- ③如果子类没有这个属性,就看父类有没有属性(如果父类有该属性,并且可以访问,就返回信息)
- ④如果父类没有就按照(3)的规则,继续找上级父类,知道Object
- ⑤向上找遇上私有属性的则调用get方法;
4、super关键字
- super:代表父类的引用,用于访问父类的属性,方法,构造器
- 在子类的构造方法中显式的调用父类构造方法
- 访问父类的成员方法和变量。
(1)super基本语法
①访问父类的属性,但不能访问父类的private属性;
super.属性
②访问父类的方法,但不能访问父类的private方法;
super.方法名(参数列表)
③访问父类的构造器,但只能放在构造器第一句,和this冲突;
super(参数列表)
(2)super调用父类构造方法
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器,
子类的无参构造器中默认有个 super(),代表调用父类的无参构造器;
当创建子类对象时,不管使用子类的哪个构造器,默认情况下都会调用父类的无参构造器
(3)super调用父类属性
- 当父类和子类具有相同的数据成员时,JVM 可能会模糊不清,需要用super指定
- 范例1 - 不加super就近查找
//1、创建父类
class Person {
int age = 12;
}
//2、创建子类
class Student extends Person {
int age = 18;
void display() {
System.out.println("学生年龄:" + age);
}
}
//3、创建测试类
class Test {
public static void main(String[] args) {
Student stu = new Student();
stu.display();
}
}
- 运行结果
- 范例2 : 加上super则跟随父类
//1、创建父类
public class Person {
int age = 12;
}
//2、创建子类
public class Student extends Person {
int age = 18;
void display() {
System.out.println("学生年龄:" + super.age);
}
}
//3、创建测试类
public class Test {
public static void main(String[] args) {
Student stu = new Student();
stu.display();
}
}
- 运行结果
(4)super调用成员方法
-
当父类和子类都具有相同的方法名时,可以使用 super 关键字访问父类的方法;
-
范例:
//1、创建父类
class Person {
void message() {
System.out.println("This is person class");
}
}
//2、创建子类
class Student extends Person {
void message() {
System.out.println("This is student class");
}
void display() {
message(); // 调用当前类的message方法
super.message(); // 调用父类的message方法
}
}
//3、main调用子类
class Test {
public static void main(String args[]) {
Student s = new Student();
s.display();
}
}
- 运行结果
- 只调用方法 message( ),是当前的类 message( ) 被调用;
- 使用 super 关键字时,是父类的 message( ) 被调用。
(5)super和this区别
区别点 | this | super | |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类中没有此属性则从父类中继续查找 | 直接访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,如果本类中没有此方法则从父类继承 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器首行 | **直接调用父类构造器,**必须放在构造器首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
5、方法重写(override)
(1)方法重写概述
- 方法重写:子类中有一个方法,和父类的某个方法名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法;
//1、创建父类
public class Animal {
public void cry(){
System.out.println("动物汪汪叫...");
}
}
//2、创建子类
public class Dog extends Animal {
public void cry(){
System.out.println("小狗汪汪叫...");
}
}
//3、main方法调用子类
public class OverrideMain {
public static void main(String[] args) {
//演示方法重写
Dog dog = new Dog();
dog.cry();
}
}
- 运行结果
(2)方法重写细节
Ⅰ、子类方法的参数,方法名称要和父类方法的参数,方法名称完全一样;
Ⅱ、子类方法的返回类型
-
父类的方法返回类型一样;
-
父类的返回类型的子类;
//父类
public object getInfo()
//子类
public String getInfo()
Ⅲ、子类方法不能缩小父类方法的访问权限
//父类
void sayOk()
//子类
public void sayOk()
(3)方法重写和方法重载区别
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载 | 本类 | 必须一样 | 类型、个数、顺序至少一个不同 | 无要求 | 无要求 |
重写 | 父子类 | 必须一样 | 必须相同 | 一致/父子类 | 子类>父类访问范围 |