【Java之轨迹】第三章:面向对象总结(抽象类,接口,内部类)(最终)


◉ 抽象类

属于引用数据类型

抽象类:有 abstract 修饰,且必须有构造器(默认无参构造器)

抽象方法:有 abstract 修饰,且所属的类必须为抽象类,不能有方法体
— 定义:public abstract void test();

—— 特性:
抽象类既可以有抽象方法也可以有实例方法
甚至可以没有抽象方法

有得有失:

  • 得:得到了拥有抽象方法的能力
  • 失:失去了创建对象的能力(但依旧可以有构造器)

解析:

  • 如果可以创建对象,那么将会调用抽象方法,但抽象方法没有方法体不能被调用
  • 子类继承父类后,在调用构造器时必须先调用父类的构造器(默认调用无参构造器)所以父类必须有构造器,抽象类也一样

—— 注意点:

  • 如果一个类继承了抽象类,那么它必须重写该抽象类的所有抽象方法
  • 只要有一个抽象方法没有被重写,该类也必须成为抽象类(也说明了抽象类可以再被抽象类继承)
  • 子类重写父类的抽象方法时,abstract 修饰要去掉,最好加上@Override

◉ 接口

也属于引用数据类型
编译后生成的依然是 .class 字节码文件
— 定义:public interface Interface1

—— 特性:

  • 接口是一种更彻底的抽象,只能包含抽象方法,不能有方法体的出现
  • 一个接口可以继承多个接口,用 extends
  • 接口也可以被多实现,一个类可以同时实现多个接口,用 implement

如果继承的接口中有同名的抽象方法,那么会重叠在一起当作一个来处理,这也是接口可以多继承的原因。

—— 省略:
接口中的抽象方法都是公开的,作为规范来使用,使用其他修饰符就会报错
所以在接口中可以不写 public abstract ,会自动添加上的

接口中只能有常量,没有变量的存在
所以如果没有加上常量声明,会自动加上 public static final
如以下代码编译通过

public interface Interface1
{
	double PI = 3.1415926;
    void in1();
}

—— 注意:
实现接口时,所有实现的方法都必须是 public 修饰的
原因:接口中的方法默认为 public ,而重写方法时要求访问权限不能低于原方法,那么不低于 public 访问权限的就只有 public ,所以只能用 public 修饰。

public class Main implements Interface1
{
    @Override
    void in1() 
    {
    	
    }
}

在这里插入图片描述

—— 特别注意:
没有继承关系的接口可以互转,编译可以通过不会报错。
但运行阶段时,如果发现互转后两个接口真正所代表的没有继承关系,就会出现异常 ClassCastException
例子:

interface Inf1
{
    void in1();
}

interface Inf2
{
    void in2();
}

interface Inf3
{
    void in3();
}

public class Test2 implements Inf1, Inf2
{
    @Override
    public void in1() {
        System.out.println("in1");
    }

    @Override
    public void in2() {
        System.out.println("in2");
    }

    public static void main(String[] args) {

        Inf1 inf1 = new Test2();
        Inf2 inf2 = new Test2();

        inf1.in1();
        // 这个和多态一样,inf1 中并没有in2方法,所以即使 Test2 中有也会报错
        // inf1.in2();   
        inf2.in2();
        // inf2.in1();   同上

        // 接口互转 1
        // 这里 Inf2 和 Inf1 并没有继承关系,但依旧可以互换不报错
        Inf2 inf22 = (Inf2)inf1;
        // inf1 转化成 Inf2 之后就可以调用 in2 方法了
        // 这里运行时没有问题,因为 inf1 的类型为 Test2 ,是 Inf2 的子类
        // 有继承关系,所以可以正常运行
        inf22.in2();

        // 接口互转2
        Inf3 inf3 = new Inf3() {
            @Override
            public void in3() {
                System.out.println("in3");
            }
        };

        // 同上,没有继承关系但互转不报错
        Inf2 inf222 = (Inf2)inf3;
        // 这里就会出现异常了
        // 因为 inf3 的类型为 Inf3 和 Inf2 没有继承关系,运行时出现异常
        inf222.in2();
    }
}

总结:
接口没有继承关系仍旧可以互转,编译时不会报错。但真正运行时,必须要求被转化的接口和转化后的接口有继承关系,否则出现 ClassCastException 异常

从上面例子知道:
inf1 的类型是 Test2 , inf22 的类型是 Inf2 ,而 Test2 又是 Inf2 的子类,有继承关系,所以 inf1 可以通过转化变成 inf22,然后正常使用 Inf2 中的方法。

inf3 的类型是 Inf3 , inf2 的类型是 Inf2 ,两者并没有继承关系,所以将 inf3 强转成 inf222 就会出现异常

优化:
跟多态一样,可以使用 instanceof 对转化进行判断,避免程序运行过程中突然中断

修改这一段

Inf2 inf222 = (Inf2)inf3;
inf222.in2();
if(inf3 instanceof Inf2)
{
    Inf2 inf222 = (Inf2)inf3;
    inf222.in2();
}
else
{
    System.out.println("inf3 不能转化成 Inf2类型");
}
运行结果:inf3 不能转化成 Inf2类型
不会出现异常

—— 新增:
JDK1.8 之后
接口可以有 类方法 和 实例方法(必须用 public default 修饰)
如以下代码编译通过

public interface Interface1
{
	// 实例方法
    default void din1()
    {

    }

	// 类方法 / 静态方法
    static void din2()
    {

    }
}


◉ 内部类

在类里边定义类
而不是同一个 .java 文件的两个类,这两个类仅仅只是单独的两个类

作用: 提供了更安全的访问机制,内部类允许用 private 修饰,而类中的成员也可以用 private 修饰,更加安全

静态内部类

可以访问外部类的静态变量,不可以访问实例变量
继承静态内部类:直接 extends 外部类.内部类

实例内部类

特性:

  • 不能有静态方法和静态成员,但可以有常量,使用 static final 修饰
  • 可以访问外部类的静态成员,也可以访问实例成员
  • 因为实例内部类属于对象而不属于类,当对象被new出来很多份之后,实例内部类也相应地会有很多份,而静态只要求有一份,所以不允许有静态方法和静态变量出现在实例内部类。所以静态的东西都应该放在外部类。
  • 内部类和外部了有同名的变量,默认会访问内部类中的变量
    那么如何访问外部类的变量?
    解决:使用 外部类名称.this.变量名称 进行访问
  • 在继承实例内部类时,不能直接用 super 去构造内部类,因为不知道该内部类是属于哪个外部类对象的,所以应该先提供一个外部类对象,再指定由该外部类对象去创建一个实例内部类对象。
public class Main extends Test.Inner
{
    public Main(Test te)
    {
        te.super();
    }

    public static void main(String[] args)
    {
        Test te = new Test();
        Main main = new Main(te);
    }
}
public class Test
{
    public class Inner
    {
        
    }

}
  • 在创建实例内部类对象时,要先创建出外部类的实例,再由外部类去 new 一个实例内部类
Test te = new Test();
Test.Inner ti = te.new Inner();

局部内部类

存在于方法中的类,随方法的结束而结束
定义:

public void test()
{
    class T1
    {
        
    }
}

匿名内部类

局部内部类的一员

很多时候我们要使用一个抽象类或者一个接口,都得单独写一个类去实现它们,这样就会显得很繁琐
匿名内部类就是用来对付这个问题的
抽象类和接口不能直接获取对象的原因是含有未实现的方法,也就是说只要实现了就可供以使用。

匿名内部类就是不创建新的类而直接实现抽象方法,以接口为例用法如下:

public class Main
{
    public static void main(String[] args)
    {
        Interface1 inf = new Interface1() 
        {
            @Override
            public void in1() {
                
            }

            @Override
            public void in2() {

            }
        };
    }
}
public interface Interface1
{
    void in1();
    void in2();
}

◉ 总结

abstract 和 final 不能联合使用

abstract 和 final 在修饰类和方法的时候一定是互斥的
因为 abstract 的存在是为了派生子类,被子类继承和重写;
而 final 则强调该类或方法是最终的,不能再被继或者重写。

抽象类和接口的异同点

—— 相同点:
① 都不能被实例化

—— 不同点:
① 抽象类只能被单继承,而接口可以多继承
② 抽象类有构造器而接口没有
③ 抽象类可以有变量而接口中全部为常量
④ 接口需要用 implements 实现,而抽象类使用 extends 继承


public interface IceClean
{
    void getUp();
    void eat();
    void study();
    void releax();
    void sleep();
}

(寒冰小澈)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

寒冰小澈IceClean

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

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

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

打赏作者

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

抵扣说明:

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

余额充值