深入理解Java面向对象编程的核心概念

引言

Java作为一种面向对象的编程语言,其核心概念如构造方法、封装、继承、多态等,是理解和掌握Java编程的关键。本文是我根据自己考过和学过知识点整理出来的。

一、25道面试题

1. 构造方法何时被调用?

构造方法在创建对象时被调用,即在使用new关键字创建类的新实例时。

1.1 构造方法的作用?

构造方法的主要作用是初始化对象的状态。它设置对象的属性,为对象的生命周期的开始做准备。

1.1 构造函数有没有返回值?

构造方法没有返回值,也不允许有返回值。它的返回类型总是与创建的对象的类名相同。

2. 为什么需要封装?

封装是面向对象编程的核心概念之一,它提供了以下好处:

  • 安全性:隐藏内部状态,防止外部直接访问和修改。
  • 灵活性:可以在不影响使用它的代码的情况下更改实现。
  • 可维护性:简化了代码的维护和调试。

3. 属性怎么样封装?

属性封装通常涉及将属性设为私有(private),并提供公共(public)的访问器(getter)和修改器(setter)方法。

5. 静态属性和非静态属性的区别?

静态属性属于类,由类的所有实例共享。非静态属性属于类的每个实例,每个实例都有自己的属性副本。

6. 静态的方法能不能直接调用非静态的方法?

静态方法不能直接调用非静态方法,因为非静态方法需要对象的上下文。

7. 静态方法有什么特点?

静态方法属于类而不是类的实例,可以通过类名直接调用,它们不能访问类的非静态成员。

8. 怎么样进行方法重载?

方法重载是指在同一个类中定义多个同名方法,只要它们的参数列表不同(参数的类型、数量或顺序不同)。

10. 怎么样判断父类引用的真实对象?

可以使用instanceof操作符来判断对象是否是特定类的实例。

11. 父类只有一个个带参数构造方法。

12. 子类怎么样调用父类的构造方法和属性?

子类可以使用super关键字来调用父类的构造方法,通过super来访问父类的属性。

13. super与this的区别?

super用于引用当前对象的直接父类中的成员,而this用于引用当前对象的成员。

14. 继承的条件?

继承适用于存在"是一个"(is-a)关系的情况下,子类继承父类的特性和行为。

15. 继承的作用和特点?

  • 作用:代码复用,减少重复代码,提高开发效率。
  • 特点:支持多态,可以访问父类的所有非私有成员。

16. 方法重载多态与方法重写多态的区别?

  • 方法重载(编译时多态):发生在编译时,基于方法名和参数列表的匹配。
  • 方法重写(运行时多态):发生在运行时,子类重写父类的方法,调用哪个方法取决于对象的实际类型。

17. 方法重写多态的条件?

方法重写要求子类的方法与父类中被重写的方法具有相同的方法名、参数列表和返回类型。

18. 父类引用指向子类对象的时候、只能访问到子类的什么方法?

父类引用指向子类对象时,只能访问父类中定义的方法和变量,以及被子类重写的方法。

19. final关键字

  • (1) 用在变量前面:该变量成为常量,不能被重新赋值。
  • (2) 用在方法的前面:该方法不能被子类重写。
  • (3) 用在类前面:该类不能被继承,且该类的所有成员方法都是final的。

20. 接口的特性?

接口定义了方法的规范,不包含方法的实现。接口可以被多实现,接口中的所有方法默认是public。

21. 接口与抽象类的区别?

  • 接口可以被多个类实现,抽象类只能被单继承。
  • 接口中的方法默认是public,抽象类可以包含非抽象方法。

22. throw与throws的区别?

  • throw用于抛出一个异常实例。
  • throws用于声明方法可能抛出的异常类型。

23. finally的作用?

finally块中的代码无论是否发生异常都会执行,常用于执行清理工作,如关闭资源。

24. 多catch结构要注意的地方。

在多catch结构中,应该按照从最具体的异常类型到最一般的异常类型的顺序来捕获异常。

25. 异常的体系结构?

Java的异常体系基于Throwable类,分为两大类:

  • Error:JVM无法处理的问题,如OutOfMemoryError
  • Exception:程序可以处理的问题,分为checked异常(需要显示处理或声明抛出)和unchecked异常(如RuntimeException,不需要显示处理)。

二、类与对象

1.构造方法

在Java中使用构造方法时,需要注意以下几个关键点:

  1. 构造方法的名称:构造方法的名称必须与类名完全相同,包括大小写。
  2. 返回类型:构造方法不能有返回类型,包括void
  3. 参数列表:构造方法可以通过不同的参数列表进行重载。
  4. 访问修饰符:构造方法可以有访问修饰符(如publicprivate等),以控制访问权限。
  5. 默认构造方法:如果类中没有定义任何构造方法,编译器会自动提供一个无参的默认构造方法。如果类中定义了任何构造方法,编译器将不再提供默认构造方法。
  6. 调用其他构造方法:可以使用this()来调用同一个类中的其他构造方法,但必须放在构造方法的第一行。
  7. 调用父类构造方法:可以使用super()来调用父类的构造方法,也必须放在子类构造方法的第一行。
  8. 初始化块:静态初始化块(static{})在类加载时执行一次,非静态初始化块({})在每个对象创建时执行。
  9. 构造方法不能被继承:子类不能继承父类的构造方法,但可以通过super()显式调用。
  10. 构造方法的执行顺序:首先执行静态初始化块,然后是非静态初始化块,接着是构造方法。
  11. 构造方法的调用:构造方法只能通过new关键字来调用,且在对象创建时自动执行。
  12. 构造方法的异常:构造方法可以声明抛出异常,但通常不推荐这样做,因为这可能会使对象的创建变得复杂。
  13. 构造方法与类变量:构造方法用于初始化类变量,确保对象在使用前处于一致的状态。
  14. 构造方法的覆盖:在子类中,可以定义与父类相同签名的构造方法,但这并不是覆盖父类的构造方法,因为构造方法名必须与类名相同。
  15. 对象的初始化:构造方法提供了一种机制来确保对象在使用前被正确初始化。
  16. 构造方法与垃圾回收:构造方法的执行与垃圾回收机制无关。一旦对象不再被引用,它将被垃圾回收器回收。
  17. 避免在构造方法中执行复杂操作:尽量保持构造方法简单,避免执行复杂的逻辑或长时间运行的操作,这可能会导致对象创建的延迟。

理解并遵循这些注意事项,可以帮助你更有效地使用构造方法来初始化Java对象。

在Java中,类(Class)和对象(Object)之间的关系是面向对象编程(OOP)的基础。以下是类和对象的关系以及它们的定义:

2.类(Class)的定义:

  1. 蓝图:类可以被看作是创建对象的蓝图或模板。它定义了一组特定的属性(变量)和方法(函数),这些属性和方法共同描述了一个对象的状态和行为。
  2. 抽象:类是对现实世界中某些具有共同特征的事物的抽象。例如,Car类可以抽象出所有汽车共有的属性和行为。
  3. 封装:类封装了数据(属性)和操作这些数据的方法。这有助于隐藏内部实现的细节,只暴露必要的接口。
  4. 创建实例:类本身不是程序运行时的实际实体,它需要通过实例化(使用new关键字)来创建具体的对象。

3.对象(Object)的定义:

  1. 实例:对象是类的一个具体实例。每个对象都有其自己的状态(由类的属性定义)和行为(由类的方法定义)。
  2. 唯一性:每个对象在内存中都有唯一的地址,即使两个对象属于同一个类并且具有相同的属性值。
  3. 交互:对象之间可以通过方法调用进行交互。一个对象可以调用另一个对象的方法,也可以作为参数传递给方法。
  4. 多态性:对象可以表现出多种形态。在Java中,这意味着一个引用类型可以指向多种实际类型的对象,并且可以调用适合这些对象的适当方法。

4.类与对象的关系:

  1. 实例化:类通过实例化过程创建对象。这个过程会分配内存,并根据类的定义初始化对象的状态。
  2. 继承:一个类(子类)可以继承另一个类(父类)的属性和方法。这允许创建层次结构,其中子类是父类的特化。
  3. 多态:类可以定义方法,允许其子类提供特定的实现。这允许将子类的对象视为父类的对象,从而实现多态性。
  4. 接口:类可以实现一个或多个接口,这定义了对象必须遵守的契约或行为规范。

5.Java中的引用数据类型:

  1. 类(Class):用户自定义的类或Java API中的类,例如StringIntegerSystem等。
  2. 接口(Interface):定义了一组方法规范,可以被类实现(Implement)或被接口继承。
  3. 数组(Array):一种容器对象,可以包含固定数量的单一类型元素。
  4. 枚举(Enum):一种特殊的类,其实例在编译时就已知。
  5. 包装类(Wrapper Class):基本数据类型的包装类,如IntegerDoubleCharacter等。
  6. 字符串(String):在Java中,String是一个特殊的对象,用于表示文本数据。尽管它表现上类似于基本数据类型,但实际上是一个对象。
  7. 集合(Collection):Java集合框架中的数据结构,如ListSetMap等。
  8. 异常(Exception):异常类,用于处理程序运行时的错误。
  9. 注解(Annotation):一种接口类型,用于为类、方法或变量提供元数据。
  10. 泛型(Generics):允许在编译时进行类型检查,提高代码的复用性与安全性。
  11. 变长参数(Varargs):在方法定义中使用,允许传递任意数量的参数。
  12. 内部类(Inner Class):定义在另一个类中的类,可以访问外部类的成员。
  13. 匿名内部类(Anonymous Inner Class):没有名字的内部类,常用于实现接口或继承抽象类。
  14. Lambda 表达式:从Java 8开始引入,允许更简洁地表示实例化对象的匿名类。
  15. 方法引用(Method References):Java 8引入,允许直接引用已有方法或构造函数。
  16. 构造器(Constructor):虽然不是数据类型,但构造器也是一种特殊的方法,用于创建对象。

这些引用数据类型在Java中扮演着重要的角色,它们允许创建复杂的数据结构和对象,实现面向对象编程的各种特性。

6.Java的基本数据类型:

是Java语言预定义的、占用固定内存大小的数据类型。它们包括以下8种:

  1. 整型
    • byte:8位有符号整数,取值范围从 -128 到 127。
    • short:16位有符号整数,取值范围从 -32,768 到 32,767。
    • int:32位有符号整数,默认的整数类型,取值范围从 -2^31 到 2^31-1。
    • long:64位有符号整数,取值范围从 -2^63 到 2^63-1。在数值后加上 L 或 l 来表示。
  1. 浮点型
    • float:32位单精度浮点数。在数值后加上 F 或 f 来表示。
    • double:64位双精度浮点数,默认的浮点数类型。
  1. 字符型
    • char:16位Unicode字符,可以存储任何字符。
  1. 布尔型
    • boolean:只有两个可能的值:true 和 false。

这些基本数据类型的特点包括:

  • 固定大小:每种基本数据类型都占用固定数量的内存空间。
  • 没有方法:基本数据类型没有提供方法,它们是不可变的。
  • 自动装箱与拆箱:Java 5 引入了自动装箱和拆箱机制,允许自动地在基本数据类型和相应的包装类之间转换,例如 int 到 Integer。
  • 默认值:如果变量声明时没有初始化,它们会被赋予默认值。例如,整型默认为 0,浮点型为 0.0,字符型为 '\u0000'(即 Unicode 编码中的空字符),布尔型为 false。

Java中的static关键字用于表示类级别的属性或方法,而不是属于某个对象的实例。以下是static的应用、定义和一些注意事项:

java中Static用法

static应用:
1. 类变量:使用static定义的变量称为类变量或静态变量,它们不属于类的任何特定实例,而是被类的所有实例共享。
2. 类方法:使用static定义的方法称为类方法或静态方法,它们可以通过类名直接调用,不需要创建类的实例。
3. 静态初始化块static关键字也可以用于静态初始化块,它在类加载时执行一次,用于初始化静态变量。
4. static定义:
  • static变量:在类中,但在任何方法或构造方法之外定义的变量。
  • static方法:在类中定义的方法,使用static关键字修饰。
  • 静态初始化块:用static关键字标记的代码块,通常用于静态变量的初始化。
5. static注意事项:
  1. 访问控制:静态方法和静态变量可以有访问修饰符(如publicprivate等),以控制它们的可见性。
  2. 实例方法和变量:静态方法不能直接访问非静态变量和方法,因为它们不与任何特定实例关联。
  3. 构造方法:静态方法不能被声明为abstract,并且它们不能调用非静态变量或方法。
  4. 继承:静态变量和方法不会被继承,但可以通过子类访问,前提是它们是publicprotected
  5. 多态:静态方法不支持多态性,即不能被重写(Override)。
  6. 内存管理:静态变量在类加载时初始化,并在程序的整个生命周期内保持,直到程序结束。
  7. 线程安全:静态变量和方法在多线程环境中需要特别注意线程安全问题。
  8. 静态导入:Java允许使用import static语句导入静态方法或静态字段,这样就可以直接使用它们而不需要类名。
  9. 单例模式:静态方法常用于实现单例模式,确保一个类只有一个实例。
  10. 工具类:静态方法常用于工具类或实用类,提供一组静态工具方法。
  11. 性能:过度使用静态方法和变量可能导致设计问题,如难以进行单元测试和违反单一职责原则。
  12. 序列化:如果类实现了Serializable接口,静态变量不会被序列化。
  13. 初始化顺序:静态变量的初始化顺序按照它们在类中出现的顺序。

使用static关键字可以创建类级别的资源,但应当谨慎使用,以避免潜在的设计问题和性能问题。

js

<input type="button" value="全选" οnclick="allcheck( )">

<input type="button" value="全不选" οnclick="unallcheck( )">

  1. 垃圾回收:数组本身是对象,当数组不再被引用时,它们会被垃圾回收机制回收。
  2. 性能:数组的访问速度通常比ArrayList快,因为它们是连续存储的。但是,数组的固定大小和不可变性可能限制其使用。
  3. 泛型数组:Java不允许创建泛型数组(即数组元素类型为泛型类型),因为数组是协变的。你可以考虑使用ArrayList或其他集合类。
  4. 数组和异常:在处理数组时,注意异常处理,特别是在多线程环境中。
  5. 数组和可变参数:在方法中使用可变参数时,可以将它们转换为数组。

三、周考笔试题

1、final 在 java 中有什么作用?

特征:凡是引用final关键字的地方皆不可修改!

(1)修饰类:表示该类不能被继承;

(2)修饰方法:表示方法不能被重写

(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

2、String 类的常用方法都有哪些列举十个说明含义?

  1. byte[] getBytes() 将一个字符串转换成字节数组char[] toCharArray() 将一个字符串转换成字符数组String[] split(String) 将一个字符串按照指定内容劈开
  2. boolean equals(String) 判断两个字符串的内容是否一模一样
  3. boolean equalsIgnoreCase(String) 忽略大小写的比较两个字符串的内容是否一模一样
  4. boolean contains(String) 判断一个字符串里面是否包含指定的内容
  5. boolean startsWith(String) 判断一个字符串是否以指定的内容开头
  6. boolean endsWith(String) 判断一个字符串是否以指定的内容结尾
  7. String toUpperCase() 将一个字符串全部转换成大写
  8. String toLowerCase() 将一个字符串全部转换成小写
  9. String replace(String,String) 将某个内容全部替换成指定内容
  10. String replaceAll(String,String) 将某个内容全部替换成指定内容,支持正则
  11. String repalceFirst(String,String) 将第一次出现的某个内容替换成指定的内容
  12. String substring(int) 从指定下标开始一直截取到字符串的最后
  13. String substring(int,int) 从下标x截取到下标y-1对应的元素
  14. String trim() 去除一个字符串的前后空格

3、例举十个常见的异常类?

IOException(输入输出异常):当输入输出操作发生异常时抛出,常见于文件操作、网络操作等。

SQLExceptionSQL数据库操作异常):当执行SQL语句时发生异常抛出,常见于数据库操作。

ClassNotFoundException(类未找到异常):当试图加载不存在的类时抛出。

InterruptedException(线程中断异常):当线程在等待、睡眠或等待锁的过程中被中断时抛出。

NullPointerException(空指针异常):当应用程序试图在空对象上调用方法或访问属性时抛出。

ArrayIndexOutOfBoundsException(数组下标越界异常):当访问数组时使用了非法的索引值时抛出。

ClassCastException(类型转换异常):当试图将一个对象强制转换为不兼容的类型时抛出。

NumberFormatException(数字格式异常):当试图将一个字符串转换为数值类型,但该字符串格式不合法时抛出。

ArithmeticException(算术异常):当试图进行数学运算,但出现了异常情况,例如除零操作时抛出。

FileNotFoundException(文件未找到异常):当试图访问一个不存在的文件时抛出

IllegalArgumentException(非法参数异常):当方法接收到一个不合法的参数时抛出。

4、接口和抽象类有什么区别?

(1)抽象类可以有构造方法,接口中不能有构造方法。

(2)抽象类中可以有普通成员变量,接口中没有普通成员变量

(3)抽象类中可以包含静态方法,接口中不能包含静态方法

(4)一个类可以实现多个接口,但只能继承一个抽象类。

(5)接口可以被多重实现,抽象类只能被单一继承

(6)如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法

5、List、Set、Map 之间的区别是什么?

  1. List、Set是存储单列的数据集合,都继承与Collection接口。
  2. Map是存储键值对这样的双列数据的集合,是个独立接口。
  3. List中存储的数据是有序的,可以是重复的。
  4. Set中存储的数据是无序的,且不允许重复。
  5. Map中存储的数据是无序的,他的键是不允许重复的,值是可以重复的

6、并行和并发有什么区别?

  1. 并发(Concurrency)
    • 并发是指多个任务在同一个时间点看起来像是同时进行的。它强调的是任务的交替执行,而不是真正的同时执行。
    • 在Java中,可以通过多线程来实现并发。多个线程可以在同一时刻被操作系统调度,看起来像是同时运行,但实际上它们是在不同的时间片上交替执行的。
    • 并发的关键在于任务的调度和时间管理,使得多个任务能够高效地共享系统资源,提高程序的响应性和吞吐量。
  1. 并行(Parallelism)
    • 并行是指多个任务在同一时刻真正地同时进行。它强调的是任务的实际并行执行,通常需要多核处理器的支持。
    • 在Java中,可以通过多线程或多进程来实现并行。当程序在多核处理器上运行时,每个核心可以独立执行一个线程或进程,从而实现真正的并行处理。
    • 并行的关键在于资源的分配和任务的同步,确保多个任务能够同时进行,充分利用多核处理器的能力。

总结:

  • 并发更侧重于任务的调度和时间管理,使得多个任务能够交替执行,提高资源利用率。
  • 并行更侧重于任务的实际同时执行,需要硬件支持(如多核处理器),以实现更高的处理速度。

在实际应用中,Java的多线程编程主要利用并发来提高程序的效率和响应性,同时也可以通过多核处理器实现并行处理,进一步优化性能。

7、线程和进程的区别?

  1. 定义:
    • 进程:进程是操作系统进行资源分配和调度的基本单位。一个进程通常包含一个或多个线程,并且拥有独立的内存空间和系统资源。
    • 线程:线程是进程中的一个执行单元,是CPU调度和分派的基本单位。线程共享所属进程的资源,如内存和文件。
  1. 资源占用:
    • 进程:每个进程都有自己独立的内存空间和系统资源,进程间的数据交换需要通过进程间通信(IPC)机制。
    • 线程:线程共享所属进程的内存空间和资源,因此线程间的通信和数据交换更为简单和高效。
  1. 创建和管理:
    • 进程:创建进程需要操作系统的干预,通常涉及到系统调用和资源的重新分配,因此进程的创建和管理成本较高。
    • 线程:线程的创建和管理相对简单,可以在程序内部通过Java的多线程API(如Thread类和Runnable接口)来实现。
  1. 执行:
    • 进程:进程在执行时是独立的,操作系统会为每个进程分配独立的CPU时间片,进程间的执行是并行的。
    • 线程:线程是进程中的执行单元,多个线程可以并发执行,共享CPU时间片,因此线程的执行是并行的,但在同一时刻只能有一个线程在CPU上执行。
  1. 通信:
    • 进程:进程间通信(IPC)机制复杂,包括管道、信号、消息队列、共享内存等。
    • 线程:线程间通信相对简单,可以通过共享内存、同步机制(如锁、信号量)等实现。
  1. 上下文切换:
    • 进程:进程间的上下文切换成本较高,涉及到操作系统的内存管理、资源调度等。
    • 线程:线程间的上下文切换成本较低,因为线程共享相同的内存空间和资源,切换时不需要重新加载内存。
  1. 控制:
    • 进程:进程的控制和管理由操作系统负责,包括进程的创建、终止、调度等。
    • 线程:线程的控制和管理可以由程序内部实现,通过Java的多线程API进行控制。

总结来说,线程是进程的子单位,共享进程的资源,而进程是操作系统资源分配的基本单位,拥有独立的内存和资源。在Java中,多线程编程利用线程的并发执行来提高程序的效率和响应性。

8、创建线程有哪几种方式?

在Java中,创建线程主要有以下几种方式:

  1. 继承Thread类
public class MyThread extends Thread {
    public void run() {
        // 线程执行的代码
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}
    • 通过创建一个新的类继承java.lang.Thread类。
    • 重写run()方法,定义线程执行的代码。
    • 创建该类的实例,并调用start()方法启动线程。
  1. 实现Runnable接口
public class MyRunnable implements Runnable {
    public void run() {
        // 线程执行的代码
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable task = new MyRunnable();
        Thread t = new Thread(task);
        t.start();
    }
}
    • 创建一个新的类实现java.lang.Runnable接口。
    • 实现run()方法,定义线程执行的代码。
    • 将Runnable对象传递给Thread类的构造器,并创建Thread对象。
    • 调用Thread对象的start()方法启动线程。
  1. 实现Callable接口
    • 创建一个新的类实现java.util.concurrent.Callable接口。
    • 实现call()方法,定义线程执行的代码,并可以返回一个值。
    • 使用ExecutorService来管理线程池,提交Callable任务。
    • 通过Future对象获取Callable任务的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MyCallable implements Callable<Integer> {
    public Integer call() throws Exception {
        // 线程执行的代码
        return 123;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future<Integer> future = executor.submit(new MyCallable());
        System.out.println("Result: " + future.get());
        executor.shutdown();
    }
}
  1. 使用Executor框架
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        executor.execute(new Runnable() {
            public void run() {
                // 线程执行的代码
            }
        });
        executor.shutdown();
    }
}
    • 使用java.util.concurrent.ExecutorService接口及其实现类(如ThreadPoolExecutorScheduledThreadPoolExecutor等)来管理线程池。
    • 通过execute(Runnable)submit(Callable)方法提交任务。
    • 这种方式可以更灵活地控制线程的创建和管理。

9、描述tcp 的三次握手?

第一次握手:客户端——请求(发送请求SYN+数据包当前序列号seq,无需应答)

第二次握手:服务器——确认(发送应答ACK+请求SYN+确认收到上一个数据包的确认号ack+ 当前数据包序列号seq)

第三次握手:客户端——确认服务器的确认(发送应答ACK+确认收到上一个数据包的确认号ack+ 当前数据包序列号seq ,连接已建立,无需请求)

10、String、StringBuffer、StringBuilder的区别

String 是不可变字符串对象,每次修改字符串都会创建新对象

StringBuilder、StringBuffer 是可变字符串序列(对象),StringBuilder 和 StringBuffer 在修改字符串时不会创建新对象

当需要对字符串频繁的修改时,推荐使用StringBuilder 或 StringBuffer

StringBuffer是jkd 1.0 提供的。StringBuilder是jdk 1.5提供的,二者的常用方法名和实现原理是一样的。区别在于StringBuffer是线程安全的,StringBuilder是线程不安全的。如果不涉及线程安全问题,推荐使用StringBuilder,因为它的效率会更高一些

执行效率上: StringBuilder 快于 StringBuffer 快于 String

11、访问权限修饰符public、private、protected,以及不写(默认)时的区别?

① 类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。

12、ArrayList 和 LinkedList 的区别有哪些?

  1. 底层数据结构:ArrayList基于动态数组实现,内部维护一个Object数组,默认初始容量为10,当元素数量超过当前容量时会自动扩容。
  2. 随机访问效率高:由于基于数组,ArrayList支持通过索引快速访问元素,时间复杂度为O(1)。
  3. 插入和删除效率低:在中间或开头插入/删除元素时,需要移动后续元素,时间复杂度为O(n)。
  4. 适合随机访问:对于频繁随机访问元素的场景,ArrayList性能更好。
  5. LinkedList:
  6. 底层数据结构:LinkedList基于双向链表实现,每个节点包含数据元素和指向前后节点的引用。
  7. 插入和删除效率高:在任意位置插入/删除元素时,只需调整相邻节点的引用,时间复杂度为O(1)。
  8. 顺序访问效率低:由于基于链表,LinkedList不支持随机访问,需要从头或尾开始遍历,时间复杂度为O(n)。
  9. 适合频繁插入和删除:对于频繁插入和删除元素的场景,LinkedList性能更好。
  10. 总结:
  11. 如果需要频繁进行随机访问操作,应选择ArrayList。
  12. 如果需要频繁进行插入和删除操作而访问操作较少,应选择LinkedList。
  13. 在需要同时进行大量随机访问和插入删除操作时,可以根据具体场景进行权衡选择。

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值