Java_day20-

发布于:2025-04-23 ⋅ 阅读:(94) ⋅ 点赞:(0)


day 20

1 String、StringBuffer、StringBuilder的区别

String, StringBuilder, 和 StringBuffer 都是 Java 中用于处理字符串的类,但它们之间有一些重要的区别:

不可变性
String 内部的字符数组使用 final 修饰,为不可变的字符串类,每当我们对 String 对象进行改变时,实际上都会创建一个新的 String 对象,旧的 String 对象会被 JVM 回收, 容易触发 gc,引起系统内存抖动。
StringBuilder 和 StringBuffer是可变的。即它们都允许修改字符串,而不会创建新的对象
线程安全
String:由于 String 是不可变的,所以是线程安全的。
StringBuffer 中的方法均使用 synchronized 关键字修饰,线程安全。
而 StringBuilder 线程不安全。
性能
对于复杂的字符串操作(例如多次的拼接,插入,删除),StringBuilder 和 StringBuffer 效率高于 String,因为它们是可变的,不需要创建新的对象。

使用场景
String: 字符串不经常变化的场景中可以使用 String 类,例如常量的声明、少量的变量运算。
StringBuilder: 在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,则可以考虑使用,如SQL语句的拼装、JSON封装等。
StringBuffer: 在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用 StringBuffer,例如 XML 解析、HTTP 参数解析和封装。

2 接口和抽象类的区别

从定义上来说
接口是一种抽象类型,它定义了一组方法,但没有实现任何方法的具体代码。接口中的方法默认是抽象的,且接口中只能包含常量(static final 变量)和抽象方法,不能包含成员变量。
抽象类是一个类,可以包含抽象方法和具体方法,也可以包含成员变量和常量。抽象类中的抽象方法是没有实现的方法,而具体方法则包含实现代码。抽象类不能直接实例化,通常需要子类继承并实现其中的抽象方法。
继承
接口支持多继承,一个类可以实现多个接口。
Java 中不支持多继承,一个类只能继承一个抽象类。如果一个类已经继承了一个抽象类,就不能再继承其他类。
构造器
接口不能包含构造器,因为接口不能被实例化。类实现接口时,必须实现接口中定义的所有方法。
抽象类可以包含构造器,用于初始化抽象类的子类实例。
访问修饰符
接口中的方法默认是public abstract的。接口中的变量默认是public static final的。
抽象类中的抽象方法默认是protected的,具体方法的访问修饰符可以是public、protected或private。
实现限制
类可以同时实现多个接口,接口中的方法默认为抽象方法,不包含方法体。实现接口时必须要实现这些方法。
一个类只能继承一个抽象类,继承抽象类的子类必须提供抽象类中定义的所有抽象方法的实现。
设计目的
接口用于定义规范,强调“行为”或“能力”。
抽象类用于代码复用,提供通用的实现或基础功能,并且可以包含方法的具体实现。

3 Java常见的异常类有哪些

Java 的异常都是派生于 Throwable 类的一个实例,所有的异常都是由 Throwable 继承而来的。

异常又分为 RuntimeException 和其他异常:

由程序错误导致的异常属于 RuntimeException
而程序本身没有问题,但由于像 I/O 错误这类问题导致的异常属于其他异常。
运行时异常 RuntimeException:

顾名思义,运行时才可能抛出的异常,编译器不会处理此类异常。比如数组索引越界 ArrayIndexOutOfBoundsException、使用的对象为空 NullPointException、强制类型转换错误 ClassCastException、除 0 等等。出现了运行时异常,一般是程序的逻辑有问题,是程序自身的问题而非外部因素。

其他异常:
Exception 中除了运行时异常之外的,都属于其他异常。也可以称之为编译时异常,这部分异常编译器要求必须处置。这部分异常常常是因为外部运行环境导致,因为程序可能运行在各种环境中,如打开一个不存在的文件,此时抛出 FileNotFoundException。编译器要求Java程序必须捕获或声明所有的编译时异常,强制要求程序为可能出现的异常做准备工作。

day 21

1说一说Java面向对象三大特性

Java面向对象编程的三大特性是封装、继承和多态:

封装:封装是将对象的数据(属性)和行为(方法)结合在一起,并隐藏内部的实现细节,只暴露出一个可以被外界访问的接口。通常使用关键字 private、protected、public 等来定义访问权限,以实现封装。
继承:允许一个类(子类)继承另一个类(父类)的属性和方法的机制。子类可以重用父类的代码,并且可以通过添加新的方法或修改(重写)已有的方法来扩展或改进功能,提高了代码的可重用性和可扩展性。Java支持单继承,一个类只能直接继承一个父类。
多态:多态是指允许不同类的对象对同一消息做出响应,但具体的行为会根据对象的实际类型而有所不同。这通常通过方法重载和重写实现。

2说一说你对Java多态的理解

当把一个子类对象直接赋给父类引用变量,而运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。

多态有两种形式:编译时多态(静态多态)和运行时多态(动态多态)。

编译时多态:指在编译阶段,编译器就能够确定调用哪个方法,这是通过方法的重载来实现的。编译器在编译时根据方法的参数数量、类型或顺序来选择调用合适的方法。
运行时多态:在程序运行时,根据实际对象的类型来确定调用的方法,这是通过方法的重写来实现的。运行时多态主要依赖于对象的实际类型,而不是引用类型。

3Java重写和重载的区别

Java中的重载和重写是实现多态的两种不同方式。

方法的重载是编译时多态,指的是在同一个类中,可以有多个方法具有相同的名称,但是它们的参数列表不同(参数的类型、个数、顺序),可以有不同的返回类型和访问修饰符,通过静态绑定(编译时决定)实现。

方法的重写是运行时多态,指的是在子类中重新定义父类中已经定义的方法,方法名、参数列表和返回类型都必须相同。重写的方法的访问级别不能低于被重写的父类方法,虚拟机在运行时根据对象的实际类型来确定调用哪个方法。

总结来说,重载关注的是方法的多样性,允许同一个类中存在多个同名方法;而重写关注的是方法的一致性,允许子类提供特定于其自己的行为实现。

day 22

1final关键字有什么作用

final 就是不可变的意思,可以修饰变量、方法和类。

修饰类: final 修饰的类不可被继承,是最终类.
修饰方法: 明确禁止该方法在子类中被覆盖的情况下才将方法设置为 final
修饰变量:
final 修饰 基本数据类型的变量,其数值一旦在初始化之后便不能更改, 称为常量;
final 修饰 引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

2== 和equals的区别

在Java中,==和equals方法用来比较对象,但它们在语义和使用上仍有一定的差别:

==运算符:对于原始数据类型,==比较的是值是否相等,对于引用类型,==比较的是两个引用是否指向内存中的同一位置,即它们是否是同一个对象。
equals是一个方法,定义在Object类中,默认情况下,equals() 方法比较的是对象的引用,与 == 类似。但在子类中通常被重写,比如 String、 Integer 等,已经重写了 equals() 方法以便比较对象的内容是否相等。
一般来说,是使用 == 比较对象的引用(内存地址),用 equals() 比较对象的内容。
需要注意的是,在重写equals方法时,应同时重写hashCode方法,以保持equals和hashCode的一致性。

day 23

1Java的集合类有哪些,那些是线程安全的,那些是线程不安全的?

Java 中的集合类主要由 Collection 和 Map 这两个接口派生而出,其中 Collection 接口又派生出三个子接口,分别是 Set、List、Queue。所有的 Java 集合类,都是 Set、List、Queue、Map 这四个接口的实现类
List接口 : 有序集合,允许重复元素。常见的实现类有ArrayList、LinkedList等。
Set接口 :不允许重复元素的集合。常见的实现类有HashSet、LinkedHashSet、TreeSet等。
Queue接口: 用于表示队列的数据结构。 常见的实现类有LinkedList、PriorityQueue等。
Map接口: 表示键值对的集合。常见的实现类有HashMap、LinkedHashMap、TreeMap 等。
线程不安全的集合类
ArrayList、LinkedList、HashSet、HashMap:这些集合类是非线程安全的。在多线程环境中,如果没有适当的同步措施,对这些集合的并发操作可能导致不确定的结果。
TreeMap、TreeSet: 虽然 TreeMap 和、 TreeSet 是有序的集合,但它们也是非线程安全的。
线程安全的集合类
Vector:类似于ArrayList, 它的方法都是同步的,因此是线程安全的。然而,它相对较重,不够灵活,现在通常建议使用 ArrayList。
HashTable:类似于HashMap,但它是线程安全的,通过同步整个对象实现。但它的使用已经不太推荐,通常建议使用 HashMap。
ConcurrentHashMap:提供更好的并发性能,通过锁分离技术实现线程安全。
Collections.synchronizedList、Collections.synchronizedSet、Collections.synchronizedMap: 这些方法可以将非线程安全的集合包装成线程安全的集合。

2ArrayList 和 Array 有什么区别?ArrayList 和 LinkedList 的区别是什么?

ArrayList vs Array:
ArrayList是动态数组的实现,而Array 是固定长度的数组。
ArrayList 提供了更多的功能,比如自动扩容、增加和删除元素等,Array则没有这些功能。
Array 可以直接存储基本类型数据,也可以存储对象。 ArrayList 中只能存储对象。对于基本类型数据,需要使用其对应的包装类(如 Integer、Double 等)
在随机访问时,Array由于其连续内存存储,性能通常优于ArrayList。
ArrayList vs LinkedList:
ArrayList基于动态数组,LinkedList 基于双向链表。
随机访问:ArrayList在随机访问时性能更好,而LinkedList访问元素时效率较低,因为需要从头开始或从尾开始通过链接遍历,时间复杂度为 O(n)。
删除/添加元素:在ArrayList末尾添加元素通常很快,但在ArrayList中间或开始插入或删除元素时,可能需要移动后续元素,时间复杂度为 O(n)。而LinkedList添加和删除元素时性能更佳, 只需改变节点的引用。
扩容:当容量不足以容纳更多元素时,ArrayList 会扩容,这个过程涉及创建新数组和复制旧数组的内容,有一定的开销。
使用场景:选择 ArrayList 或者 LinkedList 通常取决于你的 Java 应用是否需要频繁的随机访问操作,还是更多的插入和删除操作。
总结来说,ArrayList和Array的主要区别在于动态大小和功能,而ArrayList和LinkedList的主要区别在于底层数据结构和它们对元素操作的性能特点。选择使用哪一个取决于具体的应用场景和性能需求。

3ArrayList 扩容机制

ArrayList扩容的本质就是计算出新的扩容数组的size 后实例化,并将原有数组内容复制到新数组中去。(不是原数组,而是新数组然后给予数组对象地址)。
当创建一个ArrayList对象时,它会分配一定的初始容量,通常为10。
当ArrayList中的元素数量达到当前容量时,ArrayList会自动增加其容量。ArrayList 扩容的计算是在一个grow() 方法里面, grow 方法先尝试将数组扩大为原数组的1.5倍。(默新容量=旧容量右移一位(相当于除于2)在加上旧容量)
若新的容量满足需求,会调用一个Arrays.copyof 方法, 将所有的元素从旧数组复制到新数组中,这个方法是真正实现扩容的步骤。如果扩容后的新容量还是不满足需求,那新容量大小为当前所需的容量加1。


网站公告

今日签到

点亮在社区的每一天
去签到