随笔分类 - Java编程
摘要:在从事软件开发的十多年间,我曾有幸读过多种开发语言的Effective系列,如《Effective C++》、《More Effective C++》、《Effective Java》(第一版/第二版)、《Effective C#》和《More Effective C#》,单单从这一点看也算是Effective系列的忠实拥趸了。毋庸置疑,这个系列的书籍对我们研习各种开发语言的进阶都有着极为重要的意义。 在与很多初、中、高级程序员一起合作开发期间,曾多次向他们推荐经典的专业书籍,尽管每种开发语言都有着属于自己的代表作,如C++ Primer、Thinking in Java和Essential.
阅读全文
摘要:七十五、考虑使用自定义的序列化形式: 设计一个类的序列化形式和设计该类的API同样重要,因此在没有认真考虑好默认的序列化形式是否合适之前,不要贸然使用默认的序列化行为。在作出决定之前,你需要从灵活性、性能和正确性多个角度对这种编码形式进行考察。一般来讲,只有当你自行设计的自定义序列化形式与默认的形式基本相同时,才能接受默认的序列化形式。比如,当一个对象的物理表示法等同于它的逻辑内容,可能就适合于使用默认的序列化形式。见如下代码示例:1 public class Name implements Serializable {2 private final String l...
阅读全文
摘要:六十六、同步访问共享的可变数据: 在Java中很多时候都是通过synchronized关键字来实现共享对象之间的同步的。事实上,对象同步并不仅限于当多个线程操作同一可变对象时,仍然能够保证该共享对象的状态始终保持一致。与此同时,他还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前所有的修改效果。 Java的语言规范保证了读写一个变量是原子的,除非这个变量的类型为long或double。换句话说,读取一个非long或double类型的变量,可以保证返回的值是某个线程保存在该变量中的,即时多个线程在没有同步的情况下并发地修改这个变量也是如此。然而需要特别指出的是,这样的..
阅读全文
摘要:五十七、只针对异常情况才使用异常: 不知道你否则遇见过下面的代码:1 try {2 int i = 0;3 while (true)4 range[i++].climb();5 } catch (ArrayIndexOutOfBoundsException e) {6 } 这段代码的意图不是很明显,其本意就是遍历变量数组range中的每一个元素,并执行元素的climb方法,当下标超出range的数组长度时,将会直接抛出ArrayIndexOutOfBoundsException异常,catch代码块将会捕获到该异...
阅读全文
摘要:四十五、将局部变量的作用域最小化: 将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性。在C语言中要求局部变量必须在一个代码块的开头处进行声明,出于习惯,有些开发者延续了这样的做法。这个习惯需要改正,Java提供了你在代码块的任何地方声明变量的语法支持。 "要使局部变量的作用域最小化,最有力的实践就是在第一次使用它的地方声明"。如果过早的声明,开发者就有可能在真正使用该变量的时候忘记了它的类型或者初始值了,而且也会带来代码块内变量名的名字污染问题,由此引发的Bug,往往是令人极为沮丧的。 "几乎每个局部变量的声明都应该包含一个初始化表达式
阅读全文
摘要:三十八、检查参数的有效性: 绝大多数方法和构造器对于传递给它们的参数值都会有些限制。比如,索引值必须大于等于0,且不能超过其最大值,对象不能为null等。这样就可以在导致错误的源头将错误捕获,从而避免了该错误被延续到今后的某一时刻再被引发,这样就是加大了错误追查的难度。就如同编译期能够报出的错误总比在运行时才发现要更好一些。事实上,我们不仅仅需要在函数的内部开始出进行这些通用的参数有效性检查,还需要在函数的文档中给予明确的说明,如在参数非法的情况下,会抛出那些异常,或导致函数返回哪些错误值等,见如下代码示例: 1 /** 2 * Returns a BigInteger w...
阅读全文
摘要:三十、用enum代替int常量: 枚举类型是指由一组固定的常量组成合法值的类型,该特征是在Java 1.5 中开始被支持的,之前的Java代码都是通过“公有静态常量域字段”的方法来简单模拟枚举的,如: public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; ... ... public static final int ORANGE_NAVEL = 0; public s...
阅读全文
摘要:二十三、请不要在新代码中使用原生态类型: 先简单介绍一下泛型的概念和声明形式。声明中具有一个或者多个类型参数的类或者接口,就是泛型类或接口,如List<E>,这其中E表示List集合中元素的类型。在Java中,相对于每个泛型类都有一个原生类与之对应,即不带任何实际类型参数的泛型名称,如List<E>的原生类型List。他们之间最为明显的区别在于List<E>包含的元素必须是E(泛型)类型,如List<String>,那么他的元素一定是String,否则将产生编译错误。和泛型不同的是,原生类型List可以包含任何类型的元素,因此在向集合插入元素时,
阅读全文
摘要:十三、使类和成员的可访问性最小化: 信息隐藏是软件程序设计的基本原则之一,面向对象又为这一设计原则提供了有力的支持和保障。这里我们简要列出几项受益于该原则的优势: 1. 更好的解除各个模块之间的耦合关系: 由于模块间的相互调用是基于接口契约的,每个模块只是负责完成自己内部既定的功能目标和单元测试,一旦今后出现性能优化或需求变更时,我们首先需要做的便是定位需要变动的单个模块或一组模块,然后再针对各个模块提出各自的解决方案,分别予以改动和内部测试。这样便大大降低了因代码无规则交叉而带来的潜在风险,同时也缩减了开发周期。 2. 最大化并行开发: 由于各个模块之间保持着较好的独立性,因...
阅读全文
摘要:八、覆盖equals时请遵守通用约定: 对于Object类中提供的equals方法在必要的时候是必要重载的,然而如果违背了一些通用的重载准则,将会给程序带来一些潜在的运行时错误。如果自定义的class没有重载该方法,那么该类实例之间的相等性的比较将是基于两个对象是否指向同一地址来判定的。因此对于以下几种情况可以考虑不重载该方法: 1. 类的每一个实例本质上都是唯一的。 不同于值对象,需要根据其内容作出一定的判定,然而该类型的类,其实例的自身便具备了一定的唯一性,如Thread、Timer等,他本身并不具备更多逻辑比较的必要性。 2. 不关心类是否提供了“逻辑相等”的测试功能。 ...
阅读全文
摘要:一、考虑用静态工厂方法代替构造器: 构造器是创建一个对象实例最基本也最通用的方法,大部分开发者在使用某个class的时候,首先需要考虑的就是如何构造和初始化一个对象示例,而构造的方式首先考虑到的就是通过构造函数来完成,因此在看javadoc中的文档时首先关注的函数也是构造器。然而在有些时候构造器并非我们唯一的选择,通过反射也是可以轻松达到的。我们这里主要提到的方式是通过静态类工厂的方式来创建class的实例,如:1 public static Boolean valueOf(boolean b) {2 return b ? Boolean.TRUE : Boolea...
阅读全文
摘要:最近接手的项目都是需要用C++和Java协同进行开发的,其中前台是Java的Swing,后台是Linux平台的C++服务器,他们之间用Socket进行数据传输。至于数据库吗?起初客户的要求是Oracle,后来鉴于成本,改成MySQL和PostgreSQL了。好了,闲话少说,还是让我们言归正传吧。 之前写这个系列的目的很清楚,就是为了进一步加强自己在Java方面的技能,毕竟从事Java的开发也就3,4年的时间,而且所有的知识都是停留在Java Core(J2SE)上。起初写这个系列博客时几乎无从下手,一个偶然的机会得到同事的提醒,他们认为我更应该充分利用自己在C++方面的优势,不仅要清晰的阐..
阅读全文
摘要:此前写这个系列博客的初衷非常简单,就是打算帮助自己梳理一下JDK中的类库,毕竟自己对J2EE知识知之甚少,因此就想,还是让已经掌握的知识更夯实一些吧。记得在很早以前曾经写过类似的电子文档,将平时积累下来的技术和技巧都记录了下来,以备后用。然而在经过一段时间之后,重新翻出这些文档时,就会感觉帮助不是很大,经过分析后总结出以下几点原因: 1. 都是一些较小的技巧和看一遍就可以牢记的知识点; 2. 记录的比较分散,或者说凌乱,因为都是随手记下来的,后来也没有再经过很好的整理和规划; 3. 实际案例较少,不能做到一看就能立刻回忆起当时的场景,并且对关键技术要点没有突出显示,不能做到一目了然; 4. .
阅读全文
摘要:本篇是该系列的最后一篇文章,就让我们以JNI作为结束吧! 众所周知,使用多种语言协同开发时,经常会导致一些未知问题的发生,也会给我们程序的调试带来一定的负担。鉴于此,我们也只有在充分了解到必要性之后再决定用C/C++来替换部分Java代码,以达到我们预期的目的,考虑使用本地代码主要有以下三个理由: 1) 你的应用需要访问系统的各个特性和设备,这些特性和设备通过Java平台是无法访问的; 2) 你已经有了大量的测试过和调试过得用另一种语言编写的代码,并且知道如何将其导出到所有的目标平台上; 3) 通过基准测试,你已经发现所编写的Java代码比用其他语言编写的等价代码要慢得多。 在Java中提供.
阅读全文
摘要:本篇为您介绍的是如何通过套接字(Socket)编写基于TCP/IP(IPv4)的网络应用程序。 在基于TCP的网络通讯中,每次进行数据传输之前,均需要在服务器端和客户端之间建立TCP连接,之后再在该连接通道上进行数据传输。然而在连接之前,我们还需要做哪些准备呢?很明显,如果我们是客户端,则需要预先知道待连接的服务器的IP地址和端口号。这就如同打电话,通话之前先拨号,拨号之前需要知道对方的电话号码。那么对于服务器而言又需要做什么呢?由于它是被动等待客户端发起连接的,因此服务器唯一需要准备工作是监听指定的IP地址和端口号。可以看出,无论是服务器还是客户端,在准备建立连接之前都是需要与IP地址和端.
阅读全文
摘要:12. 字体: 1) 通过之前介绍的图形环境(GraphicsEnvironment)工具类获取当前系统支持的所有字体。 1 public class MyTest { 2 public static void main(String[] args) { 3 String[] fontFamilies = GraphicsEnvironment.getLocalGraphicsEnvironment() 4 .getAvailableFontFamilyNames(); 5 ...
阅读全文
摘要:本篇将继续介绍Java 2D 图形部分的内容。 10. BufferedImage: BufferedImage中包含着width*height个像素点的颜色值,同时BufferedImage中还带有色彩模型(ColorModel)的信息,用于描述像素点的颜色模型,如TYPE_INT_ARGB、TYPE_INT_RGB等。Graphic2D在渲染目标图像时,也需要依照ColorModel来计算图像像素的颜色信息并执行渲染。 在有些情况下,我们需要对BufferedImage中的每一个像素的颜色值进行计算,并将计算的结果回写到BufferedImage中相应的位置。那么我们是如果获取这些像素信.
阅读全文
摘要:本篇将继续介绍Java 2D 图形部分的内容。 5. 坐标变换: 坐标变换是图形编程中非常重要的一项技术,该技术在基于3D的开发中应用更为普遍,我们这里将介绍Java 2D中四种基本的变换: 1) 比例缩放(scale):放大和缩小从一个固定点出发的所有距离; 2) 旋转(rotate):环绕着一个固定中心旋转所有点; 3) 平移(translate):将所有的点移动一个固定量; 4) 切变(shear):使一个线条固定不变,再按照与该固定线条之间的距离,成比例地将与该线条平行的各个线条"滑动"一个距离量。 这里提及的四种变换规则可以组合在一起使用,然而需要注意的是,在组合
阅读全文
摘要:本篇将主要为您介绍Java的2D图形编程技术,然而在示例代码中还将会涉及一些简单的Swing知识,我们会尽可能的将2D中的方法进行归类和抽取,以便使您的关注点始终保持在Java2D上。该篇还会和本系列中其他篇章的风格保持一致,只是介绍必要的关键概念,更多的技术点是通过已经良好分类的有代表性的示例代码来为您展示和说明。 1. 形状绘制: 在Java 2D中提供了下面几个基本形状: 1) Line2D 2) Rectangle2D 3) RoundRectangle2D 4) Ellipse2D 5) Arc2D 6) QuadCurve2D 7) CubicCurve2D 8) General.
阅读全文
摘要:1. Calendar: 对于每一个Java的开发者而言,这是一个再熟悉不过的对象了,这里我们给出一个巧妙的例子,至于如何巧妙,看了输出结果就清楚了。 1 public class MyTest { 2 public static void main(String args[]) { 3 GregorianCalendar d = new GregorianCalendar(); 4 int today = d.get(Calendar.DAY_OF_MONTH); 5 int month = d...
阅读全文