曹耘豪的博客

Java入门

  1. 二、八、十六进制
    1. 表示方法
    2. 转为对应String
    3. 从String转为对应Int
  2. String加法
    1. StringBuilder
    2. StringBuffer
    3. StringBuilder 和 StringBuffer 比较
    4. 可以直接使用"+"吗?
  3. 基本类型及其数组类型
  4. 反射
    1. 访问私有字段
    2. 访问私有方法
    3. 访问静态私有方法
  5. Java集合
    1. Queue
      1. 单线程实现
      2. 多线程实现
    2. Deque
      1. 单线程实现
      2. 多线程实现
    3. SortedMap / NavigableMap
      1. 单线程实现
      2. 多线程实现
    4. SortedSet / NavigableSet
  6. ArrayList和LinkedList的原理和使用场景
    1. ArrayList
    2. LinkedList
    3. 实现和复杂度对比
  7. 数字的大小比较

二、八、十六进制

2018-09-18

表示方法

转为对应String

从String转为对应Int

支持2~36进制,10个数字+26个字母

String加法

2018-09-16

在我们编写Java代码过程中,经常有拼接字符串的需求,通常有三种方法:

首先介绍一下StringBuilder

StringBuilder

先看一下如何使用StringBuilder

1
2
3
4
StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
sb.toString(); // "ab"

通过append操作拼接字符串,最后通过toString方法得到最终的字符串。

StringBuffer

StringBufferStringBuilder的使用方法差不多。

1
2
3
4
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.toString(); // "ab"

既然差不多,为什么还要另搞一个StringBuffer呢?查看源码可以看到,StringBuffer的操作都是有synchronized关键字的,也就是说是线程安全的。

StringBuilder 和 StringBuffer 比较

所以我们可以总结,

可以直接使用"+"吗?

当然可以,不然发明它干吗~~

假如,我们编写以下程序,直接相加2个字符串

1
String ret = str1 + str2;

build后查看class文件,发现是这样的:

1
String ret = (new StringBuilder()).append(str1).append(str2).toString())

也就是说,如果直接采用+进行字符串的合并,编译器会将其转为StringBuilder,所以放心使用String的加法吧~

只是

如果你要写下面的代码

1
2
3
4
String ret = "";
for (String str: strs) {
ret += str;
}

看看build后的结果

1
2
3
4
String ret = "";
for (String str: strs) {
ret = (new StringBuilder()).append(ret).append(str).toString();
}

显然,这将会产生n多个StringBuilder,大大增加了gc开销,这个时候,StringBuilderStringBuffer便派上用场了,所以,知道该怎么做了吗。

基本类型及其数组类型

2019-03-12

基本类型class名(=本身)对应数组class名
bytebyte[B
intint[i
longlong[J
floatfloat[F
doubledouble[D
booleanboolean[z

下面以byte为例说明这个问题

1
2
3
4
5
6
7
8
System.out.println(byte.class.getName()); // byte

// throw exception: java.lang.ClassNotFoundException: byte
System.out.println(Class.forName("byte").getName());

System.out.println(byte[].class.getName()); // [B

System.out.println(Class.forName("[B").getName()); // [B

反射

2019-04-09
有时,现有的类库接口不能满足我们的需求(它们往往封装的过好,隐藏了内部细节,以至于把这些细节都设为了private方法)。我们确实需要主动执行这些内部方法,以达到某种目的。大多数时候,这种需求会添加在该库的未来的版本中,但我们由于各种各样的原因不能升级,需要在当前版本满足我们的需求,问题来了,如何主动执行受保护的方法,答案是,反射。

有以下类A,我们要在外部访问它的 private 字段,执行它的 private 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class A {
private int intValue = 1;
private String strValue = "a";

public A(int intValue) {
this.intValue = intValue;
}

private int getIntValue() {
return intValue;
}

private static A of(int intValue) {
return new A(intValue);
}
}

访问私有字段

1
2
3
4
5
A a = new A(1);
Field field = A.class.getDeclaredField("intValue");
field.setAccessible(true); // 关键!
int fieldValue = (int) field.get(a);
System.out.println(fieldValue); // 1

访问私有方法

1
2
3
4
5
A a = new A(1);
Method getIntValueMethod = A.class.getDeclaredMethod("getIntValue");
getIntValueMethod.setAccessible(true); // 关键!
int intValue = (int) getIntValueMethod.invoke(a);
System.out.println(intValue); // 1

访问静态私有方法

1
2
3
4
Method staticMethod = A.class.getDeclaredMethod("of", int.class); // 第一个是方法名,第二个是函数参数的类型
staticMethod.setAccessible(true); // 关键!
A newA = (A) staticMethod.invoke(null, "A"); // 对于静态方法,第一个参数可以传任意类型的任意值
System.out.println(newA);

Java集合

2023-01-16

Queue

单线程实现

多线程实现

Deque

单线程实现

多线程实现

SortedMap / NavigableMap

排序Map / 可导航Map

单线程实现

多线程实现

SortedSet / NavigableSet

基于 SortedMap / NavigableMap

ArrayList和LinkedList的原理和使用场景

2023-02-23

ArrayList

LinkedList

实现和复杂度对比

类型ArrayListLinkedList
实现方式动态数组双向链表
内存分步连续不连续
查询效率O(n)O(n)
随机访问效率***O(1)***,下标访问*O(n)*,遍历至指定位置
随机增加/删除效率*O(n)*,需移动数组,但很快*O(n)*,遍历找到节点
头部添加删除效率*O(n)*,需移动后续数组O(1)
尾部添加删除效率*O(1)*,添加时可能需要扩容数组O(1)

数字的大小比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
assert 1.00f == 1.000f;
assert 1.00d == 1.000f;
assert 1.00d == 1.000d;
assert new Float("1.00").equals(new Float("1.000"));
assert new Double("1.00").equals(new Double("1.000"));
assert !new Double("1.00").equals(new Float("1.00"));

assert new BigDecimal("1.00").equals(new BigDecimal("1.00"));
assert !new BigDecimal("1.00").equals(new BigDecimal("1.000")); // 比较精度
assert new BigDecimal("1.00").compareTo(new BigDecimal("1.000")) == 0; // 不比较精度

assert new BigDecimal("0").signum() == 0;
assert new BigDecimal("0.00").signum() == 0;
assert new BigDecimal("-0.00").signum() == 0;

assert String.valueOf(Double.POSITIVE_INFINITY).equals("Infinity");
assert String.valueOf(Double.NEGATIVE_INFINITY).equals("-Infinity");
assert Double.parseDouble("Infinity") == Double.POSITIVE_INFINITY;

assert String.valueOf(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY).equals("NaN");
assert Double.parseDouble("NaN") != Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY;
assert Double.valueOf("NaN").equals(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY);

assert 0.0d != Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY;
assert !Double.valueOf(0.0d).equals(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY);
   /