
Java 的基本类型包装类(Wrapper Classes)是为了解决基本数据类型无法参与面向对象编程的问题而设计的。它们将基本数据类型封装成对象,使得基本类型具备了对象的特性(如方法调用、实现接口、存储在集合中等)。
Object 的子类,而包装类支持Serializable接口或参与反射 API。包装类解决了这个问题,允许在网络传输或文件存储中使用基本类型。所有包装类之间值的比较,都使用 equals 方法!
对于包装类,常量池特指对象缓存机制,即预创建某些常用值的对象实例,重复使用这些实例而非每次都创建新对象。
-128 到 127 之间的数值(可通过 -XX:AutoBoxCacheMax 参数调整上限)。-128 到 127 之间的数值。\u0000 到 \u007F (即 0-127 的 ASCII 字符)。TRUE 和 FALSE。在 Java 中,装箱(Boxing) 和 拆箱(Unboxing) 是基本数据类型与对应的包装类之间的自动转换机制。
手动装箱与拆箱:
手动调用包装类的构造函数和方法
// 手动装箱
Integer a = new Integer(10); // 方式 1:构造函数(已过时)
Integer b = Integer.valueOf(10); // 方式 2:静态工厂方法(推荐)
// 手动拆箱
int c = a.intValue(); // 调用 intValue()方法
boolean d = Boolean.TRUE.booleanValue();
自动装箱与拆箱:
Java 5 之后,编译器自动插入装箱 / 拆箱代码,简化写法:
// 自动装箱
Integer a = 10; // 等价于 Integer a = Integer.valueOf(10);
// 自动拆箱
int b = a; // 等价于 int b = a.intValue();
// 运算中的自动拆箱与装箱
Integer x = 5;
Integer y = 10;
Integer sum = x + y; // 等价于:int tmp = x.intValue() + y.intValue();
// 然后 Integer sum = Integer.valueOf(tmp);
实现原理:
编译器在编译时自动插入装箱 / 拆箱代码:
valueOf() 方法(如 Integer.valueOf())。 xxxValue() 方法(如 intValue())。各类型的装箱和拆箱方法:

自动拆箱时,若包装类为 NULL ,会引发空指针异常
Integer nullable = null;
int value = nullable; // 抛出 NullPointerException
new,在创建实例的时候 this(参数列表),这样可以进行代码复用方法重载:
String的 indexOf();继承使用 extends 关键字
在 Java 中,没有明确写extends的类,编译器会自动加上extends Object,Object 是所有类的父类
Java 只允许单继承(一个 class 继承一个类),即只允许有一个爹,可以有无数个娃
子类无法访问父类的private字段或者private方法,这就使得继承的作用被削弱了
成员变量基本都是 private,可不就是削弱了嘛
为了让子类可以访问父类的字段与方法,而又不至于被外部访问,就可以使用 protected 关键字修饰,用protected修饰的字段可以被子类访问
protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问
子类访问父类的字段和方法可以使用 super 关键字
如果父类定义了有参构造方法,那么子类就必须显式调用父类的构造方法 super(参数)并给出参数以便让编译器定位到父类的一个合适的构造方法,否则会编译失败
子类_不会继承_任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
方法重写(override):在继承关系中,子类定义了一个与父类方法签名完全相同的方法
重写在前面加上注解 @override ,编译器会帮助检查是否正确
签名完全相同是重写,不同则是重载
方法名相同,方法参数相同,但方法返回值不同,也是不同的方法。在 Java 程序中,出现这种情况,编译器会报错。
问题来了,子类重写了父类某个方法,父类变量接收子类实例后,父类变量调用该方法,调用的是父类还是子类的方法:
答案:子类
// override
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run(); // 应该打印 Person.run 还是 Student.run?
}
}
class Person {
public void run() {
System.out.println("Person.run");
}
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
上面代码实际上调用的方法是 Student的 run()方法
如果子类单独定义的方法,同样用父类变量接收子类实例,则需要向下转换类型后才能调用子类方法
结论:Java 的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。
这个非常重要的特性在面向对象编程中称之为多态。它的英文拼写非常复杂:Polymorphic。
多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的重写方法
Person p = new Student();
p.run(); // 无法确定运行时究竟调用哪个 run()方法
public void runTwice(Person p) {
p.run();
p.run();
}
上面的代码无法确定, p.run() 调用的是父类还是子类的方法,也无法确定 runTwice()传入的参数是 Person 类型还是子类类型
那么这种特性看似稀里糊涂,到底有什么作用呢?
观察下面的代码:
假设我们定义一种收入,需要给它报税,那么先定义一个 Income类,其中包含 getTax()方法,表示普通税收
再定义 Salary 通过继承 Income类并重写 getTax() 方法,表示薪水收入
国务院特殊津贴享受免税,则定义 StateCouncilSpecialAllowance,继承 Income 并重写 getTax()方法
这样我们就定义了三个不同类型的税收项目,现在,我们要编写一个报税的财务软件,对于一个人的所有收入进行报税,定义 totalTax() 函数,只需要通过 Income 类型来获取各个类型的税收,并最终得出总税收
如此一来,利用多态, totalTax()方法只需要和 Income打交道,它完全不需要知道 Salary和 StateCouncilSpecialAllowance的存在,就可以正确计算出总的税。如果我们要新增一种稿费收入,只需要从 Income派生,然后正确覆写 getTax()方法就可以。把新的类型传入 totalTax(),不需要修改任何代码。
// Polymorphic
public class Main {
public static void main(String[] args) {
// 给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税:
Income[] incomes = new Income[] {
new Income(3000),
new Salary(7500),
new StateCouncilSpecialAllowance(15000)
};
System.out.println(totalTax(incomes));
}
public static double totalTax(Income... incomes) {
double total = 0;
for(Income income:incomes) {
total = total + income.getTax();
}
return total;
}
}
class Income {
protected double income;
public Income(double income) {
this.income = income;
}
public double getTax() {
return income * 0.1; // 税率 10%
}
}
class Salary extends Income {
public Salary(double income) {
super(income);
}
@Override
public double getTax() {
if(income <= 5000) {
return 0;
}
return(income - 5000) * 0.2;
}
}
class StateCouncilSpecialAllowance extends Income {
public StateCouncilSpecialAllowance(double income) {
super(income);
}
@Override
public double getTax() {
return 0;
}
}
因为所有的 class最终都继承自 Object,而 Object定义了几个重要的方法:
toString():把 instance 输出为 String; equals():判断两个 instance 是否逻辑相等; hashCode():计算一个 instance 的哈希值。final :
使用
final修饰成员变量时,一般在类的构造方法中对该不可更改的变量进行初始化,这保证了实例一旦创建,其final修饰字段不可更改
一个类只能继承一个类,但可以实现(implements)多个接口
接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)
抽象类与接口对比

一个接口可以继承另外一个接口,使用 extends 关键字
接口中定义 default 方法:
相当于实现了一个方法
// interface
public class Main {
public static void main(String[] args) {
Person p = new Student("Xiao Ming");
p.run();
}
}
interface Person {
String getName();
default void run() {
System.out.println(getName() + "run");
}
}
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
实现类可以不必覆写 default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。
接口的静态字段:
因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:
public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}
实际上,因为 interface的字段只能是 public static final类型,所以我们可以把这些修饰符都去掉,编译器会自动把该字段变为 public static final类型。
public interface Person {
// 编译器会自动加上 public static final:
int MALE = 1;
int FEMALE = 2;
}
完整的类名: 包名.类名
定义 class 时,需要在第一行声明属于哪个包
如: package com.kuaishou.csc.workbench.component.hotline.service.checker;
包没有父子关系。java.util 和 java.util.zip 是不同的包,两者没有任何继承关系。
包作用域:
不使用 public、private、protected 修饰的字段和方法就是包的作用域
位于同一个包内的类,可以访问该包下作用域内的字段和方法,不需要 import
不同包下需要使用 import 来导入其他包,也可以具体指定导入的类,使用 * 就是导入该包下的所有类
注意,由于 Java 中的包没有父子关系,导入所有类时不包含子包下的类
如,import com.kuaishou.*,只导入了kuaishou 包下的类,即声明了package com.kuaishou的类文件,而com.kuaishou.staging下的类并未导入。
为了看起来结构清晰明了,且占用更少的资源,一般导入指定的类,而不是包下的所有类
如果两个包有同名类,则只能 import 一个,另一个使用完整类名
为避免类名冲突,要确定唯一的包名,推荐使用倒置的域名来确保唯一性,如:
com.kuaishou.frameworkcom.kuaishou.csc.workbenchpublic :都能访问private :类内访问protected:继承树内可以访问最佳实践:
非必要,不使用 public
包作用域有助于测试,
classpath是 JVM 用到的一个环境变量,它用来指示 JVM 如何搜索class
JVM 在查找某个类时,依次查找:
./classpath 目录强烈不推荐在系统环境变量中设置 classpath,会污染整个环境
推荐在启动 JVM 时通过 -classpath 或 -cp 来配置,如:
java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello 如果没有配置classpath `,则默认为当前目录
JVM 不依赖 classpath 来加载 Java 核心库,因此不需要在 classpath 中指定 Java 核心库路径
Java 注解(Annotation)是 JDK 5 引入的一种特殊语法元数据(metadata),它不直接影响程序的执行逻辑,但可以为编译器、工具或运行时提供额外信息。简单来说,注解就像一种 “标签”,可以标记在类、方法、变量等程序元素上,用于描述这些元素的额外信息。
注解的基本特性:
常见的内置注解:
元注解:用于修饰其他注解的注解,Java 共提供了五个
@Target:指定注解可以修饰的程序元素(类、方法、字段等)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation { ... }
@Retention:指定注解的保留策略
@Documented:标记注解会被 Javadoc 工具提取到文档中
@Inherited:标记注解可以被子类继承
@Repeatable:允许注解在同一元素上重复使用(Java 8+)
自定义注解:
定义注解:
import java.lang.annotation.*;
// 定义注解
@Target({ElementType.TYPE,ElementType.METHOD}) // 可用于类和方法
@Retention(RetentionPolicy.RUNTIME) // 运行时可获取
@Documented // 生成文档
public @interface MyAnnotation {
// 注解的属性(类似方法定义)
String value()default "默认值";
int version()default 1;
String[] authors();
}
使用注解:
// 使用注解
@MyAnnotation(value = "用户服务",version = 2,authors = {"张三", "李四"})
public class AnnotationDemo {
@MyAnnotation(value = "获取用户信息",authors = {"张三"})
public String getUserInfo() {
return"用户信息";
}
}
解析注解:
import java.lang.reflect.Method;
// 解析注解
public class AnnotationProcessor {
public static void main(String[] args)throws Exception {
// 处理类上的注解
Class<AnnotationDemo> clazz = AnnotationDemo.class;
if(clazz.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println("类注解信息:");
System.out.println("描述:" + annotation.value());
System.out.println("版本:" + annotation.version());
System.out.println("作者:" + String.join(",",annotation.authors()));
}
// 处理方法上的注解
Method method = clazz.getMethod("getUserInfo");
if(method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("\n 方法注解信息:");
System.out.println("描述:" + annotation.value());
System.out.println("作者:" + String.join(",",annotation.authors()));
}
}
}
在 Java 中,.class 是一种获取类的 Class 对象 的语法,称为 “类字面量”(class literal)。