Java—封装、继承与多态
面向对象编程的三个基本特征—封装、继承、多态。
一、封装
封装的核心思想是将数据(属性)和操作数据的方法(行为)绑定在一起,并对外隐藏内部实现细节,仅通过受控的接口与外界交互,提高代码的可维护性和安全性。
1.1 封装的目的
保护数据完整性:防止外部代码直接修改对象内部数据。
隐藏实现细节:类的内部逻辑可以自由修改,不影响外部调用者。
提高代码安全性:通过受控的访问权限,避免非法操作。
降低耦合性:外部代码依赖接口而非具体实现。
1.2 封装的实现
将属性设为private:禁止外部直接访问属性。
提供公共方法(Getter/Setter):通过方法间接读写属性,可添加逻辑控制。
构造方法初始化:确保对象创建时数据合法。
1.3 示例代码
public class test {// 私有属性,外部无法直接访问private String name;private int age;// 构造方法:初始化对象时验证数据合法性public test(String name, int age) {setName(name);setAge(age);}// Getter方法:提供对属性的受控访问public String getName() {return name;}// Setter方法:在赋值时添加验证逻辑public void setName(String name) {if (name == null || name.trim().isEmpty()) {throw new IllegalArgumentException("姓名不能为空");}this.name = name;}public int getAge() {return age;}public void setAge(int age) {if (age < 0 || age > 150) {throw new IllegalArgumentException("年龄无效");}this.age = age;}}
1.4 封装的优点
数据验证:在setAge方法中可检查年龄是否合法。
灵活性:后续可修改内部实现,不影响外部调用。
安全性:外部无法绕过验证逻辑直接修改属性。
二、继承
Java 继承(Inheritance) 是面向对象编程的另一个核心特性,它允许一个类(子类/派生类)基于另一个类(父类/基类)构建,继承父类的属性和方法,并可以扩展或修改其功能。
2.1 继承的目的
代码复用:子类可以直接使用父类的属性和方法,无需重复编写。
类层次结构:通过继承建立逻辑上的“is-a”关系。
多态支持:继承是实现多态的基础。
2.2 继承的特点
使用extends关键字定义子类。
单继承:Java 只支持单继承(一个子类只能有一个直接父类),但是一个父类能有多个子类。
隐式继承:所有类默认继承Object类(Java 的根类)。
如果子类对父类的方法进行重写,会调用子类的方法。 注:被final修饰的方法不能被重写
想实现同时继承两个类,可以通过继承链的方法实现,即C继承B,B继承A,相当于C继承B和A,实现继承多个类。
如果父类被final关键字修饰,其属性及方法不能被子类继承。
注:方法重载与方法重写的区别
- 方法重载:方法名相同,入参不同(数量,类型)
- 方法重写:子类中编写方法名与父类中方法名相同的方法,之后通过子类不能调用父类被重写的方法
2.3 示例代码
2.3.1 父类
public class Parent {public int age;public String name;public void run() {System.out.println("Parent run");}public void eat() {System.out.println("Parent eat");}
}
2.3.2 子类
public class Child extends Parent{public void m1() {System.out.println("Child run");//调用父类的eat()方法eat();super.eat();}public void eat() {System.out.println("Child eat");}public Child() {//默认调用父类的无参构造方法super();//this(); //调用自己的构造方法,但this()和super()同时出现,两者都要在第一行}
}
2.3.3 测试及结果
public class Test {public static void main(String[] args) {Child aa = new Child();aa.run();aa.m1();}
}
Parent run
Child run
Child eat
Parent eat
2.4 继承的关键
super关键字:
访问父类的属性/方法:super.方法名()
调用父类构造方法:super()(必须在子类构造方法的第一行)
方法重写(Override):
子类重新定义父类的方法(方法名、参数、返回类型必须相同)。
访问权限控制:
子类可以继承父类的public和protected成员。
父类的private成员对子类不可见(需通过公共方法访问)。
构造方法链:
子类构造方法必须调用父类构造方法(显式或隐式)。
若父类没有无参构造方法,子类必须显式调用super()。
三、多态
Java 多态允许同一操作作用于不同对象时产生不同的行为。多态的核心是“一个接口,多种实现”,通过继承和接口实现代码的灵活性与可扩展性。
3.1 多态的目的
提高代码灵活性:同一方法可以根据对象类型动态执行不同逻辑。
降低耦合性:代码依赖于抽象(父类或接口),而非具体实现。
增强可扩展性:新增子类时无需修改现有调用代码。
3.2 多态的实现
方法重写:子类重写父类方法。
向上转型:父类引用指向子类对象(例如Animal cat = new Cat())。
继承:要实现多态必须要实现继承。
3.3 多态的限制
无法重写静态方法:静态方法属于类,与对象无关。
无法重写private、final方法:这些方法不可被修改。
属性没有多态性:属性访问由引用类型决定,而非实际对象类型。
3.4 基于继承的多态
3.4.1 相关代码
//A.java
public class A {public String show(D obj) {return "A and D";}public String show(A obj) {return "A and A";}
}//B.java
public class B extends A{public String show(Object obj) {return "B and B";}public String show(A obj) {return "B and A";}
}//C.java
public class C extends B{}//D.java
public class D extends B{}
3.4.2 测试结果
public class Test1 {public static void main(String[] args) {A a1 = new A();A a2 = new B();B b = new B();C c = new C();D d = new D();System.out.println("1:"+a1.show(b));System.out.println("2:"+a1.show(c));System.out.println("3:"+a1.show(d));System.out.println("4:"+a2.show(b));System.out.println("5:"+a2.show(c));System.out.println("6:"+a2.show(d));System.out.println("7:"+b.show(b));System.out.println("8:"+b.show(c));System.out.println("9:"+b.show(d));}
}
运行结果
1:A and A
2:A and A
3:A and D
4:B and A
5:B and A
6:A and D
7:B and A
8:B and A
9:A and D
3.4.3 详细解释
(1)a1为A类型的对象,有show(A)和show(D)两种方法,b为B类型数据,B又继承A,向上转型,因此执行show(A),即A and A;
(2)c为C类型数据,C继承B,B又继承A,向上转型,因此执行show(A),即A and A;
(3)d为D类型数据,直接执行show(D),即A and D;
(4)A a2 = new B(); 先创建父类即A,再创建子类即B,a2指向父类,因为show(A)方法被B重写,因此此时能执行A中show(D)以及B中show(A),b为B类型数据,B又继承A,向上转型,因此执行show(A),即B and A;
(5)c为C类型数据,C继承B,B又继承A,向上转型,因此执行show(A),即B and A;
(6)d为D类型数据,直接执行show(D),即A and D;
(7)b为B类型数据,又是A的子类,因此会继承A中的方法,又因为show(A)方法被B重写,因此此时能执行A中show(D)以及B中show(A)和show(Object),B又继承A,向上转型,因此执行show(A),即B and A;
(8)C继承B,B又继承A,向上转型,因此执行show(A) ,即B and A;
(9)d为D类型数据,直接执行show(D),即A and D;
3.5 instanceof
即使两个类之间没有继承关系,通过强制类型转换可以实现两个类之间的转换,编译成功,但是运行会报错。
Parent xx = new GrandSon1();
Child2 yy = (Child2)xx;
Child1继承Parent, GrandSon1继承Child1,Child2与GrandSon1没有继承关系。
运行结果
为了避免这种情况发生,可以在强制类型转换前用 instanceof 判断xx是否为Child2类型的对象,或者Child2子孙后代的对象。
Parent xx = new GrandSon1();
if(xx instanceof Child2) {Child2 zz = (Child2)xx;System.out.println("可转");
}else {System.out.println("不可转");}
运行结果
不可转
补充:父类转子类可以通过强制类型转换编译成功,但是数据可能改变,运行时会报错。