java - জাভা "পাস বাই রেফারেন্স" বা "পাস বাই বাই মান"?




methods parameter-passing (20)

আমি সবসময় জাভা পাস দ্বারা রেফারেন্স ছিল

যাইহোক, আমি কয়েকটি ব্লগ পোস্ট দেখেছি (উদাহরণস্বরূপ, এই ব্লগ ) যা দাবি করে না।

আমার মনে হয় না তারা যে পার্থক্য তৈরি করছে তা আমি বুঝি।

ব্যাখ্যা কি?


প্রতিনিধিত্ব করার সময় একটি রেফারেন্স সর্বদা একটি মান, আপনি কোন ভাষা ব্যবহার করেন তা কোন ব্যাপার না।

বক্স ভিউয়ের বাহিরে আসুন, এসেম্বলি বা কিছু নিম্ন স্তরের মেমরি পরিচালনা দেখি। সিপিইউ স্তরে CPU এর কোনও রেফারেন্স মেমরিতে বা সিপিইউ রেজিস্ট্রারগুলিতে লিখিত থাকলে তা অবিলম্বে একটি মান হয়ে যায় । (কেন পয়েন্টার একটি ভাল সংজ্ঞা। এটি একটি মান, যা একই সময়ে একটি উদ্দেশ্য আছে)।

মেমরিতে ডেটা একটি অবস্থান আছে এবং সেই অবস্থানে একটি মান (বাইট, শব্দ, যাই হোক না কেন) রয়েছে। সভায় আমরা দিতে একটি সুবিধাজনক সমাধান আছে নাম নির্দিষ্ট করতে অবস্থান (পরিবর্তনশীল ওরফে), কিন্তু যখন কোড কম্পাইল, প্রতীকী ভাষান্তর কেবল প্রতিস্থাপন নাম মনোনীত অবস্থান ঠিক আপনার ব্রাউজার IP ঠিকানা দিয়ে ডোমেইন নাম প্রতিস্থাপন সঙ্গে।

মূলত এটির প্রতিনিধিত্ব ব্যতীত যেকোন ভাষায় কোনও রেফারেন্স পাস করা টেকনিক্যালি অসম্ভব (যখন এটি অবিলম্বে একটি মান হয়ে যায়)।

চলুন আমরা একটি পরিবর্তনশীল Foo আছে, তার অবস্থানটি মেমরির 47 তম বাইটে এবং এর মান 5। আমাদের অন্য একটি পরিবর্তনশীল Ref2Foo যা মেমরিতে 223 বাইটে রয়েছে এবং এর মূল্য 47 হবে। এই Ref2Foo একটি প্রযুক্তিগত পরিবর্তনশীল হতে পারে স্পষ্টভাবে প্রোগ্রাম দ্বারা তৈরি করা হয় না। যদি আপনি অন্য কোন তথ্য ছাড়া 5 এবং 47 তাকান, আপনি শুধুমাত্র দুটি মান দেখতে পাবেন । যদি আপনি রেফারেন্স হিসাবে তাদের ব্যবহার করেন তবে 5আমাদের ভ্রমণ করতে হবে:

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

এই কিভাবে লাফ-টেবিল কাজ।

আমরা যদি Foo এর মান সহ একটি পদ্ধতি / ফাংশন / পদ্ধতি কল করতে চাই তবে ভাষা এবং তার বিভিন্ন পদ্ধতির ইনভেস্টেশন মোডের উপর নির্ভর করে পদ্ধতিতে ভেরিয়েবলটি পাস করার কয়েকটি সম্ভাব্য উপায় রয়েছে :

  1. 5 সিপিপি নিবন্ধকদের (যেমন EAX) অনুলিপি করা হয়।
  2. 5 স্ট্যাক থেকে PUSHD পায়।
  3. 47 টি সিপিপি রেজিস্ট্রারের একটিতে অনুলিপি করে
  4. 47 স্ট্যাক থেকে PUSHD।
  5. ২২3 টি সিপিইউ রেজিস্ট্রারের একটিতে অনুলিপি করে।
  6. 223 স্ট্যাক থেকে PUSHD পায়।

প্রতিটি ক্ষেত্রে একটি মানের উপরে - বিদ্যমান মানের একটি অনুলিপি তৈরি করা হয়েছে, এটি এখন এটি পরিচালনা করার জন্য প্রাপ্তির পদ্ধতি পর্যন্ত। যখন আপনি পদ্ধতির ভিতরে "ফু" লিখেন, এটি ইইএক্স থেকে পড়ানো হয়, অথবা স্বয়ংক্রিয়ভাবে ডিরেকেন্স করা হয় , বা ডাবল ডিফারেন্স করা হয়, প্রক্রিয়াটি কীভাবে ভাষা কাজ করে এবং / বা কী ধরনের Foo dictates তা নির্ভর করে। ডেভেলপারিং প্রক্রিয়ার circumvents পর্যন্ত এটি বিকাশকারী থেকে লুকানো হয়। সুতরাং একটি রেফারেন্স একটি প্রতিনিধিত্ব করা হয় যখন মান , কারণ একটি রেফারেন্স একটি মান যে প্রক্রিয়া করা উচিত (ভাষা স্তর)।

এখন আমরা পদ্ধতিতে Foo পাস করেছেন:

  • ক্ষেত্রে 1. এবং 2. যদি আপনি Foo ( Foo = 9) পরিবর্তন করেন তবে এটি কেবল স্থানীয় সুযোগকে প্রভাবিত করে কারণ আপনার কাছে মূল্যের অনুলিপি রয়েছে। পদ্ধতির ভিতর থেকে আমরা মূল Foo যেখানে মেমরি মধ্যে অবস্থিত ছিল তা নির্ধারণ করতে পারে না।
  • ক্ষেত্রে 3. এবং 4. যদি আপনি ডিফল্ট ভাষা গঠনগুলি ব্যবহার করেন এবং Foo ( Foo = 11) পরিবর্তন করেন তবে এটি বিশ্বব্যাপী Foo পরিবর্তন করতে পারে (ভাষা, যেমন জাভা বা প্যাস্কালের procedure findMin(x, y, z: integer; var m এর মত : integer);)। তবে যদি আপনি যে ভাষায় ডি-রেফারেন্স প্রক্রিয়া পাশকাটিয়ে করার অনুমতি দেয়, আপনি পরিবর্তন করতে পারেন 47, বলছি 49। আপনি যদি এটি পড়েন তবে সেই সময়ে Foo পরিবর্তন করা হয়েছে বলে মনে হচ্ছে, কারণ আপনি স্থানীয় পয়েন্টারটি পরিবর্তন করেছেন । এবং যদি আপনি এই ফোকে পদ্ধতির ভিতরে সংশোধন করতে চান ( Foo = 12) আপনি সম্ভবত প্রোগ্রামটি চালানো (উর। সিগফault) ফুরিয়ে যাবেন কারণ আপনি প্রত্যাশিত চেয়ে একটি ভিন্ন মেমরিতে লিখবেন, আপনি এমন কোনও এলাকায় সংশোধন করতে পারেন যা কার্যকর করতে সক্ষম প্রোগ্রাম এবং এটি লেখা চলমান কোড পরিবর্তন করা হবে (Foo এখন নেই 47)। কিন্তু Foo এর মান47বিশ্বব্যাপী পরিবর্তন হয়নি, শুধুমাত্র পদ্ধতির মধ্যে একটি, কারণ পদ্ধতির 47একটি অনুলিপিও ছিল।
  • ক্ষেত্রে 5. এবং 6. যদি আপনি 223পদ্ধতির ভিতরে সংশোধন করেন তবে এটি একই ময়মনকে 3. বা 4 হিসাবে তৈরি করে। (একটি পয়েন্টার যা এখন একটি খারাপ মানের দিকে নির্দেশ করে যা আবার পয়েন্টার হিসেবে ব্যবহার করা হয়) তবে এটি এখনও একটি স্থানীয় সমস্যা, হিসাবে 223 অনুলিপি করা হয়েছিল । যাইহোক, যদি আপনি ডিরেকেন্স Ref2Foo(যেটি হয় 223) তে পৌঁছতে সক্ষম হন, তীক্ষ্ণ মানতে পৌঁছতে এবং সংশোধন করতে পারেন 47, বলে 49, এটি বিশ্বব্যাপী Foo প্রভাবিত করবে , কারণ এই ক্ষেত্রে এই পদ্ধতিগুলির একটি অনুলিপি পেয়েছে 223তবে রেফারেন্স 47শুধুমাত্র একবারই বিদ্যমান এবং এটি পরিবর্তন থেকে 49যে নেতৃত্ব দেবেন Ref2Fooভুল মান ডাবল dereferencing।

অসম্পূর্ণ বিবরণে নিটপিকিং, পাস-রেফারেন্স করে এমন ভাষাগুলিও কার্যগুলিতে কার্যগুলি পাস করবে, তবে সেই ফাংশনগুলি জানবে যে তাদের এটি ডিফারেন্সিং উদ্দেশ্যে ব্যবহার করতে হবে। এই পাস-রেফারেন্স-এ-মানটি প্রোগ্রামার থেকে লুকানো রয়েছে কারণ এটি কার্যত নিরর্থক এবং পরিভাষা শুধুমাত্র পাস-রেফারেন্স

কঠোর পাস-বাই- মানটিও নিরর্থক, এর অর্থ হল যে 100 বার মাইটাইট অ্যারে প্রতিলিপি করা উচিত যখন আমরা অ্যারের সাথে একটি পদ্ধতি হিসাবে কল করি, তাই জাভা কঠোরভাবে পাস-বাই-মান হতে পারে না। প্রতিটি ভাষা এই বিশাল অ্যারের (একটি মান হিসাবে) একটি রেফারেন্স পাস করবে এবং কপি-অন-রাইট মেকানিজমটি প্রয়োগ করবে যদি ঐ অ্যারে পদ্ধতিতে স্থানীয়ভাবে পরিবর্তিত হতে পারে অথবা বিশ্বব্যাপী অ্যারে সংশোধন করতে (জাভা হিসাবে) কলারের ভিউ) এবং কয়েকটি ভাষা রেফারেন্সের মান পরিবর্তন করতে পারবেন।

তাই স্বল্পমেয়াদী এবং জাভা এর নিজস্ব পরিভাষায়, জাভা পাস-বাই-মান যেখানে মান হতে পারে: হয় একটি প্রকৃত মান বা একটি মান যা একটি রেফারেন্স উপস্থাপনা


আমি "পাস-বাই-রেফারেন্স বনাম পাস-বাই-মান" সম্পর্কে বিতর্ক করার মতো সুপার-সহায়ক নয়।

আপনি যদি বলেন, "জাভা পাস-বাই-যাই হোক না কেন (রেফারেন্স / মান)", উভয় ক্ষেত্রেই, আপনি একটি সম্পূর্ণ উত্তর সরবরাহ করেন না। এখানে কিছু অতিরিক্ত তথ্য যা আশার সাথে স্মৃতিতে কী ঘটছে তা বুঝতে সাহায্য করবে।

আমরা জাভা বাস্তবায়নে যাওয়ার আগে স্ট্যাক / হিপের ক্র্যাশ কোর্স: একটি ক্যাফেটেরিয়াতে প্লেটগুলির স্ট্যাকের মতো মানগুলি একটি সুন্দর অর্ডিন্য ফ্যাশনতে স্ট্যাকের উপর এবং বন্ধ করে। হিপের মেমরি (ডায়নামিক স্মৃতি হিসাবেও পরিচিত) হঠাৎ এবং অসংগঠিত। JVM যেখানেই এটি করতে পারে সেখানে স্থান খুঁজে পায় এবং এটি ব্যবহার করে যে ভেরিয়েবলগুলি এটি ব্যবহার করে সেগুলি আর প্রয়োজন হয় না।

ঠিক আছে. প্রথম বন্ধ, স্থানীয় primitives স্ট্যাক যান। সুতরাং এই কোড:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

এই ফলাফল:

যখন আপনি ঘোষণা এবং একটি বস্তু তাত্ক্ষণিক। প্রকৃত বস্তু হিপ উপর যায়। স্ট্যাক কি যায়? হিপ উপর বস্তুর ঠিকানা। সি ++ প্রোগ্রামাররা এটি একটি পয়েন্টারকে কল করবে, তবে কিছু জাভা বিকাশকারী "পয়েন্টার" শব্দটির বিরুদ্ধে। যাই হোক. শুধু বস্তুর ঠিকানা স্ট্যাক উপর যায় যে জানি।

তাই ভালো:

int problems = 99;
String name = "Jay-Z";

একটি অ্যারে একটি বস্তু, তাই এটি হিপ উপর পাশাপাশি যায়। এবং অ্যারে বস্তুর সম্পর্কে কি? তারা তাদের নিজস্ব হিপ স্পেস পায়, এবং প্রতিটি বস্তুর ঠিকানা অ্যারের ভিতরে যায়।

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

সুতরাং, যখন আপনি একটি পদ্ধতি কল যখন পাস পায়? যদি আপনি একটি বস্তুর মধ্যে পাস, আপনি আসলে কি পাস করছি বস্তুর ঠিকানা। কেউ কেউ ঠিকানাটির "মান" বলতে পারে এবং কিছু বলে যে এটি কেবল বস্তুর একটি রেফারেন্স। এই "রেফারেন্স" এবং "মান" proponents মধ্যে পবিত্র যুদ্ধের উৎপত্তি। আপনি যা বলেন তা গুরুত্বপূর্ণ নয় যে আপনি যা বোঝেন তা হল বস্তুটির ঠিকানা।

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

এক স্ট্রিং তৈরি হয়ে যায় এবং তার জন্য স্থান hisName বরাদ্দ করা হয় এবং স্ট্রিংয়ের ঠিকানা স্ট্যাকে সংরক্ষিত হয় এবং সনাক্তকারীকে তার নাম দেওয়া হয়, দ্বিতীয় hisName ঠিকানাটি প্রথম হিসাবে একই, কোন নতুন স্ট্রিং তৈরি করা হয় না এবং কোনও নতুন হিপ স্থান বরাদ্দ করা হয় না, তবে স্ট্যাকের উপর একটি নতুন সনাক্তকারী তৈরি করা হয়। তারপর আমরা shout() : একটি নতুন স্ট্যাক ফ্রেম তৈরি করা হয় এবং একটি নতুন সনাক্তকারী, name তৈরি এবং ইতিমধ্যে বিদ্যমান স্ট্রিং ঠিকানা নির্ধারিত।

সুতরাং, মূল্য, রেফারেন্স? আপনি "আলু" বলুন।


জাভা মান দ্বারা রেফারেন্স পাস।

সুতরাং আপনি পাস পায় যে রেফারেন্স পরিবর্তন করতে পারবেন না।


জাভা সবসময় পাস দ্বারা মান হয় । দুর্ভাগ্যবশত, তারা একটি বস্তুর অবস্থান একটি "রেফারেন্স" কল করার সিদ্ধান্ত নিয়েছে। যখন আমরা কোন বস্তুর মান পাস করি, তখন আমরা রেফারেন্সটি পাস করি। এই beginners বিভ্রান্তিকর হয়।

এটা এইভাবেই চলে:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

aDog.getName() উপরের উদাহরণে এখনও "Max" ফিরে আসবে। aDog মধ্যে aDog মানটি ফাংশন foo পরিবর্তিত হয় না "Fifi" Dog সাথে বস্তুর রেফারেন্স মান দ্বারা প্রেরিত হয়। যদি এটি রেফারেন্স দ্বারা প্রেরিত হয়, তবে aDog.getName() কল করার পরে "Fifi" ফিরে আসবে।

অনুরূপভাবে:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

উপরের উদাহরণে, foo(aDog) কল করার পরে কুকুরের নাম কুকুরের নাম, কারণ বস্তুর নাম foo(...) ভিতরে সেট করা হয়েছিল। d তে সঞ্চালিত যে কোনও অপারেশনগুলি হল যে, সমস্ত বাস্তব উদ্দেশ্যে, তারা aDog তে নিজেই সঞ্চালিত হয় ( aDog পরিবর্তিত হলে d = new Dog("Boxer") মতো Dog উদাহরণের পরিবর্তে d পরিবর্তিত হয়।


জাভা সবসময় রেফারেন্স দ্বারা মান দ্বারা আর্গুমেন্ট পাস।

আমাকে একটি example দিয়ে এই ব্যাখ্যা করা যাক:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

আমি ধাপে এই ব্যাখ্যা করব:

  1. f -type Foo নামক একটি রেফারেন্স ঘোষণা করা এবং এটি "f" একটি বৈশিষ্ট্য সহ Foo এর একটি নতুন বস্তুতে বরাদ্দ করুন।

    Foo f = new Foo("f");
    

  2. পদ্ধতির দিক থেকে, একটি নাম দিয়ে Foo টাইপের একটি রেফারেন্স ঘোষিত হয় এবং এটি প্রাথমিকভাবে নিল হিসাবে নির্ধারিত হয়।

    public static void changeReference(Foo a)
    

  3. আপনি পদ্ধতি changeReference কল হিসাবে উল্লেখ, রেফারেন্স একটি আর্গুমেন্ট হিসাবে গৃহীত বস্তু বরাদ্দ করা হবে।

    changeReference(f);
    

  4. b type Foo নামক একটি রেফারেন্স ঘোষণা করে এবং এটি "b" একটি বৈশিষ্ট্য সহ Foo এর একটি নতুন বস্তুতে বরাদ্দ করুন।

    Foo b = new Foo("b");
    

  5. a = b রেফারেন্সটিকে বস্তুটির a নং f পুনরায় বরাদ্দ করছে যার বৈশিষ্ট্যটি "b"

  6. আপনি modifyReference(Foo c) রেফারেন্স modifyReference(Foo c) পদ্ধতি হিসাবে কল modifyReference(Foo c) , একটি রেফারেন্স c তৈরি করা হয় এবং "f" বৈশিষ্ট্য সহ বস্তুর জন্য বরাদ্দ করা হয়।

  7. c.setAttribute("c"); বস্তুর বৈশিষ্ট্যটি পরিবর্তন করবে যা এটিতে c পয়েন্ট উল্লেখ করে এবং এটি একই বস্তু যা এটিকে f পয়েন্ট উল্লেখ করে।

আমি আশা করি আপনি এখন বুঝতে পারবেন কিভাবে আর্গুমেন্ট হিসাবে বস্তুগুলি জাভাতে কাজ করে :)


শুধু বিপরীতে দেখানোর জন্য নিচের C++ এবং Java স্নিপেটগুলি তুলনা করুন:

সি ++ এ: দ্রষ্টব্য: খারাপ কোড - মেমরি লিক! কিন্তু এটা বিন্দু প্রদর্শন করে।

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

জাভা ইন,

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

জাভাটিতে কেবল দুটি প্রকারের পাস রয়েছে: অন্তর্নির্মিত ধরনের জন্য মান এবং বস্তুর ধরনগুলির জন্য পয়েন্টারের মান দ্বারা।


জাভা শুধুমাত্র রেফারেন্স পাস এবং মান দ্বারা পাস করা হয়:

জাভা আর্গুমেন্টগুলি মান দ্বারা পাস করা হয় (পদ্ধতি দ্বারা ব্যবহৃত হলে রেফারেন্সটি অনুলিপি করা হয়):

আদিম প্রকারের ক্ষেত্রে, জাভা আচরণটি সহজ: মানটি আদমশুমারের অন্য উদাহরণে অনুলিপি করা হয়।

বস্তুর ক্ষেত্রে, এটি একই: বস্তুর ভেরিয়েবলগুলি হল পয়েন্টার (বালতি) যা শুধুমাত্র "নতুন" শব্দটি ব্যবহার করে তৈরি করা বস্তুর ঠিকানা ধারণ করে এবং প্রবীণ প্রকারের মত অনুলিপি করা হয়।

আচরণটি আদিম প্রকারের থেকে আলাদা হতে পারে: কারণ অনুলিপি করা বস্তুর ভেরিয়েবলটিতে একই ঠিকানা (একই বস্তুতে) অবজেক্টের সামগ্রী / সদস্যগুলি এখনও একটি পদ্ধতিতে সংশোধন করা যেতে পারে এবং পরে বাইরে অ্যাক্সেস করা যেতে পারে, যা বিভ্রান্তির (ধারণকারী) নিজেই রেফারেন্স দ্বারা গৃহীত হয়।

"স্ট্রিং" অবজেক্টগুলি শহুরে কিংবদন্তীর কাছে নিখুঁত পাল্টা উদাহরণ বলে মনে হচ্ছে যে "অবজেক্টগুলি রেফারেন্স দ্বারা পাস করা হয়েছে":

ফলস্বরূপ, একটি পদ্ধতির মধ্যে আপনি কোনও আর্গুমেন্ট হিসাবে গৃহীত একটি স্ট্রিংয়ের মান আপডেট করতে পারবেন না:

একটি স্ট্রিং অবজেক্ট, একটি অ্যারে দ্বারা চরিত্রগুলিকে ধরে রাখে চূড়ান্ত ঘোষিত যা সংশোধন করা যাবে না। শুধুমাত্র "নতুন" ব্যবহার করে বস্তুর ঠিকানাটি অন্যের দ্বারা প্রতিস্থাপিত হতে পারে। পরিবর্তনশীল আপডেট করার জন্য "নতুন" ব্যবহার করে, অবজেক্টটি বাইরে থেকে অ্যাক্সেস করা যাবে না, কারণ পরিবর্তনশীলটি প্রথমে মান এবং কপি করে প্রেরিত হয়েছিল।


জাভা সবসময় মান দ্বারা পাস, রেফারেন্স দ্বারা পাস না

সর্বপ্রথম, আমাদের বোঝার দরকার কী মান দ্বারা পাস এবং রেফারেন্স দ্বারা পাস।

মূল্য দ্বারা পাস মানে আপনি যে প্রকৃত প্যারামিটারের মানটি পাস করেছেন তার স্মৃতিতে একটি অনুলিপি তৈরি করছেন। এটি প্রকৃত প্যারামিটারের সামগ্রীর একটি অনুলিপি

রেফারেন্স (এছাড়াও ঠিকানা দ্বারা পাস বলা হয়) দ্বারা পাস মানে প্রকৃত পরামিতির ঠিকানা একটি কপি সংরক্ষণ করা হয়

কখনও কখনও জাভা রেফারেন্স দ্বারা পাস বিভ্রম দিতে পারেন। আসুন নিচের উদাহরণটি ব্যবহার করে কিভাবে এটি কাজ করে দেখুন:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

এই প্রোগ্রামের আউটপুট হয়:

changevalue

আসুন ধাপে ধাপে বুঝতে পারি:

Test t = new Test();

আমরা সবাই জানি যে এটি হিপের মধ্যে একটি বস্তু তৈরি করবে এবং রেফারেন্স মানটি টিতে ফেরত পাঠাবে। উদাহরণস্বরূপ, ধরুন টিটির মান 0x100234(আমরা প্রকৃত JVM অভ্যন্তরীণ মানটি জানি না, এটি কেবল একটি উদাহরণ)।

new PassByValue().changeValue(t);

ফাংশনে টি রেফারেন্স টি পাস করার সময় এটি সরাসরি বস্তুর পরীক্ষাটির প্রকৃত রেফারেন্স মানটি পাস করবে না, তবে এটি টি একটি অনুলিপি তৈরি করবে এবং তারপরে ফাংশনে এটি প্রেরণ করবে। যেহেতু এটি মান দ্বারা প্রেরণ করা হচ্ছে , তাই এটি প্রকৃত রেফারেন্সের পরিবর্তে পরিবর্তনশীল একটি কপি পাস করে। যেহেতু আমরা বলেছি টিটির মান ছিল 0x100234, টি এবং f উভয়ের একই মান থাকবে এবং তাই তারা একই বস্তুর দিকে নির্দেশ করবে।

যদি আপনি রেফারেন্স f ব্যবহার করে ফাংশনে কিছু পরিবর্তন করেন তবে এটি বস্তুর বিদ্যমান সামগ্রী সংশোধন করবে। আমরা আউটপুট পেয়েছি changevalue, যা ফাংশন আপডেট করা হয়।

এই আরো পরিষ্কারভাবে বুঝতে, নিম্নলিখিত উদাহরণ বিবেচনা করুন:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

এই একটি নিক্ষেপ করা হবে NullPointerException? না, কারণ এটি কেবল রেফারেন্সের একটি কপি পাস করে। রেফারেন্স দ্বারা পাস করার ক্ষেত্রে, এটি NullPointerExceptionনিচে দেখানো হয়েছে, যেমন একটি নিক্ষেপ করা হতে পারে :

আশা করি এই সাহায্য করবে।


কিছু পোস্ট কয়েক সংশোধন।

সি রেফারেন্স দ্বারা পাস সমর্থন করে না। এটা সবসময় মান দ্বারা পাস করা হয়। C ++ রেফারেন্স দ্বারা পাস সমর্থন করে, কিন্তু ডিফল্ট নয় এবং বেশ বিপজ্জনক।

এটি জাভাতে মানটির কোনও ব্যাপার নয়: প্রাথমিক বা ঠিকানা (মোটামুটি) বস্তুর, এটি সর্বদা মান দ্বারা গৃহীত হয়।

যদি কোনও জাভা বস্তু রেফারেন্স দ্বারা পাস করা হয় তবে "আচরণ করে", এটি পরিবর্তনযোগ্যতার একটি সম্পত্তি এবং ক্ষণস্থায়ী প্রক্রিয়াগুলির সাথে পুরোপুরি কিছুই করার নেই।

আমি নিশ্চিত নই কেন এটি এত বিভ্রান্তিকর, সম্ভবত অনেক জাভা "প্রোগ্রামার্স" আনুষ্ঠানিকভাবে প্রশিক্ষিত না হয় এবং এভাবে বুঝতে পারছেন না যে আসলেই কি স্মৃতিতে চলছে?


জাভা মান দ্বারা একটি কল।

কিভাবে এটা কাজ করে

  • আপনি সবসময় রেফারেন্স মান বিট একটি কপি পাস!

  • যদি এটি একটি আদিম ডেটা টাইপ থাকে তবে এই বিটগুলি আদিম ডেটা টাইপের মান ধারণ করে, তাই যদি আমরা পদ্ধতির মধ্যে শিরোনামের মান পরিবর্তন করি তবে এটি বাইরে পরিবর্তনগুলিকে প্রতিফলিত করে না।

  • যদি এটি একটি বস্তুর তথ্য টাইপ Foo foo = new Foo () এর মতো হয় তবে এই ক্ষেত্রে বস্তুর ঠিকানার অনুলিপি ফাইল শর্টকাটের মত পাস করে, ধরুন আমাদের কাছে C: \ ডেস্কটপে একটি পাঠ্য ফাইল abc.txt আছে এবং আমরা শর্টকাটটি তৈরি করি একই ফাইলটি C: \ ডেস্কটপ \ abc-শর্টকাট রাখুন যাতে আপনি C: \ ডেস্কটপ \ abc.txt থেকে ফাইলটি অ্যাক্সেস করেন এবং 'স্ট্যাক ওভারফ্লো' লিখুন এবং ফাইলটি বন্ধ করুন এবং আবার আপনি শর্টকাট থেকে ফাইলটি খুলুন। লিখুন 'প্রোগ্রামাররা শিখতে বৃহত্তমতম অনলাইন কমিউনিটি ' তাহলে মোট ফাইল পরিবর্তন হবে 'স্ট্যাক ওভারফ্লো প্রোগ্রামারদের জন্য সবচেয়ে বড় অনলাইন কমিউনিটি'যার মানে এটি কোনও ব্যাপার না যে আপনি যেখানে ফাইলটি খুলছেন সেখান থেকে কোনও ব্যাপার নেই, প্রতিটি সময় আমরা একই ফাইলটি অ্যাক্সেস করছি, এখানে আমরা একটি ফাইল হিসাবে Foo অনুমান করতে পারি এবং মনে করি foo 123HD7h (আসল ঠিকানা যেমন C: \ ডেস্কটপ \ abc.txt ) এ সংরক্ষিত রয়েছে। ঠিকানা এবং 234jdid ( C: \ ডেস্কটপ \ abc- শর্টকাট মত কপি করা ঠিকানা যা প্রকৃতপক্ষে ভেতরের ফাইলের আসল ঠিকানা ধারণ করে) .. সুতরাং ভাল বোঝার জন্য শর্টকাট ফাইল তৈরি করুন এবং মনে করুন ...


না, এটা রেফারেন্স দ্বারা পাস করা হয় না।

জাভা জাভা ভাষা স্পেসিফিকেশন অনুযায়ী মান দ্বারা পাস করা হয়:

যখন পদ্ধতি বা কন্সট্রাকটরকে (§15.12) আহ্বান করা হয় , তখন প্রকৃত আর্গুমেন্ট এক্সপ্রেশনগুলির মানগুলি নতুন তৈরি প্যারামিটার ভেরিয়েবলগুলি , ঘোষিত ধরনগুলির প্রতিটি, পদ্ধতির বা গঠনকারীর শরীরের নির্বাহের পূর্বে। DeclaratorId এ প্রদর্শিত আইডেন্টিফায়ারটি আনুষ্ঠানিক প্যারামিটারটি উল্লেখ করার জন্য পদ্ধতি বা কন্সট্রকটরের শরীরের একটি সাধারণ নাম হিসাবে ব্যবহার করা যেতে পারে ।


মূলত, অবজেক্ট প্যারামিটার পুনরায় অ্যাসাইনিং যুক্তি প্রভাবিত করে না, উদাহরণস্বরূপ,

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

এর "Hah!"পরিবর্তে মুদ্রণ করা হবে null। এই কাজটি কারন কারণ barএটির একটি কপি bazযা কেবল একটি রেফারেন্স"Hah!" ।যদি এটি প্রকৃত রেফারেন্স নিজেই হয়, তাহলে fooএটি পুনরায় সংজ্ঞায়িত bazকরা হবে null


আপনি জাভাতে রেফারেন্স দ্বারা কখনো পাস করতে পারবেন না, এবং কোনও পদ্ধতির মধ্যে একটি পদ্ধতি হল যখন আপনি একটি পদ্ধতি কল থেকে একাধিক মান ফেরত দিতে চান। C ++ এ কোডের নিচের বিট বিবেচনা করুন:

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

কখনও কখনও আপনি জাভা একই প্যাটার্ন ব্যবহার করতে চান, কিন্তু আপনি করতে পারেন না; অন্তত সরাসরি না। পরিবর্তে আপনি এই মত কিছু করতে পারে:

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

পূর্ববর্তী উত্তরগুলিতে ব্যাখ্যা করা হয়েছে, জাভাতে আপনি একটি অ্যারেতে একটি পয়েন্টার হিসাবে মূল্য প্রেরণ করছেন getValues। এটি যথেষ্ট, কারণ পদ্ধতিটি অ্যারের উপাদানটি সংশোধন করে এবং কনভেনশন অনুসারে আপনি প্রত্যাবর্তন মূল্য ধারণ করার জন্য উপাদান 0 টি প্রত্যাশা করছেন। স্পষ্টতই আপনি অন্যান্য কোডগুলিতে এটি করতে পারেন যেমন আপনার কোডটি গঠন করা, তাই এটি প্রয়োজনীয় নয়, অথবা এমন একটি ক্লাস তৈরি করা যা রিটার্ন মান ধারণ করতে পারে বা এটি সেট করার মঞ্জুরি দেয়। কিন্তু উপরের সি ++ এ আপনার জন্য উপলব্ধ সহজ প্যাটার্ন জাভা পাওয়া যায় না।


আমাকে চারটি উদাহরণের সাহায্যে আমার বোঝার ব্যাখ্যা করার চেষ্টা করুন। জাভা পাস-বাই-মান, এবং পাস-বাই-রেফারেন্স নয়

/ **

মূল্য দ্বারা পাস

জাভাতে, সমস্ত প্যারামিটার মূল্য দ্বারা পাস করা হয়, অর্থাৎ একটি পদ্ধতির যুক্তি বরাদ্দ করাটি কলারের কাছে দৃশ্যমান নয়।

* /

উদাহরণ 1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

ফল

output : output
value : Nikhil
valueflag : false

উদাহরণ 2:

/ ** * * মান দ্বারা পাস * * /

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

ফল

output : output
value : Nikhil
valueflag : false

উদাহরণ 3:

/ ** এই 'মান দ্বারা পাস' রেফারেন্স দ্বারা পাস অনুভূতি আছে '

কিছু লোক আদিম প্রকার এবং 'স্ট্রিং' 'মান দ্বারা পাস করে' বলে এবং বস্তু 'রেফারেন্স দ্বারা পাস করে' বলে।

কিন্তু এই উদাহরণ থেকে, আমরা বুঝতে পারি যে এটি মান দ্বারা প্রকৃতপক্ষে পাস হয়, মনে রাখবেন যে আমরা এখানে মান হিসাবে রেফারেন্সটি পাস করছি। অর্থাৎ: রেফারেন্স মান দ্বারা পাস করা হয়। এ কারণে স্থানীয় সুযোগের পরিবর্তে এটি পরিবর্তন করতে সক্ষম এবং এখনও এটি সত্য ধারণ করে। কিন্তু আমরা মূল সুযোগ বাইরে প্রকৃত রেফারেন্স পরিবর্তন করতে পারবেন না। এর মানে কি বোঝায় পাসবইভালিউবাইটসেস 2 এর পরবর্তী উদাহরণ দ্বারা।

* /

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

ফল

output : output
student : Student [id=10, name=Anand]

উদাহরণ 4:

/ **

উদাহরণ 3 (PassByValueObjectCase1.java) এ উল্লেখ করা ছাড়াও, আমরা মূল সুযোগের বাইরে প্রকৃত রেফারেন্স পরিবর্তন করতে পারি না। "

নোট: আমি কোড পেস্ট করছি না private class StudentStudentউদাহরণ 3 হিসাবে ক্লাস সংজ্ঞা একই।

* /

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let's see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

ফল

output : output
student : Student [id=10, name=Nikhil]

আমি এখনো বিশ্বাস করি না যে বারবারা লিস্কোভ এখনো কেউ উল্লেখ করেনি। অতঃপর যখন তাকে 1974 সালে CLU পরিকল্পিত, সে এই একই পরিভাষা সমস্যাতে পড়েছি এবং তিনি শব্দটি উদ্ভাবন ভাগ করে কল (নামেও পরিচিত অবজেক্ট শেয়ারিং দ্বারা কল এবং বস্তু দ্বারা কল মান) "কলের এই নির্দিষ্ট ক্ষেত্রে জন্য যেখানে মান একটি রেফারেন্স "।


আমি সর্বদা "কপি দ্বারা পাস" হিসাবে মনে করি। এটি মান একটি কপি এটি আদিম বা রেফারেন্স হতে। যদি এটি একটি আদিম হয় তবে এটি বিটগুলির একটি অনুলিপি যা মান এবং যদি এটি একটি বস্তু হয় তবে এটি রেফারেন্সের একটি অনুলিপি।

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

জাভা PassByCopy আউটপুট:

নাম = Maxx
নাম = Fido

প্রাইম্যাটিক wrapper ক্লাস এবং স্ট্রিং অপরিবর্তনীয় হয় তাই যে ধরনের ব্যবহার করে যে কোনো উদাহরণ অন্যান্য ধরনের / বস্তু হিসাবে একই কাজ করবে না।


জাভা VALUE দ্বারা পরামিতি পাস করে এবং কেবল মান দ্বারা ।

দীর্ঘ গল্প সংক্ষিপ্ত কাটা:

সি # থেকে আসার জন্য: সেখানে কোন "আউট" পরামিতি নেই।

Pascal থেকে আসছে জন্য: সেখানে কোন "var" পরামিতি হয়

এর অর্থ আপনি বস্তুর থেকে রেফারেন্স পরিবর্তন করতে পারবেন না, তবে আপনি সর্বদা বস্তুর বৈশিষ্ট্যগুলি পরিবর্তন করতে পারেন।

একটি workaround StringBuilderপরিবর্তে পরামিতি ব্যবহার করা হয় String। এবং আপনি সবসময় অ্যারে ব্যবহার করতে পারেন!


জাভা শুধুমাত্র মান দ্বারা পাস করা হয়েছে। এই বৈধতা একটি খুব সহজ উদাহরণ।

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

পার্থক্য, অথবা সম্ভবত আমি যা মনে করি তা আমি আসল পোস্টারের মত একই ছাপের অধীনে ছিলাম: জাভা সবসময় মান দ্বারা পাস করে। জাভাতে সমস্ত বস্তু (জাভাতে, প্রাইমটিভ ছাড়া আর কিছু) রেফারেন্স। এই রেফারেন্স মান দ্বারা পাস করা হয়।


বিষয়ের মূল অংশ যে শব্দ রেফারেন্স অভিব্যক্তি "রেফারেন্স দ্বারা পাস" কিছু শব্দের স্বাভাবিক অর্থ থেকে সম্পূর্ণ ভিন্ন অর্থ রেফারেন্স জাভা।

সাধারণত জাভা রেফারেন্স মানে একটি বস্তুর aa রেফারেন্স । কিন্তু প্রযুক্তিগত ভাষাগুলি প্রোগ্রামিং ভাষা তত্ত্ব থেকে রেফারেন্স / মান দ্বারা পাস করে পরিবর্তনশীল ধারণ করে মেমরি সেলের একটি রেফারেন্স সম্পর্কে কথা বলছে , যা কিছু সম্পূর্ণ ভিন্ন।





pass-by-value