当前位置: 首页 > 编程日记 > 正文

java/android 设计模式学习笔记(1)--- 单例模式

前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使用的时候也会有一些坑。
  PS:对技术感兴趣的同鞋加群544645972一起交流。

设计模式总目录

java/android 设计模式学习笔记目录

特点

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

  单例模式的使用很广泛,比如:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置、和注册表(registry)的对象、日志对象,充当打印机、显卡等设备的驱动程序的对象等,这些类的对象只能有一个实例,如果制造出多个实例,就会导致很多问题的产生,程序的行为异常,资源使用过量,或者不一致的结果等,所以单例模式最主要的特点:

  1. 构造函数不对外开放,一般为private;
  2. 通过一个静态方法或者枚举返回单例类对象;
  3. 确保单例类的对象有且只有一个,尤其是在多线程的环境下;
  4. 确保单例类对象在反序列化时不会重新构建对象。
通过将单例类构造函数私有化,使得客户端不能通过 new 的形式手动构造单例类的对象。单例类会暴露一个共有静态方法,客户端需要调用这个静态方法获取到单例类的唯一对象,在获取到这个单例对象的过程中需要确保线程安全,即在多线程环境下构造单例类的对象也是有且只有一个,这是单例模式较关键的一个地方。
  • 主要优点单例模式的主要优点如下:
  1. 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
  2. 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
  3. 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例对象共享过多有损性能的问题。
主要缺点
  1. 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  2. 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
  3. 现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
  4. 单例对象如果持有Context,那么很容易引发内存泄漏,此时需要注意传递给单例对象的Context最好是 Application Context。

UML类图



  类图很简单,Singleton 类有一个 static 的 instance对象,类型为 Singleton ,构造函数为 private,提供一个 getInstance() 的静态函数,返回刚才的 instance 对象,在该函数中进行初始化操作。

示例与源码

单例模式的写法很多,总结一下:

lazy initialization, thread-unsafety(懒汉法,线程不安全)

延迟初始化,一般很多人称为懒汉法,写法一目了然,在需要使用的时候去调用getInstance()函数去获取Singleton的唯一静态对象,如果为空,就会去做一个额外的初始化操作。

public class Singleton {private static Singleton instance = null;private Singleton(){}public static Singleton getInstance() {if(instance == null) instance = new Singleton();return instance;}
}复制代码

需要注意的是这种写法在多线程操作中是不安全的,后果是可能会产生多个Singleton对象,比如两个线程同时执行getInstance()函数时,然后同时执行到 new 操作时,最后很有可能会创建两个不同的对象。

lazy initialization, thread-safety, double-checked(懒汉法,线程安全)

需要做到线程安全,就需要确保任意时刻只能有且仅有一个线程能够执行new Singleton对象的操作,所以可以在getInstance()函数上加上 synchronized 关键字,类似于:

public static synchronized Singleton getInstance() {if(singleton == null) instance = new Singleton();return instance;}复制代码

但是套用《Head First》上的一句话,对于绝大部分不需要同步的情况来说,synchronized 会让函数执行效率糟糕一百倍以上(Since synchronizing a method could in some extreme cases decrease performance by a factor of 100 or higher),所以就有了double-checked(双重检测)的方法:

public class Singleton {private volatile static Singleton instance = null;private Singleton(){}public static Singleton getInstance() {if (instance == null){synchronized (Singleton.class){if (instance == null){instance = new Singleton();}}}return instance;}
}复制代码

我们假设两个线程A,B同时执行到了getInstance()这个方法,第一个if判断,两个线程同时为true,进入if语句,里面有个 synchronized 同步,所以之后有且仅有一个线程A会执行到 synchronized 语句内部,接着再次判断instance是否为空,为空就去new Singleton对象并且赋值给instance,A线程退出 synchronized 语句,交出同步锁,B线程进入 synchronized 语句内部,if判断instance是否为空,防止创建不同的instance对象,这也是第二个if判断的作用,B线程发现不为空,所以直接退出,所以最终A和B线程可以获取到同一个Singleton对象,之后的线程调用getInstance()函数,都会因为Instance不为空而直接返回,不会受到 synchronized 的性能影响。

volatile关键字介绍

double-checked方法用到了volatile关键字,volatile关键字的作用需要仔细介绍一下,在C/C++中,volatile关键字的作用和java中是不一样的,总结一下:

  1. C/C++中的volatile关键字作用
  • 可见性“可见性”指的是在一个线程中对该变量的修改会马上由工作内存(Work Memory)写回主内存(Main Memory),所以会马上反应在其它线程的读取操作中。顺便一提,工作内存和主内存可以近似理解为实际电脑中的高速缓存和主存,工作内存是线程独享的,主存是线程共享的。
  • 不可优化性“不可优化”特性,volatile告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。
  • 顺序性”顺序性”,能够保证Volatile变量间的顺序性,编译器不会进行乱序优化。Volatile变量与非Volatile变量的顺序,编译器不保证顺序,可能会进行乱序优化。同时,C/C++ Volatile关键词,并不能用于构建happens-before语义,因此在进行多线程程序设计时,要小心使用volatile,不要掉入volatile变量的使用陷阱之中。
java中volatile关键字作用Java也支持volatile关键字,但它被用于其他不同的用途。当volatile用于一个作用域时,Java保证如下:
  • (适用于Java所有版本)读和写一个volatile变量有全局的排序。也就是说每个线程访问一个volatile作用域时会在继续执行之前读取它的当前值,而不是(可能)使用一个缓存的值。(但是并不保证经常读写volatile作用域时读和写的相对顺序,也就是说通常这并不是有用的线程构建)。
  • (适用于Java5及其之后的版本)volatile的读和写建立了一个happens-before关系,类似于申请和释放一个互斥锁[8]。
使用volatile会比使用锁更快,但是在一些情况下它不能工作。volatile使用范围在Java5中得到了扩展,特别是双重检查锁定现在能够正确工作[9]。

上面有一个细节,java 5版本之后volatile的读与写才建立了一个happens-before的关系,之前的版本会出现一个问题:Why is volatile used in this example of double checked locking,这个答案写的很清楚了,线程 A 在完全构造完 instance 对象之前就会给 instance 分配内存,线程B在看到 instance 已经分配了内存不为空就回去使用它,所以这就造成了B线程使用了部分初始化的 instance 对象,最后就会出问题了。Double-checked locking里面有一句话

As of J2SE 5.0, this problem has been fixed. The volatile keyword now ensures that 
multiple threads handle the singleton instance correctly. This new idiom is 
described in [2] and [3].复制代码

所以对于 android 来说,使用 volatile关键字是一点问题都没有的了。

  参考文章

  Volatile变量

  C/C++ Volatile关键词深度剖析

  Java中volatile的作用以及用法

eager initialization thread-safety (饿汉法,线程安全)

“饿汉法”就是在使用该变量之前就将该变量进行初始化,这当然也就是线程安全的了,写法也很简单:

private static Singleton instance = new Singleton();
private Singleton(){name = "eager initialization thread-safety  1";
}public static Singleton getInstance(){return instance;
}复制代码

或者

private static Singleton instance  = null;
private Singleton(){name = "eager initialization thread-safety  2";
}static {instance = new Singleton();
}
public Singleton getInstance(){return instance;
}复制代码

代码都很简单,一个是直接进行初始化,另一个是使用静态块进行初始化,目的都是一个:在该类进行加载的时候就会初始化该对象,而不管是否需要该对象。这么写的好处是编写简单,而且是线程安全的,但是这时候初始化instance显然没有达到lazy loading的效果。

static inner class thread-safety (静态内部类,线程安全)

由于在java中,静态内部类是在使用中初始化的,所以可以利用这个天生的延迟加载特性,去实现一个简单,延迟加载,线程安全的单例模式:

private static class SingletonHolder{private static final Singleton instance = new Singleton();
}
private Singleton(){name = "static inner class thread-safety";
}public static Singleton getInstance(){return SingletonHolder.instance;
}复制代码

定义一个 SingletonHolder 的静态内部类,在该类中定义一个外部类 Singleton 的静态对象,并且直接初始化,在外部类 Singleton 的 getInstance() 方法中直接返回该对象。由于静态内部类的使用是延迟加载机制,所以只有当线程调用到 getInstance() 方法时才会去加载 SingletonHolder 类,加载这个类的时候又会去初始化 instance 变量,所以这个就实现了延迟加载机制,同时也只会初始化这一次,所以也是线程安全的,写法也很简单。

PS

上面提到的所有实现方式都有两个共同的缺点:


  • 都需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
  • 可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

enum (枚举写法)

JDK1.5 之后加入 enum 特性,可以使用 enum 来实现单例模式:

enum SingleEnum{INSTANCE("enum singleton thread-safety");private String name;SingleEnum(String name){this.name = name;}public String getName(){return name;}
}复制代码

使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,Effective Java推荐尽可能地使用枚举来实现单例。但是很不幸的是 android 中并不推荐使用 enum ,主要是因为在 java 中枚举都是继承自 java.lang.Enum 类,首次调用时,这个类会调用初始化方法来准备每个枚举变量。每个枚举项都会被声明成一个静态变量,并被赋值。在实际使用时会有点问题,这是 google 的官方文档介绍:

Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android

这篇博客也专门计算了 enum 的大小:胡凯-The price of ENUMs,所以枚举写法的缺点也就很明显了。

登记式

登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

//类似Spring里面的方法,将类名注册,下次从里面直接获取。  
public class Singleton {  private static Map map = new HashMap();  static{  Singleton single = new Singleton();  map.put(single.getClass().getName(), single);  }  //保护的默认构造子  protected Singleton(){}  //静态工厂方法,返还此类惟一的实例  public static Singleton getInstance(String name) {  if(name == null) {  name = Singleton.class.getName();  System.out.println("name == null"+"--->name="+name);  }  if(map.get(name) == null) {  try {  map.put(name, (Singleton) Class.forName(name).newInstance());  } catch (InstantiationException e) {  e.printStackTrace();  } catch (IllegalAccessException e) {  e.printStackTrace();  } catch (ClassNotFoundException e) {  e.printStackTrace();  }  }  return map.get(name);  }  //一个示意性的商业方法  public String about() {      return "Hello, I am RegSingleton.";      }      public static void main(String[] args) {  Singleton single3 = Singleton.getInstance(null);  System.out.println(single3.about());  }  
}  复制代码

这种方式我极少见到,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

总结

综上所述,平时在 android 中使用 double-checked 或者 SingletonHolder 都是可以的,毕竟 android 早就不使用 JDK5 之前的版本了。由于 android 中的多进程机制,在不同进程中无法创建同一个 instance 变量,就像 Application 类会初始化两次一样,这点需要注意。

  但是不管采取何种方案,请时刻牢记单例的三大要点:

  • 线程安全;
  • 延迟加载;
  • 序列化与反序列化安全。
单例模式同时也有缺点:
  • 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现;
  • 单例对象如果持有 Context,那么很容易引发内存泄漏,此时需要注意传递给单例对象的 Context 最好为 Application Context。

创建型模式 Rules of thumb

有些时候创建型模式是可以重叠使用的,有一些抽象工厂模式和原型模式都可以使用的场景,这个时候使用任一设计模式都是合理的;在其他情况下,他们各自作为彼此的补充:抽象工厂模式可能会使用一些原型类来克隆并且返回产品对象。

  抽象工厂模式,建造者模式和原型模式都能使用单例模式来实现他们自己;抽象工厂模式经常也是通过工厂方法模式实现的,但是他们都能够使用原型模式来实现;

  通常情况下,设计模式刚开始会使用工厂方法模式(结构清晰,更容易定制化,子类的数量爆炸),如果设计者发现需要更多的灵活性时,就会慢慢地发展为抽象工厂模式,原型模式或者建造者模式(结构更加复杂,使用灵活);

  原型模式并不一定需要继承,但是它确实需要一个初始化的操作,工厂方法模式一定需要继承,但是不一定需要初始化操作;

  使用装饰者模式或者组合模式的情况通常也可以使用原型模式来获得益处;

  单例模式中,只要将构造方法的访问权限设置为 private 型,就可以实现单例。但是原型模式的 clone 方法直接无视构造方法的权限来生成新的对象,所以,单例模式与原型模式是冲突的,在使用时要特别注意。

源码下载

github.com/zhaozepeng/…

引用


www.tekbroaden.com/singleton-j…

hedengcheng.com/?p=725

www.cnblogs.com/hxsyl/archi…

www.blogjava.net/kenzhh/arch…

blog.csdn.net/jason0539/a…

sourcemaking.com/design_patt…

stackoverflow.com/questions/7…

stackoverflow.com/questions/1…

en.wikipedia.org/wiki/Single…

en.wikipedia.org/wiki/Double…

zh.wikipedia.org/wiki/Volati…

jeremymanson.blogspot.com/2008/11/wha…

www.jianshu.com/p/d8bf5d08a…

preshing.com/20130702/th…

blog.csdn.net/imzoer/arti…


相关文章:

实例解说.Net构架下的加密编程

yesky(张悦■yesky) http://www.yesky.com/SoftChannel/72342380484755456/20031225/1756613.shtml 很久以前就一直想写一个加密程序,但是一直感到太麻烦而没有动手,不过在前段时间学习.NET的过程中发现.NET的框架中封装了与加密有关的类。而且要在.NE…

人工智能新手入门学习路线!附学习资源合集

有段时间没跟大家分享编程资源福利了!今天为大家整理了人工智能新手入门学习路线,同时附700分钟的学习资源合集,相信这套福利可以帮你顺利入行AI!文末领取全部资料。一、AI基础好课学习资料整理(约317分钟)…

数据还原到指定时间点的处理示例

--创建测试数据库CREATE DATABASE DbGO--对数据库进行备份BACKUP DATABASE Db TO DISKc:\db.bak WITH FORMATGO--创建测试表CREATE TABLE Db.dbo.TB_test(ID int)--延时1秒钟,再进行后面的操作(这是由于SQL Server的时间精度最大为百分之三秒,不延时的话,可能会导致还原到时间点…

kvm cpu的亲和性绑定配置

1.CPU的绑定配置进程的处理器亲和性(process affinity):指将进程绑定到特定的一个或多个cpu上去执行,而不允许将进程调度到其他cpu上。物理cpu:表示真实的cpu个数逻辑cpu:表示所有拥有一个完整真实cpu功能的…

李开复对话Yoshua Bengio:AI技术的下一个突破

7月23日,在SGInnovate主办的“深度科技(Deep Tech)造福人类”活动上,创新工场董事长兼CEO李开复与Element AI联合创始人Yoshua Bengio进行了一场对话,讨论了人工智能的未来发展。Yoshua Bengio是深度学习三大发明人之一…

单链表功能大全

单链表很全的例子&#xff0c;增加&#xff0c;删除&#xff0c;排序&#xff0c;都有了 #include <stdio.h>#include <stdlib.h>typedef struct node{int nDate;struct node *pstnext;}Node;//链表输出void output(Node *head){Node *p head->pstnext;while(N…

Access外键 级联更新、删除

工具栏-->"关系"。 打开主表和从表。 将主表的字段拖动到从表的对应字段。 &#xff08;两个字段要求类型相同。 如果主表是自动编号&#xff0c;那么从表用长整型.&#xff09; Access添加外键约束(1)"实施完整性约束"(2)"左联接"(3)"实…

20161124网络爬虫技术学习

参考书籍&#xff1a;《自己动手写网络爬虫》 网络爬虫的基本操作是抓取网页。 “打开”网页的过程其实就是浏览器作为一个浏览的“客户端”&#xff0c;向服务器端发送了一次请穷求&#xff0c;把服务器端的文件“抓”到本地&#xff0c;再进行解释&#xff0c;展现。更进一步…

关于何种情况下使用DataGrid、DataList或Repeater的一些讨论

作者&#xff1a;Scott Mitchell &#xff3b;概述&#xff3d; WEB开发自从有了基于脚本的WEB编程技术&#xff08;如ASP&#xff09;以来&#xff0c;经历了一个漫长的过程。通过使用微软的ASP.Net技术&#xff0c;传统的ASP中大量的、单调乏味的、重复性的编程工作成为了历史…

UDP客户端不用绑定吗IP和端口?

2019独角兽企业重金招聘Python工程师标准>>> 书上都是这么说的&#xff0c;UDP客户端不用绑定IP和端口&#xff0c;操作系统会给它自动分配端口。。。。 但是虽然没有显示绑定&#xff0c;但是操作系统却似乎做了些隐蔽的事情。 首先&#xff0c;在客户端&#xff0…

算法实现太难了?机器学习也需要开源软件

作者 | Soren Sonnenburg等译者 | 刘畅出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;导读&#xff1a;开源工具已经趋于成熟&#xff0c;这使其能构建大规模的自然场景下的系统。与此同时&#xff0c;机器学习领域为各种应用开发了大量强大的学习算法。但是&#x…

数据库备份定期删除程序的开发。

第一.最近客户的服务器数据库备份每天都在进行&#xff0c;随着数据量的不断的增加&#xff0c;备份也在不断的进行变大。 第二.于是一些问题就出现了客户的服务器的磁盘的空间有限&#xff0c;定期数据库备份文件太大而导致无法进行保存问题。 第三.从而导致了服务器宕机以及服…

Repeater控件的分页问题

作者&#xff1a;zhoubinmail.sdu.edu.cn以前做ASP的时间不算短&#xff0c;可是做ASP.NET我是个新手。前几天做项目&#xff0c;遇到一个问题&#xff0c;要求比较复杂的数据格式显示&#xff0c;用DataGrid非常难实现&#xff0c;后来改用了Repeater控件来实现。不过这样就带…

华为昇腾AI全栈知识深入解读,师资培训沙龙深圳场圆满落幕!

头图 | 视觉中国极度缺乏AI人才&#xff0c;已成为中国人工智能产业发展道路上的瓶颈。在国家政策的支持下&#xff0c;众高校初建AI专业者甚多&#xff0c;但高校教师应掌握哪些知识储备来传道授业&#xff0c;大家都是“摸着石头过河”。将高校AI人才培养教学大纲与厂商成熟落…

[转载] 信息系统项目管理师考试论文写作要点

来源&#xff1a;信管网 网址&#xff1a;http://www.cnitpm.com/pm/4323.html &#xff08;1&#xff09;在论文写作之前&#xff0c;先不要急着动笔&#xff0c;认真把题看清楚&#xff0c;选择自己熟悉的论文&#xff08;考试时&#xff0c;一般是二选一&#xff09;&#x…

POM.xml 标签详解

pom作为项目对象模型。通过xml表示maven项目&#xff0c;使用pom.xml来实现。主要描述了项目&#xff1a;包括配置文件&#xff1b;开发者需要遵循的规则&#xff0c;缺陷管理系统&#xff0c;组织和licenses&#xff0c;项目的url&#xff0c;项目的依赖性&#xff0c;以及其他…

DataRow的序列化问题

来源&#xff1a;CSDN 作者&#xff1a;kroll 在.net里&#xff0c;DataRow类型的对象是不支持序列化的&#xff0c;那么如果在一个需要序列化的对象中含有DataRow类型的字段该怎么办呢&#xff1f;呵呵&#xff0c;幸好Datatable是支持序列化的。因此&#xff0c;我们可以自定…

mass Framework event模块 v4

event.js // // 事件模块&#xff08;包括伪事件对象&#xff0c;事件绑定与事件代理&#xff09; // $.define("event",document.dispatchEvent ? "node" : "node,event_fix",function(){// $.log("已加载target模块")var rhoverH…

美国 AI 博士:什么都不会怎么学 Python?

我见过市面上很多的 Python 讲解教程和书籍&#xff0c;他们大都这样讲 Python 的&#xff1a;先从 Python 的发展历史开始&#xff0c;介绍 Python 的基本语法规则&#xff0c;Python 的 list, dict, tuple 等数据结构&#xff0c;然后再介绍字符串处理和正则表达式&#xff0…

puppet 初识

【puppet初识】学习目录 领导说saltstack太慢 机器量一旦上来&#xff0c;saltstack就力不从心&#xff0c;所以要将saltstack转向puppet。【安装背景】系统centos 6.5软件地址:http://downloads.puppetlabs.com/puppet/master: 192.168.100.10agent: 192.168.100.13因为puppe…

ColorMatrix 彩色矩阵

选择自 hbzxf 的 Blog 首先对装配脑袋给出上两片文章的友好回复&#xff0c;还有网友Fisherman一起探讨ColorMatrix话题表示感谢&#xff01;ColorMatrix (彩色矩阵) 类位于System.Drawing.Imaging命名空间 先看看下面的代码 ColorMatrix cm newColorMatrix(newfloat[][]{ n…

​一个参数一张Excel表,玩转Pandas的read_excel()表格读取

作者 | 黄伟呢来源 | 数据分析与统计学之美我觉得很有必要讲述这个文章&#xff0c;进行数据处理的第一步就是Python数据读取。但是你可能没想到&#xff0c;在进行数据读取的同时&#xff0c;我们其实可以配合相关参数做很多事儿&#xff0c;这对于后续的数据处理都是极其有帮…

Android 设计模式:(三)装饰者模式 —— 装饰对象

*利用组合&#xff08;composition&#xff09;和委托&#xff08;delegation&#xff09;可以在运行时实现继承行为的效果&#xff0c;动态地给对象加上新的行为。*利用继承扩展子类的行为&#xff0c;是在编译时静态决定的&#xff1b;利用组合的做法&#xff0c;可以在运行时…

润乾报表永久授权说明

永久授权是什么&#xff1f; 永久授权是运行润乾报表的带有机器特征码的没有时间限制的正式授权文件 2 机器特征码是什么&#xff1f; 机器特征码是为了制作永久授权&#xff0c;由润乾公司提供程序从用户的服务器上获取硬件和操作系统信息&#xff0c;生成的一个数字串&#x…

维度爆炸?Python实现数据压缩如此简单

作者 | 萝卜来源 | 早起Python&#xff08;ID: zaoqi-python&#xff09;用Python基于主成分分析常见的三个应用场景中&#xff0c;其中有一个是「数据描述」&#xff0c;以描述产品情况为例&#xff0c;比如著名的波士顿矩阵&#xff0c;子公司业务发展状况&#xff0c;区域投…

深入剖析C#的多态

作者: 天雨一、什么是多态 面向对象程序设计中的另外一个重要概念是多态性。在运行时&#xff0c;可以通过指向基类的指针&#xff0c;来调用实现派生类中的方法。可以把一组对象放到一个数组中&#xff0c;然后调用它们的方法&#xff0c;在这种场合下&#xff0c;多态性作用…

/usr/local/lib/libz.a: could not read symbols: Bad value(64 位 Linux)

/usr/bin/ld: /usr/local/lib/libz.a(crc32.o): relocation R_X86_64_32 against a local symbol can not be used when making a shared object; recompile with -fPIC /usr/local/lib/libz.a: could not read symbols: Bad value 一般是64 位 电脑才会出现。 解决方法如下&am…

关于BIO | NIO | AIO的讨论

关于BIO | NIO | AIO的讨论一直存在&#xff0c;有时候也很容易让人混淆&#xff0c;就我的理解&#xff0c;给出一个解释&#xff1a; BIO | NIO | AIO&#xff0c;本身的描述都是在Java语言的基础上的。而描述IO&#xff0c;我们需要从两个层面&#xff1a; 编程语言实现原理…

NYOJ-49 开心的小明

开心的小明 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;4描述小明今天很开心&#xff0c;家里购置的新房就要领钥匙了&#xff0c;新房里有一间他自己专用的很宽敞的房间。更让他高兴的是&#xff0c;妈妈昨天对他说&#xff1a;“你的房间…

两个硬盘和文件相关的小技巧 (C#)

取系统的硬盘分区的盘符&#xff0c;用API函数&#xff1a;GetDriveType [DllImport("kernel32.dll", EntryPoint"GetDriveType")] public static extern int GetDriveType (string nDrive); 调用&#xff1a;string [] dirs Environment.GetLogicalDri…