1、抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2、抽象方法
如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
3、代码实例
1、商城模拟Product
(Product类):
package com.ret.demo5;
import java.time.LocalDate;
// 商品类
// 父类
// 抽象类的定义
// abstract抽象类 关键词修饰
public abstract class Product {
// private修饰的成员变量(方法),子类无法继承(无法访问)
private double price; // 价格
private int stock; // 库存
// 无参构造方法!!!
// public Product() {
//
// }
// 有参构造方法
public Product(double price, int stock) {
this.setPrice(price);
this.setStock(stock);
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
if (price <= 0) {
this.price = 0.01;
} else {
this.price = price;
}
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
}
// 图书类
// 子类
class Book extends Product {
private String bookName; // 图书名称
private String author; // 作者姓名
public Book(String bookName, String author, double price, int stock) {
// 直接保存至"图书类"的成员变量中
super(price, stock);
this.bookName = bookName;
this.author = author;
// 通过父类的set方法,将价格和库存,保存至父类的成员变量
// this.setPrice(price);
// this.setStock(stock);
// super关键字代表父类对象
super.setPrice(price);
super.setStock(stock);
}
@Override
public String toString() {
String s = String.format("图书商品:<<%s>>,¥%f,作者【%s】,库存%d本", this.bookName, super.getPrice(), this.author,
super.getStock());
return s;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
// 手机类
// 子类
class Phone extends Product {
private String model;
private int memory;
public Phone(String model, int memory, double price, int stock) {
// 通过父类的set方法,保存价格和库存
// super.setPrice(price);
// super.setStock(stock);
// 通过父类的有参构造方法,保存价格和库存
super(price, stock);
// 型号和内存
this.model = model;
this.memory = memory;
}
@Override
public String toString() {
String s = String.format("手机商品:【%s】,¥%f,内存%dG,库存%d部", this.model, super.getPrice(), this.memory,
super.getStock());
return s;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public int getMemory() {
return memory;
}
public void setMemory(int memory) {
this.memory = memory;
}
}
// 食品类
class Food extends Product {
private String foodName;// 名称
private int weight;// 重量
private LocalDate date;// 保质期
public Food(String foodName, int weight, LocalDate date, double price, int stock) {
super(price, stock);
this.foodName = foodName;
this.weight = weight;
this.date = date;
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
}
调用类(Demo_oop_02):
package com.ret.demo5;
import java.time.LocalDate;
public class Demo_oop_02 {
public static void main(String[] args) {
Book book = new Book("红楼梦", "吴承恩", 100, 88);
desc(book);
Phone phone = new Phone("Redmi12", 512, 1988, 100);
desc(phone);
Food food = new Food("好丽友", 120, LocalDate.now(), 100.5, 10000);
desc(food);
}
public static void desc(Product p) { // 传参时发生向上转型
if (p instanceof Book) {
Book book = (Book) p;
System.out.printf("图书名称:<<%s>>\n", book.getBookName());
System.out.printf("作者姓名:%s\n", book.getAuthor());
} else if (p instanceof Phone) {
Phone phone = (Phone) p;
System.out.printf("手机型号:【%s】\n", phone.getModel());
System.out.printf("内存容量:%dGB\n", phone.getMemory());
} else if (p instanceof Food) {
Food food = (Food) p;
System.out.printf("食品名称:%s\n", food.getFoodName());
System.out.printf("食品重量:%dkg\n", food.getWeight());
System.out.printf("保质日期:%s\n", food.getDate());
}
System.out.printf("销售价格:¥%f\n", p.getPrice());
System.out.printf("库存数量:%d\n", p.getStock());
}
}
输出结果:
图书名称:<<红楼梦>>
作者姓名:吴承恩
销售价格:¥100.000000
库存数量:88
手机型号:【Redmi12】
内存容量:512GB
销售价格:¥1988.000000
库存数量:100
食品名称:好丽友
食品重量:120kg
保质日期:2024-01-18
销售价格:¥100.500000
库存数量:10000
2、动物模拟Pet
(Pet类):
package com.ret.demo6;
//父类:宠物类
//抽象类的定义
//1.使用abstract关键字定义的类是抽象类
//2.抽象类中允许定义抽象方法
//3.抽象方法使用abstract关键字定义的方法,并且没有方法体
//4.抽象父类中如果存在抽象方法,则子类必须实现该方法
//5.如果有抽象方法但是没有
// 抽象类的使用
// 1.抽象类不允许实例化(不能new对象)
public abstract class Pet {
// 抽象方法
public abstract void eat();
}
//子类:猫
class Cat extends Pet{
//子类必须实现父类Pet的抽象方法eat()
public void eat() {
System.out.println("猫吃:鱼、猫条、冻干....");
}
}
//子类:狗
class Dog extends Pet{
//子类必须实现父类Pet的抽象方法eat()
public void eat() {
System.out.println("狗吃:狗粮、骨头、肉....");
}
}
(调用类Demo_oop_01):
package com.ret.demo6;
public class Demo_oop_01 {
public static void main(String[] args) {
// 抽象类不能被实例化
// Pet p = new Pet()
Cat c = new Cat();
Dog d = new Dog();
Dog x = new Dog();
ans(c);
ans(d);
ans(x);
}
// 使用父类作为方法参数类型,可以传入任意一个子类对象
public static void ans(Pet pet) {
// 通过父类的引用,调用eat()方法
// 运行期间,根据pet指向的子类对象,调用不同子类的eat()方法
pet.eat();
}
}
输出结果:
猫吃:鱼、猫条、冻干....
狗吃:狗粮、骨头、肉....
狗吃:狗粮、骨头、肉....