原文链接:类的封装,继承与多态
类的封装、继承与多态
类的封装相当于一个黑匣子,放在黑匣子中的东西你什么也看不到。
继承是类的一个重要属性,可以从一个简单的类继承出相对复杂高级的类,这样可使程序编写的工作量大大减轻。
多态则可动态地对对象进行调用,使对象之间变得相对独立。接下来我们来讨论:封装、继承和多态。
封装
封装定义
什么是封装性?读者可以先看下面的程序,看看会产生什么问题。
class Person{
public String name;
public int age;
public void talk(){
System.out.println("我是" + name + ",今年" + age + "岁");
}
}
public class TestPersonDemo{
public static void main(String[] args){
Person p = new Person();
p.name = "张三";
p.age = -25;
p.talk();
}
}
打印结果:我是张三,今年-25岁。
从打印结果可以发现者显然不合法。所以为了避免程序中这种错误的发生,我们可以使用类的封装来解决。接下来我们先看看封装的定义:
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
以上代码我们可以类封装这样进行修改:
public class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setAge(int age){
if(age < 0){
age = 0;
}
this.age = age;
}
public int getAge(){
return this.age;
}
public void talk(){
System.out.println("我是" + name + ",今年" + age + "岁");
}
}
public class TestPersonDemo{
public static void main(String[] args){
Person p = new Person();
p.setName("张三");
p.setAge(-25);
p.talk();
}
}
封装的优点
-
- 良好的封装能够减少耦合。
-
- 类内部的结构可以自由修改。
-
- 可以对成员变量进行更精确的控制。
-
- 隐藏信息,实现细节。
继承
继承的概念
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
实现继承的格式如下:
class 子类名 extends 父类
在Java中,通过继承可以简化类的定义,扩展类的功能。在Java中支持类的单继承和多层继承,但是不支持多继承,即一个类只能继承一个类而不能继承多个类。
Java继承只能直接继承父类中的公有属性和公有方法,而隐含地(不可见地)继承了私有属性。
比如现在有一个父亲,父亲鼻子高高的,头发比较少,并且会编程,父亲有一个儿子,儿子继承了父亲的外观特征,并且成为了一个律师,使用代码实现如下:
class Grandpa{
String nose;
int hair;
public void walk(){
System.out.println("行走");
}
}
class Father extends Grandpa{
// 编程能力
private void programmed(){
}
}
class Son extends Father{
public void law(){
}
}
继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
子类对象的实例化过程
以下代码Father类实例创建,构造方法怎么进行执行?
public class Main {
public static void main(String[] args) throws Exception {
Father son = new Father();
}
}
class Grandpa{
public Grandpa(){
System.out.println("Grandpa实例化");
}
}
class Father extends Grandpa{
public Father(){
System.out.println("Father实例化");
}
}
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
super关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
class Grandpa{
public Grandpa(String name){
System.out.println("Grandpa实例化");
}
}
class Father extends Grandpa{
public Father(){
super("李四");
System.out.println("Father实例化");
}
}
final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:
-
声明类:
final class 类名 {//类体} -
声明方法:
修饰符 (public/private/default/protected) final 返回值类型 方法名(){//方法体}
注:实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,比如父亲会说话并且说的是中文,儿子因为受过高等教育,因此会说很多国家的语言:
class Father extends Grandpa{
public void talk(){
System.out.println("说中文");
}
}
class Son extends Father{
public void talk(){
System.out.println("说很多国家语言");
}
}
多态的实现必须具备以下几个条件:
- 继承
- 重写(重载或覆写)
- 父类引用指向子类对象
重写
“重写”的概念与“覆写”或“重载”相似,它们均是Java“多态”的技术之一。所谓“重写”,即是方法名称相同,但却可在不同的场合做不同的事。当一个子类继承一个父类,而子类中的方法与父类中的方法的名称、参数个数、类型等都完全一致时,就称子类中的这个方法覆写了父类中的方法。同理,如果子类中重复定义了父类中已有的属性,则称此子类中的属性覆写了父类中的属性。
class Father extends Grandpa{
public void talk(){
System.out.println("说中文");
}
}
class Son extends Father{
// 重新了父类的方法
public void talk(){
System.out.println("说很多国家语言");
}
}
虚函数
虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
父类引用指向子类对象
public class Test{
public static void main(String[] args){
Father f = new Son();
f.talk();
}
}
程序结果:说很多国家语言
从程序的输出结果中可以看到,f是父类的对象,但调用talk()方法的时候并没有调用其本身的talk()方法,而是调用了子类中被覆写了的talk()方法。之所以会产生这样的结果,最根本的原因就是因为父类对象并非由其本身的类实例化,而是通过子类实例化,这就是所谓的对象的多态性,即子类实例化对象可以转换为父类实例化对象。
类的多态实例
在这里要着重讲解两个概念向上转型与向下转型,这两个概念的理解非常重要,稍有疏忽就可能引起意想不到的Bug。
- 向上转型
在上面的范例Test.Java中,父类对象通过子类对象去实例化,实际上就是对象的向上转型。向上转型是不需要进行强制类型转换的,但是向上转型会丢失精度。 - 向下转型
与向上转型对应的一个概念就是“向下转型”,所谓向下转型,也就是说父类的对象可以转换为子类对象,但是需要注意的是,这时则必须要进行强制的类型转换。
接下来举个例子来和大家说明:
一天,有个小孩在马路上看见了一辆跑车,他指着跑车说那是汽车。相信读者都会认为这句话没有错,跑车的确是符合汽车的标准,所以把跑车说成汽车并没有错误,只是不准确而已。不管是小轿车也好,货车也好,其实都是汽车,这在现实生活中是说得通的。在这里读者可以将这些小轿车、货车都想象成汽车的子类,它们都是扩展了汽车的功能,都具备了汽车的功能,所以它们都可以叫做汽车,那么这种概念就称为向上转型。而相反,假如说把所有的汽车都当成跑车,那结果肯定是不正确的,因为汽车有很多种,必须明确地指明是哪辆跑车才可以,需要加一些限制,这个时候就必须明确地指明是哪辆车,所以需要进行强制的说明。
上面的解释可以概括成下面的两句话。
- 向上转型可以自动完成。
- 向下转型必须进行强制类型转换。
并非全部的父类对象都可以强制转换为子类对象,请看下面的范例
public class Test{
public static void main(String[] args){
Father f = new Father();
Son s = (Son)f;
}
}
此时程序将会报异常:

父类用其本身类实例化自己的对象,但它并不知道谁是自己的子类,因此在转换的时候会出现错误。
深入探讨可以加笔者QQ:1120855315
点击获取免费Java免费视频
添加QQ群837949026可以领取更多学习资料

6009

被折叠的 条评论
为什么被折叠?



