p113 面向对象高级 包和抽象类介绍
什么是包?
包的本质就是文件夹,用来管理类文件
包名的语法格式
建包的语法格式:package 公司域名倒写.技术名称,一般全部英文小写且具备实际意义。一般idea工具会帮忙创建。
导入包
相同包下的类可以直接访问,不同包下需要导包,导包格式:
import 包名.类名;
假设一个类中需要用到不同的类并且这两个类的类名相同,那么默认只能导入一个类,另一个类要带包名访问。
举例:
a包下Student类、b包下Student类,类名相同,这样如果在另外的c包下需要同时用到这两个类名相同的类,第一个被用到的需要导入(import com.xxxxx.a.Student;),第二个被用到的类的类名需要是全类名的形式,即包名+类名(com.xxxxx.b.Student) 的形式。
抽象类
什么是抽象类?
抽象类是一种特殊的父类,内部可以编写抽象方法
什么是抽象方法?
当我们将共性的方法抽取到父类之后,发现这个方法在父类中无法给出具体明确的逻辑(描述不清)并且该方法是子类必须有的方法,就可以设计为抽象方法。
个人理解:一个类的所有子类中必须有,并且必须根据子类的不同来重写的方法,就可以在父类中编写为抽象类。
抽象方法和抽象类的定义格式
在权限修饰符后加上abstract修饰符(前面加也可以但是不符合常用规范)。注意:抽象方法必须存在于抽象类中。
p114 抽象类的注意事项
1.抽象类不能实例化(不能创建对象)
如果抽象类可以实例化,那么就可以通过对象调用内部的抽象方法,而抽象方法没有方法逻辑,也就没有实际意义。为了避免这种情况,抽象类被设计的不能实例化
2.抽象类存在构造方法
尽管抽象类不能实例化,但是抽象类中同样存在构造方法,作用是让子类通过super来访问父类的构造方法
3.抽象类中可以存在普通方法
抽象类不能实例化,但是抽象类中的普通方法可以被继承给子类,供子类继续使用
4.抽象类的子类要么重写了父类中所有的抽象方法,要么也是抽象类
abstract冲突
abstract关键字跟以下的关键字冲突,不能一起使用:
final:abstract修饰的方法强制要求子类重写,被final修饰的方法子类不能重写。
private:同final
static:被static修饰的方法可以用类名调用,但是抽象类调用抽象方法没有意义。
p115 接口介绍、定义和实现
接口:体现的思想是对规则的声明,Java中的接口更多体现的是对行为的抽象
适用场合:如果发现一个类,所有的组成方法都是抽象方法,没有成员变量和普通方法,那么这种类通常会设计为Java中的接口,因为这种累存在的唯一价值就是声明规则。
定义格式:和定义类的格式类似,但是内容没有成员变量、没有普通方法、只有抽象方法,然后把class改为interface;
注意:接口不允许实例化(原因和抽象类一样)
接口和类之间是实现关系,通过implements关键字来完成。
实现了接口的类称为实现类,也可以理解为接口的子类。
实现接口的格式:
class 类名 implements 接口名 {}
实现类的两个选择:
1.重写接口的所有抽象方法;
2.将实现类也转化为抽象类(因为实现接口相当于将一个只有抽象方法的抽象类作为父类继承,这样如果它不把父类中的所有抽象方法重写的话就只能自身也是抽象类,否则没法容下这些抽象方法)。
p116 接口的成员特点 类和接口的关系
接口的成员特点:
1.成员变量:接口中的成员变量只能定义常量,因为系统会默认为接口中的变量加入public static final这三个关键字,所以这个变量有公有化 、静态、不可再次变更的性质。
2.成员方法:只能是抽象方法(因为写了不抽象的系统也会帮你加上关键字变成抽象),因为系统会默认为接口中的方法加上public abstract,即公有化、抽象。
jdk8和9有新特性,后面会有
3.构造方法:没有(?6)
接口和类之间的各种关系:
1.类和类之间:继承关系(只支持单继承、不支持多继承、但是可以多层继承)
2.类和接口之间:实现关系(可以单实线、也可以多实现、甚至一个类可以在继承一个类的同时实现多个接口)
一个类继承的多个接口之间不会出现获得的方法的同名冲突,因为实现的多个接口里的所有方法无论同名不同名,都没有具体的逻辑内容,所以不会产生冲突。
(有一个比较极端的情况是一个类继承一个父类、实现两个接口、他们三者都有一个show方法,而最后这个实现了两个接口的子类不对这个show方法进行重写也不会报错,因为他继承了父类的show方法,也就相当于在这个子类里复制来了父类的这个show方法对两个接口实现的方法进行了重写,所以没有在这个子类里重写接口的方法也不报错)
(不是这段字怎么越看越离谱但是真的改不明白了 脑子知道就行)
3.接口和接口之间:继承关系(和类与类之间的情况不一样,可以单继承也可以多继承)
也不会出现方法同名的冲突,原因和类与接口之间的关系那部分相同(没有逻辑谈何冲突)
p117 抽象类和接口的对比
成员变量:
抽象类中可以定义变量也可以定义常量;
接口中只能定义常量;
成员方法:
抽象类中可以定义具体方法也可以定义抽象方法;
接口中定义的方法都会转变成抽象方法;
构造方法:
抽象类中有构造方法,用于给子类用super来访问;
接口中没有构造方法
怎么判断选用二者中的哪一种?
抽象类是对事物进行抽象,本质还是对事物进行描述
接口是对行为进行抽象,负责为程序制定规则使得程序更加规范
(个人理解,比如一个公司有总管和工人:
抽象类就可以对总管和工人的各个属性进行描述,比如总管和程序员都有一个工作(work方法),就可以编写一个抽象类叫做工人,让总管和程序员的类作继承,包含一个抽象方法work,代表他们都属于工人的一部分而且都要完成这个抽象类的工作(也就是继承这个抽象类的内容);
而依照这个思路,也可以创建一个接口叫做上班,包含一个工作(work方法),这两个类一旦继承了这个接口,就代表他们要完成这个接口里的工作(也就代表他们实现了这个接口)
写的什么玩意这都 后边有新的理解再来改改吧)
p118 多态引入
多态介绍
多态是指同一个行为具有多个不同表现形式或者形态的能力
示例:有两个类包含了针对国内用户的功能和针对国外用户的功能,同种功能的方法名相同;在用户选择时根据用户输入来创建不同的类的对象但起一个相同的对象名,随后调用创建的对象里的方法,就可以达成“调用的方法是同一套但是表现出来的实际功能有区别”的效果,详细的看图:
好处:提高代码的扩展性,可以根据环境和选择来灵活的对业务进行切换。
留下的疑问:
1.为什么可以把不同的类型互相赋值?所有对象都可以这么创建吗?
(这里是把OrderServiceImpl和OrderService两个类的对象进行了赋值)
实际这种创建方式就是用多态创建对象,但是使用有一定的前提,在后面会提到多态创建对象的前提、语法、访问特点和相关注意事项。
2.为什么调用的方法是同一套方法,但是有不同的表现形式?
p119 多态的前提-对象多态和行为多态
多态的前提:
1.有继承或者实现关系
2.有方法重写
3.有父类引用指向子类对象
对象多态
比如:一个父类Animal有一个抽象方法eat
一个子类dog重写了eat方法为狗吃肉
一个子类cat重写了eat方法为猫吃鱼
这个过程中就实现了继承关系和方法重写的两个前提;
什么是父类引用指向子类对象?
以前面的例子继续,
Dog dog = new Dog;
这种叫做子类引用(左边引用了Dog子类)指向子类对象(右边new了一个子类Dog的对象)
Animal animal = new Dog;
这种就叫做父类引用(左边引用了Animal父类)指向子类对象(右边new了一个子类Dog的对象)
这里就发生了上一p中出现的等号两侧类型不同但是可以赋值的情况。这种情况只有在左侧是父类、右侧是子类的情况被允许,其余情况都会报错。这种创建对象的方式叫做对象多态。
使用对象多态的场景和好处:
可以将方法的形参定义为父类类型,这样这个方法就可以接收到父类和它的任意子类对象;
行为多态
上一p中体现出来的,调用同样对象名的同样方法名的方法,却因为创建对象的不同,最终表现形式也不同的现象就是行为多态
好处:同一个方法具有多种不同表现形式和形态的能力
p120 多态的成员访问特点和好处
多态的成员访问特点
多态创建对象调用动态成员:
1.成员变量:编译看左边(父类),运行看左边(父类)
(意思就是编译的时候检查左边父类有没有这个变量,没有就报错,运行的时候也按照父类的值来)
2.成员方法:编译看左边,运行看右边
(编译的时候检查父类有没有这个方法,没有就报错,有就通过,但是运行的时候一定按照子类的这个方法来)
成员方法这种情况是为了防止调用的方法在父类中是抽象方法的情况。
多态创建对象调用静态成员:
无论变量和方法,编译看左边,运行看左边
现象和调用动态变量相同但是原理不同,因为静态成员调用方法如果使用了对象名.成员名的形式,会在编译阶段被自动更正成类名.成员名的形式,也就是一直在调用父类中的静态成员。
多态的优缺点
多态的好处:提高了程序的扩展性
对象多态:将方法的形参定义为父类类型,这个方法就可以接受该父类的任意子类对象
行为多态:同一个行为,具有多个不同表现形式或形态的能力
多态的弊端:不能使用子类的特有成员
(上面提到过,调用的时候会检测父类中有没有对应的成员,如果没有就会报错,所以不能用子类有而父类没有的成员)
(如果一定要调用子类的特有成员的话需要用到转型技术)
p121 多态的转型和案例
向上转型
从子类到父类(父类引用指向子类对象)
Fu f = new Zi();
向下转型
从父类到子类(将父类引用所指向的对象转交给子类类型)
Zi z = (Zi)f;
这里等号右边的形式是强制转换的形式,可以理解为父类比子类大,从父类转到子类就需要用强制转换。
在使用多态时可能需要/出现的东西:
ClassCastException异常
在引用数据类型的强制转换过程中,如果[实际类型]和[目标类型]不匹配就会出现此异常。
示例里的解决方法:用下面↓↓↓这个关键字做判断应对不同情况
instanceof 关键字
左侧放一个引用数据类型的变量,右侧放一个引用数据类型,随后整体作为一个boolean类型,如果左侧的变量是右侧的数据类型就返回true,否则返回false,可以配合转型操作达成行为多态的目的;
案例 模拟支付接口
某网站需要开发一个支付功能,需要支持多种支付方式(支付平台、银行卡网银、信用卡),模拟出这个功能的效果。
示例成果:有一个菜单用来选择支付方式,选择方式后输入支付金额,支付完成后打印出用户“通过xxx方式支付了xxx元“。
思路:一个接口由以下的四个类实现,要求三个实现类重写支付的方法;
一个抽象类用于给子类继承变量,代表支付金额;
三个实现类来分别定义方法来打印支付结果,对应三种支付方式;
实际编码:
接口OnlinePay包含一个方法pay();
一个抽象类Pays包含一个成员变量double price代表价格并继承给子类;
三个子类Payment,BankCards,CreditCards分别重写了支付结果打印方法,分别代表三种支付方式;
不足之处:
1.可以把方法直接写成传参类型的,由系统调用方法时传入价格参数,可以省下对象里记录价格的一部分空间
2.在1的前提下就可以省略创建父类Pays的过程,初始化对象时直接创建接口类用来接收new的实现类的对象,减少冗余代码;
本章学习目标
- 能够定义抽象方法和抽象类
- 清楚抽象类的子类的特点 要么重写所有抽象方法,要么也是个抽象类
- 能够理解接口的思想和好处 思想:制定规则 好处:根据规则编写实现类,代码更规范
- 清楚接口中的成员特点 成员变量只能是常量,成员方法只能是抽象方法,没有构造方法
- 能够理解多态的好处 提高程序的扩展性
- 掌握多态中的成员访问特点 调用变量时编译看左运行看左(先检查父类有没有,没有就报错,有就用父类的) 调用方法时编译看左运行看右(先检查父类有没有,没有就报错,有就用子类重写的)