Java八股(关键字)

final 关键字

final关键字主要有以下三个方面的作用:用于修饰类、方法和变量。

final 修饰类

final修饰一个类时,表示这个类不能被继承,是类继承体系中的最终形态。

final 修饰方法

final修饰的方法不能被重写

final 修饰变量

final 修饰基本数据类型变量

final修饰基本数据类型的变量时,该变量一旦被赋值就不能再改变。例如,final int num = 10;,这里的num就是一个常量,不能再对其进行重新赋值操作,否则会导致编译错误。并且在创建时必须赋值

final 修饰引用类型变量

当final修饰一个引用数据类型变量的时候,该变量在类初始化完成之前,必须完成赋值。

static 关键字

static 关键字主要用于修饰类的成员(变量、方法、代码块)和内部类,其核心作用是将成员与类本身关联,而非与类的实例(对象)关联

用static修饰的成员和内部类都只和类本身有关,无论创建该类的多少个对象,该值都是同一个共享值。更具这一特性,可以用静态值来实现计数器等功能。

static 修饰方法

静态方法属于类,不属于任何实例,因此不能直接访问类中的非静态成员(变量 / 方法)(因为非静态成员依赖于对象存在),但可以访问静态成员。通过 类名.方法名 直接调用,无需创建对象。

通常用于工具类方法(如 Math.random())、工厂方法等,不需要依赖对象状态即可完成操作。

正是因为static修饰的成员都是直接服务类的,而不是对象,所以可以通过类名直接调用静态的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MathUtils {
// 静态方法(无需创建对象即可调用)
public static int add(int a, int b) {
return a + b;
}
}

// 调用静态方法
public class Test {
public static void main(String[] args) {
int result = MathUtils.add(2, 3); // 直接通过类名调用
}
}

static 修饰变量

被 static 修饰的变量属于类本身,而非类的某个实例。所有对象共享同一份静态变量,内存中只存在一份副本。可以通过 类名.变量名 直接访问,无需创建对象。

通常用于存储所有对象共享的数据,如常量、计数器等。

也可以修饰引用对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Student {
// 静态变量(所有学生共享同一个学校名称)
public static String schoolName = "阳光中学";
// 实例变量(每个学生有自己的姓名)
private String name;
}

// 访问静态变量
public class Test {
public static void main(String[] args) {
System.out.println(Student.schoolName); // 直接通过类名访问
}
}

static 修饰代码块

静态代码块在类加载时执行,且只执行一次(优于对象构造方法),用于初始化静态变量或执行类级别的预处理操作。
意味着,虽然静态修饰的final对象在定义的时候必须初始化,但是可以通过静态代码块来完成初始化过程,这样的操作在redis的 java 连接池创建中很常见。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class jedisConnectionFactory {
private static final JedisPool jedisPoll;
//final 定义的引用类型变量 在类初始化完成之前 必须被初始化
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大链接
jedisPoolConfig.setMaxTotal(8);
//最大空闲链接
jedisPoolConfig.setMaxIdle(8);
//最小空闲链接
jedisPoolConfig.setMinIdle(0);
jedisPoolConfig.setMaxWait(Duration.ofMillis(200));
jedisPoll = new JedisPool(jedisPoolConfig, "123.207.22.15", 6379,1000,"Ljj20010315_");
}
public static Jedis getJedis() {
return jedisPoll.getResource();
}
}

static 修饰内部类

用static修饰内部类后,内部类就变成了静态内部类,静态内部类不能调用外部类中的变量和方法。想要访问内部类,就必须要创建外部类的实例进行访问。

当内部类与外部类的实例无关时使用,避免内部类持有外部类的引用导致的内存泄漏。(外部类没用用上,但是不用static修饰就必须创建外部类,由此导致外部类中的变量、方法都还存在在内存中,最终导致内存泄露。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class OuterClass {
private static int staticVar = 10;
private int instanceVar = 20;

// 静态内部类
public static class StaticInnerClass {
public void print() {
System.out.println(staticVar); // 可访问外部类静态变量
// System.out.println(instanceVar); // 错误:不能直接访问非静态变量
}
}
}

// 使用静态内部类
public class Test {
public static void main(String[] args) {
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
inner.print();
}
}

深拷贝和浅拷贝

深拷贝和浅拷贝的区别

浅拷贝:浅拷贝是指只复制对象本身和其内部的值类型字段,但不会复制对象内部的引用类型字段。

深拷贝:深拷贝是指在复制对象的同时,将对象内部的所有引用类型字段的内容也复制一份,而不是共享引用。

image.png

实现深拷贝的三种方法

实现 Cloneable 接口并重写 clone() 方法

要求对象及其所有引用类型字段都实现 Cloneable 接口,并且重写 clone() 方法。在 clone() 方法中,通过递归克隆引用类型字段来实现深拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyClass implements Cloneable {
private String field1;
private NestedClass nestedObject;

@Override
protected Object clone() throws CloneNotSupportedException {
MyClass cloned = (MyClass) super.clone();
cloned.nestedObject = (NestedClass) nestedObject.clone(); // 深拷贝内部的引用对象
return cloned;
}
}

class NestedClass implements Cloneable {
private int nestedField;

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

使用对象的序列化和反序列化

通过将对象序列化为字节流,再从字节流反序列化为对象来实现深拷贝。要求对象及其所有引用类型字段都实现 Serializable 接口。

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
import java.io.*;

class MyClass implements Serializable {
private String field1;
private NestedClass nestedObject;

public MyClass deepCopy() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
oos.flush();
oos.close();

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (MyClass) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}

class NestedClass implements Serializable {
private int nestedField;
}

手动的递归复制

针对特定对象结构,手动递归复制对象及其引用类型字段。适用于对象结构复杂度不高的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyClass {
private String field1;
private NestedClass nestedObject;

public MyClass deepCopy() {
MyClass copy = new MyClass();
copy.setField1(this.field1);
copy.setNestedObject(this.nestedObject.deepCopy());
return copy;
}
}

class NestedClass {
private int nestedField;

public NestedClass deepCopy() {
NestedClass copy = new NestedClass();
copy.setNestedField(this.nestedField);
return copy;
}
}

通过上面的三种方法我们可以发现,所谓深拷贝,就是想要拷贝的类的对象中包含了另外一个类的对象。
在深拷贝中,除了拷贝本身的类的成员,还要去考虑copy另外一个类的对象,但是在实际操作中,本质上都是在两个类中都去实现一个copy()方法,分别完成各个成员的拷贝,和对象的拷贝。
三种方法本质上都是去实现这个copy方法实现的。

Java八股(反射)