Java温习——面向对象第五部分

Posted twc829

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java温习——面向对象第五部分相关的知识,希望对你有一定的参考价值。

一、接口

1 概念

(1)硬件接口

指两个硬件设备之间的连接方式;

包括物理上的接口、逻辑上的数据传送协议


(2)软件接口

指程序代码,特殊的类;

在Java中,表示一种规范,是具有N个方法的特征集合;

接口只定义类中必须提供的方法,不关心类的内部数据和功能实现细节,分离规范和实现,增强系统可拓展性和可维护性;


注:

面向接口编程中,接口和实现类体验了真正的多态;

接口是多个抽象类的抽象;

在Java中最小的程序单元是类,接口其实是一个特殊的类;

Java中接口表示规范,用于定义一组抽象方法,表示某一类事物必须具备的功能,要求实现类必须实现该接口并提供方法实现;


2 语法定义

[<修饰符>] interface <接口名>


注:

接口起名原则是具有某些能力的,习惯以able或handler结尾或以I开头;

编译成功后,和类一样,生成一个字节码;


3 接口中成员

(1)没有构造器,也不能显式定义构造器,因此不能实例化;

(2)字段只能是全局静态常量,即默认使用public static final修饰字段,可不写,字段名大写;

(3)方法都是公共抽象方法,即默认使用public abstract修饰,可不加,但不能定义普通方法;

(4)内部类都是公共静态内部类,即默认使用public static修饰,可不加;


注:

接口只能继承接口,不能集成类,且接口支持多继承(类是单继承关系);

[<修饰符>] interface <接口名> extends <接口1>, <接口2>


类与类之间存在继承关系,使用extends表示;

接口与接口之间只能是继承关系,使用extends表示;

接口与类之间只能是实现关系,使用implements表示;

常量接口,用来封装多个常量,功能和常量类相同,但不推荐使用常量接口,因为没有使用接口的功能;

标志接口,表示没有任何成员,仅仅是一个接口的定义,其他类实现该接口用来表示属于该家族,同不推荐;


4 接口的实现

接口只是定义了某一事物应该具有某些功能,但没有提供具体实现,需要类实现该接口并覆盖接口中的方法,从而实现接口中定义的方法;

(1)类实现接口语法

一个类可实现多个接口,从而弥补类的单继承问题;

[<修饰符>] class <类名> extends <父类名> implements <接口名1, 2, 3, ...>


(2)接口与实现类的多态关系

严格上,接口与实现类之间的关系是实现关系;但在开发中,有时将实现关系称为特殊继承关系,即接口是实现类的父类,实现类是接口的子类;

接口和实现类的多态关系是最常见的;

<接口名> <变量名> = new <实现类名>();


注:

接口中方法是公共抽象的,因此实现类必须覆盖接口中的方法,且方法必须使用public修饰,因为子类方法访问修饰大于等于父类访问修饰符;


interface IWalkable
	void doWork();


class Cat implements IWalkable
	public void doWork()
		System.out.println("走猫步");
	


class InterfaceDemo
	public static void main(String[] args) 
	
		// new Cat().doWork();
		IWalkable w = new Cat(); // 面向接口编程,存在多态
		w.doWork(); // 多态特性,执行Cat类中的doWork方法
	


5 类和类以及类和接口的关系图

interface IWalkable
	void walk();


interface ISwimable
	void swim();


abstract class Animal


class Frog extends Animal implements IWalkable, ISwimable
	public void walk()
		System.out.println("跳跳跳");
	
	public void swim()
		System.out.println("蛙泳");
	


class InterfaceDemo
	public static void main(String[] args) 
	
		Frog f = new Frog(); // 若使用多态关系,只能调用该接口中的方法,如IWalkable w = new Frog(); w.walk(); // w.swim();错误
		f.walk();
		f.swim();
	




6 接口与抽象类的异同

(1)相同点

a 都位于继承的顶端,用于被其他实现或继承;

b 都不能实例化;

c 都可定义抽象方法,其子类或实现类都必须覆盖这些抽象方法;


(2)不同点

a 接口没有构造器,抽象类有构造器;

b 接口只包含抽象方法,抽象类可包含普通方法和抽象方法(Java8前);

c 类是单继承,接口是多继承,类和接口之间可多实现;

d 接口中的成员变量默认是public static final,抽象类中的默认是包访问权限;

e 接口中的方法默认是public abstract,抽象类中的方法默认是包访问权限;

f 接口中的内部类默认是public static,抽象类中的内部类默认是包访问权限;


注:

若接口和实现类可完成相同的功能,尽量使用接口,体现面向接口编程的思想;


7 面向接口编程思想

(1)多态的好处

把实现类对象赋值给接口类型变量,屏蔽了不同实现类之间的实现差异,实现通用编程;


(2)举例:使用USB设备工作

不使用面向接口编程思想,则需要在MotherBoard类中定义多个plugin方法;

class Mouse
	public void swapData()
		System.out.println("鼠标移动中");
	


class Printer
	public void swapData()
		System.out.println("打印中");
	


class MotherBoard
	public static void plugIn(Mouse m)
		m.swapData();
	
	public static void plugIn(Printer p)
		p.swapData();
	


class Demo
	public static void main(String[] args)
		MotherBoard.plugIn(new Mouse()); // 鼠标移动中
		MotherBoard.plugIn(new Printer()); // 打印中
	


而使用面向接口编程思想后,MotherBoard类中只需一个plugIn方法;

interface IUSB
	void swapData();


class Mouse implements IUSB
	public void swapData()
		System.out.println("鼠标移动中");
	


class Printer implements IUSB
	public void swapData()
		System.out.println("打印中");
	


class MotherBoard
	public static void plugIn(IUSB usb)
		usb.swapData();
	


class USBDemo
	public static void main(String[] args)
		MotherBoard.plugIn(new Mouse()); // 鼠标移动中
		MotherBoard.plugIn(new Printer()); // 打印中
	




二、内部类

1 概念

定义在类中的另一个类;


2 类中可定义的成员

有三种,即字段、方法、内部类;

内部类看作是外部类的一个成员,因此内部类可使用四个访问权限修饰符(public/protected/缺省/private)修饰,还可使用static修饰;

外部类只能使用public或缺省;


3 使用内部类的原因

(1)增强封装,把内部类隐藏在外部类中,不准其他类访问内部类;

(2)内部类提高代码的可读性和可维护性,把小型类嵌入到外部类中,结构更靠近;

(3)内部类可直接访问外部类的成员,但外部类不可直接使用内部类中的成员;


如双向链表由多个节点组成,将节点类作为双向链表类的内部类

// 双向链表
public class LinkedList
	private Node first; // 第一个节点
	private Node last; // 最后一个节点

	// 节点类型:把内部类看成一个整体
	class Node
		private Node prev; // 当前的上一个节点
		private Node next; // 当前的下一个节点
		private Object ele; // 当前节点存储的数据
	


4 分类

根据使用不同的修饰符或定位的位置不同,分为四种;

(1)实例内部类

a 概念

内部类没有使用static修饰,表明内部类属于外部类的对象,而非外部类本身;


b 特点

创建实例内部类前,必须存在外部类对象,通过外部类对象创建内部类对象(当存在内部类对象时,一定存在外部类对象);

Outter.Inner in = new Outter().new Inner();

实例内部类的实例自动持有外部类的实例的引用,内部类可直接访问外部成员;

外部类中不能直接访问内部类的成员,必须通过内部类的实例访问;

实例内部类中不能定义静态成员,只能定义实例成员;

若实例内部类和外部类存在同名的字段或方法abc,则在内部类中,this.abc表示访问内部类成员,外部类.this.abc表示访问外部类成员;


class Outter
	String name = "Outter.name";
	
	public void work()
		System.out.println(new Inner().age);
	
	class Inner
		int age = 18;
		String name = "Inner.name";
		
		public void test()
			String name = "local.name";
			System.out.println(name); // local.name
			System.out.println(this.name); // Inner.name
			System.out.println(Outter.this.name); // Outter.name
		
	


class InstanceInnerClassDemo
	public static void main(String[] args) 
	
		Outter.Inner in = new Outter().new Inner();
		in.test();
	


(2)静态内部类

a 概念

内部类使用static修饰;


b 特点

静态内部类的实例不会自动持有外部类的特定实例的引用(静态内部类直接属于外部类),在创建内部类的实例时不必创建外部类的实例;

Outter.Inner in = new Outter.Inner();


静态内部类可直接访问外部类的静态成员,若访问外部类的实例成员,必须通过外部类的实例访问;

静态内部类中可定义静态成员和实例成员;

测试类可通过完整的类名直接访问静态内部类的静态成员;


class Outter
	String name = "Outter.name";
	static String name2 = "name2";

	static class Inner
		static int age = 18;
		
		public void test()
			System.out.println(name2);
			System.out.println(new Outter().name);
		
	


class StaticInnerClassDemo
	public static void main(String[] args) 
	
		Outter.Inner in = new Outter.Inner();
		in.test();

		System.out.println(Outter.Inner.age);
	


(3)局部内部类(不会用)

a 概念

在方法中定义的内部类,其可见范围是当前方法和局部变量是同一级别;


b 特点

不能使用public、private、protected、static修饰符(类似于局部变量);

局部内部类只能在当前方法中使用;

局部内部类和实例内部类一样,不能包含静态成员;

局部内部类和实例内部类一样,都可访问外部类的所欲成员;

局部内部类访问的局部变量必须使用final修饰(不适用于Java8,因为Java8自动隐式加上final,依然是常量),因为若当前方法不是main方法,则当前方法调用结束后,当前方法的栈帧被销毁,方法内部的局部变量的空间全部销毁,然而局部内部类是定义在方法中,且在方法中会创建局部内部类对象,而局部内部类会访问局部变量,当前方法被销毁时对象还在堆内存,依然持有对局部变量的引用,但方法被销毁时局部变量已被销毁,此时在堆内存中一个对象引用不存在的数据,因此使用final修饰局部变量,从而变成常量,永驻内存空间;


class LocalInnerClassDemo
	static String name = "name";

	public static void main(String[] args) 
	
		final int age = 18; // 局部变量必须是final的,才能被局部内部类访问
		
		class Inner
			String info = "INFO";

			public void test()
				System.out.println(name); // 可直接访问外部类的成员变量
				System.out.println(info); // 
				System.out.println(age); // 局部内部类访问局部变量
			
		
		
		new Inner().test();
		System.out.println(new Inner().info);
	


(4)匿名内部类(使用最多)

a 概念

是一个没有名称的局部内部类,适用于仅使用一次的类,是局部内部类的特殊情况;

在开发中,经常有这样的类——只需定义一次、使用一次就可丢弃;

在JavaSE/ android的事件处理中,不同的按钮点击后应该有不同的响应操作,首先使用匿名内部类;


b 特点

匿名内部类本身没有构造器,但会调用父类的构造器;

匿名内部类尽管没有构造器,但可在匿名类中提供一段实例初始化代码块,JVM在调用父类构造器后,会执行该代码块;

内部类处理可继承类,还可实现接口;


c 语法格式

new 父类构造器([<实参列表>]) 或 接口()
	// 匿名内部类的类体部分


注:

匿名内部类必须继承一个父类或实现一个接口,但最多只能继承一个父类或实现一个接口;

匿名内部类访问局部变量时,需要将该局部变量使用final修饰;


// 创建一个IUSB的匿名实现类对象
MotherBoard.plugIn(new IUSB()
	// 匿名内部类类提体
	public void swapData()
		System.out.println("正在输入中");
	
);





注:

每个内部类经过成功编译后,生成独立的class文件;

成员内部类,外部类名$内部类名

局部内部类,外部类名$数字$内部类名

匿名内部类,外部类名$数字




三、枚举

1 概念

是从Java5开始提供的一种新的数据类型,是一个特殊的类,就是多个固定的常量对象的集合;

用来表示事物固定的状态;


2 定义

[<修饰符>] enum <枚举类名>
	常量A, 常量B, 常量C;


enum Weekday
	MONDAY, TUUESDAY, WEDNESDAY, THURSDAY, FTIDAY, SATDAY, SUNDAY;


3 特点

(1)枚举的直接父类是java.lang.Enum,但不能显式继承Enum;

(2)枚举相当于一个类,可定义构造函方法、成员变量、普通方法和抽象方法;

(3)默认私有构造方法,即使不屑访问权限也是private;

(4)每个实例分别用一个全局常量表示,枚举类的对象是固定的,实例个数有限,不能使用new关键字;

(5)枚举实例必须位于枚举体中的最开始部分,枚举实例列表后面要有分号与其他成员分开;

(6)枚举实例后有花括号时,该实例是枚举类的匿名内部类对象;


4 使用

(1)枚举中都是全局公共的静态常量,可直接使用枚举类名进行调用

(2)因为java.lang.Enum是所有枚举类的父类,因此所有的枚举对象可调用Enum类中的方法

String name = <枚举对象名>.name(); // 返回枚举对象的常量名称
int ordinal = <枚举对象>.ordinal(); // 返回枚举对象的序号,从0开始
String str = <枚举对象>.toString(); // 返回枚举对象的常量名称


(3)编译器生成的枚举类的静态方法

<枚举类型>[] values() 返回当前枚举类型所有常量,使用一个数组封装起来;

<枚举类型> valueOf(String name) 把一个指定名称的字符串转换为当前枚举类中同名的常量;

(4)从Java5开始出现枚举,switch也支持操作枚举类型,但本质上switch只支持int类型,byte、short、char类型自动转换为int类型,而枚举类型在底层使用枚举对象的ordinal()


5 枚举的单例设计模式

如下,原先使用的单例设计模式存在缺陷——使用反射便可创建对象;

而枚举的单例设计模式很安全,即使使用反射也无法创建对象,推荐!

/*
class ArrayUtil
	private static ArrayUtil instance = new ArrayUtil();
	// 将构造器私有化,说明不能在其他类进行调用,即不能创建对象
	private ArrayUtil()
	
	public static ArrayUtil getInstance()
		return instance;
	
		
	public void sort(int[] arr)
		System.out.println("排序操作");
	

*/
enum ArrayUtil
	INSTANCE;
		
	public void sort(int[] arr)
		System.out.println("排序操作");
	


class SingletonDemo
	public static void main(String[] args)
		System.out.println(ArrayUtil.INSTANCE == ArrayUtil.INSTANCE); // true
		ArrayUtil.INSTANCE.sort(null);
		ArrayUtil.INSTANCE.sort(null);
	


以上是关于Java温习——面向对象第五部分的主要内容,如果未能解决你的问题,请参考以下文章

杨玲 201771010133《面向对象程序设计(java)》第五周学习总结

java第五章:面向对象(oop)

Java面向对象详解-上

java第五章:面向对象(oop)三大特性之多态

Java面向对象

举例说明java面向对象的封装继承多态