(1)== vs equals()
==是操作符(可比较primitive),equals()是方法(继承自java.lang.Object,比较对象)
==比较对象在heap中的引用或内存中的位置(new一个对象会产生一个新的对象),equals()比较对象的状态和内容
==行为不可复写(Java不支持操作符重载),equals()可以覆写改变其比较行为
集合类一般使用compareTo()来比较对象
包装类用==比较时会先转成primitive后比较,所以要避免NPE;而且比较的是引用
Integer包装类对于-128到127有cache,所以结果会是true
(2)String literal vs new String()
new String("Java")会在heap中创建两个对象,一个String对象一个String常量。
literal会从String常量池中返回一个存在的对象(如果池中没有会默认创建一个自动调用intern()方法放入池中)
也可以自己调用intern()方法把任意对象放入池中:
String s1 = "Java";
String s2 = new String("Java");
s2 = s2.intern(); // 这里s1==s2为true
(3)String vs StringBuffer vs StringBuilder
String是immutable/final,StringBuffer是mutable
String通过+操作符连接,StringBuffer使用append()
String和StringBuffer不能强制转换,需要StringBuffer的toString()
StringBuffer同步且线程安全,StringBuilder不同步不线程安全
StringBuffer和StringBuilder都继承自AbstractStringBuilder
String/StringBuffer/StringBuilder内部都是用字符数组实现
StringBuffer/StringBuilder有初始容量capacity
(4)Interface vs Abstract Class
抽象类只能extend一个,接口可以implement多个
抽象类中可以定义非抽象方法,接口中不能定义非抽象方法(JDK8默认方法除外)
抽象类中即使没有抽象方法也可以标记为abstract
抽象类可以添加方法不影响子类,接口中添加方法需要修改所以它的实现类
抽象类适合代码重用,接口适合类型同一
接口没有方法实现更易于解耦
接口可以实现依赖注入
接口里的变量默认是public final
接口不可实例化,抽象类不可实例化但可以invoked
(5)Static vs Non Static class
类分为顶级类和嵌套内部类(还有特殊的匿名类)
顶级类不能被定义为static,嵌套内部类可以选择是否为static
嵌套内部类如果是static的话一般叫嵌套类,非static的叫内部类
嵌套类和内部类的创建方法不同:
Outer.Nested nested = new Outer.Nested();
Outer.Inner inner = new Outer().new Inner();
嵌套类可以被看做外部类的一个静态成员,可以在static方法中被使用
嵌套类不可以访问外部类的非static成员,内部类可以
(6)Primitive vs Reference variable
primitive变量有默认值
primitive变量存储值,reference变量存储对象在heap中的handle
primitive变量赋值是值的拷贝(互不影响),reference变量赋值是handle的拷贝(操作会影响另一方)
primitive变量使用==比较,reference变量使用equals()比较
primitive变量作为参数传递的是值,reference变量作为参数传递的是handle的拷贝
primitive变量参数的值在方法内被修改不影响原有值,reference变量参数修改handle不影响原有值但使用handle可以修改内部的值
primitive变量作为方法的返回值时返回的是值,返回reference变量时返回的是handle可以继续在方法外使用
primitive变量存储在stack,reference变量存储在heap
(7)Static vs Non Static method
static方法隶属于class,通过Class名直接调用(多用于工厂和单例),非static方法需要通过创建的对象来调用
static方法中不能访问非static变量
static方法不能被override
static方法多用于utility类
(8)transient vs volatile
transient变量不会被序列化(值是默认值)
volatile变量用于并发编程,被保存在单线程中,取值时也是从主线程中获取和当前线程栈无关(线程共享)
volatile经常用于实现单例来代替static final
transient和volatile只能用于变量不能用于方法或类
(9)volatile vs static
线程会缓存static变量
线程在操作static变量时是操作它们内部cache的local copy,不会影响其他线程
volatile变量只有一个main copy,各线程之间的更新会反映到其他线程
(10)throw vs throws
throw是在方法内部抛出异常,throws是在方法定义上标示该方法会抛出什么样的异常
throw一次只能抛出一个异常,throws可以通过逗号分隔标示多个异常
throw可以用于switch语句,throws只能用于方法定义处
throws只是标示给编译器用,并不会处理异常
方法内部抛出的需要checked的异常都要在方法定义处通过throws标示
不管是需要checked的或运行时异常都可以在throws标示,但运行时异常可以不标示
throws也可以只标示方法内部所有异常的父类
throw异常需要注意方法override引起的异常继承关系
(11)final vs finally vs finalize()
final用于变量/方法/类,finally用于try-catch,finalize()方法给GC调用
final变量不可修改,final方法不可override,final类不可继承
(12)Error vs Exception
常见Error:OutOfMemoryError
Error不需要try-catch即使catch了也无法解决,Exception异常catch后可做后续处理
RuntimeException表示的是程序代码的错误,Erro多为系统环境上致命错误
(13)Runtime Exception vs Checked Exception
常见Runtime异常:NullPointerException、ArrayIndexOutOfBoundException
常见Checked异常:ClassNotFoundException、IOException
Checked异常需要在调用处try-catch,Runtime异常不需要
Checked异常继承自Exception, Runtime异常继承自RuntimeException
自定义异常时需要注意标示异常是否为RuntimeException
(14)NoClassDefFoundError vs ClassNotFoundExcepiton
NoClassDefFoundError是Error不需要try-catch,ClassNotFoundException是Checked异常
NoClassDefFoundError出现在JVM或ClassLoader无法找到类(静态块初始化),ClassNotFoundException出现在程序动态加载类时
ClassNotFoundException多来自于Class.forName(),ClassLoader.findSystemClass(),ClassLoader.loadClass()
(15)Serializable vs Externalizable
Serializable是标记接口没有任何需要实现的方法,Externalizable继承自Serializable有两个需要实现的方法writeExternal()、readExternal()
Externalizable的子类JVM在序列化和反序列化对象时会调用writeExternal()、readExternal()
Serializable默认序列化所有类信息,Externalizable可以自定义序列化过程
Serializable序列化性能慢,可以通过使用transient或static
减少序列化字段来提高性能
Serializable由于字段的改变会变得不可维护
Externalizable可以处理transient变量或static变量
(16)Stack and Heap
stack存储本地变量和函数调用,heap存储对象
JVM启动可指定栈和堆得大小:stack(-Xss) heap(-Xms-Xmx)
stack空间不足时java.lang.StackOverFlowError
heap空间不足时java.lang.OutOfMemoryError: Java Heap Space
使用递归调用会让stack急剧变小
stack中的变量只能自己线程访问,stack中的对象所有线程都能访问
stack所需大小远小于heap
(17)Shallow Copy vs Deep Copy
浅拷贝:使用已知实例对新创建的实例的成员变量逐个赋值
深拷贝:不仅复制primitive变量值,还要为引用类型reference的成员变量创建新的实例
浅拷贝时,拷贝的对象和元对象不是100%隔离
浅拷贝时,任何修改元对象中引用类型变量的操作会反映到拷贝对象中,反之也是
默认clone()方法提供的是浅拷贝shallow copy,深拷贝需要override默认clone()方法
如果对象只有primitive变量,浅拷贝足够
(18) super T> vs extends T>
PECS: "Producer Extends, Consumer Super".
extends T> 表示类型的上界,可用于的返回类型限定,不能用于参数类型限定。
super T> 表示类型下界,表示参数化类型是此类型的父类型。可用于参数类型限定,不能用于返回类型限定。
List extends Number> foo1 = new ArrayList(); // Integer extends Number List extends Number> foo2 = new ArrayList (); // Double extends Number Number a1 = foo1.get(0); Number a2 = foo2.get(0); foo1.add(1); // error foo2.add(1.5); // error
List super Integer> bar1 = new ArrayList(); // Number is a superclass of Integer List super Integer> bar2 = new ArrayList
集合篇
(1)Enumeration vs Iterator vs ListIterator
Iterator支持在遍历集合时删除元素,Enumeration不支持
Enumeration是JDK早期的遍历集合类的方法现在很少用
Enumeration是早期的类现在不是所有集合都支持Vector支持ArrayList不支持
Enumeration是一个只读接口用来遍历获取集合中的元素
Iterator在遍历集合时不允许其他线程修改集合中的元素
ListIterator是针对List类型集合的
Enumeration和Iterator是单向的,ListIterator是双向的
Enumeration的方法:xxx.elements(); hasMoreElement(),nextElement()
Iterator的方法:xxx.iterator(); hasNext(), next(), remove()
ListIterator的方法:xxx.listIterator(); hasNext(),next(),previous(),hasPrevious(),remove(),nextIndex(),previousIndex()
(2)Collection vs Collections
Collection是集合类的顶级接口,除过Map其他大部分实现在该接口
Collections是一个utility类,提供一些静态方法来处理集合类
(3)Comparator vs Comparable
Package不一样:java.lang.Comparable,java.util.Comparator
被排序的Object需要实现Comparable,新建一个类实现Comparator接口
Comparable实现的方法:compareTo(Object obj1) 比较当前对象和参数对象
Comparator实现的方法:compare(Object obj1, Object obj2) 比较参数的两个对象
compareTo(Object o1)返回值:正数-当前对象大于o1 0-相等 负数-当前对象小于o1
compare(Object o1,Object o2)返回值:正数-o1大于o2 0-相等 负数-o1小于o2
Collections.sort(List)调用Comparable比较元素
Collections.sort(List, Comparator)调用Comparator比较元素
Comparable:对Object只能写一套比较方法
Comparator:对Object可以写多个比较方法多次排序
(4)List vs Set vs Map
List有序可重复,可存多个null元素,主要实现:ArrayList, Vector, LinkedList
Set无序不可重复,只能存一个null元素,主要实现:HashSet, TreeSet, LinkedHashSet
Map键值对,键不可重复,键只有一个null,主要实现:HashMap, LinkedHashMap, Hashtable and TreeMap
(5)Array vs ArrayList
Array固定长度,ArrayList不固定长度(后台通过数组实现)
Array创建时需要指定长度,ArrayList不需要(JDK有默认初始容量并会自动扩容)
Array不支持泛型,ArrayList支持泛型
Array长度:arr.length; ArrayList大小:list.size();
ArrayList只能存对象不能存primitive变量(自动装箱除外)
(6)ArrayList vs LinkedList
ArrayList后台通过数组实现,LinkedList通过链表实现(嵌套类存储前后节点)
LinkedList不只是实现了List还实现了Deque(FIFO)。add(),poll()
添加元素:ArrayList-O(1) LinkedList-O(1)
删除元素:ArrayList-O(n) LinkedList-O(n/2)
获取元素:ArrayList-O(1) LinkedList-O(n/2)
(7)HashSet vs TreeSet
HashSet比TreeSet性能好
HashSet后台用Hashtable实现,TreeSet后台用红黑树实现
HashSet允许null,TreeSet不允许null
HashSet使用equals()比较对象,TreeSet使用compareTo()比较对象
(8)Fail-Safe vs Fail-Fast Iterator
安全失败(fail-safe)和快速失败(fail-fast)
fail-safe Iterator:集合元素被修改时不抛出异常
fail-fast Iterator:集合元素被修改时抛出异常ConcurrentModificationException
fail-safe iterator:遍历的是集合的一个副本
fail-fast iterator:遍历的是集合自身
fail-safe:CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap
fail-fast:HashMap,ArrayList,HashSet
线程篇
(1)wait() vs sleep() vs yield()
Object.wait(),Thread.sleep(),Thread.yield()
wait(),wait(10),wait(10, 5); sleep(10),sleep(10, 5); yield()
wait()可通过notify()唤醒
wait()会释放对象锁,sleep(),yield()不会
wait()需要在同步块或同步方法中调用
yield()使当前线程停止时间是不定的
(2)start() vs run()
start()调用时会创建一个新线程,并且执行内部的run()方法
直接调用run()方法无法创建新线程,run()方法执行在当前线程里
start()方法不能调用多次,第二次会抛出IllegalStateException
(3)Thread vs Runnable interface
扩展继承Thread后就不能再继承其他类
Runnable标示该对象是一个Task可以被执行,执行还的通过Thread
(4)Callable vs Runnable
Callable会抛出checked异常,Runnable没有
Callable能在call()方法中返回操作结果,Runnable的run()方法不行
Callable可通过泛型传入返回类型
(5)Synchronized vs Concurrent Collections
都提供线程安全
Synchronized集合:Hashtable,Vector。Collections.synchronizedMap(),Collections.synchronizedList()
Concurrent集合:ConcurrentHashMap,CopyOnWriteArrayList
Synchronized集合比Concurrent集合性能慢
Synchronized集合锁整个对象,Concurrent集合会被分成多个segments锁其中一个segment
(6)Multi-Threading vs Multi-Tasking
multi-threading多个线程同时执行同一个程序或程序不同部分,multitasking并行执行不同的程序
multitasking有两种:基于进程process、基于线程thread。进程比线程开销跟大
(7)User thread vs Daemon thread
main thread里创建的线程默认都是user thread,通过setDaemon(true)能够是它变为daemon thread
daemon thread里边创建的线程也是daemon thread的
JVM在结束时需要等待user thread,但是不会等待daemon thread
JVM自身会创建一些daemon thread,比如GC
daemon thread的优先级更低
user thread可以被应用或线程自己关闭,JVM来终止daemon thread
不要再daemon thread里编写业务代码
设计篇
(1)Abstraction vs Encapsulation
Abstraction抽象:interface, abstract class
Encapsulation封裝:private, package-private, protected
(2)Overloading vs Overriding
Overload重载在编译期,Override重写在运行期
同一个类中overload方法,子类中override父类方法
可以overload静态方法,但不能override静态方法
private和final方法不能override,但可以overload
overload时需要修改方法的定义(参数表),override不需要
(3)Dependency Injection vs Factory Pattern
DI依赖注入需要第三方框架支持(比如Spring),工厂模式自己封装代码
DI松耦合设计,工厂模式耦合factory和object
DI框架替你常见管理实例,工厂通过getInstance()生产一个实例
DI更易于单体测试,更弹性灵活,但DI需要配置注入关系
(4)Singleton Pattern vs Static Class
单例提供的是Object,静态类提供的是方法
静态类性能更好
静态类方法不可override
静态类不便于测试
单例可以保持对象的状态等信息,静态类不可以
单例只在使用时加载(lazy load),静态类启动时加载
很多DI框架更容易支持单例
(5)Class vs Instance vs Object
Class的实例是对象。(An object is an instance of a class)
Class是用于创建对象的模板蓝图
Instance是类的一个唯一拷贝用于表现对象
Object拥有状态 state 和行为 behavior
JVM实际是先创建Instance后创建Object
具体参考 这里
(6)Stack vs Heap
Heap存贮对象(JDK 7前String存在String Pool,之后也版本也存入Heap)
Stack存贮本地变量、方法定义、调用栈
所有线程共享Heap,各线程之间是不共享Stack的(本地变量是被线程隔离的)
创建thread后JVM会为其分配Stack
Heap的空间比Stack更大
可以指定Heap(-Xms-Xmx)和Stack(-Xss)的大小
Heap和Stack存储的数据结构是不一样的
其他
(1)trustStore vs keyStore
trustStore存储公钥和CA,keyStore存储私钥
trustStore验证证书,keyStore提供证书
创建SSLContext:trustStore-TrustManager(决定是否连接可信),keyStore-KeyManager(决定哪些连接在SSL握手时需要发到Server认证)
JRE默认trustStore保存在$JAVA_HOME/lib/security/cacerts
-Djavax.net.ssl.trustStore -Djavax.net.ssl.keyStore
-Djavax.net.ssl.trustStorePassword -Djavax.net.ssl.keyStorePassword
引用
证书标准 :X.509
编码格式 :
PEM-encoded certificate (domain.pem) Apache和*NIX服务器
DER-encoded certificate (domain.der) Java和Windows服务器
文件扩展名 :
keystore.jks - Java的Keytool工具生成的密钥库,存储非对称密钥对(私钥+x509公钥证书),有时也叫xxxx.keystore
keytool -genkey -alias localhostkey -keystore localhost.keystore -storepass password -keypass password
domain.key - 存放一个公钥或者私钥,并非X.509证书
openssl genrsa -des3 -out domain.key 2048
domain.csr - 证书签名请求,存放一个公钥,并非X.509证书,生成证书时要提交给证书颁发机构
openssl req -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr
domain.crt - Self-Signed Certificate *NIX系统
openssl req -newkey rsa:2048 -nodes -keyout domain.key -x509 -days 365 -out domain.crt
domain.cer - 证书文件,只有公钥
domain.p12 - 证书文件,包含公钥及私钥,PKCS12(domain.pfx)
(2)Type 1, 2, 3, 4 JDBC Driver
Type 1:非Java实现,最早的驱动连接Access,需要把JDBC请求转化为ODBC后链接,又叫JDBC ODBC bridge driver(性能差)
Type 2:非Java实现,JDBC直接通过native API和DB链接,需要native library比如ocijdbc11.dll
Type 3:Java Net实现,通过代理服务和DB链接,client-server-database,又叫Net protocol JDBC driver
Type 4:纯Java实现,通过native protocol直接和DB链接,驱动JAR文件中包含所有数据库的调用,又叫thin JDBC driver
(3)32-bit vs 64-bit
32位CPU寻址范围是2的32次方,大概4GB的RAM(留给应用的大概Linux:2GB,Windows:1.5GB)
64位系统各OS寻址范围不同Windows可达8TB
32位OS只能按照32位Java,64位OS可以安装32位Java或64位Java
64位需要heap的大小多于32位的30-50%(object headers、object references)
64位GC中断时间更长
.class不依赖于多少位的Java编译器(互通编译,互通使用)
整型值的上限和多少位没关系Integer.MAX_VAlUE = 2^31 - 1
(4)PATH vs CLASSPATH
- JAVA_HOME:
指向JDK安装目录,比如C:\Program Files\Java\jdk1.8.0_65
Java本身不使用,第三方应用比如Tomcat使用,因为运行需要JDK支持
- JRE_HOME:
指向JRE安装目录,比如C:\Program Files\Java\jdk1.8.0_65\jre
Java本身不使用,第三方应用比如Tomcat使用,因为运行需要JDK支持,Tomcat优先使用JRE_HOME
JDK安装后有两套相同的JRE:C:\Program Files\Java\jdk1.8.0_65\jre(供JDK附属工具用的)、C:\Program Files\Java\jre1.8.0_65
***javac XXX.java 等价于 java -cp "%JAVA_HOME%\lib\tools.jar" com.sun.tools.javac.Main XXX.java
***Tomcat早期版本需要JDK把jsp编译成servlet,现在已经内置Eclipse的ECJ来支持JSP。
- CLASSPATH:
JVM类加载器查找class的路径
Java6后支持通配符不用挨个指定jar,CLASSPATH="C:\Program Files\Java\jdk1.8.0_65\lib"
也可以通过-cp 或 -classpath参数指定后供javac和java命令使用
目前来说基本无需设置:JDK工具能自己查找到这些jar;IDE会为project设置不同classpath
***Eclipse有自己的编译器ECJ来编译代码不需要javac。
- PATH:
操作系统的默认环境变量,去查找javac和java等工具的位置
-rt.jar、dt.jar、tools.jar
rt.jar: JRE提供的所有类java.lang.String、java.util.ArrayList等,在$JAVA_HOME/jre/lib下。源代码:$JAVA_HOME/src.zip
dt.jar: 不要包含Swing的BeanInfo文件,在$JAVA_HOME/lib下。
tools.jar: JDK需要的工具集,javac、javadoc等需要调用这个jar,在$JAVA_HOME/lib下。
***这些文件都是提供给JVM使用的,一般不需要特别设置。而且这三个文件在JDK9会被删掉分到不同的文件!