Java第七章总结

继承,多态,抽象类与接口

7.1类的继承

继承在人类个体之间说的是物质的继承,那么在Java中类与类之间的继承也类似于此,类在继承关系中分为两种,一种是父类,属于是被继承类,一种是子类,属于是继承类,如果某一个类继承了另一个类,那么该类就是子类,被继承的类就是父类。

Java中类的继承需要使用关键字extends
语法: 

class Child extends Parent{}//类Child 继承于类 Parent

Java只支持单继承(一个类只有一个父类),子类在继承父类后,创建子类对象的同时也会调用父类的构造方法。
例子:

创建父类Parent


}public class Parent { public Parent (){ System.out.println("调用Parent类构造方法"); }

创建子类Child 

class Child extends Parent{
		 public Child() {
		  System.out.println("调用Child类构造方法");
		 }
		}

 创建demo

public class Demo {
		 public static void main(String[] args) {
		 new Child();
		}
		}

 Java中虽然只能继承单类但是可以通过子类继承父类,父类继承父类的父类,……,来进行多重继承!

class A{}
class B extends A{}
class C extends B{
}

7.2Object类

在Java中所有的类都直接或间接的继承java.lang.Object类,他是所有类的父类,如果创建一个新类时没有指定继承,那么就默认继承Object类。
Object类中主要包括:

  1. clone()
  2. finalize()
  3. equals()
  4. toString()

 并且所有类都可以重写Object类中的方法
注意:
Object类中的getClass(),notify(),notifyAll(),wait()等方法不能进行重写
原因:这些方法是final类型
1.getClass()
它是Object类定义的方法,他会返回对象执行时的Class实例,调用getName()可以获取类的名称
语法:

getClass().getname();

2.toString()
功能:将一个对象返回为字符串形式(一个String实例)
实际运用中多会进行重写,为对象提供一个特定的输出模式,当这个类转换为字符串或字符串连接时,将自动调用重写的toString()方法
例子:

public class Student {      
 String name;
 int age;
 
 public Student(String name,int age) {
  this.name =name;
  this.age = age;
  
 }
 public String toString() {
  return "我叫"+name+",今年"+age+"岁。";
 }
 public static void main(String[] args) {
  Student s1 =new Student("张三",16);
  System.out.println(s1);
  Student s2 =new Student("李四",19);
  System.out.println(s2);
  
 }
}

3.equals()
在Java中有两种比较对象的方式:

  1. == 运算符
  2. equals()方法

区别:
== 是比较两个对象引用的内存地址
equals()是比较两个对象的实际内容

 例子

public class People {

	int id; //身份证号
	String name; //名字
	public People(int id, String name) {
		this.id = id;
        this.name = name;
    }
	 public boolean equals(Object obj) {//重写 Object 类的 equals()方法
		 if(this==obj)//如果参数与本类是同一个对象
			 return true;
		 if(obj==null)//如果参数是null
			 return false;
		 if(getClass()!=obj.getClass())//如果参数与本类类型不同
			 return false;
		 People other = (People)obj;//将参数强转成本类对象
		 if(id!=other.id)//如果两者的身份证号不相等
			 return false;
		 return true;
	 }
	 public String toString() {//重写 Object 类的 toString()方法
		 return name;  //只输出名字
	 }
	 public static void main(String[]args) {
		 People p1 = new People(220,"tom");
	     People p2 = new People(220,"汤姆");
	     People p3 = new People(330,"张三");
		 Object o=new Object();
		 System.out.println(p1+"与"+p2+"是否为同一人?"); 
	        System.out.println("equals()方法结果:"+p1.equals(p2)); 
	        System.out.println("==运算符的结果:"+(p1==p2));

	        System.out.println(); 
	        System.out.println(p1+"与"+p3+"是否为同一人?"); 
	        
	        System.out.println(p1.equals(p3));
	        System.out.print(p1+"与"+o+"是否为同一人?");
	        System.out.println(p1.equals(0));
	    }
		
	}

7.3对象类型的转换

1.向上转型
即子类转化为父类:父类 = 子类(=表示赋值符),以此来达到父类描述子类的效果。具备继承关系得类与类之间才能进行对象类型转换,非继承关系之间不能进行类型转换。向上转型中父类是无法调用子类特有的属性或方法的!

2.向下转型
与向上转型相对,是父类转型成子类对象,但向下转型是不安全的,易出错的,因为抽象类转换为具体类极易出错!
如果要进行向下转型我们通常使用强制类型转换:

子类类型 子类对象 = (子类类型)父类对象;

用instanceof关键字判断对象类型

在向下转型时如果父类对象不是子类对象的实例,就会发生ClassCastException异常,所以我们常用instanceof关键字来判断父类对象。
用处:

  1. 判断一个实例对象是否属于一个类
  2. 判断是否一个类实现了某个接口

语法:

某类的对象引用 instanceof 某个类

在进行向下转型时,需要借助强制类型转换。 

例题 :某只鸽子是一只鸟,却不能说某只鸟是一只鸽子”:鸟类是鸽子类的父类,用Bird表示鸟类,用Pigeon表示鸽子类。

 class Bird {}
class Pigeon extends Bird{}
public class Demo4{
	public static void main(String[]arge) {
		Bird bird = new Pigeon();			//某只鸽子是一只鸟	向上转型
		Pigeon pigeon = bird;				//某只鸟是一只鸽子	向下转型
	}
 
}

Eclipse会报出如下图所示的编译错误,这是因为父类对象不能直接赋值给子类对象。 

 如果想要告诉编译器“某只鸟就是一只鸽子”,应该如何修正?答案就是强制类型转换。也就是说,要想实现向下转型,需要借助强制类型转换。语法如下:

子类类型子类对象=(子类类型)父类对象; 

 因此,要想实现把鸟类对象转换为鸽子类对象(相当于告诉编译器“某只鸟就是一只鹊子”,用要将图中第8行代码修改为:

Pigeon pigeon = (Pigeon) bird; //通过强制类型转换,告诉编译器“某只鸟就是一只鸽子”

两个没有继承关系的对象不可以进行向上转型或者向下转型。
父类对象可以强制转换为子类对象,但有一个前提条件:这个父类对象要引用这个子类对象 

7.4 使用instanceof关键字判断对象类型 

当在程序中执行向下转型操作时 ,如果父类对象不是子类对象的实例  ,就会发生ClassCastException异常  ,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类对象是否为子类对象的实例。这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是否一个类实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。

instanceof的语法格式如下:

myobject instanceof ExampleClass

myobject: 某类的对象引用

ExampleClass: 某个类

使用 instanceof操作符的表达式返回值为布尔值。如果返回值为true,说明 myobject 对象为ExampleClass的实例对象;如果返回值为false,说明myobject对象不是ExampleClass的实例对象。

注意:

instanceof是Java语言的关键字,在Java语言中的关键字都为小写。

下面来看一个向下转型与instanceof关键字结合的例子。
例题 分析几何图形之间的继承关系
创建Quadrangle四边形类、Square 正方形类和Circular圆类。其中,Square类继承Quadrangle类,在主方法中分别创建这些类的对象,然后使用instanceof关键字判断它们的类型并输出结果。
1.Quadrangle四边形类代码:

 class Quadrangle {
}

2.Square 正方形类代码:

class Square extends Quadrangle {
}

 3.Circular圆形类代码:

class Circular {
}

创建一个Demo5类:

public class Demo5 {
	public static void main(String args[]) {
	Quadrangle q = new Quadrangle(); 				//四边形对象
	Square s =new Square(); 						//正方形对象
	System.out.println(q instanceof Square);		//判断四边形是否为正方形的子类
	System.out.println(s instanceof Quadrangle); 	//判断正方形是否为四边形的子类
	System.out.println(q instanceof Circular); 		//判断正方形是否为圆形的子类
}
}

 会发生错误代码 Incompatible conditional operand types Quadrangle and Circular

 因为四边形类与圆形类没有继承关系,因此两者不能使用instanceof关键字进行比较,否则会发生
“不兼容”错误。如果删除或注释掉这行代码,运行结果如下:

7.5 方法的重载


构造方法的名称由类名决定,构造方法只有一个名称,但如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到“方法重载”。虽然方法重载起源于构造方法,但是它也可以应用到其他方法中。方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。

例题 编写不同形式的加法运算方法

在项目中创建OverLoadTest类,在类中编写add()方法的多个重载形式,然后在主方法中分别输出这些方法的返回值。

代码如下:

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) {			//先int参数,后double参数
		return a;										//输出int参数值
	}
	public static int add(double a,int b) {				//先double参数,后int参数
		return b;										//输出int参数
	}
	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(1));
		System.out.println("调用add(int,double)方法:"+add(5,8.0));
		System.out.println("调用add(double,int)方法:"+add(5.0,8));
		/*
		 * 
		 * 输出结果
		 * 
		 */
	}
}

 实例中分别定义了6个方法,在这6个方法中,前两个方法的参数个数不同,所以构成了重载关系;前两个方法与第3个方法比较时,方法的参数类型不同,并且方法的返回值类型也不同,所以这3个方法也构成了重载关系;比较第4、第5两个方法时,发现除了参数出现的顺序不同之外,其他都相同,这样同样可以根据这个区别将两个方法构成重载关系;而最后一个使用不定长参数的方法,实质上与参数数量不同是一个概念,也构成了重载。 

7.6 final 关键字

7.6.1 final 变量

final关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由final定义的变量为常量。例如,在类中定义PI值,可以使用如下语句:

final double PI=3.14;

7.6.2 final 方法


定义为fǐnal的方法不能被重写。

将方法定义为fǐnal类型可以防止子类修改该类的定义与实现方式,同时定义final的方法的执行效率要高于非final方法。在修饰权限中曾经提到过private修饰符,如果一个父类的某个方法被设置为private修饰符,子类将无法访问该方法,自然无法覆盖该方法,所以一个定义为private的方法隐式被指定为fǐnal类型,这样无需将一个定义为private的方法再定义为final类型。例如下面的语句:

private final void test() {
    …//省略一些程序代码
} 

 例题  使用final关键字为电视机上儿童锁
创建Dad爸爸类,给Dad类定义一个打开电视机的方法,该方法使用final关键字修饰。创建Dad
类的子类Baby类,让Baby类尝试自己重写打开电视的方法。

父类Dad代码:

class Dad {
	public final void turnOnTheTV() {
		System.out.println("爸爸打开了电视");
		
	}
}

子类Baby代码: 

class Baby extends Dad{
	 public final void turnOnTheTV() {
		 System.out.println("宝宝也要打开电视");
	 }
}

Eclipse就会报出如下图所示的编译错误。因为打开电视这个方法是由final修饰的,子类无法重写。所以Baby 想要打开电视,就只能找爸爸来打开了。

 7.6.3 final 类 

定义为fǐnal的类不能被继承。如果希望一个类不允许任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为fǐnal形式。fǐnal类的语法如下:

final class类名()

如果将某个类设置为final形式,则类中的所有方法都被隐式地设置为final形式,但是final类中的成员变量可以被定义为final或非final形式。 

 7.7多态


多态意为一个名字可具有多种语义,在程序设计语言中,多态性是指“一种定义,多种实现”,例如,运算符“+”作用于两个整型量时是求和,而作用于两个字符型量时则是将其连接在一起。利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。类的多态性可以从两方面体现:一是方法的重载,二是类的上下转型。

例题 万能的绘图方法

创建Shape图形类,作为Square正方形类和Circular圆形类的父类。创建Demo6类,并在该类中
创建一个绘图用的draw)方法,参数为Shape类型,任何Shape类的子类对象都可以作为方法的参数,并且方法会根据参数的类型绘制相应的图形。

父类代码 图形类:

	class Shape {		//图形类
 
}
class Circular extends Shape{}

class Square extends Shape {}
public class Demo6 {

public static void draw(Shape s) {
	if(s instanceof Square) {
		System.out.println("绘制正方形");
	}else if (s instanceof Circular) {
		System.out.println("绘制圆形");
	}else {
		System.out.println("绘制父类图形");
	}
}
public static void main(String[]args) {
	draw(new Shape());
	draw(new Square());
	draw(new Circular());
}
}

 从本实例的运行结果中可以看出,以不同类对象为参数调用draw()方法,可以处理不同的图形绘制问题。使用多态节省了开发和维护时间,因为程序员无须在所有的子类中定义执行相同功能的方法,避免了大量重复代码的编写。同时,只要实例化一个继承父类的子类对象,即可调用相应的方法,如果需求发生了变更,只需要维护一个draw()方法即可。 

7.8 抽象类与接口

通常可以说四边形具有4条边,或者更具体点,平行四边形是具有对边平行且相等特性的特殊四边形,等腰三角形是其中两条边相等的三角形,这些描述都是合乎情理的,但对于图形对象却不能使用具体的语言进行描述,它有几条边,究竟是什么图形,没有人能说清楚,这种类在Java中被定义为抽象类。

7.8.1 抽象类

在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回想继承和多态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中,并不需要将父类初始化对象,我们需要的只是子类对象,所以在Java语言中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一种具体图形,但它的子类却可以。
Java 中定义抽象类时,需要使用 abstract 关键字,其语法如下:        
 

[权限修饰符] abstract class 类名{
类体                                                                                                                                                     } 

  • 采用 abstract 关键字定义的类就是抽象类,采用 abstract 关键字定义的方法就
  • 是抽象方法
  • 抽象的方法只需在抽象类中,提供声明,不需要实现
  • 如果一个类中含有抽象方法,那么这个类必须定义成抽象类。抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中
  • final和abstract不能同时同时使用,这两个关键字是对立的
  • 抽象类的子类可以是抽象类。也可以是非抽象类
  • 一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现

抽象类无法实例化,无法创建对象,抽象类是被子类来继承的
抽象类可以有抽象类的子类

抽象方法表示没有实现的方法,没有方法体的方法

  • 没有方法体,以分号结尾
  • 前面的修饰符列表中有abstract关键字
    比如public abstract void dosome();

 7.9接口

instanceof
对象名 instanceof 类名        判断对象是否属于该类或其子类
对象名 instanceof 接门名    判断对象是否属于该接口的实现类

抽象方法
修饰符 abstract 返回参数 方法名(传入参数)


抽象类 有抽象方法的类一定是抽象类修饰符abstract class 类名{

}


接口 所有方法都是抽象方法修饰符 interface 接口名{

}


实现 implements
Java语言每个类可以实现多个接口修饰符 class 类名 implements 接口1,接口2,...{

}
 

接口是特殊的抽象类,类与类是继承extends,类与接口是实现implements,其实都是继承

接口是一种“引用数据类型”,完全抽象的,支持多继承,且一个接口可以继承多个接口,只有常量+抽象方法
所有的元素都是public修饰的,抽象方法的public abstract可以省略,常量的public static final可以省略,方法不能有方法体
接口使用interface 关键字进行定义,其语法如下:

[修饰符] interface 接口名[extends 父接口名列表] {
    [public] [static] [final] 常量;
    [public] [abstract] 方法;
}

定义的格式

[修饰符列表] interface 接口名{}

支持多继承,且一个接口可以继承多个接口
每一个interface 都会生成一个class后缀名的文件

public class text1 {
    public static void main(String[] args) {
	}
}

interface a{
}

interface b extends a{
}

interface c extends a,b{
}

修饰符:可选,用于指定接口的访问权限,可选值为public。 如果省略则使用默认的访问权限。
接口名: 必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。一般情况下,要求首字母大写。                                                                                                                             extends 父接口名列表:可选参数,用于指定要定义的接口继承于哪个父接口。当使用extends关键字时,父接口名为必选参数。
方法:接口中的方法只有定义而没有被实现。

一个类实现一个接口可以使用implements 关键字,代码如下: 

public class Parallelogram extends Quadrangle implements drawTest{
    ...//

}

接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。对于1小节中遗留的问题,可以将draw()方法封装到一个接口中,这样可以让一个类既能继承图形类,又能实现draw()方法接口,这就是接口存在的必要性。

  1. 在接口中,方法必须定义为public或者abstract形式
  2. 在接口中,任何字段都要定义为static和final形式

 例题:将绘图方法设为接口方法

package 复习;

interface Paintable {  //可绘制接口
 public void draw(); //绘制抽象方法
 }

class Quadrangle { //四边形类
 public void doAnything() {
 System.out.println("四边形提供的方法");
 }
 }
 //平行四边形类,继承四边形类,并实现了可绘制接口

class Parallelogram extends Quadrangle implements Paintable{
public void draw(){//由于该类实现了接口,所以需要覆盖draw()方法
 System.out.println("绘制平行四边形");
 }
}
class Square extends Quadrangle implements Paintable{
 public void draw() {
 System.out.println("绘制正方形");
 }
 }
 
 
 public class Demo7{
 public static void main(String[] args) {
 Square s = new Square();
 s.draw();
 s.doAnything();
 Parallelogram p = new Parallelogram();
 p.draw();
 p.doAnything();
 Circular c=new Circular();
 c.draw();
}
}

从这个结果可以看出,“绘制”这个行为可不是四边形独有的,圆形也能绘制,所以draw方法被独立封装在了可绘制接口中。
正方形类与平行四边形类分别继承了四边形类并实现了可绘制接口,所以正方形类与平行四边形类既可以调用绘制方法,又可以调用四边形提供的doAnything0方法。但是,圆形不属于四边形,且可以绘制,所以最后圆形对象只调用了draw0方法。

使用接口实现多重继承:
语法:

class 类名 implements 接口1,接口2,接口3,……,接口n

一个接口去继承另一个接口语法:

interface 接口1{}
interface 接口2 extends 接口1{}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云玩java.dog️

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值