专栏:《Java面向对象程序设计》学习笔记
问题是依据考纲整理的,稍微做了一些补充。大部分答案由GPT生成,部分内容摘选自书本。
内容太多了,目前懒得浓缩精炼了,以后再说吧。
如果有大佬可以帮忙精简一些文字、补充一些内容,那我将不胜感激。
考纲
1、算法语言基础知识
(1)程序设计语言发展历史、背景和特点;
(2)Java面向对象编程特点、面向对象程序设计(OOP)的基本概念,Java语言编程规范;
(3)结构化程序设计与面向对象程序设计;
程序设计语言发展历史、背景和特点
背景
- 平台无关性:Java最重要的特点之一是它的跨平台性。Java源代码编译成字节码,然后在Java虚拟机(JVM)上运行,使得Java程序可以在不同的操作系统和硬件平台上无需修改而运行。
- 面向对象:Java是一门面向对象的编程语言,支持封装、继承和多态等面向对象的概念,使得代码结构清晰、易于维护和扩展。
- 垃圾回收:Java通过内置的垃圾回收器自动管理内存,开发人员不需要手动分配和释放内存,减少了内存泄漏和悬空指针等问题。
- 安全性:Java具有安全性的特点,通过提供安全管理器和字节码验证等机制,防止恶意代码对系统造成损害。
特点
- 简单易学:Java借鉴了C/C++的语法风格,同时去除了一些复杂和容易出错的特性,使得Java更易学和使用。
- 强大的标准库:Java提供了丰富的标准类库,包括输入/输出、网络、数据库、图形界面等功能,使得开发人员可以快速构建各种应用程序。
- 多线程支持:Java内置了对多线程编程的支持,开发人员可以方便地创建和管理多线程应用程序,充分利用多核处理器的并行计算能力。
- 开放源代码:Java的一部分实现是开源的,如OpenJDK,使得开发人员可以自由访问和修改Java的源代码。
- 广泛应用领域:由于其跨平台性和丰富的特性,Java被广泛应用于企业级应用开发、移动应用开发(Android平台)、大数据处理、云计算等领域。
Java面向对象编程特点、面向对象程序设计(OOP)的基本概念,Java语言编程规范
编程特点
- 封装(Encapsulation):将数据和操作封装在对象中,通过定义公共接口来访问和操作对象的内部状态,隐藏对象的实现细节,提高安全性和模块化。
- 继承(Inheritance):通过继承机制,可以创建一个类(子类)从另一个类(父类)继承属性和方法,实现代码重用和层次化设计。
- 多态(Polymorphism):多态允许使用统一的接口来处理不同类型的对象,可以根据对象的实际类型来调用相应的方法,提高代码的灵活性和可扩展性。
- 抽象(Abstraction):抽象是隐藏对象的复杂性,只展现必要的信息和功能,通过定义抽象类和接口来实现,提供了一种设计和组织复杂系统的方式。
- 类和对象:Java是一种基于类的编程语言,所有的代码都是在类中编写的。类是对象的蓝图,用于定义对象的属性和行为。对象是类的实例,具有独立的状态和行为。
- 消息传递:在面向对象编程中,对象之间通过发送消息来进行交互。对象可以调用其他对象的方法,传递参数,并接收返回值。
- 设计模式支持:Java提供了丰富的设计模式支持,如单例模式、工厂模式、观察者模式等,可以帮助开发人员解决常见的设计问题,并提高代码的可重用性和可维护性。
面向对象程序设计(OOP)的基本概念
- 类(Class):定义对象的属性和行为的模板或蓝图。
- 对象(Object):类的实例,具有独立的状态和行为。
- 属性(Attribute):对象的特征或状态。
- 方法(Method):对象的行为或操作。
- 封装(Encapsulation):将数据和操作封装在对象中,对外部隐藏实现细节。
- 继承(Inheritance):通过继承机制,子类可以继承父类的属性和方法。
- 多态(Polymorphism):同一种类型的对象可以具有不同的形态,允许使用统一的接口来处理不同类型的对象。
- 抽象(Abstraction):隐藏对象的复杂性,只展现必要的信息和功能。
- 接口(Interface):定义了一组方法的集合,类可以实现接口来获得相应的行为。
面向对象的三大特点
封装性
继承性
- 子类既继承了父类所具有的数据和数据上的操作,同时又可以增添子类独有的数据和数据上的操作。
多态性
Java语言编程规范
- 命名规范:使用有意义的、清晰的名称来命名类、方法、变量和常量,采用驼峰命名法。
- 缩进和空格:使用合适的缩进和空格来展示代码的层次结构,提高可读性。
- 注释规范:为代码添加适当的注释,解释代码的用途、算法、参数等,方便他人理解和维护代码。
- 包和导入规范:使用有意义的包名来组织代码,遵循包名的命名规范,并只导入需要的类。
- 类和方法规范:类名使用名词或名词短语,方法名使用动词或动词短语,遵循方法的命名规范。
- 异常处理规范:Java语言编程规范(续):
- 异常处理规范:适当处理异常,使用try-catch块捕获异常,并根据异常类型做出相应的处理或日志记录。
- 常量规范:使用final关键字将常量定义为不可修改的,并使用大写字母和下划线分隔命名。
- 源文件规范:每个源文件只包含一个公共类,并将文件名与公共类名相匹配。
- 代码格式化规范:统一代码的格式化风格,如缩进、空行、括号使用等。
- 编码规范:遵循Java语言的编码规范,如合理使用变量、避免魔法数值、避免过长的方法等。
结构化程序设计与面向对象程序设计
结构化程序设计和面向对象程序设计是两种不同的编程范式。
结构化程序设计:
结构化程序设计是一种以结构化的方式组织代码的编程方法。它主要关注程序的逻辑结构,通过使用顺序、选择和循环等结构化语句来组织代码。结构化程序设计的主要思想是将大型的复杂问题分解为小的、可管理的模块,每个模块都有清晰的输入、处理和输出。它强调模块化、可读性、可维护性和可测试性。
面向对象程序设计(OOP):
面向对象程序设计是一种以对象的方式组织代码的编程方法。它将程序看作是一组相互作用的对象,每个对象都有自己的状态(属性)和行为(方法)。面向对象程序设计的核心概念是封装、继承和多态。封装将数据和方法封装在对象内部,隐藏内部实现细节;继承允许创建一个类从另一个类继承属性和方法;多态允许使用统一的接口来处理不同类型的对象。面向对象程序设计强调对象的抽象、封装、继承和多态,以及对象之间的交互和消息传递。
对比:
结构化程序设计和面向对象程序设计有以下区别:
- 组织方式:结构化程序设计以过程为中心,通过顺序、选择和循环等结构化语句组织代码;面向对象程序设计以对象为中心,通过封装、继承和多态等机制组织代码。
- 抽象方式:结构化程序设计通过模块化的方式将问题分解为小的可管理的模块;面向对象程序设计通过对象的抽象将问题分解为对象的属性和行为。
- 代码复用:结构化程序设计通过函数的方式实现代码的复用;面向对象程序设计通过继承和多态等机制实现代码的复用。
- 设计思想:结构化程序设计强调逻辑结构和控制流程的组织;面向对象程序设计强调对象的抽象、封装和交互。
选择使用结构化程序设计还是面向对象程序设计取决于具体的应用场景和需求。一般而言,结构化程序设计适用于简单的、较小规模的程序,而面向对象程序设计适用于复杂的、大规模的程序,特别是需要处理对象之间复杂的交互和关系的情况。许多现代编程语言(如Java、C++)支持同时使用结构化程序设计和面向对象程序设计的特性,开发人员可以根据实际需要选择合适的编程方法。
考纲
2、面向对象程序设计基础
(1)面向对象程序设计的概念;
(2)类的定义;
(3)对象及引用;
(4)继承与多态;
(5)抽象与接口;
(6)内部类与匿名类;
(7)自动装箱拆箱;
(8)异常处理与异常类;
面向对象程序设计基础
面向对象程序设计的概念:
面向对象程序设计(Object-Oriented Programming,OOP)是一种编程范式,它将程序组织为对象的集合,对象通过相互发送消息来进行通信和交互。面向对象的编程强调数据和操作的封装、继承和多态等特性,以提高代码的可重用性、可维护性和灵活性。
类的定义:
类是面向对象程序设计的基本构建单元,它是一种用户自定义的数据类型。类定义了对象的属性(成员变量)和行为(方法)。可以通过关键字class来定义一个类,类可以包含构造方法、成员方法和静态方法等。
对象及引用:
对象是类的实例化结果,是具体的数据实体。在Java中,通过使用new关键字来创建对象并分配内存空间。对象可以通过引用来访问和操作,引用是指向对象内存地址的变量。可以使用类的构造方法将对象赋值给引用变量。
继承与多态:
继承是面向对象的重要特性,它允许一个类继承另一个类的属性和方法。通过继承,子类可以重用父类的代码,并可以添加自己的特定属性和方法。多态是指同一种类型的对象在不同的情境下表现出不同的行为。通过多态性,可以使用父类类型的引用来引用子类对象,并根据实际引用对象的类型来调用相应的方法。
抽象与接口:
抽象是一种将类的共同特征提取出来形成抽象类或接口的过程。抽象类是不能被实例化的类,它定义了一组抽象方法和具体方法,子类需要实现抽象方法。接口是一种完全抽象的类,它只包含抽象方法和常量的定义,没有具体方法的实现。类可以实现一个或多个接口,实现接口中定义的方法。
内部类与匿名类:
内部类是定义在其他类内部的类。它可以访问外部类的成员,并提供了更好的封装性和组织性。内部类可以分为成员内部类、局部内部类、匿名内部类等。匿名类是一种没有显式定义类名的类,通常用于创建只需使用一次的简单类。
自动装箱拆箱:
自动装箱(Autoboxing)是指将基本数据类型自动转换为对应的包装类对象,而自动拆箱(Unboxing)是指将包装类对象自动转换为对应的基本数据类型。Java中的基本数据类型和对应的包装类可以自动进行装箱和拆箱操作。
异常处理与异常类:
异常处理是Java中处理程序中出现异常的机制。在运行过程中,如果出现异常,可以使用try-catch语句块来捕获和处理异常。异常类是Java中用于表示异常的类,异常类形成了一个异常类层次结构,可以根据具体的异常类型来捕获和处理异常。
考纲
4、面向对象编程
(1)面向对象设计的基本原则;
(2)类与类关系、UML类图;
(3)几种常见的设计模式及应用:单例模式、工厂方法、抽象工厂、原型;
面向对象编程
面向对象设计的基本原则:
- 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起它变化的原因,即一个类只应该拥有一个责任。
- 开放封闭原则(Open-Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即通过扩展已有的实体,来实现新的功能,而不是修改已有的代码。
- 里氏替换原则(Liskov Substitution Principle,LSP):子类对象应该能够替换父类对象并且不影响程序的正确性。即子类应该能够使用父类的接口,而不需要改变原有代码逻辑。
- 接口隔离原则(Interface Segregation Principle,ISP):使用多个专门的接口,而不是使用单一的总接口。客户端不应该依赖它不需要的接口。
- 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现细节,具体实现细节应该依赖于抽象。
类与类关系:
- 关联关系(Association):表示类之间的关联关系,一个类对象可以与另一个类对象相互引用。
- 继承关系(Inheritance):表示类之间的继承关系,一个类可以继承另一个类的属性和方法。
- 实现关系(Implementation):表示类与接口之间的实现关系,一个类可以实现一个或多个接口。
- 聚合关系(Aggregation):表示整体与部分之间的关系,即一个类对象包含另一个类对象,但两者之间可以独立存在。
- 组合关系(Composition):表示整体与部分之间的关系,即一个类对象包含另一个类对象,且两者的生命周期相互依赖,不能独立存在。
UML类图:
UML类图是一种用于描述类与类之间关系的图形化表示方法,可以通过类图清晰地展示类之间的关联、继承、实现、聚合和组合关系。
类的 UML 图长方形垂直地分为三层。
类与类之间关系
-
接口 (lnterface)
-
泛化关系 (Generalizationl)
继承关系 实线 空心三角形
-
关联关系 (Association)
组合关系 实线 箭头
-
依赖关系 (Dependency)
弱组合关系 虚线 箭头
-
实现关系 (ReaIization)
实现接口 虚线 空心三角形
几种常见的设计模式及应用:
- 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。常用于需要共享资源的情况,如数据库连接、线程池等。
- 工厂方法(Factory Method):定义一个创建对象的接口,但将具体的对象创建延迟到子类中。通过工厂方法,可以实现对象的多态性和封装性,客户端可以通过工厂方法获取所需对象而不需要了解具体的实现细节。
- 抽象工厂(Abstract Factory):提供一个用于创建一系列相关或相互依赖对象的接口,而无需指定具体的类。抽象工厂模式可以用于创建一组具有共同主题的产品,使得客户端代码与具体产品的实现解耦。
- 原型(Prototype):通过复制现有对象来创建新对象。可以用于创建成本较大的对象,避免重复初始化等操作,提高对象的创建效率。
考纲
6、多线程
(1)多线程的概念,Java中的线程;
多线程的概念
多线程是指在一个程序中同时执行多个线程,每个线程可以独立执行不同的任务或操作。线程是操作系统中最小的执行单元,它由线程控制块(Thread Control Block)和线程栈(Thread Stack)组成。
Java中的线程
在Java中,线程是通过java.lang.Thread
类表示的。Java提供了多线程编程的支持,使得开发者可以在程序中创建和管理多个线程。
考纲
9、反射及JAVA虚拟机基本原理
(1)反射概念、作用;
(2)反射机制的应用、应用场景;
(3)垃圾回收器与内存分配策略;
(4)虚拟机类加载机制;
反射
概念:
反射是指在运行时动态地获取和操作类的信息的能力。Java中的反射机制允许程序在运行时获取类的信息(如类名、方法、属性等),并能够动态地创建对象、调用方法、访问和修改属性等。反射提供了一种灵活的方式来操作类和对象,使得程序可以在运行时根据需要动态地加载和使用类。
作用:
- 动态地创建对象:通过反射可以在运行时动态地创建类的实例,而不需要提前知道类的类型。
- 动态地调用方法:可以通过反射动态地调用类的方法,包括私有方法。
- 动态地访问和修改属性:可以通过反射访问和修改类的私有字段。
- 获取类的信息:可以在运行时获取类的各种信息,如类名、父类、接口、注解等。
反射机制的应用、应用场景
- 框架和库的开发:许多框架和库(如Spring、Hibernate)使用反射机制来实现灵活的、可扩展的功能,例如依赖注入、对象映射等。
- 动态加载类:通过反射可以在运行时动态地加载并使用类,例如根据配置文件或用户输入的类名来加载相应的类。
- 调试和测试工具:反射可以在调试和测试工具中使用,例如通过反射获取和修改对象的状态,调用私有方法等。
- 序列化和反序列化:反射被广泛用于实现对象的序列化和反序列化,例如Java的ObjectInputStream和ObjectOutputStream类。
垃圾回收器与内存分配策略
垃圾回收器(Garbage Collector)是Java虚拟机的一部分,负责自动管理内存的分配和释放。垃圾回收器的主要作用是在程序运行过程中自动识别和回收不再使用的对象,释放其占用的内存空间,以避免内存泄漏和内存溢出的问题。
在Java中,内存分配策略采用的是基于分代的垃圾回收算法。Java堆内存被划分为不同的区域,主要包括新生代(Young Generation)和老年代(Old Generation)。新生代又分为Eden空间和两个Survivor空间。一般情况下,新创建的对象首先被分配到Eden空间,当Eden空间满时,会触发Minor GC,将存活的对象复制到其中一个Survivor空间,同时清空Eden空间和另一个Survivor空间。当某个Survivor空间满时,会将其中的存活对象复制到老年代。当老年代的空间不足时,会触发Major GC(Full GC),对整个堆进行垃圾回收。
垃圾回收器的具体实现有多种算法,如标记-清除算法、复制算法、标记-整理算法等。不同的垃圾回收器可以根据应用场景和需求选择不同的垃圾回收算法和策略。
Java虚拟机基本原理
Java虚拟机(JVM)是Java程序的运行环境,它是一个虚拟的计算机,负责执行Java字节码。
- 类加载:JVM通过类加载器将Java字节码文件加载到内存中。类加载器负责查找字节码文件,并将其转换为JVM能够理解的结构,形成Java类的定义。类加载器按照双亲委派模型进行加载,从上到下依次检查父类加载器是否已加载该类,如果没有,则由当前类加载器加载。
- 字节码解释器:JVM内置了一个字节码解释器,它将加载的字节码逐条解释执行。字节码解释器逐条读取字节码指令,并根据指令执行相应的操作。
- 即时编译器(Just-In-Time Compiler,JIT):JVM还包含即时编译器,它将热点代码(频繁执行的代码)编译成本地机器码,以提高执行效率。即时编译器根据运行时的信息对字节码进行优化,并生成等效的本地机器码,以替代解释执行的字节码。
- 内存管理:JVM负责内存的分配和回收。它将内存划分为不同的区域,包括堆、栈、方法区等。堆用于存储对象实例,栈用于存储方法调用和局部变量,方法区用于存储类的结构信息。JVM的垃圾回收器负责自动回收不再使用的对象,释放内存空间。
虚拟机类加载机制
Java虚拟机的类加载机制负责将字节码文件加载到内存中,并将其转换为可执行的Java类。
- 类加载过程:类加载是JVM将字节码加载到内存的过程。类加载器按照以下顺序进行加载:首先通过Bootstrap ClassLoader加载Java核心类库,然后通过Extension ClassLoader加载扩展类库,最后通过Application ClassLoader加载应用程序的类。每个类加载器都有自己的命名空间,相同的类被不同的类加载器加载后会形成不同的类定义。
- 类加载器:类加载器负责查找并加载类的字节码文件。JVM提供了三种内置的类加载器:Bootstrap ClassLoader(引导类加载器)、Extension ClassLoader(扩展类加载器)和Application ClassLoader(应用程序类加载器)。此外,用户还可以自定义类加载器。
- 双亲委派模型:类加载器采用双亲委派模型,即在加载类时,先委派给父类加载器进行加载。这样可以避免重复加载和安全性问题。只有当父类加载器无法加载时,才由当前类加载器进行加载。
类加载机制的优势在于提供了类的隔离性和安全性。不同的类加载器加载的类彼此隔离,相同的类被不同的类加载器加载后形成的类定义是不兼容的,从而保证了类的安全性和版本的一致性。
补充
结构化程序设计定义及其优缺点
结构化程序设计(Structured Programming)是一种编程范式,通过使用顺序、选择和循环等结构化控制语句,以及模块化的程序组织方式,来提高程序的可读性、可维护性和可靠性。
结构化程序设计的主要特点包括:
- 顺序结构(Sequence):按照语句的顺序执行,一条接一条执行,没有跳转和分支。
- 选择结构(Selection):使用条件语句(如if语句)根据条件的真假选择执行不同的代码块。
- 循环结构(Iteration):使用循环语句(如for循环、while循环)来重复执行一段代码,直到满足退出条件。
结构化程序设计的优点包括:
- 可读性强:结构化程序设计通过使用清晰的结构和良好的缩进,使程序的逻辑更易于理解和阅读。程序员可以快速找到代码块和控制流程,减少了理解代码的困难。
- 可维护性高:结构化程序设计将程序划分为模块,每个模块具有清晰的功能和接口,使得程序的维护和修改更加方便。当需要修改程序时,只需关注特定的模块,而不需要修改整个程序。
- 容易调试:结构化程序设计使用顺序、选择和循环等结构化控制语句,减少了程序中的跳转和分支,使得程序的调试更加直观和容易。可以逐步执行代码块,并通过逐步跟踪程序的执行路径来定位错误。
- 可靠性高:结构化程序设计通过减少复杂的控制流程和减少错误的机会,提高了程序的可靠性。结构化程序设计的代码结构清晰,易于测试和验证,减少了潜在的错误和缺陷。
然而,结构化程序设计也有一些缺点:
- 缺乏灵活性:结构化程序设计的控制流程是通过顺序、选择和循环等结构化语句来定义的,有时可能无法灵活应对一些复杂的逻辑和场景,导致代码的复杂性增加。
- 可读性受限:尽管结构化程序设计提供了一种清晰的代码结构,但对于一些复杂的逻辑,可能需要嵌套多个选择和循环结构,导致代码的可读性下降。
总的来说,结构化程序设计是一种强调清晰结构和模块化的编程范式,它提供了一种可读性强、可维护性高和可靠性强的编程方式,适用于大多数软件开发场景。然而,在某些情况下,其他编程范式(如面向对象编程)可能更适合处理复杂的逻辑和需求。
数组和链表处理数据时的优缺点
数组和链表是常见的数据结构,用于处理数据。它们在以下方面具有不同的优缺点:
数组的优点:
- 随机访问:通过索引可以直接访问数组中的任意元素,时间复杂度为O(1)。这使得数组在需要频繁访问元素的情况下效率较高。
- 内存连续:数组的元素在内存中是连续存储的,这样可以利用局部性原理,提高访问效率。
- 缓存友好:由于数组元素在内存中连续存储,可以更好地利用CPU缓存,提高访问速度。
数组的缺点:
- 大小固定:数组的大小在创建时就需要确定,并且无法动态改变。如果需要存储的元素数量超过了数组的大小,就需要重新创建一个更大的数组并复制数据,这会导致时间和空间的额外开销。
- 插入和删除操作低效:在数组中插入和删除元素时,需要移动其他元素来保持连续性,这可能需要较大的开销。插入和删除操作的时间复杂度为O(n)。
链表的优点:
- 大小可变:链表的大小可以动态地增加或减少,不需要预先确定大小。这使得链表更适用于需要频繁插入和删除元素的场景。
- 插入和删除操作高效:在链表中插入和删除元素只需要调整节点的指针,不需要移动其他元素。这使得插入和删除操作的时间复杂度为O(1)。
- 空间灵活:链表的节点可以在内存中分散存储,不要求连续的内存空间。
链表的缺点:
- 顺序访问:链表中的元素是通过指针连接的,需要按顺序遍历链表才能访问特定位置的元素。这使得随机访问元素的效率较低,时间复杂度为O(n)。
- 非缓存友好:链表的节点在内存中分散存储,不利于CPU缓存的利用,导致访问效率较低。
- 额外的空间开销:链表中每个节点需要额外的指针来连接下一个节点,这增加了存储开销。
综上所述,数组适用于需要频繁随机访问元素的场景,而链表适用于需要频繁插入和删除元素的场景。选择使用哪种数据结构取决于具体的需求和操作类型。有时也可以结合两者的优点,例如使用链表实现动态数组(如Java中的ArrayList),以在某种程度上兼顾随机访问和插入/删除的效率。
访问权限
|
范围 |
private |
default |
protected |
public |
1 |
同一包中的同一类 |
✔ |
✔ |
✔ |
✔ |
2 |
同一包中的不同类 |
|
✔ |
✔ |
✔ |
3 |
不同包中的子类 |
|
|
✔ |
✔ |
4 |
不同包中的非子类 |
|
|
|
✔ |
四种访问权限范围的大小从小到大:private < default(默认访问权限) < protected < public
在子类中访问父类中的变量时需要用super
private和public公有/私有类型成员变量之间的联系和区别
在面向对象编程中,“private”(私有)和"public"(公有)是访问控制修饰符,用于定义类的成员变量和方法的可访问性。它们之间有以下联系和区别:
联系:
- "private"和"public"都用于限制对类的成员的访问权限。
- 无论是私有类型的成员变量还是公有类型的成员变量,它们都属于类的成员,可以在类的内部进行访问和操作。
区别:
- 私有类型成员变量(private):私有类型的成员变量只能在其所属的类内部访问和操作,对外部代码不可见。这意味着其他类无法直接访问或修改私有类型的成员变量。通常,可以通过提供公有的访问方法(如getters和setters)来间接访问和修改私有成员变量。
- 公有类型成员变量(public):公有类型的成员变量可以被所有类的对象直接访问和修改。这意味着其他类可以直接引用和修改公有类型的成员变量。公有类型的成员变量在类的外部可见,可以通过对象访问或修改。
总结:
- 私有类型成员变量提供了更严格的访问控制,封装了类的内部实现细节,对外部代码隐藏了数据,提供了更好的封装性和安全性。
- 公有类型成员变量提供了更广泛的访问权限,使得类的成员可以在类的外部直接访问,但也增加了类的成员被意外修改的风险。
- 通常情况下,建议将类的成员变量定义为私有类型,并提供公有的访问方法来控制和管理对成员变量的访问。这遵循了面向对象编程中的封装原则,可以更好地控制和保护类的内部状态。
抽象类和接口的定义以及区别
抽象类和接口是面向对象编程中两种常见的概念,用于实现抽象和多态性。它们的定义和区别如下:
抽象类(Abstract Class):
- 抽象类是一个不能被实例化的类,它只能被继承。
- 抽象类可以包含抽象方法(没有具体实现的方法)和非抽象方法(有具体实现的方法)。
- 子类继承抽象类时,必须实现(重写)父类中的所有抽象方法,除非子类本身也是抽象类。
- 抽象类可以包含成员变量、构造方法和普通方法。
- 抽象类用于定义一组相关类的通用行为和属性,并为子类提供一种共享的基础实现。
接口(Interface):
- 接口是一种纯抽象的类,它只包含抽象方法和常量字段。
- 接口中的方法没有具体的实现,只有方法的签名(方法名、参数列表和返回类型)。
- 类可以实现一个或多个接口,通过实现接口来定义自己的行为。
- 实现接口的类必须提供接口中定义的所有方法的具体实现。
- 接口可以用于定义多个类之间的共同契约,实现类可以属于不同的继承体系,提供了更大的灵活性。
区别:
- 抽象类可以包含非抽象方法和成员变量,而接口只能包含抽象方法和常量字段。
- 类可以实现多个接口,但只能继承一个抽象类。
- 子类继承抽象类时,必须实现父类中的抽象方法,而实现接口的类必须提供接口中所有方法的具体实现。
- 抽象类用于定义一组相关类的通用行为和属性,而接口用于定义类之间的契约和行为规范。
- 抽象类提供了一种共享的基础实现,而接口强调了一种规范和约束。
通常情况下,当需要定义一组相关的类,并为它们提供一种基础的实现时,可以使用抽象类。而当需要定义类之间的契约和行为规范,以及实现类可以属于不同的继承体系时,可以使用接口。在实际设计中,抽象类和接口经常一起使用,以实现更灵活和可扩展的代码结构。
设计模式,工厂模式,简单工厂及优缺点
设计模式是在软件设计中常见的可复用解决方案,用于解决特定问题或实现特定功能。其中,工厂模式是一种创建型设计模式,它提供了一种将对象的实例化过程封装在一个单独的类中的方式。简单工厂是工厂模式的一种变体。下面是对这些概念的详细解释以及它们的优缺点:
工厂模式(Factory Pattern):
- 工厂模式是通过定义一个工厂类来创建对象,而不是直接在客户端代码中使用
new
关键字实例化对象。
- 工厂类根据客户端的请求,返回适当的对象实例。
- 工厂模式将对象的创建和使用解耦,使代码更加灵活、可扩展和可维护。
简单工厂(Simple Factory):
- 简单工厂是工厂模式的一种简化形式,它在一个工厂类中提供一个静态方法或函数,根据传入的参数来创建相应的对象。
- 简单工厂隐藏了对象的创建逻辑,客户端只需通过工厂方法获取所需的对象,而无需直接实例化对象。
优点:
- 封装对象的创建过程:工厂模式将对象的创建过程封装在一个单独的类中,客户端代码只需关注所需的对象的获取,而无需关心具体的创建细节。
- 降低耦合性:工厂模式将客户端代码与具体的产品类解耦,使得客户端代码可以与抽象接口进行交互,而不是与具体的实现类进行交互,从而降低了耦合性。
- 可扩展性:通过添加新的产品类和相应的工厂方法,工厂模式可以轻松地扩展以支持新的产品类型,而无需修改现有的客户端代码。
缺点:
- 增加了代码复杂性:引入工厂模式会增加额外的类和接口,增加了代码的复杂性和理解难度。
- 不易于支持新产品族:当需要支持新的产品族时,需要修改工厂类的代码,违反了开闭原则(对扩展开放,对修改关闭)。
- 可能引入过多的工厂类:随着产品类型的增加,可能会引入大量的具体工厂类,增加了类的数量。
总结:
工厂模式和简单工厂都提供了一种将对象的创建过程从客户端代码中解耦的方式,提高了代码的灵活性和可维护性。工厂模式更加灵活、可扩展,但引入了更多的类和接口,增加了代码复杂性。简单工厂更加简化,但不够灵活,对于支持新产品族的扩展不够方便。在实际应用中,根据具体需求和复杂性,选择适合的工厂模式来实现对象的创建和管理。
Java虚拟机,有哪三个类加载器,都有什么用
Java虚拟机(Java Virtual Machine,JVM)是Java程序的运行环境,它负责将Java字节码转换为机器码并执行程序。在JVM中,有三个类加载器(Class Loader)负责加载类文件和资源文件,它们分别是:
- Bootstrap Class Loader(启动类加载器):
- 是JVM的内置类加载器,负责加载Java核心类库(如
java.lang
、java.util
等)。
- 它是JVM的一部分,由C++实现,不是Java类。
- 无法直接获取Bootstrap Class Loader的引用。
- 在JVM启动时,Bootstrap Class Loader会从JVM的安装路径加载核心类库。
- Extension Class Loader(扩展类加载器):
- 负责加载Java的扩展类库(如
javax
包下的类)。
- 它是由Java编写的类,是
sun.misc.Launcher$ExtClassLoader
类的实例。
- 扩展类加载器是由启动类加载器加载的。
- Application Class Loader(应用程序类加载器):
- 也称为系统类加载器,负责加载应用程序的类和资源文件。
- 它是由Java编写的类,是
sun.misc.Launcher$AppClassLoader
类的实例。
- 应用程序类加载器是由扩展类加载器加载的。
这三个类加载器按照父子关系形成了类加载器层次结构,每个类加载器都有其特定的加载范围和委托机制:
- 类加载器会根据类的全限定名(包括包名和类名)来查找和加载对应的类文件。
- 当一个类加载器需要加载类时,它会先委托给父类加载器尝试加载,如果父类加载器无法加载,则该类加载器自己尝试加载。
- 这种委托机制形成了类加载器层次结构,并避免了类的重复加载。
- 最终,如果所有的父类加载器都无法加载类,就会由当前类加载器负责加载。
类加载器的主要作用是动态加载类和资源文件,实现了Java的动态扩展和模块化特性。它们在Java应用程序运行时起到了关键的作用,确保了类的正确加载和运行。