1. 정적 키워드
클래스의 멤버 변수의 경우 모든 새 개체에는 고유한 멤버 변수가 있는 것으로 나타났습니다. 이러한 멤버 변수는 정적 멤버 변수가 아니기 때문입니다. 정적 멤버 변수의 경우 이 멤버 변수의 복사본은 하나만 있으며 이 복사본은 이 클래스의 모든 개체에서 공유됩니다.
1.1 정적 멤버변수와 비정적 멤버변수의 차이점
다음 예를 들어보세요
package cn.galc.test; public class Cat { /** * 静态成员变量 */ private static int sid = 0; private String name; int id; Cat(String name) { this.name = name; id = sid++; } public void info() { System.out.println("My Name is " + name + ",NO." + id); } public static void main(String[] args) { Cat.sid = 100; Cat mimi = new Cat("mimi"); Cat pipi = new Cat("pipi"); mimi.info(); pipi.info(); } }
메모리 분석 다이어그램을 그려 전체 프로그램의 실행 과정을 이해
프로그램의 첫 번째 문장인 Cat.sid = 100;을 실행할 때 여기의 sid는 정적 멤버 변수입니다. 정적 변수는 데이터 영역(data seg)에 저장되므로 먼저 작은 공간을 할당하십시오. 데이터 영역.sid, 첫 번째 문장이 실행되면 sid에 100이라는 값이 있습니다.
이때의 메모리 레이아웃 다이어그램은 다음과 같습니다
다음 단계는 프로그램을 실행하여 다음을 수행하는 것입니다.
고양이 미미 = 새로운 고양이(“미미”);
여기서는 Cat 클래스의 생성자 Cat(String name)이 호출됩니다. 생성자 메서드는 다음과 같이 정의됩니다.
고양이(문자열 이름){
this.name = 이름;
id=sid ;
}
호출 시 먼저 힙 메모리에 있는 Cat 클래스의 인스턴스 개체 주소가 포함된 작은 메모리 조각 mm을 스택 메모리에 할당합니다. mm은 힙 메모리에 있는 Cat 클래스 개체의 참조 개체입니다. . 이 생성자는 문자열 형태의 형식적인 매개변수 변수를 선언하므로 "mimi"가 실제 매개변수로 생성자에 전달된다. 문자열 상수는 데이터 영역에 할당되어 저장되기 때문에 데이터 영역에 메모리가 적다. 문자열 "mimi"를 저장하는 데 사용됩니다. 이때 메모리 분포는 아래와 같습니다.
생성자가 호출되면 먼저 형식 매개변수 이름을 위해 스택 메모리에 작은 공간을 할당합니다. 그런 다음 문자열 "mimi"가 실제 매개변수로 전달됩니다. Type, 4가지와 8가지 기본 데이터 유형을 제외한 나머지는 모두 참조 유형이므로 문자열도 객체라고 간주할 수 있습니다. 따라서 이는 "mimi" 개체의 참조를 name에 전달하는 것과 동일하므로 이제 name은 "mimi"를 가리킵니다. 그래서 이때의 메모리 레이아웃은 아래와 같습니다.
다음으로 생성자 본문의 코드를 실행합니다.
this.name=name;
여기서는 현재 객체, 즉 힙 메모리에 있는 고양이를 나타냅니다. 여기서 스택에 있는 name에 포함된 값은 힙 메모리에 있는 cat 객체의 name 속성에 전달되므로, name에 포함된 값은 데이터 영역에 위치한 문자열 객체 "mimi"에서도 찾을 수 있습니다. 이때, 이 이름은 문자열 객체 "mimi"의 참조 객체이기도 하다. 해당 속성값을 통해 데이터 영역에 위치한 문자열 객체 "mimi"를 찾을 수 있다. 이때 메모리 분포는 아래와 같습니다.
다음으로 메서드 본문에서 다른 코드 줄을 실행합니다.
id=sid;
여기서는 sid의 값이 id에 전달되므로 id의 값은 100이 됩니다. sid를 전달한 후 직접 1을 더하면 sid는 101이 됩니다. 이때의 메모리 레이아웃은 아래 그림과 같습니다.
이때 생성자 메서드가 호출되고, 이 생성자 메서드에 할당된 지역 변수가 차지하는 메모리 공간이 모두 사라지게 되므로 스택 공간에 위치한 네임 메모리도 사라진다. 스택 메모리의 데이터 영역에 있는 문자열 개체 "mimi"에 대한 참조도 사라지고, 이때 힙 메모리에 있는 문자열 개체 "mimi"에 대한 참조만 남습니다. 이때 메모리 레이아웃은 아래와 같습니다.
다음 실행:
Cat pipi = new Cat(“pipi”);
생성자 메서드 Cat()에 대한 두 번째 호출은 전체 호출 과정이 첫 번째와 동일합니다. 호출이 완료된 후 이때의 메모리 레이아웃은 다음과 같습니다.
最后两句代码是调用info()方法打印出来,打印结果如下:
通过这个程序,看出来了这个静态成员变量sid的作用,它可以计数。每当有一只猫new出来的时候,就给它记一个数。让它自己往上加1。
程序执行完后,内存中的整个布局就如上图所示了。一直持续到main方法调用完成的前一刻。
这里调用构造方法Cat(String name) 创建出两只猫,首先在栈内存里面分配两小块空间mimi和pipi,里面分别装着可以找到这两只猫的地址,mimi和pipi对应着堆内存里面的两只猫的引用。这里的构造方法声明有字符串类型的变量,字符串常量是分配在数据区里面的,所以这里会把传过来的字符串mimi和pipi都存储到数据区里面。所以数据区里面分配有存储字符串mimi和pipi的两小块内存,里面装着字符串“mimi”和“pipi”,字符串也是引用类型,除了那四类8种的基础数据类型之外,其他所有的数据类型都是引用类型。所以可以认为字符串也是一个对象。
这里是new了两只猫出来,这两只猫都有自己的id和name属性,所以这里的id和name都是非静态成员变量,即没有static修饰。所以每new出一只新猫,这只新猫都有属于它自己的id和name,即非静态成员变量id和name是每一个对象都有单独的一份。但对于静态成员变量来说,只有一份,不管new了多少个对象,哪怕不new对象,静态成员变量在数据区也会保留一份。如这里的sid一样,sid存放在数据区,无论new出来了多少只猫在堆内存里面,sid都只有一份,只在数据区保留一份。
静态成员变量是属于整个类的,它不属于专门的某个对象。那么如何访问这个静态成员变量的值呢?首先第一点,任何一个对象都可以访问这个静态的值,访问的时候访问的都是同一块内存。第二点,即便是没有对象也可以访问这个静态的值,通过“类名.静态成员变量名”来访问这个静态的值,所以以后看到某一个类名加上“.”再加上后面有一个东西,那么后面这个东西一定是静态的,如”System.out”,这里就是通过类名(System类)再加上“.”来访问这个out的,所以这个out一定是静态的。
再看下面的这段代码
package cn.galc.test; public class Cat { /** * 这里面的sid不再是静态成员变量了,因为没有static修饰符, * 此时它就是类里面一个普通的非静态成员变量,和id,name一样, * 成为每一个new出来的对象都具有的属性。 */ private int sid = 0; private String name; int id; Cat(String name) { this.name = name; id = sid++; } public void info() { System.out.println("My Name is " + name + ",NO." + id); } public static void main(String[] args) { //Cat.sid = 100;这里不能再使用“类.静态成员变量”的格式来访问sid了,因为sid现在变成了非静态的成员变量了。所以必须要把这句话注释掉,否则无法编译通过。 Cat mimi = new Cat("mimi"); Cat pipi = new Cat("pipi"); mimi.info(); pipi.info(); } }
이 코드와 이전 코드의 유일한 차이점은 sid 변수의 static 수식어가 제거되었다는 점입니다. 이때 sid는 더 이상 정적 멤버 변수가 아니며, 이때는 비정적 멤버 변수입니다. 각각 new에 의해 생성된 cat 객체는 자신만의 별도의 sid 속성을 갖습니다. 따라서 이 코드가 실행된 후 메모리의 레이아웃은 다음과 같습니다.
sid는 비정적 멤버 변수가 되었기 때문에 더 이상 카운팅 기능을 갖지 않습니다. id 및 name 속성과 마찬가지로 sid는 모든 새 개체가 갖는 속성이 되므로 모든 새 고양이에는 sid 속성이 추가됩니다. sid는 더 이상 "class name.static member object name" 형식으로 접근할 수 없으므로 "Cat.sid =100;" 코드의 첫 번째 문장은 이 방식으로 사용할 수 없으며, 그렇지 않으면 컴파일 오류가 발생합니다. 컴파일이 성공하려면 이 문장을 주석 처리해야 합니다. sid 값은 접근할 수 없으므로 초기화 시 sid 값은 항상 0 값으로 할당됩니다. 생성자가 호출되고 메서드 본문의 id=sid 코드가 실행될 때까지 sid는 먼저 자신의 값인 0을 id에 할당하므로 id의 값은 0이 되고, 그런 다음 sid는 자신에게 1을 더해 sid가 1이 됩니다.
따라서 정적 변수와 비정적 변수의 차이점은 정적 변수는 계산에 사용할 수 있지만 비정적 변수는 사용할 수 없다는 점입니다.
기억을 이해하면 모든 것을 이해하고, 다양한 언어를 이해하게 됩니다. 모든 언어는 이것에 지나지 않습니다. 지역 변수에 할당된 메모리는 항상 스택에 있고, 새로운 것에 할당된 메모리는 항상 힙에 있으며, 정적에 할당된 메모리는 항상 데이터 영역에 있습니다. 나머지 코드는 코드 영역에 있어야 합니다. 모든 언어가 이와 같습니다.
정적 메서드에서 비정적 멤버 변수에 접근하려면 직접 접근할 수 없습니다. 정적 메서드에서 새 객체를 생성해야 접근할 수 있습니다. 정적 멤버 변수가 추가되면 이 멤버 변수는 정적 멤버 변수가 되며 기본 메서드에서 직접 액세스할 수 있습니다.
메인 메소드는 정적 메소드이므로, 메인 메소드가 실행될 때 새로운 객체를 생성할 필요가 없습니다.
특정 개체에 대해서는 동적 메서드가 호출되지만 특정 개체에 대해서는 정적 메서드가 호출되지 않습니다. 따라서 "classname.method()" 형식을 사용하여 정적 메서드를 호출할 수 있습니다. 따라서 비정적 메서드는 특정 개체에 대해서만 호출할 수 있으므로 기본 메서드에서는 비정적 멤버 변수에 액세스할 수 없습니다. 개체가 없으면 찾을 수 없습니다. 이제 메서드 실행자가 옵니다.
멤버 변수는 new로 객체를 생성할 때만 힙 메모리의 저장 공간을 할당받습니다. 지역 변수는 스택 메모리에 저장 공간을 할당합니다.
정적 메서드는 더 이상 특정 개체에 대해 호출되지 않으므로 비정적 멤버에 액세스할 수 없습니다.
비정적 멤버는 특정 개체에만 적용됩니다. 비정적 멤버에 액세스하려면 새 개체를 만들어야 합니다.
정적 변수는 객체 이름이나 클래스 이름을 통해 액세스할 수 있습니다. 둘 다 동일한 메모리에 액세스합니다.
위 내용은 이 글의 전체 내용입니다. 많은 정보가 있으므로 인내심을 갖고 읽어야 진정한 java static 키워드를 배울 수 있습니다.