11.6 Class 클래스
자바는 클래스와 인터페이스의 메타 데이터를 java.lang패키지의 소속된 Class 클래스로 관리한다.
여기서 메타 데이터란 클래스의 이름, 생성자 정보, 필드 정보, 메소드 정보를 말한다.
11.6.1 Class 객체 얻기(getClass(), forName())
프로그램에서 Class 객체를 얻기 위해서는 Object 클래스가 가지고 있는 getClass() 메소드를 이용하면 된다. Object는 모든 클래스의 최상위 클래스이므로
모든 클래스에서 getClass() 메소드를 호출 할 수 있다.
Class clazz = obj.getClass();
getClass() 메소드는 해당 클래스로 객체를 생성했을 때만 사용할 수 있는데, 객체를 생성하기 전에 직접 Class 객체를 얻을 수도 있다.
Class는 생성자를 감추고 있기 때문에 new로 생성이 불가능 하고, 정적 메소드인 forName()을 이용해야 한다.
forName() 메소드는 클래스 전체 이름을 매개값으로 받고(패키지가 포함된 이름) Class 객체를 리턴한다.
Class clazz = Class.forName(String className);
package main.java.chap11.getclass;
import main.java.chap11.deepClone.Car;
import static java.lang.Class.*;
public class ClassExample {
public static void main(String[] args) {
Car car = new Car("그랜져");
Class clazz1 = car.getClass();
System.out.println(clazz1.getName());
System.out.println(clazz1.getSimpleName());
System.out.println(clazz1.getPackage().getName());
System.out.println("--------------------------");
try{
Class<?> clazz2 = forName("main.java.chap11.deepClone.Car");
System.out.println(clazz2.getName());
System.out.println(clazz2.getSimpleName());
System.out.println(clazz2.getPackage().getName());
}catch(ClassNotFoundException e){
}
// 결과
// main.java.chap11.deepClone.Car
// Car
// main.java.chap11.deepClone
// --------------------------
// main.java.chap11.deepClone.Car
// Car
// main.java.chap11.deepClone
}
}
11.6.2 리플렉션
Class 객체를 이용하면 클래스의 생성자, 필드, 메소드 정보를 알아내는것을 리플랙션이라 한다.
Class 객체는 리플렉션을 위해 getDeclaredConstructors()
, getDeclaredFields()
, getDeclaredMethods()
를 제공하고 있다.
package main.java.chap11.part6.reflection;
public class Car {
public String model;
public int speed =0;
public Car(String model) {
this.model = model;
}
public void addSpeed(int speed){
this.speed += speed;
}
public void subSpeed(int speed){
this.speed -= speed;
}
}
package main.java.chap11.part6.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionExample {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("main.java.chap11.part6.reflection.Car");
System.out.println("[클래스 이름]");
System.out.println(clazz.getName());
System.out.println();
System.out.println("[생성자 정보]");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor: constructors) {
System.out.println("constructor.getName() = " + constructor.getName());
Class[] parameterTypes = constructor.getParameterTypes();
printParameters(parameterTypes);
}
System.out.println("[필드 정보]");
Field[] fields = clazz.getDeclaredFields();
for (Field field: fields) {
System.out.println("field.getType().getSimpleName() = " + field.getType().getSimpleName());
System.out.println("field.getName() = " + field.getName());
}
System.out.println("[메소드 정보]");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method: declaredMethods) {
System.out.println("method.getName() = " + method.getName());
Class<?>[] parameterTypes = method.getParameterTypes();
printParameters(parameterTypes);
}
}
private static void printParameters(Class[] parameters){
for(int i =0 ; i < parameters.length; i ++){
System.out.println("parameters[i].getName() = " + parameters[i].getName());
if(i < parameters.length - 1){
System.out.print(",");
}
}
}
}
// [클래스 이름]
// main.java.chap11.part6.reflaction.Car
//
// [생성자 정보]
// constructor.getName() = main.java.chap11.part6.reflaction.Car
// parameters[i].getName() = java.lang.String
// [필드 정보]
// field.getType().getSimpleName() = String
// field.getName() = model
// field.getType().getSimpleName() = int
// field.getName() = speed
// [메소드 정보]
// method.getName() = addSpeed
// parameters[i].getName() = int
// method.getName() = subSpeed
// parameters[i].getName() = intnt
11.6.3 동적 객체 생성(newInstance())
Class 객체를 이용하면 new 연산자를 사용하지 않아도 동적으로 객체를 생성할 수 있다. 이 방법은 코드 작성 시에 클래스 이름을 결정할 수 없고,
런타임 시에 클래스 이름이 결정되는 경우에 매우 유용하게 사용된다.
Class.forName() 메소드로 Class 객체를 얻은 다음 newInstance() 메소드를 호출하면 Object 타입의 객체를 얻을 수 있다.
try{
Class clazz = Class.forName("런타임 시에 클래스 이름이 결정되는 클래스 이름")
Object obj = clazz.newInstance();
}catch(ClassNotFoundException e){
}catch(InstantiationException e){
}catch(IllegalAccessException e){
}
- newInstance() 메소드는 기본 생성자를 호출해서 객체를 생성하기 때문에 반드시 클래스에 기본 생성자가 존재해야 한다.
- 만약 매개 변수가 있는 생서자를 호출하고 싶다면 리플렉션으로 Constructor 객체를 얻어 newInstance() 메소드를 호출하면된다.
- 발생할 수 있는 2가지의 예외
InstantiationException → 예외는 해당 클래스가 추상 클래스거나 인터페이스일 경우에 발생,
IllegalAccessException → 예외는 클래스나 생성자가 접근제한자로 인해 접근할 수 없을 경우에 발생한다.
package main.java.chap11.part6.newInstance;
public interface Action {
public void execute();
}
package main.java.chap11.part6.newInstance;
public class ReceiveAction implements Action{
@Override
public void execute() {
System.out.println("데이터를 받습니다.");
}
}
package main.java.chap11.part6.newInstance;
public class SendAction implements Action{
@Override
public void execute() {
System.out.println("데이터를 보낸다.");
}
}
package main.java.chap11.part6.newInstance;
import java.lang.reflect.InvocationTargetException;
public class NewInstanceExample {
public static void main(String[] args) {
try {
//Class<?> clazz = Class.forName("main.java.chap11.part6.newInstance.SendAction");
Class<?> clazz = Class.forName("main.java.chap11.part6.newInstance.ReceiveAction");
Action action = (Action) clazz.getDeclaredConstructor().newInstance();
action.execute();
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
- newInstance() 메소드의 리턴 타입은 Object이므로 이것을 원래 클래스 타입으로 변환해야만 메소드를 사용 할 수 있다.
하지만 클래스 타입을 모르는 상태이므로 변환이 불가능함, 이 문제를 해결하려면 인터페이스 사용이 필요하다. - Class.forName() 메소드의 매개값으로 "SendAction" 또는 "ReceiveAction"을 주면 Class 객체가 만들어지고, Class 객체의 newInstance() 메소드로
Object객체를 얻을 수 있다. 얻어진 객체는 모두 Action인터페이스를 구현하고 있기 때문에 다음과 같이 Action 인터페이스 타입으로 변환이 가능하다.
그런다음, Action 인터페이스의 execute() 메소드를 호출하면 개별 클래스의 실체 메소드인 execue() 메소드가 실행된다.
요약하면
Class<?> clazz = Class.forName("main.java.chap11.part6.newInstance.SendAction");
Class<?> clazz = Class.forName("main.java.chap11.part6.newInstance.ReceiveAction");
(SendAction? ReceiveAction?)action = (SendAction? ReceiveAction?) clazz.getDeclaredConstructor().newInstance();
action.execute();
action으로 어떤 타입과 일치할지 모르니 인터페이스 타입을 이용하자는 내용
11.7 String 클래스
11.7.1 String생성자
- 자바의 문자열은 java.lang 패키지의 String 클래스의 인스턴스로 관리한다.
- 소스상에서 문자열 리터럴은 String 객체로 자동 생성되지만, String 클래스의 생성자를 이용해 직접 생성할 수도 있다.
//배열 전체를 String 객체로 생성
String str = new String(byte[] bytes);
//지정한 문자셋으로 디코딩
String str = new String(byte[] bytes, String charsetName);
//배열의 offset 인덱스 위치부터 length만큼 String 객체로 생성
String str = new String(byte[] bytes, int offset, int length);
//지정한 문자셋으로 디코딩
String str = new String(byte[] bytes, int offset, int length, String charsetName)
package main.java.chap11.part7.string;
import java.io.IOException;
public class KeyboardToStringExample {
public static void main(String[] args) throws IOException {
byte[] bytes = new byte[100];
System.out.println("입력: ");
int readByteNo = System.in.read(bytes);
String inputString = new String(bytes, 0, readByteNo - 1);
System.out.println(inputString);
}
}
11.7.2 String 메소드
문자열 비교(equals())
String strVar1 = new String("신민철");
String strVar2 = "신민철"
String strVar3 = "신민철"
strVar1 == strVar2 -> false
strVar2 == strVar3 -> true
strVar1.equals(strVar2) -> true
strVar2.equals(strVar3) -> true
String 클래스가 equals를 오버라이딩 해놨기 때문
바이트 배열로 변환(getBytes())
//인코딩
byte[] bytes = "문자열".getBytes();
byte[] bytes = "문자열".getBytes(Charset charset);
byte[] bytes = "문자열".getBytes("UTF-8");
//디코딩
String str = new String(byte[] bytes, "UTF-8");
문자열 대치(replace())
String 객체의 문자열은 변경이 불가한 특성을 갖기 때문에 replace() 메소드가 리턴하는 문자열은 원래 문자열의 수정본이 아니라 새로운 문자열이다.
11.9 StringBuffer, StringBuilder클래스
문자열을 저장하는 String은 내부의 문자열을 수정할 수 없다. 예를 들어 String의 replace() 메소드는 내부의 문자를 대치하는 것이 아니라, 대치된 새로운 문자열을
리턴한다. String 객체를 + 연산할때도 마찬가지다.
String data = "ABC";
data += "DEF"
StringBuffer와 StringBuilder의 사용법은 동일, 차이점은 StringBuffer는 멀티스레드 환경에서 사용할 수 있도록 동기화가 적용되어 있어 스레드 안전하지만,
StringBuilder는 단일 스레드 환경에서만 사용하도록 설계되어 있다.
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder(16);
StringBuilder sb = new StringBuilder("JAVA");
package main.java.chap11.part9;
public class StringBuilderExample {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("JAVA ");
sb.append("Program Study");
System.out.println("sb = " + sb.toString());
//4번째 뒤에 2 삽입
sb.insert(4, "2");
System.out.println("sb = " + sb.toString());
//4번째 위치의 문자열을 6으로 변경
sb.setCharAt(4, '6');
System.out.println("sb = " + sb.toString());
//index6부터 13 '전'까지 Book으로 대체
sb.replace(6, 13, "Book");
System.out.println("sb = " + sb.toString());
//index4부터 '5'전까지 삭제
sb.delete(4,5);
System.out.println("sb = " + sb.toString());
//총 문자열 수
int length = sb.length();
System.out.println("length = " + length);
//버퍼에있는 것을 String타입으로 리턴
String string = sb.toString();
System.out.println("string = " + string);
}
}
11.10 정규 표현식과 Pattern 클래스
package main.java.chap11.part10;
import java.util.regex.Pattern;
public class PatternExample {
public static void main(String[] args) {
String regExp = "(02|010)-\\d{3,4}-\\d{4}";
String data = "010-123-4567";
boolean matches = Pattern.matches(regExp, data);
if(matches){
System.out.println("정규식과 일치합니다.");
}else{
System.out.println("정규식과 일치하지 않습니다.");
}
String regExp2 = "\\w+@\\w+\\.\\w+(\\.\\w+)?";
String data2 = "angel@navercom";
boolean result2 = Pattern.matches(regExp2, data2);
if(result2){
System.out.println("정규식과 일치합니다.");
}else{
System.out.println("정규식과 일치하지 않습니다.");
}
}
}
'책 > 이것이 자바다' 카테고리의 다른 글
[11장] 기본API클래스(3) (0) | 2022.02.14 |
---|---|
[11장]기본 API클래스 (0) | 2022.02.14 |
[10장]예외처리 (0) | 2022.02.14 |
[9장]중첩 클래스와 중첩 인터페이스 (0) | 2022.02.14 |
[8장]인터페이스 (0) | 2022.02.14 |