更新類庫時爲了讓開發人員知道哪些類是被其它類引用的,不能被修改而引人了訪問權限控制。
public > protect >(默認) > private
一、包:庫單元
包內包含一組類、它們在單一的名稱空間下被組織到了一起:
java.utils.ArrayList使用,使用全限定名:
//: access/FullQualification.java
public class FullQualification {
public static void main(String[] args) {
java.util.ArrayList list = new java.util.ArrayList();
}
} ///:~
使用import導入
//: access/SingleImport.java
import java.util.ArrayList;
public class SingleImport {
public static void main(String[] args) {
ArrayList list = new java.util.ArrayList();
}
} ///:~
同一包下全導入
import java.util.*;
未指明包名爲,位於默認包
下。
一個*.java
文件爲一個編譯單元。該編譯單元有且只有一個public類且與*.java
文件名相同 和多個非public 類。編譯成功後生成以類爲單位的多個*.class
文件
1.1 代碼組織
在文件中第一行爲類指定包名(包名
必須小寫):
package access;
創建一個MyClass.java 文件,有且只有一個同名的public 類。
//: access/mypackage/MyClass.java
package access.mypackage;
public class MyClass {
// ...
} ///:~
MyClass類受access.mypackage包保護,如果MyClass類和該包下其它公共類,使用全限定名或者import關鍵字導入
全限定名:
//: access/QualifiedMyClass.java
public class QualifiedMyClass {
public static void main(String[] args) {
access.mypackage.MyClass m =
new access.mypackage.MyClass();
}
} ///:~
import導入:
//: access/ImportedMyClass.java
import access.mypackage.*;
public class ImportedMyClass {
public static void main(String[] args) {
MyClass m = new MyClass();
}
} ///:~
package 和 import 將單一的全局名稱空間割開,防止出現名稱衝突。
1.2 創建獨一無二的包名
創建一個名爲simple的類庫
包名:net.mindview.simple
該名稱下才兩個字文件
Vector.java
//: net/mindview/simple/Vector.java
// Creating a package.
package net.mindview.simple;
public class Vector {
public Vector() {
System.out.println("net.mindview.simple.Vector");
}
} ///:~
List.java
//: net/mindview/simple/List.java
// Creating a package.
package net.mindview.simple;
public class List {
public List() {
System.out.println("net.mindview.simple.List");
}
} ///:~
兩個文件都位於K:\研二\Java\thindinjava\code1\net\mindview\simple
路徑下
而向ClassPath
添加了K:\研二\Java\thindinjava\code1
路徑。
LibTest.java 文件可以位於任何目錄:
//: access/LibTest.java
// Uses the library.
import net.mindview.simple.*;
public class LibTest {
public static void main(String[] args) {
Vector v = new Vector();
List l = new List();
}
} /* Output:
net.mindview.simple.Vector
net.mindview.simple.List
*///:~
編譯器編譯該文件碰到import語句時,會去ClassPath中的目錄中查找子目錄\net\mindview\simple,然後編譯出相應的類文件。
練習二:衝突
// access/Collision.java
// TIJ4 Chapter Access, Exercise 2, page 217
/* Take the code fragments in this section and turn them into a program and
* verify that collisions do occur.
*/
import net.mindview.simple.*;
import java.util.*;
public class Collision {
public static void main(String[] args) {
// Vector v = new Vector(); // ambiquous collision
net.mindview.simple.Vector v1 = new net.mindview.simple.Vector();
java.util.Vector v2 = new java.util.Vector();
}
}
第一行取消註釋時提示:
K:\研二\Java\thindinjava\code2\Access>javac Collision.java
Collision.java:11: 錯誤: 對Vector的引用不明確
Vector v = new Vector(); // ambiquous collision
^
java.util 中的類 java.util.Vector 和 net.mindview.simple 中的類 net.mindview.simple.Vector 都匹配
Collision.java:11: 錯誤: 對Vector的引用不明確
Vector v = new Vector(); // ambiquous collision
^
java.util 中的類 java.util.Vector 和 net.mindview.simple 中的類 net.mindview.simple.Vector 都匹配
2 個錯誤
1.3 定製工具庫
創建一個名爲Print.java工具庫
//: net/mindview/util/Print.java
// Print methods that can be used without
// qualifiers, using Java SE5 static imports:
package net.mindview.util;
import java.io.*;
public class Print {
// Print with a newline:
public static void print(Object obj) {
System.out.println(obj);
}
// Print a newline by itself:
public static void print() {
System.out.println();
}
// Print with no line break:
public static void printnb(Object obj) {
System.out.print(obj);
}
// The new Java SE5 printf() (from C):
public static PrintStream
printf(String format, Object... args) {
return System.out.printf(format, args);
}
} ///:~
編譯完成後,使用import static
靜態導入該類,可以使用該類的靜態方法
//: access/PrintTest.java
// Uses the static printing methods in Print.java.
import static net.mindview.util.Print.*;
public class PrintTest {
public static void main(String[] args) {
print("Available from now on!");
print(100);
print(100L);
print(3.14159);
}
} /* Output:
Available from now on!
100
100
3.14159
*///:~
Rage.java工具類
//: net/mindview/util/Range.java
// Array creation methods that can be used without
// qualifiers, using Java SE5 static imports:
package net.mindview.util;
public class Range {
// Produce a sequence [0..n)
public static int[] range(int n) {
int[] result = new int[n];
for(int i = 0; i < n; i++)
result[i] = i;
return result;
}
// Produce a sequence [start..end)
public static int[] range(int start, int end) {
int sz = end - start;
int[] result = new int[sz];
for(int i = 0; i < sz; i++)
result[i] = start + i;
return result;
}
// Produce a sequence [start..end) incrementing by step
public static int[] range(int start, int end, int step) {
int sz = (end - start)/step;
int[] result = new int[sz];
for(int i = 0; i < sz; i++)
result[i] = start + (i * step);
return result;
}
} ///:~
1.4
練習三
// access/debug/Debug.java
// TIJ4 Chapter Access, Exercise 3, page 220
/* Create two packages: debug and debugoff, containing an identical class with a
* debug() method. The first version displays its String argument to the console,
* the second does nothing. Use a static import line to import the class into a test
* program, and demonstrate the conditional compilation effect.
*/
/* In directory access/debugoff:
* // access/debugoff/Debug.java
* package access.debugoff;
*
* public class Debug {
* public static void debug(String s) { }
* }
*/
package access.debug;
public class Debug {
public static void debug(String s) {
System.out.println(s);
}
}
1.5 對使用包的忠告
包名對應路徑
二、Java訪問權限修飾詞
public、protect和private 這幾個java方法權限修詞在使用時,是爲於類的成員定義之前,無論是域或方法。
如果不加任何詞修飾,則是包訪問權限。
2.1 包訪問權限
類的成員沒有指定修飾詞,具有包訪問權限。這意味着該類當前包中所有其它類對該成員有訪問權限,而該包外的其它類不能夠訪問。處於同一編譯單元中的類是能夠彼此方法的。嚴格的同一個包下才能訪問,同一個包下其它子目錄中的類無法訪問。
取得對該成員的訪問權限方式有:
1. 使該成員爲public。無論在哪裏都能夠訪問。
2.不加修飾詞。同一個包內的其它類都能訪問。
3。使用proteced修飾。可以被子類和同一包中的其它類訪問
4通過setter和getter訪問
2.2 public:接口訪問權限
類成員使用 public 關鍵字修飾,意味着該成員能被其它任何類使用。
下面編譯dessert包
//: access/dessert/Cookie.java
// Creates a library.
package access.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
void bite() { System.out.println("bite"); }
} ///:~
使用Cookie的類:
//: access/Dinner.java
// Uses the library.
import access.dessert.*;
public class Dinner {
public static void main(String[] args) {
Cookie x = new Cookie();
//! x.bite(); // Can't access
}
} /* Output:
Cookie constructor
*///:~
Cookie類的構造器爲public,因此可以訪問。Cookie類 dessert() 方法爲包訪問權限,因此不可以被訪問 。
默認包
//: access/Cake.java
// Accesses a class in a separate compilation unit.
class Cake {
public static void main(String[] args) {
Pie x = new Pie();
x.f();
}
} /* Output:
Pie.f()
*///:~
第二個處於相同目錄的文件Pie.java
//: access/Pie.java
// The other class.
class Pie {
void f() { System.out.println("Pie.f()"); }
} ///:~
可以正常編譯的原因是因爲它們處於相同的目錄並且沒有指定包名,編譯器認爲它們在該目錄下的默認包中。
2.3 private: 你無法訪問
private 關鍵字的含義是,除了包含該成員的類外,其它類都無法訪問。
Private允許自己隨意改變該成員,而不必考慮其它類。
使用private的示例。
//: access/IceCream.java
// Demonstrates "private" keyword.
class Sundae {
private Sundae() {}
static Sundae makeASundae() {
return new Sundae();
}
}
public class IceCream {
public static void main(String[] args) {
//! Sundae x = new Sundae();
Sundae x = Sundae.makeASundae();
}
} ///:~
將 Sundae 類構造器私有,防止別的類創建它,只能通過它提供的靜態方法makeASundae 創建。還可以阻止其它類繼承。
將類的輔助方法設爲private,防止其它類使用。
2.4 protected 繼承訪問權限
如果創建一個新包,並在新包中創建一個繼承其它包中類的繼承類,那麼該繼承類只能訪問父類public修飾的成員和protected修飾的成員。
protected 關鍵字提供包訪問權限和子類訪問權限。
之前例子Cookie.java 不可調用包訪問權限bite()。
//: access/ChocolateChip.java
// Can't use package-access member from another package.
import access.dessert.*;
public class ChocolateChip extends Cookie {
public ChocolateChip() {
System.out.println("ChocolateChip constructor");
}
public void chomp() {
//! bite(); // Can't access bite
}
public static void main(String[] args) {
ChocolateChip x = new ChocolateChip();
x.chomp();
}
} /* Output:
Cookie constructor
ChocolateChip constructor
*///:~
將之前例子Cookie.java 中bite()方法進行修改爲protected:
//: access/cookie2/Cookie.java
package access.cookie2;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
protected void bite() {
System.out.println("bite");
}
} ///:~
現在Cookie.java 中bite()方法對所有繼承它的類是可見的。
//: access/ChocolateChip2.java
import access.cookie2.*;
public class ChocolateChip2 extends Cookie {
public ChocolateChip2() {
System.out.println("ChocolateChip2 constructor");
}
public void chomp() { bite(); } // Protected method
public static void main(String[] args) {
ChocolateChip2 x = new ChocolateChip2();
x.chomp();
}
} /* Output:
Cookie constructor
ChocolateChip2 constructor
bite
*///:~
練習四
// access/CookieThief.java
// TIJ4 Chapter Access, Exercise 4, page 227
// Show that protected methods have package access but are not public.
/* In directory Cookie2:
* //access/cookie2/Cookie.java
* //Creates a library
* package access.cookie2;
*
* public class Cookie {
* public Cookie() {
* System.out.println("Cookie contstructor");
* }
* protected void bite() { System.out.println("bite"); }
* }
*/
import access.cookie2.*;
public class CookieThief {
public static void main(String[] args) {
Cookie x = new Cookie();
//! x.bite(); // access protected
}
}
練習五
// access/AccessTest.java
// TIJ4 Chapter Access, Exercise 5, page 227
/* Create a class with public, private, protected and package-access fields and
* method members. Create an object of this class and see what kind of compiler
* messages you get when you try to access all the class members. Be aware that
* classes in the same directory are part of the "default" package.
*/
/* in same directory:
* package access;
*
* public class FourWays {
* int a = 0;
* public int b = 1;
* protected int c = 2;
* private int d = 3;
* FourWays() { System.out.println("FourWays() constructor"); }
* void showa() { System.out.println(a); }
* public void showb() { System.out.println(b); }
* protected void showc() { System.out.println(c); }
* private void showd() { System.out.println(d); }
* }
*/
package access; // run command java access.AccessTest
public class AccessTest {
public static void main(String[] args) {
FourWays fw = new FourWays();
fw.showa();
fw.showb();
fw.showc();
fw.a = 10;
fw.b = 20;
fw.c = 30;
fw.showa();
fw.showb();
fw.showc();
//! fw.showd(); // private access, compiler can't touch
}
}
//輸出
FourWays() constructor
0
1
2
10
20
30
練習六
// access/ProtectedData.java
// TIJ4 Chapter Access, Exercise 6, page 228
/* Create a class with protected data. Create a second class in the same file with
* a method that manipulates the protected data in the first class.
*/
class SomeData {
protected int a = 13;
}
class DataChanger {
static void change(SomeData sd, int i) { sd.a = i; }
}
public class ProtectedData {
public static void main(String[] args) {
SomeData x = new SomeData();
System.out.println(x.a);
DataChanger.change(x, 99);
System.out.println(x.a);
}
}
//輸出
三、接口和實現
將接口和具體實現分離,將public成員 放在最上方
//: access/OrganizedByAccess.java
public class OrganizedByAccess {
public void pub1() { /* ... */ }
public void pub2() { /* ... */ }
public void pub3() { /* ... */ }
private void priv1() { /* ... */ }
private void priv2() { /* ... */ }
private void priv3() { /* ... */ }
private int i;
// ...
} ///:~
四、類的訪問權限
類的訪問權限:
1.每個編譯單元(文件)只能有一個public類。
2.public類的名稱必須與編譯單元文件名相同,包括大小寫
3. 編譯單元也可以沒有public類,這時文件名隨意命名。
將類的public屬性去掉,該類具有包訪問權限。該類只能用於該包中
練習八
// MakeWidget.java
// TIJ4 Chapter Access, Exercise 7, page 230
/* Create the library according to the code fragments describing access and Widget.
* Create a Widget in a class that is not part of the access package.
*/
/* in access package:
* // access/Widget.java
* package access;
*
* public class Widget {
* public Widget() { System.out.println("Widget()"); }
* }
*/
import access.*;
public class MakeWidget {
public static void main(String[] args) {
Widget w = new Widget();
}
}
如果將Widget中的public 關鍵字去掉,編譯會提示:
access\MakeWidget.java:20: 錯誤: Widget在access中不是公共的; 無法從外部程序包中對其進行訪問
Widget w = new Widget();
Widget類具有包訪問權限。
類僅有兩個選擇:包訪問權限或public。不能用private或protected修飾。
將類的構造器設爲私有阻止其它人創建對象,在類的static方法內部創建該對象。
示例Lunch.java
//: access/Lunch.java
// Demonstrates class access specifiers. Make a class
// effectively private with private constructors:
class Soup1 {
private Soup1() {}
// (1) Allow creation via static method:
public static Soup1 makeSoup() {
return new Soup1();
}
}
class Soup2 {
private Soup2() {}
// (2) Create a static object and return a reference
// upon request.(The "Singleton" pattern):
private static Soup2 ps1 = new Soup2();
public static Soup2 access() {
return ps1;
}
public void f() {}
}
// Only one public class allowed per file:
public class Lunch {
void testPrivate() {
// Can't do this! Private constructor:
//! Soup1 soup = new Soup1();
}
void testStatic() {
Soup1 soup = Soup1.makeSoup();
}
void testSingleton() {
Soup2.access().f();
}
} ///:~
練習八
// access/ConnectionManager.java
// TIJ4 Chapter Access, Exercise 8, page 233
/* Following the form of the example Lunch.java, create a class called
* ConnectionManager that manages a fixed array of Connection objects. The client
* programmer must not be able to explicitly create Connection objects, but can
* only get them via a static method in ConnectionManager. When ConnectionManager
* runs out of objects, it returns a null reference. Test the classes in main(). */
package access;
class Connection {
private static int count = 0;
private int i = 0;
private Connection() { System.out.println("Connection()");}
// Allow creation via static method:
static Connection makeConnection() {
count++;
return new Connection();
}
public static int howMany() { return count; }
public String toString() {
return ("Connection " + count);
}
}
public class ConnectionManager {
static int howManyLeft = 3;
static Connection[] ca = new Connection[3];
{
for(Connection x : ca)
x = Connection.makeConnection();
}
public static Connection getConnection() {
if(howManyLeft > 0)
return ca[--howManyLeft];
else {
System.out.println("No more connections");
return null;
}
}
public static void main(String[] args) {
ConnectionManager cm = new ConnectionManager();
System.out.println(cm.howManyLeft);
cm.getConnection();
System.out.println(howManyLeft);
cm.getConnection();
System.out.println(howManyLeft);
cm.getConnection();
System.out.println(cm.getConnection());
System.out.println(howManyLeft);
}
}
輸出:
Connection()
Connection()
Connection()
3
2
1
No more connections