继承
1.继承是类与类之间的一种关系
2.多个类继承单独的某个类,多个类就可以使用单独的这个类的属性和行为了
3.多个类称为子类(派生类)单独的这个类称为父类(基类或超类)
使用继承的好处:提高代码的复用性。
People(父类)
Student(子类) Teacher(子类)
学生和老师都是人类,所以People是Student Teacher父类
继承的格式:
public class Student extends People{
}
extends的意思是“扩展”。子类是父类的扩展
继承后子类的特点:
1.子类继承父类,子类可以得到父类的属性和行为,子类可以使用
2.Java中子类更强大
继承设计的规范
子类们相同的特征(共同属性,共性方法)放在父类中定义,子类独有属性和行为应该定义在子类自己里面。
继承的特点:
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器
- Java是单继承模式,一个类只能继承一个直接父类
- Java不支持多继承,但是支持多层继承
- Java中所有的类是Object类的子类
继承后:成员变量,成员方法的访问特点:
在子类方法中访问成员(成员变量,成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
如果子父类中,出现了重名的成员,会优先使用子类的,如果一定要在子类中使用父类的怎么办?
- 可以通过super关键字,指定访问父类的成员 格式:super.父类成员变量/成员方法
- 方法重写(Override) 在继承中子类出现了和父类一摸一样的方法声明,我们就称子类这个方法是重写的方法。
方法重写的应用场景:
父类
/**
* @Auther: Curran
* @Date: 2022/4/27 - 04 - 27 - 19:33
* @Description: PACKAGE_NAME
* @version: 1.0
*/
public class Phone {
public void call(){
System.out.println("打电话");
}
public void sendMessage(){
System.out.println("发送短信");
}
}
子类
public class NewPhone extends Phone{
//方法重写
@Override
public void call(){
super.call();//调用父类
System.out.println("支持视频通话");
}
@Override
public void sendMessage(){
super.sendMessage();
System.out.println("支持发送图片");
}
}
@Override重写注解
- @Override是放在重写后的方法上作为重写是否正确的校验注解
- 加上该注解后,如果重写错误编译阶段会出现错误提示
- 建议重写方法都加@Override注解代码安全
方法重写注意事项和要求:
- 重写方法的名称,形参列表必须与被重写方法的名称和参数列表一致
- 私有方法不能被重写
- 子类重写父类时访问权限必须大于或者等于父类(空白<protected<public)
- 子类不能重写父类的静态方法,如果重写会报错
方法重写与方法重载:
super关键
位置 | 方法名 | 参数表 | 返回值 | 访问修饰符 | |
方法重写 | 子类 | 相同 | 相同 | 相同或是其子类 | 不能比父类更严格 |
方法重载 | 同类 | 相同 | 不相同 | 无关 | 无关 |
字:
▪super是直接父类对象的引用。
▪可以通过super来访问父类中被子类覆盖的方法或属性。
▪普通方法:
▪没有顺序限制。可以随便调用。
▪构造函数中:
▪任何类的构造函数中,若是构造函数的第一行代码没有显式的调用super(...);那么 Java默认都会调用super();作为父类的初始化函数。 所以你这里的super();加不加 都无所谓
示例代码:
public class Test {
public static void main(String[] args) {
new ChildClass().f();
}
}
class FatherClass {
public int value;
public void f(){
value = 100;
System.out.println
("FatherClass.value="+value);
}
}
class ChildClass extends FatherClass {
public int value;
public void f() {
super.f();
value = 200;
System.out.println
("ChildClass.value="+value);
System.out.println(value);
System.out.println(super.value);
}
}
子类继承父类后 构造器的特点 :
子类中所有的构造器默认都会先访问父类中无参的构造器,在执行自己
为什么会先访问父类的构造器:
- 子类在初始化的时候有可能会使用到父类中的数据如果父类没有完成初始化,子类将无法使用父类的数
- 子类初始化之前,一定要调用父类构造器,先完成父类数据空间的初始化
怎么调用父类的构造器?
子类构造器的第一行语句默认都是:super()不写也存在
子类构造器访问父类有参构造器
super调用父类有参构造器的作用,初始化继承自父类的数据
如果父类中,没有无参数构造器,只有有参构造器会出现什么现象?
会报错,因为子类默认都是调用父类无参构造器的
如何解决?
子类构造器中可以通过书写super(...)手动调用父类的有参构造器
this和super详情:
- 子类通过this()去调用本类的其他构造器,本类其他构造器会通过super去手动调用父类的构造器,最终还是会调用父类的构造器的
- 注意:this()和super()都只能放在构造器的第一行,所以二者不能共存在同一个构造器中
final关键字
作用:
- final关键字是最终的意思,可以修饰方法,变量,类
- 修饰方法:表明该方法是最终方法,不能被重写
- 修饰变量:表明改变了第一次赋值后不能再次被赋值(有且只能被赋值一次)
- 修饰类:表明该类是最终类,不能被继承
例: 两种赋值方法
①public static final String schoolName = "少年";
②public static final String schoolName ;
static{
schoolName = "少年";
}
final修饰变量的注意:
- final修饰的变量是基本类型,那么变量存储的数据值,不能发生改变 final double rate = 3.14;不能第二次赋值
- final修饰的变量是引用类型,那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。 final int[] arr = {10,20,30}; arr[1] = 200;可以更改内容
常量
- 常量是使用了public static final修饰的成员变量必须有初始化值,而且执行的过程中其值不能被改变。
- 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
常量的命名规范:英文单词全部大写,多个单词下划线连接起来
public static final String SCHOOL_NAME = "......";
常量的执行原理:
- 在编译阶段会进行”宏替换“ 把使用常量的地方全部替换成真实的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
抽象类:
- 某个父类知道其所有子类要完成某功能,但是每个子类完成情况都不一样,父类就只定义该功能的基本要求,具体实现由子类完成这个类就可以是一个抽象类,抽象类其实是一种不完全的设计图。
- 抽象类必须使用abstract修饰: 修饰符 abstract class 类名{}
抽象方法:
- 就是抽象类中定义的子类必须要完成的功能的基本要求 格式:修饰符 abstract 返回值类型 方法名称(形参列表);
- 设有方法体,只有方法签名必须abstract修饰
抽象类的作用是什么样的?什么时候定义抽象类?
- 可以被子类继承,充当模板的同时也可以提高代码复用率
- 如果父类知道子类要完成某个功能,实现要交给子类时
继承抽象类有哪些要注意?
- 一个类如果继承了抽象类那么这个类必须重写完抽象类的全部抽象方法
- 否则这个类也必须定义成抽象类
特征:
- 有得有失,得到了抽象方法,失去了创建对象的能力
- 抽象类为什么不能创建对象
- 类有的成员(成员变量,方法,构造器)抽象类都具备
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
- 不能用abstract修饰变量,代码块,构造器
示例:
父类
public abstract class Pet {
private String name;
private int age;
public Pet(){
}
public Pet(String name,int age){
this.name = name;
this.age = age;
}
//抽象方法
public abstract void print();
public void show(){
System.out.println("Pet show... ...");
}
}
子类
public class Dog extends Pet{
private String gender;
@Override
public void print() {
System.out.println("dog print... ...");
}
}
抽象类:
* 1、创建抽象类的时候需要添加 abstract 的关键字
* 2、不能进行实例化,也就是不能new对象
* 3、抽象类中的某些方法需要子类进行更丰富的实现,父类实现没有意义,此时可以将抽象类
* 中的方法定义为抽象方法,没有具体的实现,只包含方法名称,返回值,参数列表,访问修饰符
* 4、使用abstract关键字修饰的方法叫做抽象方法,可以不写方法的实现
* 5、子类在继承抽象父类的时候,必须要将父类中的抽象方法进行实现或者将子类也定义为抽象类
* 6、有抽象方法的一定是抽象类,但是抽象类中不一定包含抽象方法
public class PetTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.print();
// Pet pet = new Pet();
}
}
final用法
/*
* final的使用:
* final可以修饰变量:
* 表示变量的值不可变
* final可以修饰方法:
* 表示方法不可以被重写
* final可以修饰类:
* 表示类不可以被继承
*
*
*/
public /*final*/ class FinalDemo {
private String name;
// public FinalDemo(){
// super();
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static final int num = 10;
public final void show(){
System.out.println("FinalDemo... ...");
}
public static void main(String[] args) {
// FinalDemo fd = new FinalDemo();
// fd.num = 20;
// FinalDemo.num = 20;
// num = 20;
// System.out.println(num);
final FinalDemo fd = new FinalDemo();
fd.show();
// fd = new FinalDemo();
fd.setName("jason");
System.out.println(fd.getName());
}
}
final和absract是什么关系?
- 互反关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承
- 抽象方法定义通用功能让子类重写,final定义的子类不能重写
抽象类的模板方法模式:
使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候
模板方法的实现步骤:
把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码
模板方法建议使用final修饰(模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法就失效了)
模板方法解决了什么问题?
- 极大的提高了代码的复用性
- 模板方法已经定义了通用结构模板,不能确定的定义成抽象的方法
- 使用者只需要关心自己需要实现的功能即可
Object类:
- 一个类要么默认继承了Object类,要么简介继承了Object类,Object类是java中所有类的的父类
- Object类的方法一切子类都可以直接使用的,所以我们要学习Object类的方法
源码解析(涉及到Object中常用的10个方法)
getClass()、hashCode()、equals(Object)、clone()、toString()、notify()、notifyAll()、wait()、wait(long)、wait(long,int)
- toString方法(默认是返回当前对象在堆内存中的地址信息:类的权限名@内存地址)
toString方法 Student s = new Student(); String rs = s.toString(); System.out.println(rs); /**/ Student s = new Student(); System.out.println(s.toString());
toString存在的意义
父类toString()方法 存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息
4.equals方法:(默认是比较当前对象与另一个对象的地址是否相同,相同返回为true,不同返回false)
Student s1 = new Student("周雄","男",19);
Student s2 = new Student("周雄","男",19);
System.out.println(s1.equals(s2));//如果不重写equals方法 将与== 一样比较的是地址
System.out.println(s1 == s2); //比较的是地址
equals存在的意义
父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则
重写equals方法与toString方法:
/**
* @Auther: Curran
* @Date: 2022/4/30 - 04 - 30 - 19:32
* @Description: TestDemo
* @version: 1.0
*/
public class Student {
String name;
String sex;
int age;
public Student(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
public Student() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name) &&
Objects.equals(sex, student.sex);
}
@Override
public int hashCode() {
return Objects.hash(name, sex, age);
}
}