项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步

注:本文是根据网络上的文章受启发,自己再写代码总结而成。因为无法找到原文的原始出处,所以没法给出原文链接。文章也会引用部分网络素材内容,如果原作者看到请与我联系。

0.前言

本博主的java水平与专业java开发同学相比水平不可同日而语,所以平时有空的话会多写写java代码。正好在网上看到一篇关于java习惯用法的总结,觉得还不错,于是按照文章里的大致架构,自己重新实现了一把里面的相关代码,相当于自己做个小小的总结。

1.基本方法实现

equals()
hashcode()
compareTo()
clone()

1.1 equals()实例与hashCode()实例

import java.util.Arrays;public class Person {String name;int birthday;byte[] raw;public boolean equals(Object obj) {// 原文中的代码有误,需要将!符号后面的表达式扩起来if (!(obj instanceof Person)) { return false;}Person other = (Person) obj;return name.equals(other.name)&& birthday == other.birthday&& Arrays.equals(raw, other.raw);}public int hashCode() {return name.hashCode() + birthday + Arrays.hashCode(raw);}public static void main(String[] args) {Person leilei = new Person();leilei.name = "leilei";leilei.birthday = 19991010;Person lulu = new Person();lulu.name = "leilei";lulu.birthday = 19991010;System.out.println(leilei.equals(lulu));}}

equals()方法需要注意的几个点
1.参数必须是Object类型,不能是外围类。jdk源码中的equals()方法,传入的参数就是Object类型。
2.foo.equals(null) 必须返回false,不能抛NullPointerException。(注意,null instanceof 任意类 总是返回false,因此上面的代码可以运行)
3.基本类型比较使用 ==,基本类型数组域的比较使用Arrays.equals()。
4.覆盖equals()时,记得要相应地覆盖 hashCode(),与 equals() 保持一致

hashCode()方法注意的几个点
1.当x和y两个对象具有x.equals(y) == true ,你必须要确保x.hashCode() == y.hashCode()。
2.根据逆反命题,如果x.hashCode() != y.hashCode(),那么x.equals(y) == false 必定成立。
3.你不需要保证,当x.equals(y) == false时,x.hashCode() != y.hashCode()。但是,如果你可以尽可能地使它成立的话,这会提高哈希表的性能。
4.hashCode()最简单的合法实现就是简单地return 0;虽然这个实现是正确的,但是这会导致HashMap这些数据结构运行得很慢。

1.2 compareTo()方法实现

package leilei.bit.edu.common;import java.util.TreeSet;public class PersonA implements Comparable<PersonA>{String firstName;String lastName;int birthday;public int compareTo(PersonA other) {if(firstName.compareTo(other.firstName) != 0) {return firstName.compareTo(other.firstName);} else if(lastName.compareTo(other.lastName) != 0) {return lastName.compareTo(other.lastName);} else if(birthday < other.birthday) {return -1;} else if(birthday > other.birthday) {return 1;} else {return 0;}}@Overridepublic String toString() {return "firstName: " + firstName + ",lastName: " + lastName;}public static void main(String[] args) {PersonA p1 = new PersonA();p1.firstName = "li";p1.lastName = "lei";PersonA p2 = new PersonA();p2.firstName = "han";p2.lastName = "meimei";PersonA p3 = new PersonA();p3.firstName = "xiao";p3.lastName = "ming";TreeSet tree = new TreeSet();tree.add(p1);tree.add(p2);tree.add(p3);for(Object obj:tree) {System.out.println(obj);}}
}

输出:

firstName: han,lastName: meimei
firstName: li,lastName: lei
firstName: xiao,lastName: ming

TreeSet在java内部是一种排序的数据结构。根据程序的输出可以看出,person对象在tree中是根据firstName排序的。

1.总是实现泛型版本 Comparable 而不是实现原始类型 Comparable 。因为这样可以节省代码量和减少不必要的麻烦。
2.只关心返回结果的正负号(负/零/正),它们的大小不重要。
3.Comparator.compare()的实现与这个类似。

1.3 clone()方法

实际中我很少使用clone方法,怕自己的理解有偏差,直接将原文的clone部分摘过来

class Values implements Cloneable {String abc;double foo;int[] bars;Date hired;public Values clone() {try {Values result = (Values)super.clone();result.bars = result.bars.clone();result.hired = result.hired.clone();return result;} catch (CloneNotSupportedException e) {  // Impossiblethrow new AssertionError(e);}}
}

1.使用 super.clone() 让Object类负责创建新的对象。
2.基本类型域都已经被正确地复制了。同样,我们不需要去克隆String和BigInteger等不可变类型。
3.手动对所有的非基本类型域(对象和数组)进行深度复制(deep copy)。
4.实现了Cloneable的类,clone()方法永远不要抛CloneNotSupportedException。因此,需要捕获这个异常并忽略它,或者使用不受检异常(unchecked exception)包装它。
5.不使用Object.clone()方法而是手动地实现clone()方法是可以的也是合法的。

2.应用类

2.1 StringBuilder 与 StringBuffer

这两个类在实际开发中用得很多,举一个很简单的小例子。

 @Testpublic void Str_Build() {StringBuilder sb = new StringBuilder();sb.append("I ");sb.append("love");sb.append(" coding");System.out.println(sb.toString());}

让代码run起来

I love coding

注意点:
1.不要像这样使用重复的字符串连接:s += item ,因为它的时间效率是O(n^2)。
2.使用StringBuilder或者StringBuffer时,可以使用append()方法添加文本和使用toString()方法去获取连接起来的整个文本。示例代码中就是这么做的。
3.优先使用StringBuilder,因为它更快。StringBuffer的所有方法都是同步的,而你通常不需要同步的方法。
4.StringBuilder的缺陷之一,就是开发过程中,经常用sb作为new出来的对象名称。。。

2.2 生成一个随机整数

 @Testpublic void int_random() {Random rand = new Random();int a = rand.nextInt(10) + 1;System.out.println("random num is: " + a);}

让代码run起来

random num is: 10

2.3 使用Iterator.remove()

 @Testpublic void collections_remove() {List<Integer> list = new ArrayList<Integer>();for(int i=1; i<=10; i++) {list.add(i);}Iterator<Integer> it = list.iterator();while(it.hasNext()) {int num = it.next();if(num%2 == 0) {System.out.println(num + "===" + num);it.remove();}}for(int each:list) {System.out.print(each + " ");}}

让代码run起来

2===2
4===4
6===6
8===8
10===10
1 3 5 7 9

remove()方法作用在next()方法最近返回的条目上。每个条目只能使用一次remove()方法。

2.4 反转字符串

c++的程序猿里的一道经典面试题就是反转字符串。对于java或者python等更高级的语言来说,反转字符串都是一句话搞定的事情。

 @Testpublic void reverse_str() {String raw_str = "some";String rev_str = new StringBuilder(raw_str).reverse().toString();System.out.println("rev_str is: " + rev_str);}

让代码run起来

rev_str is: emos

2.5 Thread/Runnable

//实现Runnable的方式
void startAThread0() {new Thread(new MyRunnable()).start();
}class MyRunnable implements Runnable {public void run() {...}
}//继承Thread的方式
void startAThread1() {new MyThread().start();
}class MyThread extends Thread {public void run() {...}
}匿名继承Thread的方式
void startAThread2() {new Thread() {public void run() {...}}.start();
}

不要直接调用run()方法。总是调用Thread.start()方法,这个方法会创建一条新的线程并使新建的线程调用run()。

2.6 try-finally

IO流的例子
void writeStuff() throws IOException {OutputStream out = new FileOutputStream(...);try {out.write(...);} finally {out.close();}
}锁的例子
void doWithLock(Lock lock) {lock.acquire();try {...} finally {lock.release();}
}

1.如果try之前的语句运行失败并且抛出异常,那么finally语句块就不会执行。但无论怎样,在这个例子里不用担心资源的释放。
2.如果try语句块里面的语句抛出异常,那么程序的运行就会跳到finally语句块里执行尽可能多的语句,然后跳出这个方法(除非这个方法还有另一个外围的finally语句块)。

3.输入输出

3.1 从输入流中读取字节数据

 @Testpublic void test1() throws Exception {InputStream in = new FileInputStream(new File("xxx"));try {while(true) {int n = in.read();if(n == -1) {break;} else {System.out.println(n);}}} finally {in.close();}}

输出:

112
97
99
107
97
103
101
...

由此可见,read()方法要么返回下一次从流里读取的字节数(0到255,包括0和255),要么在达到流的末端。

3.2 从输入流中读取块数据

 @Testpublic void test2() throws Exception {InputStream in = new FileInputStream(new File("xxx"));try {byte[] buf = new byte[100];while(true) {int n = in.read(buf);if(n == -1) {break;} else {System.out.println(n);}}} finally {in.close();}}

输出:

100
100
100
100
100
100
81

要记住的是,read()方法不一定会填满整个buf,所以你必须在处理逻辑中考虑返回的长度。

3.3 读取文本文件

 @Testpublic void test3() throws Exception {String filename = "xxx";BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filename)),"utf-8"));try {while (true) {String line = br.readLine();if (line == null) {break;} else {System.out.println(line);}}} finally {br.close();}}

1.BufferedReader对象的创建显得很冗长。这是因为Java把字节和字符当成两个不同的概念来看待
2.你可以使用任何类型的InputStream来代替FileInputStream,比如socket
3.当达到流的末端时,BufferedReader.readLine()会返回null。
4.要一次读取一个字符,使用Reader.read()方法。
5.你可以使用其他的字符编码而不使用UTF-8,但最好不要这样做。

3.4.向文本写文件

 @Testpublic void test4() throws Exception {String filename = "xxx";PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(filename)),"utf-8"));try {out.println("hello");out.println(42);out.println("world");} finally {out.close();}}

1.Printwriter对象的创建显得很冗长。这是因为Java把字节和字符当成两个不同的概念来看待
2.就像System.out,你可以使用print()和println()打印多种类型的值。
3.你可以使用其他的字符编码而不使用UTF-8,但最好不要这样做。

4 预防性(Defensive Checking)检测

4.1 预防性检测数值

package leilei.bit.edu.common;public class Checking {public static int factorial(int n) {if (n < 0) {throw new IllegalArgumentException("Undefined");} else if(n > 13) {throw new ArithmeticException("Reuslt overflow");} else if(n == 0) {return 1;} else {return factorial(n-1) * n;}}public static void main(String[] args) {int n = 5;int ret = factorial(n);System.out.println("ret is: " + ret);}
}

1.不要认为输入的数值都是正数、足够小的数等等。要显式地检测这些条件。
2.一个设计良好的函数应该对所有可能性的输入值都能够正确地执行。要确保所有的情况都考虑到了并且不会产生错误的输出(比如溢出)。

4.2 预防性检测对象

public int findIndex(List<String> list, String target) {if (list == null || target == null)throw new NullPointerException();...
}

1.不要认为对象参数不会为空(null)。要显式地检测这个条件。

4.3预防性检测数组索引

 public static void outOfBound(int[] b, int index) {if(b==null) {throw new NullPointerException();}if(index < 0 || index >= b.length) {throw new IndexOutOfBoundsException();}...}

不要认为所以给的数组索引不会越界。要显式地检测它。

4.4 预防性检测数组区间

 public static void errorRange(int[] b, int off, int len) {if(b == null) {throw new NullPointerException();}if (off < 0 || off > b.length || len < 0 || b.length - off < len) {throw new IndexOutOfBoundsException();}...}

不要认为所给的数组区间(比如,从off开始,读取len个元素)是不会越界。要显式地检测它。

5.数组

5.1 填充数组元素

5.2 复制一个范围内的数组元素

5.3 调整数组大小

具体代码如下:

package leilei.bit.edu.bigNum;import java.util.Arrays;public class Array_Test {public static void printArray(int[] a) {for(int i=0; i<a.length; i++) {System.out.print(a[i] + " ");}}public static void fill_test() {int[] a = new int[5];Arrays.fill(a,0,5,1);printArray(a);}public static void copy_test() {int[] a = {1,2,3,4,5};int[] b = new int[5];System.arraycopy(a, 0, b, 0, a.length);printArray(b);}public static void copyOf_test() {int[] a = {1,2,3,4,5};a = Arrays.copyOf(a,10);printArray(a);}public static void main(String[] args) {fill_test();System.out.println();copy_test();System.out.println();copyOf_test();}
}

代码运行结果如下:

1 1 1 1 1
1 2 3 4 5
1 2 3 4 5 0 0 0 0 0

6.包装

以下代码演示了将四个字节包装成一个int,以及将一个int分解成四个字节。

package leilei.bit.edu.bigNum;public class Pack {public static void pack() {byte[] a = {1,2,3,4};int result = (a[0] & 0xFF) << 24|(a[1] & 0xFF) << 16|(a[2] & 0xFF) << 8|(a[3] & 0xFF) << 0;System.out.println("result is: " + result);}public static void unpack() {int x = 16909060;byte[] result = {(byte)(x >>> 24),(byte)(x >>> 16),(byte)(x >>> 8),(byte)(x >>> 0)};System.out.print("the unpack result is: ");for(int i=0; i<result.length; i++) {System.out.print(result[i] + " ");}}public static void main(String[] args) {pack();unpack();}}

让代码run起来:

result is: 16909060
the unpack result is: 1 2 3 4

java 常用习惯用法总结相关推荐

  1. java用法_Java 习惯用法总结

    Java 习惯用法总结 2015/04/07 | 分类: 基础技术 | 4 条评论 | 标签: idiom,Java 分享到:97 本文由 ImportNew - 进林 翻译自nayuki.欢迎加入翻 ...

  2. java input函数怎么用_Java函数习惯用法详解

    在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的.在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法. 我把本文的所有代码都放在公共场所里.你可以根据自己的喜 ...

  3. Java习惯用法总结

    在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的.在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法.(Joshua Bloch的<Effective ...

  4. java 双重检查锁_Java中可怕的双重检查锁定习惯用法

    java 双重检查锁 本文讨论的问题不是新问题,但即使是经验丰富的开发人员也仍然很棘手. 单例模式是常见的编程习惯用法. 但是,当与多个线程一起使用时,必须进行某种类型的同步,以免破坏代码. 在相关文 ...

  5. 笔记整理2----Java语言基础(二)06 断点调试与数据加密+07 面向对象-类与对象+08 java常用API-基础+09 java集合+10 IO流-基础

    06 断点调试与数据加密+07 面向对象-类与对象+08 java常用API-基础+09 java集合+10 IO流-基础 第06天 java基础语法 今日内容介绍  Eclipse断点调试  基 ...

  6. 四种Java线程池用法解析

    四种Java线程池用法解析 本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 ...

  7. 简单介绍一下Java常用的五大框架!

    Java框架在Java开发中的作用是毋庸置疑的.那么Java常用框架有哪些?大概包括:Hibernate.Spring.Struts.jQuery.Redis五种.这些框架有什么用呢?Java常用框架 ...

  8. Java常用的5大框架介绍!

    作为常年霸榜的头牌编程语言,Java的火热程序已经毋庸置疑,Java框架在Java开发中的作用也是不可忽视.下面,小千给大家具体介绍一下Java常用的5大框架,希望对正在学习Java的人有所帮助. 1 ...

  9. java 常用类库_JAVA(三)JAVA常用类库/JAVA IO

    成鹏致远 |lcw.cnblog.com|2014-02-01 JAVA常用类库 1.StringBuffer StringBuffer是使用缓冲区的,本身也是操作字符串的,但是与String类不同, ...

  10. java.nio.ByteBuffer用法小结

    转载自  java.nio.ByteBuffer用法小结 在NIO中,数据的读写操作始终是与缓冲区相关联的.读取时信道(SocketChannel)将数据读入缓冲区,写入时首先要将发送的数据按顺序填入 ...

最新文章

  1. (0064)iOS开发之枚举NS_ENUM和NS_OPTIONS的区别
  2. 我之我见:samba共享
  3. CNN结构:序列预测复合DNN结构-AcGANs、 ENN误差编码网络
  4. research button control usage in WebIDE
  5. eclipse中ast_JavaParser中AST节点的观察者
  6. 操作系统饥饿现象_操作系统心得体会
  7. 小程序员的大梦想 与盖茨像哥们儿
  8. 【mysql安装】阿里云centos7环境mysql安装
  9. oracle导出自增设置,oracle008:oracle自增,自适应,数据闪回,导入导出
  10. 开课吧9.9学python课_python 自动化运维 零基础入门 课程
  11. 连续 3 年支撑双 11,阿里云神龙如何扛住全球流量洪峰?
  12. Java的15种锁总结
  13. dx 汇编dec_汇编语言——汇编指令
  14. linux 代码量统计命令,Linux下源代码行数统计工具(sloccount, cloc等)
  15. OSPF路由协议总结(一)
  16. 《软件方法》第二章 自测题
  17. 成都启英泰伦科技有限公司
  18. 阶梯压测线程 jp@gc - Stepping Thread Group (deprecated)
  19. 测验六python编程题,Python编程第六章习题.py
  20. vue + echars地图 省市区 + 添加点标记

热门文章

  1. 使用宝塔控制面板建站时出现网页出现404错误怎么办?
  2. spring3.1 profile 配置不同的环境
  3. 你的网站上显示Alexa世界排名的代码(表)
  4. myeclipse中删除tomcat 的server后,重新添加进来的方法
  5. 【Alpha】Scrum Meeting 10
  6. Servlet、Listener、Filter、JSP
  7. 【树莓派】树莓派常用的一些源
  8. 红帽全年总营收24亿美元,同比增长18%
  9. mysql数据库忘记密码时如何修改
  10. python3.5之输出HTML实体字符