基础知识点
Java程序包括两个重要的阶段: 第一阶段:编译阶段 Animal a2 = new Cat(); a2.move(); 在编译的时候,编译器只知道a2的类型是Animal类型。 因此在编译的时候就会去Animal类中找move()方法。 找到之后,绑定上去,此时发生静态绑定。能够绑定成功 表示编译通过。 new Cat()只能发生在运行期间开始执行,发生在堆当中开辟空间。 第二阶段:运行阶段 在运行的时候,堆内存中真实的java对象是Cat类型。 所以move()的行为一定是Cat对象发生的。 因此运行的时候就会调用Cat对象的move()方法。 这种绑定称为运行期绑定/动态绑定。 因为编译阶段是一种形态,运行的时候是另一种形态。因此得名:多态。
多态语法:向上/向下类型转换
、
关于基本数据类型之间的类型转换: 第一类:小容量转换成大容量,叫做自动类型转换; int i = 100; long x = i; 第二类:大容量转换成小容量,必须添加强制类型转换符才行。叫做强制类型转换。 int y = (int)x; 除了基本数据类型之间的类型转换之外,对于引用数据类型来说,也可以进行类型转换。只不过不叫做自动或者强制类型转换。叫做向下或向上类型转换。 关于java语言中的向上转型和向下转型: 向上转型(upcasting):子 ---> 父 (可以等同看作自动类型转换) 向下转型(downcasting):父 ---> 子 (可以等同看作强制类型转换) 不管是向上还是向下转型,两种类型之间必须要有继承关系,编译器才能编译通过,这个最基本的前提。
代码实例:
public class Animal {
public void move(){
System.out.println("动物在移动");
}
public void eat(){
System.out.println("正在吃东西");
}
}
public class Cat extends Animal {
@Override
public void move() {
System.out.println("猫在走猫步");
}
/*
这个方法/行为是子类特有的,父类没有
*/
public void catchmouse(){
System.out.println("猫在抓老鼠");
}
}
public class Bird extends Animal {
@Override
public void move() {
System.out.println("鸟儿在飞翔");;
}
}
代码里的注释非常重要!!!
涉及到向上/向下类型转换的基本原理和基本常见错误及其解决办法。
/**
* 关于基本数据类型之间的类型转换:
* 第一类:小容量转换成大容量,叫做自动类型转换;
* int i = 100;
* long x = i;
* 第二类:大容量转换成小容量,必须添加强制类型转换符才行。叫做强制类型转换。
* int y = (int)x;
*
* 除了基本数据类型之间的类型转换之外,对于引用数据类型来说,也可以进行类型转换。只不过不叫做自动或者强制类型转换。叫做向下或向上类型转换。
*
* 关于java语言中的向上转型和向下转型:
* 向上转型(upcasting):子 ---> 父 (可以等同看作自动类型转换)
* 向下转型(downcasting):父 ---> 子 (可以等同看作强制类型转换)
* 不管是向上还是向下转型,两种类型之间必须要有继承关系,编译器才能编译通过,这个最基本的前提。
**/
public class Test01 {
public static void main(String[] args) {
//创建对象
Animal a1 = new Animal();
a1.eat();
a1.move();
Cat c1 = new Cat();
c1.move();
c1.eat();
Bird b1 = new Bird();
b1.move();
b1.eat();
/*-------------------------------多态基本原理讲解-----------------------------------*/
/*
父类型更宽泛,子类型更精确
* 向上转型(upcasting):
* 1.子---> 父
* 2.也可以等同看作自动类型转换
* 3.前提:两种类型之间要有继承关系
4.父类型引用指向子类型对象。这个就是多态机制最核心的语法。
Animal a2 = new Cat();
* */
Animal a2 = new Cat();
/*
Java程序包括两个重要的阶段:
第一阶段:编译阶段
Animal a2 = new Cat();
a2.move();
在编译的时候,编译器只知道a2的类型是Animal类型。
因此在编译的时候就会去Animal类中找move()方法。
找到之后,绑定上去,此时发生静态绑定。能够绑定成功
表示编译通过。
new Cat()只能发生在运行期间开始执行,发生在堆当中开辟空间。
第二阶段:运行阶段
在运行的时候,堆内存中真实的java对象是Cat类型。
所以move()的行为一定是Cat对象发生的。
因此运行的时候就会调用Cat对象的move()方法。
这种绑定称为运行期绑定/动态绑定。
因为编译阶段是一种形态,运行的时候是另一种形态。因此得名:多态。
*/
a2.move();
/*以下代码是编译错误
会报错,因为在编译阶段编译器只知道a2得类型是Animal类型,父类型中没有catchmouse()这个方法
a2.catchmouse();
*/
/*
假如现在就是要让a2去抓老鼠怎么办?
向下转型:downcasting(父--->子)
什么时候我们会考虑使用向下转型?
当调用的方法是子类中特有的方法。
*/
Cat c2 = (Cat)a2;
c2.catchmouse();
/*
发生编译错误,在多态中,大前提是有继承关系,Bird和Cat没有直接得继承关系。
没有继承关系,编译器报错。
Bird b2 = new Bird();
Cat c3 =(Cat)b2;
*/
//向下转型
//为什么编译的时候可以通过?因为x是Animal类型,Animal和Bird之间存在继承关系,语法没问题,所以编译通过了。
//为什么运行的时候出现ClassCastException异常?因为运行时堆中真实对象是Cat对象,Cat无法转换成Bird,则出现类型转换异常
/*
Bird y = (Bird)x;
*/
Animal x = new Cat();
// 做向下转型之前,为了避免ClassCastException得发生,一般建议使用instanceof进行判断
/*
instanceof 运算符的语法规则:
1.instanceof运算符的结果一定是:true/false
2. 语法格式:
(引用 instanceof 类型)
3.例如:
(a instanceof Cat)
true表示什么?
引用指向的对象是Cat类型.
false表示什么?
*/
if(x instanceof Bird){
System.out.println("======");
Bird y = (Bird)x;
}
}
}
软件开发七大原则
开闭原则
最基本原则
多态符合开闭原则
无多态机制
如果主人要养狗,不想养猫了,创建一个狗的类,但是Master主人类中的feed()方法是喂猫,要是想养狗还得改动此方法,这样就动了源代码。
不符合0CP。不符合开闭原则.(因为这个功能的扩展是建立在修改Master类的基础之上的。)
OCP倡导的是什么?进行功能扩展的时候,最好不要修改有代码,最好是以新增代码来完成扩展对修改关闭、刘扩展开放。
Cat类
主人类
主程序入口
多态机制
在无多态机制中,如果主人要想养其他宠物,我们除了新建一个其他的宠物类,还得修改主人类中的feed(要养的宠物)喂养的代码。
但是有了多态,我们可以解除Master类和不同宠物之间的联系。
//宠物类 public class Pet{ public void eat(){} } //猫类 public class Cat extends Pet{ @Override public void eat(){ Sysyem.out.println("猫吃鱼"); } //狗类 public class Dog extends Pet{ @Override public void eat(){ Sysyem.out.println("狗吃骨头"); } //主人类 public class Master{ public void feed(Pet p){ p.eat(); } } //main public static void main(String[] args){ Cat c = new Cat(); Dog d = new Dog(); Master lisi = new Master(); //喂养 lisi.feed(c); lisi.feed(d); }
假如主人又想养兔子了,我们用多态机制,则不需要动Master中的源码,只需要增加一个新的兔子类即可。
public class Rabbit{ @Override public void eat(){ Sysyem.out.println("兔子吃胡萝卜"); }
//main public static void main(String[] args){ Cat c = new Cat(); Dog d = new Dog(); Rabbit r = new Rabbit(); Master lisi = new Master(); //喂养 lisi.feed(c); lisi.feed(d); lisi.feed(r); }
静态方法与方法覆盖
都知道,java的三大特性,封装、继承、多态。继承中有方法覆盖,方法有静态方法与动态方法。
但是方法覆盖针对的是实例方法,和静态方法无关。(方法覆盖和多态机制联合起来才有意义。)
public class Animal { public static void test(){ System.out.println("Animal执行了"); } } public class Cat extends Animal { public static void test() { System.out.println("Cat执行了"); } public calss Test{ public static void main(String[] args){ //结果分别执行每个类里边的test方法 Animal.test(); Cat.test(); Animal a = new Cat(); a.test(); //执行结果是“Animal执行了”。 //原因:静态方法在内存中单独开辟空间,不与对象绑定,与类绑定。a对应的是Animal类,所以直接调用的是Animal中的test方法。与多态无关。 } }
实例变量与多态无关
实例变量没有多态这一说,与多态无关。记住就行,不需要弄懂原理。
下例代码结果为 “张三”。