第一章:继承(Extexnds)
1.1什么是继承?作用?
继承:指一个对象直接使用另一对象的属性和方法
继承的作用:
基本作用:子类继承父类,代码可以得到复用。
主要(重要)作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。
2.1继承相关特性
① B类继承A类,则称A类为超类(superclass)、父类、基类, B类则称为子类(subclass)、派生类、扩展类。 class A{} class B extends A{} 我们平时聊天说的比较多的是:父类和子类。 superclass 父类 subclass 子类 ② java 中的继承只支持单继承,不支持多继承,C++中支持多继承, 这也是 java 体现简单性的一点,换句话说,java 中不允许这样写代码: class B extends A,C{ } 这是错误的。 ③ 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果, 例如:class C extends B,class B extends A,也就是说,C 直接继承 B, 其实 C 还间接继承 A。 ④ java 中规定,子类继承父类,除构造方法不能继承之外,剩下都可以继承。 但是私有的属性无法在子类中直接访问。(父类中private修饰的不能在子类中 直接访问。可以通过间接的手段来访问。) ⑤ java 中的类没有显示的继承任何类,则默认继承 Object类,Object类是 java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有 Object类型中所有的特征。 ⑥ 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它 们之间的耦合度非常高,Account 类发生改变之后会马上影响到 CreditAccount 类
什么时候可以使用继承?
能使用“is a”关系可描述清楚。
3.1方法覆盖(OverRide)
什么是方法覆盖?
当子类继承父类后,继承过来的方法无法满足子类的需求,子类将这个方法进行重写。
将继承过来的方法进行覆盖,执行覆盖后的方法。
方法覆盖满足的条件:
1.两个类为继承关系。
2.重写之后的方法和之前方法具有:相同返回值类型,相同方法名,相同形参列表
3.访问权限不能更低,只能更高。
4.重写之后的方法不能比之前的方法抛出个更多异常。
注意事项:
1.方法覆盖只针对于方法,与属性无关。
2.私有方法无法覆盖
3.构造方法无法覆盖
4.方法覆盖只针对实例方法,静态方法无意义。
代码演示:
/** * 父类:动物类 */ class Animal{ private String name; //无参构造方法 public Animal() { } //有参构造方法 public Animal(String name) { this.name = name; } //动物具有吃东西的方法 public void eat(){ } }
/** * 猫继承动物类 猫是动物 */ class Cat extends Animal{ //无参构造方法 public Cat(){ } //猫类重写父类eat()方法! public void eat(){ System.out.println("猫吃猫粮!"); } }
/** * 狗继承动物类 狗是动物 */ class Dog extends Animal{ //无参构造方法 public Dog(){ } //狗类重写父类的eat()方法! public void eat(){ System.out.println("狗吃狗粮!!"); } }
/** 1. 测试类 */ public class Demo { public static void main(String[] args) { //创建猫对象 Cat cat=new Cat(); //猫吃东西 cat.eat(); //创建狗对象 Dog dog=new Dog(); //狗吃东西 dog.eat(); } }
第二章:多态
1.1什么是多态?
多种形态,多种状态,编译和运行有两个不同的状态。 编译期叫做静态绑定。 运行期叫做动态绑定。 Animal a = new Cat(); // 编译的时候编译器发现a的类型是Animal,所以编译器会去Animal类中找move()方法 // 找到了,绑定,编译通过。但是运行的时候和底层堆内存当中的实际对象有关 // 真正执行的时候会自动调用“堆内存中真实对象”的相关方法。 a.move(); 多态的典型代码:父类型的引用指向子类型的对象。
2.1基础语法
向上转型和向下转型的概念:
向上转型:子--->父 (upcasting) 又被称为自动类型转换:Animal a = new Cat(); 向下转型:父--->子 (downcasting) 又被称为强制类型转换:Cat c = (Cat)a; 需要添加强制类型转换符。 什么时候需要向下转型? 需要调用或者执行子类对象中特有的方法。 必须进行向下转型,才可以调用。 向下转型有风险吗? 容易出现ClassCastException(类型转换异常) 怎么避免这个风险? instanceof运算符,可以在程序运行阶段动态的判断某个引用指向的对象 是否为某一种类型。 养成好习惯,向下转型之前一定要使用instanceof运算符进行判断。 不管是向上转型还是向下转型,首先他们之间必须有继承关系,这样编译器就不会报错。
代码演示:
public class OverrideTest05{ public static void main(String[] args){ // 静态方法可以使用“引用.”来调用吗?可以 // 虽然使用“引用.”来调用,但是和对象无关。 Animal a = new Cat(); //多态 // 静态方法和对象无关。 // 虽然使用“引用.”来调用。但是实际运行的时候还是:Animal.doSome() a.doSome(); } } class Animal{ // 父类的静态方法 public static void doSome(){ System.out.println("Animal的doSome方法执行!"); } } class Cat extends Animal{ // 尝试在子类当中对父类的静态方法进行重写 public static void doSome(){ System.out.println("Cat的doSome方法执行!"); } }
3.1 instanceof运算符
类型向下转型时,必须使用instanceof进行判断。
强调:
1.在运行阶段动态判断引用指向对象类型
2.语法: 引用 instanceof 类型
3.运算结果为:true 或者false
4.假设C instanceof Cat为true,则C引用指向堆内存Java对象为一个Cat,如果为False 说明不是一个Cat
代码演示:
/** * 父类:动物类 */ class Animal{ public Animal() { } //动物具有吃东西的方法 public void eat(){ } }
/** * 猫继承动物类 猫是动物 */ class Cat extends Animal { public Cat() { } //猫类重写父类eat()方法! public void eat() { System.out.println("猫吃猫粮!"); } public void run(){ System.out.println("猫在走猫步"); } }
/** * 狗继承动物类 狗是动物 */ class Dog extends Animal{ public Dog(){ } //狗类重写父类的eat()方法! public void eat(){ System.out.println("狗吃狗粮!!"); } public void run(){ System.out.println("狗仔跳墙!1"); } }
/** * 测试类 */ public class Demo { public static void main(String[] args) { //向下转型:父类调用子类特有的方法。 Animal animal=new Cat(); Animal animal1=new Dog(); //必须使用instanceof进行判断 if(animal instanceof Cat) { Cat c=(Cat) animal; c.run();//子类特有 }else if (animal1 instanceof Dog){ Dog d=(Dog) animal1; d.run();//子类特有 } } }
4.1 super关键字
1.super能出现在实例方法和构造方法中。
2.super的语法是:“super.”、“super()”
3.super不能使用在静态方法中。、
4.super. 大部分情况下是可以省略的。
5.super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。
6.super()表示通过子类对象调用父类的构造方法
7.当一个无参构造方法第一行既没有this()也没有super()默认有一个super().
8.this()与super()不饿共存,且只在第一行.父类构造方法一定执行
什么时候super不能省略?
如果父与子类有相同的属性,想在子类中访问父类的特征。
代码演示:
//书 public class Book { //书名 String name; //构造方法 public Book(){ super(); } public Book(String name){ super(); this.name = name; } }
//纸质书 public class PaperBook extends Book { //构造方法 public PaperBook(){ super(); } public PaperBook(String name){ super(); this.name = name; } //打印书名 public void printName(){ System.out.println("this.name->书名 : " + this.name); System.out.println("super.name->书名 : " + super.name); } }
public class BookTest { public static void main(String[] args) { PaperBook book1 = new PaperBook("零基础学 Java 卷 I"); book1.printName(); } }
通过以上内存结构图发现 this.name 和 super.name 实际上是同一块内存空间,所以它们输出结果是完全一样的。
如果在子类中加一个name属性呢?
//纸质书 public class PaperBook extends Book { String name; //在子类中也定义了一个 name 属性 //构造方法 public PaperBook(){ super(); } public PaperBook(String name){ super(); this.name = name;//这里的 this.name 代表子类的 name } //打印书名 public void printName(){ System.out.println("this.name->书名 : " + this.name); System.out.println("super.name->书名 : " + super.name); } }
父类 Book 的构造方法在执行的时候给 super.name 赋值null,子类 PaperBook 的构造方法在执行的时候给 this.name 赋值“零基础学 Java 卷 I”,由于在子类 PaperBook 中定义了重名的变量 name 导致在当前对象中有两个 name,一个是从父类中继承过来的,一个是自己的,如果此时想访问父类中继承过来的 name 则必须使用 super.name,当直接访问 name 或者 this.name 都表示访问当前对象自己的 name。