单例模式

饿汉式

package Singleton;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;

//饿汉式单例模式
/*
* 饿汉式当类加载进内存时便会生成一个类对象,若类中定义了大容量数组,这样十分浪费空间
* 显然这种写法比较简单,但问题是无法做到延迟创建对象,
* 事实上如果该单例类涉及资源较多,创建比较耗时间时,
* 我们更希望它可以尽可能地延迟加载,从而减小初始化的负载,
* 反射和序列化会破坏此单例
*/
public class Hungryman implements Serializable {

    private static final long serialVersionUID = -5520679614965399284L;
    private static Hungryman instance = new Hungryman();

    private Hungryman(){}

    public static Hungryman getInstance(){
        return instance;
    }
}

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Hungryman instance = Hungryman.getInstance();
        Hungryman instance2 = Hungryman.getInstance();
        System.out.println(instance==instance2);
    }
}

懒汉式

package Singleton;

import java.lang.reflect.InvocationTargetException;

//懒汉式
/*
 * 这种写法能够在多线程中很好的工作避免同步问题,
 * 同时也具备lazy loading机制,遗憾的是,由于synchronized的存在,
 * 效率很低,在单线程的情景下,完全可以去掉synchronized,为了兼顾效率与性能问题
 * 反射和序列化会破坏此单例
 */
public class Lazyman {
    //volatile防止指令重排序
    private static volatile Lazyman instance;

    private Lazyman(){}

    //synchronized可以防止线程不安全
    public synchronized static Lazyman getInstance(){
        if(instance==null){
            instance = new Lazyman();
        }
        return instance;
    }
}

class Test1{
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        Lazyman instance = Lazyman.getInstance();
        Lazyman instance1 = Lazyman.getInstance();
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
    }

}

DCL懒汉式

package Singleton;
//DCL懒汉式(双重检查锁,进行了两次null检查)
/*
 * volatile防止指令重排
 * instance = new TestInstance();可以分解为3行伪代码
 * a.memory = allocate() //分配内存
 * b.ctorInstanc(memory) //初始化对象
 * c.instance = memory //设置instance指向刚分配的地址
 * 下面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。
 * 在多线程的情况下会出现以下问题。当线程A在执行instance=new DLCLazyman()时,B线程进来执行到if(instance==null)。
 * 假设此时A执行的过程中发生了指令重排序,即先执行了a和c,没有执行b。
 * 那么由于A线程执行了c导致instance指向了一段地址,所以B线程判断instance不为null,会直接跳到return instance并返回一个未初始化的对象。
 * 反射和序列化会破坏此单例
 * */
public class DLCLazyman {

    private static volatile DLCLazyman instance;
    private DLCLazyman(){}

    public static DLCLazyman getInstance(){
        if(instance==null){
            synchronized (DLCLazyman.class){
                if(instance==null){
                    instance=new DLCLazyman();
                }
            }

        }
        return instance;
    }
}

class test2{
    public static void main(String[] args) {
        DLCLazyman instance = DLCLazyman.getInstance();
        DLCLazyman instance1 = DLCLazyman.getInstance();
        System.out.println(instance==instance1);
    }
}

静态内部类式

package Singleton;
//静态内部类单例模式
/*
* 静态内部类只会被加载一次,这种写法是线程安全的。
* 反射和序列化会破坏此单例
* */
public class Holder {
    private static class holder{
        private static  Holder instance = new Holder();
    }

    private Holder(){}

    public static Holder getInstance(){
        return holder.instance;
    }
}

class test3{
    public static void main(String[] args) {
        Holder instance = Holder.getInstance();
        Holder instance1 = Holder.getInstance();
        System.out.println(instance==instance1);
    }
}

如何解决序列化和反射破坏单例?

  1. 序列化可能会破坏单例模式,比较每次反序列化一个序列化的对象实例时都会创建一个新的实例,解决方案如下:
//测试例子(上述四种方式的解决方式类似)
public class Singleton implements java.io.Serializable {     
   public static Singleton INSTANCE = new Singleton();     

   protected Singleton() {     
   }  

   //反序列时直接返回当前INSTANCE
   private Object readResolve() {     
            return INSTANCE;     
      }    
}   
  1. 使用反射获得私有构造器,解决方式可以修改构造器,让它在创建第二个实例的时候抛异常,如下:
public static Singleton INSTANCE = new Singleton();     
private static volatile  boolean  flag = true;
private Singleton(){
    if(flag){
    flag = false;   
    }else{
        throw new RuntimeException("不要试图用反射破坏单例");
    }
}

枚举式

package Singleton;

import java.lang.reflect.InvocationTargetException;
//枚举单例
/*
 * 枚举序列化是由jvm保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,
 * 在枚举类型的序列化和反序列化上,Java做了特殊的规定:
 * 在序列化时Java仅仅是将枚举对象的name属性输出到结果中,
 * 反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。
 * 同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,
 * 从而保证了枚举实例的唯一性
 * 无法使用反射创建枚举实例,创建枚举实例只有编译器能够做到
 * */
public enum EnumSingleton{
    INSTAECS;
}

class test4{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingleton instance = EnumSingleton.INSTAECS;
        EnumSingleton instaces1 = EnumSingleton.INSTAECS;
        System.out.println(instance==instaces1);

    }
}

资源下载链接为: https://pan.quark.cn/s/5c50e6120579 在Android移动应用开发中,定位功能扮演着极为关键的角色,尤其是在提供导航、本地搜索等服务时,它能够帮助应用获取用户的位置信息。以“baiduGPS.rar”为例,这是一个基于百度地图API实现定位功能的示例项目,旨在展示如何在Android应用中集成百度地图的GPS定位服务。以下是对该技术的详细阐述。 百度地图API简介 百度地图API是由百度提供的一系列开放接口,开发者可以利用这些接口将百度地图的功能集成到自己的应用中,涵盖地图展示、定位、路径规划等多个方面。借助它,开发者能够开发出满足不同业务需求的定制化地图应用。 Android定位方式 Android系统支持多种定位方式,包括GPS(全球定位系统)和网络定位(通过Wi-Fi及移动网络)。开发者可以根据应用的具体需求选择合适的定位方法。在本示例中,主要采用GPS实现高精度定位。 权限声明 在Android应用中使用定位功能前,必须在Manifest.xml文件中声明相关权限。例如,添加<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />,以获取用户的精确位置信息。 百度地图SDK初始化 集成百度地图API时,需要在应用启动时初始化地图SDK。通常在Application类或Activity的onCreate()方法中调用BMapManager.init(),并设置回调监听器以处理初始化结果。 MapView的创建 在布局文件中添加MapView组件,它是地图显示的基础。通过设置其属性(如mapType、zoomLevel等),可以控制地图的显示效果。 定位服务的管理 使用百度地图API的LocationClient类来管理定位服务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值