回车, 换行符, CRLF, LF

回车, 换行符, CRLF, LF 换行 line feed, LF, newline, \n, ascii 码: 0x0A 回车, carriage return, CR, return, \r, ascii 码: 0x0D CRLF, Carriage return & line feed unix: 换行, \n windows: 换行(回车+换行), \r\n, Unix/Mac 下打开会显示成 ^M macos: Line Feed, LF https://blog.csdn.net/wjcquking/article/details/6634504 回车符号和换行符号产生背景 关于"回车" (carriage return) 和"换行" (line feed) 这两个概念的来历和区别。 在计算机还没有出现之前,有一种叫做电传打字机 (Teletype Model 33) 的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。 于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做"回车",告诉打字机把打印头定位在左边界;另一个叫做"换行",告诉打字机把纸向下移一行。 这就是"换行"和"回车"的来历,从它们的英语名字上也可以看出一二。 后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。 Unix系统里,每行结尾只有"<换行>",即"\n";Windows系统里面,每行结尾是" <回车><换行>",即"\r\n";Mac系统里,每行结尾是"<回车>"。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在 Unix/Mac 下打开的话,在每行的结尾可能会多出一个^M符号 windows创建的文件是 \n\r 结束的, 而linux,mac这种unix类系统是\n结束的。 所以unix的文本到windows会出现换行丢失 (ultraedit这种软件可以正确识别) ; 而反过来就会出现^M的符号了 ...

2012-09-27 · 1 min · 138 words · -

java读写文件

java读写文件 Java获取当前路径 利用System.getProperty()函数获取当前路径: System.out.println(System.getProperty(“user.dir”));//user.dir指定了当前的路径 使用File提供的函数获取当前路径: File directory = new File("");//设定为当前文件夹 try{ System.out.println(directory.getCanonicalPath());//获取标准的路径 System.out.println(directory.getAbsolutePath());//获取绝对路径 }catch(Exceptin e){} File.getCanonicalPath()和File.getAbsolutePath()大约只是对于new File(".")和new File("..")两种路径有所区别。 对于getCanonicalPath()函数,".“就表示当前的文件夹,而”..“则表示当前文件夹的上一级文件夹 对于getAbsolutePath()函数,则不管”."、"..",返回当前的路径加上你在new File()时设定的路径 至于getPath()函数,得到的只是你在new File()时设定的路径 http://sharewind.iteye.com/blog/227538 ```java //=============================写文件 package fier; import java.io.*; public class write { public static void main(String[] args) { write("E:\123.txt", "hello"); } public static void write(String path, String content) { String s = new String(); String s1 = new String(); try { File f = new File(path); if (f.exists()) { System.out.println("文件存在"); } else { System.out.println("文件不存在,正在创建..."); if (f.createNewFile()) { System.out.println("文件创建成功!"); } else { System.out.println("文件创建失败!"); } } BufferedReader input = new BufferedReader(new FileReader(f)); while ((s = input.readLine()) != null) { s1 += s + "n"; } System.out.println("文件内容: " + s1); input.close(); s1 += content; BufferedWriter output = new BufferedWriter(new FileWriter(f)); output.write(s1); output.close(); } catch (Exception e) { e.printStackTrace(); } } } //=============================读文件 package fier; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; public class sdsd { /** * @param args */ public static void main(String[] args) { read("E:\123.txt"); } public static void read(String file) { String s = null; StringBuffer sb = new StringBuffer(); File f = new File(file); if (f.exists()) { System.out.println("文件存在"); try { BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(f))); while ((s = br.readLine()) != null) { sb.append(s); } System.out.println(sb); } catch (Exception e) { e.printStackTrace(); } }else{ System.out.println("文件不存在!"); } } } ```

2012-09-27 · 2 min · 217 words · -

Java Http连接中 (HttpURLConnection) 中使用代理 (Proxy) 及其验证 (Authentication)

Java Http连接中 (HttpURLConnection) 中使用代理 (Proxy) 及其验证 (Authentication) System.setProperty("http.proxyHost", "www.proxy.com"); System.setProperty("http.proxyPort", "8080"); 使用Java的HttpURLConnection类可以实现HttpClient的功能,而不需要依赖任何其他类库。所有有时候大家就直接使用它来完成一些简单 (或复杂) 的功能。但是你活在伟大的{print G.F.W}后面,如果你需要访问的网站被墙了,那HttpURLConnection类就会出现连接超时的错误。这时候就需要给他设置代理 (Proxy) 了。 设置代理 (Proxy) 可以有两种方式: 1、通过设置系统属性(System.setPropery(String key, String value)的方式 首先你可以在这里看到Java支持的属性。我们可以使用其中的http.proxyHost,http.proxyPort这两个属性。顾名思义,就是分别设置代理服务器地址和代理端口。 [c language="-sharp"][/c] 替换上面的www.proxy.com为你的代理服务器地址或IP地址,以及相应的端口为真实端口,Http连接及可以工作了。需要注意的是如果你设置了这些属性,那么所有的Http请求都会通过代理服务器。这些属性是JVM级别的,设置了以后对所有的同类请求都有效。比如上面的是关于http的,还有关于ftp的等等。 如果你的代理服务器不需要验证,那到此就结束了。但一般都是需要验证的。但是你要是看了上面Java支持的属性列表,你就会发现那里面并没有期望中的 [c language="-sharp"][/c] http://blog.csdn.net/redhat456/article/details/6149774# http.proxyUserName=username http.proxyPassword=password 这两个属性。 这时就需要java.net.Authenticator类来完成一般的Http验证。但是java.net.Authenticator这个类却是个抽象类,我们要使用还需要实例化一下子自己的类。个人觉得这里很不方便。如下: ```java``` http://blog.csdn.net/redhat456/article/details/6149774# http://blog.csdn.net/redhat456/article/details/6149774# Authenticator.setDefault(new BasicAuthenticator(userName, password)); 这样就提供了基于Http Basic的验证,接着就可以顺畅的使用需要验证的代理了。 2、通过java.net.Proxy类。 这种方式是实例化一个Proxy类提供代理服务器的信息,如端口和地址。 ```java``` http://blog.csdn.net/redhat456/article/details/6149774# Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); URLConnection conn = url.openConnection(proxy); 使用代理的方式是在打开Http连接的时候同时传递一个Proxy参数。如果需要验证信息的话我们可以添加一个Http头参数来实现。 ```java``` http://blog.csdn.net/redhat456/article/details/6149774# ...

2012-09-27 · 1 min · 83 words · -

Java HTTP Helper

Java HTTP Helper import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthCache; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.BasicHttpEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.util.EntityUtils; public class HttpHelper { /*\* The Constant MAX_NUMBER_OF_CONNECTIONS. */ private static final int MAX_NUMBER_OF_CONNECTIONS = 200; /*\* The http client. */ private DefaultHttpClient httpClient; /*\* The http context. */ private BasicHttpContext httpContext; /*\* The target host. */ private HttpHost targetHost; /*\* The proxy host. */ private HttpHost proxyHost; /*\* The X509TrustManager. */ protected static X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; protected String execute(HttpRequest request) throws Exception { // logger.debug("executing request: " + request.getRequestLine()); // logger.debug("via proxy: " + getProxyHost()); // logger.debug("to target: " + getTargetHost()); StringBuffer resXML = new StringBuffer(); // Send request DefaultHttpClient httpClient = getHttpClient(); HttpResponse response = httpClient.execute(getTargetHost(), request, getHttpContext()); // Get response HttpEntity entity = response.getEntity(); StatusLine statusLine = response.getStatusLine(); // logger.debug("Response code: " + statusLine); int httpStatus = statusLine.getStatusCode(); if (httpStatus >= 200 && httpStatus <= 207) { // Get response XML InputStream is = entity.getContent(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line = null; while ((line = br.readLine()) != null) { resXML.append(line); } EntityUtils.consume(entity); } else { // Http access error throw new Exception(statusLine.toString()); } return resXML.toString(); } /** * Gets the target host. * * @return the target host */ protected HttpHost getTargetHost() { if (targetHost == null) { targetHost = new HttpHost(getTargetHost(), getTargetPort(), getProtocol()); } return targetHost; } /** * Gets the proxy host. * * @return the proxy host */ protected HttpHost getProxyHost() { if (proxyHost == null) { proxyHost = new HttpHost(getProxyHost(), getProxyPort()); } return proxyHost; } /** * Gets the http context. * * @return the http context */ @SuppressWarnings("static-access") protected BasicHttpContext getHttpContext() { if (httpContext == null) { // Create AuthCache instance AuthCache authCache = new BasicAuthCache(); // Generate BASIC scheme object and add it to the local BasicScheme basicAuth = new BasicScheme(); basicAuth.authenticate( new UsernamePasswordCredentials(getUserName(), getPassword()), "UTF-8", true); authCache.put(getTargetHost(), basicAuth); // Add AuthCache to the execution context httpContext = new BasicHttpContext(); httpContext.setAttribute(ClientContext.AUTH_CACHE, authCache); } return httpContext; } /** * Gets the http client. * * @return the http client * @throws NoSuchAlgorithmException the no such algorithm exception * @throws KeyManagementException the key management exception */ @SuppressWarnings("deprecation") protected DefaultHttpClient getHttpClient() throws NoSuchAlgorithmException, KeyManagementException { if (httpClient == null) { ThreadSafeClientConnManager cm =new ThreadSafeClientConnManager(); cm.setMaxTotal(MAX_NUMBER_OF_CONNECTIONS); httpClient = new DefaultHttpClient(cm); if (isProxyEnabled()) { httpClient.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, getProxyHost()); } String protocol = getProtocol(); if ("HTTPS".equalsIgnoreCase(protocol)) { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[] { tm }, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = httpClient.getConnectionManager(); SchemeRegistry sr = ccm.getSchemeRegistry(); sr.register(new Scheme(protocol, ssf, getTargetPort())); httpClient = new DefaultHttpClient(ccm, httpClient.getParams()); } httpClient.getCredentialsProvider().setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(getUserName(), getPassword())); } return httpClient; } /** * Shuts down this httpClient connection manager and releases allocated resources. * This includes closing all connections, whether they are currently * used or not. */ public void shutdown(){ if (httpClient == null) { httpClient.getConnectionManager().shutdown(); } } }

2012-09-26 · 3 min · 543 words · lcf

Java JAXB Unmarshaller

Java JAXB Unmarshaller import java.io.IOException; import java.io.InputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.springframework.context.support.GenericApplicationContext; import org.w3c.dom.Document; public class ConfigReader { public final <T>T read(Class<T> t, String filePath) throws ISException { InputStream input; try { input = new GenericApplicationContext() .getResource(filePath).getInputStream(); } catch (IOException e) { throw new ISException(e); } return read(input, t); } public final <T>T read(InputStream inputStream, Class<T> t) throws ISException { T root = null; synchronized (this) { try { if (inputStream != null) { final DocumentBuilderFactory dbf = DocumentBuilderFactory .newInstance(); dbf.setNamespaceAware(true); final JAXBContext context = JAXBContext.newInstance(t); final Unmarshaller unmarshaller = context .createUnmarshaller(); final DocumentBuilder documentBuilder = dbf .newDocumentBuilder(); final Document doc = documentBuilder.parse(inputStream); final JAXBElement<T> rootElement = unmarshaller.unmarshal( doc, t); root = rootElement.getValue(); } } catch (Exception e) { throw new ISException(e); } } return root; } }

2012-09-26 · 1 min · 135 words · lcf

Java ClassLoader

Java ClassLoader 不同的JVM的实现不同,本文所描述的内容均只限于Hotspot Jvm. 本文将会从JDK默认的提供的ClassLoader,双亲委托模型,如何自定义ClassLoader以及Java中打破双亲委托机制的场景四个方面入手去讨论和总结一下。 JDK默认ClassLoader JDK 默认提供了如下几种ClassLoader Bootstrp loader Bootstrp加载器是用C++语言写的,用来加载核心类库,如 java.lang.* 等.它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。 ExtClassLoader Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。 AppClassLoader Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。 综上所述,它们之间的关系可以通过下图形象的描述: 双亲委托模型 Java中ClassLoader的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤: 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。 每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader. 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。 说到这里大家可能会想,Java为什么要采用这样的委托机制?理解这个问题,我们引入另外一个关于Classloader的概念"命名空间", 它是指要确定某一个类,需要类的全限定名以及加载此类的ClassLoader来共同确定。也就是说即使两个类的全限定名是相同的,但是因为不同的 ClassLoader加载了此类,那么在JVM中它是不同的类。明白了命名空间以后,我们再来看看委托模型。采用了委托模型以后加大了不同的 ClassLoader的交互能力,比如上面说的,我们JDK本生提供的类库,比如hashmap,linkedlist等等,这些类由bootstrp 类加载器加载了以后,无论你程序中有多少个类加载器,那么这些类其实都是可以共享的,这样就避免了不同的类加载器加载了同样名字的不同类以后造成混乱。 如何自定义ClassLoader Java除了上面所说的默认提供的classloader以外,它还容许应用程序可以自定义classloader,那么要想自定义classloader我们需要通过继承java.lang.ClassLoader来实现,接下来我们就来看看再自定义Classloader的时候,我们需要注意的几个重要的方法: 1.loadClass 方法 loadClass method declare public Class loadClass(String name) throws ClassNotFoundException 上面是loadClass方法的原型声明,上面所说的双亲委托机制的实现其实就实在此方法中实现的。下面我们就来看看此方法的代码来看看它到底如何实现双亲委托的。 loadClass method implement public Class loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); ...

2012-09-26 · 8 min · 1507 words · lcf

Java Spring LDAP

Java Spring LDAP import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import javax.naming.Context; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.AbstractContextMapper; import org.springframework.ldap.core.support.DirContextSource; import org.springframework.ldap.support.LdapUtils; /** * This Service does user authentication with LDAP. It uses Spring to do the authentication * * A good chunk of the LDAP code is copy-paste from: * http://static.springsource.organization/spring-ldap/docs/1.3.x/reference/pdf/spring-ldap-reference.pdf * */ public class LDAPService { private Map<String, String> ldapPropertiesMap=new HashMap<String, String>(); // LDAP Settings public static final String LDAP_PROVIDER_URL = "ldap.provider.url"; public static final String LDAP_SECURITY_AUTHENTICATION = "ldap.security.authentication"; public static final String LDAP_SECURITY_PRINCIPAL = "ldap.security.principal"; public static final String LDAP_SECURITY_CREDENTIALS = "ldap.security.credentials"; public static final String LDAP_ADMIN_OU = "ldap.admin.ou"; public static final String LDAP_USER_OU = "ldap.user.ou"; public static final String LDAP_USER_FILTER = "ldap.user.filter"; private DirContextSource dirCtxSrc; private String userAuthority; private static final String ldapFactory = "com.sun.jndi.ldap.LdapCtxFactory"; private static final String DEFAULT_USER_FILTER = "uid"; public LDAPService() throws Exception { ldapPropertiesMap.put(LDAP_PROVIDER_URL, "ldap://10.0.1.1:10389/ou=system"); ldapPropertiesMap.put(LDAP_SECURITY_AUTHENTICATION, "simple"); ldapPropertiesMap.put(LDAP_SECURITY_PRINCIPAL, "uid=admin,ou=system"); ldapPropertiesMap.put(LDAP_SECURITY_CREDENTIALS, "secret"); ldapPropertiesMap.put(LDAP_ADMIN_OU, "ou=admins,ou=system"); ldapPropertiesMap.put(LDAP_USER_OU, "ou=users,ou=system"); ldapPropertiesMap.put(LDAP_USER_FILTER, "uid=?"); this.init(); } private void init() throws Exception { Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, ldapFactory); env.put(Context.PROVIDER_URL, ldapPropertiesMap.get(LDAP_PROVIDER_URL)); env.put(Context.SECURITY_AUTHENTICATION, ldapPropertiesMap.get(LDAP_SECURITY_AUTHENTICATION)); env.put(Context.SECURITY_PRINCIPAL, ldapPropertiesMap.get(LDAP_SECURITY_PRINCIPAL)); env.put(Context.SECURITY_CREDENTIALS, ldapPropertiesMap.get(LDAP_SECURITY_CREDENTIALS)); dirCtxSrc = new DirContextSource(); dirCtxSrc.setBaseEnvironmentProperties(env); dirCtxSrc.setUserDn(ldapPropertiesMap.get(LDAP_SECURITY_PRINCIPAL)); dirCtxSrc.setPassword(ldapPropertiesMap.get(LDAP_SECURITY_CREDENTIALS)); dirCtxSrc.setUrl(ldapPropertiesMap.get(LDAP_PROVIDER_URL)); dirCtxSrc.afterPropertiesSet(); } /** * Return user authority after authentication * * @return */ public String getUserAuthority() { return userAuthority; } public boolean validateLoginCredentials(String username, String password) throws Exception { boolean result = false; try { String userDn = getDnForUser(username); result = authenticate(userDn, password); if (result) { String adminGroup = ldapPropertiesMap.get(LDAP_ADMIN_OU); String userGroup = ldapPropertiesMap.get(LDAP_USER_OU); if (adminGroup != null && userDn.toLowerCase().endsWith( adminGroup.toLowerCase())) { this.userAuthority = adminGroup; } else if (userGroup != null && userDn.toLowerCase().endsWith( userGroup.toLowerCase())) { this.userAuthority = userGroup; } else { logger.info(username + " is a valid user, but is not in the either the UserOU or AdminOU"); throw new RuntimeException( "Could not locate Authority for User: '" + username + "' in LDAP"); } } return result; } catch (Exception ex) { logger.error("Exception during ldap authentication for User: '" + username + "'", ex); throw ex; } } /** * This will find the distinguished name given a uid * * @param uid * @return */ protected String getDnForUser(String uid) { LdapTemplate ldapTemplate = new LdapTemplate(dirCtxSrc); // Filter String userFilter = ldapPropertiesMap.get(LDAP_USER_FILTER); if (userFilter != null && !userFilter.isEmpty()) { userFilter = userFilter.replaceAll("\?", uid); } else { userFilter = "(" + DEFAULT_USER_FILTER + "=" + uid + ")"; } Logger.debug("LDAP USER Filter:" + userFilter); // Filter f = new EqualsFilter("uid", uid); List result = ldapTemplate.search(DistinguishedName.EMPTY_PATH, userFilter, new AbstractContextMapper() { protected Object doMapFromContext(DirContextOperations ctx) { return ctx.getNameInNamespace(); } }); if (result.size() != 1) { throw new RuntimeException("User-'" + uid + "' not found in LDAP or not unique"); } return (String) result.get(0); } /** * This try to create a Context using the supplied username and password * * @param userDn * @param password * @return */ protected boolean authenticate(String userDn, String password) throws Exception { DirContext ctx = null; Hashtable<String, String> env = new Hashtable<String, String>(); String providerUrl = (ldapPropertiesMap.get(LDAP_PROVIDER_URL)); String securityAuth = (ldapPropertiesMap .get(LDAP_SECURITY_AUTHENTICATION)); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, providerUrl); env.put(Context.SECURITY_AUTHENTICATION, securityAuth); env.put(Context.SECURITY_PRINCIPAL, userDn); env.put(Context.SECURITY_CREDENTIALS, password); try { ctx = new InitialDirContext(env); return true; } catch (Exception e) { // Context creation failed - authentication did not succeed logger.error("Login failed for userDn-'" + userDn + "'", e); throw e; } finally { // It is imperative that the created DirContext instance is always // closed LdapUtils.closeContext(ctx); } } public Map<String, String> getLdapPropertiesMap() { return ldapPropertiesMap; } public void setLdapPropertiesMap(Map<String, String> ldapPropertiesMap) { this.ldapPropertiesMap = ldapPropertiesMap; } }

2012-09-26 · 3 min · 581 words · lcf

Java Base64

Java Base64 // encode byte[] in; byte[] out=java.util.Base64.getEncoder().encode(in); System.out.println(new String(out)); // decode byte[] byteArray = Base64.getDecoder().decode(value.getBytes()); String base64Decode = new String(byteArray, StandardCharsets.UTF_8); System.out.println("base64 decode: " + base64Decode);

2012-09-26 · 1 min · 27 words · lcf

Java Singleton

Java Singleton public class ConnEnv { /** The instance. */ private static ConnEnv instance; private ConnEnv(){ } public static ConnEnv getInstance(){ if (instance == null) { createInstance(); } return instance; } /** Creates the instance. */ private static synchronized void createInstance() { if (instance==null) { try { instance = new ConnEnv(); } catch (Exception e) { // throw e; } } } }

2012-09-26 · 1 min · 63 words · lcf

hash, hashCode, 哈希, 散列

hash, hashCode, 哈希, 散列 Hash,一般直接音译成“哈希”,按真正含义译作“散列”比较合适。通过散列算法,把任意长度的输入,转换成固定长度的输出,输出就叫做散列值 (hashCode)。这种转换是一种压缩映射,也就是说,散列值所占用的空间通常远小于输入值所占用的空间,不同的输入可能会有相同的散列值。散列的目的,在于尽量分散数据的存储位置,使数据散列在不同的哈希桶(bucket)中。 hashCode:是一串固定长度的整型的数字,hashCode可以由hash函数生成。hash函数常用的算法有:直接取余法、乘法取整法、平方取中法等。由hashCode可以得到对象在hash表中的位置 hashMap、hashTable:都是基于hash表实现的,都通过单链表解决数据通途的问题。二者很类似,但是也有很明显的区别。 hashcode() 方法返回该对象的哈希码值。 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 以下情况不 是必需的: 如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。 (这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。) 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。 hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。 使用hashCode()和equals() hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。 重写默认的实现 如果你不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。 来看看这个例子,让我们创建一个简单的类Employee public class Employee { private Integer id; private String firstname; private String lastName; private String department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } } ...

2012-09-26 · 3 min · 569 words · -

类方法 静态方法

‘类方法 静态方法’ http://www.blogjava.net/magicblw/archive/2007/08/22/138610.html 对于实例方法,实例变量和类方法,类变量的区分的不是很清楚,谈一下个人现在理解程度: 1,类方法类变量是静态方法,静态方法和静态变量可以看作是全局的。 2,实例方法和是实例变量可以看作是由类new出来的对象,是局部的。 规则: 1,全局的可以调用全局的 2,局部的可以调用局部的 3,局部可以调用全局的 4,全局不可以直接调用局部的 5,实例方法和实例变量只可以由对象引用,而类方法和类变量可以由类名或对象引用。 以上是自己的一些看法,不对的地方请指教。

2012-09-26 · 1 min · 13 words · -

Java Hex To Byte & CRCChecker

‘Java Hex To Byte & CRCChecker’ public final String byte2Hex(final byte[] btSrc) throws Exception { final StringBuffer sbuffer = new StringBuffer(); if (btSrc != null) { for (int n = 0; n < btSrc.length; n++) { String stmp = "00" + Integer.toHexString(btSrc[n] & 0XFF); stmp = stmp.substring(stmp.length() - 2); sbuffer.append(stmp.toUpperCase()); } } return sbuffer.toString(); } /** * Hex2 byte. * * @param src * the src * @return the byte[] */ public final byte[] hex2Byte(final String src) throws Exception { byte[] btRtn = new byte[0]; String srcTmp = src; if (isHex(srcTmp)) { int len = srcTmp.length(); if (len % 2 != 0) { len++; srcTmp = "0" + srcTmp; } len = len / 2; btRtn = new byte[len]; for (int n = 0; n < len; n++) { final int index = 2 * n; final String strTemp = String.valueOf(srcTmp.charAt(index)) + String.valueOf(srcTmp.charAt(index + 1)); final byte btTemp = (byte) Integer.parseInt(strTemp, 16); btRtn[n] = btTemp; } } return btRtn; } /** * Checks if is hex. * * @param src * the src * @return true, if is hex */ public final boolean isHex(final String src) { boolean blRtn = false; if (src != null) { final String pattern = "([0-9a-fA-F])*"; blRtn = src.matches(pattern); } return blRtn; } CRCChecker.java ...

2012-09-26 · 2 min · 336 words · lcf

Java Console Input

Java Console Input import java.io.Console; public class ConsoleInput { public static void main(String[] args) { Console console = System.console(); String titleUser = "Please enter value"; String label = "[Encrypted value]:"; String titlePWD = "password:"; String description = "Please enter a Digit:: 0: Start encrypt words 1: Exit"; String flag ="0"; if(console!=null){ while("0".equals(flag.trim())) { String srcWord = console.readLine("[%s]:", titleUser); console.printf("%s", label + srcWord); console.flush(); System.out.println(); System.out.println(description); flag =console.readLine(); System.out.println(); char[] password = console.readPassword("[%s]", titlePWD); String strPassword = String.valueOf(password); java.util.Arrays.fill(password, '*'); console.printf("%s", titlePWD + String.valueOf(password)); console.flush(); System.out.println(); } } } }

2012-09-26 · 1 min · 89 words · lcf

refactor, 重构

refactor, 重构 重构是克服演进式设计中大杂烩问题的主力,通过在单独的类及方法级别上做一系列小步重构来完成。 在解决现实问题时,我们会将问题映射到脑海中的概念模型,在模型中解决问题,再将解决方案转换为实际的代码。上述问题在于我们解决了设计到代码之间的重构,但提炼出来的设计模型,并不具有实际的业务含义,这就导致在开发新需求时,其他同学并不能很自然地将业务问题映射到该设计模型。设计似乎变成了重构者的自娱自乐,代码继续腐败,重新重构……无休止的循环。

2012-09-26 · 1 min · 4 words · -

CNI

CNI 常见的 CNI 插件包括 Calico、flannel、Terway、Weave Net 以及 Contiv。 在对CNI插件们进行比较之前,我们可以先对网络中会见到的相关术语做一个整体的了解。不论是阅读本文,还是今后接触到其他和CNI有关的内容,了解一些常见术语总是非常有用的。 一些最常见的术语包括: 第2层网络: OSI (Open Systems Interconnections,开放系统互连)网络模型的“数据链路”层。第2层网络会处理网络上两个相邻节点之间的帧传递。第2层网络的一个值得注意的示例是以太网,其中MAC表示为子层。 第3层网络: OSI网络模型的“网络”层。第3层网络的主要关注点,是在第2层连接之上的主机之间路由数据包。IPv4、IPv6和ICMP是第3层网络协议的示例。 VXLAN:代表“虚拟可扩展LAN”。首先,VXLAN用于通过在UDP数据报中封装第2层以太网帧来帮助实现大型云部署。VXLAN虚拟化与VLAN类似,但提供更大的灵活性和功能 (VLAN仅限于4096个网络ID)。VXLAN是一种封装和覆盖协议,可在现有网络上运行。 Overlay网络:Overlay网络是建立在现有网络之上的虚拟逻辑网络。Overlay网络通常用于在现有网络之上提供有用的抽象,并分离和保护不同的逻辑网络。 封装:封装是指在附加层中封装网络数据包以提供其他上下文和信息的过程。在overlay网络中,封装被用于从虚拟网络转换到底层地址空间,从而能路由到不同的位置 (数据包可以被解封装,并继续到其目的地)。 网状网络:网状网络 (Mesh network)是指每个节点连接到许多其他节点以协作路由、并实现更大连接的网络。网状网络允许通过多个路径进行路由,从而提供更可靠的网络。网状网格的缺点是每个附加节点都会增加大量开销。 BGP:代表“边界网关协议”,用于管理边缘路由器之间数据包的路由方式。BGP通过考虑可用路径,路由规则和特定网络策略,帮助弄清楚如何将数据包从一个网络发送到另一个网络。BGP有时被用作CNI插件中的路由机制,而不是封装的覆盖网络。 CNI比较 Flannel 链接:https://github.com/coreos/fla… 由CoreOS开发的项目Flannel,可能是最直接和最受欢迎的CNI插件。它是容器编排系统中最成熟的网络结构示例之一,旨在实现更好的容器间和主机间网络。随着CNI概念的兴起,Flannel CNI插件算是早期的入门。 与其他方案相比,Flannel相对容易安装和配置。它被打包为单个二进制文件flanneld,许多常见的Kubernetes集群部署工具和许多Kubernetes发行版都可以默认安装 Flannel. Flannel可以使用Kubernetes集群的现有etcd集群来使用API存储其状态信息,因此不需要专用的数据存储。 Flannel配置第3层IPv4 overlay网络。它会创建一个大型内部网络,跨越集群中每个节点。在此overlay网络中,每个节点都有一个子网,用于在内部分配IP地址。在配置pod时,每个节点上的Docker桥接口都会为每个新容器分配一个地址。同一主机中的Pod可以使用Docker桥接进行通信,而不同主机上的pod会使用flanneld将其流量封装在UDP数据包中,以便路由到适当的目标。 Flannel有几种不同类型的后端可用于封装和路由。默认和推荐的方法是使用VXLAN,因为VXLAN性能更良好并且需要的手动干预更少。 总的来说,Flannel是大多数用户的不错选择。从管理角度来看,它提供了一个简单的网络模型,用户只需要一些基础知识,就可以设置适合大多数用例的环境。一般来说,在初期使用Flannel是一个稳妥安全的选择,直到你开始需要一些它无法提供的东西。 Calico 链接:https://github.com/projectcal… Calico是Kubernetes生态系统中另一种流行的网络选择。虽然Flannel被公认为是最简单的选择,但Calico以其性能、灵活性而闻名。Calico的功能更为全面,不仅提供主机和pod之间的网络连接,还涉及网络安全和管理。Calico CNI插件在CNI框架内封装了Calico的功能。 在满足系统要求的新配置的Kubernetes集群上,用户可以通过应用单个manifest文件快速部署Calico。如果您对Calico的可选网络策略功能感兴趣,可以向集群应用其他manifest,来启用这些功能。 尽管部署Calico所需的操作看起来相当简单,但它创建的网络环境同时具有简单和复杂的属性。与Flannel不同,Calico不使用overlay网络。相反,Calico配置第3层网络,该网络使用BGP路由协议在主机之间路由数据包。这意味着在主机之间移动时,不需要将数据包包装在额外的封装层中。BGP路由机制可以本地引导数据包,而无需额外在流量层中打包流量。 除了性能优势之外,在出现网络问题时,用户还可以用更常规的方法进行故障排除。虽然使用VXLAN等技术进行封装也是一个不错的解决方案,但该过程处理数据包的方式同场难以追踪。使用Calico,标准调试工具可以访问与简单环境中相同的信息,从而使更多开发人员和管理员更容易理解行为。 除了网络连接外,Calico还以其先进的网络功能而闻名。 网络策略是其最受追捧的功能之一。此外,Calico还可以与服务网格Istio集成,以便在服务网格层和网络基础架构层中解释和实施集群内工作负载的策略。这意味着用户可以配置强大的规则,描述pod应如何发送和接受流量,提高安全性并控制网络环境。 如果对你的环境而言,支持网络策略是非常重要的一点,而且你对其他性能和功能也有需求,那么Calico会是一个理想的选择。此外,如果您现在或未来有可能希望得到技术支持,那么Calico是提供商业支持的。一般来说,当您希望能够长期控制网络,而不是仅仅配置一次并忘记它时,Calico是一个很好的选择。

2012-09-26 · 1 min · 48 words · -

Java 数据对象 JDO,JDBC

Java 数据对象 JDO,JDBC http://developer.51cto.com/art/200510/6932.htm 介绍 Java 数据对象 (JDO) is 是一个存储Java对象的规范. 它已经被JCP组织定义成JSR12规范。JDO的第一个版本在2000年6月6日提交并在2002年4月30日正式发布1.0版本规范。规范的两个主要目的是提供数据处理和访问机制的API以及允许规范的实现作为应用服务器的一部分。 JDO 和 JDBC JDBC和JDO都是Java调用数据库的APIs。区别在于,他们访问数据存储的具体方法不同。JDBC提供了一种非常好的机制,它可以使应用程序代码与具体的数据库厂商和数据库位置无关。在大多数情况下数据存储使用的是关系数据库。但是当使用JDBC驱动运行在一个非关系数据库时就不是很好了。而对于JDO来说,运行在它之下的数据库就可以是一个关系数据库,面向对象的数据库或者其他完全不同的数据库。在JDO运行于关系数据库之上时,它可以通过JDBC很好的完成数据存储。而这一切对于开发人员来说都是透明的,所有实现都有JDO本身来完成。 JDBC不支持面向对象的数据库表示。JDBC数据库表示完全围绕关系数据库模型。这常常导致书写代码时在应用程序和数据库之间存在一个中间层。这个层的作用也许是分解Java对象。分解Java对象的过程会对使用的对象稍作改变,这样对象才能存储到关系数据库之中。同样的必须存在一种机制来把数据库中的记录转换成适当的Java对象。JDO与JDBC刚好相反,他的数据库标示完全面向对象。这种机制并不新奇,在一些面向对象数据库中已经使用了这种机制。 JDBC的查询语言一般都是用SQL。JDO的查询语言看起来与Java更接近。使用JDO无需再专门学习一种查询语言比如SQL。如果你会用Java那么你就能够使用JDO的查询语言。 执行 JDO规范定义了JDO实现的接口已经JDO实现的内容。SUN提供了一个JDO规范的参考实现,但是在2002年5月份发布的版本中它并没有完全实现规范中所要求的内容。还有众多的厂商提供的JDO实现可以使用。在下面的表格中列出了一部分。 厂商 产品 SolarMetric Kodo JDO PrismTech OpenFusion Java Data Objects Signsoft intelliBO Poet FastObjects API

2012-09-25 · 1 min · 34 words · -

Collection List Set Map 区别

Collection List Set Map 区别 这些都代表了Java中的集合,这里主要从其元素是否有序,是否可重复来进行区别记忆,以便恰当地使用,当然还存在同步方面的差异,见上一篇相关文章。 有序否 允许元素重复否 Collection 否 是 List 是 是 Set 是 是 HashSet TreeSet 是 (用二叉树排序) Map AbstractMap 否 HashMap 是 (用二叉树排序) http://tb.blog.csdn.net/TrackBack.aspx?PostId=584112 List接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayList和LinkedList。你可以将任何东西放到一个List容器中,并在需要时从中取出。ArrayList从其命名中可以看出它是一种类似数组的形式进行存储,因此它的随机访问速度极快,而LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。在具体应用时可以根据需要自由选择。前面说的Iterator只能对容器进行向前遍历,而ListIterator则继承了Iterator的思想,并提供了对List进行双向遍历的方法。 Set接口也是Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。它的常用具体实现有HashSet和TreeSet类。HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法,它使用了前面说过的哈希码的算法。而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。有时多个类具有相同的排序算法,那就不需要在每分别重复定义相同的排序算法,只要实现Comparator接口即可。集合框架中还有两个很实用的公用类: Collections和Arrays。Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。 Map是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求。你可以将任意多个键都映射到一个值对象上,这不会发生任何问题 (不过对你的使用却可能会造成不便,你不知道你得到的到底是那一个键所对应的值对象) 。Map有两种比较常用的实现: HashMap和TreeMap。HashMap也用到了哈希码的算法,以便快速查找一个键,TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。 http://zhidao.baidu.com/question/16113509.html

2012-09-25 · 1 min · 39 words · lcf

Java 流, Stream

Java 流, Stream IO流的分类: 根据流的数据对象来分: 高端流: 所有的内存中的流都是高端流,比如: InputStreamReader 低端流: 所有的外界设备中的流都是低端流,比如InputStream,OutputStream 如何区分: 所有的流对象的后缀中包含Reader或者Writer的都是高端流,反之,则基本上为低端流,不过也有例外,比如PrintStream就是高端流 根据数据的流向来分: 输出流: 是用来写数据的,是由程序 (内存) ->外界设备 输入流: 是用来读数据的,是由外界设备->程序 (内存) 如何区分: 一般来说输入流带有Input,输出流带有Output 根据流数据的格式来分: 字节流: 处理声音或者图片等二进制的数据的流,比如InputStream 字符流: 处理文本数据 (如txt文件) 的流,比如InputStreamReader 如何区分: 可用高低端流来区分,所有的低端流都是字节流,所有的高端流都是字符流 根据流数据的包装过程来分: 原始流: 在实例化流的对象的过程中,不需要传入另外一个流作为自己构造方法的参数的流,称之为原始流。 包装流: 在实例化流的对象的过程中,需要传入另外一个流作为自己构造方法发参数的流,称之为包装流。 如何区分: 所以的低端流都是原始流,所以的高端流都是包装流 流是 Java 中最重要的基本概念之一。文件读写、网络收发、进程通信,几乎所有需要输入输出的地方,都要用到流。 流是做什么用的呢?就是做输入输出用的。为什么输入输出要用"流"这种方式呢?因为程序输入输出的基本单位是字节,输入就是获取一串字节,输出就是发送一串字节。但是很多情况下,程序不可能接收所有的字节之后再进行处理,而是接收一点处理一点。比方你下载魔兽世界,不可能全部下载到内存里再保存到硬盘上,而是下载一点就保存一点。这时,流这种方式就非常适合。 在 Java 中,每个流都是一个对象。流分为两种: 输入流(InputStream)和输出流(OutputStream)。对于输入流,你只要从流当中不停地把字节取出来就是了;而对于输出流,你只要把准备好的字节串传给它就行。 __Java 程序 | | 外部系统 –|–(输入流)–> 处理逻辑 –(输出流)—|–> 外部系统 |_________________________________| 流对象是怎么获得的呢?不同的外部系统,获取流的方式也不同。例如,文件读写就要创建 FileInputStream/FileOutputStream 对象,而网络通信是通过 Socket 对象来获取输入输出流的。一般来说,如果一个类有 getInputStream() 或 getOutputStream() 这样的方法,就表明它是通过流对象来进行输入输出的。 InputStream 是输入流,下面是一个通过 InputStream 读取文件的例子: import java.io.File; ...

2012-09-25 · 3 min · 635 words · lcf

stop() 和 suspend() 方法为何不推荐使用?

stop() 和 suspend() 方法为何不推荐使用? http://blog.csdn.net/yakihappy/article/details/3979912 反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行: synchronized void { x = 3; y = 4;}由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop方法,以后我们再也不要说"停止线程"了。而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果 很难检查出真正的问题所在。 suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此 时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就 会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

2012-09-25 · 1 min · 23 words · lcf

corba, ejb, webservice, rest分布式 区别

corba, ejb, webservice, rest分布式 区别 Corba,还是EJB,都有一些共同点: 通过专有的网络协议通讯 不能跨平台调用 通过分布式对象调用来实现分布式架构,换句话来说就是,分布式架构是绑定在面向对象的机制上的 分布式对象架构的缺陷在EJB2时代被充分暴露了出来 web services有一些明显不同于Corba和EJB分布式对象架构的特征: 通过标准SOAP协议通讯,一般走HTTP通道 能够跨平台调用 通讯格式是xml文本,而不是二进制数据格式 通过RPC机制来实现分布式调用,而不是通过面向对象机制实现分布式调用 REST也是一种分布式系统的架构风格,那么REST和上面这些分布式架构有哪些明显的区别呢? REST走的是HTTP协议,并且充分利用或者说极端依赖HTTP协议 Corba和EJB是采用专有的二进制协议,SOAP可以但不依赖HTTP,并且仅仅使用HTTP POST。 REST是基于HTTP抽象资源的分布式调用,换句话来说,就是分布式调用是绑定在资源的操作上面的。 分布式架构 协议 调用方式 Corba架构 专有二进制协议 对象的CRUD操作 EJB架构 专有二进制协议 对象的CRUD操作 Web Services SOAP协议 RPC方式 REST HTTP协议 对资源的CRUD操作 REST最大的特点是什么呢?REST是为通过HTTP协议来进行分布式调用量身定造的架构 REST是专门为分布式调用设计的架构,在REST里面,分布式是通过对资源的操作来实现的,不是像EJB那样通过对象的方法调用来实现的。资源是一种抽象的概念,资源被映射到相应的一套URL规则上面了。所以资源只和URL相关,而与具体实现无关,因此REST具有更好的解藕性。

2012-09-25 · 1 min · 39 words · lcf