Jason

Jason 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

以终为始,闭环迭代,持续提高。

个人动态

Jason 发布了文章 · 7月15日

本地缓存(Cache)系统简易设计

本地Cache系统简易设计

为什么使用缓存?

  • 降低数据库的访问压力。
  • 提高查询效率。
  • 改善用户体验。

你都了解哪些缓存?

  • 数据库内置缓存(DBA修改)。
  • 数据层缓存(由持久层框架决定,例如mybatis)
  • 业务层缓存(由业务层框架以及第三缓存产品决定:本地缓存+分布式缓存)
  • 浏览器缓存(Cache-Control)

设计缓存都应该考虑什么问题?

  • 存储结构:使用什么结构存储数据?(数组,链表,散列存储-哈希存储)
  • 淘汰算法:有限容量(LRU,FIFO,.....),不限容量(GC)
  • 并发安全:保证线程安全。
  • 任务调度:每隔多长时间清理一下缓存。
  • 日志记录:是否命中?(命中率)

缓存系统设计基础

缓存标准定义

package com.cy.java.cache;  
/\*\* Cache接口设计\*/  
public interface Cache {  
 public void putObject(Object key,Object value);  
 public Object getObject(Object key);  
 public Object removeObject(Object key);  
 public void clear();  
 public int size();  
}

简易Cache实现

场景应用:
  • 存储数据量比较小(因为没有考虑淘汰机制)
  • 没有线程共享(一个线程的内部缓存)
  • 缓存对象生命周期比较短
package com.cy.java.cache;  
import java.util.HashMap;  
import java.util.Map;  
/**负责真正存储数据的一个对象,将数据存储到一个map中*/  
public class PerpetualCache implements Cache {  
/** 特点:线程不安全,key不允许重复,不能保证key的顺序  */  
private Map<Object,Object> cache=new HashMap<>();  
@Override  
public void putObject(Object key, Object value) {  
cache.put(key, value);  
}  
@Override  
public Object getObject(Object key) {  
return cache.get(key);  
}  
@Override  
public Object removeObject(Object key) {  
return cache.remove(key);  
}  
@Override  
public void clear() {  
 cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
 Cache cache=new PerpetualCache();  
 cache.putObject("A", 100);  
 cache.putObject("B", 200);  
 cache.putObject("C", 300);  
 System.out.println(cache);  
 cache.removeObject("D");  
 cache.clear();  
 System.out.println(cache.size());  
}  
}

构建线程安全Cache对象

场景应用:并发环境
package com.cy.java.cache;  
/**线程安全的cache对象*/  
public class SynchronizedCache implements Cache{  
private Cache cache;  
public SynchronizedCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public synchronized void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public synchronized Object getObject(Object key) {  
// TODO Auto-generated method stub  
return cache.getObject(key);  
}  
@Override  
public synchronized Object removeObject(Object key) {  
// TODO Auto-generated method stub  
return cache.removeObject(key);  
}  
@Override  
public synchronized void clear() {  
cache.clear();  
}  
@Override  
public synchronized int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
SynchronizedCache cache=

 new SynchronizedCache(new PerpetualCache());  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
System.out.println(cache);  
}  
  
}

思考:对于SynchronizedCache 有什么优势,劣势?

支持日志记录的Cache实现

package com.cy.java.cache;  
/** 用于记录命中率的日志cache*/  
public class LoggingCache implements Cache {  
private Cache cache;  
/**记录请求次数*/  
private int requests;  
/**记录命中次数*/  
private int hits;  
public LoggingCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
requests++;  
Object obj=cache.getObject(key);  
if(obj!=null)hits++;  
System.out.println("Cache hit Ratio : "+hits\*1.0/requests);  
return obj;  
}  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
public static void main(String\[\] args) {  
SynchronizedCache cache=  
 new SynchronizedCache(  
new LoggingCache(  
new PerpetualCache()));  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
System.out.println(cache);  
cache.getObject("D");  
cache.getObject("A");  
}  
  
}

思考:你觉得LoggingCache记录日志的方式有什么不好的地方?(信息的完整性,同步问题)

LruCache实现

应用场景:基于LRU算法的的基本实现
package com.cy.java.cache;  
import java.util.LinkedHashMap;  
import java.util.Map;  
  
/** 缓存淘汰策略:LRU(最近最少使用算法)*/  
public class LruCache implements Cache {  
 private Cache cache;  
 /**通过此属性记录要移除的数据对象*/  
 private Object eldestKey;  
 /**通过此map记录key的访问顺序*/  
 private Map<Object,Object> keyMap;  
 @SuppressWarnings("serial")  
 public LruCache(Cache cache,int maxCap) {  
 this.cache=cache;  
//LinkedHashMap可以记录key的添加顺序或者访问顺序  
 this.keyMap=

 new LinkedHashMap<Object,Object>(maxCap, 0.75f, true)

 {//accessOrder  
//此方法每次执行keyMap的put操作时调用  
@Override  
protected boolean removeEldestEntry

 (java.util.Map.Entry<Object, Object> eldest) {  
boolean isFull=size()>maxCap;  
if(isFull)eldestKey=eldest.getKey();  
return isFull;  
}  
 };  
}  
@Override  
public void putObject(Object key, Object value) {  
//存储数据对象  
cache.putObject(key, value);  
//记录key的访问顺序,假如已经满了,就要从cache中移除数据  
keyMap.put(key, key);//此时会执行keyMap对象的removeEldestEntry  
if(eldestKey!=null) {  
cache.removeObject(eldestKey);  
eldestKey=null;  
}  
}  
@Override  
public Object getObject(Object key) {  
keyMap.get(key);//记录key的访问顺序  
return cache.getObject(key);  
}  
  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
  
@Override  
public void clear() {  
cache.clear();  
keyMap.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
  
public static void main(String\[\] args) {  
SynchronizedCache cache=  
 new SynchronizedCache(  
new LoggingCache(  
new LruCache(new PerpetualCache(),3)));  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
cache.getObject("A");  
cache.getObject("C");  
cache.putObject("D", 400);  
cache.putObject("E", 500);  
System.out.println(cache);  
  
}  
}

设置Cache淘汰算法:FIFO算法

package com.cy.java.cache;  
import java.util.Deque;  
import java.util.LinkedList;  
/**  
* FifoCache :基于FIFO算法(对象满了要按先进先出算法移除对象)实现cache对象  
*/  
public class FifoCache implements Cache{  
/**借助此对象存储数据*/  
private Cache cache;  
/**借助此队列记录key的顺序*/  
private Deque<Object> keyOrders;  
/**通过此变量记录cache可以存储的对象个数*/  
private int maxCap;  
public FifoCache(Cache cache,int maxCap) {  
this.cache=cache;  
keyOrders=new LinkedList<>();  
this.maxCap=maxCap;  
}  
@Override  
public void putObject(Object key, Object value) {  
//1.记录key的顺序(起始就是存储key,添加在队列最后位置)  
keyOrders.addLast(key);  
//2.检测cache中数据是否已满,满了则移除。  
if(keyOrders.size()>maxCap) {  
Object eldestKey=keyOrders.removeFirst();  
cache.removeObject(eldestKey);  
}  
//3.放新的对象  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
return cache.getObject(key);  
}  
  
@Override  
public Object removeObject(Object key) {  
Object obj=cache.removeObject(key);  
keyOrders.remove(key);  
return obj;  
}  
@Override  
public void clear() {  
cache.clear();  
keyOrders.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
public static void main(String\[\] args) {  
Cache cache=

 new SynchronizedCache(  
new LoggingCache(  
 new FifoCache(  
  new PerpetualCache(),3)));  
cache.putObject("A",100);  
cache.putObject("B",200);  
cache.putObject("C",300);  
cache.getObject("A");  
cache.putObject("D",400);  
cache.putObject("E",500);  
System.out.println(cache);  
}   
}

序列化Cache的实现

场景:存储到cache的是对象的字节
package com.cy.java.cache;  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
public class SerializedCache implements Cache {  
  
private Cache cache;  
public SerializedCache(Cache cache) {  
this.cache=cache;  
}  
/**序列化*/  
private byte\[\] serialize(Object value) {  
//1.构建流对象  
ByteArrayOutputStream bos=null;  
ObjectOutputStream oos=null;  
try {  
//1.2构建字节数组输出流,此流对象内置可扩容的数组。  
bos=new ByteArrayOutputStream();  
//1.3构建对象输出流  
oos=new ObjectOutputStream(bos);  
//2.对象序列化  
oos.writeObject(value);

 //此时对象会以字节的方式写入到字节数组输出流  
oos.flush();  
return bos.toByteArray();  
}catch (Exception e) {  
 throw new RuntimeException(e);  
}finally {  
//3.关闭流对象  
if(bos!=null)

 try{bos.close();bos=null;}catch(Exception e) {}  
if(oos!=null)

 try{oos.close();oos=null;}catch (Exception e2) {}  
}  
}  
/**反序列化*/  
public Object deserialize(byte\[\] value) {  
//1.创建流对象  
ByteArrayInputStream bis=null;  
ObjectInputStream ois=null;  
try {  
//1.1构建字节数组输入流,此对象可以直接读取数组中的字节信息  
bis=new ByteArrayInputStream(value);  
//1.2构建对象输入流(对象反序列化)  
ois=new ObjectInputStream(bis);  
//2.反序列化对象  
Object obj=ois.readObject();  
return obj;  
}catch(Exception e) {  
throw new RuntimeException(e);  
}finally {  
//3.关闭流对象  
if(bis!=null)

 try{bis.close();bis=null;}catch(Exception e) {}  
if(ois!=null)

 try{ois.close();ois=null;}catch (Exception e2) {}  
}  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, serialize(value));  
}  
@Override  
public Object getObject(Object key) {  
return deserialize((byte\[\])cache.getObject(key));  
}  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache=new SerializedCache(new PerpetualCache());  
cache.putObject("A", 200);  
cache.putObject("B", 300);  
Object v1=cache.getObject("A");  
Object v2=cache.getObject("A");  
System.out.println(v1==v2);  
System.out.println(v1);  
System.out.println(v2);  
}  
}

软件引用Cache实现

应用场景:内存不足时淘汰缓存中数据
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.SoftReference;  
/**软引用*/  
public class SoftCache implements Cache {  
private Cache cache;  
private ReferenceQueue<Object> garbageOfRequenceQueue=

 new ReferenceQueue<>();  
public SoftCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.将对象存储到cache(key不变,Value为为soft引用对象)  
cache.putObject(key, 

 new SoftEntry(key, value, garbageOfRequenceQueue));  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取软引用对象并判断  
SoftEntry softEntry=(SoftEntry)cache.getObject(key);  
if(softEntry==null)return null;  
//2.基于软引用对象获取它引用的对象并判断  
Object target = softEntry.get();  
if(target==null)cache.removeObject(key);  
return target;  
}  
  
@Override  
public Object removeObject(Object key) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.从cache中移除对象  
Object removedObj=cache.removeObject(key);  
return removedObj;  
}  
  
@Override  
public void clear() {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.清空cache  
cache.clear();  
}  
@Override  
public int size() {  
removeGarbageObjects();  
return cache.size();  
}  
private void removeGarbageObjects() {  
SoftEntry softEntry=null;  
//1.从引用队列中获取已经被GC的一些对象的引用  
 while((softEntry=

 (SoftEntry)garbageOfRequenceQueue.poll())!=null){  
//softEntry不为null表示softEntry引用的对象已经被移除  
//2.从cache中将对象引用移除。  
cache.removeObject(softEntry.key);  
}  
}  
/\*\*定义软引用类型\*/  
private static class SoftEntry extends SoftReference<Object\>{  
private final Object key;  
public SoftEntry(Object key,

 Object referent, ReferenceQueue<? super Object> rQueue) {  
super(referent, rQueue);  
this.key=key;  
}  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
  
public static void main(String\[\] args) {  
Cache cache=new SoftCache(new PerpetualCache());  
cache.putObject("A", new byte\[1024\*1024\]);  
cache.putObject("B", new byte\[1024\*1024\]);  
cache.putObject("C", new byte\[1024\*1024\]);  
cache.putObject("D", new byte\[1024\*1024\]);  
cache.putObject("E", new byte\[1024\*1024\]);  
System.out.println(cache.size());  
System.out.println(cache);  
}  
  
}

弱Cache对象实现

应用场景:GC触发清除缓存对象
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.WeakReference;  
/\*\*弱引用\*/  
public class WeakCache implements Cache {  
private Cache cache;  
private ReferenceQueue<Object> garbageOfRequenceQueue=

 new ReferenceQueue<>();  
public WeakCache(Cache cache) {  
this.cache=cache;  
}  
  
@Override  
public void putObject(Object key, Object value) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.将对象存储到cache(key不变,Value为为soft引用对象)  
cache.putObject(key, 

 new WeakEntry(key, value, garbageOfRequenceQueue));  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取软引用对象并判断  
WeakEntry softEntry=(WeakEntry)cache.getObject(key);  
if(softEntry==null)return null;  
//2.基于软引用对象获取它引用的对象并判断  
Object target = softEntry.get();  
if(target==null)cache.removeObject(key);  
return target;  
}  
  
@Override  
public Object removeObject(Object key) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.从cache中移除对象  
Object removedObj=cache.removeObject(key);  
return removedObj;  
}  
  
@Override  
public void clear() {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.清空cache  
cache.clear();  
}  
  
@Override  
public int size() {  
removeGarbageObjects();  
return cache.size();  
}  
private void removeGarbageObjects() {  
WeakEntry softEntry=null;  
//1.从引用队列中获取已经被GC的一些对象的引用  
while((softEntry=

 (WeakEntry)garbageOfRequenceQueue.poll())!=null) {  
//softEntry不为null表示softEntry引用的对象已经被移除  
//2.从cache中将对象引用移除。  
cache.removeObject(softEntry.key);  
}  
}  
/**定义软引用类型*/  
private static class WeakEntry extends WeakReference<Object\>{  
private final Object key;  
public WeakEntry(Object key,

 Object referent, ReferenceQueue<? super Object> rQueue) {  
super(referent, rQueue);  
this.key=key;  
}  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
Cache cache=new WeakCache(new PerpetualCache());  
cache.putObject("A", new byte\[1024\*1024\]);  
cache.putObject("B", new byte\[1024\*1024\]);  
cache.putObject("C", new byte\[1024\*1024\]);  
cache.putObject("D", new byte\[1024\*1024\]);  
cache.putObject("E", new byte\[1024\*1024\]);  
cache.putObject("F", new byte\[1024\*1024\]);  
cache.putObject("G", new byte\[1024\*1024\]);  
System.out.println(cache.size());  
System.out.println(cache);  
}  
  
}

缓存系统设计进阶

缓存应用需求升级

  • 缓存系统既要保证线程安全又要保证性能。
  • 缓存日志的记录要写到文件,而且是异步写
  • 向缓存中写数据时要提高序列化性能。

缓存对象读写锁应用

package com.cy.java.cache;  
  
import java.util.concurrent.locks.ReentrantReadWriteLock;  
  
/**  
* 构建线程安全对象,基于ReentrantReadWriteLock对象实现读写锁应用。  
* @author qilei  
*/  
public class ReentrantLockCache implements Cache {  
  
private Cache cache;  
/**  
* 此对象提供了读锁,写锁应用方式.  
* 1)写锁:排他锁  
* 2)读锁:共享锁  
* 说明:读写不能同时执行。  
*/  
private final ReentrantReadWriteLock readWriteLock =  
new ReentrantReadWriteLock();  
public ReentrantLockCache(Cache cache) {  
this.cache=cache;  
// TODO Auto-generated constructor stub  
}  
@Override  
public void putObject(Object key, Object value) {  
readWriteLock.writeLock().lock();  
try {  
cache.putObject(key, value);  
}finally {  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public Object getObject(Object key) {  
readWriteLock.readLock().lock();  
try {  
 Object object=cache.getObject(key);  
 return object;  
}finally{  
readWriteLock.readLock().unlock();  
}  
}  
  
@Override  
public Object removeObject(Object key) {  
readWriteLock.writeLock().lock();  
try {  
 Object object=cache.removeObject(key);  
 return object;  
}finally{  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public void clear() {  
readWriteLock.writeLock().lock();  
try {  
 cache.clear();  
}finally{  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public int size() {  
readWriteLock.readLock().lock();  
try {  
 int size=cache.size();  
 return size;  
}finally{  
readWriteLock.readLock().unlock();  
}  
}  
}

异步日志Cache实现

第一步:添加依赖
` <dependency>  
 <groupId>ch.qos.logback</groupId>  
 <artifactId>logback-classic</artifactId>  
 <version>1.2.3</version>  
 </dependency> `
第二步:添加配置文件logback.xml (参考项目代码)
 
<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  
  
<logger name="com.cy" level="TRACE" />  
<appender name="FILE"  
class\="ch.qos.logback.core.rolling.RollingFileAppender"\>  
<rollingPolicy  
class\="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"\>  
<!--文件路径,定义了日志的切分方式----把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间 -->  
<fileNamePattern>logs/context-log.%d{yyyy-MM-dd}.log  
</fileNamePattern>  
<!--只保留最近30天的日志 -->  
<maxHistory>30</maxHistory>  
</rollingPolicy>  
<encoder charset="UTF-8"\>  
<pattern>\[%-5level\] %date --%thread-- \[%logger\] %msg %n</pattern>  
</encoder>  
</appender>  
  
<appender name="ASYNC\_FILE"  
class\="ch.qos.logback.classic.AsyncAppender"\>  
<discardingThreshold>0</discardingThreshold>  
<queueSize>256</queueSize>  
<appender-ref ref="FILE" />  
</appender>  
  
<root level="debug"\>  
<appender-ref ref="ASYNC\_FILE" />  
</root>  
  
</configuration>
第三步:构建AsyncLoggingCache
package com.cy.java.cache;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
/**  
* 通过此对象异步记录查询操作的命中率  
* 1)选择日志库  
* 2)执行异步写操作。  
*/  
public class AsyncLoggingCache implements Cache {  
 //日志门面应用  
 private static Logger log=LoggerFactory.getLogger(LoggingCache.class);  
private Cache cache;  
/**表示请求次数*/  
private int requests;  
/**命中次数(命中表示从缓存中取到数据了)*/  
private int hits;  
public AsyncLoggingCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
requests++;  
 Object obj=cache.getObject(key);  
 if(obj!=null)hits++;  
 //记录日志耗时  
 log.info("Cache hit Ratio:{}",hits\*1.0/requests);  
return obj;  
}  
@Override  
  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache= 

 new AsyncLoggingCache(new PerpetualCache());  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
cache.putObject("D", 400);  
//System.out.println(cache);  
cache.getObject("E");  
cache.getObject("A");  
cache.getObject("B");  
}  
  
}

Kryo构建序列化Cache

第一步:添加依赖
<dependency>  
 <groupId>com.esotericsoftware</groupId>  
 <artifactId>kryo</artifactId>  
 <version>5.0.0-RC5</version>  
</dependency>
第二步:构建项目工具类
public class KryoUtils {  
  
/**  
* 多线程并发执行时,可能会出现线程不安全,具体原因是什么?  
* 1)多个线程的并发  
* 2)多个线程有数据共享  
* 3)多个线程在共享数据集上的操作不是原子操作  
*  
* 分析:当出现了线程不安全,如何进行修改来保证线程安全  
* 1)将多线程改为单线程。  
* 2)取消共享 (例如在当前应用中我们一个线程一个Kryo对象)  
* 3)加锁+CAS  
*  
* ThreadLocal提供了这样的一种机制:  
* 1)可以将对象绑定到当前线程(其实是将对象存储到当前线程的map中)  
* 2)可以从当前线程获取绑定的对象(从当前线程的map中获取对象)  
*/  
static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {  
  protected Kryo initialValue() {  
  Kryo kryo = new Kryo();  
  // Configure the Kryo instance.  
  kryo.setRegistrationRequired(false);  
  //....  
  return kryo;  
  };  
 };  
 public static Object deserialize(byte\[\] array){  
Kryo kryo=kryos.get();  
 Input input = new Input(new ByteArrayInputStream(array));  
 Object obj=kryo.readClassAndObject(input);  
return obj;  
 }  
 public static byte\[\] serialize(Object object){  
 //从当前线程获取kryo对象,当前线程没有会调用ThreadLocal的initialValue方法创建对象并绑定线程  
 Kryo kryo=kryos.get();  
 ByteArrayOutputStream bos=new ByteArrayOutputStream();  
 Output output = new Output(bos);  
 kryo.writeClassAndObject(output, object);  
  output.close();  
 return bos.toByteArray();  
 }  
 }

> 构建高性能序列化Cache

public class KryoSerializedCache implements Cache {  
  
private Cache cache;  
public KryoSerializedCache(Cache cache) {  
this.cache=cache;  
}  
  
@Override  
public void putObject(Object key, Object value) {  
//1.将对象序列化  
byte\[\] array=KryoUtils.serialize(value);  
//2.将序列化后的字节数组引用存储到cache  
cache.putObject(key,array);  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取缓存中的字节数组引用  
byte\[\] array=(byte\[\])cache.getObject(key);  
//2.将字节数组反序列化为对象  
return KryoUtils.deserialize(array);  
}  
  
@Override  
public Object removeObject(Object key) {  
return KryoUtils.deserialize((byte\[\])cache.removeObject(key));  
}  
  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache=new KryoSerializedCache(new PerpetualCache());  
cache.putObject("A", 500);  
Object a1=cache.getObject("A");  
Object a2=cache.getObject("A");  
System.out.println("a1="+a1);  
System.out.println("a2="+a2);  
System.out.println(a1==a2);//false  
}  
  
}
查看原文

赞 41 收藏 14 评论 5

Jason 关注了专栏 · 7月14日

SegmentFault 行业快讯

第一时间为开发者提供行业相关的实时热点资讯

关注 27811

Jason 关注了用户 · 7月14日

蒋鹏飞 @jiangpengfei_5ecce944a3d8a

掘金优秀作者,ID同名~
公众号:进击的大前端
分享各种大前端进阶知识!
不打广告,不写水文,只发高质量原创,与君共勉,共同学习~
更多文章和示例源码请看:https://github.com/dennis-jia...

关注 1742

Jason 关注了专栏 · 7月14日

前端小码农

分享有趣的代码,让编程更有趣;总结自己学习过程中遇到的坑

关注 1501

Jason 关注了用户 · 7月14日

Java旅途 @javatrip

公众号:Java旅途
github:https://github.com/binzh303/s...

关注 1658

Jason 关注了专栏 · 7月14日

前端巅峰

注重前端性能优化和前沿技术,重型跨平台开发,即时通讯技术等。 欢迎关注微信公众号:前端巅峰

关注 16737

Jason 关注了用户 · 7月14日

敖丙 @aobing

关注 3888

Jason 关注了标签 · 7月14日

前端

Web前端开发是从网页制作演变而来的,名称上有很明显的时代特征。在互联网的演化进程中,网页制作是Web 1.0时代的产物,那时网站的主要内容都是静态的,用户使用网站的行为也以浏览为主。2005年以后,互联网进入Web 2.0时代,各种类似桌面软件的Web应用大量涌现,网站的前端由此发生了翻天覆地的变化。网页不再只是承载单一的文字和图片,各种富媒体让网页的内容更加生动,网页上软件化的交互形式为用户提供了更好的使用体验,这些都是基于前端技术实现的。

Web前端优化
  1. 尽量减少HTTP请求 (Make Fewer HTTP Requests)
  2. 减少 DNS 查找 (Reduce DNS Lookups)
  3. 避免重定向 (Avoid Redirects)
  4. 使得 Ajax 可缓存 (Make Ajax Cacheable)
  5. 延迟载入组件 (Post-load Components)
  6. 预载入组件 (Preload Components)
  7. 减少 DOM 元素数量 (Reduce the Number of DOM Elements)
  8. 切分组件到多个域 (Split Components Across Domains)
  9. 最小化 iframe 的数量 (Minimize the Number of iframes)
  10. 杜绝 http 404 错误 (No 404s)

关注 155750

Jason 关注了标签 · 7月14日

mysql

MySQL是一个小型关系型数据库管理系统,开发者为瑞典MySQL AB公司。在2008年1月16号被Sun公司收购。而2009年,SUN又被Oracle收购。MySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内。这样就增加了速度并提高了灵活性。MySQL的SQL“结构化查询语言”。SQL是用于访问数据库的最常用标准化语言。MySQL软件采用了GPL(GNU通用公共许可证)。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了MySQL作为网站数据库。

关注 64267

Jason 关注了标签 · 7月14日

android

Android(安卓或安致)是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

简介

  Android一词的本义指“机器人”,同时也是Google于2007年11月5日宣布的基于Linux平台的开源手机操作系统的名称,该平台由操作系统、中间件、用户界面和应用软件组成。 

  系统架构

  android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。

  应用程序

  Android会同一系列核心应用程序包一起发布,该应用程序包包括客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等。所有的应用程序都是使用JAVA语言编写的。

  应用程序框架

  开发人员也可以完全访问核心应用程序所使用的API框架。该应用程序的架构设计简化了组件的重用;任何一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循框架的安全性)。同样,该应用程序重用机制也使用户可以方便的替换程序组件。

  隐藏在每个应用后面的是一系列的服务和系统, 其中包括;

  丰富而又可扩展的视图(Views),可以用来构建应用程序, 它包括列表(lists),网格(grids),文本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器。

  内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据

  资源管理器(Resource Manager)提供 非代码资源的访问,如本地字符串,图形,和布局文件( layout files )。

  通知管理器 (Notification Manager) 使得应用程序可以在状态栏中显示自定义的提示信息。

  活动管理器( Activity Manager) 用来管理应用程序生命周期并提供常用的导航回退功能。

  有关更多的细节和怎样从头写一个应用程序,请参考 如何编写一个 Android 应用程序。

  系统运行库

  Android 包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过 Android 应用程序框架为开发者提供服务。以下是一些核心库:

  * 系统 C 库 - 一个从BSD继承来的标准 C 系统函数库( libc ), 它是专门为基于 embedded linux的设备定制的。

  * 媒体库 - 基于PacketVideo OpenCORE;该库支持多种常用的音频、视频格式回放和录制,同时支持静态图像文件。编码格式包括MPEG4, H.264, MP3, AAC, AMR, JPG, PNG 。

  * Surface Manager - 对显示子系统的管理,并且为多个应用程序提 供了2D和3D图层的无缝融合。

  * LibWebCore - 一个最新的web浏览器引擎用,支持Android浏览器和一个可嵌入的web视图。

  应用程序组件

  Android开发四大组件分别是:活动(Activity): 用于表现功能。服务(Service): 后台运行服务,不提供界面呈现。广播接收器(BroadcastReceiver):用于接收广播。内容提供商(Content Provider): 支持在多个应用中存储和读取数据,相当于数据库。

  活动

  Android 中,Activity 是所有程序的根本,所有程序的流程都运行在Activity 之中,Activity可以算是开发者遇到的最频繁,也是Android 当中最基本的模块之一。在Android的程序当中,Activity 一般代表手机屏幕的一屏。如果把手机比作一个浏览器,那么Activity就相当于一个网页。在Activity 当中可以添加一些Button、Check box 等控件。可以看到Activity 概念和网页的概念相当类似。

  一般一个Android 应用是由多个Activity 组成的。这多个Activity 之间可以进行相互跳转,例如,按下一个Button 按钮后,可能会跳转到其他的Activity。和网页跳转稍微有些不一样的是,Activity 之间的跳转有可能返回值,例如,从Activity A 跳转到Activity B,那么当Activity B 运行结束的时候,有可能会给Activity A 一个返回值。这样做在很多时候是相当方便的。

  当打开一个新的屏幕时,之前一个屏幕会被置为暂停状态,并且压入历史堆栈中。用户可以通过回退操作返回到以前打开过的屏幕。我们可以选择性的移除一些没有必要保留的屏幕,因为Android会把每个应用的开始到当前的每个屏幕保存在堆栈中。

  服务

  Service 是android 系统中的一种组件,它跟Activity 的级别差不多,但是他不能自己运行,只能后台运行,并且可以和其他组件进行交互。Service 是没有界面的长生命周期的代码。Service 是一种程序,它可以运行很长时间,但是它却没有用户界面。这么说有点枯燥,来看个例子。打开一个音乐播放器的程序,这个时候若想上网了,那么,我们打开Android 浏览器,这个时候虽然我们已经进入了浏览器这个程序,但是,歌曲播放并没有停止,而是在后台继续一首接着一首的播放。其实这个播放就是由播放音乐的Service进行控制。当然这个播放音乐的Service也可以停止,例如,当播放列表里边的歌曲都结束,或者用户按下了停止音乐播放的快捷键等。service 可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD 卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。

  开启service有两种方式:

  (1) Context.startService():Service会经历onCreate -> onStart(如果Service还没有运行,则android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次 );stopService的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。 注意,多次调用Context.startservice()不会嵌套(即使会有相应的onStart()方法被调用),所以无论同一个服务被启动了多少次,一旦调用Context.stopService()或者stopSelf(),他都会被停止。补充说明:传递给startService()的Intent对象会传递给onStart()方法。调用顺序为:onCreate --> onStart(可多次调用) --> onDestroy。

  (2) Context.bindService():Service会经历onCreate() --> onBind(),onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind --> onDestroyed相应退出,所谓绑定在一起就共存亡了。[20]

  广播接收器

  在Android 中,Broadcast 是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver 是对发送出来的Broadcast进行过滤接受并响应的一类组件。可以使用BroadcastReceiver 来让应用对一个外部的事件做出响应。这是非常有意思的,例如,当电话呼入这个外部事件到来的时候,可以利用BroadcastReceiver 进行处理。例如,当下载一个程序成功完成的时候,仍然可以利用BroadcastReceiver 进行处理。BroadcastReceiver不能生成UI,也就是说对于用户来说不是透明的,用户是看不到的。BroadcastReceiver通过NotificationManager 来通知用户这些事情发生了。BroadcastReceiver 既可以在AndroidManifest.xml 中注册,也可以在运行时的代码中使用Context.registerReceiver()进行注册。只要是注册了,当事件来临的时候,即使程序没有启动,系统也在需要的时候启动程序。各种应用还可以通过使用Context.sendBroadcast () 将它们自己的intent broadcasts广播给其他应用程序。

  注册BroadcastReceiver有两种方式:

  (1)在AndroidManifest.xml进行注册。这种方法有一个特点即使你的应用程序已经关闭了,但这个BroadcastReceiver依然会接受广播出来的对象,也就是说无论你这个应用程序时开还是关都属于活动状态都可以接受到广播的事件;

  (2)在代码中注册广播。

  第一种俗称静态注册,第二种俗称动态注册,这两种注册Broadcast Receiver的区别:

  动态注册较静态注册灵活。实验证明:当静态注册一个Broadcast Receiver时,不论应用程序是启动与否。都可以接受对应的广播。

  动态注册的时候,如果不执行unregister Receiver();方法取消注册,跟静态是一样的。但是如果执行该方法,当执行过以后,就不能接受广播了。

  内容提供

  Content Provider 是Android提供的第三方应用数据的访问方案。

  在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件等内容,都是不允许其他直接访问的。Andorid当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是Content Provider。应用想对外提供的数据,可以通过派生Content Provider类, 封装成一枚Content Provider,每个Content Provider都用一个uri作为独立的标识,形如:content://com.xxxxx。所有东西看着像REST的样子,但实际上,它比REST 更为灵活。和REST类似,uri也可以有两种类型,一种是带id的,另一种是列表的,但实现者不需要按照这个模式来做,给你id的uri你也可以返回列表类型的数据,只要调用者明白,就无妨,不用苛求所谓的REST。

  另外,Content Provider不和REST一样只有uri可用,还可以接受Projection,Selection,OrderBy等参数,这样,就可以像数据库那样进行投影,选择和排序。查询到的结果,以Cursor(参见:reference/android/database/Cursor.html )的形式进行返回,调用者可以移动Cursor来访问各列的数据。

  Content Provider屏蔽了内部数据的存储细节,向外提供了上述统一的接口模型,这样的抽象层次,大大简化了上层应用的书写,也对数据的整合提供了更方便的途径。Content Provider内部,常用数据库来实现,Android提供了强大的Sqlite支持,但很多时候,你也可以封装文件或其他混合的数据。

  在Android中,Content Resolver是用来发起Content Provider的定位和访问的。不过它仅提供了同步访问的Content Provider的接口。但通常,Content Provider需要访问的可能是数据库等大数据源,效率上不足够快,会导致调用线程的拥塞。因此Android提供了一个AsyncQueryHandler(参见:reference/android/content/AsyncQueryHandler.html),帮助进行异步访问Content Provider。

  在各大组件中,Service和Content Provider都是那种需要持续访问的。Service如果是一个耗时的场景,往往会提供异步访问的接口,而Content Provider不论效率如何,都提供的是约定的同步访问接口。

软件开发

  Java方面

  Android支持使用Java作为编程语言来开发应用程序,而Android的Java开发方面从接口到功能,都有层出不穷的变化。考虑到Java虚拟机的效率和资源占用,谷歌重新设计了Android的Java,以便能提高效率和减少资源占用,因而与J2ME等不同。其中Activity等同于J2ME的MIDlet,一个 Activity 类(Class)负责创建视窗(Windows),一个活动中的Activity就是在 foreground(前景)模式,背景运行的程序叫做Service。两者之间通过由ServiceConnection和AIDL连结,达到复数程序同时运行效果。如果运行中的 Activity 全部画面被其他 Activity 取代时,该 Activity 便被停止(Stopped),甚至被系统清除(Kill)。

  View等同于J2ME的Displayable,程序人员可以通过 View 类与“XML layout”档将UI放置在视窗上,Android 1.5的版本可以利用 View 打造出所谓的 Widgets,其实Widget只是View的一种,所以可以使用xml来设计layout,HTC的Android Hero手机即含有大量的widget。至于ViewGroup 是各种layout 的基础抽象类(abstract class),ViewGroup之内还可以有ViewGroup。View的构造函数不需要再Activity中调用,但是Displayable的是必须的,在Activity 中,要通过findViewById()来从XML 中取得View,Android的View类的显示很大程度上是从XML中读取的。View 与事件(event)息息相关,两者之间通过Listener 结合在一起,每一个View都可以注册一个event listener,例如:当View要处理用户触碰(touch)的事件时,就要向Android框架注册View.OnClickListener。另外还有BitMap等同于J2ME的Image。   

关注 63943

Jason 关注了标签 · 7月14日

python

Python(发音:英[ˈpaɪθən],美[ˈpaɪθɑ:n]),是一种面向对象、直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法非常简捷和清晰,与其它大多数程序设计语言不一样,它使用缩进来定义语句。

Python支持命令式程序设计、面向对象程序设计、函数式编程、面向切面编程、泛型编程多种编程范式。与Scheme、Ruby、Perl、Tcl等动态语言一样,Python具备垃圾回收功能,能够自动管理存储器使用。它经常被当作脚本语言用于处理系统管理任务和网络程序编写,然而它也非常适合完成各种高级任务。Python虚拟机本身几乎可以在所有的作业系统中运行。使用一些诸如py2exe、PyPy、PyInstaller之类的工具可以将Python源代码转换成可以脱离Python解释器运行的程序。

Python的主要参考实现是CPython,它是一个由社区驱动的自由软件。目前由Python软件基金会管理。基于这种语言的相关技术正在飞快的发展,用户数量快速扩大,相关的资源非常多。

关注 102748

Jason 关注了标签 · 7月14日

算法

算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。

关注 13095

Jason 关注了标签 · 7月14日

spring

Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。

关注 27575

Jason 关注了标签 · 7月14日

关注 65931

Jason 关注了标签 · 7月14日

vue.js

Reactive Components for Modern Web Interfaces.

Vue.js 是一个用于创建 web 交互界面的。其特点是

  • 简洁 HTML 模板 + JSON 数据,再创建一个 Vue 实例,就这么简单。
  • 数据驱动 自动追踪依赖的模板表达式和计算属性。
  • 组件化 用解耦、可复用的组件来构造界面。
  • 轻量 ~24kb min+gzip,无依赖。
  • 快速 精确有效的异步批量 DOM 更新。
  • 模块友好 通过 NPM 或 Bower 安装,无缝融入你的工作流。

官网:https://vuejs.org
GitHub:https://github.com/vuejs/vue

关注 99917

Jason 关注了标签 · 7月14日

关注 84998

Jason 关注了标签 · 7月14日

程序员

一种近几十年来出现的新物种,是工业革命的产物。英文(Programmer Monkey)是一种非常特殊的、可以从事程序开发、维护的动物。一般分为程序设计猿和程序编码猿,但两者的界限并不非常清楚,都可以进行开发、维护工作,特别是在中国,而且最重要的一点,二者都是一种非常悲剧的存在。

国外的程序员节

国外的程序员节,(英语:Programmer Day,俄语:День программи́ста)是一个俄罗斯官方节日,日期是每年的第 256(0x100) 天,也就是平年的 9 月 13 日和闰年的 9 月 12 日,选择 256 是因为它是 2 的 8 次方,比 365 少的 2 的最大幂。

1024程序员节,中国程序员节

1024是2的十次方,二进制计数的基本计量单位之一。程序员(英文Programmer)是从事程序开发、维护的专业人员。程序员就像是一个个1024,以最低调、踏实、核心的功能模块搭建起这个科技世界。1GB=1024M,而1GB与1级谐音,也有一级棒的意思。

从2012年,SegmentFault 创办开始我们就从网络上引导社区的开发者,发展成中国程序员的节日 :) 计划以后每年10月24日定义为程序员节。以一个节日的形式,向通过Coding 改变世界,也以实际行动在浮躁的世界里,固执地坚持自己对于知识、技术和创新追求的程序员们表示致敬。并于之后的最为临近的周末为程序员们举行了一个盛大的狂欢派对。

2015的10月24日,我们SegmentFault 也在5个城市同时举办黑客马拉松这个特殊的形式,聚集开发者开一个编程大爬梯。

特别推荐:

【SF 黑客马拉松】:http://segmentfault.com/hacka...
【1024程序员闯关秀】小游戏,欢迎来挑战 http://segmentfault.com/game/

  • SF 开发者交流群:206236214
  • 黑客马拉松交流群:280915731
  • 开源硬件交流群:372308136
  • Android 开发者交流群:207895295
  • iOS 开发者交流群:372279630
  • 前端开发者群:174851511

欢迎开发者加入~

交流群信息


程序员相关问题集锦:

  1. 《程序员如何选择自己的第二语言》
  2. 《如何成为一名专业的程序员?》
  3. 《如何用各种编程语言书写hello world》
  4. 《程序员们最常说的谎话是什么?》
  5. 《怎么加入一个开源项目?》
  6. 《是要精于单挑,还是要善于合作?》
  7. 《来秀一下你屎一般的代码...》
  8. 《如何区分 IT 青年的“普通/文艺/二逼”属性?》
  9. 程序员必读书籍有哪些?
  10. 你经常访问的技术社区或者技术博客(IT类)有哪些?
  11. 如何一行代码弄崩你的程序?我先来一发
  12. 编程基础指的是什么?
  13. 后端零起步:学哪一种比较好?
  14. 大家都用什么键盘写代码的?

爱因斯坦

程序猿崛起

关注 113939

Jason 关注了标签 · 7月14日

javascript

JavaScript 是一门弱类型的动态脚本语言,支持多种编程范式,包括面向对象和函数式编程,被广泛用于 Web 开发。

一般来说,完整的JavaScript包括以下几个部分:

  • ECMAScript,描述了该语言的语法和基本对象
  • 文档对象模型(DOM),描述处理网页内容的方法和接口
  • 浏览器对象模型(BOM),描述与浏览器进行交互的方法和接口

它的基本特点如下:

  • 是一种解释性脚本语言(代码不进行预编译)。
  • 主要用来向HTML页面添加交互行为。
  • 可以直接嵌入HTML页面,但写成单独的js文件有利于结构和行为的分离。

JavaScript常用来完成以下任务:

  • 嵌入动态文本于HTML页面
  • 对浏览器事件作出响应
  • 读写HTML元素
  • 在数据被提交到服务器之前验证数据
  • 检测访客的浏览器信息

《 Javascript 优点在整个语言中占多大比例?

关注 137826

Jason 关注了标签 · 7月14日

java

Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaSE, JavaEE, JavaME)的总称。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

Java编程语言的风格十分接近 C++ 语言。继承了 C++ 语言面向对象技术的核心,Java舍弃了 C++ 语言中容易引起错误的指針,改以引用取代,同时卸载原 C++ 与原来运算符重载,也卸载多重继承特性,改用接口取代,增加垃圾回收器功能。在 Java SE 1.5 版本中引入了泛型编程、类型安全的枚举、不定长参数和自动装/拆箱特性。太阳微系统对 Java 语言的解释是:“Java编程语言是个简单、面向对象、分布式、解释性、健壮、安全与系统无关、可移植、高性能、多线程和动态的语言”。

版本历史

重要版本号版本代号发布日期
JDK 1.01996 年 1 月 23 日
JDK 1.11997 年 2 月 19 日
J2SE 1.2Playground1998 年 12 月 8 日
J2SE 1.3Kestrel2000 年 5 月 8 日
J2SE 1.4Merlin2002 年 2 月 6 日
J2SE 5.0 (1.5.0)Tiger2004 年 9 月 30 日
Java SE 6Mustang2006 年 11 月 11 日
Java SE 7Dolphin2011 年 7 月 28 日
Java SE 8JSR 3372014 年 3 月 18 日
最新发布的稳定版本:
Java Standard Edition 8 Update 11 (1.8.0_11) - (July 15, 2014)
Java Standard Edition 7 Update 65 (1.7.0_65) - (July 15, 2014)

更详细的版本更新查看 J2SE Code NamesJava version history 维基页面

新手帮助

不知道如何开始写你的第一个 Java 程序?查看 Oracle 的 Java 上手文档

在你遇到问题提问之前,可以先在站内搜索一下关键词,看是否已经存在你想提问的内容。

命名规范

Java 程序应遵循以下的 命名规则,以增加可读性,同时降低偶然误差的概率。遵循这些命名规范,可以让别人更容易理解你的代码。

  • 类型名(类,接口,枚举等)应以大写字母开始,同时大写化后续每个单词的首字母。例如:StringThreadLocaland NullPointerException。这就是著名的帕斯卡命名法。
  • 方法名 应该是驼峰式,即以小写字母开头,同时大写化后续每个单词的首字母。例如:indexOfprintStackTraceinterrupt
  • 字段名 同样是驼峰式,和方法名一样。
  • 常量表达式的名称static final 不可变对象)应该全大写,同时用下划线分隔每个单词。例如:YELLOWDO_NOTHING_ON_CLOSE。这个规范也适用于一个枚举类的值。然而,static final 引用的非不可变对象应该是驼峰式。

Hello World

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

编译并调用:

javac -d . HelloWorld.java
java -cp . HelloWorld

Java 的源代码会被编译成可被 Java 命令执行的中间形式(用于 Java 虚拟机的字节代码指令)。

可用的 IDE

学习资源

常见的问题

下面是一些 SegmentFault 上在 Java 方面经常被人问到的问题:

(待补充)

关注 107911