一.面向对象-多态
1.什么是多态:事物存在的多种形态
比如说有一个男人,我们可以说:来了个男人,也可以说来了个人,甚至说来了个生物;我们说的都是那个指定的男人对象;
class Animal{ } class Cat extends Animal{ } Cat c = new Cat(); Animal a = new Cat(); |
我现在new了一个猫的实体,我可以把这个实体装到专门装猫的一和盒子c里
也可以把它装到撞门装动物的盒子a里面,都是可以的;
现在这个猫的实体就有多了各种表现形态.Animal a = new Cat(); 这就是多态!
分析:
猫和动物的关系.猫类是动物类的所属,他们是继承关系.猫继承动物.装猫这个实体对象的盒子必须是猫类型,或动物类型.也就是猫本身或父类类型的变量;
现在加一个狗类
class Animal{ public void sleep(){ System.out.println("睡觉"); } } class Cat extends Animal{ } class Dog extends Animal{ } |
以前我们要让猫睡觉,或狗睡觉. 就得
Cat c = new Cat(); Dog d = new Dog(); c.sleep(); d.sleep(); |
现在不管是猫还是狗
Animal a1 = new Cat(); Animal a2 = new Dog(); a1.sleep(); a2.sleep(); |
用另一种说法就是:把猫这个对象的地址值指向a1,只要a1的变量类型是猫或者猫的父类类型,就可以用多态!
现在测试类写一个方法,让猫或者狗睡觉
public class AnimalTest { public static void main(String[] args) { } public static void toSleep(Cat c){ c.sleep(); } public static void toSleep(Dog d){ d.sleep(); } } |
我现在传一只猫进去,就可以让猫睡觉,传一狗进去可以让狗睡觉.如果说,我动物里面有很多子类,猪,牛,羊,龙什么的.这里岂不是要写很多个toSleep
那么怎么用多态的形式来优化一下;
public static void toSleep(Animal a){ a.sleep(); } |
不管你来什么动物,只要是Animal的子类我都可以用Animal的变量来接收.这样只需要一个方法就行了大家看到这幅图
2.多态的代码体现
就是父类的引用(变量)指向了自己的子类对象
Animal a = new Cat(); 必须有继承关系
3.多态的好处:提高代码的扩展性,简化代码
4.多态的向上向下转型
我们刚才传一个子类的对象进来,只能调用sleep()睡觉方法.如果说子类里边有自己独有的功能
public void catchFish(){ System.out.println("抓鱼"); } public void kanJia(){ System.out.println("看家"); } |
我现在传一个猫进来让他抓鱼!
public static void toSleep(Animal a){ a.catchFish(); } |
报错了.因为动物类里边没有抓鱼的方法,抓鱼只能是猫的特有功能,动物不具备抓鱼的功能;
Animal a = new Cat(); |
现在这样的表达式,我们可以看成这个猫对象向上转型了,转成动物型了,可以暂时把他看成一个动物类型.动物类型就不具备抓鱼的功能;
所以多态:父类的引用只能使用父类的方法(成员)这就是向上自动转型
如果现在我就要调用子类的方法可以吗?因为现在可以看成是一个动物,我们现在只能把这个动物转成猫类型才能调用抓鱼的方法;
向下强制转型
跟基本数据类型强转差不多
public static void toSleep(Animal a){ Cat c = (Cat)a; c.catchFish(); } |
把这个a动物类型强转成猫类型,就可以调用猫里边特有的方法;
现在又要变魔术了啊!如果说我现在想要 狗看家
public static void main(String[] args) { Dog d = new Dog(); toSleep(d); } public static void toSleep(Animal a){ Cat c = (Cat)a; } |
这时候强转就要报错!
类型的转换异常,说得很清楚, Dog这个类不能转换为Cat这个类;
为什么呢,我们说这个狗传到a变量里边就自动向上转型为动物了.然后把动物转换成猫没问题啊!其实是有问题的,这里要涉及到向下强转的本质:
强转的本质是:只是转换了他的引用类型,变量.而不是转换了那个实体对象
这里a变量里面的本质是一个狗的对象,狗的对象你现在强转把它放到装猫的盒子里,不适合了吧!
注:向下强转,首先得看实体对象的类型,使用多态后,才能强转回原来的类型!
那么现在我的需求是来一个狗,我让它看家,来一个猫我就让它抓鱼!
5.instanceof关键字
判断是不是什么类型!返回一个boolean值
public static void main(String[] args) { Dog d = new Dog(); toSleep(d); } public static void toSleep(Animal a){ boolean b = a instanceof Cat; System.out.println(b); } |
boolean isDog = a instanceof Animal; |
boolean isDog = a instanceof Cat; |
通过测试我们看出instanceof可以判断是一个类型本身和父类类型?
所以说这儿就可以判断出我们拿到的实体是猫还是狗,然后做出相应的动作!
public static void toSleep(Animal a){ if(a instanceof Cat){ Cat c = (Cat)a; c.catchFish(); } if(a instanceof Dog){ Dog d = (Dog)a; d.kanJia(); } } |
有人说:如果有很多中动物怎么办,岂不是要写很多的判断!是的.必须写,这个是没有办法的;除非以后接触到反射,就可以不写.直接一反射一句话搞定;
现在思考:
System.out.println(a instanceof Object); |
毫无疑问,皆成立嘛! 如果是
System.out.println(null instanceof Object); |
null是没有任何实体的.没任何动物.所以不属于任何类型!
这儿讲一下默认值!
public class AnimalTest { private static String name; private static int age; private static double high; private static boolean sex; private static Animal a; public static void main(String[] args) { System.out.println(name); System.out.println(age); System.out.println(high); System.out.println(sex); System.out.println(a); } } |
作为成员变量不赋值,他是有默认值的.基本数据类型都有自己的默认值!
如果是对象(引用数据类型)默认都为null;没任何内容;
当对象为null 的时候,如果调用他的方法就报错!
空针对异常.也就是找不到这个对象 |
这个错误,是我们最常见的错误!
二.equals方法
昨天我们学到了toString方法给对象用字符串的形式打印出来.
今天我们学另外一个常用的方法equals
equals的作用: 判断与某个对象是否”相等”!
我们看到这个方法返回的是一个boolean值如果相等就返回true 如果不相等就返回false
我们要比较两个对象是否相等,得再传一个对象过来比较吧,传什么对象,我们暂时不确定嘛,不管你传什么对象,我都可以用Object来接收,这里用了多态!
先来试试什么效果吧
public class Student { public static void main(String[] args) { Student s1 = new Student(); Student s2 = new Student(); Student s3 = s1; System.out.println(s1==s2); System.out.println(s1==s3); System.out.println(s1.equals(s3)); } } |
首先看s1==s2 他比的是对象的地址值,也就是hashCode()的值,如果不相等就false,这里是两个对象所以是false
然后s1==s3 开始s1指向了一个地址值,现在s3=s1 s3也指向了这个地址值.所以为true
第三个s1.equals(s3) 其实就是比的地址值
对象与对象之间==和equals没什么区别
在咱们生活当中,只要两个学生的姓名和年龄都相等,我们就可以认为他们是同一个学生!
public class Student { private String name; private int age; Student(String name ,int age){ this.name = name; this.age = age; } public static void main(String[] args) { Student s1 = new Student("张三",20); Student s2 = new Student("张三",20); System.out.println(s1.equals(s2)); } } |
现在判断出来是false,我们并不是要去比他们的地址值,我们要比的就是他们的姓名和年龄!
所以说Object中的equals满足不了我们的需求!
所以要复写!
public boolean equals(Object obj){ if(this.name==obj.name && this.age == obj.age){ return true; }else{ return false; } } |
报错了.因为obj当中没有name和age字段.这时候要向下强转!
public boolean equals(Object obj){ if(obj instanceof Student){ Student s = (Student)obj; if(s.name == name && s.age == age){ return true; } return false; }else{ return false; } } |
现在运行一下,就可以判断两个学生是否相等!
题目:下面的运行结果是?
1.
2.
1.报错 2.多此一举
注:向下强转,首先得看实体对象的类型,使用多态后,才能强转回原来的类型!
三.String的== 和equals
1.String的常量池
因为两个都是比的地址值.
而String类里面的==也是比的地址值,但是String涉及到一个常量池
String s1 = "hehe"; String s2 = "hehe"; String s3 = new String("hehe"); System.out.println(s1==s2); System.out.println(s1==s3); System.out.println(s1.equals(s3)); |
为什么呢?String有一个常量池:
String的equals比的字符串内容!不管你是不是在常量池!
通过API可以看到String类也复写了Object的equals方法!
Object--equals:比地址值
String--equals:比字符串内容
很多面试题用这个可以让大多数人懵逼!