JavaSE基础篇【5】
2022年6月21日
第八部分 接口、继承与多态
8.1 类的继承
继承的主要思想是基于某个父类进行扩展,得到一个新的子类。子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。
在 Java 中使用 extends 关键字来标识两个类的继承关系。
class Test{
public Test(){ //构造方法
//SomeSentence
}
protected void doSomething(){ //成员方法
//SomeSentence
}
protected Test doIt(){ //方法返回值类型为Test类型
return new Test();
}
}
class Test2 extends Test{ //继承父类
public Test2(){ //构造方法
super(); //调用父类构造方法
super.doSomething(); //调用父类成员方法
}
public void doSomethingnew(){ //新增方法
//SomeSentence
}
public void doSomething(){ //重写父类方法
//SomeNewSentence
}
protected Test2 doIt(){
return new Test2; //重写父类方法,方法返回值类型为Test2类型
}
}
继承并不只是扩展父类的功能,还可以重写父类的成员方法。重写(还可以称为覆盖)就是在子类中将父类的成员方法名称保留,重写成员方法的实现内容,更改成员方法的存储权限,或是修改成员方法的返回值类型。(重写时不能降低方法的修饰权限范围)
在继承中还有一种特殊的重写方式,除方法实现内容以外,其他都相同,这种特殊的重写方法称为重构。
8.2 Object 类
在 Java 中所有的类都直接或者间接继承了 java.lang.Object 类。Object 类是比较特殊的类,它是所有类的父类,是 Java 类层中最高的类。用户创建一个类时,除非已经指定要从其他类继承,否则都默认是从 java.lang.Object 类继承而来的。由于所有类都是 Object 的子类,所以在定义类的时候 extends Object 关键字。也就是说,下面两个语句是等价的。
class anything {…}
class anything extends Object {…}
在 Object 类中主要包括 clone( )、finalize( )、equals( )、toString( ) 等方法,其中常用的两个方法为 equals( ) 方法和 toString( ) 方法。需要注意的是,Object 类中的 getClass( )、notify( )、notifyAll( )、wait( ) 等方法不能被重写,因为这些方法被定义为 final 类型。下面详细讲述 Object 类中的几个重要方法。
1. getClass( ) 方法
getClass() 方法是 Object 类定义的方法,它会返回对象执行时的 Class 实例,然后使用此实例调用getClass() 方法可以得到此类的名称。语法如下:
getClass().getname();
可以将 getClass() 方法和 toString() 方法联合使用。
2. toString() 方法
toString() 方法的功能是将一个对象返回为字符串形式,它会返回一个 String 实例。在实际的应用时,通常重写 toString() 方法,为对象提供一个特定的输出模式。当这个类转换为特殊字符串或者与字符串连接时,将自动调用重写的 toString() 方法。
public class ObjectInstance{
pubilc String toString(){ //重写 toString() 方法
return "在"+getClass().getname()+"类中重写 toString() 方法";
}
public static void main(String[] args){
System.out.println(ne ObjectInstance());
}
}
3. equals() 方法
equals() 方法比较的是两个对象的实际内容。而 “==” 比较的是两个对象的引用是否相等。
class V{ //自定义类V
}
public class OverWriteEquals{
public static void main(String[] args){
String s1 = "123";
String s2 = "123";
System.out.println(s1.equals(s2));
V v1 = new V();
V v2 = new V();
System.out.println(v1.equals(v2));
}
}
从上述的例子可以看出,在自定义类中使用 equals() 方法进行比较时, 将返回 false,因为 equals() 方法的默认实现是使用 “==” 运算符比较两个对象的引用地址,而不是比较对象的内容,所以要想真正比较两个对象的内容,需要在自定义类中重写 equals() 方法。
8.3 对象类型的转换
对象类型的转换主要包括向上转型和向下转型操作。
- 向上转型:将子类对象看作是父类对象
- 向下转型:将父类对象看作是子类对象](https://img-
8.4 使用 instanceof 操作符判断对象类型
当在程序执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生 ClassCastException 异常,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类是否为子类对象的实例。这个判断通常使用 instanceof 操作符来实现。instanceof 操作符判断是否一个类实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。
instanceof 的语法格式如下:
myobject instanceof ExampleClass
其中:
- myobject:某类的对象引用
- ExampleClass:某个类
使用 instanceof 操作符的表达式返回值为布尔值。如果返回值为 true,说明 myobject 对象为 ExampleClass 的实例对象;如果返回值为 false,则说明不是。
(instanceof 为 Java 的关键字,在 Java 中关键字都是小写)
8.5 方法的重载
构造方法的名称由类名决定。所以构造方法只有一个名称。如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到方法重载。
方法的重载就是在同一个类中允许存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。
public class OverLoadTest{
public static int add(int a,int b){ //定一个方法
return a+b;
}
//定义与第一个方法相同名、参数类型不同的方法
public static double add(double a,double b){
return a+b;
}
//定义与第一个方法参数个数不同的方法
public static int add(int a){
return a;
}
//定义一个成员方法
public static int add(int a,double b){
return 1;
}
//定义一个与前述方法参数次序不同的方法
public static int add(double a,int b){
return 1;
}
public static void main(String[] args){
System.out.println("调用add(int,int)方法:"+add(1,2));
System.out.println("调用add(double,double)方法:"+add(2.1,3.3));
System.out.println("调用add(int)方法:"+add(2));
}
总结上述例子,可以知道,参数类型不同、参数顺序不同、参数个数不同均能构成重载。
在谈到参数个数可以确定两个方法是否具有重载关系时,会想到不定长参数方法。
public static int add(int...a){ //定义不定长参数方法
int s = 0;
for(int i=0;o<a.length;i++)
s+=a;
return s;
}
不定长方法的语法如下:
返回值 方法名(参数数据类型…参数名称)
8.6 多态
利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。
假如现在要绘制一个平行四边形,这时可以在平行四边形类中定义一个 draw() 方法。
public class Parallelogram{
//实例化保存平行四边形对象的数字对象
public void draw(Parallelogram p){
...//绘图语句
}
}
如果需要定义各异绘制正方形的方法,通过定义一个正方形类来处理正方形类的对象,会出现代码冗余的缺点;通过定义一个正方形和平行四边形综合类,分别处理正方形和平行四边形对象,也没太太的意义。
如果定义一个四边形类,让它处理所有继承该类的对象。根据 “向上转型” 原则可以使每个继承四边形类的对象作为 draw() 方法的参数,然后在 draw() 方法中作一些限定就可以根据不同图形类对象绘制相应的图形,从而以更为通用的四边形类来取代具体的正方形类和平行四边形类。这样处理能够很好地解决代码冗余问题,同时也易于维护,因为可以加入任何继承父类的子类对象,而父类方法也无须修改。
public class Quadrangle{
//实例化保存四边形类对象的数组对象
private Quadrangle[] qtest = new Quadrangle[6];
private int nextIndex = 0;
public void draw(Quadrangle q){ //定义draw方法,参数为四边形对象
if(nextIndex<qtest.length){
qtest[nextIndex] = q;
System.out.println(nextIndex);
nextIndex++;
}
}
public static void main(String[] args){
//实例化两个四边形对象,用于调用draw()方法
Quadrangle q = new Quadrangle();
q.draw(new Square());//以正方形对象为参数调用draw()方法
q.draw(new Parallelogramgle());//以平行四边形对象为参数调用draw()方法
}
}
//定义一个正方形类,继承四边形类
class Square extends Quadrangle{
public Square(){
System.out.println("正方形");
}
}
//定义一个平行四边形类,继承四边形类
class Parallelogramgle extends Quadrangle{
public Parallelogramgle(){
System.out.println("平行四边形");
}
}
以不同的类对象为参数调用 draw() 方法,可以处理不同的图形问题。使用多态节省了开发和维护时间。
8.7 抽象类与接口
1.抽象类
在 Java 中,不能使用具体对象描述的类称为抽象类。在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。也就是说,继承树中越是在上方的类越抽象。在多态机制中,并不需要将父类初始化对象,我们需要的只是子类对象,所有在 Java 语言中设置抽象类不可以实例化对象。
抽象类的语法如下:\
public abstract class Test{
abstract void testAbstract();
}
其中,abstract 是定义抽象类的关键字。
使用 abstract 关键字定义的类称为抽象类,而使用这个关键字定义的方法称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承以外没有任何意义。
反过来讲,如果声明一个抽象方法,就必须将承载这个抽象方法的类定义为抽象类,不可能在非抽象类中获取抽象方法。
抽象类被继承以后需要实现其中所有的抽象方法,也就是保证相同的方法名参数列表和相同返回值类型创建出非抽象方法,当然也可以是抽象方法。
继承抽象类的所有子类需要将抽象类中的抽象方法进行覆盖。这样在抽象类中就可以将父类修改为抽象类,将 draw() 方法,然后每个子类都重写这个方法来处理。但是这样会出现很多冗余代码。并且父类的局限性会很大,也许某个不需要 draw() 方法的子类也不得不重写 draw() 方法。为了应对这种问题,接口的概念便出现了。
2. 接口
接口就是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。接口使用 interface 关键字来定义,语法如下:
public interface drawTest{
void draw(); //接口内的方法,省略abstrac关键字
}
其中:
- public:接口可以像类一样被权限修饰符修饰,但 public 关键字仅限于接口在与其同名的文件中被定义。
- interface:定义接口关键字。
- drawTest:接口名称。
一个类实现一个接口,可以使用 implements 关键字,代码如下:
public class Parallelogram Quadrangle implements drawTest{
...//
}
将多态技术与接口相结合,
interface drawTest{ //定义接口
public void draw(); //定义方法
}
//定义平行四边形类,该类继承了四边形类,并实现了drawTest接口
class ParallelpgramgleUseInterface extends QuadrangleUseInterface implements drawTest{
public void draw(){
System.out.println("平行四边形.draw()");
}
void doAnything(){
//SomeSentence
}
}
class SquareUseInterface extends QuadrangleUseInterface implements drawTest{
public void draw(){
System.out.println("正方形.draw()");
}
void doAnything(){
//SomeSentence
}
}
class AnythingUseInterface extends QuadrangleUseInterface{
public void doAnything(){
//SomeSentence
}
public static void main(String[] args){
drawTest[] d = { //接口也可以进行上述转型操作
new SquaraUseInterface().new ParallelogramgleUseInterface()};
for(int i = 0;i<d.length;i++){
d[i].draw();
}
}
}
Java 中不允许出现多重继承,但使用接口就可以实现多重继承。一个类可以同时实现多个接口,因此可以将所有需要继承的接口放置在 implements 关键字后并使用逗号隔开。但这可能会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。
多重继承的语法如下:
class 类名 implements 接口1,接口2,…,接口n
例如,在定义一个接口时,使该接口继承另外一个接口。
interface intf1{
}
interface intf2 extends intf1{
}