1. 程式人生 > >java基礎語法2.

java基礎語法2.

第二章
2.1 class檔案的生成
java檔案為原始碼檔案
class為程式.
class檔案實時修改.
eclipse自動生成.
project下面clean.


2.2 jar檔案
如何將有用的類傳給別人使用.
1.把*.java檔案發給對方.
2.把*.class打包發給對方.

匯出為jar檔案.
右鍵export Java JAR file


2.3使用jar檔案
new java project test2
右鍵 new folder libs
複製jar 貼上到libs
右鍵jar build path add to
然後呼叫裡面的方法,再宣告就行了
庫Library


2.4系統jar檔案
String java.lang.String
ArrayList java.util.ArrayList

第三方jar
系統庫System Library
三方庫


第三章
3.1 抽象類Abstract Class
建立抽象類
public abstract class XXX //抽象類
{
    public abstract void yyy(); //抽象方法 不能有大括號(方法體)

}
抽象方法可以沒有定義,稱為抽象方法

抽象類不可例項化: FunnyThing f=new FunnyThing(); //錯誤 不可以例項化.

抽象類僅僅用於描述一類事情:應該有什麼,應該能做什麼.它不是具體類,不能建立物件.

所有抽象類方法的定義在子類裡實現.


3.2抽象類的用法
暫時不需要寫抽象類
java.io.InputStream
java.io.OutputStream

第四章
4.1介面
介面 interface(或翻做:介面)
定義一個介面
public interface AudioOutput
{
public void play(AudioData s); //不能寫方法體{} 必須是public 預設介面都是抽象方法.
}

使用介面
和抽象類一樣 必須派生一個子類;
public class XiaoMi implements AudioOutput
{

@Override
public void play(AudioData s)
{


}

}

介面和抽象類相似,區別為:
1.用implements 而不是extends(不表示繼承關係)
2.一個類可以implements多個介面
public class X extends Y implements A,B,C
3介面不應該新增屬性.(可以新增,但沒有意義)

介面和繼承是兩個不同的設計概念

4.2介面的使用
當一個系統與另外一個系統對接時.
介面的使用

第五章 內部類
5.1內部類
當一個類寫在另一個類內部時,稱為內部類
內部類通常使用private,外部不可見.
如果你想在外部使用內部類,定義為public.


5.2內部類的使用
在內部類的裡面可以訪問外部類的所有方法和屬性.
訪問外部類時 + class名.this.

public class Example
{
private String name;

private void show()
{
System.out.println("名字:"+name);
}

public void test()
{
ABC abc=new ABC();
abc.work();
}

public class ABC
{
public void work()
{
Example.this.name="shao fa";
Example.this.show();
}
}

}


5.3靜態內部類

在外面建立內部類物件
public class Example
{
public class ABC
{
}
}

Example e=new Example(); //例項化a
Example.ABC a=e.new ABC(); //例項化a

 

靜態內部類
public class X
{
public String name;
public static class Y
{
}
}

Example.ABC a=new Example.ABC(); //例項化a

使用靜態內部類 就無法使用Example.this.name="shao fa";

5.4 匿名內部類 //非常常用
內部類的簡化寫法
public interface XXX
XXX a=new XXX(){};
直接在大括號內重寫XXX介面的方法
不用建立子類.

匿名內部類
還有一個寫法
c.xxx(new XXX(){
})


第六章 靜態物件
靜態static
在Java中表示"全域性的"
靜態物件,即全域性物件,一直存在的.
定義靜態物件
public class MMM
{
public static XXX a=new XXX();
}

使用靜態物件.
MMM.a.yyy(); //yyy 為XXX下面的方法

要點
1.在第一次使用時,靜態物件被建立
例如:Example類被使用時,Example.a被建立
如果Example從未被使用,Exampee.a永不建立

2.靜態物件不會被系統回收

3.靜態物件只有一個例項
無論建立多少個Example物件,Example.a只建立一次.


6.2 單例模式
全域性&&唯一例項 全域性使用static實現,單例使用private實現.
在程式執行期間一直存在,唯一:只有一個例項
public class Earth
{
public static Earth i=new Earth(); //static成為了全域性例項.

private Earth() //使用private 不能在外面新增新的例項,成為單例. 限制例項的建立. 讓構造方法私有化
{

}
public void showCountries()
{
System.out.print("唯一地球");
}
}


第七章 出錯處理
7.1出錯處理
考慮2種情況,如果輸入的字串有問題.
1.輸入非法字元.
2.使用者輸入過長的字元,超出int的極限.

public class Converter
{
public int status = 0;

// 把一個字串轉成整數
// 例如: "123" -> 123

public int str2int (String str)
{
status = 0;
if(str.length()>11)
{
status = -2; // 第2種情況
return 0;
}

int result = 0;
for(int i=0; i<str.length(); i++)
{
char ch = str.charAt(i);
if( ! isValid(ch) )
{
status = -1; // 第1種情況
return 0;
}
result = result * 10 + (ch - '0');
}

return result;
}

private boolean isValid(char ch)
{
if(ch >= '0' && ch <= '9')return true;
if(ch == '-') return false;
return false;
}
}

public class Test
{

public static void main(String[] args)
{
Converter conv = new Converter();

int result = conv.str2int("2201234");
if(conv.status == 0)
{
System.out.println("轉換結果: " + result);
}
else
{
if(conv.status == -1)
System.out.println("非法字元");
else if(conv.status == -2)
System.out.println("超出範圍");
}

}

}

可預期的錯誤情況.

7.2異常機制
1.在方法裡,丟擲異常
int str2int(String str)throws Exception
{
if(錯誤1發生)
throw new Exception("錯誤1");
if(錯誤2發生)
throw new Exception("錯誤2");
}

public int str2int (String str) throws Exception //新增throws Exception
{
status = 0;
if(str.length()>11)
throw new Exception("超出範圍"); //丟擲異常 也可以寫作Exception ex=new Exception("超出範圍")
int result = 0; //throw ex;
for(int i=0; i<str.length(); i++)
{
char ch = str.charAt(i);
if( ! isValid(ch) )
throw new Exception("非法字元"); //丟擲異常
result = result * 10 + (ch - '0');
}

return result;
}

public static void main(String[] args) throws Exception //在main方法的入口新增throws Exception

 



2.在呼叫時,捕獲異常
呼叫時,用try...catch...來捕獲異常
try:監視若干行程式碼,如果裡面丟擲異常,則進入catch{}
catch:出錯處理,引數為剛剛丟擲的異常物件,所有出錯資訊都在異常物件裡.

public static void main(String[] args) //或者使用try catch
{
Converter conv = new Converter();

int result;
try
{
result = conv.str2int("12134");
System.out.println("正常"+result); //正常情況下走try
}
catch (Exception e)
{
System.out.println(e.getMessage()); //異常時走catch
}


Java裡普遍使用異常機制來進行出錯處理
Exception物件本身就可以攜帶所有出錯資訊

7.3自定義異常
自定義異常 派生出Exception的子類
try {
int result = conv.str2int("2023321238");
System.out.println("正常:" + result);
}
catch( InvalidCharException e1)
{
System.out.println(e1.getMessage());
}
catch ( TooLargeException e2)
{
System.out.println(e2.getMessage());
}
catch( Exception e)
{
System.out.println(e.getMessage());
}

 

public class InvalidCharException extends Exception
{
public int pos; // 非法字元出現的位置
public char ch; // 非法字元

public InvalidCharException(int pos, char ch)
{
this.pos = pos;
this.ch = ch;
}

@Override
public String getMessage()
{
return "非法字元'" + ch + "',位置:" + pos;
}


}


public int str2int (String str) throws Exception
{
if(str.length()>11)
{
Exception ex = new TooLargeException(str.length()); //throw new TooLargeException(str.length());
throw ex;
}

int result = 0;
for(int i=0; i<str.length(); i++)
{
char ch = str.charAt(i);
if( ! isValid(ch) )
throw new InvalidCharException(i, ch);

result = result * 10 + (ch - '0');
}

return result;
}


7.4異常執行規則
1.丟擲異常時,退出當前的方法

執行規則:捕獲異常時
1.try{...}中出現異常時,退出try,進入catch
2.如果沒有匹配到catch.
中斷當前方法
繼續向上丟擲
上層呼叫者有義務抓住這個異常
main()-->a()-->b()-->c()
3.如果一個異常最終沒被抓住,程式崩潰.

7.5退出清理
當異常出現時,doCleanJobs沒有機會執行

退出清理finally
使用finally語句可以保證tyu{}退出時執行某些退出清理程式碼

tyu{}
catch..... //若干個
finally{}

規則:當退出try時,總是執行finally中的語句.

當異常發生時,跳出當前執行finally後結算.


e.printStackTrace(); //相對於e.getMessage有更多的資訊
包含:-出錯的程式碼行位置
異常的型別
每層方法的呼叫位置(函式棧)


7.6基本異常
語法類(基本異常)
1.空指標異常 NullPointerException
2.陣列越界異常 ArrayIndexOutOfBoundsException
3.除零異常 ArithmeticException


即使沒有throws宣告,也能捕獲異常.
Throwable父類 Error子類 Exception子類.

第八章 泛型
8.1泛型
通用的型別,
通用連結串列 GenericList 封裝一個通用的連結串列設計
設計思路
1.不限制節點型別.
private static class GenericNode
{
Object value; //使用Object做為節點型別.
GenericNode next;
}

2.迭代器

 

8.2泛型的定義
泛型一般用於描述一種通用的演算法,資料結構,物件型別其實不關心
在Java中泛型的規範寫法.
public class Sample<T>
{
}
在類的定義裡,T代表一個通用的型別,把T稱為型別引數
T只是一個代號.


泛型的使用
public class Sample<T>
{
T value;

public void setValue(T value)
{
this.value=value;
}
public T getValue()
{
return this.value;
}

}


Simple<Student> sa=new Simple<Student>();

sa.setValue(new Student(1,"s","sd"));

Student s=sa.getValue();

System.out.println(s);

 

8.3常用的泛型
List,ArrayList
Map,HashMap

通常不需要我們自己寫泛型,JDK自帶的兩個泛型足夠使用

ArrayList:陣列連結串列.
ArrayList<Student>
ArrayList<String>
ArrayList<Integer>
必須使用包裝類.
使用
新增,插入
遍歷:陣列形式遍歷,連結串列遍歷
刪除:陣列形式刪除,連結串列刪除
1.排序
ArrayList<Student> list = new ArrayList<Student>();
list.add( new Student(1, "shao", "13810012345"));
list.add( new Student(2, "wang", "15290908889"));
list.add( new Student(3, "li", "13499230340"));
list.add( 0,new Student(4, "xxx", "1293023923923")); //從0的位置去插入

2.遍歷
import java.util.Iterator;
Iterator<Student> iter=list.iterator();
while(iter.hasNext())
{
Student s=iter.next();
System.out.println(s);
}


連結串列的方式遍歷


陣列的方式遍歷
for(int i=0;i<list.size();i++)
{
Student s=list.get(i);
System.out.println(s);
}


3.刪除元素
陣列方式刪除
list.remove(index)

連結串列方式刪除
Iterator<Student>iter=list.iterator();


4.排序
package my;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

public class Test
{

public static void main(String[] args)
{
ArrayList<Student> list=new ArrayList<Student>();
list.add(new Student(1,"陳","139")); //新增
list.add(new Student(2,"人","138"));
list.add(new Student(-1,"好","137"));
list.add(0,new Student(4,"我","136")); //插入

// Iterator<Student> iter=list.iterator();//刪除
// while(iter.hasNext())
// {
// Student s=iter.next();
// if(s.id==2)
// {
// iter.remove();
// }
// }
//排序
Comparator<Student> comp=new Comparator<Student>() //定義一個比較器
{
@Override
public int compare(Student a,Student b)
{
if(a.id<b.id)
return -1;
if(a.id>b.id)
return 1;
return 0;
}
}; //有分號


//排序
Collections.sort(list, comp);

Iterator<Student> iter1=list.iterator(); //遍歷
while(iter1.hasNext())
{
Student s=iter1.next();
System.out.println(s);
}

}

}

8.4
雜湊對映表 HashMap
HashMap儲存 key<=>value

HashMap的建立 需要制定key和value的型別
1.建立
HashMap<Integer,Student> map=.... //指定key的型別是Interger,value的型別是Student.

HashMap<Integer,Student>map=new HashMap();
2 新增物件

map.put(1, new Student(1,"2","3")); //map.put(key,new value());新增
map.put(2, new Student(4,"5","6")); //key為1,2,3,3, value為Student物件
map.put(3, new Student(7,"8","9"));
map.put(3, new Student(11,"12","13")); //同key值以後面一個為準

3.查詢

Student s=map.get(3); //查詢的是key值
if(s!=null)
{
System.out.println(s);
}

4.刪除
刪除一個物件
map.remove(key); //輸入要刪除的key值

刪除所有
map.clear();

5遍歷所有key和value
通常情況下map不遍歷
//遍歷所有的key
import java.util.Set; //宣告
Set<Integer>keys=map.keySet(); //遍歷所有的key
for(Integer k:keys)
{
System.out.println("key:"+k);
}


//遍歷所有的value
for(Student v:map.values())
{
System.out.println("values:"+v);
}

 


重點能否以姓名做為key?
key的要求
1.可以比較 //String可以比較
2.唯一不重複 //如果姓名不重複,則可以.

Map的查詢比list快?
HashMap查詢更快

第九章JDK和JRE
9.1jdk jre
jdk Java開發工具包
jre Java執行環境
JDK是開發時的環境,JRE時程式執行環境.
JRE時JDK的子集;


9.2Java命令列
類的載入
類載入器ClassLoader
ClassLoader裡尋找並載入需要的類
載入主類:my.Hello
根據import宣告載入需要的類.

 

9.3可執行JAR包
cmd

cd /d d:\eclipse-workspace //移動到d盤目錄下


java -jar example804.jar //執行jar包

jar包中有哪些東西
所有的class,所依賴的jar包裡的class
清單檔案META-INF/MANIFEST.MF
可發現,在清單檔案裡已經指定了Main Class

打包jar包 右鍵JAVA -Runnable JAR file

Java應用程式釋出
-JRE環境
-class,jar
-執行指令碼(雙擊執行).bat .cmd .sh


9.4命令列引數
命令列裡的引數將被傳遞給main方法
public static void main(String[] args)
{
}
main方法的引數:來自命令列引數;

java -cp bin my.Hello 2 3
9.5JVM
JVM Java虛擬機器
一個虛擬主機可以執行class檔案

當Java程式執行時
開啟jconsole,連線到java程序(JVM),觀測CPU/記憶體/執行緒等資訊
jdk1.8下面
做網站時會用到jconsole工具.

第十章
Java官方文件
https://docs.oracle.com/en/
常用api
Java API即java再帶的類庫

常用的
lang util io>net math>sql security nio>.......
java官方文件相當於字典.

左上package

左下class

10.2整合文件和原始碼
多看文件,少看原始碼


10.3
文件的使用方法
整數1234,轉換成2進位制字串


第11章
11.1時間的表示
long 時間值
java.util.Date 時間物件
java.util.Calendar 時間操作工具
java.text.SimpleDateFormat 格式化工具類.

時間值long 單位:毫秒 從1970開始的時間值
執行一段程式碼花費的時間
public class Test
{
public void someBusyWork()
{
long start =System.currentTimeMillis(); //初始時間

for(int i=0;i<2002000;i++)
{
double a=Math.sin(i);
}

long duration=System.currentTimeMillis()-start; //求時間
System.out.println(duration);

}
public static void main(String[] args)
{
long now =System.currentTimeMillis();

Test t=new Test();
t.someBusyWork();

}

}

java.util.Date 基本都是Deprecated的類.不贊成使用.

java.util.Calendar 計算年月日時分秒
封裝的時間工具.
Calendar cal=Calendar.getInstance();
int year=cal.get(Calendar.YEAR);
int month=cal.get(Calendar.MONTH); //月份的時間是從0開始計算,其他都是正常.
int day=cal.get(Calendar.DAY_OF_MONTH);
int house=cal.get(Calendar.HOUR);
int minute=cal.get(Calendar.MINUTE);
int second=cal.get(Calendar.SECOND);

Calendar與long的轉換.
//Calendar ->long
long ms=cal.getTimeInMillis();

//long->Calendar
cal.setTimeInMillis(ms);


Calendar與Date轉換.
//Calendar->Date
Dated=cal.getTime();

//Date->Calendar
cal.setTime(d);


11.2時間的處理
-時間與日期的差值
計算2018年2月10日-2018年3月10日之間多少天.
public static void main(String[] args)
{
Calendar c1=Calendar.getInstance(); //建立一個c1的例項 getInstance例項化.
Calendar c2=Calendar.getInstance();

c1.set(2018, 1, 10, 0, 0, 0); //日期轉為long的毫米 月份的起始數為0,0表示1月,1表示2月.
c2.set(2018, 2, 10, 0, 0, 0);

long ms=c2.getTimeInMillis()-c1.getTimeInMillis(); //相差的毫米
long days=ms/(24*3600*1000); //因為結果是毫米所以*1000

System.out.println("相差天數為: "+days);
}

 

日期的推算.
public static void main(String[] args)
{
Calendar c1=Calendar.getInstance();

c1.set(2018, 2, 2, 0, 0, 0); //3月2日

c1.add(Calendar.DAY_OF_MONTH,+32); //第一個引數表示要修改哪個欄位,第二個引數表示差值

System.out.printf("結果為:%d-%d-%d ",
c1.get(Calendar.YEAR),
c1.get(Calendar.MONTH)+1,
c1.get(Calendar.DAY_OF_MONTH)); //月份因為初始值為0所有要+1顯示.
}

其中,add()的第一個引數表示要修改哪個欄位,第二個引數表示差值

時間和日期的格式化
Date->String

public static void main(String[] args)
{
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//大寫的HH 24小時制,小寫的hh12小時制.

Date now =new Date();
String str=sdf.format(now);

System.out.println("日期:"+str);

}

String->Date

public static void main(String[] args)
{
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

try
{
String s1="2018-3-2 11:29:00";
Date t1=sdf.parse(s1);
}
catch(ParseException e)
{
e.printStackTrace();
}

第12章檔案的操作
File檔案,用於進行檔案/目錄相關操作
java.io.File

在Java文件裡找到此類的說明.
1.File物件用於指向一個檔案或者目錄
"e:/examples/abc.txt"
"e\\examples\\abc.txt" (要轉義)
並未建立實際物件,只是建立File物件.
public static void main(String[] args)
{
File f=new File("e:/examples/abc.txt");
}

2.判斷物件是否存在.

public static void main(String[] args)
{
File f=new File("e:/examples/abc.txt");

if(f.exists()) //判斷語句 f.exists
{
System.out.println("檔案存在"+f);
}
else
{
System.out.println("檔案不存在");
}
}

3.獲取檔案的屬性
是檔案還是目錄isFile() isDirectory()
檔案長度length()
最後修改時間lastModified()
public static void main(String[] args)
{
File a=new File("D:\\eclipse\\abc.txt"); //檔案是否存在判斷

if(a.isFile())
{
System.out.println("是檔案");
}
long size=a.length(); //長度判斷
System.out.println(size);

long s=a.lastModified(); //最後的修改時間獲取和輸出
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestr =sdf.format(s);
System.out.println(timestr);
}
可讀,可寫,可執行?


4檔案的操作
重新命名 File.renameTo
public static void main(String[] args)
{
File a=new File("D:\\eclipse\\abc.txt");

if(a.isFile())
{
System.out.println("是檔案");
}
a.renameTo(new File("D:\\eclipse\\abcd.txt"));
}

5.絕對路徑與相對路徑
相對路徑
"../other/video.mp4"
"subdir/xy.doc"

絕對路徑 即是完整路徑
.getAbsolutePath();
"D:\\eclipse\\abc.txt"

12.2目錄的操作
File類
if(f.exists)判斷目錄是否存在

if(f.isDirectory)
判斷是否是目錄


建立層級目錄
public static void main(String[] args)
{
File d=new File("E:\\abc\\a\\b\\c");

d.mkdirs();
}
會建立沒有的目錄


遍歷子項
使用listFile()可以遍歷目錄下的子目錄/檔案
僅掃描一級目錄
public static void main(String[] args)
{
File d=new File("D:\\eclipse");

File[]subFiles=d.listFiles(); //設立一個數組subFiles儲存

for(File f:subFiles)
{
if(f.isDirectory())
{
System.out.println("掃描到目錄"+f);
}
else
{
System.out.println("掃描到檔案"+f);
}

}

可以在listFilter的時候設定一個過濾器
File d=new File("D:\\eclipse");

FileFilter filter=new FileFilter() //過濾器fliter 建立一個匿名類
{

@Override
public boolean accept(File pathname)
{
String filePath=pathname.getAbsolutePath(); //建立一個字元創filePath獲得他的絕對路徑
if(filePath.endsWith(".rar")) //判斷結尾是否是.rar .endsWith();
return true;
return false;
}
};



File[]subFiles=d.listFiles(filter); //設立一個數組subFiles儲存,把filter過濾器引數傳給他

for(File f:subFiles)
{
if(f.isDirectory())
{
System.out.println("掃描到目錄"+f);
}
else
{
System.out.println("掃描到檔案"+f);
}

}

3思考如何做遞迴查詢,繼續查詢子目錄


如何建立一個子目錄
File homeDir =new File("e:/examples"); //先寫出父目錄
File f=new File(homeDir,"some.txt"); //再建立子目錄

newFile時可以直接指定它的父目錄.

File時個輕量級物件,不必轉為String


12.3 相對路徑
絕對路徑從根目錄開始指定.

相對路徑,指定一個相對於當前目錄的路徑
File f=new File("src/my/Test.java");
當前目錄
E:/JavaProjects/example1203

全路徑
E:/JavaProjects/example1203/src/my/Test.java

相對路徑示例
./data/config.txt
../example1101/src
../../WebProjects/
./src/../data/config.txt

.表示本目錄
..表示父目錄


工作目錄
我們所說的當前路徑,指的是程式的工作目錄
預設的,工作目錄時程式執行時的起始目錄

注意: 工作目錄不是class檔案所在的目錄
注意: 在Eclipse|Run Configuration 裡可以指定 就是執行這個程式時的起始目錄.

String workDir=System.getProperty("user.dir"); //查詢當前的工作目錄在哪

//修改工作目錄
System.setProperty("user.dir","d:");


12.4 複製與移動
apache commons io
使用第三方的庫來完成檔案和目錄操作

新建一個專案,新增commons-io-2.4.jar
(1)建立目錄libs
(2)拷貝commons-io-2.4.jar到libs
(3)右鍵jar檔案,選Add to Build Path


常用檔案操作
FileUtils類支援的檔案和目錄
FileUtils.deleteQuietly()刪除檔案
FileUtils.copyFile()拷貝檔案
FileUtils.copyFileToDirectory()複製檔案到目錄
FileUtils.moveFile()移動檔案
FileUtils.moveFileDirectory()移動目錄

//拷貝檔案示例
public static void main(String[] args)
{
File src=new File("E:\\abc.txt");

File dst=new File("E:\\abc\\a.txt");

try
{
FileUtils.copyFile(src, dst);
}catch(IOException e)
{
e.printStackTrace();
}
System.out.println("完成");
}


第13章
13.1 檔案的儲存
檔案:視訊,音訊,圖片,文件,程式....

資料儲存在檔案中,關閉電腦後,資料不丟失.

無論是各種檔案裡面儲存的都是byte[]位元組資料

字串的儲存

String<=>byte[]

String=>byte[]

String text="abcdefg";
byte[] data=text.getBytes("UTF-8");

byte[]=>String
String text2=new String(data,"UTF-8");

寫入檔案 output寫入
public static void main(String[] args)
{
File dir=new File("e:/examples");
dir.mkdirs(); //建立目錄

File f=new File(dir,"123.txt");
String text="人人人1234aaaa";

try
{
FileOutputStream outputStream=new FileOutputStream(f);

byte[] data=text.getBytes("UTF-8"); //data把字串編碼成位元組資料
outputStream.write(data); //將位元組資料寫入檔案
outputStream.close(); //關閉檔案
}catch(Exception e)
{
e.printStackTrace();
}

}

win10下面不能再分割槽根目錄下面建立檔案.


讀取檔案 input讀取
public static void main(String[] args)
{
File dir=new File("e:/examples");
File f=new File(dir,"123.txt");

try {
FileInputStream inputStream=new FileInputStream(f);

int size=(int)f.length();
byte[]data=new byte[size]; //設立data[]的大小根據f.length
inputStream.read(data); //讀取資料
inputStream.close();

//將讀取來的資料轉換成String
String text=new String(data,"UTF-8");
System.out.println(text);
}catch(Exception e)
{
e.printStackTrace();
}

}

13.2資料的格式化儲存
整數,小數,布林,字串,日期....
統一的解決方法:把各項資料轉換為String
寫入
public static void main(String[] args)
{
Student stu=new Student(2018,"仁豪","1399009900");

String text="";
text+=("id:"+stu.id+",");
text+=("姓名:"+stu.name+",");
text+=("電話:"+stu.phone+",");

//儲存到檔案//
File f=new File("e:/examples/student.txt");
f.getParentFile().mkdirs();

try
{
FileOutputStream outputStream=new FileOutputStream(f);
byte[]data=text.getBytes("UTF-8");
outputStream.write(data);
outputStream.close();
}catch(Exception e)
{
e.printStackTrace();
}
System.out.println("exit");
}

讀取 //沒完全理解

public class ReadFile
{
public static String readTextFile(File f) //讀取文件
{
try {
FileInputStream inputStream = new FileInputStream(f);

int size = (int) f.length();
byte[] data = new byte[size];
inputStream.read(data);
inputStream.close();

// 將讀取來的資料轉成String
String text = new String(data, "UTF-8");
return text;

}catch(Exception e)
{
e.printStackTrace();
}
return "";
}

public static HashMap parseText (String text) //HashMap
{
HashMap<String,String> values = new HashMap();
String[] abc = text.split(",");
for(String k : abc)
{
k = k.trim();
if(k.length() == 0) continue;

String[] nv = k.split(":");
String name = nv[0];
String value = nv[1];
values.put(name, value);
}
return values;
}


public static void main(String[] args)
{
File dir = new File("e:/examples");
File f = new File(dir, "student.txt");

// 從檔案讀出字串
String text = readTextFile(f);

// 將字串解析為 key-value
HashMap<String,String> values = parseText(text);

// 提取出Student資訊
Student stu = new Student();
stu.id = Integer.valueOf( values.get("id"));
stu.name = values.get("name");
stu.phone = values.get("phone");

System.out.println("exit");

}

}

13.3
XML Extensible Markup Language
可擴充套件標記語言 (類似HTML), 一種用文字化表示的資料格式
特點:簡單,使用廣泛.

一個Student物件的儲存

<?xml version="1.0" encoding="UTF-8"?> //示例1
<student>
<id> 20180001</id>
<name> 邵發 </name>
<sex> true </sex>
<cellphone> 13810012345</cellphone>
</student>

version 表示版本號
- 第一行是固定不變的,叫XML的宣告
- 後面內容主體是一個樹狀層次的元素節點樹。
-每個元素成對出現都是閉合的,以<XXX>開始,以</XXX>結束.

<?xml version="1.0" encoding="UTF-8"?> //示例2
<student>
<id> 20180001</id>
<name> 邵發 </name>
<sex> true </sex>
<cellphone> 13810012345</cellphone>

<score>
<chinese> 99 </chinese>
<math> 98 </math>
<english> 97 </english>
</score>
</student>


<?xml version="1.0" encoding="UTF-8"?> //示例3
<data>
<student> //同級元素可重名,可以儲存陣列型別.
<id> 20180001</id>
<name> shao </name>
<sex> true </sex>
<cellphone> 13810012345</cellphone>
</student>

<student>
<id> 20180002</id>
<name> wang </name>
<sex> true </sex>
<cellphone> 13822244452</cellphone>
</student>

</data>

13.3.2 建立XML檔案
1.用notepad++建立
直接建立xml字尾的檔案開啟,把編碼改為utf8格式編碼.


2在eclipse中建立XML
右鍵project new file
建立.xml
右鍵properties other UTF-8

XML教程3:dom4j

在dom4j中,定義了以下幾個術語:
Document : 指整個XML文件
Element : 指元素
Element Text : 指元素的值
Element Attribute : 元素的屬性 ( 後面介紹)
如何建立和新增元素
public class MyTest
{
public static void haha() throws Exception
{
// 建立一個空的Document
Document x_doc = DocumentHelper.createDocument();

// 新增根元素: <root>
Element x_root = x_doc.addElement( "root" );

// 新增兩個子元素: <server>, <port>
Element e1 = x_root.addElement( "server" ) .addText( "www.afanihao.cn" );
Element e2 = x_root.addElement( "port" ) .addText( String.valueOf(80));

Element e3 = x_root.addElement("xxxx");
e3.addText("你好");

// 輸出到檔案
File xmlFile = new File("output.xml");
OutputStream outputStream = new FileOutputStream(xmlFile);
try {
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8"); // 字元編碼為UTF-8
XMLWriter writer = new XMLWriter(outputStream , format);
writer.write( x_doc ); // 把xml文件輸出到檔案裡
writer.close();
}finally
{
// 確保檔案控制代碼被關閉
try { outputStream.close();}catch(Exception e) {}
}
}

public static void main(String[] args)
{
try
{
haha();
} catch (Exception e)
{
e.printStackTrace();
}
System.out.println("hahha exit");
}
}

13.3.4生成XML

public static void writeXML( Student s, File xmlFile) throws Exception
{
// 建立一個空的Document
Document x_doc = DocumentHelper.createDocument();

// 新增根元素: <root>
Element x_root = x_doc.addElement( "root" );

// 新增兩個子元素: <server>, <port>
x_root.addElement( "id" ) .addText(String.valueOf(s.id));
x_root.addElement( "name" ) .addText( s.name);
x_root.addElement( "sex" ) .addText( s.sex ? "男" : "女");
// 男 male , 女 female 三目運算子b?x:y
//先計算條件b,然後進行判斷。如果b的值為true,計算x的值,運算結果為x的值;否則,計算y的值,運算結果為y的值。
x_root.addElement( "cellphone" ) .addText(s.cellphone);

// 輸出到檔案
OutputStream outputStream = new FileOutputStream(xmlFile);
try {
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8"); // 字元編碼為UTF-8
XMLWriter writer = new XMLWriter(outputStream , format);
writer.write( x_doc ); // 把xml文件輸出到檔案裡
writer.close();
}finally
{
// 確保檔案控制代碼被關閉
try
{
outputStream.close();
}
catch(Exception e)
{

}
}
}

public static void main(String[] args)
{
try
{
Student s = new Student(20180001, "邵", true, "13810012345");
writeXML ( s , new File("output1.xml"));

} catch (Exception e)
{
e.printStackTrace();
}
System.out.println("exit");
}

 

新增陣列元素
public static void writeXML( List<Student> sss, File xmlFile) throws Exception
{
// 建立一個空的Document
Document x_doc = DocumentHelper.createDocument();

// 新增根元素: <root>
Element x_root = x_doc.addElement( "root" );

Element x_student_list = x_root.addElement( "student_list" );

for( Student s:sss )
{
Element x_student = x_student_list.addElement( "student" );
x_student.addElement( "id" ) .addText( String.valueOf(s.id));
x_student.addElement( "name" ) .addText( s.name);
x_student.addElement( "sex" ) .addText( s.sex ? "male" : "female"); // 男 male , 女 female
x_student.addElement( "cellphone" ) .addText(s.cellphone);
}


// 輸出到檔案
OutputStream outputStream = new FileOutputStream(xmlFile);
try {
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8"); // 字元編碼為UTF-8
XMLWriter writer = new XMLWriter(outputStream , format);
writer.write( x_doc ); // 把xml文件輸出到檔案裡
writer.close();
}finally
{
// 確保檔案控制代碼被關閉
try { outputStream.close();}catch(Exception e) {}
}
}

public static void main(String[] args)
{
try
{
List<Student> sss = new ArrayList<>();

sss.add( new Student(20180001, "shao", true, "13810012345") );
sss.add( new Student(20180002, "wang", true, "17489938290") );
sss.add( new Student(20180003, "li", false, "19982998898") );

writeXML ( sss , new File("output3.xml"));

} catch (Exception e)
{
e.printStackTrace();
}
System.out.println("exit");
}

 

解析資料
public static Student readXML (File xmlFile) throws Exception
{
FileInputStream inputStream = new FileInputStream(xmlFile);
SAXReader xmlReader = new SAXReader(); // SAXReader 用於解析XML
Document x_doc = xmlReader.read(inputStream);// 得到一個Document物件
inputStream.close(); // 得到Document後即可關閉檔案

Element x_root = x_doc.getRootElement();
Student s = new Student();
s.id = Integer.valueOf( x_root.element("id").getText());
s.name = x_root.element("name").getText().trim(); //.trim()的作用是清除兩邊的空格.
String sex = x_root.elementText("sex");
s.sex = sex.equals("male");
s.cellphone = x_root.elementText("cellphone");
return s;
}

public static void main(String[] args)
{
File xmlFile = new File("data1.xml");
try
{
Student s = readXML (xmlFile);
System.out.println("read complete");
} catch (Exception e)
{

e.printStackTrace();
}


System.out.println("exit");
}


(1)取值時要注意兩邊的空白
String name = x_root.elementText ("name").trim();

(2)由於XML裡只儲存文字,所以在讀取Text之後,需要自己轉成int, boolean 或其他型別的變數。
例如,
int id = Integer.valueOf( x_root.element("id").getText());

(3)取值時一般要注意 null 的判斷
Element sex = x_root.element("sex");
if ( sex != null)
{
}

(4)注意關閉檔案控制代碼 (高階話題)
在Java程式裡,所有的檔案FileInputStream/FileOutputStream在用完之後,都得確保close掉。否則會引起控制代碼洩露。

public static Document parseXmlDocument(File xmlFile) throws Exception
{
FileInputStream inputStream = new FileInputStream(xmlFile);
try {
SAXReader xmlReader = new SAXReader();
Document doc = xmlReader.read(inputStream);
return doc;
}finally {
// 確保關閉檔案控制代碼
try{ inputStream.close(); }catch(Exception e) {}
}
}

 

 


假設從<parent>元素下查詢子元素<id>的值,則有3種寫法:

第一種寫法:
Element e = parent.element("id");
String text = e.getText();

第二種寫法:
String text = parent.element("id").getText();

第三種寫法:
String text = parent.elementText("id");

XML教程6
元素的屬性

使用第三方庫dom4j來操作XML


先前我們已經學到的,如果用XML來表示一個學生的資訊,可以表示為:
<student>
<id> 20180001</id>
<name> 邵發 </name>
<sex> true </sex>
<cellphone> 13810012345</cellphone>
</student>

也可以表示為:
<student id="20180001" >
<name> 邵發 </name>
<sex> true </sex>
<cellphone> 13810012345</cellphone>
</student>

其中,id作為元素<student>的屬性出現。屬性的名字為id,值為"20180001",必須使用雙引號。

 

 

也可以表示為:
<student id="20180001" name="邵發" sex="true" cellphone="13810012345">
</student>
其中,<student>元素有4個屬性。

由於<student>元素沒有Text內容,所以可以簡寫為:
<student id="20180001" name="邵發" sex="true" cellphone="13810012345" />


新增元素


Element x_student = x_root.addElement("student");
x_student.addAttribute("id", String.valueOf(s.id));
x_student.addAttribute( "name" , s.name);
x_student.addAttribute( "sex" , s.sex ? "male" : "female");
x_student.addAttribute( "cellphone", s.cellphone);
也可以連在一起寫,
Element x_student = x_root.addElement("student")
.addAttribute("id", String.valueOf(s.id))
.addAttribute( "name" , s.name)
.addAttribute( "sex" , s.sex ? "male" : "female")
.addAttribute( "cellphone", s.cellphone);

之所以可以連在一起寫,是因為addAttribute() 的返回值仍然是當前的Element物件。


讀取元素
public static Student readXML (File xmlFile) throws Exception
{
FileInputStream inputStream = new FileInputStream(xmlFile);
SAXReader xmlReader = new SAXReader(); // SAXReader 用於解析XML
Document x_doc = xmlReader.read(inputStream);// 得到一個Document物件
inputStream.close(); // 得到Document後即可關閉檔案

Element x_root = x_doc.getRootElement();

Element x_student = x_root.element("student");


Student s = new Student();
s.id = Integer.valueOf( x_student.attributeValue("id").trim());
s.name = x_student.attributeValue("name").trim();
String sex = x_student.attributeValue("sex").trim();
s.sex = sex.equals("male");
s.cellphone = x_student.attributeValue("cellphone").trim();
return s;
}

public static void main(String[] args)
{
File xmlFile = new File("student.xml");
try
{
Student s = readXML (xmlFile);
System.out.println("read complete");
} catch (Exception e)
{

e.printStackTrace();
}


System.out.println("exit");
}


.addAttribute輸入
.attributeValue讀取


常用工具類.AfXml

 

13.4 JSON
XML多用於配置檔案,JSON多用於資料傳輸
{
"id":20111111, //示例
"name":"仁豪",
"phone":"1300099";
}
使用JSON-ORG 或者jsonlib來操作JSON

其中,大括號表示一個JSON物件。在這個物件裡,包含4個欄位。
例如,
"id" 欄位的值為 20180001,是數字型別 ( Number )
"name" 欄位的值為 "邵", 是字串型別 ( String )
"sex" 欄位的值為 true,是布林型別 (Boolean)

可以很直觀的發現,
如果值是一個String,則應該用雙引號,如"邵發"
如果值是一個Number,則不加雙引號,如20180001
如果值是一個Boolean,則應該是 true 或者是 false, 不加雙引號

加入JSON-java JSON-lib

JSONObject jobj = new JSONObject();
jobj.put("id", 20180001); // Integer
jobj.put("name", "邵發"); // String
jobj.put("sex", true); // Boolean
jobj.put("cellphone", "13810012345"); // String

String jsonstr = jobj.toString(2);
System.out.println(jsonstr);


JSONObject裡的欄位顯示順序是無關的,誰先上、誰在下不影響最終結果。


JSON的語法格式
用JSON可以表示Object資訊,以一對大括號包圍,裡面可以多個欄位。
例如:
{
"name": "邵發",
"id": 1208439,
"sex": true,
"phone": "13810012345"
}
語法要點:
- 以大括號包圍
- 欄位名稱要加雙引號
- 欄位的值可以為String, Integer, Boolean, Array, null 等型別
- 每個欄位以逗號分隔 (最後一個欄位末尾不能加逗號)
- 欄位的順序無關
注:JSONObject在概念上對應於Java裡的Map

JSON Array
例如
[
{
"name": "shao",
"id": 2018001,
"phone": "13810012345",
"sex": true
},
{
"name": "wang",
"id": 2018002,
"phone": "13810043245",
"sex": true
},
{
"name": "li",
"id": 2018003,
"phone": "1345015445",
"sex": true
}
]
語法要點:
- 以中括號包圍
- 元素型別可以是任意型別。例如,元素型別可以是String,也可以是Object

注:JSONObject在概念上對應於Java裡的陣列


巢狀
Object的欄位型別可以是Array
例如
{
"classId": 201801,
"className": "計科1801",
"members":
[
"shaofa",
"wang",
"li"
]
}

也就是說,Object 和 Array 是可以互相巢狀的。


JSON的生成與解析
JSON的生成
public class CreateJSON
{
// 生成 JSONObject
public static void test1()
{
JSONObject j1 = new JSONObject();
j1.put("name", "邵發");
j1.put("id", 1239349);
j1.put("sex", true);
j1.put("phone", "13810012345");
String jsonstr = j1.toString(2); // 縮排2
System.out.println(jsonstr);
}

// 生成JSONArray : 元素是字串
public static void test2()
{
JSONArray j = new JSONArray();
j.put("shao");
j.put("wang");
j.put("li");
j.put("chen");
String jsonstr = j.toString(2); // 縮排2
System.out.println(jsonstr);
}

// POJO -> JSONObject
// 如果是POJO物件 ( 已經添加了 getter ),則可以直接轉成JSONObject
public static void test3()
{
Student stu = new Student("邵發", 1238909, true, "13810012345");

JSONObject j = new JSONObject(stu);
String jsonstr = j.toString(2); // 縮排2
System.out.println(jsonstr);
}

// List -> JSONArray
public static void test4()
{
List<Student> sss = new ArrayList<Student>();
sss.add(new Student("shao", 1238901, true, "13810012345"));
sss.add(new Student("wang", 1238902, true, "13456678895"));
sss.add(new Student("qian", 1238903, false, "1381432435"));
sss.add(new Student("chen", 1238904, true, "13342353446"));

JSONArray jarray = new JSONArray();
for( Student s : sss)
{
JSONObject j1 = new JSONObject(s);
jarray.put( j1 );
}

String jsonstr = jarray.toString(2); // 縮排2
System.out.println(jsonstr);
}

// List -> JSONArray
public static void test5()
{
List<Student> sss = new ArrayList();
sss.add(new Student("shao", 1238901, true, "13810012345"));
sss.add(new Student("wang", 1238902, true, "13456678895"));
sss.add(new Student("qian", 1238903, false, "1381432435"));
sss.add(new Student("chen", 1238904, true, "13342353446"));

// 直接把ArrayList轉成JSON,前提是ArrayList裡的元素是可以轉化的
JSONArray jarray = new JSONArray(sss);

String jsonstr = jarray.toString(2); // 縮排2
System.out.println(jsonstr);
}

JSON儲存到檔案13.4_05網盤

json.org庫檔案


13.5 Properties
另外一種配置檔案
#開頭是註釋
port=80
server=127.0.0.1
*.properties檔案的格式;
1.#開頭是註釋行
2.key=value

顯然*.properties裡的key不允許重複.

不適合儲存樹狀形式的資料

public class Config
{
public String server;
public int port;


public void load(File f) throws Exception
{
Properties props = new Properties();

// 從 *.properties 檔案載入資料
InputStream inputStream = new FileInputStream(f);
try {
props.load( new FileInputStream(f));

}finally
{
inputStream.close();
}

// 獲取 Property 配置
server = props.getProperty("server");
port = Integer.valueOf( props.getProperty("port", "80"));



}

public void save (File f) throws Exception
{
Properties props = new Properties();
props.put("server", server);
props.put("port", String.valueOf(port));

// 儲存到 *.properties 檔案
OutputStream outputStream = new FileOutputStream(f);
try {
props.store(outputStream, "just test");
}finally {
outputStream.close();
}
}
}

java.util.Properties
Properties工具類,可以儲存多個key-value
-getProperties/-setProperties
-load/store 讀取檔案/寫入檔案


第14章 反射機制
java.lang.Class
它是Java的基礎類,用於描述一個class物件.
在檔案系統中,class以檔案的形式存在Student.class在執行時的JVM(Java虛擬機器)中,
該*.class檔案被載入到記憶體中成為一個物件,物件的型別是java.lang.Class

Class cls = Student.class;
System.out.println("Name: " + cls.getName());
其中,cls這個物件就是這個Class的描述。

Student obj = new Student();
Class cls = obj.getClass();
其中,obj是一個物件,obj.getClass()則是獲取它的Class 描述。
Class有什麼用
用於判斷一個執行時物件的型別
public static void test1(Object obj)
{
Class cls = Student.class;
if(cls.isInstance(obj))
{
// 判斷obj是不是Student型別
System.out.println("is an instance of Student");
}
else
{
System.out.println("不是 an instance of Student");
}
}
其中,cls.Instance(obj)意思是判斷obj是否為my.Student 的一個例項。

另一種寫法,也可以判斷一個執行時物件的型別
public static void test2(Object obj)
{
String clsName = obj.getClass().getName();
if(clsName.equals("my.Student"))
{
System.out.println("is an instance of Student");
}
else
{
System.out.println("不是 an instance of Student");
}
}

 

比較S1和s2
Student s1 = new Student(123, "shaofa", "199900000");
Student s2 = new Student(124, "shaofa", "199900000");
if(s1.equals(s2))
{
System.out.println("equal");
}
else
{
System.out.println("not equal");
}

public boolean equals(Object obj)
{
// 與一個Student物件比較
if(this.getClass().isInstance(obj))
{
Student other = (Student) obj;
return other.id == this.id;
}

// 與一個String物件比較
if(String.class.isInstance(obj))
{
String other = (String)obj;
return other.equals(this.name);
}

// 與一個Integer物件比較
if(Integer.class.isInstance(obj))
{
Integer other = (Integer)obj;
return this.id == other;
}

return false;
}

14.2反射
Reflection
給定一個*.class檔案中,我們可以得到以下資訊:
類名 (含package路徑)
函式 (名稱,引數型別,返回值)
域 (名稱,型別)
實現的介面 (interfaces) ……

使用java.lang.reflect.*下的類來實現。。。

注意:不需要原始檔,只需要*.class檔案

public static Class loadClass() throws Exception
{
// 載入my/Student.class
Class cls = Class.forName("my.Student");

// 獲取函式列表
Method[] methods = cls.getMethods();

// 獲取成員變數列表
Field[] fields = cls.getFields();

for(Method m : methods)
{
System.out.println("函式: " + m.toString());
}

return cls;
}

public static void main(String[] args)
{
try
{
// 得到Class
Class cls = loadClass();

//test2();

}catch(Exception e)
{
e.printStackTrace();
}
}


也就是說,雖然我們不知道Student類的程式碼,但是這個 class檔案本身可以反映(reflect)出這些資訊。。。
結論:通過Reflection機制,我們可以直接 從class檔案反推出它有哪個成員變數、有哪 些函式


遍歷Method
已經函式名,找到物件的
// 從Class中獲取Method : 按名稱查詢 (假設沒有重名的函式)
public static void findMethod(Class cls) throws Exception
{
String methodName = "setId";

// 獲取所有Method列表,順序比對
Method[] methods = cls.getMethods();
for(Method m : methods) //遍歷
{
if(m.getName().equals(methodName)) //比較
{
break;
}
}

}

例2 查詢Method
已經函式名,引數列表,尋找Method
public static void findMethod2(Class cls) throws Exception
{
String methodName = "setId";
Class[] parameterTypes = { int.class };
Method m = cls.getMethod(methodName, parameterTypes);

System.out.println("got the method");
}

例3 Reflection的運用

// 一個完整的reflection測試
public static void test1() throws Exception
{
// 載入my/Student.class
Class cls = Class.forName("my.Student");

// 建立一個例項, 要求有一個不帶引數的建構函式
Object obj = cls.newInstance();

// 找到method
Class[] parameterTypes = { int.class };
Method m1 = cls.getMethod("setId", parameterTypes); //"setId"引數是函式名,parameterTypes是函式值型別.

// 呼叫method
Object[] paramters = { 123 };
m1.invoke(obj, paramters);

// 顯示結果
System.out.println("result: " + obj.toString());
}
public static void main(String[] args)
{
try
{
// 得到Class
Class cls = loadClass();
test1();

}catch(Exception e)
{
e.printStackTrace();
}
}

Class.forName(…) 可以載入一個class檔案。。
(1)由誰負責載入? 由Class Loader負責載入,具體地講,JVM提供了一 個內建的 bootstrap class loader。

(2)從哪裡載入? 從classpath下尋找class,以及該class裡面import 的class

小結 1.Reflection: 從*.class檔案中,可以reflect得到它 的描述資訊:類名,函式名等
2. 通過Class,可以建立一個例項instance
3. 通過Class,可以找到它的某個Method,進而就 可以呼叫這個函式。
4. 需要知道Class Loader的存在及其作用

反射機制
Reflection 應用框架
一般地,當設計一個應用框架時才有可能用 到Reflection技術。

例如,著名的Struts Spring Hibernate框架

AfShell: Command Ui Framework
AfShell: 一個自定義的Framework,使用這個 Framework可以很方便的寫出基於命令列介面的應用 程式。

新增AfShell框架支援
(1)加入afshell.jar到BuildPath
(2)新增afshell.properties
(3)在main中呼叫 AfShell.start() 完成!

AfShell: Command Ui Framework
新增命令處理
(1)自定義Action類 此類必須有一個函式public int execute()

(2)配置afshell.properties login=my.LoginAction

AfShell: Command Ui Framework
新增命令列引數的支援 例如,使用者輸入 login username=shaofa password=123456

(1)新增成員變數 username password
(2)新增setter setUsername() setPassword() 則AfShell框架會自動的把引數值傳進來,然後再呼叫 execute()函式。。。


設計方法
那麼,這樣的一個Framework該如何實現? 例如,使用者輸入了 login username=shaofa password=123456

(1)得到命令名 "login"
(2)通過配置檔案,找到my.LoginAction
(3)載入my.LoginAction,建立一個例項
(4)根據username=shaofa,找到setter函式setUsername, 呼叫此函式
(5)根據password=123456,找到setter函式並呼叫
(6)呼叫excute()函式,得到返回值 完成!

小結 介紹一個應用框架AfShell的使用方法
如果要使用這個框架,並不需要知道Reflection技術
如果自己設計一個框架給別人使用,則需要精確掌 握和運用Reflection技術。
請試著使用Reflection技術,自己來實現這個框架。

反射4

(1)得到命令名 "login"
(2)通過配置檔案,找到my.LoginAction
(3)載入my.LoginAction,建立一個例項
(4)根據username=shaofa,找到setter函式setUsername, 呼叫此函式
(5)根據password=123456,找到setter函式並呼叫
(6)呼叫excute()函式,得到返回值 完成!

瞭解AfShell框架的架構方法.

 

15.1執行緒
:你手下管理著兩名員工,一個是Confucian,一 個是Buddhist,如何讓他們同時工作?

Confucian e1 = new Confucian(); e1.doJob();

Buddhist e2 = new Buddhist(); e2.doJob();

我們發現,只有e1先工作完成了,才能開始e2的工作, 兩者無法“同時”進行。。。

執行緒 Thread
引入執行緒機制,可以並行地做多件事情。。。
繼承於Thread類,重寫run(),填上它要做的工作。。 extends Thread

public class Buddhist extends Thread
{
@Override
public void run()
{
for(int i=1; i<=500; i++)
{
System.out.println("ma mi ma mi hong ..." + i);

try
{
Thread.sleep(100);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args)
{
Confucian e1 = new Confucian();
e1.start();
}

執行緒 Thread
啟動執行緒 Confucian e1 = new Confucian(); e1.start();

Buddhist e2 = new Buddhist(); e2.start();

啟動一個執行緒 start()

執行緒主函式 run() : 描述了這個執行緒的工作任務

執行緒的排程
CPU只有一個,而系統中同時執行的程序有幾十 個。每個程序又同時執行n個執行緒。忙得過來?


時間片劃分:將1秒鐘劃分為N個小的時間片,大家 輪流執行。

比如,以5ms為一個時間片,每個執行緒執行5ms之後 就輪到下一個執行緒執行。

這樣,所有的執行緒都有機會被執行,由於切換的速 度很快,給使用者的感覺是:它們好像同時在執行.

Sleep暫歇

執行緒可以執行Sleep操作,用於告訴作業系統: 我要 休息一會,接下來的一段時間內不要管我。。。

例如, Thread.sleep(10000); 表示接下來將休息10秒鐘,在這10秒鐘內該執行緒主 動放棄CPU的使用權。。。

排隊機制

假設執行緒①休息了10秒之後,那麼醒來之後就能立 即獲得CPU的使用權嗎?不能。醒來之後要排隊!

系統中有一個佇列,所有需要CPU的執行緒在這裡排隊。

作業系統根據特定的演算法,來決定下一個是誰。 (比始說,有的執行緒優先順序較高,而有的較低)

使用sleep

當建立執行緒時,sleep是一個經常要使用的函式 原則:儘可能少的佔用CPU,讓別的執行緒也有機制執行。

試想,如果一個執行緒裡是這樣的:
while(true)
{
System.out.println("我就賴著CPU。。。");
}
那其他的執行緒會高興嗎?

雖然我們看到在工作管理員下很多執行緒,但在同一時刻,大多數執行緒都在sleep

引入執行緒的概念 (多條線並行發展) 介紹了執行緒排程的機制,由作業系統負責排程 介紹了sleep的作用,及其重要意義

15.2 執行緒的建立
第一種方法繼承Thread
extends Thread
重寫run方法,在主函式入口用start()啟動.

第二章方法:實現Runnable介面
public class Buddhist implements Runnable
{

@Override
public void run()
{
for(int i=1;i<=500;i++)
{
System.out.println("emmmmmmmm.");
try
{
Thread.sleep(100);
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}

}
public static void main(String[] args)
{
Buddhist e2 = new Buddhist();
Thread t=new Thread(e2);
t.start();
}

這是因為java不允許多重繼承,如果你的類已經繼承於別的類,又要用執行緒來執行,則使用這種方法.

有時候使用匿名類更方便一些
Thread t=new Thread(){
public void run()
{
}
};
t.start();


public static void main(String[] args)
{


Thread t2=new Thread() {

@Override
public void run()
{
for(int i=1;i<=500;i++)
{
System.out.println("emmmmmmmm.");
try
{
Thread.sleep(100);
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
};
t2.start();
}


15.3執行緒
執行緒的終止
當一個執行緒的start()呼叫後,執行緒為Alive狀態。 當一個執行緒的主函式run()退出後,執行緒死亡(Dead)。
public void run()
{
for(int i=1; i<=10; i++)
{
System.out.println("..." + i);
}
}
該執行緒列印10句後終止。
(可以類比一下主執行緒,當main()中退出時,主程式終止。。。)

讓一個執行緒終止,就是要想辦法讓它從run()中退出。 例如,設定一個標識變數,
public boolean quitflag = false;
public void run()
{
for(int i=1; i<=10; i++)
{
if(quitflag) break; // 退出迴圈
System.out.println("..." + i);
}
}


例子
public class Confucian extends Thread
{
public boolean quitflag = false;

@Override
public void run()
{
for(int i=1; i<=10; i++)
{
if(quitflag) break;

System.out.println("人之初,性本善 ..." + i);

try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

System.out.println("Confucian exit.");
}
}

public class Test1
{

public static void main(String[] args)
{
// 建立並啟動執行緒
Confucian t1 = new Confucian();
t1.start();

// 按回車後,中止執行緒,然後退出程式
InputStreamReader m = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(m);
try{
reader.readLine(); //由這句來讀回車
reader.close();

// 控制執行緒的退出
t1.quitflag = true;
}
catch(Exception e)
{
}

}

}

sleep與interrupt
背景:Buddhist中每5秒輸出一次,sleep的時間很長,如何讓它立即退出??

方法: 呼叫 t2.interrupt()來中斷目標執行緒。打斷執行緒的sleep
則sleep()函式將丟擲異常,結束sleep狀態

public static void main(String[] args)
{
// 建立並啟動執行緒

Buddhist t2 = new Buddhist();
t2.start();

// 按回車後,中止執行緒,然後退出程式
InputStreamReader m = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(m);
try{
reader.readLine();
reader.close();

// 控制執行緒的退出
t2.quitflag = true;
t2.interrupt(); //打斷sleep
}
catch(Exception e)
{
}

等待執行緒的退出
Buddhist t3 = new Buddhist(); t3.start();

// 等待t4退出 t3.join();

join() 函式會阻塞住當前執行緒,等待目標執行緒退出後, 再繼續執行。。。

注:在Java裡, join()只起等待作用(執行緒死亡後不需 要手工回收,由JVM自動回收)

public static void main(String[] args)
{
// 建立並啟動執行緒
Buddhist t3 = new Buddhist();
t3.start();

// 等待t3退出
try
{
t3.join(); //阻塞住當前執行緒,等待目標執行緒退出後,再繼續執行.
} catch (InterruptedException e)
{
e.printStackTrace();
}

// 在t3執行緒完成之後,再執行下面的事情
for(int i=0; i<3; i++)
{
System.out.println("我自己的事情");
}

System.out.println("program exit");
}

注意事項
本質上,執行緒只有一種退出方式:從run()函式自然退出。 我們需要通過程式設計,控制它從run()中退出。

不能 stop() ,不能destroy()… deprecated

很多初學者會有一個想法:有沒有簡單暴力的手段, 直接殺死一個執行緒?
比如kill() ? 千萬不要這麼想! (正在使用ATM存錢的時候,斷電了可以嗎?)

小結
執行緒只有一種終止方式:從run()中退出。

介紹如何控制一個執行緒的退出。

15.4
引例
金鑰Key :一個16位元組的陣列
金鑰更新執行緒KeyUpdater:定期更新金鑰
金鑰獲取執行緒KeyPrinter:列印顯示金鑰


金鑰的完整性:金鑰的16位元組必須相同。

例如,
下面是有效金鑰 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A
下面是無效金鑰 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1B 1B 1B

金鑰更新時應保證金鑰的完整性。

什麼樣的情況算是出錯?KeyPrinter獲取金鑰,發現金鑰 不完整(16個位元組不全相同),則認為此金鑰無效!

分析程式碼,推斷有沒有可能出錯。。