1继承的介绍与基本使用
继承主要指一种类与类的关系,其中有父类和子类,一个父类可以继承给多个子类,但是一个子类只能继承一个父类。
那么在什么情况下主要使用继承呢,我们在写项目时候会发现我们需要创造很多类,例如游戏角色,每个游戏角色都去创建一个类但是每个角色都有血量与技能这就是类之间的共性,我们就可以把共性提取出来定义一个父类,然后让很多子类去继承父类,大大减少代码量,提高代码复用性。
继承代码形式:class A extends B{}
例如:
父类Animal
public class Animal {
// 成员变量
protected String name;
// 构造方法
public Animal(String name) {
this.name = name;
}
// 成员方法
public void eat() {
System.out.println(name + " is eating.");
}
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
子类Dog
public class Dog extends Animal {
// 子类特有的成员变量
private int legcount;
// 构造方法
public Dog(String name, int legcount) {
super(name); // 调用父类的构造方法
this.legcount = legcount;
}
// 子类特有的方法
public void bark() {
System.out.println(name + " is have"+legcount+" 条腿,还会汪汪汪!");
}
}
主方法
public class Main {
public static void main(String[] args) {
// 创建父类对象
Animal animal = new Animal("任何动物");
animal.eat();
animal.sleep();
// 创建子类对象
Dog dog = new Dog("吉娃娃", 4);
dog.eat(); //调用继承方法
dog.sleep(); // 调用继承的方法
dog.bark(); // 调用子类特有的方法
}
}
运行结果如图:
2成员变量和成员方法访问特点
这里说明一下等式左右类不同是多态的知识点即 父类类型 对象名=new 子类类型();的形式。
1访问先后问题
子父类成员方法或成员变量同名
若是成员变量则是等式左值是父类就先访问父类成员变量,若是子类反之
若是成员方法则是等式右边new的是父类就先访问父类,子类反之
2访问权限问题
子类可访问继承父类的变量和方法,父类不可访问子类特有的变量和方法。
具体看是什么类,new后面的是子类就是子类,是父类就是父类。
代码示例:
父类
class Parent {
String name = "父类成员变量";
void display() {
System.out.println("父类方法");
}
}
子类
class Child extends Parent {
String name = "子类成员变量"; // 隐藏父类同名变量
@Override
void display() {
System.out.println("子类重写方法");
}
void childMethod() {
System.out.println("子类特有方法");
}
}
测试类
public class Main {
public static void main(String[] args) {
// 1. 成员访问顺序验证
System.out.println("===== 访问顺序验证 =====");
Parent parent = new Parent();
System.out.println("父类实例访问成员变量: " + parent.name); // 父类变量
parent.display(); // 父类方法
Child child = new Child();
System.out.println("子类实例访问成员变量: " + child.name); // 子类变量
child.display(); // 子类方法
Parent polymorphic = new Child(); // 多态核心语句
System.out.println("多态访问成员变量: " + polymorphic.name); // 父类变量(编译时类型决定)
polymorphic.display(); // 子类方法(运行时类型决定)
// 2. 访问权限验证
System.out.println("\n===== 访问权限验证 =====");
// 父类引用可访问子类重写方法
polymorphic.display(); // 允许:子类重写父类方法
// 父类引用不能访问子类特有成员
// polymorphic.childMethod(); // 编译错误:Parent类没有childMethod()
// 向下转型后可访问子类特有成员
if(polymorphic instanceof Child) {
Child realChild = (Child) polymorphic;
System.out.println("向下转型后访问子类变量: " + realChild.name);
realChild.childMethod(); // 正常调用
}
}
}
运行结果如图
3方法的重写
1重写基本形式
前提条件:继承
基本形式就是子类与父类成员方法同名,但是子类中方法有不同于父类的新功能。
父类
class Parent {
String name = "父类成员变量";
void display() {
System.out.println("父类方法");
}
}
子类
class Child extends Parent {
String name = "子类成员变量"; // 隐藏父类同名变量
@Override
void display() {
System.out.println("子类重写方法");
}
void childMethod() {
System.out.println("子类特有方法");
}
}
2使用场景
一般是项目版本更新的部分功能改造,那么有人会问为什么不直接修改源代码,因为版本功能靠类的方法实现而方法一般都有很多子类,直接修改源类影响较大,所以我们new一个新子类继承源类,其他保持不变只重写需要更新的功能方法。
3注意事项
1子类重写方法权限必须大于等于父类权限(这里根据使用场景也可以理解的新功能肯定要覆盖原来的功能的,不能重写了调用的时候权限低于父类直接调用父类的了,那就白重写了)
2访问权限即public-protected-默认-private(默认一般不写任何修饰符,写了defult反而不行)
3子类重写方法必须方法名与父类相同且参数列表相同(不同就是新方法了很好理解)
4父类私有权限方法无法重写、构造方法无法重写、静态方法无法重写--这里作者并不清楚为什么该怎么理解其中的原因,欢迎大佬补充。
5子类重写父类方法之后,返回值类型应该是父类方法返回值类型的子类类型--这个同上深层原因待补充。
父类
class Parent {
String name = "父类成员变量";
public Parent display() {//返回值是父类类型
System.out.println("父类方法");
return null;
}
}
子类
class Child extends Parent {
String name = "子类成员变量"; // 隐藏父类同名变量
@Override
public Child display() {//返回值是子类类型
System.out.println("子类重写方法");
return null;
}
void childMethod() {
System.out.println("子类特有方法");
}
}
4继承中构造方法的特点
new子类对象时要先初始化父亲(先走父类无参构造方法)
原因:
每个子类构造方法无论是子类的有参构造还是无参构造第一行默认会有一个super();即调用父类无参构造,即使不写jvm会自动提供一个(jvm虚拟机)。
代码示例
父类
class Parent {
// 父类无参构造方法
public Parent() {
System.out.println("父类无参构造方法被调用");
}
// 父类有参构造方法
public Parent(String name) {
System.out.println("父类有参构造方法被调用,参数: " + name);
}
}
子类
class Child extends Parent {
// 子类无参构造方法
public Child() {
// 这里实际上有一个隐式的super()调用,即使不写,JVM也会自动添加
System.out.println("子类无参构造方法被调用");
}
// 子类有参构造方法
public Child(String name) {
// 这里实际上有一个隐式的super()调用,即使不写,JVM也会自动添加
System.out.println("子类有参构造方法被调用,参数: " + name);
}
}
Main 类
public class Main {
public static void main(String[] args) {
System.out.println("创建子类对象...");
Child child1 = new Child();
System.out.println("\n创建带参数的子类对象...");
Child child2 = new Child("小明");
}
}
运行结果
5super和this关键字的使用
1super
代表父类引用,可以调用父类中的成员
基本使用形式
1调用父类构造方法->在子类中重写
super();调用父类无参构造
super(参数);调用父类有参构造
2调用父类成员变量
super.成员变量名;
3调用父类成员方法
super.成员方法名;
代码示例
父类
class Parent {
protected String name;
protected int age;
// 父类无参构造方法
public Parent() {
System.out.println("父类无参构造方法被调用");
}
// 父类有参构造方法
public Parent(String name, int age) {
this.name = name;
this.age = age;
System.out.println("父类有参构造方法被调用,参数: " + name + ", " + age);
}
// 父类成员方法
public void showInfo() {
System.out.println("父类方法: 姓名=" + name + ", 年龄=" + age);
}
// 父类另一个方法
public void parentMethod() {
System.out.println("这是父类的特定方法");
}
}
子类
class Child extends Parent {
private String hobby;
// 子类构造方法 - 使用super调用父类有参构造
public Child(String name, int age) {
// 使用super调用父类有参构造方法
super(name, age); // 必须放在第一行
this.hobby = "编程";
System.out.println("子类构造方法被调用");
}
// 子类方法 - 使用super访问父类成员变量和方法
public void display() {
// 使用super访问父类成员变量
System.out.println("使用super.name访问父类name: " + super.name);
// 使用super调用父类方法
super.parentMethod();
}
// 重写父类方法
@Override
public void showInfo() {
// 先调用父类的showInfo方法
super.showInfo();
// 然后添加子类特有的信息
System.out.println("子类方法: 爱好=" + hobby);
}
}
Main类
public class Main {
public static void main(String[] args) {
System.out.println("=== 创建子类对象 ===");
Child child = new Child("张三", 25);
System.out.println("\n=== 调用子类方法 ===");
child.display();
System.out.println("\n=== 调用子类重写的方法 ===");
child.showInfo();
}
}
运行结果
2this
代表引用当前对象成员
作用:区分重名的成员变量和局部变量和调用当前对象的成员。
基本使用形式
1调用当前对象的构造方法-在构造方法中写
this();调用当前对象的无参构造
this(参数);调用当前对象的有参构造
2调用当前对象的成员
this.成员变量名;
3调用当前对象的成员
this.成员方法名;
代码示例
这里没有继承的知识点不用分父子类
public class Main {
public static void main(String[] args) {
System.out.println("=== 创建Person对象 ===");
Person person1 = new Person("李四", 30);
System.out.println("\n=== 创建另一个Person对象 ===");
Person person2 = new Person("王五");
System.out.println("\n=== 调用对象方法 ===");
person1.displayInfo();
person2.displayInfo();
System.out.println("\n=== 使用方法链 ===");
person1.setName("赵六").setAge(35).displayInfo();
}
}
class Person {
private String name;
private int age;
// 无参构造方法
public Person() {
// 使用this调用当前类的有参构造方法
this("未知", 0); // 必须放在第一行
System.out.println("无参构造方法被调用");
}
// 有参构造方法 - 只接收名字
public Person(String name) {
// 使用this调用当前类的另一个有参构造方法
this(name, 18); // 设置默认年龄为18
System.out.println("单参数构造方法被调用");
}
// 有参构造方法 - 接收名字和年龄
public Person(String name, int age) {
// 使用this区分成员变量和参数
this.name = name; // this.name指成员变量,name指参数
this.age = age; // this.age指成员变量,age指参数
System.out.println("双参数构造方法被调用");
}
// 设置姓名 - 返回当前对象以支持方法链
public Person setName(String name) {
this.name = name; // 使用this区分成员变量和参数
return this; // 返回当前对象
}
// 设置年龄 - 返回当前对象以支持方法链
public Person setAge(int age) {
this.age = age; // 使用this区分成员变量和参数
return this; // 返回当前对象
}
// 显示信息
public void displayInfo() {
// 使用this调用当前对象的成员方法
this.printHeader();
System.out.println("姓名: " + this.name); // 使用this访问成员变量
System.out.println("年龄: " + this.age); // 使用this访问成员变量
this.printFooter();
}
// 辅助方法 - 打印头部
private void printHeader() {
System.out.println("===== 个人信息 =====");
}
// 辅助方法 - 打印底部
private void printFooter() {
System.out.println("===================");
}
// 获取当前对象信息
public String getInfo() {
// 使用this调用当前对象的成员方法
return "Person[name=" + this.name + ", age=" + this.age + "]";
}
// 比较两个Person对象是否相同
public boolean isSamePerson(Person other) {
// 使用this引用当前对象
return this == other;
}
}
运行结果
注意事项
super()和this()调用构造方法都只能写在第一行,并且不能同时调用---
6继承特点
1继承只支持单继承,不支持多继承,一个子类只能继承一个父类
2一个父类可以继承给多个子类
3继承支持多层继承有父类,可以有父类的父类或子类的子类
(以上三点都可以根据现实的父子关系理解 非常简单)
4构造方法,静态方法、私有方法都可以继承不能重写---未理解深层原因
7如何使用父类中的私有化成员变量
1通过get和set成员方法修改使用
2通过构造方法间接使用
代码示例
父类
class Parent {
// 私有成员变量
private String name;
private int age;
// 无参构造方法
public Parent() {
System.out.println("Parent无参构造方法被调用");
}
// 有参构造方法 - 用于初始化私有成员变量
public Parent(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Parent有参构造方法被调用");
}
// Get和Set方法 - 提供对私有成员变量的访问
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0 && age < 150) { // 可以添加验证逻辑
this.age = age;
} else {
System.out.println("年龄无效: " + age);
}
}
// 显示信息的方法
public void displayInfo() {
System.out.println("姓名: " + name + ", 年龄: " + age);
}
}
子类
class Child extends Parent {
private String childId;
// 子类无参构造方法
public Child() {
super(); // 调用父类无参构造方法
this.childId = "未设置";
System.out.println("Child无参构造方法被调用");
}
// 子类有参构造方法 - 通过super调用父类有参构造方法
public Child(String name, int age) {
super(name, age); // 调用父类有参构造方法,间接设置父类私有成员
this.childId = "C" + System.currentTimeMillis(); // 生成一个简单的ID
System.out.println("Child有参构造方法被调用");
}
// 子类有参构造方法 - 接收所有参数
public Child(String name, int age, String childId) {
super(name, age); // 调用父类有参构造方法
this.childId = childId;
}
// Get和Setf方法
public String getChildId() {
return childId;
}
public void setChildId(String childId) {
this.childId = childId;
}
// 重写显示信息的方法
@Override
public void displayInfo() {
// 通过getter方法获取父类私有成员
System.out.println("子类信息 - 姓名: " + getName() +
", 年龄: " + getAge() +
", ID: " + childId);
}
// 子类特有的方法
public void play() {
System.out.println(getName() + "正在玩耍"); // 通过getter获取父类私有成员
}
}
Main类
public class Main {
public static void main(String[] args) {
System.out.println("=== 通过getter/setter方法使用父类私有成员 ===");
Child child1 = new Child();
child1.setName("张三"); // 使用setter设置父类私有成员
child1.setAge(20); // 使用setter设置父类私有成员
System.out.println("姓名: " + child1.getName()); // 使用getter获取父类私有成员
System.out.println("年龄: " + child1.getAge()); // 使用getter获取父类私有成员
System.out.println("\n=== 通过构造方法使用父类私有成员 ===");
Child child2 = new Child("李四", 22); // 通过构造方法设置父类私有成员
child2.displayInfo();
System.out.println("\n=== 修改父类私有成员 ===");
child2.setName("王五");
child2.setAge(25);
child2.displayInfo();
}
}
运行结果