继承与多态之动手动脑
继承与多态
一、继承条件下的构造方法的调用
class Grandparent { public Grandparent() { System.out.println("GrandParent Created."); } public Grandparent(String string) { System.out.println("GrandParent Created.String:" + string); } } class Parentt extends Grandparent { public Parentt() { super("Hello.Grandparent."); System.out.println("Parent Created"); // super("Hello.Grandparent."); } public Parentt(String s) { System.out.println("Parent Created.String:"+s); } } class Childd extends Parentt { public Childd() { super("Hello.Parent."); System.out.println("Child Created"); } } public class TestInherits { public static void main(String args[]) { Childd c = new Childd(); } }
输出:
GrandParent Created.
Parent Created.String:Hello.Parent.
Child Created
在构造子类的方法之前会先构造父类的方法,如果是多级继承,会先执行最顶级父类的构造方法,然后依次执行各级个子类的构造方法。若想在子类中调用父类的有参构造方法,需要在子类的构造方法的第一行加上super方法,注意:只能是第一行
为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不能反过来?
因为子类继承自父类,会沿用父类的东西(没被覆盖的函数以及可见的成员变量等),而这些东西子类是没有的,需要先初始化父类才能被使用。
构造一个对象,先调用其构造方法,来初始化其成员函数和成员变量。
子类拥有父的成员变量和成员方法,如果不调用,则从父类继承而来的成员变量和成员方法得不到正确的初始化。
不能反过来调用也是这个原因,因为父类根本不知道子类有神魔变量而且这样一来子类也得不到初始化的父类变量,导致程序运行出错!
二、不允许继承的类
不可变的“类”有何作用:
①可方便和安全的用于多线程的环境中
②访问他们不用加锁,可高性能的执行
public final class Address { private final String detail; private final String postCode; //在构造方法里初始化两个实例属性 public Address() { this.detail = ""; this.postCode = ""; } public Address(String detail , String postCode) { this.detail = detail; this.postCode = postCode; } //仅为两个实例属性提供getter方法 public String getDetail() { return this.detail; } public String getPostCode() { return this.postCode; } //重写equals方法,判断两个对象是否相等。 public boolean equals(Object obj) { if (obj instanceof Address) { Address ad = (Address)obj; if (this.getDetail().equals(ad.getDetail()) && this.getPostCode().equals(ad.getPostCode())) { return true; } } return false; } public int hashCode() { return detail.hashCode() + postCode.hashCode(); } public static void main(String args[]) { Address add=new Address("123","456"); System.out.println(add.getDetail()); System.out.println(add.getPostCode()); Address arr=new Address("123","456"); System.out.println(add.equals(arr)); } }
输出:
123
456
true
三、反汇编
public class ExplorationJDKSource { /** * @param args */ public static void main(String[] args) { System.out.println(new A()); } } class A{}
输出:
A@15db9742
我们用javap -c 来反编译该文件的.class文件,得到:
点进object里,发现tostring方法
public void main(object x)调用了String类的valueOf方法,
故而出现了这样的输出
三、神奇的“+”号和方法覆盖
public class Fruit { public String toString() { return "Fruit toString."; } public static void main(String args[]) { Fruit f=new Fruit(); System.out.println("f="+f); System.out.println("f="+f.toString()); } }
在输出字符串+对象时,会隐式的调用tostring()的方法
方法的覆盖:要求子类和父类的方法一样,并且实现在子类中调用父类的被覆盖的方法
class ABC { public void print() { System.out.println("Parent print"); } } public class test extends ABC{ public void print() { super.print(); System.out.println("Children print"); } public static void main(String[] args) { // TODO Auto-generated method stub test t1=new test(); t1.print(); } }
输出:
Parent print
Children print
利用super()的方法调用父类的对象,然后再调用父类的覆盖方法
覆盖的语法规则:
四、如何判断对象是否可以转换
public class TestInstanceof { public static void main(String[] args) { //声明hello时使用Object类,则hello的编译类型是Object,Object是所有类的父类 //但hello变量的实际类型是String Object hello = "Hello"; //String是Object类的子类,所以返回true。 System.out.println("字符串是否是Object类的实例:" + (hello instanceof Object)); //返回true。 System.out.println("字符串是否是String类的实例:" + (hello instanceof String)); //返回false。 System.out.println("字符串是否是Math类的实例:" + (hello instanceof Math)); //String实现了Comparable接口,所以返回true。 System.out.println("字符串是否是Comparable接口的实例:" + (hello instanceof Comparable)); String a = "Hello"; //String类既不是Math类,也不是Math类的父类,所以下面代码编译无法通过 //System.out.println("字符串是否是Math类的实例:" + (a instanceof Math)); } }
输出:
字符串是否是Object类的实例:true
字符串是否是String类的实例:true
字符串是否是Math类的实例:false
字符串是否是Comparable接口的实例:true
类型转换事例:
import java.net.InterfaceAddress; class Mammal{} class Dog extends Mammal {} class Cat extends Mammal{} public class TestCast { public static void main(String args[]) { Mammal m; Dog d=new Dog(); Cat c=new Cat(); m=d; //d=m; d=(Dog)m; //d=c; //c=(Cat)m; System.out.println("good"); } }
将子类dag赋值给基类m,可以实现,而将基类赋值给d需要进行强制类型转换,两个子类之间不能进行转换,m已经是Dog类,不能再被强制转换为Cat类
五、变态的类
public class ParentChildTest { public static void main(String[] args) { Parent parent=new Parent(); parent.printValue();//100 Child child=new Child(); child.printValue();//200 //父类变量去引用子类对象 parent=child; parent.printValue();//调用子类的方法200 //如果子类被当作父类使用,则通过子类访问的字段是父类的! parent.myValue++; System.out.println(parent.myValue);//101 parent.printValue();//200 ((Child)parent).myValue++; parent.printValue(); } } class Parent{ public int myValue=100; public void printValue() { System.out.println("Parent.printValue(),myValue="+myValue); } } class Child extends Parent{ public int myValue=200; public void printValue() { System.out.println("Child.printValue(),myValue="+myValue); } }
输出:
Parent.printValue(),myValue=100
Child.printValue(),myValue=200
Child.printValue(),myValue=200
101
Child.printValue(),myValue=200
Child.printValue(),myValue=201
前两个输出没问题,将子类赋值给基类对象,基类对象指向子类对象,调用的是子类的方法,故而输出第三条,
parent.myValue++;就是将子类当成父类来使用,通过子类访问的字段是父类的所以,当输出myvalue时是101,是父类的值+1;
最后将基类强制转换成子类后,将value++,此时访问的才是子类的字段,故而输出结果为201.
六、多态
package zoo4; import java.util.Vector; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); Vector<Animal> ans = new Vector<Animal>(); //饲养员小李喂养一只狮子 ans.add(new Lion()); //饲养员小李喂养十只猴子 for (int i = 0; i < 10; i++) { ans.add(new Monkey()); } //饲养员小李喂养5只鸽子 for (int i = 0; i < 5; i++) { ans.add(new Pigeon()); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Vector<Animal> ans) { for (Animal an : ans) { an.eat(); } } } abstract class Animal { public abstract void eat(); } class Lion extends Animal { public void eat() { System.out.println("我不吃肉谁敢吃肉!"); } } class Monkey extends Animal { public void eat() { System.out.println("我什么都吃,尤其喜欢香蕉。"); } } class Pigeon extends Animal { public void eat() { System.out.println("我要减肥,所以每天只吃一点大米。"); } }
输出:
我不吃肉谁敢吃肉!
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我什么都吃,尤其喜欢香蕉。
我要减肥,所以每天只吃一点大米。
我要减肥,所以每天只吃一点大米。
我要减肥,所以每天只吃一点大米。
我要减肥,所以每天只吃一点大米。
我要减肥,所以每天只吃一点大米。
通过定义一个抽象类anmial,来控制所以动物吃的功能,通过vector这个可变长数组,来控制动物的增减,最终实现当再加入其他不同的动物时,用较少的修改来完成动物的添加。
除了可以用抽象类来实现,还可以使用接口的形式来实现
package zoo5; import java.util.Vector; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); Vector<Animal> ans = new Vector<Animal>(); //饲养员小李喂养一只狮子 ans.add(new Lion()); //饲养员小李喂养十只猴子 for (int i = 0; i < 10; i++) { ans.add(new Monkey()); } //饲养员小李喂养5只鸽子 for (int i = 0; i < 5; i++) { ans.add(new Pigeon()); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Vector<Animal> ans) { for (Animal an : ans) { an.eat(); } } } interface Animal { public void eat(); } class Lion implements Animal { public void eat() { System.out.println("我不吃肉谁敢吃肉!"); } } class Monkey implements Animal { public void eat() { System.out.println("我什么都吃,尤其喜欢香蕉。"); } } class Pigeon implements Animal { public void eat() { System.out.println("我要减肥,所以每天只吃一点大米。"); } }
抽象类与抽象方法
抽象类的三种方法:
抽象类不能用来创建对象,一般用他来引用子类的对象
接口:
接口的使用:
接口的扩充:
接口与抽象类的区别: