一、三者的对比

  1. HttpClient:代码复杂,还得操心资源回收等。代码很复杂,冗余代码多,不建议直接使用。
  2. RestTemplate: 是 Spring 提供的用于访问Rest服务的客户端, RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
  3. okhttp:OkHttp是一个高效的HTTP客户端,允许所有同一个主机地址的请求共享同一个socket连接;连接池减少请求延时;透明的GZIP压缩减少响应数据的大小;缓存响应内容,避免一些完全重复的请求

二、HttpClient的使用

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。

  • 使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。
  1. 创建HttpClient对象。
  2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
  3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
  4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
  5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
  6. 释放连接。无论执行方法是否成功,都必须释放连接
  • pom.xml
  <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency><!--json--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.0</version></dependency>
  • httpClient–Get
public class HttpClientExample_get {public static void main(String[] args) throws IOException {HttpGet request = new HttpGet("https://httpbin.org/get");// add request headersrequest.addHeader("custom-key", "test");request.addHeader(HttpHeaders.USER_AGENT, "Googlebot");try (CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = httpClient.execute(request)) {// Get HttpResponse StatusSystem.out.println(response.getProtocolVersion());              // HTTP/1.1System.out.println(response.getStatusLine().getStatusCode());   // 200System.out.println(response.getStatusLine().getReasonPhrase()); // OKSystem.out.println(response.getStatusLine().toString());        // HTTP/1.1 200 OKHttpEntity entity = response.getEntity();if (entity != null) {// return it as a StringString result = EntityUtils.toString(entity);System.out.println(result);}}}
}
  • httpClient–Post
public class HttpClientExample_post {public static void main(String[] args) {try {String result = sendPOST("https://httpbin.org/post");System.out.println(result);} catch (IOException e) {e.printStackTrace();}}private static String sendPOST(String url) throws IOException {String result = "";HttpPost post = new HttpPost(url);// add request parameters or form parametersList<NameValuePair> urlParameters = new ArrayList<>();urlParameters.add(new BasicNameValuePair("username", "abc"));urlParameters.add(new BasicNameValuePair("password", "123"));urlParameters.add(new BasicNameValuePair("custom", "secret"));post.setEntity(new UrlEncodedFormEntity(urlParameters));try (CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = httpClient.execute(post)){result = EntityUtils.toString(response.getEntity());}return result;}
}
  • httpClient–PostWithJson
public class HttpClientExample_JsonPost {public static void main(String[] args) {try {String result = sendPOST("https://httpbin.org/post");System.out.println(result);} catch (IOException e) {e.printStackTrace();}}private static String sendPOST(String url) throws IOException {String result = "";HttpPost post = new HttpPost(url);String json = "{" +"\"name\":\"mkyong\"," +"\"notes\":\"hello\"" +"}";// send a JSON datapost.setEntity(new StringEntity(json));try (CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = httpClient.execute(post)) {result = EntityUtils.toString(response.getEntity());}return result;}
}
  • HttpClient认证
public class HttpClientAuthentication {public static void main(String[] args) throws IOException {HttpGet request = new HttpGet("http://localhost:8080/books");CredentialsProvider provider = new BasicCredentialsProvider();provider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials("user", "password"));try (CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();CloseableHttpResponse response = httpClient.execute(request)) {// 401 if wrong user/passwordSystem.out.println(response.getStatusLine().getStatusCode());HttpEntity entity = response.getEntity();if (entity != null) {// return it as a StringString result = EntityUtils.toString(entity);System.out.println(result);}}}
}

参考文章
参考文章

三、OKHttp的使用

  • 出现背景
    网络访问的高效性要求,可以说是为高效而生

  • 解决思路

  • 提供了对 HTTP/2 和 SPDY 的支持,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接
  • 如果 HTTP/2 和 SPDY 不可用,OkHttp会使用连接池来复用连接以提高效率
  • 提供了对 GZIP 的默认支持来降低传输内容的大小
  • 提供了对 HTTP 响应的缓存机制,可以避免不必要的网络请求
  • 当网络出现问题时,OkHttp 会自动重试一个主机的多个 IP 地址
  • OkHttp3设计思路
  • Requests(请求): 每一个HTTP请求中都应该包含一个URL,一个GET或POST方法以及Header或其他参数,当然还可以含特定内容类型的数据流。
  • Responses(响应): 响应则包含一个回复代码(200代表成功,404代表未找到),Header和定制可选的body。
  • 代码
  1. pom.xml
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.1</version>
</dependency>
  1. 创建OkHttpClient实例

简单来说,通过OkHttpClient可以发送一个Http请求,并读取该Http请求的响应,它是一个生产Call的工厂。此外,受益于一个共享的响应缓存/线程池/复用的连接等因素,绝大多数应用使用一个OkHttpClient实例,便可以满足整个应用的Http请求。

三种创建实例的方法:

  • 创建一个默认配置OkHttpClient,可以使用默认的构造函数。
  • 通过new OkHttpClient.Builder()方法来一步一步配置一个OkHttpClient实例。
  • 如果要求使用现有的实例,可以通过newBuilder()方法来进行构造。
OkHttpClient client = new OkHttpClient();
OkHttpClient clientWith30sTimeout = client.Builder().readTimeout(30, TimeUnit.SECONDS).build();
OkHttpClient client  = client.newBuilder().build();

看一下OkHttpClient的源码,会发现缓存/代理等等需求,一应俱全的按照类封装到了Builder中。

Dispatcher dispatcher;          // 分发
Proxy proxy;                    // 代理
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>(); // 拦截器
final List<Interceptor> networkInterceptors = new ArrayList<>(); // 网络拦截器
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;    // 缓存
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;   // 代理证书
Authenticator authenticator;        // 证书
ConnectionPool connectionPool;
Dns dns;        // DNS
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
  1. okhttp–get
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {Request request = new Request.Builder().url(url).build();Response response = client.newCall(request).execute();return response.body().string();
}
  • Request

简单看一下Request类,可以发现它代表一个Http请求,需要注意的是Request一旦build()之后,便不可修改。主要通过new Request.Builder()来一步一步构造的。看一下Builder的代码。

public Builder() {this.method = "GET";this.headers = new Headers.Builder();
}
  • 默认是Get方法,

此外还创建了头信息。Headers类中是通过List<String> namesAndValues = new ArrayList<>(20),来存放头信息的,一开始我也很纳闷,头信息都是一对一对的为什么要用List,看一下源码发现,在存取的时候都是将索引+2或者-2。并且头信息可以存在多个相同的Key信息。

  • 发起请求

跟到newCall()方法中发现,又使用OkHttpClient实例和Request的实例,一起构造了一个RealCall的实例。RealCall类简单做了一个托管并通过Dispather类对请求进行分发和执行,实际开启线程发起请求的方法就在这个类中。随后又调用execute()方法,拿到了一个响应。这个execute()方法,实际上执行的就是RealCall中的execute()方法,最后调用了Dispatcher的execute()方法。

  • Response

Response代表一个Http的响应,这个类的实例不可修改。

一个简单的Get请求和说明就结束了

  1. okhttp–post
  • POST提交字符串
public static final MediaType JSON= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {RequestBody body = RequestBody.create(JSON, json);Request request = new Request.Builder().url(url).post(body).build();Response response = client.newCall(request).execute();return response.body().string();
}

MediaType用于描述Http请求和响应体的内容类型,也就是Content-Type。一次请求就是向目标服务器发送一串文本。什么样的文本?有下面结构的文本。

  • 例子
POST /meme.php/home/user/login HTTP/1.1
Host: 114.215.86.90
Cache-Control: no-cache
Postman-Token: bd243d6b-da03-902f-0a2c-8e9377f6f6ed
Content-Type: application/x-www-form-urlencodedtel=13637829200&password=123456

例如,MediaType.parse(“application/json; charset=utf-8”);这个就带表请求体的类型为JSON格式的。定义好数据类型,还要将其变为请求体,最后通过post()方法,随请求一并发出。

  • POST提交键值对

OkHttp也可以通过POST方式把键值对数据传送到服务器

OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {RequestBody formBody = new FormBody.Builder().add("platform", "android").add("name", "bug").add("subject", "XXXXXXXXXXXXXXX").build();Request request = new Request.Builder().url(url).post(formBody).build();Response response = client.newCall(request).execute();if (response.isSuccessful()) {return response.body().string();} else {throw new IOException("Unexpected code " + response);}
}
  1. HTTP头部的设置和读取

HTTP 头的数据结构是 Map<String, List<String>>类型。也就是说,对于每个 HTTP 头,可能有多个值。但是大部分 HTTP 头都只有一个值,只有少部分 HTTP 头允许多个值。至于name的取值说明,可以查看这个请求头大全。

OkHttp的处理方式是:

  • 使用header(name,value)来设置HTTP头的唯一值,如果请求中已经存在响应的信息那么直接替换掉。
  • 使用addHeader(name,value)来补充新值,如果请求头中已经存在name的name-value,那么还会继续添加,请求头中便会存在多个name相同而value不同的“键值对”。
  • 使用header(name)读取唯一值或多个值的最后一个值
  • 使用headers(name)获取所有值
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://github.com").header("User-Agent", "My super agent").addHeader("Accept", "text/html").build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {throw new IOException("服务器端错误: " + response);
}
System.out.println(response.header("Server"));
System.out.println(response.headers("Set-Cookie"));

参考文章

四、RestTemplate的使用

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。

  • pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 初始化(3种)
RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());

当然如果想使用OkHttp的话也得引入相应的jar包

  • 默认配置

默认情况下RestTemplate自动帮我们注册了一组HttpMessageConverter用来处理一些不同的contentType的请求。如果现有的转换器不能满足你的需求,你还可以实现org.springframework.http.converter.HttpMessageConverter接口自己写一个。详情参考官方api。其他相关的配置,也可以在官方文档中查看。当然,对于一个请求来说,超期时间,请求连接时间等都是必不可少的参数,为了更好的适应业务需求,所以可以自己修改restTemplate的配置

  • RestTemplate使用总结
  • RestTemplate提供了六种常用的HTTP方法实现远程服务调用,RestTemplate的方法名遵循一定的命名规范,第一部分表示用哪种HTTP方法调用(get,post),第二部分表示返回类型。

  • getForObject – 发送GET请求,将HTTP response转换成一个指定的object对象

  • postForEntity –发送POST请求,将给定的对象封装到HTTP请求体,返回类型是一个HttpEntity对象(包含响应数据和响应头)

  • 每个HTTP方法对应的RestTemplate方法都有3种。其中2种的url参数为字符串,URI参数变量分别是Object数组和Map,第3种使用URI类型作为参数

  • exchange 和execute方法比上面列出的其它方法(如getForObject、postForEntity等)使用范围更广,允许调用者指定HTTP请求的方法(GET、POST、PUT等),并且可以支持像HTTP PATCH(部分更新)

  • GET 请求

做好了准备工作,先来看使用 RestTemplate 发送 GET 请求。在 RestTemplate 中,和 GET 请求相关的方法有如下几个:

  • 代码
public class TemplateGet {public static void main(String[] args) throws UnsupportedEncodingException {//生成一个设置了连接超时时间、请求超时时间、异常重试次数3次RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build();HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(3, false));HttpClient httpClient = builder.build();ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(requestFactory);//三种方式请求String forObject = restTemplate.getForObject("https://httpbin.org/get", String.class);ResponseEntity<String> forEntity = restTemplate.getForEntity("https://httpbin.org/get", String.class);ResponseEntity<String> responseEntity = restTemplate.exchange("https://httpbin.org/get", HttpMethod.GET, null, String.class);//遍历响应头System.out.println("响应头--start");forEntity.getHeaders().entrySet().forEach(System.out::println);System.out.println("响应头--end");assert forObject != null;byte[] bytes = forObject.getBytes(StandardCharsets.UTF_8);String res = new String(bytes, StandardCharsets.UTF_8);System.out.println(res);System.out.println(forEntity.getBody());System.out.println(responseEntity.getBody());}
}
  • post请求
  1. json形式
public class TemplatePostJson {public static void main(String[] args) {//生成一个设置了连接超时时间、请求超时时间、异常重试次数3次RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build();HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(3, false));HttpClient httpClient = builder.build();ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(requestFactory);String url = "https://httpbin.org/post";String json = "{" +"\"name\":\"mkyong\"," +"\"notes\":\"hello\"" +"}";HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);//Content-Typeheaders.set("CustomHeader", "myCustom"); //Other headersHttpEntity<String> httpEntity = new HttpEntity<>(json, headers);//三种方式请求ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);String s = restTemplate.postForObject(url, httpEntity, String.class);ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, httpEntity, String.class);assert s != null;byte[] bytes = s.getBytes(StandardCharsets.UTF_8);String body=new String(bytes,StandardCharsets.UTF_8);System.out.println(body);System.out.println(responseEntity.getBody());System.out.println("响应头--start");stringResponseEntity.getHeaders().entrySet().forEach(System.out::println);System.out.println("响应头--end");}
}
  1. map(表单)形式
public class TemplatePostMap {public static void main(String[] args) {//生成一个设置了连接超时时间、请求超时时间、异常重试次数3次RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build();HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(3, false));HttpClient httpClient = builder.build();ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(requestFactory);String url = "https://httpbin.org/post";MultiValueMap<Object, Object> map = new LinkedMultiValueMap<>();map.add("aa", "aa");map.add("bb", "bb");map.add("cc", "cc");HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);//Content-Typeheaders.set("CustomHeader", "myCustom"); //Other headersHttpEntity<MultiValueMap<Object, Object>> httpEntity = new HttpEntity<>(map, headers);//三种方式请求ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);String s = restTemplate.postForObject(url, httpEntity, String.class);ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, httpEntity, String.class);assert s != null;byte[] bytes = s.getBytes(StandardCharsets.UTF_8);String body=new String(bytes,StandardCharsets.UTF_8);System.out.println(body);System.out.println(responseEntity.getBody());System.out.println("响应头--start");stringResponseEntity.getHeaders().entrySet().forEach(System.out::println);System.out.println("响应头--end");}
}

其实,如果参数是一个 MultiValueMap 的实例,则以 key/value 的形式发送,如果是一个普通对象,则会被转成 json 发送。

  • postForLocation

postForLocation 方法的返回值是一个 URL 对象,因为 POST 请求一般用来添加数据,有的时候需要将刚刚添加成功的数据的 URL 返回来,此时就可以使用这个方法,一个常见的使用场景如用户注册功能,用户注册成功之后,可能就自动跳转到登录页面了,此时就可以使用该方法。

  • PUT 请求

只要将 GET 请求和 POST 请求搞定了,接下来 PUT 请求就会容易很多了,PUT 请求本身方法也比较少,只有三个,如下:

这三个重载的方法其参数其实和 POST 是一样的,可以用 key/value 的形式传参,也可以用 JSON 的形式传参,无论哪种方式,都是没有返回值的

  • DELETE 请求

和 PUT 请求一样,DELETE 请求也是比较简单的,只有三个方法,如下:


不同于 POST 和 PUT ,DELETE 请求的参数只能在地址栏传送,可以是直接放在路径中,也可以用 key/value 的形式传递,当然,这里也是没有返回值的。

public class TemplateDeleteJson {public static void main(String[] args) {//生成一个设置了连接超时时间、请求超时时间、异常重试次数3次RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build();HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(3, false));HttpClient httpClient = builder.build();ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(requestFactory);String url = "https://httpbin.org/delete";String json = "{" +"\"name\":\"mkyong\"," +"\"notes\":\"hello\"" +"}";HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);//Content-Typeheaders.set("CustomHeader", "myCustom"); //Other headersHttpEntity<String> httpEntity = new HttpEntity<>(json, headers);restTemplate.delete(url,httpEntity);}
}

参考文章
参考文章
参考文章

HttpClient、OKhttp、RestTemplate对比相关推荐

  1. webClient和restTemplate对比

    通过一个小案例 将webClient 和 restTemplate 做一个简单的比较. 详细的WebFlux/WebClient 和 Spring MVC/RestTemplate 性能对比请参考:h ...

  2. 【REST】基于RESTful服务端的客户端实现(HttpClient、RestTemplate、HttpURLConnection)

    最近一直在做针对webservice的接口协议对接,总结了一下基于restful服务端的客户端的实现方式,有以下三种: HTTPClient RestTemplate HttpURLConnectio ...

  3. HttpURLConnection与 HttpClient 区别/性能测试对比

    ttpClient是个开源框架,封装了访问http的请求头,参数,内容体,响应等等, HttpURLConnection是java的标准类,什么都没封装,用起来太原始,不方便 HttpClient实际 ...

  4. Java常用http请求库

    文章目录 常用http请求库 1. HttpClient 使用方法 使用示例 2. OKhttp 使用方法 使用示例 3. Retrofit 相关注解 使用方法 使用示例 4. RestTemplat ...

  5. 我终于决定要放弃 okhttp、httpClient,选择了这个牛逼的神仙工具!贼爽!

    点击上方蓝色"方志朋",选择"设为星标"回复"666"获取独家整理的学习资料! 在SpringBoot项目直接使用okhttp.httpCl ...

  6. 我终于决定要放弃 okhttp、httpClient,选择了这个牛逼的神仙工具!贼爽

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 在SpringBoot项目直接使用okhttp.httpC ...

  7. 扔掉okhttp、httpClient,来试试这款轻量级HTTP客户端神器?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者: 伍陆七 juejin.cn/post/689848 ...

  8. spring resttemplate 中文参数_SpringBoot使用RestTemplate访问第三方接口

    养成习惯,先赞后看!!! 前言 相信大家都知道如何在自己的项目里面调用自己的接口,只需要调用自己项目里面接口特定的URL地址就行了,但是如果是调用其他项目的接口呢,这时候如果是直接调用的话,很明显我们 ...

  9. 框架--NoHttp和OkHttp哪个好用,Volley和NoHttp哪个好用?

    NoHttp和OkHttp哪个好用,Volley和NoHttp哪个好用? NoHttp 源码及Demo托管在Github欢迎大家Star: https://github.com/Y0LANDA/NoH ...

最新文章

  1. STM32单片机外部中断配置讲解
  2. c实现三角形角度大于一个值_初中数学|高分必备!数学三角形相关知识点梳理汇总...
  3. PHP 8 确认支持 JIT
  4. 【小练习02】CSS--网易产品
  5. Git之深入解析48个经典操作场景的分析和处理,专治不会合并代码
  6. 【CodeForces - 527C】Glass Carving(线段树或者SBT或者set)
  7. 通过可视化来了解你的Spark应用程序
  8. 怎么入门Java?java拦截器怎么配置
  9. 艾伟:自己实现memcached客户端库
  10. python多重继承super父类参数_Python super()函数使用及多重继承
  11. 慎用"加速"一词,可以使用"早日"代替
  12. 兰州大学计算机英语分数线,兰州大学09MBA分数线A线140综合72英语42
  13. html5 模拟scrollview,horizontalScrollView
  14. 查看dll文件的两种办法
  15. 我37岁,从互联网大厂跳槽到国企后,发现没有一劳永逸的工作。。。
  16. JAVA前端————HTML—W3C—基本标签—超链接锚链接
  17. getElementById(‘divid‘).innerHTML赋值【js基础】
  18. c语言做搬山游戏,C语言实现搬山小游戏,适合新手的项目实战,超易上手!
  19. 安卓多媒体开发!Android高级工程师面试实战,系列篇
  20. js实现粒子特效,particles.js的使用

热门文章

  1. 数据分析方法论2——流量分析
  2. 【转】ssh服务器启动和客户端常用操作
  3. JavaScript学习笔记:你必须要懂的原生JS(一)
  4. JSON 常量详情参考 (内含对中文不转义的参数)
  5. c# 多线程中lock用法的经典实例
  6. 分享:根据svg节点对象类型和路径值转换坐标值
  7. Ubuntu12.10-amd64系统上搭建Android4.2(JellyBean)源码开发环境
  8. 微软推出Windows XP SP3中文版
  9. MYsql:net start mysql 失败 发生系统错误5
  10. Python学习笔记:返回函数