一文带你深入理解【Java基础】· 面向对象编程(下)③接口和内部类

写在前面


        Hello大家好, 我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误不足之处,请多多指正!谢谢大家!!!

        如果小哥哥小姐姐们对我的文章感兴趣,请不要吝啬你们的小手,多多点赞加关注呀!❤❤❤ 爱你们!!!


目录

写在前面

1. 接口(interface)

1.1 接口是什么

1.2 接口(interface)演示代码

1.3 代理模式(Proxy)

1.4 接口练习

1.5 Java8中关于接口的改进

 2. 类的内部成员之五:内部类

2.1 什么是内部类

2.2 匿名内部类

2.3 内部类代码演示

结语


【往期回顾】

一文带你深入理解【Java基础】· 面向对象编程(下)②代码块、final和abstract

一文带你深入理解【Java基础】· 面向对象编程(下)①static和main方法

一文带你深入理解【Java基础】· 面向对象编程(中)③Object类和包装类的使用

一文带你深入理解【Java基础】· 面向对象编程(中)②子类对象实例化和多态

一文带你深入理解【Java基础】· 面向对象编程(中)①继承、方法的重写、权限修饰符和super

一文带你深入理解【Java基础】· 面向对象编程(上)②重载、封装、构造器、this、package和import

一文带你深入理解【Java基础】· 面向对象编程(上)①面向对象的理解、类和对象、对象的创建使用和属性


【习题总结】

【Java基础】· 面向对象编程(下)习题详解


1. 接口(interface)


1.1 接口是什么

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/...必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

  • 接口(interface)抽象方法常量值定义的集合。

接口的特点:

  • interface来定义。
  • 接口中的所有成员变量都默认是由public static final修饰的。
  • 接口中的所有抽象方法都默认是由public abstract修饰的。
  • 接口中没有构造器。
  • 接口采用多继承机制。

  • 定义Java类的语法格式:先写extends,后写implements
    • class SubClass extends SuperClass implements InterfaceA{ }
  • 一个类可以实现多个接口,接口也可以继承其它接口。
  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
  • 接口的主要用途就是被实现类实现。(面向接口编程)
  • 与继承关系类似,接口与实现类之间存在多态性
  • 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。

1.2 接口(interface)演示代码

/*
 * 接口的使用
 * 1.接口使用interface来定义
 * 2.Java中,接口和类是并列的两个结构
 * 3.如何定义接口:定义接口中的成员
 * 		
 * 		3.1 JDK7及以前:只能定义全局常量和抽象方法
 * 			>全局常量:public static final的.但是书写时,可以省略不写
 * 			>抽象方法:public abstract的
 * 			
 * 		3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
 * 
 * 4. 接口中不能定义构造器的!意味着接口不可以实例化
 * 
 * 5. Java开发中,接口通过让类去实现(implements)的方式来使用.
 *    如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
 *    如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
 *    
 * 6. Java类可以实现多个接口   --->弥补了Java单继承性的局限性
 *   格式:class AA extends BB implements CC,DD,EE
 *   
 * 7. 接口与接口之间可以继承,而且可以多继承
 * 
 * *******************************
 * 8. 接口的具体使用,体现多态性
 * 9. 接口,实际上可以看做是一种规范
 * 
 * 面试题:抽象类与接口有哪些异同?
 * 
 */
public class InterfaceTest {
	public static void main(String[] args) {
		System.out.println(Flyable.MAX_SPEED);
		System.out.println(Flyable.MIN_SPEED);
		// Flyable.MIN_SPEED = 2;

		Plane plane = new Plane();
		plane.fly();
	}
}

interface Flyable {

	// 全局常量
	public static final int MAX_SPEED = 7900;// 第一宇宙速度
	int MIN_SPEED = 1;// 省略了public static final

	// 抽象方法
	public abstract void fly();

	// 省略了public abstract
	void stop();

	// Interfaces cannot have constructors
	// public Flyable(){
	//
	// }
}

interface Attackable {

	void attack();

}

class Plane implements Flyable {

	@Override
	public void fly() {
		System.out.println("通过引擎起飞");
	}

	@Override
	public void stop() {
		System.out.println("驾驶员减速停止");
	}

}

abstract class Kite implements Flyable {

	@Override
	public void fly() {

	}

}

class Bullet extends Object implements Flyable, Attackable, CC {

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

	}

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

	}

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

	}

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

	}

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

	}

}
// ************************************

interface AA {
	void method1();
}

interface BB {

	void method2();
}

interface CC extends AA, BB {

}
/*
 * 接口的使用
 * 1.接口使用上也满足多态性
 * 2.接口,实际上就是定义了一种规范
 * 3.开发中,体会面向接口编程!
 * 
 */
public class USBTest {
	public static void main(String[] args) {

		Computer com = new Computer();
		// 1.创建了接口的非匿名实现类的非匿名对象
		Flash flash = new Flash();
		com.transferData(flash);

		// 2. 创建了接口的非匿名实现类的匿名对象
		com.transferData(new Printer());

		// 3. 创建了接口的匿名实现类的非匿名对象
		USB phone = new USB() {

			@Override
			public void start() {
				System.out.println("手机开始工作");
			}

			@Override
			public void stop() {
				System.out.println("手机结束工作");
			}

		};
		com.transferData(phone);

		// 4. 创建了接口的匿名实现类的匿名对象

		com.transferData(new USB() {
			@Override
			public void start() {
				System.out.println("mp3开始工作");
			}

			@Override
			public void stop() {
				System.out.println("mp3结束工作");
			}
		});
	}
}

class Computer {

	public void transferData(USB usb) {// 多态  USB usb = new Flash();
		usb.start();

		System.out.println("具体传输数据的细节");

		usb.stop();
	}

}

interface USB {
	// 常量:定义了长、宽、最大最小的传输速度等

	void start();

	void stop();

}

class Flash implements USB {

	@Override
	public void start() {
		System.out.println("U盘开启工作");
	}

	@Override
	public void stop() {
		System.out.println("U盘结束工作");
	}

}

class Printer implements USB {
	@Override
	public void start() {
		System.out.println("打印机开启工作");
	}

	@Override
	public void stop() {
		System.out.println("打印机结束工作");
	}

}


1.3 代理模式(Proxy)

概述:
代理模式是 Java 开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
应用场景:
  • 安全代理:屏蔽对真实角色的直接访问。
  • 远程代理:通过代理类处理远程方法调用(RMI
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
  • 比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

分类:

  • 静态代理(静态定义代理类)
  • 动态代理(动态生成代理类)
  • JDK自带的动态代理,需要反射等知识
/*
 * 接口的应用:代理模式
 * 
 */
public class NetWorkTest {
	public static void main(String[] args) {
		Server server = new Server();
//		server.browse();
		ProxyServer proxyServer = new ProxyServer(server);
		
		proxyServer.browse();
		
	}
}

interface NetWork {

	public void browse();
	
}

//被代理类
class Server implements NetWork{

	@Override
	public void browse() {
		System.out.println("真实的服务器访问网络");
	}

}
//代理类
class ProxyServer implements NetWork{
	
	private NetWork work;
	
	public ProxyServer(NetWork work){
		this.work = work;
	}
	

	public void check(){
		System.out.println("联网之前的检查工作");
	}
	
	@Override
	public void browse() {
		check();
		
		work.browse();
		
	}
	
}
public class StaticProxyTest {

	public static void main(String[] args) {
		Proxy s = new Proxy(new RealStar());
		s.confer();// 经纪人面谈
		s.signContract();// 经纪人签合同
		s.bookTicket();// 经纪人订票
		s.sing();// 明星:歌唱~~~
		s.collectMoney();// 经纪人收钱
	}
}

interface Star {
	void confer();// 面谈

	void signContract();// 签合同

	void bookTicket();// 订票

	void sing();// 唱歌

	void collectMoney();// 收钱
}

// 被代理类
class RealStar implements Star {

	public void confer() {
	}

	public void signContract() {
	}

	public void bookTicket() {
	}

	public void sing() {
		System.out.println("明星:歌唱~~~");
	}

	public void collectMoney() {
	}
}

// 代理类
class Proxy implements Star {
	private Star real;

	public Proxy(Star real) {
		this.real = real;
	}

	public void confer() {
		System.out.println("经纪人面谈");
	}

	public void signContract() {
		System.out.println("经纪人签合同");
	}

	public void bookTicket() {
		System.out.println("经纪人订票");
	}

	public void sing() {
		real.sing();
	}

	public void collectMoney() {
		System.out.println("经纪人收钱");
	}
}


1.4 接口练习

  • 定义一个接口用来实现两个对象的比较。
    • interface CompareObject{
    • public int compareTo(Object o); //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
    • }
  • 定义一个Circle类,声明redius属性,提供gettersetter方法
  • 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
  • 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。
public interface CompareObject {
	// 若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
	public int compareTo(Object o);
}

public class Circle {
	private Double radius;// 包装类
	public Double getRadius() {
		return radius;
	}
	public void setRadius(Double radius) {
		this.radius = radius;
	}
	public Circle() {
		super();
	}
	public Circle(Double radius) {
		super();
		this.radius = radius;
	}
}

public class ComparableCircle extends Circle implements CompareObject {
	public ComparableCircle(double radius) {
		super(radius);
	}

	@Override
	public int compareTo(Object o) {
		if (this == o) {
			return 0;
		}
		if (o instanceof ComparableCircle) {
			ComparableCircle c = (ComparableCircle) o;
			// 错误的:
			// return (int) (this.getRadius() - c.getRadius());
			// 正确的方式一:
			// if(this.getRadius() > c.getRadius()){
			// return 1;
			// }else if(this.getRadius() < c.getRadius()){
			// return -1;
			// }else{
			// return 0;
			// }
			// 当属性radius声明为Double类型时,可以调用包装类的方法
			// 正确的方式二:
			return this.getRadius().compareTo(c.getRadius());
		} else {
			// return 0;
			throw new RuntimeException("传入的数据类型不匹配");
		}
	}
}

public class ComparableCircleTest {
	public static void main(String[] args) {
		ComparableCircle c1 = new ComparableCircle(3.4);
		ComparableCircle c2 = new ComparableCircle(3.6);

		int compareValue = c1.compareTo(c2);
		if (compareValue > 0) {
			System.out.println("c1对象大");
		} else if (compareValue < 0) {
			System.out.println("c2对象大");
		} else {
			System.out.println("c1与c2一样大");
		}

		int compareValue1 = c1.compareTo(new String("AA"));
		System.out.println(compareValue1);// 报RuntimeException异常
	}
}


1.5 Java8中关于接口的改进

Java 8 中,你可以为接口添加 静态方法 默认方法 。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法: 使用 static 关键字修饰。 可以通过接口直接调用静态方法 ,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections 或者 Path/Paths 这样成对的接口和类。
默认方法: 默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection List Comparator 等接口提供了丰富的默认方法。
  • 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
  • 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
public interface CompareA {
	// 静态方法
	public static void method1() {
		System.out.println("CompareA:北京");
	}
	// 默认方法
	public default void method2() {
		System.out.println("CompareA:上海");
	}
	default void method3() {// 省略了public
		System.out.println("CompareA:上海");
	}
}

public interface CompareB {
	default void method3(){
		System.out.println("CompareB:上海");
	}
}

public class SuperClass {
	public void method3(){
		System.out.println("SuperClass:北京");
	}
}

public class SubClassTest {
	public static void main(String[] args) {
		SubClass s = new SubClass();
		// s.method1();
		// SubClass.method1();
		// 知识点1:接口中定义的静态方法,只能通过接口来调用。
		CompareA.method1();// CompareA:北京
		// 知识点2:通过实现类的对象,可以调用接口中的默认方法。
		// 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
		s.method2();// SubClass:上海
		// 知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
		// 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
		// 知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
		// 那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
		// 这就需要我们必须在实现类中重写此方法
		s.method3();// SubClass:深圳
	}
}

class SubClass extends SuperClass implements CompareA, CompareB {
	public void method2() {
		System.out.println("SubClass:上海");
	}
	public void method3() {
		System.out.println("SubClass:深圳");
	}
	// 知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
	public void myMethod() {
		method3();// 调用自己定义的重写的方法
		super.method3();// 调用的是父类中声明的
		// 调用接口中的默认方法
		CompareA.super.method3();
		CompareB.super.method3();
	}
}

接口冲突的解决方式:重写接口中发生冲突的方法

interface Filial {// 孝顺的
	default void help() {
		System.out.println("老妈,我来救你了");
	}
}

interface Spoony {// 痴情的
	default void help() {
		System.out.println("媳妇,别怕,我来了");
	}
}

class Father {
	public void help() {
		System.out.println("儿子,就我媳妇!");
	}
}

class Man extends Father implements Filial, Spoony {

	@Override
	public void help() {
		System.out.println("我该就谁呢?");
		Filial.super.help();
		Spoony.super.help();
	}

}

 2. 类的内部成员之五:内部类


2.1 什么是内部类

  • 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
  • Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类
  • Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
    • Inner class的名字不能与包含它的外部类类名相同;

分类:

  • 成员内部类static成员内部类和非static成员内部类)
  • 局部内部类(不谈修饰符)、匿名内部类
成员内部类作为类的成员的角色:
  • 和外部类不同,Inner class还可以声明为privateprotected
  • 可以调用外部类的结构
  • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
成员内部类作为类的角色:
  • 可以在内部定义属性、方法、构造器等结构
  • 可以声明为abstract类 ,因此可以被其它的内部类继承
  • 可以声明为final
  • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
【注意】
  • static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
  • 外部类访问成员内部类的成员,需要内部类.成员内部类对象.成员的方式
  • 成员内部类可以直接使用外部类的所有成员,包括私有的数据
  • 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
如何使用局部内部类
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
  • 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
局部内部类的特点
  • 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
  • 局部内部类可以使用外部类的成员,包括私有的。
  • 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
  • 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
  • 局部内部类不能使用static修饰,因此也不能包含静态成员

2.2 匿名内部类

  • 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
  • 格式:

        new 父类构造器(实参列表)|实现接口(){

        //匿名内部类的类体部分

        }

匿名内部类的特点
  • 匿名内部类必须继承父类或实现接口
  • 匿名内部类只能有一个对象
  • 匿名内部类对象只能使用多态形式引用

2.3 内部类代码演示

/*
 * 类的内部成员之五:内部类
 * 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
 * 
 * 2.内部类的分类:成员内部类(静态、非静态)  vs 局部内部类(方法内、代码块内、构造器内)
 * 
 * 3.成员内部类:
 * 		一方面,作为外部类的成员:
 * 			>调用外部类的结构
 * 			>可以被static修饰
 * 			>可以被4种不同的权限修饰
 * 
 * 		另一方面,作为一个类:
 * 			> 类内可以定义属性、方法、构造器等
 * 			> 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
 * 			> 可以被abstract修饰
 * 
 * 
 * 4.关注如下的3个问题
 *   4.1 如何实例化成员内部类的对象
 *   4.2 如何在成员内部类中区分调用外部类的结构
 *   4.3 开发中局部内部类的使用  见《InnerClassTest1.java》
 * 
 */
public class InnerClassTest {
	public static void main(String[] args) {

		// 创建Dog实例(静态的成员内部类):
		Person.Dog dog = new Person.Dog();
		dog.show();// 卡拉是条狗
		// 创建Bird实例(非静态的成员内部类):
		// Person.Bird bird = new Person.Bird();//错误的
		Person p = new Person();
		Person.Bird bird = p.new Bird();
		bird.sing();
		// 我是一只小小鸟
		// 人:吃饭
		// 人:吃饭
		System.out.println();

		bird.display("黄鹂");
		// 黄鹂
		// 杜鹃
		// 小明
	}
}

class Person {

	String name = "小明";
	int age;

	public void eat() {
		System.out.println("人:吃饭");
	}

	// 静态成员内部类
	static class Dog {
		String name;
		int age;

		public void show() {
			System.out.println("卡拉是条狗");
			// eat();
		}

	}

	// 非静态成员内部类
	class Bird {
		String name = "杜鹃";

		public Bird() {

		}

		public void sing() {
			System.out.println("我是一只小小鸟");
			Person.this.eat();// 调用外部类的非静态属性
			eat();
			System.out.println(age);
		}

		public void display(String name) {
			System.out.println(name);// 方法的形参
			System.out.println(this.name);// 内部类的属性
			System.out.println(Person.this.name);// 外部类的属性
		}
	}

	public void method() {
		// 局部内部类
		class AA {

		}
	}

	{
		// 局部内部类
		class BB {

		}
	}

	public Person() {
		// 局部内部类
		class CC {

		}
	}

}
public class InnerClassTest1 {

	// 开发中很少见
	public void method() {
		// 局部内部类
		class AA {

		}
	}

	// 返回一个实现了Comparable接口的类的对象
	public Comparable getComparable() {

		// 创建一个实现了Comparable接口的类:局部内部类
		// 方式一:
		// class MyComparable implements Comparable{
		//
		// @Override
		// public int compareTo(Object o) {
		// return 0;
		// }
		//
		// }
		//
		// return new MyComparable();

		// 方式二:
		return new Comparable() {

			@Override
			public int compareTo(Object o) {
				return 0;
			}

		};

	}

}

结语


本人会持续更新文章的哦!希望大家一键三连,你们的鼓励就是作者不断更新的动力

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

麟-小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值