面向对象三大特性:封装、继承、多态
- 一、封装
- 二、继承
- 2.1 继承:继承是使用已存在的类的定义作为基础建立新类的技术,通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率。【1、子类拥有父类非 private 的属性和方法;2、子类可以拥有自己属性和方法,即子类可以对父类进行扩展;3、子类可以用自己的方式实现父类的方法(重写)。】
- 继承所描述的是 “is-a” 的关系:
- 能向上转型:将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。【向上转型是从一个叫专用类型向较通用类型转换】,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在“未曾明确表示转型” 和 “未曾指定特殊标记”的情况下,仍然允许向上转型的原因。
- 2.2 继承相关
- 2.3 谨慎使用继承,继承的缺陷
- 三、多态
一、封装
本文转发自:http://cmsblogs.com/?p=41
1.1 封装:
1)封装:从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。
- 数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节(当然也无从知道),但可以通过该对象对外的提供的接口来访问该对象。
2)使用封装有四大好处:
1、良好的封装能够减少耦合。
- 有助于建立各个系统之间的松耦合关系,提高系统的独立性。当某一个系统的实现发生变化,只要它的接口不变,就不会影响到其他的系统。1,2,3
2、类内部的结构可以自由修改。
3、隐藏信息和实现细节
4、可以对成员进行更精确的控制。
二、继承
2.1 继承:继承是使用已存在的类的定义作为基础建立新类的技术,通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率。【1、子类拥有父类非 private 的属性和方法;2、子类可以拥有自己属性和方法,即子类可以对父类进行扩展;3、子类可以用自己的方式实现父类的方法(重写)。】
继承所描述的是 “is-a” 的关系:
- 如果有两个对象 A 和 B,若可以描述为 “A 是 B”,则可以表示 A 继承 B,其中 B 是被继承者称之为父类或者超类,A 是继承者称之为子类或者派生类。
能向上转型:将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。【向上转型是从一个叫专用类型向较通用类型转换】,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在“未曾明确表示转型” 和 “未曾指定特殊标记”的情况下,仍然允许向上转型的原因。
- 继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独有得特性。例如猫有抓老鼠、爬树等其他动物没有的特性。同时在继承关系中,继承者完全可以替换被继承者,【子类转换成父类】反之则不可以,例如我们可以说猫是动物,但不能说动物是猫就是这个道理,说猫是动物就是向上转型
2.2 继承相关
2.2.1 构造器【对于构造器而言,它只能够被调用,而不能被继承】
- 子类可以继承父类的属性和方法,除了那些 private 的外还有一样是子类继承不了的,即构造器。对于构造器而言,它只能够被调用,而不能被继承。 调用父类的构造方法我们使用 super() 即可。
- 对于继承而言,子类会默认调用父类的构造器【编译器会做】,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。
public class Person {
protected String name;
protected int age;
protected String sex;
Person(String name){
System.out.println("Person Constrctor-----" + name);
}
}
public class Husband extends Person{
private Wife wife;
Husband(){
super("chenssy");
System.out.println("Husband Constructor...");
}
public static void main(String[] args) {
Husband husband = new Husband();
}
}
Output:
Person Constrctor-----chenssy
Husband Constructor...
2.2.2 protected关键字
- protected :它指明就类用户而言,它是 private,但是对于任何继承于此类的子类而言或者其它任何位于同一个包的类而言,它却是可以访问的。
- private 访问修饰符,对于封装而言,是最好的选择,但这个只是基于理想的世界
- 诚然尽管可以使用 protected 访问修饰符来限制父类属性和方法的访问权限,但是最好的方式还是将属性保持为 private (我们应当一致保留更改底层实现),通过 protected 方法来控制类的继承者的访问权限。
2.2.2 向上转型
- 将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。由于向上转型是从一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在“未曾明确表示转型” 和 “未曾指定特殊标记”的情况下,仍然允许向上转型的原因。
public class Person {
public void display(){
System.out.println("Play Person...");
}
static void display(Person person){
person.display();
}
}
public class Husband extends Person{
public static void main(String[] args) {
Husband husband = new Husband();
Person.display(husband); // 向上转型
}
}
在这我们通过 Person.display(husband)。这句话可以看出 husband 是 Person 类型。
2.3 谨慎使用继承,继承的缺陷
缺陷:
1、父类变,子类就必须变。
2、继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。
3、继承是一种强耦合关系。
- 接口约定了外部调用的规范,继承类必须按照这些规范去实现。只要规范不变,继承类的实现可以调整而不将影响传递出去。糟糕的是,不管是规范还是实现,都基本上不可能一开始就确定好。当变化发生的时候,接口类和继承类都需要做大量的修改,而这些修改也很容易影响到所有使用接口的那些模块。
- A和B耦合”的时候,我们是想表达A和B之间有紧密的联系。
4、只能单继承,和接口相比,可扩展性差。
- 所以说当我们使用继承的时候,我们需要确信使用继承确实是有效可行的办法。那么到底要不要使用继承呢?《Think in java》中提供了解决办法:问一问自己是否需要从子类向父类进行向上转型。如果必须向上转型,则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承。
三、多态
3.1 多态的定义
- 面向对象编程有三大特性:封装、继承、多态。
- 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。
- 继承是为了重用父类代码。两个类若存在 IS-A 的关系就可以使用继承。同时继承也为实现多态做了铺垫。那么什么是多态呢?多态的实现机制又是什么?请看我一一为你揭开:
多态:多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是方法的重载,通过参数列表的不同来区分不同的方法;运行时多态,也叫作动态绑定,一般是指在执行期间(非编译期间)判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。主要用于继承父类和实现接口时,父类引用指向子类对象。
- 对于面向对象而言,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。
- 而运行时多态是动态的,它是通过动态绑定来实现的,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
- 指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。
ublic class Wine {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Wine(){
}
public String drink(){
return "喝的是 " + getName();
}
/**
* 重写toString()
*/
public String toString(){
return null;
}
}
public class JNC extends Wine{
public JNC(){
setName("JNC");
}
/**
* 重写父类方法,实现多态
*/
public String drink(){
return "喝的是 " + getName();
}
/**
* 重写toString()
*/
public String toString(){
return "Wine : " + getName();
}
}
public class JGJ extends Wine{
public JGJ(){
setName("JGJ");
}
/**
* 重写父类方法,实现多态
*/
public String drink(){
return "喝的是 " + getName();
}
/**
* 重写toString()
*/
public String toString(){
return "Wine : " + getName();
}
}
public class Test {
public static void main(String[] args) {
// 定义父类数组
Wine[] wines = new Wine[2];
// 定义两个子类
JNC jnc = new JNC();
JGJ jgj = new JGJ();
//父类引用子类对象
wines[0] = jnc;
wines[1] = jgj;
for(int i = 0 ; i < 2 ; i++){ //在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
System.out.println(wines[i].toString() + "--" + wines[i].drink());
}
System.out.println("-------------------------------");
}
}
OUTPUT:
Wine : JNC--喝的是 JNC
Wine : JGJ--喝的是 JGJ
-------------------------------