8. 함수(function)

9. 변수의 생존 범위

10. closure(클로저)

11. 일급함수

12. Module

13. 사용자 정의 모듈

14. turtle

15. 외부 모듈 사용

16. Class

17. 포함관계 (has a 관계)

18. 상속

19. 메소드 오버라이딩


8. 함수(function)

 : 여러개의 명령문을 하나의 묶음으로 만든 실행단위.

* test07_func

 - 내장함수

 - sum(a), bin(a), int(a), float(a), str(a)

print(sum([3, 5, 7]))
print(bin(8))
print(int(1.7), float(7), str(5) + '오')

 

eval(문자열) : 문자열을 계산 

a = 10
b = eval('a + 5')
print(b)

 

zip(a, b) : tuple로 묶어준다.

x = [10, 20, 30]
y = ['a', 'b']
for i in zip(x, y):
    print(i)
# (10, 'a')
# (20, 'b')

 

 

- 사용자 정의 함수

def DoFunc1():
    print('사용자정의 함수 1')
    
print("do1")
DoFunc1()
print("do2")
DoFunc1()
print(DoFunc1)       # 함수 이름은 객체(함수 본체)의 주소를 기억함수
print(type(DoFunc1)) # function
otherFunc = DoFunc1  # 주소 치환
otherFunc()

globals() : 모든 전역 변수 출력

print(globals()) # 모든 전역 변수 출력

 

def DoFunc2(name):
    print('hi', name)
    
DoFunc2('tom')
#DoFunc2('tom', 'tom2') # error
DoFunc2(100)

 

def DoFunc3(ar1, ar2):
    temp = ar1 + ar2
    print('temp', temp)
    # return None # default : None
    #return temp
    a = 2
    if a % 2 ==1:
        return
    else:
        return a
    print('dead')

    
DoFunc3(10, 20)
DoFunc3('kbs', 'mbc')
print(DoFunc3('kbs', 'mbc'))

 

def area_tri(a, b):
    c = a * b / 2
    if a == 0:
        return # 함수 탈출
    area_print(c) # 함수는 함수 호출가능
    
def area_print(c):
    print('삼각형의 넓이는 ',c)

area_tri(20, 30) # 삼각형의 넓이는  300.0

 

def abc():
    pass

 

def exam(a, b):
    ss = str(a) + '+' + str(b) + '=의 답은 : '
    ans = input(ss)
    return a + b == int(ans)

if(exam(5, 2)):
    print('good')
else:
    print('sad')

 

def swap(a, b):
    return b, a

a = 10; b = 20
print(swap(a, b)) # (20, 10)

 

def isOdd(arg):
    return arg % 2 == 1

print(isOdd(3)) # True
print(isOdd(4)) # False

myDict = {x:x*x for x in range(11) if isOdd(x)}
print(myDict) # {1: 1, 3: 9, 5: 25, 7: 49, 9: 81}

9. 변수의 생존 범위

 Local < Enclosing function > Global

* test08_func

player = '전국대표'        # Global Variable

def funcSoccer():
    player = '지역대표'    # Local Variable
    name = '한국인'       # Local Variable
    print(name, player)

funcSoccer()
# print(name, player) # 지역변수는 해당 블록에서만 사용 가능. Error

global a : 블록 내의 지역변수가 아닌 전역변수를 사용하도록 한다.
nonlocal a : 블록 내의 지역변수가 아닌 가까운 함수의 변수를 사용하도록 한다.

a = 10; b = 20; c = 30
print('1) a:{}, b:{}, c:{}'.format(a,b,c))

def Foo():
    a = 40
    b = 50
    def Bar():
        #c = 60
        global c # 블록 내의 지역변수가 아닌 전역변수를 사용하도록 한다.
        nonlocal b # 블록 내의 지역변수가 아닌 가까운 함수의 변수를 사용하도록 한다.
        print('2) a:{}, b:{}, c:{}'.format(a,b,c)) # Enclosing function
        c = 60   # UnboundLocalError: local variable 'c' referenced before assignment
        b = 70
    Bar()

Foo()
#Foo().Bar()
print('3) a:{}, b:{}, c:{}'.format(a,b,c))
print()

 

 - argument 키워드로 매칭

def ShowGugu(start = 1, end = 5):
    print(start, end)

ShowGugu(2, 8)      # 2 8
ShowGugu()          # 1 5
ShowGugu(2)         # 2 5
ShowGugu(start = 2) # 2 5
ShowGugu(end = 10)  # 1 10
ShowGugu(start = 2, end = 7)
ShowGugu(end = 7, start = 2)
#ShowGugu(start = 2, 7) # SyntaxError: positional argument follows keyword argument
ShowGugu(2, end = 7)

 

 - 가변인수 : 인수의 개수가 부정확한 경우

def func1(*ar):
    print(ar)
    for i in ar:
        print('음식 : ' + i)

func1('비비비')
func1('비비비','이이이','트트트트')

 

def func2(a, *ar):
#def func2(*ar, a): # TypeError
    print(a)
    print(ar)
    for i in ar:
        print('음식 : ' + i)

func2('비비비')
func2('비비비','이이이','트트트트')

 

re = 0
def selectProcess(choice, *ar):
    if choice == '+':
        re = 0
        for i in ar:
            re += i
    elif choice == '*':
        re = 1
        for i in ar:
            re *= i
    return re

print(selectProcess('+',1,2,3,4,5))
print(selectProcess('*',1,2,3,4,5))

 

def func3(w, h, **ect):
    print('몸무게 {}, 키 {}'.format(w, h))
    print(ect)
    
func3(65, 175, name = '홍길동')
func3(65, 178, name = '고길동', age = 22)
func3(w=80, h=175, name = '김길동')

 

def func4(a, b, *v1, **v2):
    print(a, b)
    print(v1)
    print(v2)
    
func4(1, 2)
func4(1, 2, 3, 4, 5)
func4(1, 2, 3, 4, 5, k=9, s=5)

10. closure(클로저)

 : 내부 함수의 주소를 반환해서 함수의 멤버를 계속적으로 참조

* test09_closure

def out():
    count = 0
    def inn():
        nonlocal count
        count += 1
        return count
    print(inn())

#print(count)
out() # 1
out() # 1
def outer():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner     # 클로저(내부 함수의 주소를 반환)

obj1 = outer() # inner의 주소를 치환
print(obj1)
print(obj1()) # 1

result = obj1()
print(result) # 2

print(obj1()) # 3
print()

 

 - 수량 * 단가 * 세급 결과 출력

def outer2(tax):
    def inner2(su, dan):
        amount = su * dan * tax
        return amount
    return inner2 # 클로저

q1 = outer2(0.1)  # tax = 0.1
print(q1)
result1 = q1(5, 10000)
print(result1)    # 5000.0

result2 = q1(10, 20000)
print(result2)    # 20000.0

q2 = outer2(0.05) # tax = 0.05
result3 = q2(5, 10000)
print(result3)    # 2500.0

result4 = q2(10, 20000)
print(result4)    # 10000.0

 

 - 재귀함수 - 반복처리

def CountDown(n):
    if n == 0:
        print('처리완료')
    else:
        print(n, end = ' ')
        CountDown(n - 1) # 함수가 자신을 호출
        
CountDown(5) # 5 4 3 2 1 처리완료
def totFunc(su):
    if su == 1:
        print('처리 끝')
        return 1
    return su + totFunc(su - 1)

re = totFunc(10)
print('10까지의 합은 : ', re) # 10까지의 합은 :  55

11. 일급함수

 : 함수 안에 함수 선언 가능, 인자로 함수 사용, 반환값이 함수 가능

def func1(a, b):
    return a + b

func2 = func1      # 함수의 주소를 치환
print(func1(2,3))  # 5
print(func2(2,3))  # 5

def func3(func):   # 인자로 함수 사용
    def func4():   # 함수 안에 함수 선언 가능
        print('내부함수')
    func4()
    return func    # 반환값이 함수 가능

mbc = func3(func1) # func1
print(mbc(3, 4))   # 내부함수 7

 

 - Lambda : 이름이 없는 한 줄짜리 함수
형식 : lambda arg, ... :표현식   => return문 없이 결과 반환

def hap(x, y):
    return x + y
print(hap(2, 4))   # 6

aa = lambda x, y: x + y
print(aa(2, 4))    # 6

kbs = lambda a, su = 10: a + su
print(kbs(5))      # 15
print(kbs(5, 6))   # 11

sbs = lambda a, *tu, **di : print(a, tu, di)
sbs(1,2,3,tvn=3, ytn=24) # 1 (2, 3) {'tvn': 3, 'ytn': 24}

li = [lambda a, b:a+b, lambda a, b:a*b]
print(li[0](3,4))  # 7
print(li[1](3,4))  # 12

 

 - 다른 함수에서 인자 값으로 람다를 사용
filter(함수, 집합형 자료) : 자료의 요소 하나씩를 함수에 입력하여 결과값을 집합형으로 출력

print(list(filter(lambda a: a < 5, range(10))))  # [0, 1, 2, 3, 4]
print(tuple(filter(lambda a: a % 2, range(10)))) # (1, 3, 5, 7, 9)

 

 - 함수 장식자 - meta 기능을 가짐. @함수명

def make2(fn):
    return lambda : 'hi '+ fn()

def make1(fn):
    return lambda : 'hello '+ fn()

def hello():
    return '이이이'

hi = make2(make1(hello))
print(hi()) # hi hello 이이이
print()

@make2
@make1
def hello2():
    return '리리리'

print(hello2()) # hi hello 리리리

hi2 = hello2()
print(hi2)

hi3 = hello2
print(hi3)
print(hi3())

12. Module

 : 소스 코드의 재사용을 가능하게 하며, 소스 코드를 하나의 이름 공간으로 구분하고 관리할 수 있다.
멤버 : 일반 명령문, 함수, 모듈, 클래스
하나의 파일로 처리된다.
내장된 표준 모듈, 사용자 정의 모듈, 제 3자 모듈(third party)

 

* test11_module

 -  내장된 표준 모듈(로딩 필요 없음) 일부 사용해 보기
 작업 중 외부모듈이 필요한 경우 import 모듈명하여 사용

print(sum([2, 3])) # 내장된 표준모듈

import sys
print('모듈 경로 : ',sys.path)
sys.exit() # 프로그램 강제 종료
print('프로그램  종료')
import math
print(math.pi)# 3.141592653589793
print(math.sin(math.radians(30))) # 0.49999999999999994
import calendar
calendar.setfirstweekday(6) # 첫주 시작을 일요일로 변경
calendar.prmonth(2021,2)    # 달력 출력
#    February 2021
# Mo Tu We Th Fr Sa Su
#  1  2  3  4  5  6  7
#  8  9 10 11 12 13 14
# 15 16 17 18 19 20 21
# 22 23 24 25 26 27 28

 

 - 난수 출력

import random
print(random.random())       # 난수 출력 
print(random.randint(1, 10)) # 1~10 사이의 난수

from random import random
print(random())

from random import randint
from random import *
print(randint(1, 10))

13. 사용자 정의 모듈

* test12_my (동일 패키지)

# 사용자 정의 모듈
tot = 123 # 전역변수

def ListHap(*ar):
    print(ar)
    
    if __name__ == "__main__":
        print('응용프로그램이 시작되는 모듈')
    
    
def kbs():
    ch = 9
    print('공영방송', ch)
    
def mbc():
    print('문화방송')

 

* test12_our (다른 패키지)

# 사용자 정의 모듈
def Hap(a, b):
    return a + b

def Cha(a, b):
    return a - b

* test12_our2 (lib폴더 경로)

# 사용자 정의 모듈
def Gop(a, b):
    return a * b

def Nanugi(a, b):
    return a / b

* test12_module

# 사용자 정의 모듈
a = 10
print(a)

def aa():
    print('aa 출력')
    
# 외부 모듈의 멤버 사용하기1
import pack2.test12_my  # 경로

print('tot : ', pack2.test12_my.tot) # tot :  123

li1 =[1, 2]
li2 =[3, 4]
pack2.test12_my.ListHap(li1, li2)


def abc():
    if __name__ == "__main__":
        print('응용프로그램이 시작되는 모듈')
        
abc()

pack2.test12_my.kbs()
print()

# 외부 모듈의 멤버 사용하기2
from pack2 import test12_my
test12_my.mbc() 

from pack2.test12_my import mbc
mbc() 

from pack2.test12_my import mbc, kbs, tot
mbc()
kbs()
print(tot)
print()

# 외부 모듈의 멤버 사용하기3 - package가 다른 경우
from other.test12_our import Hap, Cha
print(Hap(5, 3)) # 8
print(Cha(5, 3)) # 2

# C:\anaconda3\Lib 경로의 모듈 사용
import test12_our2
print(test12_our2.Gop(5, 3))
print(test12_our2.Nanugi(5, 3))

import math
print(math.pi)

from test12_our2 import Gop
print(Gop(5, 3))

from math import pi
print(pi)

window - Preferences - PyDev - Interpreters - Python Interpreters - Libraries Tab 선택 안의 경로에 추가시 import 할 수 있다. 


14. turtle

 : Graphic 지원 모듈 사용 - turtle

* test13_graphic

import turtle
from turtle import *
pen = Pen()
pen.color('red', 'yellow')
pen.begin_fill()
while True:
    pen.forward(200)
    pen.left(170)
    if abs(pen.pos()) < 1:
        break
pen.end_fill()
done()

15. 외부 모듈 사용

* test14

① http://www.lfd.uci.edu/~gohlke/pythonlibs/ 사이트에서 모듈 다운로드하여 lib경로에 설치 

② 시작  - anconda prompt - pip install pygame

import pygame

16. Class

: OOP 기법 구사
: 클래스는 새로운 이름 공간을 지원하는 단위. 멤버는 변수, 메소드, 생성자로 구성. 접근 지정자X, 메소드 오버로딩X

 

* test15_class

print('do')

def func():
    print('함수')
class TestClass: # 원형 클래스  - prototype (객체가 실행과 동시에 생성. new 미사용)
    abc = 1 # 멤버변수(전역변수)
    
    def __init__(self):
        print('생성자')
        
    def __del__(self):
        print('소멸자')
        
    def printMsg(self): # 메소드
        name = '홍길동'
        print(name)
        print(self.abc)
        self.show()
        
    def show(self):
        print('show')
print(TestClass.abc) # 원형 클래스의 멤버 변수 호출
TestClass.printMsg(self) # NameError: name 'self' is not defined
test = TestClass() # 생성자 호출된 후 객체 생성(instance)
# 생성자
print(test.abc) # 1
test.printMsg()          # 1. Bound method call
# 홍길동 1 show
TestClass.printMsg(test) # 2. UnBound Method call
# 홍길동 1 show
print('클래스 타입확인 : ', type(1))    # int
print('클래스 타입확인 : ', type(test)) # __main__.TestClass
print(id(test))      # 2502643056496
print(id(TestClass)) # 2502639316704

 

 * test16_class

class Car:
    handle = 0 # 멤버 변수(필드). 클래스 내 전역
    speed = 0
    
    def __init__(self, name, speed): # 생성자 오버로딩 불가
        self.name = name
        self.speed = speed
        
    def showData(self): # 메소드 오버로딩 불가
        km = ' 킬로미터' # 지역변수
        msg = '속도 : ' + str(self.speed) + km
        return msg
# car1    
car1  = Car('tom', 10)
print(car1.handle, car1.name, car1.speed) # 0 tom 10
car1.color = '핑크'
print('car1.color : ', car1.color)
# car2
car2 = Car('james', 30)
print(car2.handle, car2.name, car2.speed) # 0 james 30
print('car2.color : ', car2.color) # AttributeError: 'Car' object has no attribute 'color'
# __dict__ : 해당 인스턴스의 멤버변수 dict
print(car1.__dict__) # {'name': 'tom', 'speed': 10, 'color': '핑크'}
print(car2.__dict__) # {'name': 'james', 'speed': 30}
# method
print('car1 - ', car1.showData()) # car1 -  속도 : 10 킬로미터
print('car2 - ', car2.showData()) # car2 -  속도 : 30 킬로미터
car1.speed = 100
car2.speed = 200
print('car1 - ', car1.showData()) # car1 -  속도 : 100 킬로미터
print('car2 - ', car2.showData()) # car2 -  속도 : 200 킬로미터
print(Car.speed)  # 0
print(car1.speed) # 100
print(car2.speed) # 200

print(Car.color)  # AttributeError: type object 'Car' has no attribute 'color'
print(car1.color)  # 핑크
print(car2.color) # AttributeError: type object 'Car' has no attribute 'color'

 

* test17_class

kor = 100 # 모듈의 전역변수

def abc():
    print('모듈의 멤버 함수')
    
class MyClass:
    kor = 88
    """
    def __init__(self): # 기본 생성
        pass
    """
    def abc(self):
        print('클래스의 멤버 메소드')
       
    def showData(self):
        print(kor) # 메소드 내에서 없을 경우 모듈의 전역변수를 찾는다. 
        print(self.kor) 
        self.abc() # 현재 클래스 내의 메소드 콜
        abc()      # 모듈의 함수 콜
obj = MyClass()
obj.showData()
# 100
# 88
# 클래스의 멤버 메소드
# 모듈의 멤버 함수
class My:
    a = 1
print(My.a) # 1

my1 = My # 주소
print(my1.a) # 1

my2 = My() # 인스턴스 생성
my2.a = 100
print(my2.a) # 100

my3 = My() # 인스턴스 생성
my3.a = 200
print(my3.a) # 200
my3.b = 123
print(my3.b) # 123

print(My.b)  # Error
print(my1.b) # Error
print(my2.b) # Error

* test18_class

# 클래스 = 설계도
# Singer가 갖추어야 할 기본 속성, 행위를 설계도(원형클래스)로 만든 후 모든 가수들은 Singer type으로 존재하면 됨

class Singer:
    title_song ='노래제목' # 멤버 변수
    
    def __init__(self):
        pass
    
    def sing(self):
        msg = '노래는'
        print(msg, self.title_song)
        
# 편의 상 아래에서 객체를 만들지만 별도의 모듈에서 Singer를 호출하여 사용하는 것이 일반적        
bts = Singer()
bts.sing()              # 노래는 노래제목
bts.title_song = '다이너마이트'
bts.sing()              # 노래는 다이너마이트
bts.co = '빅히트'
print('소속사 :', bts.co) # 소속사 : 빅히트

* test18_class2

# 클래스 연습
print('do')
a = 10
def aa():
    print(a)
    
# 새로운 가수 타입의 객체 필요
import pack2.test18_class

print('-----------------')
twice = pack2.test18_class.Singer()
twice.sing() # 노래는 노래제목
twice.title_song = '우아하게'
twice.sing() # 노래는 우아하게
print('소속사 :', twice.co) # error

17. 포함관계 (has a 관계)

 * test19_handle

# 움직이는 기계에 사용할 부품 클래스

class PohamHandle:
    quantity = 0 # 회전량
    
    def LeftTurn(self, quantify):
        self.quantity = quantify
        return '좌회전'
    
    def RightTurn(self, quantify):
        self.quantity = quantify
        return '우회전'

 

 * test19_car

# Car 클래스 : 여러개의 부품(클래스)을 조립해서 완성된 차를 생성
class PohamCar:
    speed = 0
    turnShow = '정지'
    
    def __init__(self, ownerName):  # ownerName 인스턴트에만 존재. 원형클래스에는 없음
        self.owerName = ownerName
        self.handle = PohamHandle() # 클래스의 포함 관계
        
    def TurnHandle(self, q):        # PohamCar 메소드로 핸들을 움직이는 행위
        if q > 0:
            self.turnShow = self.handle.RightTurn(q)
        elif q < 0:
            self.turnShow = self.handle.LeftTurn(q)
        elif q == 0:
            self.turnShow = '직진'
            
if __name__ == '__main__':
    tom = PohamCar('톰')
    tom.TurnHandle(10)
    print(tom.owerName+'의 회전량은 ' + tom.turnShow + str(tom.handle.quantity)) # 톰의 회전량은 우회전10
    tom.TurnHandle(0)
    print(tom.owerName+'의 회전량은 ' + tom.turnShow) # 톰의 회전량은 우회전10
    print()
    
    oscar = PohamCar('오스카')
    oscar.TurnHandle(-5)
    print(oscar.owerName+'의 회전량은 ' + oscar.turnShow + str(oscar.handle.quantity))
    # 오스카의 회전량은 좌회전-5
    

 * test20_has_a

 - 냉장고(class)에 음식(class)을 저장

class FoodData:
    def __init__(self, name, expiry):
        self.name = name
        self.expiry = expiry
class Fridge:
    isOpend = False # 냉장고 문 개폐 여부
    foods = []      # 리스트
    
    def open(self):
        self.isOpend = True
        print('냉장고 문이 열렸습니다.')
        
    def close(self):
        self.isOpend = False
        print('냉장고 문이 닫혔습니다.')
        
    def put(self, thing):
        if self.isOpend == True:
            self.foods.append(thing) # 클래스의 포함
            print('냉장고에 음식을 저장하였습니다.')
            self.listFood()
        else:
            print('냉장고 문이 닫혀있어 음식을 담을 수 없습니다.')
        
    def listFood(self):
        for f in self.foods:
            print('-', f.name, f.expiry)
f = Fridge()
apple = FoodData('사과', '2021-3-5')
f.put(apple)    # 냉장고 문이 닫혀있어 음식을 담을 수 없습니다.
f.open()        # 냉장고 문이 열렸습니다.
f.put(apple)    # 냉장고에 음식을 저장하였습니다.
                # - 사과 2021-3-5
f.close()       # 냉장고 문이 닫혔습니다.
cola = FoodData('콜라', '2022-12-5')
f.open()        # 냉장고 문이 열렸습니다.
f.put(cola)     # 냉장고에 음식을 저장하였습니다.
                # - 사과 2021-3-5
                # - 콜라 2022-12-5
f.close()       # 냉장고 문이 닫혔습니다.

18. 상속

: 클래스가 다른 클래스의 멤버를 활용 - 상속 : 다형성을 구사

* test21_inherit

class Animal: # 별도의 모듈에서 작성하고 호출하였다고 가정
    age = 0
    
    def __init__(self):
        print('Animal 생성자')
    
    def move(self):
        print('움직이는 생물')
class Dog(Animal): # 상속
    age = 10
    
    def __init__(self):
        print('Dog 생성자')
    
    def dogShow(self):
        age = 2
        print('개')
        print('age :', age)         # 지역변수
        print('age :', self.age)    # 클래스 -> 부모
        print('age :', super().age) # 부모
dog1 = Dog()    # 자식 생성자가 있을 경우 자식 생성자만 수행
                # 자식 생성자가 없을 경우 부모 생성자가 수행
print('dog1.age :',dog1.age) # dog1.age : 0
dog1.move()                  # 움직이는 생물
dog1.dogShow()
# 개
# age : 2
# age : 10
# age : 0
class Horse(Animal):
    pass

horse = Horse()
horse.move()

 클래스 상속 연습

* test22_inherit

class Person:
    say = '사람'
    age = 20
    __kbs = '공영방송' # __변수명 : private 멤버 - 현재 클래스에서만 호출 가능
    
    def __init__(self, age):
        print('Person 생성자')
        self.age = age
        
    def printInfo(self):
        print('나이 :{}, 이야기:{}'.format(self.age, self.say))
    
    def hello(self):
        print('hello')
        print('hello : ', self.say, self.__kbs)
        
        
pe = Person(22) # Person 생성자
pe.printInfo()  # 나이 :22, 이야기:사람
pe.hello()
# hello
# hello :  사람 공영방송
class Employee(Person):
    say = "직원"          # 자식과 동일한 멤버변수에 의해 부모의 say가 숨겨짐
    subject = '근로자'     # Employee 고유 멤버 변수
    
    def __init__(self):
        print("Employee 생성자")
        
    def printInfo(self): # 자식과 동일한 메소드에 의해 부모의 메소드가 숨겨짐. 메소드 오버라이드
        print('Employee printInfo 메소드')
        
    def empShow(self):
        say = "empShow"
        print(say)
        print(self.say)
        self.printInfo()    # 자식 -> 부모
        super().printInfo() # 부모
        

emp = Employee()         # Employee 생성자
print(emp.say, emp.age)  # 직원 20
print(emp.subject)       # 근로자
emp.printInfo()          
# 나이 :20, 이야기:직원
# Employee printInfo 메소드
emp.empShow()
# empShow
# 직원
# Employee printInfo 메소드
# 나이 :20, 이야기:직원
class Worker(Person):
    pass

wo = Worker(33)       # Person 생성자
print(wo.say, wo.age) # 사람 33
wo.printInfo()        # 나이 :33, 이야기:사람
class Worker(Person):
    hobby = '코딩'
    
    def __init__(self, age):
        print('Worker 생성자')
        #super().__init__(age) # 부모의 생성자 호출. Bound call
        Person.__init__(self, age) # UnBound call
    
    def woShow(self):
        self.printInfo()
        super().printInfo()
        
wo = Worker(27)
# Worker 생성자
# Person 생성자
wo.woShow()
# 나이 :27, 이야기:사람
# 나이 :27, 이야기:사람
class Programmer(Worker):
    def __init__(self, age):
        print('Programmer 생성자')
        Worker.__init__(self, age)
        
    def prShow(self):
        self.printInfo()
        super().printInfo()
        
    def kbsShow(self):
        print(self.say)
        #print(self.__kbs) # AttributeError: 'Programmer' object has no attribute '_Programmer__kbs'

pr = Programmer(25)
# Programmer 생성자
# Worker 생성자
# Person 생성자
print(pr.say, pr.age) # 사람 25
pr.prShow()
# 나이 :25, 이야기:사람
# 나이 :25, 이야기:사람
pr.hello()
# hello
# hello :  사람 공영방송
pr.kbsShow()
# 클래스 타입확인
a = 3
print(type(a)) # int
print(type(pr)) # __main__.Programmer
print(Programmer.__bases__) # 부모클래스 확인. __main__.Worker
print(Worker.__bases__) # __main__.Person
print(Person.__bases__) # object

19. 메소드 오버라이딩

: 부모 클래스의 메소드와 동일한 이름의 메소드를 자식 클래스에서 만듦(재정의)
다형성의 근거를 제시

* test23_override

class Parent:
    def printData(self): # 추상 메소드와 유사. 강제하지않음.
        pass
    
    def displayData(self):
        print('Parent의 displayData')
class Child1(Parent):
    def printData(self): # 메소드 오버라이딩
        a = 10
        b = 20
        print(str(a + b) + " 출력")
class Child2(Parent):
    def printData(self):   # override
        print('Child2 printData')
        
    def displayData(self): # override 부모 메소드를 재정의
        print('Child2 displayData')
        
    def c2method(self):    # Child2 고유 메소드
        print('Child2 c2method')
c1 = Child1()
c1.printData()   # 30 출력
c1.displayData() # Parent의 displayData
c2 = Child2()
print()
c2.printData()   # Child2 printData
c2.displayData() # Child2 displayData
print()
# 다형성
par = Parent()
par = c1          # 자식객체의 주소를 치환
par.printData()   # 30 출력
par.displayData() # Parent의 displayData
print()

par = c2          # 자식객체의 주소를 치환
par.printData()   # Child2 printData
par.displayData() # Child2 displayData
par.c2method()    # 오버라이딩 하지않은 메소드도 호출 가능
print()
# 자바와 파이썬 차이
sbs = c1          # 주소 치환시 같은 타입이 아니더라도 객체 및 메소드 사용가능
sbs.printData()
sbs.displayData()
print()
sbs = c2
sbs.printData()
sbs.displayData()
sbs.c2method()
print()

plist = [c1, c2]
for i in plist:
    i.printData()

10. 중요 메소드 
    ① toString() : 오버라이딩을 통한 사용자 정의 toString()

public String toString(){
	String 변수 = 출력하고 싶은 문자열+...
	return 변수;
}
Object obj1= new Object(); // 최상위 객체
System.out.println(obj1.getClass()); // 클래스 이름
System.out.println(obj1.hashCode()); // 객체가 갖는 고유한 값 - 주소(10진수)
System.out.println(obj1.toString()); // 클래스 이름 @ 16진수의 hash code
				     // java.lang.Object@123a439b
System.out.println(obj1);    // toString()과 동일


String s = new String("str");    // String과 Date는 toString()이 오버라이딩된 값이 출력됨
Date   d = new Date();
System.out.println(s);    // s.toString이 호출.(toString() 기본 메소드로 생략됨)
System.out.println(d);    // d.toString이 호출.(toString() 기본 메소드로 생략됨)

Random r = new Random();    // 그외 Random, 사용자 정의함수는 주소값을 리턴한다.
Person p = new Person();
System.out.println(r);
System.out.println(p);

 

    ② equals() : 오버라이딩을 통한 사용자 정의 equals(). 

                    사용자 정의 객체의 경우 주소값을 비교함으로 equals 비교시 false를 리턴

class Person{
	...
	public boolean equals(Object obj) { // Person객체끼리 비교
		if(obj != null & obj instanceof Person) {
			// obj가 Person이라고 하더라도 타입이 Object이므로 Object의 멤버만 사용할 수 있으므로
			// obj를 Person으로 캐스팅하여 Person.equals를 사용한다.
			Person p = (Person) obj;
			if(this.name.equals(p.getName()) & this.addr.equals(p.getAddr())) {
				return true;
			}
		}
		return false;
	}
}

Person obj1 = new Person();
Person obj2 = new Person();

if(obj1.equals(obj2)){ // 사용자 정의된 equals() 비교
}

 

 

 

11. 중요 클래스

    ① String : 문자열 생성은 2가지 방법 존재.
        1) String 생성

String str = new String("문자열"); // new 이용
String str = "문자열"; // 리터럴 이용

        - new를 이용 시 동일한 문자열이 존재하여도 새로 생성.
        - 리터럴을 이용 시 동일한 문자열이 존재할 경우 기존 문자열 재사용. 상수풀(constant pool)에 저장되어 불변성

          을 가짐.
        - String 객체의 문자열이 어떤 방법으로 할당되는지 상관없이 문자열을 비교하고 싶은 경우 equals()사용

 

        2) String 생성자

byte[] data = {65,66,67,69,70};
		char[] data2 = new char[] {'0','1','0','-','1','2','3'};
		
		String str1 = new String(data); // ABCEF
		String str2 = new String(data2); // 010-123
		String str3 = new String(data2,2,2); // data2의 2번 index 부터 2개의 문자열. "1-"

        - String클래스를 사용하는 경우 다양한 타입의 입력데이터를 문장열로 만들 수 있도록 하기 위해

          다양한 형태의 생성자가 오버로딩 되어있음.

 

        3) String - final

class MyClass extends String{
}

        - String은 final class로 상속할 수 없음.

 

        4) String 메소드 : String 객체는 원본이 변경되지않고 메소드 실행결과로 새로운 String객체가 만들어짐.
                              : 문자열 조작이 많은 곳에서 String을 사용하는 것은 프로그램을 무겁게 함.

String str1 = new String("java programming");
String str2 = new String("입니다.");

str1.charAt(1); // str1에서 1번 index의 문자
str1.concat(str2); // str1과 str2를 연결
str1.indexOf('a'); // str1에서 가장 왼쪽의 'a'의 index
str1.indexOf("합"); // 문자가 없을 경우 -1 리턴
str1.lastIndexOf('a'); // str1에서 가장 오른쪽의 'a'의 index
str1.length(); // str1의 길이
str1.equalsIgnoreCase("java programming");
str1.equalsIgnoreCase("JAVA programming"); // 대소문자 구분하지않고 문자열 비교
str1.startsWith("java")); // str1이 "java"로 시작하면 true아니면 false 리턴
str1.endsWith("amming")); // str1이 "amming"로 끝나면 true아니면 false 리턴
str1.toUpperCase(); // 대문자 변환값 리턴
					// 원본 문자열 변화 없음. 새로운 객체를 생성하여 리턴.
str1.toLowerCase()); // 소문자 변환값 리턴
str1.substring(4); // 4번째 문자열부터 끝까지의 문자열 추출. programming
str1.substring(4,8); // 4번째 문자열부터 8-1번까지의 문자열 추출. pro
str1.replace('a','A')); // 'a' 문자를 'A'로 변경. jAvA progrAmming

 

        5) String 의 데이터 타입 변환과 관련된 메소드

String str1 = new String("java programming");
String str2 = new String("java servlet spring bigdata");

//1) getBytes() : String -> byte[] 변환
byte[] data1 = str1.getBytes();
for(int i=0;i<data1.length;i++) {
	System.out.print(data1[i]+" ");
}

//2) toCharArray() : String -> char[] 변환
char[] data2 = str1.toCharArray();
for(int i=0;i<data2.length;i++) {
	System.out.print(data2[i]+" ");
}

//3) split(" ") : String -> String[] 변환
String[] data3 = str2.split(" ");
for(int i=0;i<data3.length;i++) {
	System.out.print(data3[i]+" ");
}

//4) valueOf(x) : 기본형 -> String 변환
int i =1000;
double d = 10.5;
test(String.valueOf(i));
test(String.valueOf(d));

test(i+"");
test(d+"");

public static void test(String data) {
	System.out.println("변환된 데이터 "+data);
}

        6) StringBuffer : String의 불변성으로 메모리 사용이 많아 대안으로 원본 배열이 변경되는
                             StringBuffer사용하기도 함.

           StringBuffer와 StringBuilder의 차이

               StringBuffer  : thread 고려 무겁다 안전. JDK 1.0이상
              StringBuilder : thread 비고려 가볍다 웹서버에서 처리함으로 무관. JDK 5.0이상

 

           StringBuffer 사용

StringBuffer sb = new StringBuffer("java programming");

sb.append("입니다."); // 맨뒤에 추가
sb.insert(2, "자바"); // 지정한 위치에 문자열 삽입
sb.delete(2, 6); //start~end-1 위치의 문자열 삭제 
sb.reverse(); // 문자열 반전

           ③ String과 StringBuffer의 비교

public static void stringCheck(int count) {
	// 시작할때 현재 시간을 측정 - nano초
	long start = System.nanoTime();
	String str = new String("java");
	for(int i=1; i<=count;i++) {
		str+="java";
	}
	long end = System.nanoTime();
	System.out.print("str+='java' ");
	System.out.println("수행시간\t: "+(end-start)+" ns");

}
public static void stringBufferCheck(int count) {
	long start = System.nanoTime();
	StringBuffer sb = new StringBuffer("java");
	for(int i=1; i<=count;i++) {
		sb.append("java");
	}
	long end = System.nanoTime();
	System.out.print("sb.append('java') ");
	System.out.println("수행시간\t: "+(end-start)+" ns");
		
}
public static void main(String[] args) {
	int count = 10000;
	System.out.println("실행횟수 : "+count);
	stringCheck(count);
	stringBufferCheck(count);
}

 

    ② Math

Math.PI;          // PI
Math.abs(-1);     // 절대값
Math.ceil(10.5);  // 올림
Math.round(10.5); // 반올림
Math.floor(10.5); // 버림
Math.max(100,10); // 최대값
Math.min(100,10); // 최소값
Math.random();    // 난수

 

    ③ Wrapper 클래스

        1) 5.0 이전(기본형 -> 참조형)

int num =100; 
Integer obj = new Integer(num); // 기본형 -> 참조형 
run(obj); 

public static void run(Object obj) { // Integer -> Object 
Integer inObj = (Integer)obj;    // Object -> Integer 
int num = inObj.intValue();      // Interger -> int (참조형->기본형) 
} 


        2) 5.0 이후 (기본형 -> 참조형)

int num = 1000; // 5.0 이후 버전의 jdk에서는 컴파일러가 자동으로 변환해준다. 
run(num); // 참조형으로 매개변수가 정의되어 있어도 기본형을 전달하는 경우 자동으로  
// 컴파일러가 Integer in = new Integer(num) 이 코드를 실행해서  
// Integer타입으로 변환해준다. 이를 오토박싱이라 한다. 
public static void run(Object obj) { 
Integer inObj = (Integer)obj; 
int i = inObj; // (참조형->기본형) 

int j = (Integer)obj; // 오토 언박싱 
// int형 변수에 참조형 변수를 전달하는 경우 컴파일러가 자동으로 객체를  
// int로 바꾸는 코드를 만든다. 
// int j = obj.intValue(); 
}

 

    ④ Calendar 클래스 : 날짜 시간

//java.util.date : 시스템의 기본 날짜와 시간
//java.sql.date : DBMS(오라클)에서 date타입으로 전의한 칼럼값을 다루기 위해서 사용하는 타입

//Date 객체는 deplecate
//GregorianCalendar는 Calendar의 자식 클래스

import java.util.Calendar;

Calendar cal = new GregorianCalendar(); // 오늘 날짜기간 정보
cal.get(Calendar.YEAR);
cal.get(Calendar.MONTH)+1;
cal.get(Calendar.DATE);
cal.get(Calendar.HOUR);
cal.get(Calendar.MINUTE);
cal.get(Calendar.SECOND);
cal.getTimeInMillis();
cal.set(2020,11,30);

 

    ⑤ SimpleDateFormat 클래스 : 출력되는 데이터의 형식을 변경할 수 있는 API

SimpleDateFormat sdf = new SimpleDateFormat("yyyy년MM월dd일");
Calendar today = new GregorianCalendar();
sdf.format(today.getTime()); //getTime() : Calendar객체를 Date로 변환

 

    ⑥ DecimalFormat 클래스 : 출력되는 데이터의 형식을 변경할 수 있는 API

import java.text.DecimalFormat;

DecimalFormat df = new DecimalFormat("#,###.##");
df.format(1234567);
df.format(1234567.48);
String data = "25465.174";
df.format(Double.parseDouble(data);
try {
	double data3 = (double) df.parse(data);
	String num3 = df.format(data3);
}
catch(ParseException e) {
	
}

 

    ⑦ 어노테이션

@Override

 

     StringTokenizer : String클래스의 split()을 사용하는것과 비슷한 개념. token화.

import java.util.StringTokenizer;
StringTokenizer st = new StringTokenizer("java:servlet:jdbc:spring:hadoop",":");

System.out.println(st.countTokens());//토근의 개수 리턴
while(st.hasMoreTokens()) { // StringTokenizer 내부 저장소에 token이 있으면 true 없으면 false 리턴
	String token = st.nextToken(); // token을 리턴.
	System.out.println(token);
}

 

    Arrays

import java.util.Arrays;

int[] arr = {10,20,30,40,50};
Arrays.fill(arr,100); // 배열 요소를 특정 값으로 채우기
Arrays.equals(arr, arr2); // 배열값이 같은지 비교
Arrays.sort(arr); // 배열의 데이터 정렬(default : 오름차순)
Arrays.binarySearch(arr,50); // 배열에서 특정 값이 저장된 요소의 index

 

    향상된 for문

for(요소의 타입 요소를 저장할 변수:컬렉션을 참조하는 변수){
}

 

 

 

12. 컬렉션 : List, Map, Set을 생성하기 전에 저장될 요소의 타입을 미리정의
                컬렉션 클래스<요소의 타입> 변수 = new 컬렉션 클래스<요소의 타입>();
                 <요소의 타입> : Generic

    ① List : 배열과 동일한 구조, 중복 가능. 주로 ArrayList 사용.
        1) Vector : 무겁 Thread에 안전

import java.util.Vector;

Vector v = new Vector(); // 다른 타입도 저장가능. 5.0이전 사용
//=> Vector안에 저장되는 데이터의 타입을 제한(요소타입이 기본형인 경우 Wrapper타입으로 명시)

Vector<Integer> v = new Vector<Integer>(); // Generic으로 Vector의에 저장되는 요소의 타입을 명시
v.add(10); // 값 추가.
v.size(); // 현재벡터에 저장된 요소의 개수
v.capacity(); // 현재 벡터의 용량
v.get(0) // 값 read

Vector<String> v2 = new Vector<String>();
v2.add("aa");

for(String s:v2) {
	System.out.print(s+" ");
}

        2) ArrayList : 가볍 thread에 unsafe

import java.util.ArrayList;

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10); // 값 추가.
list.size(); // 저장된 요소의 개수
list.get(0) // 값 read

        3) 2차원 배열

import java.util.ArrayList;

ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> row = new ArrayList<Integer>();

for(int i=0;i<3;i++) {
	row.add(i);
}

ArrayList<Integer> row2 = new ArrayList<Integer>();
for(int i=3;i<7;i++) {
	row2.add(i);
}

ArrayList<Integer> row3 = new ArrayList<Integer>();
for(int i=7;i<9;i++) {
	row3.add(i);
}
list.add(row);
list.add(row2);
list.add(row3);

for(int i=0;i<list.size();i++) {
	for(int j=0;j<list.get(i).size();j++) {
		System.out.print(list.get(i).get(j)+"\t");
	}
}

    ② Set : 집합. 합집합, 교집합가능. 중복이 불가능. 주로 HashSet 사용.
        1) HashSet

import java.util.HashSet;

HashSet<String> set = new HashSet<String>(); // 생성
set.add("aa"); // 데이터 추가
set.add("bb");
set.add("cc");
set.add("dd");
set.add("ee");
set.add("aa"); // 중복불가
System.out.println("요소의 개수: " + set.size()); //5 

// 데이터 추출
// set은 선형구조가 아니라 순서대로 읽어올 수 없다.
// 여러종류의 자료구조에 저장된 데이터를 내부에서 Iterator로 변환해서 읽을 수 있도록 지원
// HashSet -> Iterator
Iterator<String> it = set.iterator();
while(it.hasNext()) { // iterator안에 데이터가 있는 지 확인. boolean리턴
	String element = it.next(); // Iterator에 저장된 요소 꺼내기
	System.out.println("set에 저장된 요소 : "+element);
}

 

        2) HashSet 교집합, 합집합

HashSet<String> set = new HashSet<String>();
set.add("aa");
set.add("bb");
set.add("cc");
set.add("dd");
set.add("ee");
set.add("ff");

//합집합 - Set을 생성할때 매개변수로 다른 Set을 전달
HashSet<String> set2 = new HashSet<String>(set);
set2.add("zz");
set2.add("yy");
set2.add("xx");
set2.add("aa");

//교집합
HashSet<String> set3 = new HashSet<String>();
set3.add("aa");
set3.add("dd");
set3.add("zz");
set.retainAll(set3); // 두 set의 교집합이 set에 새로 저장된다.


        3) SortedSet

    ③ Map : key와 value. 주로 HashMap사용.
        1) HashMap : Thread에 비안전, 빠름

HashMap<Integer,String> map1 = new HashMap<Integer, String>();
// key가 중복되면 마지막 작업한 내용으로 덮어쓴다.
map1.put(1, "aa");
map1.put(1, "bb");
map1.put(1, "cc");

System.out.println(map1.get(1)); //cc 마지막 값 리턴.
System.out.println(map1.size()); //1

HashMap<String, String> map2 = new HashMap<String, String>();
map2.put("a001", "aa");
map2.put("a002", "bb");
map2.put("a003", "cc");
map2.put("a004", "dd");
map2.put("a005", "ee");
map2.put("a006", "ff");
map2.put("a007", "gg");
map2.put("a008", "hh");

System.out.println(map2.size());
System.out.println(map2.get("2"));

//map에 저장된 데이터를 꺼내기
//1. map이 갖고 있는 키들을 set으로 변환
Set<String> keyList = map2.keySet();

//2. set에 저장된 key목록을 iterator로 변환하고
Iterator<String> it = keyList.iterator();

//3. iterator에서 key를 하나씩 꺼내서 get
while(it.hasNext()) {
	String key = it.next();
	String value = map2.get(key);
	System.out.println(key+":"+value);
}

 

        2) HashTable : Thread에 안전, 느림
        3) Properties

 

 

 

13. I/O

    Input용클래스  - _____InputStream     (byte단위)
                          _____Reader            (문자단위)
    Output용클래스 - _____OutputStream (byte단위)
                            _____Writer           (문자단위)

 

    1) InputStream, PrintStream : byte 단위 입출력 처리

class System{
	public static final InputStream in;
	public static final PrintStream out;
}
InputStream myin = System.in;
PrintStream myout = System.out;
try {
	while(true) {
		int data = myin.read(); // 키보드로 입력한 단어를 읽어서 리턴.
		myout.print((char)data+" "); // 입력받은 데이터는 int로 문자로 변환하여 출력.
		//System.out.print((char)data+" "); // System.out = myout
		if(data==13) // 개행문자가 오면 종료.
			break;
		
	}
}
catch(IOException e) { // read()는 예외처리가 필요.
	e.printStackTrace();
}

 

    2) InputStreamReader : 문자 단위 입출력 처리. 사용방법이 InputStream와 동일

InputStreamReader myin = new InputStreamReader(System.in); // 객체 생성만 다르다.
PrintStream myout = System.out;

try {
	while(true) {
		int data = myin.read();
		myout.print((char)data);
		if(data==13)
			break;
	}
}
catch(IOException e) {
	e.printStackTrace();
}

 

    3) File 클래스

File dir = new File("C:/java/test"); //액세스 하고 싶은 폴더나 파일의 경로 - 절대경로, 상대경로
//File dir = new File("C:\\java\\test");
File file = new File("test.txt"); // ROOT

System.out.println(file1); // toString()
System.out.println(file2); // 생성자에 입력값 출력.
file.canRead(); // 해당 파일을 읽을 수 있으면 true, 아니면 false 리턴.
file.canWrite(); // 해당 파일을 쓸 수 있으면 true, 아니면 false 리턴.
file.getAbsolutePath(); // 절대 경로 리턴
file.getName(); // 파일의 확장자를 포함한 이름 리턴
file.getParent(); // 파일 또는 폴더의 상위 경로 리턴
file.getPath(); // 상대경로 리턴
dir.isDirectory(); // 디렉토리이면 true, 아니면 false 리턴.
file.isDirectory();
dir.isFile(); // 파일이면 true, 아니면 false 리턴.
file.isFile();
dir.lastModified(); // 마지막 수정일자 리턴
// 폴더의 기본사이즈 : 4096
dir.length(); // 폴더 or 파일 크기 리턴
file.length();
file.setReadOnly(); // 읽기전용으로 변경.
file.setWritable(true); // 쓰기가능으로 변경.

File[] fileArr = file.listFiles(); // 파일객체 내부의 파일에 대한 파일객체 배열을 리턴
String[] fileName = file.list; // 파일객체 내부의 파일이름 배열 리턴

 

    4) FileInputStream : byte 단위 파일 액세스

FileInputStream fis = null; // 인스턴스를 try안에서 생성 시 {}안에서만 사용할 수 있어
							// 인스턴스만 생성 후 파일열기는 try 내부에서 진행. 
try {
	fis = new FileInputStream("src/data/test.txt");//1. 파일 열기 - FileNotFoundException 처리 필요
	while(true) {
		int data = fis.read(); // IOException 처리 필요
		if(data == -1) { // 파일읽기가 끝나면 -1을 리턴한다.
			break;
		}
		System.out.print((char)data);
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
}
catch(IOException e) {
	e.printStackTrace();
}
finally {
	// 3. 파일 닫기 - 자원반납
	//IO 작업을 하다가 오류가 발생하거나 정상적으로 처리된다고 하더라도 모두 자원을 반납해야하므로 finally 블럭에서 처리.
	// input은 오류가 없는 output은 close를 안하면 파일에 output한 데이터가 write되기 전에 파일을 내부에서 종료한다.
	try {
		if(fis!=null) // 파일이 null로 파일열기에 실패할 경우 file close를 진행하면 안되므로 예외처리 진행.
			fis.close(); // IO Exception 처리 필요.
	}
	catch(IOException e) {
		e.printStackTrace();
	}	
}

 

    5) FileReader : 문자 단위 파일 액세스. FileInputStream과 동일.

FileReader fr = null;
try {
	fr = new FileReader("src/data/test.txt");
	
	while(true) {
		int data = fr.read();
		if(data == -1) {
			break;
		}
		System.out.print((char)data);
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
}
catch(IOException e) {
	e.printStackTrace();
}
finally {
	try {
		if(fr!=null)
			fr.close();
	}
	catch(IOException e) {
		e.printStackTrace();
	}
	
}

 

    6) FileWriter : 문자 단위 파일 쓰기. 파일이 존재하지 않으면 새로 만들어서 출력한다.

FileWriter fw = new FileWriter("src/data/output.txt");
// 두번째 매개변수가 없을 경우 default로 덮어쓰기을 진행한다.
FileWriter fw = new FileWriter("src/data/output.txt",true);
// 생성자에 두번째 매개변수가 true를 두면 apped모드 활성화된다.
fw.write(97);
fw.close();

 

    7) BufferedReader : 버퍼를 이용하여 파일의 한 라인씩 읽을 수 있어 실행속도가 빨라 FileReader보다 많이 사용된다.

BufferedReader br = null;
// BufferedReader는 생성자 인자로 FileReader객체를 매개변수로 받는다.
// FileReader fr = null;

try {
	br = new BufferedReader(new FileReader("src/io/InputStreamTest.java"));
	// fr = new FileReader("src/data/test.txt");
	// br = new BufferedReader(fr);
	// FileReader 객체를 따로 생성해서 BufferedReader 매개변수에 전달할 수 있으나
	// 코드를 줄이기 위해 한줄에 사용
	while (true) {
		String line = br.readLine(); // 한줄씩 읽는다.
		if (line == null) { // 읽을 라인이 없다면 null을 리턴.
			break;
		}
		System.out.println(line);
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
} finally {

	try {
		if (br != null) {
			br.close();
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

 

 

 

<tip>

* toString 자동 생성: Source - Generate toString

* API 문서에서의 Deprecated : 미사용 권고

+ Recent posts

1