Java Web整合开发(3) -- Servlet
Servlert基本程序架构: (FirstServlet.java + web.xml)
FirstServlet.java
package com.helloben.servlet; import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class FirstServlet extends HttpServlet { @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
execute(request, response);
} @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
execute(request, response);
} @Override
public String getServletInfo() {
return "Short description";
} protected void execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8"); request.setCharacterEncoding("UTF-8"); String requestUri = request.getRequestURI();
String method = request.getMethod();
String param = request.getParameter("param"); try (PrintWriter out = response.getWriter()) { out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet FirstServlet</title>");
out.println("</head>");
out.println("<body>"); out.println("<h1>FirstServlet at " + request.getContextPath() + "</h1>"); out.println("以 " + method + " 方式访问该页面。取到的 param 参数为:" + param + "<br/><br/>"); out.println("<form action='" + requestUri + "' method='get'><input type='text' name='param' value=''><input type='submit' value='以 GET 方式访问 RequestServlet'></form>");
out.println("<br/><br/>");
out.println("<form action='" + requestUri + "' method='post'><input type='text' name='param' value=''><input type='submit' value='以 POST 方式访问 RequestServlet'></form>");
out.println("<br/><br/>"); out.println("<script>document.write('本页面最后更新时间:' + document.lastModified + '<br /><br/>'); </script>");
out.println("<script>document.write('本页面URL:' + location + '<br/>' ); </script>"); out.println("</body>");
out.println("</html>");
}
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.helloben.servlet.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/FirstServlet</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
访问路径: http://localhost:8080/ch03servlet/FirstServlet
1. 获取request变量
String authType = request.getAuthType();
String localAddr = request.getLocalAddr();
Locale locale = request.getLocale();
String localName = request.getLocalName();
String contextPath = request.getContextPath();
int localPort = request.getLocalPort();
String method = request.getMethod();
String pathInfo = request.getPathInfo();
String pathTranslated = request.getPathTranslated();
String protocol = request.getProtocol();
String queryString = request.getQueryString();
String remoteAddr = request.getRemoteAddr();
int port = request.getRemotePort();
String remoteUser = request.getRemoteUser();
String requestedSessionId = request.getRequestedSessionId();
String requestURI = request.getRequestURI();
StringBuffer requestURL = request.getRequestURL();
String scheme = request.getScheme();
String serverName = request.getServerName();
int serverPort = request.getServerPort();
String servletPath = request.getServletPath();
Principal userPrincipal = request.getUserPrincipal(); String accept = request.getHeader("accept");
String referer = request.getHeader("referer");
String userAgent = request.getHeader("user-agent"); String serverInfo = this.getServletContext().getServerInfo();
2.生成图片验证码(IdentityServlet.java)
package com.helloben.servlet; import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.servlet.*;
import javax.servlet.http.*; public class IdentityServlet extends HttpServlet { public static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; public static Random random = new Random(); public static String getRandomString() { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < 6; i++) {
buffer.append(CHARS[random.nextInt(CHARS.length)]);
}
return buffer.toString();
} public static Color getRandomColor() { return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
} public static Color getReverseColor(Color c) { return new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c.getBlue());
} protected void execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setContentType("image/jpeg"); String randomString = getRandomString();
request.getSession(true).setAttribute("randomString", randomString); int width = 100;
int height = 30; Color color = getRandomColor();
Color reverse = getReverseColor(color); BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
g.setColor(color);
g.fillRect(0, 0, width, height);
g.setColor(reverse);
g.drawString(randomString, 18, 20);
for (int i = 0, n = random.nextInt(100); i < n; i++) {
g.drawRect(random.nextInt(width), random.nextInt(height), 1, 1);
} // 转成JPEG格式
ServletOutputStream out = response.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(bufferedImage);
out.flush();
}
}
3. 获取初始化变量
Enumeration params = this.getInitParameterNames();
while(params.hasMoreElements()){
String usernameParam = (String)params.nextElement();
String passnameParam = this.getInitParameter(usernameParam);
}
4. 获取上下文变量
ServletContext servletContext = this.getServletConfig().getServletContext(); String uploadFolder = servletContext.getInitParameter("upload folder");
String allowedFileType = servletContext.getInitParameter("allowed file type");
5. 资源注入(InjectionServlet.java)
@Resource(name="hello")
private String hello; @Resource(name="i")
private int i; @Resource(name="persons")
private String persons;
web.xml
<env-entry>
<env-entry-name>persons</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>HelloBen, Tony, Rose, Alix</env-entry-value>
</env-entry>
6. 使用JNDI获取资源
Context ctx = new InitialContext();
String hello = (String)ctx.lookup("hello");
Integer i = (Integer)ctx.lookup("i");
String persons = (String)ctx.lookup("person");
7. 注射数据源
@Resource
javax.sql.DataSource datasource; public Connection getConnection() throws SQLException{
Connection conn = datasource.getConnection();
return conn;
}
8. GET方式提交表单(search.jsp)
<form action='SearchServlet' method='get'>
<input type="radio" name="type" value="web" checked/>网页
<input type="text" name="word" value="" style="width:300px; "/>
<input type="submit" value="用雅虎搜索" style="width:100px; "/>
</form>
URL: http://server:8080/webapp/SearchServlet?type=web&word=wbsn
SearchServlet.java
String word = request.getParameter("word");
String type = request.getParameter("type");
9. POST方式提交表单(postPersonInfo.jsp)
<form action="PostServlet" method="POST">
<input type="text" name="name" class="text" />
<input type="password" name="password" class="text" />
</form>
PostServlet.java
String name = request.getParameter("name");
String password = request.getParameter("password");
String sex = request.getParameter("sex"); int age = 0;
try {
age = Integer.parseInt(request.getParameter("age"));
}
catch (Exception e) {
} Date birthday = null;
try {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
birthday = format.parse(request.getParameter("birthday"));
}
catch (Exception e) {
} String[] interesting = request.getParameterValues("interesting");
String area = request.getParameter("area");
String description = request.getParameter("description");
String btn = request.getParameter("btn");
10. 上传文件
Web文件上传也采用POST方式。(upload.jsp)
<form action="UploadServlet" method="post" enctype="multipart/form-data">
<input type="file" name="file1" class="text">
</form>
上传文件时,浏览器以二进制方式发送数据。解析二进制数据需要类库,如SmartUpload与Apache Commons Fileupload。SmatUpload是商业类库,解析过程中数据存放在内存中,因此比较快,但是大文件时会内存溢出。
UploadServlet.java
File file1 = null;
File file2 = null;
String description1 = null;
String description2 = null; // 使用 DiskFileUpload 对象解析 request
DiskFileUpload diskFileUpload = new DiskFileUpload();
try {
// 将 解析的结果 放置在 List 中
List<FileItem> list = diskFileUpload.parseRequest(request);
out.println("遍历所有的 FileItem ... <br/>");
// 遍历 list 中所有的 FileItem
for(FileItem fileItem : list){
if(fileItem.isFormField()){
// 如果是 文本域
if("description1".equals(fileItem.getFieldName())){
// 如果该 FileItem 名称为 description1
out.println("遍历到 description1 ... <br/>");
description1 = new String(fileItem.getString().getBytes(), "UTF-8");
}
if("description2".equals(fileItem.getFieldName())){
// 如果该 FileItem 名称为 description2
out.println("遍历到 description2 ... <br/>");
description2 = new String(fileItem.getString().getBytes(), "UTF-8");
}
}
else{
// 否则,为文件域
if("file1".equals(fileItem.getFieldName())){
// 客户端文件路径构建的 File 对象
File remoteFile = new File(new String(fileItem.getName().getBytes(), "UTF-8"));
out.println("遍历到 file1 ... <br/>");
out.println("客户端文件位置: " + remoteFile.getAbsolutePath() + "<br/>");
// 服务器端文件,放在 upload 文件夹下
file1 = new File(this.getServletContext().getRealPath("attachment"), remoteFile.getName());
file1.getParentFile().mkdirs();
file1.createNewFile(); // 写文件,将 FileItem 的文件内容写到文件中
InputStream ins = fileItem.getInputStream();
OutputStream ous = new FileOutputStream(file1); try{
byte[] buffer = new byte[1024];
int len = 0;
while((len=ins.read(buffer)) > -1) {
ous.write(buffer, 0, len);
}
out.println("已保存文件" + file1.getAbsolutePath() + "<br/>");
}
finally{
ous.close();
ins.close();
}
}
if("file2".equals(fileItem.getFieldName())){
// 客户端文件路径构建的 File 对象
File remoteFile = new File(new String(fileItem.getName().getBytes(), "UTF-8"));
out.println("遍历到 file2 ... <br/>");
out.println("客户端文件位置: " + remoteFile.getAbsolutePath() + "<br/>");
// 服务器端文件,放在 upload 文件夹下
file2 = new File(this.getServletContext().getRealPath("attachment"), remoteFile.getName());
file2.getParentFile().mkdirs();
file2.createNewFile(); // 写文件,将 FileItem 的文件内容写到文件中
InputStream ins = fileItem.getInputStream();
OutputStream ous = new FileOutputStream(file2); try{
byte[] buffer = new byte[1024];
int len = 0;
while((len=ins.read(buffer)) > -1) {
ous.write(buffer, 0, len);
}
out.println("已保存文件" + file2.getAbsolutePath() + "<br/>");
}
finally{
ous.close();
ins.close();
}
}
}
}
out.println("Request 解析完毕");
}
catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
11. 带进度条的文件上传
实时显示上传进度的原理是服务器在处理上传文件的同事,将上传进度的信息如文件总长度、已上传多少,传输速率等写入Session中。客户端浏览器利用Ajax技术再新开一个独立的线程从Session中获取上传进度信息,并实时显示。Session可以看做是服务器内存。
uploadprogress.jsp
<%@page language="java" contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>上传文件</title>
<style type="text/css">
body, td, div {
font-size: 12px;
font-familly: 宋体;
} #progressBar {
width: 400px;
height: 12px;
background: #FFFFFF;
border: 1px solid #000000;
padding: 1px;
} #progressBarItem {
width: 30%;
height: 100%;
background: #FF0000;
}
</style>
<script type="text/javascript">
var _finished = true; function $(obj){
return document.getElementById(obj);
} function showStatus(){
_finished = false;
$('status').style.display = 'block';
$('progressBarItem').style.width = '1%';
$('btnSubmit').disabled = true; setTimeout("requestStatus()", 1000);
} function requestStatus(){ if(_finished) {
return;
} var req = createRequest(); req.open("GET", "ProgressUploadServlet");
req.onreadystatechange = function(){
callback(req);
}
req.send(null); setTimeout("requestStatus()", 1000);
} function createRequest()
{
if(window.XMLHttpRequest)//ns
{
return new XMLHttpRequest();
}
else//IE
{
try{
return new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e){
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
return null;
} function callback(req){ if(req.readyState == 4) {
if(req.status != 200){
_debug("发生错误。 req.status: " + req.status + "");
return;
} _debug("status.jsp 返回值:" + req.responseText); var ss = req.responseText.split("||"); // 格式:百分比||已完成数(M)||文件总长度(M)||传输速率(K)||已用时间(s)||估计总时间(s)||估计剩余时间(s)||正在上传第几个文件
$('progressBarItem').style.width = '' + ss[0] + '%';
$('statusInfo').innerHTML = '已完成百分比: ' + ss[0] + '% <br />已完成数(M): ' + ss[1] + '<br/>文件总长度(M): ' + ss[2] + '<br/>传输速率(K): ' + ss[3] + '<br/>已用时间(s): ' + ss[4] + '<br/>估计总时间(s): ' + ss[5] + '<br/>估计剩余时间(s): ' + ss[6] + '<br/>正在上传第几个文件: ' + ss[7]; if(ss[1] == ss[2]){
_finished = true;
$('statusInfo').innerHTML += "<br/><br/><br/>上传已完成。";
$('btnSubmit').disabled = false;
}
}
} function _debug(obj){
var div = document.createElement("DIV");
div.innerHTML = "[debug]: " + obj;
document.body.appendChild(div);
}
</script>
</head>
<body>
<iframe name=upload_iframe width=0 height=0></iframe> <form action="ProgressUploadServlet" method="post" enctype="multipart/form-data" target="upload_iframe" onsubmit="showStatus()">
<input type="file" name="file1" style="width: 350px; "> <br />
<input type="file" name="file2" style="width: 350px; "> <br />
<input type="file" name="file3" style="width: 350px; "> <br />
<input type="file" name="file4" style="width: 350px; ">
<input type="submit" value=" 开始上传 " id="btnSubmit"></form> <div id="status" style="display: none; ">
上传进度条:
<div id="progressBar">
<div id="progressBarItem"></div>
</div>
<div id="statusInfo"></div>
</div>
</form>
</body>
</html>
ProgressUploadServlet.java
package com.helloben.servlet.upload; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload; public class ProgressUploadServlet extends HttpServlet { @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragrma", "no-cache");
response.setDateHeader("Expires", 0); UploadStatus status = (UploadStatus) request.getSession(true).getAttribute("uploadStatus"); if (status == null) {
response.getWriter().println("没有上传信息");
return;
} long startTime = status.getStartTime();
long currentTime = System.currentTimeMillis(); long time = (currentTime - startTime) / 1000 + 1;
double velocity = ((double) status.getBytesRead()) / (double) time;
double totalTime = status.getContentLength() / velocity;
double timeLeft = totalTime - time;
int percent = (int) (100 * (double) status.getBytesRead() / (double) status.getContentLength());
double length = ((double) status.getBytesRead()) / 1024 / 1024;
double totalLength = ((double) status.getContentLength()) / 1024 / 1024; // 格式:百分比||已完成数(M)||文件总长度(M)||传输速率(K)||已用时间(s)||估计总时间(s)||估计剩余时间(s)||正在上传第几个文件
String value = percent + "||" + length + "||" + totalLength + "||"
+ velocity + "||" + time + "||" + totalTime + "||" + timeLeft + "||" + status.getItems(); response.getWriter().println(value);
} @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { UploadStatus status = new UploadStatus();
UploadListener listener = new UploadListener(status); request.getSession(true).setAttribute("uploadStatus", status); // Apache 上传工具
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
upload.setProgressListener(listener); try {
List itemList = upload.parseRequest(request); for (Iterator it = itemList.iterator(); it.hasNext();) {
FileItem item = (FileItem) it.next();
if (item.isFormField()) {
System.out.println("FormField: " + item.getFieldName() + " = " + item.getString());
}
else {
System.out.println("File: " + item.getName()); // 统一 Linux 与 windows 的路径分隔符
String fileName = item.getName().replace("/", "\\");
fileName = fileName.substring(fileName.lastIndexOf("\\")); File saved = new File("C:\\upload_test", fileName);
saved.getParentFile().mkdirs(); InputStream ins = item.getInputStream();
OutputStream ous = new FileOutputStream(saved); byte[] tmp = new byte[1024];
int len = -1; while ((len = ins.read(tmp)) != -1) {
ous.write(tmp, 0, len);
} ous.close();
ins.close(); response.getWriter().println("已保存文件:" + saved);
}
}
}
catch (Exception e) {
e.printStackTrace();
response.getWriter().println("上传发生错误:" + e.getMessage());
}
} @Override
public String getServletInfo() {
return "Short description";
}
}
UploadListener.java
package com.helloben.servlet.upload; import org.apache.commons.fileupload.ProgressListener; public class UploadListener implements ProgressListener { private UploadStatus status; public UploadListener(UploadStatus status) {
this.status = status;
} @Override
public void update(long bytesRead, long contentLength, int items) {
status.setBytesRead(bytesRead);
status.setContentLength(contentLength);
status.setItems(items);
}
}
UploadStatus.java
package com.helloben.servlet.upload; class UploadStatus {
private long bytesRead;
private long contentLength;
private int items;
private long startTime = System.currentTimeMillis(); public long getBytesRead() {
return bytesRead;
}
public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
} public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
} public int getItems() {
return items;
}
public void setItems(int items) {
this.items = items;
} public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
12. Servlet生命周期(init() --> service() --> destroy())
13. web.xml文件详解 (http://www.cnblogs.com/hellojava/archive/2012/12/28/2835730.html)
Java Web整合开发(3) -- Servlet的更多相关文章
- Java Web整合开发王者归来(JSP + Servlet + Struts + Hibernate + Spring) - 读书笔记
第1章 状态码表示响应类型: 保留 表示请求成功地接收 完成请求客户需进一步细化请求 客户错误 服务器错误 Web服务器: Apache服务器,特长是处理静态页面,效率非常高. Tomcat提供对JS ...
- Java Web整合开发实战:基于Struts 2+Hibernate+Spring 目录
第1篇 Java Web开发基础第1章 Web的工作机制( 教学视频:31分钟) 1.1 理解Web的概念 1.1.1 Web的定义 1.1.2 Web的三个核心标准 1.2 C/S与B/S两种软件体 ...
- java web 整合开发王者归来学习总结
第一章java web开发概述 胖客户端CS,瘦客户端BS(Browser) 网址请求---服务器处理响应-----返回结果-----浏览器显示 CGI可以动态生成页面,但是每个进程都要启动一个CGI ...
- 《轻量级Java Web整合开发入门SSH》 - 快速理解Java框架的又一积木
学习JAVA不难,难的是没有多余的时间给你仔细学习. 伴随着项目的不断跟进,责任重于泰山,必须快速提升. 我不能期望把一本书或者一个项目完全吃透,只希望能用数量去 ...
- Java Web整合开发(79) -- Struts 2
一. Struts 2.x 概述 不继承任何类的Action Struts 2的Action并不一定要实现Action接口或者继承ActionSupport,任何POJO都可以做Action,只要这个 ...
- Java Web整合开发(14) -- Struts 1.x 概述
整合Spring与Struts1的三种方法总结 无论用那种方法来整合,第一步就是要装载spring的应用环境,有三种方式: #1. struts-config.xml <?xml version ...
- [Java Web整合开发王者归来·刘京华] 1、 Java Web开发
目录: 1.Web技术简介 2.动态网站与静态网站 3.Java Web开发模式 4.JavaScript简介 1.Web技术简介 PS: 最近还有更凶残的技术,即整个操作系统都是基于Web的,如 ...
- Java Web整合开发(4) -- JSP
JSP脚本中的9个内置对象: application: javax.servlet.ServletContext config: javax.servlet.ServletCo ...
- [Java Web整合开发王者归来·刘京华] 2、 Java Web开发概述
1.Web相关概念 1-1.胖客户与瘦客户 >_<" RCP的定义及优缺点: >_<"TCP的定义及优缺点: 1-2.B ...
随机推荐
- Microsoft Visual C++ Runtime Library Runtime Error解决的方式
打开浏览器时,出现Microsoft Visual C++ Runtime Library Runtime Error错误,初步预计是软件冲突,可能有多种出错的方式,我的是浏览器自己主动关闭. 一. ...
- 联想A798T刷机包 基于百度云V6 集成RE3.1.7美化版 精简冗余文件
ROM介绍 1.apk进行odex合并及zipaliang优化-省电及降低内存暂用. 2.測试相机.通话.数据.wifi.蓝牙.等传感器均正常,. 3.提供时间居中防iphone状态栏补丁 4.增加I ...
- Unbound classpath container: 'JRE System Library [jdk17060]' in project ***
项目报告的错误列表 Unbound classpath container: 'JRE System Library [jdk17060]' in project **** 误. 原因是,我升级JDK ...
- OTN&互换amp; P-OTN有效降低100G 网络成本 (两)
OTN互换& P-OTN有效降低100G 网络成本 (两) 在全球范围内.网流量的增长速度是空前的,导致此现象的缘由包含云服务的增长.移动宽带和基于互联网的视频点播服务的增长. Cisco估计 ...
- A simple Test Client built on top of ASP.NET Web API Help Page
Step 1: Install the Test Client package Install the WebApiTestClient package from the NuGet Package ...
- HDU 1274 展开字符串 (递归+string类)
题目链接:HDU 1274 展开字符串 中文题. 左括号进入DFS函数,右括号return到上一层. 注意return回去的是这个一层递归中的括号中的字母串. AC代码: #include<st ...
- pdf转换为word小工具,挺好
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGFwZW5nMDExMg==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- c# 在cmd中用 7z解压缩文件
var exePath = @"C:\Program Files\7-Zip\7z.exe"; var path = @"I:\work\MusicCatcher2\Wi ...
- cocos2d-x box2d Demo注解
勤奋努力,持之以恒. 核心概念 Box2D 中有一些主要的对象,这里我们先做一个简要的定义,在随后的文档里会有更具体的描写叙述. 刚体(rigid body) 一块十分坚硬的物质,它上面的不论什么两点 ...
- Tuple
Tuple(组元)是C# 4.0引入的一个新特性,编写的时候需要基于.NET Framework 4.0或者更高版本. 在以前编程中,当需要返回多个值得方法中,常常需要将这些值放置到一个结构体或者对象 ...