文件上傳
一般情況下,我們?nèi)绻诒韱沃猩蟼魑募?,一般會將form的enctype參數(shù)設(shè)置為multipart/form-data
。通常文件上傳使用POST請求方式。
原始方法
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%--
1.文件上傳,表單必須是post提交
2.文件上傳,還需要在表單上加上enctype屬性(提交請求正文的類型)
application/x-www-form-urlencoded(默認(rèn)取值,普通提交)
multipart/form-data(多段式提交)
3. 文件上傳,使用<input type="file" name="photo" /> 標(biāo)簽,并且必須有name屬性
--%>
<form action="/fileupload/Aservlet" method="post" enctype="multipart/form-data">
用戶名:<input type="text" name="name" /><br>
個人近照:<input type="file" name="photo" /><br>
<input type="submit" value="上傳" />
</form>
</body>
</html>
Aservlet
package fileupload;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/Aservlet")
public class Aservlet extends HttpServlet {
// 文件上傳只能用POST提交,實現(xiàn)這個方法就好了
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.獲得瀏覽器提交上來的全部信息
InputStream is = request.getInputStream();
//2.提取流中的內(nèi)容,再根據(jù)分隔符
OutputStream os = System.out;
byte[] bytes = new byte[1024];
int len = -1;
while((len=is.read(bytes))!=-1){
os.write(bytes, 0, len);
os.flush();
//3.手動從請求頭中獲得分割線是什么
//4.使用分隔線分隔請求正文
//5. 從分隔的每一段中 提取信息.
}
}
}
使用工具類方便上傳
上面的方法復(fù)雜,使用第三方庫可以很方便的完成文件上傳。提交表達轉(zhuǎn)到Bservlet
package fileupload;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
@WebServlet("/Bservlet")
public class Bservlet extends HttpServlet {
// 需要用到commons-fileupload.jar, commons-io.jar
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 創(chuàng)建配置工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2. 根據(jù)配置工廠創(chuàng)建解析請求中文件上傳內(nèi)容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 3. 判斷當(dāng)前請求是不是多段提交
if (!upload.isMultipartContent(request)) {
throw new RuntimeException("不是多段提交!");
}
try {
// 4. 解析request對象,將已經(jīng)分割過的內(nèi)容放進了List
List<FileItem> list = upload.parseRequest(request);
if (list != null) {
for (FileItem fileItem : list) {
// 判斷當(dāng)前段是普通字段還是文件,這個方法是判斷普通段
if (fileItem.isFormField()) {
// 獲得jsp里name屬性對應(yīng)的值,這里是username
String fname = fileItem.getFieldName();
// 獲得用戶輸入的用戶名
String value = fileItem.getString();
System.out.println(fname + "=>"+value );
// 否則就是文件了
} else {
// 獲得上傳文件的文件名
String name = fileItem.getName();
// 獲得文件上傳段中,文件的流
InputStream in = fileItem.getInputStream();
// 字節(jié)輸出流,用以保存文件
FileOutputStream fos = new FileOutputStream("C:\\Users\\"+name);
// 將輸入流復(fù)制到輸出流中
IOUtils.copy(in, fos);
fos.close();
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
詳解DiskFileItemFactory
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1 創(chuàng)建配置工廠=> 有參的構(gòu)造可以 直接設(shè)置下面兩個配置new DiskFileItemFactory(new File("d:/temp"), 10240);
DiskFileItemFactory factory = new DiskFileItemFactory();
// 1.1 設(shè)置文件上傳臨時目錄 => 默認(rèn)位置 => tomcat/temp
factory.setRepository(new File("d:/temp"));
// 1.2 設(shè)置文件寫入硬盤的緩沖區(qū)大小=>默認(rèn)值=>10k
factory.setSizeThreshold(10240);
}
詳解ServletFileUpload
//2 根據(jù)配置工廠創(chuàng)建解析器(解析request對象)
ServletFileUpload upload = new ServletFileUpload(factory);
//2.1 判斷當(dāng)前請求是否是多段式提交
upload.isMultipartContent(request);
//2.2 設(shè)置多段中每段 段頭 在解析時,使用什么碼表解碼 => 當(dāng)段頭中出現(xiàn)中文時,一定要調(diào)用該方式指定段頭碼表。Content-Disposition: form-data; name="username"
// 這種就是段頭
upload.setHeaderEncoding("UTF-8");
//2.3 設(shè)置文件最大上傳大小 (單位:字節(jié))
upload.setSizeMax(1024*1024*10); // 單次請求,總上傳大小限制 10兆.因為是多段式提交,所以可以一次上傳多個文件。對單個文件的限制可以使用下面的方法
upload.setFileSizeMax(1024*1024);// 每個文件上傳段,大小限制 1兆
FileItem表示分割后的每一段的內(nèi)容,主要方法如下
boolean isFormField()。// isFormField方法用來判斷FileItem對象里面封裝的數(shù)據(jù)是一個普通文本表單字段,還是一個文件表單字段。如果是普通文本表單字段,返回一個true否則返回一個false。因此可以用該方法判斷是否是普通表單域還是文件上傳表單域。
// 注意下面這兩個方法的區(qū)別
String getName()// getName方法用來獲得文件上傳字段中的文件名。
String getFieldName()// getFieldName方法用來返回表單標(biāo)簽的name屬性的值
String getString("utf-8") // 空參或者傳入編碼方式如"UTF-8"。將FileItem對象中保存的數(shù)據(jù)流內(nèi)容以一個字符串返回。如果是普通表單字段,如登錄時候輸入用戶名。則返回用戶輸入的字段。如果是文件上傳則返回文件的內(nèi)容。
boolean isInMemory() // 判斷FileItem對象封裝的數(shù)據(jù)是保存在內(nèi)存中還是硬盤中。
void write(File file) // write方法將FileItem對象中的內(nèi)容保存到某個指定的文件中。如果FileItem對象中的內(nèi)容是保存在某個臨時文件中,該方法完成后,臨時文件可以會被刪除。該方法也可以將普通表單字段保存在一個文件中,但最主要的用途是把上傳的文件內(nèi)容保存在本地文件系統(tǒng)中。
// 上例中也可以直接用fileItem.write(File file)來講數(shù)據(jù)寫到本地文件
String getContentType() // 此方法用來獲得上傳文件的類型,即標(biāo)段字段元素描述頭屬性“content-type”的值,如image/jpeg。如果FileItem對象對應(yīng)的是普通的表單字段,將返回null。
InputStream getInputStream() // 以流的形式返回上傳文件的主體內(nèi)容。
OutputStream getOutputStream() // 可以用此方法將輸入流寫到FileItem中
void delete() // 此方法用來清空FileItem對象中封裝的主體內(nèi)容,如果內(nèi)容是被保存在臨時文件中,該方法會把臨時文件刪除。
long getSize() // 返回上傳文件的大小。
List<FileItem> list = null;
try {
// 2.4 解析request,將每個分段中的內(nèi)容封裝到FileItem中
list = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
if (list != null) {
for (FileItem item : list) {
// 3.1 item 判斷當(dāng)前分段是否是普通表單段
boolean flag = item.isFormField();
// 3.2獲得 表單提交的鍵.(input元素,name屬性的值)
// 普通段和文件上傳段都有
String fname = item.getFieldName();
// 3.3 返回文件名稱,普通段返回null
String name = item.getName();
// 3.4 獲得文件上傳中的正文,如果是普通段,如用戶登錄則是用戶自己輸入的值。若是文件,則是文件內(nèi)容。以字符串形式返回段體中的內(nèi)容 注意:文件上傳段不建議使用該方法.使用item.getInputStream()更好
String content = item.getString();
System.out.println("是否是普通表單提交:" + flag + ",表單提交的鍵:" + fname + ",文件名稱:" + name + ",文件內(nèi)容:" + content);
}
}
解決亂碼
文件上傳時候的文件名包含中文
ServletFileUpload的setHeaderEncoding("UTF-8");
設(shè)置一下即可。
段體內(nèi)容亂碼
即getString()
返回的內(nèi)容亂碼。fileItem.getString("UTF-8");
即可。
上傳文件后應(yīng)該把文件保存到什么位置?
1. 上傳后如果需要其他用戶可以直接訪問,就放到webRoot下.
2. 上傳后其他用戶不能直接訪問, 不直接放在webRoot下。比如WEB-INF下或硬盤其他位置例如 D:\db\xxx.xxx
保存用戶上傳的文件時的注意事項
使用用戶上傳的文件名來保存文件的話,文件名可能重復(fù)。所以保存文件之前,要保證文件名不會重復(fù)。
- 可以使用UUID生成隨機字符串
- 可以使用登錄用戶名+當(dāng)前系統(tǒng)毫秒數(shù)
在一個目錄下放所有用戶上傳的文件顯然不明智??梢杂萌缦路椒?/p>
-
/upload/2017/04/15/xxxx
使用當(dāng)前日期作為子文件夾名稱 - 當(dāng)前登錄用戶的用戶名作為文件夾名稱
package fileupload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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;
import org.apache.commons.io.IOUtils;
@WebServlet("/Dservlet")
public class Dservlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 創(chuàng)建配置工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2. 根據(jù)配置工廠創(chuàng)建解析請求中文件上傳內(nèi)容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 3. 判斷當(dāng)前請求是不是多段提交
if (!upload.isMultipartContent(request)) {
throw new RuntimeException("不是多段提交!");
}
try {
// 4. 解析request對象,將已經(jīng)分割過的內(nèi)容放進了List
List<FileItem> list = upload.parseRequest(request);
if (list != null) {
for (FileItem fileItem : list) {
// 判斷當(dāng)前段是普通字段還是文件,這個方法是判斷普通段
if (fileItem.isFormField()) {
// 獲得jsp里name屬性對應(yīng)的值,這里是username
String fname = fileItem.getFieldName();
// 獲得用戶輸入的用戶名
String value = fileItem.getString("utf-8");
System.out.println(fname + "=>"+value );
// 否則就是文件了
} else {
// 獲得文件上傳段中,文件的流
InputStream in = fileItem.getInputStream();
// 使用用戶上傳的文件名來保存文件的話,文件名可能重復(fù)。
// 所以保存文件之前,要保證文件名不會重復(fù)。使用UUID生成隨機字符串
String fileName = UUID.randomUUID().toString();
// 格式化日期
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("/yyyy/MM/dd/");
String datePath = simpleDateFormat.format(new Date()); // 解析成 /2017/04/15/ 的樣子, 注意這是三個文件夾
String wholePath = "D:/upload"+datePath;
// 字節(jié)輸出流,用以保存文件,也不需要后綴名,因為我們只是保存用戶的數(shù)據(jù),不需要查看他們的數(shù)據(jù)。待用戶想下載的時候,再加上后綴名
File dir = new File(wholePath);
// 判斷文件夾是否已經(jīng)存在,不存在就新建
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(wholePath+fileName);
// 將輸入流復(fù)制到輸出流中
IOUtils.copy(in, fos);
fos.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
mkdir()和mkdirs()的區(qū)別
mkdirs()
可以建立多級目錄。即使所有層級的目錄都不存在。這些文件夾都會創(chuàng)建。比如我們事先并沒有在D盤創(chuàng)建upload和2017等這些文件夾。-
kdir只能用于父級目錄已經(jīng)存在的情況下使用,在已存在的父級目錄下再新建一級。只能一級!比如File("D:\upload\2017\04")。且D:\upload\2017是已經(jīng)存在的。父級 目錄存且只新建一級。故
file.makedir()
返回true成功創(chuàng)建。但是File("D:\upload\2017\04\15")且D:\upload\2017存在,但不存在15文件夾。所以這里是想新建兩級目錄。因為父級目錄不存在所以創(chuàng)建失敗返回false。
多文件同時上傳
同時上傳多個文件,無非是多寫幾個<input type="file" name="photo">
這樣的標(biāo)簽,提交的時候一并上傳,同時上傳的文件不過是被分成了幾段而已。由于List<FileItem> list = upload.parseRequest(request);
返回的是全部的FileItem的,所以后臺處理的代碼不用變。這里用js來處理添加和刪除的邏輯。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<script type="text/javascript">
function fun1() {
//1 創(chuàng)建出想要添加的行
var tr = document.createElement("tr");
// this表示當(dāng)前標(biāo)簽,這里是button
tr.innerHTML = "<td><input type='file' name='photo' /></td><td><input type='button' value='刪除' onclick='fun2(this)' /></td>";
//2 找到表格
var table = document.getElementById("one");
//3 找到表格最后一行
var lastRow = table.rows[table.rows.length-1];
//4 insertBefore,由于瀏覽器解析table的原因。tr的父節(jié)點可能并不是table,所以下面的寫法兼容了兩種情況
lastRow.parentNode.insertBefore(tr, lastRow);
}
//參數(shù): 要刪除行中的刪除按鈕對象,button的父節(jié)點是td,td父節(jié)點是tr,tr的父節(jié)點不一定是table。不過沒關(guān)系,用tr的父節(jié)點去刪除tr就行(obj.parentNode.parentNode)
function fun2(obj){
obj.parentNode.parentNode.parentNode.removeChild(obj.parentNode.parentNode);
}
</script>
<form action="/fileupload/Dservlet" method="post" encType="multipart/form-data">
<table border="1" id="one">
<tr>
<th colspan="2">照片上傳</th>
</tr>
<tr>
<td><input type="file" name="photo" /></td>
<td><input type="button" value="添加"
onclick="fun1()" /></td>
</tr>
<tr>
<td colspan="2" align="center"><input
type="submit" value="上傳" /></td>
</tr>
</table>
</form>
</body>
</html>
文件下載
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<%-- get提交,?name="name" 表示參數(shù)--%>
<body>
<a href="/filedownload/Aservlet?name=西電.zip">西電.zip</a> <br>
<a href="/filedownload/Aservlet?name=課.png">課.png</a> <br>
<a href="/filedownload/Aservlet?name=Android基礎(chǔ)(四).md">Android基礎(chǔ)(四).md</a> <br>
</body>
</html>
package filedownload;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.print.URIException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
@WebServlet("/Aservlet")
public class Aservlet extends HttpServlet {
// 點擊超鏈接屬于GET提交
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 原則: 凡是響應(yīng)正文中需要輸出內(nèi)容, 一定要設(shè)置content-type頭
String fileName = request.getParameter("name");
System.out.println(fileName);
ServletContext sc = getServletContext();
// 只會截取文件后綴名
String type = sc.getMimeType(fileName);
response.setContentType(type);
// 設(shè)置這里讓瀏覽器知道我們下載的文件名,要不默認(rèn)使用Aservlet這個名稱,且沒有后綴名.注意這里加了attachment;filename=
// 文件名可能是中文的,下載時候可能出現(xiàn)亂碼。要轉(zhuǎn)碼成UTF-8
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
// 獲得資源輸入流
InputStream inputStream = sc.getResourceAsStream("/WEB-INF/resource/"+fileName);
OutputStream outputStream = response.getOutputStream();
IOUtils.copy(inputStream, outputStream);
}
}
注解
什么是注解?它有什么作用?
注解就是 @xxx 這種就是注解.
- 注釋:給程序員看的.
- 注解:給程序看。
使用注解的目的: 其實將來使用注解目的就是為了代替?zhèn)鹘y(tǒng)配置文件。
比如下面三個注解
public class Demo1 implements Person {
@Override
//向編譯器描述,該方法是被重寫的.
//幫你檢查,被注解修飾方法是否是被重寫的,如果不是編譯報錯!
public void eat() {
}
@Deprecated
//該注解告訴編譯器,被修飾的方法是過時方法
public static void show(){
System.out.println("hello world!");
}
}
// ***** 另外一個文件 *****
package annotation;
import java.util.ArrayList;
import java.util.List;
public class Test {
// 告訴編譯器,不要檢查什么錯誤。
@SuppressWarnings({ "null", "rawtypes", "unchecked" })
// @SuppressWarnings("all") 全部警告都不檢查
public static void main(String[] args) {
String str = null;
str.substring(0);
// @SuppressWarnings("unused")
String str2 = null;
// @SuppressWarnings("rawtypes")
List list = new ArrayList();
list.add("abc");
}
}
自定義注解
package annoation;
public @interface MyAnnotation {
}
上面的內(nèi)容被編譯成.class
再反編譯過來就是這樣
interface MyAnnotation extends Annotation
{
}
注解本質(zhì)上就是一個接口。它擴展了java.lang.annotation.Annotation接口;在java中所有注解都是Annotation接口的子接口。
package annoation;
public @interface MyAnnotation {
//聲明屬性=> 用抽象方法
//聲明一個名為name的屬性 類型是String
String name();
}
自定義注解的使用
package annoation;
public class Demo {
// 注意一定要鍵name
@MyAnnotation(name = "hello")
public void test() {
}
}
注解支持的類型
常見的比如
- 八大基本數(shù)據(jù)類型
- String
- Array
- Enum枚舉
package annoation;
import java.lang.annotation.ElementType;
public @interface MyAnnotation {
//可以使用default關(guān)鍵字,添加屬性的默認(rèn)值。就不用寫 a= 了
byte a() default 10;
short b();
int c();
long d();
float e();
double f();
char g();
boolean h();
String i();
String[] j();
ElementType k();
}
package annoation;
import java.lang.annotation.ElementType;
public class Demo {
@MyAnnotation(
b = 1000,
c = 10000,
d = 100000,
e = 3.14f,
f = 3.1415926,
g = 'a',
h = true,
i = "tom",
j = "jack",
k = ElementType.FIELD)
//數(shù)組屬性在賦值時使用大括號. => j = { "jack","rose" }
//如果數(shù)組中只有一個元素.那么可以忽略大括號 j = "jack"
public void test() {
}
}
注意
如果注解中,必填屬性只有一個. 這個屬性的名字是"value".那么在賦值時不需要加屬性的鍵.
package annoation;
import java.lang.annotation.ElementType;
public @interface MyAnnotation {
String value();
// 或者String[] value();
}
package annoation;
public class Demo {
// 沒加value="good",直接下面這樣就行
@MyAnnotation("good")
public void test() {
}
}
可以使用default關(guān)鍵字,添加屬性的默認(rèn)值。就不用寫 a=
了
int a() default 10;
元注解
package annoation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//元注解 (4個)
//修飾注解的注解
//RetentionPolicy.SOURCE 注解會保留到Java源文件這一階段 ,編譯成class文件后就不存在了
//RetentionPolicy.CLASS 注解在源文件和編譯成class文件后也還存在
//RetentionPolicy.RUNTIME 注解在源文件、編譯后的class文件、以及運行時期都存在
//
@Retention(RetentionPolicy.RUNTIME)
//@Target 注解支持加在什么位置
//ElementType.CONSTRUCTOR 構(gòu)造方法
//ElementType.METHOD 方法
// 比如下面,注解可以加、在構(gòu)造函數(shù),方法和類上
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
//@Inherited 所加的注解可否被繼承
@Inherited
//@Documented 生成java文檔時候也保留注解
@Documented
public @interface MyAnnotation2 {
}
注解小例子
package annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation3 {
double value();
}
模擬銀行轉(zhuǎn)賬,注解的作用是:單筆轉(zhuǎn)賬不得超過1000
package annoation;
import java.lang.reflect.Method;
public class BankTransfer {
@MyAnnotation3(10000)
public static void trans(String person, String another, double money) throws ClassNotFoundException, NoSuchMethodException {
// Method m = BankService.class.getMethod("zz",String.class,String.class,double.class);
// 也可以上面的寫法
// 1. 獲得注解所在的反射對象
Method m = Class.forName("annoation.BankTransfer").getMethod("trans", String.class, String.class, double.class);
//2 判斷方法是否被注解修飾
if (m.isAnnotationPresent(MyAnnotation3.class)) {
//3 獲得注解的屬性值
MyAnnotation3 myAnnotation = m.getAnnotation(MyAnnotation3.class);
double maxmoney = myAnnotation.value();
if (money > maxmoney) {
throw new RuntimeException("單次轉(zhuǎn)賬不能超過" + maxmoney + "元!");
}
System.out.println(person + "給" + another + "轉(zhuǎn)了" + money + "元!");
} else {
//沒被注解修飾
throw new RuntimeException("系統(tǒng)異常,不能轉(zhuǎn)賬!");
}
}
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {
trans("我", "你", 100000);
}
}
重寫JDBCUtils
代替配置文件,使用注解
package annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
// 加到類上
@Target(ElementType.TYPE)
public @interface JDBCInfo {
String className();
String url();
String user();
String password();
}
package annoation;
import java.sql.*;
@JDBCInfo(className = "com.mysql.jdbc.Driver", url = "jdbc:mysql://localhost:3306/example", user = "root", password ="admin")
public class JDBCUtils {
private static String driver;
private static String url;
private static String user;
private static String password;
// 靜態(tài)代碼塊,隨著類的加載而加載,只加載一次
static {
try {
//獲得注解中配置的屬性
//1 獲得注解所在的反射對象
//2 獲得注解的實現(xiàn)類
JDBCInfo info = Class.forName("annoation.JDBCUtils").getAnnotation(JDBCInfo.class);
//3 獲得4個屬性值
driver = info.className();
url=info.url();
user=info.user();
password=info.password();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("創(chuàng)建連接失??!");
}
return connection;
}
// 釋放資源
// 參數(shù)可能為空
// 調(diào)用close要拋出異常,即使出現(xiàn)異常也能關(guān)閉
public void close(Connection conn, Statement state, ResultSet result) {
try {
if (result != null) {
result.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (state != null) {
state.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
System.out.println(getConnection());
}
}
以前寫的使用properties來保存并讀取配置文件。對比一下
package jdbc;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBCUtil {
private static String driver;
private static String url;
private static String user;
private static String password;
// 靜態(tài)代碼塊,隨著類的加載而加載,只加載一次
static {
try {
Properties prop = new Properties();
// load()接收InputStream,所以向上轉(zhuǎn)型
InputStream is = new FileInputStream("src/jdbc/jdbc_setting.properties");
prop.load(is);
driver = prop.getProperty("ClassName");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
Class.forName(driver);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("創(chuàng)建連接失敗!");
}
return connection;
}
// 釋放資源
// 參數(shù)可能為空
// 調(diào)用close要拋出異常,即使出現(xiàn)異常也能關(guān)閉
public static void close(Connection conn, Statement state, ResultSet result) {
if (result != null) {
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public static void main(String[] args) {
System.out.println(getConnection());
}
}
枚舉簡介
通常描述數(shù)量固定的屬性。比如性別,一周從周一到周日,這些都是固定的 。為了定義這些量,我們可以這樣寫。
public class Sex {
// 靜態(tài)代碼塊最先執(zhí)行,所以這里定義的時候不初始化也不會報錯
public static final Sex MALE;
public static final Sex FEMALE;
static {
MALE = new Sex();
FEMALE = new Sex();
}
// 或者不用靜態(tài)代碼塊的方法,直接下面這樣
// public static final Sex MALE = new Sex();
// public static final Sex FEMALE = new Sex();
public static void main(String[] args) {
//男性
Sex male = Sex.MALE;
//女性
Sex female = Sex.FEMALE;
}
}
如果使用枚舉就很省事
package annoation;
public enum Sex2 {
MALE, FEMALE;
}
測試一下
public static void main(String[] args) {
//男性
Sex2 male = Sex2.MALE;
//女性
Sex2 female = Sex2.FEMALE;
}
by @sunhaiyu
2017.4.15