비검사 예외 : 예외 처리 강제하지 않음 → 사용자가 할 수 있는 일이 없거나 종료시키는 것이 바람직한 경우
비검사 예외로 만들기 위해서는 RuntimeException을 상속해야 한다.
Exception을 상속할 경우 try/catch 또는 throws를 통해 예외 처리를 한다.
[DivideException에서 RuntimeException을 상속]
package org.opentutorials.javatutorials.exception;
class DivideException extends RuntimeException {
DivideException() {
super();
}
DivideException(String message) {
super(message); // 부모 클래스의 생성자를 호출할 때 메시지 전달
}
}
class Calculator {
int left, right;
public void setOperands(int left, int right) {
this.left = left;
this.right = right;
}
public void divide() {
if (this.right == 0) {
throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
}
System.out.print(this.left/this.right);
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOperands(10, 0);
c1.divide();
}
}
[DivideException에서 Exception을 상속 : try/catch로 검사 예외를 처리]
package org.opentutorials.javatutorials.exception;
class DivideException extends Exception {
DivideException() {
super();
}
DivideException(String message) {
super(message); // 부모 클래스의 생성자를 호출할 때 메시지 전달
}
}
class Calculator {
int left, right;
public void setOperands(int left, int right) {
this.left = left;
this.right = right;
}
public void divide() {
try {
if (this.right == 0) {
throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
}
System.out.print(this.left/this.right);
}
catch(DivideException e) {
e.printStackTrace();
}
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOperands(10, 0);
c1.divide();
}
}
[DivideException에서 Exception을 상속 : try/catch로 검사 예외를 처리]
package org.opentutorials.javatutorials.exception;
class DivideException extends Exception {
DivideException() {
super();
}
DivideException(String message) {
super(message); // 부모 클래스의 생성자를 호출할 때 메시지 전달
}
}
class Calculator {
int left, right;
public void setOperands(int left, int right) {
this.left = left;
this.right = right;
}
public void divide() throws DivideException{
if (this.right == 0) {
throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
}
System.out.print(this.left/this.right);
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOperands(10, 0);
try {
c1.divide();
}
catch (DivideException e) {
e.printStackTrace();
}
}
}
equals() 메소드를 직접 구현해야 한다면 hashCode() 메소드도 함께 구현해야 한다.
자식이 부모가 될 때는 자동으로 형변환 된다.
Object obj = s2
부모가 자식이 될 때는 명시적으로 형변환해야 한다.
Student s = (Student)obj;
4.3 finalize
객체가 소멸될 때 호출되기로 약속한 메소드
사용할 일이 거의 없는 메소드이다. 사용을 권장하지 않는다.
4.4 clone
객체를 복제한다.
복제 가능한 객체라는 사실을 자바 가상 머신에 알려줘야 한다. 따라서 Cloneable이라는 인터페이스를 구현한다.
그러나 Cloneable은 비어있는 인터페이스로 클래스가 복제 가능함을 자바 가상머신에게 알려주기 위한 구분자에 불과하다.
clone() 메소드의 접근 제어자는 protected이기 때문에 반드시 메소드를 public 접근 제어자로 오버라이딩해야 한다.
clone() 메소드는 Exception을 상속해서 예외 처리가 필수적이다.
5. enum
enum은 열거형이라고도 한다.
열거형은 서로 연관된 상수의 집합이다.
package org.opentutorials.javatutorials.constant;
enum Fruit {
APPLE, PEACH, BANANA;
}
enum Company {
GOOGLE, APPLE, ORACLE;
}
public class ConstantDemo {
public static void main(String[] args) {
Fruit type = Fruit.APPLE;
switch (type) {
case APPLE:
System.out.println(57 + " kcal");
break;
case PEACH:
System.out.println(34 + " kcal");
break;
case BANANA:
System.out.println(93 + " kcal");
break;
}
}
}
enum은 사실상 클래스이므로 생성자를 가질 수 있다.
열거형에 포함된 상수를 values() 메소드를 통해 상수가 담긴 배열을 가져올 수 있다.
6. 참조
데이터 타입을 생성할 때 new를 통해 생성하는 것들은 기본 데이터 타입이 아니고 참조형 또는 참조 데이터 타입이다.
package org.opentutorials.javatutorials.reference;
class A {
public int id;
A(int id) {
this.id = id;
}
}
public class ReferenceParameterDemo {
static void _value(int b) {
b = 2;
}
public static void runValue() {
int a = 1;
_value(a);
System.out.println("runvalue, " + a);
}
static void _reference1(A b) {
b = new A(2);
}
public static void runReference1() {
A a = new A(1);
_reference1(a);
System.out.println("runReference1, " + a.id);
}
static void _reference2(A b) {
b.id = 2;
}
public static void runReference2() {
A a = new A(1);
_reference2(a);
System.out.println("runReference2, " + a.id);
}
public static void main(String[] args) {
runValue(); // runValue, 1
runReference1(); // runReference1, 1
runReference2(); // runReference2, 2
}
}
package org.opentutorials.javatutorials.generic;
import java.net.Inet4Address;
class EmployeeInfo {
public int rank;
EmployeeInfo(int rank) {
this.rank = rank;
}
}
class Person<T, S> {
public T info;
public S id;
Person(T info, S id) {
this.info = info;
this.id = id;
}
}
public class GenericDemo {
public static void main(String[] args) {
EmployeeInfo e = new EmployeeInfo(1);
Integer i = 10;
Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);
// 제네릭을 명시적으로 언급하지 않고 생략할 수 있다.
// Person<EmployeeInfo, Integer> p1 = new (e, i);
}
}
7.1 제네릭의 제한
extends 키워드를 이용해 특정 데이터 타입만 오도록 할 수 있다.
package org.opentutorials.javatutorials.generic;
abstract class Info {
public abstract int getLevel();
}
class EmployeeInfo extends Info {
public int rank;
EmployeeInfo(int rank) {
this.rank = rank;
}
public int getLevel() {
return this.rank;
}
}
class Person<T extends Info> { // Info 클래스의 자식만 T로 올 수 있다
public T info;
Person(T info) {
this.info = info;
}
}
public class GenericDemo {
public static void main(String[] args) {
Person p1 = new Person(new EmployeeInfo(1));
// String은 Info의 자식이 아니기 때문에 에러 발생
// Person<String> p2 = new Person<String>("부장");
}
}
정의 : 객체에 속한 멤버를 사용하는 입장에서 누가 이것을 사용할 수 있는가에 대한 권한을 부여하는 역할
public : 언제나 접근 가능
private : 같은 클래스에서만 접근 가능
예시 코드
package org.opentutorials.javatutorials.accessmodifier;
class A{
public String y(){
return "public void y()";
}
private String z() {
return "public void z()";
}
public String x() {
return z(); // 같은 클래스 내부에서는 접근 가능
}
}
public class AccessDemo {
public static void main(String[] args) {
A a = new A();
System.out.println(a.y());
// System.out.println(a.z());
System.out.println(a.x());
}
}
추상 클래스는 반드시 그것을 상속받아 사용해야 한다. 즉 main메소드에서 직접 접근이 불가능하다.
추상 메소드를 사용하려면 메소드를 사용하는 쪽에서 반드시 오버라이딩하여 구체적인 로직을 정의해야 한다.
abstract가 지정된 메소드는 로직을 가지고 있으면 안된다.
클래스의 멤버 중 하나라도 abstract 키워드가 지정되어 있으면 해당 멤버를 담고 있는 클래스도 추상클래스가 된다.
abstract class A {
// 추상 메소드
public abstract int b();
// 본체가 있는 메소드는 abstract 키워드를 가질 수 없다.
public abstract int c(){System.out.println("Hello");}
// 추상 클래스 내에서는 추상 메소드가 아닌 메소드가 존재할 수 있다.
public void d(){
System.out.println("world");
}
}
6. final
final을 지정하면 바꿀 수 없는 상수가 된다.
7. 인터페이스
7.1 역할 및 사용 이유
역할 : 특정 클래스에서 특정한 인터페이스를 사용한다면 그 클래스가 반드시 해당 인터페이스에 포함된 메소드를 구현하도록 강제한다
사용 이유 : 어떤 클래스가 어떤 멤버(메소드)를 가지고 있는지 명세서와 같은 역할을 한다. 인터페이스에 명시된 대로 구현을 해야하기 때문에 커뮤니케이션의 착오를 방지한다.
// 인터페이스
interface I() {
public void z();
}
// A 클래스는 I 인터페이스를 '구현한다'
class A implements I {
public void z() {
// 구현이 되어 있지 않기 때문에 컴파일 에러
}
}
7.2 특징
인터페이스를 선언할 때 인터페이스에 정의되는 멤버의 접근 제어자는 반드시 public이어야 한다.
인터페이스는 한 번에 여러 개를 상속할 수 있다.
인터페이스끼리도 상속이 되고 상속된 인터페이스는 부모 인터페이스가 가진 기능을 그대로 자식들이 갖게 된다.
interface I1 {
public void x();
}
interface I2 {
public void z();
}
// 한 번에 여러 개의 인터페이스를 상속받을 수 있다.
class A implements I1, I2 {
public void x() {
}
public void z() {
}
}
interface I3 {
public void x();
}
// 인터페이스도 상속이 된다
interface I4 extends I3 {
public void z();
}
// 부모 인터페이스가 가진 기능도 구현해야 한다
class B implements I4 {
public void x() {
}
public void z() {
}
}
8. 다형성
정의 : 한 메소드나 클래스가 다양한 방식으로 동작하는 것
8.1 클래스와 다형성
B 클래스를 인스턴스화한 obj 변수가 현재 A 클래스 행세를 한다.
A obj = new B();
자식 클래스에서 오버라이딩한 메소드의 내용이 실행된다
A 클래스에서 구현된 내용("A.x")이 출력되지 않는다
부모 클래스에는 없지만 자식 클래스에 있는 메소드를 실행하려고 하면 부모 클래스에서 정의된 바가 없기에 존재하지 않는 메소드가 된다.
y() 메소드는 실행되지 않는다
package org.opentutorials.javatutorials.polymorphism;
class A {
public String x(){
return "A.x";
}
}
class B extends A {
public String x(){
return "B.x";
}
public String y(){
return "y";
}
}
class B2 extends A {
public String x() {
return "B2.x";
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
A obj = new B();
A obj2 = new B2();
System.out.println(obj.x()); // B.x
System.out.println(obj2.x()); // B2.x
// System.out.println(obj.y()); -> error!
}
}
8.2 인터페이스와 다형성
어떤 클래스가 어떤 인터페이스를 구현하고 있다면 그 클래스의 인스턴스는 그 인터페이스일 수 있다.
어떤 클래스의 데이터 타입이 특정한 인터페이스일 경우 해당 클래스에서 특정 인터페이스에서 정의한 멤버만을 가진 클래스인 것처럼 사용할 수 있다.
package org.opentutorials.javatutorials.polymorphism;
interface I2{
public String A();
}
interface I3{
public String B();
}
class D implements I2, I3 {
public String A() {
return "A";
}
public String B() {
return "B";
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
D obj = new D();
I2 objI2 = new D();
I3 objI3 = new D();
obj.A();
obj.B();
objI2.A();
// objI2.B(); error!
// objI3.A(); error!
objI3.B();
}
}
은닉화, 캡슐화 : 내부의 동작방법을 객체에 숨기고 사용자에게는 그 부품의 사용법만 노출하는 것을 정보 은닉화/캡슐화
인터페이스 : 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면
2. 클래스와 인스턴스, 그리고 객체
클래스 : 객체를 만들기 위한 일종의 설계도
static 키워드가 붙음
인스턴스 : 그 설계도에 따라서 만든 구체적인 제품
static 이 붙지 않음
인스턴스 메소드는 클래스 멤버에 접근 가능
인스턴스 메소드는 인스턴스에 속한 메소들로서 static이라는 키워드가 지정되지 않은 메소드이다.
인스턴스 메소드 안에서 클래스 변수와 클래스 메소드에 접근할 수 있다
클래스 메소드는 인스턴스 멤버에 접근 불가능
클래스라는 것은 언제나 메소드보다 먼저 존재한다. 왜냐하면 설계도를 만들고 이 설계도에 따라 만들어진 구체적인 제품인 인스턴스를 나중에 만들기 때문.
클래스 메소드는 그 클래스를 기반으로 만들어진 인스턴스에 접근하는 것이기 때문에 아직 생성되지 않은 인스턴스에 접근하는 것이다.
어떤 변수에 인스턴스가 담겨 있는지 조차도 클래스 안에서는 알 수 없다.
3. 유효 범위
package org.opentutorials.javatutorials.scope;
class C{
int v = 10;
void m() {
int v = 20;
System.out.println(v);
System.out.println(this.v);
}
}
public class ScopeDemo {
public static void main(String[] args) {
C c1 = new C();
c1.m();
}
}
// 출력 결과
// 20
// 10
this : 인스턴스의 인스턴스 자신을 의미하는 값
4. 상속과 생성자
상속받은 하위 클래스의 인스턴스가 생성될 때 상속한 상위 클래스의 기본 생성자를 자동으로 호출한다.
만약 부모 클래스에서 매개변수가 있는 생성자를 선언하면 자동으로 기본생성자가 생성되지 않아서 오류 발생
4.1 super
super : 부모 클래스 의미
super() : 부모 클래스의 생성자
주의! : 하위 클래스의 초기화 코드를 super보다 먼저 등장시키면 안된다.
5. 오버라이딩
상속은 부모 클래스에 어떠한 기능을 추가만 하는 경우
오버라이딩은 부모 클래스의 메소드를 그대로 쓰지 않고 자식 클래스의 필요에 따라 메소드를 재정의. 이때 부모 클래스의 메소드는 무시된다.
5.1 오버라이딩 주의사항
부모 클래스의 메소드 형식과 자식 클래스의 메소드 형식이 불일치하는 경우 오버라이딩할 수 없다. 즉 오버라딩하기 위해서는 부모 클래스와 자식 클래스의 반환 타입이 일치해야한다.
메소드의 이름이 같아야한다
매개변수 개수와 반환 타입이 같아야한다
5.2 오버라이딩 조건 (메소드의 서명=signature)
메소드 이름
메소드 매개변수의 개수와 데이터 타입, 순서
메소드의 반환 타입
Tip! 자식 메소드에서 부모 메소드를 호출할 때는 super키워드를 사용한다.
6. 오버로딩
정의 : 클래스에 메소드를 정의할 때 이름이 같지만 서로 다른 매개변수 형식을 지닌 메소드를 여러 개 정의할 수 있는 방법
float 보다 double이 더 많은 데이터를 표현할 수 있기 때문에 데이터가 유실될 가능성이 있다.
결론 : 자동 형 변환의 원칙은 표현 범위가 좁은 데이터 타입에서 넓은 데이터 타입으로만 허용한다.
2.1.1 예시
int a = 3;
float b = 1.0F;
double c = a + b;
System.out.println(c);
a+b를 진행한다. int 와 float의 덧셈이므로 우선 형을 같도록 형 변환을 한다. 즉 int가 float형으로 변환된다. 따라서 a+b = 4.0F
4.0F를 c에 대입한다. c의 데이터 타입은 double형이고 double형에 float형을 대입하면 float형은 double형으로 형 변환이 일어난다.
2.2 명시적 형 변환
정의 : 수동으로 직접 형 변환
float a = (float)100.0;
int b = (int)100.0F;
3. 연산자
3.1 연산자의 우선순위
우선순위
연산자
결합 방향
1
[ ] () .
->
2
++ -- +(양수) -(음수) ~ ! (type) new
<-
3
* / %
->
4
+(더하기) -(빼기) +(문자열 결합 연산자)
->
5
<< >> >>>
->
6
< <= > >= instanceof
->
7
== !=
->
8
& &
->
9
^ ^
->
10
| |
->
11
&&
->
12
||
->
13
? :
<-
14
= *= /= += -= %= <<= >>= >>>= &= ^= !=
<-
3.2 비교와 불린
주의!문자열을 비교할 때는 ==(동등 비교 연산자)를 사용하는 것이 아니라 .equals()라는 메소드를 사용해야 한다.
4. 입력과 출력
package org.opentutorials.javatutorials.io;
public class InputDemo {
public static void main(String[] args) {
System.out.println(args.length);
}
}
String[] : 문자열로 구선된 배열을 의미
args : 문자열 데이터 타입을 담을 수 있는 배열
4.1 시작할 때 입력
Run Configuration 클릭Input 작성
4.2 실행 중에 입력
:자바에서 기본적으로 제공하는 라이브러리 가운데 Scanner라는 라이브러리를 이용해 실행중에 입력을 받는다.
package org.opentutorials.javatutorials.io;
// Scanner를 사용하려면 애플리케이션에 로드해야 한다.
// java.util 안에 있는 Scanner라는 객체를 사용하겠다
import java.util.Scanner;
public class InputDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int i = sc.nextInt(); // 실행을 멈추고 입력을 기다리는 상태가 된다
System.out.println(i*1000);
sc.close();
}
}