本节书摘来异步社区《Java学习指南》一书中的第1章,第1.4节,作者:【美】Patrick Niemeyer , Daniel Leuck,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.4 设计安全

Java被设计为一种安全语言,对于这一事实你肯定早已耳熟能详了。但是在此“安全”指的是什么呢?对什么而言安全,或者对谁安全呢?对于Java,得到颇多关注的安全性是那些使新型动态可移植软件成为可能的有关特性。Java提供了多层保护以避免恶意代码,并防止诸如病毒和特洛伊木马等更具危险性的东西。在下一节中,我们将查看Java虚拟机体系结构如何在代码运行前评估其安全性,还将介绍Java类加载器(Java解释器的字节码加载机制)如何在不可信类周围加筑围墙。这些特性为高级安全性策略提供了基础,从而可以在每个应用的基础上允许或禁止各种操作。

不过,在本节中,我们将了解Java编程语言的一些通用特性。较之于特定的安全特性,Java通过解决通用设计和编程问题所提供的安全性可能更为重要,但在安全性讨论中这一点往往被忽视了。Java力图做到尽可能安全,即不仅要“抵制”我们自己所犯的简单错误,而且还要避免由原有软件所遗传的错误。Java的目标是保持语言的简单性,并提供展示其有用性的工具,同时令用户可以在需要时基于该语言构建更为复杂的功能。

1.4.1 语法简单性

Java有着简单性的原则。因为Java出身清白,它可以避免那些在其他语言中已经证实为糟糕或有争议的那些特性。例如,Java不允许程序员自定义操作符重载(overloading),而在某些语言中,允许程序员重新定义+和-这样的基本操符号的含义。Java没有源代码预处理器,因此没有宏、#define语句或条件源编译。这些在其他语言中存在的构造主要是为了支持平台依赖性,因此从这个意义上讲,它们在Java中是不需要的。条件编译通常还用于调试,但是Java的高级运行时优化以及断言这样的功能,较为优雅地解决了该问题。(我们将在第4章中讨论有关内容)。

Java为组织类文件提供了一个定义良好的包结构。此包系统允许编译器处理传统make实用工具的某些功能(make是用于将源代码构建为可执行代码的一个工具)。编译器还可以直接处理已编译Java类,因为所有类型信息都得到了保留;在此无需“头文件”,这一点与C或C++ 有所不同。所有这些都意味着Java代码需要读取的上下文环境信息更少。实际上,你有时可能会发现查看Java源代码比参考类文档更为快捷。

对于在其他语言中遭遇麻烦的一些特性,Java则将其取而代之。例如,Java只支持单一的类继承层次体系(每个类只能有一个“父”类),但是允许对接口多重继承。接口类似于C++ 中的一个抽象类,可以指定一个对象的多个操作,但是不会定义其实现,这是一个功能强大的机制,它允许开发者为对象定义一个“契约”,任何具体的对象实现都可以使用并引用该契约。Java中的接口消除了类的多重继承需求,同时不会导致与多重继承相关的问题。在第4章中你将会看到,Java是一种简单而又优雅的编程语言,而这仍然是它最大的吸引力。

1.4.2 类型安全和方法绑定

语言的一大属性是其采用何种类型检查。一般地,在将一种语言划归为“静态”或“动态”时,我们所指的是:有关变量类型的信息究竟是在编译时更多地得到明确,还是直至应用运行时方能更多地加以确定。

在诸如C或C++ 这样的严格静态类型语言中,数据类型在编译源代码时即已固化。这有利于编译器得到足够的信息,从而在代码执行前就能捕获多种错误,例如,编译器不会允许你在一个整数变量中保存一个浮点值。这样,代码将不再需要运行时类型检查,因此可以编译为小而快速的可执行代码。但是静态类型语言不够灵活。它们不能支持诸如集合的高级构造,而这些构造对于带有动态类型检查的语言则相当自然,另外对于静态类型语言而言,应用在运行时也不可能安全地导入新的数据类型。

与此相反,诸如Smalltalk或Lisp等动态语言则有一个运行时系统,可以管理对象的类型,并在应用执行时完成必要的类型检查。这些语言允许更为复杂的操作,另外在许多方面,其功能也更为强大。不过,它们往往速度较慢,不太安全,同时也较难调试。

语言之间的差别可以比作不同汽车之间的差别1。静态类型语言(如C++)可以比作跑车,相当安全,速度也很快,但是只有在柏油大道上才能很好地奔驰。动态性很好的语言(如Smalltalk)则更像是越野车:它们可以提供更大的自由度,但是稍难操控。也许在丛林里驾驶着它驰骋相当有趣(有时也更快),但是有时则未免会陷入壕沟或者遭到熊的袭击。

语言的另一个属性是采用何种方式将方法调用绑定至其定义。在诸如C或C++这样的语言中,方法的定义通常在编译时绑定,除非程序员特别指出。Smalltalk则有所不同,它被称为是一种“延迟绑定”(late-binding)语言,因为它在运行时才会动态地确定方法的定义。出于性能方面的原因,早期绑定(early-binding)相当重要;如此可以运行应用,而不会有运行时搜索方法所带来的开销。但是延迟绑定更为灵活。另外在面向对象语言中,这也是必要的,在此子类可以覆盖其超类中的方法,而且只有运行时系统才能确定应当运行哪个方法。

Java博采了C++ 和Smalltalk的优点,它是一种静态类型、延迟绑定的语言。Java中的每个对象都有一个编译时即已确定的定义良好的类型。这说明,Java编译器可以像是在C++中一样,完成同样的静态类型检查和使用分析。因此,你无法给对象赋予错误的变量类型,也不能在一个对象上调用不存在的方法。更有甚者,Java编译器还可以防止使用未初始化的变量以及创建不会执行的语句(请见第4章)。

不过,Java同时也完全可以做到在运行时确定类型。Java运行时系统会跟踪所有对象,并使得在执行时确定其类型和关系成为可能。这说明,可以在运行时检查一个对象以确定它究竟是什么。与C或C++不同的是,将一种对象类型强制转换为另一种类型时,要由运行时系统加以检查,而且有可能使用新型的动态加载对象(具有一定类型安全性的)。另外,由于Java是一种延迟绑定语言,一个子类总是有可能覆盖其超类中的方法,即使这是一个运行时加载的子类。

1.4.3 递增开发

Java从其源代码中将所有数据类型和方法签名信息带入到其编译后的字节码形式中。这就意味着,Java类可以递增地进行开发。你自己的Java类也可以安全地与来自于其他来源(编译器从未见过此来源)的类一同使用。换句话说,可以编写新的代码来引用二进制类文件,而不会丢失从源代码所得到的类型安全性。

困扰C++ 的一个常见问题是“脆弱基类”问题(fragile base class)。在C++ 中,由于一个基类有多个派生类,因此其实现可能被有效地“冻结”了;修改基类可能需要重新编译所有的派生类。对于类库的开发人员来说,这个问题尤其困难。Java通过在类中动态地定位字段,从而避免了这一问题。只要类维护了其原始结构的一个合法形式,那么就可以对其加以改进,而不会对由该类派生或使用了该类的其他类造成破坏。

1.4.4 动态内存管理

Java和C(C++)这样的低级语言之间的一些最为重要的差别涉及到Java如何管理内存。Java取消了可以引用内存的任意部分的临时的指针,并且为语言增加了垃圾回收和高级数组。这些特性消除了有关安全性、可移植性和优化的许多问题,否则这些问题将很难解决。

垃圾回收本身就可以使无数的程序员免于进行显式的内存分配和释放,而这在C或C++ 中也最容易导致错误。除了在内存中维护对象外,Java运行时系统还记录了对这些对象的所有引用。只要某个对象不再使用,Java即会自动地将其从内存中删除。你只需在不再使用对象时将其忽略,并确信解释器在适当的时候会予以清除。

Java使用了一个复杂的垃圾回收器,它在后台间歇性地运行,这意味着大多数垃圾回收工作均发生在空闲时间里,即介于I/O暂停、鼠标点击或按键之间。高级的运行时系统(如HotSpot)则可完成更高级的垃圾回收工作,甚至可以区分对象的使用模式(如对短期对象和长期对象加以区别),并且可以优化其收集过程。Java运行时现在可以自动调整自身,以便针对不同的应用程序,根据其行为来优化内存的分配。通过这种运行时探查,自动化的内存管理比最勤奋的程序员所管理的资源也要快很多,而某些老派的程序员仍然对此难以置信。

你可能听说过Java没有指针。严格地说,这种说法是正确的,但是它也会带来误导。Java所提供的是引用(reference),这是一种“安全型”指针,而且在Java中,引用是相当普遍的。引用是对象的一个强类型句柄。除了基本数字类型之外,Java中的所有对象都可以通过引用来访问。如果必要的话,可以使用引用来构建所有一般的数据结构,如链表、树等等,对于这些数据结构,以往C程序员惯用的做法是采用指针来构建。唯一的区别在于利用引用必须以一种类型安全的方式来操作。

在引用和指针间还有一个重要的区别,即无法通过引用更改其值(执行指针的算术运算)。引用只能指向特定的对象或某个数组中的元素。引用是一个原子性事物;除非将引用赋给一个对象,否则无法操作引用的值。引用采用传值方式传递,而且引用一个对象时,间接层不能多于一层。对引用的保护是Java安全性中最基本的一个方面。这说明,Java代码必须“按规章办事”,即不得“越权”行事。

不同于C或C++ 指针,Java引用只能指向类的类型。在此不存在指向方法的指针。人们有时会对此有所抱怨,但是你会发现,若任务需要方法指针,那么大多数时候,采用接口和适配器类会更为漂亮地将其完成。另外还需提到一点,Java有一个复杂的“反射(reflection)”API,这确实允许你引用和调用单个的方法。不过,这并不是常规做法。我们将在第7章讨论反射。

最后,Java中的数组是真正的头等(first-class)对象。它们可以像其他对象一样动态地分配和赋值。数组知道其自己的大小和类型,而且尽管你无法直接定义或派生数组类的子类,但是基于其基类型的关系,它们确实有一个定义良好的继承关系。语言中若拥有真正的数组,则可以消除C或C++等语言中对指针算术运算的需求。

1.4.5 错误处理

Java的出发点在于网络化设备和嵌入式系统。对于这些应用,拥有健壮而且智能的错误管理机制是至关重要的。Java有一个强大的异常处理机制,这一点有些类似于C++ 的最新实现。异常提供了一个更为自然和优雅的方式来处理错误。异常可以将错误处理代码从一般的代码中分离出来,从而得到更为简洁、更具可读性的应用。

出现一个异常时,将导致程序执行流程转移到一个提前指定的“捕获”代码块。异常附带有一个对象,其中包含有导致出现异常的情形的相关信息。Java编译器要求方法所声明的异常要么是其能够生成的,要么是可以自行捕获和处理的。这将错误信息的重要性,提高到与参数(argument)和返回类型相同的层次。作为一个Java程序员,应当清楚地知道哪些异常情况需要处理,而且编译器还有助于你编写正确的软件,从而不会让这些异常“放任自流”而未加处理。

1.4.6 线程

如今的应用都需要高度的并行性。即使一个非常简单的应用也可能有一个复杂的用户界面,而这就需要并发的活动。随着机器速度越来越快,用户对于为完成无关任务而占用时间的现象也越来越敏感。线程为客户和服务器应用提供了高效的多处理和任务分配机制。Java使得线程很易于使用,因为其对线程的支持是内置于语言中的。

并发性固然很好,但是采用线程编程所做的不仅仅是同时完成多项任务。在大多数情况下,线程需要得到同步(协调),如果没有显式的语言支持则会相当棘手。Java基于监视器和条件模型可以支持同步,这是一种用于访问资源的加锁和钥匙系统。关键字synchronized指定方法和代码块要在对象内得到安全、串行化的访问。也存在一些简单的基本方法,从而可以在对同一对象加以处理的线程之间显式地等待和标记。

Java还有一个高级的并发包,它提供了强大的工具来解决多线程编程中的常见模式,例如,线程池、任务的协调以及复杂的锁定。通过这个并发包和相关的工具,Java提供了一些比任何其他语言都更为高级的线程相关工具。

尽管一些开发者可能永远不必编写多线程代码,但学习使用线程编程,是掌握Java编程的一个重要部分,这是所有程序员都应该掌握的内容。参见第9章关于这个主题的更多讨论。

1.4.7 可伸缩性

在最低的层次上,Java程序由类组成。类被设计为小型的模块化组件。在类之上,Java提供了包,这是一个结构层,它将类分组为功能单元。包为类的组织提供了一个命名约定,另外还对Java应用中变量和方法的可见性提供了另一级组织控制。

在一个包中,类可以是公开可见的,也可能有所保护以避免外部访问。包构成了另一种类型的作用域,它与应用级更为接近。这有助于构建能够在系统中协同工作的可复用组件。包还有助于设计一个可伸缩的应用,从而在扩展应用时,代码不至于过于相互依赖。

《Java学习指南》—— 1.4 设计安全相关推荐

  1. Java学习指南从入门到入土

    Java学习指南从入门到入土 本身其实只是刚刚入门,只是经历了两年时间的风吹雨打,经历了各种bug的折磨和学习各种框架的辛酸,才有得现有的 刚刚入门.有句老话说的好叫做 从入门到放弃,人生不易要及时放 ...

  2. 视频教程-Java学习指南(Swing高级篇)-Java

    Java学习指南(Swing高级篇) 邵发,清华大学毕业,从业软件开发十余年,自2015年起致力于C/C++/Java等基础教育领域,希望能通过提高每一个个体的素质来推动中国IT业的整体发展.代表作: ...

  3. 《Java学习指南》—— 1.1进入Java世界

    本节书摘来异步社区<Java学习指南>一书中的第1章,第1.1节,作者:[美]Patrick Niemeyer , Daniel Leuck,更多章节内容可以访问云栖社区"异步社 ...

  4. 真是恍然大悟啊!免费Java高级工程师学习资源,详细的Java学习指南

    前言 小编看过很多讲Git的文章但感觉还是不够详细,所以出现了这篇文章. 今天来说一说Git命令全方位学习 文章目录 Git是什么? Git的相关理论基础 日常开发中,Git的基本常用命令 Git进阶 ...

  5. [Java学习之路篇] 设计原则与设计模式

    编程可谓博大精深,按照不同的思路逻辑.框架规范编写出来的程序运行的效率都大相径庭.本篇并不只针对Java程序,应适用于所有编写的程序,是编程世界中的一套方法论,俗称编程套路.它们会出现在目前很多大公司 ...

  6. 成功跳槽字节跳动,详细的Java学习指南

    最近几天,有的人在偷偷地买蚂蚁的基金,有的人却偷偷的在蚂蚁面试. 最近确实是面试的好时候.大家都奔着大厂去的,最近也分享了好多大厂的面经了,什么阿里.字节.京东.美团.百度.腾讯.滴滴.网易-已经数不 ...

  7. 如何化身BAT面试收割机?详细的Java学习指南

    前言 正值金九银十面试旺季,现在的面试官一天少说得看几百份简历,你投出去的简历如果没有特别吸引人的点和新颖突出,毫无疑问你的简历不是在垃圾桶就是在回收站里边待着了,那么什么样的简历才能吸引到面试官的眼 ...

  8. 超硬核Java工程师学习指南,真正的从入门到精通,众多粉丝亲测已拿offer!

    最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我之前写的文章都是站在Java后端的全局上进行思 ...

  9. Java并发指南1:并发基础与Java多线程

    什么是并发 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的"同一时间点",而是多个 ...

最新文章

  1. 这应该是你见过的最全前端下载总结
  2. 数据库技巧——MySQL十大优化技巧
  3. 3D打印攻破无人车激光雷达,这个奇怪的盒子它看不见
  4. 【OpenGL】十二、OpenGL 绘制线段 ( 绘制单条线段 | 绘制多条线段 | 依次连接的点组成的线 | 绘制圈 | 绘制彩色的线 )
  5. goj基础环境的配置
  6. Java软件开发:自定义MyBatis持久层框架
  7. java中gradlew 命令_gradle命令学习
  8. 列表合并变种题,map()函数扩展
  9. nginx负载均衡实验笔记
  10. mysql5.7审计功能开启_开启mysql的审计功能
  11. C语言希冀平台作业-南京晓庄
  12. redis中以层级关系、目录形式存储数据
  13. 大数据-什么是云计算技术,云技术用什么语言开发
  14. 宁夏开票系统服务器地址,宁夏增值税发票综合服务平台网址
  15. strcpy和strncpy用法和区别
  16. org.wltea.analyzer.lucene.IKAnalyzer(IK分词器)
  17. Janus源码分析(1)——源码结构
  18. pillow进行图像处理
  19. 痛彻心扉:学了半年 Python,还是找不到工作!
  20. VC新潮流,Tiger DAO VC以DAO形式入侵

热门文章

  1. demo_ajax_json.js,ajax小demo-----ajax中json的使用
  2. reducebykeyandwindow java_Spark Streaming笔记整理(三):DS的transformation与output操作
  3. android jni 调用java对象_Android NDK开发之Jni调用Java对象
  4. java项目如何更改路径_Java修改eclipse中web项目的server部署路径问题
  5. java字符串笔试题_五道Java常见笔试题及答案汇总
  6. android动态更新配置文件,Android如何动态修改Manifest文件
  7. HashMap 源码阅读
  8. Netbeans 中创建数据连接池和数据源步骤(及解决无法ping通问题)
  9. 2.2线性表的顺序表
  10. HTML音乐播放没声音,网页没有声音但系统显示有声音怎么回事?如何解决?