編譯和運行
- javac 編譯文件 需要一個文件名(Welcome.java)
- java 運行程序 需要指定類名(Welcome) 不需要帶擴展名
- 通配符javac Employee*.java
- EmployeeTest類中包含Employee類,直接編譯EmployeeTest類,Employee也會被自動編譯。可以認為Java編譯器內(nèi)置了make/nmake功能
Microsoft_Program_Maintenance_Utility,外號NMAKE,顧名思義,是用來管理程序的工具。其實說白了,就是一個解釋程序。它處理一種叫做makefile的文件(以mak為后綴),解釋里面的語句并執(zhí)行相應(yīng)的指令。我們編寫makefile文件,按照規(guī)定的語法描述文件之間的依賴關(guān)系,以及與該依賴關(guān)系相關(guān)聯(lián)的一系列操作。然后在調(diào)用NMAKE時,它會檢查所有相關(guān)的文件,如果相關(guān)文件的time_stamp(文件最后一次被修改的時間,一個32位數(shù))小于依賴文件(dependent_file)的times_tamp,NMAKE就執(zhí)行依賴關(guān)系相關(guān)聯(lián)的操作。
打包文件
- jar cvfm *.class將所有以.class結(jié)尾的字節(jié)碼文件打包到一個“JAR文件”
applet相關(guān)
appletviewer可以快速測試applet
- Java區(qū)分大小寫
- 訪問修飾符
- 根據(jù)Java語言規(guī)范,main方法必須聲明為public
- 每個Java應(yīng)用程序都必須有一個main方法,并且是靜態(tài)的。
- 每個句子必須用分號結(jié)束。特別需要說明,回車不是語句的結(jié)束標(biāo)志, 如果需要可以將一條語句寫在多行上。
- object.method(parameters)
- 強語言類型,每個變量都需要聲明類型。
數(shù)據(jù)類型
8基本類型 4整型,2浮點型,字符類型char,和真值boolean型。
整型
byte 1字節(jié),short 2字節(jié),int 4字節(jié),long 8字節(jié)
- 長整型,后綴L或l(400000000000000L)
- 十六進制,前綴0x或0X(0xCAFE)
- 八進制,前綴0([010] = [8])
- 二進制,前綴0b或0B([0b1001] = [9])
- 數(shù)字面量,1_000_000(或0b1111_0100_0100_0000)表示一百萬。下劃線只是為了易讀。Java編譯器會去除這些下劃線。
浮點類型
float 4字節(jié),double 8字節(jié)
- float,后綴F或f(3.14F)
- double,數(shù)值精度為float類型的兩倍。沒有后綴F的浮點數(shù)值(3.14),默認為double類型,也可以添加后綴D或d
- 十六進制表示浮點數(shù)值。0.125 = 2的-3次方→0x1.0p-3。p表示指數(shù),不是e。尾數(shù)采用十六進制,指數(shù)采用十進制。指數(shù)的基數(shù)是2,為不是10
- Double.POSITIVE_INFINITY 表示正無窮大。
- Double.NEGATIVE_INFINITY表示負無窮大。
- Double.NaN表示不是一個數(shù)字。使用方法:
if(x == Double.NaN) //is never true
if(Double.isNaN(x)) // check whether x is "not a number"
- 浮點數(shù)值不適用于無法接受誤差的金融計算中。
System.out.print(2.0 - 1.1);
0.8999999999999999
// 浮點數(shù)值采用二進制系統(tǒng)表示, 而在二進制系統(tǒng)中無法精確地表示分數(shù) 1/10
// 就好像十進制無法精確地表示出分數(shù)1/3一樣
// 不允許有任何舍入誤差,應(yīng)該使用BigDecimal類
char類型
- char類型可以表示為十六進制值。\u005B\u005D→[],注意注釋中的\u可能會產(chǎn)生語法錯誤。
boolean類型
- false和true。整型值和布爾值不能轉(zhuǎn)換。
變量
- 變量名的長度基本上沒有限制。
- 哪些 Unicode 字符屬于 Java 中的“ 字母”可以存在變量中,使用Character類的isJavaldentifierStart、isJavaldentifierPart方法檢查。
- 不能使用 Java 保留字作為變量名。
- 可以在一行中聲明多個變量,但不推薦。
- 不要使用未初始化的變量。
常量
const是Java保留字,但并未使用。必須使用final定義常量。
- 用關(guān)鍵字final指示常量。表示這個變量只能被賦值一次。
- 用關(guān)鍵字static final設(shè)置一個類常量。
- 習(xí)慣上,常量名為全大寫。
運算符
- 整數(shù)被0除,產(chǎn)生一個異常。
- 浮點數(shù)被0除,得到無窮大或NaN結(jié)果。
- 對于使用strictfp 關(guān)鍵字標(biāo)記的方法必須使用嚴格的浮點計算來生成可再生的結(jié)果。如果將一個類標(biāo)記為strictfp, 這個類中的所有方法都要使用嚴格的浮點計算。實際的計算方式取決于Intel處理器的行為
public static strictfp void main(String[] args)
數(shù)學(xué)函數(shù)
import static java.1ang.Math.*;
- sqrt(x) 開方
- pow(x,a) x的a次冪
- floorMod(x,y) 對x求余y
自增與自減
int m = 7;
int n = 7;
int a = 2 * ++m; // now a is 16, m is 8
int b = 2 * n++; // now b is 14, n is 8
不建議在表達式中使用++
位運算符
- & ("and")、 | ("or") 、^("xor") 、~ ("not")
這些運算符按位模式處理。例如,n是一個整數(shù)變量,用二進制表示的n從右邊的第四位為1,則:
int m= (n & 0b1000)/0b1000;
返回1,否則返回0。利用&并結(jié)合使用適當(dāng)?shù)?的冪,可以把其他位掩掉只保留其中的某一位。
- >>、<<運算符將位模式左移/右移
- >>>運算符用0填充高位,>>會用符號位填充高位。不存在<<<
運算級別
- a && b || c
等價于
(a && b) || c - a += b += c;(+=為右結(jié)合運算符)
等價于
a += (b += c);
字符串是否相等
- s.equals(t)
- "Hello".equals(greeting)
- "Hello".equalsIgnoreCase("hello")忽略大小寫
- 不要使用 == 檢測兩個字符串是否相等。只有字符串常量才是共享的,+或subString操作產(chǎn)生的結(jié)果是不共享的。
- compareTo方法
空串和null
- 空串“”,是一個對象:
if(str.length == 0)
if(str.equals("")) - null,沒有任何對象:
if(str == null)
遍歷一個字符串,并且依次査看每一個碼點
int[] codePoints = str.codePoints().toArray();
把一個碼點數(shù)組轉(zhuǎn)換為一個字符串:
String str = new String(codePoints, 0, codePoints.length) ;
輸出格式
沿用了C庫函數(shù)的printf方法,可以進行格式化
double x = 333.333333333334f;
System.out.printf("%8.2f", x);
String name = "Jack";
int age = 12;
System.out.printf("Hello,%s,you'll be %d year-old", name, age);

System.out.printf("%,.2f", 10000.0 / 3.0);
3,333.33

switch語句
有可能觸發(fā)多個case分支,編譯代碼時可以考慮加上-Xlint:fallthrough選項,例如:
javac -Xlint:fallthrough Test.java
如果確實是想用這種“直通式(fallthrough)行為”,可以在外圍方法添加注解
@SuppressWarnings("fallthrough")
就不會對這個方法生成警告了
帶標(biāo)簽的break
- 跳出多重嵌套的循環(huán)語句。
read_data:
while(...){
...
for(...){
....
break read_data;
}
}
標(biāo)簽需要緊跟一個":"冒號。
- 跳出if語句/塊語句
- 只能跳出語句塊,不能跳入語句塊。
帶標(biāo)簽的continue
跳至與標(biāo)簽匹配的循環(huán)首部
大數(shù)值
BigInteger和BigDecimal
這兩個類可以處理包含任意長度數(shù)字序列的數(shù)值。BigInteger類實現(xiàn)了任意精度的整數(shù)運算,BigDecimal類實現(xiàn)了任意精度的浮點數(shù)運算。
常用Biglnteger API:
Biglnteger add(Biglnteger other) // +
Biglnteger subtract(Biglnteger other) // -
Biglnteger multipiy(Biginteger other) // *
Biglnteger divide(Biglnteger other) // /
Biglnteger mod(Biglnteger other) // 取余
int compareTo(Biglnteger other) // 相等為0 小于為-1,大于為1
static Biglnteger valueOf(long x) // 返回一個值為x的Biglnteger 類型數(shù)據(jù)
常用BigDecimal API:
BigDecimal add(BigDecimal other) // +
BigDecimal subtract(BigDecimal other) // -
BigDecimal multipiy(BigDecimal other) // *
BigDecimal divide(BigDecimal other, RoundingMode mode) // / 給出舍入方式,RoundingMode.HALF_UP 為四舍五入
Biglnteger mod(Biglnteger other) // 取余
int compareTo(Biglnteger other) // 相等為0 小于為-1,大于為1
static BigDecimal valueOf(long x); // 返回值為x
static BigDecimal valueOf(long x ,int scale);// 返回值為x/(10的scale次方)的實數(shù)
數(shù)組
聲明:int[] a=new int[100];
簡化: int[] b={1,2,3,4} → b = new int{1,2,3,4}
- 整型數(shù)組初始化所有元素為0
- boolean數(shù)組初始化所有元素為false
- string數(shù)組初始化所有元素為null
- new int[0] 與 null不同
- Arrays.toString()方法返回一個包含數(shù)組元素的字符串
一旦創(chuàng)建了數(shù)組,就不能改變它的大小。如果需要經(jīng)常在運行過程中擴展數(shù)組的大小,使用數(shù)組列表 array list有關(guān)數(shù)組列表
數(shù)組拷貝
int[] b = {1,2,3,4,5};
int[] a = b;
a[2] = 10; // b[2] == 10 為 true
上述代碼,兩個變量引用同一個數(shù)組。
int[] copied = Arrays.copyOf(b, b.length);
上述代碼,可以將一個數(shù)組的值拷貝到一個新數(shù)組中。第二個參數(shù)為新數(shù)組的長度。如果長度小于原數(shù)組的長度,則只拷貝前面的數(shù)據(jù)元素。
多維數(shù)組
即 數(shù)組的數(shù)組

balances[i]表示引用第i個子數(shù)組,即第i行
balances[i][j]表示引用第i個子數(shù)組的第j項
double[][] balances = new double[10] [6]; // Java
等價于(分配了一個包含10個指針的數(shù)組)
double** balances = new double*[10] ; // C++
for (i = 0; i < 10; i++)
balances[i] = new double[6];
多維數(shù)組的拷貝
int[][] a = {{1,2,3,4},{1,2,3,4},{1,2,3,4}};
int[][] b = a.clone();
System.out.println(a == b); // false
b[2][2] = 100;
System.out.println(a[2][2]);// 100
上述為淺拷貝,a,b為兩個不同的對象,但指向的是同一地址。因為是包含對象的對象??聪耲dk描述:

尤其注意紅框部分:

即用clone()時,除了基礎(chǔ)數(shù)據(jù)和String類型的不受影響外,其他復(fù)雜類型(如集合、對象等)還是會受到影響的,除非你對每個對象里的復(fù)雜類型又進行了clone(),但是如果一個對象的層次非常深,那么clone()起來非常復(fù)雜,還有可能出現(xiàn)遺漏。
所以需要深克?。簩崿F(xiàn)Cloneable接口、重寫clone方法
值得一提的是,在Java中,任何對象變量的值都是對存儲在另外一個地方的一個對象的引用
Date d = harry.getHireDay();

對d調(diào)用更改器方法同樣會修改這個雇員對象的私有狀態(tài)
日期
標(biāo)準(zhǔn)Java類庫包含了兩個類:
- Date類 GWT格林威治時間,科學(xué)標(biāo)準(zhǔn)時間(遵循了世界上大多數(shù)地區(qū)使用的陽歷表示)
- LocalDate類 日歷表示法(本地時間)
Date birthday = new Date();
Date deadline = birthday;
System.out.println(deadline); // Wed Jul 11 11:22:22 CST 2018 //
System.out.println(LocalDate.now()); // 2018-07-11
LocalDate date1999_12_31 = LocalDate.of(1999,12,31); // 1999-12-31
LocalDate localDate = date1999_12_31.plusDays(1000); // 返回一個新對象 不改變對象date1999_12_31
System.out.println(localDate);// 2002-09-26
GregorianCalendar someDay = new GregorianCalendar(1999,12,31);
someDay.add(Calendar.DAY_OF_MONTH, 1000);
System.out.println(someDay.get(Calendar.YEAR) + "-" + someDay.get(Calendar.MONTH) + "-" +
someDay.get(Calendar.DAY_OF_MONTH));// someDay對象狀態(tài)會改變
只訪問對象而不修改對象的方法稱為訪問器方法(access method),更改器方法(mutator method)會改變對象狀態(tài)。
GregorianCalendar.add方法為更改器方法
LocalDate.getYear 和 GregorianCalendar.get 均為訪問器方法
打印當(dāng)前月份日歷:
// 打印當(dāng)月日歷
LocalDate date = LocalDate.now();
System.out.println("\n\nMON TUE WED THU FRI SAT SUN");
// 獲取月、日
int month = date.getMonthValue();
int day = date.getDayOfMonth();
// 獲取本月第一天
date = date.minusDays(day - 1);
// 本月第一天第一天為周幾?
int dayOfWeek = date.getDayOfWeek().getValue();
// 打印日歷第一行的縮進
for (int i = 1; i < dayOfWeek; i++) {
System.out.print(" ");
}
// 打印主體
while (date.getMonthValue() == month) {
System.out.printf("%3d", date.getDayOfMonth());
if (date.getDayOfMonth() == day)
System.out.print("*");
else
System.out.print(" ");
date = date.plusDays(1);
if (date.getDayOfWeek().getValue() == 1)
System.out.println();
}
輸出如下:
MON TUE WED THU FRI SAT SUN
1
2 3 4 5 6 7 8
9 10 11* 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
JavaSE8引入另外一些類來處理日期和時間
// 時間線 Instant 實現(xiàn)接口 Temporal
// 查看算法的運行時間
Instant start = Instant.now();
printCalendar();
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end);
long millis = timeElapsed.toMillis();
System.out.println(millis); // 9
// 比較兩個算法運行時間
Instant start1 = Instant.now();
printCalendar1();
Instant end1 = Instant.now();
Duration timeElapsed1 = Duration.between(start1, end1);
boolean faster = timeElapsed1.minus(timeElapsed).isNegative();
Instant為時間線上的某個點
Duration.between獲取時間差。
日期調(diào)整器
TemporalAdjusters類提供了大量常見調(diào)整的靜態(tài)方法。
// 日期調(diào)整器
LocalDate nextTuesday = LocalDate.of(2018, 7, 1).with(TemporalAdjusters.nextOrSame(
DayOfWeek.TUESDAY)); // 從2018.7.1號開始的下一個周二
// 實現(xiàn)自己的日期調(diào)整器
TemporalAdjuster NEXT_SUNDAY = w -> {
LocalDate result = (LocalDate) w; //需要強制轉(zhuǎn)換 TemporalAdjuster任何一個實現(xiàn)類都可
do {
result = result.plusDays(1);
}while (result.getDayOfWeek().getValue()!=7);
return result;
};
System.out.println(LocalDate.now().with(NEXT_SUNDAY)); // 2018-7-15
// TemporalAdjusters.ofDateAdjuster(UnaryOperator<LocalDate> dateBasedAdjuster) w默認為LocalDate類型則無須強制轉(zhuǎn)換
TemporalAdjuster NEXT_SATURDAY = TemporalAdjusters.ofDateAdjuster(w -> {
LocalDate date = w;
do {
date = date.plusDays(1);
} while (date.getDayOfWeek().getValue() != 6);
return date;
});
與遺留util.Date類的轉(zhuǎn)換
Date birthday = new Date();
System.out.println(birthday); // Wed Jul 11 15:47:36 CST 2018
birthday.toInstant(); // 2018-07-11T07:47:36.172Z
Date.from(Instant.now()); // Wed Jul 11 15:50:40 CST 2018
隱式參數(shù)與顯式參數(shù)
例如:
public void raiseSalary(double byPercent){
double raise = salary * byPercent / 100;
salary += raise;
}
其中byPercent為顯式參數(shù),salary為隱式參數(shù)(方法的調(diào)用目標(biāo)的參數(shù)),我們也可使用關(guān)鍵字this表示隱式參數(shù),這樣便于將實例域與局部變量明顯區(qū)分開。
final實例域
對象包含final實例域,構(gòu)建對象時則必須初始化這樣的域。即確保在每一個構(gòu)造器執(zhí)行之后。這個域的值被設(shè)置,并在后面的操作中,不能夠再對其修改。
final修飾符大多應(yīng)用于基本(primitive)類型域,或不可變(immutable)類的域(如果類中的每個方法都不會改變其對象,這種類就是不可變的類)
對于可變對象,使用final容易造成混亂:
private final StringBuiIder evaluations;
構(gòu)造器構(gòu)造:
evaluations = new StringBuilder();
final的修飾只是表示存儲在evaluations變量中的對象引用不會指向其他StringBuilder對象。不過這個對象內(nèi)容可以更改:
public void giveGoldStarO
{
evaluations.append(LocalDate.now() + ":Gold star!\n");
}
而String是個不可變的類。String中最核心的就是value[]值了,這個值不會被類中任何方法修改到。
private final char value[];
靜態(tài)域與靜態(tài)方法
將域定義為static,每個類中只有一個這樣的域。
爾每一個對象對于所有的實例域都有自己的一份拷貝。
靜態(tài)域可以理解為類域,屬于類所有。
class Employee{
private static int nextId = 1;
private int id;
}
每一個雇員都有一個自己的id域,但是Employee所有實例都共享一個nextId域。即,1000個Employee對象,有1000個實例域id,但是只有一個靜態(tài)域nextId。
即使沒有一個雇員對象,靜態(tài)域nextId也存在。它屬于類,不屬于任何獨立的對象。
public void setld()
{
id = nextld;
nextld++;
}
靜態(tài)變量使用較少,靜態(tài)常量使用較多。
public static final double PI = 3.14159265358979323846;
Math.PI就可以取到值。
如果沒有static這個字段,每個Math對象都將會有一份PI拷貝。
public final static PrintStream out = null;
System.out也是靜態(tài)常量。
每個類對象都可以對公有域進行修改,最好不要將域聲明為public。然而公有常量(final域)是沒問題的。因為被聲明為final,out不會被更改為其他打印流。
總結(jié):類中聲明static域可以看作是公有量,static+final修飾的為公有常量。
/**
* Reassigns the "standard" output stream.
*
* <p>First, if there is a security manager, its <code>checkPermission</code>
* method is called with a <code>RuntimePermission("setIO")</code> permission
* to see if it's ok to reassign the "standard" output stream.
*
* @param out the new standard output stream
*
* @throws SecurityException
* if a security manager exists and its
* <code>checkPermission</code> method doesn't allow
* reassigning of the standard output stream.
*
* @see SecurityManager#checkPermission
* @see java.lang.RuntimePermission
*
* @since JDK1.1
*/
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
private static native void setOut0(PrintStream out);
setOut方法可以將System.out設(shè)置為不同的流,即修改final變量的值。
因為setOut0方法是native,本地方法不是用Java語言實現(xiàn)的,可以繞過Java的存取控制機制。
靜態(tài)方法
靜態(tài)方法是一種不能向?qū)ο髮嵤┎僮鞯姆椒???梢哉J為靜態(tài)方法是沒有隱式的參數(shù),沒有this參數(shù)的方法。
例如Emloyee類中靜態(tài)方法getNextld,
public static int getNextld()
{
return nextld; // returns static field
}
這里不能訪問實例域Id,因為靜態(tài)方法不可以操作對象。但是可以訪問類域。
建議用類名訪問靜態(tài)方法,不建議使用對象的引用調(diào)用靜態(tài)方法,防止混淆。
下面兩種情況才使用靜態(tài)方法:
- 方法只需要訪問類的靜態(tài)域(Emloyee.getNextld)
- 方法不需要訪問對象狀態(tài),所需參數(shù)都是通過顯式參數(shù)提供(eg: Math.pow)。
靜態(tài)方法main方法不對任何對象進行操作。啟動程序時,還沒有任何一個對象,靜態(tài)的main方法將執(zhí)行并創(chuàng)建程序所需要的對象。
方法參數(shù)
1.對于基本類型參數(shù):
public static void tripieValue(double x){ // 并不能修改x的值
x = 3 * x;
}
double percent = 10;
tripieValue(percent); // percent = 10;

- x被初始化為percent的一個copy,10
- x=x*3; 則x=30,但percent仍為10
- 方法結(jié)束,參數(shù)變量x不再使用。
方法不可能修改一個基本數(shù)據(jù)類型的參數(shù)
2.對于對象參數(shù):
public static void tripleSalary(Employee x){ // works
x.raiseSalary(200) ;
}
harry = new Employee(. . .) ;
tripleSalary(harry) ; // harry.salary+200

- x被初始化為harry的一個copy,這里是一個對象的引用。
- x.raiseSalary(200),這個被引用的對象salary上漲200
- 方法結(jié)束,參數(shù)變量x不再使用,對象變量harry繼續(xù)引用該對象。
3.Java對對象采用的就是引用調(diào)用嗎?答案是No
看個例子,swap為交換x,y
public void swap(Employee x, Employee y){
Employee tmp = x;
x = y;
y = tmp;
}
...
Employee a = new Employee("Alice", . . .);
Employee b = new Employee("Bob", . . .);
swap(a, b);

- 方法結(jié)束時參數(shù)變量x和y被丟棄了。原來的變量a,b依然引用之前的對象。
Java 程序設(shè)計語言對對象采用的不是引用調(diào)用,實際上,對象引用是按值傳遞的
總結(jié):Java中方法參數(shù)的使用情況:
- 一個方法不能修改一個基本數(shù)據(jù)類型的參數(shù)(即數(shù)值型或布爾型)
- 一個方法可以改變一個對象參數(shù)的狀態(tài)
- 一個方法不能改變對象參數(shù)原有的引用(不能讓對象參數(shù)引用一個新的對象)
重載(overloading)
多個方法具有相同的名字、不同的參數(shù)即重載。簽名(方法名+參數(shù)類型)。
finalize方法
可以為任何一個類添加 finalize 方法,它將在垃圾回收器清楚對象之前調(diào)用。
但在實際應(yīng)用中,不要依賴于使用finalize 方法回收任何短缺的資源,因為很難知道這個方法什么時候才能調(diào)用。
包
import java.util.;
import static java.lang.Math.;//可以直接使用sqrt(pow(x, 2) + pow(y, 2));
文檔注釋
JDK有個很有用的工具: javadoc,可以由源文件生成一個HTML文檔
為以下幾部分編寫注釋:
- 包
- 公有類與接口
- 公有的和受保護的構(gòu)造器及方法
- 公有的和受保護的域
以/** ... */的格式,自由格式文本第一局應(yīng)該是一個概要性的句子。javadoc自動將這些句子抽取出來形成概要頁。
<em>用于強調(diào)
<strong>著重強調(diào)
<img>圖片
不要使用<hl>或<hr> 會與文檔格式產(chǎn)生沖突。
{@code ...}等寬代碼
可以參考源碼中的標(biāo)簽
類注釋
必須放在import語句之后,類定義之前。需要注意的是@see 分隔類名與方法名用的是#,多個see標(biāo)簽要放在一起
/**
* The {@code String} class represents character strings. All
* string literals in Java programs, such as {@code "abc"}, are
* implemented as instances of this class.
* @author Lee Boynton
* @see java.lang.Object#toString()
*/
方法注釋
@param 變量描述
@return 描述
@throw 類描述
/**
* Allocates a new {@code String} that contains characters from a subarray
* of the character array argument. The {@code offset} argument is the
* index of the first character of the subarray and the {@code count}
* argument specifies the length of the subarray. The contents of the
* subarray are copied; subsequent modification of the character array does
* not affect the newly created string.
*
* @param value
* Array that is the source of characters
*
* @param offset
* The initial offset
*
* @param count
* The length
*
* @throws IndexOutOfBoundsException
* If the {@code offset} and {@code count} arguments index
* characters outside the bounds of the {@code value} array
*/
public String(char value[], int offset, int count)
域注釋
只需要對公有域(通常指的是靜態(tài)常量)建立文檔
/**
* The {@code double} value that is closer than any other to
* <i>pi</i>, the ratio of the circumference of a circle to its
* diameter.
*/
public static final double PI = 3.14159265358979323846;
包與概述注釋
想要產(chǎn)生包注釋,需要在每一個包目錄中添加一個單獨的文件。兩種方式
- 提供一個package.html命名的HTML文件。<body></body>中的所有文本都會被抽取出來
- 提供一個以package-info.java命名的Java文件。這個文件必須包含一個初始的以 /**
和 */ 界定的 Javadoc 注釋, 跟隨在一個包語句之后。它不應(yīng)該包含更多的代碼或注釋。
為所有的源文件提供一個概述性的注釋。這個注釋將被放置在一個名為 overview.html 的文件中,這個文件位于包含所有源文件的父目錄中。標(biāo)記 <body>... </body> 之間的所有文本將被抽取出來。當(dāng)用戶從導(dǎo)航欄中選擇“Overview” 時,就會顯示出這些注釋內(nèi)容。
注釋抽取
切換到包含想要生成文檔的源文件目錄。 如果有嵌套的包要生成文檔, 例如 com.horstmann.corejava, 就必須切換到包含子目錄 com 的目錄(如果存在 overview.html 文件的
話, 這也是它的所在目錄 )如果是一個包,應(yīng)該運行命令:
javadoc -d docDirectory nameOfPackage
或?qū)τ诙鄠€包生成文檔, 運行:
javadoc -d docDirectory nameOfPackage1 nameOfPackage2 . . .
如果文件在默認包中, 就應(yīng)該運行:
javadoc -d docDirectory *. java
可參考javadoc相關(guān)文檔
繼承
例如Manager和Employee,Manager除了享有Employee待遇和屬性之外,還會有其他權(quán)利等。根本上講,Manager is a Employee,is-a 關(guān)系是繼承的一個明顯特征
- Java中所有的繼承都是公有繼承。
- 域繼承、方法繼承
- 方法覆蓋、重寫
- super可調(diào)用父類的方法、構(gòu)造器。調(diào)用父類構(gòu)造器的語句只能作為當(dāng)前構(gòu)造器的第一個語句出現(xiàn)。
- 不支持多繼承
- 在覆蓋一個方法的時候,子類方法不能低于超類方法的可見性
阻止繼承:final類和方法
不允許擴展的類被稱為final類,被final修飾的方法不能被子類覆蓋。
域也可以被聲明為 final。對于 final 域來說,構(gòu)造對象之后就不允許改變它們的值了。 不過, 如果將一個類聲明為 final, 只有其中的方法自動地成為 final,而不包括域。
將方法或類聲明為 final 主要目的是: 確保它們不會在子類中改變語義。
多態(tài)
一個對象變量可以指示多種實際類型的現(xiàn)象被稱為多態(tài),在運行時能夠自動選擇調(diào)用哪個方法的現(xiàn)象稱為動態(tài)綁定。如下e,即可引用Employee對象,又可引用Manager對象:
Manager boss - new Manager("Carl Cracker", 80000,1987, 12 , 15) ;
boss.setBonus(5000) ;
Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e : staff)
System.out.println(e.getName0 + " " + e.getSalary());
對象變量是多態(tài)的,一個Employee變量既可以引用Employee對象也可引用Employee類的任何一個子類對象。
- 子類數(shù)組的引用可以轉(zhuǎn)換成父類數(shù)組的引用,不需要強制轉(zhuǎn)換。
- 將一個超類的引用賦給子類變量,必須進行類型轉(zhuǎn)換。并使用instanceof檢查。
- 只能在繼承層次內(nèi)進行類型轉(zhuǎn)換。
養(yǎng)成這樣一個良好的程序設(shè)計習(xí)慣: 在進行類型轉(zhuǎn)換之前, 先查看一下是否能夠成功地轉(zhuǎn)換。instanceof
if (staff[1] instanceof Manager){
boss = (Manager) staff[1]:
}
在一般情況下,應(yīng)該盡量少用類型轉(zhuǎn)換和 instanceof 運算符。并檢查一下超類的設(shè)計是否合理。
抽象類
- 包含抽象方法的類必須被聲明為抽象的。
- 抽象方法充當(dāng)著占位的角色??梢蕴峁┕驳姆椒ǎ恍枰獙懢唧w實現(xiàn)。
- 抽象類中也可以包含具體數(shù)據(jù)和具體方法。
- 擴展抽象類:
- 子類中只定義部分抽象類方法或不定義抽象類方法,子類必須被標(biāo)記為抽象類。
- 子類定義了全部的抽象方法,子類就不是抽象的了。
- 抽象類不能被實例化
- 可以定義一個抽象類的對象變量,但是它只能引用非抽象子類的對象
Object
equals方法
關(guān)于重寫equals,需要注意遵循自反、對稱、傳遞、一致性,equals(null)=false;
- 如果子類能夠擁有自己的相等概念, 則對稱性需求將強制采用 getClass 進行檢測。
- 如果由超類決定相等的概念,那么就可以使用 instanceof進行檢測, 這樣可以在不同子類的對象之間進行相等的比較,并且應(yīng)將超類.equals設(shè)為final
完美equals建議:
- 顯示參數(shù)名定為otherObject
- 檢測 this 與 otherObject 是否引用同一個對象
- 檢測 otherObject 是否為 null
- 比較 this 與 otherObject 是否屬于同一個類,如果 equals 的語義在每個子類中有所改變,就使用 getClass 檢測,如果所有的子類都擁有統(tǒng)一的語義,就使用 instanceof 檢測。
- 將otherObject轉(zhuǎn)為相應(yīng)的類型。
- 對所有需要比較的域進行比較了。使用=比較基本類型域,使用 equals 比較對象域。如果所有的域都匹配, 就返回 true; 否則返回 false。
- 如果在子類中重新定義 equals, 就要在其中包含調(diào)用 super.equals(other)。
public boolean equals(Object otherObject){
if (this = otherObject) return true;
if (otherObject = null) return false;
if (getClass() != otherObject.getClass()) return false; // equals 的語義在每個子類中有所改變
if (!(otherObject instanceof ClassName)) return false; //所有的子類都擁有統(tǒng)一的語義
ClassName other = (ClassName) otherObject;
return field1 == other.field1 && Objects.equa1s(field2, other.field2) && ... ;
}
String中的equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
hashCode方法
如果重新定義 equals方法, 就必須重新定義hashCode 方法,以便用戶可以將對象插人到散列表中
String中的hashCode()
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
————for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
- hashCode 方法應(yīng)該返回一個整型數(shù)值(也可以是負數(shù)
- 合理地組合實例域的散列碼,以便能夠讓各個不同的對象產(chǎn)生的散列碼更加均勻
比較好的做好是組合多個散列值,調(diào)用 Objects.hash 并提供多個參數(shù):
public int hashCode(){
return Objects,hash(name, salary, hireDay);
}
如果存在數(shù)組類型的域, 那么可以使用靜態(tài)的 Arrays.hashCode 方法計算一個散列碼,這個散列碼由數(shù)組元素的散列碼組成
toString方法
強烈建議為自定義的每一個類增加 toString 方法。
Employee:
public boolean equals(Object otherObject){
// a quick test to see if the objects are identical
if (this == otherObject) return true;
// must return false if the explicit parameter is null
if (otherObject == null) return false;
// if the classes don't match, they can 't be equal
if (getClass() != otherObject.getClass() ) return false;
// now we know otherObject is a non-null Employee
Employee other = (Employee) otherObject;
// test whether the fields have identical values
return Objects.equals(name, other.name) && salary == other.salary&& Objects.equals(hireDay, other.hireDay) ;
public String toString(){
return getClass().getName() + "[name:" + name +",salary:" + salary + ",hireDay=" + hireDay + "]";
}
Manager:
public boolean equals(Object otherObject){
if (!super.equals(otherObject)) return false;
Manager other = (Manager) otherObject;
// super.equals checked that this and other belong to the same class
return bonus == other.bonus;
}
public String toString(){
return super.toString() + "[bonus=" + bonus + "]";
}
泛型數(shù)組列表
ArrayList<Employee> staff = new ArrayList<Employee>();
如果調(diào)用 add 且內(nèi)部數(shù)組已經(jīng)滿了,數(shù)組列表就將自動地創(chuàng)建一個更大的數(shù)組,并將所有的對象從較小的數(shù)組中拷貝到較大的數(shù)組中。
如果已經(jīng)清楚或能夠估計出數(shù)組可能存儲的元素數(shù)量, 就可以在填充數(shù)組之前調(diào)用
ensureCapacity方法:
staff.ensureCapacity(100);
ArrayList<Employee> staff = new ArrayList<>(100) ;
注意區(qū)分 數(shù)組列表和數(shù)組 分配空間的區(qū)別:
- new ArrayList<>(100) // capacity is 100
數(shù)組列表只是擁有保存100個元素的潛力(實際上,重新分配空間的話,會超過100),但是最初(初始化構(gòu)造),數(shù)組列表不含有任何元素 - new Employee[100]// size is 100
數(shù)組有100個位置可用
確認數(shù)組列表的大小不再發(fā)生變化,可以調(diào)用trimToSize方法。這個方法將存儲區(qū)域的大小調(diào)整為當(dāng)前元素數(shù)量所需要的存儲空間數(shù)目。垃圾回收器將回收多余的存儲空間。
list.add(0,"hello"); //添加新元素
list.set(0,"world"); //替換已有元素內(nèi)容
list.add(n,e); // 位于n之后所有元素向后移動一個位置
list.remove(n); // 移除n下標(biāo)元素,且之后的元素都前移一個位置
轉(zhuǎn)數(shù)組:
ArrayList<X> list = new ArrayList();
while (. . .){
x = . .
list.add(x);
}
X[] a = new X[list.size()];
list.toArray(a);
- 為了能夠看到警告性錯誤的文字信息,要將編譯選項置為 -Xlint:unchecked
- 能確保不會造成嚴重的后果, 可以用@SuppressWamings("unchecked") 標(biāo)注來標(biāo)記這個變量能夠接受類型轉(zhuǎn)換
對象包裝器和自動裝箱
包裝器(wrapper):Integer類對應(yīng)基本類型int
Integer、Long、Float、Double、Short、Byte(前6個類派生于公共的超類Number)、Character 、 Void 和 Boolean
- 對象包裝器類是不可變的,即一旦構(gòu)造了包裝器,就不允許更改包裝在其中的值。
- 對象包裝器類還是 final , 因此不能定義它們的子類。
public final class Integer extends Number implements Comparable<Integer>
自動裝箱(autoboxing)
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
// 自動變成
list.add(Integer.value0f(3));
list.add(3)的調(diào)用即為自動裝箱
自動拆箱
int n = list.get(i);
即編譯器會翻譯為int n = list.get(i).intValue();
Integer n = 3;
n++;
編譯器自動插入一條對象拆箱指令,然后自增,然后再將結(jié)果裝箱
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // false
Integer c = 100;
Integer d = 100;
System.out.println(c == d); // true
== 檢測的是對象是否指向同一個存儲區(qū)域。自動裝箱將經(jīng)常出現(xiàn)的值包裝到同一個對象中,即:
自動裝箱規(guī)范要求 boolean、byte、char 127,介于-128 ~ 127之間的 short 和 int 被包裝到固定的對象中。
所以經(jīng)過自動裝箱,a,b所指向的100是同一個對象,而c,d所指向的1000是兩個不同的對象。
為了避免這種不確定,兩個包裝器對象比較時,調(diào)用equals方法
在一個表達式中混用Integer和Double,Integer會拆箱,提升為double,再裝箱為Double
Integer n = 1;
Double x = 2.0;
System.out.println(true ? n : x); // 1.0
敲黑板~ 裝箱和拆箱是編譯器認可的, 而不是虛擬機。編譯器在生成類的字節(jié)碼時, 會插入必要的方法調(diào)用。虛擬機只是執(zhí)行這些字節(jié)碼。
有些人認為包裝器類可以用來實現(xiàn)修改數(shù)值參數(shù)的方法, 然而這是錯誤的。
public static void triple(int x) {// won't work
x = 3 * x; // modifies local variable
}
public static void triple(Integer x) // won't work
因為Java是值傳遞:int基本類型參數(shù)不會變、Integer對象不可變:包含在包裝器中的內(nèi)容不會變。
如果需要一個修改數(shù)值參數(shù)值的方法,需要使用在 org.omg.CORBA包中定義的持有者(holder)類型,包含IntHolder、BooleanHolder等。每個持有者類型都包含一個共有域值,通過它可以訪問存儲在其中的值。
public final class IntHolder implements Streamable {
/**
* The <code>int</code> value held by this <code>IntHolder</code>
* object in its <code>value</code> field.
*/
public int value;
如下:
{
...
IntHolder y = new IntHolder();
y.value = 3;
changeInt(y);
System.out.println(y.value); // 9
...
public static void changeInt(IntHolder x){
x.value = x.value * 3;
}}
參數(shù)數(shù)量可變的方法
eg printf:
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
這里的 ... 省略號 是Java代碼的一部分,表明這個位置可以接收任意數(shù)量的對象。
即fmt的第i個格式說明符與arg[i]的值匹配
public static double max(double ... values){
double largest = Double.NEGATIVE_INFINITY;
for (double val : values)
if (val > largest) largest = val;
return largest;
}
...
// 調(diào)用
double[] values = {1.2, 123123.12, -0.999, 12312};
System.out.println(max(values)); // 123123.12
System.out.println(max(1.2, 123123.12, -0.999, 12312)); // 123123.12
枚舉類
public enum Size {
SMALL("S"), MEDIUM("M"), LARGR("L"), EXTRA_LARGE("XL");
private String abbr; // 縮寫
private Size(String abbr) {
this.abbr = abbr;
}
public String getAbbr() {
return abbr;
}
public static void main(String[] args) {
SMALL.getAbbr(); // S
SMALL.toString(); // SMALL
Size s = Enum.valueOf(Size.class, "SMALL");// toString的逆方法 valueOf
Size[] sizes = Size.values();
System.out.println(sizes);
Size.EXTRA_LARGE.ordinal(); // 聲明中枚舉常量的位置 3
}
}

反射(reflective)5.7
能夠分析類能力的程序稱為反射。反射機制可以用來:
- 在運行時分析類的能力。
- 在運行時查看對象
- 實現(xiàn)通用的數(shù)組操作代碼
- 利用Method對象(類似于C++中的函數(shù)指針)
繼承的設(shè)計技巧
- 將公共操作和域放在超類
- 不要使用受保護的域
- 使用繼承實現(xiàn)“ is-a” 關(guān)系
- 除非所有繼承的方法都有意義, 否則不要使用繼承
- 在覆蓋方法時,不要改變預(yù)期的行為
- 使用多態(tài),而非類型信息
- 不要過多地使用反射