KDT/Java

240110 Java

001cloudid 2024. 1. 10. 17:41
728x90

String 클래스

String을 선언하는 방법

String s1 = new String("CString"); //생성자의 매개변수로 문자열 생성

String s2 = "String"; //문자열 상수를 가리키는 방식

 

문자열을 생성자의 매개변수로 하여 생성하는 방식과

이미 생성된 문자열 상수를 가리키는 방식이 있다.

 

이 두 방법은 내부적으로 큰 차이가 있다.

new 예약어를 사용하여 객체를 생성하는 경우 "CString" 문자열을 위한 메모리가 할당되고 새로운 객체가 생성된다.

문자열 상수를 가리키는 방식은 s2가 기존에 만들어져 있던 "String"이라는 문자열 상수의 메모리 주소를 가리키게 된다.

String s3 = "String"; 코드를 작성하게 되면 s2와 s3는 주소 값이 같게 된다.(프로그램에서 사용되는 상수 값을 저장하는 공간을 '상수 풀'이라고 한다.

package stringClass;

 

public class StringTest1 {

 

public static void main(String[] args) {

String s1 = new String("java");

String s2 = new String("java");

 

System.out.println(s1==s2); //주소값

System.out.println(s1.equals(s2)); //문자값

 

String s3 = "java";

String s4 = "java";

 

System.out.println(s3==s4); //주소값

System.out.println(s3.equals(s4)); //문자값

 

}

 

}

false

true

true

true

 

한 번 생성된 문자열은 변경되지 않는다. 이런 문자열의 특징을 '문자열은 불변한다.'라고 한다.

package stringClass;

 

public class StringTest2 {

 

public static void main(String[] args) {

String s1 = new String("java");

String s2 = new String("JAVA");

System.out.println(s1);

System.out.println("처음 문자열 주소 값 : " + System.identityHashCode(s1));

 

s1 = s1.concat(s2);

 

System.out.println(s1);

System.out.println("연결된 문자열 주소 값 : " + System.identityHashCode(s1));

 

}

 

}

java

처음 문자열 주소 값 : 93122545

javaJAVA

연결된 문자열 주소 값 : 142666848

 

두 문자열이 연결된 것 같지만, 변수 값 자체가 변하는 것이 아니라 새로운 문자열이 생성된 것이다.

 

상황에 따라 문자열을 변경하거나 연결해야 할 때가 많다. 하지만 String 클래스는 한 번 생성되면 그 내부의 문자열이 변경되지 않기 때문에 String 클래스를 사용하여 문자열을 계속 연결하거나 변경하는 프로그램을 작성하면 메모리가 낭비된다.

이 문제를 해결하는 것을 StringBuffer와 StringBuilder 클래스이다.

StringBuffer와 StringBuilder는 내부에 변경 가능한 char[]를 변수로 가지고 있다. 이 클래스들을 사용하여 문자열을 연결하면 기존에 사용하던 char[] 배열이 확장되므로 추가 메모리를 사용하지 않는다. 따라서 문자열을 연결하거나 변경할 경우 두 클래스 중 하나를 사용하면 된다.

두 클래스의 차이는 여러 작업(스레드)이 동시에 문자열을 변경하려 할 때,

StringBuffer 클래스는 문자열이 안전하게 변경되도록 보장한다.

StringBuilder 클래스는 문자열이 안전하게 변경되도록 보장하지 않는다(실행 속도가 더 빠르다).

※두 스레드가 동시에 문자열을 변경할 때 문자열의 안전한 변경을 보장해 주는 것을 '스레드 동기화의 안정성'이라고 한다.

package stringClass;

 

public class StringBuilderTest {

 

public static void main(String[] args) {

String s1 = new String("java");

System.out.println("s1 문자열 주소 : " + System.identityHashCode(s1));

 

StringBuilder buffer = new StringBuilder(s1);

System.out.println("연산 전 buffer 메모리 주소 : " + System.identityHashCode(buffer));

 

buffer.append(" is ");//.append 문자열을 추가

buffer.append(" hard.");

System.out.println("연산 후 buffer 메모리 주소 : " + System.identityHashCode(buffer));

 

s1 = buffer.toString();

System.out.println(s1);

System.out.println("새로 만들어진 s1 문자열 주소 : " + System.identityHashCode(s1));

 

 

}

 

}

s1 문자열 주소 : 93122545

연산 전 buffer 메모리 주소 : 142666848

연산 후 buffer 메모리 주소 : 142666848

java is hard.

새로 만들어진 s1 문자열 주소 : 1060830840

 

append() 메소드가 실행될 때마다 메모리가 새로 생성되는 것이 아니라, 하나의 메모리에 계속 연결되는 것을 해시 코드 값을 통해 알 수 있다. 연산 전 메모리 주소와 연산 후 메모리 주소가 같기 때문이다. 문자열을 변경한 후 toString() 메소드를 호출하면 다시 문자열로 반환할 수 있다.

 

Wrapper 클래스

매개변수가 객체거나 반환 값이 객체형인 경우에 정수를 객체형으로 사용하는 경우가 있다.

public void setValue(Integer i) {} //객체를 매개변수로 받는 경우

public Integer returnValue() {} //반환 값이 객체형인 경우

 

이를 위해 자바에서는 기본 자료형처럼 사용할 수 있는 클래스를 제공한다. 이러한 클래스를 기본 자료형으로 감쌌다는 의미로 Wrapper 클래스라고 한다.

 

Class 클래스

자바의 모든 클래스와 인터페이스는 컴파일되고 나면 class 파일로 생성된다.

.java 파일이 컴파일되면 .class 파일이 생성되고 이 class 파일에는 클래스나 인터페이스에 대한 변수, 메소드, 생성자 등의 정보가 들어 있다. Class 클래스는 컴파일 된 class 파일에 저장된 클래스나 인터페이스 정보를 가져오는 데 사용한다.

 

여러 클래스 중에 상황에 따라 다른 클래스를 사용해야 할 때도 있고, 반환받는 클래스가 정확히 어떤 자료형인지 모를 때도 있다. 이렇게 모르는 클래스의 정보를 사용할 경우 클래스 정보를 직접 찾아야하는데, 이 때 Class 클래스를 활용한다.

 

Class 클래스를 선언하고 클래스 정보를 가져오는 방법

1. Object 클래스의 getClass() 메소드 사용하기

String s = new String();

Class c = s.getClass(); //getClass() 메소드의 반환형은 Class

Object에 선언한 getClass() 메소드는 모든 클래스가 사용할 수 있는 메소드이다. 이 메소드를 사용하기 위해서는 이미 생성된 인스턴스가 있어야한다.

 

2. 클래스 파일 이름을 Class 변수에 직접 대입하기

Class c = String.Class;

컴파일된 클래스 파일이 있다면 클래스 이름만으로 Class 클래스를 반환받는다.

 

3. Class.forName("클래스 이름") 메소드 사용

Class c = Class.forName("java.lang.String");

 

컴파일된 클래스 파일이 있다면 클래스 이름만으로 Class 클래스를 반환받는다.

 

Class 클래스를 반환받고 활용

package classClass;

 

public class Person {

private String name;

private int age;

 

 

public Person() {} //기본 생성자

 

//이름만 입력받는 생성자

public Person(String name) {

this.name = name;

}

 

//이름, 나이를 입력 받는 생성자

public Person(String name, int age) {

this.name = name;

this.age = age;

}

 

public String getName() {

return name;

}

 

public void setName(String name) {

this.name = name;

}

 

public int getAge() {

return age;

}

 

public void setAge(int age) {

this.age = age;

}

 

}

 

package classClass;

 

public class ClassTest {

 

public static void main(String[] args) throws ClassNotFoundException{

 

Person person = new Person();

Class pClass1 = person.getClass(); //Object의 getClass()메소드 사용하기

System.out.println(pClass1.getName());

 

Class pClass2 = Person.class; //직접 class 파일 대입하기

System.out.println(pClass2.getName());

 

Class pClass3 = Class.forName("classClass.Person"); //클래스 이름으로 가져오기

System.out.println(pClass3.getName());

 

}

 

}

classClass.Person

classClass.Person

classClass.Person

 

forName() 메소드는 '패키지이름.클래스이름'으로 가져오는 경우에 매개변수로 쓰이는 값이 문자열이다.

이때 매개변수로 받은 문자열에 해당하는 클래스가 존재하지 않으면 클래스를 가져오는 데 실패한다.

ClassNotFoundException이 발생한다.

Class 클래스를 가져온 후 getName() 메소드를 호출하면 클래스의 이름이 출력된다.

즉, Class 클래스를 통하여 클래스 정보를 알 수 있다.

 

Class 클래스를 활용해 클래스 정보 알아보기

사용할 클래스의 자료형을 모르는 경우가 있을 수 있다. 이때 Class 클래스를 가져올 수 있다면 해당 클래스 정보,

즉 생성자, 메소드, 멤버 변수 정보를 찾을 수 있다. 사용하려는 클래스의 자료형을모르는 상태에서 Class 클래스를 활용하여 그 클래스의 정보를 가져오고, 이 정보를 활용하여 인스턴스를 생성하거나 메소드를 호출하는 방식을 '리플렉션(reflection)'이라고 한다.

package stringClass;

 

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

 

public class StringClassTest {

 

public static void main(String[] args) throws ClassNotFoundException{

Class strClass = Class.forName("java.lang.String"); //클래스 이름으로 가져오기

 

Constructor[] cons = strClass.getConstructors(); //모든 생성자 가져오기

for(Constructor c : cons) {

System.out.println(c);

}

 

System.out.println("======================");

Field[] fields = strClass.getFields(); //모든 멤버 변수(필드) 가져오기

for(Field f : fields) {

System.out.println(f);

}

 

System.out.println("======================");

Method[] methods = strClass.getMethods();//모든 메소드 가져오기

for(Method m : methods) {

System.out.println(m);

}

 

 

}

 

}

public java.lang.String(java.lang.StringBuffer)

public java.lang.String(java.lang.StringBuilder)

public java.lang.String(byte[],int,int,java.nio.charset.Charset)

public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException

public java.lang.String(byte[],java.nio.charset.Charset)

public java.lang.String(byte[],int,int)

public java.lang.String(byte[])

public java.lang.String(char[],int,int)

public java.lang.String(char[])

public java.lang.String(java.lang.String)

public java.lang.String()

public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException

public java.lang.String(byte[],int)

public java.lang.String(byte[],int,int,int)

public java.lang.String(int[],int,int)

======================

public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER

======================

public boolean java.lang.String.equals(java.lang.Object)

public int java.lang.String.length()

public java.lang.String java.lang.String.toString()

public int java.lang.String.hashCode()

public void java.lang.String.getChars(int,int,char[],int)

public int java.lang.String.compareTo(java.lang.String)

public int java.lang.String.compareTo(java.lang.Object)

public int java.lang.String.indexOf(int)

public int java.lang.String.indexOf(java.lang.String)

public int java.lang.String.indexOf(java.lang.String,int)

public int java.lang.String.indexOf(int,int)

public static java.lang.String java.lang.String.valueOf(int)

public static java.lang.String java.lang.String.valueOf(char[])

public static java.lang.String java.lang.String.valueOf(java.lang.Object)

public static java.lang.String java.lang.String.valueOf(boolean)

public static java.lang.String java.lang.String.valueOf(char[],int,int)

public static java.lang.String java.lang.String.valueOf(char)

public static java.lang.String java.lang.String.valueOf(double)

public static java.lang.String java.lang.String.valueOf(float)

public static java.lang.String java.lang.String.valueOf(long)

public char java.lang.String.charAt(int)

public int java.lang.String.codePointAt(int)

public int java.lang.String.codePointBefore(int)

public int java.lang.String.codePointCount(int,int)

public int java.lang.String.offsetByCodePoints(int,int)

public byte[] java.lang.String.getBytes(java.nio.charset.Charset)

public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException

public void java.lang.String.getBytes(int,int,byte[],int)

public byte[] java.lang.String.getBytes()

public boolean java.lang.String.contentEquals(java.lang.CharSequence)

public boolean java.lang.String.contentEquals(java.lang.StringBuffer)

public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int)

public boolean java.lang.String.regionMatches(int,java.lang.String,int,int)

public boolean java.lang.String.startsWith(java.lang.String,int)

public boolean java.lang.String.startsWith(java.lang.String)

public int java.lang.String.lastIndexOf(java.lang.String)

public int java.lang.String.lastIndexOf(java.lang.String,int)

public int java.lang.String.lastIndexOf(int,int)

public int java.lang.String.lastIndexOf(int)

public java.lang.String java.lang.String.substring(int,int)

public java.lang.String java.lang.String.substring(int)

public boolean java.lang.String.isEmpty()

public java.lang.String java.lang.String.replace(char,char)

public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)

public boolean java.lang.String.matches(java.lang.String)

public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String)

public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String)

public java.lang.String[] java.lang.String.split(java.lang.String)

public java.lang.String[] java.lang.String.split(java.lang.String,int)

public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[])

public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable)

public java.lang.String java.lang.String.toLowerCase()

public java.lang.String java.lang.String.toLowerCase(java.util.Locale)

public java.lang.String java.lang.String.toUpperCase()

public java.lang.String java.lang.String.toUpperCase(java.util.Locale)

public java.lang.String java.lang.String.trim()

public java.lang.String java.lang.String.strip()

public java.lang.String java.lang.String.stripLeading()

public java.lang.String java.lang.String.stripTrailing()

public java.util.stream.Stream java.lang.String.lines()

public java.lang.String java.lang.String.repeat(int)

public boolean java.lang.String.isBlank()

public char[] java.lang.String.toCharArray()

public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[])

public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[])

public java.lang.Object java.lang.String.resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup) throws java.lang.ReflectiveOperationException

public java.lang.String java.lang.String.resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup)

public java.util.stream.IntStream java.lang.String.codePoints()

public boolean java.lang.String.equalsIgnoreCase(java.lang.String)

public int java.lang.String.compareToIgnoreCase(java.lang.String)

public boolean java.lang.String.endsWith(java.lang.String)

public java.lang.CharSequence java.lang.String.subSequence(int,int)

public java.lang.String java.lang.String.concat(java.lang.String)

public boolean java.lang.String.contains(java.lang.CharSequence)

public java.lang.String java.lang.String.indent(int)

public java.lang.String java.lang.String.stripIndent()

public java.lang.String java.lang.String.translateEscapes()

public java.util.stream.IntStream java.lang.String.chars()

public java.lang.Object java.lang.String.transform(java.util.function.Function)

public java.lang.String java.lang.String.formatted(java.lang.Object[])

public static java.lang.String java.lang.String.copyValueOf(char[],int,int)

public static java.lang.String java.lang.String.copyValueOf(char[])

public native java.lang.String java.lang.String.intern()

public java.util.Optional java.lang.String.describeConstable()

public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

public final void java.lang.Object.wait() throws java.lang.InterruptedException

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException

public final native java.lang.Class java.lang.Object.getClass()

public final native void java.lang.Object.notify()

public final native void java.lang.Object.notifyAll()

 

Class 클래스를 가져오기 위해 forName() 메소드를 사용한다. 이 메소드는 정적 메소드이므로 클래스를 생성하지 않아도 사용할 수 있다. 

Class 클래스와 java.lang.reflect 패키지에 있는 클래스를 활용하면 클래스 이름만 알아도 클래스의 생성자, 메소드 등 정보를 알 수 있다.

 

newInstance() 사용해 클래스 생성

Class 클래스를 사용하여 클래스 정보를 확인할 수 있다. 이 정보를 바탕으로 인스턴스도 생성할 수 있다. 

Class 클래스의 메소드 중 newInstance() 메소드를 사용하면 된다.

newInstance() 메소드는 항상 Object를 반환하므로 생성된 객체형으로 형 변환해야 한다.

package stringClass;

 

import classClass.Person;

 

public class NewInstanceTest {

 

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{

Person p1 = new Person();

System.out.println(p1);

 

Class pClass = Class.forName("classClass.Person");

Person p2 = (Person)pClass.newInstance(); //Class 클래스의 newInstance() 메소드로 생성하기

System.out.println(p2);

 

}

 

}

classClass.Person@7c30a502

classClass.Person@49e4cb85

 

두 가지 방법으로 인스턴스를 생성하고 있다.

Person 클래스의 변수를 선언하고 생성자를 사용하여 생성하는 방법과

Class 클래스의 newInstance()를 사용하여 인스턴스를 생성했다

Person 클래스 이름을 사용하여 Class 클래스를 반환했다. 그리고 Class 클래스의 newInstance() 메소드를 호출하면 Person 클래스의 디폴트 생성자가 호출되어 인스턴스가 생성된다.

newInstance()의 반환 값이 Object이므로 Person 클래스로 다운 캐스팅한 것을 알 수 있다.

 

리플렉션 프로그래밍은 컴파일 시점에 알 수 없는 클래스, 즉 프로그램 실행 중에 클래스를 메모리에 로딩하거나 객체가 다른 곳에 위치해서 원격으로 로딩하고 생성할 때 사용한다.

728x90

'KDT > Java' 카테고리의 다른 글

240115 Java  (0) 2024.01.15
240111 Java  (0) 2024.01.11
240108 Java  (0) 2024.01.08
240104 Java  (0) 2024.01.04
240103 Java  (0) 2024.01.03