Java有三大特性——封装,继承,多态
封装采用的是访问限制符类实现的,其目的是让使用者尽量少的了解到代码信息,使得代码在简洁性上面做出贡献;
继承通过extends来使得子类获得父类的成员属性和成员方法,其主要作用是为了类的代码重用性,使得重复使用相同代码不再显得累赘;
而多态则是通过三个语法来实现——向上转型、动态绑定、方法重写,当然,多态的实现也离不开继承的贡献;多态的功能体现在同一操作在面对不同对象的时候,可以有不同的解释,也可以用不同的执行结果.
多态
什么是多态呢?
多态其实是“封装”的更进一步的语法,具体特点如下:
1、使用多态:类的使用者不仅不需要知道类的实现细节,也不需要知道这个类的具体类型,只要知道这个类有一个draw方法就可以了;
2、方便扩展:味蕾如果需要增加一个新的形状,只需要增加一个子类即可;
3、减少一些分支语句;
多态的语法
向上转型
什么是向上转型?
向上转型其实是一种调用方法,在Java中,我们假设有A,B两个类,而B类继承A类,那么向上转型是对A的实例化的扩展,即A的对象能够访问到B类继承自A类的和B重写A的方法;
通俗的说,Animal是父类,Bird是子类,同样是一个eat()方法,在B类中重写之后Animal实例化的实例是不能够直接访问到Bird中的eat(),但是通过向上转型之后就能够做到;
Animal bird = new Animal("糊涂");
bird = new Bird("咔咔");
这就是向上转型的写法,但是我们更为常见的是将二者合二为一的写法:
Animal bird = new Bird("咔咔");
bird的类型是Animal类型,指向的对象是Bird;此时bird就能够调用到子类的Bird中的重写方法;
运行下列代码我们就会看到“咔咔在吃鸟食”;
public static void main(String[] args) {
Animal bird = new Bird("咔咔");
bird.eat("鸟食");
}
______________________________
咔咔在吃鸟食
向上转型的表示方法由三种:
- 直接赋值
- 方法传参
- 方法返回
直接赋值
直接赋值便是上述讲的的方法,便不再进行演示;
方法传参
方法传参顾名思义便是把引用作为参数传过去,然后父类的形参类型进行表示,这样也是隐式的进行向上转型了;
public class Polymorphism {
public static void main(String[] args) {
Bird bird = new Bird("咔咔");
eating(bird);
}
private static void eating(Animal animal) {
animal.eat("鸟食");
}
}
方法返回
这个也不难理解,即返回一个Animal类型的数据:
public class Polymorphism {
public static void main(String[] args) {
Animal animal = eating();
animal.eat("鸟食");
}
private static Animal eating() {
Bird bird = new Bird("咔咔");
return bird;
}
}
动态绑定
动态绑定名字虽然看起来很高大上,但是其实理解起来并不复杂,就是在向上转型的基础上对方法调用应该是怎么样调用的解释;
其在代码上并不能明确的看出来,但有的的确确的存在并且不可或缺;
在向上转型的基础上,主方法在调用父类和子类共同拥有的方法时,如果父类包含的方法在子类中有对应的同名同参数的方法时,会根据引用指向的对象来判断是执行父类中的方法还是子类中的方法;
以下列代码为例:
在代码中父类和子类的eat的方法的方法名相同,参数也相同,且指向的是Bird对象,所以调用的是子类中的eat方法;
而在第二段代码中,得出的结果是却是父类中的eat;
由此得出结论,在父类盒子类的方法名相同,参数也相同时,引用指向的是哪个对象,调用的方法就是哪个类中的方法.
public static void main(String[] args) {
Animal bird = new Bird("咔咔");
bird.eat("鸟食");
}
_________________________________________________
public class Polymorphism {
public static void main(String[] args) {
Animal animal = new Animal("糊涂");
animal.eat("米饭");
}
}
__________________________________________________
class Animal{
String name;
public Animal(String name){
this.name = name;
}
public void eat(String food){
System.out.print("结果1>");
System.out.println(name + "在吃" + food);
}
}
class Bird extends Animal{
public Bird(String name){
super(name);
}
@Override
public void eat(String food) {
System.out.print("结果2>");
System.out.println(name + "在吃" + food);
}
}
_______________________________
结果2>咔咔在吃鸟食
结果1>糊涂在吃米饭
在动态绑定是有以下几点要记住:
以调用eat方法为例子:
- 如果eat方法只在父类中存在,那么调用的就是父类中的eat方法;
- 如果eat只在子类中存在,那么调用eat方法就会编译报错;
- 如果父类和子类中都存在相同参数的eat方法,那么就涉及到动态绑定
- 如果父类和子类中的eat方法参数不相同,则调用子类的eat时就会编译报错
重写
子类实现父类同名、同参数、同参数个数方法,这就是重写,也叫方法覆盖;
在使用方法重写的时候有以下几条需要注意:
- 重写和重载是两回事,二者并没有任何关系,不要将二者混为一谈
- 普通方法可以重写,但是静态方法不能够重写
- 子类的重写的方法访问权限不能低于父类的访问权限(即如果父类的方法访问权限为public,那么重写的方法访问权限不能低于public)
- 重写方法的返回类型不要求和父类的方法相同,但一般都是写成相同的,特殊情况除外(重写的方法与父类的类型有关,即子类的返回类型可以是父类)
向下转型
向下转型的概念是相对与向上转型而言的,我们知道向上转型之后的实例化只能够访问到被重写的子类方法和父类的方法,而子类的其余方法是不能够访问到的,难道为了访问子类中的其他方法又要重新实例化一个子类的对象?
所以Java开发者对这个问题实行了向下转型,顾名思义就是通过向上转型后得到父类的引用,又通过向下转型复原到原来的类型;
向下转型必须保证这个引用所指向的对象是这个类型的子类,否则就会报错,那么我们在实际开发过程中往往无法辨别这个引用是否指向子类,这时又引进方法instanceof.
public static void main(String[] args) {
Animal animal = new Bird("糊涂");
boolean is_ins = animal instanceof Bird;
System.out.println(is_ins);
}
__________________________
true
在开发过程中比较常用的是向上转型,向下转型几乎很难使用到,那么什么场景会使用到呢?
在使用一些类的时候,子类有一些自己特有的方法,那么向上转型之后就无法通过引用访问到,这时候就要进项向下转型,将父类引用转为子类引用.;
向下转型代码演示:
public static void main(String[] args) {
Animal animal = new Bird("糊涂");
Bird bird = (Bird) animal;
bird.eat("鸟食");
animal.eat("鸟食");
_________________________________
class Animal{
String name;
public Animal(String name){
this.name = name;
}
public void eat(String food){
System.out.println(name + "在吃" + food + "父类");
}
public void run(){
System.out.println("走");
}
}
糊涂在吃鸟食
糊涂在吃鸟食
多态的实现
public class Abstract_class{
public static void main(String[] args) {
Shape shape1 = new Rect();
Shape shape2 = new Triangle();
Shape shape3 = new Circular();
//想要哪个类的执行结果,在draw中的参数就填哪个
draw(shape1);
draw(shape2);
}
public static void draw(Shape shape){
shape.draw();
}
}
class Shape{
public void draw() {
}
}
class Rect extends Shape{
@Override
public void draw(){
System.out.println("□");
}
}
class Triangle extends Shape{
@Override
public void draw(){
System.out.println("△");
}
}
class Circular extends Shape{
@Override
public void draw(){
System.out.println("○");
}
}
□
○