Practical Java-68 条

本文分享了Java编程中的关键实践,涵盖参数传递、多线程、异常处理等方面,旨在帮助开发者写出高质量、高效率的代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实践1:参数一by value 方式而不是by reference方式传递

所有Java对象都是通过object reference被访问。常见的一个误解是javaby reference方式传递参数。事实上所有参数都以by value方式传递

实践2:对不变的dataobject reference使用final

为了让dataobject reference成为不变量(常数),请使用final。注意,final仅仅令object reference自身成为不变量,并不限制它所指对象的改变

实践3:缺省情况下所有non-static函数都可被覆写

缺省情况下,所有non-static函数都可以被subclass覆写。但是如果加上关键词final,便可以防止被subclass覆写

实践4:在arraysvectors之间慎重选择

       Arraysvectors是常见的容器类(storage classes)。选用它们之前应该先了解他们的功用和特性

实践5;多态polymorphism优于instanceof

       Instanceof的许多用途都会因为改用多态而消失。使用多态,代码将更清晰、更易于扩展和维护。

实践6:必要时才使用instanceof

有时我们无法回避使用instanceof。我们应该了解在什么情况下必须使用它。

实践7:一旦不要需要object reference,就将它设为null

              不要忽视内存可能带来的问题。尽管有了垃圾回收机制(garbage collection),但是你仍然需要关注你的代码如何运用内存。如果能够领悟垃圾回收机制和内存运用的细节,你就能更好的知道何时将object reference设为null,你将得到高效的代码

2对象与相等性

实践8:区分reference性别和primitive型别

Java是面向对象语言,但其操控的东西并非都是对象(objects)。理解reference

Typeprimitive types之间的差异,以及它们在JVM中的表述(represention)会使你在运用它们是得以明智的选择

实践9:区分==equals()

==用来测试基本型别的相等性,亦可判定两个object references是否指向同一个对象。但若要测试values值或semantic(语义)相等,应用equals()。

实践10:不要依赖equals()的缺省实现

不要不假思索的认定一个class总是会正确实现出equals()。此外,java.lang.Object提供的equals()大多数时候并非进行你想要的比较。   

实践11:实现equals()时必须考虑深思熟虑

如果某个class所生的两个对象[即使不占用相同的内存空间],也被视为逻辑上相等,那么就该为这个class提供一个equals()

实践12:实现equals()时优先考虑使用getclass()

实现equals()时请优先考虑采用getClass(),毕竟,【隶属同一个class下对象才能被视为相等】是正确实现equals()的一个简明方案

实践13:调用super.equals()以唤起base class 的相关行为

任何base class如果实现equals(),其derived class都应该调用super.equals()

实践14:在equals()函数中谨慎使用instanceof

唯有当你考虑允许【一个derived class对象可以相等于其base class对象】时,才在equals()中使用instanceof,使用这项技术前请先弄清楚其影响。

实践15:实现equals()时需要遵循某些规则

撰写equals()并非那么直接浅白。如果想要恰当的实现出equals(),请遵循某些规则

3异常处理(exception Handling)

实践16:认识[异常控制流]exception control flow机制

让自己晓谙异常控制流程细节。了解这些细微之处有助于你回避问题。

       实践17:绝对不比轻忽异常(never ignore an exception)

一旦异常出现且没有被捕获,抛出异常的那个线程(thread)就会中止运行。是的,异常意味错误,永远不要忽略它。

实践18:千万不要遮掩异常(never hide an exception

如果在处理异常期间又从catchfinally区段抛出异常,原先的异常会因为被隐藏起来。一旦发生这样的事情,就会丢失错误信息。就应该撰写专门负责处理这种情形的代码,即将所有异常回传给调用者。

实践19:明察throws子句的缺点

将一个异常加入某函数的throws子句,会影响该函数的所有调用者

实践20:细致而全面的理解throws子句

任何函数的throws子句都应当列出它所传播的所有异常,包括衍生异常型别

实践21:使用finally避免资源泄漏

不要忽视内存以外的资源,垃圾回收机制不会替你释放它们。请使用finally确保内存以外的资源被释放

实践22:不要从try区段中返回

不要从try区段中发出return语句,因为这个函数未必会立即从那里返回。如果存在finally区段,它就会被运行起来并可能改变回传值。

实践23:将try/catch区段置于循环之外

撰写含有异常处理的循环时,请将trycatch区段置于循环外部。在某些实现版本上,这会产生更快的运行码。

实践24:不要将异常(exception)用于流程控制

请将异常用于预期行为之外的情况。不要以异常来控制流程,请采用标准的语言流程构件(flow contructs),这样的流程会更加清晰,更高效

实践25:不要每逢出错就使用异常(exceptions)

只有面对程序行为可能出乎预料的情况下才使用异常。[预期中的行为]应该使用返回码(return codes)来处理

实践26:在构造函数(contrutors)中抛出异常

构造函数并非函数,因而不能回传一个值,但它有可能失败。如果它们失败了,请抛出一个异常。

实践27:抛出异常之前先将对象恢复为有效状态(valid state

抛出异常很容易,困难的是[将异常所引发的伤害减到最小]。抛出异常之前应确保[当异常被处理好,流程再次进入抛出异常的那个函数后,该函数可以成功完成]

4性能

实践28:先把焦点放在设计、数据结构和算法上

java带来最大性能提升的办法就是:在设计和算法中使用与语言无关的技术。因此首先将你的精力集中于这些上面

实践29:不要依赖于编译期(compile-time)优化技术

java编译器生成的代码,通常不会比你自己撰写的更好。别指望编译期能偶多么优化你的源码

实践30:理解运行期(runtime)代码优化技术

Java对性能的大部分努力都围绕着[运行期优化]展开,这种做法有利有弊

实践32:将对象的创建成本(creation cost)降至最小

在许多面向对象系统中,【创建对象】意味着高昂的成本。了解成本所在,以及了解【加速对象创建速度】的技术,都可以导致更快速的程序

实践33:谨防未必用上的对象[unused objects]

非必要别产生对象,否则会减慢你的程序速度

实践34:将同步化【synchronization】降至最低

声明synchronized函数或synchronized区段,会显著降低性能。应该只在对象有所需要时才使用同步机制

实践35:尽可能使用stack变量

Stack变量为JVM提供了更高效的byte code指令序列。所有在循环内重复访问static变量或instance变量时,应当将它们存储于stack变量中,以便获得更快的运行速度

实践36:使用staticfinalprivate函数以促成inlining

以函数本体(methods body)替换函数调用(methods call),会导致更快速的程序,如果要令函数为inline,则必须先声明它们为staticfinalprivate

实践37instance变量的初始化一次就好

       由于所有static变量和instance变量都会自动获得缺省值,所以不必重新将它们设为缺省值

       实践38:使用基本型别(primitive types)使得代码更快更小

       使用基本型别,比使用其外覆类(wrapper),产生的代码小而快

实践39:不要使用EnumerationIteraror来遍历vector

遍历vector时,请使用get()函数而非Enumerationiterator,这样做会导致更少的函数调用,意味程序速度更快。

实践40:使用System.arraycopy()来复制

请使用System.arraycopy()来复制arrays。那是个本机函数,速度更快

实践41:优先使用array,然后才考虑vectorArrayList

如果可能,就使用array,如果你需要vector的功能但不需要它的同步特性,可以使用ArrayList

实践42:尽可能复用(reuse)对象

       复用现有对象,几乎总是比创建新对象更划算

       实践43:使用缓式评估(延迟求值,lazy evaluation)

       如果某个成本昂贵的计算并非必要,就应尽量少做。请使用【缓式评估lazy evaluation】技术避免那些永远不需要的工作。

实践44:以手工方式将代码优化

       由于java编译期在优化方面的作为很少,为了生存最佳byte code,请以手工方式将你的源码优化

       实践45:编译为本机代码(compile to native code)

编译为本机代码,通常可以获得运行速度更快的代码。但你却因此必须在各种不同的本机方案(native solution)中取舍

5多线程

       实践46:面向instance函数,synchronized锁定的是对象(objects)而非函数(methods)或代码(code)

       关键词synchronized锁定的是对象,而非函数或代码。一个函数或程序区段被声明为synchronized,并不意味着同一时刻只能由一个线程运行它。

       实践47:弄清楚synchronized statics 函数与synchrinized instance函数之间的差异

       两个函数被声明为synchronized,并不意味它们是【多线程安全thread safe】,对instance函数或object reference同步化,与对static函数或class literal(字面常数)同步化相比,得到的lock全然不同

       实践48:以【private数据+相应的访问函数】替换【public/protected数据】

如果没有适当的保护你的数据,用户便有机会绕过你的同步机制

实践49:避免无谓的同步控制

一般情况下请不要同步化所有函数。同步化不仅会造成程序缓慢,而且有丧失并发的可能,请采用【单对象多锁】技术以允许更多并发动作

实践50:访问共享变量时请使用synchronizedvolatile

不可切割(原子化atomic)操作并非意味着【多线程安全】,JVM实现品被允许在私有内存中保留变量的工作副本。这可能会产生陈旧数值(stale values).为避免这个问题,请使用同步化机制或将变量声明为volatile

实践51:在单一操作(single operation)中锁定所有的对象

同步化某一函数,并不一定就会使其成为[多线程安全]如果synchronized函数操控着多个对象,而它们并不都是此函数所属classprivate instance data,那么你必须对这些对象自身也进行同步化。

实践52:以固定而全局的顺序取得多个locks(机锁)以避免死锁

当你同步化多个对象,请以固定的顺序获得locks,以避免死锁

实践53:优先使用notifyAll()而非notify()

Notify()只唤醒一个线程。要想唤醒多个线程,请使用notifyAll()

实践54:针对wait()和notifAll()使用旋锁(spin locks)

当你等待条件变量时,请总是使用旋锁以确保结果正确

实践55:使用wait()notifyAll()替换轮询循环(polling loops

将所有polling loops替换为使用wait()、notify(),notifyAll()spin locksSpinlocks直观而高效,pollingloops则慢很多倍

实践56:不要对Locked object(上锁对象)之object reference重新赋值

当一个对象被锁定时,其他线程有可能会因同一个object lock()而受阻(blocked)假如你对上锁对象的object reference重新赋值,则其他线程悬而未决的那些locks将不再有意义

实践57:不要调用stop()或suspend()

实践58:通过线程之间的协作来终止线程

不应该调用stop()。如欲安全的停止线程,必须需要它们相互协作,才能姿态优雅的终止

6类与接口

实践59:运用interface支持多重继承

当你想要支持interface的单一继承或多重继承,或想要实现一个标示型interface时,请使用interfaces

实践60:避免interfaces中的函数发生冲突

没有任何办法能够阻止两个interfaces使用同样的常数和函数。为了避免可能冲突,应当小心命名常数和函数

实践61:如需提供部分实现(partial implementation)请使用abstract classes

使用abstract classes来为一个class提供部分实现,这些事项很可能对derived class是共通的

实践62:区分interfaceabstract classconcrete class

一旦正确理解interfaceabstractconcrete的差异,你就可以在设计和撰写代码时作出正确选择

实践63:审慎的定义和实现immutable classes(不可变类)

如果你希望对象内容永远不被改动,请使用不可变对象。这种对象自动拥有【多线程安全性】

实践64:欲传递或接收mutable objectsobject references时,请实施clone()

为了保证immutable objects,你必须在传入和回传它们时对它们施行clone()

实践65:使用继承或委托来定义immutable classes

使用immutable interfaceCommon interfacebase class,或是immutable delegation classes,来定义immutable classes

实践66:实现clone()时记得调用super clone()当你实现一个clone()时,记得调用super.clone()

实践67:别只依赖finalize()清理non-memory(内存以外)的资源

你不能保证finalize()是否被调用,以及何时被调用,请专门实现一个public函数来释放内存以外的资源

实践68:在构造函数内调用non-final函数时要当心

如果一个non-final函数被某个derived class覆写,在构造函数中调用这个函数可能会导致不可预期的结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值