java編程思想 第 6 章 訪問權限控制

更新類庫時爲了讓開發人員知道哪些類是被其它類引用的,不能被修改而引人了訪問權限控制。

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章