基础知识
约 4506 字大约 15 分钟
2025-01-15
1.基本数据类型
1.1 八种基本数据类型
Java 中有 8 种基本数据类型,分别为:
- 6 种数字类型:
- 4 种整数型:
byte
、short
、int
、long
- 2 种浮点型:
float
、double
- 4 种整数型:
- 1 种字符类型:
char
- 1 种布尔型:
boolean
这 8 种基本数据类型的默认值以及所占空间的大小如下:
基本类型 | 位数 | 字节 | 默认值 | 取值范围 |
---|---|---|---|---|
byte | 8 | 1 | 0 | -128 ~ 127 |
short | 16 | 2 | 0 | -32768(-2^15) ~ 32767(2^15 - 1) |
int | 32 | 4 | 0 | -2147483648 ~ 2147483647 |
long | 64 | 8 | 0L | -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1) |
float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
char | 16 | 2 | 'u0000' | 0 ~ 65535(2^16 - 1) |
boolean | 1 | false | true、false |
1.2 基本类型和包装类型的区别
用途:
- 基本类型用于定义一些常量和局部变量;包装类型用于方法参数、对象属性
- 包装类型可用于泛型,而基本类型不可以。
存储方式:
- 基本数据类型的局部变量存放在JVM中的局部变量表中,基本数据类型的成员变量(未被
static
修饰 )存放在 JVM的堆中。 - 包装类型属于对象类型,存在于堆
- 基本数据类型的局部变量存放在JVM中的局部变量表中,基本数据类型的成员变量(未被
默认值:
- 成员变量包装类型不赋值就是
null
- 基本类型有默认值且不是
null
- 成员变量包装类型不赋值就是
比较方式:
- 对于基本数据类型来说,
==
比较的是值。 - 对于包装数据类型来说,
==
比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用equals()
方法。
- 对于基本数据类型来说,
注意注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆中
public class Test {
// 成员变量,存放在堆中
int a = 10;
// 被 static 修饰,也存放在堆中,但属于类,不属于对象
// JDK1.7 静态变量从永久代移动了 Java 堆中
static int b = 20;
public void method() {
// 局部变量,存放在栈中
int c = 30;
static int d = 40; // 编译错误,不能在方法中使用 static 修饰局部变量
}
}
1.3 包装类型的缓存机制
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据Character
创建了数值在 [0,127] 范围的缓存数据Boolean
直接返回True
orFalse
。
如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
- 举例一:
Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true
Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false
Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false
- 举例二:注意新建包装类型对象
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);
//Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接使用的是缓存中的对象。而Integer i2 = new Integer(40) 会直接创建新的对象
//结果为fasle
1.4 自动装箱/拆箱
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
举例:
Integer i = 10; //装箱
int n = i; //拆箱
查看字节码文件,如下:
L1
LINENUMBER 8 L1
ALOAD 0
BIPUSH 10
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
PUTFIELD AutoBoxTest.i : Ljava/lang/Integer;
L2
LINENUMBER 9 L2
ALOAD 0
ALOAD 0
GETFIELD AutoBoxTest.i : Ljava/lang/Integer;
INVOKEVIRTUAL java/lang/Integer.intValue ()I
PUTFIELD AutoBoxTest.n : I
RETURN
- 装箱其实就是调用了 包装类的
valueOf()
方法 - 拆箱其实就是调用了
xxxValue()
方法。
Integer i = 10
等价于 Integer i = Integer.valueOf(10)
int n = i
等价于 int n = i.intValue()
;
2.关键字
2.1 static关键字
static
关键字用于声明静态成员(静态变量、静态方法)静态成员属于类,而不是类的实例。因此,无论创建多少个类的实例,静态成员只有一份。
使用场景:
- 类级别的共享数据
- 静态方法
- 单例模式的实例对象
JVM层面:
- 静态变量、静态方法均存储于方法区中,而不是堆
- 对于静态变量,JVM 会在类加载时为其分配内存并初始化其默认值(如
null
或0
),确保被访问时,JVM已经加载并完成任何静态初始化操作
2.2 final关键字
final
关键字用于声明不可改变的字段、方法和类。
使用场景:
- 常量:当你希望某个变量的值不能改变时,可以使用
final
。它使得变量成为常量,通常与static
结合使用(static final
)来表示类常量 - 防止方法被重写
- 防止类被继承
2.3 成员变量与局部变量区别
语法形式:
成员变量属于类,可以被
public
,private
,static
等修饰符所修饰局部变量属于代码块或者方法,能被访问控制修饰符及
static
所修饰;
存储方式:
成员变量(不带static修饰符)存在于堆内存
局部变量则存在于栈内存。
3.面向对象基础
3.1 面向对象三大特征
封装、继承、多态
封装:
- 封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息,但是可以提供一些可以被外界访问的方法来操作属性。
- 举例:我们看不到挂在墙上的空调的内部的零件信息(也就是属性),但是可以通过遥控器(方法)来控制空调。
继承:
- 是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法
多态:顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
3.2 辨别接口和抽象类的异同
共同点
- 都不能被实例化
- 都可以包含抽象方法
- 都可以有默认实现的方法(Java 8 可以用
default
关键字在接口中定义默认方法)
不同点
- 单继承、多实现
- 成员变量修饰符不同、赋值不同:
- 接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值
- 抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值
用途不同:
- 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。
- 抽象类主要用于代码复用,强调的是所属关系。
3.3 深、浅、引用、对象拷贝
引用拷贝:创建一个指向对象的引用变量的拷贝,例子如下
Teacher teacher = new Teacher("riemann", 28); Teacher otherTeacher = teacher; sout(teacher==otherTeacher); // true
对象拷贝:创建对象的一个拷贝,即创建新的对象,例子如下
Teacher teacher = new Teacher("riemann", 28); Teacher otherTeacher = (Teacher) teacher.clone(); Teacher teacher = new Teacher("riemann", 28); Teacher otherTeacher = teacher; sout(teacher==otherTeacher); // false
浅拷贝:如果拷贝对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象
深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
以下图来描述浅拷贝、深拷贝、引用拷贝:
4.Object类
4.1==和equals的区别
==是运算符
- 若比较的对象是基本数据类型,则比较数值是否相等
- 若比较的对象是引用数据类型,则比较的是对象的内存地址是否相等
equals是方法
equals是比较基本数据类型,还是引用数据类型的变量,其比较的都是值,
只是引用类型变量存的值是对象的地址。引用类型对象变量其实是一个引用,它们的值是指向对象所在的内存地址。
equals()方法存在于Object类中,而Object类是所有类的父类。在Object类中定义了equals方法:
public boolean equals(Object obj) { return (this == obj); }
如果类未重写equals方法,调用equals时,会调用Object中的equals方法,实际使用的也是操作符==
如果类重写了equals方法,调用equals时,会调用该类自己的equals方法,一般是比较对象的内容是否相同。比如:
- String:比较字符串内容是否相同;
- Integer:比较对应的基本数据类型int的值是否相同。
5.String
5.1 String、StringBuffer、StringBuilder区别
三者的区别可以从可变性、线程安全性、性能三个角度去分析:
可变性:
String
是不可变的StringBuilder
与StringBuffer
都继承自AbstractStringBuilder
类,在AbstractStringBuilder
中也是使用字符数组保存字符串,不过没有使用final
和private
关键字修饰,最关键的是这个AbstractStringBuilder
类还提供了很多修改字符串的方法比如append
方法abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } //... }
线程安全性:
String
中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder
是StringBuilder
与StringBuffer
的公共父类,定义了一些字符串的基本操作,如expandCapacity
、append
、、insert
、indexOf
等公共方法。StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
性能:
- 每次对
String
类型进行改变的时候,都会生成一个新的String
对象,然后将指针指向新的String
对 StringBuffer
每次都会对StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。- 相同情况下使用
StringBuilder
相比使用StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
- 每次对
- 总结:
- 操作少量的数据: 适用
String
- 单线程操作字符串缓冲区下操作大量数据: 适用
StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用
StringBuffer
- 操作少量的数据: 适用
5.2 String为什么是不可变的
String
类中使用 private 和 final 两个关键字修饰字符数组(作用:保存字符串)- 被
final
关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象 - 但是当数组存放的对象是引用类型时,这个数组保存的字符串的内容是可变的(虽然不能再指向其他对象,但是可以修改里面对象的数据)
- 真正不可变的原因:
- 存储字符串的数组被
final
修饰且为私有的,并且String
类没有提供/暴露修改这个字符串的方法。 String
类被final
修饰导致其不能被继承,进而避免了子类破坏String
不可变。
- 存储字符串的数组被
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
//...
}
5.3 内存分配不同
String a = "111";
使用的是字符串常量池,引用池中已有的字符串对象,节省内存。String a = new String("111");
会在堆内存中创建一个新的String
对象,即使"111"
字符串已经存在于常量池中。
6.Java引用
在Java中,引用(Reference)是用来指向对象的实体,并提供对这些对象的访问。
从JDK 1.2版本开始,对象的引用被划分为4种级别,从而使程序能更加灵活地控制对象的生命周期,这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
6.1 强引用
1、定义
强引用是最常见的引用类型,在Java中默认使用的就是强引用。只要存在强引用指向一个对象,该对象就不会被垃圾回收器回收。
即使系统内存不足时,JVM也不会回收被强引用指向的对象,可能会导致内存溢出。
2、举例
以下例子把一个对象person()赋给一个引用变量p,这个引用变量p就是强引用
Person p=new Person();

4、回收
如果一个对象具有强引用,垃圾回收器不会回收该对象,当内存空间不足时,JVM 宁愿抛出 OutOfMemoryError异常。
如果强引用对象不使用时,需要弱化从而使GC能够回收,
//弱化该强引用对象
p=null;
显式地设置mikechen对象为null,或让其超出对象的生命周期范围,则GC认为该对象不存在引用,这时就可以回收这个对象,具体什么时候收集这要取决于GC算法。
如下:
public class StrongRefenenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = o1;
o1 = null;
System.gc(); //弱化强引用对象o1
System.out.println(o1); //null
System.out.println(o2); //报错:java.lang.Object@2503dbd3
}
}
6.2 软引用
软引用用来描述一些还有用但非必需的对象。当系统内存不足时,JVM会尝试回收软引用指向的对象,以释放内存空间。软引用通常用于实现缓存功能。
例如,下面
String str=new String("abc"); // 强引用对象
SoftReference<String> softRef=new SoftReference<String>(str); // 软引用对象
String result = softRef.get(); // 获取软引用对象指向的对象 结果为abc
举例回收:
import java.lang.ref.SoftReference;
public class SoftReferenceExample {
public static void main(String[] args) {
// 创建一个字符串对象,并创建对它的软引用对象
String data = "Hello, World!";
SoftReference<String> softRef = new SoftReference<>(data);
// 尝试获取软引用对象指向的对象
String result = softRef.get();
System.out.println("软引用获取到的数据:" + result); //Hello, World
// 释放强引用,触发垃圾回收
data = null;
//注意:调用System.gc()方法只是起通知作用,JVM什么时候扫描回收对象是JVM自己的状态决定的
System.gc();
System.out.println("gc之后的值: " + softRef.get()); // 对象依然存在
//申请较大内存使内存空间使用率达到阈值,强迫gc
byte[] bytes = new byte[100 * 1024 * 1024];//如果obj被回收,则软引用会进入引用队列
//得到软引用对象
Reference<?> reference = queue.remove();
if (reference != null){
System.out.println("对象已被回收: "+ reference.get()); // 对象为null
}
}
}
Java 的软引用 有:Mybatis 缓存类 SoftCache 用到的软引用:
public Object getObject(Object key) {
Object result = null;
SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
this.delegate.removeObject(key);
} else {
synchronized(this.hardLinksToAvoidGarbageCollection) {
this.hardLinksToAvoidGarbageCollection.addFirst(result);
if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
this.hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;}
6.3 弱引用
弱引用(Weak Reference):弱引用比软引用更弱,当垃圾回收器工作时,无论内存是否充足,都会回收被弱引用指向的对象。弱引用通常用于避免内存泄漏或协助实现某些特定的功能。
举例如下:
MikeChen mikechen = new MikeChen();
WeakReference<MikeChen> wr = new WeakReference<MikeChen>(mikechen );
public class WeakReferenceDemo {
public static void main(String[] args) {
Object o1 = new Object(); //强引用
WeakReference<Object> w1 = new WeakReference<Object>(o1); //弱引用
System.out.println(o1); //对象地址,不为null
System.out.println(w1.get()); //对象地址,不为null
o1 = null; //取消强引用
System.gc(); //调用GC
System.out.println(o1); //为null
System.out.println(w1.get()); //为null
}
}
举例如下:
ThreadLocal
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//......
}
6.4 虚引用
虚引用是最弱的一种引用关系。通过虚引用可以跟踪对象被垃圾回收器回收的状态,
- 但虚引用本身并不影响对象的生命周期。
- 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
- 虚引用通常结合引用队列使用,用于在对象被回收时收到通知或执行一些清理操作
- 虚引用需要java.lang.ref.PhantomReference 来实现:
举例如下:
A a = new A();
ReferenceQueue<A> rq = new ReferenceQueue<A>();
PhantomReference<A> prA = new PhantomReference<A>(a, rq);
虚引用主要用来跟踪对象被垃圾回收器回收的活动。
虚引用与软引用和弱引用的一个区别在于:
- 虚引用必须和引用队列 (ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
6.5 小结
java4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用。
以下将从被回收时间点、用途、以及死亡时间共三个角度区分这4种引用类型
引用类型 | 被回收时间点 | 用途 | 死亡时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | 将强引用弱化,并gc后 |
软引用 | 内存不足 | 对象缓存 | 内存不足,并gc后 |
弱引用 | 垃圾回收 | 对象缓存 | gc后 |
虚引用 | 未知 | 未知 | 未知 |