ตัวแปรแบบไม่คงที่ไม่สามารถอ้างอิงได้จากบริบทแบบคงที่


288

ฉันเขียนโค้ดทดสอบนี้:

class MyProgram
{
    int count = 0;
    public static void main(String[] args)
    {
        System.out.println(count);
    }
}

แต่มันให้ข้อผิดพลาดต่อไปนี้:

Main.java:6: error: non-static variable count cannot be referenced from a static context
        System.out.println(count);
                           ^

ฉันจะทำให้วิธีการของฉันจดจำตัวแปรคลาสได้อย่างไร


พยายามหลีกเลี่ยงการใช้สแตติกเมื่อทำได้ คุณสามารถCเขียนโปรแกรมที่สมบูรณ์แบบคงที่ทั้งหมดเช่นเดียวกับใน แต่มันจะไม่ดีมาก พยายามใช้ Java ตามที่ตั้งใจให้ใช้เป็นภาษาเชิงวัตถุ
Erick G. Hagstrom

คำตอบ:


294

คุณต้องเข้าใจความแตกต่างระหว่างคลาสและอินสแตนซ์ของคลาสนั้น หากคุณเห็นรถยนต์บนถนนคุณจะรู้ได้ทันทีว่าเป็นรถยนต์แม้ว่าคุณจะไม่เห็นรูปแบบหรือประเภทใดก็ตาม นี่เป็นเพราะคุณเปรียบเทียบสิ่งที่คุณเห็นกับรถ " คลาส " ชั้นเรียนมีลักษณะคล้ายกับรถทุกคัน คิดว่ามันเป็นเทมเพลตหรือไอเดีย

ในเวลาเดียวกันรถที่คุณเห็นเป็นตัวอย่างของรถ "ระดับ" เนื่องจากมีคุณสมบัติทั้งหมดที่คุณคาดหวัง: มีใครบางคนกำลังขับขี่มันมีเครื่องยนต์ล้อ

ดังนั้นคลาสพูดว่า "รถยนต์ทุกคันมีสี" และอินสแตนซ์บอกว่า "รถยนต์คันนี้เป็นสีแดง"

ในโลก OO Colorที่คุณกำหนดระดับและภายในชั้นเรียนที่คุณกำหนดเขตของประเภท เมื่อคลาสนั้นอินสแตนซ์ (เมื่อคุณสร้างอินสแตนซ์เฉพาะ), หน่วยความจำจะถูกสงวนไว้สำหรับสีและคุณสามารถให้สีอินสแตนซ์ที่เฉพาะเจาะจงนี้ เนื่องจากคุณสมบัติเหล่านี้มีความเฉพาะเจาะจงจึงไม่คงที่

เขตข้อมูลแบบคงที่และวิธีการที่ใช้ร่วมกันกับทุกกรณี พวกเขามีไว้สำหรับค่าที่เฉพาะเจาะจงกับชั้นเรียนและไม่ใช่อินสแตนซ์ที่เฉพาะเจาะจง สำหรับวิธีการนี้มักจะเป็นวิธีการช่วยเหลือทั่วโลก (เช่นInteger.parseInt()) สำหรับเขตข้อมูลมักจะเป็นค่าคงที่ (เช่นประเภทรถยนต์คือสิ่งที่คุณมีชุด จำกัด ซึ่งไม่เปลี่ยนบ่อย)

ในการแก้ปัญหาของคุณคุณต้องสร้างอินสแตนซ์ (สร้างวัตถุ) ของคลาสของคุณเพื่อให้รันไทม์สามารถจองหน่วยความจำสำหรับอินสแตนซ์ (มิฉะนั้นอินสแตนซ์ที่แตกต่างกันจะเขียนทับกันซึ่งคุณไม่ต้องการ)

ในกรณีของคุณลองใช้รหัสนี้เป็นบล็อกเริ่มต้น:

public static void main (String[] args)
{
    try
    {
        MyProgram7 obj = new MyProgram7 ();
        obj.run (args);
    }
    catch (Exception e)
    {
        e.printStackTrace ();
    }
}

// instance variables here

public void run (String[] args) throws Exception
{
    // put your code here
}

main()วิธีการใหม่จะสร้างอินสแตนซ์ของคลาสที่มีอยู่ (ฟังดูแปลก ๆ แต่เนื่องจากmain()ถูกสร้างด้วยคลาสแทนที่จะเป็นอินสแตนซ์สามารถทำได้) จากนั้นเรียกเมธอดอินสแตนซ์ ( run())


ฉันกำลังอธิบายเรื่องนี้กับเพื่อนร่วมงานคนใหม่ของเราในตอนนี้ - ขอบคุณสำหรับคำอธิบายที่ดีนี้ นี่คือคำตอบที่ควรยอมรับ
Supahupe

83

เขตข้อมูลและวิธีการแบบคงที่เชื่อมต่อกับชั้นเรียนของตัวเองและไม่ใช่กรณี ถ้าคุณมีคลาสAวิธี 'ปกติ' bและวิธีแบบสแตติกcและคุณสร้างอินสแตนซ์aของคลาสของคุณAการเรียกไปA.c()และa.b()ถูกต้อง เมธอดc()ไม่มีความคิดที่เชื่อมต่ออินสแตนซ์ดังนั้นจึงไม่สามารถใช้ฟิลด์ที่ไม่คงที่ได้

ทางออกสำหรับคุณคือคุณทำให้ฟิลด์ของคุณคงที่หรือวิธีการของคุณไม่คงที่ คุณหลักอาจมีลักษณะเช่นนี้แล้ว:

class Programm {

    public static void main(String[] args) {
        Programm programm = new Programm();
        programm.start();
    }

    public void start() {
        // can now access non-static fields
    }
}

54

staticคำหลักปรับเปลี่ยนวงจรชีวิตของวิธีการหรือตัวแปรที่อยู่ในชั้นเรียน staticวิธีการหรือตัวแปรจะถูกสร้างขึ้นในช่วงเวลาที่ชั้นมีการโหลด เมธอดหรือตัวแปรที่ไม่ได้ประกาศว่าstaticถูกสร้างขึ้นเฉพาะเมื่อคลาสนั้นสร้างอินสแตนซ์เป็นวัตถุเช่นโดยใช้newโอเปอเรเตอร์

วงจรชีวิตของคลาสในวงกว้างคือ:

  1. ซอร์สโค้ดสำหรับคลาสนั้นถูกสร้างขึ้นโดยสร้างเทมเพลตหรือรูปแบบหรือตราประทับซึ่งสามารถใช้ในการ
  2. สร้างวัตถุด้วยตัวnewดำเนินการโดยใช้คลาสเพื่อสร้างอินสแตนซ์ของคลาสเป็นวัตถุจริงจากนั้นเมื่อทำกับวัตถุ
  3. ทำลายวัตถุที่อ้างสิทธิ์ในทรัพยากรที่มีอยู่เช่นหน่วยความจำระหว่างการรวบรวมขยะ

เพื่อให้มีจุดเริ่มต้นเริ่มต้นสำหรับแอปพลิเคชัน Java ได้นำการประชุมที่โปรแกรม Java ต้องมีคลาสที่มีวิธีการที่มีการตกลงหรือชื่อพิเศษ main()วิธีพิเศษนี้เรียกว่า เนื่องจากเมธอดต้องมีอยู่ไม่ว่าคลาสที่มีเมธอดหลักจะถูกสร้างอินสแตนซ์หรือไม่main()เมธอดต้องถูกประกาศด้วยโมดิstaticฟายเออร์เพื่อที่ทันทีที่คลาสถูกโหลดmain()เมธอดจะพร้อมใช้งาน

ผลลัพธ์คือเมื่อคุณเริ่มต้นแอ็พพลิเคชัน Java ของคุณโดยใช้บรรทัดรับคำสั่งเช่นjava helloworldชุดของแอ็คชันที่เกิดขึ้น ก่อนอื่น Java Virtual Machine จะเริ่มต้นและเริ่มต้นได้ ถัดไปไฟล์ helloworld.class ที่มีรหัส Java ที่คอมไพล์แล้วจะถูกโหลดลงใน Java Virtual Machine จากนั้นโปรแกรม Java Virtual Machine จะมองหาวิธีการในส่วนชั้นที่เรียกว่าhelloworld main(String [] args)วิธีนี้จะต้องเป็นstaticอย่างนั้นแม้ว่ามันจะไม่ได้รับการยกตัวอย่างเช่นในชั้นเรียนเป็นวัตถุ Java Virtual Machine ไม่ได้สร้างอินสแตนซ์ของคลาสโดยการสร้างวัตถุจากคลาส มันแค่โหลดคลาสและเริ่มต้นการดำเนินการที่main()วิธีการ

ดังนั้นคุณจำเป็นต้องสร้างอินสแตนซ์ของคลาสของคุณเป็นวัตถุแล้วคุณสามารถเข้าถึงวิธีการและตัวแปรของคลาสที่ยังไม่ได้ประกาศด้วยโมดิstaticฟายเออร์ เมื่อโปรแกรม Java ของคุณเริ่มต้นด้วยmain()ฟังก์ชั่นแล้วคุณสามารถใช้ตัวแปรหรือวิธีการใด ๆ ที่มีตัวปรับปรุงstaticเนื่องจากมีอยู่เป็นส่วนหนึ่งของคลาสที่โหลด

อย่างไรก็ตามตัวแปรและเมธอดของคลาสที่อยู่ภายนอกmain()เมธอดที่ไม่มีstaticตัวแก้ไขไม่สามารถใช้ได้จนกว่าอินสแตนซ์ของคลาสจะถูกสร้างเป็นวัตถุภายในmain()เมธอด หลังจากสร้างวัตถุแล้วคุณสามารถใช้ตัวแปรและวิธีการของวัตถุ ความพยายามในการใช้ตัวแปรและเมธอดของคลาสที่ไม่มีstaticตัวดัดแปลงโดยไม่ผ่านวัตถุของคลาสนั้นถูกคอมไพเลอร์ Java ถูกคอมไพล์ในเวลารวบรวมและตั้งค่าสถานะเป็นข้อผิดพลาด

import java.io.*;

class HelloWorld {
    int myInt;      // this is a class variable that is unique to each object
    static int myInt2;  // this is a class variable shared by all objects of this class

    static void main (String [] args) {
        // this is the main entry point for this Java application
        System.out.println ("Hello, World\n");
        myInt2 = 14;    // able to access the static int
        HelloWorld myWorld = new HelloWorld();
        myWorld.myInt = 32;   // able to access non-static through an object
    }
}

11

ลองวิเคราะห์โปรแกรมของคุณก่อน .. ในโปรแกรมวิธีแรกของคุณคือmain()และเก็บไว้ในใจว่ามันเป็นวิธีการแบบคงที่ ... จากนั้นคุณประกาศตัวแปรท้องถิ่นสำหรับวิธีการนั้น (comparCount, low, high, etc .. ) ขอบเขตของตัวแปรนี้เป็นเพียงวิธีการประกาศโดยไม่คำนึงว่ามันเป็นวิธีคงที่หรือไม่คงที่ ดังนั้นคุณไม่สามารถใช้ตัวแปรเหล่านั้นนอกวิธีการนั้น นี่เป็นข้อผิดพลาดพื้นฐานที่คุณทำ

จากนั้นเรามาถึงจุดต่อไป คุณบอกว่าคงฆ่าคุณ (อาจเป็นการฆ่าคุณ แต่ให้ชีวิตกับโปรแกรมของคุณเท่านั้น !!) ก่อนอื่นคุณต้องเข้าใจสิ่งพื้นฐาน * วิธีการแบบคงที่เรียกเฉพาะวิธีการแบบคงที่และใช้เฉพาะตัวแปรแบบคงที่ * ตัวแปรสแตติกหรือวิธีสแตติกไม่ได้ขึ้นอยู่กับอินสแตนซ์ของคลาสนั้น (เช่นถ้าคุณเปลี่ยนสถานะของตัวแปรสแตติกใด ๆ มันจะสะท้อนให้เห็นในวัตถุทั้งหมดของคลาส) * ด้วยเหตุนี้คุณจึงเรียกมันว่าเป็นตัวแปรคลาสหรือวิธีการเรียน และอีกมากมายที่เกี่ยวกับคำหลัก "คงที่" ฉันหวังว่าตอนนี้คุณจะได้รับความคิด ก่อนอื่นให้เปลี่ยนขอบเขตของตัวแปรและประกาศว่าเป็นแบบคงที่ (เพื่อให้สามารถใช้งานได้ในวิธีการคงที่)

และคำแนะนำสำหรับคุณคือคุณเข้าใจผิดแนวคิดเกี่ยวกับขอบเขตของตัวแปรและฟังก์ชันการทำงานแบบคงที่ รับแนวคิดที่ชัดเจนเกี่ยวกับเรื่องนั้น


11

สิ่งพื้นฐานมากคือตัวแปรแบบคงที่หรือวิธีการคงที่ในระดับชั้นเรียน ตัวแปรระดับเมธอดหรือเมธอดได้รับการโหลดก่อนที่เมธอดหรือตัวแปรระดับอินสแตนซ์และแน่นอนว่าสิ่งที่ไม่ได้โหลดไม่สามารถใช้ได้ ดังนั้นคอมไพเลอร์ภาษาจาวาไม่ยอมให้สิ่งต่าง ๆ ได้รับการจัดการ ณ รันไทม์แก้ไขในเวลาที่คอมไพล์ นั่นเป็นสาเหตุที่ทำให้คุณเกิดข้อผิดพลาดที่ไม่คงที่สิ่งที่ไม่สามารถเรียกจากบริบทคงที่ คุณเพียงแค่ต้องอ่านเกี่ยวกับขอบเขตระดับชั้นขอบเขตระดับอินสแตนซ์และขอบเขตท้องถิ่น


8

เพื่อให้สามารถเข้าถึงได้จากวิธีการคงที่ของคุณพวกเขาจะต้องเป็นตัวแปรสมาชิกคงที่เช่นนี้

public class MyProgram7 {
  static Scanner scan = new Scanner(System.in);
  static int compareCount = 0;
  static int low = 0;
  static int high = 0;
  static int mid = 0;  
  static int key = 0;  
  static Scanner temp;  
  static int[]list;  
  static String menu, outputString;  
  static int option = 1;  
  static boolean found = false;

  public static void main (String[]args) throws IOException {
  ...

7

ตอนนี้คุณสามารถเพิ่ม / ใช้อินสแตนซ์ด้วยในวิธีการ

public class Myprogram7 {

  Scanner scan;
  int compareCount = 0;
  int low = 0;
  int high = 0;
  int mid = 0;  
  int key = 0;  
  Scanner temp;  
  int[]list;  
  String menu, outputString;  
  int option = 1;  
  boolean found = false;  

  private void readLine() {

  }

  private void findkey() {

  }

  private void printCount() {

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

    Myprogram7 myprg=new Myprogram7();
    myprg.readLine();
    myprg.findkey();
    myprg.printCount();
  }
}

ตัวอย่างแข็งมากที่ฉันใช้เป็นแม่แบบเพื่อแก้ไขไฟล์ src ที่ซับซ้อนเป็นโครงสร้างที่เหมาะสม
XMAN

3

ฉันจะพยายามอธิบายสิ่งคงที่ให้คุณ ตัวแปรสแตติกแรกทั้งหมดไม่ได้อยู่ในอินสแตนซ์เฉพาะของคลาส พวกเขาได้รับการยอมรับด้วยชื่อของชั้นเรียน วิธีการคงที่อีกครั้งไม่ได้เป็นของอินสแตนซ์เฉพาะใด ๆ อีกครั้ง พวกเขาสามารถเข้าถึงตัวแปรคงที่เท่านั้น ลองนึกภาพคุณโทร MyClass.myMethod () และ myMethod เป็นวิธีการคงที่ หากคุณใช้ตัวแปรที่ไม่คงที่ภายในวิธีการนั้นนรกบนโลกจะรู้ได้อย่างไรว่าจะใช้ตัวแปรใด นั่นเป็นเหตุผลที่คุณสามารถใช้จากวิธีการคงที่เท่านั้นตัวแปรแบบคงที่ ฉันทำซ้ำอีกครั้งพวกเขาไม่ได้อยู่ในอินสแตนซ์เฉพาะใด ๆ


2
  • สิ่งแรกคือการทราบความแตกต่างระหว่างอินสแตนซ์ของคลาสและคลาสเอง คลาสเป็นแบบจำลองคุณสมบัติบางอย่างและพฤติกรรมทั้งหมดในบริบทของคุณสมบัติเหล่านั้น อินสแตนซ์จะกำหนดค่าเฉพาะสำหรับคุณสมบัติเหล่านั้น

  • สิ่งที่ผูกพันกับคำหลักคงที่มีอยู่ในบริบทของชั้นเรียนมากกว่าในบริบทของตัวอย่างของชั้นเรียน

  • เป็นข้อพิสูจน์ข้างต้น

    1. ตัวแปรภายในวิธีการไม่สามารถคงที่
    2. เขตข้อมูลคงที่และวิธีการจะต้องเรียกใช้โดยใช้ชื่อคลาสเช่น MyProgram7.main (... )
  • อายุการใช้งานของฟิลด์ / วิธีแบบคงที่เทียบเท่ากับอายุการใช้งานของแอปพลิเคชันของคุณ

เช่นพูดว่ารถยนต์มีสีของคุณสมบัติและแสดงพฤติกรรม 'เคลื่อนไหว' ตัวอย่างของรถจะเป็น Red Volkswagen Beetle ที่เคลื่อนไหวที่ 25kmph

ตอนนี้คุณสมบัติคงที่ของรถคือจำนวนล้อ (4) บนท้องถนนและสิ่งนี้จะใช้กับรถยนต์ทุกคัน

HTH


1

มันเป็น ClassLoader รับผิดชอบในการโหลดไฟล์คลาสให้ดูว่าเกิดอะไรขึ้นเมื่อเราเขียนชั้นเรียนของเราเอง

ตัวอย่างที่ 1:

class StaticTest {

      static int a;
      int b;
      int c;
}

ตอนนี้เราจะเห็นว่าคลาส "StaticTest" มี 3 ฟิลด์ แต่ที่จริงแล้วไม่มีการมีอยู่ของตัวแปรสมาชิก b, c แต่ทำไม ??? ตกลงดูสิ ที่นี่ b, c เป็นตัวแปรอินสแตนซ์เนื่องจากตัวแปรอินสแตนซ์ได้รับหน่วยความจำ ณ เวลาที่สร้างวัตถุ ดังนั้นที่นี่ b, c ยังไม่ได้รับความทรงจำใด ๆ นั่นเป็นสาเหตุที่ว่าทำไมไม่มี b, c ดังนั้นจึงมีเพียงการดำรงอยู่ของ สำหรับ ClassLoader มีเพียงหนึ่งข้อมูลเกี่ยวกับ ClassLoader ยังไม่รู้จัก b, c เพราะมันเป็นวัตถุที่ยังไม่สร้างอินสแตนซ์

ลองดูตัวอย่างอื่น: ตัวอย่างที่ 2:

class StaticTest {

      public void display() {
          System.out.println("Static Test");
      }


      public static void main(String []cmd) {

             display();       
      }

}

ตอนนี้ถ้าเราพยายามคอมไพล์โค้ดนี้จะทำให้เกิดข้อผิดพลาด CE CE: การแสดงวิธีการไม่คงที่ () ไม่สามารถอ้างอิงได้จากบริบทแบบคงที่

ตอนนี้สำหรับ ClassLoader ดูเหมือนว่า:

class StaticTest {

      public static void main(String []cmd) {

             display();       
      }

}

ในตัวอย่างที่ 2 ข้อผิดพลาด CE เป็นเพราะเราเรียกวิธีที่ไม่คงที่จากบริบทคงที่ ดังนั้นจึงเป็นไปไม่ได้ที่ ClassLoader จะจดจำวิธีการแสดงผล () ณ เวลาที่รวบรวมดังนั้นจึงเกิดข้อผิดพลาดในการรวบรวมเวลา


อาจส่งคำตอบของคุณโดยไม่ตั้งใจก่อนที่คุณจะจัดการให้เสร็จหรือไม่ โปรดแก้ไขและเพิ่มเนื้อหาที่ขาดหายไปขอบคุณ!
plamut

1

ก่อนที่คุณจะเรียกใช้เมธอดอินสแตนซ์หรือตัวแปรอินสแตนซ์มันต้องการวัตถุ (อินสแตนซ์) เมื่อตัวแปรอินสแตนซ์ถูกเรียกจากคอมไพเลอร์เมธอดแบบสแตติกไม่ทราบว่าเป็นวัตถุใดที่ตัวแปรนี้เป็นของ เนื่องจากวิธีการคงที่ไม่มีวัตถุ (คัดลอกเพียงหนึ่งเสมอ) เมื่อคุณเรียกใช้ตัวแปรอินสแตนซ์หรือวิธีการอินสแตนซ์จากวิธีอินสแตนซ์มันอ้างถึงthisวัตถุ มันหมายถึงตัวแปรที่เป็นของวัตถุใด ๆ ที่สร้างขึ้นและแต่ละวัตถุมีสำเนาของวิธีการและตัวแปรของตัวเอง

ตัวแปรสแตติกถูกทำเครื่องหมายเป็นstaticและตัวแปรอินสแตนซ์ไม่มีคำหลักเฉพาะ


0

นี่คือความแตกต่างเล็กน้อยเพื่ออธิบายเกี่ยวกับคำสำคัญคงที่สำหรับผู้เริ่มต้นทั้งหมด
คุณจะได้รับรู้อย่างชัดเจนเมื่อคุณทำงานกับคลาสและวัตถุมากขึ้น

| * | คงที่:รายการแบบคงที่สามารถเรียกได้ด้วยชื่อชั้น
ถ้าคุณสังเกตในรหัสฟังก์ชั่นบางอย่างจะถูกเรียกโดยตรงกับชื่อชั้นเช่น

NamCls.NamFnc();

System.out.println();

นี่เป็นเพราะ NamFnc และ println จะถูกประกาศโดยใช้คำสำคัญคงที่ก่อนที่พวกเขา

| * | ไม่คงที่:รายการคงที่ไม่สามารถเรียกได้ด้วยตัวแปรระดับ
หากมันไม่คงที่คุณต้องมีตัวแปรของชั้นเรียน
ใส่จุดหลังตัวแปรระดับชั้น
แล้วเรียกฟังก์ชั่น

NamCls NamObjVar = new NamCls();
NamObjVar.NamFnc();


โค้ดด้านล่างอธิบายคุณอย่างเรียบร้อย

| * | ฟังก์ชั่นคงที่และไม่คงที่ในชั้นเรียน:

public class NamCls
{
    public static void main(String[] args)
    {
        PlsPrnFnc("Tst Txt");

        NamCls NamObjVar = new NamCls();
        NamObjVar.PrnFnc("Tst Txt");
    }

    static void PlsPrnFnc(String SrgPsgVal)
    {
        System.out.println(SrgPsgVal);
    }

    void PrnFnc(String SrgPsgVal)
    {
        System.out.println(SrgPsgVal);
    }
}


| * | คลาสแบบคงที่และไม่คงที่ภายในคลาส:

public class NamCls
{
    public static void main(String[] args)
    {
        NamTicCls NamTicVaj = new NamTicCls();
        NamTicVaj.PrnFnc("Tst Txt");

        NamCls NamObjVar = new NamCls();
        NamNicCls NamNicVar = NamObjVar.new NamNicCls();
        NamNicVar.PrnFnc("Tst Txt");
    }

    static class NamTicCls
    {
        void PrnFnc(String SrgPsgVal)
        {
            System.out.println(SrgPsgVal);
        }
    }

    class NamNicCls
    {
        void PrnFnc(String SrgPsgVal)
        {
            System.out.println(SrgPsgVal);
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.