Java基礎

*List集合中的對象按照某個字段去重實現

jdk7版本:https://blog.csdn.net/u013821825/article/details/66969683/

jdk8 lambda版本:https://blog.csdn.net/caitianchao/article/details/80338458

 

*for循環刪除元素避免因爲索引變更導致數組下表越界的方法:

for(int i=list.size()-1;i>=0;i--)
{
    NetworkNode  networkNodei=list.get(i);
    for(int j=list.size()-1;j>=0;j--)
    {
        NetworkNode  networkNodej=list.get(j);
        if(networkNodei.delay > networkNodej.delay)
        {
            list.remove(j);
        }
    }
}

*深拷貝和淺拷貝的區別:https://blog.csdn.net/demonliuhui/article/details/54572908

*時間戳類型的字符串轉Date

select to_char(#{item.productionDate, jdbcType=VARCHAR} / (1000*60 * 60 * 24) + to_date('1970-01-01 08:00:00', 'YYYY-MM-DD HH24:MI:SS')) from dual

 

*Java遞歸遍歷json對象,支持無線層級>>原文內容

 

*我想要從set中取出指定位置的元素,百度好多沒有解決辦法
set轉成list可以解決
List <A> list = new ArrayList<A>(B);//B是set型的
取的時候只要這樣
list.get(0);
完美……

 

*迭代器獲取map的key和value

Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry entry = (Map.Entry) iter.next();
    Object key = entry.getKey();
    Object val = entry.getValue();
}

*href和Action,get和post的區別:https://blog.csdn.net/angus_17/article/details/7174510

*什麼時候需要顯式調用父類的構造方法super()

當你沒有使用父類默認的構造方法時,此時在子類的構造方法中就需要顯示的調用父類定義的構造方法。
比如:
父類:
class Animal{
  private String name;
  
  //如果你定義一個新的構造方法
  public Animal(String name) {
    this.name = name;
  }
}
子類:
public Dog extends Animal{
  
  //這是你就要顯示的調用父類的構造方法,
  //因爲子類默認調用的是父類的無參構造方法Animal()
  public Dog(){
    super("小狗");  //顯示調用父類的有參構造方法
    ....  //子類的構造方法處理
  }
}
當然,如果你在父類裏面把無參的構造方法,顯示的寫出來了,比如:
父類:
class Animal{
  private String name;
  //無參的構造方法
  public Animal() {
    .....  //處理
  }
  
  //如果你定義一個新的構造方法
  public Animal(String name) {
    this.name = name;
  }
}
那麼在子類的構造方法中,就可以不用顯示的調用父類的構造方法,因爲子類有個無參的構造方法,子類在構造方法中會自動調用父類已經定義的無參構造方法。
不過一般的,在父類中使用了構造方法的重載,在子類中就可以根據需要,調用相應的父類構造方法。

 

*java8教程彙總>>點擊打開鏈接

jdk8中Collectors的各種方法,乾貨,絕對乾貨>>點擊打開鏈接

jdk8之Stream類(一)>>點擊打開鏈接

jdk8之Stream類(二)>>點擊打開鏈接

jdk8中Duration和Period的區別>>點擊打開鏈接

函數式編程關心數據的映射,這也是爲什麼「函數式編程」叫做「函數」式編程

jdk8中接口的默認方法和靜態方法區別

1、接口默認方法、靜態方法可以有多個。
2、默認方法通過實例調用,靜態方法通過接口名調用。
3、default默認方法關鍵字只能用在接口中。
4、默認方法可以被繼承,如果繼承了多個接口,多個接口都定義了多個同樣的默認方法,實現類需要重寫默認方法不然會報錯。

5、靜態方法不能被繼承及覆蓋,所以只被具體所在的接口調用。

*什麼時候用繼承,什麼時候用接口?

繼承表示你把人家的東西拿過來。這樣你就可以在它的基礎上直接修改了。少了很多的工作。

接口是別人要求你這麼做,你必須按它要求去做。所以你要實現接口。
框架的東西就是讓我們少做點事情,方便開發。

抽象類與接口的完美配合

有時,思考一個抽象方法放在哪裏時,會有抽象類或接口這兩個選擇,經常令人無所適從。但是,只要判斷這個抽象方法是否會被下游用戶利用多態來調用,就可以簡單分類。
此外,即使一個項目的所有抽象方法都是會被多態調用、是在接口聲明的,也同樣可能存在抽象類,因爲它們有一個完美配合。很多時候,一個接口的多個方法,在實現時是分層級的。在這個類只能實現其中一部分,在其子類才能實現剩餘部分,這時父類就需要是抽象方法。
比如,有一個接口叫『可繁衍的』,其中有兩個方法,一是『進食』,二是『性交』。『人類』implements了這個接口,但是隻能實現其中『進食』這個方法。而其子類『男人』和『女人』,分別實現了不同的『性交』方法。這種情況下,『人類』必然是無法實例化的抽象類。
只要這個配合仍然存在,即使抽象方法的聲明源都被接口搶走了,抽象類也不會消失。

(Java 5加入的enum蹲牆角鬱悶地畫了一萬個圈,心想:倫家也是可以聲明各類方法、變量、常量,可以出現在文件級作用域,可以做Utils和Constants的生意,可以……)

 

在開發過程中常常使用抽象類實現接口,再用類集成抽象類做法的原因>>點擊打開鏈接

 

*Stream.of(list)和list.stream()的區別

 

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = Stream.of(numbers).mapToInt((x) -> x).summaryStatistics();//第一種
IntSummaryStatistics stats2 = numbers.stream().mapToInt((x) -> x).summaryStatistics();//第二種

System.out.println("列表中最大的數 : " + stats.getMax());
System.out.println("列表中最小的數 : " + stats.getMin());
System.out.println("所有數之和 : " + stats.getSum());
System.out.println("平均數 : " + stats.getAverage());

 我大概的理解是Stream.of是把整個list轉成stream了,而toStream是將list裏的數據一個一個轉成流的,但是後面的操作是針對每一個元素的,所以第一種報錯了 

 

*雙冒號運算就是Java中的[方法引用],[方法引用]的格式是 類名::方法名。

一般是用作Lambda表達式例如表達式:person -> person.getName();可以替換成Person::getName
表達式() -> new HashMap<>();可以替換成HashMap::new

這種[方法引用]或者說[雙冒號運算]對應的參數類型是Function<T,R> T表示傳入類型,R表示返回類型。

 

*ConcurrentHashMap是使用了鎖分段技術來保證線程安全的(jdk1.7及以下版本)。
鎖分段技術:首先將數據分成一段一段的存儲,然後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據的時候,其他段的數據也能被其他線程訪問。ConcurrentHashMap提供了與Hashtable和SynchronizedMap不同的鎖機制。Hashtable中採用的鎖機制是一次鎖住整個hash表,從而在同一時刻只能由一個線程對其進行操作;而ConcurrentHashMap中則是一次鎖住一個桶。

ConcurrentHashMap默認將hash表分爲16個桶,諸如get、put、remove等常用操作只鎖住當前需要用到的桶。這樣,原來只能一個線程進入,現在卻能同時有16個寫線程執行,併發性能的提升是顯而易見的。

1.JDK1.8版本的ConcurrentHashMap的數據結構已經接近HashMap,相對而言,ConcurrentHashMap只是增加了同步的操作來控制併發,從JDK1.7版本的ReentrantLock+Segment+HashEntry,到JDK1.8版本中synchronized+CAS+HashEntry+紅黑樹,相對而言,總結如下思考:
2.JDK1.8的實現降低鎖的粒度,JDK1.7版本鎖的粒度是基於Segment的,包含多個HashEntry,而JDK1.8鎖的粒度就是HashEntry(首節點)
3.JDK1.8版本的數據結構變得更加簡單,使得操作也更加清晰流暢,因爲已經使用synchronized來進行同步,所以不需要分段鎖的概念,也就不需要Segment這種數據結構了,由於粒度的降低,實現的複雜度也增加了
4.JDK1.8使用紅黑樹來優化鏈表,基於長度很長的鏈表的遍歷是一個很漫長的過程,而紅黑樹的遍歷效率是很快的,代替一定閾值的鏈表,這樣形成一個最佳拍檔
5.JDK1.8爲什麼使用內置鎖synchronized來代替重入鎖ReentrantLock,我覺得有以下幾點:
-因爲粒度降低了,在相對而言的低粒度加鎖方式,synchronized並不比ReentrantLock差,在粗粒度加鎖中ReentrantLock可能通過Condition來控制各個低粒度的邊界,更加的靈活,而在低粒度中,Condition的優勢就沒有了
-JVM的開發團隊從來都沒有放棄synchronized,而且基於JVM的synchronized優化空間更大,使用內嵌的關鍵字比使用API更加自然

-在大量的數據操作下,對於JVM的內存壓力,基於API的ReentrantLock會開銷更多的內存,雖然不是瓶頸,但是也是一個選擇依據

 

*Java併發:CompareAndSwap(CAS)>>點擊打開鏈接

 

*JDK 1.8 以前 HashMap 的實現是 數組+鏈表,即使哈希函數取得再好,也很難達到元素百分百均勻分佈。當 HashMap 中有大量的元素都存放到同一個桶中時,這個桶下有一條長長的鏈表,這個時候 HashMap 就相當於一個單鏈表,假如單鏈表有 n 個元素,遍歷的時間複雜度就是 O(n),完全失去了它的優勢。針對這種情況,JDK 1.8 中引入了 紅黑樹(查找時間複雜度爲 O(logn))來優化這個問題。

 

*關於常量是使用常量類或者常量接口,還是使用枚舉,有什麼區別?>>原文內容

 

*Iterator遍歷Set,List,Map>>點擊打開鏈接

keySet()方法返回值是Map中key值的集合
entrySet()返回此映射中包含的映射關係的視圖

keySet()形式遍歷Map

Map<String,String> map = new HashMap<String,String>();
map.put("01", "zhangsan");
map.put("02", "lisi");
map.put("03", "wangwu");

Set<String> keySet = map.keySet();//先獲取map集合的所有鍵的Set集合
Iterator<String> it = keySet.iterator();//有了Set集合,就可以獲取其迭代器。
while(it.hasNext()){
	String key = it.next();
	String value = map.get(key);//有了鍵可以通過map集合的get方法獲取其對應的值。
	System.out.println("key: "+key+"-->value: "+value);//獲得key和value值
}

entrySet()方式遍歷Map

Map<String,String> map = new HashMap<String,String>();
map.put("01", "zhangsan");
map.put("02", "lisi");
map.put("03", "wangwu");

//通過entrySet()方法將map集合中的映射關係取出(這個關係就是Map.Entry類型)
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//將關係集合entrySet進行迭代,存放到迭代器中
Iterator<Map.Entry<String, String>> it2 = entrySet.iterator();
while(it2.hasNext()){
	Map.Entry<String, String> me = it2.next();//獲取Map.Entry關係對象me
	String key2 = me.getKey();//通過關係對象獲取key
	String value2 = me.getValue();//通過關係對象獲取value
    System.out.println("key: "+key2+"-->value: "+value2);
}

雖然使用keyset及entryset來進行遍歷能取得相同的結果
但兩者的遍歷速度是有差別的
keySet():迭代後只能通過get()取key
entrySet():迭代後可以e.getKey(),e.getValue()取key和value。返回的是Entry接口

說明:keySet()的速度比entrySet()慢了很多,也就是keySet方式遍歷Map的性能不如entrySet性能好,因爲對於keySet其實是遍歷了2次,一次是轉爲iterator,一次就從hashmap中取出key所對於的value。而entryset只是遍歷了第一次,他把key和value都放到了entry中,所以就快了,爲了提高性能,以後多考慮用entrySet()方式來進行遍歷。

 

*有序Map:LinkedHashMap 是根據元素增加或者訪問的先後順序進行排序,而 TreeMap 則根據元素的 Key 進行排序。

 

for循環和while循環對比
1.for循環比while循環節約內存空間,因爲迭代器在for循環中,循環結束,迭代器屬於局部變量,循環結束就消失了,while循環中迭代器對象雖然也是局部變量但是要等方法運行完畢才能在內存中消失

2.當循環次數比較多時,while循環理論上要比for循環要高效,因爲for循環比for多一條彙編語句

while循環的兩種方式

Set<Integer> keySet = map.keySet();
Iterator<Integer> iterator = keySet.iterator();
while (iterator.hasNext() ){
    Integer key = iterator.next();
    String value = map.get(key);
    System.out.println("1.0"+key+"------"+value);
}
Set<Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Entry<Integer, String>> aiterator = entrySet.iterator();
while (aiterator.hasNext()) {
    Entry<Integer, String> entry = aiterator.next();
    Integer key = entry.getKey();
    String value = entry.getValue();
    System.out.println("2.0"+key+"------"+value);
}

 

*動態類型語言:指在運行期間纔去做數據類型檢查,也就是說,用動態語言編程時,永遠不用去給任何變量去指定數據類型。該語言會在你第一次給該變量賦值的時候,在內部把數據類型記錄下來。ruby或者Python是典型的動態類型的語言(這個我其實也不清楚,對這兩門語言不太瞭解),一些腳本語言也多少屬於動態類型語言。 

靜態類型語言:指在編譯期間就去做數據類型檢查,也就是說在編碼時要聲明數據類型。 


還有一個分類:
編譯型語言:把做好的源程序全部編譯成二進制代碼的可運行程序。然後,可直接運行這個程序。
解釋型語言:把做好的源程序翻譯一句,然後執行一句,直至結束!
java很特殊,java程序也需要編譯,但是沒有直接編譯稱爲機器語言,而是編譯稱爲字節碼,然後用解釋方式執行字節碼。

 

*抽象類可以只實現接口的一部分方法>>點擊打開鏈接

 

*Java8中雙冒號運算就是Java中的[方法引用],[方法引用]的格式是 類名::方法名。一般是用作Lambda表達式例如表達式:person -> person.getName();

可以替換成Person::getName
表達式() -> new HashMap<>();
可以替換成HashMap::new
這種[方法引用]或者說[雙冒號運算]對應的參數類型是Function<T,R> T表示傳入類型,R表示返回類型。

 

*阻塞和非阻塞,同步和異步是node.js裏經常遇到的詞彙,我舉個簡單的例子來說明:

我要看足球比賽,但是媽媽叫我燒水,電視機在客廳,燒水要在廚房。家裏有2個水壺,一個是普通的水壺,另一個是水開了會叫的那種水壺。我可以:
用普通的水壺燒,人在邊上看着,水開了再去看球。(同步,阻塞)這個是常規做法,但是我看球不爽了。
用普通水壺燒,人去看球,隔幾分鐘去廚房看看。(同步,非阻塞)這個又大問題,萬一在我離開的幾分鐘水開了,我就麻煩了。
用會叫的水壺,人在邊上看着。(異步,阻塞)這個沒有問題,但是我太傻了。
用會叫的水壺,人去看球,聽見水壺叫了再去看。(異步,非阻塞)這個應該是最好的。
等着看球的我:阻塞
看着電視的我:非阻塞
普通水壺:同步
會叫的水壺:異步
所以,異步往往配合非阻塞,才能發揮出威力。

 

*List<?>和List<T>的區別?>>點擊打開鏈接,慎用List<?>

 

*棧和堆的區別>>點擊打開鏈接

 

1.棧內存存儲的是局部變量而堆內存存儲的是實體;

2.棧內存的更新速度要快於堆內存,因爲局部變量的生命週期很短;

3.棧內存存放的變量生命週期一旦結束就會被釋放,而堆內存存放的實體會被垃圾回收機制不定時的回收。

 

 

 

*使用FastDateFormat來代替JDK自帶的DateFormat:點擊打開鏈接

Java奇淫巧技之Lombok:點擊打開鏈接

maven項目正確使用Lombok中@Data註釋:點擊打開鏈接

Java自定義註解@Retention和@Target:點擊打開鏈接

SpringMVC重要註解(一)@ExceptionHandler和@ResponseStatus:點擊打開鏈接

SpringMVC重要註解(二)@ControllerAdvice:點擊打開鏈接

 

*如何實現動態代理

1.創建一個實現接口InvocationHandler的類,它必須實現invoke方法

2.創建被代理類以及接口

3.通過Proxy的靜態方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler)創建一個代理

4.通過代理調用方法

點擊打開鏈接

實現動態代理的兩種方法:點擊打開鏈接

 

*java中的clone()方法是淺複製
java深度複製和淺度複製>>點擊打開鏈接

 

*在程序啓動的時候就創建若干線程來響應處理,它們被稱爲線程池,裏面的線程叫工作線程 

第一:降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗。 
第二:提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。 
第三:提高線程的可管理性。 
常用線程池:ExecutorService 是主要的實現類,其中常用的有 :
Executors.newSingleThreadPool()
Executors.newFixedThreadPool()
Executors.newcachedTheadPool()

Executors.newScheduledThreadPool()

 

*什什麼是冪等?什什麼情況下需要考慮冪等?你怎麼解決冪等的問題?

冪等性:就是用戶對於同一操作發起的一次請求或者多次請求的結果是一致的,不會因爲多次點擊而產生了副作用。舉個最簡單的例子,那就是支付,用戶購買商品使用約支付,支付扣款成功,但是返回結果的時候網絡異常,此時錢已經扣了,用戶再次點擊按鈕,此時會進行第二次扣款,返回結果成功,用戶查詢餘額返發現多扣錢了,流水記錄也變成了兩條...

那麼如何設計接口才能做到冪等呢?


方法一、單次支付請求,也就是直接支付了,不需要額外的數據庫操作了,這個時候發起異步請求創建一個唯一的ticketId,就是門票,這張門票只能使用一次就作廢,具體步驟如下:

異步請求獲取門票

調用支付,傳入門票

根據門票ID查詢此次操作是否存在,如果存在則表示該操作已經執行過,直接返回結果;如果不存在,支付扣款,保存結果

返回結果到客戶端

如果步驟4通信失敗,用戶再次發起請求,那麼最終結果還是一樣的


方法二、分佈式環境下各個服務相互調用

這邊就要舉例我們的系統了,我們支付的時候先要扣款,然後更新訂單,這個地方就涉及到了訂單服務以及支付服務了。

用戶調用支付,扣款成功後,更新對應訂單狀態,然後再保存流水。

而在這個地方就沒必要使用門票ticketId了,因爲會比較閒的麻煩

(支付狀態:未支付,已支付)


步驟:


1、查詢訂單支付狀態


2、如果已經支付,直接返回結果


3、如果未支付,則支付扣款並且保存流水


4、返回支付結果


如果步驟4通信失敗,用戶再次發起請求,那麼最終結果還是一樣的


對於做過支付的朋友,冪等,也可以稱之爲衝正,保證客戶端與服務端的交易一致性,避免多次扣款。

 

*filter,interceptor,spring攔截器的區別

Filter過濾器:攔截web訪問url地址。
Interceptor攔截器:攔截以 .action結尾的url,攔截Action的訪問。

Spring AOP攔截器:只能攔截Spring管理Bean的訪問(業務層Service)

 

*序列化和反序列化的概念

把對象轉化爲字節序列的過程稱之爲對象的序列化
反之,稱之爲反序列化

 

*形參和實參

int max(int a, int b)
{
函數體...;
a...;
b...;
}
此處的a,b就是形式參數,形參的作用域只在函數max內有效,max外不識別,在函數運行的時候並不給形式參數分配內存;
當我們在其他函數中(如main函數)調用max函數時,如 result = max(x, y);
此處的x,y就是實際參數,運行的時候將x的值傳遞給a,y的值傳遞給b,系統需要給x,y分配內存地址,其作用域範圍爲調用函數中.

 

*表單中不應帶有url地址,否則可能會被安全策略攔截

 

*LinkedList類實現了Queue接口,因此我們可以把LinkedList當成Queue來用。

 

*replace和replaceAll的區別

replace的參數是char和CharSequence,即可以支持字符的替換
replaceAll的參數是regex,即基於規則表達式的替換

 

*Java讀取數據流的時候,一定要指定數據流的編碼方式,否則將使用本地環境中的默認字符集,可能會存在亂碼問題。

Reader 類是 Java 的 I/O 中讀字符的父類,而 InputStream 類是讀字節的父類,InputStreamReader 類就是關聯字節到字符的橋樑,它負責在 I/O 過程中處理讀取字節到字符的轉換,而具體字節到字符的解碼實現它由 StreamDecoder 去實現,在 StreamDecoder 解碼過程中必須由用戶指定 Charset 編碼格式。值得注意的是如果你沒有指定 Charset,將使用本地環境中的默認字符集,例如在中文環境中將使用 GBK 編碼。

 

List lines=new ArrayList();  
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(fileName),"UTF-8"));  
String line = null;  
while ((line = br.readLine()) != null) {  
      lines.add(line);  
}  
br.close();  BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(fileName),"UTF-8"));  
String line = null;  
while ((line = br.readLine()) != null) {  
      lines.add(line);  
}  
br.close();  

 

 

 

 

 

 

*for循環和迭代器的區別之一:在刪除元素的時候,for循環中list刪除,會改變list索引位置,將被刪除元素後面的元素向前移動,但是用iterator循環list刪除,只會刪除內容,而保留被刪除元素的索引位置.這個區別的重要性在於,比如你向數據庫中插入元素,在插入元素之前,應該查詢元素是否重複.如果重複的話,就把源文件裏的內容刪除,比如我要插入兩條數據,這兩條數據在數據庫都已經存在了,用for循環,第一條數據肯定能檢測除重複的,但是list.remove(0)之後,後面的元素向前移動,也就是list(1)變成了list(0),但是當前的循環已經變成循環1而不是循環0了,所以就不會執行重複性校驗,然後第二條數據還是插入了數據庫中.用iterator就不會出現此類問題,原因之前已經講過了,不再贅述.

 

for (int i = 0; i < list.size(); i++) {
				List renewalList = tianAnCpfRenewalService.isRepeat(list.get(i));
				if (CollectionUtils.isNotEmpty(renewalList)) {
					logger.info("存在重複數據,分保單號爲:" + renewalList.get(0).getSubPolicyNo());
					list.remove(i);
				}
			}
Iterator it = list.iterator();
			while (it.hasNext()) {
				CpfRenewalLibrary it2 = it.next();
				List renewalList = tianAnCpfRenewalService.isRepeat(it2);
				if (CollectionUtils.isNotEmpty(renewalList)) {
					it.remove();
				}
	        }

 

 

 

 

 

 

 

 

*Java8 lambda表達式和stream的運用

 

*自定義Tag如何來寫?繼承哪個類,overwrite哪個方法?

繼承SimpleTagSupport,配置映射java類的tld文件,override  doTag()方法

 

*getAttribute()方法和getParameter()方法的區別?getAttribute()能否取到url?後面的參數值?

getAttribute()是取setAttribute()保存的值
getParameter()取參數的值 可以取到url後面的值.但是如果是中文就要進行轉碼。

 

*JSP程序中作用域從小到大的組合是

Request page session application

 

*建造者模式與抽象工廠模式的比較:
與抽象工廠模式相比, 建造者模式返回一個組裝好的完整產品 ,而抽象工廠模式返回一系列相關的產品,這些產品位於不同的產品等級結構,構成了一個產品族。
在抽象工廠模式中,客戶端實例化工廠類,然後調用工廠方法獲取所需產品對象,而在建造者模式中,客戶端可以不直接調用建造者的相關方法,而是通過指揮者類來指導如何生成對象,包括對象的組裝過程和建造步驟,它側重於一步步構造一個複雜對象,返回一個完整的對象。
如果將抽象工廠模式看成汽車配件生產工廠 ,生產一個產品族的產品,那麼建造者模式就是一個汽車組裝工廠 ,通過對部件的組裝可以返回一輛完整的汽車。

外觀模式和中介者模式比較:
外觀模式處理的是類之間複雜的依賴關係,中介者模式處理的是對象之間複雜的交互關係

 

*冒煙測試和迴歸測試的區別:
  冒煙測試是自由測試的一種。冒煙測試(smoketest)在測試中發現問題,找到了一個Bug,然後開發人員會來修復這個Bug。這時想知道這次修復是否真的解決了程序的Bug,或者是否會對其它模塊造成影響,就需要針對此問題進行專門測試,這個過程就被稱爲SmokeTest。在很多情況下,做SmokeTest是開發人員在試圖解決一個問題的時候,造成了其它功能模塊一系列的連鎖反應,原因可能是隻集中考慮了一開始的那個問題,而忽略其它的問題,這就可能引起了新的Bug。SmokeTest優點是節省測試時間,防止build失敗。缺點是覆蓋率還是比較低。
  迴歸測試是指修改了舊代碼後,重新進行測試以確認修改沒有引入新的錯誤或導致其他代碼產生錯誤。自動迴歸測試將大幅降低系統測試、維護升級等階段的成本。迴歸測試作爲軟件生命週期的一個組成部分,在整個軟件測試過程中佔有很大的工作量比重,軟件開發的各個階段都會進行多次迴歸測試。在漸進和快速迭代開發中,新版本的連續發佈使迴歸測試進行的更加頻繁,而在極端編程方法中,更是要求每天都進行若干次迴歸測試。因此,通過選擇正確的迴歸測試策略來改進迴歸測試的效率和有效性是非常有意義的。

 

*代碼應該邏輯清晰,遵循從上到下,從左到右,儘量不用循環;

CpfPaymentNoCar mainDTO = null;
for (CpfPaymentNoCar item : noCarPaymentList2) {
if (null!=item && "1".equals(item.getProductType())) {
mainDTO=item;
break;
}
}

 

*在判斷的時候能用if。。。else就用,不要用兩個if,因爲兩個if增加了判斷;在嵌入代碼的時候,最好將嵌入的功能寫成一個方法,這樣有利於理清代碼邏輯。

 

*JTA可以跨數據庫做事務

 

*構造函數執行順序:http://www.blogjava.net/rocket/archive/2008/05/27/203165.html,構造函數沒有返回值

 

*格式化xml:http://blog.csdn.net/zxcvbnmluton/article/details/5528747

 

*後臺返回到前臺如果是JSON對象,且其中一個字段有可能是JSON格式的字符串,可能會出現頁面無法顯示的情況,可以將返回對象擴大至Object,並return jObj.toString()。這種處理方式雖然不能顯示該字段,但不至於整條記錄都不顯示。

 

*正序刪除list元素和倒序刪除list元素的區別

 

public static void main(String[] args) {
		String[] str = new String[] { "a", "", "", "c", };
		List list = new ArrayList(Arrays.asList(str));
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i).equals("")) {
				list.remove(i);
			}
		}
		System.out.println(list);
	}


這段代碼原意是想刪除數組中所有的空串,可是輸出結果爲[a, ,c],是因爲在刪除元素的過程中list長度隨之改變,解決方法,倒序刪除即可,或者用一個新的list去裝刪除空串後的list,或者用迭代器刪除。

 

 

 

public static void main(String[] args) {
		String[] str = new String[] { "a", "", "", "c", };
		List list = new ArrayList(Arrays.asList(str));
		for (int i = list.size()-1; i > 0; i--) {
			if (list.get(i).equals("")) {
				list.remove(i);
			}
		}
		System.out.println(list);
	}

 

 

 

 

*使用Arrays.asList()將數組轉list會導致轉型後的list無法進行操作(add,remove),大概原因是Arrays.asLisvt() 返回java.util.Arrays$ArrayList, 而不是ArrayList。Arrays$ArrayList和ArrayList都是繼承AbstractList,remove,add等 method在AbstractList中是默認throw UnsupportedOperationException而且不作任何操作。ArrayList override這些method來對list進行操作,但是Arrays$ArrayList沒有override remove(int),add(int)等,所以throw UnsupportedOperationException。解決方法是用ArrayList包裝。

 

*使用Arrays.asList()將數組轉list會導致轉型後的list無法進行操作(add,remove),大概原因是Arrays.asLisvt() 返回java.util.Arrays$ArrayList, 而不是ArrayList。Arrays$ArrayList和ArrayList都是繼承AbstractList,remove,add等 method在AbstractList中是默認throw UnsupportedOperationException而且不作任何操作。ArrayList override這些method來對list進行操作,但是Arrays$ArrayList沒有override remove(int),add(int)等,所以throw UnsupportedOperationException。解決方法是用ArrayList包裝。

String[] str = new String[] { "a", "", "c", };
List list = new ArrayList(Arrays.asList(str));

 

 

*try...catch無法捕獲多線程異常原因:

try {
  //異步代碼
} catch (err) {
}

try {} 這一塊,直接執行到底部了,異步代碼都沒返回呢,怎麼捕捉?

 

*異常處理的原則
1、如果無法處理某個異常,那就不要捕獲它。 
2、如果捕獲了一個異常,請不要胡亂處理它。 
3、儘量在靠近異常被拋出的地方捕獲異常。 
4、在捕獲異常的地方將它記錄到日誌中,除非您打算將它重新拋出。 
5、按照您的異常處理必須多精細來構造您的方法。 
6、需要用幾種類型的異常就用幾種,尤其是對於應用程序異常。  

 

*幾種java通信(rmi,http,hessian,webservice)協議性能比較:RMI > Httpinvoker >= Hessian >> Burlap >> web serviceRMI:http://www.cnblogs.com/yeahwell/p/4684492.html

 

*RMI和Socket的對比:http://www.cnblogs.com/grefr/p/5046313.html

 

*forEach可以避免for循環中get(i)報錯的bug

 

*HashMap不是線程安全的,如果想要線程安全的HashMap,可以通過Collections類的靜態方法synchronizedMap獲得線程安全的HashMap。

Map map = Collections.synchronizedMap(new HashMap());多線程環境下,使用Hashmap進行put操作會引起死循環,導致CPU利用率接近100%,所以在併發情況下不能使用HashMap。

 

 

*用volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最的值。volatile很容易被誤用,用來進行原子性操作。

下面看一個例子,我們實現一個計數器,每次線程啓動的時候,會調用計數器inc方法,對計數器進行加一

 

 

 

public class Counter {
    public static int count = 0;
    public static void inc() {
        //這裏延遲1毫秒,使得結果明顯
        try {
            Thread.sleep(1);
        }catch (InterruptedException e) {
        }
        count++;
    }
 
    public static void main(String[] args) {
        //同時啓動1000個線程,去進行i++計算,看看實際結果
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                }
            }).start();
        }
        //這裏每次運行的值都有可能不同,可能爲1000
        System.out.println("運行結果:Counter.count=" + Counter.count);
    }
}

 

運行結果:Counter.count=995

1

實際運算結果每次可能都不一樣,本機的結果爲:運行結果:Counter.count=995,可以看出,在多線程的環境下,Counter.count並沒有期望結果是1000

1

  

1

很多人以爲,這個是多線程併發問題,只需要在變量count之前加上volatile就可以避免這個問題,那我們在修改代碼看看,看看結果是不是符合我們的期望

 

 

public class Counter {
    public volatile static int count = 0;
    public static void inc() {
        //這裏延遲1毫秒,使得結果明顯
        try {
            Thread.sleep(1);
        }catch (InterruptedException e) {
        }
        count++;
    }
 
    public static void main(String[] args) {
        //同時啓動1000個線程,去進行i++計算,看看實際結果
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                }
            }).start();
        }
        //這裏每次運行的值都有可能不同,可能爲1000
        System.out.println("運行結果:Counter.count=" + Counter.count);
    }
}

在 java 垃圾回收整理一文中,描述了jvm運行時刻內存的分配。其中有一個內存區域是jvm虛擬機棧,每一個線程運行時都有一個線程棧,

線程棧保存了線程運行時候變量值信息。當線程訪問某一個對象時候值的時候,首先通過對象的引用找到對應在堆內存的變量的值,然後把堆內存

變量的具體值load到線程本地內存中,建立一個變量副本,之後線程就不再和對象在堆內存變量值有任何關係,而是直接修改副本變量的值,

在修改完之後的某一個時刻(線程退出之前),自動把線程變量副本的值回寫到對象在堆中變量。這樣在堆中的對象的值就產生變化了。下面一幅圖

描述這寫交互

 

java volatile1

 

 

read and load 從主存複製變量到當前工作內存
use and assign  執行代碼,改變共享變量值 
store and write 用工作內存數據刷新主存相關內容

其中use and assign 可以多次出現

但是這一些操作並不是原子性,也就是 在read load之後,如果主內存count變量發生修改之後,線程工作內存中的值由於已經加載,不會產生對應的變化,所以計算出來的結果會和預期不一樣

對於volatile修飾的變量,jvm虛擬機只是保證從主內存加載到線程工作內存的值是最新的

例如假如線程1,線程2 在進行read,load 操作中,發現主內存中count的值都是5,那麼都會加載這個最新的值

在線程1堆count進行修改之後,會write到主內存中,主內存中的count變量就會變爲6

線程2由於已經進行read,load操作,在進行運算之後,也會更新主內存count的變量值爲6

導致兩個線程及時用volatile關鍵字修改之後,還是會存在併發的情況。

 

 

 

*hibernate通過sql查詢分兩種方法一種按照實體類查一種按照字段名查。

按照實體類查:

public void query() {  
    Session session = HibernateUtils.openSession();  
    String sql = "select * from student where id = ?";  
    List stus = session.createSQLQuery(sql)//  
            .addEntity(Student.class)//  
            .setParameter(0, 1)//  
            .list();  
    for (Student stu : stus) {  
        System.out.println(stu);  
    }  
}  

按照字段名查:

public Pager query(Pager pages, String sql) {
try {
Session session = getSessionFactory().getCurrentSession();
// 創建 Query 語句對象
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addScalar("id");
sqlQuery.addScalar("channelName");
sqlQuery.setResultTransformer(Transformers.aliasToBean(BaseCyChannelConfig.class));
sqlQuery.setFirstResult((pages.getPageNo() - 1) * pages.getPageSize());
sqlQuery.setMaxResults(pages.getPageSize());

int count = session.createSQLQuery(sql).list().size();
pages.setRowCount(count);
pages.setResult(sqlQuery.list());

return pages;
} catch (HibernateException e) {
logger.error("渠道信息[查詢]異常:" + e.getMessage(), e);
throw new TimeoutException("渠道信息[查詢]異常:" + e.getMessage());
}
}
第二種方法相比較第一種方法而言更靈活,且要注意兩點sql要有別名,sql不加“;”,sql要有別名的意思是比如要查詢student表裏的name,一般寫成select t.stuName from student t where t.id='001';如果sqlQuery.addScalar("name");則sql應該改成select t.stuName as name from student t where t.id='001';即addScalar中的字段要和sql中字段名稱一致,否則報錯。

 

*對於多個條件篩選,首先要確定各個條件的維度高低。

案例一:假如我有5個條件分別爲條件A,B,C,D,E,條件A的維度低於條件B,但A,C,D,E形成一個維度,B,C,D,E組成一個維度。
在查詢的時候應將A,C,D,E組成一個查詢條件,B,C,D,E組成一個查詢條件,而不是A,B,C,D,E分別查詢。因爲判斷條件越多,複雜度越高
案例二:假如我有5個條件分別爲條件A,B,C,D,E,條件A的維度高於條件B高於條件C高於條件D高於條件E
在這種情況下則必須要從A,B,C,D,E分開判斷了

 

 

*this的用法

外部函數

MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; 
Map map1 = multipartRequest.getFileMap(); 
MultipartFile excelfile = map1.get("excelfile");
InputStream input = excelfile.getInputStream();
Workbook readwb=Workbook.getWorkbook(input);
if(null == readwb){
logger.debug("積分差異化配置信息導入爲空");
result.put("status", "error");
return result;
}
Sheet readsheet = readwb.getSheet(0);   
//int rsColumns = readsheet.getColumns();
int rsRows = readsheet.getRows(); 
List list = new ArrayList();

for (int i = 1; i < rsRows; i++)  {
BaseManageRates  baseManageRates = null;
baseManageRates = new BaseManageRates(readsheet.getCell(0, i).getContents(),

readsheet.getCell(1, i).getContents().toString(),

/*外部函數baseManageRates.checkChannelEx(i)調用*/

if(!StringUtils.equalsIgnoreCase("success", baseManageRates.checkChannelEx(i))){
logger.debug(baseManageRates.checkChannelEx(i));
result.put("msg", baseManageRates.checkChannelEx(i));
result.put("status", "error");
return result;
}

baseManageRates.setCreateUser(userid);
baseManageRates.setCreateDate(new Date());
baseManageRates.setDelflag(EnumPojo.DEL_FALSE.getValue());
baseManageRates.setModifiedUser(userid);
baseManageRates.setModifiedDate(new Date());
baseManageRates.setStatus("1");
list.add(baseManageRates);
}

內部函數

public String checkChannelEx(Integer row){
if(StringUtils.isBlank(this.getChannelEx())){
return "第"+row+"行,“客戶來源”不能爲空!";
}
if (StringUtils.isNotBlank(this.getChannelEx())) {
this.setChannelCode(this.getChannelEx().substring(0, 5));   
} else {
this.setChannelCode(this.getChannelEx());   
}
return "success";
}

外部函數調用內部函數時,傳入的的是第i個對象,在內涵書中用this表示傳進來的對象,然後用get方法可以直接獲取對應的屬性值。

 

*hbm.xml和entity的關係:hbm.xml更偏重於與數據庫的關係,entity更偏重於數據庫查出來的內容,舉個例子:比如你現在要從數據庫的order表中查出一個訂單的信息,但是order表中的信息是不全的,需要關聯其他表查數據,這時候hbm只有數據庫裏對應的字段沒有其他表裏的內容(總不能把所有字段都放在order表裏吧),當然不包括one-to-one,many-to-one,many-to-many對應關係,這時候sql(包含關聯其他非order表信息的記錄)查出來的內容映射是映射到實體類上,而不是hbm.xml。簡單來所,實體類可以比hbm.xml的字段多,但不能少,而且hbm.xml裏的所有字段都必須是來自於entity的。

 

*easyui前臺傳到後臺的值可能在傳輸過程中被改名,目前沒有好的解決辦法,唯一的辦法是瀏覽器debug去尋找取不到值的字段名字,再在代碼中修改。

 

* public Pager query2(Pager pages, String sql) {
try{
Session session = getSessionFactory ( ).getCurrentSession ( );

SQLQuery sqlQuery = session.createSQLQuery(sql); 

sqlQuery.addScalar("ID");

  sqlQuery.setResultTransformer(Transformers.aliasToBean(CpfOrder.class));
sqlQuery.setFirstResult((pages.getPageNo()-1)*pages.getPageSize());
sqlQuery.setMaxResults(pages.getPageSize());

int count = session.createSQLQuery(sql).list().size(); 
pages.setRowCount(count);
pages.setResult(sqlQuery.list());

return pages;

}catch (HibernateException e) {
logger.error("渠道信息[查詢]異常:"+e.getMessage(),e);
throw new TimeoutException("[查詢]異常:"+e.getMessage());
}

這種方法的前提是sql裏的字段名稱必須顯示出來,比如,你要查order表裏的大部分字段,是不可以直接select * from order表的,因爲session.createSQLQuery(sql); 找不到對應的字段。

 

*apache服務器加入環境變量後可以使用cmd命令來啓動,關閉,重啓:httpd -k start/shutdown/restart

 

*hibernate criteria如何查詢所有數據 去掉指定字段的重複記錄hibernate criteria如何查詢所有數據 去掉指定字段的重複記錄

criteria.add(Restrictions.sqlRestriction("rowid in (select  min(t.rowid)  from 你的表名 t group by a.重複的字段)"));
Restrictions.sqlRestriction代表 where後面跟的語句,rowid是每張表必有的.所以通過子查詢可以將條件過濾掉,只剩下a.重複的字段不重複的所有數據

 

*靜態代碼在程序運行的時候首先執行且只執行一次,好處是不會頻繁調用方法,節省服務器資源。比如要做一個讀取properties文件,應該將讀取的代碼塊放到static中,

這樣就不會每次請求該方法就請求一次。

package com.httpinvoke.core.server.controller;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


import com.framework.util.Regexp;
import com.framework.util.web.RequestUtils;
import com.httpinvoke.core.server.core.util.CheckUtil;


@Controller("viewLogController")
@RequestMapping("/jobLog")
public class ViewLogController {
private final static Logger logger = LoggerFactory.getLogger(ViewLogController.class);
static final String SYSCONFIG_PROPERTIES = "sysconfig.properties";
private static String maxSize;
private static String regExp;

static{
Properties props = null;
try {
props = PropertiesLoaderUtils.loadProperties(new ClassPathResource(SYSCONFIG_PROPERTIES));
maxSize = (String) props.get("log.maxSize");
regExp = (String) props.get("log.regExp");
} catch (IOException e) {
e.printStackTrace();
}
}

@RequestMapping(value = "/getMessage", method = { RequestMethod.POST })
@ResponseBody
public String getLogs(HttpServletRequest request, HttpServletResponse response) {
String requestBody;
try {
requestBody = RequestUtils.getRequestBodyAsString(request);
} catch (IOException e) {
e.printStackTrace();
return "請求報文錯誤";
}
String path = CheckUtil.scriptingFilter(Regexp.getHardRegexpMatchResult(requestBody, "\"uri\":\"(.*?)\"").group(1));
// 報文路徑校驗
if (isValidDocPath(path) == false) {
return "文件路徑受限";
}
StringBuilder sb = new StringBuilder();
File file = new File(path);
if(!file.exists()){
return "文件不存在";
}
if(checkFileSize(file)==false){
return "文件大小超過限制";
}
BufferedReader br;
try {
br = new BufferedReader(new FileReader(file));
String str = null;
while ((str = br.readLine()) != null) {
sb.append(str);
}
br.close();
return URLEncoder.encode(sb.toString(), "UTF-8");
}catch (IOException e) {
e.printStackTrace();
return "文件讀取異常";
}

}


private boolean isValidDocPath(String path) {
logger.info(">>>>>>文件路徑:{}" + path);
logger.debug(">>>>>>>regExp:" + regExp);
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(path);
if (!m.matches()) {
return false;
}
return true;
}

private boolean checkFileSize(File doc){
logger.debug(">>>>>>>maxSize:" + maxSize);
if (doc.length() >= Long.parseLong(maxSize)) {
logger.error("文件大小:"+doc.length()+"  限制大小:"+maxSize);
return false;
}
return true;
}
}

 

*@RequestMapping(value = "/getReqLogContent.mvc")
public @ResponseBody String getReqLogContent(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String requireurl = request.getParameter("requireurl");
request.setCharacterEncoding("utf-8");
response.setContentType("text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
String reqPath = requireurl.substring(0, requireurl.indexOf("#"));
String reqCatalogue = requireurl.substring(requireurl.indexOf("#") + 1);

JSONObject reqParams = new JSONObject();
reqParams.put("uri", reqCatalogue);
String newReqParams = reqParams.toString().replace("\\", "");
logger.debug("reqPath>>>>>>>" + reqPath);
logger.debug("reqCatalogue>>>>>>>" + reqCatalogue);
String requestcontent = URLDecoder.decode(HttpUtils.postJsonBody("http://" + reqPath + ":8001/tianan_cpf/jobLog/getMessage.mvc", newReqParams), "UTF-8");
requestcontent = requestcontent.replaceAll("<","<").replaceAll(">",">") + " ";
logger.debug("報文內容: " + requestcontent);
PrintWriter out = response.getWriter(); 
out.print(requestcontent);
out.flush();
out.close();
return null;
}

servlet返回由HttpServletResponse負責,不是由方法返回的。

 

*ajax接收返回數據格式爲json或xml,解決辦法:content = content.replaceAll("<","<").replaceAll(">",">") + " ";末尾的" "是爲了解決末尾的>無法顯示添加的。

 

*hibernate openSession和getCurrentSession的區別

1)openSession和getCurrentSession的區別
  openSession必須關閉,currentSession在事務結束後自動關閉
  openSession沒有和當前線程綁定,currentSession和當前線程綁定
2)如果使用currentSession需要在hibernate.cfg.xml文件中進行配置:
  a、如果是本地事務(jdbc事務)
  thread
  b、如果是全局事務(jta事務)
  jta
  全局事務:資源管理器管理和協調的事務,可以跨越多個數據庫和進程。資源管理器一般使用XA 二階段提交協議與“企業信息系統”(EIS) 或數據庫進行交互。 
  本地事務:在單個 EIS或數據庫的本地並且限制在單個進程內的事務。本地事務不涉及多個數據來源。

 

*兩個系統之間傳輸字符串有中文字符可能會出現亂碼問題,可以嘗試用URLEncoder.encode(res, "UTF-8");URLDecoder.decode(res,"UTF-8")的方式解決。

 

*分佈式與集羣的區別
簡單說,分佈式是以縮短單個任務的執行時間來提升效率的,而集羣則是通過提高單位時間內執行的任務數來提升效率。

 

*三次法則:第一次儘管去做,第二次做類似的事情會厭煩還能勉強去做,第三次做類似事情就要重構。(事不過三,三則重構)。

 

*.size()是要遍歷一遍集合的,難怪那麼慢,所以儘量要避免用size而改用isEmpty().

 

*線程安全的概念:
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。


在Java裏,線程安全一般體現在兩個方面: 
 1、多個thread對同一個java實例的訪問(read和modify)不會相互干擾,它主要體現在關鍵字synchronized。如 ArrayList和Vector,HashMap和Hashtable(後者每個方法前都有synchronized關鍵字)。如果你在 interator一個List對象時,其它線程remove一個element,問題就出現了。 
  2、每個線程都有自己的字段,而不會在多個線程之間共享。它主要體現在java.lang.ThreadLocal類,而沒有Java關鍵字支持,如像static、transient那樣。 


servlet:非線程安全
Servlet體系結構是建立在Java多線程機制之上的,它的生命週期是由Web容器負責的。當客戶端第一次請求某個Servlet時,Servlet容器將會根據web.xml配置文件實例化這個Servlet類。當有新的客戶端請求該Servlet時,一般不會再實例化該Servlet類,也就是有多個線程在使用這個實例。因此對於servlet的成員變量,存在線程安全性問題
如何保證servlet線程安全?
  1.實現 SingleThreadModel 接口
   該接口指定了系統如何處理對同一個Servlet的調用。如果一個Servlet被這個接口指定,那麼在這個Servlet中的service方法將不會有兩個線程被同時執行,當然也就不存在線程安全的問題。
  2.同步對共享數據的操作
  使用synchronized 關鍵字能保證一次只有一個線程可以訪問被保護的區段,在本論文中的Servlet可以通過同步塊操作來保證線程的安全.
  3. 避免使用實例變量
  本實例中的線程安全問題是由實例變量造成的,只要在Servlet裏面的任何方法裏面都不使用實例變量,那麼該Servlet就是線程安全的。


struts1:非線程安全

struts1是單例模式,也就是所,Web容器(例如:Tomcat)啓動的時候,就會實例化一個Action對象,那麼所有請求都是用的這個對象。所以當遇到2個請求併發的時候,那麼其實他們調用的是同一個類,這個時候當你在Action內部定義屬性的時候,就會存在線程安全的問題


struts2:線程安全

Struts 2 Action對象爲每一個請求產生一個實例,因此沒有線程安全問題。所以我們可以在Struts2的Action裏面去定義屬性。但是Struts2由於 Action和普通的Java類沒有任何區別(也就是不用像Struts1裏面那樣去實現一個Struts的接口,有興趣的朋友可以自己去了解),所以我們可以用Spring去管理Struts2的Action,這個時候我們就要注意了,因爲當我們在spring裏面去定義bean的時候,spring默認用的是單例模式。所以在這個時候,你就要修改Spring的配置文件---即修改scope爲prototype。
爲什麼struts1中並沒有考慮到線程問題,因爲所有的代碼都是寫在execute的方法中,所有變量都是定義在裏面,所以沒有線程安全問題。
而現在的struts2就不一樣了。struts2的action中就像一個POJO一樣,定義了很多的類變量。這就有線程安全問題了。。此時,就使用scope=prototype來指定是個原型模式,而不是單例,這樣就解決了線程安全問題。每個線程都是一個新的實例.


spring mvc:非線程安全

spring的Controller默認是Singleton的,這意味着每個request過來,系統都會用原有的instance去處理,這樣導致了兩個結果:一是我們不用每次創建Controller,二是減少了對象創建和垃圾收集的時間;由於只有一個Controller的instance,當多個線程調用它的時候,它裏面的instance變量就不是線程安全的了。
如何保證 spring mvc 線程安全?
1.Controller使用ThreadLocal成員變量。
2.將spring mvc 的 Controller中聲明 scope="prototype",每次都創建新的controller .

 

*JSP中一共預先定義了9個這樣的對象,分別爲:request、response、session、application、out、pagecontext、config、page、exception

1、request對象
request 對象是 javax.servlet.httpServletRequest類型的對象。 該對象代表了客戶端的請求信息,主要用於接受通過HTTP協議傳送到服務器的數據。(包括頭信息、系統信息、請求方式以及請求參數等)。request對象的作用域爲一次請求。


2、response對象
response 代表的是對客戶端的響應,主要是將JSP容器處理過的對象傳回到客戶端。response對象也具有作用域,它只在JSP頁面內有效。


3、session對象
session 對象是由服務器自動創建的與用戶請求相關的對象。服務器爲每個用戶都生成一個session對象,用於保存該用戶的信息,跟蹤用戶的操作狀態。session對象內部使用Map類來保存數據,因此保存數據的格式爲 “Key/value”。 session對象的value可以使複雜的對象類型,而不僅僅侷限於字符串類型。


4、application對象
 application 對象可將信息保存在服務器中,直到服務器關閉,否則application對象中保存的信息會在整個應用中都有效。與session對象相比,application對象生命週期更長,類似於系統的“全局變量”。


5、out 對象
out 對象用於在Web瀏覽器內輸出信息,並且管理應用服務器上的輸出緩衝區。在使用 out 對象輸出數據時,可以對數據緩衝區進行操作,及時清除緩衝區中的殘餘數據,爲其他的輸出讓出緩衝空間。待數據輸出完畢後,要及時關閉輸出流。


6、pageContext 對象
pageContext 對象的作用是取得任何範圍的參數,通過它可以獲取 JSP頁面的out、request、reponse、session、application 等對象。pageContext對象的創建和初始化都是由容器來完成的,在JSP頁面中可以直接使用 pageContext對象。


7、config 對象
config 對象的主要作用是取得服務器的配置信息。通過 pageConext對象的 getServletConfig() 方法可以獲取一個config對象。當一個Servlet 初始化時,容器把某些信息通過 config對象傳遞給這個 Servlet。 開發者可以在web.xml 文件中爲應用程序環境中的Servlet程序和JSP頁面提供初始化參數。


8、page 對象
page 對象代表JSP本身,只有在JSP頁面內纔是合法的。 page隱含對象本質上包含當前 Servlet接口引用的變量,類似於Java編程中的 this 指針。


9、exception 對象
exception 對象的作用是顯示異常信息,只有在包含 isErrorPage="true" 的頁面中纔可以被使用,在一般的JSP頁面中使用該對象將無法編譯JSP文件。excepation對象和Java的所有對象一樣,都具有系統提供的繼承結構。exception 對象幾乎定義了所有異常情況。在Java程序中,可以使用try/catch關鍵字來處理異常情況; 如果在JSP頁面中出現沒有捕獲到的異常,就會生成 exception 對象,並把 exception 對象傳送到在page指令中設定的錯誤頁面中,然後在錯誤頁面中處理相應的 exception 對象。

 

*forward是不能誇context轉發的(例如:/context1/url 的不能用forward 轉發到 /context2/url 中去),但是redirect就可以(因爲它是瀏覽器端轉發的,是服務器端發送一個http頭部信息,瀏覽器根據這個信息再進行轉發,但有一些情況要注意,例如是不同的端口號或者ip的url話,請用絕對url,不要用相對的,否則會出問題,自己想想吧). 如果是用forward轉發的話,因爲是內部(容器中)轉發的,所以也就不用再經過過濾器(filter)的過濾了(如果要是再經過,可能會造成無限遞歸),但是redirect就要(因爲它是多次向服務器請求). 在平時的時候,我們是不可以直接用url來訪問WEB-INF目錄下的資源的,但是用forward就可以訪問得到或者用其它的辦法(只要是在服務器執行就應該行了吧).不錯,大多數情況下我們還是在用forward方式,但是仍然需要有一個原則去指導我們什麼時候用forward,什麼時候用redirect,對於這個問題,我的看法就是如果兩個處理單元(Servlet/JSP/Struts Action)沒有必然的聯繫,就要用redirect,否則用forward。那麼是什麼決定了處理單元之間的關係?當然是用例中的需求。所以考慮此類問題的時候,出發點是需求,而不是參數傳遞、性能等技術因素。我想不出讓用戶看到redirect後的地址會產生什麼樣的安全問題。因爲由於forward會屏蔽轉發的url,所以呢,當你刷新時還是向原來的Url進行請求,所以要是insert數據的話,那就是insert again.

 

*我們知道,在servlet中調用轉發、重定向的語句如下:
request.getRequestDispatcher("new.jsp").forward(request, response);   //轉發到new.jsp
response.sendRedirect("new.jsp");   //重定向到new.jsp
在jsp頁面中你也會看到通過下面的方式實現轉發:

當然也可以在jsp頁面中實現重定向:
<%response.sendRedirect("new.jsp"); %> //重定向到new.jsp
二、本質區別
解釋一
一句話,轉發是服務器行爲,重定向是客戶端行爲。爲什麼這樣說呢,這就要看兩個動作的工作流程:
轉發過程:客戶瀏覽器發送http請求——》web服務器接受此請求——》調用內部的一個方
法在容器內部完成請求處理和轉發動作——》將目標資源發送給客戶;在這裏,轉發的路徑必須是同一個web容器下的url,其不能轉向到其他的web路徑上
去,中間傳遞的是自己的容器內的request。在客戶瀏覽器路徑欄顯示的仍然是其第一次訪問的路徑,也就是說客戶是感覺不到服務器做了轉發的。轉發行爲
是瀏覽器只做了一次訪問請求。
重定向過程:客戶瀏覽器發送http請求——》web服務器接受後發送302狀態碼響應及對
應新的location給客戶瀏覽器——》客戶瀏覽器發現是302響應,則自動再發送一個新的http請求,請求url是新的location地址——》
服務器根據此請求尋找資源併發送給客戶。在這裏location可以重定向到任意URL,既然是瀏覽器重新發出了請求,則就沒有什麼request傳遞的
概念了。在客戶瀏覽器路徑欄顯示的是其重定向的路徑,客戶可以觀察到地址的變化的。重定向行爲是瀏覽器做了至少兩次的訪問請求的。
解釋二
重定向,其實是兩次request
第一次,客戶端request   A,服務器響應,並response回來,告訴瀏覽器,你應該去B。這個時候IE可以看到地址變了,而且歷史的回退按鈕也亮了。重定向可以訪問自己web應用以外的資源。在重定向的過程中,傳輸的信息會被丟失。
例子:
response.sendRedirect("loginsuccess.jsp");
請求轉發是服務器內部把對一個request/response的處理權,移交給另外一個
對於客戶端而言,它只知道自己最早請求的那個A,而不知道中間的B,甚至C、D。傳輸的信息不會丟失。
例子:
       RequestDispatcher dis=request.getRequestDispatcher(“loginsuccess.jsp”);
       Dis.forward(request,response);
解釋三
假設你去辦理某個執照
重定向:你先去了A局,A局的人說:“這個事情不歸我們管,去B局”,然後,你就從A退了出來,自己乘車去了B局。
轉發:你先去了A局,A局看了以後,知道這個事情其實應該B局來管,但是他沒有把你退回來,而是讓你坐一會兒,自己到後面辦公室聯繫了B的人,讓他們辦好後,送了過來。

 

*接口比抽象類更抽象(接口不能有屬性,只能有方法,但抽象類可以,抽象類還可以有具體實現的方法,接口則只能有抽象方法),對於抽象類,如果需要添加新的方法,可以直接在抽象類中添加具體的實現,子類可以不進行變更;而對於接口則不行,如果接口進行了變更,則所有實現這個接口的類都必須進行相應的改動。這篇博客寫得非常好,可以參考一下:點擊打開鏈接

什麼時候該用繼承,什麼時候該用組合?

如果存在一種IS-A的關係(比如Bee“是一個”Insect),並且一個類需要向另一個類暴露所有的方法接口,那麼更應該用繼承的機制。
如果存在一種HAS-A的關係(比如Bee“有一個”attack功能),那麼更應該運用組合。

注意括號裏的描述:繼承是一種 "是不是"的關係,而 接口 實現則是 "有沒有"的關係

 

*避免實現接口的同時實現接口的所有方法,可以先用抽象類實現接口,再用實現類繼承抽象類。

 

*jsp導入含有多個css和js的jsp頁面有兩種導入方法,一種<%@include file="" %> ,另一種

假如頁面taglib.jsp裏有很多css和js,他是被引用的頁面,order.jsp要引用taglib.jsp

第一種是靜態導入,大白話就是order.jsp會把taglib.jsp的內容copy到自己的頁面組成一個新的頁面;所以如果taglib.jsp的頁頭部分有

<%
String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

 

之類的,要去掉,否則會報錯,因爲同一個頁面只允許有一個。

因爲去掉了basePath,所以taglib.jsp裏導入的css,js文件要用相對路徑導入,不能用絕對路徑(因爲basePah被刪掉了)

第二種是動態導入,和靜態導入的區別是,被引用的頁面如果發生變化,則導入的頁面也會跟着改變,網上推薦用這種方法,雖然說性能可能會有降低,但是兩者相差非常有限,幾乎可以忽略不計。

用第二種方式導入,頁面必須滿足三個條件:

1.taglib.jsp必須有

 

<%
String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

2.taglib.jsp必須有

 

3.taglib.jsp導入css和js必須路徑不要以“/”開頭

如:

不要寫成

因爲basePath已經把“/”添加上了。

4.order.jsp引用taglib.jsp用絕對路徑的時候要加上${basePath}

如:

當然,如果覺得麻煩,${basePath}也可不要,但是後面的“/”一定要加上。

 

*hibernate的模板查詢並不支持count(1)/count(*)大哭

 

*Java後臺獲取session,頁面展示session中的內容(ssh框架)

登錄提交之後,在action類的方法中  
HttpServletRequest request= ServletActionContext.getRequest();
HttpSession session=request.getSession();
session.setAttribute("admin",admin);//  admin可以是一般屬性,也可以是類的實例

 

*23種設計模式

創建型:兩廠建單原(抽象工廠,工廠,建造者,單例,原型)
結構型:組合外觀裝飾器,橋接代理適配器,享元(組合,外觀,裝飾器,代理,橋接,適配器)
行爲型:觀察策略迭代器,中介責任解釋器,問命模態備忘錄(觀察,策略,迭代器,中介者,責任鏈,解釋器,訪問者,命令,模板方法,狀態,備忘錄)

 

*在一個類中,如果一個變量能夠用來描述一個類的屬性,那就定義爲成員變量,否則,它就應該定義爲局部變量。而如果一個變量在全局中都能使用(某個內容是被所有對象所共享),那麼我們就可以把這個變量用static來修飾,即爲靜態變量。

 

列表對比:

 

       成員變量、局部變量、靜態變量的區別 

 

成員變量

局部變量

靜態變量

定義位置

 在類中,方法外

方法中,或者方法的形式參數

在類中,方法外

初始化值

有默認初始化值

無,先定義,賦值後才能使用

有默認初始化值

調用方式

對象調用

---

對象調用,類名調用

存儲位置

堆中

棧中

方法區

生命週期

與對象共存亡

與方法共存亡

與類共存亡

別名

實例變量

---

類變量

 

*類和對象的區別

類是現實世界或思維世界中的實體在計算機中的反映,它將數據以及這些數據上的操作封裝在一起。   

對象是具有類類型的變量。

類是對象的抽象,而對象是類的具體實例。類是抽象的,不佔用內存,而對象是具體的,佔用存儲空間。

類是用於創建對象的藍圖,它是一個定義包括在特定類型的對象中的方法和變量的軟件模板。

 

*反射(背景): 
什麼是反射?有什麼用? 
先了解一些基本的概念:運行時,編譯時,編譯型,解釋型,類加載器,動態加載類 
什麼是編譯?將原程序翻譯成計算機語言,就是二進制代碼,在java中是將.java文件也就是源程序翻譯成.class的字節碼 
什麼是編譯時?將原程序翻譯成計算機語言的過程中,將.java翻譯爲.class文件的過程 
什麼是運行時?就是在啓動這個程序的時候,在java中是,類加載器加載.class文件,並交給jvm處理 
什麼是編譯型語言?將原程序一次性全部轉換爲二進制代碼,然後執行程序 
什麼是解釋型語言?轉換一句,執行一句,java是既編譯又解釋的語言 
編譯型語言和解釋型語言的區別:編譯型語言效率高,依賴於編譯器,但是跨平臺差,解釋型的效率低,依賴於解釋器,但跨平臺強 
什麼是類加載器?類加載器就是JVM中的類裝載器,作用就是將編譯好的.class字節碼運到檢查器進行安全檢查的,檢查通過後開始解釋執行 
什麼是運行時動態加載類? 
反射就是可以將一個程序(類)在運行的時候獲得該程序(類)的信息的機制,也就是獲得在編譯期不可能獲得的類的信息,因爲這些信息是保存在Class對象中的,而這個Class對象是在程序運行時動態加載的 
它就是可以在程序運行的時候動態裝載類,查看類的信息,生成對象,或操作生成對象。類在運行的時候,可以得到該類的信息,並且 可以動態的修改這些信息,自己能看到自己,跟照鏡子一樣,class對象是在運行的時候產生的,通過class對象操作類的信息是在運行時進行的,當運行 程序的時候,類加載器會加載真正需要的類,什麼是真正需要的呢?就是該類真正起作用,如:有該類的對象實例,或該類調用了靜態方法屬性等 
反射的作用第一,遠程方法調用,第二,通過容器得到組件的對象 

實例化Class對象

Java中實例化Class類對象的三種方式:

第一種、通過forName();

第二種、類.class

第三種、對象.getClass()

 

方式一:調用Object類的getClass()方法。這種方法的缺點是先要明確具體的類,並創建其對象。在反射中不建議使用,如果不知道具體類,在反射中也沒法使用。
方式二:利用任何數據類型都有一個靜態的屬性:.class;利用此靜態屬性來獲取對應的Class對象。此方法相對簡單,但仍需要明確類中的靜態成員,不夠擴展,不建議使用。
方式三:用Class類的方法Class.forName(className)來獲取,這個只需要知道對應類的字符串名即可,擴展性好,操作方便,建議在反射中使用。

Java的反射機制是Java特性之一,反射機制是構建框架技術的基礎所在。靈活掌握Java反射機制,對大家以後學習框架技術有很大的幫助。 

那麼什麼是Java的反射呢? 
       大家都知道,要讓Java程序能夠運行,那麼就得讓Java類要被Java虛擬機加載。Java類如果不被Java虛擬機加載,是不能正常運行的。現在我們運行的所有的程序都是在編譯期的時候就已經知道了你所需要的那個類的已經被加載了。 
Java的反射機制是在編譯並不確定是哪個類被加載了,而是在程序運行的時候才加載、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射。 

那麼Java反射有什麼作用呢? 
假如我們有兩個程序員,一個程序員在寫程序的時候,需要使用第二個程序員所寫的類,但第二個程序員並沒完成他所寫的類。那麼第一個程序員的代碼能否通過編 譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程序員在沒有得到第二個程序員所寫的類的時候,來完成自身代碼的編譯。 

Java的反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱爲Java類的“自審”。大家都用過Jcreator和eclipse。 當我們構建出一個對象的時候,去調用該對象的方法和屬性的時候。一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供用戶進行 選擇。這就是利用了Java反射的原理,是對我們創建對象的探知、自審。

 

Class類 
       要正確使用Java反射機制就得使用java.lang.Class這個類。它是Java反射機制的起源。當一個類被加載以後,Java虛擬機就會自動產 生一個Class對象。通過這個Class對象我們就能獲得加載到虛擬機當中這個Class對象對應的方法、成員以及構造方法的聲明和定義等信息。

 

反射API 
       u反射API用於反應在當前Java虛擬機中的類、接口或者對象信息 
u功能 
—獲取一個對象的類信息. 
       —獲取一個類的訪問修飾符、成員、方法、構造方法以及超類的信息. 
       —檢獲屬於一個接口的常量和方法聲明. 
       —創建一個直到程序運行期間才知道名字的類的實例. 
       —獲取並設置一個對象的成員,甚至這個成員的名字是 
   在程序運行期間才知道. 
       —檢測一個在運行期間才知道名字的對象的方法 

       利用Java反射機制我們可以很靈活的對已經加載到Java虛擬機當中的類信息進行檢測。當然這種檢測在對運行的性能上會有些減弱,所以什麼時候使用反射,就要靠業務的需求、大小,以及經驗的積累來決定。 

那麼如何利用反射API在運行的時候知道一個類的信息呢? 

代碼示例: 

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import javax.swing.JOptionPane; 

public class MyTest { 

    public MyTest(){ 
      
       String classInfo=JOptionPane.showInputDialog(null,"輸入類全路徑");//要求用戶輸入類的全路徑 
      
       try { 
           Class cla=Class.forName(classInfo);//根據類的全路徑進行類加載,返回該類的Class對象 
          
           Method[] method=cla.getDeclaredMethods();//利用得到的Class對象的自審,返回方法對象集合 
          
           for(Method me:method){//遍歷該類方法的集合 
              System.out.println(me.toString());//打印方法信息 
           } 
          
           System.out.println("********"); 
          
           Field[] field=cla.getDeclaredFields();//利用得到的Class對象的自審,返回屬性對象集合 
           for(Field me:field){ //遍歷該類屬性的集合 
              System.out.println(me.toString());//打印屬性信息 
           } 
       } catch (ClassNotFoundException e) { 
           e.printStackTrace(); 
       } 
    } 
    public static void main(String[] args) { 
       new MyTest(); 
    } 


運行的時候,我們輸入javax.swing.JFrame,那麼運行結果如下: 

public void javax.swing.JFrame.remove(java.awt.Component) 
public void javax.swing.JFrame.update(java.awt.Graphics) 
………… 

******** 
public static final int javax.swing.JFrame.EXIT_ON_CLOSE 
private int javax.swing.JFrame.defaultCloseOperation 

………… 

    大家可以發現,類的全路徑是在程序運行的時候,由用戶輸入的。所以虛擬機事先並不知道所要加載類的信息,這就是利用反射機制來對用戶輸入的類全路徑來對類自身的一個自審。從而探知該類所擁有的方法和屬性。 

通過上面代碼,大家可以知道編譯工具爲什麼能夠一按點就能列出用戶當前對象的屬性和方法了。它是先獲得用戶輸入對象的字符串,然後利用反射原理來對這樣的類進行自審,從而列出該類的方法和屬性。 


使用反射機制的步驟: 
u導入java.lang.relfect 包 

u遵循三個步驟 
第一步是獲得你想操作的類的 java.lang.Class 對象 
第二步是調用諸如 getDeclaredMethods 的方法 
第三步使用 反射API 來操作這些信息 

獲得Class對象的方法 

u如果一個類的實例已經得到,你可以使用 
       【Class c = 對象名.getClass(); 】 
      例: TextField t = new TextField(); 
              Class c = t.getClass(); 
              Class s = c.getSuperclass(); 

u如果你在編譯期知道類的名字,你可以使用如下的方法 
Class c = java.awt.Button.class; 
或者 
         Class c = Integer.TYPE; 

u如果類名在編譯期不知道, 但是在運行期可以獲得, 你可以使用下面的方法 
          Class c = Class.forName(strg); 
   這樣獲得Class類對象的方法,其實是利用反射API把指定字符串的類加載到內存中,所以也叫類加載器加載方法。這樣的話,它會把該類的靜態方法和靜態 屬性,以及靜態代碼全部加載到內存中。但這時候,對象還沒有產生。所以爲什麼靜態方法不能訪問非靜態屬性和方法。因爲靜態方法和屬性產生的時機在非靜態屬 性和方法之前。 


代碼示例: 
package  com; 

public class MyTest { 
    public static void main(String[] args) { 
       TestOne  one=null; 
       try{ 
       Class  cla=Class.forName("com.TestOne");//進行com.TestOne類加載,返回一個Class對象 
       System.out.println("********"); 
       one=(TestOne)cla.newInstance();//產生這個Class類對象的一個實例,調用該類無參的構造方法,作用等同於new TestOne() 
       }catch(Exception e){ 
           e.printStackTrace(); 
       } 
       TestOne two=new TestOne(); 
  System.out.println(one.getClass() == two.getClass());//比較兩個TestOne對象的Class對象是否是同一個對象,在這裏結果是true。說明如果兩個對象的類型相同,那麼它們會有相同的Class對象 
    } 


class TestOne{ 
    static{ 
       System.out.println("靜態代碼塊運行"); 
    } 
    TestOne(){ 
       System.out.println("構造方法"); 
    } 

  以上代碼過行的結果是: 
靜態代碼塊運行 
*********** 
構造方法 
構造方法 

代碼分析: 

在進行Class.forName("com.TestOne")的時候,實際上是對com.TestOne進行類加載,這時候,會把靜態 屬性、方法以及靜態代碼塊都加載到內存中。所以這時候會打印出"靜態代碼塊運行"。但這時候,對象卻還沒有產生。所以"構造方法"這幾個字不會打印。當執 行cla.newInstance()的時候,就是利用反射機制將Class對象生成一個該類的一個實例。這時候對象就產生了。所以打印"構造方法"。當 執行到TestOne two=new TestOne()語句時,又生成了一個對象。但這時候類已經加載完畢,靜態的東西已經加載到內存中,而靜態代碼塊只執行一次,所以不用再去加載類,所以只會打印"構造方法",而"靜態代碼塊運行"不會打印。 


反射機制不但可以例出該類對象所擁有的方法和屬性,還可以獲得該類的構造方法及通過構造方法獲得實例。也可以動態的調用這個實例的成員方法。 
代碼示例: 
package reflect; 
import java.lang.reflect.Constructor; 

public class ConstructorTest { 

    public static void main(String[] args) { 
       try { 
           //獲得指定字符串類對象 
           Class cla=Class.forName("reflect.Tests"); 
           //設置Class對象數組,用於指定構造方法類型 
           Class[] cl=new Class[]{int.class,int.class}; 
          
           //獲得Constructor構造器對象。並指定構造方法類型 
           Constructor con=cla.getConstructor(cl); 
          
           //給傳入參數賦初值 
           Object[] x={new Integer(33),new Integer(67)}; 
          
           //得到實例 
           Object obj=con.newInstance(x); 
       } catch (Exception e) { 
           e.printStackTrace(); 
       } 
    } 



class Tests{ 
    public Tests(int x,int y){ 
       System.out.println(x+"    "+y); 
    } 

運行的結果是” 33    67”。說明我們已經生成了Tests這個類的一個對象。 
同樣,也可以通過反射模式,來執行Java類的方法 

代碼示例: 
package reflect; 

import java.lang.reflect.Method; 
public class MethodTest { 

    public static void main(String[] args) { 
       try { 
           //獲得窗體類的Class對象 
           Class cla=Class.forName("javax.swing.JFrame"); 
          
           //生成窗體類的實例 
           Object obj=cla.newInstance(); 
          
       //獲得窗體類的setSize方法對象,並指定該方法參數類型爲int,int 
           Method methodSize=cla.getMethod("setSize", new Class[]{int.class,int.class}); 
          
           methodSize.invoke(obj, new Object[]{new Integer(300),new Integer(300)}); 
          
       //獲得窗體類的setSize方法對象,並指定該方法參數類型爲boolean 
           Method methodVisible=cla.getMethod("setVisible", new Class[]{boolean.class}); 
          
           methodVisible.invoke(obj, new Object[]{new Boolean(true)}); 
          
       } catch (Exception e) { 
           e.printStackTrace(); 
       } 
    } 


反射技術大量用於Java設計模式和框架技術,最常見的設計模式就是工廠模式(Factory)和單例模式(Singleton)。 

單例模式(Singleton) 

       這個模式主要作用是保證在Java應用程序中,一個類Class只有一個實例存在。在很多操作中,比如建立目錄 數據庫連接都需要這樣的單線程操作。這樣做就是爲了節省內存空間,保證我們所訪問到的都是同一個對象。 

       單例模式要求保證唯一,那麼怎麼樣才能保證唯一性呢?對了,這就是靜態變量。單例模式有以下兩種形式: 

第一種形式: 
package reflect; 

public class Singleton { 
   
    private Singleton() { 
    } 

    private static Singleton instance = new Singleton(); 

    // 這裏提供了一個供外部訪問本class的靜態方法,可以直接訪問 
    public static Singleton getInstance() { 
           return instance; 
    } 
   



class SingRun{ 
    public static void main(String[] args){ 
       //這樣的調用不被允許,因爲構造方法是私有的。 
       //Singleton x=new Singleton(); 
      
       //得到一個Singleton類實例 
       Singleton x=Singleton.getInstance(); 
      
       //得到另一個Singleton類實例 
       Singleton y=Singleton.getInstance(); 
      
       //比較x和y的地址,結果爲true。說明兩次獲得的是同一個對象 
       System.out.println(x==y); 
    } 



第二種形式: 
public class Singleton { 
    //先申明該類靜態對象 
    private static Singleton instance = null; 
   
    //創建一個靜態訪問器,獲得該類實例。加上同步,表示防止兩個線程同時進行對象的創建 
    public static synchronized Singleton getInstance() { 
      
       //如果爲空,則生成一個該類實例 
       if (instance == null){ 
           instance = new Singleton(); 
       } 
       return instance; 
    } 




工廠模式(Factory) 

       工廠模式是我們最常用的模式了,著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程序系統可以說是隨處可見。 

爲什麼工廠模式是如此常用?是因爲工廠模式利用Java反射機制和Java多態的特性可以讓我們的程序更加具有靈活性。用工廠模式進行大型項目的開發,可 以很好的進行項目並行開發。就是一個程序員和另一個程序員可以同時去書寫代碼,而不是一個程序員等到另一個程序員寫完以後再去書寫代碼。其中的粘合劑就是 接口和配置文件。 
之前說利用接口可以將調用和實現相分離。 
那麼這是怎麼樣去實現的呢?工廠模式可以爲我們解答。 

我們先來回顧一下軟件的生命週期,分析、設計、編碼、調試與測試。其中分析就是指需求分析,就是知道這個軟件要做成什麼樣子,要實現什麼樣的功能。功能知 道了,這時就要設計了。設計的時候要考慮到怎麼樣高效的實現這個項目,如果讓一個項目團隊並行開發。這時候,通常先設計接口,把接口給實現接口的程序員和 調用接口的程序員,在編碼的時候,兩個程序員可以互不影響的實現相應的功能,最後通過配置文件進行整合。 

代碼示例: 

interface InterfaceTest{ 
    public void getName();//定義獲得名字的方法 



接口有了,那麼得到這個接口,進行實現編碼的程序員應該怎麼做呢?對了,實現這個接口,重寫其中定義的方法 

接口實現方: 
class Test1 implements InterfaceTest{ 
    public void getName() { 
       System.out.println("test1"); 
    } 
   


class Test2 implements InterfaceTest{ 
    public void getName() { 
       System.out.println("test2"); 
    } 
   


大家可以發現,當接口定義好了以後,不但可以規範代碼,而且可以讓程序員有條不紊的進行功能的實現。實現接口的程序員根本不用去管,這個類要被誰去調用。 

那麼怎麼能獲得這些程序員定義的對象呢?在工廠模式裏,單獨定義一個工廠類來實現對象的生產,注意這裏返回的接口對象。 

工廠類,生產接口對象: 

class Factory{ 
    //創建私有的靜態的Properties對象 
    private static Properties pro=new Properties(); 
   
    //靜態代碼塊 
    static{ 
       try { 
          
           //加載配置文件 
           pro.load(new FileInputStream("file.txt")); 
       } catch (Exception e) { 
           e.printStackTrace(); 
       } 
    } 
   
   
    private static Factory factory=new Factory(); 
    private Factory(){} 
   
    public static Factory getFactory(){ 
       return factory; 
    } 
   
   
    public  InterfaceTest getInterface(){ 
       InterfaceTest interfaceTest=null;//定義接口對象 
      
       try { 
           //根據鍵,獲得值,這裏的值是類的全路徑 
           String classInfo=pro.getProperty("test"); 
          
           //利用反射,生成Class對象 
           Class c=Class.forName(classInfo); 
          
           //獲得該Class對象的實例 
           Object obj=c.newInstance(); 
          
           //將Object對象強轉爲接口對象 
           interfaceTest=(InterfaceTest)obj; 
       } catch (Exception e) { 
           e.printStackTrace(); 
       } 
      
       //返回接口對象 
       return interfaceTest; 
    } 


配置文件內容: 
test=factory.Test2 

通過這個類,大家可以發現,在調用的時候,得到的是個接口對象。而一個接口變量可以指向實現了這個接口的類對象。在利用反射的時候,我們並沒有直接把類的 全路徑寫出來,而是通過鍵獲得值。這樣的話,就有很大的靈活性,只要改變配置文件裏的內容,就可以改變我們調用的接口實現類,而代碼不需做任何改變。在調 用的時候,我們也是通過接口調用,甚至我們可以連這個接口實現類的名字都不知道。 

調用方: 

public class FactoryTest { 

    public static void main(String[] args) { 
       //獲得工廠類的實例 
       Factory factory=Factory.getFactory(); 
       //調用獲得接口對象的方法,獲得接口對象 
       InterfaceTest inter=factory.getInterface(); 
       //調用接口定義的方法 
       inter.getName(); 
    } 


上面的代碼就是調用方法。大家可以發現,在調用的時候,我們根本沒有管這個接口定義的方法要怎麼樣去實現它,我們只知道這個接口定義這個方法起什麼作用就 行了。上面代碼運行結果要根據配置文件來定。如果配置文件裏的內容是test=factory.Test2。那麼表示調用factory.Test2這個 類裏實現接口的方法,這時候打印“test2”。如果配置文件裏的內容是test=factory.Test1。那麼表示調用factory.Test1 這個類裏實現接口的方法,這時候打印“test1”。 

反射機制是框架技術的原理和核心部分。通過反射機制我們可以動態的通過改變配置文件(以後是XML文件)的方式來加載類、調用類方法,以及使用類屬性。這 樣的話,對於編碼和維護帶來相當大的便利。在程序進行改動的時候,也只會改動相應的功能就行了,調用的方法是不用改的。更不會一改就改全身。 

原文鏈接:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html

 

*IO:同步阻塞IO;NIO:同步非阻塞IO;AIO:異步非阻塞IO;在實際應用中通常使用Netty框架(封裝了NIO)來做做IO處理

NIO主要是爲了提升性能,使用了操作系統提供的一些更高性能的 I/O 操作。同時在 socket 中支持異步的事件處理方式,這樣就不需要在處理 socket IO 時使用多線程了。

NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。
AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與併發操作,編程比較複雜,JDK7開始支持。

I/O屬於底層操作,需要操作系統支持,併發也需要操作系統的支持,所以性能方面不同操作系統差異會比較明顯。另外NIO的非阻塞,需要一直輪詢,也是一個比較耗資源的,所以出現AIO。

對緩衝區的讀寫操作首先要知道緩衝區的下限、上限和當前位置。下面這些變量的值對Buffer類中的某些操作有着至關重要的作用:

  1. limit:所有對Buffer讀寫操作都會以limit變量的值作爲上限。
  2. position:代表對緩衝區進行讀寫時,當前遊標的位置。
  3. capacity:代表緩衝區的最大容量(一般新建一個緩衝區的時候,limit的值和capacity的值默認是相等的)。

flip、rewind、clear這三個方法便是用來設置這些值的。

 

 

 

clear方法將緩衝區清空,一般是在重新寫緩衝區時調用。

flip方法反轉緩衝區。首先將限制設置爲當前位置,然後將位置設置爲 0。如果已定義了標記,則丟棄該標記。 常與compact方法一起使用。通常情況下,在準備從緩衝區中讀取數據時調用flip方法。

以上三種方法均使用final修飾,java.nio.Buffer的所有子類均使用同一種flip、clear和rewind機制。

 

*內部類

內部類引用外部類:

[outerClass].this

外部類創建內部類:

B b = new B();
B.C c = b.new C();

 

*抽象類和接口的區別

簡單來說,
接口是公開的,裏面不能有私有的方法或變量,是用於讓別人使用的,而抽象類是可以有私有方法或私有變量的,

另外,實現接口的一定要實現接口裏定義的所有方法,而實現抽象類可以有選擇地重寫需要用到的方法,一般的應用裏,最頂級的是接口,然後是抽象類實現接口,最後纔到具體類實現。

還有,接口可以實現多重繼承,而一個類只能繼承一個超類,但可以通過繼承多個接口實現多重繼承,接口還有標識(裏面沒有任何方法,如Remote接口)和數據共享(裏面的變量全是常量)的作用.

 

*Java成員變量,實例變量,類變量

 

public class Variable{

     static int allClicks=0; //類變量(static)

     String str="hello world"; //實例變量(域,成員變量)

     public void method(){

        int i =0; //局部變量

     }

}

 

 

 

 

 

 

*多用組合,少用繼承這句話的優點是組合比繼承更靈活,組合常常利用接口,實現了所謂的“多繼承”效果。

繼承:

 

package test;

//假設我們有一個名爲Insect(昆蟲)的類,這個類包含兩個方法:1)移動move(); 2)攻擊attack()。代碼如下:
class Insect {
	private int size;
	private String color;

	// 構造函數
	public Insect(int size, String color) {
		this.size = size;
		this.color = color;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public void move() {
		System.out.println("Fly");
	}

	public void attack() {
		move(); // 假設昆蟲在攻擊前必須要先移動一次
		System.out.println("Attack");
	}
}

 
package test;

//現在,你想要定義一個名爲Bee(蜜蜂)的類。Bee(蜜蜂)是Insect(昆蟲)的一種,
//但實現了不同於Insect(昆蟲)的attack()和move方法。這時候我們可以用繼承的設計機制來實現Bee類,就像下面的代碼一樣:
class Bee extends Insect {
    public Bee(int size, String color) {
        super(size, color);
    }
 
    public void move() {
        System.out.println("Fly2");
    }
 
    public void attack() {
        move();
        super.attack();
    }
}

 


 
package test;

public class InheritanceVSComposition {
	public static void main(String[] args) {
		Insect i = new Bee(1, "red");
		i.attack();
	}
}

 
輸出結果:
Fly2
Fly2
Attack

 

使用繼承,子類覆蓋父類屬性和方法。

 

組合:

 

package test2;
//attack這一功能不再是一個方法,而是被抽象爲一個接口。
public interface Attack {
	public void move();

	public void attack();
}
package test2;

//通過對Attack接口的實現,就可以在實現類當中定義不同類型的attack。
class AttackImpl implements Attack {
	private String move;
	private String attack;

	public AttackImpl(String move, String attack) {
		this.move = move;
		this.attack = attack;
	}

	@Override
	public void move() {
		System.out.println(move);
	}

	@Override
	public void attack() {
		move();
		System.out.println(attack);
	}
}
package test2;
//因爲attack功能已經被抽象爲一個接口,所以Insect類不再需要有attack方法。
class Insect {
  private int size;
  private String color;

  public Insect(int size, String color) {
      this.size = size;
      this.color = color;
  }

  public int getSize() {
      return size;
  }

  public void setSize(int size) {
      this.size = size;
  }

  public String getColor() {
      return color;
  }

  public void setColor(String color) {
      this.color = color;
  }
}
package test2;

public class Bee extends Insect implements Attack {

	private Attack attack;

	public Bee(int size, String color, Attack attack) {
		super(size, color);
		this.attack = attack;
	}

	public void move() {
		attack.move();
	}

	public void attack() {
		attack.attack();
	}

}
package test2;


public class InheritanceVSComposition2 {
  public static void main(String[] args) {
      Bee a = new Bee(1, "black", new AttackImpl("fly", "move"));
      a.attack();


      // if you need another implementation of move()
      // there is no need to change Insect, we can quickly use new method to attack


      Bee b = new Bee(1, "black", new AttackImpl("fly2", "sting"));
      b.attack();
  }
}
輸出結果:
fly
move
fly2
sting

用組合的方法能夠自定義Bee的屬性和方法,這都是使用接口實現的,而且還實現其它接口。

 

 

*Java中continue用於處理循環中遇到例外情況跳出當前循環進行下一循環,處理此類情況有其原生的優勢。

 

* 實體類命名規則:駝峯法;數據庫命名規則:下劃線法

 

* java的hashCode方法
首先,想要明白hashCode的作用,你必須要先知道Java中的集合。總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。你知道它們的區別嗎?前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後添加到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。     於是,Java採用了哈希表的原理。哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。如果詳細講解哈希算法,那需要更多的文章篇幅,我在這裏就不介紹了。初學者可以這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際上並不是真正的在內存的物理地址,不過可以這樣理解)。     這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。所以這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。     所以, 
Java對於eqauls方法和hashCode方法是這樣規定的:
1、如果兩個對象相同,那麼它們的hashCode值一定要相同;
2、如果兩個對象的hashCode相同,它們並不一定相同     上面說的對象相同指的是用eqauls方法比較。     你當然可以不按要求去做了,但你會發現,相同的對象可以出現在Set集合中。同時,增加新元素的效率會大大下降。
如果你改寫了equal()方法,令兩個實際不是一個對象的兩個實例在邏輯上相等了,但是hashcode卻是不等。
所以要記得改寫hashcode。 
不改寫會帶來什麼後果呢?當然,比如你在用hashmap,hashtable之類的設計hashcode的類的時候,就會出麻煩了。 
至於如何改寫一個hashcode,這就有好有壞了,看各人的功底了。現在還有專門的人在研究優秀的hash算法。

也就是說 List 是一個有序的、可重複的對象容器接口,Set是一個無序的、不可重複的對象容器接口 。

後面都講了 Set 是如何實現不重複的 :爲了避免多次重複的使用 equal 方法帶來的系統負擔 ,

set 首先調用hashCode 方法來檢測 是否被佔用 如果被佔用 然後調用 equal 方法判斷被佔用的是否相同


*1、如果兩個對象equals,Java運行時環境會認爲他們的hashcode一定相等。 
2、如果兩個對象不equals,他們的hashcode有可能相等。 
3、如果兩個對象hashcode相等,他們不一定equals(我理解是由於hash衝突造成的)。 
4、如果兩個對象hashcode不相等,他們一定不equals。

*在使用方法之前先看看有沒有jdk對應的方法,可能你想到的Java早就已經幫你想到了,不需要浪費時間在實現功能上。

*hashCode()此方法比較兩個對象的內存地址是不是相等
HashMap map1 = new HashMap();
                map1.put("test", "testValue");
                map1.put("test1", "testValue1");
                map1.put("test2", "testValue2");
                
                HashMap map2 = new HashMap();
                map2.put("test", "testValue");
                map2.put("test1", "testValue1");
                map2.put("test2", "testValue2");
                
                System.out.println(map1.hashCode());
                System.out.println(map2.hashCode());
下面代碼的輸出值是相等的
所以! 在任何時候! 不要使用HashMap或一些特殊容器的Hashcode作爲key來進行緩存

*Thread.currentThread().sleep(5*1000)可以讓一閃而過的程序暫停指定時間。

*尋找程序bug的方法一般有兩種:第一debug,這是一種非常常用的方法。對於一般的問題都能很輕鬆解決;第二log日誌,適用於不變直接在控制檯運行的的項目,因爲有的系統必須放到特定的環境運行,本地無法運行,而導致本地無法檢測,放到其他環境(比如linux環境)用log打印則可以顯示錯誤信息;第三try...catch,這種方法要對異常方法比較清楚,比第一種稍難,但是使用得當比第一種更有效,可以直接在控制檯獲取異常產生的原因。


*獲取類名
.getClass().getName()一般用於新進項目組,在開發過程中對整個項目把控不到位(其實這也是沒辦法的事情,每個人都有自己的事情要做,不可能天天來給你講),可能造成找不對類名的情況,這個方法非常有效。

*request.getSession().setAttribute()和request.setAttribute()的區別
request.setattrbute()的參數只能在一個request內有效,如果重定向回客戶端,將取不到值。request.getsession.setattribute()則可以通過sessionid得到自己的session,將參數存儲在session中,進而可以多次取回其中的值,即使重定向回客戶端也無所謂,可以再不同頁面中多次使用.

*在寫代碼前應該先把整個流程寫出來而不是自上而下從頭到尾,因爲往往在一般情況下寫到後面的時候會忘掉前面寫了什麼,從而不知道現在該寫什麼。這是初級程序員必須養成的習慣。對於MVC模型,view層只提供展示功能,不應該加入邏輯,或只能加入最少量的邏輯,對於controller是要處理邏輯業務的,service層,dao層之類也不負責對邏輯業務的處理,進入service,dao層的前提是必須在保證查詢查詢能夠獲取得到數據存在與否的,而不是在service層和dao層加入邏輯業務,如果在service層和dao層加入邏輯業務那麼跟controller層就沒有區別了,因爲這大大增加了controller和service,dao的耦合度,這樣service和dao就相當於變相的controller,已經不具備複用的條件了。

*debug的時候前臺後臺一起看!!!

*字符串判斷是否爲空:workNum != null&& !"".equals(workNum.trim()),注意.trim()方法;還有StringUtils.isNotEmpty()方法。

*AJAX如果用href提交一定要禁止href跳轉否則會使局部刷新失效。禁止href跳轉的方法一般有三種



推薦第一種,簡潔,第二種最正規,第三種會把頁面拉倒最上面,算是一種變相的禁止跳轉(其實也跳轉了,只是跳轉到本頁的頂部。)

*完成一個功能後一定要備份和提交。。。

*Java三大特性:封裝,繼承,多態,具體表現在:
1.JaveBean的set,get方法(封裝);
2.Dao中有一個getList()方法,Service沒有getList()方法,但是Service繼承Dao,我們調用Service的時候可以調用getList()方法();
3.Map map = new HashMap();

*Calendar獲取時間
 Calendar ca = Calendar.getInstance();
 
int year = ca.get(Calendar.YEAR);//獲取年份
int month=ca.get(Calendar.MONTH);//獲取月份
int day=ca.get(Calendar.DATE);//獲取日
int minute=ca.get(Calendar.MINUTE);//分
System.out.println("用Calendar獲得日期是:" + year +"年"+ month +"月"+ day + "日");
 
int hour=ca.get(Calendar.HOUR);//小時
int second=ca.get(Calendar.SECOND);//秒
int WeekOfYear = ca.get(Calendar.DAY_OF_WEEK);
System.out.println("用Calendar獲得時間是:" + hour +"時"+ minute +"分"+ second +"秒");
 
System.out.println(WeekOfYear);


*獲取map的鍵和值
法一:
Map map = new HashMap();
map.put("01", "zhangsan");
map.put("02", "lisi");
map.put("03", "wangwu");
Set<Map.Entry> entrySet = map.entrySet();
Iterator<Map.Entry> it2 = entrySet.iterator();
while(it2.hasNext()){
       Map.Entry me = it2.next();//獲取Map.Entry關係對象me
       String key2 = me.getKey();//通過關係對象獲取key
       String value2 = me.getValue();//通過關係對象獲取value
       System.out.println("key: "+key2+"-->value: "+value2);
}
法二:
Map map = new HashMap();
map.put("01", "zhangsan");
map.put("02", "lisi");
map.put("03", "wangwu");
for(String key : map.keySet()){
System.out.println(key);
System.out.println(map.get(key));
}

*!a&&!b可能會出錯加兩個括號(!a)&&(!b)就解決了.

*要達到快速開發一個功能,其實很多東西都是重複的,前人已經幫我實現了,而且已經做成了工具類,在用的時候直接調用就可以了,不必要再重新設計一邊,站在巨人的肩膀上才能走得更遠。

*subString(beginIndex,endIndex)方法,前一個從0開始,表示從第幾個開始截取字符串,後一個表示截取到哪一位,不包含指的那一位,
比如
String name = "abc123";
String subName = name.subString(2,5);
System.out.println(subName);
輸出:c12 (從第三個字符開始截取,截取到第六個字符,不包括第六個字符)


*對於接口調試,如果一個方法裏有兩個類似的請求會造成第二個請求得不到返回值,如果在可能的情況下可以只使用其中一個,使用返回參數更豐富,更全面的請求方法.如果一定兩個方法都要使用那隻能採取間隔一段時間再進行第二次請求,不過一般情況下是不會這樣的,因爲這樣的設計本身就不合理,這裏只作爲一種解決方法.

*對需要有傳入參數的方法,最好將核心方法封裝起來,這樣的好處是,在遇到傳入參數爲空時,方便處理。比如在playframework中,如果要測試某個頁面,是在瀏覽器的url中直接輸入地址,就可以進入對應的頁面了,因爲初始化頁面並沒有參數傳入方法,可能就會產生無法進入頁面的情況。對於這種情況就要在方法裏對這種情況做初始化處,將核心方法抽離出去,更容易理清邏輯。根據《代碼簡潔之道》的思想,編碼應該以清晰第一,功能第二的標準嚴格執行。

*用post+servlet亂碼過濾器一般是不會出現亂碼的,但是用get仍可能會出現亂碼
解決方案:手動編碼,解碼
編碼:(script中)var userName=encodeURI($(“#userName”).attr(“value”))
解碼:(java中)String userName=URLDecoder.decode(user.getUserName,”UTF-8”)
 
*亂碼

      字符集過濾器
      encodingFilter
      org.springframework.web.filter.CharacterEncodingFilter
     
         字符集編碼
         encoding
         UTF-8
     
   
   
      encodingFilter
      /*
   
 
*form表單用js
 
*jsp頁面亂碼局部處理:
 
*instanceof方法可以檢測向下轉型是否可行
 
*類對象注重有哪些屬性,類的對象那個屬性的值
 
*final修飾的屬性和方法是不能被修改的,而且被final修飾的屬性,必須要給定初始值,系統不會自動設置,否則報錯.
 
*set,get方法是爲了體現java的封裝特性,在private修飾符下,只能通過set,get方法對屬性進行操作
 
*字符截取
 
*接口基礎知識

1, java語言不支持一個類有多個直接的父類(多繼承),但可以實現(implements)多個接口,間接的實現了多繼承.
2, 與接口相關的設計模式:
1, 定製服務模式
   設計精粒度的接口,每個接口代表相關的一組服務,通過繼承來創建複合接口
2, 適配器模式
   當每個系統之間接口不匹配時,用適配器來轉換接口
3, 默認適配器模式
   爲接口提供簡單的默認實現
4, 代理模式
   爲接口的實現類創建代理類,使用者通過代理來獲得實現類的服務
5, 標識類型模式
   用接口來標識一種沒有任何行爲的抽象類型
6, 常量接口模式
   在接口中定義靜態常量,在其它類中通過importstatic語句引入這些常量
3, 接口的特徵歸納:
1, 接口中的成員變量默認都是public,static,final類型的(都可省略),必須被顯示初始化,即接口中的成員變量爲常量(大寫,單詞之間用"_"分隔)
2, 接口中的方法默認都是public,abstract類型的(都可省略),沒有方法體,不能被實例化
   public interface A
   {
    int CONST = 1; //合法,CONST默認爲public,static,final類型
    void method(); //合法,method()默認爲public,abstract類型
    public abstract void method2();//method2()顯示聲明爲public,abstract類型
   }
3, 接口中只能包含public,static,final類型的成員變量和public,abstract類型的成員方法
   public interface A
   {
    int var; //錯,var是常量,必須顯示初始化 
    void method(){...};   //錯,接口中只能包含抽象方法
    protected void method2(); //錯,接口中的方法必須是public類型
    static void method3(){...};   //錯,接口中不能包含靜態方法
   }
4, 接口中沒有構造方法,不能被實例化
   public interface A
   {
    public A(){...}; //錯,接口中不能包含構造方法
    void method();
   }
5, 一個接口不能實現(implements)另一個接口,但它可以繼承多個其它的接口
   public interface A
   {
    void methodA();
   }
   public interface B
   {
    void methodB();
   }
   public interface C extends A, B   //C稱爲複合接口
   {
    void methodC();
   }
   public interface C implementsA{...}   //錯
6, 接口必須通過類來實現它的抽象方法
   public class A implements B{...}
7, 當類實現了某個接口時,它必須實現接口中的所有抽象方法,否則這個類必須聲明爲抽象的
8, 不允許創建接口的實例(實例化),但允許定義接口類型的引用變量,該引用變量引用實現了這個接口的類的實例
   public class B implements A{}
   A a = new B(); //引用變量a被定義爲A接口類型,引用了B實例
   A a = new A(); //錯誤,接口不允許實例化
9, 一個類只能繼承一個直接的父類,但可以實現多個接口,間接的實現了多繼承.
   public class A extends B implements C,D{...} //B爲class,C,D爲interface
4, 通過接口,可以方便地對已經存在的系統進行自下而上的抽象,對於任意兩個類,不管它們是否屬於同一個父類,只有它
們存在相同的功能,就能從中抽象出一個接口類型.對於已經存在的繼承樹,可以方便的從類中抽象出新的接口,但從類
中抽象出新的抽象類卻不那麼容易,因此接口更有利於軟件系統的維護與重構.對於兩個系統,通過接口交互比通過抽象
類交互能獲得更好的鬆耦合.
5, 接口是構建鬆耦合軟件系統的重要法寶,由於接口用於描述系統對外提供的所有服務,因此接口中的成員變量和方法都
必須是public類型的,確保外部使用者能訪問它們,接口僅僅描述系統能做什麼,但不指明如何去做,所有接口中的方法
都是抽象方法,接口不涉及和任何具體實例相關的細節,因此接口沒有構造方法,不能被實例化,沒有實例變量.二, 比較抽象類與接口
1, 抽象類與接口都位於繼承樹的上層
相同點
1, 代表系統的抽象層,當一個系統使用一顆繼承樹上的類時,應該儘量把引用變量聲明爲繼承樹的上層抽象類型,
   這樣可以提高兩個系統之間的送耦合
2, 都不能被實例化
3, 都包含抽象方法,這些抽象方法用於描述系統能提供哪些服務,但不提供具體的實現
不同點:
1, 在抽象類中可以爲部分方法提供默認的實現,從而避免在子類中重複實現它們,這是抽象類的優勢,但這一優勢
   限制了多繼承,而接口中只能包含抽象方法.
   由於在抽象類中允許加入具體方法,因此擴展抽象類的功能,即向抽象類中添加具體方法,不會對它的子類造
   成影響,而對於接口,一旦接口被公佈,就必須非常穩定,因爲隨意在接口中添加抽象方法,會影響到所有的實
   現類,這些實現類要麼實現新增的抽象方法,要麼聲明爲抽象類
2, 一個類只能繼承一個直接的父類,這個父類可能是抽象類,但一個類可以實現多個接口,這是接口的優勢,但這
   一優勢是以不允許爲任何方法提供實現作爲代價的三, 爲什麼Java語言不允許多重繼承呢?
當子類覆蓋父類的實例方法或隱藏父類的成員變量及靜態方法時,Java虛擬機採用不同的綁定規則,假如還允許
一個類有多個直接的父類,那麼會使綁定規則更加複雜,因此,爲了簡化系統結構設計和動態綁定機制,Java語言
禁止多重繼承.
而接口中只有抽象方法,沒有實例變量和靜態方法,只有接口的實現類纔會實現接口的抽象方法(接口中的抽象方
法是通過類來實現的),因此,一個類即使有多個接口,也不會增加Java虛擬機進行動態綁定的複雜度.因爲Java虛
擬機永遠不會把方法與接口綁定,而只會把方法與它的實現類綁定.四, 使用接口和抽象類的總體原則:
1, 用接口作爲系統與外界交互的窗口
   站在外界使用者(另一個系統)的角度,接口向使用者承諾系統能提供哪些服務,站在系統本身的角度,接口制定
   系統必須實現哪些服務,接口是系統中最高層次的抽象類型.通過接口交互可以提高兩個系統之間的送耦合
   系統A通過系統B進行交互,是指系統A訪問系統B時,
   把引用變量聲明爲系統B中的接口類型,該引用變量引用系統B中接口的實現類的實例.
   public interface B
   {
   }
   public class C implements B
   {
   }
   public class A
   {
   }
   B a = new C();
2, 接口本身必須非常穩定,接口一旦制定,就不允許隨意更改,否則對外面使用者及系統本身造成影響
3, 用抽象類來定製系統中的擴展點
   抽象類來完成部分實現,還要一些功能通過它的子類來實現                   2008/1/9
一, Java多態機制中的綁定規則深入剖析
class Base
{
   String var = "BaseVar";   //實例變量
   static String staticVar ="StaticBaseVar";   //靜態變量   void method()   //實例方法
   {
    System.out.println("Basemethod");
   }  static void staticMethod()   //靜態方法
   {
    System.out.println("Static Basemethod");
   }
}public class Sub extends Base
{
   String var = "SubVar";   //實例變量
   static String staticVar ="StaticSubVar";   //靜態變量

   void method()   //隱藏父類的method()方法
   {
    System.out.println("Submethod");
   }  static void staticMethod()   //隱藏父類的staticMethod()方法
   {
    System.out.println("Static Submethod"); 
   }  String subVar = "Var only belonging to Sub";   void subMethod()
   {
    System.out.println("method onlybelonging to Sub");
   }  public static void main(String args[])
   {
    //引用變量who被聲明爲Base類型,引用Sub類的實例
    Base who = new Sub();  


    //成員變量(靜態變量,實例變量)與引用變量所聲明的類型(Base類型)的成員變量綁定
    System.out.println("who.var ="+who.var);   //所以,打印Base類的var變量
   System.out.println("who.staticVar = "+who.staticVar); //所以,打印Base類的staticVar變量    //實例方法與引用變量實際引用的對象(Sub對象)的方法綁定
    who.method();   //所以,打印Sub實例的method()方法    //靜態方法與引用變量所聲明的類型(Base類型)的方法綁定
    who.staticMethod();   //所以,打印Base類的staticMethod()方法
   }
}


【分析過程】
1, 對於一個引用類型的變量,Java編譯器按照它聲明的類型來處理.
   例如在以下代碼中,編譯器認爲who是Base類型的引用變量,不存在subVar成員變量喝subMethod()方法,編譯報錯
   Base who = new Sub(); //引用變量who被聲明爲Base類型,引用Sub類的實例
   who.subVar = "123";   //編譯錯,在Base類中沒有subVar屬性
   who.subMethod();   //編譯錯,在Base類中沒有submethod()方法
   如果要訪問Sub類的成員,必須通過強制類型轉換:
   Base who = new Sub();
   //把Base引用類型的who成員變量強制轉換爲Sub引用類型
   //把引用變量轉換爲子類的類型稱爲向下轉型,把引用變量轉換爲父類的類型稱爲向上轉型
   ((Sub)who).subVar = "123";
   ((Sub)who).subMethod();
   Java編譯器允許在具有直接或間接繼承關係的類之間進行類型轉換,對於向上轉型,Java編譯器會自動進行,對於
   向下轉型,需要進行強制類型轉換
   如果兩種類型之間沒有繼續關係,即不在繼承樹的同一個繼承分支上,那麼Java編譯器不允許進行類型轉換
2, 對於一個引用類型的變量,運行時Java虛擬機按照它實際引用的對象來處理
   例如以下代碼雖編譯可通過,但運行時會拋出ClassCastException運行時異常
   Base who = new Base();   //who引用Base類的實例
   Sub s = (Sub)who;   //運行時會拋出ClassCastException
   在運行時,子類的對象可以轉換爲父類類型,而父類的對象實際上無法轉換爲子類類型
3, 在運行時環境中,通過引用類型變量來訪問所引用對象的方法和屬性時,Java虛擬機採用以下綁定規則:
   1, 實例方法與引用變量實際引用的對象的方法綁定,這種綁定屬於動態綁定,因爲是在運行時由Java虛擬機
    動態決定的
   2, 靜態方法與引用變量所聲明的類型的方法綁定,這種綁定屬於靜態綁定,因爲實際上是在編譯階段就已經
    綁定
   3, 成員變量(靜態變量,實例變量)與引用變量所聲明的類型的成員變量綁定,這種綁定屬於靜態綁定,因爲
    實際上是在編譯階段就已經綁定
 
 
*Integer.parseInt()把String   型轉換爲Int型,  
Integer.valueOf()把String   型轉換爲Integer對象。
大概知道一點了,就是說Integer.valueOf(S)是針對包裝類來說的,而Integer.parseInt(s) 是針對變量而言
 
*request是請求,即客服端發來的請求、
response是響應,是服務器做出的響應
Response.Redirect()是重定向到另外一個頁面,這裏面要分兩個步驟,步驟一,服務器返回信息,讓你的瀏覽器重新發送請求到要轉向的頁面,步驟二,你的瀏覽器收到返回信息(也就是那個Response.Redirect()),重新請求要轉到的那個頁面,這裏面的兩個步驟是先response,再request
 
*JSP 的3個內置對象request,session,application,其實都有一個作用域,這些對象內部有一個Map成員用於存放數據,比如session對象的setAttribute(key,value)就是將value存入session的作用域,getAttribute(key)則是取出作用域中相應的值。三者的差別在於request在一次請求後清空map,session在一次會話結束後清空map,application則在服務器重啓後清空map,因此可以根據作用域的不同,來控制對象的生命週期。
 
*String driver ="com.mysql.jdbc.Driver";
         Stringurl = "jdbc:mysql://localhost:3306/student?user=root&password=mw130283&characterEncoding=GBK";
         Connectioncon = null;
?是請求路徑和參數的分隔符。?之前的是請求路徑,?之後是以&連接的參數字符串
 
*數組定義:數據類型[] 參數=new 數據類型[數組大小];
 
*排序方法Arrays.sort();在java.util.*中;
 
*for(int i:num)一般用於輸出;
 
*當兩個指針相等時,若一個指針發生變化,則另一個也跟着變化;
 
*if(input.hasNextInt())可以直接判斷是否是數字;
 
*staic方法一般放在main函數之外,但是可以放在同一個類;
 
*stack方法
 
*String.format("%.3f %.3f\n",area, (Gx + Gy)/ area)格式化輸出;
 
*凸多邊形面積:各個座標的橫縱座標交叉相乘再相減,然後將結果相加,最後再除以2.
 
網頁和數據庫的連接:action,bean,servlet,util
 
泛化(空三角形):泛化關係通常表示繼承關係
聚集(空菱形):就像機場和飛機的關係,機場可以沒有飛機,飛機也不一定在機場;
組成(滿菱形):就像汽車和輪胎,汽車必須有輪胎,輪胎離開汽車也沒有意義。
 
 java中ArrayList 、LinkList區別
1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。
2.對於隨機訪問get和set,ArrayList優於LinkedList,因爲ArrayList可以隨機定位,而LinkedList要移動指針一步一步的移動到節點處。(參考數組與鏈表來思考)
3.對於新增和刪除操作add和remove,LinedList比較佔優勢,只需要對指針進行修改即可,而ArrayList要移動數據來填補被刪除的對象的空間。
 
String[]strs=str.trim().split("0");
str.trim()將字符串首尾的空格去掉,然後split("0")將去除空格後的字符串以字符0 分割(不包含該字符0)成若干子字符串賦給字符串數組strs。
 
排序方法Arrays.sort();在java.util.*中;
 
for(int i:num)一般用於輸出;
 
當兩個指針相等時,若一個指針發生變化,則另一個也跟着變化;
 
if(input.hasNextInt())可以直接判斷是否是數字;
 
輸入Scanner input=new Scanner(System.in);
         int r=input.nextInt();
 
對象:指的是客觀的事物
 
面向對象:找到對象---調用對象的方法
 
類:對象的抽取,對象的模板,客觀事物在人腦中的主觀的反映
 
流和文件使用完了必須要關閉.
 
實例變量:1.定義在類以內,方法以外的 2.有默認值,可以先不賦值 3.作用域至少在全類內有效 4.可以和局部變量命名一致,局部優先
 
方法(聲明能幹什麼)有返回類型,方法名,參數名,拋出異常
 
方法體:怎麼去幹
 
重載(overloading)1、方法名相同,2參數列表不同3.返回值沒有要求
 
編譯時多態:就近類型匹配
 
構造方法:1.方法名要和類名相同 2.沒有返回值,但是不能有關鍵字void 3.用戶不能直接調用,必須通過new關鍵字自動調用它
 
構造方法的重載:多個構造函數
 
java中只允許單繼承(繼承一個類)
 
super:調用父類的構造函數,調用父類被覆蓋的
 
數組:存放相同數據類型的一組數據
 
聲明:Int[] a;或int a[];分配空間:int[] a=new int[4];數組有默認值
 
this:1.指代當前對象2.調用其他構造函數,只能出現在構造函數中
 
訪問對象:通過引用訪問對象例如:Cat c=new Cat(); c.eat();c.name;
 
在構造對象時只能調用一個構造函數例如:Cat c=new Cat("哺乳動物",2,"喵喵");
 
構造對象的步驟:1.分配空間2.初始化實例變量,初始化代碼塊3.調用構造函數(賦值)
 
封裝:public ---公開的 private---私有的,只能在本類中使用;所謂的封裝就是把屬性和方法都用private修飾,如果不加修飾符則默認爲default,即屬性要私有,方法要公開。
 
訪問控制符:private :同一類中 friendly:同一類中和同一包中 protected:同一類中,同一包中還有不同包中的子類 public:同一類中,同一包中,不同包中的子類,不同包中的非子類
 
override(重寫):發生在父子類之間,要求:方法名,返回值,參數表都必須相同,改寫父類的實現,把父類中一般的實現改成特殊的實現,修飾符只能更寬。使用步驟:1.分配空間2.初始化父類對象(初始化父類屬性,調用父類構造函數)3.初始化子類屬性4.調用子類構造函數
 
多態:把子類當成父類來看 Animal(引用 new類型也叫主觀類型) a=new Dog()(對象類型);以主觀類型爲主。特點:1.對象類型不變2.子類引用只能調用父類中的方法3.運行時,調用子類覆蓋後的方法(運行時多態)
 
數組的擴充:System.array(a,0,b,0,a.length)把數組A中下標從0-a拷貝到數組B的下標從0-b中,長度爲:a.length
 
static :修飾屬性,修飾方法(要求:共有的方法用類名直接調用,在靜態方法中不能使用非靜態的,而非靜態的方法中可以使用靜態變量成員,沒有多態),還有初始化代碼塊
 
變量分爲:局部變量,實例變量(成員變量),還有類變量,其中實例變量和類變量稱爲屬性,屬性和方法稱爲成員。
 
public class Sample
{
 private int a=1;  //實例變量
 
 public void b()
 {
  inta=2;  //局部變量
 System.out.println("局部變量:a="+a);
 System.out.println("實例變量:a="+this.a);//局部變量的作用域內引用實例變量:this.變量名
 }
 public static void main(String[] args)
 {
  newSample().b();
 }
}
 
final:修飾變量(局部變量)用final修飾的變量都大寫;修飾類不能被子類繼承 修飾方法:不能被覆蓋final類中所有的方法都是final
 
abstract:類可以聲明引用,不能new對象 方法:只有聲明,沒有方法的實現,如果類中有抽象方法則一定是抽象類;如果子類既想繼承抽象方法又想new對象,則子類必須實現父類中所有的方法,他將方法的聲明和實現分離,聲明放在父類,實現放在子類更體現繼承共性的父類說法。
 
String:str.inter(),獲得相同的空間,返回值是字符串在串池中的地址,String型的屬性都是final
 
StringBuffer:內容可以改變,不會創建新對象,不會改變原有的地址
 
.append(" ")方法在字符串的後面追加另一個字符串
 
str[i]=String.ValueOf(i),把i看成一個字符串賦給str[i]
 
long time=System.nanoTime();System.out.println(time);輸出當前系統的時間
 
StringBuilder和StringBuffer相同,但是速度要快
 
接口就是一個特殊的抽象類1.所有屬性都是公開的靜態常量2.所有的方法都是公開的抽象方法3,沒有構造函數抽象類中有構造函數
 
特點:支持多繼承(接口間的繼承),一個類可以有一個父類實現多個接口
 
接口的作用:1.多繼承,接口是次要的抽象,不會破壞類的樹形繼承關係,首先繼承父類,其次纔是實現接口2.標準,弱耦合性的體現,解耦合的工具3.接口回調,先有接口和接口的使用者,再有接口的實現者
 
Object o引用指向任何對象,方法:所有類都有方法getClass對象類型
 
finalize()對象在垃圾回收時系統自動調用GC
 
Scanner和Buffer流一起使用只能和BufferedInputStream,BufferedOutputStream,不能和BufferedReader,BufferedWriter連用,Scanner的構造方法是InputStream
 
BufferedReader中才有readLine()方法
 
圖片的複製可以用BufferedInputStream,BufferedOutputStream,用其他的實現類如FileInputStream,FileOutputStream不行(圖片受損,無法打開)
 
File f = new File(/home/li/java.txt);
這名話並不真正會創建 /home/li/java.txt 這個文件,而是創建了一個代表 /home/li/java.txt 這個文件的一個File對象, 你需要判斷,如果文件不存在,再創建,如:
       File f = new File("/home/li/java.txt");
       if(!f.exists()){//判斷文件是否真正存在,如果不存在,創建一個;
           f.createNewFile();
       }
 
Java中字符輸入輸出流與字節輸入輸出流的區別
主要區別
實現上字節輸出流是繼承OutputStream 而字符輸出流繼承OutputStreamWriter
應用上字符流是專門用來處理文字的,包含了對多國語言的支持,而字節流主要是用來處理文字以外的如binary文件。
 
InputStream,OutputStream是抽象類,FileInputStream等是他的具體實現類
InputStream myfile=newBufferInputStream(new FileInputStream(new File("c:\\ERER.txt")));
Stream類                                                       Reader,Writer類
InputStream,OutputStream(抽象類)                         InputStreamReader,OutputStreamWriter(實現類)
FileInputStream,FileOutputStream(實現類)                     FileReader,FileWriter(實現類)
BufferedInputStream,BufferedOutputStream(實現類)           BufferedReader,BufferedWriter(實現類)
PrintStream(實現類)                                             PrintWriter(實現類)
 
FilterInputStream,FilterOutputStream(實現類)
DataInputStream,DataOutputStream(實現類)

 

ObjectInputStream,ObjectOutputStream(實現類)

 

 

*處理流和節點流的區別

1)按照流是否直接與特定的地方 (如磁盤、內存、設備等) 相連,分爲節點流和處理流兩類。節點流可以從或向一個特定的地方(節點)讀寫數據;處理流是對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀寫。
2)處理流的構造方法總是以一個其他的流對象做參數。一個流對象經過其他流的多次包裝,稱爲流的鏈接。 

 

 更多Java高級部分請看我的收藏》點擊打開鏈接
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章