【JavaSE】什么是抽象类?什么是内部类?以及它们的作用是什么?

news2024/12/23 20:47:27

 这篇文章我们主要学习的是两个知识点,可以来解决文章标题所提出来的三个问题。

 

目录

1.抽象类

1.1 抽象类概念

1.2 抽象类语法

1.3 抽象类特性

1.4 抽象类的作用

2.内部类

2.1 内部类的分类

2.2 实例内部类

2.3 静态内部类

2.4 匿名内部类

2.5 局部内部类


1.抽象类

在多态的学习中,我们曾写过这样的一段代码:

class Shape {
    //属性....
    public void draw() {
        System.out.println("画图形!");
    }
}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}
class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("▲");
    }
}

但是,我们会发现,在父类Shape中的draw()方法似乎不曾被使用过,我们可不可以不写draw()的实现部分呢?答案是可以的,抽象类将回来帮我们解决这个问题。


1.1 抽象类概念

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 比如:

说明:

  1. 矩形、三角形、圆形都是图形,因此和Shape类的特性应该是继承关系。
  2. 虽然图形图Shape中也存在draw的方法,但由于Shape类并不是具体的图形,因此其内部的draw方法实际是没有办法实现的。
  3. 由于Shape类没有办法描述一个具体的图形,导致其draw0方法无法具体实现,因此可以将Shape类设计为“抽象类"。

在打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由 Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个抽象方法(abstract method), 包含抽象方法的类我们称为抽象类(abstract class)

1.2 抽象类语法

Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。

对于上面的shape类,我们可以做出如下修改:

abstract class Shape {
    //属性....
    public abstract void draw();
}

这样,shape就是一个抽象类。

// 抽象类:被abstract修饰的类
abstract class Shape {
    // 抽象方法:被abstract修饰的方法,没有方法体
    abstract public void draw();
    abstract void calcArea();
    // 抽象类也是类,也可以增加普通方法和属性
    public double getArea() {
        return area;
    }
    protected double area; // 面积
}

 注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

1.3 抽象类特性

1. 抽象类不能直接实例化对象

2. 抽象方法不能是 private

3. 抽象方法不能被finalstatic修饰,因为抽象方法要被子类重写

4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰

5. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

6. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

1.4 抽象类的作用

抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。

我们可能会想, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

确实如此. 但是使用抽象类相当于多了一重编译器的校验。

使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成.。那么此时如果不小心误用成父类 了, 使用普通类编译器是不会报错的.。但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题。

很多语法存在的意义都是为了 "预防出错", 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不 就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们。充分利用编译器的校验, 在实际开发中是非常有意义的。

2.内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服 务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

【注意事项】

1. 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类

 2. 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件

2.1 内部类的分类

先来看下,内部类都可以在一个类的哪些位置进行定义

class OutClass {
    // 成员位置定义:未被static修饰 --->实例内部类
    public class InnerClass1 {
    }

    // 成员位置定义:被static修饰 ---> 静态内部类
    static class InnerClass2 {
    }

    public void method() {
        // 方法中也可以定义内部类 ---> 局部内部类:几乎不用
        class InnerClass5 {
        }
    }
}

 根据内部类定义的位置不同,一般可以分为以下几种形式:

  1. 成员内部类(普通内部类:未被static修饰的成员内部类 和 静态内部类:被static修饰的成员内部类)
  2. 局部内部类(不谈修饰符)、匿名内部类

注意:内部类其实日常开发中使用并不是非常多,大家在看一些库中的代码时候可能会遇到的比较多,日常开始中使用最多的是匿名内部类。

2.2 实例内部类

即未被static修饰的成员内部类。

实例内部类成员变量的定义:

在实例内部类中,不能定义static修饰的成员变量,除非加final修饰:

创建实例内部类对象时,先将外部类对象先创建出来,然后再创建实例内部类对象:

在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员

如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的

如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字

class OuterClass {
    public int data1 = 1;
    public static int data2 = 2;
    private int data3 = 3;

    class InnerClass {
        public int data1 = 111;

        private int data4 = 4;
        public static final int data5 = 5;  //data5为常量
        protected int data6 = 6;

        public void test() {
            System.out.println("InnerClass::test()");
            System.out.println(data1);//优先访问的是内部类自己的data1
            System.out.println(OuterClass.this.data1); //访问外部类的data1
            System.out.println(data2);
            System.out.println(data3);
            System.out.println(data4);
            System.out.println(data5);
            System.out.println(data6);
        }
    }

    public void test() {
        System.out.println("OuterClass::test()");
        System.out.println(data1); //访问外部类成员变量
        System.out.println(data2);
        System.out.println(data3);
        //外部类方法访问内部类需要先创建内部类对象来访问
        InnerClass innerClass = new InnerClass();
        System.out.println(innerClass.data1);
        System.out.println(innerClass.data4);
        System.out.println(innerClass.data6);
    }
}

public class Test3 {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.test();
        outerClass.test();
    }
}

输出结果:

 【注意事项】

1. 外部类中的任何成员都可以在实例内部类方法中直接访问。

2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的        约束。

3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成          员,必须:外部类名称.this.同名成员 来访问。

4. 实例内部类对象必须在先有外部类对象前提下才能创建。

5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用。

6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。

2.3 静态内部类

static修饰的内部成员类称为静态内部类。

创建静态内部类对象不需要先创建外部类对象:

静态内部类不能直接访问外部类非静态成员变量:

class OuterClass {
    public int data1 = 1;
    public static int data2 = 2;
    private int data3 = 3;

    public static class InnerClass {
        public int data4 = 4;
        public static int data5 = 5;
        private int data6 = 6;
        void test() {
            System.out.println("InnerClass::test()");
            OuterClass outerClass = new OuterClass();
            //System.out.println(data1); //编译报错
            System.out.println(outerClass.data1);
            System.out.println(data2);
            //System.out.println(data3); //编译报错
            System.out.println(outerClass.data3);            
            System.out.println(data5);
            System.out.println(data6);
        }
    }
}

【注意事项】

1. 在静态内部类中只能访问外部类中的静态成员

2. 创建静态内部类对象时,不需要先创建外部类对象

2.4 匿名内部类

学过接口的大家都知道,接口是不能实例化出对象的:

 但是我们可以通过接口创建出匿名内部类对象:

 输出结果:

2.5 局部内部类

定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。

public class Test3 {
    public static void func() {
        class AA {
            public int a = 1;
        }

        AA aa = new AA();
        System.out.println(aa.a);
    }

    public static void main(String[] args) {
        func();
    }
}

 

【注意事项】

1. 局部内部类只能在所定义的方法体内部使用

2. 不能被public、static等修饰符修饰

3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.class

4. 几乎不会使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/846055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

标准化归一化 batch norm, layer norm, group norm, instance norm

Layer Normalization - EXPLAINED (in Transformer Neural Networks) Layer Normalization - EXPLAINED (in Transformer Neural Networks) 0~4min:什么是multi-head attention 5~7min:layer norm图示 7~9min:公式举例layer norm 9:54-end:layer norm的代码示例 group n…

2023金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer?还是多少万年薪?其实这些都离不开日积月累的过程。 为此我特意整理出一份(超详细笔记/面试题)它几乎涵盖了所有的测试开发技术栈,非常珍贵,人手一份 肝完进大厂 妥妥的&#…

时序预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆神经网络时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆…

用队列实现栈——数据结构与算法

😶‍🌫️Take your time ! 😶‍🌫️ 💥个人主页:🔥🔥🔥大魔王🔥🔥🔥 💥代码仓库:🔥🔥魔…

【JS交互篇】DOM操作基础

一、DOM概述 1.1 什么是DOM DOM(Document Object Model)文档对象模型,用来表示和操作html或xml文档内容的基础API;当网页被加载时,浏览器会创建页面的文档对象模型DOM,而DOM模型被构造为对象的树(Dom Html Tree);DOM包…

真我V3 5G(RMX2200 RMX2201)解锁刷机全过程

安卓系统新Rom包为GSI,更具有通用性,可以比较放心刷。 原厂系统垃圾多、广告多,甚至热点功能不支持ipv6,严重偏离热点机的定位。 主要参考 https://www.bilibili.com/read/cv20730877/https://www.bilibili.com/read/cv2073087…

DSV-080-2NCP-N-MM两位两通常闭先导式电磁阀

该插装阀具有正向关断作用,设计用于负荷保持状态。 动作状况 断电时,DSV -080-2NCP-*-M*为止回阀,允许介质从1到2,同时阻止介质从2到1。 通电时,提升阀提升,打开从 2到1的通通道。 手动关闭选择:按下按…

分布式协议与算法——CAP理论、ACID理论、BASE理论

CAP理论 CAP理论,对分布式系统的特性做了高度抽象,比如抽象成了一致性、可用性和分区容错性,并对特性间的冲突(也就是CAP不可能三角)做了总结。 CAP三指标 CAP理论对分布式系统的特性做了高度抽象,形成了…

境内金融信息服务报备33家机构名单

2022年01月04日,国家互联网信息办公室关于发布第一批境内金融信息服务机构报备编号的公告,公开发布第一批20家金融信息服务机构的名称及报备编号。 2022年10月28日,国家互联网信息办公室关于发布第二批境内金融信息服务机构报备编号的公告&am…

Linux常用命令学习总结

Linux命令分类 1. Linux目录操作命令2. Linux文件名称3. Linux磁盘命令4. Linux进程与防火墙5. Linux用户与组的关系6. Linux权限操作(chmod命令)7. Linux中的文件类型文件的寻找 最近系统地学习下Linux命令的使用,因此作如下记录,以便随时复习和翻阅。 …

栈和队列的实现以及OJ题讲解

💓博主个人主页:不是笨小孩👀 ⏩专栏分类:数据结构与算法👀 刷题专栏👀 C语言👀 🚚代码仓库:笨小孩的代码库👀 ⏩社区:不是笨小孩👀 🌹欢迎大家三连关注&…

Android安卓实战项目(9)—漂亮的健身APP主页控件+开机动画+BMI计算(源码在文末)可用于比赛项目或者作业参考中

Android安卓实战项目(9)—漂亮的健身控件APP开机动画BMI计算(源码在文末🐕🐕🐕) 介绍: BMI(Body Mass Index,身体质量指数)是一种常用的健康指标…

Spring Cloud Alibaba (一)

1 微服务介绍 1.1 系统架构演变 随着互联网的发展,网站应用的规模也在不断的扩大,进而导致系统架构也在不断的进行变化。 从互联网早起到现在,系统架构大体经历了下面几个过程: 单体应用架构--->垂直应用架构--->分布 式架构--->S…

LeetCode 29题:两数相除

题目 给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.…

一生一芯1——windows与Ubuntu双系统安装

UltraISO下载 下载链接:https://pan.baidu.com/s/18ukDs6yL64qU6thYyZEo-Q?pwdo8he 提取码:o8he 一路傻瓜安装,安装后点击继续试用 Ubuntu系统下载 这里我使用的是官网的22.04版本,由于大于4G,无法上传至百度网盘…

【CSS】CSS 选择器

CSS 选择器 1.基础选择器 1.1 元素选择器 语法:标签名{...} 元素选择器会选中对应标签名的HTML元素,例如:p{...},div{...},span{...}等 1.2 类选择器 语法:.类名{...} 类选择器会选中class属性为指定…

老胡的周刊(第102期)

老胡的信息周刊[1],记录这周我看到的有价值的信息,主要针对计算机领域,内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 🎯 项目 ChatGPT-Shortcut[2] ChatGPT 快捷指令&…

MyBatis-Plus是什么以及特性[MyBatis-Plus系列] - 第481篇

​ 悟纤:师傅,宝宝不开心呢。 师傅:怎么不开心? 悟纤:感觉好多重复的代码来着。 师傅:是哪个部分重复的代码来着? 悟纤:就是对于一个model的增删改查部分。 师傅:那这…

周赛357(模拟、脑经急转弯、多源BFS+并查集、反悔贪心)

文章目录 周赛357[2810. 故障键盘](https://leetcode.cn/problems/faulty-keyboard/)模拟双端队列O(n) [2811. 判断是否能拆分数组](https://leetcode.cn/problems/check-if-it-is-possible-to-split-array/)脑经急转弯 [2812. 找出最安全路径](https://leetcode.cn/problems/f…

SpringBoot系列---【三种启动传参方式的区别】

三种启动传参方式的区别 1.三种方式分别是什么? idea中经常看到下面三种启动传参方式 优先级 Program arguments > VM options > Environment variable > 系统默认值 2.参数说明 2.1、VM options VM options其实就是我们在程序中需要的运行时环境变量,它需…