1.单例设计模式
1.1单例模式是什么?
单例模式,属于创建类型的一种常用的软件设计模式。
通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)。就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
1.2单例模式使用的场景:
需要频繁的进行创建和销毁的对象
创建对象时耗时过多或耗费资源过多(重量级对象)
经常用到的对象、工具类对象、频繁访问数据库或文件的对象(数据源、session工厂)
1.3饿汉式
所谓饿汉式,就是直接创建出类的实例化,然后用private私有化,对外只用静态方法暴露。
静态变量(可用)
步骤
构造器私有化
类的内部创建对象
向外暴露一个静态的公共方法
优点 | 缺点 |
---|---|
写法简单,在类装载的时完成实例化,避免了线程同步问题 | 类装载时完成实例化,没有达到LazyLoading的效果,若该实例从未使用,则会造成内存浪费 |
java
class Singleton {
//私有化构造器
private Singleton() {
}
//内部创建对象实例
private final static Singleton instance = new Singleton();
//对外公有的静态方法
public static Singleton getInstance() {
return instance;
}
}
静态代码块(可用)
将类的实例化放到静态代码块中的写法,基本同上。
java
class Singleton { //静态代码块
//私有化构造器
private Singleton() {}
//内部创建对象实例
private static Singleton instance;
static { // 在静态代码块中,创建单例对象
instance = new Singleton();
}
//对外公有的静态方法
public static Singleton getInstance() {
return instance;
}
}
测试代码
java
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
1.4懒汉式
所谓懒汉式,就是在需要调用的时候再创建类的实例化。
线程不安全 (不推荐)
起到了懒加载效果,但是只能在单线程使用,多线程会不安全,因为当多个线程并发同时判断instance为空时,就会相应的实例化多个对象。
java
class Singleton {
//线程不安全
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
//调用时才实例化对象,懒汉式
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全 (不推荐)
上面线程不安全,那上锁不就好了,使用synchronized关键字。
这样虽然解决了线程安全,但其实实例化操作只做一次,而获取实例(即getInstance)的操作是很多次的,把调用的方法加上同步,会大大降低效率。
java
class Singleton { //线程安全
private static Singleton instance;
private Singleton() {}
//synchronized同步处理
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
1.5 双重检查 *****最重要的一种
上面代码效率低,那在同步前判断一下有没有实例化不就好了,
若没有实例化则用同步方法new一个,否则直接return即可。即所谓的双重检查。
需要用到关键字volatile,防止指令重排。如果不用volatile关键字,就会和线程不安全情形一样,在if判断那会有并发。
java
class Singleton {
//双重检查
private static volatile Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(instance == null) {
//判断是否实例化
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance; //否则直接return
}
}
这样既实现了懒加载,又保证了线程安全。
1.6静态内部类
静态内部类在外部类装载时不会实例化,当调用的时候才会装载并实例化,且JVM保证了其装载时的线程安全性。也能保证懒加载和线程安全,有点像自带版的双重检查。
java
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
//静态内部类,包含一个静态属性:Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//对外公有的静态方法,直接返回SingletonInstance.INSTANCE
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
1.7枚举
其实,使用枚举也能实现单例模式,不仅能避免多线程同步问题,也能防止反序列化重新创建新的对象。
java
enum Singleton {
INSTANCE; //属性
public void say() {
System.out.println("记得三连~");
}
}
2.排序
、
3.冒泡排序
算法步骤
- 比较相邻的元素,如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的比价,从开始第一对到结尾的最后一对,这样在最后的元素就是最大的数;
- 针对所有的元素重复以上的步骤,除了数组最后已经排好序的数组;
- 重复步骤1~3,直到排序完成。
java
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
boolean flag = true;
for (int j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = false;
}
}
if (flag) {
break;
}
}
}
}
4.选择排序(Selection Sort)
算法步驟
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置;
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
- 重复第2步,直到所有元素均排序完毕。
java
public class SelectionSort {
public static void selectionSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len - 1; i++)
{
int minVal = i;
//确定本轮最小的数字的定位
for (int j = i + 1; j < len; j++) {
if (arr[minVal] > arr[j]) {
minVal = j;
}
}
//进行一次选择---选到最小的数字
if (minVal != i) {
int tmp = arr[i];
arr[i] = arr[minVal];
arr[minVal] = tmp;
}
}
}
}
5.快速排序(Quick Sort)
算法步驟
- 从序列中随机挑出一个元素,做为基准(pivot,这里选择序列的最左边元素作为基准);
- 重新排列序列,将所有比基准值小的元素摆放在基准前面,所有比基准值大的摆在基准的后面。该操作结束之后,该基准就处于数列的中间位置。这个操作称为分区(partition);
- 递归地把小于基准值元素的子序列和大于基准值元素的子序列进行上述操作即可。
、
java
public class QuickSort {
public static void quickSort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int left, int right) {
if (left < right) {
int pivotIdx = partition(arr, left, right);
sort(arr, 0, pivotIdx - 1);
sort(arr, pivotIdx + 1, right);
}
}
private static int partition(int[] arr, int left, int right) {
int idx = left + 1;
for (int i = idx; i <= right; i++) {
if (arr[left] > arr[i]) {
swap(arr, i, idx++);
}
}
swap(arr, left, idx - 1);
return idx - 1;
}
private static void swap(int[] arr, int idx1, int idx2) {
int tmp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = tmp;
}
}