04_Java面向对象特征之继承与多态
1. 继承
Java继承的实现(只支持单继承,而不是多继承,但有接口的多实现)
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类. 定义类时直接通过extends关键字指明要继承的父类.子类对象除了可以访问子类中直接定义的成员外,也可访问父类的所有非私有成员.能否直接访问父类私有成员则取决于访问权限设置.
继承的作用:(注意:不要仅为了获取其他类中某个功能而去继承, 类与类之间要有所属( " is a " )关系)
继承提高了代码的复用性。
继承的出现让类与类之间产生了关系,提供了多态的前提。
如何使用一个继承体系中的功能:
查阅父类功能(定义了共性的功能)
创建子类对象使用功能(因为父类可能不能创建对象, 而且子类提供了更丰富的功能)
继承中自子类变量的特点:
如果子类出现非私有的同名变量时, 子类访问本类变量用this, 子类访问父类中的同名变量用super.
继承中函数的特点:
函数覆盖(Override)
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写。
父类中的私有方法不可以被覆盖。
在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
覆盖注意事项:
• 覆盖时,子类方法权限一定要大于等于父类方法权限
• 静态只能覆盖静态。
覆盖的应用:
• 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内
构造方法在类继承中的作用
构造方法不能继承.由于子类对象要对来自父类的成员进行初始化,因此,在创建子类对象时除了执行子类的构造方法外,还需要调用父类的构造方法.具体遵循如下原则:
(1) 当子类未定义构造方法时,创建对象时将无条件地调用父类的空构造方法,以为每行第一条super();
(2) 对于父类的含参数构造方法,子类可以在自己构造方法中使用关键字super来调用它,但super调用语句必须是子类构造方法中的第一个可执行语句;
(3) 子类在自己定义构造方法中如果没有用super明确调用父类的构造方法,则在创建对象时,将自动先执行父类的无参构造方法,然后再执行自己定义的构造方法。
所以在一个类的设计时如果有构造方法,最好提供一个无参构造方法.因此,系统类库中的类大多提供了无参构造方法,用户编程时最好也要养成此习惯.
【注意】使用this查找匹配的方法时首先在本类查找,找不到时再到其父类和祖先类查找;使用 super 查找匹配方法时,首先到直接父类查找,如果不存在,则继续到其祖先类逐级往高层查找。
2. final修饰符的使用
1.final作为类修饰符: 这种类成为最终类,特点是不允许继承.例如Api中的Math,String,Integer类都是final类.
2.final修饰方法: 是功能和内部语句不能被更改的最终方法,在子类中不能再对父类的final方法重定义.所有private修饰的为私有方法和final类中的方法都默认为是final.
3.final定义常量:只能被赋值一次 例如 final double PI=3.14159;
//注意带static的类常量只能在定义或静态代码块中赋值
4.内部类只能访问被final修饰的局部变量。
3. 抽象类和抽象方法
1.抽象类的定义:抽象类代表了一种优化了的概念组织方式,来描述事物的一般状态和行为,然后在子类中去实现这些状态和行为,以适应对象的多样性.
abstract class 类名称{
成员变量;
方法(){……} //一般方法,可存在,可也不存在
abstract 方法(); //抽象方法
}
说明:抽象类中可以包含具体方法和抽象方法,抽象方法的定义与具体方法不同,抽象方法在后直接跟分号,而具体方法都含有方法体.
//普通类加上abstract关键字就变成了抽象类,没有任何条件限制.但是有抽象方法的类必须定义为抽象类,否则会提示"The type XXX must be an abstract class to define abstract methods"
2. 抽象方法表示一个抽象的概念,不能被实例化为对象.
3.抽象类中是有默认的构造函数.我的理解是不过隐藏了不能直接建立对象罢了.当然自己也可以写自己的构造函数. 抽象类中的构造方法供子类实例化调用。
抽象关键字abstract不可以和哪些关键字共存?
private 私有内容子类继承不到,所以,不能重写。
但是,abstract修饰的方法,要求被重写。两者冲突。
final
final修饰的方法不能被重写。
而abstract修饰的方法,要求被重写。两者冲突。
static
假如一个抽象方法能通过static修饰,那么这个方法,就可以直接通过类名调用。
而抽象方法是没有方法体的,这样的调用无意义。所以,不能用static修饰。
牵扯到的模版方法设计模式
在定义功能时,功能的一部分是确定的,但是有一部分是不确定的.就将不确定的部分暴露出去,有该类的子类去完成.
4. 接口
Java中不支持多继承,而是通过接口实现比多重继承更强的功能,java通过接口可以使出于不同层次,甚至不相关的类具有相同的行为.可认为是一种特殊的抽象类.
1.接口的定义:由常量和抽象方法组成
[public] interface 接口名 [extends 父接口名列表 ] {
[public] [static] [final] 域类型 域名 = 常量值 ;
[public] [abstract] 返回值 方法名(参数列表) [throw 异常列表];
}
[1]声明接口可给出访问控制符;
[2]一个接口还可以继承多个父接口,父接口间用逗号分隔。
[3]系统默认接口中所有属性的修饰都是public static final,毕老师说的全局常量.
[4]系统默认接口中所有方法的修饰都是public abstract;即抽象方法.
接口是抽象类的一种,不能用于直接创建对象.接口的作用在于规定一些功能框架,具体功能的实现则通过该接口约束的类完成.
2.接口的实现
接口定义了一套行为规范,一个类实现这个接口就是要遵守接口的定义规范,要实现接口中定义的所有方法.
一个类可以实现多个接口。接口间用逗号分隔;
如果实现某接口的类不是抽象类,则在类的定义部分必须实现指定接口的所有抽象方法;否则编译时会指示该类只能为抽象类是不能创建对象的.
接口的抽象方法的访问限制符默认为 public,在实现时要在方法头中显式地加上public修饰。
5. 多态性(体现在父类或者接口的引用指向或者接收自己的子类对象)
作用:
多态的存在提高了程序的扩展性和后期可维护性.但弊端是父类的引用只能访问父类中的成员.
前提:
• 需要存在继承或者实现关系
• 要有覆盖操作
1.方法的重载
方法调用的匹配处理原则是,首先按“精确匹配”原则去查找匹配方法,如果找不到,则按“自动类型转换匹配”原则去查找能匹配的方法。
所谓“精确匹配”就是实参和形参类型完全一致。
所谓“自动转换匹配”是指虽然实参和形参类型不同,但能将实参的数据按自动转换原则赋值给形参。
2.方法的覆盖
方法名、参数列表、完全相同才会产生方法覆盖;返回类型通常也要一致,只有返回类型为引用类型时,允许子类方法的返回类型是父类方法返回类型的子类型。其他情形导致类型不一致时编译将指示错误。
覆盖不能改变方法的静态与非静态属性。子类中不能将父类非静态方法定义为静态方法,反之也一样。
不允许子类方法的访问修饰符比父类有更多的限制。例如:子类不能将父类的public方法定义为protected方法。但可以将父类的private方法在子类中重新定义为public方法.通常将子类方法访问修饰符与父类保持一致.
final方法不能被覆盖。
[注意] 如果子类中定义了与父类同名的属性,在子类中将隐藏来自父类的同名属性变量. 对象执行方法时由实际对象的类型决定,而不是引用变量类型 ;
访问属性时则由引用类型决定,因为编译程序在分析程序时是基于类型来决定访问哪个属性变量 ;
静态成员的访问是基于引用类型,而不是对象类型.
3.访问继承的成员
得知通过子类对象赋值给父类的引用变量后,通过该引用变量去访问对象的成员时,访问的行为方法是子类对象的,而属性值确是父类的.其实就是编译系统是基于类型来决定的.
是基于引用类型/实际对象?
当子类和父类有相同成员时,通过子类引用访问的成员都是子类定义的,而通过父类引用变量操作子类对象时,只有实例方法存在覆盖关系(编译时看左边:要查看引用变量所属的类中是否有所调用的成员。在运行看右边:要查看对象所属的类中是否有所调用的成员),而对象属性,静态属性,静态方法均是父类定义的.
4.几个特殊类
Object类: 所有Java类的最终祖先,编译系统默认继承Object类,Object类包含了所有Java类的公共属性和方法,下面是四个常用方法.
public boolean equals(Object obj) :该方法本意用于两个对象的“深度”比较,也就是比较两对象封装的数据是否相等;而比较运算符“==”在比较两对象变量时,只有当两个对象引用指向同一对象时才为真值。但在Object类中,equals方法是采用“==”运算进行比较;
public String toString():该方法返回对象的字符串描述,建议所有子类都重写此方法。
public final Class getClass():返回对象的所属类;
protected void finalize(): 该方法Java垃圾回收程序在删除对象前自动执行。
Class类
1.获取Class类型的对象
Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。有以下3种方法可以获取Class的对象。
方法1:调用Object类的getClass()方法。
方法2:使用Class类的forName()方法。
方法3:如果T是一个Java类型,那么T.class就代表了与该类型匹配的Class对象。例如,String.class代表字符串类型,int.class代表整数类型。
2.Class类的常用方法
java运行环境中提供了反射机制,这种机制允许程序中动态获取类的信息,以及动态调用对象的方法.
[1] static Class<?> forName(String className):返回给定串名相应的Class对象。
[2] T newInstance():创建类的一个实例。
[3] String getName():返回Class对象表示的类型(类、接口、数组或基类型)的完整路径名字符串。
[4] Method[] getMethods():返回当前Class对象表示的类或接口的所有公有成员方法对象的数组。
进一步利用Method类提供的invoke方法可实现相应类的成员方法的调用。
Object invoke(Object obj,Object[ ] args)
其中,obj代表调用该方法的类实例对象,args代表存放方法参数的对象数组。
Method getMethods(String name,Class…parameterType):返回指定方法名和参数类型的方法对象。
Field[ ] getFields():返回当前Class对象表示的类或接口的所有可访问的公有域对象的数组。
6. 对象引用转换
对象引用赋值转换: 允许将子类对象赋值给父类引用,父类表示的范围比较广,反之则不行.
对象引用强制转换
将父类引用赋值给子类变量时要进行强制转换,强制转换在编译时总是认可的,但运行时的情况取决于对象的值.如果父类对象引用指向的就是该子类的一个对象,则转换是成功的.否则会抛出异常.
7. 内部类(一个类定义在另一个类里面)
访问特点: 内嵌类经过编译后产生的字节码文件名为:OuterOne$InnerOne.class
1.内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类的成员,是因为内部类中持有了一个外部类的引用,格式: 外部类名.this
2.外部类要访问内部类,必须建立内部类的对象。
内部类可以使用访问控制符public、protected、private修饰
静态inner类(只能访问外部内的静态成员,出现了访问局限)
内部类定义在成员位置上
• 可以被private static成员修饰符修饰。
• 被static修饰的内部类只能访问外部类中的静态成员。
内部类在定义在局部时(方法中的内部类):
内部类可以直接访问外部类中的成员。
内部类在局部位置上只能访问局部中被final修饰的局部变量。
匿名内部类
Java允许创建对象的同时定义类的实现,但是未规定类名,视为匿名内部类.
1.匿名内部类是内部类的简写
2.定义匿名内部类的前提: 必须是继承一个类或实现接口.
7. 访问控制修饰符
1.公共访问控制符public
[1]作为类的修饰符,将类声明为公共类, 表明它可以被所有的其它类所访问和引用
[2]作为类的成员的访问修饰符,表明在其他类中可以无限制地访问该成员。
要真正做到类成员可以在任何地方访问,在进行类设计时必须同时满足两点:首先类被定义为public,其次,类的成员被定义为public。
2.默认访问控制符
没有给出访问控制符情形,该类只能被同一个包中的类访问和引用,不能被其他包中的类使用.
3.私有访问控制符private
用来声明类的私有成员,它提供了最高级的保护.用private修饰的域和方法只能被该类自身访问和修改,不能被任何其他类(包括该类的子类)来获取和引用.
通常,出于系统设计的安全考虑,将类的成员属性定义为private保护起来,而类的成员方法public对外公开,这是类封装特性的一个体现.
4.保护访问控制符protected
用protected修饰的成员可以在三种类中所引用:
该类本身;与它在同一个包中的其它类;在其它包中的该类的子类.
图7.1