目录
🚀前言

大家好!我是 EnigmaCoder 。
本文主要介绍java面向对象编程中的继承部分,包括定义、权限修饰符、特点、方法重写等。
🤔什么是继承?
在java中,继承是面向对象的三大特性之一,它允许一个类(称为子类或派生类)直接获取另一个类(称为父类或超类或基类)的属性和方法,从而实现代码重用和建立类之间的层次关系。
- 使用
extends
关键字完成继承,格式如下:
java
class A{} //父类A
class B extends A{} //子类B继承父类A
- 子类能继承父类的非私有成员(成员变量、成员方法)。
- 子类的对象是由子类和父类共同完成的。
代码案例:
java
class Person {
private String name;
private int age;
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
}
class Student extends Person{
}
class Test{
public static void main(String[] args){
Student stu =new Student();
stu.setName("张三");
stu.setAge("18");
System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge());
}
}
运行结果:
java
姓名:张三,年龄:18
可见,通过继承的功能可以直接将父类中的操作拿到子类中使用,即使子类中没有定义任何操作。
当然,子类也可以定义自己的属性和方法,可以通过子类扩展父类的功能,例如:
java
class Student extends Person{
private String school;
public void setSchool(String school){
this.school=school;
}
public String getSchool(){
return school;
}
}
这样,主方法中也能
setSchool()
和getSchool()
方法。
🌟权限修饰符
💯private 修饰符
- 访问权限:只能在定义它的类内部被访问。
- 使用场景:常用于隐藏类的内部数据和实现细节,防止外部直接访问和修改,保证数据的安全性和封装性。
- 示例代码:
java
class PrivateExample {
// 私有成员变量
private int privateVariable = 10;
// 私有方法
private void privateMethod() {
System.out.println("This is a private method.");
}
public void accessPrivateMembers() {
// 可以在类内部访问私有成员
System.out.println(privateVariable);
privateMethod();
}
}
public class Main {
public static void main(String[] args) {
PrivateExample example = new PrivateExample();
// 以下代码会报错,因为不能在类外部访问私有成员
// System.out.println(example.privateVariable);
// example.privateMethod();
example.accessPrivateMembers();
}
}
💯默认(无修饰符)
- 访问权限:也称为包访问权限,只能被同一个包中的类访问。
- 使用场景:当你希望某些类、方法或变量仅在同一个包内可见时使用,有助于组织和管理代码,将相关的类放在同一个包中并相互访问。
- 示例代码:
java
// 假设在 com.example 包下
package com.example;
class DefaultExample {
// 默认成员变量
int defaultVariable = 20;
// 默认方法
void defaultMethod() {
System.out.println("This is a default method.");
}
}
// 同一个包中的另一个类
class AnotherClassInSamePackage {
public static void main(String[] args) {
DefaultExample example = new DefaultExample();
// 可以在同一个包中访问默认成员
System.out.println(example.defaultVariable);
example.defaultMethod();
}
}
💯protected 修饰符
- 访问权限:可以被同一个包中的类访问,也可以被不同包中的子类访问。
- 使用场景:当你希望某个类的成员不仅能在同一个包中被访问,还能被不同包的子类继承和使用时使用。
- 示例代码:
java
// 假设在 com.parent 包下
package com.parent;
public class ParentClass {
// 受保护的成员变量
protected int protectedVariable = 30;
// 受保护的方法
protected void protectedMethod() {
System.out.println("This is a protected method.");
}
}
// 假设在 com.child 包下
package com.child;
import com.parent.ParentClass;
class ChildClass extends ParentClass {
public void accessProtectedMembers() {
// 可以在不同包的子类中访问受保护成员
System.out.println(protectedVariable);
protectedMethod();
}
}
public class Main {
public static void main(String[] args) {
ChildClass child = new ChildClass();
child.accessProtectedMembers();
}
}
💯public 修饰符
- 访问权限:可以被任何类访问,无论这些类是否在同一个包中,也无论它们是否存在继承关系。
- 使用场景:常用于定义公共的 API,如公共类、公共方法和公共常量等,方便其他类使用。
- 示例代码:
java
// 假设在 com.publicapi 包下
package com.publicapi;
public class PublicExample {
// 公共成员变量
public int publicVariable = 40;
// 公共方法
public void publicMethod() {
System.out.println("This is a public method.");
}
}
// 可以在任何包中的类中访问公共成员
public class Main {
public static void main(String[] args) {
PublicExample example = new PublicExample();
System.out.println(example.publicVariable);
example.publicMethod();
}
}
💯归纳
权限修饰符 | 本类 | 同一个包中的类 | 不同包中的子类 | 不同包中的非子类 |
---|---|---|---|---|
private |
可以访问 | 不可访问 | 不可访问 | 不可访问 |
默认(无修饰符) |
可以访问 | 可以访问 | 不可访问 | 不可访问 |
protected |
可以访问 | 可以访问 | 可以访问 | 不可访问 |
public |
可以访问 | 可以访问 | 可以访问 | 可以访问 |
🦜继承的特点
💯单继承
java只允许单继承,不能使用多重继承,即一个子类只能继承一个父类;也允许多层继承,即一个子类可以继承一个父类,这个父类也可以继承一个父类。
- 错误代码:
java
class A{}
class B{}
class C extends A,B{} //同时继承两个类,代码错误
- 多层继承:
java
class A{}
class B extends A{}
class C extends B{}
💯Object类
在 Java 里,Object
类是所有类的父类,即每个类都直接或间接地继承自 Object
类。这意味着 Java 中的任何对象都能调用 Object
类所定义的方法。
- 顶级父类:作为 Java 类层次结构的顶端,所有类默认继承它,无需显式声明。
- 多态基础:由于所有类都继承自
Object
类,所以可以将任何对象赋值给Object
类型的变量,为多态性提供了基础。
💯就近原则
在子类方法中访问其它成员,是依照就近原则的。
- 先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果都未找到就报错。
注意:如果子父类中,出现了重名成员,会优先使用子类的。如果想要在子类中使用父类的,就需要用到super
关键字。
格式如下:
java
super.父类成员变量/父类成员方法
代码示例如下:
java
public class Test {
public static void main(String[] args) {
Zi zi=new Zi();
zi.show();
}
}
class Fu {
String name="父类的name";
public void run(){
System.out.println("父类的run方法");
}
}
class Zi extends Fu{
String name="子类的name";
public void show(){
String name="show的name";
System.out.println(name);//show的name
System.out.println(this.name);//子类的name
System.out.println(super.name);//父类的name
super.run();//父类的run方法
run();//子类的run方法
}
public void run(){
System.out.println("子类的run方法");
}
}
运行结果:
java
show的name
子类的name
父类的name
父类的run方法
子类的run方法
✍️方法重写
当子类觉得父类中的某一个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写(声明不变,重新实现)。
- 代码示例:
java
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
- 运行结果:
java
猫吃鱼
这段代码中,子类
Cat
继承父类Animal
,子类中的eat
方法重写了父类中的eat
方法,所以输出结果为子类方法中的内容。
💯@Override
在 Java 里,@Override
属于注解,其用途是告知编译器,我们打算对父类中的一个方法进行重写。
用途:
- 编译器检查:借助 @Override 注解,编译器会核查该方法是否确实重写了父类的方法。若没有重写,编译器就会报错,这样能避免因拼写错误或者方法签名不匹配引发的潜在问题。
- 增强代码可读性:明确表明这个方法是对父类方法的重写,使代码的意图更加清晰。
💯方法重写的注意事项
- 子类重写父类方法时,访问权限必须大于或等于父类该方法的权限。(public>protected>默认)
- 重写的方法返回值类型必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写,如果重写会报错。
💯toString方法重写
在 Java 中,toString
方法是 Object
类中的一个方法,所有类都继承自 Object
类,因此都拥有 toString
方法。toString
方法的主要作用是返回对象的字符串表示形式。默认情况下,toString
方法返回的是对象的类名和哈希码,这通常对开发者和用户来说没有太大的实际意义。所以,我们经常会重写 toString
方法,以提供更有意义的对象信息。
注意:
- 直接输出对象,默认会调用
Object
类的toString
方法(可以省略不写toString
的方法)。
以下是一个重写 toString
方法的示例代码,我会为其添加详细的注释并进行简单说明:
java
// 定义一个名为 Person 的类,用于表示一个人的信息
class Person {
// 定义两个私有属性,分别表示人的姓名和年龄
private String name;
private int age;
// 构造函数,用于初始化 Person 对象的姓名和年龄
public Person(String name, int age) {
// 使用 this 关键字来区分成员变量和局部变量
this.name = name;
this.age = age;
}
// 重写 Object 类的 toString 方法,以提供更有意义的对象信息
@Override
public String toString() {
// 返回一个包含姓名和年龄信息的字符串
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class Test {
public static void main(String[] args) {
// 创建一个 Person 对象,并传入姓名和年龄
Person person = new Person("Alice", 25);
// 调用 person 对象的 toString 方法,并将结果打印输出
System.out.println(person);
}
}
代码说明:
- 类的定义 :定义了一个名为
Person
的类,该类有两个私有属性name
和age
,分别表示人的姓名和年龄。 - 构造函数 :
Person
类的构造函数用于初始化name
和age
属性。 - 重写
toString
方法 :在Person
类中重写了toString
方法,使用@Override
注解告诉编译器这是一个重写的方法。重写后的toString
方法返回一个包含name
和age
信息的字符串。 - 主方法 :在
Test
类的main
方法中,创建了一个Person
对象,并调用其toString
方法将结果打印输出。由于System.out.println
方法会自动调用对象的toString
方法,所以可以直接传入person
对象。
通过重写
toString
方法,我们可以方便地获取对象的详细信息,这在调试和日志记录中非常有用。
补充:
- Object 类中
toString
方法的源码如下:
java
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
🐧子类构造器的特点
- 子类中的全部构造器,都必须先调用父类的构造器,再执行自己。
- 使用
super(...)
可以调用父类有参数构造器,为对象中包含父类这部分的成员变量进行赋值。
java
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
}
}
class Zi extends Fu {
public Zi() {
super(666);//指定调用父类的有参构造器
System.out.println("子类构造器");
}
}
class Fu{
public Fu() {
System.out.println("父类构造器");
}
public Fu(int a) {
System.out.println("父类有参构造器");
}
}
运行结果:
java
父类有参构造器
子类构造器
注意:super关键字必须写在首行。
💯this与super的区别
比较项 | this | super |
---|---|---|
基本含义 | 指代当前对象的引用,即调用方法或构造器的对象本身 | 代表父类(直接超类)的引用,用于访问父类中被覆盖的成员或构造器 |
主要作用 | 1. 区分成员变量与局部变量(两者同名时);2. 在构造器中调用同一类的其他构造器(需为构造器首行);3. 在方法中引用当前对象 | 1. 在子类构造器中调用父类的构造器(需为子类构造方法首行);2. 访问父类中被覆盖的成员变量或方法(子类重写父类成员时) |
使用限制 | 1. 不能在静态方法或静态代码块中使用;2. 构造器中调用其他构造方法时,只能出现一次且必须为第一行 | 1. 子类构造器若未显式调用 super() ,编译器会自动插入无参的父类构造器(若父类存在无参构造器);2. 若父类没有无参构造器,子类必须显式调用父类的有参构造器(通过 super(参数) ) |
本质区别 | 操作当前类的成员(包括从父类继承的成员) | 操作父类的成员(即使子类重写了父类成员,也能通过 super 访问父类的版本) |
适用场景 | 访问当前对象成员或构造器 | 访问父类成员或构造器 |