• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Liutauras Vilda
  • Ron McLeod
Sheriffs:
  • Jeanne Boyarsky
  • Devaka Cooray
  • Paul Clapham
Saloon Keepers:
  • Scott Selikoff
  • Tim Holloway
  • Piet Souris
  • Mikalai Zaikin
  • Frits Walraven
Bartenders:
  • Stephan van Hulst
  • Carey Brown

Why this equality is true ? (String constant poll / Heap)

 
Greenhorn
Posts: 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm making some experiments about the string constants pool and interning in java
And I came across an example where I couldn't get it clear because I'm missing something. Here is the code:



I would like to know how come s2 is same as s3 even that they point to different memory areas (heap, scp)
Could someone points to what I'm missing here, thanks.
 
Marshal
Posts: 80120
414
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Welcome to the Ranch

Obviously your interning has changed the object s2 points to. Remember, this is only worth knowing about until you pass your cert exam, after which you can forget it completely. Link for String#intern().
Thank you for finding the code button Please don't double‑space code, nor use long trailing comments; both make the code harder to read especially on a small (mobile) screen.
 
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I agree on the two important parts of what Campbell said:
1. Welcome to the Ranch!
2. Only worry about these issues until you pass your exam, and then not again unless you find yourself working on the team that maintains the String class for OpenJDK code!

I disagree about what he said that "obviously your call to .intern() changed the object that s2 pointed to".
In fact, it did not.

I do not believe that System.identityHashCode() is covered on the exam, but it is the closest thing in the world you can get to "id(ref)" from Python

jshell> String s1 = new String("GFG");
s1 ==> "GFG"

jshell> String s2 = s1.concat("GFG");
s2 ==> "GFGGFG"

jshell> System.identityHashCode(s2)
$17 ==> 670576685

jshell> String s3 = s2.intern()
s3 ==> "GFGGFG"

jshell> System.identityHashCode(s2)
$19 ==> 670576685

jshell> System.identityHashCode(s3)
$20 ==> 670576685


If the call to s2.intern() changed the object to which s2 referred, the System.identityHashCode() would have changed for it.  It did not.

Do I have that wrong?

Look at this:
jshell> s3 = new String("GFGGFG")
s3 ==> "GFGGFG"

jshell> System.identityHashCode(s3)
$22 ==> 1637506559

jshell> System.identityHashCode(s2)
$23 ==> 670576685
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I already spent way, way, WAY too much time looking at this when I first arrived at the Ranch! (from the point of view of someone trying to pass an exam soon, I indeed found it interesting).
My ridiculously long thread about it can be found here for those who think they can afford the time:
https://coderanch.com/t/737223/certification/String-Constant-Pool-Behavior-Scope

What happened is that the String Constant Pool in modern Java versions is no longer necessarily distinct from the rest of the String pool.

When you call .intern() it will look to see if the value you are attempting to .intern() is already there (anywhere at all in the String pool).

It was, so s3 gets assigned the value of s2, which does not change.

Java didn't originally work this way for 15 or more years, but now it does.

The consensus was that this should be beyond the scope of the 808/809/811/817/819 exam, but is part of "Advanced Java Trivia".

Welcome to Advanced Java Trivia.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Also in my defense, when I started I was sure that this WAS in scope for the 819 exam I've been preparing for on and off.
I can't say for sure if I would have pursued it so far had I realized it was not.
The thread was even titled to ask others to weigh in on whether it would be in scope.
I like to think I might not have, but who knows.
 
Rancher
Posts: 5090
82
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Obviously your interning has changed the object s2 points to.


Why is that obvious?  To me, it seems obvious that since the newly-created concatenation GFGGFG was not already in the string pool, the intern() call added GFGGFG to the pool.  That doesn't require any change to the string - it's a change to the pool.  Think of the pool as a big Map, and we just added a copy of reference s2 to that Map.  Two copies really, as both key and value.  Both of which point to the same single String instance which was originally created on the heap.
 
Redwan hamm
Greenhorn
Posts: 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you very much for all the responses, I'm overwhelmed by how friendly the community is here!
For the answer it looks like s3 was not pointing to a string object in SCP from the beginning, but it has the same reference of s2 thus references an object in the Heap
that is because intern() returns the the reference of the same string if it does not find an equal value to the string in the SCP, and only creates an equal value string in SCP.
And yes this is going right to the "never remember area" just after getting my certification.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is in scope for the exams, and that is why I am asking about it, not to pile on Campbell.

String s2 = s1.concat("GFG"); // here s2 also should be created in the HEAP (runtime operation)

String s3 = s2.intern();      // here s3 has a reference form SCP

Neither the 2nd line nor anything that does not begin with:
s2 =

can change s2 in Java, right?  Threads don't share local variables, there are no C++ style references which are just aliases to the same local variables.
You can't pass a reference by reference, but only by value, another way you could do this in other languages....

Now, in the case of String, and ONLY String, because the one exception for "NO OPERATOR OVERLOADING" in Java is + and += for String, no place else but primitives and String special-cased ever get to use +...

A favorite question to ask is what is happening here?

String s3 = "A";
s3 += "HA!";

Strings are immutable, so what's happening?
While it looks like the String referred to by s3 is being changed, that isn't true because every String in existence is 100% immutable.

This gets converted to:
s3 = s3 + "HA!";

which concatenates "HA!" after s3, leaving s3 as it is, and creating the new String "AHA!" on the heap.
This new String object gets assigned to reference variable s3, reducing the reference count of "A"...

So while parts of this are out of scope of the Oracle exams, I think disagreeing with Campbell's probably hasty post isn't?

There is simply no way that a local reference variable can be changed in Java unless a line containing that reference variable on the left side of an = or += executes, I think.

This isn't true in C++, because you could have the equivalent of 'String& s4 = s3' somewhere but then again, there's all sorts of crazy stuff that an happen there that can never happen in Java.

No invocation of any instance or static method associated with a local reference variable is going to be able to change the value of that local reference variable unless the result is assigned back with a simple or compound =
For String references, only = and += are defined.

I lied.  Due to the weird (and also very special-cased) auto-boxing and unboxing behavior with numeric wrapper types, in modern Java version 5 and above, the following sorts of thing works analogous to s3 += "HA!":
jshell> Integer I = 1
I ==> 1

jshell> ++I
$27 ==> 2

jshell> I
I ==> 2

jshell> I--
$29 ==> 2

jshell> I
I ==> 1

jshell> Character C = 'D';
C ==> 'D'

jshell> ++C
$32 ==> 'E'

jshell> --C
$33 ==> 'D'


jshell> I *= 40
$46 ==> 40

jshell> I *= 40
$47 ==> 1600

jshell> I /= 4
$48 ==> 400

This confuses the heck out of many people because the Wrapper Class instances are Immutable, but these lines make it look like they aren't if you don't realize what is going on.
They are just shorthand for unboxing, adding or subtracting, and then auto-boxing again creating a new wrapped instance (or not if it is cached!) and re-assigning it back to the reference variable.
That's a lot of syntactic sugar.  This weirdness applies to Byte, Short, Integer, Long, Character, Float and Double

My original statement still stands tho, there is no way to change what String a local String reference points to by calling any static or instance method on it, .intern() or otherwise.  Only s1= or s1+= can possibly do that in Java.  With Character or the numeric wrapper classes, you can also use any of the compound assignments as well, but that's it.  No weird changes to a local reference variable in ways you can easily bring about in other languages.
 
Campbell Ritchie
Marshal
Posts: 80120
414
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I appear to be mistaken; maybe the String in question was moved into the constant pool.
There are three other overloaded operators: & ^ |. Those bitwise operators are overloaded for use on booleans too. The correct meaning of the term, “no operator overloading” is that a user cannot overload operators themselves.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:I appear to be mistaken; maybe the String in question was moved into the constant pool.


The long-true notion that the String Constant Pool was completely disjoint from the rest of the String objects on the heap is what I believe is no longer true.
A reference to a compile-time constant expression that is already found on the heap when that line is first encountered for execution will share the existing one on the heap (that came from a calculation or read from a file or user input) will just use the existing one on the heap now, if it has been interned, I think.

That is, the results of .intern() may be a reference to an object that was only created at run time, if that creation happened before a matching literal was encountered at runtime.

This should be beyond anything someone trying to write a program to do something useful is thinking about, but the tricky exams abounded with questions about whether a given string would be unique or not, which caused my attention to be drawn there.

In particular, the JLS in 3.10.5 does not show or say what happens in this case:

Which I always get as 'true', 'true'.

They have three rules in that section:
• Strings concatenated from constant expressions (§15.29) are computed at compile time
and then treated as if they were literals.
• Strings computed by concatenation at run time are newly created and therefore distinct.
• The result of explicitly interning a computed string is the same String object as any
pre-existing string literal with the same contents.

While the second is true with respect to each other, i.e. separate concatenations done at different times during the run will be distinct, they may be (will be) identical to interned strings and string literals with the same value, provided someone interns them before a line containing them as a literal gets executed.

Too much detail for almost everyone, but I think it demonstrates a possible (not serious) harm of speaking of a String Constant Pool as if it is completely disjoint from the strings created at runtime.
To the extent it still exists, it consists of unique values that are literals, compile-time constant concatenations, and strings computed at runtime and then interned before lines referencing them as literals were executed.

I guess the notion that the String Constant Pool is completely disjoint from strings created at runtime is much closer to the truth than that all strings are in such a pool, but is no longer strictly 100% true in the face of .intern().

I think that in addition to "don't compare strings with ==' one big misconception we want to avoid (that people have due to sloppy presentations that are commonly watched/read) is that "all strings are in the string constant pool".  Such presentations had me thinking that for some period of time!  That is a huge misconception that can be very harmful for understanding, and this corner case only comes up if you are calling .intern() which should probably be avoided anyway.

Jeanne and Scott's books plainly say "Don't call .intern() on String objects, it seems like a good idea but really isn't."

The other thread illustrates me being cured of that deceptively appealing temptation.

Campbell Ritchie wrote:There are three other overloaded operators: & ^ |. Those bitwise operators are overloaded for use on booleans too. The correct meaning of the term, “no operator overloading” is that a user cannot overload operators themselves.


Yes, in my haste to explain lack of overloading, I forgot that those three operators do double duty with booleans as well as byte/short/int/long/char.
With booleans, of course, ^ does the obvious, whereas & and | behave as the short-circuit && and || except they do not short-circuit.
Occasionally you want to evaluate both operands regardless, so it is legal, tho rare to use them on boolean expressions with side-effects.

 
Campbell Ritchie
Marshal
Posts: 80120
414
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:. . . the JLS in 3.10.5 does not show or say what happens in this case:
. . .

That may mean that the behaviour isn't strictly defined and might change in future versions. Just as the constant pool has moved.

Occasionally you want to evaluate both operands regardless, so it is legal, tho rare to use them on boolean expressions with side-effects.

You mean we should avoid side‑effects anyway?
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:

Jesse Silverman wrote:. . . the JLS in 3.10.5 does not show or say what happens in this case:
. . .

That may mean that the behaviour isn't strictly defined and might change in future versions. Just as the constant pool has moved.



Good point.  There have been a number of changes in internal String class behavior that normal programs shouldn't even notice.  This being one of the most subtle.

Campbell Ritchie wrote:

Jesse Silverman wrote:Occasionally you want to evaluate both operands regardless, so it is legal, tho rare to use them on boolean expressions with side-effects.

You mean we should avoid side‑effects anyway?


If both are already computed variables it makes no difference.  When the second is an expression with an important side effect, they are completely different logic flows.
And, well , THIS happened:
https://www.theverge.com/2021/7/23/22590425/chrome-os-release-bricked-ampersand-typo-reddit-update

I always think of Google as being chock full of people who might not be better human beings than me, but are fantastic programmers.
This is the kind of bug I have found and fixed on other people's code for 30 years now...
How the heck did that get all the way thru everything?  It sounds like some kind of significant hole in testing.
 
Campbell Ritchie
Marshal
Posts: 80120
414
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Careful with quote tags; it looked as if I had sais what you said. I have taken the liberty of correcting that.

The && vs & problem goes to show you how difficult programming can be.
 
I’m tired of walking, and will rest for a minute and grow some wheels. This is the promise of this tiny ad:
Smokeless wood heat with a rocket mass heater
https://woodheat.net
reply
    Bookmark Topic Watch Topic
  • New Topic