Notice
Recent Posts
Recent Comments
Link
nathan_H
[Java] 빠르게 정리하는 자바 클래스 본문
들어가기전
객체 개념
- 물리적으로 존재하는 것(자동차, 책 등등)
- 추상적인 것 중 자신의 속성과 동작을 가지는 모든 것
- 객체는 필드과 메소드로 구성되어 자바 객체 모델링이 가능함
- 현실 세계의 객체를 소프트웨어의 객체로 모델링 하는 과정
객체 지향 프로그래밍
-
캡슐화
https://docsplayer.org/104244489-Chapter-01-html.html
- 객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것
- 외부 객체는 객체 내부 구조를 알지 못하며 객체가 노출해 제공하는 필드와 메소드만 이용 가능
- 필드와 메소드를 캡슐화 하여 보호하는 이유는 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하기 위함
- 자바 언어는 캡슐화된 멤버를 노출 시킬 것인지 숨길 것인지 결정하기 위해 접근 제한자(Access Modifier) 사용
-
상속
- 상위(부모)객체의 필드와 메소드를 하위(자식) 객체에게 물려 주는 것
- 하위 객체는 상위 객체를 확장해서 추가적인 필드와 메소드를 가질 수 있음
- 상속의 효과
- 반복된 코드의 중복을 줄임
- 유지 보수의 편리성 제공
- 객체의 다형성 구현
-
다형성
- 같은 타입이지만 실행 결과가 다양한 객체를 대입할 수 있는 성질
- 부모 타입에는 모든 자식 객체가 대입
- 인터페이스 타입에는 모든 구현 객체가 대입
- 효과
- 객체를 부품화
객체 vs 클래스
- 현실세계 : 설계도 → 객체
- 자바 : 클래스 → 객체
- 자바에서의 객체는 개발자가 클래스를 통해 설계를 진행한 후 클래스를 인스턴스화 시켜 실제 사용이 이루어짐
- 하나의 객체는 여러 개의 인스턴스를 만들 수 있음
자바 클래스 선언
- 소스 파일당 하나의 클래스를 선언하는 것이 관례
- 두 개 이상의 클래스도 선언 가능하지만 파일당 하나가 관례
- 소스 파일 이름과 동일한 클래스만 public으로 선언 가능
// Nathan.java public Nathan { String name; int age; }
클래스 생성
- new 연산자와 함께 힙 메모리에 생성이 됨
- new 연산자는 객체를 생성한 후, 객체 생성 번지를 넘겨줌
Nathan n1 = new Nathan();
- 그리고 객체를 생성하고 저장되는 변수의 값은 객체의 주소를 저장하기 때문에 변수는 객체를 "참조"하게 됨

https://programmer-seva.tistory.com/76
스택 영역
- 함수가 호출될 때 메모리에 할당, 지역 변수, 매개변수
public class StackMemoryTest { public static void m1(int a) { m2(++a); System.out.printf("m1() : %d \n", a); } public static void m2(int a) { m3(++a); System.out.printf("m2() : %d \n", a); } public static void m3(int a) { ++a; System.out.printf("m3() : %d \n", a); } public static void main(String[] args) { int a = 20; m1(a); System.out.printf("main() : %d \n", a); } }
결과
m3() : 23 m2() : 22 m1() : 21 main() : 20
- m1을 호출 한 후 main함수에 a 값을 찍으면 힙 영역으로 인해 값이 바뀌지 않고 그대로 출력이 됨
- m1, m2, m3도 마찬가지로 매개변수로 받은 값이 그대로 찍힘
힙 영역
- 함수 호출이 끝나도 유지, 참조 형태의 변수, 인스턴스 변수, 객체, 배열
public class HeapMemoryTest { public static int[] m1(int a) { int[] arr = m2(a+1); arr[2] = a; return arr; } private static int[] m2(int a) { int[] arr = m3(a+1); arr[1] = a; return arr; } private static int[] m3(int a) { int[] arr = new int[3]; arr[0] = a; return arr; } public static void main(String[] args) { int[] arr = m1(100); for (int i = 0; i < arr.length; i++) { System.out.printf("arr[%d] = %d\n", i, arr[i]); } } }
출력
arr[0] = 102 arr[1] = 101 arr[2] = 100
- 위 예시는 배열을 참조해서 실행이 되기 때문에 arr의 값이 m1, m2, m3로 인해 저장된 값으로 출력이 이루어짐
클래스 구성요소
필드
- 객체의 "데이터"가 저장되는 곳
- 객체의 "상태"라고도 표현
필드의 내용
- 객체의 고유 데이터
- 객체가 가져야 할 부품 객체
- 객체의 현재 상태 데이터
public class Car { String company; // 고유 데이터 int rmp; // 상태 Engine engine; // 부품 객체 }
- 위 코드 예시를 들면 차라는 객체에는 회사라는 고유 데이터 값과 rmp이라는 상태, Engine이라는 부품 객체를 포함해 데이터가 저장이 이루어질 수 있음
사용 예시
public class Car { String company = "tesla"; String model = "ModelS"; String color = "black"; int maxSpeed = 350; public static void main(String[] args) { Car nathanCar = new Car(); System.out.println("Company : " + nathanCar.company); System.out.println("Model Name : " + nathanCar.model); System.out.println("Color : " + nathanCar.color); System.out.println("Max speed : " + nathanCar.maxSpeed); } }
결과
Company : tesla Model Name : ModelS Color : black Max speed : 350
필드의 기본 초기값

https://m.blog.naver.com/PostView.nhn?blogId=heartflow89&logNo=220956313502
public class FieldInitValue { //필드 byte byteField; short shortField; int intField; long longField; boolean booleanField; char charField; float floatField; double doubleField; int[] arrField; String referenceField; public static void main(String[] args) { FieldInitValue fiv = new FieldInitValue(); System.out.println("ByteField : " + fiv.byteField); System.out.println("ShortField : " + fiv.shortField); System.out.println("IntField : " + fiv.intField); System.out.println("LongField : " + fiv.longField); System.out.println("BooleanField : " + fiv.booleanField); System.out.printf("charField: \\u%04X%n", (int)fiv.charField); System.out.println("floatField: " + fiv.floatField); System.out.println("doubleField: " + fiv.doubleField); System.out.println("arrField: " + fiv.arrField); System.out.println("referenceField: " + fiv.referenceField); } }
출력
ByteField : 0 ShortField : 0 IntField : 0 LongField : 0 BooleanField : false charField: \u0000 floatField: 0.0 doubleField: 0.0 arrField: null referenceField: null
생성자
- new 연산자에 의해 호출되어 객체의 초기화 담당
- 필드의 값 설정
- 메소드 호출해 객체를 사용할 수 있도록 준비하는 역할 수행
생성자 종류
기본 생성자
- 모든 클래스는 생성자가 반드시 존재하며 하나 이상 가질 수 있음
- 생성자 선언을 생략하면 컴파일러는 다음과 같은 기본 생성자 추가
생성자 선언
- 개발자가 직접 생성하는 생성자로 컴파일러는 컴파일 시 선언된 생성자가 있으면 기본 생성자를 선언하지 않음
- 초기화 값 없이 선언된 필드는 객체를 생성될 때 기본값으로 자동 설정되는데, 특정 값으로 필드를 초기화 할 수도 있음
- 매개 변수와 필드명이 같은 경우 this 사용
People p1 = new People("nathan", 23); People p2 = new People("홍길동", 24);
그렇다면 생성자 다양화 왜 필요할까?
- 객체 생성할 때 외부 값으로 객체를 초기화 할 필요
- 외부 값이 어떤 타입으로 몇 개가 제공될 지 모름(생성자도 다양화)
- 이로 인해 자바는 생성자 오버로딩 기능을 제공해 생성자 다양화를 시킬 수 있음
- 오버로딩 : 매개변수의 타입, 개수, 순서가 다른 생성자를 여러 개 선언
package object.field; public class Car { String company = "tesla"; String model = "ModelS"; String color = "black"; int maxSpeed = 350; public Car(String company) { this.company = company; } public Car(String model, String color) { this.model = model; this.color = color; } public Car(String model, String color, int maxSpeed) { this.model = model; this.color = color; this.maxSpeed = maxSpeed; } }
메소드
- 객체의 동작을 하는 역할
- 객체의 "행위"라고도 표현
메소드 선언
리턴타입 메소드이름 (매개변수, ..) { // 실행 코드 작성 }
public class Calculator { void powerOn() { System.out.println("Power On!"); } int plus(int x, int y) { return x + y; } double divide(int x, int y) { if (y == 0) { throw new IllegalArgumentException("0으로 나눌 수 없음."); } return x / y; } void powerOff() { System.out.println("Power Off.."); } public static void main(String[] args) { Calculator calculator = new Calculator(); calculator.powerOn();; System.out.println("result 1 : " + calculator.plus(5 ,6)); byte x = 10; byte y = 4; System.out.println("result 2 : " + calculator.divide(x, y)); calculator.powerOff(); } }
결과
Power On! result 1 : 11 result 2 : 2.0 Power Off..
메소드 오버로딩
- 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것
- 하나의 메소드 이름으로 다양한 매개변수 값을 받기 위해 사용
- 오버로딩 조건 : 매개변수의 타입, 개수, 순서가 달라야 함
class Nathan { void printNathan() { } void printNathan(int weight) { } void printNathan(int age) { } void printNathan(int age, int weight) { } }
인스턴스 멤버와 this
- 인스턴스 멤버
- 객체마다 가지고 있는 필드와 메소드
- 인스턴스 멤버는 객체 소속 멤버이기 때문에 객체 없이 사용 불가능
this
키워드- 객체(인스턴스) 자신의 참조(번지)를 가지고 있는 키워드
- 객체 내부에서 인스턴스 멤버임을 명확히 하기 위해 this 사용
- 매개변수와 필드명이 동일할 때 인스턴스 필드임을 명확히 하기 위해 사용
예시 코드
class Nathan { int age; int height; Nathan(int age, int height) { this.age = age; this.height = height; }
정적 멤버와 static
정적 멤버
- 클래스에 고정된 필드와 메소드
- 정적 필드, 정적 메서드
- 정적 멤버는 클래스에 소속된 멤버
- 객체 내부에 존재하지 않고, 메소드 영역에 존재
- 정적 멤버는 객체를 생성하지 않고 클래스로 바로 접근해 사용
public class Calculator { static double pi = 3.14159; static int plus(int x, int y) { return x + y; } static int minus(int x, int y) { return x - y; } public static void main(String[] args) { double result1 = 10 * 10 * Calculator.pi; int result2 = Calculator.plus(10, 5); int result3 = Calculator.minus(10, 5); System.out.println("result 1 : " + result1); System.out.println("result 2 : " + result2); System.out.println("result 3 : " + result3); } }
인스턴스 멤버 선언 vs 정적 멤버 선언
-
필드
-
인스턴스 필드 : "객체 마다" 가지고 있어야 할 데이터
-
정적 필드 : 공용적인 데이터
public class Calculator { static double pi = 3.14159; // 계산기에 사용되는 pi 값은 항상 동일 String color; // 계산기별 색은 상이할 수 있음 } -
정적 필드의 경우 공용적으로 사용되는 데이터이기 때문에 외부에서 해당 정적 필드를 사용 시 주의를 해야함
- 정적변수 사용 시
final
을 붙여 변경 불가능하게 해줄 수도 있음
- 정적변수 사용 시
-
-
메소드
- 인스턴스 메소드 : 인스턴스 필드로 작업해야 할 메소드
- 정적 메소드 : 인스턴스 필드로 작업하지 않는 메소드
- 정적 메소드의 경우 객체 내부에 존재하는 것이 아니기 때문에 인스턴스 필드에 접근하지 못함!
정적 초기화 블록
- 클래스가 메소드 영역에서 로딩될 때 자동으로 실행되는 블록
- 정적 필드의 복잡한 초기화 작업과 정적 메소드 호출 가능
- 클래스 내부에 여러 개가 선언되면 선언된 순서대로 실행
- 정적 초기화가 왜 필요한가?
- 정적 필드에 초기화 작업이 필요한 경우가 존재할 수 있음
- 정적 필드의 경우 객체 생성 없이도 사용할 수 있어야 하므로 생성장에서 초기화 작업을 할 수 없음
- 생성자는 객체 생성 시에만 실행되기 때문에
- 그래서 자바는 정적 블록을 통해 초기화 작업 기능을 제공
public class Car { static String company = "tesla"; static String model = "ModelS"; static String info; static { info = company + " - " + model; } }
정적 메소드와 정적 블록 사용시 주의할 점
- 정적 메소드와 블록은 객체가 없이도 실행이 가능하기 때문에 블록 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없음
- 당연히 객체 자신을 참조하는 this도 사용 불가
싱글톤
- 프로그램을 짤 때 가끔 단 하나의 객체만 만들도록 보장하는 경우가 존재하는데, 이 객체를 바로 싱글톤이라고 함
- 싱글톤 만드는 방법
- 외부에서 new 연산자로 생성자를 호출할 수 없도록 막기
private
접근 제한자를 생성자 앞에 붙임
- 클래스 자신의 타입으로 정적 필드 선언
- 자신의 객체를 생성해 초기화
private
접근 제한자 붙여 외부에서 필드 값 변경 불가능하도록
- 외부에서 호출될 수 있는 정적 메소드인 getInstance() 선언
- 정적 필드에서 참조하고 있는 자신의 객체 리턴
- 외부에서 new 연산자로 생성자를 호출할 수 없도록 막기
예시 코드
public class Singleton { private static Singleton singleton = new Singleton(); private Singleton() { } static Singleton getInstance() { return singleton; } public static void main(String[] args) { // Singleton obj1 = new Singleton(); // 컴파일 에러 // Singleton obj2 = new Singleton(); // 컴파일 에러 Singleton obj1 = Singleton.getInstance(); Singleton obj2 = Singleton.getInstance(); if (obj1 == obj2) { System.out.println("같은 Singleton 객체"); } else { System.out.println("다른 Singleton 객체"); } } }
출력
같은 Singleton 객체
final 필드와 상수
final 필드
- 최종적인 값을 가지고 있는 필드로써 값을 변경할 수 없는 필드
- final 필드의 딱 한 번의 초기값 지정 방법
- 필드 선언 시
- 생성자
- 생성자는 final 필드의 최종 초기화를 마쳐야 하는데, 만약 초기화 되지 않는 final 필드를 남겨두면 컴파일 에러가 남
성공
public class Person { final String nation = "Korea"; final String ssn; String name; int age = 25; public Person(String ssn, String name) { this.ssn = ssn; this.name = name; } }
컴파일 에러
package object.staticfinal; public class Person { final String nation = "Korea"; final String ssn; String name; int age = 25; public Person(String name) { this.name = name; } }
상수 (static final)
- 상수 : 불변의 값 (원주율 파이, 지구의 무게 및 둘레 등등)
- final
- 객체마다 가지는 불변의 인스턴스 필드
- final을 상수라고 부르지 않는 이유
- 불변의 값은 객체마다 저장할 필요가 없는 공용성을 띄고 있으며, 여러 가지 값으로 초기화될 수 없기 때문
- final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수가 될 수 없음
- 상수 (static final)
- 객체마다 가지고 있지 않음
- 메소드 영역에 클래스 별로 관리되는 불변의 정적 필드
- 공용 데이터로서 사용
- final
- 상수 이름은 전부 대문자로 작성
- 다른 단어가 결합되면 _로 연결
enum
이 나오기 전에static final
로 상수를 표현함- enum이란?
예제 코드
public class Earth { static final double EARTH_RADIUS = 6400; static final double EARTH_SURFACE_AREA; static { EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS; } public static void main(String[] args) { System.out.println("지구의 반지름 : " + Earth.EARTH_RADIUS + " km"); System.out.println("지구의 표면적 : " + Earth.EARTH_SURFACE_AREA + " km^2"); } }
출력
지구의 반지름 : 6400.0 km 지구의 표면적 : 5.147185403641517E8 km^2
접근 제한자

- 자바에서는 접근 제한자를 활용해 객체의 메소드 생성자, 필드들에 대해 접근을 제한할 수 있음
- 클래스 제한 : 다른 패키지에서 클래스를 사용하지 못하도록 함
- 생성자 제한 : 클래스로부터 객체를 생성하지 못하도록 함
- 필드와 메소드 제한 : 특정 필드와 메소드를 숨김 처리
- 초반에 언급한 객체 지향 프로그램에서 캡슐화를 함에 있어 위 접근 제한자를 사용해 진행
- 종류는 위 그림과 같이 4가지 존재
마무리
- 본 내용은 자바에서의 클래스의 "기본 개념"에 대해서 정리한 내용이다. 위 내용에서 몇 가지는 포스팅 하나 각잡고 해도 부족할 정도로 깊게 들어갈 수 있는 내용들도 많다. 또한 객체 지향 프로그램에 있어 다양한 의견과 식견이 존재하기 때문에 추후에 "객체 지향"이라는 주제로 포스팅도 해볼까 한다.
'Programming Laguage > Java' 카테고리의 다른 글
[Java] Java 애플리케이션에서 JVM 구조와 실행 과정 (0) | 2020.05.05 |
---|---|
[Java] 상속 핵심 개념과 추상 클래스 (0) | 2020.05.01 |
[Java] Call By Value와 Call By Reference (0) | 2020.04.27 |
[Java] Enum을 잡아보자 (0) | 2020.04.25 |
Class member value encapsulation. (0) | 2019.05.21 |
Comments