변수
클래스의 구성요소 중 변수에는 클래스변수, 스태틱변수, 전역변수가 있다.
변수 값은 진행의 흐름에 따라서 바뀐다.
package com.bit.day05;
class Ex26 {
public static int su=1234; //전역변수
public static void func01(){
System.out.println("func01 su=" + su);
}
public static void main(String[] args){
System.out.println("main start");
su=4321;
System.out.println(su);
func01(); //메인 안에서 전역변수 su의 값을 바꿔 4321출력
System.out.println("main end");
}
}
class Ex26 {
public static int su=1234;
public static void func02(){
System.out.println("func02 su=" + su);
}
public static void func01(){
su=4321;
System.out.println("func01 su=" + su);
}
public static void main(String[] args){
System.out.println("main start");
System.out.println(su);
func01();
func02(); //func01에서 su를 4321로 바꿨으므로 똑같이 4321이 출력된다.
System.out.println("main end");
}
}
공통으로 작업하고 싶을때, 메소드에 매개변수를 주지 않아도 클래스 변수를 쓸 수 있다.
return은 하나만 돌려줄 수 있는 반면 클래스 변수를 사용하면 여러개의 값을 리턴하는 것과 동일한 효과를 줄 수 있다.
값을 주면 받지만, 값을 주지 않으면 default값을 자동으로 부여한다.
default 값은 클래스 내부의 (메소드 안에 들어가지 않은) 변수에서만 사용된다.
class Ex26 {
public static int su;
su=1234; //이렇게 되면 작업, 일을 수행하는 것이라 어느시점에 누가 시킬 것인지 모르기 때문에 수행 불가.
//그렇지만 변수 선언, 즉 그냥 메모리 공간 확보 하는 것은 가능.
//그 확보된 공간에 값을 쓰는 작업 x , int su=1234;로 선언과 초기화를 함께 하는 것은 가능하다.
//그러므로 확보되는순간 일시적으로 넣어놓고 시작할 수 있도록 값을 주지않으면 default값 0을(false) 준다.
//(전기 다 끊어버리면 0)
public static void main(String[] args){
System.out.println("main start");
System.out.println(su);
System.out.println("main end");
}
}
지역변수와 전역변수가 동시에 존재하면 지역변수가 우선순위를 갖는다.
이름만 똑같을 뿐, 메모리상의 위치가 다르다. 여기 su는 스택영역에서 선언한 것이다.
class Ex26 {
public static int su=1234;
public static void main(String[] args){
System.out.println("main start");
System.out.println(su); //1234출력
int su=4321; //지역변수와 전역변수가 동시에 존재하면 지역변수가 우선순위를 갖는다.
// 여기 su는 스택영역에서 선언한 것.
System.out.println(su); //4321출력
System.out.println("main end");
}
}
동작 원리
클래스영역에는 상수영역, core영역(자바를 기본적으로 돌려주는 손댈 수 없는 영역), static 영역이 있다.
프로그램 동작시
1. core영역을 통한 일 준비시작 - 코드상에 등장하는 모든 상수를 상수영역에 적재하고 시작한다(그래서 일 시작시 복사할 수 있도록)
2. static 영역에 static 키워드가 붙은 것들을 다 갖다 놓는다.
3. 그리고 준비가 끝나면 실행
4. static이 main을 코어에서 가져와서 스택에 올린다.
5. 메인이 끝나면 스택이 비워지면서 끝난다.
자바가 끝나면 JVM에서 마무리작업(메모리 반납 등등 관리)
준비 시에 코어영역을 serach 후 상수영역에 올림. static 붙여진 것들 static 영역에 갖다놓기.
갖다놓는 과정에서 값이 있으면 상수에서 가져와서 써놓고 프로그램 시작.
시작과 동시에 main을 static에서 찾아 스택에 적재.
su를 호출했는데 없으면 staticd을 뒤진다. static에 있는 su의 값을 가져와서 쓴다.
즉, 메인(함수)에 없는 변수를 호출하면 클래스 영역의 static을 뒤진다. 두군데 다 없으면 오류!
static은 프로그램이 구동되기 이전에 메모리상에 올려놓고 시작한다. 그래서 static 코드가 많이 붙어있으면 로딩이 오래 걸리는 것이다. 준비할 때 공간이 넓어져서 실제 일할 공간(스택영역)이 적어지기 때문에 static을 적게 쓰려고 한다. 그렇지만 메모리상에 다 올려놓고 시작하기 때문에 구동은 빠르다. 스택에 선언, 대입 등의 만들고 사라지는 반복작업이 없으므로 진행이 빠르다는 얘기이다(static 공간을 따로 할당할 필요없이 가져와서 쓸 수 있는 것이 장점). 그러므로 최소한의 static 사용은 필요하다.
메인 안에 static을 붙이면 구동을 해봐야 static이 붙은지 알 수있어지기 때문에 static을 붙일 수 없다. static은 구동되기 전, 사전에 준비하기 때문이다. main 앞에 static을 붙이는 이유도 마찬가지이다. static이 붙어야 사전에 main을 메모리에 적재를하고 불러올 수 있기 때문에 붙이는것이다.
아래의 경우, static 영역에 구분되도록 Ex229.su / Ex29.su의 형태로 올려놓는다.
main의 정의는 Ex29에 있으므로 결국 main도 Static영역에 할당될때 Ex29.main으로 되어있는것이다.
class Ex229{
public static int su=999;
public static func(){
System.out.println("Ex229-func()");
}
}
class Ex29 {
public static int su=1234;
public static void main(String[] args){
System.out.println("main start");
System.out.println(su); //1234출력(su=Ex29.su) 지역변수가 없을때는 내 클래스 안의 main이므로 Ex29.su 실행
int su=4321;
System.out.println(su); //지역변수 4321
System.out.println(Ex29.su); //전역의 수 1234
System.out.println(Ex229.su); //전역의 수 999
System.out.println("main end");
Ex229.func();
}
}
주의할 점: 남들이 만들어놓은 클래스를 사용하기 위해서는 소스파일이 아니라 그 클래스파일이 존재를 해야 실행 시킬 수 있다. (즉, 컴파일해야한다)
class Ex31{
public int su1=1234; //static이 빠지면 프로그램 로딩과동시에 static에 할당하지 않는다. --> 존재x
public void func01(){
System.out.println("func01()...");
}
public static void main(String[] args){
System.out.println(su1); //존재하지않는 su1을 호출하므로 오류
}
}
객체
class Ex31{
//변수(멤버필드(정식명칭), 인스턴스 변수, non-static변수... 모두 같은 말)
public int su1=1234;
//메소드(멤버메소드, non-static메소드...)
public void func01(){
System.out.println("func01()...");
}
public static void main(String[] args){
Ex31 me = new Ex31(); //new = 객체 생성 = 존재를 만든다 = 존재는 하지만 static 공간은 아니다.
System.out.println(me.su1); //그러므로 출력가능.
me.func01();
}
}
Ex31 me; --- 선언 //참조변수 me : 포인터의 역할 즉, 주소값이다. 객체의 주소가 대입되는 변수이다.
me = new Ex31(); ---초기화 //객체의생성
Q.메모리는 찍어낸 객체의 사이즈가 얼마일줄 알고 공간을 확보할까?
: 주소를 쓸 수 있는 사이즈면 되는 것이다. 따라서 객체의 크기는 중요하지 않다. 참조변수는 결국 주소를 나타내기 때문이다.
힙영역은 객체가 생성되는 위치이다. 영역에 접근하고자 한다면 스택영역에 me변수를 만들어 그 객체의 주소값을 대입한다.
Q.static과의 차이점은 무엇일까?
: 찍어내기 전까진 존재하지 않는다. main 시작 전까진 객체를 찍지 않으니까 준비가 빠름. 최소한의 메모리로 동작 시작하는것이 장점이나 실행적인 측면에서는 약간 떨어질 수 있다. static은 사용하지 않아도 지울 수 없지만 객체는 사용하지 않으면(그 객체를 가리키는 게 없다면) 가비지 컬렉션이 알아서 지워준다. 객체는 필요한만큼만 쓰고 다시 비워준다. 잘 사용하면 static보다 효율적이다.
Q.자바의 장점은?
자원관리 직접하지않고 가비지 컬렉션이 알아서 관리해준다. 자바는 객체지향 언어이고, 가비지 컬렉션은 더이상 사용할 수 없는 객체를 지워준다. 객체는 힙영역에 있으므로 힙영역을 관리한다.
me 확보 - 메모리 주소를 쓸 수 있을만큼
객체생성 - 클래스를 가지고 객체를 찍어냄 (su=1234와 func01)
찍어낸 객체의 주소를 me 공간에 쓴다.
class Ex31{
public int su1=1234;
public void func01(){
System.out.println("func01()...");
}
public static void main(String[] args){
Ex31 me = new Ex31();
me.su1=1111;
System.out.println(me.su1);
me.func01();
me = new Ex31(); //클래스를 가지고 객체 또 생성
System.out.println(me.su1);
// 이후로 다시 1111을 출력할 순 없다. me가 덮어씌워져서 그 전 객체에 접근 불가능하기 때문이다.
// --> 쓸 수가 없는 것은 없애야 하는데, 자바가 JVM의 가비지 컬렉션을 이용해 지워준다.
}
}
static 변수 - 누군가 값을 바꾸면 똑같이 적용 받는다.
non- static 변수 - 객체에 접근해서 값을 바꾸기 때문에 해당 객체만 적용을 받는다.
객체 생성시에는 static 키워드가 붙은 것들은 다 빼버리고 non static인 것들만 가지고 객체를 만든다.
찾는 변수가 객체에 없으면 static에서 검색하는데 static에는 사실 클래스명.su1 이런식으로 저장이 되어있다. 그러므로 접근이 가능하다.
static 으로 선언된 변수는 클래스 로드 시 메모리의 클래스 영역의 static영역에 할당이 된다.
따라서 객체를 생성해서 힙영역에 올리기전에도 Class명.변수명을 통해서 접근이 가능하다.
class Ex31{
public static int su1=1234; //static으로 바꾸면 1111로 출력.
public void func01(){
System.out.println("func01()...");
}
public static void func02(){
Ex31 me = new Ex31();
me.su1=1111;
System.out.println(me.su1);
me.func01();
me = new Ex31();
System.out.println(me.su1);
}
public static void main(String[] args){
func02();
}
}
: 객체 생성 때 하고 싶은 것을 명세한다.
class Ex32{
//생성자 : 해당객체에서 단 한번만 수행, 생성자의 이름=클래스명
//클래스명(매개변수){실행코드;}
//default 생성자 : 생성자를 정의하지 않을시 컴파일 과정에서 자동으로 붙여진다.
//단, 어떠한 생성자라도 정의되는 순간 default 생성자는 만들어지지 않는다.
public Ex32(int su){}
public static void main(String[] args){
Ex32 me; //변수의 선언 - 자료형 변수명; (Ex32타입의 객체로 생성된 주소만 올 수 있다)
me = new Ex32(); //변수의 초기화 --- new 키워드와함께 생성자 호출
//생성자 매개변수에 int가 있으므로 매개변수에 아무것도 넣어주지 않으면 오류
//인자가 없는 생성자를 만들어 오버로드 가능. (생성자를 추가적으로 만들어야한다)
}
}
class Ex32{
public static void main(String[] args){
int su1=1111;
int su2=1111;
int su3=su1;
System.out.println(su1==su2); //value 비교
System.out.println(su1==su3);
System.out.println("-------------------------");
Ex32 me = new Ex32();
Ex32 you = new Ex32();
Ex32 them = me;
System.out.println(me==you); //주소(reference) 비교(둘 중에 하나가 참조로 들어가는 순간 reference비교)
System.out.println(me==them);
}
}
static과 non-static
package com.bit.day05;
/* static -> static 접근 : class명 접근
static -> non-static 접근 : 참조변수로 접근
*/
class Ex30{
public static int su1=1111;
public int su2=2222;
public static void func01(){
System.out.println("static-method func01");
}
public void func02(){
System.out.println("non-static-method func02");
}
public static void main(String[] args){
Ex30.func01();
System.out.println(Ex30.su1);
Ex30 me = new Ex30();
me.func02();
System.out.println(me.su2);
}
}
package com.bit.day05;
/* static -> static 접근 : (class명.) 접근
static -> non-static 접근 : 참조변수. 접근
non-static -> static 접근 : (class명.) 접근
non-static -> non-static : (this. 즉 참조변수.) 접근
지역변수처럼 호출 가능 --> 그 말은 뭔가 생략됐다 --> 참조변수가 생략된 것
*/
class Ex30{
public static int su1=1111;
public int su2=2222;
public static void func01(){
System.out.println("static-method func01");
}
public void func02(){
System.out.println("non-static-method func02");
}
public void func(){
System.out.println(su1); //1111출력
func01();
System.out.println(su2); //2222출력 non->non
func02();
}
public static void main(String[] args){
Ex30.me = new Ex30();
me.func();
}
}
this
: non static에서만 쓰일 수 있고 객체 자기 자신의 주소를 얘기하는 것이다. 메소드를 실행한 자기 자신의 주소를 말한다.
//me, you를 똑같이 Ex31타입으로 객체 생성 후
public void func04(me, you){
System.out.print(this == me ) //true 출력
System.out.print(this == you) //false 출력 (me와 you는 완전히 다른 주소값을 가지니까)
}
public static void main(String[] args){
me.func04(me); //이것을 실행하면
}
함수형 언어 문법 체이닝
: this를 이렇게까지도 활용 가능하다
public Ex31 func05(){
System.out.println("func01 run...");
return this;
}
public static void main(String[] args){
new Ex31().func05().func05().func05();
}
non static-non static 접근시 지역변수가 su2를 덮어쓰면 메인 밖의 su2를 부를 수 있는 방법은 this. 으로 접근해야 한다.
non -static이 중요한 이유는 객체지향언어이기 때문이다. 객체 = static 쓰는 것을 줄이기 위해 만든 것이니까?
this는 생성자에 들어올 수 있되 모든 생성자에 있으면 무한루프가 되므로, 어느 생성자 하나에는 this가 없어야한다.
class Ex34{
public int su;
public Ex34(){
this(1234); //앞서 this는 참조변수의 역할이었으나, 여기서는 Ex34(1234)와 같다. 생성자에서 다른생성자 호출.
//Ex34(1234)로하면 새로운 객체가 찍혀버리니까 this는 생성자에만 존재한다.
--> 이런식으로 다른 생성자를 호출하는 이유는 중복되는 역할을 한쪽 생성자로 몰아넣고, 그 생성자를 호출하는 방식으로 중복을 제거할 수 있기 때문.
}
public Ex34(int su){
this.su=su;
System.out.println("");
}
public Ex34(int su, int su2){
this(su-su2);
}
public static void main(String[] args){
Ex34 me = new Ex34(3333,1111);
System.out.println(me.su);
}
}
class Ex34{
public int su;
public Ex34(){
this(1234);
System.out.println("인자안받는생성자호출")
}
public Ex34(int su){
this.su=su;
System.out.println("인자를 받는 생성자 호출");
}
public Ex34(int su, int su2){
this(su-su2);
}
public static void main(String[] args){
Ex34 me = new Ex34();
System.out.println(me.su);
}
}
//또한 this는 최상단에 와야한다. 위에 다른 코드가 있으면 오류.
//생성자가 클래스를 초기화 하는 놈인데, 클래스 초기화도 전에 다른 것을 수행하면 안되기 때문이다.
final
class Ex33{
public static void main(String[] args){
//상수형 변수 : 값을 변하지 못하게 한다.
// final int su; 라고 쳐도 오류가 뜨지 않는다. 값이 변한 것이 아니니까.
final int su=1234;
su=4321;
System.out.println(su);
}
}
//파이처럼 바뀌면 안될 상수 값을 변수에 넣어놓으면 계속 값을 바꿀수 있다. 그것을 막기 위해 final을 사용한다.
class Ex33{
public static final int su; //오류 : static - default값이 존재 - > 존재하는 이유가 언제든지 값을 바꿀수 있기에 존재.
//그런데 일단 final을 붙이면 값을 못바꾸므로 default값을 넣어버리면 의미가 없으므로 오류.
public static void main(String[] args){
System.out.println(su);
}
}
class Ex33{
public final int su;
public Ex33(){ //생성자는 객체를 생성하는 시점에 값을 주기에 가능하다.
su=1234; //int에 static을 붙이면 불가능. 왜냐면 static은 준비단계에 올려놓고 시작하는 거니까.
}
public static void main(String[] args){
System.out.println(su);
}
}
class Ex33{
public final int su;
public Ex33(int a){
su=a;
}
public static void main(String[] args){
System.out.println(new Ex33(1111).su); //객체를 생성하면서 su에 1111을 넣는다. 이후 값은 못바꾼다.
System.out.println(new Ex33(2222).su); //새로운 객체이므로 오류가 나지 않는다.
}
}
class Ex33{
public int su = 3333;
public Ex33(int a){
su=a;
}
public static void main(String[] args){
Ex33 me = new Ex33(1111);
Ex33 you = new Ex33(2222);
System.out.println(me.su); //1111이 출력된다.
System.out.println(you.su); //2222 출력
}
}
class Ex33{
public int su = 3333;
public Ex33(int a){
su=a;
print(); //객체생성시 필드를 초기화하여 메소드를 실행하겠다.
}
public void print(){
System.out.println(su);
}
public static void main(String[] args){
Ex33 me = new Ex33(1111); //1111출력
Ex33 you = new Ex33(2222); //2222출력
}
}
필드에있는 su여야하는데 참조변수가 없다.
참조변수는 클래스에 대한 정보도 가지고 있으므로 static 변수에 접근이 가능(객체엔 static변수가 안들어갔지만)
->실행은 되지만 좋은 코딩은 아니다.
예제
static 있을 때 = 클래스명으로 접근
package com.bit.day05;
class Car {
public static int speed=0;
public static final int limit = 180;
public static void speedUp(int a){
if(speed+a>180){speed=limit;}else{speed+=a;}
}
public static void speedDown(){
if(speed-10<0){speed=0;}else{speed-=10;}
}
}
class Ex35{
public static void main(String[] args){
System.out.println("내 차의 스피드는 " + Car.speed+ "km");
for(int i=0; i<20; i++){
Car.speedUp(i);
System.out.println("내 차의 스피드는 " + Car.speed+ "km");
}
for(int i=0; i<10; i++){
Car.speedDown();
System.out.println("내 차의 스피드는 " + Car.speed+ "km");
}
}
}
static 없을 때 = 참조변수로 접근
package com.bit.day05;
class Car {
public static int speed=0;
public static final int limit = 180;
public void speedUp(int a){
if(speed+a>180){speed=limit;}else{speed+=a;}
}
public void speedDown(){
if(speed-10<0){speed=0;}else{speed-=10;}
}
}
class Ex35{
public static void main(String[] args){
Car myCar = new Car();
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
for(int i=0; i<20; i++){
myCar.speedUp(i);
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
}
for(int i=0; i<10; i++){
myCar.speedDown();
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
}
}
}
객체 버리고 새 객체 넣어 사용
package com.bit.day05;
class Car {
public int speed=0;
public final int limit = 180;
public void speedUp(int a){
if(speed+a>180){speed=limit;}else{speed+=a;}
}
public void speedDown(){
if(speed-10<0){speed=0;}else{speed-=10;}
}
}
class Ex35{
public static void main(String[] args){
Car myCar = new Car();
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
for(int i=0; i<40; i++){
myCar.speedUp(i);
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
}
for(int i=0; i<40; i++){
myCar.speedDown();
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
}
System.out.println("새차구매");
myCar = new Car(); //그 객체 버리고 새로운 객체 만들어 넣음
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
for(int i=0; i<40; i++){
myCar.speedUp(i);
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
}
for(int i=0; i<40; i++){
myCar.speedDown();
System.out.println("내 차의 스피드는 " + myCar.speed+ "km");
}
}
}