当前位置: 首页 > article >正文

【Java基础入门篇】三、面向对象和JVM底层分析(3)

Java基础入门篇


三、面向对象和JVM底层分析

3.5 抽象类/接口和外/内部类

(1)抽象类

**“抽象方法”**是使用abstract修饰的方法,没有方法体,只有声明,定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
**“抽象类”**是包含抽象方法的类,通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
抽象类的使用要点:

  1. 有抽象方法的类只能定义成抽象类
  2. 抽象类不能实例化(不能new)
  3. 抽象类可以包含属性、方法、构造方法,但是构造方法不能用来new实例,只能用来被子类调用
  4. 抽象类只能用来被继承
  5. 抽象方法必须被子类实现
package BasicJava.abstractClass;
/*
*   测试抽象类
 */
public abstract class Animal {
    int age;
    public abstract void rest();
    public abstract void run();
    public void shout(){
        System.out.println("Animal.shout");
    }
}

// 子类必须实现父类的抽象方法,否则会报错
class Dog extends Animal{
    @Override
    public void rest() {
    }

    @Override
    public void run() {
    }
}

class Cat extends Animal{
    @Override
    public void rest() {
    }

    @Override
    public void run() {
    }
}
(2)接口

**“接口”**是一组规范,是所有类都要遵守的,面相对象的精髓就是接口,接口是比抽象类还抽象的“抽象类”,可以更加规范对子类进行约束,接口全面地专业地实现了:规范和具体实现的分离,在接口中只能有抽象的方法,不可以包含普通的属性和方法。
“接口”和实现类不是父子关系,是实现规则的关系,是多个模块之间通信的标准,做系统往往就是“面相接口”的思想来设计
“接口”定义:

  1. 访问修饰符只能是public或者默认
  2. 接口名和类名采用相同的命名机制
  3. extends部分,接口可以多继承
  4. 接口中的属性只能是常量,即用public static final修饰,不写默认也是
  5. 接口中的方法只能是public abstract,省略也是

子类通过implements来实现接口中的规范,接口不能创建实例,但是可以用于声明引用变量类型。一个类实现类接口,必须实现接口中的所有方法,并且这些方法只能是public的。

  • JDK1.8(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
  • JDK1.8(含8)之后,接口中包含普通的静态方法、默认方法。

接口中定义静态方法和默认方法(JDK≥8),JDK8之前接口中只能有抽象方法
**默认方法:**使用default关键字,默认方法和抽象方法的区别是抽象方法必须被实现,默认方法不是,接口可以提供默认方法的实现,所有接口的实现类都可以得到默认方法,也可以重写
**静态方法:**在接口中定义静态方法,这个静态方法直接从属于接口(接口也是一种特殊的类),可以通过接口名调用。如果子类定义了相同名字的静态方法,那就是完全不同的方法,子类中定义的静态方法直接从属于子类,可以通过子类名直接调用
注意:默认方法相当于普通方法,可以调用静态方法,但是静态方法无法调用默认方法

package BasicJava.testInterface;

public interface TestDefault {
    void printInfo();

    // 定义默认方法
    default void moren(){
        System.out.println("TestDefault.moren");
        System.out.println("这是一个默认方法");
        // 默认方法可以调用静态方法
        testStatic();
    }

    // 定义静态方法
    static void testStatic(){
        System.out.println("TestDefault.testStatic");
    }

}

class Test01 implements TestDefault{
    @Override
    public void printInfo() {
        System.out.println("Test01.printInfo");
    }

    public static void testStatic(){
        System.out.println("Test01.testStatic");
    }
}
package BasicJava.testInterface;

// 飞行接口
public interface Volant {
    /*public static final*/ int Fly_HEIGHT = 100;
    /*public abstract*/ void fly();
}

// 善良接口
interface Honest{
    void helpOther();
}

class GoodMan implements Honest{
    // 实现接口的方法
    @Override
    public void helpOther() {
        System.out.println("扶老奶奶过马路");
    }
}

class Angel implements Volant{
    // 实现接口的方法
    @Override
    public void fly() {
        System.out.println("Angel.fly");
    }
}

class HonestBird implements Honest, Volant{

    @Override
    public void fly() {
        System.out.println("飞起来");
    }

    @Override
    public void helpOther() {
        System.out.println("日行一善");
    }
}

编写主函数测试接口:

package BasicJava.testInterface;

/*
*   测试接口
 */
public class Test {
    public static void main(String[] args) {
        Angel a = new Angel();
        a.fly();
        HonestBird hb = new HonestBird();
        hb.fly();
        hb.helpOther();
        // 访问接口的常量
        System.out.println(Volant.Fly_HEIGHT);

        // 实现一个向上转型,只能使用编译类型中有的方法
        Volant a2 = new HonestBird();
        a2.fly();
        // 通过强制类型转换为,向下转型,使用更多的方法
        HonestBird hb2 = (HonestBird)a2;
        hb2.helpOther();

        // 测试默认方法
        System.out.println("====测试默认方法===");
        Test01 td = new Test01();
        td.moren();

        // 测试静态方法
        System.out.println("====测试静态方法===");
        TestDefault.testStatic(); // 直接调用接口中的静态方法
        Test01.testStatic(); // 不是接口的静态方法实现,相当于一个全新的方法


    }

}

在这里插入图片描述

接口支持**“多继承”**,和类的继承类似,子接口会继承父接口的一切。

package BasicJava.testInterface;
/*
*   测试接口多继承
 */
public class TestMutipleInheritance {
    public static void main(String[] args) {
        C c = new CImp01();
        c.testA();
        c.testB();
        c.testC();
    }

}

interface A{
    void testA();
}

interface B{
    void testB();
}

// C接口同时继承A,B两个接口
interface C extends A, B{
    void testC();
}

// 实现接口C之后,需要把C继承的接口的抽象方法也实现
class CImp01 implements C{
    @Override
    public void testA() {
        System.out.println("CImp01.testA");
    }

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

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

在这里插入图片描述

(3)外/内部类

**内部类:**我们把一个类放在另一个类内部定义,称为内部类(inner class),分为:

  1. 成员内部类(静态内部类、非静态内部类)
  2. 匿名内部类
  3. 局部内部类

内部类:1.提供了更好的封装,只能让外部类直接访问,不允许同一个包中的其他类直接访问,2.内部类可以直接访问外部类的私有属性,内部类被当做其外部类的成员,但是外部类不能访问内部类的内部属性

  • 内部类只是**“编译”**时的一个概念,一旦变异成功,就会成为完全不同的两个类,对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。
  • 编译完成后会出现Outer.class 和 Outer.Inner.class两个类的字节码文件,所以内部类是相对独立的一种存在,其成员变量/方法名可以与外部类相同

非静态内部类:(外部类里使用非静态内部类和平时使用其他类没有什么不同):1.非静态内部类对象必须寄存在一个外部类对象里,因此,如果有一个非静态内部类对象,那么一定存在对应的外部类对象,非静态内部类对象单独属于外部类的某个对象, 2.非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员,3.非静态内部类不能有静态方法/属性/初始化块

静态内部类:,定义方式static class ClassName{//类体},1.静态内部类可以访问外部类的静态成员,不能访问外部类的普通成员,2.静态内部类看作是外部类的一个静态成员

在内部类中进行成员变量访问要点:1.内部类属性:this.变量名,2.外部类属性:外部类名.this.变量名

package BasicJava.TestInnerClass;
/*
*   测试内部类的用法
 */
public class Outer {
    private int age = 10;
    private static int b = 100;
    public void show(){
        System.out.println("Outer.show");
        System.out.println(age);
    }

    // 定义非静态内部类
    public class Inner{
        int age = 20;
        public void show(){
            System.out.println("Inner.show");
            System.out.println(age);
            // 访问外部类属性和方法
            System.out.println(Outer.this.age);
            Outer.this.show();
        }
    }

    // 定义静态内部类
    static class Inner2{
        public void test(){
            System.out.println(b); // 调用外部类的静态成员
            // System.out.println(age); // 不能调用外部类的非静态成员
        }
    }


}

外部类定义内部类new Inner(),外部类以外的地方使用非静态内部类:Outer.Inner n = new Outer().new Inner();
外部类以外的地方使用静态内部类:Outer.Inner n = new Outer.Inner();

package BasicJava.TestInnerClass;
/*
*   测试创建内部类对象
 */
public class TestInnerClass {
    public static void main(String[] args) {
        // 创建同一个包下的外部类对象
        Outer o = new Outer();
        o.show();
        // 创建同一个包下的非静态内部类对象
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
		// 创建同一个包下的静态内部类对象
        Outer.Inner2 oi2 = new Outer.Inner2();
        oi2.test();
    }
}

在这里插入图片描述

匿名内部类:适合那种只需要使用一次的类,比如:键盘监听操作等,在“安卓开发”,awt、swing开发(窗口GUI开发)中常见,语法:new 父类构造器(实参类表) \实现接口 (){//类体}

package BasicJava.TestInnerClass;
/*
*   测试匿名内部类
 */
public class TestAnonymousClass {

    public void test1(A a){
        a.run();
    }

    public static void main(String[] args) {
        TestAnonymousClass t1 = new TestAnonymousClass();
        // 原来的做法,首先额外定义一个类实现接口,再调用
        t1.test1(new AImpl());
        //匿名类的用法
        t1.test1(new A() {
            // 在这里单独实现了一次接口内容
            @Override
            public void run() {
                System.out.println("TestAnonymousClass.run");
            }
        });

        t1.test1(new A() {
            // 在这里单独实现了一次接口内容
            @Override
            public void run() {
                System.out.println("第二次实现匿名内部类");
            }
        });

    }
}

//定义一个类实现接口
class AImpl implements A{
    @Override
    public void run() {
        System.out.println("跑!");
    }
}
// 定一个接口
interface A{
    void run();
}

在这里插入图片描述

**局部内部类:**定义在方法内部,作用域只限于本方法,成为局部内部类,在实际开发中应用较少

package BasicJava.TestInnerClass;
/*
*   测试局部内部类
 */
public class TestLocalClass {

    public void show(){
        // 局部内部类作用域仅限于该方法
        class Inner3{
            public void fun(){
                System.out.println("Hello world!");
            }
        }

        new Inner3().fun();
    }
    public static void main(String[] args) {
        new TestLocalClass().show();
    }

}

在这里插入图片描述


http://www.kler.cn/a/420377.html

相关文章:

  • Linux 各个目录作用
  • MAUI APP开发蓝牙协议的经验分享:与跳绳设备对接
  • 在Scala中Array不可变的学习
  • 第1章:CSS简介 --[CSS零基础入门]
  • API 数据接口使用与安全指南
  • xiaolin coding 图解 MySQL笔记——事务篇
  • 【设计模式系列】工厂方法模式(二十一)
  • CSS函数
  • 为什么混合精度训练中优化器参数仍然以 FP32 存储?LLaMA 2 7B 模型在混合精度下的显存需求
  • react 父子组件通信
  • 【Qt】QDateTimeEdit控件实现清空(不保留默认时间/最小时间)
  • Pytorch使用手册- TorchVision目标检测微调Tutorial的使用指南(专题十二)
  • bash命令缓存导致命令执行失败的问题
  • 插入数据如何确保redis与数据库同步 详解
  • 单链表---链表分割
  • 基于米尔全志T527开发板的FacenetPytorch人脸识别方案
  • 【C++】深入解析 using namespace std 语句
  • npm error code ETIMEDOUT 简单排查
  • 双向长短期记忆(Bi-LSTM)神经网络介绍
  • Linux - 前端程序员常用的 Linux 命令
  • LearnOpenGL学习(光照 -- 投光物,多光源)
  • 在云上怎么样让环境更加安全?
  • SQLAlchemy
  • Spring,SpringMVC,SpringBoot,SpringCloud有什么区别和联系?
  • 汽车操作系统详解
  • dhcpd服务器的配置与管理(超详细!!!)