编辑访问控制练习 - java

Edited Access control exercise - java

我需要通过在 class Terminal 中创建 public hackCar 方法来打印 TestCar class 的属性。 hackCar方法需要传入一个TestCar作为参数,打印TestCar的属性。此作业的警告是我不能触摸 TestCar class 中的任何内容。

我仍在努力打印 TestCar 中的两个 private 属性。如何使用 TestCar 对象作为 hackCar 方法中的参数从 TestCar class 打印两个 private 属性?

故事 class:

class Story {
    public static void main(String args[]) {
        TestCar testCar = new TestCar();
        Terminal terminal = new Terminal();
        terminal.hackCar(testCar);
    }
}

class Terminal {

    public void hackCar(TestCar other) {

        System.out.println(other.doorUnlockCode);

        System.out.println(other.hasAirCondition);

        System.out.println(other.brand);

        System.out.println(other.licensePlate);
    }
}

class TestCar {

    private int doorUnlockCode = 602413;
    protected boolean hasAirCondition = false;
    String brand = "TurboCarCompany";
    public String licensePlate = "PHP-600";
}

谢谢!

您无法访问 class 之外的对象的私有属性。这就是 EncapsulationOOP 中的意义所在。如果 class 的属性必须可以从 class 外部访问,则它们不能是私有的。或者您应该为它们提供一些访问器方法。我认为这就是 Java Beans 在 java 框架中如此受欢迎的原因。

您也可以使用 Reflection 访问 java 中 class 的私有属性,但我认为这不是您要找的。

因为你不能接触 class TestCar,你不能设置任何 Getter 方法从 class 外部访问 class 的私有成员=].

所以,唯一的方法(在不使用任何 Getter 函数的情况下访问 class 的私有成员)是在 [=44] 中使用反射 API =].

这是代码,

public void hackCar(TestCar other) {

        Field f = other.getDeclaredField("doorUnlockCode");  /*here we create the object of the desired field*/

        f.setAccessible(true);  //here, we set its access to true

        System.out.println(f.get(obj));   /*here, we use the field object to get its value*/

        Field f1 = other.getDeclaredField("hasAirCondition");

        f1.setAccessible(true);

        System.out.println(f1.get(obj));

        Field f2 = other.getDeclaredField("brand");

        f2.setAccessible(true);

        System.out.println(f2.get(obj));

        System.out.println(other.licensePlate);  /*this can be accessed directly since the access specifier for the field is "public"*/
    }

你可以这样修改你的HackCar方法。

您需要导入这些。

import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;

这是解决上述问题的方法,但也有其自身的缺陷。它会导致性能开销,还会导致安全问题,因为它违反了抽象原则]OOP.

因此,请明智地使用此解决方案。

注意 - 您还可以通过定义 class 来访问具有访问说明符 protecteddefault 的字段(我们称它为 C)与 TestCar class 在同一个包中,并使用 class C 的成员函数作为 protected[=] 的 getter 函数36=] 和 默认 TestCar 字段 class.

您提到了三个 classes:StoryTerminalTestCar。在您的问题中,所有这些都被定义为私有包(默认可见性)——也许是为了简单起见。但是 TestCar 被定义 public and/or Story / Terminal 驻留在同一个包中。只有这样 TestCar 才能在 StoryTerminal.

内解决

如果 classes 都在一个包中,则可以立即访问 TestCarhasAirConditionbrandlicensePlate 字段在 Terminal.hackCar(...)。因此,这种访问无需反射即可实现。

如果 TestCar 被定义 public 并且在包 lib.cars 中,但是 StoryTerminal 在项目的包 app.cars 中,那么class Accessor 在包 lib.cars 中定义的项目可以提供对包可见字段 hasAirConditionbrand:

的访问
package lib.cars;

public class Accessor {

  public static boolean hasAirCondition(TestCar testCar) {
    return testCar.hasAirCondition;
  }

  public static String getBrand(TestCar testCar) {
    return testCar.brand;
  }

}

因此您仍然可以访问 TestCar.

四个字段中的三个

如果没有反射,private 字段将始终无法访问。由于其他答案都在解释如何使用反射访问 TestCar 的字段,因此我不会重复。因此,我的回答是对其他答案的补充。

但如其他答案所述,使用反射有其缺点 - 即重构,例如将字段 doorUnlockCode 简单重命名为 doorsUnlockCode。因此,也许人们应该尽可能长时间地使用常规方式,并将反射作为最后的手段。

我们可以将这两种方式合并到 Accessor class:

package lib.cars;

import java.lang.reflect.Field;

public class Accessor {

  public static boolean hasAirCondition(TestCar testCar) {
    return testCar.hasAirCondition;
  }

  public static String getBrand(TestCar testCar) {
    return testCar.brand;
  }

  public static int getDoorUnlockCode(TestCar testCar) throws Exception {
    Field field = TestCar.class.getDeclaredField("doorUnlockCode");
    field.setAccessible(true);
    field.setAccessible(true);
    return (int) field.get(testCar);
  }

}

Terminal中使用如下:

public class Terminal {

  public void hackCar(TestCar other) throws Exception {

    System.out.println(Accessor.getDoorUnlockCode(other));

    System.out.println(Accessor.hasAirCondition(other));

    System.out.println(Accessor.getBrand(other));

    System.out.println(other.licensePlate);
  }

}