原文链接:类的封装,继承与多态
类的封装相当于一个黑匣子,放在黑匣子中的东西你什么也看不到。
继承是类的一个重要属性,可以从一个简单的类继承出相对复杂高级的类,这样可使程序编写的工作量大大减轻。
多态则可动态地对对象进行调用,使对象之间变得相对独立。接下来我们来讨论:封装、继承和多态。
什么是封装性?读者可以先看下面的程序,看看会产生什么问题。
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(){ } }以下代码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关键字来实现对父类成员的访问,用来引用当前对象的父类。
class Grandpa{ public Grandpa(String name){ System.out.println("Grandpa实例化"); } } class Father extends Grandpa{ public Father(){ super("李四"); System.out.println("Father实例化"); } }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 关键字变成非虚函数。
程序结果:说很多国家语言
从程序的输出结果中可以看到,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可以领取更多学习资料