扩展最后的课程或替换它

问题描述 投票:1回答:3

“不,它无法完成”

已经讨论了很多次,唯一的回答是“不,它不能完成”。但是,我们会编写代码。一段时间后,一切都会[[理论上。

基本上,我正在尝试扩展最后一个类以更改单个行为。这无法以传统方式完成-在编译时使用extend关键字。我在寻找其他方法:

创建代理-仅适用于接口;

    编辑类的Modifiers,删除final修饰符并在运行时扩展类-可以对字段执行但不能对类执行;
  • 使用自定义ClassLoader加载该类的新版本-之所以无效,是因为我要扩展的类是java包的一部分。幸运的是,由于ClassLoaderSecurityException无法加载此程序包的类。否则,那会弄乱很多证券。
  • static class TestClassLoader extends ClassLoader { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { if (name.equals("java.io.StringReader")) { try { InputStream is = Test.class.getClassLoader().getResourceAsStream("java/io/StringReader.class"); byte[] buf = new byte[10000]; int len = is.read(buf); return defineClass(name, buf, 0, len); // This line throws the SecurityException :( } catch (IOException e) { throw new ClassNotFoundException("", e); } } return getParent().loadClass(name); } }
  • 所以我们来了。有什么方法可以用来替换或扩展我的最终课程吗?
    PS:我使用Java 8是因为SecurityManager比新的Java版本更喜欢丑陋的东西:)

    语境


    我有一个作业-erk,这在SO上从未得到很好的认可:我必须为我无法更改的类编写单元测试。这部分非常容易,除了无法访问catch块:

    Properties props = new Properties(); try { props.load(new StringReader(rules)); } catch (IOException e) { e.printStackTrace(); throw e; }

    我们的老师发送给我们的代码覆盖率报告显示,他尚未测试此块,因此我们不必这样做。但是我不信任Java,因此我可以确定此块可以按预期工作。然后我必须对其进行测试!

    [[注:由于不是必需的,它只是发现新的Java hack的一种手段,它甚至可能不会算入最终成绩。]

    我唯一可以访问的变量是String rules。然后,我应该对其进行处理以使load调用引发IOException。 load调用引发IOException的唯一原因是在StringReader的流关闭时。这是通过ensureOpen方法检查的:

    /** Check to make sure that the stream has not been closed */ private void ensureOpen() throws IOException { if (str == null) throw new IOException("Stream closed"); }

    因此,只有空String可以使此方法抛出IOException。这样,将空String注入到rules中就很容易了。坏消息是StringReader构造函数:

    public StringReader(String s) {
        this.str = s;
        this.length = s.length();
    }
    

    [通过null String时将引发NPE。 ERK

    [我的最后一个想法是更改String的值以使其内部的value字段null-使用此字段的Modifiers时很容易。这行不通,因为引用而不是值本身应该是null。由于在read上调用了StringReader方法,因此只会抛出NPE:

    public int read() throws IOException { synchronized (lock) { ensureOpen(); if (next >= length) return -1; return str.charAt(next++); } }

    你有什么主意吗?一个可以为我做到这一点的框架?我可以探索解决无用问题的一些方法?
    这个问题是

    与代码测试无关。它是关于

    如何破解Java及其最终类。

  • java java-8 final
    3个回答
    4
    投票
    但是请注意,代码注入的特定目标是错误的。在代码中

    Properties props = new Properties(); try { props.load(new StringReader(rules)); } catch (IOException e) { e.printStackTrace(); throw e; }

    IOException被抛出[[never”。如您所知,StringReader的构造函数不会抛出它,并且对它的read方法也是如此。 loadProperties方法声明了它,因为它必须处理任意的Reader实现,但它自己不会产生此类异常。

    因此,在这种情况下,当您从String中读取具有新构造的StringReader时,将永远不会抛出该错误,并且要注入一个实际上不存在的行为来测试一个永远不会出现的情况,这无济于事发生。

    您还指出了自己,唯一可能抛出IOException的地方就是方法ensureOpen(),顾名思义,该方法检查读取器是否仍然打开。因此,您可以通过简单地在读取器上调用close()来强制引发行为,但是,正如所说的那样,这与被测代码的实际行为不匹配,并且毫无意义地引发被测异常,永远不会发生。


    3
    投票

    0
    投票
    1)使用PowerMock。它的一个基本部分是修改字节代码

    MockClassLoader的类加载器。 PowerMock的优点在于,您可以从原始类中获取所有方法签名,这就是为什么您可以在IDE(Eclipse,IDEA)中使用autocompletion的原因因此,与AspectJ,Spring AOP或ASM相比,您可以更轻松地覆盖方法,并且在包名,类名和方法名(它们在其他工具中为String)中的错误更少,因此更容易出错。

    2)修改字节码加载前。例如,从目标库中提取所需的类,根据需要对其进行修改,例如,使该类不是最终类,重新打包该库,然后在应用程序中使用它。可以使用不同的工具:AspectJ(拥有源代码并进行编译时),ASM(还可以修改现有的字节码)。与其他方法的本质区别是,在编译期间在应用程序中使用此类修改后的类时,编译器不知道修改后的类的原始字节码,因此可以使用该类的常规inheritance那是最后的决定。

    3)修改字节码加载期间。您可以使用AsppectJ,Spring AOP,ASM。最强大的是ASM,但它也需要付出更多的努力。相较于ASM,AspectJ功能强大且具有高级API。与AspectJ相比,Spring AOP受到很大的限制,但是在某些情况下,它的使用可能更为简单。

    4)您可以组合不同的方法。例如,您可以使用自己的类加载器,并使该类不是最终的(将一位设置为0)。这是很小的更改,即使没有任何库也可以执行此操作。然后,您可以根据需要使用CGLIB来修改类的行为。有什么不同吗?如果使用方面,则只能修改

    existing

    方法的行为。但是,使用CGLIB,您可以将新方法添加到您的类中。
    © www.soinside.com 2019 - 2024. All rights reserved.