1 数据类型

数据类型 描述 占用空间 默认值
boolean 布尔值 1字节 false
byte 8位有符号整数 1字节 0
char 16位Unicode字符 2字节 ‘\u0000’
short 16位有符号整数 2字节 0
int 32位有符号整数 4字节 0
float 32位单精度浮点数 4字节 0.0f
long 64位有符号整数 8字节 0L
double 64位双精度浮点数 8字节 0.0d

new Integer() && Integer.valueOf()

1
2
3
4
5
6
7
Integer integer1 = new Integer(100);
Integer integer2 = new Integer(100);
System.out.println(integer1 == integer2); // 输出 false,因为它们是不同的对象

Integer integer3 = Integer.valueOf(100);
Integer integer4 = Integer.valueOf(100);
System.out.println(integer3 == integer4); // 输出 true,因为它们是同一个对象

valueOf() 方法中,首先检查传入的整数是否在 -128127 的范围内。如果在这个范围内,会返回 IntegerCache 缓存中的对象;如果超出范围,则会创建新的 Integer 对象。

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
26
27
28
29
30
31
32
33
34
35
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}

public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

2 String

1
2
3
4
public final class String {
private final char value[];
...
}
可变性 线程安全性 性能
String 不可变 线程安全 性能较低
StringBuffer 可变 线程安全(使用synchronized 性能较高
StringBuilder 可变 非线程安全 性能较高

字符串创建的两种方式对比:

1
2
3
4
5
// 方式1:使用new,不会自动加入 String Pool
String s1 = new String("aaa");

// 方式2:使用字面量,自动加入 String Pool
String s2 = "aaa";
1
2
3
4
// 当需要大量重复的字符串时,使用intern()可以显著节省内存
String str1 = new String("hello").intern();
String str2 = new String("hello").intern();
System.out.println(str1 == str2); // true,因为都指向池中同一对象

3 运算

StackOverflow: Is Java “pass-by-reference” or “pass-by-value”?

4 继承

4.1 访问修饰符

  • public:可以被任何其他类访问。
  • default:只能被同一包内的类访问。
1
2
3
4
5
6
7
8
// 1. 类的访问控制
public class PublicClass { // 可以被任何其他类访问,必须与文件名相同
// ...
}

class PackagePrivateClass { // 默认访问级别,只能被同一包内的类访问
// ...
}

字段(成员变量)/方法(成员方法)

  • private:只能在当前类中访问/调用。
  • default:同包内的类可以访问/调用。
  • protected:同包内的类和不同包的子类可以访问/调用。
  • public:所有类都可以访问/调用。
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
// 2. 类的成员(字段和方法)的访问控制
public class AccessExample {
// 2.1 字段的四种访问级别
private int privateField; // 只能被当前类访问
int packagePrivateField; // 默认访问级别,只能被同包的类访问
protected int protectedField; // 只能被同包内的类和不同包的子类访问
public int publicField; // 可以被所有类访问

// 2.2 方法的四种访问级别
private void privateMethod() {
// 只能被当前类调用
}

void packagePrivateMethod() {
// 默认访问级别,只能被同包内的类调用
}

protected void protectedMethod() {
// 只能被同包内的类和不同包的子类调用
}

public void publicMethod() {
// 可以被所有类调用
}
}

内部类

  • 成员内部类:可以使用所有四种访问修饰符
  • 局部内部类:不能使用访问修饰符
  • 静态内部类:可以使用所有四种访问修饰符
  • 匿名内部类:不能使用访问修饰符
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 3. 内部类的访问控制
public class OuterClass {
// 3.1 成员内部类(可以使用四种访问修饰符)
public class PublicInnerClass {
// ...
}

protected class ProtectedInnerClass {
// ...
}

class PackagePrivateInnerClass {
// ...
}

private class PrivateInnerClass {
// ...
}

// 3.2 局部内部类(定义在方法内,不能使用访问修饰符)
public void method() {
class LocalInnerClass { // 只在方法内部可见
// ...
}
}

// 3.3 静态内部类(可以使用四种访问修饰符)
public static class PublicStaticInnerClass {
// ...
}

private static class PrivateStaticInnerClass {
// ...
}

// 3.4 匿名内部类(没有访问修饰符)
Runnable runnable = new Runnable() {
@Override
public void run() {
// ...
}
};
}

4.2 抽象类(abstract)

抽象类是一种不能被实例化、只能被继承的特殊类,使用 abstract 关键字声明。它可以包含抽象方法(没有实现的方法,由子类实现)和具体方法(有实现的方法),并且可以拥有构造方法、成员变量和静态方法。抽象类主要用于定义子类的共同特征和行为,通过将公共部分抽象到父类中来实现代码重用,同时通过抽象方法强制子类实现特定的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class AbstractDataProcessor {
// 模板方法,使用 final 修饰防止子类重写
public final void processData() {
openConnection();
readData();
processBusinessLogic();
saveData();
closeConnection();
}

// 具体方法,所有子类共用的实现
private void openConnection() {
System.out.println("Opening database connection...");
}

private void closeConnection() {
System.out.println("Closing database connection...");
}

// 抽象方法,子类必须实现的特定业务逻辑
protected abstract void readData();
protected abstract void processBusinessLogic();
protected abstract void saveData();
}

具体实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserDataProcessor extends AbstractDataProcessor {
@Override
protected void readData() {
System.out.println("Reading user data...");
}

@Override
protected void processBusinessLogic() {
System.out.println("Processing user data...");
}

@Override
protected void saveData() {
System.out.println("Saving processed user data...");
}
}

4.3 接口(interface)

接口是抽象类的延伸

修饰符规则:

  • 字段默认是 public static final
  • 方法默认是 public abstract
1
2
3
4
public interface InterfaceExample {
int field = 0; // 等同于 public static final int field = 0
void method(); // 等同于 public abstract void method()
}

访问权限:

  • 所有成员都必须是 public
  • 不允许使用 privateprotected
1
2
3
4
5
6
7
8
9
public interface InterfaceExample {
// 错误:不能使用 private
// private int field = 0;
// private void method();

// 错误:不能使用 protected
// protected int field = 0;
// protected void method();
}

接口演进历史:

  1. Java 8 之前:只能有抽象方法,不能有方法实现。
  2. Java 8 的改进:增加了默认方法和静态方法。

重载(override) vs 重写(overload

5 Object

java.lang.Object

1
2
3
4
5
6
7
8
9
10
11
12
private static native void registerNatives();
public final native Class<?> getClass();
public native int hashCode();
public boolean equals(Object obj);
protected native Object clone() throws CloneNotSupportedException;
public String toString();
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
public final void wait() throws InterruptedException;
protected void finalize() throws Throwable;

equals() vs ==

  • 对于基本类型,== 判断两个值是否相等。
  • 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。

hashCode():主要作用是为每个对象提供一个哈希值。默认的实现是 Object 类中提供的实现,它基于对象的内存地址来生成哈希值。每个对象调用 hashCode() 时都会返回一个整数,作为该对象的标识符。hashCode() 方法与 equals() 方法一起工作,用于判断两个对象是否相等。

在哈希表中,不同的对象可能会有相同的哈希值,这种情况称为 哈希冲突。哈希冲突是无法避免的,但是 hashCode() 方法应该尽量保证哈希值分布均匀,从而减少冲突的概率。

根据 Java 的规范,如果重写了 equals() 方法,那么必须也重写 hashCode() 方法,以确保相等的对象具有相等的哈希值。否则,哈希表可能无法正常工作。

clone()

1
2
3
4
5
6
7
8
public class CloneExample implements Cloneable {
// ...

@Override
protected CloneExample clone() throws CloneNotSupportedException {
return (CloneExample)super.clone();
}
}

浅拷贝:创建一个新对象,复制原对象的基本数据类型值和引用类型的引用(地址)。

  • 基本类型:直接复制值
  • 引用类型:复制引用,指向同一个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Address {
private String city;
// getter/setter
}

public class ShallowCopyDemo implements Cloneable {
private int id;
private String name;
private Address address;

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

ShallowCopyDemo original = new ShallowCopyDemo();
try {
ShallowCopyDemo copy = (ShallowCopyDemo) original.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}// original.address 和 copy.address 指向同一个对象

深拷贝:创建一个新对象,复制原对象的所有字段值,并递归复制所有引用类型的对象。

  • 基本类型:直接复制值
  • 引用类型:创建新对象,递归复制
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Address implements Cloneable{
private String city;

// getter/setter

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

public class DeepCopyDemo implements Cloneable {
private int id;
private String name;
private Address address;

public DeepCopyDemo(int id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}

// getter/setter

@Override
protected Object clone() throws CloneNotSupportedException {
DeepCopyDemo copy = (DeepCopyDemo) super.clone();
copy.address = (Address) address.clone();
return copy;
}
}

DeepCopyDemo original = new DeepCopyDemo(1, "测试", new Address("北京"));
try {
DeepCopyDemo copy = (DeepCopyDemo) original.clone();

System.out.println(original.getId() == copy.getId());
System.out.println();

System.out.println(original.getName().equals(copy.getName()));
System.out.println(original.getName() == copy.getName());
System.out.println();

System.out.println(original.getAddress() == copy.getAddress());
System.out.println(original.getAddress().getCity().equals(copy.getAddress().getCity()));
System.out.println(original.getAddress().getCity() == copy.getAddress().getCity());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

true

true
true

false
true
true

6 关键字

6.1 final

  • 修饰类:
1
2
3
4
5
6
7
final class FinalClass {
// 该类不能被继承
}

// 错误示例:
class SubClass extends FinalClass { // 编译错误
}
  • 修饰方法:
1
2
3
4
5
6
7
8
9
10
11
class Parent {
final void finalMethod() {
// 该方法不能被子类重写
}
}

class Child extends Parent {
// 错误示例:
void finalMethod() { // 编译错误
}
}
  • 修饰变量:
  1. 基本类型
1
2
final int num = 10;
num = 20; // 编译错误
  1. 用类型
1
2
3
final StringBuilder sb = new StringBuilder("hello");
sb.append(" world"); // 正确:可以改变对象的内容
sb = new StringBuilder("hi"); // 编译错误:不能改变引用
  1. 类成员变量
1
2
3
4
5
6
7
8
class Demo {
// 必须在声明时或构造器中初始化
final int value;

Demo() {
value = 10; // 构造器中初始化
}
}
  1. 静态常量
1
2
3
4
class Constants {
public static final double PI = 3.14159;
public static final String PREFIX = "user_";
}

6.2 static

  • 静态变量
  • 静态方法
  • 静态代码块
  • 静态内部类
  • 静态导入