一、面向对象之继承
(1)继承:把原有类中的公共的代码抽取到一个新类中,原有类和新类使用extends关键字进行关联。新类称之为父类,原有类
称之为子类,子类会继承父类的所有属性和方法。但是子类只能使用父类对子类可见的属性和方法(权限设置)。
案例:
public class ExtendDemo {
public static void main(String[] args) {
Student stu = new Student();
stu.eat();
}
}
class Person{
private int name ;
private int age ;
public Person() {
}
public Person(int name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭中...");
}
private void work(){
System.out.println("工作中...");
}
}
class Student extends Person{
private int num;
private int name ;
private int age ;
public Student() {
}
public Student(int num, int name, int age) {
this.num = num;
this.name = name;
this.age = age;
}
}
子类Student可以调用父类中,可见(public)方法eat,而不可以调用work方法,因为work的权限为private。
(2)权限修饰符
本类 | 同包类 | 子类 | 其他类 | |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
default | 可以 | 可以 | 不可以(同包子类可以) | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
(3)多重继承与多层继承
java中类是多层继承,而不是多继承。换句话说 Student 能继承Person类,Person类又可以继承Person1类。但是,它不能同时继承Person、Person1、、、、这样的目的是考虑到如果,使用多继承,继承的类中定义了同名的方法,子类就不知道调用哪种方法,容易导致方法歧义。故Java设计者在设计的时候,考虑到这一点,所以,Java继承类是单继承,Java的继承结构是树状的。
(4)super关键字
super关键字可以调用父类可见的方法和属性
super.eat();//调用父类可见的方法
super.height = height;//调用父类可见的属性
当创建子类对象时,会默认去调用父类的构造方法,即在子类的构造方法中调用“super()”,即使没有书写super(),也会有默认
的“super()”。前提父类有无参构造方法。如果super(参数列表),则表示调用父类的有参构造方法。
注意:
super():调用父类的无参构造,super()代码必须放在方法的有效代码的第一行
this():调用本类的构造方法,必须放在有效代码的第一行
所以,super()和this()不能共存
(5)重写
在父子类中出现了方法签名一致的非静态方法,就称之为重写(Override),或者称之为覆盖
重写的"两等两小一大"原则:
- 方法签名一致,即方法名和参数列表保持一致
- 如果父类方法的返回值类型是基本数据类型/void,那么子类中的方法的返回值类型必须和父类的返回值类型保持一致
- 子类重写的方法的权限修饰符,必须要比父类方法的权限修饰符范围更大或相等
- 如果父类方法的返回值类型是引用数据类型,那么子类方法的返回值类型要么和父类的返回值类型一致,要么是父类返回值类型的子类。
- 子类抛出的编译时异常小于等于父类方法抛出异常(运行时异常对于重写没有影响)
补充:父子类中出现了方法签名一致的静态方法,就称之为隐藏。此时,子类的方法不会覆盖父类的方法。子类的方法被隐藏了。
二、面向对象之多态
多态:事物的多种形态
编译时多态:方法的重载
运行时多态:方法的重写
(1)运行时多态-向上转型
public class PolyDemo {
public static void main(String[] args) {
//向上转型
Pets p = new Dog();
p.eat();
//向下转型
Dog dog = (Dog) p;
dog.play();
}
}
class Pets{
public void eat(){
System.out.println("宠物要吃饭");
}
}
class Dog extends Pets{
public void eat(){
System.out.println("狗狗要吃饭");
}
public void play(){
System.out.println("狗狗要玩耍");
}
}
结果:
狗狗要吃饭
狗狗要玩耍
分析:通过多态性,向上转型调用子类覆写的方法,然后向下转型,调用子类特有的方法。
向上转型 : 父类 名称 = new 子类();
向下转型 : 子类 名称 = (子类) 父类名称();
多态编译时只会检查“=”左边和右边是否有继承关系,并不会真的去检查右边的实际类型。但在真正运行代码时,会根据右边实际创建的对象来调用对应的方法。
补充:
public class PolyDemo {
public static void main(String[] args) {
//向上转型
Pets p = new Dog();
p.eat();
//向下转型
Cat cat = (Cat) p;
cat.play();
}
}
class Pets{
public void eat(){
System.out.println("宠物要吃饭");
}
}
class Dog extends Pets{
public void eat(){
System.out.println("狗狗要吃饭");
}
public void play(){
System.out.println("狗狗要玩耍");
}
}
class Cat extends Pets{
public void eat(){
System.out.println("猫猫要吃饭");
}
public void play(){
System.out.println("猫摸要玩耍");
}
}
运行以上案例:会报“java.lang.ClassCastException”异常。
分析:因为编译的时候,只会检查左右两边是否具有继承关系,如果左右两边具有继承关系,那么就不会报编译错误。
在编译阶段,发现p的类型是Pets,Pets和Dog有继承关系,所以编译通过,实际运行过程中,发现p的实际类型是Dog,Dog不能转换为Cat,所以会报“java.lang.ClassCastException”异常。