转载

Java基础(笔记)

引言

Java基础(笔记)

在学习《 Java 编程的逻辑》一书时记录的一些笔记,扫清了一些 Java 基础的知识盲区,感谢作者马俊昌老师。

数据类型

序号 数据类型 大小(位) 包装类 默认值 数据范围
1 byte 8 Byte 0 -128 ~ 127
2 short 16 Short 0 -32768 ~ 32767
3 int 32 Integer 0 -2147483648 ~ 2147483647
4 long 64 Long 0L -9223372036854775808 ~ 9223372036854775807
5 boolean 8 Boolean false true/false
6 char 16 Character 0 ~ 65535
7 float 32 Float 0.0F 1.4E-45 ~ 3.4028235E38
8 double 64 Double 0.0D 4.9E-324 ~ 1.7976931348623157E308

装/拆箱

装箱:将基本类型转换为包装类的过程。

拆箱:将包装类型转换为基本类型的过程。

Java 5 以后引入了自动装箱和拆箱技术:

Integer a = 100;
int b = a;

自动装箱/拆箱是 Java 编译器提供的能力,背后,它会替换为调用对应的 valueOf/xxx-Value 方法,比如,上面的代码会被 Java 编译器替换为:

Integer a = Integer.valueOf(100);
int b = a.intValue();

valueOf

一般建议使用 valueOf 方法。

new 每次都会创建一个新对象,而除了 FloatDouble 外的其他包装类,都会缓存包装类对象,减少需要创建对象的次数,节省空间,提升性能。

缓存

IntegerCache 表示 Integer 缓存,其中的 cache 变量是一个静态 Integer 数组,在静态初始化代码块中被初始化。

默认情况下,保存了 -128~127256 个整数对应的 Integer 对象。

valueOf 代码中,如果数值位于被缓存的范围,即默认 -128~127 ,则直接从 Integer-Cache 中获取已预先创建的 Integer 对象,只有不在缓存范围时,才通过 new 创建对象。

序号 数据类型 数据缓存
1 Boolean true, false
2 Byte -128 ~ 127
3 Short -128 ~ 127
4 Integer -128 ~ 127
5 Long -128 ~ 127
6 Character 0 ~ 127
7 Float 无缓存
8 Double 无缓存

Switch语句

支持的数据类型

  • Java 5 之前,只支持 byteshortintchar 四种。
  • Java 5 中,引入了枚举类型。因为枚举类的 ordinal 返回一个 int 值。
  • Java 7 中,支持 String 类型,使用 StringhashCode 方法。

原因

switch 的效率相对较高,编译后使用跳转表实现。

跳转表有序,可以二分查找,所以支持的数据类型都是整数,枚举和 String 也是转换为 int 值。

跳转表的存储空间是 32 位,容纳不下 long ,所以 switch 不支持 long 类型。

接口

接口中变量修饰符是 public static final

接口中方法修饰符是 public abstract

java 8 允许在接口里定义默认方法 default 和静态方法 static

java 9 允许默认方法和静态方法可以是 private

序列化

条件

一个类的对象要想序列化成功,必须满足两个条件:

java.io.Serializable
transient

否则会抛出 NotSerializableException 异常。

注意

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致。

序列化并不保存静态变量。

父类如果没有实现 Serializable 接口时,反序列化时,会调用父类无参的构造函数。

Externalizable

Externalizable 接口继承自 java.io.Serializable

public interface Externalizable extends java.io.Serializable {

    void writeExternal(ObjectOutput out) throws IOException;

    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

实现 Externalizable 接口后,序列化的细节由开发人员自己实现。

并且 Externalizable 的优先级比 Serializable 的优先级高。

异常

try/catch/finally 语法中, catch 不是必需的,也就是可以只有 tryfinally ,表示不捕获异常,异常自动向上传递,但 finally 中的代码在异常发生后也执行。

java 7 开始支持:多个异常之间可以用 | 操作符:

try {
    // 可能抛出ExceptionA和ExceptionB
} catch (ExceptionA | ExceptionB e) {
    // 异常处理逻辑
}

try-with-resources

java 7 提供了一种新的语法,称之为 try-with-resources

这种语法针对实现了 java.lang.AutoCloseable 接口的对象,接口定义:

public interface AutoCloseable {
    void close() throws Exception;
}

语法形式如下:

try (AutoCloseable resource = new FileInputStream("yunzhi.txt")) {
    // 使用资源
}

资源 resource 的声明和初始化放在 try 语句内,不用再调用 finally ,在执行完 try 语句后,会自动调用资源的 close 方法。

String

String 类内部用一个字符数组表示字符串,实例变量定义为:

private final char value[];

常量字符串

System.out.println("yunzhi.club".length());
System.out.println("yunzhi.club".contains("yunzhi"));
System.out.println("yunzhi.club".indexOf("yunzhi"));

实际上,这些常量就是 String 类型的对象,在内存中,它们被放在一个共享的地方,这个地方称为字符串常量池,它保存所有的常量字符串,每个常量只会保存一份,被所有使用者共享。

当通过常量的形式使用一个字符串的时候,使用的就是常量池中的那个对应的 String 类型的对象。

+/+=

Java 中, String 可以直接使用 ++= 运算符,这是 Java 编译器提供的支持,背后, Java 编译器一般会生成 StringBuilder++= 操作会转换为 append

String hello = "hello";
hello += ", world";
System.out.println(hello);

背后, Java 编译器一般会转换为:

StringBuilder hello = new StringBuilder("hello");
hello.append(", world");
System.out.println(hello.toString());

对于简单的情况,可以直接使用 String++= ,对于复杂的情况,尤其是有循环的时候,应该直接使用 StringBuilder

为什么要定义为不可变类呢?

不可变使得程序更为简单安全,因为不用操心数据被意外改写的可能,可以安全地共享数据,尤其是在多线程的环境下。

Map和Set

HashMap

hash

默认采用单链表解决冲突,如果链表长度超过 8 ,将单链表转换为红黑树。

负荷系数

static final float DEFAULT_LOAD_FACTOR = 0.75f;

为什么容量是2的幂

  • 申请内存时,减少内存碎片。
  • 进行移位操作,效率高。
  • tab[i = (n - 1) & hash] ,使用 n-1& 运算,提高效率。

其他

扩容的条件:实际节点数大于等于容量的 0.75 ,就是负荷系数。

因为不同版本的 JDK 计算的 hash 值可能是不同的。只存储了数组的容量、实际节点数量和各个节点的 key value 值。

HashSet

O(1)

HashSet 内部是调用 HashMap 来实现的,核心就是利用 HashMap 中的 key 不能相同进行去重, key 就是 Set 中的值, value 存了一个空对象。

TreeMap

TreeMap 按键有序,为了实现有序,要求要么键实现 Comparable 接口,要么创建 TreeMap 时传递一个 Comparator 对象。

内部使用红黑树实现,存储映射。

红黑树

红黑树是一种平衡二叉树,但它不是高度平衡的,而是大致平衡的。确保任意一条从根到叶子节点的路径,没有任何一条的路径的长度会比其他路径长过两倍。

红黑树减弱了对平衡的要求,降低了保持树平衡需要的开销,在实际应用中,统计性能超过平衡二叉树。

TreeSet

TreeSet 内部是基于 TreeMap 的,实现了有序。

LinkedHashMap

HashMap 的子类,内部使用双向链表维护键值对的顺序,每个键值对既位于哈希表中,也位于这个双向链表中。

支持插入顺序维护或访问顺序维护。

插入顺序:先添加的在前面,后添加的在后面,修改操作不影响顺序。

访问顺序:对一个键值对进行 get/put 操作,该键值对会移动到链表末尾,最末尾的是最近访问的,最开始的是最久没有访问的。

LinkedHashSet

HashSet 的子类,内部采用 LinkedHashMap 实现。

EnumMap

一个 key 是枚举类型的 Map

内部使用了两个长度相同的数组,一个存键,一个存储对应的值,值为 null 表示没有该键值对。键都有一个对应的索引,根据索引可直接访问和操作其键的值,效率高。

EnumSet

EnumSet 的实现和 EnumMap 没有任何关系!

采用位向量实现。 0 代表没有存这个枚举值, 1 代表存了这个枚举值,效率很高。

Java基础(笔记)

PriorityQueue

优先级队列,内部采用堆实现。和其他需要比较的操作一样,要么实现 Comparable 接口,要么创建时传递一个 Comparator 对象。

Java基础(笔记)

适用场景

TOP K问题

维护一个最小堆,堆的根节点是最小的,即第 K 大元素。

每次有新数据来,和根节点比,如果小,不操作,如果大,替换根节点,调整堆。调整的复杂度为 O(logK)

求中值元素

维护一个最大堆和最小堆,假设当前中位数为 M ,最大堆维护 <=M 的元素,最小堆维护 >=M 的元素,但两堆中都不包含 M

新数据来时,如果 <=M ,放到最大堆中,如果 >=M ,放到最小堆中。

数据加入之后,如果两堆元素相差 >=2 ,将 M 加入到元素少的堆中,元素多的堆的根节点移除作为新的中值元素。

Java基础(笔记)

集合关系图

之前一直很怕这张图,但是随着积累,图中的类我们也逐渐地学会并掌握了。

Java基础(笔记)

Collections

Collections 常用方法。

查找和替换

方法名 方法描述
binarySearch 二分查找
max 最大值
min 最小值
frequency 集合中元素出现次数
indexOfSubList lastIndexOfSubList 在原 List 中查找目标 List 的位置
disjoint 集合是否有交集
replaceAll List 中元素替换

排序和调整顺序

方法名 方法描述
sort 排序
swap 元素交换
reverse 列表反转
shuffle 洗牌,随机打乱顺序
rotate 循环移位

适配器

方法名 方法描述
emptyList emptySet emptyMap emptyIterator 返回空的集合,使用时比返回 null 更合理,更安全
singleton singletonList singletonMap 将单一对象转化为标准容器接口对象

装饰器

方法名 方法描述
unmodifiableCollection unmodifiableList unmodifiableMap unmodifiableSet 让容器只读
synchronizedCollection synchronizedList synchronizedMap synchronizedSet 让容器线程安全,不是最优实现。
原文  https://segmentfault.com/a/1190000020010418
正文到此结束
Loading...