私はC
で次のことを行うことに慣れています。
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
出力は次のとおりです。
foo
ただし、Javaでは、これは機能していないようです。 String
オブジェクトは参照渡しではなくコピーであるためだと思います。文字列はオブジェクトであり、常に参照によって渡されると思いました。
ここで何が起こっていますか?
次の3つのオプションがあります。
StringBuilderを使用します。
StringBuilder zText = new StringBuilder ();
void fillString(StringBuilder zText) { zText.append ("foo"); }
コンテナクラスを作成し、コンテナのインスタンスをメソッドに渡します。
public class Container { public String data; }
void fillString(Container c) { c.data += "foo"; }
配列を作成します。
new String[] zText = new String[1];
zText[0] = "";
void fillString(String[] zText) { zText[0] += "foo"; }
パフォーマンスの観点から、通常はStringBuilderが最適なオプションです。
Javaでは、参照によって何も渡されません。すべてが値渡しです。オブジェクト参照は値で渡されます。さらに、文字列は不変です。したがって、渡された文字列に追加すると、新しい文字列が取得されます。戻り値を使用するか、代わりにStringBufferを渡すことができます。
何が起こっているのかというと、参照は値で渡されます。つまり、参照のcopyが渡されます。 Javaの何も参照によって渡されません。また、文字列は不変であるため、その割り当てにより参照のコピーが指す新しい文字列オブジェクトが作成されます。元の参照はまだ空の文字列を指しています。
これはどのオブジェクトでも同じです。つまり、メソッドで新しい値に設定します。以下の例は、何が起こっているかを明確にするだけですが、文字列の連結は実際には同じことです。
void foo( object o )
{
o = new Object( ); // original reference still points to old value on the heap
}
Java.lang.Stringは不変です。
URLの貼り付けは嫌いですが、 https://docs.Oracle.com/javase/10/docs/api/Java/lang/String.html は、あなたが読んでいて、 Javaランド。
オブジェクトは参照によって渡され、プリミティブは値によって渡されます。
文字列はプリミティブではなく、オブジェクトであり、オブジェクトの特殊なケースです。
これは、メモリを節約するためです。 JVMには、文字列プールがあります。作成されたすべての文字列について、JVMは文字列プールに同じ文字列が存在するかどうかを確認し、すでに存在する場合はそれを指し示します。
public class TestString
{
private static String a = "hello world";
private static String b = "hello world";
private static String c = "hello " + "world";
private static String d = new String("hello world");
private static Object o1 = new Object();
private static Object o2 = new Object();
public static void main(String[] args)
{
System.out.println("a==b:"+(a == b));
System.out.println("a==c:"+(a == c));
System.out.println("a==d:"+(a == d));
System.out.println("a.equals(d):"+(a.equals(d)));
System.out.println("o1==o2:"+(o1 == o2));
passString(a);
passString(d);
}
public static void passString(String s)
{
System.out.println("passString:"+(a == s));
}
}
/ *出力* /
a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false
==はメモリアドレス(参照)をチェックし、.equalsは内容(値)をチェックします
Javaのすべての引数は値で渡されます。 String
を関数に渡すと、渡される値はis Stringオブジェクトへの参照ですが、その参照を変更することはできず、基になるStringオブジェクトは不変です。
課題
zText += foo;
以下と同等です:
zText = new String(zText + "foo");
つまり、パラメータzText
を新しい参照として(ローカルで)再割り当てします。この参照は、String
の元のコンテンツを含む新しいzText
である新しいメモリ位置を指します。 "foo"
が追加されました。
元のオブジェクトは変更されず、main()
メソッドのローカル変数zText
は依然として元の(空の)文字列を指します。
class StringFiller {
static void fillString(String zText) {
zText += "foo";
System.out.println("Local value: " + zText);
}
public static void main(String[] args) {
String zText = "";
System.out.println("Original value: " + zText);
fillString(zText);
System.out.println("Final value: " + zText);
}
}
プリント:
Original value:
Local value: foo
Final value:
文字列を変更したい場合は、前述のように、StringBuilder
またはその他のレベルのポインター間接化を提供するコンテナー(配列またはAtomicReference
またはカスタムコンテナークラス)を使用できます。または、新しい値を返して割り当てます。
class StringFiller2 {
static String fillString(String zText) {
zText += "foo";
System.out.println("Local value: " + zText);
return zText;
}
public static void main(String[] args) {
String zText = "";
System.out.println("Original value: " + zText);
zText = fillString(zText);
System.out.println("Final value: " + zText);
}
}
プリント:
Original value:
Local value: foo
Final value: foo
これはおそらく、一般的な場合で最もJavaに似たソリューションです。Effective Java項目「Favor immutability」を参照してください。
ただし、前述のように、StringBuilder
を使用するとパフォーマンスが向上することがよくあります。特にループ内で追加する必要がある場合は、StringBuilder
を使用します。
ただし、可能であれば、可変のStrings
ではなく、不変のStringBuilders
を渡すようにしてください。コードが読みやすく、保守しやすくなります。パラメーターfinal
を作成し、IDEを構成して、メソッドパラメーターを新しい値に再割り当てするときに警告することを検討してください。
文字列はJavaの不変オブジェクトです。次のように、StringBuilderクラスを使用して、目的のジョブを実行できます。
public static void main(String[] args)
{
StringBuilder sb = new StringBuilder("hello, world!");
System.out.println(sb);
foo(sb);
System.out.println(sb);
}
public static void foo(StringBuilder str)
{
str.delete(0, str.length());
str.append("String has been modified");
}
別のオプションは、次のように、文字列をスコープ変数としてクラスを作成することです(非常に推奨されません)。
class MyString
{
public String value;
}
public static void main(String[] args)
{
MyString ms = new MyString();
ms.value = "Hello, World!";
}
public static void foo(MyString str)
{
str.value = "String has been modified";
}
文字列はJavaの特別なクラスです。 「一度Stringインスタンスが作成されると、Stringインスタンスのコンテンツは変更されない」という意味のスレッドセーフです。
ここで何が起こっているのか
zText += "foo";
最初に、JavaコンパイラーはzText Stringインスタンスの値を取得し、次に値が「foo」を付加したzTextである新しいStringインスタンスを作成します。これで、zTextが指すインスタンスが変更されない理由がわかります。これはまったく新しいインスタンスです。実際、String "foo"でさえ新しいStringインスタンスです。そのため、このステートメントでは、Javaは2つのStringインスタンスを作成します。1つは「foo」、もう1つはzTextの値「foo」を追加します。ルールは単純です。Stringインスタンスの値は変更されません。
メソッドfillStringの場合、パラメーターとしてStringBufferを使用するか、次のように変更できます。
String fillString(String zText) {
return zText += "foo";
}
答えは簡単です。 Javaでは、文字列は不変です。したがって、「final」修飾子(またはC/C++の「const」)を使用するようなものです。そのため、割り当てられた後は、自分のように変更することはできません。
which文字列が指す値を変更できますが、この文字列が現在指している実際の値は変更できません。
すなわち。 String s1 = "hey"
。 s1 = "woah"
を作成することはできますが、これはまったく問題ありませんが、plusEqualsなどを使用して割り当てた後、文字列の基になる値(この場合は "hey")を実際に変更することはできません。 。s1 += " whatup != "hey whatup"
)。
これを行うには、StringBuilderクラスまたはStringBufferクラス、またはその他の可変コンテナを使用し、.toString()を呼び出してオブジェクトを文字列に変換し直します。
注:文字列はハッシュキーとしてよく使用されるため、文字列が不変である理由の一部です。
これはStringBufferを使用して動作します
public class test {
public static void main(String[] args) {
StringBuffer zText = new StringBuffer("");
fillString(zText);
System.out.println(zText.toString());
}
static void fillString(StringBuffer zText) {
zText .append("foo");
}
}
さらにStringBuilderを使用する
public class test {
public static void main(String[] args) {
StringBuilder zText = new StringBuilder("");
fillString(zText);
System.out.println(zText.toString());
}
static void fillString(StringBuilder zText) {
zText .append("foo");
}
}
Javaでは文字列は不変です。既存の文字列リテラル/オブジェクトを変更/変更することはできません。
文字列s = "Hello"; s = s + "hi";
ここで、以前の参照は、値 "HelloHi"を指す新しい参照に置き換えられます。
ただし、可変性をもたらすために、StringBuilderとStringBufferがあります。
StringBuilder s = new StringBuilder(); s.append( "Hi");
これにより、新しい参照「Hi」が同じ参照に追加されます。 //
文字列は、Javaでは 不変 です。
Javaで参照によってオブジェクト(Stringを含む)を渡す場合、周囲のアダプターのメンバーとして渡すことができます。ジェネリックを使用したソリューションは次のとおりです。
import Java.io.Serializable;
public class ByRef<T extends Object> implements Serializable
{
private static final long serialVersionUID = 6310102145974374589L;
T v;
public ByRef(T v)
{
this.v = v;
}
public ByRef()
{
v = null;
}
public void set(T nv)
{
v = nv;
}
public T get()
{
return v;
}
// ------------------------------------------------------------------
static void fillString(ByRef<String> zText)
{
zText.set(zText.get() + "foo");
}
public static void main(String args[])
{
final ByRef<String> zText = new ByRef<String>(new String(""));
fillString(zText);
System.out.println(zText.get());
}
}
もっと興味がある人のために
class Testt {
static void Display(String s , String varname){
System.out.println(varname + " variable data = "+ s + " :: address hashmap = " + s.hashCode());
}
static void changeto(String s , String t){
System.out.println("entered function");
Display(s , "s");
s = t ;
Display(s,"s");
System.out.println("exiting function");
}
public static void main(String args[]){
String s = "hi" ;
Display(s,"s");
changeto(s,"bye");
Display(s,"s");
}
}
上記のコードを実行すると、文字列変数sでアドレス
hashcodes
がどのように変化するかを確認できます。 sが変更されると、関数changeto
の変数sに新しいオブジェクトが割り当てられます
これまでのところ、Aaron Digullaが最良の答えを持っています。 2番目のオプションのバリエーションは、commons langライブラリバージョン3+のラッパークラスまたはコンテナクラスMutableObjectを使用することです。
void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }
コンテナクラスの宣言を保存します。欠点は、commons lang libへの依存です。しかし、ライブラリには非常に多くの便利な機能があり、私が取り組んできたほとんどすべての大きなプロジェクトがそれを使用しました。