簡體   English   中英

Java新手問題:包含私有訪問權限

[英]Java newbie problem: package with private access

Pack.java導入pack.TestPack; 但它無法訪問它。 我無法理解為什么它無法訪問該類,盡管導入。

錯誤

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(){}
}

你應該讓TestPack的構造函數公開。

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的構造函數。

在使用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());
        }
}

我建議您不要將類設為public,而是將構造函數設為public,並讓人們使用您的類實現的公共接口。 最好將您的包啟動為公共接口(可能還有一些公共抽象類),並通過不將它們標記為公共來隱藏您的實現類,以便您可以隨時更改這些類。 然后,您可以在包中提供公共工廠方法,該方法實例化包私有類並將它們作為接口類型返回。 這是一個公共接口:

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

這是“包私有”的實現。 編譯器不會讓同一個包之外的代碼導入,也不會使用這個類:

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

請注意,第二次出現的單詞/ package /是一個注釋(在java中使用那個單詞在那里是不合法的)但是許多程序員在那個位置使用這樣的注釋來向人們表明這不是一個意外的類不公開; 它表示開發人員真的打算將該課程故意“打包私有”。 要讓人們從包外部實例化類,您需要提供一個靜態工廠類(否則是實例工廠類):

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

工廠類的重點是它隱藏了你的內部類(你隱藏的包作為私有包)。 隨着時間的推移,您可以更改工廠類以返回新類或重命名或刪除WidgetHidden類。

許多框架通過將其放入名為“internal”的包中來指示其他開發人員不應使用的類。 公共接口將位於主程序包中(例如“com.stackoverflow.widget”),而隱藏的類將位於內部程序包中,只顯示公共工廠類(例如“com.stackoverflow.widget.internal”)。

主題的變體是不在工廠類上使用靜態方法; 使它成為常規方法。 替代方案稱為“靜態工廠”或“實例工廠”,具體取決於方法是否為靜態。 不使該方法靜態對於使用您的包的人來說似乎更多的工作,因為他們首先必須在使用它來創建Widget之前實例化您的工廠對象。 有用的是人們可能想要在工廠的構造函數上為所有小部件設置一些默認值,然后使用none static 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第2版的“Effective Java”一書有很多討論和例子以及嘗試編寫優秀API時陷阱的細節。 它有很多細節可以解釋為什么你應該總是用很多其他好的“交易技巧”來隱藏盡可能多的課程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM