java基礎複習(集合、泛型、IO流、多執行緒、Junit 、內省 、Properties、 路徑問題)
集合
---|Collection: 單列集合 ---|List: 有儲存順序, 可重複 ---|ArrayList: 陣列實現, 查詢快, 增刪慢 由於是陣列實現, 在增和刪的時候會牽扯到陣列 增容, 以及拷貝元素. 所以慢。陣列是可以直接按索引查詢, 所以查詢時較快 ---|LinkedList: 連結串列實現, 增刪快, 查詢慢由於連結串列實現, 增加時只要讓前一個元素記住自己就可以, 刪除時讓前一個元素記住後一個元素, 後一個元素記住前一個元素. 這樣的增刪效率較高但查詢時需要一個一個的遍歷, 所以效率較低 ---|Vector: 和ArrayList原理相同, 但執行緒安全, 效率略低 和ArrayList實現方式相同, 但考慮了執行緒安全問題, 所以效率略低 ---|Set: 無儲存順序, 不可重複 ---|HashSet 執行緒不安全,存取速度快。底層是以雜湊表實現的。 ---|TreeSet 紅-黑樹的資料結構,預設對元素進行自然排 序(String)。如果在比較的時候兩個物件 返回值為0,那麼元素重複。 ---| Map: 鍵值對 鍵不可重複,鍵可以重複 ---|HashMap 執行緒不安全,存取速度快。底層是以雜湊表實現的. ---|TreeMap 紅-黑樹的資料結構,預設對元素進行自然排 序(String)。如果在比較的時候兩個物件 返回值為0,那麼元素重複 ---|HashTable 底層也是使用了雜湊表 維護的,存取的讀取快,儲存元素是 無序的。
泛型
- 泛型型別必須是引用型別
- 使用泛型方法前需要進行泛型宣告,使用一對尖括號 <泛型>,宣告的位置在static後返回值型別前。
- 當一個類中有多個函式聲明瞭泛型,那麼該泛型的宣告可以宣告在類上。
函式泛型:
public <T> T getData(T data) {
return data;
}
類泛型:
public class Demo6<T> {
public static void main(String[] args) {
System.out.println(getData2(100 ));
}
public T getData(T data) {
return data;
}
//靜態方法不可以使用類中定義的泛型,錯誤
public static T getData2(T data) {
return data;
}
}
- 建立物件的時候要指定泛型的具體型別
- 建立物件時可以不指定泛型的具體型別(和建立集合物件一眼)。預設是Object,例如我們使用集合儲存元素的時候沒有使用泛型就是那麼引數的型別就是Object
- 類上面宣告的泛型只能應用於非靜態成員函式,如果靜態函式需要使用泛型,那麼需要在函式上獨立宣告(加T)。
- 如果建立物件後指定了泛型的具體型別,那麼該物件操作方法時,這些方法只能操作一種資料型別。
- 所以既可以在類上的泛型宣告,也可以在同時在該類的方法中宣告泛型。
IO流
讀檔案:
public static void main(String[] args) {
String path = "c:/a.txt";
FileInputStream in = null;
try {
// 開啟流
in = new FileInputStream(path);
// 使用流讀檔案內容
int b = in.read();
while (b != -1) {
System.out.print((char) b);
b = in.read();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 釋放資源
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
拷貝檔案
public static void main(String[] args) {
String srcPath = "c:/a.txt";
String destPath = "c:/b.txt";
// 一定要使用位元組流
InputStream in = null;
OutputStream out = null;
try {
// 開啟流
in = new FileInputStream(srcPath);
out = new FileOutputStream(destPath);
// 使用流
byte[] buf = new byte[1024 * 8];
for (int len = -1; (len = in.read(buf)) != -1;) {
out.write(buf, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 釋放資源
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
多執行緒
兩種啟動執行緒方法:
private static int count = 100;
public static void main(String[] args) {
// 用繼承Thread類的方式啟動一個執行緒
new Thread() {
public void run() {
synchronized (StartThreadTest.class) {
while (count > 0) {
count--;
System.out.println(Thread.currentThread() + "賣了一張票,還剩" + count);
}
}
}
}.start();
// 用實現Runnable介面的方式啟動一個執行緒
new Thread(new Runnable() {
public void run() {
synchronized (StartThreadTest.class) {
while (count > 0) {
count--;
System.out.println(Thread.currentThread() + "賣了一張票,還剩" + count);
}
}
}
}).start();
}
Junit
一、搭建環境:
匯入junit.jar包(junit4)
二、寫測試類:
0. 一般一個類對應一個測試類。
1. 測試類與被測試類最好是放到同一個包中(可以是不同的原始檔夾)
2. 測試類的名字為被測試類的名字加Test字尾。
三:寫測試方法:
0. 一般一個方法對應一個單元測試方法。
1. 測試方法的名字為test字首加被測試方法的名字,如testAddPerson()。
2. 單元測試方法上面要加上@Test註解(org.junit.Test)!
3,單元測試方法不能有引數,也不能有返回值(返回void)!測試的方法不能是靜態的方法。
四、測試方法的基本使用:
1. 可以單獨執行一個測試方法,也可以一次執行所有的、一個包的、一個類中所有的測試方法。
2. 執行完後,顯示綠色表示測試成功;顯示紅色表示測試失敗(拋異常後會測試失敗)。
Assert
assertTrue(...) 引數的值應是true
assertFalse(...) 引數的值應是false
assertNull(...) 應是null值
assertNotNull(...) 應是非null的值
assertSame(...) 使用==比較的結果為true(表示同一個物件)
AssertNotSame(...) 使用==比較的結果為false
assertEquals(...) 兩個物件equals()方法比較結果為true
註解:
@Test
表示單元測試方法。
@Before
所修飾的方法應是非static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的每個單元測試方法之前都執行一次。
@After
所修飾的方法應是非static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的每個單元測試方法之後都執行一次。
@BeforeClass
所修飾的方法應是static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的所有單元測試方法之前執行,只執行一次。
@AfterClass
所修飾的方法應是static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的所有單元測試方法之後執行,只執行一次。
內省
開發框架時,經常需要使用java物件的屬性來封裝程式的資料,每次都使用反射技術完成此類操作過於麻煩,所以sun公司開發了一套API,專門用於操作java物件的屬性。
內省是用於操作java物件的屬性的,那麼以下問題我們必須要清楚。
問題一: 什麼是Java物件的屬性和屬性的讀寫方法?
問題二: 如何通過內省訪問到javaBean的屬性 ?
1.通過PropertyDescriptor類操作Bean的屬性.
public static void testPropertyDescriptor() throws Exception{
Person p = new Person();
PropertyDescriptor propertyDescriptor = new PropertyDescriptor("id",Person.class);
//獲取屬性的寫的方法。
Method writeMethod = propertyDescriptor.getWriteMethod();
Method readMethod = propertyDescriptor.getReadMethod();
propertyDescriptor.getReadMethod();
writeMethod.invoke(p, 12);
System.out.println(readMethod.invoke(p, null));
}
2.通過Introspector類獲得Bean物件的 BeanInfo,然後通過 BeanInfo 來獲取屬性的描述器( PropertyDescriptor ),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然後通過反射機制來呼叫這些方法。
public static void testIntrospector() throws Exception{
BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
PropertyDescriptor[] descriptor = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor itemProperty : descriptor){
System.out.println(itemProperty.getReadMethod().getName());
}
}
存在的問題: sun公司的內省API過於繁瑣,所以Apache組織結合很多實際開發中的應用場景開發了一套簡單、易用的API操作Bean的屬性——BeanUtils。
public static void main(String[] args) throws Exception {
Person p = new Person();
ConvertUtils.register(new Converter() {
@Override
public Object convert(Class type, Object value) {
try {
if(value!=null){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM dd");
Date d = dateFormat.parse((String) value);
return d;
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}, Date.class);
BeanUtils.setProperty(p,"id","110");
BeanUtils.setProperty(p,"name","狗娃");
BeanUtils.setProperty(p, "birthDay","1992 12 12");
System.out.println(p.getId() +"=="+ p.getName()+"======"+p.getBirthDay());
}
Properties
Properties類對應.properties檔案。檔案內容是鍵值對,鍵值對之間使用”=”或空格隔開。開頭是”#”的表示註釋
Properties類在載入.properties檔案時使用的iso8859-1的編碼。所以這個檔案中的中文要特殊處理:如果這個配置檔案中有中文就必須要進行轉義,使用native2ascii.exe命令操作:
native2ascii d:/my.properties d:/my2.properties
使用Properties類中的load(InputStream) 方法可以載入配置檔案,使用其中的store(OutputStream) 方法可以儲存配置到指定檔案。
載入:
public static void testLoadProperties() throws Exception {
Properties properties = new Properties();
InputStream in = new FileInputStream("E:/itcast/config.properties");
properties.load(in); // 載入
in.close();
System.out.println(properties);
}
寫配置檔案:
public static void testStoreProperties() throws Exception {
// 準備配置資訊
Properties properties = new Properties();
properties.setProperty("name", "李四");
properties.setProperty("age", "20");
// 準備
OutputStream out = new FileOutputStream("d:/my.properties");
String comments = "這是我的配置檔案";
// 寫出去
properties.store(out, comments);
out.close();
}
案例:使用properties讀取配置檔案,讀取資料庫的使用者名稱、密碼
public class DBUtil {
static Properties properties = new Properties();
static{
try {
Class clazz = DBUtil.class;
InputStreamReader fileReader =
new InputStreamReader(clazz.getResourceAsStream("/db.properties"));
properties.load(fileReader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getUserName(){
String userName =properties.getProperty("userName");
return userName;
}
public static String getPassword(){
return properties.getProperty("password");
}
public static void main(String[] args) {
System.out.println("使用者名稱:"+ getUserName());
System.out.println("密碼: "+ getPassword());
}
}
路徑問題
絕對路徑的問題: 比如C:\abc\a.properties檔案路徑,該路徑在windows上執行沒有 問題,但是如果把該專案移動到linux上面執行 ,該路徑就會出現問題了,因為在linux上面沒有c盤的,只有根目錄\。
相對路徑存在的問題:相對路徑是相對於目前執行class檔案的時候,控制檯所在的路徑,這樣子也會導致出現問題。
在Java程式中使用File時寫相對路徑,是指相對於執行java命令時當前所在的資料夾。
public class PathTest {
public static void main(String[] args) throws Exception {
System.out.println(new File("a.txt").getAbsolutePath());
}
}
獲取classpath中的資源(InputStream)
public static void main(String[] args) throws Exception {
Class clazz = new ClassPathTest().getClass();
// 開頭的'/'表示classpath的根目錄,這個是表示從classpath的根目錄中開始查詢資源
InputStream in = clazz.getResourceAsStream("/cn/itcast/my.properties");
// 如果開頭沒有'/',表示從當前這個class所在的包中開始查詢
InputStream in2 = clazz.getResourceAsStream("my.properties");
}