//一.单例模式的简要概述
1.单例模式被破坏的情况
暴力反射:在私有构造方法中,判非空时,直接抛出异常。禁止使用构造
序列化和反序列化:添加一个readResolve方法,返回单例对象
Unsafe破坏:目前没有解决策略
2.枚举单例是一种饿汉式的单例,不会受反序列化破坏,不会被暴力反射破坏,但unsafe束手无策。
3.普通懒汉式不能保证多线程安全
尝试在获取实例的整个方法上加synchronized关键字,但是在第一次获取之后的效率较低。
4.双重检验锁(DCL)
两次判空分别保证高效和单例、线程安全
volatile修饰单例对象保证 多线程下变量的可见性和有序性,能够有效地防止指令重排(Jvm的优化手段)
5.静态内部类
//进化史——普通饿汉式、枚举饿汉式、普通懒汉式、双检锁懒汉式、静态内部类懒汉式
二.jdk中的单例模式
System类中调用exit、gc方法本质都是调用了Runtime类。而Runtime是一个饿汉式单例对象
System类中的成员变量Console对象——双检锁懒汉式
Collections集合工具类中有很多单例对象
//三.五种单例模式的实现
public class ExplainSingleton {
public static void main(String[] args) {
//测试枚举类实现单例模式
System.out.println(SingletonObject5.INSTANCE==SingletonObject5.INSTANCE);
SingletonObj1 singletonObj1 = SingletonObj1.getInstance();
new Thread(){//匿名内部类是局部内部类的一种简化形式.本质上是一个对象,是实现了该接口或继承了该抽象类的子类对象.
@Override
public void run() {
SingletonObj1 singletonObj11 = SingletonObj1.getInstance();
System.out.println(singletonObj1==singletonObj11);
}
}.start();
SingletonObject2 singletonObject2 = SingletonObject2.getSingletonObject2();
new Thread(() -> {
SingletonObject2 singletonObject21 = SingletonObject2.getSingletonObject2();
System.out.println(singletonObject21==singletonObject2);
}).start();
}
}
//1.懒汉式——保证线程安全
class SingletonObj1 {
//私有化成员变量,禁止外类直接通过类获取,然后对该变量进行引用的修改
private static SingletonObj1 singletonObj1;
//覆盖默认的构造方法,提供私有构造方法
private SingletonObj1(){
}
//静态方法
public synchronized static SingletonObj1 getInstance(){
// 静态方法可以被调用多次,每次调用都会执行方法体中的代码。但是,如果一个类中有静态代码块(static block),
// 那么这个代码块只会在类加载的时候执行一次,并且在所有对象中全局共享。静态代码块通常用来初始化一些静态变量或者做一些只需要执行一次的操作
// 总之,静态方法可以被多次调用,但是静态代码块只会被执行一次。
if(singletonObj1==null){
singletonObj1= new SingletonObj1();
}
return singletonObj1;
}
}
//2.饿汉式
//饿汉式单例的写法适用于单例对象较少的情况,这样写可以保证绝对的线程安全,执行效率比较高。
// 但是缺点也很明显,饿汉式会在类加载的时候就将所有单例对象实例化,这样系统初始化的时候会造成大量的内存浪费.
// 从而导致系统的内存不可控,换句话说就是不管对象用不用,对象都已存在,占用内存。
class SingletonObject2{
//加载类的时候,旧产生单例对象.由于类加载只会有一次,故只会产生唯一的一个单例对象
private static SingletonObject2 singletonObject2=new SingletonObject2();
//构造私有化后,就不允许通过new产生对象,故只能通过 类获取,所以相关的成员变量和方法都要有 static关键字
private SingletonObject2(){
}
public static SingletonObject2 getSingletonObject2(){
return singletonObject2;
}
}
//3.双重校验锁.volatile 和 synchronized
//双重判空
//特点为 懒初始化、线程安全、实现难度复杂、在多线程情况下能保持高性能。
class SingletonObject3{
/*
当一个变量被声明为 volatile 时,意味着它的值可能会在程序的执行过程中被意外地修改,
例如在多线程或中断处理程序中。这样,编译器就不能对该变量进行优化,以免导致代码的行为与预期不符。
volatile 关键字通常被用于以下情况:
1.访问硬件寄存器或设备状态,这些状态可能会随时被修改。
2.在多线程环境中共享变量,以确保线程间的可见性。
3.在信号处理程序中访问变量,以避免编译器对变量的优化导致的问题。
需要注意的是,虽然使用 volatile 可以避免编译器的优化,但它并不能保证线程安全和数据一致性,
因此在多线程环境中,还需要使用其他同步机制(CAS、Unsafe、AtomicInteger)来确保数据的正确性。
*/
//当一个变量被声明为 volatile 时,volatile 变量的写操作会立即刷新到主内存,
// 而读操作会从主内存中读取最新值,而非从本地线程缓存中读取。
//因此,当多个线程同时访问一个共享的 volatile 变量时,它们总是能够看到最新的值。
//需要注意的是,volatile 变量并不能完全保证线程安全,因为它只保证了可见性和有序性,而不保证原子性。
private volatile static SingletonObject3 singletonObject3;
private SingletonObject3(){
}
public static SingletonObject3 getSingletonObject3(){
if(singletonObject3==null){
//在多线程的竞争下,如果第一次没有其他线程进行修改。则使用重量级锁synchronized创建单例对象。
//synchronized 的使用可以保证线程安全,避免多个线程同时对共享资源进行修改而导致的数据不一致问题。
//需要注意的是,synchronized 的过度使用可能会导致程序性能下降,因此在使用时应该考虑到性能和实际需求。
synchronized (SingletonObject3.class){
if(singletonObject3==null){
singletonObject3=new SingletonObject3();
}
}
}
return singletonObject3;
}
}
//4.静态内部类
//实现懒加载,内部类只加载一次,线程安全
//静态内部类的加载是在程序中调用静态内部类的时候加载的,和外部类的加载没有必然关系,
//但是在加载静态内部类的时候 发现外部类还没有加载,那么就会先加载外部类.
//加载完外部类之后,再加载静态内部类(初始化静态变量和静态代码块etc)
//如果在程序中单纯的使用外部类,并不会触发静态内部类的加载
//类的加载时机:(暂时的认知里是四种)
//1.new 一个类的时候,
//2.调用类内部的 静态变量,
//3.调用类的静态方法,
//4.调用类的 静态内部类
class SingletonObject4{
private SingletonObject4(){
//为了防止暴力反射破坏私有构造方法,可以直接在获取构造方法时,直接抛出异常
throw new RuntimeException("不允许调用构造方法");
}
public SingletonObject4 getInstance(){
//外部类可以直接访问内部类的
return SingletonObjectInner.instance;
}
private static final class SingletonObjectInner{
private static final SingletonObject4 instance=new SingletonObject4();
}
}
//5.枚举式
//JAVA规定:不允许通过反射调用构造方法的类---枚举
//枚举类有以下特点:
//1.枚举常量在枚举类中是唯一的,并且是不可改变的。
//2.枚举常量可以具有属性、方法和构造函数。
//3.枚举常量可以作为参数传递给方法或构造函数。
//4.枚举常量可以使用switch语句进行比较。
//5.枚举可以实现接口,但是不能继承其他类。
//6.枚举可以定义在类内部或外部,但是不能在方法内部定义。
//7.枚举常量通常使用大写字母表示,并且使用下划线分隔单词。
//8.枚举类可以具有静态方法和静态属性。
//9.枚举类可以实现Serializable和Comparable接口。
//10.枚举类可以使用valueOf()方法将一个字符串转换为枚举常量。
enum SingletonObject5 {
//1.枚举是一种特殊的类,每个枚举常量都是该类的一个实例。
//枚举实际上是一种语法糖,枚举类的每一个实例static final修饰的,只会在类加载时初始化一次,因此为单例,并且由JVM来保证线程安全。
// 枚举类实现单例模式的优势是:
// - 简洁易读,不需要额外的代码来保证单例。
// - 可以防止懒汉模式下的双重检查锁定(DCL)问题,因为枚举类型在类加载时就已经初始化了。
//2.枚举为什么是单例
// 因为当一个类为enum的时候,其会被编译为public static final T extends Enum,
// 因为是final修饰的,所以其首先是不能被继承的。其次,Enum类中只有一个构造,其源码的注释解释:唯一的构造函数,程序员无法调起此构造函数(protected修饰)。
// 它只能供编译器响应枚举类型声明发出的代码使用。
INSTANCE;
//3.枚举实现单例模式特点之——天然不受序列化影响
// 首先,枚举都是默认继承自java中Enum类的,枚举类实现Serializable接口,但在枚举类中禁用了readObject等一系列方法(通过直接抛异常)。
// 我们知道,如果一个类实现Serializable接口,那么就不可能是单例,因为每次调用readObject方法都会返回一个新的实例。
// 所以,完全可以通过序列化来破坏单例,但是枚举类有其自己的一套序列化方式,因此其禁用readObject方法。所以,不会因为序列化而破坏单例。
//4.枚举实现单例模式特点之——天然禁止暴力反射
// 枚举避免了反射破坏单例,因为枚举类型没有构造方法,且来自父类Enum类的构造方法无法继承,故无法被反射创建。
//5.枚举类为什么没有公有的构造方法
// 枚举没有构造方法,是因为枚举类是一种特殊的类,它的实例是固定的,并且由JVM在类加载时创建。
// 如果枚举有公有的构造方法,那么就可以在外部创建新的枚举实例,这样就破坏了枚举的唯一性和不变性。
// 所以,枚举类只能有私有的构造方法,这样就保证了只有在枚举类内部才能创建枚举实例。
public void whateverMethod() {
//do something
}
}
//单例模式的扩展——序列化问题
//创建完单例对象之后,有时候我们会使用序列化将对象写入磁盘,当下次使用时再从磁盘中反序列化转化为内存对象,这样也会破坏单例模式。
//那么如何保证在序列化的情况下保证单例呢?很简单,只需要增加readResolve方法。
//在jdk源码中,规定了这个方法已经约定方法名称readResolve.这时候执行类中的readResolve方法,直接返回已经创建的实例。
//即在源码中,只要实现序列化接口的单例类拥有一个名为readResolve的返回单例对象的私有方法,那么反序列化后的结果变成了在单例类中已创建的实例对象。
class HungrySingleton implements Serializable {
private static final HungrySingleton instance;
static {
instance = new HungrySingleton();
}
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return instance;
}
private Object readResolve(){
return instance;
}
}
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我经常迷上ruby的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情
这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo
我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho
有没有一种方法可以自动生成种子数据文件并创建种子数据,就像您在下面链接中的Laravel中看到的那样?LaravelDatabaseMigrations&Seed我在另一个应用程序上看到在Rails的db文件夹下创建了一些带有时间戳的文件,其中包含种子数据。创建它的好方法是什么? 最佳答案 我建议你使用Fabrication的组合gem和Faker.Fabrication允许您编写一个模式来构建您的对象,而Faker为您提供虚假数据,如姓名、电子邮件、电话号码等。这是制造商的样子:Fabricator(:user)dousernam
我有一个交互式RubyonRails应用程序,我想在特定时间将其置于“只读模式”。这将允许用户读取他们需要的数据,但阻止他们执行写入数据库的操作。执行此操作的一种方法是在数据库中放置一个true/false变量,该变量在进行任何写入之前进行检查。我的问题。有没有更优雅的解决方案来解决这个问题? 最佳答案 如果你真的想阻止任何数据库写入,我能想到的最简单的方法是覆盖readonly?始终返回true的模型方法,无论是在选定模型中还是对于所有ActiveRecord模型。如果模型设置为只读(通常通过调用#readonly!来完成),任何