继承
在Java中,继承是一种重要的面向对象编程概念,允许一个类(子类)从另一个类(父类)继承属性和方法。子类可以继承父类的非私有属性和方法,同时可以添加自己的属性和方法。
子类继承父类:private 构造方法 是不能被继承的。但是子类可以显示的调用父类的构造super
语法
public class ParentClass {
// 父类的属性和方法
}
public class ChildClass extends ParentClass {
// 子类的属性和方法
}
示例代码
// 父类
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void bark() {
System.out.println(name + " is barking.");
}
}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog("Buddy");
myDog.eat();
myDog.bark();
}
}
在上面的示例中,Animal类是父类,Dog类是子类。Dog类继承了Animal类的属性和方法,并添加了自己的bark方法。在Main类中,创建了一个Dog对象并调用了父类和子类的方法。
在上述代码中,Animal类是超类(super class),也被称为父类(parent class)或基类(base class)。它是被其他类继承的类。
Dog类是子类(subclass),也被称为扩展类(extended class)。它继承了Animal类的属性和方法,并且可以添加自己的属性和方法。
在Dog类的定义中,使用了extends关键字来指定Animal类作为父类。这意味着Dog类继承了Animal类的所有非私有属性和方法。通过使用super关键字,Dog类可以访问父类的构造方法和成员变量。
在Main类中,创建了一个Dog对象myDog,并调用了父类Animal的eat方法和子类Dog的bark方法。这是因为子类继承了父类的方法,可以直接使用。
继承树
继承树是指通过继承关系组织起来的类的层次结构。在继承树中,顶部是最通用的类,也称为根类或基类,而底部是更具体的类,也称为子类。子类可以继承父类的属性和方法,同时可以添加自己的属性和方法,形成了一种层级关系。
在一个继承树中,一个类可以有多个子类,但通常只有一个父类。父类可以有多个子类,这种关系被称为单继承和多继承。在Java中,类是单继承的,即一个类只能有一个直接父类。
通过继承树,我们可以更好地组织和管理代码,实现代码的重用和扩展。父类中定义的通用属性和方法可以被多个子类共享,避免了重复编写相同的代码。同时,子类可以根据自身的需求进行扩展和定制,实现了代码的灵活性和可维护性。
继承树的顶部是Object类,它是所有类的根类。所有类都直接或间接地继承自Object类。在Java中,如果一个类没有显式指定父类,则默认继承Object类。
继承树的设计要遵循面向对象编程的原则,如单一职责原则、开闭原则等,以确保代码的可扩展性和可维护性。通过合理设计和使用继承,可以更好地组织代码结构,提高代码的复用性和可读性。
protected 关键字
在 Java 中,protected 是一种访问修饰符,用于限制类的成员(属性和方法)的访问范围。使用 protected 修饰的成员可以被同一包内的类访问,也可以被子类访问,但不能被其他包中的类访问。
语法示例
public class ParentClass {
protected String protectedField;
protected void protectedMethod() {
// 方法实现
}
}
public class ChildClass extends ParentClass {
public void accessProtectedMember() {
System.out.println(protectedField);
protectedMethod();
}
}
在上面的示例中,ParentClass 类中的 protectedField 和 protectedMethod 都被修饰为 protected。这意味着这两个成员可以被 ChildClass 类中的方法访问。
ChildClass 类继承了 ParentClass 类,因此可以访问 ParentClass 中被 protected 修饰的成员。在 ChildClass 类的 accessProtectedMember 方法中,可以直接访问 protectedField 和调用 protectedMethod。
代码示例
// 父类
public class ParentClass {
protected String parentName;
protected void parentMethod() {
System.out.println("This is a method in ParentClass.");
}
}
// 子类
public class ChildClass extends ParentClass {
public void displayParentInfo() {
parentName = "Parent";
System.out.println("Parent's name: " + parentName);
parentMethod();
}
}
public class Main {
public static void main(String[] args) {
ChildClass child = new ChildClass();
child.displayParentInfo();
}
}
在上述示例中,ParentClass 类中的 parentName 和 parentMethod 被修饰为 protected。ChildClass 类继承了 ParentClass 类,并在 displayParentInfo 方法中访问了父类的 protected 成员。
在 Main 类中,创建了一个 ChildClass 对象 child,并调用了 displayParentInfo 方法。该方法中通过子类对象访问了父类的 protected 成员,实现了对父类成员的访问和调用。
通过 protected 关键字,子类可以访问父类的 protected 成员,实现了在继承关系中对成员的保护和共享。
super 关键字
在 Java 中,super 是一个关键字,用于引用父类的构造方法、成员变量和方法。通过 super 关键字,子类可以访问父类的构造方法、成员变量和方法,实现对父类的调用和重用。
示例代码
// 父类
public class ParentClass {
String parentName;
public ParentClass(String name) {
this.parentName = name;
}
public void displayParentInfo() {
System.out.println("Parent's name: " + parentName);
}
}
// 子类
public class ChildClass extends ParentClass {
String childName;
public ChildClass(String parentName, String childName) {
super(parentName); // 调用父类的构造方法
this.childName = childName;
}
public void displayChildInfo() {
System.out.println("Child's name: " + childName);
}
public void displayParentInfo() {
super.displayParentInfo(); // 调用父类的方法
System.out.println("Child's parent name: " + parentName);
}
}
public class Main {
public static void main(String[] args) {
ChildClass child = new ChildClass("Parent", "Child");
child.displayParentInfo(); // 调用子类的方法
child.displayChildInfo(); // 调用子类的方法
}
}
在上述示例中,ParentClass 类有一个构造方法和一个方法用于显示父类信息。ChildClass 类继承了 ParentClass 类,并添加了自己的构造方法和方法用于显示子类信息。
在 ChildClass 类的构造方法中,通过 super(parentName) 调用了父类 ParentClass 的构造方法,初始化了父类的成员变量。这样子类就可以继承父类的属性。
在 ChildClass 类的 displayParentInfo 方法中,通过 super.displayParentInfo() 调用了父类 ParentClass 的方法,实现了对父类方法的重用。同时,子类也可以添加自己的方法,如 displayChildInfo 方法用于显示子类信息。
在 Main 类中,创建了一个 ChildClass 对象 child,并调用了子类和父类的方法,展示了 super 关键字的使用方式。
通过 super 关键字,子类可以访问父类的构造方法、成员变量和方法,实现了对父类的调用和重用,同时也可以在子类中添加自己的属性和方法,实现了代码的扩展和灵活性。
当父类没有默认的构造方法时,子类必须显式调用父类的构造方法,并提供合适的参数。这是因为子类的构造方法需要初始化父类的成员变量,而没有默认的构造方法时,编译器无法自动调用父类的构造方法。
子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。如果父类有多个构造方法,子类必须选择一个合适的构造方法来调用。
下面是一个示例代码:
// 父类
public class ParentClass {
private int age;
public ParentClass(int age) {
this.age = age;
}
public void displayAge() {
System.out.println("Age: " + age);
}
}
// 子类
public class ChildClass extends ParentClass {
private String name;
public ChildClass(int age, String name) {
super(age); // 调用父类的构造方法
this.name = name;
}
public void displayName() {
System.out.println("Name: " + name);
}
}
public class Main {
public static void main(String[] args) {
ChildClass child = new ChildClass(10, "John");
child.displayAge(); // 调用父类的方法
child.displayName(); // 调用子类的方法
}
}
在上述示例中,父类 ParentClass 有一个带有参数的构造方法,用于初始化父类的成员变量 age。子类 ChildClass 继承了父类,并在自己的构造方法中调用了父类的构造方法 super(age),以初始化父类的成员变量。子类也添加了自己的成员变量 name 和方法 displayName。
在 Main 类中,创建了一个 ChildClass 对象 child,并调用了子类和父类的方法,展示了子类调用父类构造方法的过程。
通过显式调用父类的构造方法,子类可以初始化父类的成员变量,并在子类中添加自己的成员变量和方法,实现了代码的扩展和灵活性。
一般建议:如果给类中编写构造方法,则手动编写一个无参构造,防止报错
阻止继承
有时候,我们希望某个类不被其他类继承,即不允许创建该类的子类。在 Java 中,可以通过使用 final 关键字来阻止继承。当一个类被声明为 final 时,它不能被其他类继承。
示例代码
// final 类
final class FinalClass {
// 类的成员和方法
}
// 试图继承 final 类,会导致编译错误
// class ChildClass extends FinalClass {
// // 子类的成员和方法
// }
在上面的示例中,FinalClass 类被声明为 final,因此不能被其他类继承。如果尝试创建一个子类 ChildClass 继承 FinalClass,编译器会报错,因为 FinalClass 是一个 final 类,不允许被继承。
通过将类声明为 final,可以有效地阻止其他类对该类的继承,确保该类的独立性和完整性。这在某些情况下可以提高代码的安全性和稳定性。
阻止继承的类通常是一些工具类或者具有特定功能的类,不希望被修改或继承的情况下使用。通过使用 final 关键字,可以明确表明该类不应被继承,从而避免意外的继承行为。
总结
通过 final 关键字可以阻止类的继承,确保类的独立性和完整性。声明为 final 的类不能被其他类继承,从而避免意外的继承行为。这种做法适用于一些特定场景,如工具类或具有特定功能的类。
向上转型
向上转型是指将一个子类的实例赋值给其父类类型的变量。通过向上转型,可以实现将子类对象当做父类对象来处理,从而实现多态性。在 Java 中,向上转型是自动进行的,无需显式转换。
示例代码
// 父类
class Animal {
public void eat() {
System.out.println("Animal is eating.");
}
}
// 子类
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 向上转型
animal.eat(); // 调用父类方法
// animal.bark(); // 编译错误,无法调用子类方法
}
}
在上面的示例中,Dog 类是 Animal 类的子类,通过 new Dog() 创建了一个 Dog 类的实例,并将其赋值给 Animal 类型的变量 animal,这就是向上转型。通过向上转型,Dog 类的实例可以当做 Animal 类的实例来处理。
在 Main 类中,通过 animal.eat() 调用了 Animal 类的 eat 方法,这是因为 animal 变量的类型是 Animal 类型。但是无法通过 animal.bark() 调用 Dog 类的 bark 方法,因为 animal 变量的类型是 Animal 类型,无法访问子类特有的方法。
通过向上转型,可以实现对父类和子类的统一处理,提高代码的灵活性和可扩展性。在实际开发中,向上转型经常用于方法参数的传递和返回值的处理,以实现多态的效果。
总之,向上转型是将子类对象赋值给父类类型的变量,实现多态性和统一处理的一种机制。
向下转型
向下转型是指将一个父类类型的变量转换为其子类类型的变量。在 Java 中,向下转型需要显式进行类型转换,并且需要注意转换的安全性,因为并非所有的父类类型都可以成功转换为子类类型。如果尝试将一个父类类型的变量转换为其非对应的子类类型,会导致编译错误或运行时异常。
示例代码
// 父类
class Animal {
public void eat() {
System.out.println("Animal is eating.");
}
}
// 子类
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 向上转型
animal.eat(); // 调用父类方法
// 向下转型
Dog dog = (Dog) animal;
dog.bark(); // 调用子类方法
}
}
在上面的示例中,首先通过向上转型将 Dog 类的实例赋值给 Animal 类型的变量 animal。然后通过向下转型,将 animal 变量转换为 Dog 类型的变量 dog。通过向下转型,可以调用 Dog 类特有的方法 bark()。
需要注意的是,在进行向下转型时,需要确保原始对象的实际类型是目标类型或其子类类型,否则会导致 ClassCastException 运行时异常。因此,在进行向下转型时,通常会使用 instanceof 运算符来先进行类型检查,确保安全进行转型。
示例代码(带类型检查)
// 父类
class Animal {
public void eat() {
System.out.println("Animal is eating.");
}
}
// 子类
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 向上转型
animal.eat(); // 调用父类方法
// 向下转型
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark(); // 调用子类方法
} else {
System.out.println("Cannot cast to Dog.");
}
}
}
在上述示例中,通过使用 instanceof 运算符进行类型检查,确保了向下转型的安全性。如果 animal 变量的实际类型不是 Dog 类型或其子类类型,就会输出提示信息,避免了可能的运行时异常。
通过向下转型,可以实现对父类对象的具体子类方法的调用,实现了多态性和灵活性。在实际开发中,向下转型通常用于需要调用子类特有方法的场景,但需要注意安全性和类型检查,以避免潜在的异常情况。
总之,向下转型是将父类类型的变量转换为其子类类型的变量,需要显式进行类型转换,并通过 instanceof 运算符进行类型检查,确保转换的安全性。
区分继承和组合
继承
定义:继承是一种面向对象编程的概念,允许一个类(子类)从另一个类(父类)继承属性和方法。
特点:子类继承父类的属性和方法,可以添加自己的属性和方法,形成一种层级关系。
示例代码:
// 父类
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类
class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void bark() {
System.out.println(name + " is barking.");
}
}
组合
定义:组合是一种对象关系,一个类包含另一个类的对象作为自己的成员变量。
特点:通过组合,一个类可以使用其他类的功能,但不继承其属性和方法。
示例代码:
// 组合示例
class Engine {
public void start() {
System.out.println("Engine started.");
}
}
class Car {
private String model;
private Engine engine;
public Car(String model) {
this.model = model;
this.engine = new Engine();
}
public void startCar() {
System.out.println("Car model: " + model);
engine.start();
}
}
在上述示例中,Car 类通过组合的方式包含了 Engine 类的对象作为自己的成员变量,从而实现了使用 Engine 类的功能。与继承不同的是,Car 类并没有继承 Engine 类的属性和方法,而是通过组合来使用 Engine 类的功能。
通过继承,子类可以获得父类的属性和方法,形成一种层级关系;而通过组合,一个类可以包含其他类的对象,实现代码的复用和灵活性。在设计中,需要根据实际需求来选择使用继承还是组合,以实现代码的结构清晰和可维护性。
方法重载
方法重载是指在同一个类中可以定义多个方法,这些方法具有相同的名称但参数列表不同(参数类型、参数个数或参数顺序)。通过方法重载,可以让同名方法具有不同的行为,提高代码的灵活性和可读性。这种方法名相同,但各自的参数不同,称为方法重载(Overload)。
示例代码
public class Calculator {
// 方法重载:两个整数相加
public int add(int a, int b) {
return a + b;
}
// 方法重载:三个整数相加
public int add(int a, int b, int c) {
return a + b + c;
}
// 方法重载:两个浮点数相加
public double add(double a, double b) {
return a + b;
}
// 方法重载:字符串拼接
public String add(String a, String b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// 调用不同版本的 add 方法
System.out.println(calculator.add(1, 2)); // 调用两个整数相加的方法
System.out.println(calculator.add(1, 2, 3)); // 调用三个整数相加的方法
System.out.println(calculator.add(1.5, 2.5)); // 调用两个浮点数相加的方法
System.out.println(calculator.add("Hello, ", "World!")); // 调用字符串拼接的方法
}
}
在上面的示例中,Calculator 类定义了多个同名方法 add,它们具有不同的参数列表。通过方法重载,可以根据传入的参数类型、数量或顺序来调用不同的方法。
在 Main 类中,创建了一个 Calculator 对象 calculator,并分别调用了不同版本的 add 方法。根据传入的参数类型和数量,编译器会自动匹配调用对应的方法,实现了方法重载的效果。
通过方法重载,可以根据不同的需求定义多个同名方法,提高代码的灵活性和可读性。在实际开发中,方法重载经常用于实现相似功能但参数类型、数量或顺序不同的方法。
总之,方法重载是指在同一个类中可以定义多个同名方法,它们具有不同的参数列表,通过方法重载可以实现同名方法的多态性和灵活性。
注意:方法重载的返回值类型通常都是相同的。
方法重载的目的是,功能类似的方法使用同一名字,更容易记住,因此,调用起来更简单。
举个例子,String类提供了多个重载方法indexOf(),可以查找子串:
int indexOf(int ch):根据字符的Unicode码查找;int indexOf(String str):根据字符串查找;int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。
方法重写
方法重写是面向对象编程中的一个重要概念,也称为覆写(override)。当子类继承父类后,子类可以对父类的方法进行重写,即在子类中重新定义一个与父类方法签名(方法名、参数列表、返回类型)相同的方法。通过方法重写,子类可以根据自身的需求来重新实现父类的方法,从而实现对方法的定制和扩展。
特点
方法名、参数列表、返回类型必须与父类方法一致。
访问修饰符不能比父类方法的访问修饰符更严格。
子类方法不能比父类方法抛出更多的异常。
重写的方法不能是
static或final类型。
示例
// 父类
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound.");
}
}
// 子类
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.makeSound(); // 调用子类重写的方法
}
}
在上面的示例中,Animal 类有一个 makeSound 方法,Dog 类继承了 Animal 类并重写了 makeSound 方法。在 Main 类中,创建了一个 Dog 类的实例,并将其赋值给 Animal 类型的变量 animal,然后调用 animal.makeSound() 方法。由于方法重写,实际调用的是 Dog 类中重写的 makeSound 方法,输出结果为 "Dog barks."。
通过方法重写,子类可以根据自身的特性和需求来重新实现父类的方法,实现了对方法的个性化定制。方法重写是实现多态性的一种方式,提高了代码的灵活性和可扩展性。
调用父类方法
在子类中重写父类方法后,有时候我们需要在子类方法中调用父类的方法。可以通过 super 关键字来实现调用父类方法。
// 父类
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound.");
}
}
// 子类
class Dog extends Animal {
@Override
public void makeSound() {
super.makeSound(); // 调用父类方法
System.out.println("Dog barks.");
}
}
在上面的示例中,Dog 类重写了 makeSound 方法,并通过 super.makeSound() 调用了父类 Animal 的 makeSound 方法。这样可以在子类方法中保留父类方法的功能,并在其基础上添加新的功能。
注意事项
方法重写是子类对父类方法的重新实现,要求方法名、参数列表、返回类型必须一致。
访问修饰符不能比父类方法的访问修饰符更严格,如父类方法为
public,子类方法不能为private。重写的方法不能比父类方法抛出更多的异常,可以抛出相同的异常或其子类异常。
重写的方法不能是
static或final类型,因为static方法属于类,不是实例方法,而final方法不能被重写。
通过方法重写,子类可以根据自身的需求来重新实现父类的方法,实现对方法的个性化定制和扩展。方法重写是面向对象编程中实现多态性的重要手段,提高了代码的灵活性和可维护性。
Comments