面向对象
接口
接口概念
接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”。
接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。 接口定义了某一批类所需要遵循的规范,接口不关心这些类的内部状态,也不关心这些类里方法的实现细节,它只规定这批类必须具有某些方法
请记住:一切事物均有功能,即一切事物均有接口。
接口的定义
与定义类的class不同,接口定义时需要使用 interface 关键字。
定义接口所在的仍为.java 文件,虽然声明时使用的为 interface 关键字,但编译后仍然会产生.class文件。这点可以让我们将接口看做是一种只包含了功能声明的特殊类。
- 定义格式:
public interface 接口名 {
抽象方法1;
抽象方法2;
抽象方法3;
}
使用interface 代替了原来的 class,其他步骤与定义类相同:
接口中的方法均为公共访问的抽象方法
接口中无法定义普通的成员变量
类实现接口
类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,只是关键字不同,实现使用 implements。
其他类(实现类)实现接口后,就相当于声明:”我应该具备这个接口中的功能”。实现类仍然需要重写方法以实现具体的功能。
格式:
class 类 implements 接口 {
重写接口中方法
}
在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
接口中定义功能,当需要该功能时,可以让类实现该接口,接口只声明了应该具备该方法
在具体实现类中重写方法,实现功能,是方法的具体实现。
于是,通过以上两个动作将功能的声明与实现便分开了。(此时请重新思考:类是现实事物的描述,接口是功能的集合。)
接口中成员的特点
接口的成员特点
- 成员变量:接口中可以定义变量,但是变量只能是最终变量,并且是静态的 public static final
- 成员方法:接口中可以定义方法,但是只能是抽象方法 public abstract
- 构造方法:接口不可以创建对象
- 接口中没有构造方法
- 子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类。
示例代码
interface Inter{ //定义一个名称为Inter的接口。
public static final int NUM = 3;// NUM的值不能改变
public abstract void show1();
public abstract void show2();
}
//定义子类去覆盖接口中的方法,通过 implements 关键字去实现接口。
class InterImpl implements Inter{ //子类实现Demo接口。
//重写接口中的方法。
public void show1(){}
public void show2(){}
}
接口的多实现
了解了接口的特点后,那么想想为什么要定义接口,使用抽象类描述也没有问题,接口到底有啥用呢?
接口最重要的体现:解决多继承的弊端。将多继承这种机制在 Java 中通过多实现完成了。
interface Fu1Inter{
void show1();
}
interface Fu2Inter{
void show2();
}
class Zi implements Fu1Inter,Fu2Inter{// 多实现。同时实现多个接口。
public void show1(){}
public void show2(){}
}
怎么解决多继承的弊端呢?
弊端:多继承时,当多个父类中有相同功能时,子类调用会产生不确定性。 其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。
为什么多实现能解决了呢?
因为接口中的功能都没有方法体,由子类来明确。
类继承类同时实现接口
接口和类之间可以通过实现产生关系,同时也学习了类与类之间可以通过继承产生关系。当一个类已经继承了一个父类,它又需要扩展额外的功能,这时接口就派上用场了。 子类通过继承父类扩展功能,通过继承扩展的功能都是子类应该具备的基础功能。如果子类想要继续扩展其他类中的功能呢?这时通过实现接口来完成。
示例代码
class FuClass {
public void show(){}
}
interface Inter {
pulbic abstract void show1();
}
class Zi extends FuClass implements Inter {
public void show1() {
}
}
接口的出现避免了单继承的局限性。父类中定义的事物的基本功能。接口中定义的事物的扩展功能。
接口的多继承
学习类的时候,知道类与类之间可以通过继承产生关系,接口和类之间可以通过实现产生关系,那么接口与接口之间会有什么关系。
多个接口之间可以使用 extends进行继承。
示例代码
interface Fu1{
void show();
}
interface Fu2{
void show1();
}
interface Fu3{
void show2();
}
interface Zi extends Fu1,Fu2,Fu3{
void show3();
}
在开发中如果多个接口中存在相同方法,这时若有个类实现了这些接口,那么就要实现接口中的方法,由于接口中的方法是抽象方法,子类实现后也不会发生调用的不确定性。
接口的思想
举例:我们都知道电脑上留有很多个插口,而这些插口可以插入相应的设备,这些设备为什么能插在上面呢?主要原因是这些设备在生产的时候符合了这个插口的使用规则,否则将无法插入接口中,更无法使用。发现这个插口的出现让我们使用更多的设备。
总结:接口在开发中的它好处
1、接口的出现扩展了功能。
2、接口其实就是暴漏出来的规则。
3、接口的出现降低了耦合性,即设备与设备之间实现了解耦。
接口和抽象类的区别
相同点:
- 都位于继承的顶端,用于被其他类实现或继承;
- 都不能直接实例化对象;
- 都包含抽象方法,其子类都必须覆写这些抽象方法;
区别:
- 抽象类为部分方法提 ,避免子类重复实现这些方法,提高代码的重用性 ;
- 接口只能包含抽象方法;
- 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java 的单继承)
- 抽象类是这个事物中应该具备的你内容, 继承体系是一种 is..a关系
- 接口是这个事物中的额外内容,继承体系是一种 like..a 关系
二者的选用:
- 优先选用接口,尽量少用抽象类;
- 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类 ;
类与类、类与接口、接口与接口等关系
类与类
- 继承关系,只能单继承,可以多层继承
类与接口
- 实现关系,可以多实现
接口与接口
- 继承关系,可以单继承,也可以多继承
匿名对象、内部类
匿名对象
匿名对象的概念
匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量
如:已经存在的类:
public class Person{
public void eat(){
System.out.println();
}
}
创建一个普通对象
Person p = new Person();
创建一个匿名对象
new Person();
匿名对象的特点
创建匿名对象直接使用,没有变量名。
new Person().eat(); //eat方法被一个没有名字的Person对象调用了。匿名对象在没有指定其引用变量时,只能使用一次。
new Person().eat(); 创建一个匿名对象,调用eat方法
new Person().eat(); 想再次调用eat方法,重新创建了一个匿名对象匿名对象可以作为方法接收的参数、方法返回值使用
内部类
内部类概念
什么是内部类
将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。其他类也称为外部类。 简单理解就是将一个类定义在另一个类里面,这样称为内部类(类中类)什么时候使用内部类
在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。
示例1
class 机箱{ //外部类
class 硬盘{ //内部类
}
}
示例2
class 汽车 { //外部类
class 发动机 { //内部类
}
}
- 内部类的分类
内部类分为成员内部类、局部内部类、静态内部类、匿名内部类 我们定义内部类时,就是一个正常定义类的过程,同样包含各种修饰符、继承与实现关系等。在内部类中可以直接访问外部类的所有成员。
成员内部类
成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问
定义格式
class 外部类 {
修饰符 class 内部类 {
//其他代码
}
}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类,前提是必须存在一个外部类对象。
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
成员内部类代码演示
public class Demo {
public static void main(String[] args) {
Outer inner = new Outer();
inner.m1();
}
}
class Outer {
public void m1() {
System.out.println("已进入方法");
// 局部内部类
class Inner {
public void show() {
System.out.println("show...");
}
}
Inner i = new Inner();
i.show();
}
}
成员内部类访问外部类成员
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况
下访问的是成员内部类的成员。如果要访问外部类的同名成员,那么访问方式为:
外部类.this.成员变量
外部类.this.成员方法
示例代码
class Outer {
private int num = 10;
public void show2() {
System.out.println(num);
}
public class Inner {
private int num = 20;
public void show() {
//访问外部类成员
System.out.println(Outer.this.num);
//访问本类成员(内部类)
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
成员内部类使用 private 修饰
成员内部类相当于外部类的一个成员 被private 修饰的成员不能直接访问,如果想要访问,那么怎么办?
按照封装成员变量的思想,提供外界访问方法即可
如何理解?
比如人的心脏,心脏生病了只能医生来访问,普通人是不能进行访问的
示例代码
class Body {// 外部类,身体
private class Heart { // 内部类,心脏
public void jump() {
System.out.println("心脏在跳动");
}
}
Heart heart = new Heart();
//获取心脏
public void jump(){
heart.jump();;
}
}
class Demo {
public static void main(String[] args) {
Body body = new Body();
body.jump();//调用身体的jump方法,再由Body进行控制
}
}
局部内部类
局部内部类,是定义在外部类方法中的局部位置,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
注意:局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及static修饰符的。
定义格式
class 外部类 {
修饰符 返回值类型 方法名(参数) {
class 内部类 {
//其他代码
}
}
}
访问方式
在内部类所在的法中,创建内部类对象,进行访问
局部内部类代码演示
class Outer {
public void m1() {
//局部内部类
class Inner {
public void show() {
System.out.println("show...");
}
}
}
public class Demo {
public static void main(String[] args) {
Outer inner = new Outer();
inner.m1();
}
}
静态内部类
在类的前面多了一个关键字 static。静态内部类是不需要依赖于外部类的,在没有外部类的对象的情况下,可以创建静态内部类的对象,静态内部类只能访问静态外部类成员
class Outer {
private int num = 20;
static public class Inner {
public void show() {
//错误只能访问静态成员
//System.out.println(Outer.this.num);
}
}
}
匿名内部类
匿名内部类概念
最常用到的内部类就是匿名内部类,它是局部内部类的一种。
定义的匿名内部类有两个含义:
- 临时定义某一指定类型的子类
- 定义后即刻创建刚刚定义的这个子类的对象
本质
是一个继承了该类或者实现了该接口的子类匿名对象
匿名内部类的作用与格式
作用:匿名内部类是创建某 子类对象的便捷方式。
格式:
new 父类或接口(){
//进行方法重写
};
示例代码
//已经存在的父类:
public abstract class Person{
public abstract void eat();
}
//定义并创建该父类的子类对象,并用多态的方式赋值给父类引用变量
Person p = new Person(){
public void eat() {
System.out.println( 我吃了 );
}
};
//调用eat方法
p.eat();
使用匿名对象的方式,将定义子类与创建子类对象两个步骤由一个格式一次完成,。虽然是两个步骤,但是两个步骤是连在一起完成的。
匿名内部类如果不定义变量引用,则也是匿名对象。
代码如下:
new Person(){
public void eat() {
System.out.println( 我吃了 );
}
}.eat();
示例代码
//抽象类
abstract class Person {
public abstract void show();
}
class PersonDemo {
public void method(Person p) {
p.show();
}
}
abstract class Person {
public abstract void show();
}
class PersonTest {
public static void main(String[] args) {
//如何调用PersonDemo中的method方法呢?
//使用普通方式
//PersonDemo pd = new PersonDemo();
//Person s = new Person();
//pd.method(s);
//使用匿名对象
//new PersonDemo().method(new Person());//里面传递的是一个抽象类的子类对象
/*
new Person(){
public void show(){
System.out.println("我就是叶良辰,我就是爱学习");
}
};
上面代码相当于 new Person();
*/
//使用匿名内部类怎么做?
PersonDemo pd = new PersonDemo();
pd.method( new Person(){
public void show(){
System.out.println("我就是叶良辰,我就是爱学习");
}
});//里面传递该类的子类匿名对象
}
}
内部类应用场景
方法参数是类类型
调用方式:直接创建类的对象
方法是参数是抽象类型
调用方式:
1. 创建该抽象类的子类对象
2. 使用匿名内部类实现
方法参数是接口类型
调用方式:
1. 创建该接口的实现类
2. 使用匿名内部类