클래스 변수와 인스턴스
클래스 변수는 인스턴스에 속하는가에 대해서 1. 클래스 변수는 어떤 인스턴스에도 속하지 않고, 이 변수가 선언된 클래스의 인스턴스들이 바로 접근할 권한이 있을 뿐이다. 라는 관점과 2. 클래스 변수도 인스턴스에 속하지만 모든 인스턴스가 공유할 뿐이다. 라는 관점이 존재한다. 관점 2에 대한 근거 정리
변수가 하위 클래스에 상속되지 않는다면 접근도 불가능하다. 상위클래스의 클래스 변수인 i가 하위클래스로 상속이 되지 않는다면 SubClass.i 또는 s2.i의 방식으로 접근하는 것이 불가능할 것이다.
하지만 i가 하위클래스로 상속되었기 때문에 하위클래스인 SubClass를 통해 접근이 가능하다.
다만, 클래스 변수이다보니 상위클래스와 하위클래스가 하나의 공간을 공유하게 되고 상위클래스의 모든 인스턴스와 하위클래스의 모든 인스턴스가 같은 값을 공유하게 된다.
디버거를 이용해 확인해보면 상위클래스와 하위클래스의 인스턴스 모두 클래스 변수 staticVar를 포함하고 있음을 알 수 있다.
"상속"은 "인스턴스마다 개별 값을 가진다"의 의미가 아니라 "하위클래스에서 선언하지는 않았지만 상위클래스에 선언되어 있는 변수나 메소드에 접근할 수 있다." 라는 의미로, 상속되지 않았다면 main() 메소드 내에서 SubClass.staticVar 또는 ins2.staticVar 같은 접근은 불가능해야 한다.
추상 클래스/인터페이스의 접근한정자 정리
제네릭
다양한 종류의 데이터를 처리할 수 있는 클래스와 메소드를 작성하는 기법으로, 클래스 정의 시 자료형(타입)을 구체적으로 명시하지 않고 객체의 타입을 컴파일 시간에 확정한다.
public class GenericTest<T> { // 제네릭 클래스, 타입 매개변수 T
private T att; // 제네릭 타입의 인스턴스 변수
// 제네릭 타입의 값을 설정하는 메소드
public void set(T x) {
this.att = x;
}
// 제네릭 타입의 값을 반환하는 메소드
public T get() {
return this.att;
}
public static void main(String[] args) {
// String 타입으로 제네릭 클래스 인스턴스 생성
GenericTest<String> t1 = new GenericTest<String>();
t1.set("First"); // "First" 문자열 설정
String str = t1.get(); // 문자열 반환
System.out.println(str); // 출력: First
// Integer 타입으로 제네릭 클래스 인스턴스 생성
GenericTest<Integer> t2 = new GenericTest<Integer>();
t2.set(150); // 150 설정
Integer num = t2.get(); // 정수 반환
System.out.println(num); // 출력: 150
// 오류 발생 부분
t2.set("Second"); // 컴파일 오류: set(Integer)에 String 인수를 사용할 수 없음
}
}
위 코드를 보면 제네릭 타입으로 Integer, String이 사용되었다. 제네릭 클래스에 대하여 기본 자료형의 이름은 타입으로 사용할 수 없다. 하지만 기본 자료형에 대한 래퍼 클래스가 존재하고, 필요한 상황에서 박싱과 언박싱이 자동으로 이루어지기 때문에 래퍼 클래스를 이용할 수 있다.
public class GenericTest {
// 제네릭 메소드: 다양한 타입의 배열을 출력
public static <T> void printArray(T[] arr) {
for (T tmp : arr) {
System.out.print(tmp + " / "); // 배열의 각 요소를 출력
}
System.out.println(); // 줄바꿈
- 47
}
public static void main(String[] args) {
// Integer 배열
Integer[] iarr = {1, 2, 3};
printArray(iarr); // Integer 배열 출력
// String 배열
String[] sarr = {"Hello", "Good", "Morning", "Ajou"};
printArray(sarr); // String 배열 출력
// Double 배열
Double[] darr = {1.2, -39.401, 5.39726, 8.5, -0.0004};
printArray(darr); // Double 배열 출력
}
}
위와 같이 제네릭 메소드를 정의할 수도 있다. 앞서 제시한 예시 코드와 다른 점은 위 코드는 제네릭 클래스가 아니라는 점이다. 제네릭 클래스로 정의된 클래스의 모든 메소드는 동일한 제네릭 타입을 사용할 수 있다.
예외처리
예외: 프로그램 실행 중에 발생하는 ‘예외적인 상황’ 즉, 단순한 문법 오류가 아닌 실행 중간에 발생하는 ‘정상적이지 않은 상황’을 뜻한다.
ex) 0으로 / 연산
ex) 파일 입출력을 위해 지정한 파일이 존재하지 않거나 파일명이 틀린 경우
ex) 배열의 인덱스가 배열의 크기를 벗어나는 경우
자바는 예외 상황별로 그 상황을 알리기 위한 클래스를 정의한다. (JAVA는 모두 클래스 단위)
try-catch finally 구문
try {
// 예외가 발생할 가능성이 있는 범위를 블록으로 지정
// 하나 이상의 catch 블록을 가져야 함
}
catch(예외타입1 매개변수1) {
// try 블록의 바로 다음에 위치
// 예외타입: java.lang.Throwable 클래스의 하위 클래스
// 매개변수: 예외객체를 가리키는 참조변수 (예외에 대한 정보 저장)
}
catch(예외타입2 매개변수2) {
...
}
catch(예외타입n 매개변수n) {
...
}
finally {
// 생략 가능
// 예외 발생과 상관없이 무조건 수행
}
try 블록에서 예외 발생
-> JVM이 즉각 수행 중단
-> 발생한 예외의 타입에 따라 적합만 catch 블록(try 블록이 나머지 부분은 실행x) 실행
-> finally 블록으로 이동&실행
예외처리의 책임전가(회피) - throws 절
public class ThrowsTest {
// ArithmeticException이 발생할 수 있는 메소드. ArithmeticException이 발생하면 이 메소드를
호출한 메소드에 예외 처리를 넘긴다.
Static void func(int a, int b) throws ArithmeticException {
System.out.println(a/b);
}
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
try {
func(num1, num2);
// ArithmeticException 발생. 에외 처리를 func를 호출한 메소드, 즉 main 메소드에 넘김
}
// ArithmeticException에 대한 catch문이 예외 처리
catch (ArithmeticException e) {
System.out.println(“Cant divide by zero”);
}
}
}
'설회' 카테고리의 다른 글
[Sulhoe] 2024.10.29 상속 Upcasting/Downcasting 보충 설명 (0) | 2024.10.30 |
---|---|
[Sulhoe] 2024.09.23 실습 코드 (0) | 2024.09.23 |
[Sulhoe] 세미나 진행을 위한 JDK, Eclipse 설치 (1) | 2024.09.09 |