从零开始的Java开发1-4-4 多态与内部类:接口:定义并测试抽象方法常量默认方法静态方法重名默认方法和重名静态方法的解决方案继承;成员静态方法匿名 内部类

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始的Java开发1-4-4 多态与内部类:接口:定义并测试抽象方法常量默认方法静态方法重名默认方法和重名静态方法的解决方案继承;成员静态方法匿名 内部类相关的知识,希望对你有一定的参考价值。

文章目录

引入

Java只支持单继承:一个子类只有唯一的一个父类。
那么,如何解决一个类型中需要兼容多种类型特征呢?以及多个不同类型具有相同特征呢?

举一个手机的例子:
一开始的手机Telphone只能打电话:

//原始手机
public class Telphone 
	private String brand;
	private int price;
	
	public Telphone() 
		
	
	
	//打电话
	public void call() 
		System.out.println("打电话");
	

	public String getBrand() 
		return brand;
	

	public void setBrand(String brand) 
		this.brand = brand;
	

	public int getPrice() 
		return price;
	

	public void setPrice(int price) 
		this.price = price;
	
	
	


之后的手机Telphone2可以发短信:

public class Telphone2 extends Telphone
	public void message() 
		System.out.println("发短信");
	

再之后的手机Telphone3可以听音乐和看视频:

public class Telphone3 extends Telphone2
	public void vedio() 
		System.out.println("看视频");
	
	public void music() 
		System.out.println("听音乐");
	

现在的手机Telphone4可以上网:

public class Telphone4 extends Telphone3
	public void network() 
		System.out.println("上网");
	

写一个测试类:

public class PhoneTest 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		Telphone4 phone=new Telphone4();
		phone.call();
		phone.message();
		phone.vedio();
		phone.music();
		phone.network();
	


输出:

打电话
发短信
看视频
听音乐
上网

由此可见,我们可以通过继承关系来描述手机演变史的记录。
但是,电脑也可以上网,智能手表也可以打电话,MP3也可以听音乐…这些类有手机的部分功能,但它们无法组合成一个公共的父类。我们如果想实现电脑、智能手表、MP3的功能,难道要一个个重复地去实现这些类(和它们重复的功能)吗?

答案是否定的,接下来我们介绍的接口可以完美地解决上述的问题。

接口:定义接口并测试

定义一个“拍照”接口Interface

public interface IPhoto 
	//具有拍照能力
	public void phone();

Camera类里调用这个接口:(调用关键字implements

public class Camera implements IPhoto
	
	@Override
	public void phone() 
		System.out.println("相机可以拍照");
	

在第四代手机Telphone4里也调用这个接口:

public class Telphone4 extends Telphone3 implements IPhoto
	public void network() 
		System.out.println("上网");
	
	
	@Override
	public void phone() 
		System.out.println("手机可以拍照");
	

测试类:调用不同类的phone方法。

IPhoto ip=new Telphone4();
ip.phone();
ip=new Camera();
ip.phone();

输出:

手机可以拍照
相机可以拍照

注意:这里的ip只能访问photo,不能访问如network这种类的其他方法。换言之,我们可以通过接口来描述不同类型具有相似的行为特征,从而建立关系后,以接口引用指向实现类的方式来描述不同类型对接口行为的具体表现。

接口成员:抽象方法、常量

概念

  • 接口定义了某一批类所需要遵守的规范
  • 接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定了这些类里必须提供某些方法

如:Smartwatch类要用INet接口,那么它要么实现接口中的所有方法,要么让Smartwatch类自身变为abstract的类。

举例:电脑、智能手表、第四代手机都有上网的能力。

接口INet

//接口访问修饰符:通常是public 或 默认 
public interface INet 
	/*接口中抽象方法可以不写abstract关键字
	 *访问修饰符默认public
	 *当类实现接口时,需要去实现接口中的所有抽象方法,否则要将其类变为抽象类
	 */
	public void network();
	
	//接口中可以包含常量,默认public static final
	int temp=20;

电脑Computer使用这个接口:

public class Computer implements INet
	@Override
	public void network() 
		System.out.println("电脑可以上网");
	

智能手表Smartwatch

public class Smartwatch implements INet

	@Override
	public void network() 
		// TODO Auto-generated method stub		
		System.out.println("智能手表可以上网");		
	

第四代手机Telphone4

public class Telphone4 extends Telphone3 implements INet
	@Override
	public void network() 
		System.out.println("手机可以上网");
	

测试类:

INet it=new Computer();
it.network();
it=new Smartwatch();
it.network();
it=new Telphone4();
it.network();

输出:

电脑可以上网
智能手表可以上网
手机可以上网

关于常量

接下来测试一下INet中的常量:

System.out.println(INet.temp);
INet it=new Computer();
System.out.println(it.temp);

输出:

20
20

如果我们在接口中的常量temp为20,在实现类中定义一个temp为30,那么在测试类中会输出哪一个呢?

System.out.println(INet.temp);
INet it=new Computer();
System.out.println(it.temp);
Computer com=new Computer();
System.out.println(com.temp);

输出:

20
20
30

注意:接口的访问修饰符通常是public 或 默认
因为其他的类要重写接口里的方法,所以不能是private
而如果是protected的话,会报错。


注意:接口中的常量默认public static final

接口成员:默认方法、静态方法

默认方法

//default:默认方法 可以带方法体
default void connection() 
	System.out.println("我是接口中的默认链接");

这样在实现类中没有connection这个方法也不会报错。

静态方法

//static:静态方法 可以带方法体
static void stop() 
	System.out.println("我是接口中的静态方法");

实现类中没有重写它也不会报错。

如何调用默认方法和静态方法
默认方法可以实例化一个类后调用,静态方法用接口名调用。
ps:默认方法和静态方法在jdk 1.8之后新增

INet.stop();
INet it=new Computer();
it.connection();

输出:

我是接口中的静态方法
我是接口中的默认链接

使用场景:想用一个接口,但接口中某些方法对类来说没有意义。

重写默认方法
按Alt+/,会自动补全重写的默认方法。
我们发现,尽管在接口中的默认方法没有写访问权限的修饰,它是默认为public的。

如果没有super,那么只能访问接口中的静态成员。

@Override
public void connection() 
	// TODO Auto-generated method stub
	INet.super.connection();//调用接口中默认的方法

default默认方法可以在实现类中重写,并可以通过接口的引用调用,而static静态方法不可以在实现类中重写,可以通过接口名调用。

关于多接口中重名默认方法处理的解决方案

在Java中一个类可以实现多个接口:在implements后通过逗号添加新的接口名即可。

public class Smartwatch implements INet,IPhoto

IPhoto中的默认方法为:

default void connection() 
		System.out.println("我是IPhoto的默认方法");
	

INet中的默认方法为:

default void connection() 
		System.out.println("我是INet的默认方法");
	

那么,Smartwatch会报错——不知道应该调用哪个connection

解决方法——重写connection,说明要调用哪个connection,如:说明要调用INetconnection,这样就不报错了。

@Override
	public void connection() 
		// TODO Auto-generated method stub
		INet.super.connection();
	

或者:重写connection,定义一个自己的connection

public void connection() 
		// TODO Auto-generated method stub
		System.out.println("Smartwatch中的connection");
	

接下来测试一下:

INet it=new Smartwatch();
it.connection();
IPhoto itt=new Smartwatch();
itt.connection();

输出:

Smartwatch中的connection
Smartwatch中的connection

说明调用的都是重写的connection

一个类只能继承自一个类,但是可以实现若干接口。

那么,如果一个类继承自一个类,同时又实现两个接口,且这三者都有同名的方法,那么它是否可以分辨这些方法呢?

INetIPhoto中已经有connection方法。给Telphone3中也添加一个connection方法:

public class Telphone3 extends Telphone2	
	public void connection() 
		System.out.println("Telphone3的connection方法");
	

Telphone4继承Telphone3

public class Telphone4 extends Telphone3 implements INet,IPhoto

测试类:

INet it=new Telphone4();
it.connection();
IPhoto itt=new Telphone4();
itt.connection();

输出:

Telphone3的connection方法
Telphone3的connection方法

由此可见,子类会选择父类派生下来的方法。当然,如果子类重写了这个方法,那么在调用的时候会选择子类的方法。

总结

  1. 当一个类实现多个接口,而接口中有同名的默认方法时,实现类要重写这个方法
  2. 如果这个类已经继承自父类(且自身没有重写方法),那么默认情况下会调用父类的同名方法
  3. 如果这个类已经重写了这个方法,那么会调用自身重写的方法

关于多接口中重名常量处理的解决方案

三段代码是否正确?若正确,输出是什么?若错误,请说明原因。

代码1:错误。不知道是哪个x(接口One的还是接口Two的?)。
代码2:错误。不知道是哪个x(接口One的还是接口Two的还是父类Three的?)。
代码3:正确。输出44.

接口的继承

接口可以实现继承关系,并且可以继承多个父接口。一个类如果实现了某个子接口,那么它要实现所有的方法。

如:
父接口1:

public interface IFather 
	void run();

父接口2:

public interface IFather2 
	void fly();

子接口继承两个父接口:

public interface ISon extends IFather,IFather2
	void say();

实现类实现子接口——要实现所有方法:

public class Demo implements ISon

	@Override
	public void run() 
		// TODO Auto-generated method stub
		
	

	@Override
	public void say() 
		// TODO Auto-generated method stub
		
	

	@Override
	public void fly() 
		// TODO Auto-generated method stub
		
	


如果继承的多个接口中有同名的默认方法,那么子接口也要重写这个方法。实现类调用方法的时候调用的也是子接口的方法。

内部类

  • 在Java中,可以将一个类定义在另一个类里面或一个方法里面,这样的类称为内部类
  • 与之对应,包含内部类的类称为外部类

如:

//外部类:人
public class Person 
	int age;
	
	public Heart getHeart() 
		return new Heart();
	
	
	//内部类:心脏
	class Heart
		public String beat() 
			return "心脏在跳";
		
	

内部类隐藏在外部类之内,更好的实现了信息隐藏——是更好的封装手段。

内部类的分类

  • 成员内部类
  • 静态内部类
  • 方法内部类
  • 匿名内部类

成员内部类

内部类中最常见的就是成员内部类,也成为普通内部类

定义一个person类:

//外部类
public class person 
	int age;
	
	public Heart getHeart() 
		return new Heart();
	
	
	//成员内部类
	class Heart 
		public String beat() 
			return "心脏跳动";
		
	

如何获取内部类的实例对象

方案1:new一个外部类的实例对象,再new它的内部类

//new 外部类.new 内部类
person.Heart myHeart=new person().new Heart();

方案2:使用外部类对象获取内部类实例

person a=new person();
a.age=1;

person.Heart myHeart=a.new Heart();

方案3:通过外部类对象的获取方法GetHeart
ps:一般外部类都会设置一个获取内部类的方法,以便于内部类对象的实例化操作

person a=new person();
a.age=1;

person.Heart myHeart=a.getHeart();

总结

  • 内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化
  • 内部类访问修饰符可以任意,但访问范围会受到影响
  • 内部类可以直接访问外部类的成员:如果出现同名属性,优先访问内部类定义的属性
  • 若想访问外部类的属性,可以通过外部类.this.成员的方式访问外部类同名的信息
  • 若外部类想访问内部类的信息,要通过内部类的实例获取到,否则无法访问
  • 内部类编译后.class文件命名是外部类$内部类.class

查看内部类在类中的结构
点击:

会弹出文件夹:

src放的是源码。

点击bin->com->people,可以看到:内部类的命名是外部类$内部类

静态内部类

静态内部类对象可以不依赖于外部类对象,直接创建。

  1. 静态内部类中只能直接访问外部类的静态成员,若想调用非静态成员,可以通过对象实例
  2. 静态内部类对象实例时,可以不依赖于外部类对象
  3. 可以通过外部类.内部类.静态成员的方式,访问内部类中的静态成员
  4. 当内部类属性与外部类属性重名,默认直接调用内部类中的成员;如果要访问外部类中的静态属性,则可通过外部类.属性名 来访问,如果要访问外部类中的非静态属性,则可通过new 外部类().属性名 来访问

获取静态对象内部类对象实例

person.Heart myHeart = new person.Heart();

访问内部类中的静态成员
类:

//外部类
public class person 	
	//静态内部类
	public static class Heart 
		public static int temp=1;		
	

测试类:System.out.println(person.Heart.temp);
输出:1

方法内部类

定义在外部类方法中的内部类,也称局部内部类。

//外部类
public class person 

	public String getHeart() 
		//方法内部类
		class Heart
			public int age=23;
			
			public void eat() 
				System.out.println("方法内部类的eat");
			
			
			public String beat() 
				return "心脏跳动";
			
		
	
	return new Heart().beat();
	
	

注意,方法内部类里的方法只能在方法内部类里用。

测试类:关于如何调用方法内部类的方法:其结果是方法内部类的返回值。

person a=new person();
System.out.println(a.getHeart());

输出:

心脏跳动

查看方法内部类的命名规则——查看文件结构
对工程名右键,点击show in,一路向下点击得到:

可以看到,内部类是person$1Heart.class

总结:方法内部类

  1. 定义在方法内部,作用范围也在方法内
  2. 和方法内部成员使用规则一样,class前面不可以添加publicprivateprotectedstatic
  3. 类中不能包含静态成员
  4. 类中可以包含finalabstract成员

匿名内部类

一个Person类:

public abstract class Person 
	private String name;
	
	public Person() 
		
	

	public String getName() 
		return name;
	

	public void setName(String name) 
		this.name = name;
	
	
	public abstract void read();		

Man类:

public class Man extends Person

	@Override
	public void read() 
		// TODO Auto-generated method stub
		System.out.println("man read");
	


Women类:

public class Women extends Person

	@Override
	p

以上是关于从零开始的Java开发1-4-4 多态与内部类:接口:定义并测试抽象方法常量默认方法静态方法重名默认方法和重名静态方法的解决方案继承;成员静态方法匿名 内部类的主要内容,如果未能解决你的问题,请参考以下文章

从零开始的Java开发 笔记目录(持续更新)

Java开发从零开始!java游戏服务器开发教程

Java开发从零开始!java游戏服务器开发教程

从零开始的Java开发1-3-3 综合案例:学生信息管理

从零开始的Java开发1-3-2 Java封装包packagestatic关键字代码块

从零开始的Java开发1-5-1 异常:分类异常处理try-catch-finally多重catchfinally与returnthrowsthrow关键字自定义异常类异常链