2010-05-03 46 views
4

Pack.java import pack.TestPack;但它無法訪問它。我無法理解爲什麼即使進口也無法進入班級。Java新手問題:包私有訪問

錯誤

Pack.java:7: TestPack() is not public in pack.TestPack; cannot be accessed from outside package 
     System.out.println(new TestPack().getHello()); 
         ^
1 error 

Pack.java

import pack.TestPack; 
import java.io.*; 

public class Pack 
{ 
     public static void main(String[] args){ 
       System.out.println(new TestPack().getHello()); 
     } 
} 

TestPack.java

package pack; 
import java.util.*; 
import java.io.*; 

public class TestPack 
{ 
     private String hello="if you see me, you ar inside class TestPack"; 
     public String getHello(){return hello;} 
     TestPack(){} 
} 

回答

4

你應該讓TestPac k的構造函數public。

public class TestPack 
{ 
     private String hello="if you see me, you ar inside class TestPack"; 
     public String getHello(){return hello;} 
     public TestPack(){} 
} 

的事情是,即使TestPack知名度是公共的,它的參數構造函數能見度package(這是可視性,當你沒有明確指定一個)。

package可見性表示同一包中的類將能夠看到它。由於TestPack和Pack不在同一個包中,Pack不能調用TestPack的構造函數。

+0

是的,但目標是與私人,我可以得到它的工作方式,但它不是我想要的。我想用私人範圍來限制範圍。 – hhh 2010-05-03 02:15:53

+1

你想限制哪個範圍?構造函數的?類'?如果你想要一個私有的構造函數,你將無法實例化這個類。通常,你想要一個私有構造函數的唯一場景是一個Factory類。 – 2010-05-03 02:18:13

+0

「同一個包」意思是「在文件開頭有'導入pkg_name'還是需要同一個目錄? – hhh 2010-05-03 02:29:55

0

在您使用getHello功能的方式,你可以開始考慮使用靜態方法

public class TestPack 
{ 
     private static String hello="if you see me, you ar inside class TestPack"; 
     public static String getHello(){return hello;} 
     private TestPack(){} 
} 

那麼你只要會做:

public class Pack 
{ 
     public static void main(String[] args){ 
       System.out.println(TestPack.getHello()); 
     } 
} 
+1

你需要打個招呼才能被定義爲靜態的,以便工作。 – ig0774 2010-05-03 02:34:45

0

我建議你不要使課堂public但公開構造函數並讓人們使用您的類實現的公共接口。將API啓動爲公共接口(也許是一些公共抽象類)並隱藏實現類是一個好主意,不要將它們標記爲公共,以便可以隨時改變它們。然後你可以在你的包中提供一個公共工廠方法,它實例化你的包私有類並將它們作爲接口類型返回。這是一個公共的接口:

package stackoverflow; 
public interface Widget { 
    public void doWidgetWork(String work); 
} 

這裏是「package private」的實現。編譯器不會讓代碼相同的包導入之外,也使用這個類在所有:

package stackoverflow; 
/*package*/ class WidgetHidden implements Widget { 
    public WidgetHidden(String configOptionA, String configOptionB){ 
     // ... 
    } 
    public WidgetHidden(){ 
     // ... 
    } 
    public void doWidgetWork(String work)[ 
     // ... 
    } 
} 

通知有該字/ 第二次出現的/是註釋(這是不合法的在Java在那裏使用那個詞),但是很多程序員在這個位置使用這樣的評論來向人們表明,這個類不是公開的,它意味着開發者真的打算讓這個班級故意「封裝私人」。爲了讓人們實例從你的包之外你提供一個靜態工廠類的類(其他實例工廠類):

package stackoverflow; 
public class WidgetFactory { 
    public static Widget newInstance(String configOptionA, String configOptionB) { 
     return new Widget(String configOptionA, String configOptionB); 
    } 
} 

工廠類的全部意義在於,它隱藏了您的內部類(那些你隱藏爲私人包)。隨着時間的推移,您可以更改您的工廠類以返回新類或重命名或刪除WidgetHidden類。

許多框架通過將其放入名爲「內部」的包中來指示其他開發人員不應該使用哪些類。公共接口將位於主包(例如「com.stackoverflow.widget」)中,而隱藏類僅包含在公開的工廠類(例如「com.stackoverflow.widget.internal」)中的隱藏類。

該主題的變體是對工廠類不使用靜態方法;使其成爲一種常規方法。取決於方法是否是靜態的,這些替代品被稱爲「靜態工廠」或「實例工廠」。不使這個方法成爲靜態的,對於使用你的包的人來說似乎更多的工作,因爲他們首先必須在使用它來創建Widget之前實例化你的工廠對象。哪裏是有幫助的是,當人們可能要設置一些默認設置在工廠的構造函數的所有部件,然後使用無靜態的newInstance方法來指定任何超出默認值:

public class WidgetInstanceFactory { 
    private String defaultOptionA = null; 
    public WidgetInstanceFactory(String defaultOptionA) { 
     this.defaultOptionA = defaultOptionA; 
    } 
    public Widget newInstance(String optionB) { 
     return new WidgetHidden(this.defaultOptionA, optionB); 
    } 
} 

它可以避開包私有使用反射保護以查找和調用構造函數。 Spring框架的一個非常好的功能是,它將實例化即使沒有工廠類也不公開的類(儘管提供Spring很樂意使用的工廠類也更有禮貌)。下面的代碼將工作:

package stackoverflow.other; 
class TestInstantiate { 
    private Widget myWidget = null; 
    public TestInstantiate(){ 
     this.myWidget = instantiatePackagePrivateClass("stackoverflow.WidgetHidden"); 
    } 
private Widget instantiatePackagePrivateClass(String className) 
    throws ClassNotFoundException, NoSuchMethodException, 
    InstantiationException, IllegalAccessException, 
    InvocationTargetException { 
    @SuppressWarnings("unchecked") 
    Class<FileUploadSequence> clazz = (Class<Widget>) Class.forName(className); 
    Constructor<Widget> constructor = clazz.getConstructor(new Class[]{}); 
    constructor.setAccessible(true); 
    Widget widget = (Widget) constructor.newInstance((Object[])null); 
    return widget; 
} 
} 

在那個例子中我使用了無參數的構造函數,但顯然你可以找到並使用相同的方法調用兩個字符串構造函數。很明顯,這樣的代碼繞過了編寫WidgetHidden的程序員的意圖;他們想隱藏它,因爲它們很可能會改變它。任何使用這樣的後門來操縱包私人對象的人都應該知道,WidgetHidden類不是他們正在使用的框架的公共API的一部分,因此可能在沒有事先通知的情況下被刪除或更改,你正在使用的軟件包。將它重命名爲WidgetInternal並將其放入「內部」包中,使得您更多地告訴人們「不要使用」。 JVM具有可選的安全設置,可以防止人們使用這些技巧;但運行JVM的人必須對其進行外部配置,以便禁用此類技巧,這些技巧僅在您希望運行別人不信任的代碼時纔有用,並且可以防止此類技巧出現。

Josha Block 2nd Edition的Effective Java書有很多討論,例子以及嘗試編寫一個好的API時的陷阱細節。它有很多細節來解釋爲什麼你總是應該儘可能多地隱藏許多類,以及其他許多好的「交易技巧」。