일반 변수와 객체를 가리키는 변수.
// 일반 변수 int score = 88; // 객체를 가리키는 변수 Cat cat1 = new Cat();
미묘하게 다른 듯하다. 어떤 차이가 있을까?
이를 알아보기 위해 아래 내용을 학습해보자.
레퍼런스 변수
기본형 변수와의 차이
static 키워드
클래스 변수
클래스 메소드
Reference variable
레퍼런스(reference) 변수는 객체를 가리킨다.
엄밀히 말하면 객체의 주소 정보를 담고 있다.// 객체 생성 후, 그 객체의 주소를 marine1 변수에 저장. 그 객체를 Marine 타입 으로 해석. Marine marine1 = new Marine("레이너", 80); // marine1 주소의 객체이 접근하여, stimpack() 동작을 실행 marine1.stimpack();
Difference from primitive variable
write type >> 레퍼런스 형은 대문자로 시작한다. 이와 반면, 원시형 변수는 소문자로 시작한다.
save type >> 레퍼런스 변수는 객체를 간접적으로 저장한다. 어떻게? 객체의 주소를 저장함으로써.
기본형(primitive) 변수는 값을 직접 저장한다.int score = 88; double weight = 73.6; boolean isMale = true; char alphabet = 'A';
adressing >> 객체는 하나의 변수에 담기에 너무 크다. 뿐만 아니라 그 크기가 유동적이다. 때문에 자바는 객체 접근을 위해 어드레싱(addresing) 방식 즉, 주소지를 통한 접근 방식을 취한다.
Static
static >> static은 “공유”라는 개념을 담은 키워드이다. static은 클래스의 필드와 메소드에 적용될 수 있다. static 필드는 클래스 변수, static 메소드는 클래스 메소드라 부른다.
class variable vs instance variable >> 클래스 변수(static 필드)는 클래스 영역에 존재한다. 객체 외부에 존재하므로 여러 객체가 공유에 좋다.
이와 반대로 non-static 필드는 “인스턴스 변수”라 부른다. 인스턴스 변수는 각 객체 내부에 존재한다.
클래스 변수와 인스턴수 변수 선언 예.public class Main { public static void main(String[] args) { Student std0 = new Student(2019122104, "Park"); Student std1 = new Student(2019206028, "Kim"); Student std2 = new Student(2019153237, "Lee"); } } class Student { // 클래스 변수 static int count = 0; // 인스턴스 변수 int id; String name; // 생성자 Student (int i, String s) { id = i; name = str; count++; } }
class method vs instance method >> 클래스 메소드(static 메소드)는 주체 객체 없이 실행된다. 간편한 기능 구현에 좋다.
그 대표적인 예로 Math.random()이 있겠다.// Math의 static 메소드 random()을 호출 double rand = Math.random();
이와 반면 인스턴스 메소드는 주체 객체를 통해 수행된다.
// 인스턴스 메소드를 수행하려면 먼저, 주체 객체가 생성되야 한다. Student std4 = new Student(2007122104, "Choi"); // std4 변수에 연결된 객체가 주체가 되어, 인스턴스 메소드를 수행 std4.study();
reference variable >> 레퍼런스 변수란, 객체를 가리키는 변수입니다.
example code >> 아래의 변수 e0는 레퍼런스 변수이며, 그 타입은 Employee 입니다. 따라서 e0는 Employee 객체를 가리킬 수 있습니다.int[] hours = { 2, 4, 3, 4, 5, 8, 8 }; Employee e0 = new Employee("직원", hours);
CODE
public class EmployeeTest { public static void main(String[] args) { // 배열 생성 int[] hours0 = { 2, 4, 3, 4, 5, 8, 8 }; int[] hours1 = { 7, 3, 4, 3, 3, 4, 4 }; int[] hours2 = { 3, 3, 4, 3, 3, 2, 2 }; int[] hours3 = { 9, 3, 4, 7, 3, 4, 1 }; int[] hours4 = { 3, 5, 4, 3, 6, 3, 8 }; int[] hours5 = { 3, 4, 4, 6, 3, 4, 4 }; int[] hours6 = { 3, 7, 4, 8, 3, 8, 4 }; int[] hours7 = { 6, 3, 5, 9, 2, 7, 9 }; // 객체 생성 Employee e0 = new Employee("직원0", hours0); Employee e1 = new Employee("직원1", hours1); Employee e2 = new Employee("직원2", hours2); Employee e3 = new Employee("직원3", hours3); Employee e4 = new Employee("직원4", hours4); Employee e5 = new Employee("직원5", hours5); Employee e6 = new Employee("직원6", hours6); Employee e7 = new Employee("직원7", hours7); // 객체 배열 만들기 Employee[] employees = { e0, e1, e2, e3, e4, e5, e6, e7 }; // 정보 출력 for (int i = 0; i < employees.length; i++) { employees[i].printTotalHours(); } } } // 직원 클래스 class Employee { // 필드 String name; // 이름 int[] hours; // 요일별 일한 시간 // 생성자 Employee(String str, int[] arr) { name = str; hours = arr; } // 메소드 void printTotalHours() { System.out.printf("%s -> %d 시간\n", name, totalHours()); } int totalHours() { int sum = 0; for (int i = 0; i < hours.length; i++) { sum += hours[i]; } return sum; } }
CODEpublic class PlayerTest { public static void main(String[] args) { // 점수 배열 생성 int[] points0 = { 10, 9, 9, 8 }; int[] points1 = { 9, 10, 9, 9 }; int[] points2 = { 10, 9, 10, 10 }; // 선수 객체 생성 Player p0 = new Player("Kim", points0); Player p1 = new Player("Lee", points1); Player p2 = new Player("Park", points2); // 객체 배열 만들기 Player[] players = { p0, p1, p2 }; // 선수별 총점 출력 for (int i = 0; i < players.length; i++) { players[i].printTotalPoints(); } } } class Player { // 필드 String name; // 이름 int[] points; // 점수 // 생성자 Player(String str, int[] arr) { /* 1. 생성자를 완성하세요. */ name = str; points = arr; } // 메소드 void printTotalPoints() { /* 2. 형식 문자열을 만드세요. */ System.out.printf("%s -> %d점\n", name, totalPoints()); } int totalPoints() { /* 3. 총점을 반환하세요. */ int sum = 0; for (int i = 0; i < points.length; i++){ sum += points[i]; } return sum; } }
instance in instance >> 한 객체 내부에 또 다른 객체들이 존재할 수 있습니다. 하나의 팀에 여러 선수들이 존재하는 것 같이 말이죠.
example code >> 이를 코드로 나타내면 아래와 같습니다.// 팀 class Team { String nation; // 국가 Player[] players; // 선수들(객체 배열) }
CODE
public class TeamTest { public static void main(String[] args) { // 선수 객체 생성 Player kim = new Player("Kim", new int[] { 9, 8, 10 }); Player lee = new Player("Lee", new int[] { 10, 9, 10 }); Player park = new Player("Park", new int[] { 8, 10, 9 }); Player Xiau = new Player("Xiau", new int[] { 10, 9, 10 }); Player Yu = new Player("Yu", new int[] { 8, 9, 10 }); Player Xui = new Player("Xui", new int[] { 8, 9, 9 }); // 객체 배열 만들기 Player[] koreaPlayers = { kim, lee, park }; Player[] chinaPlayers = { Xiau, Yu, Xui }; // 팀 객체 생성 Team korea = new Team("KOREA", koreaPlayers); Team china = new Team("CHINA", chinaPlayers); // 팀 점수 출력 korea.printTeamPoints(); china.printTeamPoints(); } } // 팀 클래스 class Team { // 필드 String nation; // 나라 Player[] players; // 선수들 // 생성자 Team (String str, Player[] arr) { nation = str; players = arr; } // 메소드 void printTeamPoints() { int sum = 0; for (int i = 0; i < players.length; i++) { sum += players[i].totalPoints(); } System.out.printf("%s -> %d points\n", nation, sum); } } // 선수 클래스 class Player { // 필드 String name; // 이름 int[] points; // 득점 현황 // 생성자 Player (String str, int[] arr) { name = str; points = arr; } // 메소드 int totalPoints() { int sum = 0; for (int i = 0; i < points.length; i++) { sum += points[i]; } return sum; } }
클래스 변수란, static 키워드가 필드에 적용된 것을 말합니다. 이와 반면, non-static 필드(static이 없는 일반적인 필드)는 인스턴스 변수라 합니다.
class Student { // 클래스 변수(static 필드) static int count = 0; // 인스턴스 변수(non-static 필드) int stdNum; String name; }
인스턴스 변수는 각 객체 내부에 존재하지만, 클래스 변수는 객체 밖, 클래스 영역에 존재합니다.
클래스 변수의 사용은, 아래와 같이 클래스 이름을 통해 접근 가능합니다.System.out.printf("총 학생 수: "); System.out.printf("%d\n", Student.count);
CODE
public class GalaxyTest { public static void main (String[] args) { // 5칸 크기의 객체 배열 생성 Galaxy[] phones = new Galaxy[5]; // 배열 속 객체 할당 for (int i = 0; i < phones.length; i++) { phones[i] = new Galaxy(); } // 모든 객체 정보 출력 for (int i = 0; i < phones.length; i++) { phones[i].print(); } System.out.println("========================="); /* 2. Galaxy 객체의 개수를 클래스 변수로 출력하세요.*/ System.out.printf("Galaxy 객체의 개수: %d", Galaxy.count); } } // 클래스 class Galaxy { // 필드(인스턴스 변수) String serialNum; // 일련번호 /* 1. 해당 필드를 클래스 변수화 하세요. */ static int count = 0; // 생성자 Galaxy() { count++; char c = randomAlphabet(); // A ~ Z 중 택1 serialNum = String.format("%c-%d", c, count); } // 메소드 char randomAlphabet() { return (char) ('A' + Math.random() * 26); // A to Z } void print() { System.out.printf("Galaxy { serialNum: %s }\n", serialNum); } }
CODE
public class FishBreadTest { public static void main(String[] args) { // 객체 배열 생성 FishBread[] breads = new FishBread[4]; /* 1. 붕어빵 객체를 팥/고구마/치즈/슈크림 순으로 만드시오. */ breads[0] = new FishBread(0); // 팥 붕어빵 breads[1] = new FishBread(1); // 고구마 붕어빵 breads[2] = new FishBread(2); // 치즈 붕어빵 breads[3] = new FishBread(3); // 슈크림 붕어빵 // 모든 붕어빵 객체 정보 출력 for (int i = 0; i < FishBread.count; i++) { breads[i].print(); } System.out.println("=============="); /* 2. 전체 붕어빵 객체 수를 출력하시오. */ System.out.printf("붕어빵 객체 수: %d", FishBread.count); } } class FishBread { // 인스턴스 변수(non-static 필드) String contents; // 내용물 // 클래스 변수(static 필드) static int count; // 붕어빵 객체 수 // 생성자 public FishBread(int n) { // 내용물 선택 - 0: 팥, 1: 고구마, 2: 치즈, 3: 슈크림 String[] arr = {"팥", "고구마", "치즈", "슈크림"}; contents = arr[n]; // 붕어빵 객체 수 카운팅 FishBread.count++; } // 메소드 void print() { System.out.printf("[%s] 붕어빵\n", contents); } }
class method vs instance method >> 클래스 메소드란, static 적용된 메소드입니다. 이와 반면 static이 없으면 인스턴스 메소드가 됩니다.
// 클래스 메소드(static 메소드) static void callClassMethod() { System.out.println("스태틱 메소드 호출!"); } // 인스턴스 메소드(non-static 메소드) void callInstanceMethod() { System.out.println("인스턴스 메소드 호출!"); }
subject of method >> 이 두 메소드의 차이는 “주체 객체가 있는가?”의 여부입니다. 클래스 메소드의 경우, 주체 객체 없이 클래스명으로 호출됩니다.
// Math의 클래스 메소드 random() 호출 예 double rand = Math.random();
반면 인스턴스 메소드는 먼저 주체 객체를 생성해야만 하고, 이를 통해 메소드 호출이 이루어집니다.
// 주체 객체 생성 Hero h1 = new Hero("닥터 스트레인지", 80); // 주체 객체를 통한 인스턴스 메소드 호출 h1.teleport();
CODE
public class CircleTest { public static void main(String[] args) { // 객체 생성 Circle c1 = new Circle(0, 0, 3); // 중심(0,0) - 반지름3 Circle c2 = new Circle(2, 3, 4); // 중심(2,3) - 반지름4 /* 1. 클래스 메소드를 호출하여 원의 넓이를 구하세요. */ double area1 = Circle.area(c1); double area2 = Circle.area(c2); // 결과 출력 System.out.printf("%s => 넓이: %.2f\n", c1.toStr(), area1); System.out.printf("%s => 넓이: %.2f\n", c2.toStr(), area2); } } class Circle { // 필드 int x; // 원의 중심 - X 좌표 int y; // 원의 중심 - Y 좌표 int r; // 반지름 // 생성자 Circle(int centerX, int centerY, int radius) { x = centerX; y = centerY; r = radius; } // 인스턴스 메소드 String toStr() { return String.format("Circle { 중심: (%d, %d), 반지름: %d }", x, y, r); } // 클래스 메소드 static double area(Circle c) { // 원의 넓이 = 원주율 x 반지름 x 반지름 return Math.PI * c.r * c.r; } }
CODEpublic class PointTest { public static void main(String[] args) { // 객체 생성 Point p1 = new Point(0, 0); Point p2 = new Point(3, 4); // 거리 계산 double dist = Point.distance(p1, p2); // 결과 출력 System.out.printf("두 점 A%s, B%s 사이의 거리: %.2f", p1.toStr(), p2.toStr(), dist); } } class Point { /* 1. 필드를 만드시오. */ int x; int y; /* 2. 생성자를 정의하시오. */ Point(int X, int Y){ x = X; y = Y; } /* 3. 객체 정보를 문자열로 반환하는 인스턴스 메소드를 만드시오. */ String toStr(){ return String.format("(%d, %d)", x, y); } /* 4. 두 점 사이의 거리를 반환하는 클래스 메소드를 만드시오. */ static double distance(Point A, Point B) { double dist = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)); return dist; } }