Java 高阶函数和回调

Higher Order Functions A higher order function is a function that treats other functions as data. 在 Java 7 及之前的版本, memory boxes (variables) 不能包含指向 functions 的 pointers, 也就是无法给 functions 指定 types. 所以不能像Python一样直接把 function 作为参数传递到另一个 function 中。只能借用 interface: public interface IntUnaryFunction { int apply(int x); } // 定义一个方法 public class TenX implements IntUnaryFunction { /* Returns ten times the argument. */ public int apply(int x) { return 10 * x; } } // 高阶方法 public static int do_twice(IntUnaryFunction f, int x) { return f....

2017-05-29 · 1 min · Cong Chan

Java Hash @Override equals() hashcode()

主要介绍: Hashcode(哈希码)与 equals(判断相等)的关系 Hashcode 方法的底层实现原理 开发中需要掌握的原则和方法 HashSet, HashMap, HashTable HashSet底层是调用HashMap. HashMap 使用hashCode和equals来进行对象比较。 拿HashSet和add()举例(其余的数据结构,和 remove, contains等方法类似): 假设HashSet里面已经有了obj1, 那么当调用HashSet.add(obj2)时: if (obj1 == obj2), 那么没有必要调用 hashCode(), 已经有了这个对象, 没必要添加了 else, if hashCode 不同,那么可以直接添加了, 没必要进一步调用 obj1.equals(obj2) 来判断对象是否相等 else hashCode 相同,那么需要进一步调用obj1.equals(obj2) 下面这段代码虽然 HashSet 只存了 a 对象,但当检查是否包含 b 对象时,返回true。 HashSet<String> wordSet = new HashSet<String>(); String a = "hello"; String b = "hello"; wordSet.add(a); return wordSet.contains(b); // return true 根据Javadoc for Set. adds the specified element e to this set if the set contains no element e2 such that (e==null ?...

2017-02-26 · 3 min · Cong Chan

Java 多线程

多线程 线程是独立的执行空间,Java语言内置多线程功能,用类Thread来表达。每个Java应用程序会启动一个主线程 – 将main()放在自己的执行空间的最开始处. JVM会负责主线程的启动(以及其他比如GC的系统线程). 程序员负责启动自己的建立的线程. 启动新线程 建立Runnable对象作为线程任务Runnable job = new MyRunnable(), Runnable接口只有一个方法run() 建立Thread对象并赋值Runnable Thread thread1 = new Thread(job); Thread thread2 = new Thread(job) 启动thread.start(); 另一种创建线程的方法是用Thread的子类覆盖掉run(), 构造新线程Thread t = new Thread();. 从OO的角度看待, 此时Thread 与线程任务是不同概念的. 让子类继承Thread的目的通常是需要更特殊的Thread, 需要特殊的行为, 如果没有这种需求, 就没必要继承Thread. 线程调度器 多线程间的切换由调度器scheduler来管理, 线程有多种状态: 执行中, sleep(2000): 睡眠2000ms, 时间到之前不会被执行, 但时间到了并不保证一定会被执行. 可能会抛出InterruptedException, 所以对它的调用要包含在try/catch中. locked. 线程的run完成执行后, 将无法重新启动. 调度器在不同的JVM有不同的做法. 测试多线程时需要在不同机器上测试. 并发 Concurrency并发环境中, 为了避免冲突, 需要上锁, 使用synchronized来修饰方法使之每次只能被单一线程读写. 同步化是有代价的, 查询钥匙有性能上的损耗, 同步化也会强制线程排队执行, 还可能出现死锁. 死锁 因为两个线程互相持有对方正在等待的东西, 导致没有一方可以脱离等待. 数据库有事务回滚机制来复原死锁的事务, 但Java没有处理死锁的机制. Volatile Java为了提高程序运行效率, 编译器自动会优化, 把经常被访问的变量混存起来, 程序在读取这个变量时有可能会直接从缓存(例如寄存器)中读取这个值, 而不会去内存中读取....

2017-02-26 · 1 min · Cong Chan

Java 套接字Socket

Socket 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,确切的说Socket是个代表两台机器之间网络连接的对象。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。 Socket socket = new Socket("192.168.2.1", 5000), 第一个参数是IP地址, 第二个参数是端口. Socket连接的建立代表两台机器之间存有对方的信息, 包括网络地址和TCP端口号. 但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。 使用BufferedReader从Socket上读取数据: 建立对服务器的Socket连接Socket socket = new Socket("192.168.2.1", 5000) 建立连接到Socket上低层输入串流的InputStreamReader InputStreamReader stream = new InputStreamReader(socket.getInputStream());, 作为从低层和高层串流间的桥梁, 把来自服务器的字节转换为字符 建立缓冲区BufferedReader来读取 BufferedReader reader = new BufferedReader(stream); String msg = reader.readLine(); 用PrintWriter写数据到Socket上: 建立对服务器的Socket连接 建立链接到Socket的PrintWriter PrintWriter writer = new PrintWriter(socket.getOutputStream()), 作为字符数据和字节间的转换桥梁, 可以衔接Strings和Socket两端 写入数据 writer.println("You have a message"); TCP/IP TCP/IP协议集包括应用层, 传输层,网络层,网络访问层。 应用层协议主要包括如下几个:FTP、TELNET、DNS、SMTP、NFS、HTTP。 FTP(File Transfer Protocol)是文件传输协议,一般上传下载用FTP服务,数据端口是20H,控制端口是21H。 Telnet服务是用户远程登录服务,使用23H端口,使用明码传送,保密性差、简单方便。 DNS(Domain Name Service)是域名解析服务,提供域名到IP地址之间的转换,使用端口53。 SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,用来控制信件的发送、中转,使用端口25。 NFS(Network File System)是网络文件系统,用于网络中不同主机间的文件共享。 HTTP(Hypertext Transfer Protocol)是超文本传输协议,用于实现互联网中的WWW服务,使用端口80。 网络接口层:...

2017-02-26 · 2 min · Cong Chan

Java 抽象类

抽象类 有些情况下,有些父类在实际应用中只有被继承的和产生多态的意义,而没有实例化的意义(比如抽象的Animal, Canine等,实例化这些抽象概念没有实际意义),所以不希望这种父类会被初始化。通过标记类为抽象类,编译器就知道该类不能创建实例(不能作为new实例, 但可以用于声明类型). abstract class Canine extends Animal { ... } Canine c = new Dog; // Canine c = new Canine(); 无法编译 反之,不抽象的类就是具体类. 有抽象类自然就有抽象方法,抽象的方法没有实体,所有就不会含有具体的实现public abstract void doSomething();. 只有抽象类才能拥有抽象方法. 在抽象父类中定义可继承的抽象方法, 可以定义出一组子类共同的协议, 这样能够保证多态. 但因为抽象方法只是为了标记处多态而存在, 它们没有具体的内容, 这样在继承树结构下的第一个具体类就必须要实现所有的抽象方法. 当然, 树结构中的抽象类也可以提前把抽象方法实现了(default方法). 抽象类和接口的比较 接口是抽象类的极端形式,接口是完全抽象的,不包含实现(新的特性支持default method)。 有很多情景需要某个类继承多种父类。比如,因为原来的业务需求比较单一,只需要Animal - Canine - Dog这种类结构就满足需求了, 此时Dog只是Animal的子类. 但后来有了新的功能需求, 上线了宠物功能, 理论上可以为每一种具体的属于宠物的子类添加宠物功能, 这就涉及大量的人工和bug. 但假如额外设计一种Pet类, 那么Pet和Animal会有交叉重叠, 如果让宠物子类同时继承两种超类, 那就是多重继承. 因为多重继承会有致命方块的问题. 所以Java不支持这种方式. 而接口这个概念, 就是用于解决这个问题的: public interface Pet { public abstract void beFriendly(); public abstract void play(); } // 对于属于宠物的子类,让其实现接口`Pet`....

2017-02-26 · 2 min · Cong Chan

Java 类的继承扩展 Extends

类的继承扩展 定义class之间的层次关系. 假设要构建一个RotatingSLList,它具有与SLList相同的功能,如addFirst, size等,但是需要额外的rotateRight操作将最后一项放到列表的前面,因为继承允许子类重用已经定义的类中的代码。所以让RotatingSLList类从SLList继承部分代码: public class RotatingSLList<Item> extends SLList<Item> {} RotatingSLList“是一种”SLList, extends可以让我们继承SLList的原始功能,并能修改或添加其他功能。 /** The rotateRight method takes in an existing list, and rotates every element one spot to the right, moving the last item to the front of the list. For example, input [5, 9, 15, 22] should return [22, 5, 9, 15]. */ public void rotateRight() { Item x = removeLast(); addFirst(x); } 通过extends, 子类继承父类的所有成员,成员包括: 所有实例和静态变量 所有方法 所有嵌套类 但注意,构造函数不继承,并且私有成员不能被子类直接访问。...

2017-02-25 · 4 min · Cong Chan

Java 13 | 接口默认方法

除了单纯提供声明之外,Java 8 也允许接口提供具体的实现方法。 缺省方法 从 Java 8开始支持 Default method。 我们可以在List中列出已实现的method。这些方法就是 default method,定义了List hypernyms的一些默认行为:default public void method() { ... }. 我们可以自由调用interface中定义的方法,而不用操心具体的实现。Default method 适用于实现接口的任何类型的对象!子类可以直接调用,而不必重新实现 default method。 // List default public void print() { ... } 不过,我们仍然可以override default method,在子类中重新定义该方法。这样,只要我们在LinkedLList上调用print(),它就会调用子类override的方案,而不是父类的。 // LinkedList @Override public void print() { ... } Dynamic type Java是通过一个叫“dynamic method selection”的特性,来确定要调用 default method 还是已经被子类override的method。 当实例声明List<String> l = new LinkedList<String>();, 则指明l是 static 类型的 List。由 new 生成的 object 是LinkedList类型,也从属于 List 类型。但是,因为这个对象本身是使用 LinkedList 构造函数实例化的,所以我们称之为 dynamic type。...

2017-02-24 · 1 min · Cong Chan

Java 12 | 接口 Interface

子类在什么情况下需要多个父类? 比如,因为原来的业务需求比较单一,只需要Animal - Canine - Dog这种类结构就满足需求了, 此时Dog只是Animal的子类. 但后来有了新的功能需求, 上线了宠物功能, 理论上可以为每一种具体的属于宠物的子类添加宠物功能, 这就涉及大量的人工和bug. 但假如额外设计一种Pet类, 那么Pet和Animal会有交叉重叠, 如果让宠物子类同时继承两种超类, 那就是多重继承. 因为多重继承会有致命方块的问题, 不同父类对同一个方法的可能有不同的实现方式, 这会导致冲突. 所以Java不支持这种方式. 接口 而接口这个概念, 就可以用于解决这个问题的. 类不需要继承多个父类, 只需要实现一个或多个接口指定的所有方法/行为的关系. public interface Pet { public abstract void beFriendly(); public abstract void play(); } // 对于属于宠物的子类,让其实现接口`Pet`. public class Dog extends Canine implements Pet {} 接口作为参数 我们前面创建的 LinkedList and ArrayList 其实很相似 - 所有的method都一样. 如果我们需要写一个需要用到数组的类比如WordUtils class, public class WordUtils { /** Returns the length of the longest word. */ public static String longest(LinkedList<String> list) { ....

2017-02-23 · 2 min · Cong Chan

Java 11 | 测试 Testing

测试 如何知道自己的程序是否真的在工作?在现实世界中,程序员相信他们的代码,因为代码通过了他们自己编写的测试。常用的测试有 Ad Hoc Testing, Unit test 和 Integration Testing。 Ad Hoc Testing,是指没有计划和记录的软件测试,除非发现缺陷,不然一般只运行一次。 Unit test 程序可分解为单元(或程序中可测试的最小部分),Unit test 严格测试代码的每个单元,最终确保项目正确运行。 Unit test 好处: Unit test 保证良好的代码结构(每个 method “只打一份工”),帮助我们较好地解析任务, 允许我们考虑每个方法的所有边界情况,并单独测试它们。 让我们每次只专注于一个单元,进行测试,debug,对准确度有信心后,再进行下一个单元的开发。相比于一次性写完所有代码,再测试debug,Unit test 减少了 debugging 时间。 坏处: 测试也要花时间 测试本身也是有可能出错的,测试可能不全面,不规范,或者有bug 有些单元是依赖于其他单元的 Unit testing 无法保证各个模块的交互,无法保证整个系统作为一个整体是否正常工作。 JUnit JUnit是一个给Java做测试的框架,由Erich Gamma(Design Patterns)和Kent Beck(eXtreme Programming)编写。 JUnit使用Java的 reflection 功能(Java程序可以检查自己的代码)和注释。 JUnit允许我们: 定义并执行测试和测试套件 使用测试作为规范的有效手段 使用测试来支持重构 将修改的代码集成到构建中 JUnit可用于多个IDE,例如BlueJ,JBuilder和Eclipse在一定程度上具有JUnit集成。 import org.junit.Test; import static org.junit.Assert.*; @Test public void testMethod() { assertEquals(<expected>, <actual>); } assertEquals测试一个变量的实际值是否等于它的期望值。 JUnit test 各个测试方法,必须是非静态的(JUnit的设计人员设计规定的)。...

2017-01-29 · 1 min · Cong Chan

Java 10 | 数据结构 - LinkedList 还是 ArrayList

Java 提供了 ArrayList, ArrayDeque 和 LinkedList 几个API. 队列 queue, 通俗的含义, 就是不能插队, 只能在末尾插入. 双端队列 Double Ended Queue (Deque) 是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩 –http://www.cplusplus.com/reference/deque/deque/ CS61b的project 1a需要实现两种双端队列(array based 和 linkedklist based). 不同的API, 在考虑什么时候应该用哪个时, 我们需要考虑它们的性能差异: 搜索/定位:与LinkedList相比,ArrayList搜索更快。 ArrayList的get(int index)性能是O(1)的,而LinkedList的性能是O(n)。因为ArrayList基于array数据结构,可以直接用 array index 定位元素。 删除/插入:LinkedList 操作性能是O(1),而ArrayList的性能从O(n)(删除/插入第一个元素)到O(n)(最后一个元素)都有可能。因为LinkedList的每个元素都包含两个指向其相邻前后元素的指针(地址),因此仅需要改变,被删节点的prev和next指针位置。而在ArrayList中,需要移动剩余元素,来重新填充array空间。 内存开销:LinkedList的每个元素都有更多的内存开销(额外的指针), 而ArrayLists没有这个开销。但是,ArrayLists需要占用初始容量。一般ArrayList的默认初始容量非常小(Java 1.4 - 1.8使用10)。但是,往ArrayLists添加元素时, 它可能会适当地增大容量,所以如果添加了很多元素,则必须不断调整数组的大小,那样也可能会导致元素频繁挪动位置。 综上所述: 如果在应用中需要频繁插入和删除,那么选择LinkedList。 假如一开始,就知道后面要添加大量元素,那就使用较高的初始容量来构造ArrayList。 大部分用例中, 相比LinkedList, 人们更偏爱ArrayList以及ArrayDeque。如果你不确定应该选哪个, 那么就直接考虑ArrayList吧(参考 https://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist).

2017-01-28 · 1 min · Cong Chan