接口
--------------------------------------------------------------------------------
1、接口(是一種規範)
1.接口名/接口文件與類相似,也是用.java文件編寫
2.關鍵字 interface 接口名命名規範與類名相同 習慣上可使用I開頭表示
3. 接口額訪問修飾符只能使用public和default修飾不能使用private和protected
在接口中默認缺省爲public
4.接口中的全部屬性只能爲公開的靜態的常量
並且public/static/final均可省略
5.接口中的全部方法必須爲公共的抽象方法 且抽象方法中的abstract/public可省略
省略後依然是公開的java
--------------------------------------------------------------------------------
2、實現類實現接口
1.一個類實現接口使用Implements關鍵字
實現類實現一個接口必須重寫接口中的全部抽象方法 除非是抽象類
2.一個類能夠實現多個接口 多個接口之間用逗號間隔
3.接口的引用能夠指向其實現類的對象。相似於父類引用指向子類對象
所以可使用接口實現多態
--------------------------------------------------------------------------------
3、接口繼承接口
1.接口能夠繼承接口,使用extends關鍵字;接口的繼承與類相同
2.接口能夠多繼承 使用多個父接口,使用逗號分割,
子接口繼承父接口將擁有父接口的全部抽象方法
--------------------------------------------------------------------------------
4、接口的優勢
1.能夠被多繼承
2.設計和實現徹底分離
3.更天然的使用多肽
4.更容易搭建程序框架
5.更容易更換實現
--------------------------------------------------------------------------------
5、接口和抽象類的區別
1.本質區別 關鍵字不一樣 class interface
子類繼承抽象類 子類必須與父類是一類事物 必須符合"is a"關係
例如Chinese are peope
接口只是對功能的擴展,多個實現類實現接口時
並不要求全部的抽象類是一類事物
接口符合"like a"關係,理解爲X具有一個X功能
例 人\狗 都有吃飯功能 均可以實現吃飯接口
可是二者吃飯功能不能從同一父類中繼承
2.抽象類是類 接口是規範
3.接口可繼承接口 並可多繼承接口可多實現 但類只能單根繼承
4.接口只能作方法聲明,抽象類中能夠作方法聲明,也能夠作方法實現
5.抽象類可以保證明現的層次關係,而藉口則是可以更有效地分離行爲和實現
6.抽象類能夠有本身屬性 接口智能有靜態常量
7.接口中智能有抽象方法,抽象類中能夠有抽象方法
集合框架
對經常使用的數據結構和算法作一些規範(接口)和實現(具體實現接口的類)
--------------------------------------------------------------------------------
1、集合框架的接口
Collection:存儲一組不惟一 無序的對象
List:存儲一組不惟一 有序
Set存儲一組惟一 無序的對象
Map存儲一組鍵值對象,提供key到value的映射(key不能重複)算法
--------------------------------------------------------------------------------
2、List 經常使用方法 (ArraysList:長度可變的數組)apache
【List接口】數組
一、經常使用方法:
① add():在列表的最後添加元素;
② add(int index,E element):在列表的指定位置插入元素;
③ size():返回當前列表的元素個數;
④ get(int index):返回下標爲index的元素。
若是沒有泛型約束,返回Object類型,須要強轉;若是有泛型約束,直接返回泛型類型,無需強轉。
⑤ clear():清除列表中的全部數據
isEmpty():檢測列表是否爲空
⑥ contains():傳入一個對象,檢測列表中是否包含該對象。
若是傳入的是String和基本數據類型,能夠直接比對
若是傳入的是實體類,則默認只比對兩個對象的地址。所以,須要在實體類重寫equals()方法;
Tips:
String s = "123";
="123".equals(s);//這個順序能夠防止空指針
⑦ indexOf():傳入一個對象,返回該對象在列表中首次出現的地址。
lastIdexOf():傳入一個對象,返回該對象在列表中最後一次出現的地址。
⑧remove():傳入一個下標,或者一個對象,刪除指定元素;
若是傳入下標,返回被刪除的對象,若是下標大於size(),會報下標越界異常;
若是傳入對象,則要求重寫equals方法,返回true或false表示刪除是否成功
⑨set(index, obj):用新傳入的對象,將指定位置的元素替換掉;
返回被替換掉的元素對象。
⑩subList(1,3):截取一個子列表,返回List類型
⑪toArray() 將列表轉爲數組 返回一個object類型的數據
--------------------------------------------------------------------------------
3、使用iterator迭代器遍歷列表
1.使用列表調用 .iterator()返回一個迭代器對象
2.使用迭代器對象調用.hasNext()判斷是否有下一條數據
3.使用迭代器對象調用.next()取出下一條數據
--------------------------------------------------------------------------------
4、ArrayList LinkedList
1.ArrayList 實現一個長度可變的數組,在內存空間中開闢一串連續的空間,
與數組的區別在於長度能夠隨意的改變,
這隻能存儲結構在尋歡遍歷和隨機訪問元素的速度比較快
2.LinkedList 使用鏈表結構存儲數據,再插入和刪除元素時速度很是快
特有方法
①addFirst():開頭插入元素
addLast():結尾插入元素
②removeFirst() 刪除第一個元素,並返回被刪除的元素
removeLast()刪除最後一個元素,並返回被刪除的元素
③getFirst()返回列表的第一個元素 不刪除
getLast()返回列表的最後一個元素 不刪除緩存
Set接口
1.經常使用方法:與List接口基本相同
可是,因爲set接口中的元素是無序的,所以沒有與下標相關的方法安全
2.Set接口的特色:惟一,無序服務器
取出set方法
使用for each遍歷
使用迭代器遍歷數據結構
3.HashSet 底層調用HashMap的方法 傳入數據後 根據數據的hashcode進行散列運算
獲得一個散列值後在進行運算,肯定數據在序列中存儲的位置多線程
4.HashSet如何肯定一個對象是否相等
先判斷對象的hashcode()是否相等 若不等 確定不是一個對象
若相等 繼續判斷equals()方法;
重寫equals()方法app
因此使用HashSet存儲實體對象時,必須重寫對象的hashCode() 和equals()兩個方法
LinkedHashSet 在HashSet的基礎上,新增一個鏈表
用鏈表來記錄HashSet中元素放入的順序,所以使用迭代器遍歷時,能夠按照放入的順序依次
讀出元素
comparator 須要單獨一個比較類進行實現,重寫compare()的方法,實例化TreeSet的對象,須要哦轉入這個比較類的對象
Map
1.Map 接口特色 以鍵值對的形式存儲數據,以鍵取值,鍵不能重複值能夠重複
2.put(key value) 最後追加一個鍵值
get 經過鍵取值
clear清除全部數據
HashMap 與Hashtable區別
1.後者線程安全 前者不安全
2.後者鍵不能爲null 前者能夠
HashMap和Hashtable的區別
二者最主要的區別在於Hashtable是線程安全,而HashMap則非線程安全。Hashtable的實現方法裏面都添加了synchronized關鍵字來確保線程同步,所以相對而言HashMap性能會高一些,咱們平時使用時若無特殊需求建議使用HashMap,在多線程環境下若使用HashMap須要使用Collections.synchronizedMap()方法來獲取一個線程安全的集合(Collections.synchronizedMap()實現原理是Collections定義了一個SynchronizedMap的內部類,這個類實現了Map接口,在調用方法時使用synchronized來保證線程同步,固然了實際上操做的仍是咱們傳入的HashMap實例,簡單的說就是Collections.synchronizedMap()方法幫咱們在操做HashMap時自動添加了synchronized來實現線程同步,相似的其它Collections.synchronizedXX方法也是相似原理。
HashMap可使用null做爲key,不過建議仍是儘可能避免這樣使用。HashMap以null做爲key時,老是存儲在table數組的第一個節點上。而Hashtable則不容許null做爲key。
HashMap繼承了AbstractMap,HashTable繼承Dictionary抽象類,二者均實現Map接口。
HashMap的初始容量爲16,Hashtable初始容量爲11,二者的填充因子默認都是0.75。
HashMap擴容時是當前容量翻倍即:capacity*2,Hashtable擴容時是容量翻倍+1即:capacity*2+1。
HashMap和Hashtable的底層實現都是數組+鏈表結構實現。
二者計算hash的方法不一樣:
Hashtable計算hash是直接使用key的hashcode對table數組的長度直接進行取模:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
HashMap計算hash對key的hashcode進行了二次hash,以得到更好的散列值,而後對table數組長度取摸:
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
在HashMap 中,null 能夠做爲鍵,這樣的鍵只有一個;能夠有一個或多個鍵所對
應的值爲null。當get()方法返回null 值時,既能夠表示HashMap 中沒有該鍵,也可
以表示該鍵所對應的值爲null。所以,在HashMap 中不能用get()方法來判斷HashM
ap 中是否存在某個鍵,而應該用containsKey()方法來判斷。Hashtable 的鍵值都不能
爲null,因此能夠用get()方法來判斷是否含有某個鍵。
LinkedHashMap
可使用鏈表 計入數據放入的次序
Collections是Java中專門用於操做的集合工具類
Collection是一個接口
sort方法 對 集合中的數據進行排序
若是集合存儲的是一個實體對象那麼
1 實體類實現Comparable接口,並重寫CompareTo方法
2.在sort的第二個參數,傳入比較器,比較器需實現comparable接口,並重寫compare方法
【泛型】
一、泛型就是「參數化類型」。在定義類型時,不將類型定死。而是採用泛型參數的形式進行定義。調用時傳入具體的泛型參數。
【泛型類的特色】
在聲明類的時候,進行泛型約束,這樣的類叫泛型類。
class Test<T>{
//<T>能夠理解爲泛型聲明時的形參,能夠用任何字母代替。經常使用T N E
//在泛型類中,T就能夠當作特殊的數據類型進行使用
private T arg;
}
//泛型調用時,須要將實際的數據類型進行傳入
Test<String> test1 = new Test<String> ("姜浩真帥!");
Test<Integer> test2 = new Test<Integer>(123);
一、泛型只在編譯階段生效。同一個類經過不一樣泛型拿到的對象,使用getClass()判斷是屬於同一個類的。
System.ou.println(test1.getClass() == test2.getClass());
//取到的test1的類爲Test類,不是Test<T>類
二、同一個類,經過不一樣泛型拿到的對象,相互不兼容。不能互相賦值。
test2 = test1;//test1與test2不兼容
3.泛型類 在實例化的時候,能夠不傳入泛型,類中的數據類型,在賦值的時候傳入的變量類型爲空
4.實例化泛型時,智能傳入類名,能夠是系統類也能夠是自定義實體類
泛型通配符上邊界,使用?extends類名錶示通配符只支持指定類的子類
Test<?extends Number>testS = new Test<Number>();
泛型通配符下邊界,使用?super類名,表示通配符只支持指定類及其超類;
泛型的接口
1.聲明
interface inter<T>{
void Test(T t );
}
2.實現類 若是實現泛型接口,那麼不能直接使用接口的泛型,那麼須要從新聲明
class Test3<T> implements inter<T>{
@Override
public void Test(T t){}
}
3.若是實現類,不想做爲泛型類,那麼,能夠在實現接口時,直接給接口的泛型賦值
class Test3 implements inter<String>{
@Override
public void Test(String t){}
}
泛型方法
1.不必定在泛型類中 能夠獨立於泛型類在任何類中均可以單獨使用
class Test{
public static <T> void test(T t){}
}
調用 Test.test("String");
2.使用泛型方法能夠實現可變參數的方法
class Test{
/使用...表示能夠接受n個任意類型的參數也能夠是數組
public static <T> void test(T......t){}
}
通用:Test.test("String","String2",123,true)
3.只有在方法中聲明瞭<T>的方法 才能是泛型方法
而若是在方法中,使用額類的泛型 則不是泛型方法
注意:
靜態方法不能使用類的泛型
智能將靜態方法單獨聲明爲泛型方法
遞歸
1.在函數自身內部,調用函數自己的方式,稱爲遞歸
2.包括遞進去 歸出來兩部分
3.遞歸必須經過合適的語句,及時退出 不然出現死循環
++BuffererdInputStream
1.做用:
在基本流的基礎上進行包裝 讀取或者寫入文件時 將經過緩存進行
即現將內容寫入到緩存區 緩存區已滿才進行讀取或者寫入操做
優勢: 能夠大大減小文件的操做次數,提升寫入效率
2.緩存流的使用、
在基礎流之上進行包裝
new BufferedInputStream(new FileInputStream())
爲IO鏈 ,關閉只須要你關閉最外層流,內容將自動關閉
3.BufferedOutputStream在關閉前一般bos.flush();
在程序最後刷新緩存流 將緩存流中未滿的內容錯寫入到文件中,調用close方法 將自動刷新
DataOutputStream DataInputStream
採用二進制對文件進行操做
與基本數據流相比,能夠直接讀寫java中的基本數據類型
另外 若是操做的文件是一個二進制文件 須要使用DataOutputSream替代FileOutputStream
一樣 Data系列的流,也有read和write方法,操做與基本相同
注意 DataOutputStream 寫入二進制文件 只能使用 DataInputStream文件讀取
ObjectOutputStream ObjecInputStream
做用
直接繼承自:java.IO.OutputStream
1.與基本流相同 能夠用read write 方法進行讀寫
2.與DataOutputStream相同 樂意對java基本數據類型進行讀寫 :readInt() writeDouble()
3可使用readObject() 和 直接對對象進行操做
對象的序列化和反序列化
1.對象的序列化 將程序中的對象 持久化保存在文件中的過程
2.反序列化 將文件中保存的對象 從新讀取到程序中的過程
private static final long serialVersionUID = 6869255210100342059L;
添加之後 ,能夠用ID表示序列化和反序列化時操做的對象,是同一個對象
若是不添加版本ID,當序列化一個對象後,若是實體類屬性有增刪,在進行反序列化時,會形成錯誤,由於系統認爲這已經不是一個類
I/O操做
1.File類
(1)做用:用於對磁盤文件進行操做。刪除、建立等。
(2)三種構造函數:
①File file1 = new File("F:\\test\\test.txt");
直接傳入一個路徑,拿到一個文件或者是文件夾
②File file2 = new File("F:\\test","test.txt");
第一個參數傳入一個父路徑、第二個參數傳入子路徑或者文件。
③File file3 = new File(file1,"test.txt");
第一個參數傳入一個父路徑的file對象,第二個參數傳入子路徑或者文件。
(3)路徑的表示:.
文件夾的分隔,可使用"/"(一般用於Linux,Windows也適用)也可使用"\\"(一般用於Windows),注意一個\須要轉義。
/**
* 按照字節一個一個讀取文件
*/
int n = -1;
while((n=fis.read()) != -1){
sb.append((char)n);
}
System.out.println(sb);
/**
* 將比特數組直接聲明爲輸入流的長度,一次性讀出全部文字
*/
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
sb.append(new String(bytes));
System.out.println(sb);
/**
* 一次性讀取1024個字節
*/
byte[] bytes = new byte[1024];
int n=-1;
while ((n = fis.read(bytes))>-1) {
sb.append(new String(bytes));
}
System.out.println(sb);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
File類
*/
File file1 = new File("F");
File file3 = new File("F");
System.out.println();
/*
檢測文件是否刻度
*/
System.out.println(file1.canRead());
/*
檢測文件是否可寫
*/
System.out.println(file1.canWrite());
/*
檢測兩個對象是否相等
*/
System.out.println(file1.equals(file3));
/*
檢測文件是否存在
*/
System.out.println(file1.exists());
/*
取到文件的絕對路徑
*/
System.out.println(file1.getAbsoluteFile());
/*
取到文件夾名
*/
System.out.println(file1.getName());
/*
取到文件的父路徑
*/
System.out.println(file1.getParent());
/*
檢測文件是否爲絕對路徑
*/
System.out.println(file1.isAbsolute());
/*
檢測當前文件是不是目錄
*/
System.out.println(file1.isDirectory());
/*
檢測當前文件是不是文件
*/
System.out.println(file1.isFile());
/*
刪除文件
刪除成功爲true
*/
System.out.println(file1.delete());
/*
刪除文件
*/
try {
System.out.println(file1.createNewFile());
} catch (IOException e) {
e.printStackTrace();
}
/*
建立文件夾,只有當文件不存在時,才能建立成功
mkdir: 只能建立一層目錄,可是倒數第二層目錄也不存在 將建立失敗
mkdirs: 能夠建立多層目錄,不管有幾層不存在均可以依次建立
*/
System.out.println(file1.mkdir());
/*
得到文件所在分區的總大小和可用大小 以字節爲單位
*/
System.out.println(file1.getTotalSpace());
System.out.println(file1.getUsableSpace());
/*
返回當前文件或文件夾的大小
*/
System.out.println(file1.length());
/*
返回當前文件全部文件和文件夾名字 返回String數組
*/
String[] list = file1.list();
for (String item : list){
System.out.println(item);
}
/*
返回當前目錄全部的文件和文件夾路徑 返回File數組
*/
File[] files = file1.listFiles();
for (File file:files
) {
System.out.println(file.getParent()+"--------"+file.getName());
}
Reader&Writer
字符流
1.在處理數據單元時,以一個字符做爲單位而字節流以字節爲單位
2.字符流基類: Reader Writer 爲抽象類
FileReader FileWriter是直接繼承自抽象類的兩個字符流的基類
3.FileReader FileWriter在讀寫文件的時候
只能使用系統默認的編碼格式 沒法制定編碼 若是文件格式和系統默認格式不一致 那使用這兩種方式讀寫 將產生亂碼
InputStringReader outputStringWriter
1.將字符流轉爲字節流 同時支持自定義讀寫編碼格式
2.常見編碼格式
ASCII:美國標準信息碼
ISO8859-1:歐洲碼
ANSU編碼:能夠分爲多種】
簡體中文:
GB2312
GBK
繁體中文
big_5
Unicode編碼:國際標準碼 :兼容絕大部分國家編碼格式
能夠分爲:UTF-6 UTF-8 UTF-16
BufferedReader BufferedWriter
緩存流存儲。
異常及日誌
--------------------------------------------------------------------------------
學習目標
使用try_catch_finally處理異常
使用throw throws拋出異常
使用log4j記錄日誌
--------------------------------------------------------------------------------
1、異常
--------------------------------------------------------------------------------
1.定義:在程序的運行中多發生的不正常的事,會中斷正在運行的程序。
2.Java中全部異常和錯誤的基類:Throwable.
3.Java中的異常分爲運行時異常和檢查時異常;
運行時異常是表示RuntimeException以及全部子類,
這些異常無需在程序中進行捕獲,大多能夠經過代碼進行控制避免。
檢查時異常表示除了RuntimeException以及全部子類,
這些異常必須進行捕獲。
--------------------------------------------------------------------------------
4.使用try—catch進行異常的捕獲
try :包裹可能出現異常的代碼
catch多個: 進行不一樣的異常處理操做
當try中的程序出現異常時,將進行對應的catch進行操做,而不會再執行try塊裏剩餘打的代碼
捕獲異常後,若出現異常,將不會中斷程序運行
catch塊能夠有多個 最後使用Exception結束,表示捕獲全部異常,可是多個catch塊在順序上必須從小到大
5.e.getMessage()拿到錯誤信息
e.printStackTrace();打印錯誤堆棧信息
6.try-catch結構,若是要確保運行不被中斷,必須確保捕獲全部異常。
7.finally表示不管程序是否出現異常,都必須執行的語句.即便try中有return語句,也必須執行。一般用於流的關閉,資源將再也不釋放。
可是System.exit(0);退出程序 finally再也不執行
8.try-finally能夠組合存在去,而不必定包含catch
表示異常不進行出路,可是finally必須執行
--------------------------------------------------------------------------------
1.異常的第二種處理機制 拋出異常
使用throws在方法的聲明上進行拋出,由調用該方法的方法進行捕獲 或繼續拋出,在main方法中進行處理 若繼續拋出將會致使城西出現異常沒法被發現。
2.throws若是拋出多個異常 使用逗號分隔!
3.throw在程序中,手動拋出異常。
4.若是使用throw拋出的是一個檢查型異常,那麼必須在方法體上,使用theows進行拋出聲明,若爲運行時異常,則沒必要。
5.自定義異常類:必須繼承自現有異常
一般繼承Exception或者RuntimeException,分別代表了檢查和運行時異常
--------------------------------------------------------------------------------
日誌
1.經過Logger.getLogger()拿到一個日誌對象,參數傳入本類.class
2. 導入log4j.jar
3.在src目錄同級下,建立log4j.properties配置文件
4.使用日誌對象,分別調用不一樣級別的打印語句,進行日誌的輸出
log.debug("打印一條測試信息");
log.info("打印一條info信息");
log.warn("打印一條warn信息");
log.error("打印一條error信息");
5.Appender 爲日誌輸出目的地,Log4j提供的appender有如下幾種:
org.apache.log4j.ConsoleAppender(控制檯),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(天天產生一個日誌文件),
org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件),
org.apache.log4j.WriterAppender(將日誌信息以流格式發送到任意指定的地方)
6.Layout:日誌輸出格式,Log4j提供的layout有如下幾種:
org.apache.log4j.HTMLLayout(以HTML表格形式佈局),
org.apache.log4j.PatternLayout(能夠靈活地指定佈局模式),
org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串),
org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等信息)
7.打印參數: Log4J採用相似C語言中的printf函數的打印格式格式化日誌信息,以下:
%m 輸出代碼中指定的消息
%p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
%r 輸出自應用啓動到輸出該log信息耗費的毫秒數
%c 輸出所屬的類目,一般就是所在類的全名
%t 輸出產生該日誌事件的線程名
%n 輸出一個回車換行符,Windows平臺爲「\r\n」,Unix平臺爲「\n」
%d 輸出日誌時間點的日期或時間,默認格式爲ISO8601,也能夠在其後指定格式,好比:%d{yyy MMM dd HH:mm:ss , SSS},輸出相似:2002年10月18日 22 : 10 : 28 , 921
%l 輸出日誌事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java: 10 )
8.在代碼中初始化Logger:
1)在程序中調用BasicConfigurator.configure()方法:給根記錄器增長一個ConsoleAppender,輸出格式經過PatternLayout設爲"%-4r [%t] %-5p %c %x - %m%n",還有根記錄器的默認級別是Level.DEBUG.
2)配置放在文件裏,經過命令行參數傳遞文件名字,經過PropertyConfigurator.configure(args[x])解析並配置;
3)配置放在文件裏,經過環境變量傳遞文件名等信息,利用log4j默認的初始化過程解析並配置;
4)配置放在文件裏,經過應用服務器配置傳遞文件名等信息,利用一個特殊的servlet來完成配置。
9.
### set log levels ###
log4j.rootLogger = debug , stdout , D , E
### 輸出到控制檯 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n
### 輸出到日誌文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG ## 輸出DEBUG級別以上的日誌
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 保存異常信息到單獨文件 ###log4j.appender.D = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.D.File = logs/error.log ## 異常日誌文件名log4j.appender.D.Append = truelog4j.appender.D.Threshold = ERROR ## 只輸出ERROR級別以上的日誌!!!log4j.appender.D.layout = org.apache.log4j.PatternLayoutlog4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n