Do java support 'String' methods properly ?

T

Thomas Hawtin

Roedy said:
Strings are immutable. That is iron clad. The caller's String will
not change neither will the caller's reference to the String.

The only way I know of to change a String is to use JNI and once
inside cheat, taking advantage of C's total lack of restraints.

setAccessible(true) takes the remaining seat belts off reflection.

Tom Hawtin
 
B

Benji

Thomas said:
setAccessible(true) takes the remaining seat belts off reflection.

That doesn't have anything to do with strings being immutable. Immutable
types (String, Integer) depend on immutability for optimizations. Even if
calling setAccessible(true) could let you modify a String, it would really
screw up your program. For example...if I have
String a = "foo";
String b = "foo";

And I modify a, I will also end up modifying b because of the way that the
compiler stores Strings. (since they're immutable, they might as well be
the same object)

Another optimization that a compiler can make (that I'm not sure if it does
or not), if you have

String a = "sub";
String b = "I live on a submarine";

a can be stored as a substring of b. I believe current JVMs do this.
 
D

Daniel Dyer

That lets you view a private field. Does it also let you write a
final?

You can modify the contents of the String's character array, this is
especially fun when dealing with literals, since they are pooled. You can
write code (without using JNI) that makes this line

System.out.println("Hello World");

print out "Goodbye".

Dan.
 
D

Daniel Dyer

Actually, that's not true. Strings are kinda like real objects and
kinda like primatives. For instance you can say
String foo = "hello" just like you can say int i =5. You cannot say
StringBuffer foo = "hello" (because you must create the object first).

OK, I'll give you that, the String class has some special language support
(literals and the + operator), but they are still just objects like any
others.
Here is the test though...
public class stringtest
{

stringtest()
{
String foo = "hello";
StringBuffer bar = new StringBuffer("hello");
testString (foo);
testBuffer(bar);
System.out.println("String is "+foo);
System.out.println("Buffer is "+bar);
}

private void testBuffer(StringBuffer bar)
{
bar.append("bye");
}

private void testString(String foo)
{
foo.toUpperCase();
}

public static void main(String[] args)
{
new stringtest();
}
}

The out put looks like
String is hello
Buffer is hellobye

Notice the string is left untouched (because it was passed by value)
but the StringBuffer was changed (because it was passed by reference).

The toUpperCase method returns a new String (you are discarding it by not
assigning it to anything), it does not change the original String. If
StringBuffers were passed by reference Pascal-style, the following code
would print out "Goodbye", but it prints hello because the reference is
passed by value (http://javadude.com/articles/passbyvalue.htm).

public class ReferenceTest
{
private StringBuffer myBuffer = new StringBuffer("Hello");

public static void main(String[] args)
{
changeStringBuffer(myBuffer);
System.out.println(myBuffer.toString());
}

private static void changeStringBuffer(StringBuffer buffer)
{
buffer = new StringBuffer("Goodbye");
}
}

Dan.
 
B

Benji

Daniel said:
You can modify the contents of the String's character array, this is
especially fun when dealing with literals, since they are pooled. You can
write code (without using JNI) that makes this line
System.out.println("Hello World");
print out "Goodbye".

import java.lang.reflect.Field;
public class Main
{
public static void main(String[] argv) throws Exception
{
String s1 = "This is only a test.";
String s2 = "This is only a test.";
destructiveMutate(s2, "Well I'll be darned");
System.out.println(s1);
}

public static void destructiveMutate(String s, String sNew) throws Exception
{
Field value = String.class.getDeclaredField("value");
Field offset = String.class.getDeclaredField("offset");
value.setAccessible(true);
offset.setAccessible(true);

assert sNew.length() > s.length() : "too long";

int off = offset.getInt(s);
char[] arr = (char[])value.get(s);
for(int i=0;i<sNew.length();i++)
{
arr[off+i] = sNew.charAt(i);
}
}
}
 
B

Benji

Daniel said:
You can modify the contents of the String's character array, this is
especially fun when dealing with literals, since they are pooled. You can
write code (without using JNI) that makes this line
System.out.println("Hello World");
print out "Goodbye".

import java.lang.reflect.Field;
public class Main
{
public static void main(String[] argv) throws Exception
{
String s1 = "This is only a test.";
String s2 = "This is only a test.";
destructiveMutate(s2, "Well I'll be darned");
System.out.println(s1);
}

public static void destructiveMutate(String s, String sNew) throws Exception
{
Field value = String.class.getDeclaredField("value");
Field offset = String.class.getDeclaredField("offset");
value.setAccessible(true);
offset.setAccessible(true);

assert sNew.length() <= s.length() : "too long";

int off = offset.getInt(s);
char[] arr = (char[])value.get(s);
for(int i=0;i<sNew.length();i++)
{
arr[off+i] = sNew.charAt(i);
}
}
}
 
T

Thomas Hawtin

Benji said:
That doesn't have anything to do with strings being immutable. Immutable

No, it shows Strings are not passed by value. As in "Notice the string
is left untouched (because it was passed by value) ...". (The reference
is passed by value.)
types (String, Integer) depend on immutability for optimizations. Even if
calling setAccessible(true) could let you modify a String, it would really
screw up your program. For example...if I have
String a = "foo";
String b = "foo";

You don't need to change final fields to modify String. You can get the
value array. You can fiddle with the hashCode cache. In old versions,
value is not final. synchronize also modifies the object.
And I modify a, I will also end up modifying b because of the way that the
compiler stores Strings. (since they're immutable, they might as well be
the same object)
Yup.

Another optimization that a compiler can make (that I'm not sure if it does
or not), if you have

String a = "sub";
String b = "I live on a submarine";

a can be stored as a substring of b. I believe current JVMs do this.

Not on the version I use.

class InternSubstring {
public static void main(String[] args) throws Exception {
String a = "$sub$";
String b = "I live on a $sub$marine";
show(a);
show(b);
}
private static void show(String str) throws Exception {
System.err.println(
field("value", str)+", "+
field("offset", str)+", "+
field("count", str)
);
}
private static String field(
String name, Object object
) throws Exception {
java.lang.reflect.Field field =
String.class.getDeclaredField(name);
field.setAccessible(true);
return name+" <"+field.get(object)+">";
}
}
 
I

Ingo R. Homann

Hi Roedy,

Roedy said:
That lets you view a private field. Does it also let you write a
final?

That's not necessary (see below), since you do not need to change the
array itself, but only the elements of the array.

Ciao,
Ingo


import java.lang.reflect.Field;

public class MutableString
{

public static void main(String[] args)
{
String s = "Hello";
modify(s);
System.out.println(s);
}

private static void modify(String s)
{
try
{
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
char[] cs = (char[]) f.get(s);
for (int i = 0; i < cs.length; i++)
{
cs = 'X';
}
}
catch (Exception e)
{
e.printStackTrace();
}
}

}
 
R

Roedy Green

You can modify the contents of the String's character array, this is
especially fun when dealing with literals, since they are pooled. You can
write code (without using JNI) that makes this line

System.out.println("Hello World");

My God. It that seems like building a bank vault then leaving the
combination on a slip of paper under the mat.
 
D

Daniel Dyer

My God. It that seems like building a bank vault then leaving the
combination on a slip of paper under the mat.

But think of the possibilities for driving your co-workers nuts. Hide
some of this literal-altering code somewhere obscure and watch them debug
for hours and finally start crying like a baby because even their simple
System.out.println calls don't do what they expect.

Dan.
 
C

Chris Uppal

Daniel said:
But think of the possibilities for driving your co-workers nuts. Hide
some of this literal-altering code somewhere obscure and watch them debug
for hours and finally start crying like a baby because even their simple
System.out.println calls don't do what they expect.

;-)

Also add some complicated and obscure JNI code (is there any other kind ?)
which does /not/ play games with String contents. Watch them scratching their
heads over C code, trying to puzzle out the JNI error...

-- chris
 
B

Benji

Thomas said:
Benji wrote:
<snip incorrect stuff that I cancelled>

lol. sorry, nobody had replied when I realized that I was wrong, and I
cancelled this. It was only up for like a minute. You're quick. =P
 
B

Benji

My God. It that seems like building a bank vault then leaving the
combination on a slip of paper under the mat.

Looks like another entry in the unmaintainable code database, Roedy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top