使用反射修改字符串的目的是什么?
我正在阅读一篇 文章 说 Java 字符串并不完全不可变的。但是,在本文修改字符串的示例代码中,它调用了 string.toUpperCase().toCharArray(),这会返回一个新字符串。那么,如果您无论如何调用 toUpperCase() ,那么经历更改字符串的过程的目的是什么?这是代码:
public static void toUpperCase(String orig)
{
try
{
Field stringValue = String.class.getDeclaredField("value");
stringValue.setAccessible(true);
stringValue.set(orig, orig.toUpperCase().toCharArray());
}
catch (Exception ex){}
}
另外,我注意到 string.toUpperCase() 本身不起作用。它需要是 string.toUpperCase().toCharArray()。这是有原因的吗?
I was reading an article that said that Java strings are not completely immutable. However, in the article's sample code that modifies the string, it makes a call to string.toUpperCase().toCharArray(), which returns a new string. So what's the purpose of going through the process of changing the string if you call toUpperCase() anyway? Here is the code:
public static void toUpperCase(String orig)
{
try
{
Field stringValue = String.class.getDeclaredField("value");
stringValue.setAccessible(true);
stringValue.set(orig, orig.toUpperCase().toCharArray());
}
catch (Exception ex){}
}
Also, I noticed that string.toUpperCase() by itself doesn't work. It needs to be string.toUpperCase().toCharArray(). Is there a reason for this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
他正在做什么:
他正在获取一些他知道长度正确的字符数组(例如字符串的大写版本)并将其作为字符串的支持数组。 (支持数组在 String 类中称为
value
。)为什么这样做:
为了说明您可以在其中放置任何您想要的 char 数组。
为什么这有用:
字符串是不可变的,这允许您规避不可变性。当然,永远不建议这样做。相反,如果他说“小心,因为人们可能会对您的代码执行此操作。即使您认为您的东西是安全的,但它可能并不安全!”我不会感到惊讶。
其影响是广泛的。不可变变量不再是不可变的。最终变量不再是最终变量。线程安全对象不再是线程安全的。您认为可以信赖的合同,现在您不能再信赖了。这一切都是因为某个地方的某个工程师遇到了一个他无法用正常方法解决的问题,所以他深入思考来解决它。 不要成为“那个人”。
您还会注意到该字符串的 hashCode 现在将如何更改。因此,如果您从未计算过该字符串的 hashCode,它仍然是 0,所以没问题。另一方面,如果你计算过它,当你把它放入HashMap或HashSet时,它就不会被检索到。
考虑以下几点:
我敢打赌他这样做是为了警告不良行为,而不是展示“很酷”的把戏。
编辑:我找到了您所指的文章及其所基于的文章。原始文章指出:“这意味着,如果另一个包中的类“摆弄”了一个实习字符串,它可能会对您的程序造成严重破坏。这是一件好事吗?(您不需要回答;-)”我我认为这很清楚地表明,这更多的是保护指南,而不是如何编码的建议。
因此,如果您离开此线程时只留下一条信息,那就是反射是危险的、不可靠的、不容忽视的!
What he's doing:
He's acquring some character array that he knows is the right length (such as the uppercase version of the String) and putting it as the backing array of the String. (The backing array is called
value
inside the String class.)Why he's doing it:
To illustrate that you could put any char array there you wanted.
Why this is useful:
String is immutable, and this allows you to circumvent the immutability. Of course, this is not recommended to do - EVER. On the contrary, I would not be surprised if he was saying "Watch out, because people could potentially do this to YOUR code. Even if you think your stuff is safe, it might not be!"
The implications of this are wide reaching. Immutable variables are no longer immutable. Final variables are no longer final. Thread safe objects are no longer thread safe. Contracts you thought you could rely upon, you can no longer do so. All because some engineer somewhere had a problem he couldn't fix with normal means, so he delves into reflection to solve it. Don't be 'that guy'.
You'll also note that how the hashCode for that String would now be changed. So, if you've never calculated the hashCode for that String, it's still 0 so you're okay. On the other hand, if you have calculated it, when you go to put it in a HashMap or HashSet, it won't be retrieved.
Consider the following:
I would bet he is doing this as a warning against bad behavior rather than showing a 'cool' trick.
EDIT: I found the article you're referring to, and the article it is based on. The original article states: "This means that if a class in another package "fiddles" with an interned String, it can cause havoc in your program. Is this a good thing? (You don't need to answer ;-) " I think that makes it quite clear this is more of a protection guide than advice on how to code.
So if you walk away from this thread with only one piece of information, it is that reflection is dangerous, unreliable, and not to be trifled with!
不要在家里尝试这个!
你正在破坏 String 的不变性。 没有充分的理由这样做。
Don't try this at home!
You are subverting String's immutability. There is no good reason to do this.
我想我无法对已经提供的解释进行补充,所以也许我可以通过建议如何防止这种情况来补充讨论。
您可以通过使用安全管理器来防止有人以这些和其他意外方式篡改您的代码。
如果您没有向默认策略文件中的所有代码库授予所有权限,这将在 toUpperCase 方法中生成异常。 (在您当前的代码中,您的异常目前已被吞掉)。
I think I cannot add to the explanations already provided, so perhaps I can add to the discussion by suggesting how this can be prevented.
You can prevent somebody tampering with your code in these and other unintended ways by means of using a security manager.
This will generate an exception in the toUpperCase method, provided that you are not granting all privileges to all code bases in the default policy files. (In your current code your exceptions are currently swallowed).
目的是什么?我不太确定,请问写这个东西的人。你通常不应该做这样的事情。 String 不可变是有原因的。
如果字段是公共的,即没有反射,则该方法的外观如下:
由于
value
的类型为char[]
,因此无法分配String< /code> 到此字段 - 这就是为什么您需要在
.toUpperCase()
之后调用toCharArray
。如果您尝试这样做,您将得到一个异常(我想是ClassCastException
),但是那里的 try-catch 块将其吞噬。 (这给我们带来了另一个教训:永远不要使用这样的空 catch 块。)注意:这段代码可能不会做正确的事情,因为原始字符串的实际数据可能不会从
char[]
。由于您没有更新offset
字段,因此在使用此类修改后的字符串时,您将得到IndexOutOfBoundsExceptions
。另外,String 对象缓存了它的 hashCode,因此这也是错误的。这是一个正确的方法:
通过反射,它看起来像这样:
What is the purpose? I'm not sure, ask the one that wrote this stuff. You normally should not do something like this. There is a reason String is immutable.
Here how this method would look if the fields were public, i.e. without reflection:
As
value
is of typechar[]
, you can't assign aString
to this field - this is why you need thetoCharArray
call after.toUpperCase()
. You will get an exception if you try to do this (I supposeClassCastException
), but the try-catch block there eats it away. (This gets us another lesson: Never use such empty catch blocks.)Pay attention: This code might not do the correct thing, since the actual data of the original string might not start at the start of the
char[]
. Since you don't update theoffset
field, you will getIndexOutOfBoundsExceptions
when using such a modified String. Also, the String object caches its hashCode, thus this will be wrong, too.Here would be a correct way:
With reflection, it looks like this:
1.) 阅读
Bohemian
答案。2.) 字符串内部存储在 char 数组中,这就是为什么您需要调用
toCharArray
来设置字段。1.) Read
Bohemian
answer.2.) Strings are internally stored in a char array, that's why you need to call
toCharArray
to set the field.默认情况下,String.toUpperCase() 保持原始字符串不变,同时返回一个新的字符串对象。
您上面定义的函数会就地编辑原始字符串对象的内容。
By default, String.toUpperCase() leaves the original string intact, whilst returning a new string object.
The function you defined above, edits the contents of the original string object in-place.
您可以通过反射更改最终字符串以进行测试。有时,该字符串包含生产环境中使用的默认位置的路径,但不适合测试。然而,该变量被测试中触发器的多个对象/方法引用,因此在测试期间您可能希望将其设置为特定值。
正如其他人所说,这可能是您不想做的事情(经常/永远)。
You change a final string with reflection for testing. Sometimes that string contains the path to a default location used in the production environment but not suitable for testing. Yet, that variable is referenced by several objects/methods your trigger in your test, and hence during your tests you might want to set it to a particular value.
As others said, it's probably something you don't want to be doing (often/ever).