文章目录
- 面向对象高级
- Object类的常用方法
- 常用方法一(面向对象阶段)
- **== 和 equals 的区别**
- 关键字native
- **单例设计模式(Singleton)**
- 前情回顾(学习基础)静态修饰符Static
- 设计模式概念
- 开发步骤
- **两种实现方式**
- **饿汉式**
- **懒汉式**
- **单例设计模式的线程安全问题**
- main方法
- 分析public static void main(String[] args)
- 命令行参数以及IDEA如何设置使用终端运行
- 实例变量赋值顺序
- **总结···类的加载顺序**
- 分析
- 关键字final
- 关键字final的意义
- **关键字 `final` 用于声明不可变的实体,包括类、方法和变量。**
- `final` 关键字的使用场景
- 关键字abstract(抽象类或抽象方法)
- **关键字 `abstract` 用于声明抽象类和抽象方法**
- 模板设计模式
- 概念
- 细节分析
- 关键字interface(接口)
- **`interface`关键字的详细信息**
- **接口(interface)和抽象类(abstract class)**
- 枚举类
- 内部类
- 四种类型
- Annotation(注解)和元注解
- JUnit单元测试(@Test)
- 包装类
- 用途
- 介绍
- 包装类和基本数据类型之间的转换
- **注意事项**
- 包装类缓存优化:Java中常用数据的缓存技术
- ***包装类与基本数据类型的比较大小***
面向对象高级
Object类的常用方法
常用方法一(面向对象阶段)
clone()
方法:clone()
方法用于创建并返回当前对象的一个副本(克隆)。要使用该方法,类必须实现Cloneable
接口,并重写clone()
方法。Cloneable
接口是一个标记接口,它没有任何方法,只是用于标识该类可以被克隆。- 在实现类中重写
clone()
方法时,通常需要调用super.clone()
方法来获得对象的浅拷贝副本,然后根据需要进行深拷贝。 - 默认情况下,
clone()
方法执行的是浅拷贝,即复制对象的字段值,但不复制引用对象本身。如果需要实现深拷贝,需要在clone()
方法中对引用对象进行单独的拷贝操作。
finalize()
方法:finalize()
方法是垃圾回收器在销毁对象之前调用的方法。- 垃圾回收器(Garbage Collector)负责回收不再使用的对象,但在回收对象之前,会调用对象的
finalize()
方法进行一些清理操作。 - 默认实现的
finalize()
方法为空,但可以在子类中重写该方法,以定义对象在被销毁之前的清理行为,如关闭文件、释放资源等。 - 注意,由于垃圾回收的时机不确定,无法保证
finalize()
方法的及时执行,因此不应该过于依赖该方法来释放资源,而应该使用显式的资源释放方法(如close()
)来确保资源的及时释放。
toString()
方法:toString()
方法返回表示对象的字符串表示。- 在打印对象时,通常会自动调用该方法来获取对象的字符串表示,并输出到控制台。
- 默认实现返回一个由类名、
@
符号和对象的哈希码组成的字符串。 - 可以在类中重写
toString()
方法,以返回更有意义的对象描述。通常,重写的toString()
方法会返回对象的属性值以及其他有用信息的字符串表示。
equals()
方法:equals()
方法用于比较两个对象是否相等。- 默认情况下,
equals()
方法比较的是对象的引用是否相等,即两个对象是否指向同一个内存地址。 - 通过重写
equals()
方法,可以改变相等的定义。通常,重写的equals()
方法会比较对象的属性值,判断它们是否相等。 - 在重写
equals()
方法时,通常还需要重写hashCode()
方法,以保证在使用基于哈希的集合类(如HashMap
、HashSet
)时能够正确地比较和存储对象。- 重写
equals()
方法原则 - 对称性(Symmetry): 如果
x.equals(y)
返回true
,那么y.equals(x)
也应该返回true
。 - 自反性(Reflexivity):
x.equals(x)
必须返回true
。 - 传递性(Transitivity): 如果
x.equals(y)
返回true
,且y.equals(z)
返回true
,那么z.equals(x)
也应该返回true
。 - 一致性(Consistency): 如果
x.equals(y)
返回true
,只要x
和y
的内容不变,无论重复多少次调用x.equals(y)
,都应该返回true
。 - 非空性(Non-nullity):
x.equals(null)
应该始终返回false
。 - 类型检查(Type check):
x.equals(obj)
中,如果obj
的类型与x
不同,应该始终返回false
。
- 重写
== 和 equals 的区别
==
运算符既可以用于比较基本类型的值,也可以用于比较引用类型的内存地址。对于基本类型,它比较的是值是否相等;对于引用类型,它比较的是引用是否指向同一个对象(即内存地址是否相等)。equals()
方法是java.lang.Object
类中定义的方法,如果没有在自定义类中重写该方法,那么默认的行为就是使用==
运算符进行引用相等性的比较。然而,许多类(如String
)会重写equals()
方法,以便根据对象的值来确定相等性,而不仅仅是引用的比较。这可能导致一些误解,使人误认为equals()
方法在所有情况下都比较值。- 在自定义类中,如果需要比较对象的相等性,通常需要重写
equals()
方法,并根据类的属性来确定相等性。这样可以根据具体需求定义对象相等的条件
区别点 | == | equals |
---|---|---|
定义 | == 是一个操作符,用于比较两个变量的值是否相等 | equals 是一个方法,用于比较两个对象的内容是否相等 |
适用类型 | 适用于基本数据类型和对象引用。 | 适用于对象 |
比较方式 | 比较操作数的值 | 比较操作数所引用的对象的内容 |
返回值 | true 如果两个操作数的值相等,否则返回 false | true 如果两个对象的内容相等,否则返回 false |
重载 | == 不能被重载 | equals 方法可以被重载 |
需要根据具体情况选择使用哪种比较方式。
- 对于基本类型,优先使用
==
。 - 对于对象,优先使用
equals()
,除非只想检查是否引用同一个对象,然后才使用==
。
主要差别在于:
==
只检查值。equals()
检查值和类型。
所以总的来说:
- 使用
==
时要小心底层对象可能改变(如String
包装类Integer、
Double等)。 - 优先使用
equals()
来判断对象的相等性。
关键字native
Java中的’ native '关键字用于表示方法是本机函数,这意味着该方法是用Java以外的语言实现的,例如C/ c++,并被编译成Java调用的DLL(动态链接库)。
下面是一些需要理解的关于原生方法的要点:
-
本机方法具有用不同的编程语言(通常是C或c++)实现的主体。然而,由于本机方法体的源代码不对我们开放,我们无法看到它的实现。
-
在Java中定义本机方法时,只声明其签名而不提供实现。
为什么使用本机方法?
虽然Java使用起来很方便,但是有些任务在Java中不容易完成,或者性能很关键。在这种情况下,可以使用本机方法,特别是在与低级操作系统或特定硬件交互时。本机方法提供了一个简洁的接口来执行这些任务,而不必深入研究Java领域之外的复杂细节。
本机方法可以像任何其他Java方法一样被调用者使用
本机方法的存在不会影响调用这些方法的其他类。实际上,调用这些方法的其他类甚至可能不知道它们正在调用本机方法。JVM处理调用本机方法的所有细节。
总之,Java中的本机方法提供了一种方法,可以将用其他语言实现的功能合并到Java程序中,从而实现与低级操作或特定于硬件的任务的有效交互,同时为应用程序的其余部分保持Java语言的便利性和简单性。
单例设计模式(Singleton)
前情回顾(学习基础)静态修饰符Static
设计模式概念
设计模式是在大量的实践中总结
和理论化
之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。“套路”
经典的设计模式共有23种。每个设计模式均是特定环境下特定问题的处理方法。
简单工厂模式并不是23中经典模式的一种,是其中工厂方法模式的简化版
开发步骤
单例设计模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
在单例模式中,类的构造函数是私有的,这样外部无法直接实例化该类。而通过一个静态方法或静态变量,类提供了对唯一实例的访问。
单例模式的主要特点包括:
- 私有构造函数:将类的构造器的访问权限设置为私有(private),这样外部无法通过构造函数直接创建类的实例。
- 静态实例变量:在类内部创建一个私有静态实例变量,用于保存唯一的实例。
- 提供一个公共的静态访问方法,例如
getInstance()
,用于获取单例实例。在该方法内部进行逻辑判断,如果实例变量为空,则创建一个新的实例并赋值给实例变量。随后的调用都直接返回已创建的实例。
两种实现方式
饿汉式
- 特点:
立即加载
,即在使用类的时候已经将对象创建完毕。 - 优点:实现起来
简单
;没有多线程安全问题。 - 缺点:当类被加载的时候,会初始化static的实例,静态变量被创建并分配内存空间,从这以后,这个static的实例便一直占着这块内存,直到类被卸载时,静态变量被摧毁,并释放所占有的内存。因此在某些特定条件下会
耗费内存
。
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
懒汉式
- 特点:
延迟加载
,即在调用静态方法时实例才被创建。 - 优点:实现起来比较简单;当类被加载的时候,static的实例未被创建并分配内存空间,当静态方法第一次被调用时,初始化实例变量,并分配内存,因此在某些特定条件下会
节约内存
。 - 缺点:在多线程环境中,这种实现方法是完全错误的,
线程不安全
,根本不能保证单例的唯一性。- 说明:在多线程章节,会将懒汉式改造成线程安全的模式。
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if(single == null) {
single = new Singleton();
}
return single;
}
}
单例设计模式的线程安全问题
(先看完线程同步,在回过头看这个内容)
单例设计模式在多线程环境下可能存在线程安全问题,特别是懒汉式单例模式。为了解决线程安全问题,可以使用synchronized
关键字、双重检查锁定、静态内部类等机制来确保只有一个实例被创建,并提供线程安全的访问方式。
- 懒汉式单例模式:
- 懒汉式单例模式指的是在首次使用时才创建实例。当多个线程同时调用获取实例的方法时,可能会导致创建多个实例的问题。
- 若不考虑线程安全,可能出现以下情况:
- 线程A进入获取实例的方法,发现实例为空,创建一个实例。
- 线程B也进入获取实例的方法,此时实例仍为空,线程B也创建一个实例。
- 最终导致多个线程创建了多个实例,违背了单例模式的原则。
- 解决线程安全问题的方法包括:
- 在获取实例的方法上添加
synchronized
关键字,使用同步锁保证只有一个线程能够进入临界区,但会降低性能。 - 使用双重检查锁定(Double-Checked Locking)机制,即在同步块内再进行一次实例是否为空的检查,这样可以避免每次都进入同步块。
- 在获取实例的方法上添加
- 饿汉式单例模式:
- 饿汉式单例模式指的是在类加载时就创建实例。由于实例在类加载时就已经创建,因此不存在并发创建多个实例的问题。
- 饿汉式单例模式是线程安全的,但可能存在性能问题,因为实例在类加载时就创建,无论是否使用都会占用内存。
- 双重检查锁定(Double-Checked Locking):
- 双重检查锁定是一种在懒汉式单例模式中解决线程安全问题的常用方法。
- 通过在获取实例的方法内使用双重检查锁定机制,可以避免每次都进入同步块,提高性能。
- 在双重检查锁定中,需要注意使用
volatile
关键字修饰实例变量,以确保多线程环境下的可见性和有序性。
- 静态内部类单例模式:
- 静态内部类单例模式是一种常用的线程安全的单例模式实现方式。
- 在静态内部类中创建单例实例,由于静态内部类只在需要时才会被加载,因此实现了懒加载的效果。
- 静态内部类单例模式是线程安全的,因为静态内部类的加载是线程安全的,并且只会加载一次。
main方法
分析public static void main(String[] args)
在Java中,程序的入口点是main()
方法。为了使JVM能够调用main()
方法,它必须具有以下特征:
- 访问权限必须是
public
:main()
方法必须声明为public
,以便JVM能够访问它。 - 方法必须是
static
:main()
方法必须声明为static
,因为在调用main()
方法时,JVM无需创建类的实例。 - 方法参数:
main()
方法接受一个String
类型的数组参数(通常命名为args
),该数组保存执行Java命令时传递给程序的参数。
命令行参数以及IDEA如何设置使用终端运行
实例变量赋值顺序
总结···类的加载顺序
- 类的加载顺序:
- 在创建子类对象时,会先加载父类,然后再加载子类。
- 静态初始化块按照它们在代码中的顺序执行,先执行父类的静态初始化块,然后执行子类的静态初始化块。
- 普通初始化块和构造方法按照它们在代码中的顺序执行,先执行父类的普通初始化块和构造方法,然后执行子类的普通初始化块和构造方法。
分析
- 静态初始化块(
static
代码块):- 静态初始化块在类加载时执行,且只执行一次。
- 静态初始化块按照它们在代码中的顺序执行。
- 静态初始化块用于初始化类级别的静态成员变量或执行其他静态操作。
- 普通初始化块(初始化代码块):
- 普通初始化块在创建对象时执行,每次创建对象都会执行一次。
- 普通初始化块按照它们在代码中的顺序执行,在构造方法之前执行。
- 普通初始化块用于初始化实例级别的成员变量或执行其他实例级别的操作。
- 构造方法:
- 构造方法在创建对象时执行,用于完成对象的初始化。
- 构造方法可以重载,通过不同的参数列表来区分。
- 在构造方法中,可以通过
super()
调用超类的构造方法,或者通过this()
调用同一类中的其他构造方法。
关键字final
关键字final的意义
- 不可修改性:
final
可以将实体声明为不可修改的,确保其数值或行为不会被改变。 - 安全性和稳定性:
final
可以提供代码的安全性和稳定性,防止意外的修改和不必要的变动。 - 性能优化:
final
可以用于性能优化,编译器可以进行更多的优化,提高执行效率。 - API 设计:在设计 API 时,使用
final
可以提供清晰的接口定义,减少对外部代码的依赖,促进代码的可维护性。
关键字 final
用于声明不可变的实体,包括类、方法和变量。
final
类:- 声明为
final
的类**不能被继承**,即它是最终类。 final
类中的方法默认为final
,但可以被子类继承,除非它们被子类重写并标记为final
。- 声明为
final
的类通常用于安全性、稳定性或效率等方面的考虑。
- 声明为
final
方法:- 声明为
final
的方法不能被子类重写或覆盖。 final
方法在父类中定义实现,并且子类不能对其进行更改。final
方法可以被继承,但无法被子类修改。
- 声明为
final
变量:- 声明为
final
的变量是一个常量,一旦赋予初始值后,就不能再改变。 final
变量必须在声明时初始化,可以通过直接赋值或构造方法进行初始化。final
变量通常使用大写字母表示,并使用下划线分隔单词(例如:MAX_SIZE
)。final
变量可以是基本数据类型(如int
、char
等)或引用类型(如String
、Object
等)。- 对于引用类型的
final
变量,引用本身是不可变的,但是对象的内部状态仍然可以修改。
- 声明为
final
参数:- 声明为
final
的方法参数表示该参数在方法内部不可修改。 - 使用
final
参数可以确保方法中不会意外地修改参数的值。
- 声明为
final
和继承:- 声明为
final
的类不能被继承。 - 声明为
final
的方法不能被子类重写。 - 声明为
final
的变量在被赋值后不能再修改。
- 声明为
final
关键字的使用场景
包括但不限于以下几种情况:
- 希望确保类不被继承或方法不被重写。
- 希望创建不可变的常量。
- 希望避免参数在方法内部被修改。
- 用于优化代码,例如在方法内部缓存计算结果。
关键字abstract(抽象类或抽象方法)
关键字 abstract
用于声明抽象类和抽象方法
- 抽象类:
- 抽象类是用
abstract
关键字声明的类。 - 抽象类不能被实例化,即不能创建抽象类的对象。
- 抽象类可以包含抽象方法、普通方法、静态方法、构造方法和成员变量。
- 抽象类可以有构造方法,但不能直接通过构造方法创建对象,只能通过其子类来创建对象。
- 抽象类可以拥有抽象方法和非抽象方法的具体实现。
- 抽象类可以被继承,子类需要实现抽象类中的所有抽象方法或声明自身为抽象类。
- 抽象类是用
- 抽象方法:
- 抽象方法是用
abstract
关键字声明的方法,没有方法体。 - 抽象方法在抽象类中声明,子类必须实现抽象方法。
- 抽象方法用于定义方法的接口,具体的实现由子类提供。
- 子类继承抽象类后,必须实现父类中的所有抽象方法,除非子类也是抽象类。
- 抽象方法不能是私有、静态、final 或 native。
- 抽象方法是用
- 抽象类和抽象方法的作用:
- 抽象类提供了一种模板或基础,用于派生具体子类。
- 抽象类可以定义抽象方法,强制子类实现这些方法,确保方法在各个子类中的一致性。
- 抽象类可以包含具体的方法实现,提供通用的功能或默认的行为。
- 抽象类可以作为多态的基础,通过父类引用指向子类对象。
模板设计模式
概念
模板设计模式(Template Design Pattern)是一种行为型设计模式,用于定义算法的框架结构,将算法的具体实现延迟到子类中。模板设计模式通过定义一个抽象类或接口作为算法的模板,并在其中定义算法的骨架,而将一些具体步骤的实现延迟到子类中。
在模板设计模式中,通常包含以下角色:
- 抽象类(Abstract Class):抽象类定义了算法的模板,其中包含了算法的骨架和一些抽象方法或钩子方法。抽象类负责控制算法的执行流程,并定义了算法中不变的部分。
- 具体类(Concrete Class):具体类是抽象类的子类,实现了抽象方法或钩子方法,完成算法中可变的部分。具体类实现了抽象类中定义的算法模板,并提供了具体的实现细节。
细节分析
-
角色:
- 模板(Template):定义了算法的骨架,包含一个或多个抽象方法,用于延迟实现的步骤。
- 具体模板(Concrete Template):实现了模板中定义的抽象方法,完成算法中的具体步骤。
-
工作原理:
- 模板设计模式通过定义一个模板方法来实现算法的骨架。模板方法包含了算法的主要逻辑,它调用了多个抽象方法和具体方法。
- 抽象方法由模板定义,用于延迟实现的步骤。具体方法在模板中有默认的实现,也可以由子类进行重写。
- 子类通过继承模板并实现其中的抽象方法来完成算法的具体实现。
-
优点:
- 提供了一种封装算法的方式,使得算法的骨架可以在不改变结构的情况下进行扩展和修改。
- 遵循了开闭原则,模板方法定义了算法的骨架,具体实现可以在子类中进行扩展,而不需要修改模板方法本身。
- 通过模板方法的定义和抽象方法的实现,实现了算法的复用和代码的共享。
关键字interface(接口)
interface
关键字的详细信息
- 声明接口:
- 使用
interface
关键字来声明接口。 - 接口的名称应该采用大写字母开头的驼峰命名法。
- 使用
- 方法声明:
- 接口中的方法只有方法签名,没有具体的实现。
- 方法声明包括方法的返回类型、方法名和参数列表。
- 方法默认为公共的(
public
),可以省略访问修饰符。
- 常量声明:
- 接口中可以定义常量,常量被隐式声明为
public static final
。 - 常量的命名应该使用全大写字母和下划线的命名规范。
- 接口中可以定义常量,常量被隐式声明为
- 默认方法(Default Methods):
- 从Java 8开始,接口可以包含默认方法,使用
default
关键字进行声明和实现。 - 默认方法提供了接口的默认实现,实现类可以直接使用或重写默认方法。
- 默认方法可以通过接口的实例调用,也可以在实现类中被调用。
- 从Java 8开始,接口可以包含默认方法,使用
- 静态方法(Static Methods):
- 从Java 8开始,接口可以包含静态方法,使用
static
关键字进行声明和实现。 - 静态方法是接口的类级别方法,可以直接通过接口名调用,无需实例化接口。
- 从Java 8开始,接口可以包含静态方法,使用
- 继承接口(Interface Inheritance):
- 接口可以继承其他接口,使用
extends
关键字。 - 一个接口可以继承多个接口,采用逗号分隔。
- 继承的接口中的方法和常量会被继承到子接口中。
- 接口可以继承其他接口,使用
- 实现接口(Interface Implementation):
- 类可以通过使用
implements
关键字来实现接口。 - 实现接口要求类提供接口中定义的所有方法的具体实现。
- 类可以实现多个接口,使用逗号分隔。
- 类可以通过使用
- 接口与抽象类(Interface vs Abstract Class):
- 接口只能包含常量和方法声明,没有字段和具体实现。
- 抽象类可以包含字段、方法和具体实现。
- 类可以实现多个接口,但只能继承一个抽象类。
- 接口的使用场景:
- 定义契约和协议,规范类的行为和能力。
- 实现多态,允许一个类实现多个接口。(跳转接口的多态)
- 接口隔离,将系统功能进行拆分,降低类之间的耦合度。
- 规范约束,提供代码的规范和可读性。
接口(interface)和抽象类(abstract class)
特点 | 接口 (interface) | 抽象类 (abstract class) |
---|---|---|
实例化 | 不能实例化 | 不能实例化 |
构造函数 | 不能包含构造函数 | 可以包含构造函数 |
字段 | 不能包含字段 | 可以包含字段 |
方法 | 只包含方法声明,没有方法实现 | 可以包含方法声明和方法实现 |
多继承 | 支持多继承 | 不支持多继承 |
单继承 | 可以继承多个接口 | 只能继承一个抽象类 |
默认方法 | 可以包含默认方法 | 不支持默认方法 |
静态方法 | 可以包含静态方法 | 可以包含静态方法 |
访问修饰符 | 默认为公共(public) | 可以使用各种访问修饰符 |
构建范围 | 用于定义契约和协议,规范类的行为和能力 | 用于抽象概念和部分实现,提供共享代码和行为的能力 |
实现方式 | 类实现接口时使用implements关键字 | 类继承抽象类时使用extends关键字 |
设计目的 | 接口隔离,实现多态 | 提供共享代码和行为的能力,提供抽象概念 |
实例化要求 | 类要实现接口中定义的所有方法 | 类可以选择性地实现抽象类中的方法 |
常见设计模式 | 简单工厂、工厂方法、代理模式 | 模板方法设计模式 |
枚举类
内部类
四种类型
内部类是指在一个类的内部定义的类。Java中的内部类有四种类型:成员内部类、静态内部类、局部内部类和匿名内部类。
- 成员内部类(Member Inner Class):
- 定义:成员内部类是定义在类的内部,并且与类的成员变量和方法同级别的类。
- 特点:
- 成员内部类可以访问外部类的所有成员变量和方法,包括私有成员。
- 成员内部类可以使用访问控制修饰符进行修饰(public、private等)。
- 成员内部类的实例必须依赖于外部类的实例,即需要通过外部类的实例来创建内部类的对象。
- 使用情况:
- 成员内部类适合用于需要访问外部类的成员变量和方法,并且与外部类有密切关联的情况。
- 静态内部类(Static Inner Class):
- 定义:静态内部类是定义在类的内部,但使用 static 关键字修饰的类。
- 特点:
- 静态内部类与外部类的实例无关,可以直接创建静态内部类的对象。
- 静态内部类可以访问外部类的静态成员变量和方法,但不能直接访问外部类的非静态成员。
- 静态内部类可以拥有自己的静态成员变量和方法。
- 使用情况:
- 静态内部类适合用于与外部类没有紧密关联的情况,或者需要创建独立于外部类实例的对象的情况。
- 局部内部类(Local Inner Class):
- 定义:局部内部类是定义在方法内部的类。
- 特点:
- 局部内部类只在所在方法中可见,外部方法无法访问局部内部类。
- 局部内部类可以访问方法中的 final 或 effectively final 的局部变量。
- 局部内部类不能声明静态成员变量和方法。
- 使用情况:
- 局部内部类适合用于需要在方法内部定义一个辅助类,且该类只在该方法内部使用的情况。
- 匿名内部类(Anonymous Inner Class):
- 定义:匿名内部类是没有显式定义类名的内部类,通常直接作为参数或方法内部的一部分来使用。
- 特点:
- 匿名内部类没有类名,直接定义在方法内部或作为参数传递。
- 匿名内部类可以继承一个类或实现一个接口。
- 匿名内部类可以访问外部类的成员变量和方法,以及方法中的 final 或 effectively final 的局部变量。
- 使用情况:
- 匿名内部类适合用于需要定义一次性的类,不需要命名或重复使用的情况,比如创建事件处理器、实现接口等。
Annotation(注解)和元注解
JUnit单元测试(@Test)
包装类
用途
- 编码过程中只接收对象的情况,比如List中只能存入对象,不能存入基本数据类型;比如一个方法的参数是Object时,不能传入基本数据类型,但可以传入对应的包装类; 比如泛型等等。
- 基本数据类型没有toString()方法等
介绍
包装类(Wrapper Class)是Java中提供的一组类,用于将基本数据类型(如int、char、boolean等)封装成对象。每种基本数据类型都有对应的包装类,包装类提供了许多方法和功能,使得基本数据类型可以像对象一样进行操作。
- 特点和功能:
- 封装:包装类将基本数据类型封装成对象,使其具有对象的特性,如可以作为方法的参数和返回值,可以参与对象的存储和传递等。
- 自动装箱和拆箱:Java提供了自动装箱和拆箱机制,可以自动在基本数据类型和对应的包装类之间进行转换。
- 不可变性:包装类的实例是不可变的,即一旦创建就无法修改其值。
- 包装类提供了许多方法用于操作和处理基本数据类型,如比较、转换、格式化等。
- 包装类还提供了常量和静态方法,如最大值、最小值、类型转换等。
包装类和基本数据类型之间的转换
- 自动装箱(Autoboxing):
- 自动装箱是指将基本数据类型自动转换为对应的包装类对象。
- 自动拆箱(Unboxing):
- 自动拆箱是指将包装类对象自动转换为对应的基本数据类型。
- 包装类提供的方法进行转换
- valueOf()方法:用于将基本数据类型转换为对应的包装类对象。
- xxxValue()方法:用于将包装类对象转换为基本数据类型的值。
- parseXxx()方法:用于将字符串转换为基本数据类型的值。
注意事项
- 在进行包装类与基本数据类型之间的比较时,应使用包装类提供的 equals() 方法,而不是直接使用 “==” 进行比较。
- 在使用自动拆箱时,需要确保包装类对象不为 null,否则会抛出 NullPointerException 异常。
包装类缓存优化:Java中常用数据的缓存技术
Java对部分经常使用的数据采用缓存技术,在类第一次被加载时创建缓存和数据。当使用等值对象时直接从缓存中获取,从而提高了程序执行性能(通常只对常用数据进行缓存)。
包装类与基本数据类型的比较大小
在Java中,包装类与基本数据类型之间可以进行相等性比较。对于基本数据类型,可以直接使用关系运算符(如>
、<
、==
等)进行比较。而对于包装类,则需要使用equals()
方法进行比较。
在某些情况下,使用valueOf()
方法创建的包装类对象可以利用缓存技术,使得在一定范围内的比较结果为相等。
各个包装类的缓存范围和一些特殊注意事项:
- Integer类型有缓存-128到127的对象。缓存上限可以通过配置JVM参数来更改。
- Byte、Short、Long类型有缓存-128到127的对象。
- Character缓存0到127的对象。
- Boolean缓存TRUE和FALSE的对象。
需要注意的是,只有使用valueOf()
方法构造对象时才会使用缓存。使用new
方法等方式创建对象不会使用缓存。
故当使用valueOf()
方法创建包装类对象时,与基本数据类型进行大小比较时,在缓存范围内的值将被认为是相等的。这是因为它们引用了缓存中的同一个对象。