Java面向对象综合实战:从Person到Company的继承与覆盖设计模式解析
1. 从Person到Company的类层次设计企业人员管理系统中最核心的挑战是如何用面向对象思维建模真实世界的复杂关系。我们先从最基础的Person类开始逐步构建完整的类层次结构。Person类的设计要点作为所有人员类的基类包含name、age、gender三个基础属性使用protected修饰符保证子类能直接访问属性重写toString()方法实现标准格式输出重写equals()方法实现对象比较public abstract class Person { protected String name; protected int age; protected boolean gender; public Person(String name, int age, boolean gender) { this.name name; this.age age; this.gender gender; } Override public String toString() { return name - age - gender; } Override public boolean equals(Object obj) { if (!(obj instanceof Person)) return false; Person p (Person)obj; return name.equals(p.name) age p.age gender p.gender; } }1.1 Student类的继承实现学生类需要扩展学号和班级信息这里演示如何通过继承复用Person的代码public class Student extends Person { private String stuNo; private String clazz; public Student(String name, int age, boolean gender, String stuNo, String clazz) { super(name, age, gender); // 调用父类构造器 this.stuNo stuNo; this.clazz clazz; } Override public String toString() { return Student: super.toString() - stuNo - clazz; } Override public boolean equals(Object obj) { if (!super.equals(obj)) return false; Student s (Student)obj; return stuNo.equals(s.stuNo) clazz.equals(s.clazz); } }关键技巧使用super调用父类构造器初始化公共属性在equals()中先调用父类比较再比较子类特有属性在toString()中复用父类的字符串格式1.2 Employee类的关联设计员工类除了继承Person还需要关联Company对象演示了组合关系的实现public class Employee extends Person { private Company company; private double salary; public Employee(String name, int age, boolean gender, double salary, Company company) { super(name, age, gender); this.salary salary; this.company company; } Override public String toString() { return Employee: super.toString() - company - salary; } Override public boolean equals(Object obj) { if (!super.equals(obj)) return false; Employee e (Employee)obj; DecimalFormat df new DecimalFormat(#.#); return company.equals(e.company) df.format(salary).equals(df.format(e.salary)); } }空值处理技巧对company属性进行非空判断使用DecimalFormat统一薪资比较精度重写equals时要考虑所有可能为null的属性2. 多态与集合操作实战2.1 多态集合的排序处理包含不同类型人员的集合时多态展现出强大威力ListPerson personList new ArrayList(); // 添加Student和Employee对象 Collections.sort(personList); // 需要Person实现Comparable // 实现Comparable接口示例 public abstract class Person implements ComparablePerson { Override public int compareTo(Person p) { int nameCompare this.name.compareTo(p.name); return nameCompare ! 0 ? nameCompare : Integer.compare(this.age, p.age); } }排序要点让Person实现Comparable接口先按姓名排序姓名相同再按年龄使用Collections.sort()自动处理多态类型2.2 类型区分与集合转换将混合集合按类型分类的实用技巧ListStudent stuList new ArrayList(); ListEmployee empList new ArrayList(); for (Person p : personList) { if (p instanceof Student !stuList.contains(p)) { stuList.add((Student)p); } else if (p instanceof Employee !empList.contains(p)) { empList.add((Employee)p); } }注意事项使用instanceof进行类型检查转换前用contains()避免重复添加注意处理可能的null值情况3. 对象比较的陷阱与解决方案3.1 equals方法的正确实现开发中最容易出错的点之一看Employee类的完整实现Override public boolean equals(Object obj) { if (this obj) return true; if (obj null || getClass() ! obj.getClass()) return false; if (!super.equals(obj)) return false; Employee e (Employee)obj; if (company null || e.company null) { return company e.company Double.compare(salary, e.salary) 0; } return company.equals(e.company) Double.compare(salary, e.salary) 0; }最佳实践先进行引用相等和空值检查调用父类equals比较基础属性对浮点数使用Double.compare避免精度问题谨慎处理可能为null的对象属性3.2 hashCode的配套实现遵循equals和hashCode必须同时重写原则Override public int hashCode() { int result super.hashCode(); result 31 * result (company ! null ? company.hashCode() : 0); long temp Double.doubleToLongBits(salary); result 31 * result (int)(temp ^ (temp 32)); return result; }4. 企业级应用扩展建议4.1 使用工厂模式创建对象改进原始代码中的直接构造方式public class PersonFactory { public static Person createPerson(String type, Scanner sc) { switch(type) { case s: return new Student(sc.next(), sc.nextInt(), sc.nextBoolean(), sc.next(), sc.next()); case e: String companyName sc.next(); Company company null.equals(companyName) ? null : new Company(companyName); return new Employee(sc.next(), sc.nextInt(), sc.nextBoolean(), sc.nextDouble(), company); default: return null; } } }4.2 引入接口增强扩展性定义可排序接口public interface Sortable { int compareTo(Sortable other); } public abstract class Person implements Sortable { // 实现compareTo方法 }4.3 使用Stream API处理集合Java8的现代化处理方式// 分类处理 MapClass?, ListPerson grouped personList.stream() .filter(Objects::nonNull) .collect(Collectors.groupingBy(Person::getClass)); // 排序输出 personList.stream() .sorted() .forEach(System.out::println);5. 常见问题排查指南问题1ClassCastExceptionwhen downcasting原因未做类型检查直接强制转换解决始终先用instanceof检查if (person instanceof Employee) { Employee emp (Employee)person; // 安全操作 }问题2排序结果不符合预期检查compareTo实现是否满足自反性x.compareTo(x) 0对称性x.compareTo(y)与y.compareTo(x)结果相反传递性如果x.compareTo(y)0且y.compareTo(z)0则x.compareTo(z)0问题3equals与hashCode不一致确保根据equals比较的属性都参与hashCode计算可以使用IDE自动生成这对方法6. 性能优化建议缓存hashCode对于不可变对象可以缓存hashCode值懒加载对于计算代价高的属性对象池频繁创建的重量级对象可以考虑对象池选择性排序对超大集合考虑使用Comparator代替Comparable// 使用Comparator的惰性排序 personList.sort( Comparator.comparing(Person::getName) .thenComparingInt(Person::getAge) );7. 测试用例设计完整的单元测试应该覆盖public class PersonTest { Test public void testEquals() { Person p1 new Student(Tom, 20, true, 001, CS); Person p2 new Student(Tom, 20, true, 001, CS); Assert.assertEquals(p1, p2); } Test public void testNullCompany() { Employee e1 new Employee(Alice, 30, false, 5000, null); Employee e2 new Employee(Alice, 30, false, 5000, null); Assert.assertEquals(e1, e2); } Test public void testSorting() { ListPerson list Arrays.asList( new Student(Bob, 22, true, 002, Math), new Employee(Alice, 30, false, 5000, null) ); Collections.sort(list); Assert.assertEquals(Alice, list.get(0).getName()); } }8. 扩展思考设计模式应用模板方法模式在Person中定义模板方法public abstract class Person { // 模板方法 public final String getInfo() { return String.format(Name:%s, Age:%d, Gender:%s, name, age, gender ? Male : Female); } // 由子类实现 protected abstract String getSpecificInfo(); }建造者模式构建复杂对象Employee emp new Employee.Builder() .name(John) .age(35) .gender(true) .salary(8000) .company(new Company(Google)) .build();在实际项目中我经常发现开发人员容易忽视equals/hashCode的配套实现这会导致在使用HashSet等集合时出现难以排查的问题。另一个常见误区是在继承体系中不恰当的方法重写比如扩大方法可见性范围或修改异常声明这些都会破坏里氏替换原则。