首页 文章 精选 留言 我的

精选列表

搜索[java],共10000篇文章
优秀的个人博客,低调大师

Java描述设计模式(01):单例模式

一、单例模式 1、概念图解 单例设计模式定义:确保这个类只有一个实例,并且自动的实例化向系统提供这个对象。 2、样例代码 package com.model.test; public class Singleton { // 使用静态变量记录唯一实例 private static Singleton singleton = null; private Singleton (){} public static Singleton getInstance (){ if (singleton == null){ singleton = new Singleton() ; } return singleton ; } public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance() ; Singleton singleton2 = Singleton.getInstance() ; /** * com.model.test.Singleton@15db9742 * com.model.test.Singleton@15db9742 */ System.out.println(singleton1); System.out.println(singleton2); } } Singleton称为单例类,构造函数使用private修饰,确保系统中只能产生一个实例,并且自动生成的。上面代码也就是所谓的懒汉式加载:只有到使用该对象的时候才来创建,意思饿了才来做饭吃。 二、线程安全问题 在上面的代码中存在一个很明显的线程安全问题,当有多条线程来请求对象实例的时候,因为对象的创建是需要时间的,假设A线程进来判断singleton == null,就会进入对象的创建过程,这时如果同时在过来几条线程,那么他们都会得到一个对象实例,这个就是所谓的线程安全问题。 1、同步控制方式 package com.model.test; public class Singleton { // 使用静态变量记录唯一实例 private static Singleton singleton = null; private Singleton (){} public static synchronized Singleton getInstance (){ if (singleton == null){ singleton = new Singleton() ; } return singleton ; } } 这样操作会影响系统性能 2、饿汉式加载 public class Singleton { // 使用静态变量记录唯一实例 private static Singleton singleton = new Singleton(); private Singleton (){} public static Singleton getInstance (){ return singleton ; } } 这里先把对象创建出来,有需要直接使用; 3、双重检查 public class Singleton { // 使用静态变量记录唯一实例 // volatile可以确保当singleton被初始化后,多线程才可以正确处理 // 被volatile修饰的变量的值,将不会被本地线程缓存 // 对该变量读写都是直接操作共享内存,确保多个线程能正确的处理该变量。 private static volatile Singleton singleton = null ; private Singleton (){} public static Singleton getInstance (){ // 如果实例不存在,则进入同步区 if (singleton == null){ // 只有第一次才会彻底执行这里面的代码 synchronized (Singleton.class) { if (singleton == null){ singleton = new Singleton() ; } } } return singleton ; } } 4、枚举方式 package com.model.design.base.node01.singleton; import org.junit.Test; /** * 类级内部类里面创建对象实例 */ public class C06_Singleton { @Test public void test01 (){ SingletonDemo INSTANCE1 = SingletonDemo.INSTANCE ; SingletonDemo INSTANCE2 = SingletonDemo.INSTANCE ; System.out.println(INSTANCE1 == INSTANCE2); INSTANCE1.info(); INSTANCE2.info(); } } enum SingletonDemo { INSTANCE ; public void info (){ System.out.println("枚举方式实现单例"); } } 三、延迟类初始化 1、基础概念 1)、类级内部类 简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。 类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。 2)、多线程缺省同步锁 在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: 1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时 2.访问final字段时 3.在创建线程之前创建对象时 4.线程可以看见它将要处理的对象时 2、实现方式 要想很简单地实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。比如前面的饿汉式实现方式,在类装载的时候就初始化对象,不管是否需要,存在一定的空间浪费。一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象实例,从而同时实现延迟加载和线程安全。 public class LazySingleton { /** * 类级内部类 */ private static class SingletonHolder { private static LazySingleton lazySingleton = new LazySingleton() ; } public static LazySingleton getInstance (){ return SingletonHolder.lazySingleton ; } public static void main(String[] args) { LazySingleton lazySingleton1 = LazySingleton.getInstance() ; LazySingleton lazySingleton2 = LazySingleton.getInstance() ; /** * com.model.test.LazySingleton@15db9742 * com.model.test.LazySingleton@15db9742 */ System.out.println(lazySingleton1+";;"+lazySingleton2); } } 四、JDK源码单例模式 Runtime单例实现源码。 1、案例演示 /** * JDK 单例模式分析 */ public class C07_Singleton { public static void main(String[] args) { Runtime runtime1 = Runtime.getRuntime() ; Runtime runtime2 = Runtime.getRuntime() ; /* * 1229416514 * 1229416514 */ System.out.println(runtime1.hashCode()); System.out.println(runtime2.hashCode()); } } 2、源代码分析 public class Runtime { private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } private Runtime() {} } 基于饿汉模式实现的单例模式。 五、Spring框架中应用 1、创建测试类 public class UserBean { } 2、Spring配置文件 <!-- 单例Bean --> <bean id="user" class="com.model.design.spring.node01.singleton.UserBean" /> 3、测试读取Bean对象 package com.model.design.spring.node01.singleton; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Spring框架中单例模式 */ public class S01_Singleton { @Test public void test01 (){ ApplicationContext context01 = new ClassPathXmlApplicationContext("/spring/spring-context.xml"); ApplicationContext context02 = new ClassPathXmlApplicationContext("/spring/spring-context.xml"); UserBean user01 = (UserBean)context01.getBean("user") ; UserBean user02 = (UserBean)context01.getBean("user") ; UserBean user03 = (UserBean)context02.getBean("user") ; // com.model.design.spring.node01.singleton.UserBean@364841 System.out.println(user01); // com.model.design.spring.node01.singleton.UserBean@364841 System.out.println(user02); // com.model.design.spring.node01.singleton.UserBean@c4711c System.out.println(user03); } } 结论Spring单例模式与纯粹的单例设计模式的主要区别尽管使用相同的类加载器来加载两个应用程序上下文,但是UserBean的实例是不一样的。也就是Spring框架中的单例对象是基于应用程序中。 六、单例模式总结 1、注意事项 单例模式注意事项和细节说明 1) 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。 2) 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new Object() 的方式。 3) 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象。 2、优缺点 优点: 1、单例模式只会创建一个对象实例,减少内存消耗 2、设置全局访问点,优化共享资源的访问 缺点: 1、没有接口,很难扩展 2、不利于测试 3、与单一职责原则冲突 七、源代码地址 GitHub地址:知了一笑 https://github.com/cicadasmile/model-arithmetic-parent 码云地址:知了一笑 https://gitee.com/cicadasmile/model-arithmetic-parent

优秀的个人博客,低调大师

Java中多个ifelse语句的替代设计

今天在改老代码的过程中,亲眼见证了一段30个if-else嵌套的代码... 然后搜集了一些资料做了以下简单整理。 概述 ifelse是任何编程语言的重要组成部分。但是我们编写了大量嵌套的if语句,这使得我们的代码更加复杂和难以维护。 接下来,让我们探索如何简化代码的中的ifelse语句写法。 案例研究 我们经常遇到涉及很多条件的业务逻辑,并且每个逻辑都需要不同的处理方式。以Calculator类为例。我们将有一个方法,它接受两个数字和一个运算符作为输入,并根据操作返回结果: public int calculate(int a, int b, String operator) { int result = Integer.MIN_VALUE; if ("add".equals(operator)) { result = a + b; } else if ("multiply".equals(operator)) { result = a * b; } else if ("divide".equals(operator)) { result = a / b; } else if ("subtract".equals(operator)) { result = a - b; } return result; } 我们也可以使用switch语句来实现它: switch (operator) { case "add": result = a + b; break; // other cases } return result; } 在典型的开发中,if语句可能会变得更大,更复杂。此外,当存在复杂条件时,switch语句不适合。 拥有嵌套决策结构的另一个副作用是它们变得难以管理。例如,如果我们需要添加一个新的运算符,我们必须添加一个新的if语句并实现该操作。 重构 可以通过设计模式,来达到我们要的效果。 工厂模式 很多时候,我们遇到ifelse结构,最终在每个分支中执行类似的操作。这提供了提取工厂方法的机会,该工厂方法返回给定类型的对象并基于具体对象行为执行操作。 对于我们的示例,让我们定义一个具有单个apply方法的Operation接口: int apply(int a, int b); } 该方法将两个数字作为输入并返回结果。让我们定义一个用于执行添加的类: public class Addition implements Operation { @Override public int apply(int a, int b) { return a + b; } } 我们现在将实现一个工厂类,它根据给定的运算符返回Operation的实例: public class OperatorFactory { static Map<String, Operation> operationMap = new HashMap<>(); static { operationMap.put("add", new Addition()); operationMap.put("divide", new Division()); // more operators } public static Optional<Operation> getOperation(String operator) { return Optional.ofNullable(operationMap.get(operator)); } } 现在,在Calculator类中,我们可以查询工厂以获取相关操作并应用源数: public int calculateUsingFactory(int a, int b, String operator) { Operation targetOperation = OperatorFactory .getOperation(operator) .orElseThrow(() -> new IllegalArgumentException("Invalid Operator")); return targetOperation.apply(a, b); } 在这个例子中,我们已经看到了如何将责任委托给工厂类提供的松散耦合对象。但是有可能嵌套的if语句只是转移到了工厂类,这违背了我们的目的。 或者,我们可以在Map中维护一个对象存储库,可以查询该存储库以进行快速查找。正如我们所见,OperatorFactory#operationMap服务于我们的目的。我们还可以在运行时初始化Map并将它们配置为查找。 使用枚举 除了使用Map之外,我们还可以使用Enum来标记特定的业务逻辑。之后,我们可以在嵌套的if语句或switch case 语句中使用它们。或者,我们也可以将它们用作对象的工厂并制定策略以执行相关的业务逻辑。 这样可以减少嵌套if语句的数量,并将责任委托给单个Enum值。 让我们看看我们如何实现它。首先,我们需要定义我们的枚举: public enum Operator { ADD, MULTIPLY, SUBTRACT, DIVIDE } 可以观察到,这些值是不同运算符的标签,将进一步用于计算。我们总是可以选择在嵌套的if语句或switch case中使用这些值作为不同的条件,但让我们设计一种将逻辑委托给Enum本身的替代方法。 我们将为每个Enum值定义方法并进行计算。例如: @Override public int apply(int a, int b) { return a + b; } }, // other operators public abstract int apply(int a, int b); 然后在Calculator类中,我们可以定义一个执行操作的方法: public int calculate(int a, int b, Operator operator) { return operator.apply(a, b); } 现在,我们可以通过使用Operator#valueOf()方法将String值转换为Operator来调用该方法: @Test public void test() { Calculator calculator = new Calculator(); int result = calculator.calculate(3, 4, Operator.valueOf("ADD")); assertEquals(7, result); } 命令模式 在前面的讨论中,我们已经看到使用工厂类来返回给定运算符的正确业务对象的实例。稍后,业务对象用于在计算器中执行计算。 我们还可以设计一个Calculator#calculate方法来接受可以在输入上执行的命令。这将是替换嵌套if语句的另一种方法。 我们首先定义我们的Command接口: public interface Command { Integer execute(); } 接下来,让我们实现一个AddCommand: public class AddCommand implements Command { // Instance variables public AddCommand(int a, int b) { this.a = a; this.b = b; } @Override public Integer execute() { return a + b; } } 最后,让我们在Calculator中引入一个接受并执行Command的新方法: public int calculate(Command command) { return command.execute(); } 接下来,我们可以通过实例化AddCommand调用计算并将其发送到Calculator#calculate方法: @Test public void test() { Calculator calculator = new Calculator(); int result = calculator.calculate(new AddCommand(3, 7)); assertEquals(10, result); } 规则引擎 当我们最终编写大量嵌套if语句时,每个条件都描述了一个业务规则,必须对其进行评估才能处理正确的逻辑。规则引擎从主代码中获取了这种复杂性。一个RuleEngine评估规则和返回基于输入的结果。 让我们通过设计一个简单的RuleEngine来演示一个例子,该RuleEngine通过一组规则处理Expression并返回所选规则的结果。首先,我们将定义一个Rule接口: public interface Rule { boolean evaluate(Expression expression); Result getResult(); } 其次,让我们实现一个RuleEngine: public class RuleEngine { private static List<Rule> rules = new ArrayList<>(); static { rules.add(new AddRule()); } public Result process(Expression expression) { Rule rule = rules .stream() .filter(r -> r.evaluate(expression)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule")); return rule.getResult(); } } 所述RuleEngine接受一个表达对象,并返回结果。现在,让我们将Expression类设计为一组包含两个Integer对象的Operator,它将被应用: public class Expression { private Integer x; private Integer y; private Operator operator; } 最后让我们定义一个自定义的AddRule类,该类仅在指定ADD操作时进行求值: public class AddRule implements Rule { @Override public boolean evaluate(Expression expression) { boolean evalResult = false; if (expression.getOperator() == Operator.ADD) { this.result = expression.getX() + expression.getY(); evalResult = true; } return evalResult; } } 我们现在将使用Expression调用RuleEngine: @Test public void test() { Expression expression = new Expression(5, 5, Operator.ADD); RuleEngine engine = new RuleEngine(); Result result = engine.process(expression); assertNotNull(result); assertEquals(10, result.getValue()); } 结论通过这些设计模式,可以作为我们的ifelse语句的替代方案,具体用哪一种可以根据你的实际业务场景来决定。 5万人关注的大数据成神之路,不来了解一下吗?5万人关注的大数据成神之路,真的不来了解一下吗?5万人关注的大数据成神之路,确定真的不来了解一下吗? 欢迎您关注《大数据成神之路》

优秀的个人博客,低调大师

JAVA8 MAP新增方法详解

1、compute default V compute(K key,BiFunction<? super K,? super V,? extends V> remappingFunction)Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). 尝试通过Lambda表达式重新计算给定KEY的映射值并更新MAP(值为null则删除KEY,否则重新写入)。 Map<String, String> tMap = new HashMap<String, String>() { { put("A", "AAA"); put("B", "BBB"); } }; tMap.compute("A", (k, v) -> v == null ? "AAA" : v.concat("AAA"));//KEY存在VALUE不为空且Lambda计算结果不为空,更新KEY System.out.println(tMap); tMap.compute("B", (k, v) -> v == null ? "BBB" : null);//KEY存在VALUE不为空但Lambda计算结果为空,删除KEY System.out.println(tMap); tMap.compute("C", (k, v) -> v == null ? "CCC" : v.concat("CCC"));//KEY不存在但Lambda计算结果不为空,新增KEY System.out.println(tMap); 2、computeIfAbsent default V computeIfAbsent(K key,Function<? super K,? extends V> mappingFunction) If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null. 如果给定的KEY为关联VALUE或关联到null,则尝试通过给定的Lambda函数计算其值并写入MAP(值为null则不写入)。 Map<String,String> map = new HashMap<>(); map.computeIfAbsent("A",k -> null); System.out.println(map); map.computeIfAbsent("B",k -> "BBB"); System.out.println(map); 3、computeIfPresent default V computeIfPresent(K key,BiFunction<? super K,? super V,? extends V> remappingFunction) If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.如果给定KEY存在映射值且非null,尝试通过Lambda表达式计算新值并更新MAP If the function returns null, the mapping is removed. If the function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.如果Lambda计算结果为null,则删除KEY。如果Lambda计算发生异常,则原映射关系不变。 Map<String, String> tMap = new HashMap<String, String>() { { put("A", "AAA"); put("B", "BBB"); } }; tMap.computeIfPresent("A", (k, v) -> v == null ? "AAA" : v.concat("AAA"));//Lambda计算结果不为空,更新KEY System.out.println(tMap); tMap.computeIfPresent("B", (k, v) -> v == null ? "BBB" : null);//Lambda计算结果为空,删除KEY System.out.println(tMap);

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册