从零玩转设计模式之单例模式-danlimos

title: 从零玩转设计模式之单例模式
date: 2022-12-12 12:41:03.604
updated: 2022-12-23 15:35:29.0
url: http://www.yby6.com/archives/danlimos
categories: 
- 单例模式
- 设计模式
tags: 
- Java模式
- 单例模式
- 设计模式

前言

单例设计模式是23种设计模式中最常用的设计模式之一,无论是三方类库还是日常开发几乎都有单例设
计模式的影子。单例设计模式提供了一种在多线程情况下保证实例唯一性的解决方案。单例设计模式虽然简单,但是实现方案却非常多,大体上有以下7种最常见的方式。

饿汉模式

所谓饿汉式,就是不管你用不用这个对象,都先把这个对象进行创建出来,这样子在使用的时候就可以保证是单例。

特点

  • 线程安全性
    在加载的时候已经被实例化,所以只有这一次,线程安全的
  • 懒加载
    没有延迟加载,好长时间不使用,影响性能

示例:

// 没有延迟加载,好长时间不使用,影响性能
public class test1 {
	/**
	 * 直接初始化对象
	 * */
	private static final test1 INSTANCE = new test1();

	/**
	 * 不允许外界进行new对象
	 **/
	private test1() {

	}

	/**
	 * 放行唯一方法 获取对象
	 * @return
	 */
	public static test1 getInstance() {
		return INSTANCE;
	}
}

总结:
这种方案实现起来最简单,当test1被加载后,就会立即创建instance,因此该方法可以保证百分百的单例,instance不可能被实例化两次。但是这种做instance可能被加载后很长一段时间才会被使用,就意味着instance开辟的内存占用时间更多。

注意:
如果一个类中成员属性比较少,且占用内存资源不多,那么就可以使用饿汉式。如果一个类中都是比较重的资源,这种方式就比较不妥

懒汉模式

所谓懒汉式就是在使用时再去创建,可以理解成懒加载。

示例:

public class test2 {
	private static test2 instance;

	private test2() {
		System.out.println("类被实例化了");
	}

	public static test2 getInstance() {
		if (instance == null) {
			instance = new test2();
		}
		return instance;
	}

}

总结:
当instance为null时,getInstance会首先去new一个实例,那之后再将实例返回。

注意:
但是这种实现方式会存在线程安全问题,多个线程同时获取将会出现不同的对象实例,破坏了单例的原则。

懒汉模式+同步方法

为了解决懒汉式线程安全问题,我们可以加上同步方法

特点

  • 直接在方法上进行加锁
  • 锁的力度太大. 性能不是太好
  • synchronized 退化到了串行执行

示例:

public class test2 {
	private static test2 instance;

	private test2() {
		System.out.println("类被实例化了");
	}

	public static synchronized test2 getInstance() {
		if (instance == null) {
			instance = new test2();
		}
		return instance;
	}

}

总结:
这种做法就保证了懒加载又能够百分百保证instance是单例的,但是synchronized关键字天生的排他性导致该方法性能过低。

双重检查锁

Double-Check-Locking是一种比较聪明的做法,我们其实只需要在instance为null时,保证线程的同步性,让只有一个线程去创建对象即可,而其他线程依然是直接使用,而当instance已经有实例之后,我们并不需要线程同步操作,直接并行读即可,这里我们再给类里面加上两个属性.

特点

  • 保证了线程安全
  • 如果实例中存在多个成员属性. 由于在代码执行过程当中,会对代码进行重排,,重排后, 可能导致别一个线程获取对象时初始化属性不正确的情况
  • 加volatile
    • 创建对象步骤
      • memory = allocate(); //1:分配对象的内存空间
        ctorInstance(memory); //2:初始化对象
        instance = memory; //3:设置instance指向刚分配的内存地址
      • memory = allocate(); //1:分配对象的内存空间
        instance = memory; //3:设置instance指向刚分配的内存地址
        //注意,此时对象还没有被初始化!
        ctorInstance(memory); //2:初始化对象
    • 重排问题
      • image-1670812009354

示例:

public class test2 {
	private static test2 instance;
	private Object o1;
	private Object o2;

	private test2() {
		o1=new Object();
		o2=new Object();
		System.out.println("类被实例化了");
      	}
 	public static test2 getInstance() {
 		// 为null时,进入同步代码块,同时避免了每次都需要进入同步代码块
 		if (instance == null) {
 			// 只有一个线程能够获取到锁
 			synchronized (test2.class) {
 				// 如果为Null在创建
 				if (instance == null) {
 					instance = new test2();
                		}
            		}
        	}
 		return instance;
    	}

}

总结:
当两个线程发现 instance == null 时,只有一个线程有资格进入同步代码块,完成对instance的初始化,随后的线程再次进入同步代码块之后,因为 instance == null 不成立,就不会再次创建,这是未加载情况下并行的场景,而instance加载完成后,再有线程进入getInstance方法后,就直接返回
instance,不会进入到同步代码块,从而提高性能。

注意:
这种做法看似完美和巧妙,既满足懒加载,又保证instance的唯一性,但是这种方式实际上是会出现空指针异常的。

解析空指针异常的问题:

在test2构造方法中,我们会初始化 o1 和 o2两个资源,还有Single自身,而这三者实际上并无前后关系的约束,那么极有可能JVM会对其进行重排序,导致先实例化test2,再实例化o1和o2,这样在使用test2时,可能会因为o1和o2没有实例化完毕,导致空指针异常。

创建实例

双重检查锁+volatile

解决上面的方法其实很简单,给instance加上一个volatile关键字即可,这样就防止了重排序导致的程序异常。

private volatile static test2 instance;

内部类(Holder)方式

holder方式借助了类加载的特点,我们直接看代码。

public class test3 {
	private test3() {
		System.out.println("类被实例化了");
	}

	/**
	 * 使用内部类方式不会主动加载,只有主类被使用的时候才会进行加载
     * 第一次使用到的时候才去执行  只执行一次
	 */
	private static class Holder {
		private static test3 instance = new test3();
	}

	/**
	 * 提供外界进行调用
	 * @return
	 */
	public static test3 getInstance() {
		return Holder.instance;
	}

}

特点

  • 它结合了饿汉模式 安全性,也结合了懒汉模式懒加载。不会使用synchronized 所以性能也有所保证

  • 声明类的时候,成员变量中不声明实例变量,而放到内部静态类中

  • 不存在线程安全问题

  • 懒加载的

    • 反序列化问题
      // 该方法在反序列化时会被调用
      protected Object readResolve() throws ObjectStreamException {
      System.out.println("调用了readResolve方法!");
      return Hoder.instance;
      }

总结:
我们发现,在test3中并没有instance,而是将其放到了静态内部类中,使用饿汉式进行加载。但是实际上这并不是饿汉式。因为静态内部类不会主动加载,只有主类被使用时才会加载,这也就保证了程序运行时并不会直接创建一个instance而浪费内存,当我们主动引用Holder时,才会创建instance实例,从而保证了懒加载。

枚举方式

枚举的方式实现单例模式是《Effective Java》作者力推的方式,枚举类型不允许被继承,同样是线程安全的并且只能被初始化一次。但是使用枚举类型不能懒加载,比如下面的代码,一旦使用到里面的静态方法,INSTANCE就会立即被实例化。

特点

  • 不存在线程安全
  • 没有懒加载

示例:


public enum test4 {
	INSTANCE;

	test4() {
		System.out.println("类被实例化了");
	}
	public static test4 getInstance() {
	    return INSTANCE;
	}
}


源码

  • Runtime类
  • Mybatis ErrorContext
  • 类加载器
你的压力来源于无法自律,只是假装努力,现状跟不上内心欲望,所以你焦虑又恐慌。——杨不易
本文转载于网络 如有侵权请联系删除

相关文章

  • HibernateTemplate的使用方法

    大家好,又见面了,我是你们的朋友全栈君。HibernateTemplate提供非常多的常用方法来完成基本的操作,比如通常的增加、删除、修改、查询等操作,Spring2.0更增加对命名SQL查询的支持,也增加对分页的支持。大部分情况下,使用Hibernate的常规用法,就可完成大多数DAO对象的CRUD操作。1、常用方法: 1)voiddelete(Objectentity):删除指定持久化实例 2)deleteAll(Collectionentities):删除集合内全部持久化类实例 3)find(StringqueryString):根据HQL查询字符串来返回实例集合 4)findByNamedQuery(StringqueryName):根据命名查询返回实例集合 5)get(ClassentityClass,Serializableid):根据主键加载特定持久化类的实例 6)load(ClassentityClass,Serializableid) 7)save(Objectentity):保存新的实例 8)saveOrUpdate(Objectentity):根据实例状态,选择保

  • 作为软件工程师,给年轻时的自己的建议(上)

    如果能够穿越到10年前,你会给年轻的自己什么建议?如果穿越到刚从业时,我会给年轻时的自己一个建议——“为你想要实现的目标设定各种各样的目标。只要有正确的计划和愿景,任何目标都不会太大。”在过去的25年里,我经历了成功和失败。这段旅程令人难忘,因为它让我明白了失败和成功的重要性。每一次成功都贴近我的内心,不断让我更好地理解团队合作和成就感,但每一次失败都为我提供了一种新的创新方式,让我找到非凡的解决方案。另一个帮助我走到今天的品质是敢于冒险。我从不害怕障碍,寻找具有挑战性的任务,而不是接受我遇到的第一个项目。有意识地走出自己的舒适区,让我比同龄人更快地学会了高级技能。当我回顾十多年前,有几件事,我希望我应该或不应该做。这些职业建议本可以帮助我避免错误的决定,并以更专注和更快的方式前进。这就是为什么我决定写下我给年轻时的自己的一些建议,以帮助那些刚刚开始软件工程工作的职业旅程的人。如果我能回到过去,这些是我作为软件工程师会教给年轻的自己的18条建议(上篇仅展示1-9条,下篇展示10-18条):一、乐于提问作为一名专业人士提出问题可以让你清楚自己的角色、任务,并展示出理想的领导品质。即使你认

  • CentOS Docker 安装命令

    使用官方安装脚本自动安装安装命令如下:curl-fsSLhttps://get.docker.com|bash-sdocker--mirror复制手动安装卸载旧版本较旧的Docker版本称为docker或docker-engine。如果已安装这些程序,请卸载它们以及相关的依赖项。$sudoyumremovedocker\          docker-client\          docker-client-latest\          docker-common\          docker-latest\          docker-latest-logrotate\          docker-logrotate\          docker-engine 复制安装DockerEngine-Community使用Docker仓库进行安装在新主机上首次安装DockerEngine-Community之前,需要设置Docker仓库。之后,您可以从仓库安装和更新Docker。设置仓库安装所需的软件包。yum-utils提供了yum-config-manager

  • 腾讯云CLB日志接入/分析/可视化/告警

    LB常见问题异常定位大量QPS的场景,少量客户端请求异常,RS端未收到请求,LB是否接收到,无从判断。终端客户反馈部分请求异常,比如缓慢,RS日志记录response_time正常,耗时来自哪里?某段时间,内网7层请求异常,是哪里的问题统计分析期望有一个全链路的耗时拓扑,request_time,connect,response_time。开启了http2,是否生效,整个协议占比如何?核心域名分布在不同实例上,请求占比情况分析。LB7层接入CLS的方式单实例接入选择对应7层实例,点击小圆笔进行编辑即可。打开"启用日志"开关选择对应日志集和日志主题即可,如无合适日志集或者日志主题,可以去"访问日志"页面新建,然后点击提交即可完毕。到日志集管理,选择对应的日志主题(topic)编辑索引。有日志进来,可以选择自动配置,建议全部打开"开启统计",以便后续的统计分析功能。批量接入(创建CLB专有日志集)备注:目前批量接入,需要找CLB产品开启白名单,才可以看到入口。建议:按照业务实际情况区分不同的日志主题,比如http层,缓存层,数据层

  • 深入B端SaaS产品设计核心理念

    来源:产品晓思作者:李晓杰why 为什么要用SaaS模式,这个话题我们从面向B端的传统软件厂商的痛点来聊。传统软件厂商通常的交付模式是,销售和售前根据线索参与招投标,中标后项目实施团队入驻客户现场,根据客户的实际需求开发或改造功能,完成软件部署交付并经客户业务验收后,核心团队离场由维护人员接手更新。这种模式的局限性总结来说是“赚钱慢”,具体说来如下:1、成本高。主要包括三方面:销售成本、部署人力成本和维护人力成本。有多少项目,就必须配备多少人力。2、速度慢。主要包括两方面:交付慢和回款慢。项目周期动辄半年,甚至一年、两年。3、可复制性低。主要包括两方面:人力依赖和定制化。项目的成交依赖售前的行业见解和对客户KP动机的洞察能力;项目的成功依赖于需求分析师对客户真实需求的挖掘和方案设计能力,以及项目经理对人、事的控制能力。对特定能力人的需求,限制了传统软件厂商的扩张能力,同时,频繁出差也造成这个领域的优秀人才流失严重。产品化是降低成本、提高复制性的关键,然而,大客户另外30%的个性定制化需求,是无法跨越的鸿沟。从客户角度,传统软件交付模式也存在着局限性,主要包括:价格贵、交付慢、升级难、失

  • 什么是缓存击穿、雪崩、穿透

    随着互联网的越来越普及,用户越来越多,系统性能瓶颈成了越来越热门的话题。要解决性能问题的技术手段有很多,比如:缓存、CDN加速、页面静态化、集群、分布式、异步等。缓存通常被作为首先技术方案,简单而且提升效果明显,它能够将速度提升100倍。那么问题来了,缓存为啥会怎么快呢?因为传统的数据库操作是基于磁盘的,而缓存是基于内存的,内存操作和磁盘操作的速度根本不是一个数量级的。目前市面上主流的缓存有:redis和memcache,这两个都是基于内存的缓存技术,二者的区别我在这里暂时不讲。使用缓存的伪代码一般如下:Stringorder=redisClient.get(key); if(order!=null){ returnorder; } order=db.get(key); redisClient.put(key,order); redisClient.expire(key,3000); returnorder;复制根据key获取数据,先从缓存中查一下有没有,如果有则直接返回。如果没有,再从数据库中查到数据,然后将数据放入缓存中,并且给当前key设置一个失效时间,下次再用同样的key来请求

  • 机器人混合控制的连续-离散强化学习

    许多实际控制问题既涉及离散决策变量(如控制模式的选择、档位切换或数字输出),也涉及连续决策变量(如速度设定值、控制增益或模拟输出)。然而,当定义相应的最优控制或强化学习问题时,通常用完全连续或完全离散的作用空间来近似。这些简化旨在将问题定制为特定的算法或求解器,这些算法或求解器可能只支持一种类型的动作空间。或者,专家试探法被用来从连续的空间中移除离散的动作。相比之下,我们建议通过混合强化学习来解决混合问题,从而以“自然”的形式处理混合问题,混合强化学习可以同时优化离散和连续的动作。在我们的实验中,我们首先证明了所提出的方法有效地解决了这种本地混合强化学习问题。然后,我们在模拟和机器人硬件上展示了移除可能不完美的专家设计的试探法的好处。最后,混合强化学习鼓励我们重新思考问题的定义。我们建议重新制定控制问题,例如通过增加元动作,以改善探索或减少机械磨损。原文题目:Continuous-DiscreteReinforcementLearningforHybridControlinRobotics原文:Manyreal-worldcontrolproblemsinvolvebothdiscre

  • 重磅!2019大数据白皮书发布(附PPT解读)

    公众号后台回复:“大数据”,获取本文资料中国信通院发布了《大数据白皮书(2019)》(以下简称“白皮书”),这是中国信通院第四次发布大数据白皮书。白皮书在前三版的基础上,聚焦一年多来大数据各领域的发展,探讨了大数据技术、产业、应用、安全及数据资产管理的进展和趋势。白皮书显示,2019年以来,全球大数据技术、产业、应用等多方面的发展呈现了新的趋势,也正在进入新的阶段。当前,大数据技术呈现出六大融合趋势:(一)算力融合:多样性算力提升整体效率(二)流批融合:平衡计算性价比的最优解(三)TA融合:混合事务/分析支撑即时决策(四)模块融合:一站式数据能力复用平台(五)云数融合:云化趋势降低技术使用门槛(六)数智融合:数据与智能多方位深度整合更多精彩,敬请阅读现场发布PPT。1 2345 67891011 12 131415 1617181920212223242526

  • Linux的内核和模块

    Linux内核的作用管理内存提高效率,管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。调度任务某些机制执行从用户空间到内核的函数调用。管理进程内核通过SCI提供了一个应用程序接口(API)来创建一个新进程,停止进程(kill、exit),并在它们之间进行通信和同步。网络功能支持大量网络协议,包括TCP/IP,也可以支持流控制传输协议(SCTP)之类的协议,提供了很多比TCP更高级的特性。管理I/O给设备提供驱动,让设备注册I/O,完成中断及中断处理等。管理安全selinux、md5、sha1等。管理文件系统VFS(虚拟文件系统)上层,是对open、close、read之类函数的一个通用API抽象。VFS下层是文件系统抽象,定义了上层函数的实现方式。(ext3、ext4、xfs、iso9660...)内核模块arch 表示各种各样的平台 crypto 安全加密 drivers 驱动各种各样的硬件 fs 文件系统 lib 各种各样的库 net 网络功能 sound 声卡 查看所有模块 lsmod 查看指定模块的详细信息 modinfo模块名 动态加载模块 modp

  • Detectron2学习一:环境配置,YAML语法

    一、环境配置参考链接:https://blog.csdn.net/weixin_39916966/article/details/103199105安装Cuda9.0+cuDNN7.4复制anaconda创建虚拟环境:condacreate-npytorch13python=3.6.8,进入环境,condaactivatepytorch13复制安装pytorch1.3:condainstallpytorch=1.3torchvisioncudatoolkit=10.1-cpytorch复制安装opencv:pipinstallopencv-python==3.4.5.20复制安装fvcore:pipinstall'git+https://github.com/facebookresearch/fvcore'复制安装pycocotools: pipinstallcython; pipinstall'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI复制确保gcc>=

  • GraphQL的schema定义文件加载到内存里的例子

    GraphQL里我定义了一个名为signup的mutation:解析成JavaScriptobject后如下图所示:一个接受id为参数的query,返回类型为Product:productquery在内存中的JavaScript对象:一个名为deal的subscription:

  • MySQL每秒57万的写入,带你飞~

    本文作者:吴炳锡来源:https://yq.aliyun.com/articles/278034一、需求一个朋友接到一个需求,从大数据平台收到一个数据写入在20亿+,需要快速地加载到MySQL中,供第二天业务展示使用。 二、实现再分析对于单表20亿,在MySQL运维,说真的这块目前涉及得比较少,也基本没什么经验,但对于InnoDB单表Insert如果内存大于数据情况下,可以维持在10万-15万行写入。但很多时间我们接受的项目还是数据超过内存的。这里使用XeLabsTokuDB做一个测试。 三、XeLabsTokuDB介绍项目地址:https://github.com/XeLabs/tokudb 相对官方TokuDB的优化:内置了jemalloc内存分配; 引入更多的内置的TokuDB性能指标; 支持Xtrabackup备份; 引入ZSTD压缩算法; 支持TokuDB的binlog_group_commit特性;四、测试表TokuDB核心配置: 表结构:利用loaddata写入数据:计算一下每秒写入速度:文件大小:实际文件8.5G,写入TokuDB大小3.5G,只是接近于一半多点的压缩量

  • Android - 自定义View,实现不一样的输入框

    最近看到一个效果,就是在登陆界面输入账号密码的时候,会有一个动画效果,感觉不错,找了一些资料,学习了一下。已经实现效果,效果如下:undefined_腾讯视频ok,首先先分析一下这个输入框(账号和密码是一样的),上代码:<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><!-- 顶部文本控件--><TextViewandroid:id="@+id/tv_topmessage"android:layout_width="wrap_content"android:layout_height="wrap_content"andro

  • 是什么让我们爱上Javascript

    引子   过去,人们对于Javascript一直报着轻视的态度,人们认为它又慢又容易出错,而且在不同浏览器中解释也不一样,但是现在,Javascript确确实实的在改变我们的网络,越来越多的网络和APP应用开始使用Javascript。今天,我们就来讲讲我们为什么会有这种观念,回顾Javascript的历史来诊断他的现状,同时通过一些片段来表明Javascript同其他开发语言的本质不同,唯有如此我们才能明白为何Javascript这么重要。早期的迷惑   从某种程度上来讲,Javascript是Java的恶魔兄弟(Java'seviltwin)。他们同岁,都于1995年以Beta的版本出现,并且都在次年1996年推出了1.0版本。在语法上他们也很相近,从名称上来看他们就像是一家人一样。   我们第一次听说Javascript是在1995年,当时Netscape推出了Navigator2.0的Beta版本,这个版本中没有包含Javascript,但是有对于JavaApplet的支持。同时,Netscape宣布页面内嵌的语言LiveScript。这个声明并没有引起太多的注意,在

  • FFmpeg菜鸡互啄#第4篇#音频解码

    解码过程音频解码跟上一篇的视频解码过程是一样的:打开输入文件,查找音频流,打开解码器,循环读帧解码帧,关闭解码器,关闭输入文件。Code#define_CRT_SECURE_NO_WARNINGS #include<stdio.h> /* #define__STDC_CONSTANT_MACROS #ifndefINT64_C #defineINT64_C(c)(c##LL) #defineUINT64_C(c)(c##ULL) #endif */ extern"C" { #include"libavcodec/avcodec.h" #include"libavformat/avformat.h" #include"libswscale/swscale.h" #include"libavdevice/avdevice.h" } #pragmacomment(lib,"avcodec.lib") #pragmacomment(lib,"avd

  • echarts的markline的使用 y轴预警线

    代码示例: app.title='坐标轴刻度与标签对齐'; option={ color:['#3398DB'], tooltip:{ trigger:'axis', axisPointer:{//坐标轴指示器,坐标轴触发有效 type:'shadow'//默认为直线,可选为:'line'|'shadow' } }, grid:{ left:'3%', right:'4%', bottom:'3%', containLabel:true }, xAxis:[ { type:'category', data:['Mon','Tue','Wed','Thu','Fri','Sat','Sun'], axisTick:{ alignWithLabel:true } } ], yAxis:[ { type:'value' } ], series:[ { name:'直接访问', type:'bar', barWidth:'60%', data:[10,52,200,334,390,330,220] } ] }; 复制  效果图:     主要代码:(设置预警线值,样

  • 学习笔记:linux之文件空洞

    文件空洞 我们知道lseek()系统调用可以改变文件的偏移量,但如果程序调用使得文件偏移量跨越了文件结尾,然后再执行I/O操作,将会发生什么情况?read()调用将会返回0,表示文件结尾。令人惊讶的是,write()函数可以在文件结尾后的任意位置写入数据。在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。从原来的文件结尾到新写入数据间的这段空间被成为文件空洞。调用write后文件结尾的位置已经发生变化。 在Linux系统之中,文件结束符EOF根本不是一个字符,而是当系统读取到文件结尾,所返回的一个信号值(也就是-1),至于系统怎么知道文件的结尾,资料上说是通过比较文件的长度。 文件空洞占用任何磁盘空间,直到后续某个时点,在文件空洞中写入了数据,文件系统才会为之分配磁盘块。空洞的存在意味着一个文件名义上的大小可能要比其占用的磁盘存储总量要大(有时大出许多)。向文件空洞中写入字节,内核需要为其分配存储单元,即使文件大小不变,系统的可用磁盘空间也将减少。这种情况并不常见,但也需要了解。 下面看一个例子:(转自http://bl

  • 栈的实现(后有完整经过测试的代码需要可以自取)

    文章目录 零.前言1.栈的特点2.栈的结构1.逻辑结构2.物理结构 3.栈的基本操作1.建立一个栈2.栈的初始化3.栈的销毁4.打印一个栈5.判断栈的大小6.判断栈是否为空7.得到栈顶元素 4.栈的插入和删除1.栈的插入2.栈的删除 5.栈的实现1.ST.h文件2.ST.c文件3.test.c文件 6.测试7.总结 零.前言 栈作为一种数据结构是一种特殊的顺序表,即规定只能在·末尾·进行插入和删除,栈和队列的提出实际是为了减少思考量,比如你发现某个存储结构满足栈的特点,那就不用考虑进行什么头插头删和查找等操作了,直接把想要实现的操作(比如顺序表)写成栈就可以了。 1.栈的特点 栈是一种存储数据的结构,只支持后进先出,通常用顺序表来进行栈的实现。我们实现栈只需要遵循一个原则就是后进后出就足够了。 2.栈的结构 1.逻辑结构 只能从顶部进行插入和删除。 2.物理结构 物理结构和顺序表时一样的,都是用数组来存储数据。 3.栈的基本操作 1.建立一个栈 typedefstructstack{ DataType*a; inttop; intcapacity; }ST; 复制

  • Python tuple元组学习

    1.tuple和list非常类似,但是tuple一旦初始化就不能修改 classmates=('Michael','Bob','Tracy')复制 现在,classmates这个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的, 你可以正常地使用classmates[0],classmates[-1],但不能赋值成另外的元素。 不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。 2.定义一个元素tuple 要定义一个只有1个元素的tuple,如果你这么定义: t=(1)复制 定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号, 这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。 所以,只有1个元素的tuple定义时必须加一个逗号,,来消除歧义: t=(1,)复制 3.“可变”的tuple t=('a','b',['A','B'])复制 t[2][0]='X'复制 表

  • HTTP

    对http的复习整理。 HTTP即超文本标记语言,是WWW是描述语言。HTML语言是web页面的规范,它使用各种HTML标签来设置网页的显示格式。静态页面是标准的HTML格式,动态页面经过应用程序服务器的处理后也生成HTML代码。 一.设置页面属性 1body标签属性设置页面属性 1.      Background属性:页面背景图片的URL 2.      bgcolor属性:页面背景颜色。 3.      text属性:页面文本的默认颜色。 4.      link属性:页面中超文本链接的颜色。 5.      vlink属性:页面中已访问的超文本链接的颜色。 6.      alink属性:页面中活动链接的颜色。 7.&nb

  • 轻松理解 Spirng IoC/控制反转

    目录SpringIoC概述IoC:InverseofControl(控制反转)一个例子SpringIoC的好处IoC实例SpringIoC容器的设计设计BeanFactoryApplicationContextApplicationContext常见实现类:Bean的定义与初始化依赖注入(DI)什么是依赖什么是依赖注入IoC和DI的关系如何自己实现一个的IoC容器SpringBoot中IoC的使用案例总结 SpringIoC概述 IoC:InverseofControl(控制反转) 控制反转不是一种技术,而是一种思想。 既然说是反转就说先明白什么是正,什么是反 正控:就是我们平时最常见的那种使用形式,要使用某个对象,需要自己去负责对象的创建,属于自力更生。 反控:若要使用某个对象,无需自己创建,只需要从IoC容器中去获取,创建对象的过程交给Spring来处理,Spring来维护这个IoC容器,属于富二代,需要啥找管家。 所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,通过描述反转给容器来帮忙实现。 在Java中可以是XML或者注解,通过Spring去产生或获

相关推荐

推荐阅读