面向对象的三大特性:封装,继承,多态。
封装
封装将类的内部信息隐藏,不允许外部直接访问(private),通过该类提供的方法(get(),set())来访问操作类隐藏的信息。隐藏了类的信息,留出了访问操作的接口。
继承
为了复用父类的代码,两个类如果有is-a关系,子类就可以继承父类。
多态
多态就是指:方法中定义的引用变量(形参reference)指向的具体类型,和通过该reference调用的方法在编程的时候并不确定,要到了程序运行的时候才会确定。简而言之:引用变量指向的对象,和相应的方法调用,必须在程序运行时才能确定。
因为在程序运行的时候才能确定具体的类,这样,不用修改源代码,就可以让引用变量绑定在不同的类实现上,导致不同的实现。让程序可以选择多个运行状态,这就是多态。
举一个实际的例子:你去抽奖转转盘,奖品有A,B,C三种(转盘指针最终指向的奖品不确定(引用变量指向的具体对象不确定))。转完了盘你才知道你中什么奖(程序运行时,才确定调用的方法)。这就是多态表现形式:
具体了解多态之前,需要了解向上转型(UpCasting)
向上转型(Upcasting)
转型必须是在有继承关系之间的类中进行,Upcasting简而言之就是:父类引用指向子类对象。表现形式为
Father father = new Son();
在Upcasting中,指向子类对象的父类reference,只能访问父类中拥有的方法和属性,而对于子类存在父类没有的方法属性,该reference是没法使用的。如果子类重写了方法的话,在调用方法时,必调用子类重写后的方法。
多态的实现
实现条件
java实现多态必须有三个条件:继承,重写,向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
对于Java而言,它多态的实现机制遵循一个原则:当父类对象引用变量指向子类对象时,子类对象的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在父类中定义过的,也就是被子类覆盖的方法。
实现形式
在java中有两种形式可以实现多态,接口和继承。
基于继承实现的多态
基于继承机制和主要表现在父类和子类对方法的重写,对一个方法表现的不同行为
public 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
-------------------------------
在上面的代码中JNC、JGJ继承Wine,并且重写了drink()、toString()方法,程序运行结果是调用子类中方法,输出JNC、JGJ的名称,这就是多态的表现。不同的对象可以执行相同的行为,但是他们都需要通过自己的实现方式来执行,这就要得益于向上转型了。
基于接口实现的多态
//汽车接口
interface Car{
//要求 接口中有:汽车名称和售价
String getName();
int getPrice();
}
//宝马类
class BMW implements Car{
@Override
public String getName() {
// TODO Auto-generated method stub
//return null;
return "宝马";
}
@Override
public int getPrice() {
// TODO Auto-generated method stub
//return 0;
return 300000;
}
}
//奇瑞QQ
class CheryQQ implements Car{
@Override
public String getName() {
// TODO Auto-generated method stub
return "奇瑞QQ";
}
@Override
public int getPrice() {
// TODO Auto-generated method stub
return 40000;
}
}
//汽车出售店
class CarShop{
//收入
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 class jieKouDemo {
public static void main(String[]args){
CarShop shop=new CarShop();
//卖出一辆宝马
shop.sellCar(new BMW());
//卖出一辆奇瑞QQ
shop.sellCar(new CheryQQ());
System.out.println("总收入:"+shop.getMoney());
}
}
接口是通过实现接口并覆盖接口中同一方法的几不同的类体现的。
继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。
一个较为复杂的多态实例
首先需要了解的是多态的优先级注意:优先级从高到低:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
public class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
public class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
public class C extends B{
}
public class D extends B{
}
public class Test {
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 B
8--B and B
9--A and D
分析结果
第一行:,a1是A的实例对象,this指向A,但是A中没有this.show(b),所以到父类找,但是A没有父类(object不算),于是寻找下一个优先级,this.show(super b),B的父类是A,所以这里是this.show(a).结果为 A and A,
第二行:同上
第三行:直接this.show(d)
第四行:a2是指向B的实例化对象,输出B里面重写的
第五行:a2是B类的引用对象,类型为A,所以this指向A类,然后在A类里面找this.show(C)方法,没有找到,所以到了super.show(C)方法,由于A类没有超类,所以到了this.show(super C),C的超类是B,所以在A类里面找show(B),同样没有找到,发现B还有超类,即A,所以还继续在A类里面找show(A)方法,找到了,但是由于a2是一个类B的引用对象,而B类里面覆盖了A类的show(A)方法,所以最终执行的是B类里面的show(A)方法,即输出B and A;
第六行:略
第七行:
第八行:b是B类的一个实例化对象,首相执行this.show(C),在B类里面找show(C)方法,没有找到,所以到了super.show(c),B的超类是A,所以在A类中找show(C)方法,没有找到,于是到了this.show(super C),C的超类是B,所以在B类中找show(B)f方法,找到了,所以执行B类中的show(B)方法输出B and B;
第九行:b是B类的一个实例化对象,首相执行this.show(D),在B类里面找show(D)方法,没有找到,于是到了super.show(D),B的超类是A类,所以在A类里面找show(D)方法,找到了,输出A and D;