继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法。或子类从父类继承方法,使得子类具有父类相同的行为。父类更通用,子类更具体。子类会具有父类的一般特性也会具有自身的特性。
重写(Override) ------ 只能发生在继承关系里
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
方法的重写规则:
参数列表,返回类型必须完全与被重写方法的相同;
访问权限不能比父类中被重写的方法的访问权限更低;
声明为final的方法不能被重写;
声明为static的方法不能被重写,但是能够被再次声明;
构造方法不能被重写。
重载(Overload) ------- 继承,和同一个类
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
重载规则:
被重载的方法必须改变参数列表(参数个数或类型不一样);
被重载的方法可以改变返回类型,和访问修饰符;
方法能够在同一个类中或者在一个子类中被重载
5. 继承的初始化顺序
父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化--->子类对象构造方法
6. 构造器
子类是不继承父类的构造器的,它只是调用。子类的构造过程必须调用父类的构造方法。如果子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法。如果用super关键字在子类里调用父类的构造方法,则必须在子类的构造方法中的第一行。所以,子类的每一个构造方法的第一条语句默认都是super()。显式调用或隐式调用(无参构造方法)。例如:
class SuperClass { private int n; SuperClass(){ System.out.println("SuperClass()"); } SuperClass(int n) { System.out.println("SuperClass(int n)"); this.n = n; } } // SubClass 类继承 class SubClass extends SuperClass{ private int n; SubClass(){ // 自动调用父类的无参数构造器 System.out.println("SubClass"); } public SubClass(int n){ super(300); // 调用父类中带有参数的构造器 System.out.println("SubClass(int n):"+n); this.n = n; } } // SubClas2 类继承 class SubClass2 extends SuperClass{ private int n; SubClass2(){ super(300); // 调用父类中带有参数的构造器 System.out.println("SubClass2"); } public SubClass2(int n){ // 自动调用父类的无参数构造器 System.out.println("SubClass2(int n):"+n); this.n = n; } } public class TestSuperSub{ public static void main (String args[]){ System.out.println("------SubClass 类继承------"); SubClass sc1 = new SubClass(); SubClass sc2 = new SubClass(100); System.out.println("------SubClass2 类继承------"); SubClass2 sc3 = new SubClass2(); SubClass2 sc4 = new SubClass2(200); } } 输出结果为: ------SubClass 类继承------ SuperClass() SubClass SuperClass(int n) SubClass(int n):100 ------SubClass2 类继承------ SuperClass(int n) SubClass2 SuperClass() SubClass2(int n):200
要注意的是:如果子类构造方法中既没有显示调用父类的构造方法,而父类没有无参的构造方法,则编译出错。
7. Object类
Object类是所有类的父类,如果一个类没有使用extends关键字明确标识继承另一个类,那么这个类默认继承Object类。Object类中的方法,适合所有子类!!!
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。封装可以使我们容易地修改类的内部实现,而无需修改使用了该类的客户代码。并且,可以对成员变量进行更精确的控制。
指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)。
多态存在的三个必要条件
继承、重写、父类引用指向子类对象(向上转型)
Animal animal = new Dog()
1.可以调用父类中所有的方法和属性;
2.如果子类重写了父类的方法,则调用子类重写的方法;
3.子类特有的属性和方法不可以调用。
Animal animal = new Dog() Dog dog2 = (Dog)animal
1.父类引用没有指向该子类的对象,则不能向下类型转换,如:Dog dog2 = (Dog)new Animal();
2.父类的引用指向其他子类的对象,则不能通过强制转为该子类的对象,如:Animal animal = new Dog();
Dog dog2 = (Dog)animal;Cat cat = (Cat)animal;// 编译Cat,运行Dog;
3.有风险,可能出现数据溢出
instanceof运算符,来解决引用对象的类型,避免类型转换的安全性问题。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。
if(animal instanceof Cat ){ Cat cat = (Cat)animal; }
instanceof和getClass这两种方法都可以比较一个对象是否和另一个对象属于同一个类的实例,但是两者在判断上面是有差别的。Instanceof进行类型检查规则是:你属于该类吗?或者你属于该类的派生类吗?而通过getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断,不会存在继承方面的考虑。
多态的应用实例: Java 多态 ——一个案例 彻底搞懂它
1.了解继承关系;
2.优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O);
3.判断是不是多态,多态需要考虑子类是否有重写该方法,如果有重写需要调用重写的方法。
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