本文章介绍了关于Java中的面向对象封装、抽象、继承、多态特点
Java面向对象主要有四大特性:封装、抽象、继承和多态。
一、封装
封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
在面向对象语言中,封装特性是由类来体现的,我们将现实生活中的一类实体定义成类,其中包括属性和行为(在Java中就是方法),就好像人类,可以具有name,sex,age等属性,同时也具有eat(),sleep()等行为,我们在行为中实现一定的功能,也可操作属性,这是面向对象的封装特性.
封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过 外部接口,一特定的访问权限来使用类的成员。
二、抽象
抽象就是将一类实体的共同特性抽象出来,封装在一个抽象类中,所以抽象在面向对象语言是由抽象类来体现的。比如鸟就是一个抽象实体,因为抽象实体并不是一个真正的对象,它的属性还不能完全描述一个对象,所以在语言中体现为抽象类不能实例化。
三、多态
多态就是通过传递给父类对象引用不同的子类对象从而表现出不同的行为,多态可为程序提供更好的可扩展性,同样也可以代码重用。
程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种 表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方 法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。方法的重写Overriding和重载Overloading是Java多态性的不同表现。
多态中的两种类型转换:
① 向上类型转换(upcast)(上转型):将子类型转换成父类型。对于向上类型转换,不需要显示指定:
Parent p = newChild();
② 向下类型转换(downcast)(下转型):将父类型转换为子类型。对于向下类型转换,必须要显示指定,使用强制类型转换:
Parent p = newChild();
Child child =(Child)p;
示例
<span style="font-size:18px;"><span style="font-size:18px;">//汽车接口
interface Car{
// 汽车名称
String getName();
// 获得汽车售价
int getPrice();
}
// 宝马
class BMWimplements Car {
public String getName() {
return "BMW";
}
public int getPrice() {
return 300000;
}
}
// 奇瑞QQ
class CheryQQimplements Car {
public String getName() {
return "CheryQQ";
}
public int getPrice() {
return 20000;
}
}
// 汽车出售店
public classCarShop {
// 售车收入
private int money = 0;
// 卖出一部车
public void sellCar(Car car) {
System.out.println("车型:" + car.getName() + " 单价:" +car.getPrice());
// 增加卖出车售价的收入
money += car.getPrice();
}
// 售车总收入
public int getMoney() {
return money;
}
public static void main(String[] args){
CarShop aShop = new CarShop();
// 卖出一辆宝马
aShop.sellCar(new BMW());
// 卖出一辆奇瑞QQ
aShop.sellCar(new CheryQQ());
System.out.println("总收入:" + aShop.getMoney());
}
}
运行结果:
车型:BMW 单价:300000
车型:CheryQQ 单价:20000
总收入:320000</span></span>
四、继承
简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法的方式,产生一个新的类型。我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,java.lang.Object类是所有类最根本的基类(或者叫父类、超类),如果我们新定义的一个类没有明确地指定继承自哪个基类,那么JAVA就会默认为它是继承自Object类的。
我们可以把JAVA中的类分为以下三种:
基本类: 使用class定义且不含有抽象方法的类。也就是一般的类(一般所说的类就是基本类),是对象的模板,是属性和方法的集合。可以继承其他基本类、抽象类、实现接口。
抽象类:使用abstract class定义的类,它可以含有,也可以不含有抽象方法。有抽象方法的类(抽象方法就是该方法必须由继承来实现,本身只定义,不实现)。抽象类可以有一个或多个抽象方法,他是基本类和接口类的过度。
接口类:使用interface定义的类。一般叫做接口,该类中的所有方法都是抽象方法,该类的方法本身只定义不实现。
抽象类和接口一个由【abstract class + 抽象类名】一个由【interface +接口名】定义,接口中的所以方法都是抽象方法,而抽象类有部分方法是自身实现了的,一些只定义不实现。
在这三种类型之间存在下面的继承规律:
类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
抽象类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
接口只能继承(extends)接口。
示例
<span style="font-size:18px;"><span style="font-size:18px;">public class Fruit{
public void price() {
System.out.println("水果价格");
}
public void weight() {
System.out.println("水果重量");
}
}
抽象类的定义:
public abstractclass Fruit {
public void price() {
System.out.println("水果价格");
}
public abstract void weight();
}
接口类的定义:
public interfaceFruit {
public void price();
public void weight() ;
}</span></span>
从上面我们可以看出接口就是抽象类的升级版,由于该类的方法全是抽象方法,所以把abstract换成interface。而接口的方法必须由子类才能实现。
请注意上面三条规律中每种继承情况下使用的不同的关键字extends和implements,它们是不可以随意替换的。大家知道,一个普通类继承一个接口后,必须实现这个接口中定义的所有方法,否则就只能被定义为抽象类。我在这里之所以没有对implements关键字使用“实现”这种说法是因为从概念上来说它也是表示一种继承关系,而且对于抽象类implements接口的情况下,它并不是一定要实现这个接口定义的任何方法,因此使用继承的说法更为合理一些。
以上三条规律同时遵守下面这些约束:
类和抽象类都只能最多继承一个类,或者最多继承一个抽象类,并且这两种情况是互斥的,也就是说它们要么继承一个类,要么继承一个抽象类。
类、抽象类和接口在继承接口时,不受数量的约束,理论上可以继承无限多个接口。当然,对于类来说,它必须实现它所继承的所有接口中定义的全部方法。
抽象类继承抽象类,或者实现接口时,可以部分、全部或者完全不实现父类抽象类的抽象(abstract)方法,或者父类接口中定义的接口。
类继承抽象类,或者实现接口时,必须全部实现父类抽象类的全部抽象(abstract)方法,或者父类接口中定义的全部接口。
继承给我们的编程带来的好处就是对原有类的复用(重用)
示例:
<span style="font-size:18px;"><span style="font-size:18px;">public class Sub{
private Parent p = new Parent();
public void doSomething() {
// 复用Parent类的方法
p.method();
// other code
}
}
class Parent{
public void method() {
// do something here
}
}</span></span>
当然,为了使代码更加有效,我们也可以在需要使用到原有类型(比如Parent p)时,才对它进行初始化。
五、继承、接口与多态的相关问题:
1、继承的作用?好处?坏处?
继承:通过继承实现代码复用。Java中所有的类都是通过直接或间接地继程java.lang.Object类得到的。继承而得到的类称为子类,被继承的类称为父类。子类不能继承父类中访问权限为private的成员变量和方法。子类可以重写父类的方法,及命名与父类同名的成员变量。但Java不支持多重继承,即一个类从多个超类派生的能力。
优点:
a因为大部分是继承而来的,实现代码重用,减少代码书写量;
b很容易修改和扩展已有的实现
缺点:
a打破了封装,因为基类向子类暴露了实现细节
b白盒重用,因为基类的内部细节通常对子类是可见的
c当父类的实现改变时可能要相应的对子类做出改变
d不能在运行时改变由父类继承来的实现
2、接口的好处?坏处?
优点:帮助Java语言实现一个类似于多继承的功能.但是实现的多继承功能不会使代码中的类之间出现网状关系,而是比较清楚的树状关系,类似于家谱的感觉。
缺点:如果向一个java接口加入一个新的方法时,所有实现这个接口的类都得编写具体的实现。
3、多态的作用?好处?坏处?
作用:简单的说就是一个接口,多种实现;继承的表现就是多态(没有继承就没有多态。)
a应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。
b派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。
优点:
a可替换性(可以替换一存在的代码);
b可扩充性(增加新的子类不影响原有类的特性);
c接口性;
d灵活性;
e简化性
缺点:a“遮盖”私有方法。只有非private的方法才能够被笼罩,尽管编译器不会报错,然而也不会遵照我们所渴望的来实行。在导出类中,对于基类中的private方法,优秀采纳不同的名字。
b域在转型时候的问题。对于成员变量(域),导出类将占有从基类承袭而来的成员变量和自己的成员变量(变量名字相一同也是如此),况且,将分摊不同的存储空间,这么,导出类将具有两个名目一样的域。为了取得基类的域,务须实际地著名super.field能力走访,而默许的域则是导出类自己的域。
c静态计策是与类相关系的,而非与某个对象相干联的,那么它就不拥有多态行动。
4、什么是重载?什么是重写?
重载:
a方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载Overloading是一个类中多态性的一种表现。
b Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
c重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
重写:
a父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
b若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
c子类函数的访问修饰权限不能少于父类的;
5、什么是组合?
组合:
a通过创建一个由其他对象组合的对象来获得新功能的重用方法
b新功能的获得是通过调用组合对象的功能实现的
c有时又叫聚合
优点:
a被包含对象通过包含他们的类来访问
b黑盒重用,因为被包含对象的内部细节是不可见的
c很好的封装
d每个类专注于一个任务
e通过获得和被包含对象的类型相同的对象引用,可以在运行时动态定义组合的方式
缺点:
a结果系统可能会包含更多的对象
b为了使组合时可以使用不同的对象,必须小心的定义接口