十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

第五章 Java继承和多态

第五章 Java继承和多态 目录1. 类的继承1.1 继承的概念1.2 定义类的继承关系补充说明1.3. super 关键字及方法重写1.3.1 super 关键字的应用示例1.3.2 测试代码与练习2. 访问限定符2.1 访问限定符的使用场景3. 抽象类与多态3.1 抽象类和使用final关键字修饰属性3.1.1 抽象类的定义与使用3.1.2 抽象类的子类实现3.2多态3.3 使用instanceof关键字检查对象类型3.4 对象数组4 使用final、record和sealed关键字修饰类4.1 使用final关键字修饰类4.2 使用record关键字简洁定义Java类4.3 使用sealed关键字修饰类5.定义Java程序类最佳实践5.1 PO类5.2 VO类5.3 FO类总结核心要点回顾知识关联与进阶学习建议1. 类的继承类之间可能存在继承关系。例如用于描述学生的Student类、用于描述老师的Teacher类和用于描述工人的Worker类这三个类都是一个更为宽泛的Person类的特殊情况因为不论是学生、老师还是工人他们首先都是人。是否存在采用一种更为有效的方式来定义Person类、Student类、Teacher类和Worker类及其之间的关系在Java中确实存在描述这种关系的机制这种机制就是类的继承。1.1 继承的概念对于Person类而言都具有这些基本属性姓名、身份证号码、联系地址、出生日期等同时还具有如下基本方法显示人的基本信息。对Student类而言除了具有Person类的基本属性和方法外还有自己独特的属性包括学号、所在学校名称等还具有自己的用于显示信息的方法等。Teacher类和Worker类也是类似的。在Java中可以使用“继承”这种技术简化类的设计。在面向对象概念中Person类称为“父类”Student类、Teacher类、Worker类称为Person类的“子类”。1.2 定义类的继承关系第一步第二步第三步编写父类代码列如Person类的代码package com.ttt.human; /** 人类基本信息类 包含姓名、身份证号和地址信息 */ public class Person { // 成员变量 private String name; // 姓名 private String id; // 身份证号码 private String address; // 居住地址 /** 无参构造函数 初始化所有属性为Unknown */ public Person() { this(Unknown, Unknown, Unknown); } /** 全参构造函数 param name 姓名 param id 身份证号 param address 居住地址 */ public Person(String name, String id, String address) { this.name name; this.id id; this.address address; } /** 设置姓名 param name 要设置的姓名 */ public void setName(String name) { this.name name; } /** 设置身份证号 param id 要设置的身份证号 */ public void setId(String id) { this.id id; } /** 设置居住地址 param address 要设置的居住地址 */ public void setAddress(String address) { this.address address; } /** 获取姓名 return 当前姓名 */ public String getName() { return name; } /** 获取身份证号 return 当前身份证号 */ public String getId() { return id; } /** 获取居住地址 return 当前居住地址 */ public String getAddress() { return address; } /** 控制台打印人员信息 */ public void display() { System.out.println(this); } /** 重写toString方法 return 格式化的字符串表示 */ Override public String toString() { return String.format(姓名%s, 身份证号码%s, 联系地址%s, name, id, address); } }第四步编写子类代码列如Student类的代码package com.ttt.human; public class Student extends Person { String sno; String school; public Student(String name, String id, String address, String sno, String school) { this.name name; this.id id; this.address address; this.sno sno; this.school school; } Override public void display() { System.out.println(姓名 name); System.out.println(身份证号码 id); System.out.println(联系地址 address); System.out.println(学号 sno); System.out.println(所在学校 school); } Override public String toString() { return 姓名 name , 身份证号码 id , 联系地址 address , 学号 sno , 所在学校 school; } }代码功能说明该代码定义了一个Student类继承自Person类父类未在代码中显示用于描述学生的基本信息包括学号、学校等属性并重写了display()和toString()方法。类定义注释/** * Student类继承自Person类表示学生实体。 * 扩展了学号(sno)和所在学校(school)属性。 */ public class Student extends Person {属性注释String sno; // 学生学号 String school; // 学生所在学校名称构造方法注释/** * 全参构造方法用于初始化学生对象的所有属性。 * param name 学生姓名继承自Person类 * param id 身份证号继承自Person类 * param address 联系地址继承自Person类 * param sno 学号 * param school 所在学校 */ public Student(String name, String id, String address, String sno, String school) { this.name name; // 调用父类属性 this.id id; this.address address; this.sno sno; this.school school; }方法重写注释display()方法/** * 重写父类方法以格式化输出学生详细信息到控制台。 * 输出内容包括姓名、身份证号、地址、学号和学校。 */ Override public void display() { System.out.println(姓名 name); System.out.println(身份证号码 id); System.out.println(联系地址 address); System.out.println(学号 sno); System.out.println(所在学校 school); }toString()方法/** * 重写Object类的toString()方法返回学生信息的字符串表示。 * return 格式为姓名XXX, 身份证号码XXX, ...的字符串 */ Override public String toString() { return 姓名 name , 身份证号码 id , 联系地址 address , 学号 sno , 所在学校 school; }补充说明继承关系Student类继承了Person的name、id和address属性需确保Person类已正确定义这些字段。注解使用Override明确表明方法重写增强代码可读性。格式化输出display()和toString()分别适用于控制台输出和日志记录等场景。1.3. super 关键字及方法重写在继承体系中子类不仅拥有自身的特性还包含了父类的成员。当我们需要在子类中明确调用父类的构造方法或普通方法时super 关键字便派上了用场。简单来说super 关键字可以在子类中指称父类对象在子类的构造函数中使用它可以调用父类的构造方法在子类的方法中使用super.父类方法的形式可以调用父类的指定方法。1.3.1 super 关键字的应用示例以下是一个Student 类继承Person 类并使用super 关键字的典型代码package com.ttt.human; public class Student extends Person { String sno; String school; public Student(String name, String id, String address, String sno, String school) { // 使用 super 关键字调用父类的构造函数 super(name, id, address); this.sno sno; this.school school; } // 方法重写 Override public void display() { // 使用 super 关键字调用父类的 display 方法 super.display(); System.out.println(学号 sno); System.out.println(所在学校 school); } // 方法重写 Override public String toString() { return super.toString() , 学号 sno , 所在学校 school; } }1.3.2 测试代码与练习我们可以通过以下Main 类来测试上述代码的正确性package org.example; import com.ttt.human.Student; public class Main { public static void main(String[] args) { Student student new Student(Wanger, 910101199910101234, 广州市, 202200001234, 英才中学); student.display(); student.setName(王二); student.display(); } }课后练习参考上述Student 类的代码以Person 类为父类完成Teacher 子类和Worker 子类的代码编写。请编写一个Main 类进行测试要求合理使用继承和super 关键字使代码更为简洁和清晰。2. 访问限定符类实现了对同类事物的封装而在封装技术中访问限定符是一个极其重要的概念。它主要用于限定哪些类可以访问对象的属性和方法从而保障数据的安全性。2.1 访问限定符的使用场景为了直观理解我们在com.ttt.modifier包下新建One 类和Two 类并在org.example包下新建One 类的子类SubOne 类。One 类定义了四种不同的访问限定符package com.ttt.modifier; public class One { public int pub_one_inta; // public 访问限定符 protected int pro_one_intb; // protected 访问限定符 int def_one_intc; // default默认访问限定符 private int pri_one_intd; // private 访问限定符 public One(int a, int b, int c, int d) { this.pub_one_inta a; this.pro_one_intb b; this.def_one_intc c; this.pri_one_intd d; } public void display() { // 同类中可以访问 one 对象的所有访问限定符控制的成员 System.out.println(pub_inta: pub_one_inta); System.out.println(pro_intb: pro_one_intb); System.out.println(def_intc: def_one_intc); System.out.println(pri_intd: pri_one_intd); } }Two 类与One 类同包用于测试同包访问权限package com.ttt.modifier; public class Two { public void testAccess(One one) { // 同包下可以访问 public、protected、default 成员 System.out.println(Two 访问 One 的 public 成员 one.pub_one_inta); System.out.println(Two 访问 One 的 protected 成员 one.pro_one_intb); System.out.println(Two 访问 One 的 default 成员 one.def_one_intc); // System.out.println(one.pri_one_intd); // 编译错误private 成员不可访问 } }SubOne 类是One 类的子类位于不同包用于测试子类访问权限package org.example; import com.ttt.modifier.One; public class SubOne extends One { public SubOne(int a, int b, int c, int d) { super(a, b, c, d); } public void testAccess() { // 子类中可以访问父类的 public、protected 成员 System.out.println(SubOne 访问父类 One 的 public 成员 pub_one_inta); System.out.println(SubOne 访问父类 One 的 protected 成员 pro_one_intb); // System.out.println(def_one_intc); // 编译错误default 成员不同包不可访问 // System.out.println(pri_one_intd); // 编译错误private 成员不可访问 } }访问限定符总结表访问限定符同类同包子类不同包非子类public✅✅✅✅protected✅✅✅❌default(包访问)✅✅❌❌private✅❌❌❌更多示例跨包非子类访问在org.example包下创建一个与One无关的Other类尝试访问One的成员只有public成员可以访问。内部类访问在One类内部定义一个内部类可以访问One的所有成员包括private。静态成员访问将One类的成员改为static访问方式变为One.pub_one_inta但访问权限规则不变。补充1.public访问限定符public称为公开的访问限定符。被public修饰的成员可以被任何类访问到。public访问限定符可以修饰类、构造方法、属性成员、方法成员。2.protected访问限定符protected称为受保护的访问限定符。被protected修饰的成员可以被同一个类的成员访问也可以被同一包中其他类访问还可以被子类访问。protected访问限定符可以修饰属性成员、构造方法、方法成员。3.default默认访问限定符default表示在不写修饰符时的默认值。被默认访问限定符修饰的成员被同一个类的成员访问也可以被同一包中其他类访问。默认访问限定符可以修饰类、构造方法、属性成员、方法成员。4.private访问限定符private称为私有访问限定符。被private修饰的成员只能在当前类中被访问到。private访问限定符可以修饰构造方法、属性成员、方法成员。3. 抽象类与多态在Java中还可以定义另一种类型的类称为“抽象类”。所谓抽象类就是不能使用new关键字创建实例对象的类。抽象类一般用于在类的继承中作为父类而存在并且与多态有密切关系。抽象类是一种特殊的类它不能被实例化只能被继承。抽象类中可以包含抽象方法没有方法体的方法这些抽象方法必须在子类中被实现。抽象类通常用于定义一种规范或模板强制子类遵循特定的结构。3.1 抽象类和使用final关键字修饰属性final 关键字可以修饰类、方法和变量final类不能被继承。final方法不能被子类重写。final变量常量一旦赋值不可修改。观察图形所示的一组图形形状这组图形中包括三角形、矩形和圆。虽然它们的外观各异但是它们都有一些共同的属性和方法。例如任何图形形状都有名字可以计算任何图形形状的周长和面积等。考虑到图形形状都有一些共同的属性和方法为了提高编码效率和代码重用性可以先定义一个Shape类来表示图形形状的共有属性和方法。为此现在ch05-01工程下新建一个名称为com.ttt.shapes的包。3.1.1 抽象类的定义与使用不完美的设计一个普通的图形类其计算周长和面积的方法无法实现返回 0.0f。package com.ttt.shapes; /** 抽象形状类作为所有具体形状的基类 定义了形状的通用属性和方法 / public class Shape { /* 圆周率常量用于各种形状的计算 */ public static final float PI 3.14f; /** 形状的名称 */ private String name; /** 构造函数初始化形状名称 param name 形状名称 */ public Shape(String name) { this.name name; } /** 获取形状名称 return 当前形状的名称 */ public String getName() { return name; } /** 设置形状名称 param name 要设置的新名称 */ public void setName(String name) { this.name name; } /** 计算形状周长抽象方法 由于基类不知道具体形状返回0 子类应该重写此方法提供具体实现 return 周长值基类默认返回0 */ public float getCircumference() { return 0.0f; } /** 计算形状面积抽象方法 由于基类不知道具体形状返回0 子类应该重写此方法提供具体实现 return 面积值基类默认返回0 */ public float getArea() { return 0.0f; } }完美的设计将Shape 类定义为抽象类。Java 语言的设计者提供了优雅的方式来解决上述问题使用abstract关键字修饰类和方法。package com.ttt.shapes; /** 抽象类 Shape表示几何图形的基础类。 定义了图形的通用属性和方法具体图形需继承并实现抽象方法。 / public abstract class Shape { /* 圆周率常量用于计算圆形或弧形相关的图形。 */ public static final float PI 3.14f; /** 图形的名称如 Circle、Rectangle。 */ private String name; /** 构造方法初始化图形名称。 param name 图形的名称不可为 null 或空字符串。 */ public Shape(String name) { this.name name; } /** 获取图形名称。 return 当前图形的名称。 */ public String getName() { return name; } /** 设置图形名称。 param name 新的图形名称不可为 null 或空字符串。 */ public void setName(String name) { this.name name; } /** 计算图形的周长。 由子类实现具体逻辑。 return 图形的周长单位与具体实现相关。 */ abstract public float getCircumference(); /** 计算图形的面积。 由子类实现具体逻辑。 return 图形的面积单位与具体实现相关。 */ abstract public float getArea(); }注意事项必须使用abstract关键字修饰抽象类和抽象方法。因为Shape 类是抽象类所以不能使用new关键字创建Shape 类的对象。抽象类都是用在继承中作为父类而存在的。3.1.2 抽象类的子类实现Triangle 类继承Shape 类并实现抽象方法package com.ttt.shapes; public class Triangle extends Shape { private float a, b, c; public Triangle(String name, float a, float b, float c) { super(三角形); this.a a; this.b b; this.c c; } Override public float getCircumference() { return a b c; } Override public float getArea() { float p (a b c) / 2; return (float) Math.sqrt(p * (p - a) * (p - b) * (p - c)); } }Circle 类继承Shape 类并实现抽象方法package com.ttt.shapes; public class Circle extends Shape { private float r; public Circle(float r) { super(圆); this.r r; } Override public float getCircumference() { return 2 * PI * r; } Override public float getArea() { return PI * r * r; } }Rectangle类的代码如下package com.ttt.shapes; public class Rectangle extends Shape { private float a, b; public Rectangle(float a, float b) { super(矩形); this.a a; this.b b; } Override public float getCircumference() { return 2*(ab); } Override public float getArea() { return a*b; } }更多抽象类与 final 关键字的例子抽象类中的具体方法抽象类中不仅可以有抽象方法也可以有具体实现的方法。子类可以直接继承使用这些方法也可以选择重写。final 类的示例Java 中的String类就是final类不能被继承。final 方法的示例在父类中如果一个方法被声明为final则子类不能重写该方法。例如Object类中的getClass()方法就是final方法。final 变量的示例常量通常使用public static final修饰如Math.PI。抽象类与接口的对比抽象类可以包含成员变量和具体方法而接口在 Java 8 之前只能包含抽象方法和常量。抽象类更适合 is-a 关系而接口更适合 has-a 或 can-do 关系。3.2多态在Java中使用父类变量引用子类对象的现象称为“多态”也就是说父类变量可以引用子类的任何对象。例如可以定义Shape的变量shape然后使shape变量引用Triangle对象。为了例示这种现象修改Main类在main方法中定义Shape类的变量并使Shape类的变量引用Triangle对象。Main类代码如下package org.example; import com.ttt.shapes.Shape; import com.ttt.shapes.Triangle; public class Main { public static void main(String[] args) { Shape shape new Triangle(10.0f, 15.0f, 10.0f); System.out.println(图形名称为 shape.getName()); System.out.println(三角形的周长 shape.getCircumference()); System.out.println(三角形的面积 shape.getArea()); } }运行结果3.3 使用instanceof关键字检查对象类型由于多态现象有时需要判定父类变量所引用的子类对象所属的类。例如Shape类的变量shape可以引用任何Shape子类对象如何才能知道shape变量当前所引用的对象属于哪个类呢Java提供了instanceof关键字用于解决这个问题。package org.example; // 导入不同形状类的定义 import com.ttt.shapes.Circle; import com.ttt.shapes.Rectangle; import com.ttt.shapes.Shape; import com.ttt.shapes.Triangle; public class Main { public static void main(String[] args) { // 创建Triangle对象并通过父类Shape引用 Shape shape new Triangle(10.0f, 15.0f, 10.0f); // 检查shape引用的实际对象类型 if (shape instanceof Circle) { // 如果是Circle类型进行类型转换 Circle c (Circle) shape; System.out.println(shape引用的对象是圆类); } else if (shape instanceof Rectangle) { // 如果是Rectangle类型进行类型转换 Rectangle r (Rectangle) shape; System.out.println(shape引用的对象是矩形类); } else if (shape instanceof Triangle) { // 如果是Triangle类型进行类型转换 Triangle t (Triangle) shape; System.out.println(shape引用的对象是三角形类); } } }3.4 对象数组对于Java类也可以定义数组。例如下面的代码定义了Shape类的数组并且这个数组有10个元素Shape[] shape;shape new Shape[10];由于目前shape数组中的每个元素只是Shape类型的一个变量注意每个元素仅仅只是一个变量而已数组元素的变量都还没有引用到具体的对象所以此时还不能使用数组元素否则Java会出现运行时错误。为了能够正常使用数组元素必须使每个数组元素引用到合法的对象为此对于每个数组元素还需要使用new关键字使每个数组元素引用到合法的对象。例如可以使用下面的语句使第0个数组元素引用到合法对象shape[0] new Triangle(10.0f, 13.0f, 10.0f);此时shape[0]已经引用到了合法的三角形对象。下面通过一个例子来介绍如何定义对象数组并使用instanceof关键字来判定数组中的每个对象所属的类。// 导入自定义的形状类包路径为com.ttt.shapes import com.ttt.shapes.Circle; import com.ttt.shapes.Rectangle; import com.ttt.shapes.Shape; import com.ttt.shapes.Triangle; // 导入Java随机数生成工具 import java.util.Random; public class Main { public static void main(String[] args) { // 声明一个长度为10的Shape类型数组 Shape[] shapes; shapes new Shape[10]; // 创建Random对象用于生成随机数 Random random new Random(); // 循环填充数组 for(int i0; ilt;10; i) { // 生成0-2的随机数决定当前元素的形状类型 int which random.nextInt(3); // 使用switch表达式创建具体形状对象 shapes[i] switch(which){ case 0 -amp;gt; new Triangle( // 创建三角形参数为随机边长 random.nextInt(), random.nextInt(), random.nextInt()); case 1 -amp;gt; new Circle( // 创建圆形参数为随机半径 random.nextInt()); case 2 -amp;gt; new Rectangle( // 创建矩形参数为随机长宽 random.nextInt(), random.nextInt()); default -amp;gt; null; // 理论上不会执行因random.nextInt(3)范围是0-2 }; } // 遍历数组并检查每个元素的实际类型 for(int i0; iamp;lt;shapes.length; i) { // 使用模式匹配的instanceof语法Java 16特性 if (shapes[i] instanceof Triangle t) System.out.println(数组中的这个元素对象是三角形); else if (shapes[i] instanceof Circle c) System.out.println(数组中的这个元素对象是圆); else if (shapes[i] instanceof Rectangle r) System.out.println(数组中的这个元素对象是矩形); } System.out.println(Over); } }4 使用final、record和sealed关键字修饰类在Java编程实践中可能需要对某些类的定义做一些限制例如限制某个类不能作为父类使用限制一旦创建了某个类的对象则不能修改对象的属性值等等。为了满足这些要求Java提供类必要的关键字达到这些目标。4.1 使用final关键字修饰类final关键字可以用来修饰类、修饰属性和修饰方法。顾名思义final就是“最后”的意思。因此使用final关键字修饰的属性意味着一旦被修饰的属性被初始化则在后续的任何时候都不可以再次修改该属性的值。可以在两个地方对final修饰的属性进行初始化一是其定义处也就是说在 final 属性定义时直接给其赋值二是在构造函数中二者只能选其一使用final关键字修饰了某个类的方法则意味着在将该类作为父类使用时在子类中不能重写被父类中被final关键字修饰的方法使用final关键字修饰的类不能被继承也就是被final关键字修饰的类不能作为父类在继承中被使用。4.2 使用record关键字简洁定义Java类record关键字用于创建不可变的数据类使用record关键字可以快速简洁地定义属性不可变更地Java类。当使用record 关键字声明一个类时Java将为该类自动创建这些方法构造方法用于创建对象hashCode()方法用于为该类的每个对象生成一个hash码euqals()方法用于比较该类的两个对象是否相等toString()方法用于将该类的对象转换为一个字符串。同时使用record关键字修饰的类被final关键字修饰从而不能被继承。举一个例子说明record关键字的使用。在Main类中创建一个用record关键字修饰的类Person然后在Main类的main方法中创建Person类的对象。Main类的代码如下所示package org.example; public class Main { // 定义一个不可变的记录类Person包含name和age两个属性 private record Person(String name, int age){} public static void main(String[] args) { // 创建Person对象p1 Person p1 new Person(张三, 20); // 打印p1的age属性 System.out.println(p1.age); // 打印p1的name属性 System.out.println(p1.name); // 打印p1的hashCode值 System.out.println(p1.hashCode()); // 打印p1的toString方法返回值 System.out.println(p1.toString()); // 创建另一个Person对象p2属性值与p1相同 Person p2 new Person(张三, 20); // 打印p2的age属性 System.out.println(p2.age); // 打印p2的name属性 System.out.println(p2.name); // 打印p2的hashCode值 System.out.println(p2.hashCode()); // 打印p2的toString方法返回值 System.out.println(p2.toString()); // 将p3引用指向p1对象 Person p3 p1; // 比较p1和p2的引用地址输出false不同对象 System.out.println(p1 p2); // 比较p1和p3的引用地址输出true相同对象 System.out.println(p1 p3); } }运行结果4.3 使用sealed关键字修饰类sealed顾名思义就是“密封”的意思。在面向对象语言中可以通过继承实现类的能力复用、扩展与增强。但有的时候不希望一个类被无限制地被其他类继承因此需要对继承关系有一些限制的控制手段。而密封类的作用就是限制类的继承。密封类可以控制哪些类可以对超类进行继承使用sealed关键字和permits关键字即可实现这种限制。举个例子说明sealed的使用。定义一个家具类Furniture作为父类这个父类被限制只有Table类、Chair类可以继承它。为此在ch05-01工程下新建名称为com.ttt.furnitures的程序包在这个包下新建Furniture类、Table类、Chair类。Furniture类代码如下package com.ttt.furnitures; public sealed class Furniture permits Table, Chair { private String name; private float price; private String producer; public Furniture(String name, float price, String producer) { this.name name; this.price price; this.producer producer; } public String toString() { return Name: name , Price price , Producer: producer; } }Table类的定义如下package com.ttt.furnitures; public final class Table extends Furniture { private int length; //桌子的长度 private int width; //桌子的宽度 public Table( float price, String producer, int length, int width) { super(桌子, price, producer); this.length length; this.width width; } public void display() { String info super.toString() , length: length , width: width; System.out.println(info); } }Chair类的代码如下package com.ttt.furnitures; public non-sealed class Chair extends Furniture { private int room; //能够容纳的人数 public Chair(float price, String producer, int room) { super(椅子, price, producer); this.room room; } public void display() { String info super.toString() , room: room; System.out.println(info); } }Main类的代码如下package org.example; import com.ttt.furnitures.Chair; import com.ttt.furnitures.Table; public class Main { public static void main(String[] args) { Table t new Table(123.4f, 第一家具厂, 20, 10); t.display(); Chair c new Chair(23.5f, 第二家具厂, 1); c.display(); } }5.定义Java程序类最佳实践Java提供了灵活地定义Java类的手段。从工程实践角度Java类大致包括如下几种类型用于封装对象实体的Java类、用于程序功能部件之间进行数据交互的Java类、用于完成某些特定功能的Java功能类。人们还为这些具有明确指向性的Java类赋予了便于记忆的名字PO类Persistent Object、VO类Value ObjectFO类Function Object。5.1 PO类PO类Persistent Object是Java中非常简单但是是非常常用的一种类主要用于封装实体对象例如用于封装学生实体对象、书籍实体对象等这类对象经常用于与数据库的交互。PO类有时也称为POJO类也就是Plain Old Java Object。这些对象都有明确的属性并且可以修改及读取实体对象的属性值因此这类对象除了有构造函数外都有所谓的setter、getter方法和一些其他的辅助方法例如toString方法用于将对象属性字符串化、hashCode方法用于产生对象的hash码等。例子以书籍对象为例介绍PO类的典型模式。对于一本书籍显然都包括如下一些属性书籍名称name、书籍价格price、书籍出版社press、书籍简介memo、书籍作者author等属性。对于每个属性都需要定义相应的设置setter及读取getter方法。为此在ch05-01工程下新建名称为com.ttt.best的Java包在这个包下新建Book类。PO类是如此常用IDEA为定义PO类提供了便利的工具可以为PO类自动生成setter方法、getter方法、构造函数等。Book类的代码如下所示package com.ttt.best; public class Book { private String name; private float price; private String press; private String memo; private String author; public Book(String name, float price, String press, String memo, String author) { this.name name; this.price price; this.press press; this.memo memo; this.author author; } public String getName() { return name; } public void setName(String name) { this.name name; } public float getPrice() { return price; } public void setPrice(float price) { this.price price; } public String getPress() { return press; } public void setPress(String press) { this.press press; } public String getMemo() { return memo; } public void setMemo(String memo) { this.memo memo; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author author; } Override public String toString() { return Book{ name name \ , price price , press press \ , memo memo \ , author author \ }; } }5.2 VO类VO类Value Object是一种一旦创建了这个类的对象就不能再修改对象属性的特殊Java类也是常用的Java类之一。例如在使用Java编写基于Web的网络应用中在Java程序中创建了某个类的对象然后将这个对象交给浏览器显示其中的信息这时可以将类定义为VO类因为不希望也不允许浏览器修改对象的属性值。VO类一般作为Java成员内部类或者静态内部类使用。Java的record关键字是定义VO类的有力工具。例子通过一个例子来说明VO类应用。GrandCreator类根据用户的输入信息不同创建不同的VO类对象。GrandCreator类代码如下package com.ttt.best; import java.util.Scanner; public class GrandCreator { public void generate() { Scanner sc new Scanner(System.in); System.out.print(Which VO to create: ); int which sc.nextInt(); switch (which) { case 1 - { Sky sky new Sky(Blue, 10000); System.out.println(sky); } case 2 - { Earth earth new Earth(Home, 100); System.out.println(earth); } case 3 - { Ocean ocean new Ocean(tuna, 2000); System.out.println(ocean); } default - System.out.println(Invalid); } } private record Sky(String color, int height){} private record Earth(String name, float area){} private record Ocean(String fish, float depth){}Main类的代码如下package org.example; import com.ttt.best.GrandCreator; public class Main { public static void main(String[] args) { GrandCreator gc new GrandCreator(); gc.generate(); } }5.3 FO类FO类Function Object顾名思义就是定义完成一定的特定功能的Java类例如完成网络通信的Java类、进行数据库操作的Java类等。不同的FO类还被赋予了不同的名称例如对专门完成数据库操作的类被赋予了DAO类也就是Data Access Object类等。关于FO类将在后续章节介绍。总结本章系统性地探讨了 Java 面向对象编程中类定义与继承的核心机制从基础概念到高级特性再到工程实践构建了一个完整的知识体系。核心要点回顾类的继承通过extends关键字建立父子类关系子类自动获得父类的非私有属性和方法实现了代码的复用和层次化设计。super 关键字与方法重写super用于在子类中显式调用父类的构造方法或成员方法Override注解明确方法重写是面向对象多态性的基础。访问限定符public、protected、default包私有、private四级权限控制是封装思想的直接体现保障了数据的安全性和模块间的清晰边界。抽象类与多态abstract关键字用于定义抽象类和抽象方法强制子类实现特定契约。父类引用指向子类对象是多态的核心配合方法重写实现了“一个接口多种实现”。final、record 与 sealed 关键字final用于修饰类不可继承、方法不可重写、变量常量。record用于快速创建不可变的数据载体类自动生成构造器、访问器、equals()、hashCode()和toString()方法。sealed用于定义密封类通过permits子句精确控制哪些类可以继承它实现了对继承体系的精细化管控。面向对象设计实践介绍了 PO持久化对象、VO值对象、FO功能对象等常见类设计模式将理论知识与实际工程应用相结合。知识关联与进阶理解本章内容是掌握更高级 Java 特性的基石接口Interface与抽象类共同构成 Java 多态的“双翼”定义行为契约。设计模式工厂模式、策略模式、模板方法模式等都深度依赖于继承、多态和抽象类。框架应用Spring 等主流框架中Bean 的定义、AOP 的切面、事务管理等大量使用了本章涉及的面向对象特性。学习建议1.动手实践务必亲手编写并运行文中的所有示例代码理解继承链的构建、多态的表现以及各关键字的编译期和运行期行为。2.对比学习将abstract class与interface、final与sealed、record与传统 POJO 进行对比理解其适用场景。3.思考设计在设计中思考“是不是”is-a关系来决定是否使用继承优先使用组合而非继承来降低耦合度。掌握这些面向对象的核心机制将使你能够设计出更加灵活、健壮且易于维护的 Java 程序为后续学习框架和复杂系统设计打下坚实基础。
返回列表