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温习——面向对象第五部分的主要内容,如果未能解决你的问题,请参考以下文章