규칙
을 추가한 언어이다.클래스의 정의 : 클래스란 객체를 정의해 놓은 것
클래스의 용도 : 클래스는 객체를 생성하는데 이용
-> 프로그래밍에서 객체는 클래스에 정의돈 내용대로 메모리에 생성된 것
객체의 정의 : 실제로 존재하는 것. 사물 또는 개념
객체의 용도 : 객체가 가지고 있는 기능과 속성에 따라 다름
유형의 객체 : 책상, 의자, 자동차, TV와 같은 사물
무형의 객체 : 수학공식, 프로그램 에러와 같은 논리나 개념
-> 객체를 사용한다는 것은 객체가 가지고 있는 속성과 기능을 사용한다는 뜻이다.
-> 소스파일의 이름과 main이 존재하는 클래스를 일치시켜야 이클립스가 바로 main있는 클래스를 시작 할 수 있다.
클래스명 변수명; // 클래스의 객체를 참조하기 위한 참조변수를 선언
변수명 = new 클리스명(); // 클래스의 객체를 생성 후, 객체의 주소를 참조변수에 저장
<예제 6-1 >
✍️ 입력
class Ex6_1 {
public static void main(String args[]) {
Tv t; // Tv인스턴스를 참조하기 위한 변수 t를 선언
t = new Tv(); // Tv인스턴스를 생성한다.
t.channel = 7; // Tv인스턴스의 멤버변수 channel의 값을 7로 한다.
t.channelDown(); // Tv인스턴스의 메서드 channelDown()을 호출한다.
System.out.println("현재 채널은 " + t.channel + " 입니다.");
}
}
class Tv {
// Tv의 속성(멤버변수)
String color; // 색상
boolean power; // 전원상태(on/off)
int channel; // 채널
// Tv의 기능(메서드)
void power() { power = !power; } // TV를 켜거나 끄는 기능을 하는 메서드
void channelUp() { ++channel; } // TV의 채널을 높이는 기능을 하는 메서드
void channelDown() { --channel; } // TV의 채널을 낮추는 기능을 하는 메서드
}
💻 출력
현재 채널은 6 입니다.
-> 참조변수는 생성된 객체의 주소를 참조하고 있다.
<예제 6-2 >
✍️ 입력
class Ex6_2 {
public static void main(String args[]) {
Tv t1 = new Tv(); // Tv t1; t1 = new Tv();를 한 문장으로 가능
Tv t2 = new Tv();
System.out.println("t1의 channel값은 " + t1.channel + "입니다.");
System.out.println("t2의 channel값은 " + t2.channel + "입니다.");
t1.channel = 7; // channel 값을 7으로 한다.
System.out.println("t1의 channel값을 7로 변경하였습니다.");
System.out.println("t1의 channel값은 " + t1.channel + "입니다.");
System.out.println("t2의 channel값은 " + t2.channel + "입니다.");
}
}
💻 출력
t1의 channel값은 0입니다.
t2의 channel값은 0입니다.
t1의 channel값을 7로 변경하였습니다.
t1의 channel값은 7입니다.
t2의 channel값은 0입니다.
-> 객체배열 생성하고 꼭 각 인덱스마다 객체 생성해주기!
-> 폭, 넓이는 카드 모두 공통이므로 클래스 변수(static variable)로 선언하자.
-> 무늬, 숫자는 각 인스턴스마다 고유해야 하므로 인스턴스 변수(instance variable)로 선언하자.
<예제 6-3 >
✍️ 입력
class Ex6_3 {
public static void main(String args[]) {
System.out.println("Card.width = " + Card.width);
System.out.println("Card.height = " + Card.height);
Card c1 = new Card();
c1.kind = "Heart";
c1.number = 7;
Card c2 = new Card();
c2.kind = "Spade";
c2.number = 4;
System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")");
System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")");
System.out.println("c1의 width와 height를 각각 50, 80으로 변경합니다.");
c1.width = 50;
c1.height = 80;
System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")");
System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")");
}
}
class Card {
String kind;
int number;
static int width = 100;
static int height = 250;
}
💻 출력
Card.width = 100
Card.height = 250
c1은 Heart, 7이며, 크기는 (100, 250)
c2는 Spade, 4이며, 크기는 (100, 250)
c1의 width와 height를 각각 50, 80으로 변경합니다.
c1은 Heart, 7이며, 크기는 (50, 80)
c2는 Spade, 4이며, 크기는 (50, 80)
-> 클래스 변수는 클래스이름.클래스변수 형태로 사용하자.
why? 인스턴스 변수롤 착각 할 수 있다.
-> 물론, 객체이름.인스턴스변수로 사용해도 클래스 변수의 값은 바뀐다.
why? 클래스 변수는 모든 인스턴스가 공유하고 있기 때문이다.
-> 반환값은 최대 한 개이다. 여러개를 반환하고 싶다면 배열이나 클래스를 로 반환하자.
-> 매개변수도 지역변수이다.
<예제 6-4 >
✍️ 입력
class Ex6_4 {
public static void main(String args[]) {
MyMath mm = new MyMath();
long result1 = mm.add(5L, 3L);
long result2 = mm.subtract(5L, 3L);
long result3 = mm.multiply(5L, 3L);
double result4 = mm.divide(5L, 3L);
System.out.println("add(5L, 3L) = " + result1);
System.out.println("subtract(5L, 3L) = " + result2);
System.out.println("multiply(5L, 3L) = " + result3);
System.out.println("divide(5L, 3L) = " + result4);
}
}
class MyMath {
long add(long a, long b) {
long result = a + b;
return result;
// return a + b; // 위의 두 줄을 이와 같이 한 줄로 간단히 할 수 있다.
}
long subtract(long a, long b) { return a - b; }
long multiply(long a, long b) { return a * b; }
double divide(double a, double b) {
return a / b;
}
}
💻 출력
add(5L, 3L) = 8
subtract(5L, 3L) = 2
multiply(5L, 3L) = 15
divide(5L, 3L) = 1.6666666666666667
<예제 6-5 >
✍️ 입력
class Ex6_5 {
public static void main(String[] args) {
System.out.println("Hello");
}
}
💻 출력
Hello
기본형 매개변수 : 변수의 값을 읽기만 할 수 있다.(read only)
참조형 매개변수 : 변수의 값을 읽고 변경할 수 있다.(read & write)
<예제 6-6 >
✍️ 입력
class Data { int x; }
class Ex6_6 {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x);
change(d.x);
System.out.println("After change(d.x)");
System.out.println("main() : x = " + d.x);
}
static void change(int x) { // 기본형 매개변수
x = 1000;
System.out.println("change() : x = " + x);
}
}
💻 출력
main() : x = 10
change() : x = 1000
After change(d.x)
main() : x = 10
-> 호출스택 그림으로 그릴 줄 알아야 함!
-> 'd.x'의 값이 변경된 것이 아니라, change메서드의 매개변수 x의 값이 변경된 것이다. 즉, 원본은 건들 수 없다. (기본형 - only read)
<예제 6-7 >
✍️ 입력
class Data2 { int x; }
class Ex6_7 {
public static void main(String[] args) {
Data2 d = new Data2();
d.x = 10;
System.out.println("main() : x = " + d.x);
change(d);
System.out.println("After change(d)");
System.out.println("main() : x = " + d.x);
}
static void change(Data2 d) { // 참조형 매개변수
d.x = 1000;
System.out.println("change() : x = " + d.x);
}
}
💻 출력
main() : x = 10
change() : x = 1000
After change(d)
main() : x = 1000
-> 매개변수가 참조형이므로 주소를 넘긴다.
-> 그림으로 그릴 수 있어야 한다!
<예제 6-8 >
✍️ 입력
class Data3 { int x; }
class Ex6_8 {
public static void main(String[] args) {
Data3 d = new Data3();
d.x = 10;
Data3 d2 = copy(d);
System.out.println("d.x ="+d.x);
System.out.println("d2.x="+d2.x);
}
static Data3 copy(Data3 d) {
Data3 tmp = new Data3(); // 새로운 객체 tmp를 생성한다.
tmp.x = d.x; // d.x의 값을 tmp.x에 복사한다.
return tmp; // 복사한 객체의 주소를 반환한다.
}
}
💻 출력
d.x =10
d2.x=10
-> 똑같다. 단지 주소를 반환할 뿐이다. 그림으로 그려서 이해해보자.
-> 클래스 메서드에서 인스턴스변수를 사용 할 수 없는 이유?
클래스 메서드는 객체 생성 없이 사용 할 수 있다. 이 때 객체는 인스턴스 변수의 묶음으로 간단히 설명 할 수 있다.
따라서 클래스 메서드 입장에서는 인스턴스 변수의 묶음이 존재하는지, 존재하지 않는지 알 수 없다.
<예제 6-9 >
✍️ 입력
class MyMath2 {
long a, b;
// 인스턴스 변수 a, b만을 잉요해서 작업하므로 매개변수가 필요없다.
long add() { return a + b; } // a, b는 인스턴스 변수
long subtract() { return a - b; }
long multiply() { return a * b; }
double divide() { return a / b; }
// 인스턴스 변수와 관계없이 매개변수만으로 작업이 가능하다.
static long add(long a, long b) { return a + b; } // a, b는 지역변수
static long subtract(long a, long b) { return a - b; }
static long multiply(long a, long b) { return a * b; }
static double divide(long a, long b) { return a / (double)b; }
}
class Ex6_9 {
public static void main(String args[]) {
// 클래스 메서드 호출. 인스턴스 생성없이 호출가능
System.out.println(MyMath2.add(200L, 100L));
System.out.println(MyMath2.subtract(200L, 100L));
System.out.println(MyMath2.multiply(200L, 100L));
System.out.println(MyMath2.divide(200L, 100L));
MyMath2 mm = new MyMath2(); // 인스턴스를 생성
mm.a = 200L;
mm.b = 100L;
// 인스턴스 메서드는 객체생성 후에만 호출이 가능함.
System.out.println(mm.add());
System.out.println(mm.subtract());
System.out.println(mm.multiply());
System.out.println(mm.divide());
}
}
💻 출력
300
100
20000
2.0
300
100
20000
2.0
1. 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용한는 것에 static을 붙인다.
-> 생성된 각 인스턴스는 서로 독립적이기 때문에 각 인스턴스의 변수(iv)는 서로 다른 값을 유지한다. 그러나 모든 인스턴스에서의 같은 값이 유지되어야 하는 변수는 static을 붙여서 클래스변수로 정의해야 한다.
2. 클래스 변수(static)는 인스턴스를 생성하지 않아도 사용 할 수 있다.
-> static이 붙은 변수(클래스 변수)는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문이다.
3. 클래스 메서드(static메서드)는 인스턴스 변수를 사용 할 수 없다.
-> 클래스메서드(static이 붙은 메서드)는 인스턴스 생성 없이 호출가능하므로 클래서 메서드가 호출되었을 때 인스턴스가 존재하지 않을 수 있다.
따라서, 클래스 메서드에서 인스턴스변수의 사용을 금지한다.
-> 반면에 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용한는 것이 언제나 가능하다. 인스턴스 변수가 존재한다는 것은 static변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.
4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
-> 인스턴스를 필요로 하지 않는다면 static을 붙이자. 메서드 호출시간이 짧아지므로 성능이 향상된다. static을 안 붙인 메서드(인스턴스메서드)는 실행 시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.
-> static메서드에서 인스터스 변수나 인스턴스 메서드를 사용 및 호출 할 수 없다.
-> why?
인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문이다.
<예제 6-10 >
✍️ 입력
class Ex6_10 {
public static void main(String args[]) {
MyMath3 mm = new MyMath3();
System.out.println("mm.add(3, 3) 결과:" + mm.add(3,3));
System.out.println("mm.add(3L, 3) 결과: " + mm.add(3L,3));
System.out.println("mm.add(3, 3L) 결과: " + mm.add(3,3L));
System.out.println("mm.add(3L, 3L) 결과: " + mm.add(3L,3L));
int[] a = {100, 200, 300};
System.out.println("mm.add(a) 결과: " + mm.add(a));
}
}
class MyMath3 {
int add(int a, int b) {
System.out.print("int add(int a, int b) - ");
return a+b;
}
long add(int a, long b) {
System.out.print("long add(int a, long b) - ");
return a+b;
}
long add(long a, int b) {
System.out.print("long add(long a, int b) - ");
return a+b;
}
long add(long a, long b) {
System.out.print("long add(long a, long b) - ");
return a+b;
}
int add(int[] a) { // 배열의 모든 요소의 합을 결과로 돌려준다.
System.out.print("int add(int[] a) - ");
int result = 0;
for(int i=0; i < a.length;i++)
result += a[i];
return result;
}
}
💻 출력
int add(int a, int b) - mm.add(3, 3) 결과:6
long add(long a, int b) - mm.add(3L, 3) 결과: 6
long add(int a, long b) - mm.add(3, 3L) 결과: 6
long add(long a, long b) - mm.add(3L, 3L) 결과: 6
int add(int[] a) - mm.add(a) 결과: 600
인스턴스를 생성할 때는 다음의 2가지 사항을 결정해야 한다.
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?
<예제 6-12 >
✍️ 입력
class Car {
String color; // 색상
String gearType; // 변속기 종류 - auto(자동), manual(수동)
int door; // 문의 개수
Car() {}
Car(String c, String g, int d) {
color = c;
gearType = g;
door = d;
}
}
class Ex6_12 {
public static void main(String[] args) {
Car c1 = new Car();
c1.color = "white";
c1.gearType = "auto";
c1.door = 4;
Car c2 = new Car("white", "auto", 4);
System.out.println("c1의 color=" + c1.color + ", gearType=" + c1.gearType+ ", door="+c1.door);
System.out.println("c2의 color=" + c2.color + ", gearType=" + c2.gearType+ ", door="+c2.door);
}
}
💻 출력
c1의 color=white, gearType=auto, door=4
c2의 color=white, gearType=auto, door=4
<예제 6-13 >
✍️ 입력
class Car2 {
String color; // 색상
String gearType; // 변속기 종류 - auto(자동), manual(수동)
int door; // 문의 개수
Car2() {
this("white", "auto", 4);
}
Car2(String color) {
this(color, "auto", 4);
}
Car2(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
class Ex6_13 {
public static void main(String[] args) {
Car2 c1 = new Car2();
Car2 c2 = new Car2("blue");
System.out.println("c1의 color=" + c1.color + ", gearType=" + c1.gearType+ ", door="+c1.door);
System.out.println("c2의 color=" + c2.color + ", gearType=" + c2.gearType+ ", door="+c2.door);
}
}
💻 출력
c1의 color=white, gearType=auto, door=4
c2의 color=blue, gearType=auto, door=4
-> 스택에서 지역변수는 빠르게 생기고 사라진다. 따라서 초기화를 매 번 해주게 되면 성능이 저하된다.
-> 초기화 해주지 않는다면 다른 메소드의 모르는 값으로 초기화 된다.
따라서, 어떤 값인지 알 수 없기 때문에 컴파일과정에서 =를 통해 변수에 넣을 수 없게 된다.
1. 클래스 변수(cv) 초기화 -> 인스턴스 변수(iv) 초기화
2. 자동 초기화 -> 명시적 초기화(간단) -> 초기화 블럭, 생성자(복잡)
-> 자동 초기화 : '0'으로 초기화
-> 간단 초기화 : '='으로 초기화
-> 복잡 초기화 블럭
1. {} (iv 초기화)
2. 생성자 (iv 초기화)
3. static {} (cv 초기화)
<예제 6-14 >
✍️ 입력
class Ex6_14 {
static {
System.out.println("static { }");
}
{
System.out.println("{ }");
}
public Ex6_14() {
System.out.println("생성자");
}
public static void main(String args[]) {
System.out.println("Ex6_14 bt = new Ex6_14(); ");
Ex6_14 bt = new Ex6_14();
System.out.println("Ex6_14 bt2 = new Ex6_14(); ");
Ex6_14 bt2 = new Ex6_14();
}
}
💻 출력
static { }
Ex6_14 bt = new Ex6_14();
{ }
생성자
Ex6_14 bt2 = new Ex6_14();
{ }
생성자
-> 예제가 실행되면서 Ex6_14이 메모리에 로딩될 때, 클래스 초기화 블럭이 가장 먼저 수행되어 'static {}'이 화면에 출력된다. 그 다음에 main메서드가 수행되어 Ex6_14의 인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 수행되고, 끝으로 생성자가 수행된다.
-> 클래스 초기화 블럭은 처음 메모리에 로딩될 때 한번만 수행되었지만, 인스턴스 초기화 블럭은 인스턴스가 생성될 때 마다 수행된다.
<예제 6-15 >
✍️ 입력
class Ex6_15 {
static int[] arr = new int[10];
static {
for(int i=0;i<arr.length;i++) {
// 1과 10사이의 임의의 값을 배열 arr에 저장한다.
arr[i] = (int)(Math.random()*10) + 1;
}
}
public static void main(String args[]) {
for(int i=0; i<arr.length;i++)
System.out.println("arr["+i+"] :" + arr[i]);
}
}
💻 출력
arr[0] :9
arr[1] :9
arr[2] :8
arr[3] :6
arr[4] :8
arr[5] :10
arr[6] :7
arr[7] :7
arr[8] :7
arr[9] :2
-> 명시적 초기화를 통해 배열 arr을 생성하고, 클래스 초기화 블럭을 이용해서 배열의 각 요소들을 random()을 사용해서 임의의 값으로 채우도록 했다.
-> 이처럼 배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화 작업을 할 수 없다. 이런 경우에는 추가적으로 클래스 초기화 블럭을 사용하도록 한다.
[출처] 자바의 정석 <기초편> (남궁 성 지음)