前言
「作者主页」:雪碧有白泡泡
「个人网站」:雪碧的个人网站
「推荐专栏」:
★java一站式服务 ★
★ React从入门到精通★
★前端炫酷代码分享 ★
★ 从0到英雄,vue成神之路★
★ uniapp-从构建到提升★
★ 从0到英雄,vue成神之路★
★ 解决算法,一个专栏就够了★
★ 架构咱们从0说★
★ 数据流通的精妙之道★
★后端进阶之路★
继上一篇Java——基础语法(一)后的续文
文章目录
5. 继承与多态:
Java支持继承和多态。继承是一种面向对象编程的重要技术,它可以使一个新的类获取已有类的属性和方法。多态指的是同一个方法在不同情况下表现出不同的行为。
下面我将详细介绍Java中的继承与多态。
继承的基本概念
在Java中,继承是一种面向对象编程的机制,它允许一个类从另一个类继承属性和行为。被继承的类称为父类或基类,继承的类称为子类或派生类。继承使得代码重用更加容易,同时也提高了代码的可读性和可维护性。
Java中使用关键字extends来实现继承关系,示例代码如下:
public class Animal { public void eat() { System.out.println("Animal is eating."); }}public class Dog extends Animal { public void bark() { System.out.println("Dog is barking."); }}
在上面的代码中,Dog类继承自Animal类,因此Dog类具有父类Animal中的成员方法eat()。
继承的特点
- 子类可以继承父类的成员变量和成员方法,包括公有、受保护和默认访问级别的成员。
- 子类可以重写父类的成员方法以实现特定需求。
- 子类可以添加新的成员变量和成员方法,以满足特定需求。
- 子类不能直接访问父类的私有成员变量和私有成员方法,但可以通过父类的公有或受保护成员方法来访问。
多态的基本概念
多态是面向对象编程中的一种特性,它允许不同的对象对同一消息做出不同的响应。在Java中,多态可以通过方法重载和方法重写来实现。
- 方法重载:方法重载是指在一个类中定义多个具有相同方法名但参数列表不同的方法。Java会根据方法的参数列表来决定调用哪个方法。
- 方法重写:方法重写是指在子类中对父类的某个方法进行重新定义。子类重写的方法必须与父类的方法具有相同的名称、参数列表和返回类型,并且访问修饰符不能更严格。
示例代码:
public class Animal { public void eat() { System.out.println("Animal is eating."); }}public class Dog extends Animal { @Override public void eat() { System.out.println("Dog is eating bones."); }}public class Cat extends Animal { @Override public void eat() { System.out.println("Cat is eating fish."); }}
上述代码中,Dog类和Cat类都重写了父类Animal中的eat()方法。当调用eat()方法时,Java虚拟机会根据具体调用的对象类型来决定调用哪个方法。
多态的实现方式
在Java中,多态有两种实现方式:通过继承和接口来实现多态。
-
通过继承实现多态:
在继承体系中,父类指针可以指向子类对象。使用父类的引用变量来调用重写的方法时,Java虚拟机会根据具体调用的对象类型来决定调用哪个方法。
示例代码:
Animal ani1 = new Dog();Animal ani2 = new Cat();ani1.eat(); // 输出:Dog is eating bones.ani2.eat(); // 输出:Cat is eating fish.
-
通过接口实现多态:
接口是一种特殊的抽象类,它只定义了方法的声明而没有提供方法的实现。一个类可以实现多个接口,从而具有不同的行为特征。Java中通过将对象引用转换为接口类型来实现多态。使用接口的引用变量来调用实现类中的方法时,Java虚拟机会根据具体调用的对象类型来决定调用哪个方法。
示例代码:
interface Shape { void draw();}class Circle implements Shape { @Override public void draw() { System.out.println("Draw a circle."); }}class Rectangle implements Shape { @Override public void draw() { System.out.println("Draw a rectangle."); }}public class Main { public static void main(String[] args) { Shape s1 = new Circle(); Shape s2 = new Rectangle(); s1.draw(); // 输出:Draw a circle. s2.draw(); // 输出:Draw a rectangle. }}
在上述代码中,Circle和Rectangle类都实现了Shape接口,并重写了接口中的draw()方法。当使用接口的引用变量来调用实现类中的方法时,Java虚拟机会根据具体调用的对象类型来决定调用哪个方法。
强制类型转换
在Java中,可以使用强制类型转换将父类引用变量转换为子类引用变量,以方便访问子类特有的成员。
示例代码:
Animal ani = new Dog();if (ani instanceof Dog) { Dog d = (Dog)ani; d.bark();}
在上述代码中,ani是一个Animal类的引用变量,但它指向的是一个Dog类的实例。通过使用instanceof运算符可以判断ani是否指向了一个Dog类的实例。如果是,则可以使用强制类型转换将ani转换为Dog类型,以便访问Dog类特有的方法bark()。
需要注意的是,如果进行强制类型转换时,实际对象不是所需类型的子类,会抛出ClassCastException异常。因此,在进行强制类型转换之前,最好先使用instanceof运算符进行类型检查。
6. 调用父类的构造方法
在Java中,子类继承了父类的所有属性和方法,但并没有继承父类的构造方法。因此,在子类的构造方法中调用父类的构造方法是非常必要的,以确保父类的成员变量得到正确的初始化。
在子类的构造方法中使用super关键字来调用父类的构造方法。如果父类有多个构造方法,则可以通过super关键字指定需要调用的构造方法。
示例代码:
public class Animal { private String name; public Animal(String name) { this.name = name; }}public class Dog extends Animal { private int age; public Dog(String name, int age) { super(name); // 调用父类的构造方法 this.age = age; }}
在上述代码中,Dog类继承自Animal类,并在其构造方法中调用了Animal类的构造方法。
抽象类与抽象方法
抽象类是一种特殊的类,它不能被实例化,只能作为其他类的父类使用。抽象类中可以包含抽象方法,抽象方法没有具体的实现,只有方法的声明。
在Java中,使用abstract关键字来定义一个抽象类或抽象方法。如果一个类包含抽象方法,则这个类必须是抽象类。
示例代码:
abstract class Shape { abstract void draw();}class Circle extends Shape { @Override void draw() { System.out.println("Draw a circle."); }}class Rectangle extends Shape { @Override void draw() { System.out.println("Draw a rectangle."); }}
在上述代码中,Shape类是一个抽象类,其中包含了一个抽象方法draw()。Circle类和Rectangle类都继承自Shape类,并实现了draw()方法。
需要注意的是,如果一个类继承了一个抽象类,则必须实现抽象类中的所有抽象方法,否则该类也必须声明为抽象类。
接口
接口是一种特殊的抽象类,它只定义了方法的声明而没有提供方法的实现。一个类可以实现多个接口,从而具有不同的行为特征。
在Java中,使用interface关键字来定义一个接口。接口中的方法默认都是公有的抽象方法,也可以定义常量和默认方法、静态方法。
示例代码:
interface Shape { double PI = 3.14; // 定义常量 void draw(); // 定义抽象方法 default void show() { // 定义默认方法 System.out.println("This is a shape."); } static void info() { // 定义静态方法 System.out.println("This is a shape interface."); }}class Circle implements Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override public void draw() { System.out.println("Draw a circle with radius " + radius); }}
在上述代码中,Shape接口定义了一个常量PI,以及一个抽象方法draw()、一个默认方法show()、一个静态方法info()。Circle类实现了Shape接口,并重写了其中的draw()方法。
需要注意的是,如果一个类实现了一个接口,则必须实现接口中的所有方法,否则该类必须声明为抽象类。
总结
继承和多态是面向对象编程中非常重要的概念。继承使得代码重用更加容易,同时也提高了代码的可读性和可维护性。多态是面向对象编程中一个非常强大的特性,它可以让不同的对象对同一消息做出不同的响应。
在Java中,使用extends关键字来实现继承关系,使用super关键字来调用父类的构造方法。多态可以通过方法重载和方法重写来实现,以及通过接口来实现。在进行强制类型转换时,需要注意进行类型检查,避免发生ClassCastException异常。此外,抽象类和接口也是面向对象编程中非常重要的概念,它们可以帮助我们更好地组织代码、封装数据和实现复杂功能。
6. 接口:
Java中的接口是一组抽象方法的集合。接口可以被类实现,而一个类也可以实现多个接口。接口使用interface关键字进行定义。
下面我将详细介绍Java中接口的概念、特点、使用方法及应用场景。
接口的概念
在Java中,接口是一种特殊的抽象类,它只包含方法定义和常量声明,而没有具体的方法实现。接口可以让多个不同的类实现相同的方法,从而达到代码重用的目的。同时,接口也可以用于实现回调机制(callback),即某个对象在特定情况下调用另一个对象的方法。
接口的特点
- 接口中的所有方法都是公有的抽象方法,不包含具体的方法实现。
- 接口中可以定义常量,但不能定义变量。
- 接口不能被实例化,只能被实现。
- 一个类可以实现多个接口。
- 一个接口可以继承多个接口。
接口的使用方法
在Java中,使用关键字interface来定义一个接口。接口中的方法默认都是公有的抽象方法,可以在方法名前添加abstract关键字,但不是必须的。
示例代码:
public interface Shape { double getArea(); double getPerimeter();}
在上述代码中,定义了一个Shape接口,其中包含了两个方法getArea()和getPerimeter(),这两个方法都是公有的抽象方法。该接口可以被其他类实现,以提供不同的形状计算面积和周长的功能。
接口的实现
在Java中,使用关键字implements来实现一个接口。一个类可以实现多个接口,实现接口的类必须实现接口中声明的所有方法。
示例代码:
public class Circle implements Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override public double getArea() { return Math.PI * radius * radius; } @Override public double getPerimeter() { return 2 * Math.PI * radius; }}
在上述代码中,Circle类实现了Shape接口,并重写了其中的getArea()和getPerimeter()方法。
接口的继承
在Java中,接口也可以继承另一个接口,从而允许子接口继承父接口的方法定义。
示例代码:
public interface Drawable { void draw();}public interface Shape extends Drawable { double getArea(); double getPerimeter();}
在上述代码中,Shape接口继承了Drawable接口,并扩展了两个新的方法getArea()和getPerimeter()。
接口的应用场景
- 实现多态:接口可以让多个不同的类实现相同的方法,从而达到代码重用的目的。
- 实现回调机制:接口可以用于实现回调机制,即某个对象在特定情况下调用另一个对象的方法。
- 限制类的继承:接口可以作为一种约束,限制某个类的继承关系。如果某个类实现了某个接口,就必须实现该接口中声明的所有方法。
- 定义常量:接口可以定义常量,供其他类使用。
总之,接口是Java中非常重要的概念之一,它可以让我们更好地组织代码、封装数据和实现复杂功能。在设计API时,接口也是非常常见的一种设计模式,它可以提高 API 的可扩展性和可维护性。
7. 泛型:
泛型是Java语言中的一个强大特性,它可以使代码更加灵活和安全。使用泛型可以将类型参数化,从而使代码更好地适应不同的数据类型。
好的,下面我将详细介绍Java中泛型的概念、特点、使用方法及应用场景。
泛型的概念
在Java中,泛型(Generic)是一种参数化类型的机制,它可以让我们定义一个类或方法,使其可以支持多种不同的数据类型。通俗地说,泛型就是把数据类型作为参数传递给类或方法,从而实现代码的重用和扩展。
泛型的特点
- 泛型支持可编译时类型检查,能够在编译时发现类型错误。
- 泛型可以减少代码重复,提高代码的可读性和可维护性。
- 泛型可以增加程序的灵活性和扩展性。
泛型的使用方法
在Java中,使用尖括号<>来指定泛型类型,例如:
List<String> list = new ArrayList<>();
在上述代码中,List是一个泛型接口,其中包含了多个操作列表的方法,String表示元素的类型,ArrayList是一个基于数组实现的 List 接口的可变长度的数组序列。
泛型类
在Java中,我们可以定义一个泛型类,使其可以支持多种不同类型的对象。泛型类在定义时要在类名后面添加类型参数,如下所示:
public class Box<T> { private T data; public Box(T data) { this.data = data; } public T getData() { return data; }}
在上述代码中,Box类是一个泛型类,其中定义了一个类型参数T,表示Box可以存储任意类型的数据。在创建Box对象时,需要指定T的具体类型。
泛型方法
在Java中,我们也可以定义一个泛型方法,使其可以支持多种不同类型的参数。泛型方法在定义时要在方法名前面添加类型参数,如下所示:
public class ArrayUtil { public static <T> void printArray(T[] array) { for (T element : array) { System.out.printf("%s ", element); } System.out.println(); }}
在上述代码中,printArray()是一个泛型方法,其中定义了一个类型参数T,表示该方法可以处理任意类型的数组。在调用printArray()方法时,需要指定T的具体类型。
通配符
在Java中,我们可以使用通配符(wildcard)来表示某种类型的子类或父类。
- ? extends T 表示某种类型的子类。
- ? super T 表示某种类型的父类。
示例代码:
public class Animal {}public class Dog extends Animal {}List<? extends Animal> list1 = new ArrayList<Dog>(); // 子类向父类转换List<? super Dog> list2 = new ArrayList<Animal>(); // 父类向子类转换
在上述代码中,list1表示包含所有Animal的子类的List,可以接收Dog类型的List;list2表示包含所有Dog的父类的List,可以接收Animal类型的List。这样就可以实现子类向父类转换和父类向子类转换。
泛型的应用场景
- 集合类:Java中的集合类(如List、Set、Map等)都是使用泛型来实现的。
- 接口与抽象类:泛型可以被用于接口和抽象类中,使其支持多种不同类型的对象。
- 类型安全:泛型可以保证编写类型安全的代码,避免了强制类型转换时的运行时错误。
- 反射机制:泛型可以与反射机制相结合,使得我们可以在运行时获取到泛型信息。
总之,泛型是Java中非常重要的概念之一,泛型可以使我们在编译时发现类型错误,从而提高代码的健壮性和可读性。它还可以减少代码重复,提高代码的可维护性和可扩展性。
二,泛型不仅适用于类,还适用于方法。在定义泛型方法时,需要在方法名前面添加类型参数。
三,通配符是泛型中一个非常重要的概念,它可以用来表示某种类型的子类或父类。比如List extends Number>表示包含所有Number的子类的List,而List super Integer>表示包含所有Integer的父类的List。
四,泛型的应用场景非常广泛,常见的有集合类、接口和抽象类、类型安全和反射机制等等。
下面是一些使用泛型的示例代码:
泛型类
public class Box<T> { private T data; public Box(T data) { this.data = data; } public T getData() { return data; }}
在上述代码中,Box是一个泛型类,其中定义了一个类型参数T,表示Box可以存储任意类型的数据。
泛型方法
public class ArrayUtil { public static <T> void printArray(T[] array) { for (T element : array) { System.out.printf("%s ", element); } System.out.println(); }}
在上述代码中,printArray()是一个泛型方法,其中定义了一个类型参数T,表示该方法可以处理任意类型的数组。
通配符
public class Animal {}public class Dog extends Animal {}List<? extends Animal> list1 = new ArrayList<Dog>(); // 子类向父类转换List<? super Dog> list2 = new ArrayList<Animal>(); // 父类向子类转换
在上述代码中,list1表示包含所有Animal的子类的List,可以接收Dog类型的List;list2表示包含所有Dog的父类的List,可以接收Animal类型的List。这样就可以实现子类向父类转换和父类向子类转换。
泛型接口
public interface Comparable<T> { int compareTo(T o);}
在上述代码中,Comparable是一个泛型接口,其中定义了一个类型参数T,表示Comparable可以比较任意类型的对象。
泛型枚举
public enum Color {RED, GREEN, BLUE}public enum Size {SMALL, MEDIUM, LARGE}public class Shirt<T extends Enum<T>> { private T color; private T size; public Shirt(T color, T size) { this.color = color; this.size = size; } public T getColor() { return color; } public T getSize() { return size; }}
在上述代码中,Shirt是一个泛型类,其中定义了两个类型参数T和E。T必须是一个枚举类型(如Color或Size),E必须是实现了Comparable接口的类型。这样就可以创建一个泛型枚举类,用于表示衬衫的颜色和大小。
总之,泛型是Java中非常重要的概念之一,它可以让我们编写更健壮、更灵活和更可扩展的代码。在实际开发中,我们应该熟练掌握泛型的语法和使用方法,以便更好地完成自己的工作。
来源地址:https://blog.csdn.net/Why_does_it_work/article/details/132177804