Java有哪些数据类型?
Java语言的数据类型分为两种:基本数据类型和引用数据类型。
1.基本数据类型包括 boolean(布尔型)、float(单精度浮点型)、char(字符型)、byte(字节型)、short(短整型)、int(整型)、long(长整型)和 double (双精度浮点型)共 8 种,如下表所示。
| 基本类型 | 位数 | 字节 | 默认值 |
|---|---|---|---|
| int | 32 | 4 | 0 |
| short | 16 | 2 | 0 |
| long | 64 | 8 | 0L |
| byte | 8 | 1 | 0 |
| char | 16 | 2 | 'u0000' |
| float | 32 | 4 | 0f |
| double | 64 | 8 | 0d |
| boolean | 1 | false |
对于 boolean,官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1 位,但是实际中会考虑计算机高效存储因素。
Java虚拟机规范讲到:在JVM中并没有提供boolean专用的字节码指令,而boolean类型数据在经过编译后在JVM中会通过int类型来表示,此时boolean数据4字节32位,而boolean数组将会被编码成Java虚拟机的byte数组,此时每个boolean数据1字节占8bit。
注意:
①. Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析:
②. char a = 'h'char :单引号,String a = "hello" :双引号
switch是否能作用在byte上,是否能作用在long上,是否能作用在String上?
Java5 以前 switch(expr)中,expr 只能是 byte、short、char、int。
从 Java 5 开始,Java 中引入了枚举类型, expr 也可以是 enum 类型。
从 Java 7 开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。 2.引用数据类型建立在基本数据类型的基础上,包括数组、类和接口。引用数据类型是由用户自定义,用来限制其他数据的类型。另外,Java 语言中不支持 C++中的指针类型、结构类型、联合类型和枚举类型。
访问修饰符public、private、protected、以及不写(默认)时的区别?
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
- default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类) 。
break、continue、return的区别及作用?
- break 跳出总上一层循环,不再执行循环(结束当前的循环体)
- continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
- return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
常见的集合有哪些?
Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生了三个子接口:List、Set、Queue,因此,Java集合大致也可分成List、Set、Queue、Map四种接口体系。
Java集合框架图如下:
图中List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。
Map代表的是存储key-value对的集合,可根据元素的key来访问value
淡蓝色背景覆盖的是集合体系中常用的实现类,分别是ArrayList、LinkedList、ArrayQueue、HashSet、TreeSet、HashMap、TreeMap等实现类
为什么使用集合而不是数组?
- 集合与数组的相似点:都可以存储多个对象,对外作为一个整体存在
- 数组的缺点:长度必须在初始化时指定,且固定不变;数组采用连续存储空间,删除和添加效率低下;数组无法直接保存映射关系;数组缺乏封装,操作频繁
集合中有哪些是线程安全的?哪些是线程不安全的?
线程安全的:
- Hashtable:比HashMap多了个线程安全
- ConcurrentHashMap:是一种高效但是线程安全的集合
- Vector:比ArrayList多了个同步化机制
- Stack:栈,也是线程安全的,继承于Vector 线程不安全的:
- HashMap
- ArrayList
- LinkedList
- HashSet
- TreeSet
- TreeMap
ArrayList和LinkedList的区别有哪些?
- ArrayList的底层数据结构是数组,支持下标访问,查询数据快,默认初始值大小为10,容量不足时会进行扩容
- LinkedList的底层数据结构是链表,将元素添加到链表的末尾,无需扩容
ArrayList和LinkedList分别适用于什么场景?
对于随机index访问的get和set方法,ArrayList的速度要优先于LinkedList,因为ArrayList直接通过数组下标找到元素;LinkedList要移动指针遍历每个元素直到找到为止
新增和删除元素,LinkedList的速度要优于ArrayList,因为ArrayList在新增和删除元素时,可能扩容和复制数组,而LinkedList的新增和删除操作只需要修改指针即可。
因此,ArrayList适用于查询多,增删少的场景,而LinkedList适用于查询少,增删多的场景
ArrayList和Vector的区别?
- Vector是线程安全的,ArrayList不是线程安全的。其中,Vector在关键性的方法前面都加了synchronized关键字,来保证线程的安全性。如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
- ArrayList在底层数组不够用时在原来的基础上扩展0.5倍,Vector是扩展1倍,这样ArrayList就有利于节约内存空间。
讲一下ArrayList的扩容机制?
ArrayList实现了List的接口,继承于AbstractList,从源码可以看到,在grow方法里面进行扩容,将数组容量扩大为原来的1.5倍,举个例子,如果初始化的值为8,当添加第九个元素时,发现数组空间不够,就会进行扩容。扩容后的容量为12,扩容后会调用Arrays.copyOf()方法对数组进行拷贝,把原数组的数据,原封不动的复制到新数组中,然后把ArrayList的地址指向新数组
以JDK1.8为例说明:
public boolean add(E e) {
//判断是否可以容纳e,若能,则直接添加在末尾;若不能,则进行扩容,然后再把e添加在末尾
ensureCapacityInternal(size + 1); // Increments modCount!!
//将e添加到数组末尾
elementData[size++] = e;
return true;
}
// 每次在add()一个元素时,arraylist都需要对这个list的容量进行一个判断。通过ensureCapacityInternal()方法确保当前ArrayList维护的数组具有存储新元素的能力,经过处理之后将元素存储在数组elementData的尾部
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 若ArrayList已有的存储能力满足最低存储要求,则返回add直接添加元素;如果最低要求的存储能力>ArrayList已有的存储能力,这就表示ArrayList的存储能力不足,因此需要调用 grow();方法进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 获取elementData数组的内存空间长度
int oldCapacity = elementData.length;
// 扩容至原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//校验容量是否够
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//若预设值大于默认的最大值,检查是否溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用Arrays.copyOf方法将elementData数组指向新的内存空间
//并将elementData的数据复制到新的内存空间
elementData = Arrays.copyOf(elementData, newCapacity);
}
Array和ArrayList有什么区别?
- Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
- Array 大小是固定的,ArrayList 的大小是动态变化的。
- ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator() 等等。
Set和List有什么区别?
List是以索引来存取元素,有序的,元素是允许重复的,可以插入多个null;Set不能存放重复元素,无序的,只允许插入一个null
List底层实现有数组、链表两种方式;Set基于Map实现,Set里的元素值就是Map的数值
还知道哪些线程安全的List?
可以使用Collections.synchronizedList()方法返回一个线程安全的List
还有另一种方式,使用CopyOnWriteArrayList
请详细讲下CopyOnWriteArrayList的原理?
CopyOnWriteArrayList是一个线程安全的List,底层是通过复制数组的方式来实现的,所谓的CopyOnWrite就是写时复制,当我们的容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器,这样做的好处就是可以对CopyOnWrite容器进行并发的读而不需要加锁,因为当前容器不会被修改
CopyOnWriteArrayList有什么缺点吗?
内存占用问题:由于CopyOnWrite写时复制机制,在进行写操作时,内存里会同时驻扎两个对象的内存
CopyOnWrite容器不能保证数据的实时一致性,可能读取到旧数据