一.Java语言中的基本数据类型是指不包含对象和方法的数据类型。Java中有八种基本数据类型,分别为:
-
byte:字节型数据类型,占用1个字节(8位),取值范围为 -128 到 127。
-
short:短整型数据类型,占用2个字节(16位),取值范围为 -32768 到 32767。
-
int:整型数据类型,占用4个字节(32位),取值范围为 -2147483648 到 2147483647。
-
long:长整型数据类型,占用8个字节(64位),取值范围为 -9223372036854775808 到 9223372036854775807。
-
float:单精度浮点型数据类型,占用4个字节(32位),取值范围为 1.401298E-45 到 3.4028235E38,精度为小数点后7位。
-
double:双精度浮点型数据类型,占用8个字节(64位),取值范围为 4.9E-324 到 1.7976931348623157E308,精度为小数点后15位。
-
char:字符型数据类型,占用2个字节(16位),用来表示单个字符。
-
boolean:布尔型数据类型,用于表示 true 和 false 两个值。
在Java中,基本数据类型是按值传递的,也就是说,当将一个基本数据类型赋值给另一个变量时,会复制该值。相反,Java中的引用类型则是按引用传递,也就是说,实际传递的是对象的引用,而非对象本身。
二.String类能被继承吗
在Java中,String类是被final关键字修饰的,因此不能被继承。final修饰的类表示该类是最终的,不允许其他类继承它。
String类是Java中用于表示字符串的类,在Java中广泛使用。由于String类在设计上考虑了安全性和不可变性,为了确保字符串对象的内容不会被修改,String类被设计为不可变类(Immutable Class),即一旦创建了String对象,其内容就不可更改。
通过将String类设置为final,可以防止其他类对其进行继承和修改,确保了字符串对象的内容不会被意外或恶意地修改,保证了程序在使用字符串时的安全性和稳定性。
虽然不能直接继承String类,但可以使用String类提供的方法和操作符来处理和操作字符串,例如连接、截取、比较等操作。
三.String、StringBuffer和StringBuilder都是Java中用于操作字符串的类,它们之间的主要区别如下:
-
可变性:
- String类是不可变的,一旦创建了String对象,就无法修改其内容。每次对String进行操作(拼接、替换等),都会生成一个新的String对象。
- StringBuffer和StringBuilder类是可变的,可以对其内容进行修改。每次对StringBuffer和StringBuilder进行操作时,在原有对象上进行修改,不会生成新的对象。
-
线程安全性:
- String类是线程安全的,多个线程同时操作同一个String对象是安全的。
- StringBuffer类是线程安全的,多个线程同时操作同一个StringBuffer对象是安全的,因为StringBuffer的方法都是同步的(synchronized)。
- StringBuilder类是非线程安全的,多个线程同时操作同一个StringBuilder对象可能会导致数据不一致,因为StringBuilder的方法没有进行同步。
-
性能:
- 在单线程环境下,由于String是不可变的,每次对String进行操作都会产生一个新的String对象,频繁的字符串拼接操作会产生大量的临时对象,对性能会有一定的影响。
- StringBuffer在进行字符串操作时,不会生成新的对象,而是在原有的对象上进行操作,因此在频繁的字符串拼接场景下,使用StringBuffer比String性能更好。
- StringBuilder与StringBuffer类似,也是在原有的对象上进行操作,但不保证线程安全,因此在单线程环境下,使用StringBuilder比StringBuffer性能更好。
基于以上区别,一般情况下推荐如下使用场景:
- 需要频繁进行字符串拼接和修改,并且在多线程环境下使用:使用StringBuffer。
- 需要频繁进行字符串拼接和修改,但在单线程环境下使用:使用StringBuilder。
- 不需要频繁进行字符串修改时,可以使用String,因为String具有不可变性,更加安全可靠。
四.ArrayList和LinkedList是Java集合框架中List接口的两个实现类,它们之间的主要区别如下:
-
内部实现机制:
- ArrayList底层使用数组来存储元素,通过索引位置可以快速访问和修改元素。
- LinkedList底层使用双向链表(每个节点都包含前后两个指针)来存储元素,每个节点只存储自身的值和前后节点的引用。因此,LinkedList在增删操作时具有更好的性能。
-
随机访问和定位效率:
- ArrayList通过索引位置可以进行高效的随机访问,时间复杂度为O(1)。
- LinkedList需要从头节点或尾节点开始遍历链表,直到找到目标元素,时间复杂度为O(n)。
-
插入和删除操作效率:
- ArrayList在插入和删除元素时,涉及到元素的移动,特别是在列表的中间位置进行插入和删除操作时,需要将后续的元素进行位移,因此时间复杂度为O(n)。
- LinkedList在插入和删除元素时,只需要修改节点的前后指针,不需要移动其他节点,因此在列表的任何位置都可以高效地执行插入和删除操作,时间复杂度为O(1)。
-
内存占用:
- ArrayList需要预先分配一块连续的内存空间来存储元素,因此在创建时需要占用较多的内存空间。
- LinkedList由于是基于链表的实现,每个节点只需存储自身的值和前后节点的引用,因此内存占用相对较少。
根据以上特点,一般情况下建议使用:
- 需要频繁进行随机访问或按索引位置操作元素:使用ArrayList。
- 需要频繁进行插入、删除或操作列表的首尾元素:使用LinkedList。
当然,具体选择哪种实现取决于具体的场景和需求。
五. 在Java中,当使用new
关键字实例化一个对象时,以下是类的实例化顺序:
-
父类的静态代码块:如果存在父类,并且父类有静态代码块,在子类实例化之前会首先执行父类的静态代码块。父类的静态代码块只会在类加载的过程中执行一次。
-
子类的静态代码块:紧接着会执行子类的静态代码块。和父类的静态代码块类似,子类的静态代码块也只会在类加载的过程中执行一次。
-
父类的实例变量和代码块:父类会按照在类中定义的顺序执行实例变量和代码块。
-
父类的构造函数:然后会调用父类的构造函数来完成父类对象的初始化。
-
子类的实例变量和代码块:接着执行子类的实例变量和代码块。
-
子类的构造函数:最后调用子类的构造函数来完成子类对象的初始化。
需要注意的是,静态成员(静态变量和静态代码块)的初始化仅在类加载阶段进行一次,而实例成员(实例变量、实例代码块、构造函数)则在每次实例化对象时都会执行。
总结起来,类的实例化顺序是:父类静态代码块 -> 子类静态代码块 -> 父类实例变量和代码块 -> 父类构造函数 -> 子类实例变量和代码块 -> 子类构造函数。
六.用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么,比如存储方式;
Java中常用的Map类有HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap等。
HashMap
HashMap是基于哈希表实现的Map,它采用了数组+链表(或红黑树)的数据结构。HashMap中的键值对没有固定的顺序,允许存在一个null键和多个null值。HashMap的性能较好,在大多数场景下都能满足需求。HashMap是非线程安全的,因此在并发场景下需要进行同步处理。
TreeMap
TreeMap是基于红黑树实现的Map,它对键进行排序,因此在遍历时会按照键的自然顺序或者自定义的顺序进行遍历。TreeMap的性能相对较低,适合在键需要排序的场景下使用。
LinkedHashMap
LinkedHashMap是基于哈希表和链表实现的Map,它可以维护键值对的插入顺序,或者按照最近访问的顺序进行遍历。LinkedHashMap是非线程安全的。
ConcurrentHashMap
ConcurrentHashMap是Java并发包中提供的线程安全的Map,它采用了锁分段技术,将整个Map分成若干个段,每个段都可以被一个线程独立访问,从而实现了高并发下的安全性和效率。ConcurrentHashMap在并发场景下的性能远优于HashMap和Hashtable,但在单线程场景下会略逊于HashMap。
在并发场景下,除了ConcurrentHashMap之外,还可以使用Java8中新增的ConcurrentHashMap的替代品ConcurrentHashMapV8、Google Guava中的ConcurrentHashMultimap等并发Map类。
这些Map的内部原理大多基于哈希表实现,其中HashMap是最常用的一种,其底层数据结构是一个数组加上链表(或红黑树),通过计算键的hashCode来确定其在数组中的位置,并通过链表或红黑树解决哈希冲突。HashMap的默认容量为16,加载因子为0.75,当Map的大小超过容量*加载因子时,会进行扩容。HashMap在多线程并发访问时不是线程安全的,因此需要进行同步操作,或者使用线程安全的ConcurrentHashMap。同时,HashMap和其他哈希表实现的Map,在键的hashCode相等时,还需要通过equals方法来比较键的值是否相等。