Java新手问题:包含私有访问权限

时间:2010-05-03 02:13:00

标签: java package

Pack.java import 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(){}
}

3 个答案:

答案 0 :(得分:4)

你应该让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的构造函数。

答案 1 :(得分: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());
        }
}

答案 2 :(得分:0)

我建议您不要将类设为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 2nd Edition的Effective Java一书有很多讨论和例子,以及在编写优秀API时陷阱的细节。它有很多细节可以解释为什么你应该总是用很多其他好的“交易技巧”来隐藏尽可能多的课程。