如何避免多次创建相同的对象引用?

时间:2015-09-01 15:40:29

标签: java

想象一下,我有一些看起来像这样的类:

class Car {
    private Image carImage;

    public Car(int imageIndex) {
        switch (imageIndex) {
            case 1: carImage = generateCarImage(1); break;
            # and so forth
        }
    }
}

class Audi extends Car {
    private int numberOfSeats;
    public Audi(int imageIndex, int numberOfSeats) {
        super(imageIndex);
        this.numberOfSeats = numberOfSeats;
    }
}

现在想象一下,我使用相同的图像创建了多个奥迪:

Audi car1 = new Audi(1,2);
Audi car2 = new Audi(1,3);

car1和car2会延伸同一个物体吗?我假设没有,但有没有办法可以做到这一点?我问,因为我想避免两次生成和存储相同的图像。

编辑:

这两个奥迪将参考同一辆车,例如图像只生成并存储一次,对一个图像的任何更改都会影响另一个图像?

class Car {
    private Image carImage;
    public Car(int imageIndex) {
        switch (imageIndex) {
            case 1: # carImage = readfile(1.jpeg)
            # and so forth
        }
    }
}

class Audi{
    private int numberOfSeats;
    private Car car;
    public Audi(Car car, int numberOfSeats) {
        this.car = car;
        this.numberOfSeats = numberOfSeats;
    }
}

Car car = new Car(1);
Audi audi1 = new Audi(car,2);
Audi audi2 = new Audi(car,2);

编辑2:

这里有很多好的答案,我最后结合使用它们来创造一个不错的解决方案。我最初的问题并没有很好地定义,主要是因为我并不确切地知道自己到底是什么。

无论如何,对于这个问题,不可能事先生成所有数据(下面的例子中的PartsInfo),也不能显式生成数据(如上面的switch-case例子所暗示的那样)。下面解决方案的最大问题是我无法访问PartsInfo中的各个字段而无需检索整个事物(如调用Car.getPartsInfo()时在解决方案中所做的那样)或创建多个实例相同的对象(在这种情况下,Car类将获得自己的PartsInfo变量)。

弱散列图也可以,但不是最佳的,因为问题不是垃圾收集,而是存储在不同实例中的大量相同数据。

如果ID类似于" audi-a4-2003"该解决方案适用。并且PartsInfo对于所有人来说是相同的#aud;-a4-2003"独立于颜色,所有者,年龄,座位数等,但与#34; audi-a4-2004"完全不同。

谢谢

Class PartsInfo {
    // lots of stuff I'd rather not create nor save multiple times
}

Class PartsInfoFactory {
    private static HashMap<String, PartsInfo> partsInfoMap = new HashMap<String, PartsInfo>();

    public static getPartsInfo(String id) {
        if (!partsInfoMap.containsKey(id)) {
            generatePartsInfo(id);
        }
        return partsInfoMap(id)
    }

    private static generatePartsInfo(String id) {
        // Do stuff I don't want to do twice for same ID
        partsInfoMap.put(id)
    }
}

Class Car {
    private Color color;
    private String id;
    // Notice that PartsInfo is not stored here

    public Car(Color color, String id) {
        this.color = color;
        this.id = id;
    }

    public PartsInfo getPartsInfo() {
        return PartsInfoFactory.getPartsInfo(id);
    }
}

7 个答案:

答案 0 :(得分:5)

  

car1car2会扩展相同的对象吗?

一个类可以从另一个类扩展。对象不会扩展任何东西。在Java中,继承仅适用于类和接口。您在此处所做的是创建同一班级的两个实例,AudiAudiCar延伸。

  

有没有办法可以做到这一点?

没有

  

我问,因为我想避免生成和存储两次相同的图像。

这是一个值得回答的正确问题。您真正的问题是避免多次创建相同的对象实例。为此,最好使用WeakHashMap来使用对象池。以下是对使用此结构的原因的解释:When would you use a WeakHashMap or a WeakReference?

答案 1 :(得分:4)

避免多次创建同一图像的好方法是使用依赖注入:将图像作为构造函数参数注入,而不是将参数传递给generateCarImage

class Car {
  private final Image image;

  Car(Image image) {
    this.image = image;
  }
}

class Audi extends Car {
  Audi(Image image, int numDoors) {
    super(image);
    // ...
  }
}

这意味着image可以来自任何地方 - 让您更明确地控制图像的生命周期。所以,如果你想一遍又一遍地使用相同的图像,你可以,并且很明显你是:

Image image = generateCarImage(1);
Audi car1 = new Audi(image, 4);
Audi car2 = new Audi(image, 2);

此外,通过删除与generateCarImage方法的静态耦合,它使类更易于测试,因为您可以创建不同的图像进行测试,例如生成起来比较简单。

答案 2 :(得分:3)

你永远不会扩展对象,你扩展了类。当然,你会一直扩展同一个班级。

答案 3 :(得分:2)

在Java中没有扩展对象这样的东西(其他语言都有这种继承,称为 prototypal 。但是,Java没有原型继承;只有类继承)。

在Java中扩展意味着扩展一个类,而不是一个对象,它是一个类的实例。

因此,尽管car1car2的类扩展了同一个类,但这两个对象彼此无关。

  

我想避免生成和存储两次相同的图像

多个对象共享第三个对象没有问题,在您的情况下可能是图像。处理此问题的一种方法是创建Car的所有实例共有的图像缓存,在第一次请求时生成图像,然后根据需要重新使用相同的图像对象以节省空间: / p>

Simple diagram

  

是否有可能不是搜索图像缓存,而是搜索Car的所有实例的缓存,然后选择在Audi类中实例化哪一个?

您无法再次实例化对象。但是,您可以创建Car个对象的缓存,并在Car上实现一个工厂方法,该方法在创建新实例之前在其缓存中搜索合适的汽车。

答案 4 :(得分:2)

每当你使用new子句时,你将创建一个对象的新实例,一个完整的单独的类表示;所以回答直接的问题:不,你没有扩展对象。

潜在的问题是你可能不想重复创建相同的图像:然后你必须采取不同的方法。我首先建议对Java的OO方面进行另一次读取,然后考虑(可能)工厂模式,这可能是一个类,如果已经创建了另一个,它将负责不重复创建相等的图像。

答案 5 :(得分:2)

我的解决方案是使用静态引用作为常量值。这是最简单的解决方案,因为enum不能与对象一起使用,因为它必须在编译时进行评估。

但是我们想要获得运行时常量,以及使用类似于使用单实例的枚举的好处,并且可以在switch语句中使用。

因此,我们将实现enum以返回在编译时可用的另一个类的常量静态属性,并返回对在运行时创建的对象的常量引用。

class CarImageDirectory
{
    // Created at Run-time

    public static final Image Audi = new Image("Audi");
    public static final Image Toyota = new Image("Toyota");
    // ..etc
}

enum CarImage
{
  // Created at Compile-time

  Audi
  {
    @Override public Image image () { return CarImageDirectory.Audi; }
  },
  Toyota
  {
    @Override public Image image () { return CarImageDirectory.Toyota; }
  }; // ..etc

  public abstract Image image ();
}

CarImage将如下工作:

CarImage A = CarImage.Audi;
CarImage B = CarImage.Audi;

if (A == B) System.out.println("A and B are both Audi");

然后我们用它来定义我们的Car类:

class Car
{
    private CarImage carImg;
    public Car (CarImage carImg) { this.carImg = carImg; }
    public Image getImage () { return carImg.image(); }
    public CarImage getCarImage () { return carImg; }
}

class AudiCar extends Car
{
    private int numOfSeats;

    public AudiCar (int numOfSeats)
    {
        super(CarImage.Audi);
        this.numOfSeats = numOfSeats;
    }
}

class ToyotaCar extends Car
{
    private int numOfSeats;

    public ToyotaCar (int numOfSeats)
    {
        super(CarImage.Toyota);
        this.numOfSeats = numOfSeats;
    }
}

同样CarImage本身也可用于switch语句:

CarImage A = CarImage.Audi;

switch(A)
{
  case CarImage.Audi:
    System.out.println("This car is Audi");
    break;

  case CarImage.Toyota:
    System.out.println("This car is Toyota");
    break;

  default:
}

答案 6 :(得分:1)

您是否研究过"flyweight"模式?这可能会减少对象的创建。 从技术上讲,它可以减少内存占用,但如果对象创建成本高且重用率高,则可以在启动时间不存在的情况下使用它,例如应用程序服务器启动时。

在任何情况下,只有在您知道时才会优化,这是一个性能问题。

希望这有帮助!