目录
1. 概述
2. 继承的限制
2.1 单继承
2.2 访问修饰符
2.3 . final
3. 重写
4. super
4.1super的作用
4.2访问父类的成员和被重写方法
4.3调用父类的构造器
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么就无需在多个类中定义这些相同属性和行为,只要继承那个类即可。
继承
继承
Teacher
Person
Student
在 Java 中通过extends
关键字可以实现类与类的继承。
public class Person {
String name;
int age;
void eat() {
System.out.println("能吃饭");
}
void sleep() {
System.out.println("能睡觉");
}
}
public class Teacher extends Person {
String job;
void educate() {
System.out.println("会讲课");
}
}
public class Student extends Person {
String major;
void study() {
System.out.println("会学习");
}
}
/***************************************/
public class PersonTest {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Student student = new Student();
teacher.name = "马老师";
teacher.eat();
student.sleep();
}
}
/**********************************************/
public class Person {
/*
* 父类中私有的成员是不能继承给子类的
*/
private String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
......
}
public class PersonTest {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Student student = new Student();
// 报错,父类中私有的成员是不能继承给子类的
teacher.name = "马老师";
// 父类中公有方法是可以继承给子类的
teacher.setName("马老师");
System.out.println(teacher.getName());
}
}
继承的格式:class A extends B { }
A:子类、派生类、subclass
B:父类、超类、基类、superclass
体现:一旦子类A继承父类B以后,子类A中就获取到了B类中所有的公开的属性和方法。特别的,父类中声明为private的属性和方法,子类无法继承。
子类继承父类以后,还可以声明自己特有的属性或者方法:实现功能的扩展。
子类和父类的关系,不同于子集和集合的关系。
优缺点
优点:
提高了代码的复用性,减少代码冗余
提高了代码的可维护性即可扩展性
是多态的前提
缺点:
提高了代码的耦合性
耦合性:事物与事物之间的依赖关系,互相影响的程度
开发原则:
高内聚:一个对象自身可以完成所有的东西
低耦合:对其他代码的依赖性非常
Java支持单继承,支持多层继承,不支持多重继承。
单继承:一个子类只能继承一个父类(一个孩子只能有一个亲爹)
多层继承:A类可以继承B类,B类可以继承C类,A类中拥有B、C类中的所有属性和方法。
如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类,意味着,所有java类都具有java.lang.Object类声明的功能
修饰符 | 同类中 | 同一个包中(子类和无关类) | 不同包(子类) | 不同包(无关类) |
---|---|---|---|---|
public |
✔️ | ✔️ | ✔️ | ✔️ |
protected |
✔️ | ✔️ | ✔️ | ❌ |
default |
✔️ | ✔️ | ❌ | ❌ |
private |
✔️ | ❌ | ❌ | ❌ |
含义:
public
(公共的)接口访问权限:使用public
关键字,就意味着被声明的成员或方法对所有类都是可以访问的。
protected
(受保护的)继承访问权限:使用protected
关键字,就意味着被声明的成员或方法,在子类以及相同包内的其他类都是可以访问的。
default
(默认的)包访问权限:即不写任何关键字,就意味着相同包内的其他类(包括子类)可以访问,包外都不可以访问。
private
(私有的)无法访问:使用private
关键字,就意味着被声明的成员或方法,除了本类,其他任何类都无法访问。
作用:访问权限范围越小,安全性越高
对于类的修饰,我们只能使用public 和 缺省。
final关键字:可以修饰类、方法、变量。
修饰类:表示一个最终类,表示不能有子类【不能被其他类继承】
修饰方法:表示一个最终方法,【该方法不能被重写】
修饰变量:表示一个最终变量,该【变量变成了常量】,就只能赋值一次,不能二次赋值了,变量名大写
String类是否可以被继承?
为什么需要重写?父类的方法对于子类来说并不适用。
子类中定义了与父类中一模一样的方法声明,但是实现方式不同,称为方法的重写。
override / overwrite覆写、覆盖
重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
应用:重写以后,当创建子类对象以后,通过子类对象调用父类中的同名同参方法时,实际执行的是子类重写父类的方法
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
public class Teacher extends Person {
String job;
void educate() {
System.out.println("会讲课");
}
@Override
void eat() {
System.out.println("讲课费体力,所以要多吃饭!");
}
@Override
void sleep() {
System.out.println("讲课费体力,所以要多休息!");
}
}
/*********************************************************/
public class PersonTest {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.eat();
teacher.sleep();
}
}
注意事项:
子类重写方法的方法名和形参列表与父类被重写方法的方法名和形参列表形同
子类重写方法的权限修饰符不小于父类重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
返回值类型
父类被重写方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
父类被重写方法的返回值是基本数据类型,则子类重写的方法的返回值必须是相同的基本数据类型
父类被重写方法的返回值是A类型,则子类重写的方法的返回值可以是A类或者A类的子类,比入Object与String
重写 & 重载 区别:(高频面试题)
重写:在子父类中,方法名相同,参数列表相同,与返回值类型有关(相同)。
重载:在同一个类中,方法名相同,参数列表不同,与返回值类型无关。
方法重写的检查:
关键字:@Override
让编译器帮助我们检查,当前的这个方法,究竟是否是对父类方法的重写。
说明:方法本身还是属于父类,只是在子类中重新实现了这个方法的内容。
注意:子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
示例:
public class Person {
String name;
int age;
int id = 1001;//身份证号
public void eat() {
System.out.println("人:吃饭");
}
}
public class Student extends Person {
String major;
int id = 1002;//学号
@Override
public void eat() {
System.out.println("学生:多吃有营养的食物");
}
void info() {
System.out.println("学生的id是:"+id);
}
}
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
student.info();
}
}
在子类中,既可以访问子类的成员变量,也可以访问父类的成员变量。
在子父类中定义了同名的成员变量,在子类中,根据就近原则来访问,因此父类的同名属性会被隐藏,但可以通过super
关键字来显示访问父类成员。
当子类重写了父类中的方法以后,我们想要在子类中方法中调用父类中被重写的方法时,则必须显式的使用super.方法的方式,表明调用的是父类中被重写的方法
void info() {
System.out.println("学生的id是:"+this.id);
System.out.println("父类的id是:"+super.id);
// 调用重写之后的方法
eat();
// 调用父类未重写方法
super.eat();
}
父类的构造方法不能被继承
父类的构造方法需要和父类的类名一致、子类的构造方法需要和子类类名一致,父类和子类的类名不一样。因此无法继承,名称有冲突。
子类的构造方法必须调用父类的构造方法
当生成子类对象时,Java 默认首先调用父类的不带参数的构造方法。
// 父类无参构造
public Person() {
System.out.println("父类无参构造");
}
// 子类无参构造
public Student() {
super();
System.out.println("子类无参构造");
}
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
}
}
如果父类没有无参构造,子类构造方法中必须显示调用父类构造方法,通过super();
,该语句必须作为构造方法中的第一条执行语句。
// 父类无参构造
// public Person() {
// System.out.println("父类无参构造");
// }
// 父类有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 子类有参构造
public Student(String name, int age, String major) {
super(name, age);
this.major = major;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", pid=" + super.id +
", major='" + major + '\'' +
", id=" + id +
'}';
}
public class StudentTest {
public static void main(String[] args) {
Student student = new Student("小明", 18, "Computer");
System.out.println(student);
}
}
总结:
/*
* super关键字的使用
* 1.super理解为:父类的
* 2.super可以用来调用:属性、方法、构造器
* 3.super的使用 调用属性和方法
* 3.1 我们可以在子类的方法或构造器中,通过super.属性或者super.方法的方式,显示的调用
* 父类中声明的属性或方法。但是,通常情况下,我们习惯省略super.
* 3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类的属性,则必须显式
* 的使用super.属性的方式,表明调用的是父类中的属性
* 3.3 特殊情况,当子类重写了父类中的方法以后,我们想要在子类中方法中调用父类中被重写的方法时,
* 则必须显式的使用super.方法的方式,表明调用的是父类中被重写的方法
* 4.super调用构造器
* 4.1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定构造器
* 4.2 super(形参列表)的使用,必须声明在子类构造器的首行!
* 4.3 在类的多个构造器中,至少有一个类的构造器中使用了super(形参列表)调用父类中的构造器
* 4.4 super的追溯不仅限于直接父类
* 4.5 super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
*/
子类对象实例化的全过程
1.从结果上看(继承性)
子类继承父类以后,就获取了父类中声明的属性和方法 ,创建子类的对象,在堆空间中,就会加载父类中声明的属性
2.从过程上看
当我们通过子类的构造器创建子类对象时,我们一定会直接或者间接的调用父类的构造器,进而调用父类的父类构造器,
直到调用了java.lang.Object类中空参的构造器位置,正是因为加载过所有的父类的结构,所以才可以看到内存中有
了父类中的结构,子类对象才可以进行调用。
明确:虽然创建了子类对象时调用了父类的构造器,但是自始至终就只创建过一个对象,即为new的子类对象
练习:
程序员类:
属性:姓名、工号、工资
方法:敲代码
项目经理类:
属性:姓名、工号、工资、奖金
方法:控制项目进度
定义程序员类和项目经理类
public class Programmer {
String name;
int id;
double salary;
public Programmer(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
void work() {
System.out.println("会敲代码!");
}
}
public class ProjectManager extends Programmer {
double bonus;
public ProjectManager(String name, int id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
@Override
void work() {
System.out.println("控制项目进度");
}
@Override
public String toString() {
return "ProjectManager{" +
"name='" + name + '\'' +
", id=" + id +
", salary=" + salary +
", bonus=" + bonus +
'}';
}
}
public class ProgrammerTest {
public static void main(String[] args) {
ProjectManager xiaoming = new ProjectManager("小明", 3, 24000, 3500);
System.out.println(xiaoming);
xiaoming.work();
}
}