어떤 값이 하나의 참조 자료형이 아닌 여러 참조 자료형을 사용할 수 있도록 프로그래밍 하는 것.
클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법.
참조자료형이 변환될 때 컴파일러가 검증을하기 때문에 매우 안정적이다.
제네릭 클래스 정의하기
package javastudy2;
//GenericPrinter<T> : 클래스이름
public class GenericPrinter<T> { //<T> : 타입의 약자 , 자료형 매개변수
private T material;
public T getMaterial() {
return material;
}
public void setMaterial(T material) {
this.material = material;
}
public String toString() {
return material.toString();
}
}
제네릭에서는 여러 참조 자료형을 사용해야하는 부분에 하나의 문자로 표현한다.
여러 자료형으로 바꾸어 사용할 material 변수의 자료형을 T 라고(타입변수) 작성한 것이다.
클래스 이름을 GenericPrinter<T>로 정의하고 나중에 클래스를 사용할 때 T 위치에 실제 사용할 자료형을 지정한다.
클래스의 각 메소드에서 해당 자료형이 필요한 부분에는 모두 T문자를 사용하여 구현한다.
여러개의 타입변수는 콤마( , )로 구분하여 명시할 수 있다.
다이아몬드 연산자<>
ArrayList<String> list = new ArrayList<>();
<> : 다이아몬드 연산자
생략된 <> 부분이 String임을 컴파일러가 유추하기때문에 생성부분에서는 생략이 가능함.
자료형 매개변수 T와 static
static변수나 메소드는 인스턴스를 생성하지 않아도 클래스 이름으로 호출할 수 있다.
static 변수는 인스턴스 변수가 생성되기 이전에 생성된다.
static 메소드에서는 인스턴스 변수를 사용할 수 없다.
T의 자료형이 정해지는 순간은 제네릭 클래스의 인스턴스가 생성되는 순간이다.
T의 자료형이 결정되는 시점보다 빠르기 때문에 static변수의 자료형이나 static메소드 내부 변수의 자료형으로 T를 사용할 수 없다.
제네릭에서 자료형 추론하기
ArrayList<String> list = new ArrayList<String>();
var list = new ArrayList<String>();
지역 변수에 한해서 자료형을 추론할 수 있다.
powder 클래스 정의하기
package javastudy2;
public class Powder {
public void doPrinting() {
System.out.println("Powder 재료로 출력합니다.");
}
public String toString() {
return "재료는 Powder입니다.";
}
}
plastic 클래스 정의하기
package javastudy2;
public class Plastic {
public void doPrinting() {
System.out.println("Plastic 재료로 출력합니다.");
}
public String toString() {
return "재료는 Plastic입니다.";
}
}
GenericPrinter<T> 클래스 사용하기
package javastudy2;
public class GenericPrinterTest {
public static void main (String[] args) {
//Powder 형으로 GenericPrinter클래스 생성
GenericPrinter<Powder> powderPrinter = new GenericPrinter<Powder>();
powderPrinter.setMaterial(new Powder()); //powder클래스 생성
Powder powder = powderPrinter.getMaterial(); //변수에 getMaterial값 대입
System.out.println(powderPrinter);
//Plastic 형으로 GenericPrinter클래스 생성
GenericPrinter<Plastic> plasticPrinter = new GenericPrinter<Plastic>();
plasticPrinter.setMaterial(new Plastic());
Plastic plastic = plasticPrinter.getMaterial();
System.out.println(plasticPrinter);
}
}
선언된 제네릭클래스를 생성할때에는 타입변수 자리에 실제사용할 변수를 작성해야한다.
<T extends 클래스>
public class GenericPrinter<T extends Material>{
private T material;
}
상위클래스 Material에서 선언한 메소드를 사용할 수 있다.
제네릭 메소드
//public <자료형 매개변수> 반환형 메소드(자료형 배개변수...){...}
public T getX(){
return x;
}
Error 클래스의 하위클래스는 시스템에서 발생하는 오류를 다루며 프로그램에서 제어하지 않는다.
Exception 클래스와 그 하위에있는 예외 클래스들이 프로그램을 제어한다.
자바에서 자주사용하는 예외클래스는 다음과 같다.
ClassCastException : 수행할 수 없는 타입변환이 진행될 경우
ArrayIndexOutofBoundsException : 배열에 잘못된 인덱스를 사용하여 접근하는 경우
NullPointerException : null 객체의 인스턴스 메소드를 호출하는 경우
ArithmeticException : 산술연산에서 정수를 0으로 나누는 등 연산을 수행할 수 없는 경우
try-catch문
try{
예외가 발생할 수 있는 코드부분
}catch(처리할 예외 타입 e){
try블록 안에서 예외가 발생했을 때 예외를 처리하는 부분
}
위와 같이 코드 작성시 a.text 파일이 존재하지않는 오류가 발생할 수 있다는 경고문이 뜬다.
예외 상황에 대한 예외처리를 위해 'Surround with try/catch'를 클릭해 try-catch문으로 감싼다.
예외가 발생할 위험이 있는 코드는 try 블록으로 감싸고 예외가 발생하면 catch 블록을 수행한다.
try문으로 감싸진 부분에서 발생할 수 있는 예외는 FlieNotFoundException이고 변수이름은 e로 선언되었다.
그리고 어디에서 예외가 발생했는지 따라가는 printStackTrace() 메소드가 호출되었다.
코드를 실행해보면 결과화면에 예외 이름과 내용이 보인다.
예외가 발생했을 때 FileNotFoundException e의 toString() 메소드가 호출되는 코드이다.
출력결과에서 보면 첫번째 줄은 e의 출력내용이다.
만약 비정상 종료되었다면 다른수행이 일어나지 않았겠지만 두번째줄이 출력된 결과를 보면
예외처리 후에도 프로그램이 계속 수행되었음을 알 수 있다.
try-catch-finally문
프로그램에서 사용한 리소스는 프로그램이 종료되면 자동으로 해제된다. 하지만 끝나지 않고 계속 수행되는 서비스같은
경우에는 리소스를 여러 번 반복해서 열기만 하고 닫지않는다면 문제가 발생한다.
따라서 사용한 리소스는 사용 후 close()메소드로 닫아 주어야한다.
만약 try블록 안에서 발생할 수 있는 예외상황이 여러개라면 catch블록을 예외상황 수만큼 구현을 해야한다.
하지만 리소스를 해제하는 코드를 각 블록에 모두 작성해야 한다면 매우 번거롭게된다.
이때 사용하는 블록이 'finally'이다. finally 블록은 어떤 경우에도 반드시 수행된다.
try{
예외가 발생할 수 있는 부분
}catch(처리할 예외타입 e){
예외를 처리하는 부분
}finally{
항상 수행되는 부분
}
출력결과
입력받는 파일이 없는 경우에 대해 try-catch문을 사용해 FileNotFoundException 예외처리를 했다.
프로그램을 실행하면 a.txt파일이 없으므로 예외처리가 발생하여 catch 블록이 수행된다.
예외를 출력하고 17행에서 강제로 return을 했지만 상관없이 finally 블록이 수행되어 '항상 수행됩니다.'
문장이 출력되고있다.
try-with-resources문
자바 7부터 try-with-resources문을 제공하여 close() 메소드를 명시적으로 호출하지 않아도 try 블록 내에서
열린 리소스를 자동으로 닫도록 만들 수 있다.
try-with-resources 문법을 사용하려면 해당 리소스가 AutoCloseable 인터페이스를 구현해야한다.
AutoCloseable 인터페이스에는 close() 메소드가 있고 이를 구현한 클래스는 close()를 명시적으로 호출하지
않아도 정상적인 경우와 예외가 발생한 경우 모두 close()메소드 부분이 호출된다.
예외가 발생하지않고 정상종료되는 예제
//AutuClosealbe 인터페이스 구현하기
package javastudy2;
public class AutoCloseObj implements AutoCloseable{
@Override
public void close( ) throws Exception{
System.out.println("리소스가 close() 되었습니다.");
}
}
package javastudy2;
public class AutoClassTest {
public static void main(String[] args) {
try(AutoCloseObj obj = new AutoCloseObj()){
}catch(Exception e) {
System.out.println("예외부분입니다.");
}
}
}
try-with-resources 문을 사용할 때 try문의 괄호()안에 리소스를 선언한다.
출력결과
//소스코드를 여러개 생성해야하는 경우
try(A a = new A(); B b = new B()){
....
}catch(Exception e){
....
}
소스를 여러개 생성해야한다면 세미콜론(;)으로 구분한다.
예외가 발생하여 종료되는 예제
package javastudy2;
public class AutoClassTest {
public static void main(String[] args) {
try(AutoCloseObj obj = new AutoCloseObj()){
throw new Exception(); //강제예외발생
}catch(Exception e) {
System.out.println("예외부분입니다.");
}
}
}
'throw new Exception()' 문장을 사용하면 프로그램에서 강제로 예외를 발생시켜 catch블록이 수행되도록 한다.
출력결과
향상된 try-with-resources문 (자바 9에서 추가된 문법)
package javastudy2;
public class AutoClassTest {
public static void main(String[] args) {
AutoCloseObj obj = new AutoCloseObj(); //리소스변수 외부선언
try(obj){ //외부에서 선언한 변수를 그대로 쓸 수 있음
throw new Exception(); //강제예외발생
}catch(Exception e) {
System.out.println("예외부분입니다.");
}
}
자바 9부터는 try문의 괄호 안에서 외부에서 선언한 변수를 그대로 사용할 수 있다.
예외 처리 미루기 (throws)
예외를 해당 메소드에서 처리하지 않고 미룬 후 메소드를 호출하여 사용하는 부분에서 예외를 처리하는 방법.
8행을 보면 FileNotFoundException 과 ClassNotFoundException 이 발생할 수 있는 예외처리를 미루겠다는 뜻의
'thows' 를 메소드 선언부에 추가하였다.
이 두가지 예외는 해당 메소드를 호출하여 사용하는 부분에서 예외처리를 해야한다.
main() 함수의 16행을 보면 loadClass() 메소드를 호출하는 부분에서 오류가 표시되고 세가지 옵션중 하나를 선택하여
오류를 처리할 수 있다.
Add throws declaration : main() 함수 선언부분에서 throws FileNotFoundException , ClassNotFoundException을 추가하고 예외처리를 미룬다는 뜻. 하지만 예외를 처리하는 것이 아니라 대부분의 프로그램이 비정상 종료되기때문에 사용하지 않는 것이 좋다.
Surround with try/multi-catch : 여러 예외를 한번에 처리하기
Surrround with try.catch : 예외 상황마다 처리하기
Surround with try/multi-catch 옵션
이 옵션은 하나의 catch문에서 여러 예외를 한 문장으로 처리하겠다는 뜻이다.
Surround with try/catch 옵션
예외 상황의 수만큼 catch문이 생성된다.
각 예외 상황마다 다른 방식으로 처리해야하고 로그도 다르게 남겨야하는 경우 사용하는 옵션이다.
인터페이스란? 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서 다른 클래스 사이의 중간 매개역할을 해주는 일종의 추상클래스를 의미한다.
인터페이스
인터페이스는 추상메소드와 상수로만 이루어져있다.
구현된 코드가 없기 때문에 인스턴스를 생성할 수 없다.
인터페이스 만들기
package javastudy;
public interface Calc {
//인터페이스에서 선언한 변수는 컴파일 과정에서 상수로 변환됨.
double PI = 3.14;
int ERROR = -999999999;
//인터페이스에서 선언한 메소드는 컴파일 과정에서 추상메소드로 변환됨.
int add(int num1, int num2);
int substract(int num1, int num2);
int times(int num1, int num2);
int devide(int num1, int num2);
}
인터페이스에서 선언한 메소드는 모두 구현 코드가 없는 추상메소드이다.
public abstract예약어를 명시적으로 쓰지않아도 컴파일 과정에서 자동으로 추상메소드로 변환된다.
인터페이스에서 선언한 변수 또한 컴파일 과정에서 값이 변하지 않는 상수도 자동 변환되기때문에
static final 예약어를 사용하지 않아도 된다.
클래스에서 인터페이스 구현하기
package javastudy;
public abstract class Calculator implements Calc{
@Override
public int add(int num1, int num2) {
return num1 + num2;
}
@Override
public int substract(int num1, int num2) {
return num1 - num2;
}
}
위에서 선언한 인터페이스를 클래스가 사용하는 것을 '클래스에서 인터페이스를 구현한다(implements)'라고 표현한다.
따라서 인터페이스에서 선언한 기능을 클래스가 구현한다는 의미로 implements 예약어를 사용한다.
Calc 인터페이스에 포함된 추상메소드를 Calculator 클래스에서 구현하여 사용해야한다.
여기에서는 4개의 Calc 메소드중 2개만 구현하여 추상클래스를 만들었다.
계산기 클래스 만들기
package javastudy;
public class CompleteCalc extends Calculator {
@Override
public int times(int num1, int num2) {
return num1 * num2;
}
@Override
public int devide(int num1, int num2) {
if(num2 != 0) {
return num1 / num2 ;
}else {
return Calc.ERROR;
}
}
//CompleteCalc에서 추가로 구현한 메소드
public void showInfo() {
System.out.println("Calc 인터페이스를 구현하였습니다.");
}
}
CompleteCalc클래스에서는 아직 구현되지 않은 나머지 2개의 추상메소드를 구현한다.
CompleteCalc클래스 실행하기
package javastudy;
public class CalculatorTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 5;
CompleteCalc calc = new CompleteCalc();
System.out.println(calc.add(num1, num2));
System.out.println(calc.substract(num1, num2));
System.out.println(calc.times(num1, num2));
System.out.println(calc.devide(num1, num2));
calc.showInfo();
}
}
출력화면
Calculator 클래스는 인터페이스에서 선언한 추상메소드 중 일부만 구현했으므로 추상클래스이다.
그리고 이를 상속받은 CompleteCalc 클래스는 Calclator에서 구현하지 못한 추상메소드를 모두 구현하였다.
package javastudy;
public abstract class Car extends Object {
public Car() {
super();
}
int wheel;
String carName;
int handle;
public void drive() {
System.out.println(carName + "이동하다");
}
public abstract void openDoor(); //호출이 아니라 구현부가 없다. 메소드 선언만함
}
클래스 생성시 abstract를 체크하면 추상클래스 생성된다.
이러한 추상클래스는 동작이 정의되어있지 않은 추상메소드를 포함하고있으므로 인스턴스를 생성할 수 없다.
package javastudy;
public class AbstClass {
public static void main(String[] args) {
Car car = new Car(); //추상클래스는 객체생성이 안된다.
}
}
추상클래스는 객체생성이안된다.
Car Class를 Sonata Class에 상속 후 미구현된 메소드이기에 구현을해줘야함
Sonata Class에서 Car Class의 openDoor 오버라이딩으로 재정의를 한다.
오버라이딩 후 인스턴스를 생성하여 Car클래스에 접근 할 수 있다.
Sonata s = new Sonata(); //소나타 객체생성
s.carName = "소나타";
s.drive();
s.openDoor();
출력결과
package javastudy;
public class Sonata extends Car {
public Sonata(String carName) { //매개변수가 있는 생성자
this.carName = carName;
}
@Override
public void openDoor() {
System.out.println("문을열다");
}
}
Sonata s = new Sonata("소나타"); //소나타 객체생성
//s.carName = "소나타";
s.drive();
s.openDoor();
Sonata Class의 생성자에 매개변수를 입력하고 인자값을 넣어 출력할수도 있다.
출력값
출력값은 동일하다.
setter / getter
//setter(담을때) getter(꺼낼때)
//멤버변수가 private으로 접근제어되었을때 우회해서 메소드를 통해 입출력할때
public void setHand(int hand) {
this.hand = hand; //setHand라는 메소드 호출해서 hand라는 변수 대입
}
public int getHand() {
return hand;
}
public void setLeg(int leg) {
this.leg = leg;
}
public int getLeg() {
return leg;
}
Person person = new Person();
person.setHand(2);
person.setLeg(2);
person.eat();
person.run();
에디터의 기능으로 setter 와 getter를 직접 타이핑하지않고 만들 수 있다.
//setter getter 메소드 사용하기 객체변수(멤버)가 private으로 막혀있을때
private int a;
private int b;
private int c;
private String d;
private String e;
private String f;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
public String getF() {
return f;
}
public void setF(String f) {
this.f = f;
}
//인스턴스 생성하기
private static Company instance = new Company();//instance에 Conmpany주소값을 담는다.
private으로 선언하여 외부에서 인스턴스에 접근하지 못하도록 제한해야 오류를 방지할 수 있다.
외부에서 접근이 안되는 Company class의 변수 instance를 선언하고 객체생성 시켜서 초기화한다.
public 메소드 만들기
//외부에서 참조할 수 있는 public메소드 만들기
public static Company getInstance(){
//인스턴스를 반환하는 메소드는 static으로 선언해야함
//getInstance메소드는 인스턴스 생성과 상관없이 호출할수 있어야하기때문
return instance; //생성된 인스턴스 반환
private을 선언한 인스턴스를 외부에서도 사용할 수 있도록 설정하기 위해 public메소드를 생성한다.
생성한 인스턴스를 반환해준다.
getInstance메소드를 인스턴스 생성과 상관없이 호출할 수 있게 인스턴스를 반환하는 메소드를 static으로 선언한다.
변수의 주소값 비교하기
Company inst = Company.getInstance();
System.out.println("생성된 객체 주소값은 "+inst);
Company inst2 = Company.getInstance();
System.out.println("생성된 객체 주소값은 "+inst2);//하나의 객체만 생성하기때문에 주소값이 동일함
출력값
참조 값을 가지는 동일한 인스턴스이기 때문에 여러번을 호출해도 항상 같은 주소의 인스턴스가 반환된다.
int[] aa = new int[] {1,2,3,4,5,6,7,8,9,10};//배열선언과 동시에 초기화, 개수는 생략한다. 개수작성시 오류발생함.
int[] aaa = {1,2,3,4,5,6,7,8,9,10}; //선언과 동시에 초기화 시 new int[] 생략 가능하다.
int[] aa; //int형 배열 선언
aa = new int[]{1,2,3}; //배열생성 후 초기화
배열의 자료형을 먼저 선언하고 초기화하는 경우에는 new int[]를 생략할 수 없다.
a[0] = 1; //배열의 첫번째 요소 인덱스는 0부터 시작
a[1] = 2;
a[3] = 10;
선언한 배열의 각 요소에 값을 넣을때나 가져올 때는 []를 사용하고,
배열 요소에 값을 저장할땐 위와 같은 코드를 작성한다.
숫자 배열
int[] a;
a = new int[1000];
for(int i = 0; i <a.length; i++) {//a.length 배열이 크기를 알수있음.
a[i] = i+1;
System.out.println("각 배열방에 담긴 값은? "+a[i]);}
자바의 배열은 배열 길이를 나타내는 length 속성을 가진다.
배열길이는 처음에 선언한 배열의 전체 요소 개수를 의미한다.
전체길이를 알고 싶은 배열 이름뒤에 도트( . ) 연산자를 붙이고 length속성을 쓰면 배열 길이를 반환한다.
//1에서 10까지의 합을 배열을 통해서 구하시오.
int[] b = new int[10];
int sum = 0;
for(int i = 0; i<b.length; i++) {
b[i] = i+1;
sum += b[i];
}
System.out.println("1에서 10까지의 합은? "+sum);
package javastudy;
public class Book {
//멤버변수 선언
String name;
//책 이름을 매개변수로 받는 생성자
public Book(String nm){ //생성자생성, String타입의 매개변수nm
this.name = nm; //매개변수의 값을 멤버변수에 담는다.
}
//책의 정보를 출력해주는 메소드
public void explain() {
System.out.println("책이름은? "+ name);
}
}
먼저 클래스를 생성한다.
책 이름을 매개변수로 받는 생성자를 만든다.
//객체배열 생성
//Book타입의 배열 3개를 만든다.
Book[] book = new Book[3]; //Book 클래스의 생성자를 참조변수 book에 담는다.
//인스턴스 생성 후 배열에 저장
book[0] = new Book("국어"); //매개변수의 인자값으로 국어를 넘긴다.
book[1] = new Book("수학");
book[2] = new Book("영어");
for(int i = 0; i<book.length; i ++) {
System.out.println("책이름을 알려주세요. ");
book[i].explain();
}
향상된 for문
향상된 for문은 배열의 처음부터 끝까지 모든 요소를 참조할 때 사용하기 편리한 반복문이다.
배열 요소 값을 순서대로 하나씩 가져와서 변수에 대입한다.
초기화와 종료조건이 없기 때문에 모든 배열의 시작 요소부터 끝요소까지 실행한다.
//향상된 for문 - 주로 객체배열을 돌릴때 사용한다.
for(Book b :book) {
System.out.println("책이름을 알려주세요.");
b.explain();
}
문법 - for(자료형 변수명 : 배열명) { 반복실행문 }
2차원 배열
//2차원 배열
int[][] arr = {{1,2,3},{4,5,6}};
System.out.println("첫번째방 첫번째 값은?" +arr[0][0]);
System.out.println("두번째방 두번째 값은?" +arr[1][1]);
for(int i = 0; i<arr.length; i++) { //각 방의 크기
System.out.println(arr[i]);
for(int j = 0; j<arr[i].length; j++) { //각 방마다 들어가 있는 요소의 크기
System.out.println(arr[i][j]);
}
}