上午剛參加完一公司的筆試,整個筆試給我的感受是,比較基礎也很全面。但自己就是有好多不確定,好多需要時間思考,總之就是基礎需要補回來。下面我就總結一下這次的筆試,舉一反三地把相關知識點也梳理下。
一、最讓我抓狂的SQL題
這種SQL題我以前做過好多次了,工作上SQL寫得不多,運用SQL的思維能力都下降了。題目大概是“有一張銷售營業額表(t_sales),表結構如下:
CREATE TABLE t_sales( t_person VARCHAR(20), #銷售人員 t_item VARCHAR(100), #商品 t_sale INT, #銷售額 t_time DATE #時間 )
“求各種商品今年銷售額最高的銷售人員和商品名”
這題目看它的數據結構應該是用Mysql寫的,我就用Mysql來解答吧(我當時用了oracle語法解答的,悲劇呀)
SELECT t.* FROM (SELECT t_item,t_person,SUM(t_sale) year_sale FROM t_sales WHERE YEAR(t_time) = YEAR(NOW()) GROUP BY t_item,t_person order by year_sale desc) t GROUP BY t.t_item
-------2016.09.22更新---------------
其實上面就是用到了oracle的dense_rank() over(partition by order by)排序函數(非跳躍,兩個第一名的話,第三個就是第二名),上面的需求不太明確,下面我們就用學生每科成績來吧,找出每個科目成績最高分的學生和分數。
新建一張表並插入數據:
CREATE TABLE student ( id INT(3), name VARCHAR(20), subject VARCHAR(100), score INT(3)); INSERT INTO student VALUES (1, 'jack','語文', 90); INSERT INTO student VALUES (2, 'tom','語文', 85); INSERT INTO student VALUES (3, 'allen','語文', 95);#成績相同 INSERT INTO student VALUES (4, 'michael','語文', 95);#成績相同 INSERT INTO student VALUES (5, 'jack','數學', 93); INSERT INTO student VALUES (6, 'tom','數學', 84); INSERT INTO student VALUES (7, 'allen','數學', 89); INSERT INTO student VALUES (8, 'michael','數學', 86);
mysql查詢的sql如下:
SET @rn=1; SET @last_subject=NULL;#用來判斷是否在同一科目 SET @last_score=NULL;#用來判斷是否相同的分數 SELECT s.name AS "姓名",s.subject AS "科目",s.score AS "分數",s.rank "名次" from (SELECT id,name,subject,score, #上一次查詢的科目等於本次查詢的,說明是同一個組,這實現了oracle的partition by #同組的還要判斷是否分數相同,如果相同還要有相同的名次 IF(@last_subject = subject,IF(@last_score = score,@rn,@rn := @rn + 1),@rn := 1) AS rank, #把本條記錄的科目和分數賦值給變量 @last_subject := subject as "last_subject",@last_score := score AS last_score FROM student #ORDER BY實際上實現了分組的功能 ORDER BY subject,score DESC) s WHERE s.rank = 1
mysql查詢結果如下:
oracle就簡單了,sql如下:
SELECT s.name AS "姓名",s.subject AS "科目",s.score AS "分數",s.rank "名次" from (SELECT t.*,DENSE_RANK() OVER(PARTITION BY subject ORDER BY score desc) rank FROM student t) s where s.rank = 1;
oracle運行結果:
二、cookie和session的代碼實現
1、設置cookie
今天筆試題考的是cookie的設置,我竟然選了request也可以設置cookie,我的天呀。
我們來看如何在response設置吧
public void service(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{ Cookie c = new Cookie("name","jenkins"); resp.addCookie(c); }
這樣子設置,實際上是在返回的消息頭中加了一個key-value,key為Set-Cookie,value也是key-value的形式,上面的就是:name=jenkins,多個cookie用分號(;)隔開
消息頭中的樣式是:Set-Cookie:name=jenkins;JSESSIONID=ERERE23343423
其實如果要在request中把cookie返回到服務器端,我們可以在請求的消息頭中加一個值:
package servletbase; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; public class MyServlet01 extends HttpServlet{ public void postCookieToServer(){ HttpClient client = new HttpClient(); List<Header> headers = new ArrayList<Header>(); //把key為JSESSIONID的cookie上傳到服務器,服務器會根據這個id來判斷 //發起該會話的用戶是不是之前登錄過,做鑒權 headers.add(new Header("cookie","JSESSIONID=RERER3")); //設置消息頭 client.getHostConfiguration().getParams().setParameter("http.default-headers", headers); } }
2、查詢Cookie
在請求對象中獲取:
public void getCookieFromRequest(HttpServletRequest request){ //獲取cookie數組 Cookie[] cookies = request.getCookies(); for(Cookie cookie:cookies){ //獲取cookie的key-value String name = cookie.getName(); String value = cookie.getValue(); } }
3、修改Cookie
public void updateCookieFromRequest(HttpServletRequest request,HttpServletResponse response){ //獲取cookie數組 Cookie[] cookies = request.getCookies(); for(Cookie cookie:cookies){ //修改cookie的value String name = cookie.getName(); if(name.equals("city")){ cookie.setValue("guangzhou"); response.addCookie(cookie); } } }
4、Cookie的編解碼(value為中文時需要編碼,否則訪問時會報錯)
//編碼 Cookie c = new Cookie("city",URLEncoder.encode("廣州","utf-8")); //解碼 String value = c.getValue(); value = URLDecoder.decode(value,"utf-8");
5、Cookie的其他方法
//設置生存時間,單位是秒,默認小於零,保存在內存中。等於零是即刻刪除 cookie.setMaxAge(30) //設置路徑,瀏覽器訪問服務器時只向cookie路徑或者子路徑發送cookie,以下訪問/web/index.html是會發cookie,但訪問/myweb/index.html就不會了 cookie.setPath("/web");
6、Cookie的限制
a、可以被用戶限制
b、保存在瀏覽器,不安全,敏感數據要進行加密
c、只能保存少量數據,大約4k左右
d、個數有限制
e、只能保存字符串
7、Session定義和工作原理
簡單來說Session就是服務器為每一個訪問的瀏覽器分配的內存空間,它有一個唯一的id,並且會將這個jsessionid以cookie的形式發送到瀏覽器(服務器調用了getSession()方法才會返回jsessionid到瀏覽器,並不是每個請求過來都返回),瀏覽器再次訪問時就會把這個jsessionid發送到服務器,服務器就能找到session對象。
如果瀏覽器再次訪問(請求消息頭中帶有jessionid)時,服務器能找到對應的sessionid,則服務器不再返回sessionid到瀏覽器。
8、獲取Session
//參數為true時一定會返回session對象 HttpSession session = request.getSession(true); //不加參數默認是true HttpSession session = request.getSession(); //參數為false時,則沒有sessionid時返回null //有sessionid,但找不到session對象時也返回null,能找到session對象就返回該對象 HttpSession session = request.getSession(false);
9、使用Session綁定對象
//綁定對象 void session.setAttribute(String name,Object boj); //獲取綁定對象 Object session.getAttribute(String name); //移除綁定對象 void session.removeAttribute(String name); //刪除session對象 session.invalidate();
10、Session超時
服務器會將空閑時間過長的session對象刪除以節省內存空間資源,一般是30分鍾
在tomcat的conf/web.xml文件夾設置(單位是分鍾):
<session-config>
<session-timeout>30</session-timeout>
</session-config>
通過編程修改:
void session.setMaxInactiveInterval(int seconds)
11、瀏覽器禁用cookie后果及解決方法
瀏覽器禁用cookie后,session就不能用了,因為jsessionid不能在瀏覽器保存。
解決辦法:使用URL重寫方法解決,就是在重定向時在url中加sessionid參數,就不需要瀏覽器保存了。看以下的例子,假如登錄驗證過程了,需要重定向到首頁
if(name.equals("admin") && pwd.equals("admin")){ session.setAttribute("name",name); //重定向到首頁 //response.sendRedirect("index.jsp"); //改為,其會在路徑后面加上jsessionid, //System.out.println("url:" + resp.encodeRedirectURL("index.html")); --> url:index.html;jsessionid=4E885D5D87247441F761C96ACA9A7B68 response.sendRedirect( response.encodeRedirectURL("index.jsp")); }
登錄之后我們就可以把sessionid保存到頁面,訪問時把它帶上就好了,可以在url后面加上sessionid,如下:
localhost:8080/testweb/servlet01;jsessionid=4E885D5D87247441F761C96ACA9A7B68
另外如果在服務器端沒有生成session對象,服務器是不會返回sessionid的cookie到瀏覽器的,即在服務器端要調用request.getSession()
12、Session的優缺點
優點:
a、安全(狀態保存在服務器端)
b、能保存的數據類型更多,cookie只能是字符串
c、能保存更多數據
缺點:
session將狀態保存在服務器端,占用服務器內存,當用戶量過大時,會嚴重影響服務器性能。
三、重定向與轉發的區別
今天只考了代碼如何實現的,我就把他們原理和區b別總結一下吧
1、重定向
定義:服務器向瀏覽器發送一個302的狀態碼和一個Location消息頭(其值是一個地址),瀏覽器收到后會馬上向該地址發出請求。
代碼實現:response.sendRedirect("/index.html");
之后的代碼不能使用request和response,如下所示:
response.sendRedirect("/index.html"); //response.sendRedirect("/login.html");
2、轉發
定義:一個Web組件(Servlet/JSP)將未完成的處理通過容器轉交給另一個Web組件繼續完成。常見的是Servlet獲取數據之后將這些數據轉發給JSP,由這個JSP來負責展示
代碼實現:request.getRequestDispatcher("index.jsp").forward(request,response);
之后的代碼不能使用request和response,如下
req.getRequestDispatcher("/servlet02").forward(req, resp); //req.getRequestDispatcher("/servlet03").forward(req, resp);
3、重定向與轉發的區別
a、是否任意地址:重定向的地址可以任意地址,轉發必須是應用內的某一地址
b、瀏覽器地址是否變:重定向瀏覽器地址欄會變,轉發瀏覽器地址欄不會變
c、是否共享數據:重定向有兩次請求對象,不共享數據;轉發只生產一次請求對象,且在同一應用中共享數據。
d、原理上的不同:重定向是瀏覽器收到響應后再向另外一個地址發出請求,轉發是收到請求為了完成響應轉到一個新地址。
四、request.getParameter()與request.getAttribute()的區別
筆試考了他們返回的數據類型,request.getParameter()返回的是String,request.getAttribute()返回的是Object。下面看看他們的區別
1、HttpServletRequest類有setAttribute()方法,而沒有setParameter()方法
2、request.getParameter()負責獲取從瀏覽器或者其他HttpClient(get,post方式)上傳的參數。而request.getAttribute()是獲取容器內部轉發過來的數據,該數據需要用request.setAttribute()方法綁定,常見於Servlet中把獲取的數據轉發到JSP顯示。
參考:http://www.cnblogs.com/jirglt/archive/2012/10/25/2738617.html
五、javascript題
1、考變量賦值和返回
代碼如下,問執行后y的值是多少?
var y = 0; function f(n){ n += 1; } function m(){ y = f(0); }
結果是:undefined
js的語法是比較弱的,沒有返回值的函數也能賦值到變量。
2、考js的日期函數
就是考Date對象的幾個函數,年月日 時分秒,我都總結一遍吧
var now = new Date(); var yy = now.getYear();//返回從1900開始算得年份間隔,2016就返回116 yy = 1900 + yy;//加上1900就是2016了 var MM = now.getMonth();//返回月份,范圍是0-11 MM = MM + 1;//加上1就是正常月份 var dd = now.getDate();//返回月份的第幾天,從1開始算 var arr = new Array("星期日","星期一","星期二","星期三","星期四","星期五","星期六"); var day = now.getDay();//返回星期,0是星期日,6是星期六 var week = arr[day]; var date = yy+"年"+MM+"月"+dd+"日"+" "+week+" "; var hh = now.getHours();//返回小時數,24小時制 var mm = now.getMinutes();//返回分鍾數 var ss = now.getTime();//返回日期的毫秒數 ss = ss % 60000;//一分鍾取余數,單位是毫秒 //ss % 1000是求得不夠一秒的毫秒數,與ss相減是為了求得能夠被1000整除的數 ss = (ss - (ss % 1000)) / 1000; var time = hh + ":" + mm + ":" + ss; alert("現在的時間是:" + date + time);
以上比較特殊的是年份、月份和星期
六、到處考的字符集編碼
字符集編碼是特別困擾程序員的,我之前也有去研究過,但可能認識地不夠深入,筆試時也沒能答得很好。筆試是問“什么編碼是可以兼容gb2312的?”,我只選了GB18030,其實GBK也可以兼容的。
網上找了些資料,再來梳理下這些麻煩的字符編碼集,就從他們的歷史順利開始吧
1、ASCII(American Standard Code for Information Interchange,美國信息互換標准代碼)
這應該是最早的編碼集,單字節,最多保存256個狀態(2的8次方),前127號已經用於英文、數、符號編碼,128以后的是擴展位
2、ISO-8859-1
單字節,兼容ASCII,用到了ASCII的擴展位,支持部分歐洲使用的語音。以ISO-8859-1編碼的文本,都以bytes[]的形式保存,若要顯示中文,則用中文編碼集解碼即可。如果用ISO-8859-1解碼則顯示亂碼。通常頁面上傳輸的編碼都是ISO-8859-1。
3、gb2312
1980年,一共收錄了7445個字符,包括6763個漢字和682個其它符號。很少用了。
4、GBK
是微軟對gb2312的擴展,是微軟標准但不是國際標准,向下兼容gb2312,出現於Windows 95簡體中文版中
5、GB18030
2000年,取代了GBK1.0的正式國家標准。該標准收錄了27484個漢字,同時還收錄了一些少數民族文字。編碼是一二四字節變長編碼。向下兼容gb2312,基本兼容gbk,單字節兼容ASCII
6、Unicode(統一碼、萬國碼、單一碼)
Unicode 是為了解決傳統的字符編碼方案的局限而產生的,它為每種語言中的每個字符設定了統一並且唯一的二進制編碼,以滿足跨語言、跨平台進行文本轉換、處理的要求。Unicode 16編碼里面已經包含了GB18030里面的所有漢字。總之它就是世界各國通用的字符編碼集,而不是每個國家自己搞一套,互不兼容。儲存每個字符都是用兩個字節,英文、漢字都是兩個字節,所以存儲英文比較浪費空間。
上面講得都是字符集,我們在寫程序中經常用到的utf-8其實不是字符集,他只是一種編碼方式,是對字符集Unicode的再次編碼。utf-8是變長字符編碼,用1-4個字節表示一個字符,如用一個字節表示英文,中文占了3個字節。utf-8是為了解決網絡傳輸和存儲問題的,網絡上傳輸是8個位傳輸的,這滿足了寬帶傳輸的要求(utf-16就是16位傳輸);儲存時可以把英文單字節存儲節省空間。