java se
逻辑运算符
& 和 &&
短路与:&& 条件1 && 条件二 两者都为true,结果返回true
逻辑与:& 条件1 & 条件二 两者都为true,结果返回true
两者的区别
- 短路与&&: 如果第一个条件为false,则第二个条件不会判断,最终结果为false,效率高
- &逻辑与: 不管第一个条件是否为false,第二个条件都要判断,效率低
- 在开发中,基本使用短路与&&,效率高。
| 和 ||
短路或|| 条件1 || 条件2 两个条件中只要有一个成立,结果为true
逻辑或| 条件1 | 条件2 两个条件中只要有一个成立,结果为true
两者的区别:
- || 短路或,如果第一个条件为true,则第二个条件不会判断,最终结果为true,效率高
- | 逻辑或,不管第一个条件是否为true,第二个条件都要判断,效率低
- 开发中基本使用短路或 ||
! 逻辑非
!取反 !条件 如果条件本身为false,结果为true,否则为false
^ 逻辑异或
a^b: 当a和b不相同时,结果为true,否则为false
位运算
包装类
String
- String是一个final类,代表不可变的字符序列
- 字符长是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
StringBuffer
- 代表可变的字符序列,可以对字符串内容进行增删
- 很多方法与String相同,但StringBuffer是可变长度的。
- StringBuffer是一个容器
- StringBuffer 的直接父类是 AbstractStringBuilder
- StringBuffer 实现了 Serializable,即StringBuffer的对象可以串行化
- 在父类中, AbstractStringBuilder 有属性 char[] vakue,不是final 该value 数组存放字符串内容,存放在堆中
- StringBuffer是一个final类,不能被继承
- StringBuffer字符内容是存在 char[] value, 所有的变化(增加/删除)不用每次都更换地址(即不是每次创建新对象),所以效率高于String
StringBuffer的构造器
StringBuilder
继承关系:
枚举类
- 英文enumeration 简写enum
- 枚举是一组常量的集合
- 可以理解为:枚举属于一种特殊的类,里面只包含一组有限的特定的对象。
枚举的两种实现方式
自定义类实现枚举
public class Enumeration01 {
public static void main(String[] args) {
String a = "sdf";
String b = "sd";
String c = "f";
String d = b+c;
}
}
class Season{
private String name;
private String desc;
public static final Season SPRING = new Season("春天","温暖");
public static final Season WINTER = new Season("冬天","寒冷");
public static final Season AUTUMN = new Season("秋天","凉爽");
public static final Season SUMMER = new Season("夏天","炎热");
//将构造器私有化,防止被直接new
//不需要写set方法,防止属性被修改
//在Season内部直接创建固定的对象
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
使用enum关键字实现枚举
注意事项:
- 当我们使用enum关键字开发一个枚举类时,默认会继承Enum类
- 定义常量(对象)简化为常量名(参数),这里必须知到调用的是哪个构造器
- 如果使用无参构造器创建枚举对象,则实例列表和小括号都可以省略
- 当有多个枚举对象时,使用逗号间隔,最后一个用分号结尾
- 枚举对象必须放在枚举类的行首
- 枚举类不能在继承其他类,但是可以实现接口
public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season2.SPRING);
System.out.println(Season2.SUMMER);
}
}
enum Season2{
//使用enum实现枚举类
//1.使用关键字enum代替class
//2.public static final Season SPRING = new Season("春天","温暖"); 直接使用SPRING("春天","温暖");
//如果有多个对象(常量),使用,号间隔。
//4.如果使用enum实现枚举,要求将定义常量对象写在最上面。
SUMMER("夏天","炎热"),AUTUMN("秋天","凉爽"),
WINTER("冬天","寒冷"),SPRING("春天","温暖"),
WHAT;
private String name;
private String desc;
// public static final Season SPRING = new Season("春天","温暖");
// public static final Season WINTER = new Season("冬天","寒冷");
// public static final Season AUTUMN = new Season("秋天","凉爽");
// public static final Season SUMMER = new Season("夏天","炎热");
Season2() {
}
//将构造器私有化,防止被直接new
//不需要写set方法,防止属性被修改
//在Season内部直接创建固定的对象
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
集合
Collection
List Set
iterator
Collection接口遍历元素方式-Iterator(迭代器)
public class Arraylist {
public static void main(String[] args) {
ArrayList list = new ArrayList();
for (int i = 0; i < 10; i++) {
list.add(i);
}
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//当while循环结束后,iterator指向list的最后一个元素,若想再次遍历,需要重置iterator
//iterator = list.iterator();
}
}
使用增强for循环也可以遍历集合,而增强for循环的底层就是iterator
在底层对于数组来说:底层依然使用普通for循环迭代数组,使用数组的索引来获取每一个元素。对于Iterable对象来说,底层依然使用迭代器(Iterator)
List接口
List接口是Collection接口的子接口
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
ArrayList
是线程不安全的。
是由数组来实现底层存储的
ArrayList底层结构及源码分析
-
在ArrayList中维护了一个elementData数组(transient Object[] elementData;),其类型为Object,所以ArrayList可以存放任何类型的数据。//transient 表示该属性不会被序列化
-
如果在创建ArrayList对象时时使用无参构造器,则初始elementData容量为0,第一次添加,则扩容为elementDatta为10,如需要再次扩容,则扩容elementData为1.5倍(扩容1.5倍根据语句:int newCaoacity = oleCapacity + (oldCapacity >> 1) )
-
如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
//源码 ArrayList无参构造 创建了一个空的数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private Object[] grow() {
return grow(size + 1);
}
/**
* Returns a capacity at least as large as the given minimum capacity.
* Returns the current capacity increased by 50% if that suffices.
* Will not return a capacity greater than MAX_ARRAY_SIZE unless
* the given minimum capacity is greater than MAX_ARRAY_SIZE.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容机制,位运算,就等于oldCapacity+oldCapacity/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
https://blog.csdn.net/weixin_46232441/article/details/122654615
此链接有jdk11版本的详细讲解。
有参构造:
//在有参构造创建ArrayList时,会跳转至此方法,将创建一个新的以参数为大小的数组,并指向elementData
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//若后续需要扩容,则与无参构造的扩容步骤一致
Vector
Vector底层也是一个对象数组,protected Object[] elementData;
Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
Vector 底层有数组实现,支持动态扩容
Vector 是线程安全的,是由 synchronized实现的, 效率较低
Vector 扩容时数组长度会变成原来两倍或者原长度加上capacityIncrement
Vector 支出存储多个相同的元素(包括多个null),且是有序的。
//Vector扩容源码
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private Object[] grow() {
return grow(elementCount + 1);
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容运算,增加一倍。
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity <= 0) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
LinkedList
- 底层实现了双向链表和双端队列的特点
- 可以添加任何元素(元素可以重复),包括null
- 线程不安全,没有实现同步
底层结构
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾结点
- 每个节点(node对象),里面有维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表
- 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
//LinkedList 添加元素的源码
//很简单地便可以发现其中的逻辑,当list集合为空时,使last和first都指向添加的新节点。
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
set接口
- 无序(添加和取出的顺序不一致),没有索引
- 不允许添加重复元素,所以最多包含一个null
- JDK API中Set接口的实现类有:
- AbstractSet
- ConcurrentSkipListSet
- CopyOnWriteArraySet
- EnumSet
- HashSet
- JobStateReasons
- LikedHashSet
- TreeSet
重点学习HashSet和LikedHashSet
HashSet
HashSet实现了Set接口
HashSet实际上是HashMap(HashMap的底层是数组+链表+红黑树)
//HashSet源码 public HashSet() { map = new HashMap<>(); }
可以存放null,但只能存放一个
HashSet不保证元素是有序的,取决于hash后,再确认索引的结果(即不保证存取的顺序与取出的顺序是一致的)
不能有重复元素/对象
HashSet若是无参构造的底层数组第一次扩容为16
经典面试题:
package edu.Collection.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Hashset学习 {
public static void main(String[] args) {
Set set = new HashSet();
set.add("yoh"); //ok
set.add("yoh"); //false
set.add(new Dog("tom")); //ok
set.add(new Dog("tom")); //ok
System.out.println(set);
set.add(new String("sdf")); //ok
set.add(new String("sdf")); //false
System.out.println(set);
}
}
class Dog{
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
HashSet扩容机制
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {