javase面向对象编程05

编程入门 行业动态 更新时间:2024-10-06 12:30:18

javase<a href=https://www.elefans.com/category/jswz/34/1769334.html style=面向对象编程05"/>

javase面向对象编程05

多态性
多态性是一种允许使用一个界面来访问一类动作的特性,特定的动作可以由不同的具体情况而定(传入不同的参数)。多态性是发送消息给某个对象,让该对象自行决定响应何种行为。
通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用
多态性通过允许同一界面指定一类动作减少了程序的复杂度,编译器工作就是选择适用于各个情况的特定动作,而程序员则无须手动进行选择,使用者仅仅是记得以及利用这个统一的界面
多态形成的三个必要条件:
有继承,父类定义方法,子类重写方法
父类的引用指向子类的对象
可以使用参数传递时多态,也可以直接创建对象时多态
多态可以用三个定义和两个方法来总结。三个定义分别是父类定义子类构建、接口定义实现类构建和抽象类定义实体类构建,而两个方法分别是方法重载和方法重写。
多态分为两种:
编译时多态:方法的重载
运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态
对象的多态性
继承表示的是is a的语义
强调的一个点:编程是对现实世界的模拟体现
public class Test1 {
public static void main(String[] args) {
Fa cc=new Fa();
//调用的是一个名称为pp的方法,但是参数不同执行的处理不同
cc.pp();//Fa…pp
cc.pp(12);//Fa.pp(int)
}
}
class Fa{
public void pp(){
System.out.println(“Fa…pp”);
}
public void pp(int k){
System.out.println(“Fa.pp(int)”);
}
}
public class 汽车 extends 车库{} 错误的
public class 学生 extends 人{} 正确的,因为任何一个学生都是人
一个对象x有两种形态,猫这类事物即具有猫的形态,又具有着动物的形态,这就是对象的多态性。简单
说就是一个对象对应着不同类型
多态在代码中的体现
父类或者接口的引用指向其子类的对象。例如:动物 x=new 猫()
class 动物{}
class 猫 extends 动物{}
猫 x=new 猫();
动物 x=new 猫(); 正确的,因为所有的猫都是动物,因为定义class 猫 extends 动物{}
猫 x=new 动物(); 错误,不是所有的动物都是猫,因为动物是猫的父类
Object x=new Student();
Person x=new Student();
Student x=new Student();
public class Test1 {
public static void main(String[] args) {
动物 x1 = new 动物();
动物 x2 = new 猫();// 猫extends动物,表示猫具有动物的所有特性,同时猫具备一些自己
都有的特性
// x2.play();语法报错的原因是:编译器类型和运行时类型的问题
}
}
class 动物 {}
class 猫 extends 动物 {
public void play() {
System.out.println(“使劲的抓老鼠…”);
}
}
public class Test1 {
public static void main(String[] args) {
动物 x2 = new 猫();// 猫extends动物,表示猫具有动物的所有特性,同时猫具备一些自己都有的特性
x2.play(); //使劲的抓老鼠…
//这里语法不报错的原因是动物类中定义了play方法,但是真正运行时系统则会发现实际上是猫,所以需要调用的不是动物类中的方法,而是猫中定义方法
}
}
class 动物 {
public void play() {
System.out.println(“使劲的睡觉…”);
}
}
class 猫 extends 动物 {
public void play() {
System.out.println(“使劲的抓老鼠…”);
}
}
对象多态性可以使程序有良好的扩展,并可以对所有类的对象进行通用处理
多态引用时,构造子类对象时的构造方法的调用顺序
先调用超类的构造方法,多重超类首先调用最远超类的方法
然后再执行当前子类的构造方法
this和super的用法
this用于指代当前对象,super用于指代父类的内存空间的标识
this关键字
this代表其所在函数所属对象的引用。换言之,this代本类的对象的引用
当成员变量和局部变量重名,可以用关键字this来区分,this就是所在函数所属对象的引用
对象调用了this所在的函数,this就代表哪个对象。一般方法调用默认加this
通过this在构造函数中调用其他构造函数的时候,只能定义在构造函数的第一行,因为初始化动作要先执行,否则就会报错
super关键字
当本类的成员和局部变量同名用this区分,当子父类中的成员变量同名用super区分父类
当子父类中出现成员函数一模一样的情况,会运行子类的函数。这种现象,称为覆盖操作,这是函数在子父类中的特性。在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取
this()表示调用当前类中的另外一个构造器,()中可以带有参数;super()表示调用父类中的某个构造器,()中可以带有参数
动物 dw = new 猫();
dw.play();//如果猫类中没有定义play方法,则执行的是从动物类中继承的play方法;如果猫类中也定义play方法,则最终执行的是猫类中定义的方法
//dw.抓苍蝇()语法错误,因为在编译时系统识别dw为动物类型,所以没有这个方法的定义
if (dw instanceof 猫) {
((猫)dw).抓苍蝇(); //强制类型转换后则可以调用猫类中所定义的特殊方法,否则只能调用动物类中定义的方法
}
public class Test1 {
public static void main(String[] args) {
需要使用动物的类 tt=new 需要使用动物的类();
tt.pp(new 动物());
tt.pp(new 猫());
}
}
class 需要使用动物的类 {
public void pp(动物 dongwu){ //假设定义方法时参数使用pp(猫 dongwu),当前程
序就和猫类耦合了,如果需要切换到狗则必须修改源代码。但是参数类型为动物,则不管是猫还是
狗都能接收
dongwu.play();
}
}
this()和super()
this()表示调用当前类中的另外一个构造器,()中可以带有参数
super()表示调用父类中的某个构造器,()中可以带有参数
new 子类()的执行过程
首先上溯子类的所有祖先类,然后再从上向下逐步执行各个层次父类的构造器Object—爷爷的构造器—父亲的构造器;最后才执行子类自己的构造器
因为每个类的构造方法中默认第一句话为super()表示调用父类的无参构造器,除非编程super。
特殊情况:
问题:父类已经定义了带参构造器后是否还有无参构造器?
没有,因为只有当不定义构造器时,系统才提供一个无参构造器。当自定义构造器时,系统不再提供无参构造器
因为子类的构造器中没有显式调用父类构造器,所以子类构造器方法中第一句是super()表示调用父类中的无参构造器,但是父类中没有无参构造器,所以报错
this和super的三种用法:
1、从语义的角度上说,this用于指代当前对象;super用于指代父类对象
this()表示调用另外一个构造器,super()表示调用父类中的某个构造器,()中的参数决定调用的是哪个
构造器
2、this.成员属性用于表示当前对象的某个成员,一般用于局部变量和属性名称一致的场景下。super.成员属性用于表示父类中定义的某个属性,一般用于子类中覆盖定义了某个父类属性的场景下。
3、this.成员方法()用于表示当前对象的某个成员方法;super.成员方法()用于表示当前类的父类中定义
的某个成员方法,一般用于覆盖定义时【就近原则】。
4、在static方法中不允许使用this/super之类关键字
面向对象设计思想的要点
认为客观世界由各种对象组成,任何事物都是对象,复杂的对象可以由比较简单的对象以某种方式组合而成
把所有对象都划分成各种对象类,每个对象类都定义了一组数据和一组方法
按照子类与父类的关系,把若干个对象类组成一个层次结构的系统
对象彼此之间仅能通过传递消息互相联系
类间多态和类内多态
多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作
类间多态性
父子类之间的一种多态型,例如:动物 x = new 猫()
public class Test1 {
public static void main(String[] args) {
方法调用中的限制:
针对一个类对象有两种类型,一种称之为编译期类型,编译时系统识别对象的类型,”动物 x = new 猫()”在编译时,系统识别x是动物类别的,所以只能调用动物类定义的方法,而不能调用猫中特殊的方法。另外一种称之为运行时类型,也就是当程序运行系统识别的类型,new谁就是谁
覆盖的方法一定不能是private的
类内多态性
在一个类对象上调用相同名称的方法,但是当参数不同时执行不同的动作
Animal a1=new Cat();
A a=new A();
a.eee(a1);
}
}
class A{
public void eee(Animal animal){
animal.eat();
}
}
class Animal{
public void eat(){
System.out.println(“Animal.eat()”);
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println(“就是爱老鼠”);
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println(“就是爱肉骨头”);
}
}
public class Test2 {
public static void main(String[] args) {
A2 a=new A2();
// a.pp();
// a.pp(12);
a.pp(“shitou”);
}
}
class A2 {
public void pp() {
System.out.println(“A2.pp()”);
}
public void pp(Integer k) {
System.out.println(“A2.pp(int)”);
}
public void pp(Object kk) {
System.out.println(“A2.pp(String)”);
方法名称相同,参数不同,和返回值类型无关
参数个数不同
参数类型不同
参数顺序不同。注意不同参数名称顺序不同
不确定个数的参数
方法的重写和重载
要求:方法的名称一致。
}
public void pp(int k1,String k2){}
public void pp(String k1,int k2){}
}
public class Test1 {
public static void main(String[] args) {
Fa ff=new Fa();
ff.pp(new Integer(123)); //原则:最佳匹配原则
ff.pp(1.234);
}
}
class Fa {
public void pp() {
System.out.println(“Fa.pp()”);
}
public void pp(Number kk){
System.out.println(“Fa.pp(Number)”);
}
public void pp(Integer kk){
System.err.println(“Fa.pp(Integer)”);
}
}
public class Test1 {
public static void main(String[] args) {
Fa ff = new Fa();
ff.pp(); //最佳匹配原则
}
}
class Fa {
public void pp() {
System.out.println(“Fa.pp()”);
}
public void pp(int… pages) {// 不确定个数的参数
System.out.println(“Fa.pp(int…)”);
}
}
方法的重写(覆盖)
要求:方法的名称一致
方法的重写(覆盖)一定发生在父子类之间
执行规则:new谁运行谁的方法,和声明的类型无关
方法的覆盖定义要求方法名称一致
方法的参数一致(个数、类型、顺序),和参数名称无关
顺序一致的问题,系统识别方法依靠是方法名称和参数类型列表,和方法参数的名称无关。例如这里系统识别的方法为ear(String,String)。要识别顺序还得依靠类型的区别,例如eat(int,double)和
eat(double,int)
public class Test4 {
public static void main(String[] args) {
F4 f=new F4(); f.pp(10);
S4 s=new S4(); s.pp(10);
F4 fs=new S4(); fs.pp(10);
}
}
class F4 {
public void pp(int k) {
System.out.println(“F4.pp(int)”);
}
}
class S4 extends F4 {
public void pp(int k) { //子类中定义的同名同参数的方法覆盖了父类中的方法定义,如
果需要调用父类中的方法则需要使用super.pp(k)进行调用
System.out.println(“S4.pp(int)”);
}
}
@Override注解可以使IDE工具在编译源代码时进行检查,如果有手写错误则IDE工具报错类型一致的问题
class Fa {
public void eat(Integer kk) {
System.out.println(“Animal.eat()”);
}
}
class Son extends Fa {
@Override
public void eat(Number kk) { //类型必须一致,即使父类类型都不可以,int和
Integer简单类型和包装类型也不可以。这里去除@override注解则不会有语法错误,这里不是方法的重写,是方法的重载
System.out.println(“就是爱老鼠”);
}
}
返回数据类型一致【面试】(因为如果返回类型不一致,则无法进行语法检查,例如父类返回Double,而子类返回Integer,调用处语法检查是按照Double进行检查还是按Integer检查?允许父类中返回的是父类型,而子类中返回子类型,例如父类中返回的是Number类型,而子类中返回的是Integer)
抛出异常一致,注意实际上允许子类抛出比父类更少的异常
要求子类中的方法范围 >= 父类中方法范围
静态方法覆盖和调用,用谁声明则调用谁的静态方法
class Fa {
public void eat(String s1,String s2) {
System.out.println(“Animal.eat()”);
}
}
class Son extends Fa {
@Override
public void eat(String s2,String s1) {//系统不能识别变量名称
System.out.println(“就是爱老鼠”);
}
}
class Fa {
public Number eat(double s1,int s2) {
System.out.println(“Animal.eat()”);
return 10.;
}
}
class Son extends Fa {
@Override
public Integer eat(double s2,int s1) { //允许子类中返回值类型是父类返回值
类型的子类型
System.out.println(“就是爱老鼠”);
return 99;
}
}
class Fa {
public Integer eat(double s1, int s2) throws Exception {
System.out.println(“Animal.eat()”);
return 10;
}
}
class Son extends Fa {
@Override
public Integer eat(double s2, int s1) throws IOException {
System.out.println(“就是爱老鼠”);
return 99;
}
}
方法的重载
方法的名称相同,参数不同,和返回值类型无关。可以在一个类内或者父子类之间
调用规则:类型最佳匹配原则
只有静态方法覆盖定义父类中的静态,实际上系统不建议这种做法
//The method pp() of type Son must override or implement a supertype
method
public class Test1 {
public static void main(String[] args) {
// 直接使用具体类调用静态方法没有任何问题,使用哪个类就调用的是哪个类中定义
的静态方法
// Fa.pp();Fa…pp
// Son.pp();Son…pp
// 事实上静态方法也可以通过创建对象后,使用对象进行调用。声明变量的类型和具
体构建的类型一致,也不会有问题
// Fa ff=new Fa();
// ff.pp();
//如果调用静态方法,则用谁声明调用谁的方法
Fa ff = new Son();
ff.pp();//执行结果是Fa…pp
}
}
class Fa {
public static void pp() {
System.out.println(“Fa…pp”);
}
}
class Son extends Fa {
public static void pp() {
System.out.println(“Son…pp”);
}
}
class A5 {
public void pp(){
System.out.println(“A5.pp()”);
}
public int pp(){} 语法错误,因为在系统中识别一个方法是用【方法名称+参数类型列表】进行,系统会将这两个pp方法识别为同一个方法。注意:在一个类中不允许方法相同
public void pp(int k){
System.out.println(“A5.pp(int)”);
}
}
class B5 extends A5 {
public void pp(String k){
System.out.println(“B5.pp(String)”);
}
public void pp(String k,int k1){}
public void pp(int k,String k1){}
要求:方法名称相同并且参数不同。参数不同有3种情况:参数个数不同、参数类型不同、参数顺序不同
和参数名称无关
和返回类型无关
为什么返回类型不同判断不同的方法?【面试】
例如调用方法时不需要处理返回值pp(10),容易造成混淆
和范围无关
和方法抛出的异常无关
方法的重载可以出现父子类之间,也可以是在一个类内。但是方法的覆盖一定是父子类之间,不能
在一个类内实现方法的覆盖
多态的优点
消除类型之间的耦合关系
可替换性、可扩充性
接口性、灵活性、简化性
}
public double pp(int k){}
public int pp(int k){}
一个方法有返回值,调用处实际上可以不接收。假设系统可以按照返回类型将这两个方法识别为不同方法,则调用pp(10)就没办法判断到底执行哪个

public void pp(){}
protected void pp(){}语法报错
public class Test1 {
public static void main(String[] args) {
Fa ff = new Son();
ff.pp(10);//因为在父类中声明的方法为Integer类型,所以在执行前会自动执行装箱操
作,所以调用的是从父类中继承到的pp(Integer),而不是最佳匹配的pp(int)
}
}
class Fa {
protected void pp() {
System.out.println(“Fa.pp()”);
}
public void pp(Integer kk){
System.out.println(“Fa.pp(Integer)”);
}
}
class Son extends Fa {
public int pp(int k) {
System.out.println(“Son.pp(int)”);
return 10;
}
练习题
1、为“无名的粉”写一个类class WuMingFen
要求: 1.有三个属性 面码:String theMa 粉的分量(两) int quantity 是否带汤 boolean likeSoup
22 }
public class Test1 {
public static void main(String[] args) {
Fa ff = new Son();
ff.pp(10);
}
}
class Fa {
protected void pp() {
System.out.println(“Fa.pp()”);
}
public void pp(Integer kk){
System.out.println(“Fa.pp(Integer)”);
}
}
class Son extends Fa {
public void pp(Integer kk){
System.out.println(“Son.pp(Integer)”);
}
public int pp(int k) {
System.out.println(“Son.pp(int)”);
return 10;
}
}

//一般情况下,不允许定义类在default包中,因为default包中的类其它包无法访问
//包名称的命名规则:全小写,采用域名反转
package com.yan;
//类名称,一般名词,采用大写字母开头,大写字母分词
public class WuMingFen {
private String theMa;
private int quantity;//采用简单类型定义属性,最大的问题是很难判定是默认值还是赋的

private boolean likeSoup=true;//按照一般的业务规则,可以一般有汤
//一般在具体开发中,建议采用私有属性,公有的get/set方法的形式定义属性
public String getTheMa() {
return theMa;
}
//业务规则:面码一旦告知则不能修改,所以可以不提供这个set方法,则面码属性就是只读属性
public void setTheMa(String theMa) {
this.theMa = theMa;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {}
2.写一个构造方法 以便于简化初始化过程 如 WuMingFen f1 = new WuMingFen(“牛肉”,3,true);
还可以通过IDE工具生成对应的构造器方法
3.重载构造方法 使得初始化过程可以多样化 WuMingFen f2 = new WuMingFen(“牛肉”,2);
//通过get/set方法可以进行一些参数的合法性验证
if(quantity<1)
quantity=1;
this.quantity = quantity;
}
public boolean isLikeSoup() {//如果针对boolean类型的属性,默认get方法的命名规
则是isXxxx不是getXxx;但是如果将属性修改为Boolean,则还是getxxx
return likeSoup;
}
public void setLikeSoup(boolean likeSoup) {
this.likeSoup = likeSoup;
}
}
// 添加一个带参构造器
// 构造器不是成员,所以不能继承
// 构造器方法不能有返回值类型,连void都不能有,否则就是普通方法,不是构造器
// 构造器方法名称必须和类名称一致
// 构造器如果不定义,则系统自动提交默认的无参构造器方法;如果定义构造器方法则系统不再
提供构造器
public WuMingFen(String theMa, int quantity, boolean likeSoup) {
// 方法参数likeSoup和属性名称一致,在当前方法中的likeSoup实际上就是局部变量,
而不是属性;如果需要使用属性,则必须通过this.进行引用
this.likeSoup = likeSoup;
this.theMa = theMa;
this.quantity = quantity;
}
//方法的重载
//方法名称相同,参数不同[个数、顺序、类型],和返回值类型无关,和参数名称无关
public WuMingFen(String theMa,int quantity){
this(theMa,quantity,true);//调用上面定义的构造器,并传入相关的参数
}
4.如何使得下列语句构造出来的粉对象是酸辣面码、2两、带汤的 WuMingFen f3 = new WuMingFen();
5.写一个普通方法 check() 用于查看粉是否符合要求。即将对象的三个属性打印在控制台上。
2、定义一个名为Vehicles交通工具的基类 该类中应包含String类型的成员属性brand商标和color颜色,
还应包含成员方法run行驶,在控制台显示“我已经开动了” 和showInfo显示信息,在控制台显示商标和
颜色,并编写构造方法初始化其成员属性。
编写Car小汽车类继承于Vehicles类,增加int型成员属性seats 座位,还应增加成员方法showCar在控制
台显示小汽车的信息,并编写构造方法。
编写Truck卡车类继承于Vehicles类,增加float型成员属性load载重,还应增加成员方法showTruck在控
制台显示卡车的信息,并编写构造方法。
在main方法中测试以上各类。
3、定义一个网络用户类,要处理的信息有用户ID、用户密码、email地址。在建立类的实例时把以上三
个信息都作为构造函数的参数输入,其中用户ID和用户密码时必须的,缺省的email地址是用户ID加上字
符串”@yan"
4、编写Addition类,该类中应包含一组实现两数相加运算的重载方法。
实现加法运算的方法,应接受两个参数,即加数和被加数,方法将两个参数进行加法运算后返回相加结
果。
考虑可能针对不同的数据类型进行计算,重载一组方法,包括整型、长整型、浮点型、双精度浮点型、
还有字符串。
在main方法中创建Addition类的实例分别调用重载方法测试其效果。应将Addition类打入到包中以自己
名字为包命名。
5、建立一个汽车类,包括轮胎个数、汽车颜色、车身重量等属性。并通过不同的构造方法创建实例。至
少要求汽车能够加速、减速、停车。要求命名规范、代码体现层次、有友好的操作提示。
6、创建一个类,为该类定义三个构造函数分别执行下列操作 1、传递两个整数值并找出其中较大的一个
值 2、传递三个double值并求出其乘积 3、传递两个字符串值并检查其是否相同 4、在main方法中测试
构造函数的调用
//方法的重在,但是按照业务规则,给属性赋默认值
public WuMingFen(){
//酸辣面码、2两、带汤
this.likeSoup=true;
this.theMa=“麻辣”;
this.quantity=2;
//方法2 this(“麻辣”,2,true);
}
public void check() {
System.out.println(“无名粉:面码” + this.theMa + “,重量” + quantity +
“,” + (likeSoup ? “有汤” : “无汤”));
}
7、创建一个名称为main.package的包,使它包含MainClass和MainSubClass类。MainClass类应当包
含变量声明,其值从构造函数中输出。MainSubClass类从MainClass派生而来。
试执行下列操作:创建一个名称为SamePackage的类,使它导入上述包,并创建一个MainSubClass类
的对象。
8、创建一个父类和子类。父类有一个数据成员,子类继承父类的该数据成员,通过构造函数初始化并显
示该数据成员的值。
9、建立一个汽车Auto类,包括轮胎个数、汽车颜色、车身重量、速度等成员变量。并通过不同的构造
方法创建实例。至少要求:汽车能够加速、减速、停车。 再定义一个小汽车类Car 继承Auto 并添加空
调、CD等成员变量,覆盖加速、减速的方法
抽象类
面向对象编程的核心是面向抽象编程,一般依赖抽象不依赖具体
包含了抽象方法的类叫作“抽象类”,所谓的抽象方法是指没有函数体的方法,抽象方法必须在子类中给
出具体实现,而抽象类本身不能创建对象。 public abstract class A{}
如果一个类中有抽象方法则这个类一定是抽象类
抽象类中可以定义的内容与普通类一样,只是可以定义抽象方法
抽象类中可以没有抽象方法
普通类继承抽象类必须实现抽象类中所有的抽象方法
抽象类一般是用来定义规范的,该规范的实现是通过子类来实现的
抽象类的特点
方法只有声明没有实现时,该方法就是抽象方法【不是空实现方法体,没有{}】,需要被abstract修饰,否则语法报错。抽象方法必须定义在抽象类中,该类必须也被abstract修饰
抽象方法只能被public、protected或不加域修饰符修饰,抽象方法一定不能是private的
public void pp(){}不是抽象方法,只是方法的实现为空,有方法体
public void pp(); 没有{}才是没有方法体,才是抽象方法,当然需要添加关键字abstract
不能定义抽象构造函数
抽象类中可以定义构造器,也可以不定义构造器,使用系统默认提供的无参构造器,但是自定义构造器不能private
抽象类不能是final class
不能定义抽象静态方法
抽象类中可以有静态方法,但是必须有方法体,不能是抽象方法
允许抽象类直接调用静态方法
抽象类不能直接创建对象,只能通过继承的方式由子类实现对应的抽象方法;
一般的使用方法为【动物 x=new 人();】
public class A{
public void pp(Pig pig){} 如果进行切换类型,则必须进行修改源代码
public class A{
public void pp(Animal animal){} 这里可以任意更换Animal的子类
}
class Pig extends Animal{}
class Cat extends Animal{}
所有抽象类的子类必须实现抽象父类中的所有抽象方法或者自己也声明成抽象类[没有实现所有的抽象方法]
抽象类除了可以有抽象函数,也可以有非抽象函数
没有任何限制,允许属性、方法,也允许抽象方法
抽象类不可以被实例化
是因为调用抽象方法没有意义?
抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以实例化,否则这个子类还是抽象类
强调:注意抽象类中可以包含构造器、析构器、抽象方法和方法以及静态方法等,也可以没有抽象方法
什么时候使用抽象类
当一个类的一个或多个方法为抽象方法时
当该类为一个抽象类的子类,并且没有为所有抽象方法提供实现细节或方法主体时
当一个类实现一个接口,并且没有为所有抽象方法提供实现细节或方法主体时
抽象类和普通类的区别
抽象类不能直接实例化,并且对抽象类使用 new 运算符是编译时错误
抽象类允许(但不要求)抽象类包含抽象成员
抽象类不能被密封
简单说就是被final修饰的类,密封类不能被继承,防止了恶意的派生
模板模式
定义抽象类的目的是提供可由其子类共享的一般形式,子类可以根据自身需要扩展抽象类
什么是模板模式
在模板模式Template Pattern中一个抽象类公开定义了总体的算法【算法骨架】,把没有办法在父类中实现的方法延迟到子类中具体实现。这种类型的设计模式属于行为型模式
何时使用
有多个子类共有的方法,且逻辑相同
重要的、复杂的方法,可以考虑作为模板方法
注意事项
为防止恶意操作,一般模板方法都加上 final 关键词
优点
封装不变部分,扩展可变部分
提取公共代码,便于维护
行为由父类控制,子类实现。
缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
分析
冒泡排序算法固定可以定义在父类中,但是两个比较的算法不同确定,所以可以将具体比较算法延迟到子类中实现。抽象父类中定义算法模板,具体的比较延迟到子类中进行实现
抽象类的作用
在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的
通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则开闭原则OCP,抽象类是其中的关键所在
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象
OOP的核心是面向抽象编程
在父类中固化算法骨架,在子类中实现特殊的方法
依赖抽象
常见问题
抽象类中有构造器吗?
有,用于给子类对象进行初始化
new 子类时抽象父类的构造器一定会被执行
抽象类可以不定义抽象方法吗?
可以的,但是很少见
目的就是不让该类创建对象
抽象类一定是个父类吗?
是的
public abstract class BubbleSorter {
//在父类中固化算法实现,没有办法实现的比较方法延迟到子类中提供实现
public final void sort(Object[] arr){ //final表示这个方法是最终方法,不允许
子类覆盖
for(int i=1;i<arr.length;i++){
for(int k=0;k<arr.length-i;k++){
if(bigger(arr[k], arr[k+1])){
Object temp=arr[k];
arr[k]=arr[k+1];
arr[k+1]=temp;
}
}
}
}
//定义protected目的在于子类中提供实现
protected abstract boolean bigger(Object obj1,Object obj2);
}
因为需要子类覆盖其方法后才可以对子类实例化
创建对象时,加载对象的执行顺序
先加载父类的静态变量和静态初始化块
加载子类的静态变量和静态初始化块
加载父类的成员变量、初始化块
加载父类的构造方法
加载子类的成员变量、初始化块
加载子类的构造方法
类和类之间的耦合问题
OOP要求类内高内聚、类间弱耦合—客户需求变动
练习题:创建一个Vehicle类并将它声明为抽象类。在Vehicle类中声明一个NoOfWheels方法使它返回一
个字符串值。创建两个类Car和Motorbike从Vehicle类继承,并在这两个类中实现NoOfWheels方法。
在Car类中应当显示“四轮车”信息,而在Motorbike类中应当显示“双轮车”信息。创建另一个带main方法
的类,在该类中创建Car和Motorbike的实例,并在控制台中显示消息

更多推荐

javase面向对象编程05

本文发布于:2024-02-19 15:29:06,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1764623.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:面向对象   javase

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!