클래스

서현서현·2022년 2월 22일
0

JAVA

목록 보기
9/27
post-thumbnail

💐 객체와 클래스

🚨 클래스(필드+메소드) = 설계도 / 클래스로부터 만들어진 객체 = 인스턴스

우선, 클래스를 선언해보자.

💐 클래스 선언

// 클래스가 여러개일 경우, public 접근제한자는 파일이름과 같은 클래스에만 붙일 수 있다
public class Car{
}

이 클래스(설계도)를 이용하여 객체, 즉 인스턴스를 만들어보자!

💐 객체생성과 클래스변수

  • new는 클래스로부터 객체를 생성하는 연산자
  • new는 힙에 객체를 생성하고 번지를 리턴함. 이 주소를 클래스변수에 저장해두는것!
// 클래스로부터 객체 생성 - car 클래스 변수가 Car객체를 참조한다
public class carExample{
	public static void main(String[] args) {
		Car car1 = new Car();
// 즉 new를 통해 힙에 Car객체를 만들고, 그 주소를 car변수에 할당한것
		Car car2 = new Car();
// 서로 다른 Car객체를 car1과 car2가 참조하고 있는것이다. 서로 독립적임
	}
}

Car 클래스와 CarExample 클래스의 용도차이

자 이제 두가지 클래스를 만들었다. 클래스는 라이브러리(API)용이고 하나는 실행용이다.

실행클래스에선 프로그램을 실행하기위한 main() 메소드를 제공한다.

이렇게 용도가 다른 클래스들은 어떻게 이루어져 있을까?

💐 클래스의 구성멤버

  • 클래스는 필드, 생성자, 메소드로 나뉘어진다

💐 필드

String campany = "현대자동차";
int maxSpeed = 100;

이런식으로 현재 데이터를 저장 해준다. 만약 저장을 따로 하지 않는다면 기본 초기값이 할당된다.

기본타입은 0, 논리타입은 false, 참조타입은 null이 들어간다.

약간 변수 선언이랑 비슷함! 근데 변수라고는 절대 안부른다.

외부 CarExample 클래스에서 Car 클래스의 필드를 바꾸고자 할땐 어떻게 할까?

필드는 객체에 소속된 데이터이다. 객체가 존재해야 값도 바꿀 수 있으므로 우선 클래스로부터 객체를 생성 한 뒤 필드를 사용한다.

public class Car{
	String company = "현대자동차";
	int maxSpeed = 350; 
}
public class CarExample{
	public static void main(String[] args) {
		Car mycar = new Car();  //새로운 Car 객체를 생성해 그 주소를 mycar에 할당
		mycar.maxSpeed = 60;  //이렇게 하면 현자동차는 그대로, 스피드는 60으로 변경된다
	}
}

💐 생성자

  • new 연산자를 이용해 클래스로부터 객체를 생성할때 호출되는것
  • 객체초기화를 담당 (객체초기화란 ? 필드를 초기화하거나 메소드를 호출)
  • new로 인해 생성자가 실행되면 힙에 객체가 생기고 번지수를 리턴하는것

생성자 선언

우선, Car 생성자를 다시한번 호출해보자

Car mycar = new Car("그랜저", "검정", 300); //3개의 매개값을 준다고 가정한다.

매개값을 받아야 하기 때문에 매개변수를 선언한다.

public class Car{
	Car(String model, String color, int maxSpeed) {...}
}

반대로, Car 클래스가 다음과같이 선언되어있다면 Example클래스에선 반드시 세 매개값을 호출하는 객체를 만들어야한다. 위와같은 생성자가 존재하므로 기본생성자는 자동으로 호출되지 않는다. 따라서 Car mycar = new Car(); 과 같은 객체는 불가능하다!

필드와 생성자에 대해 알았으니, 필드초기화 이야기를 해보겠다.

필드초기화

필드 초기화 방법은 두가지가 존재한다.

  1. 필드 선언시 초기값을 주는 방법 (이러면 동일한 클래스로부터 생성된 객체는 모두 같은값)
  2. 생성자에서 초기값을 주는 방법

1번은 필드 선언할때 값을 같이주면 되고, 2번에 대해 설명하자면

public class Korean{
	//필드
	String nation = "대한민국";
	String name;
	String ssn;

	//생성자
	public Korean(String name, String ssn){
	this.name = name;		
	this.ssn = ssn;
	}
}

이렇게 선언하고 KoreanExample로 실행하면 생성자로 인해 제공된 name과 ssn으로 초기화된다.

+) this

  • 필드와 매개변수 이름이 같으므로 필드를 가리키기 위해 this를 붙여준다.

생성자 오버로딩

  • 외부에서 제공하는 데이터가 다양하므로 생성자도 다양하다.
  • 매개변수의 타입과 개수, 순서가 다르면 다른 객체로 처리하도록 하는것이 오버로딩이다.
🚨 타입, 개수, 순서가 같으면 매개변수 이름만 바꾼다고해서 오버로딩이 되지 않는다
public class Car{

	String company = "현차";
	String color;
	String model;
	int maxSpeed;

	//생성자
	Car(){}
	Car(String model){
		this.model = model;
	}
	Car(String model, String color){
		this.model = model;
		this.color = color;
	}
} // 이렇게 선언하고, CarExample에서 주어지는 매개변수에 따라 생성자가 다르게 호출된다

다른생성자 호출 : 중복코드 줄이기(this)

public class Car{

	String company = "현차";
	String color;
	String model;
	int maxSpeed;

	//생성자
	Car(){}
	Car(String model){
		this(model,color)
	}
	Car(String model, String color){
		this.model = model;
		this.color = color;
	}
} //이런식으로 가장 긴 코드를 써주고, 나머지 생성자는 this안에 넣어 중복코드를 줄일 수 있다

이제 클래스의 구성멤버중 마지막인 메소드에 대해 알아보겠다.

💐 메소드

  • 객체의 동작에 해당하는 중괄호블럭. 이 블럭의 이름이 메소드 이름이며 호출하면 실행된다.
[형식]
리턴타입 메소드이름 ([매개변수선언,...]) {메소드 실행블록}
// 메소드 선언
public class Calculator{
	void powerOn() {
		system.out.println("전원을 켭니다");
	}
	int plus(int x, int y){
		int result = x+y;
		return result;
	}

}
//메소드 호출
public class CalculatorExample{
	public static void main(String[] args) {
		Calculator myCalc = new Calculator();
		myCalc.powerOn();
		int result1 = myCalc.plus(5,6);
		system.out.println("result1: "+result1)
	}
}

매개변수의 개수를 모를경우

  • 배열을 사용하는 방법과 가변인수를 사용하는 방법이 있다!
public class Computer {
	int sum(int ...values){
		int sum = 0;
		for(int i=0; i<values.length;i++){
			sum += values[i];
		}
		return sum;
	}
}
public class Computer {
	public static void main(String[] args) {

		Computer myCom = new Computer();{
			int[] values1 = {1,2,3};
			int result1 = myCom.sum1(values);
		}

		int result2 = muCom.sum1(new int[] {1,2,3,4,5});
	
	}
}

💐 return문

  • 리턴값이 없는 메소드는 void를 리턴타입으로 사용한다.
  • return;을 사용하면 리턴값을 저장하는것이 아니라 메소드 실행을 강제종료시킴

💐 메소드 호출

  • 클래스 내/외부에서 호출 할 수 있다.
  • 내부에서 호출하면 단순한 메소드 이름으로 호출하고, 외부에서 호출할땐 클래스로부터 객체를 생성한 뒤 참조변수를 이용하여 메소드를 호출한다.
// 내부에서 호출
double avg(int x, int y) {
	double sum = plus(x,y);
	double result = sum/2;
}

void execute() {
	double result = avg(7,10);
}
// 외부에서 호출
public class CalculatorExample{
	public static void main(String[] args) {
		Calculator myCalc = new Calculator();
		myCalc.execute();
	}
}

Calculator myCalc= new Calculator()→ myCalc.execute() → void execute() → double avg(int x, int y)

💐 메소드 오버로딩

  • 매개변수 타입, 순서, 개수 중 하나가 달라야됨
  • 방법은 생성자오버로딩과 같다

💐 인스턴스 멤버와 정적 멤버

  • 인스턴스는 클래스로부터 만들어진 객체, 즉 new로 만들어진 객체였다. 인스턴스 멤버는 바로 그 객체 안에 든 필드와 메소드같은 구성요소를 말한다. 객체마다 가지고 있다.
  • 정적멤버는 클래스에 위치시키고 객체들이 공유하고 있는 멤버를 말한다. 메모리 낭비를 줄인다

인스턴스 멤버와 this

  • 예를 하나 들어보자. 같은 Car 객체로부터 myCar과 yourCar 객체를 생성했다. 그런데 Car객체의 인스턴스 필드, gas값은 전에 배운대로 힙 영역에 존재해 스택영역에서 myCar와 yourCar가 참조중이다. 그런데 Car객체의 인스턴스 메소드, (void)setSpeed는 힙 영역이 아니라 메소드 영역에 존재한다. 왜일까? → 메소드는 코드블록이므로 객체마다 동일한 코드블록을 가질 필요가 없기 때문이다!
  • 생성자와 메소드의 매개변수이름이 필드와동일할때 인스턴스멤버인 필드임을 명시할때 사용

💐 정적멤버와 static

  • 클래스에 고정된 멤버, 객체를 생성하지 않고 사용할수있는 필드와 메소드이다.

정적멤버 선언

[정적 필드 예시]
public class Calculator{
	Stirng color; // 계산기별로 색 다르니 그냥 스트링
	static double pi = 3.14; // 파이는 모두 똑같으므로 static
}
[정적 메소드 예시]
public class Calculator{
	String color;
	void setColor(String color) { this.color = color; }
	static int plus(int x, int y) { return x+y; }
}

정적멤버 사용

// 위와같이 선언 되었다면,
double result1 = 10*10*Calculator.pi;
int result2 = Calculator.plus(10,5);
// 이렇게 사용 할 수 있다
// 또한 원칙적으로는 클래스.필드 or 클래스.메소드 라고 써야하나
// 객체참조 변수로도 접근은 가능하다. (추천x) (ex. myCalc.pi)

정적멤버 사용시 주의할점

  • 객체가 없어도 실행된다는 특징 때문에 정적 메소드 실행시엔 이들 내부에 인스턴스(new를통해 만드는객체)필드나 인스턴스메소드 사용 불가능. 객체 자신의 참조인 this역시 마찬가지
public class ClassName{
	// 인스턴스 필드와 메소드
	int field1;
	void method1() {...}
	
	// 정적필드와 메소드
	static int field2;
	static void method2() {...}

	//정적메소드
	static void method3() {
		// 컴파일에러
		this.field1 = 10;
		this.method1();
		// 컴파일 에러
		this.field2 = 10;
		this.method2();
	}
}

정적메소드에서 인스턴스 멤버를 사용하려면?

// 정적메소드 안에 객체를 생성하고 참조변수로 접근
static void method3() {
	className obj = new ClassName();
	obj.field1=10;
	obj.method2();
}

💐 싱글톤

  • 전체프로그램에서 단 하나의 객체만 만들도록 보장
  • 객체 생성을 막도록 new를 호출할 수 없도록 해야하고, 이걸 위해서 private를 이용
  • 정적 필드를 하나 생성하고, 객체를 생성해 초기화한다. 그리고 이 필드를 private를 붙여 외부에서 필드값 변경을 못하도록 막는다. 대신 외부에서 호출할 수 있는 정적메소드 getInstance()를 선언하고, 정적필드에서 참조하고있는 자신의 객체를 리턴
public class Singleton{
	private static Singleton singleton = new Singleton(); //정적필드..
	private Singleton() {}     //생성자가 private라서 객체 생성x
	static Singleton getInstance() { //대신 정적메소드엔 접근 가능!
		return singleton;                 // 새객체x, 이 필드에서 참조하는 자신의 객체 리턴
	}
}
public class SingletonExample{
 public static void main(String[] args){
		/* Singleton obj1 = new Singlerton();
			 Singleton obj2 = new Singlerton(); */   //컴파일에러. 생성자 접근 안돼서 뉴객체 생성 안되니까 당연
	Singleton obj1 = Singleton getInstance();
	Singleton obj2 = Singleton getInstance();

	if(obj1==obj2){
		system.out.println("같은 싱글톤 객체입니다");  //이게 출력됨
	} else {
		system.out.println("다른 싱글톤 객체입니다");
		}

	}
}

💐 final 필드와 상수

  • 필드값이 저장되면 최종값이 되어 중간에 수정 X
final String nation = "Korea";

상수

  • static이면서 final인 값
static final double PI = 3.14159;

💐 패키지와 접근제한자

import문

  • 컨트롤 + 쉬프트 + O

접근제한자

💐 Getter와 Setter

객체의 필드를 외부에서 접근하게 되면 객체의 무결성이 깨진다

(예를 들어, 자동차 속력은 음수가 될 수 없는데, 외부에서 음수로 변경하면 변경된다 = 무결성이 깨진다)

따라서 메소드를 통해 필드를 변경한다.

Setter : 매개값을 검증하여 유효한 값만 객체의필드로 저장

void setSpeed(double speed){
	if(speed <0){
		this.speed = 0;
		return;
} else {
	this.speed = speed;
	}
}

Getter : 필드값을 가공 한 후, 외부객체에서 데이터를 읽어온다.

double getSpeed() {
	double km = speed*1.6;
	return km;
}

0개의 댓글