当前位置:首页 > 数码 > 从C源码视角剖析Java同步锁机制的变迁历程 (c源码分析)

从C源码视角剖析Java同步锁机制的变迁历程 (c源码分析)

admin5个月前 (05-04)数码24

什么是重量级锁?

重量级锁是一种同步机制,通常与在多线程环境中经常使用synchronized关键字成功同步相关。

由于其成功的开支和复杂性较高,因此被称为重量级,适宜须要更严厉的同步和并发控制的场景。

privatesynchronizedvoidoneLock(){//doSomething();}

两个线程t1和t2正在同时访问该oneLock()方法。假设t1先失掉锁并口头其中的同步代码块,并且t2也尝试访问oneLock()方法,则它将被阻止,由于锁由t1持有。

在这种状况下,锁处于称为重量级锁的形态。

从上方的例子可以看出,t2由于无法失掉锁,因此被挂起,期待t1监禁锁后再被唤醒。

线程的挂起和唤醒触及CPU内的高低文切换,这会发生很大的开支。

由于这个环节的老本相对较高,具备这种行为的锁被称为重量级锁。

什么是轻量级锁

c源码分析

轻量级锁是一种同步机制,旨在减轻与传统重量级锁(例如synchronized关键字提供的锁)相关的性能开支。

继续前面的示例,让咱们如今思考t1和t2交替口头oneLock()方法。

在这种状况下,t1和t2不须要阻塞,由于它们之间没有争用。换句话说,不须要重量级的锁。

当线程交替口头临界区而不出现争用时,这种场景下经常使用的锁被称为轻量级锁。

轻量级锁相关于重量级锁的优势:

1、每次加锁只要要一次性CAS操作。2.无需调配ObjectMonitor对象。3、线程不须要被挂起或唤醒。

什么是倾向锁?

在只要一个线程(假定t1)分歧口头oneLock()方法的状况下,经常使用轻量级锁t1在每次失掉锁时口头CAS操作。这或许会造成一些性能开支。

于是,倾向锁的概念就出现了。

当锁倾向特定线程时,该线程可以再次失掉锁,而无需启动CAS操作。雷同,繁难的比拟就足以取得锁。这个环节十分高效。

倾向锁相比轻量级锁的优势:

怎么加锁?

让咱们从源代码的角度深化钻研一下Java中这些锁是如何成功的。

锁的实质在于共享变量,所以疑问的关键是如何访问这些共享变量。了解这一点就了解了这三种锁的演化环节的一半。

接上去我将从源码剖析的角度重点引见一下这些信息。

既然咱们处置的是锁,人造就触及到锁的失掉和监禁操作,而在倾向锁的状况下,还有锁撤销操作。

对象头是Java对象在内存中规划的一局部,用于存储对象的元数据信息和锁定形态。

在深化源码之前,咱们先推测一下线程t1失掉倾向锁的环节:

CASE(_monitorenter):{//1.失掉对象头,示意为oop(个别对象指针)。ooplockee=STACK_OBJECT(-1);CHECK_NULL(lockee);BasicObjectLock*limit=istate->monitor_base();BasicObjectLock*most_recent=(BasicObjectLock*)istate->stack_base();BasicObjectLock*entry=NULL;while(most_recent!=limit){//2.遍历线程栈找到对应的可用BasicObjectLock。if(most_recent->obj()==NULL)entry=most_recent;elseif(most_recent->obj()==lockee)break;most_recent++;}if(entry!=NULL){//3.BasicObjectLock的_obj字段指向oop。entry->set_obj(lockee);intsuccess=false;uintptr_tepoch_mask_in_place=(uintptr_t)markOopDesc::epoch_mask_in_place;//从对象头中检索标志markOopmark=lockee->mark();intptr_thash=(intptr_t)markOopDesc::no_hash;//审核能否支持倾向锁定。if(mark->has_bias_pattern()){uintptr_tthread_ident;uintptr_tanticipated_bias_locking_value;thread_ident=(uintptr_t)istate->thread();//4.失掉异或运算的结果。anticipated_bias_locking_value=(((uintptr_t)lockee->klass()->prototype_header()|thread_ident)^(uintptr_t)mark)&~((uintptr_t)markOopDesc::age_mask_in_place);if(anticipated_bias_locking_value==0){//5.假设相等,则以为是可重入失掉锁。if(PrintBiasedLockingStatistics){(*BiasedLocking::biased_lock_entry_count_addr())++;}success=true;}elseif((anticipated_bias_locking_value&markOopDesc::biased_lock_mask_in_place)!=0){//6.假设不支持倾向锁markOopheader=lockee->klass()->prototype_header();if(hash!=markOopDesc::no_hash){header=header->copy_set_hash(hash);}//口头CAS操作,将MarkWord修正为解锁形态。if(Atomic::cmpxchg_ptr(header,lockee->mark_addr(),mark)==mark){if(PrintBiasedLockingStatistics)(*BiasedLocking::revoked_lock_entry_count_addr())++;}}elseif((anticipated_bias_locking_value&epoch_mask_in_place)!=0){//7.假设epoch已过时,则经常使用以后线程的ID结构倾向锁markOopnew_header=(markOop)((intptr_t)lockee->klass()->prototype_header()|thread_ident);if(hash!=markOopDesc::no_hash){new_header=new_header->copy_set_hash(hash);}if(Atomic::cmpxchg_ptr((void*)new_header,lockee->mark_addr(),mark)==mark){if(PrintBiasedLockingStatistics)(*BiasedLocking::rebiased_lock_entry_count_addr())++;}else{CALL_VM(InterpreterRuntime::monitorenter(THREAD,entry),handle_exception);}success=true;}else{//8.结构一个匿名倾向锁。markOopheader=(markOop)((uintptr_t)mark&((uintptr_t)markOopDesc::biased_lock_mask_in_place|(uintptr_t)markOopDesc::age_mask_in_place|epoch_mask_in_place));if(hash!=markOopDesc::no_hash){header=header->copy_set_hash(hash);}//结构一个指向以后线程的倾向锁。markOopnew_header=(markOop)((uintptr_t)header|thread_ident);DEBUG_ONLY(entry->lock()->set_displaced_header((markOop)(uintptr_t)0xdeaddead);)//口头CAS操作将锁修正为与以后线程关联的倾向锁。if(Atomic::cmpxchg_ptr((void*)new_header,lockee->mark_addr(),header)==header){if(PrintBiasedLockingStatistics)(*BiasedLocking::anonymously_biased_lock_entry_count_addr())++;}else{CALL_VM(InterpreterRuntime::monitorenter(THREAD,entry),handle_exception);}success=true;}}if(!success){//假设尝试经常使用倾向锁不成功,系统会尝试将锁更新为轻量级锁。markOopdisplaced=lockee->mark()->set_unlocked();entry->lock()->set_displaced_header(displaced);boolcall_vm=UseHeavyMonitors;if(call_vm||Atomic::cmpxchg_ptr(entry,lockee->mark_addr(),displaced)!=displaced){if(!call_vm&&THREAD->is_lock_owned((address)displaced->clear_lock_bits())){entry->lock()->set_displaced_header(NULL);}else{CALL_VM(InterpreterRuntime::monitorenter(THREAD,entry),handle_exception);}}}UPDATE_PC_AND_TOS_AND_CONTINUE(1,-1);}else{istate->set_msg(more_monitors);UPDATE_PC_AND_RETURN(0);//Re-execute}}

代码比拟多,上方我将对代码注释中注释1-8标注的内容启动具体解释。

#1.oop代表对象头,蕴含MarkWord和KlassWord。

#2.BasicObjectLock的结构如下:

#basicLock.hppclassBasicObjectLockVALUE_OBJ_CLASS_SPEC{friendclassVMStructs;private:BasicLock_lock;oop_obj;...};classBasicLockVALUE_OBJ_CLASS_SPEC{friendclassVMStructs;private:volatilemarkOop_displaced_header;...};

BasicObjectLock是驰名的LockRecord的成功,它包括两个元素:

#3、将LockRecord中的_obj字段赋值给lockee,代表对象头。

#4.从对象头lockee中,检索KlassWord,它是指向Klass类型的指针。在Klass类外部,有一个名为_prototype_header的字段,它也代表MarkWord。它存储倾向锁定标志之类的信息。

在此步骤中,提取此信息并将其与以后线程ID衔接起来。

而后与对象头中的MarkWord口头XOR运算。指标是识别不同的位。

后续步骤触及确定MarkWord的哪些特定局部不相等,从而造成不同的处置逻辑。

#5.假设上方的异或运算结果相等,则标明MarkWord中蕴含以后线程ID,并且epoch和倾向锁标志分歧。

这标明该锁曾经被以后线程持有,标明是可重入的。由于线程曾经领有锁,因此不须要采取进一步的操作。

#6.观察MarkWord中的倾向锁标志与Klass中的倾向锁标志不分歧,并且思考到MarkWord曾经被辨以为具备倾向锁,因此可以推断Klass不再支持倾向锁。

鉴于不支持倾向锁定,标志字被修正以反映解锁形态。这为进一步更新到轻量级锁定或重量级锁定做好了预备。

#7.在识别出MarkWord中的纪元与Klass中的标志之间的差异后,可以推断出现了批量从新偏置。这种状况下,间接修正MarkWord,使其倾向以后线程。

#8、假设以上条件都不满足,则标明是匿名倾向锁(不倾向任何线程的倾向锁)。在这种状况下,会尝试间接修正MarkWord以倾向以后线程

总结

在倾向锁形态下,锁记载和对象头之间建设了相关。这种相关由指向对象头的锁定记载的_obj字段示意。

咱们回忆一下线程t1和t2失掉倾向锁的环节:

锁撤销

假设尝试失掉倾向锁不成功,锁将复原为未锁定形态,而后更新为轻量级锁。此环节称为倾向锁撤销。

#InterpreterRuntime.cppIRT_ENTRY_NO_ASYNC(void,InterpreterRuntime::monitorenter(JavaThread*thread,BasicObjectLock*elem))...if(UseBiasedLocking){//当经常使用倾向锁时,进程进入极速门路口头。ObjectSynchronizer::fast_enter(h_obj,elem->lock(),true,CHECK);}else{//更新为轻量级锁。ObjectSynchronizer::slow_enter(h_obj,elem->lock(),CHECK);}...#synchronizer.cppvoidObjectSynchronizer::fast_enter(Handleobj,BasicLock*lock,boolattempt_rebias,TRAPS){if(UseBiasedLocking){if(!SafepointSynchronize::is_at_safepoint()){//未在安保点口头,或许是撤销或从新倾向。BiasedLocking::Conditioncond=BiasedLocking::revoke_and_rebias(obj,attempt_rebias,THREAD);//假设从新倾向成功,则分开该环节。if(cond==BiasedLocking::BIAS_REVOKED_AND_REBIASED){return;}}else{assert(!attempt_rebias,"cannotrebiastowardVMthread");//在安保点口头撤销。BiasedLocking::revoke_at_safepoint(obj);}assert(!obj->mark()->has_bias_pattern(),"biasesshouldberevokedbynow");}slow_enter(obj,lock,THREAD);}

可见,撤销分为安保点撤销和非安保点撤销。

非安保点撤销,也称为revoke_and_rebias,出当初未期待安保点而撤销倾向锁时。在这个环节中,倾向锁被间接撤销,并且对象的标志字被更新以反映新的形态,而不须要安保点来保障分歧的形态转换。

当出现非安保点撤销时,倾向锁的形态从倾向变为反常或可重倾向。

假设它更改为可重倾向形态,则象征着假设另一个线程寻求该锁,该锁可以再次倾向。

这准许更快、更有效的锁定转换,由于假设另一个线程在撤销后不久失掉该锁,则该锁或许会跳过两边形态并间接进入倾向形态。

从实质上讲,非安保点撤销缩小了期待安保点的须要,并成功了更灵敏、照应更灵敏的方法来撤销倾向锁,从而提高了性能并缩小了某些场景下的锁争用。

批量从新倾向和批量撤销

经过以上剖析,咱们了解到以下几点:

因此,倾向锁引入了批量重倾向和批量撤销的概念。

当对象的锁被撤销的次数到达必定阈值时,例如20次,就会触发批量重偏逻辑。

这触及到修正Klass中的标志以及以后经常使用的该类型锁的MarkWord中的标志。

当线程尝试失掉倾向锁时,它会将以后对象的纪元值与Klass中的标志值启动比拟。

假设不相等,则以为锁已过时。在这种状况下,准许线程间接CAS修正MarkWord以倾向以后线程,防止撤销逻辑。这对应于倾向锁进入最后探讨中的剖析标签(7)。

雷同,当撤销次数到达40次时,就以为该对象不再适宜倾向锁。

因此,Klass中的倾向锁标志出现更改,以批示不再支持倾向锁。

当线程尝试失掉倾向锁时,它会审核Klass中的倾向锁标志。假设不再准许偏向,则标明批次撤销较早出现。

在这种状况下,准许线程间接CAS将MarkWord修正为解锁形态,防止了撤销逻辑。这对应于倾向锁进入最后探讨中的剖析标签(6)。

批量从新倾向和批量撤销是旨在提高倾向锁定性能的优化。

锁监禁

#bytecodeInterpreter.cppCASE(_monitorexit):{ooplockee=STACK_OBJECT(-1);CHECK_NULL(lockee);BasicObjectLock*limit=istate->monitor_base();BasicObjectLock*most_recent=(BasicObjectLock*)istate->stack_base();//遍历线程栈while(most_recent!=limit){//查找对应的锁记载if((most_recent)->obj()==lockee){BasicLock*lock=most_recent->lock();markOopheader=lock->displaced_header();//将锁定记载中的_obj字段设置为nullmost_recent->set_obj(NULL);//这是轻量级锁的监禁。UPDATE_PC_AND_TOS_AND_CONTINUE(1,-1);}most_recent++;}...}

您或许曾经留意到,MarkWord没有扭转;它依然倾向于前一个线程。但是,锁还没有被监禁。理想上,当线程分开临界区时,它不会监禁倾向锁。

要素是:

当再次须要锁时,繁难的按位比拟就可以极速判别能否是可重入失掉。这象征着不须要每次都口头CAS操作就可以高效地失掉锁。这种效率是倾向锁在只要一个线程访问锁的场景下的外围优势。

总结

未完待续。。。。。


Java语言好学么?

Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。 Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程[1]。 Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点[2]。 Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等[3]。 外文名Java现公司Oracle推出时间1995年5月快速导航编程开发 语言特点 工作原理 语言基础 语言标准 语言规范 技术应用 学习指南 语言评价发展历程20世纪90年代,硬件领域出现了单片式计算机系统,这种价格低廉的系统一出现就立即引起了自动控制领域人员的注意,因为使用它可以大幅度提升消费类电子产品(如电视机顶盒、面包烤箱、移动电话等)的智能化程度。 Sun公司为了抢占市场先机,在1991年成立了一个称为Green的项目小组,帕特里克、詹姆斯·高斯林、麦克·舍林丹和其他几个工程师一起组成的工作小组在加利福尼亚州门洛帕克市沙丘路的一个小工作室里面研究开发新技术,专攻计算机在家电产品上的嵌入式应用。 JAVA由于C++所具有的优势,该项目组的研究人员首先考虑采用C++来编写程序。 但对于硬件资源极其匮乏的单片式系统来说,C++程序过于复杂和庞大。 另外由于消费电子产品所采用的嵌入式处理器芯片的种类繁杂,如何让编写的程序跨平台运行也是个难题。 为了解决困难,他们首先着眼于语言的开发,假设了一种结构简单、符合嵌入式应用需要的硬件平台体系结构并为其制定了相应的规范,其中就定义了这种硬件平台的二进制机器码指令系统(即后来成为“字节码”的指令系统),以待语言开发成功后,能有半导体芯片生产商开发和生产这种硬件平台。 对于新语言的设计,Sun公司研发人员并没有开发一种全新的语言,而是根据嵌入式软件的要求,对C++进行了改造,去除了留在C++的一些不太实用及影响安全的成分,并结合嵌入式系统的实时性要求,开发了一种称为Oak的面向对象语言。 由于在开发Oak语言时,尚且不存在运行字节码的硬件平台,所以为了在开发时可以对这种语言进行实验研究,他们就在已有的硬件和软件平台基础上,按照自己所指定的规范,用软件建设了一个运行平台,整个系统除了比C++更加简单之外,没有什么大的区别。 1992年的夏天,当Oak语言开发成功后,研究者们向硬件生产商进行展示了Green操作系统、Oak的程序设计语言、类库和其硬件,以说服他们使用Oak语言生产硬件芯片,但是,硬件生产商并未对此产生极大的热情。 因为他们认为,在所有人对Oak语言还一无所知的情况下,就生产硬件产品的风险实在太大了,所以Oak语言也就因为缺乏硬件的支持而无法进入市场,从而被搁置了下来。 1994年6、7月间,在经历了一场历时三天的讨论之后,团队决定再一次改变了努力的目标,这次他们决定将该技术应用于万维网。 他们认为随着Mosaic浏览器的到来,因特网正在向同样的高度互动的远景演变,而这一远景正是他们在有线电视网中看到的。 作为原型,帕特里克·诺顿写了一个小型万维网浏览器WebRunner。 [4]1995年,互联网的蓬勃发展给了Oak机会。 业界为了使死板、单调的静态网页能够“灵活”起来,急需一种软件技术来开发一种程序,这种程序可以通过网络传播并且能够跨平台运行。 于是,世界各大IT企业为此纷纷投入了大量的人力、物力和财力。 这个时候,Sun公司想起了那个被搁置起来很久的Oak,并且重新审视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编写的,所以非常小,特别适用于网络上的传输系统,而Oak也是一种精简的语言,程序非常小,适合在网络上传输。 Sun公司首先推出了可以嵌入网页并且可以随同网页在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术),并将Oak更名为Java(在申请注册商标时,发现Oak已经被人使用了,再想了一系列名字之后,最终,使用了提议者在喝一杯Java咖啡时无意提到的Java词语)。 5月23日,Sun公司在Sun world会议上正式发布Java和HotJava浏览器。 IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Java平台。 [5] [6]1996年1月,Sun公司发布了Java的第一个开发工具包(JDK 1.0),这是Java发展历程中的重要里程碑,标志着Java成为一种独立的开发工具。 9月,约8.3万个网页应用了Java技术来制作。 10月,Sun公司发布了Java平台的第一个即时(JIT)编译器。 1997年2月,JDK 1.1面世,在随后的3周时间里,达到了22万次的下载量。 4月2日,Java One会议召开,参会者逾一万人,创当时全球同类会议规模之纪录。 9月,Java Developer Connection社区成员超过10万。 1998年12月8日,第二代Java平台的企业版J2EE发布。 1999年6月,Sun公司发布了第二代Java平台(简称为Java2)的3个版本:J2ME(Java2 Micro Edition,Java2平台的微型版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企业版),应用于基于Java的应用服务器。 Java 2平台的发布,是Java发展过程中最重要的一个里程碑,标志着Java的应用开始普及。 1999年4月27日,HotSpot虚拟机发布。 HotSpot虚拟机发布时是作为JDK 1.2的附加程序提供的,后来它成为了JDK 1.3及之后所有版本的Sun JDK的默认虚拟机[7] 。 2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。 2001年9月24日,J2EE1.3发布。 2002年2月26日,J2SE1.4发布。 自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。 在这些新特性当中,还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。 2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。 为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0),代号为“Tiger”,Tiger包含了从1996年发布1.0版本以来的最重大的更新,其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、格式化I/O及可变参数。 Java创始人之一:詹姆斯·高斯林2005年6月,在Java One大会上,Sun公司发布了Java SE 6。 此时,Java的各种版本已经更名,已取消其中的数字2,如J2EE更名为JavaEE,J2SE更名为JavaSE,J2ME更名为JavaME。 [8]2006年11月13日,Java技术的发明者Sun公司宣布,将Java技术作为免费软件对外发布。 Sun公司正式发布的有关Java平台标准版的第一批源代码,以及Java迷你版的可执行源代码。 从2007年3月起,全世界所有的开发人员均可对Java源代码进行修改[9] 。 2009年,甲骨文公司宣布收购Sun[10] 。 2010年,Java编程语言的共同创始人之一詹姆斯·高斯林从Oracle公司辞职。 2011年,甲骨文公司举行了全球性的活动,以庆祝Java7的推出,随后Java7正式发布。 2014年,甲骨文公司发布了Java8正式版[11] 。 编程开发编程环境Java概念图JDK(Java Development Kit)称为Java开发包或Java开发工具,是一个编写Java的Applet小程序和应用程序的程序开发环境。 JDK是整个Java的核心,包括了Java运行环境(Java Runtime Environment),一些Java工具和Java的核心类库(Java API)。 不论什么Java应用服务器实质都是内置了某个版本的JDK。 主流的JDK是Sun公司发布的JDK,除了Sun之外,还有很多公司和组织都开发了自己的JDK,例如,IBM公司开发的JDK,BEA公司的Jrocket,还有GNU组织开发的JDK[13] 。 另外,可以把Java API类库中的Java SE API子集和Java虚拟机这两部分统称为JRE(JAVA Runtime Environment),JRE是支持Java程序运行的标准环境[14] 。 JRE是个运行环境,JDK是个开发环境。 因此写Java程序的时候需要JDK,而运行Java程序的时候就需要JRE。 而JDK里面已经包含了JRE,因此只要安装了JDK,就可以编辑Java程序,也可以正常运行Java程序。 但由于JDK包含了许多与运行无关的内容,占用的空间较大,因此运行普通的Java程序无须安装JDK,而只需要安装JRE即可[15] 。 编程工具Eclipse:一个开放源代码的、基于Java的可扩展开发平台[16] 。 NetBeans:开放源码的Java集成开发环境,适用于各种客户机和Web应用。 IntelliJ IDEA:在代码自动提示、代码分析等方面的具有很好的功能。 [17]MyEclipse:由Genuitec公司开发的一款商业化软件,是应用比较广泛的Java应用程序集成开发环境[18] 。 EditPlus:如果正确配置Java的编译器“Javac”以及解释器“Java”后,可直接使用EditPlus编译执行Java程序[19] 。 语言特点1.简单性Java看起来设计得很像C++,但是为了使语言小和容易熟悉,设计者们把C++语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的。 例如,Java不支持go to语句,代之以提供break和continue语句以及异常处理。 Java还剔除了C++的操作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。 因为Java没有结构,数组和串都是对象,所以不需要指针。 Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集,使用户不必为存储管理问题烦恼,能更多的时间和精力花在研发上。 2.面向对象Java是一个面向对象的语言。 对程序员来说,这意味着要注意应中的数据和操纵数据的方法(method),而不是严格地用过程来思考。 在一个面向对象的系统中,类(class)是数据和操作数据的方法的集合。 数据和方法一起描述对象(object)的状态和行为。 每一对象是其状态和行为的封装。 类是按一定体系和层次安排的,使得子类可以从超类继承行为。 在这个类层次体系中有一个根类,它是具有一般行为的类。 Java程序是用类来组织的。 Java还包括一个类的扩展集合,分别组成各种程序包(Package),用户可以在自己的程序中使用。 例如,Java提供产生图形用户接口部件的类(包),这里awt是抽象窗口工具集(abstract windowing toolkit)的缩写,处理输入输出的类(包)和支持网络功能的类(包)。 3.分布性Java设计成支持在网络上应用,它是分布式语言。 Java既支持各种层次的网络连接,又以Socket类支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。 网络变成软件应用的分布运载工具。 Java程序只要编写一次,就可到处运行。 4.编译和解释性Java编译程序生成字节码(byte-code),而不是通常的机器码。 Java字节码提供对体系结构中性的目标文件格式,代码设计成可有效地传送程序到多个平台。 Java程序可以在任何实现了Java解释程序和运行系统(run-time system)的系统上运行。 在一个解释性的环境中,程序开发的标准“链接”阶段大大消失了。 如果说Java还有一个链接阶段,它只是把新类装进环境的过程,它是增量式的、轻量级的过程。 因此,Java支持快速原型和容易试验,它将导致快速程序开发。 这是一个与传统的、耗时的“编译、链接和测试”形成鲜明对比的精巧的开发过程。 5.稳健性Java原来是用作编写消费类家用电子产品软件的语言,所以它是被设计成写高可靠和稳健软件的。 Java消除了某些编程错误,使得用它写可靠软件相当容易。 JavaJava是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。 Java要求显式的方法声明,它不支持C风格的隐式声明。 这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。 可靠性方面最重要的增强之一是Java的存储模型。 Java不支持指针,它消除重写存储和讹误数据的可能性。 类似地,Java自动的“无用单元收集”预防存储漏泄和其它有关动态存储分配和解除分配的有害错误。 Java解释程序也执行许多运行时的检查,诸如验证所有数组和串访问是否在界限之内。 异常处理是Java中使得程序更稳健的另一个特征。 异常是某种类似于错误的异常条件出现的信号。 使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。 6.安全性Java的存储分配模型是它防御恶意代码的主要方法之一。 Java没有指针,所以程序员不能得到隐蔽起来的内幕和伪造指针去指向存储器。 更重要的是,Java编译程序不处理存储安排决策,所以程序员不能通过查看声明去猜测类的实际存储安排。 编译的Java代码中的存储引用在运行时由Java解释程序决定实际存储地址。 Java运行系统使用字节码验证过程来保证装载到网络上的代码不违背任何Java语言限制。 这个安全机制部分包括类如何从网上装载。 例如,装载的类是放在分开的名字空间而不是局部类,预防恶意的小应用程序用它自己的版本来代替标准Java类。 7.可移植性Java使得语言声明不依赖于实现的方面。 例如,Java显式说明每个基本数据类型的大小和它的运算行为(这些数据类型由Java语法描述)。 Java环境本身对新的硬件平台和操作系统是可移植的。 Java编译程序也用Java编写,而Java运行系统用ANSIC语言编写。 8.高性能Java是一种先编译后解释的语言,所以它不如全编译性语言快。 但是有些情况下性能是很要紧的,为了支持这些情况,Java设计者制作了“及时”编译程序,它能在运行时把Java字节码翻译成特定CPU(中央处理器)的机器代码,也就是实现全编译了。 Java字节码格式设计时考虑到这些“及时”编译程序的需要,所以生成机器代码的过程相当简单,它能产生相当好的代码。 9.多线程性Java是多线程语言,它提供支持多线程的执行(也称为轻便过程),能处理不同任务,使具有线索的程序设计很容易。 Java的lang包提供一个Thread类,它支持开始线索、运行线索、停止线索和检查线索状态的方法。 Java的线索支持也包括一组同步原语。 这些原语是基于监督程序和条件变量风范,由开发的广泛使用的同步化方案。 用关键词synchronized,程序员可以说明某些方法在一个类中不能并发地运行。 这些方法在监督程序控制之下,确保变量维持在一个一致的状态。 10.动态性Java语言设计成适应于变化的环境,它是一个动态的语言。 例如,Java中的类是根据需要载入的,甚至有些是通过网络获取的。 [20]工作原理由四方面组成:工作原理(1)Java编程语言(2)Java类文件格式(3)Java虚拟机(4)Java应用程序接口[21]当编辑并运行一个Java程序时,需要同时涉及到这四种方面。 使用文字编辑软件(例如记事本、写字板、UltraEdit等)或集成开发环境(Eclipse、MyEclipse等)在Java源文件中定义不同的类[22] ,通过调用类(这些类实现了Java API)中的方法来访问资源系统,把源文件编译生成一种二进制中间码,存储在class文件中,然后再通过运行与操作系统平台环境相对应的Java虚拟机来运行class文件,执行编译产生的字节码,调用class文件中实现的方法来满足程序的Java API调用[6] 。 语言基础基本数据结构在Java中有8种数据类型来存储数值、字符和布尔值。 整数类型整数型用来存储整数数值,即没有小数部分的数值。 可以是正数,也可以是负数。 整数数据在Java程序中有3种表示形式,分别为十进制、八进制和十六进制。 整型数据根据它所占内容大小的不同可分为4种类型。 数据类型内存byte8位short16位int32位展开全部浮点类型数据类型内存float32位double64位字符类型字符类型用于存储单个字符,占用16位(两个字节)的内存空间。 在定义字符型变量时,要以单引号表示。 使用char关键字可定义字符变量。 数据类型内存char16位布尔类型布尔类型又称逻辑类型,通过关键字boolean来定义布尔类型变量,只有只有true和false两个取值,分别代表布尔逻辑中的“真”和“假”。 布尔类型通常被用在流程控制中作为判断条件。 关键字关键字含义abstract抽象类或方法assert用来查找内部程序错误break跳出一个switch或循环展开全部关键字参考资料来源[23]运算符运算符是一些特殊的符号,主要用于数学函数、一些类型的赋值语句和逻辑比较方面。 赋值运算符赋值运算符以符号“=”表示,它是一个二元运算符(对两个操作数作处理),其功能是将右方操作数所含的值赋给左方的操作数。 例如:int a = 100;算术运算符运算符说明+加-减*乘展开全部自增和自减运算符自增和自减是单目运算符,可以放在操作元之前,也可以放在操作元之后。 操作元必须是一个整型或浮点型变量。 自增、自减运算符的作用是使变量的值增1或减1。 放在操作元前面的自增、自减运算符,会先将变量的值加1或减1,然后再使该变量参与表达式的运算。 放在操作元后面的自增、自减运算符,会先使变量参与表达式的运算,然后再将该变量的值加1或减1。 例如:假设a=5b=++a;//先将a的值加1,然后赋值给b,此时a的值为6,b的值为6b=a++;//先将a的值赋值给b,再将a的值变为6,此时a的值为6,b的值为5比较运算符比较运算符属于二元运算符,用于程序中的变量之间,变量和自变量之间以及其他类型的信息之间的比较。 比较运算符的运算结果是boolean型。 当运算符对应的关系成立时,运算的结果为true,否则为false。 比较运算符共有6个,通常作为判断的依据用于条件语句中。 运算符说明>比较左方是否大于右方<比较左方是否小于右方==比较左方是否等于右方展开全部逻辑运算符运算符作用&&逻辑与||逻辑或!逻辑非语言基础参考资料来源[24]程序举例/*一个简单的Java应用程序,显示内容:Hello World!文件名为Hello.java*/class Hello{//程序开始于main()public static void main(String args[]){(Hello World!);} }main方法,正如注释所说,这是程序开始执行的第一行。 对于一个Java应用程序来说,main方法是必需的,Java解释器在没有生成任何对象的情况下,以main作为入口来执行程序。 每个类中可以定义多个方法,但main方法只能有一个。 关键字public表示访问权限,指明所有的类都可以使用这一方法。 本例中,main必须被定义为public类型,因为当程序开始执行时它需要被它的类之外的代码调用。 关键字static指明该方法是一个类方法。 关键字void指明main()方法不返回任何值。 main方法圆括号中定义的String args[]是传送给main方法的参数,参数名为args,它是类String的一个对象。 方法的参数用“类名参数名”来指定,多个参数间用逗号分隔。 该行的最后一个字符是“{”,它表示了main程序体的开始,一个方法中包含的所有代码都将包括在这对花括号中间。 另外,main仅是解释器开始工作的地方,一个复杂的程序可能包含几十个类,但这些类仅需要一个main方法。 [25]语言标准1997年11月17日,国际标准化组织(ISO)同意采纳Sun公司的专利技术——Java作为ISO标准。 JAVA作为一个技术标准供公众无偿使用。 根据ISO的批准,Sun公司将可提交JAVA的标准化平台规范。 [26]发布时间名称2000年11月15日ISO/TS -27年5月15日ISO/IEC -2年7月15日ISO/IEC TR -6:2014展开全部语言标准参考资料[27] [28] [29] [30] [31]语言规范JCP(Java Community Process)是一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新Java技术规范、参考实现(RI)、技术兼容包(TCK)。 JCP维护的规范包括Java ME、Java SE、Java EE、XML、OSS、JAIN等。 组织成员可以提交JSR(Java Specification Requests),通过特定程序以后,进入到下一版本的规范里面。 JSR是早期提议和最终发布的Java平台规范的具体描述。 通常,一个新的JSR的提出是为了增加或者规范Java平台的功能。 某个具体的JSR由专家组共同来制定,工作由组长协调。 例如,CLDC1.0(Connected Limited Device Configuration,JSR30)由Sun公司的Antero Taivalsaari担任组长,同时专家组的成员还包括Siemens、Motorola、Nokia、Symbian等。 Java ME平台规范是在JSR68中定义的,规范组长是Sun公司的Jon Courtney。 JSR完成后,相关的规范及JavaAPI会在JCP的官方网站发布。 设备制造商可以在自己的产品中实现某个JSR,如MIDP2.0(JSRll8)。 但是这些都必须要通过TCK(Technology Compatibility Kit)测试以确保技术兼容性。 [32]按照技术类别可以分成以下几类:1、J2EE平台规范[33]2、J2SE平台规范[34]3、J2ME平台规范[35]4、运营支持系统规范(OSS)[36]5、综合网络的Java 应用(JAIN)[37]6、XML 操作规范[38]技术应用1、Android应用许多的 Android应用都是Java程序员开发者开发。 虽然 Android运用了不同的JVM以及不同的封装方式,但是代码还是用Java语言所编写。 相当一部分的手机中都支持JAVA游戏,这就使很多非编程人员都认识了JAVA。 2、在金融业应用的服务器程序Java在金融服务业的应用非常广泛,很多第三方交易系统、银行、金融机构都选择用Java开发,因为相对而言,Java较安全[39] 。 大型跨国投资银行用Java来编写前台和后台的电子交易系统,结算和确认系统,数据处理项目以及其他项目。 大多数情况下,Java被用在服务器端开发,但多数没有任何前端,它们通常是从一个服务器(上一级)接收数据,处理后发向另一个处理系统(下一级处理)。 3、网站Java 在电子商务领域以及网站开发领域占据了一定的席位。 开发人员可以运用许多不同的框架来创建web项目,SpringMVC,Struts2.0以及frameworks。 即使是简单的 servlet,jsp和以struts为基础的网站在政府项目中也经常被用到。 例如医疗救护、保险、教育、国防以及其他的不同部门网站都是以Java为基础来开发的。 4、嵌入式领域Java在嵌入式领域发展空间很大。 在这个平台上,只需130KB就能够使用Java技术(在智能卡或者传感器上)。 5、大数据技术Hadoop以及其他大数据处理技术很多都是用Java,例如Apache的基于Java的HBase和Accumulo以及 ElasticSearchas。 6、高频交易的空间Java平台提高了这个平台的特性和即使编译,他同时也能够像 C++ 一样传递数据。 正是由于这个原因,Java成为的程序员编写交易平台的语言,因为虽然性能不比C++,但开发人员可以避开安全性,可移植性和可维护性等问题。 7、科学应用Java在科学应用中是很好选择,包括自然语言处理。 最主要的原因是因为Java比C++或者其他语言相对其安全性、便携性、可维护性以及其他高级语言的并发性更好。 Java语言好学难精,配置东西比较复杂

c/c++ 类永远只是声明

的执行速度慢上约20倍。 无论什么都不能阻止Java语言进行编译。 写作本书的时候,刚刚出现了一些准实时编译器,它们能显著加快速度。 当然,我们完全有理由认为会出现适用于更多流行平台的纯固有编译器,但假若没有那些编译器,由于速度的限制,必须有些问题是Java不能解决的。 (2) 和C++一样,Java也提供了两种类型的注释。 (3) 所有东西都必须置入一个类。 不存在全局函数或者全局数据。 如果想获得与全局函数等价的功能,可考虑将static方法和static数据置入一个类里。 注意没有象结构、枚举或者联合这一类的东西,一切只有“类”(Class)!(4) 所有方法都是在类的主体定义的。 所以用C++的眼光看,似乎所有函数都已嵌入,但实情并非如何(嵌入的问题在后面讲述)。 (5) 在Java中,类定义采取几乎和C++一样的形式。 但没有标志结束的分号。 没有class foo这种形式的类声明,只有类定义。 class aType()void aMethod() }(6) Java中没有作用域范围运算符“::”。 Java利用点号做所有的事情,但可以不用考虑它,因为只能在一个类里定义元素。 即使那些方法定义,也必须在一个类的内部,所以根本没有必要指定作用域的范围。 我们注意到的一项差异是对static方法的调用:使用()。 除此以外,package(包)的名字是用点号建立的,并能用import关键字实现C++的“import .*;((7) 与C++类似,Java含有一系列“主类型”(Primitive type),以实现更有效率的访问。 在Java中,这些类型包括boolean,char,byte,short,int,long,float以及double。 所有主类型的大小都是固有的,且与具体的机器无关(考虑到移植的问题)。 这肯定会对性能造成一定的影响,具体取决于不同的机器。 对类型的检查和要求在Java里变得更苛刻。 例如:■条件表达式只能是boolean(布尔)类型,不可使用整数。 ■必须使用象X+Y这样的一个表达式的结果;不能仅仅用“X+Y”来实现“副作用”。 (8) char(字符)类型使用国际通用的16位Unicode字符集,所以能自动表达大多数国家的字符。 (9) 静态引用的字串会自动转换成String对象。 和C及C++不同,没有独立的静态字符数组字串可供使用。 (10) Java增添了三个右移位运算符“>>>”,具有与“逻辑”右移位运算符类似的功用,可在最末尾插入零值。 “>>”则会在移位的同时插入符号位(即“算术”移位)。 (11) 尽管表面上类似,但与C++相比,Java数组采用的是一个颇为不同的结构,并具有独特的行为。 有一个只读的length成员,通过它可知道数组有多大。 而且一旦超过数组边界,运行期检查会自动丢弃一个异常。 所有数组都是在内存“堆”里创建的,我们可将一个数组分配给另一个(只是简单地复制数组句柄)。 数组标识符属于第一级对象,它的所有方法通常都适用于其他所有对象。 (12) 对于所有不属于主类型的对象,都只能通过new命令创建。 和C++不同,Java没有相应的命令可以“在堆栈上”创建不属于主类型的对象。 所有主类型都只能在堆栈上创建,同时不使用new命令。 所有主要的类都有自己的“封装(器)”类,所以能够通过new创建等价的、以内存“堆”为基础的对象(主类型数组是一个例外:它们可象C++那样通过集合初始化进行分配,或者使用new)。 (13) Java中不必进行提前声明。 若想在定义前使用一个类或方法,只需直接使用它即可——编译器会保证使用恰当的定义。 所以和在C++中不同,我们不会碰到任何涉及提前引用的问题。 (14) Java没有预处理机。 若想使用另一个库里的类,只需使用import命令,并指定库名即可。 不存在类似于预处理机的宏。 (15) Java用包代替了命名空间。 由于将所有东西都置入一个类,而且由于采用了一种名为“封装”的机制,它能针对类名进行类似于命名空间分解的操作,所以命名的问题不再进入我们的考虑之列。 数据包也会在单独一个库名下收集库的组件。 我们只需简单地“import”(导入)一个包,剩下的工作会由编译器自动完成。 (16) 被定义成类成员的对象句柄会自动初始化成null。 对基本类数据成员的初始化在Java里得到了可靠的保障。 若不明确地进行初始化,它们就会得到一个默认值(零或等价的值)。 可对它们进行明确的初始化(显式初始化):要么在类内定义它们,要么在构建器中定义。 采用的语法比C++的语法更容易理解,而且对于static和非static成员来说都是固定不变的。 我们不必从外部定义static成员的存储方式,这和C++是不同的。 (17) 在Java里,没有象C和C++那样的指针。 用new创建一个对象的时候,会获得一个引用(本书一直将其称作“句柄”)。 例如:String s = new String(howdy);然而,C++引用在创建时必须进行初始化,而且不可重定义到一个不同的位置。 但Java引用并不一定局限于创建时的位置。 它们可根据情况任意定义,这便消除了对指针的部分需求。 在C和C++里大量采用指针的另一个原因是为了能指向任意一个内存位置(这同时会使它们变得不安全,也是Java不提供这一支持的原因)。 指针通常被看作在基本变量数组中四处移动的一种有效手段。 Java允许我们以更安全的形式达到相同的目标。 解决指针问题的终极方法是“固有方法”(已在附录A讨论)。 将指针传递给方法时,通常不会带来太大的问题,因为此时没有全局函数,只有类。 而且我们可传递对对象的引用。 Java语言最开始声称自己“完全不采用指针!”但随着许多程序员都质问没有指针如何工作?于是后来又声明“采用受到限制的指针”。 大家可自行判断它是否“真”的是一个指针。 但不管在何种情况下,都不存在指针“算术”。 (18) Java提供了与C++类似的“构建器”(Constructor)。 如果不自己定义一个,就会获得一个默认构建器。 而如果定义了一个非默认的构建器,就不会为我们自动定义默认构建器。 这和C++是一样的。 注意没有复制构建器,因为所有自变量都是按引用传递的。 (19) Java中没有“破坏器”(Destructor)。 变量不存在“作用域”的问题。 一个对象的“存在时间”是由对象的存在时间决定的,并非由废品收集器决定。 有个finalize()方法是每一个类的成员,它在某种程度上类似于C++的“破坏器”。 但finalize()是由废品收集器调用的,而且只负责释放“资源”(如打开的文件、套接字、端口、URL等等)。 如需在一个特定的地点做某样事情,必须创建一个特殊的方法,并调用它,不能依赖finalize()。 而在另一方面,C++中的所有对象都会(或者说“应该”)破坏,但并非Java中的所有对象都会被当作“废品”收集掉。 由于Java不支持破坏器的概念,所以在必要的时候,必须谨慎地创建一个清除方法。 而且针对类内的基础类以及成员对象,需要明确调用所有清除方法。 (20) Java具有方法“过载”机制,它的工作原理与C++函数的过载几乎是完全相同的。 (21) Java不支持默认自变量。 (22) Java中没有goto。 它采取的无条件跳转机制是“break 标签”或者“continue 标准”,用于跳出当前的多重嵌套循环。 (23) Java采用了一种单根式的分级结构,因此所有对象都是从根类Object统一继承的。 而在C++中,我们可在任何地方启动一个新的继承树,所以最后往往看到包含了大量树的“一片森林”。 在Java中,我们无论如何都只有一个分级结构。 尽管这表面上看似乎造成了限制,但由于我们知道每个对象肯定至少有一个Object接口,所以往往能获得更强大的能力。 C++目前似乎是唯一没有强制单根结构的唯一一种OO语言。 (24) Java没有模板或者参数化类型的其他形式。 它提供了一系列集合:Vector(向量),Stack(堆栈)以及Hashtable(散列表),用于容纳Object引用。 利用这些集合,我们的一系列要求可得到满足。 但这些集合并非是为实现象C++“标准模板库”(STL)那样的快速调用而设计的。 Java 1.2中的新集合显得更加完整,但仍不具备正宗模板那样的高效率使用手段。 (25) “废品收集”意味着在Java中出现内存漏洞的情况会少得多,但也并非完全不可能(若调用一个用于分配存储空间的固有方法,废品收集器就不能对其进行跟踪监视)。 然而,内存漏洞和资源漏洞多是由于编写不当的finalize()造成的,或是由于在已分配的一个块尾释放一种资源造成的(“破坏器”在此时显得特别方便)。 废品收集器是在C++基础上的一种极大进步,使许多编程问题消弥于无形之中。 但对少数几个废品收集器力有不逮的问题,它却是不大适合的。 但废品收集器的大量优点也使这一处缺点显得微不足道。 (26) Java内建了对多线程的支持。 利用一个特殊的Thread类,我们可通过继承创建一个新线程(放弃了run()方法)。 若将synchronized(同步)关键字作为方法的一个类型限制符使用,相互排斥现象会在对象这一级发生。 在任何给定的时间,只有一个线程能使用一个对象的synchronized方法。 在另一方面,一个synchronized方法进入以后,它首先会“锁定”对象,防止其他任何synchronized方法再使用那个对象。 只有退出了这个方法,才会将对象“解锁”。 在线程之间,我们仍然要负责实现更复杂的同步机制,方法是创建自己的“监视器”类。 递归的synchronized方法可以正常运作。 若线程的优先等级相同,则时间的“分片”不能得到保证。 (27) 我们不是象C++那样控制声明代码块,而是将访问限定符(public,private和protected)置入每个类成员的定义里。 若未规定一个“显式”(明确的)限定符,就会默认为“友好的”(friendly)。 这意味着同一个包里的其他元素也可以访问它(相当于它们都成为C++的“friends”——朋友),但不可由包外的任何元素访问。 类——以及类内的每个方法——都有一个访问限定符,决定它是否能在文件的外部“可见”。 private关键字通常很少在Java中使用,因为与排斥同一个包内其他类的访问相比,“友好的”访问通常更加有用。 然而,在多线程的环境中,对private的恰当运用是非常重要的。 Java的protected关键字意味着“可由继承者访问,亦可由包内其他元素访问”。 注意Java没有与C++的protected关键字等价的元素,后者意味着“只能由继承者访问”(以前可用“private protected”实现这个目的,但这一对关键字的组合已被取消了)。 (28) 嵌套的类。 在C++中,对类进行嵌套有助于隐藏名称,并便于代码的组织(但C++的“命名空间”已使名称的隐藏显得多余)。 Java的“封装”或“打包”概念等价于C++的命名空间,所以不再是一个问题。 Java 1.1引入了“内部类”的概念,它秘密保持指向外部类的一个句柄——创建内部类对象的时候需要用到。 这意味着内部类对象也许能访问外部类对象的成员,毋需任何条件——就好象那些成员直接隶属于内部类对象一样。 这样便为回调问题提供了一个更优秀的方案——C++是用指向成员的指针解决的。 (29) 由于存在前面介绍的那种内部类,所以Java里没有指向成员的指针。 (30) Java不存在“嵌入”(inline)方法。 Java编译器也许会自行决定嵌入一个方法,但我们对此没有更多的控制权力。 在Java中,可为一个方法使用final关键字,从而“建议”进行嵌入操作。 然而,嵌入函数对于C++的编译器来说也只是一种建议。 (31) Java中的继承具有与C++相同的效果,但采用的语法不同。 Java用extends关键字标志从一个基础类的继承,并用super关键字指出准备在基础类中调用的方法,它与我们当前所在的方法具有相同的名字(然而,Java中的super关键字只允许我们访问父类的方法——亦即分级结构的上一级)。 通过在C++中设定基础类的作用域,我们可访问位于分级结构较深处的方法。 亦可用super关键字调用基础类构建器。 正如早先指出的那样,所有类最终都会从Object里自动继承。 和C++不同,不存在明确的构建器初始化列表。 但编译器会强迫我们在构建器主体的开头进行全部的基础类初始化,而且不允许我们在主体的后面部分进行这一工作。 通过组合运用自动初始化以及来自未初始化对象句柄的异常,成员的初始化可得到有效的保证。 1045页程序(32) Java中的继承不会改变基础类成员的保护级别。 我们不能在Java中指定public,private或者protected继承,这一点与C++是相同的。 此外,在衍生类中的优先方法不能减少对基础类方法的访问。 例如,假设一个成员在基础类中属于public,而我们用另一个方法代替了它,那么用于替换的方法也必须属于public(编译器会自动检查)。 (33) Java提供了一个interface关键字,它的作用是创建抽象基础类的一个等价物。 在其中填充抽象方法,且没有数据成员。 这样一来,对于仅仅设计成一个接口的东西,以及对于用extends关键字在现有功能基础上的扩展,两者之间便产生了一个明显的差异。 不值得用abstract关键字产生一种类似的效果,因为我们不能创建属于那个类的一个对象。 一个abstract(抽象)类可包含抽象方法(尽管并不要求在它里面包含什么东西),但它也能包含用于具体实现的代码。 因此,它被限制成一个单一的继承。 通过与接口联合使用,这一方案避免了对类似于C++虚拟基础类那样的一些机制的需要。 为创建可进行“例示”(即创建一个实例)的一个interface(接口)的版本,需使用implements关键字。 它的语法类似于继承的语法,如下所示: 1046页程序(34) Java中没有virtual关键字,因为所有非static方法都肯定会用到动态绑定。 在Java中,程序员不必自行决定是否使用动态绑定。 C++之所以采用了virtual,是由于我们对性能进行调整的时候,可通过将其省略,从而获得执行效率的少量提升(或者换句话说:“如果不用,就没必要为它付出代价”)。 virtual经常会造成一定程度的混淆,而且获得令人不快的结果。 final关键字为性能的调整规定了一些范围——它向编译器指出这种方法不能被取代,所以它的范围可能被静态约束(而且成为嵌入状态,所以使用C++非virtual调用的等价方式)。 这些优化工作是由编译器完成的。 (35) Java不提供多重继承机制(MI),至少不象C++那样做。 与protected类似,MI表面上是一个很不错的主意,但只有真正面对一个特定的设计问题时,才知道自己需要它。 由于Java使用的是“单根”分级结构,所以只有在极少的场合才需要用到MI。 interface关键字会帮助我们自动完成多个接口的合并工作。 (36) 运行期的类型标识功能与C++极为相似。 例如,为获得与句柄X有关的信息,可使用下述代码()();为进行一个“类型安全”的紧缩造型,可使用:derived d = (derived)base;这与旧式风格的C造型是一样的。 编译器会自动调用动态造型机制,不要求使用额外的语法。 尽管它并不象C++的“new casts”那样具有易于定位造型的优点,但Java会检查使用情况,并丢弃那些“异常”,所以它不会象C++那样允许坏造型的存在。 (37) Java采取了不同的异常控制机制,因为此时已经不存在构建器。 可添加一个finally从句,强制执行特定的语句,以便进行必要的清除工作。 Java中的所有异常都是从基础类Throwable里继承而来的,所以可确保我们得到的是一个通用接口。 1047页程序(38) Java的异常规范比C++的出色得多。 丢弃一个错误的异常后,不是象C++那样在运行期间调用一个函数,Java异常规范是在编译期间检查并执行的。 除此以外,被取代的方法必须遵守那一方法的基础类版本的异常规范:它们可丢弃指定的异常或者从那些异常衍生出来的其他异常。 这样一来,我们最终得到的是更为“健壮”的异常控制代码。 (39) Java具有方法过载的能力,但不允许运算符过载。 String类不能用+和+=运算符连接不同的字串,而且String表达式使用自动的类型转换,但那是一种特殊的内建情况。 (40) 通过事先的约定,C++中经常出现的const问题在Java里已得到了控制。 我们只能传递指向对象的句柄,本地副本永远不会为我们自动生成。 若希望使用类似C++按值传递那样的技术,可调用clone(),生成自变量的一个本地副本(尽管clone()的设计依然尚显粗糙——参见第12章)。 根本不存在被自动调用的副本构建器。 为创建一个编译期的常数值,可象下面这样编码:static final int SIZE = 255static final int BSIZE = 8 * SIZE(41) 由于安全方面的原因,“应用程序”的编程与“程序片”的编程之间存在着显著的差异。 一个最明显的问题是程序片不允许我们进行磁盘的写操作,因为这样做会造成从远程站点下载的、不明来历的程序可能胡乱改写我们的磁盘。 随着Java 1.1对数字签名技术的引用,这一情况已有所改观。 根据数字签名,我们可确切知道一个程序片的全部作者,并验证他们是否已获得授权。 Java 1.2会进一步增强程序片的能力。 (42) 由于Java在某些场合可能显得限制太多,所以有时不愿用它执行象直接访问硬件这样的重要任务。 Java解决这个问题的方案是“固有方法”,允许我们调用由其他语言写成的函数(目前只支持C和C++)。 这样一来,我们就肯定能够解决与平台有关的问题(采用一种不可移植的形式,但那些代码随后会被隔离起来)。 程序片不能调用固有方法,只有应用程序才可以。 (43) Java提供对注释文档的内建支持,所以源码文件也可以包含它们自己的文档。 通过一个单独的程序,这些文档信息可以提取出来,并重新格式化成HTML。 这无疑是文档管理及应用的极大进步。 (44) Java包含了一些标准库,用于完成特定的任务。 C++则依靠一些非标准的、由其他厂商提供的库。 这些任务包括(或不久就要包括):■连网■数据库连接(通过JDBC)■多线程■分布式对象(通过RMI和CORBA)■压缩■商贸由于这些库简单易用,而且非常标准,所以能极大加快应用程序的开发速度。 (45) Java 1.1包含了Java Beans标准,后者可创建在可视编程环境中使用的组件。 由于遵守同样的标准,所以可视组件能够在所有厂商的开发环境中使用。 由于我们并不依赖一家厂商的方案进行可视组件的设计,所以组件的选择余地会加大,并可提高组件的效能。 除此之外,Java Beans的设计非常简单,便于程序员理解;而那些由不同的厂商开发的专用组件框架则要求进行更深入的学习。 (46) 若访问Java句柄失败,就会丢弃一次异常。 这种丢弃测试并不一定要正好在使用一个句柄之前进行。 根据Java的设计规范,只是说异常必须以某种形式丢弃。 许多C++运行期系统也能丢弃那些由于指针错误造成的异常。 (47) Java通常显得更为健壮,为此采取的手段如下:■对象句柄初始化成null(一个关键字)■句柄肯定会得到检查,并在出错时丢弃异常■所有数组访问都会得到检查,及时发现边界违例情况■自动废品收集,防止出现内存漏洞■明确、“傻瓜式”的异常控制机制■为多线程提供了简单的语言支持■对网络程序片进行字节码校验

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: Java

“从C源码视角剖析Java同步锁机制的变迁历程 (c源码分析)” 的相关文章

深入探讨-Java-编程中的类继承与接口实现 (深入探讨交流)

深入探讨-Java-编程中的类继承与接口实现 (深入探讨交流)

简介 类索引、父类索引和接口索引是在 Class 文件中用于确定类的继承关系的重要数据。通过这些索引,Java 虚拟机 (JVM) 可以准确地建立类之间的继承关系,实现多态性和接口的实现。...

大编程趋势-5-年-Java-2024-开发者的 (编程是大趋势吗)

大编程趋势-5-年-Java-2024-开发者的 (编程是大趋势吗)

Java 作为编程领域的支柱,拥有超过 900 万开发人员和超过 30 年的发展历史。它以其先进的安全功能、优越的性能和跨平台开发能力而闻名。展望 2024 年,Java 正准备进行一场突破性的转...

选择性-过多的关键词会增加SEO工作的复杂性-关键词不应过多 (选择性太多)

选择性-过多的关键词会增加SEO工作的复杂性-关键词不应过多 (选择性太多)

引言 在不断演进的软件开发领域,保持最新技术至关重要。从 Java 11 迁移到 Java 21 是一个明智的决定,它带来了显着的优势和创新,可以提升应用程序的安全性、性能和开发效率。...

Java循环结构与实现方式 (java循环语句案例)

Java循环结构与实现方式 (java循环语句案例)

循环简介 循环是编程中常用的一种控制结构,用于重复执行某一段代码或操作。循环的执行需要满足一定的条件,当条件满足时,循环会一直执行,直到条件不满足时才结束。所以循环其实就是重复地完成某一件事...

用Java实现自动化测试和质量控制-分步指南 (用java实现幸运抽奖)

用Java实现自动化测试和质量控制-分步指南 (用java实现幸运抽奖)

自动化测试概述 自动化测试是指使用软件工具和脚本来执行测试任务,以代替人工操作并提高测试效率。 自动化测试的优势 提高效率 可重复性 提高覆盖率...

Java中不倡导经常使用foreach的六大场景 (java中不等于怎么写)

Java中不倡导经常使用foreach的六大场景 (java中不等于怎么写)

在中,foreach是一个罕用的循环结构,它可以极大地简化遍历数组或汇合(例如List或Set)的代码。它通常被以为是一种愈加繁复和易读的迭代形式。但是,或许有一些状况下不倡导经常使用foreac...

Java-渣滓回收器的运作形式-对不再沉闷对象的跟踪机制

Java-渣滓回收器的运作形式-对不再沉闷对象的跟踪机制

作为一门面向对象的编程言语,具备智能内存治理的个性。这象征着开发人员无需手动调配和监禁内存,而是由Java虚构机的渣滓回收器担任治理。渣滓回收器经过监督程序中不再经常使用的对象来回收内存,以提高内...

掌握Java并发编程-免除竞态条件的困扰 (掌握JavaScript基础)

掌握Java并发编程-免除竞态条件的困扰 (掌握JavaScript基础)

1. 竞态条件的概念和问题 竞态条件指的是多个线程同时访问共享资源,由于执行顺序的不确定性,导致程序结果与预期不符的情况。竞态条件问题通常出现在以下情况下: 多个线程争用同一把锁 多个线程...