创建可变的 java.lang.String
众所周知,Java是不可变的。自成立以来,不可变字符串是Java的一个很好的补充。不可变性允许快速访问和大量优化,与C样式字符串相比,不易出错的因素明显降低,并有助于实施安全模型。String
可以在不使用黑客的情况下创建一个可变的,即
java.lang.reflect
sun.misc.Unsafe
- 引导类装入器中的类
- JNI(或JNA,因为它需要JNI)
但是,在普通的Java中是否可以,以便可以随时修改字符串?问题是怎么做?
众所周知,Java是不可变的。自成立以来,不可变字符串是Java的一个很好的补充。不可变性允许快速访问和大量优化,与C样式字符串相比,不易出错的因素明显降低,并有助于实施安全模型。String
可以在不使用黑客的情况下创建一个可变的,即
java.lang.reflect
sun.misc.Unsafe
但是,在普通的Java中是否可以,以便可以随时修改字符串?问题是怎么做?
使用Charset构造函数创建一个,可以注入自己的Charset,它带来了你自己的。获取对解码循环方法中对象的引用。CharBuffer 包装原始 String 对象的 char[]。由于CharsetDecoder具有对它的引用,因此您可以使用CharBuffer更改底层char[],因此您有一个可变的字符串。java.lang.String
CharsetDecoder
CharsetDecoder
CharBuffer
public class MutableStringTest {
// http://stackoverflow.com/questions/11146255/how-to-create-mutable-java-lang-string#11146288
@Test
public void testMutableString() throws Exception {
final String s = createModifiableString();
System.out.println(s);
modify(s);
System.out.println(s);
}
private final AtomicReference<CharBuffer> cbRef = new AtomicReference<CharBuffer>();
private String createModifiableString() {
Charset charset = new Charset("foo", null) {
@Override
public boolean contains(Charset cs) {
return false;
}
@Override
public CharsetDecoder newDecoder() {
CharsetDecoder cd = new CharsetDecoder(this, 1.0f, 1.0f) {
@Override
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
cbRef.set(out);
while(in.remaining()>0) {
out.append((char)in.get());
}
return CoderResult.UNDERFLOW;
}
};
return cd;
}
@Override
public CharsetEncoder newEncoder() {
return null;
}
};
return new String("abc".getBytes(), charset);
}
private void modify(String s) {
CharBuffer charBuffer = cbRef.get();
charBuffer.position(0);
charBuffer.put("xyz");
}
}
运行代码打印
abc
zzz
我不知道如何正确实现decodeLoop(),但我现在不在乎:)
这个问题得到了@mhaller个很好的回答。我会说所谓的拼图非常简单,通过查看String的可用c-tors,人们应该能够找出how部分,一个
演练
感兴趣的C-tor在下面,如果你要闯入/破解/寻找安全漏洞,总是寻找非最终的任意类。这里的情况是java.nio.charset.Charset
//String
public String(byte bytes[], int offset, int length, Charset charset) {
if (charset == null)
throw new NullPointerException("charset");
checkBounds(bytes, offset, length);
char[] v = StringCoding.decode(charset, bytes, offset, length);
this.offset = 0;
this.count = v.length;
this.value = v;
}
c-tor提供了所谓的快速转换为String的方法,通过传递Charset而不是charset名称来避免查找charet chartsetName->charset。它还允许传递任意字符集对象以创建字符串。字符集主路由将 的内容转换为 。CharBuffer可以包含对char[]的引用,并且可以通过,也可以通过CharBuffer完全修改。byte[]
java.nio.ByteBuffer
CharBuffer
array()
//StringCoding
static char[] decode(Charset cs, byte[] ba, int off, int len) {
StringDecoder sd = new StringDecoder(cs, cs.name());
byte[] b = Arrays.copyOf(ba, ba.length);
return sd.decode(b, off, len);
}
//StringDecoder
char[] decode(byte[] ba, int off, int len) {
int en = scale(len, cd.maxCharsPerByte());
char[] ca = new char[en];
if (len == 0)
return ca;
cd.reset();
ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
CharBuffer cb = CharBuffer.wrap(ca);
try {
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
cr.throwException();
cr = cd.flush(cb);
if (!cr.isUnderflow())
cr.throwException();
} catch (CharacterCodingException x) {
// Substitution is always enabled,
// so this shouldn't happen
throw new Error(x);
}
return safeTrim(ca, cb.position(), cs);
}
为了防止更改,java开发人员复制数组就像任何其他字符串结构一样(例如)。但是有一个例外 - 如果未安装安全管理器,则不会复制 char[]。char[]
public String(char value[])
//Trim the given char array to the given length
//
private static char[] safeTrim(char[] ca, int len, Charset cs) {
if (len == ca.length
&& (System.getSecurityManager() == null
|| cs.getClass().getClassLoader0() == null))
return ca;
else
return Arrays.copyOf(ca, len);
}
因此,如果没有 SecurityManager,则绝对有可能有一个由 String 引用的可修改的 CharBuffer/char[]。
现在一切看起来都很好 - 除了也被复制了(上面的粗体)。这就是Java开发人员懒惰和严重错误的地方。byte[]
复制对于防止流氓字符集(上面的示例)能够更改源字节[]是必要的。但是,想象一下包含很少字符串的大约512KB缓冲区的情况。尝试创建单个小的,很少的图表 - 导致大量512KB字节[]副本。如果缓冲区为1KB左右,则永远不会真正注意到影响。但是,对于较大的缓冲区,性能影响确实很大。简单的解决方法是复制相关部分。byte[]
new String(buf, position, position+32,charset)
...或者通过引入只读缓冲区来考虑的设计人员。简单地调用就足够了(如果 Charset.getClassLoader()!=null)* 有时即使是那些工作的人也会完全搞错。java.nio
ByteBuffer.asReadOnlyBuffer()
java.lang
*Class.getClassLoader() 为引导类返回 null,即 JVM 本身附带的类。