文章目录
- 第九章 面向对象(3)
- 9.1 抽象类
- 9.1.1 抽象方法
- 9.1.2 抽象类
- 1. 概念
- 2.抽象类的特点
- 3. 总结:
- 9.2 接口(Interface)
- 9.2.1 概念
- 9.2.2 接口的特点
- 9.3 接口的实现(implements)
- 9.3.1 概念
- 9.3.2 类与类,类与接口,接口与接口的关系
- 9.3.3 实现接口和继承类的应用场景
- 9.3.内部类(面试题重点)
- 9.3.1 概述
- 9.3.2 成员内部类
- 9.3.2.1 普通成员内部类
- 9.3.2.2 私有成员内部类
- 9.3.2.3 静态成员内部类
- 9.3.3 局部内部类(重点)
- 9.3.3.1 非静态方法定义局部内部类
- 9.3.3.2 静态方法定义局部内部类
- 9.3.3 局部内部类(重点)
- 9.3.3.1 非静态方法定义局部内部类
- 9.3.4 匿名内部类(重点)
- 9.4 多态(重点重点)
- 9.4.1 基本概念
- 9.4.2 成员变量的访问原则
- 9.4.3 成员方法的访问原则
- 9.4.4 引用数据类型的向上向下转型(重难点)
- 9.4.5 多态的弊端和好处
第九章 面向对象(3)
9.1 抽象类
9.1.1 抽象方法
- 抽象方法:java中只有方法声明没有方法实现,并且被关键abstract修饰的方法就是抽象方法。
修饰符 abstract 返回值类型 方法名(参数列表);
- 作用: 提取相同功能不同结果的方法的声明部分, 只需要定义一个这样的方法名字,但是不提供方法实现.以后谁想用 这个方法,那就自己添加方法体。
- 注意:只能定义在抽象类中和接口中.
案例:
package com.inter;
public interface Usb {
public abstract void powerOn();
public abstract void powerOff();
public abstract void intallDriver();
}
- 复习:已学过的4种方法的特点
(1)普通方法:
public 返回值类型 方法名 (参数列表) {
方法体;
}
(2)静态方法:有自己的内存空间,调用方法,直接 类名.方法名
public static 返回值类型 方法名 (参数列表) {
方法体;
}
(3)最终方法:不能被子类重写,但是可以使用
public final 返回值类型 方法名 (参数列表) {
方法体;
}
(4)抽象方法:只能在抽象类,接口中使用,目的是制定标准
public abstract 返回值类型 方法名(参数列表);
9.1.2 抽象类
1. 概念
- 属于类的一种,所有对象都是由类创建,但是不是所有的类都能创建对象。
2.概念: java中被关键字 abstract 修饰的类就是抽象类 用来存储抽象方法的类就是抽象类.
- 抽象类中一般都会定义抽象方法.
- 如果不定义抽象方法,直接定义为普通类。
- 抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
- 格式
修饰符 abstract class 类名{
}
内容:
(1) 属性(成员变量)
(2)构造方法,
(3)行为(方法)
(4)抽象方法
---- 行为分为普通行为(普通方法,静态方法)+ 特殊行为(抽象方法)
- abstract只能修饰类和方法,抽象类除了定义普通类中的所有内容,还可以定义抽象方法.
- 子类继承抽象类,必须强制实行父类中的抽象方法(重写)才可以,否则会报错,无法继承。
- this就是当前new谁,this就是谁。
2.抽象类的特点
- 特点
(1)有构造方法但是不能创建对象, 抽象类不能实例化 抽象类就是用当做父类用的 [天生就是当爹的]
(2)构造方法给子类创建对象的时候调用使用, 通过super语句使用抽象类中构造方法
(3)抽象类的抽象方法必须要求子类重写,而且要重写全部的抽象方法 ,一个普通类如果是抽象类的子类,那么这个类在继承抽象类之后, 就必须全部重写抽象方法.否则子类就会编译报错.
- 抽象类和抽象方法的关系
(1)抽象类不一定有抽象方法, 但是一般定义抽象类都会在里面写抽象方法.
(2)有抽象方法的类一定是抽象类!!!
- 抽象类的使用场景
抽象类一般用于父类,用来抽取一系列类的公共资源(属性和行为),如果父类的有些方法,即使在父类中提供了方法实现,子类继承过去后依然重写,那么就把这些方法定义为抽象方法,在父类中没必要提供实现 。
- 练习
package com.block;
public abstract class Employee {
//属性
String name;
String number;
int salary;
//构造方法
public Employee() {
}
//有参构造
public Employee(String name,String number,int salary){
System.out.println("Employee抽象类中输出this:" + this);
this.name = name;
this.number = number;
this.salary = salary;
}
//抽象方法
public abstract void work();
}
-----------------------------------------
//子类
package com.block;
public class chenXuYuan extends Employee{
int jiangj;
public chenXuYuan(String name, String number, int salary, int jiangj) {
super();
this.name = name;
this.number = number;
this.salary = salary;
this.jiangj = jiangj;
}
@Override
public void work() {
System.out.println("程序员做软件开发");
}
}
----------------------------------------------------
//子类
package com.block;
public class manager extends Employee {
int jiangj;
public manager(String name,String number,int salary,int jiangj) {
super();
this.name = name;
this.number = number;
this.salary = salary;
this.jiangj = jiangj;
}
@Override
public void work() {
System.out.println("项目经理控制进度");
}
}
--------------------------------------
//子类
package com.block;
public class ceShi extends Employee {
public ceShi (String name,String number,int sala) {
super();
this.name = name;
this.number = number;
this.salary = salary;
}
@Override
public void work() {
System.out.println("测试进行软件测试");
}
}
------------------------------------------------------
//测试
package com.block;
public class ExampleTest {
public static void main(String[] args) {
chenXuYuan c = new chenXuYuan("张三","1111111",5000,1000);
ceShi s = new ceShi("李四","22222",4000);
manager m = new manager("王五","33333",5000,2000);
c.work();
s.work();
m.work();
}
}
3. 总结:
(1)抽象类只能当做父类。
(2)抽象类中的构造方法子类只能调用,不能更改。
(3) 子类必须先实现抽象类的抽象方法。
9.2 接口(Interface)
9.2.1 概念
- 概念
(1)接口也是java一种引用数据类型。
(2)接口在Java中是一个抽象类型,是**抽象方法的集合(**只能定义抽象方法,可以默认不写abstract),以interface来声明。
(3)接口之间可以相互继承,普通类可以**实现**接口,从而实现接口的抽象方法。
- 格式:
修饰符 interface 接口名{
接口内容(抽象方法);
}
- 注意:
(1)接口不是类(没有用class声明),类描述对象的属性和方法,接口包含类的方法(只声明不实现的抽象方法)
(2)接口不能实例化,但是可以实现,一个实现接口的类,必须实现接口里面的所有方法,否则必须声明为抽象类。
(3)接口的源文件也是.java文件, 也是参与编译的 。
(4)编译后的文件也是字节码文件(*.class) [接口的本质其实还是一个类].
(5)运行时字节码文件也需要加载到内存的方法区中
- 抽象类和接口的区别
(1)抽象类是把类做增强处理,在原基础上可以增加抽象方法,不能被实例化,只能被继承。
(2)接口不是类,接口是把类做削弱处理,接口拥有抽象类的部分性质,不能被继承,但是可以被实现。
- 代码示例:
public interface Car {
//简写 - 接口中的方法在Java8以前默认的方法都是抽象方法
public void run();
// //完整写法
public abstract void stop();
}
9.2.2 接口的特点
- 接口的特性
(1) 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
(2)接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
(3) 接口中的方法是不能在接口中实现的,只是声明,只能由实现接口的类来实现接口中的方法。
- 接口支持多继承的理解:
(1)类与类之间是单继承,支持多重继承,一个父类可以有多个子类,一个子类只能有一个父类
(2) 抽象类只是类的增强,在继承体系中,完全遵从java普通类的继承规则
(3) 普通类和接口之间,接口脱离类的约束,没有继承这一说法,但是普通类可以实现接口,并且可以实现多个接口。
(4)重点:接口与接口之间可以继承,(接口不是类,但是接口与接口是同类,可以有继承体系)
- 接口与类的区别(面试题)
接口不是类:
(1)接口不能用于实例化对象
(2) 接口不能定义构造方法(构造器)
(3) 接口中所有方法都必须是抽象方法--java8之后可以定义默认(default)普通方法,格式:
修饰符 default 返回值类型 方法名(参数列表){
方法体 ;
------此时default不是访问修饰符,是接口中普通方法的定义规则。
(4) 接口不能包含成员变量,除了static和final变量(就是只能定义常量)
(5) 接口不是被类继承,是实现(implement).
(5)接口支持多继承
- 抽象类和接口的区别
(1) 抽象类中的方法可以有方法体(可以有普通方法),,但是接口中的方法只能声明,没有普通方法(java8以后包含java8可以用deafult声明普通方法 )。
(2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final(public final) 类型的。
(3)接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
(4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。(**单继承多实现**)
9.3 接口的实现(implements)
9.3.1 概念
- 概念:
(1)实现关系:接口中的规则描述的只有声明没有具体的实现内容,要想被使用必须要把实现内容具体描述出来,并且,只能通过类对接口规则进行补充,类就和接口产生了关系,这种关系就叫实现关系
(2)接口的实现:类通过关键字implement连接接口,就可以对接口中的规则进行补充,这种类连接接口的方式叫做接口的实现。
(3)注意事项:类对接口中的规则进行补充,就有了接口描述的规则的功能,并且必须实现接口中的所有抽象方法(重写)。
一个类实现接口,相当于继承了接口。
一个类可以同时实现多个接口。
多实现的多个接口中不要出现方法名相同,返回值类型不同的规则 --- 没法重写--因为不符合方法重载 的规则。
- 格式:
修饰符 class 类名 implements 接口名1,接口名2……{
类内容
}
//单实现:一个类实现1个接口
多实现:一个类实现多个接口
- 代码示例:
public interface Car {
//简写 - 接口中的方法在Java8以前默认的方法都是抽象方法
public void run();
// //完整写法
public abstract void stop();
}
-------------------------------------
//接口类
public class TuoLaJi implements Car {
String color;
double prace;
@Override
public void run() {
System.out.println("拖拉机跑得慢");
}
@Override
public void stop() {
System.out.println("拖拉机停得慢");
}
}
9.3.2 类与类,类与接口,接口与接口的关系
(1)类与类的关系:
只能是继承关系(父子关系),java中的继承只支持单继承,多层继承。
(2)类与接口的关系:
只能是实现的关系(干父子关系),一个类实现一个接口就相当于继承了这个接口。
(3)接口和接口的关系:
继承关系,java中接口支持所有继承关系
单继承:A接口继承B接口
多层继承:A接口继承B接口,B接口继承C接口
多继承:A接口继承B,C,D,F,接口,一个接口可以继承多个接口(一个儿子多个爹)
(4)注意事项:
多个父接口有相同的抽象方法,相当于只继承一个抽象方法,若有类实现这个子接口也只需要实现一个同名的抽象方法。
9.3.3 实现接口和继承类的应用场景
抽象类是做标准的,接口也是做标准的,如何选择?
注意:抽象类是对类的增强,接口是对类的弱化(提取了抽象部分的功能)。
(1)选择抽象类:定义的父类天生就具有一些属性和属性,定义的时候一定为其实现一些功能。
(2)选择接口:定义的父类完全只做标准,不需要实现功能。
一个类可以在继承父类的时候,再实现多个接口
格式:
修饰符 class 子类 extends 父类 (普通类或抽象类) implements 接口1, 接口2 { }
9.3.内部类(面试题重点)
9.3.1 概述
- 内部类的概念:
(1)定义在一个类或一个方法中的另一个类称之为内部类。
(2)一个类如果定义在某个**大括号**里,这个类就是内部类
- 为什么有内部类?
(1)进行资源封装
(2)变相的实现多继承--A类中有三个内部类B,C,D,用E类继承A类,就相当于E继承了A,B,C,D
- 格式:
修饰符 class 外部类名{
修饰符 class 内部类名{
}
}
- 分类:
(1) 成员内部类:
普通成员内部类,
私有成员内部类
静态成员内部类
(2)局部内部类:定义在方法中的内部类
(3)匿名内部类(重点)
9.3.2 成员内部类
1. 定义位置:定义在类的成员位置(类中方法外,类体中)。
2. 特点:要实例化内部类,必须先实例化外部类。
3. 分类:
普通成员内部类
私有成员内部类
静态成员内部类
9.3.2.1 普通成员内部类
1. 格式:
public class 主类名称 {
public class 普通成员内部类名称{
}
}
2. 特点:
(1)外部类定义任何内容不受限制。
(2)普通成员内部类只能定义非静态的资源(静态的成员调用方式是 类名.属性名 ,而内部类不可以直接使用 类名.属性名 )
(3)普通成员内部类访问规则:必须先实例化外部类,再实例化内部类。实例化过后,静态就没有意义了。
(4)普通成员内部类可以使用外部类的所有资源(包括私有资源)。
3. 实例化方式(举例)
第1种:
外部类名 对象名 = new 外部类名();
外部类名.私有成员内部类名 对象名 = 外部类名.new 私有成员内部类名();
Student student = new Student();
Student.Monitor monitor1 = student.new Monitor();
第2种:
外部类名.私有成员内部类名 对象名 = new 外部类名().new 私有成员内部类名
Student.Monitor monitor2 = new Student().new Monitor;
代码示例:
public class Student {
public String name = "学生";
public int age = 18;
public class Monitor{
public String name = "班长";
public String hobbits = "打篮球";
public void manage() {
System.out.println(age);
System.out.println("管理班级");
}
}
}
-----------------------------
public class Test {
public static void main(String[] args) {
//第1种
Student student = new Student();
Student.Monitor monitor = student.new Monitor();
//第2种
Student.Monitor monitor1 = new Student().new Monitor();
System.out.println(monitor.name);
System.out.println(monitor.hobbits);
monitor.manage();
}
}
9.3.2.2 私有成员内部类
1. 格式:
public class 主类名称 {
private class 普通成员内部类名称{
}
}
2. 特点:
(1)外部类定义任何内容不受限制。
(2)私有成员内部类只能定义非静态的资源不能定义静态资源。
(3)私有成员内部类可以使用外部类的所有资源(包括私有资源
3. 实例化方式:
第1种:
外部类名 对象名 = new 外部类名();
外部类名.私有成员内部类名 对象名 = 外部类名.new 私有成员内部类名();
Student student = new Student();
Student.Monitor monitor1 = student.new Monitor();
第2种:
外部类名.私有成员内部类名 对象名 = new 外部类名().new 私有成员内部类名
Student.Monitor monitor2 = new Student().new Monitor;
第3种:
私有成员内部类名 对象名 = new 私有成员内部类名();
Monitor monitor2 = new Monitor();
注意:
实例化方式与普通成员内部类相同。 因为是私有的,所以只有在当前类中才可以实例化,但是其他类可以通过外部类进行方法调用。
完全走封装的体系,私有化内部类,对外提供公开的方法(把内部类隐藏得更深)。
代码示例:
public class Student {
public String name = "学生";
public int age = 18;
private class Monitor{
public String name = "班长";
public String hobbits = "打篮球";
public void manage() {
System.out.println(age);
System.out.println("管理班级");
}
}
//只能在当前类中实例化,方法有3种,可创建方法供其他类使用
public void show() {
//第1种
Student student = new Student();
Student.Monitor monitor = student.new Monitor();
//第2种
Student.Monitor monitor1 = new Student().new Monitor();
//第3种
Monitor monitor2 = new Monitor();
System.out.println(monitor.name);
monitor.manage();
}
}
-------------------------------------------
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
9.3.2.3 静态成员内部类
1. 格式:
public class 主类名称 {
private static class 普通成员内部类名称{
}
}
2. 特点:
(1)外部类定义任何内容不受限制。
(2)静态成员内部类可以定义任何内容不受限制。
(3)静态成员内部类不能使用外部类的非静态资源,可以直接使用外部类的静态资源。
3. 实例化方式:
外部类名.静态成员内部类名 对象名 = new 外部类名.静态成员内部类名
Student.Monitor monitor2 = new Student.Monitor;
代码示例:
public class Student {
public String name = "学生";
public static int age = 18;
public static class Monitor{
public String name = "班长";
public String hobbits = "打篮球";
public void manage() {
System.out.println(age);
System.out.println("管理班级");
}
}
}
-------------------------------------------
public class Test {
public static void main(String[] args) {
Student.Monitor monitor = new Student.Monitor();
monitor.manage();
System.out.println(monitor.name);
System.out.println(monitor.hobbits);
}
}
9.3.3 局部内部类(重点)
1. 概念:定义在方法体中的内部类就是局部内部类。
2. 格式:
修饰符 返回值类型 方法名(参数列表) {
class 局部内部类名
}
9.3.3.1 非静态方法定义局部内部类
1. 格式:
public void 方法名(){
class 局部内部类名{
}
}
2. 特点:
(1)局部内部类中只能定义非静态的资源。
(2)可以使用所在类中的以及所在方法中的所有资源。
3. 访问方式(实例化方式):
Monitor monitor = new Monitor();
通过方法在当前类中提供局部内部类的调用(在局部内部类所在方法中实例化内部类),之后再提供此方法给其他类使用。
代码示例:
public class Student {
public String name = "学生";
public static int age = 18;
public void show() {
class Monitor{
public String name = "班长";
public String hobbits = "打篮球";
public void manage() {
System.out.println(age);
System.out.println("管理班级");
}
}
Monitor monitor = new Monitor();
System.out.println(monitor.name);
monitor.manage();
}
}
---------------------------------------
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
9.3.3.2 静态方法定义局部内部类
1. 格式:
public static void 方法名(){
class 局部内部类名{
}
}
2. 特点:
(1)局部内部类中只能定义非静态的资源。
(2)只能使用外界的静态资源【所在类的静态资源】
3. 访问方式(实例化方式):
在局部内部类所在方法中实例化内部类,在其他类中 静态方法直接类名.方法名 调用。
代码示例:
public class Student {
public String name = "学生";
public static int age = 18;
public static class Monitor{
public String name = "班长";
public String hobbits = "打篮球";
public void manage() {
System.out.println(age);
System.out.println("管理班级");
}
}
}
-------------------------------------------
public class Test {
public static void main(String[] args) {
Student.Monitor monitor = new Student.Monitor();
monitor.manage();
System.out.println(monitor.name);
System.out.println(monitor.hobbits);
}
}
9.3.3 局部内部类(重点)
1. 概念:定义在方法体中的内部类就是局部内部类。
2. 格式:
修饰符 返回值类型 方法名(参数列表) {
class 局部内部类名
}
9.3.3.1 非静态方法定义局部内部类
1. 格式:
public void 方法名(){
class 局部内部类名{
}
}
2. 特点:
(1)局部内部类中只能定义非静态的资源。
(2)可以使用所在类中的以及所在方法中的所有资源。
3. 访问方式(实例化方式):
Monitor monitor = new Monitor();
通过方法在当前类中提供局部内部类的调用(在局部内部类所在方法中实例化内部类),之后再提供此方法给其他类使用。
代码示例:
public class Student {
public String name = "学生";
public static int age = 18;
public void show() {
class Monitor{
public String name = "班长";
public String hobbits = "打篮球";
public void manage() {
System.out.println(age);
System.out.println("管理班级");
}
}
Monitor monitor = new Monitor();
System.out.println(monitor.name);
monitor.manage();
}
}
---------------------------------------
public class Test {
public static void main(String[] args) {
//调用静态方法不需要初始化,实例化
Student.show();
}
}
9.3.4 匿名内部类(重点)
1. 概念:没有名字的内部类,有自己独有的类定义方式。匿名内部类本质是一个子类或实现类的对象。
2. 格式:
new 父类类名(参数) {
重写父类中方法
}
或
new 接口名(参数){
重写接口的抽象方法
}
3. 匿名内部类的使用前提:必须拥有继承关系或接口实现关系(重点)。
4. 场景:
匿名内部类其实是对父类的子类对象的体现:继承关系下,匿名内部类的本质就是一个父类的子类对象。可以把匿名内部类想象为,为一个父类快速提供一个子类对象便捷方式. 现有一个父类想要使用它的子类对象做事情,但是没有定义子类就直接创建子类的对象,这个时候需要匿名内部类。(创建一个父类对象,但是因为父类需要一个子类的实现,而我们又没有提供,那么默认会生成一个子类对象,这个对象就是匿名对象,多态中使用多)
5. 匿名内部类作为对象直接使用:
(1)直接当做匿名对象调用资源使用(只能使用一次)
格式: new 父类类名(参数){
重写父类中相关功能
}.方法名;
(2)可以赋值给父类类型的变量
父类类型 对象名 = new 父类类名(参数) {
重写父类中相关功能
};
对象名.方法名();
6.匿名内部类在开发中的使用:
当发现某个方法需要类的子类对象,就可以传递一个匿名内部类过去,简化传统的代码。
代码示例:
public abstract class Animal {
public abstract void eat();
}
------------------------------------
public class AnimalExample {
public static void main(String[] args) {
//直接当做一个匿名对象调用资源使用,只能使用一次
new Animal() {
@Override
public void eat() {
System.out.println("小狗爱吃肉骨头");
}
}.eat();
//2.赋值给父类类型的变量
Animal animal = new Animal() {
@Override
public void eat() {
System.out.println("鸭子喜欢吃虫虫......");
}
}; //记住加分号
animal.eat(); //可以直接调用,匿名内部类中已经实现了抽象方法。
}
}
9.4 多态(重点重点)
9.4.1 基本概念
1. 概念
(1)多态就是同一个行为具有多个不同表现形式或者形态的能力。
(2)多态就是 ,同一个接口使用不同的实例去执行不同的操作。
例如:一个人在不同的场景下有不同的身份。
2. 多态存在的三个必要条件:
(1)父子继承(普通类继承,抽象类继承)/ 接口实现
(2) 重写
(3)父类引用指向子类对象--Parent p = new Child();
3. 多态的优点
(1) 消除类型之间的耦合性
(2) 可替换性
(3)可扩充性
(4)接口性
(5)灵活性
4. 多态特点总结
(1)父类的引用指向子类对象
父类类型的变量
指向子类对象:父类类型的变量存放了子类对象的地址值
(2)接口类型引用指向实现类对象
接口类型引用:接口类型的变量
指向实现类对象:接口类型的变量存放了实现类对象的地址值
5. 格式:
父类名 变量名(对象名) = new 子类名(实参)
接口名 变量名 = new 实现类名(实参)
案例一:继承关系中的多态
/**
* 父类
* 描述:打印机抽象类
*/
public abstract class Printer {
//功能:打印
public abstract void print();
}
-------------------------------
/**
* 子类
* 描述:打印机子类--黑白打印机
*/
public class BlackPrinter extends Printer{
//重写父类方法
@Override
public void print() {
System.out.println("打印黑白文件");
}
}
/**
* 子类
* 描述:打印机的子类--彩色打印机
*/
public class ColorPrinter extends Printer{
//重写父类方法
@Override
public void print() {
System.out.println("打印彩色文件");
}
}
-------------------------------------------
/**
* 描述:打印机实例
*/
public class PrinterExample {
public static void main(String[] args) {
//1.没有运用多态时的定义
ColorPrinter colorPrinter = new ColorPrinter();
BlackPrinter blackPrinter = new BlackPrinter();
colorPrinter.print();
blackPrinter.print();
//2.运用多态时的定义(父类对象指向子类引用)
Printer printer1 = new ColorPrinter();
Printer printer2 = new BlackPrinter();
printer1.print();
printer2.print();
// 这里代码中直接体现了两个不同子类都可以用Printer接收,那么我们称为多态(一个类的不同表现形态)
}
}
案例二:实现关系中的多态
/**
* 描述:打印机接口
*/
public interface Printer {
//功能:打印
public abstract void print();
}
------------------------------------
/**
* 描述:打印机接口实现类--黑白打印机
*/
public class BlackPrinter implements Printer{
//重写接口抽象方法
@Override
public void print() {
System.out.println("打印黑白文件");
}
}
/**
* 描述:打印机接口实现类--彩色打印机
*/
public class ColorPrinter implements Printer{
//重写接口抽象方法
@Override
public void print() {
System.out.println("打印彩色文件");
}
}
-------------------------------
/**
* 描述:打印机实例
*/
public class PrinterExample {
public static void main(String[] args) {
//1.接口中可以直接使用实现类自己去接收,但是没有多态
ColorPrinter colorPrinter = new ColorPrinter();
BlackPrinter blackPrinter = new BlackPrinter();
colorPrinter.print();
blackPrinter.print();
//2.运用多态
Printer printer1 = new ColorPrinter();
Printer printer2 = new BlackPrinter();
printer1.print();
printer2.print();
}
}
9.4.2 成员变量的访问原则
1. 编译看左边(父类) , 运行看左边 Fu f = new Zi();
2. 多态中因为使用父类型去接收,在输出变量信息时,一定需要父类中的定义,子类型中定义的变量这时候不能使用。
3. 如果非要使用子类型自定义的变量,脱离多态用子类型自己去接收, Zi z = new Zi();
代码示例:
/**
* 描述:打印机实例
*/
public class PrinterExample {
public static void main(String[] args) {
//1.没有运用多态时的定义
ColorPrinter colorPrinter = new ColorPrinter();
BlackPrinter blackPrinter = new BlackPrinter();
colorPrinter.print();
blackPrinter.print();
//2.运用多态时的定义(父类对象指向子类引用)
//多态的体现(父类引用指向子类对象):儿子有一个快递,父亲有权利签收
Printer printer1 = new ColorPrinter();
Printer printer2 = new BlackPrinter();
printer1.print();
printer2.print();
// 这里代码中直接体现了两个不同子类都可以用Printer接收,那么我们称为多态(一个类的不同表现形态)
}
}
9.4.3 成员方法的访问原则
1. 编译看左边(父类)
2. 运行时分为两种情况:
(1)子类重写,运行看右边(子类)
(2)子类不重写,运行看左边(父类)
代码示例:
/**
*非抽象类
*/
public class Tree {
public void getSunShine() {
System.out.println("父类的光合作用");
}
}
----------------------------------
/**
* Tree的子类
*/
public class YangTree extends Tree{
/**@Override
public void getSunShine() {
System.out.println("子类杨树的光合作用");
}
*/
}
-----------------------
public class TreeExample {
public static void main(String[] args) {
Tree tree = new YangTree();
//(1)子类重写,运行看右边(子类)
//(2)子类不重写,运行看左边(父类)
tree.getSunShine();
}
}
9.4.4 引用数据类型的向上向下转型(重难点)
1. 向上转型:
子类对象从子类的类型变为父类的类型体现,向上转型就是多态的体现。
向上转型是自动的,小类型向大类型
如:Printer printer1 = new ColorPrinter
3. 向下转型:
前提:必须已经发生向上转型,才能做向下转型的操作,多态对象回归自己原本类型的过程
格式:子类类型 变量名 = (子类名称)多态对象名
ColorPrinter s = ( ColorPrinter) printer1
3.转型过程中的Bug问题
(1)不确定引用原类型(向上转型的类型)是什么,程序不会报错,但是运行时有问题,会提示类型转化异常(ClassCastException类型转化异常)
如:Exception in thread "main" java.lang.ClassCastException: com.its.morestatus.ColorPrinter cannot be cast to com.its.morestatus.NoColorPrinter at com.its.morestatus.PrinterExample.main(PrinterExample.java:13)
(2)规避方式:
转换之前需要判断转换的类型是不是多态对象之前的类型
使用关键字 instanceof 判断向下转换类型是不是原本类型
格式:
boolean 变量名 = 多态对象名 instanceof 指定的数据类型
-----如果指定对象是原对象返回true,如果指定对象不是原对象返回false。
代码示例
/**
* 描述:打印机实例
*/
public class PrinterExample {
public static void main(String[] args) {
//1.向上转型:子类对象从子类的类型变为父类的类型体现,向上转型就是多态的体现。
Printer printer1 = new ColorPrinter();
//2.向下转型: 前提:必须已经发生向上转型,才能做向下转型的操作,多态对象回归自己类型的过程
// //向下转型 BUG-因为我不确定向上转型的原类型是什么?随便使用黑白打印机转化
if(printer1 instanceof BlackPrinter) {
BlackPrinter blackPrinter =(BlackPrinter)printer1;
blackPrinter.print();
}
if(printer1 instanceof ColorPrinter){
ColorPrinter colorPrinter = (ColorPrinter)printer1;
colorPrinter.print()
}
}
}
9.4.5 多态的弊端和好处
多态的弊端:
1. 一旦产生多态,相当于缩小了真实类型对象的访问范围,原因是只能访问到父类中有的内容,或者父子类中都出现的内容;无法访问子类独有的内容。
解决方法:向下转型,回复真实对象访问范围。
多态的好处:
1. 增强代码的扩展性,提升代码的复用性。
比如:在定义方法的时候,如果参数类型定义为父类类型形参.那么实参的范围就比较大了. 实参可以是父类类型 的对象,也可以它子类类型对象(会自动向上转型)
如果一个返回值类型父类类型,那么return关键后就可以跟父类对象当做返回值,也可以跟子类对象当做返回值