精华内容
下载资源
问答
  • 因项目要高web的skyline开发,确切说是terraExplorer pro的开发,参考skyline的官方示例,直接运行javascript的例子(例子代码如下),却爆出错误“对象不支持属性或方法”,调试发现是SGWorld的属性和方法好像在...

    因项目要高web的skyline开发,确切说是terraExplorer pro的开发,参考skyline的官方示例,直接运行javascript的例子(例子代码如下),却爆出错误“对象不支持此属性或方法”,调试发现是SGWorld的属性和方法好像在javascript中不识别,导致不识别的可能原因中,我首先排除了拼写错误,错误的可能只能是声明的对象可能错了,后来发现声明的对象是在“object”标签中实现的,其中有一个“CLSID”,这个标示其实是GUID,它是Globally Unique Identifier的简称,中文翻译为“全局唯一标示符”,在Windows系统中也称之为Class ID,缩写为CLSID,指windows系统对于不同的应用程序,文件类型,OLE对象,特殊文件夹以及各种系统组件分配一个唯一表示它的ID代码,用于对其身份的标示和与其他对象进行区分。问题就出在这里,因为我发现不同terraExplorer pro的版本中声明SGWorld的CLSID是不同的,可能是我的版本与当前示例中的CLSID的正好不同,后来发现果然是,我的版本是6.0的,而示例的版本是针对最新的6.1版本的,这是造成此原因的罪魁祸首啊,改回6.0的版本,即

    CLSID:3a4f91b0-65a8-11d5-85c1-0001023952c1,一切ok了

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>HTML Example</title>
        <script type="text/javascript">
            function Init() {
                try {
                    var flyPath = "http://www.skylineglobe.com/SkylineGlobe/WebClient/PresentationLayer/WebClient/SkyglobeLB.fly";
                    // attach callback to the load finished event
                    SGWorld.AttachEvent("OnLoadFinished", OnProjectLoadFinished);
                    // Load default developer fly file from www.skylineglobe.com web site.
                    // default load is in async mode
                    SGWorld.Project.Open(flyPath);
                    alert("Opening project " + flyPath + " in async mode");
                }
                catch (e) {
                    alert("Error: " + e.description);
                }
     
            }
     
            function OnProjectLoadFinished() {
                alert("Received project loaded event. Click OK to fly to Washington DC.");
                var Washington = SGWorld.Creator.CreatePosition(-77.036667, 38.895111, 1500);
                SGWorld.Navigate.FlyTo(Washington);
            }
        </script>
    </head>
    <body onload="Init()">
        <object id="TE" classid="clsid:3a4f9192-65a8-11d5-85c1-0001023952c1" style="width: 600px;height: 600px"></object>
        <object id="SGWorld" classid="CLSID:3a4f91b1-65a8-11d5-85c1-0001023952c1" style="visibility: hidden;height: 0"></object>
    </body>
    </html>

    展开全文
  • 小甲鱼零基础入门学习python笔记

    万次阅读 多人点赞 2019-08-14 11:06:30
    039 类和对象:拾遗 040 类和对象:一些相关的BIF 041 魔法方法:构造和析构 042 魔法方法:算术运算 043 魔法方法:算术运算2 044 魔法方法:简单定制 045 魔法方法属性访问 046 魔法方法:描述符(Property的...

    小甲鱼老师零基础入门学习Python全套资料百度云(包括小甲鱼零基础入门学习Python全套视频+全套源码+全套PPT课件+全套课后题及Python常用工具包链接、电子书籍等)请往我的资源(https://download.csdn.net/download/qq_32809093/13099592查看

    目录:

    000 愉快的开始
    001 我和Python的第一次亲密接触
    002 用Python设计第一个游戏
    003 小插曲之变量和字符串
    004 改进我们的小游戏
    005 闲聊之Python的数据类型
    006 Pyhon之常用操作符
    007 了不起的分支和循环
    008 了不起的分支和循环2
    009 了不起的分支和循环3
    010 列表:一个打了激素的数组
    011列表:一个打了激素的数组2
    012列表:一个打了激素的数组3
    013元组:戴上了枷锁的列表
    014字符串:各种奇葩的内置方法
    015字符串:格式化
    016 序列!序列!
    017函数:Python的乐高积木
    018 函数:灵活即强大
    019函数:我的地盘听我的(局部变量与全局变量)
    020函数:内嵌函数和闭包
    021函数:lambda表达式
    022 函数:递归是神马
    023 递归:这帮小兔崽子
    024 递归:汉诺塔
    025 字典:当索引不好用时
    026 字典:当索引不好用时2
    027 集合:在我的世界里,你就是唯一
    028 文件:因为懂你,所以永恒
    029 文件:一个任务
    030 文件系统:介绍一个高大上的东西
    031 永久存储:腌制一缸美味的泡菜(pickle)
    032 异常处理:你不可能总是对的
    033 异常处理:你不可能总是对的2
    034 丰富的else语句及简洁的with语句
    035 图形用户界面入门:EasyGui
    036 类和对象:给大家介绍对象
    037 类和对象:面向对象编程
    038 类和对象:继承
    039 类和对象:拾遗
    040 类和对象:一些相关的BIF
    041 魔法方法:构造和析构
    042 魔法方法:算术运算
    043 魔法方法:算术运算2
    044 魔法方法:简单定制
    045 魔法方法:属性访问
    046 魔法方法:描述符(Property的原理)
    047 魔法方法:定制序列
    048 魔法方法:迭代器
    049 乱入:生成器
    050 模块:模块就是程序
    051 模块:__name__='__main__'、搜索路径和包
    052 模块:像个极客一样去思考
    053 论一只爬虫的自我修养
    054 论一只爬虫的自我修养2:实战
    055 论一只爬虫的自我修养3:隐藏

    064 GUI的终极选择:Tkinter
    065 GUI的终极选择:Tkinter2
    066 GUI的终极选择:Tkinter3
    067 GUI的终极选择:Tkinter4
    068 GUI的终极选择:Tkinter5
    069 GUI的终极选择:Tkinter6
    070 GUI的终极选择:Tkinter7
    071 GUI的终极选择:Tkinter8
    073 GUI的终极选择:Tkinter10
    074  GUI的终极选择:Tkinter11
    075 GUI的终极选择:Tkinter12
    076 GUI的终极选择:Tkinter13
    077 GUI的终极选择:Tkinter14

    078 Pygame:初次见面,请大家多多关照

     

    000 愉快的开始

    python跨平台。
    应用范围:操作系统、WEB、3D动画、企业应用、云计算
    大家可以学到什么Python3的所有常用语法、面向对象编程思维、运用模块进行编程、游戏编程、计算机仿真

    Python 是脚本语言
    脚本语言(Scripting language)是电脑编程语言,因此也能让开发者藉以编写出让电脑听命行事的程序。以简单的方式快速完成某些复杂的事情通常是创造脚本语言的重要原则,基于这项原则,使得脚本语言通常比 C 语言、C++语言 或 Java 之类的系统编程语言要简单容易。也让脚本语言另有一些属于脚本语言的特性:
    •  语法和结构通常比较简单
    •  学习和使用通常比较简单
    •  通常以容易修改程序的“解释”作为运行方式,而不需要“编译”
    •  程序的开发产能优于运行性能
    一个脚本可以使得本来要用键盘进行的相互式操作自动化。一个 Shell 脚本主要由原本需要在命令行输入的命令组成,或在一个文本编辑器中,用户可以使用脚本来把一些常用的操作组合成一组串行。主要用来书写这种脚本的语言叫做脚本语言。很多脚本
    语言实际上已经超过简单的用户命令串行的指令,还可以编写更复杂的程序。

    IDLE 是一个 Python Shell,shell 的意思就是“外壳”,基本上来说,就是一个通过键入文本与程序交互的途径!像我们 Windows 那个 cmd 窗口,像 Linux 那个黑乎乎的命令窗口,他们都是 shell,利用他们,我们就可以给操作系统下达命令。同样的,我们可以利用 IDLE 这个 shell 与 Python 进行互动。

    注:在 Python 中不能把两个完全不同的东西加在一起,比如说数字和文本

    如果我需要在一个字符串中嵌入一个双引号,正确的做法是:你可以利用反斜杠(\)对双引号转义:\",或者用单引号引起这个字符串

    001 我和Python的第一次亲密接触

    IDLE启动Python

    IDLE是一个Python Shellshell的意思就是“外壳”,基本上来说,就是一个通过键入文本与程序交互的途径

    我们看到>>>这个提示符,Ta的含义是告诉你,Python已经准备好了,在等着你键入Python指令呢

    好了,大家试试在IDLE里输入:

    >>>print (“I love fishc.com”)

    我们尝试点儿新的东西,我们输入

    >>>print(5+3)

    或者直接输入

    >>>5+3

    不妨在试试计算

    >>>1234567890987654321*987654321012345678

    还有我们可以将两个字符串“相加”在一起,这种做法叫做拼接字符串

    >>>print("well water"+"river")

    先试试

    >>> print("I love fishc.com"*2)

    >>> print("I love fishc.com\n"* 2)

    002 用Python设计第一个游戏

    实例1:

    print("---------我爱鱼C工作室----------")
    temp = input("不妨猜一下小甲鱼现在心里想的是哪个数字:")
    guess = int(temp)
    if guess == 8:
        print("我草,你是小甲鱼心里的蛔虫嘛?!")
        print("哼,猜中了也没有奖励!")
    else:
        print("猜错了,小甲鱼现在心里想的是8!")
        print("游戏结束,不玩啦")

    BIF 就是 Built-in Functions,内置函数。为了方便程序员快速编写脚本程序(脚本就是要编程速度快快快!!!),Python 提供了非常丰富的内置函数,我们只需要直接调用即可,例如 print() 的功能是“打印到屏幕”,input() 的作用是接收用户输入。

    在 Python 或 IDLE 中,输入 dir(__builtins__) 可以看到 Python 提供的内置方法列表(注意,builtins 前后是两个下划线哦)其中小写的就是 BIF。如果想具体查看某个BIF 的功能,比如 input(),可以在 shell 中输入 help(input),就会得到这个 BIF 的功能描述。哦,答案应该是 68 个

    >>> dir(__builtins__)  查看 Python 提供的内置方法列表

    >>> help(input)  查看input的具体使用说明 

    注:

    只有当标识符已经赋值后( Python 的变量是不用先声明的)才能在代码中使用,未赋值的标识符直接使用会导致运行时错误

    缩进是 Python 的灵魂

    Python 不允许 if 条件中赋值,所以 if c = 1: 会报错!

     

    003 小插曲之变量和字符串

    插曲之变量

    变量名就像我们现实社会的名字,把一个值赋值给一个名字时,Ta会存储在内存中,称之为变量(variable),在大多数语言中,都把这种行为称为“给变量赋值”或“把值存储在变量中”。

    不过Python与大多数其他计算机语言的做法稍有不同,Ta并不是把值存储在变量中,而更像是把名字贴在值的上边。

    所以有些Python程序员会说“Python”没有“变量”,只有“名字”。

    需要注意的地方

    在使用变量之前,需要对其先赋值。

    变量名可以包括字母、数字、下划线,但变量名不能以数字开头

    字母可以是大写或小写,但大小写是不同的。也就是说fishcFishC对于Python来说是完全不同的两个名字

    等号(=)是赋值的意思,左边是名字,右边是值,不可写反咯。

    插曲之字符串

    到目前为止,我们所认知的字符串就是引号内的一切东西,我们也把字符串叫做文本,文本和数字是截然不同的,咱看例子:>>>5+8

    >>> '5'+'8'

    要告诉Python你在创建一个字符串,就要在字符两边加上引号,可以是单引号或者双引号Python女士表示不挑剔。但必须成对,你不能一边单引号,另一边却花心的用上双引号结尾。

    如果字符串中需要出现单引号或双引号怎么办

    例如我想打印字符串:Let’s go!

    有两种方法,第一种比较常用,就是使用我们的转义符号(\)对字符串中的引号进行转义:

    >>> 'Let\'s go!'

    原始字符串

    好像反斜杠是一个好东西,但不妨试试打印:

    >>> str = 'C:\now'

    我们可以用反斜杠对自身进行转义:

    >>> str = 'C:\\now'

    原始字符串的使用非常简单,只需要在字符串前边加一个英文字母r即可(则都会以原始字符串输出):

    >>>str = r'C:\now'

    长字符串

    如果希望得到一个跨越多行的字符串,例如:

    我爱鱼C

    正如我爱小甲鱼,

    久久不肯散去……

    这我们就需要使用到三重引号字符串!

     

    004 改进我们的小游戏

    第一个改进要求:猜错的时候程序提示用户当前的输入比答案大了还是小了

    与操作and

    第二个改进要求:程序应该提供多次机会给用户猜测,专业点来讲就是程序需要重复运行某些代码。

    条件分支

    while循环

    实例1:找8

    temp = input("请输入一个数据:")
    guess = int(temp)
    i=0
    while guess != 8 and i < 3:
        i = i + 1
        temp = input("哎呀,猜错了,请重新输入吧:")
        guess = int(temp)
        if guess == 8:
            print("我草,你是小甲鱼心里的蛔虫嘛?")
            print("哼,猜对了也没有奖励")
        else:
            if guess > 8:
                print("哥,大了大了~~")
            else:
                print("嘿,小了!小了!!")
    print("游戏结束,不玩啦~~")

    random模块里边有一个函数叫做:randint()Ta会返回一个随机的整数。

    实例2:找随机数

    import random#导入随机数函数
    secret = random.randint(1,5)#随机生成1到5的一个随机数
    temp = input("请输入一个1-5的数据:")
    guess = int(temp)
    i=0
    while guess != secret and i < 6:
        i = i + 1
        guess = int(temp)
        if guess == secret:
            print("我草,你是小甲鱼心里的蛔虫嘛?")
            print("哼,猜对了也没有奖励")
        else:
            if guess > secret:
                print("哥,大了大了~~")
            else:
                print("嘿,小了!小了!!")      
            temp = input("请重新输入吧:")
    print("游戏结束,不玩啦~~")

     

    005 闲聊之Python的数据类型

    Python的一些数值类型:整型、布尔类型(True与False)、浮点型、e记法、复数类型等

    e记法(e4相当于10的四次方,e-10相当于10的-10次方)

    类型转换

    字符型转换为整型

    其它同上

    type()函数(可查看变量类型)

    isinstance()函数(用来判断两个输入参数类型是否一致)

     

    006 Pyhon之常用操作符

    算术操作符

    注:python中 \ 为除法, \\ 为整除 ,% 为取余

    幂运算(3的二次方)

    3的二次方后取负

    注:先幂运算、然后乘除、后加减、后逻辑

    3的负二次方

    比较操作符

    逻辑操作符

    优先级问题

    007 了不起的分支和循环

    打飞机游戏框架:

    加载背景音乐

    播放背景音乐(设置单曲循环)

    我方飞机诞生

    while True:
        if 用户是否点击了关闭按钮:
           推出程序
           
        interval += 1;
        if interval == 50:
           interval = 0;
           小飞机诞生
        小飞机移动一个位置
        屏幕刷新
        
        if 用户鼠标产生移动:
           我方飞机中心位置 = 用户鼠标位置
           屏幕刷新
           
        if 我方飞机与小飞机发生肢体冲突:
           我方挂,播放撞机音乐
           修改我方飞机图案
           打印“Game over"
           停止背景音乐,最好淡出

     

    008 了不起的分支和循环2

    现在小甲鱼来考考大家:

    按照100分制,90分以上成绩为A8090B6080C60以下为D,写一个程序,当用户输入分数,自动转换为ABCD的形式打印。

    score = int(input('请输入一个分数:'))
    if 100 >= score >= 90:
        print('A')
    elif 90 > score >= 80:
        print('B')
    elif 80 > score >= 60:
        print('C')
    elif 60 > score >= 0:
        print('D')
    else:
        print('输入错误!')

    条件表达式(三元操作符)

    有了这个三元操作符的条件表达式,你可以使用一条语句来完成以下的条件判断和赋值操作:

    x, y = 4, 5

    if x < y:

      small = x

    else:

      small = y

    例子可以改进为

    small = x if x < y else y    #如果x小于y,则small等于x,否则等于y

    断言(assert)

    assert这个关键字我们称之为“断言”,当这个关键字后边的条件为假的时候,程序自动崩溃并抛出AssertionError的异常。

    举个例子:

    >>> assert 3 > 4

    一般来说我们可以用Ta再程序中置入检查点,当需要确保程序中的某个条件一定为真才能让程序正常工作的话,assert关键字就非常有用了

     

    009 了不起的分支和循环3

    while循环

    while 条件:

                      循环体

    for循环

    虽然说Python是由C语言编写而来的,但是Tafor循环跟C语言的for循环不太一样,Pythonfor循环显得更为智能和强大!

    语法:

    for 目标 in 表达式:

         循环体

    每次取FishC中一个字符及空格输出

    range()函数

    语法:range( [strat],[stop],[step] )

    这个BIF有三个参数,其中用中括号括起来的两个表示这两个参数是可选的。

    step=1表示第三个参数的值默认值是1setp为每步距离

    range这个BIF的作用是生成一个从start参数的值开始到stop参数的值结束的数字序列

     

    break语句(结束本层循环)

    实例:

    bingo = '小甲鱼是帅哥'
    answer = input('请输入小甲鱼最想听的一句话:')

    while True:
        if answer == bingo:
            break
        answer = input('抱歉,错了,请重新输入(答案正确才能退出游戏):')

    print('哎哟,帅哦~')
    print('您真是小甲鱼肚子里的蛔虫啊^_^')

    continue语句(当前位置结束本次循环,重新开始下次循环)

    实例:

    for i in range(10):
        if i%2 != 0:
            print(i)
            continue
        i += 2
        print(i)

    010 列表:一个打了激素的数组

    列表一个打了激素的数组

    创建列表

    创建一个普通列表

    创建一个混合列表

    创建一个空列表

    向列表添加元素

    append()函数向列表末尾添加一个元素

    extend()函数向列表末尾添加多个元素

    insert(n,xxx)函数向列表中第n个元素前插入一个元素

    注:0表示第一个元素

    011列表:一个打了激素的数组2

    从列表中获取元素

    跟数组一样,我们可以通过元素的索引值(index)从列表获取单个元素,注意,列表索引值是从 0 开始的。

    从列表删除元素

    remove()函数表示从列表中删除某个元素

    del()函数也表示从列表中删除某个元素

    pop()函数从列表中取出最后一个元素

    列表分片(Slice

    利用索引值,每次我们可以从列表获取一个元素,但是我们总是贪心的,如果一次性需要获取多个元素,有没有办法实现呢?利用列表分片,我们可以简单的实现这个要求。

    member[0:2]表示从第1个元素开始拷贝,一共拷贝两个元素,即member[0]和member[1]

    列表的拷贝

    012列表:一个打了激素的数组3

    列表的一些常用操作符

    比较操作符

    逻辑操作符

    连接操作符

    重复操作符

    成员关系操作符

    关于分片“拷贝”概念的补充

    >>> dir(list)可查看所有列表的操作函数

    count()函数可计算列表中相同元素个数

    index()函数可索引列表元素

    reverse()将列表中元素倒序

    sort()将列表中元素从小到大排序

    关于分片“拷贝”概念的补充

    注:list13=list11相当于多了个指向列表的标签,list12 = list[:]是实实在在的拷贝

    013元组:戴上了枷锁的列表

    由于和列表是近亲关系,所以元组和列表在实际使用上是非常相似的。

    我们这节课主要通过讨论元组和列表到底有什么不同来学习元组,酱紫大家就不会觉得老是重复一样的内容

    我们主要从以下几个点来讨论学习:

    创键和访问一个元组

    创建元组(括号可以没有,但逗号一定要有)

    访问元组前两个元素

    更新和删除一个元组

    更新一个元组

    注:其并未对原元组进行修改,而是生成了一个新的元组,并贴上temp名字标签而已。原元组由于标签没有了,则会被自动回收。

    删除一个元组

    元组相关的操作符

    注:元组不允许修改和删除。

    014字符串:各种奇葩的内置方法

     

    015字符串:格式化

    由于花括号被解释掉,所以不打印后面中文

    字符串格式化符号含义

    将ASCII码97对应的字符输出

    格式化整数

    格式化操作符辅助命令

    5表示输出为五位数

    Python 的转义字符及其含义

     

    016 序列!序列!

    列表、元组和字符串的共同点

    都可以通过索引得到每一个元素

    默认索引值总是从0开始

    可以通过分片的方法得到一个范围内的元素的集合

    有很多共同的操作符(重复操作符、拼接操作符、成员关系操作符)

    使用list方法

    元组转换为列表

    注:元组为小括号,列表为中括号。

    max() 返回序列或者参数集合中的最大值

    min() 返回序列或者参数集合中的最小值

    sum(iterable[,start=0]) 返回序列iterable和可选参数start的总和

    sorted()将元素从小到大重新排列

    reversed()将元素倒序排列

    注:元组是不可以修改和删除的,所以不可以直接对元组使用sorted与reversed命令

    enumerate()将每个元素插入枚举

    zip()返回由各个参数的序列组成的元组

     

    017函数:Python的乐高积木

    定义一个函数和调用

     

    018 函数:灵活即强大

    形参和实参

    >>> def MyFirstFunction(name):

      '函数定义过程中的name是叫形参'

      #因为Ta只是一个形式,表示占据一个参数位置

      print('传递进来的' + name + '叫做实参,因为Ta是具体的参数值!')

    >>> MyFirstFunction('小甲鱼')

    传递进来的小甲鱼叫做实参,因为Ta是具体的参数值!

    关键字参数

    默认参数(即形参中给定默认值,则在未给实参时会以默认值输出)

    收集参数

     

    019函数:我的地盘听我的

    函数与过程

    再谈谈返回值

    如果有返回值,函数则返回对应值;如果没有,则返回None

    可以返回多个值

    019函数:我的地盘听我的(局部变量与全局变量)

    def discounts(price, rate):
        final_price = price * rate
        old_price = 88 #这里试图修改全局变量
        print('修改后old_price的值是:', old_price)
        return final_price

    old_price = float(input('请输入原价:'))
    rate = float(input('请输入折扣率:'))
    new_price = discounts(old_price, rate)
    print('修改后old_price的值是:', old_price)
    print('打折后价格是:', new_price)

    global可将局部变量声明为全局变量

    020函数:内嵌函数和闭包

    内嵌函数

    闭包(closure

    注:使用nonlocal语句将x强制为不是局部变量

    021函数:lambda表达式

    lambda表达式的作用

    Python写一些执行脚本时,使用lambda就可以省下定义函数过程,比如说我们只是需要写个简单的脚本来管理服务器时间,我们就不需要专门定义一个函数然后再写调用,使用lambda就可以使得代码更加精简

    对于一些比较抽象并且整个程序执行下来只需要调用一两次的函数,有时候给函数起个名字也是比较头疼的问题,使用lambda就不需要考虑命名的问题了

    简化代码的可读性,由于普通的屌丝函数阅读经常要跳到开头def定义部分,使用lambda函数可以省去这样的步骤。

    过滤函数filter可筛选出非零元素

    筛选出奇数

    注:lambda x:x%2用来判断是否为奇,x为奇则输出1,否则输出0;range(10)可生成0-9的10个整数,filter用来筛选非零元素;如果为偶数,则被筛选掉;如果为奇数,则保留,但输出的是rang(10)产生的原始数,因为lambda只是用来判断是否为奇偶

    range生成的0-9给了x,x经过2倍运算后再赋值给x

    022 函数:递归是神马

    汉诺塔游戏

    树结构的定义

    谢尔宾斯基三角形

    递归求阶乘

    写一个求阶乘的函数

    正整数阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。

    例如所给的数是5,则阶乘式是1×2×3×4×5,得到的积是120,所以120就是4的阶乘。

    假设我们n的值传入是5,那么:

    实例:求阶乘

    def factorial(n):
        result = n
        for i in range(1, n):
            result *= i

        return result

    number = int(input('请输入一个正整数:'))
    result = factorial(number)
    print("%d 的阶乘是:%d"  % (number, result))#格式化为整数类型

    实例2:递归求阶乘

    def factorial(n):
        if n == 1:
            return 1
        else:
            return n * factorial(n-1)

    number = int(input('请输入一个正整数:'))
    result = factorial(number)
    print("%d 的阶乘是:%d" % (number, result))

    023 递归:这帮小兔崽子

    坑爹的兔子

    斐波那契数列的迭代实现

    我们都知道兔子繁殖能力是惊人的,如下图:

    我们可以用数学函数来定义:

    课间练习:假设我们需要求出经历了20个月后,总共有多少对小兔崽子?(迭代 vs 递归

    def fab(n):
        n1 = 1
        n2 = 1
        n3 = 1

        if n < 1:
            print('输入有误!')
            return -1

        while (n-2) > 0:
            n3 = n2 + n1
            n1 = n2
            n2 = n3
            n -= 1
        
        return n3

    result = fab(20)
    if result != -1:
        print('总共有%d对小兔崽子诞生!' % result)

    斐波那契数列的递归实现

    递归实现(递归计算时间将拉长)

    def fab(n):
        if n < 1:
            print('输入有误!')
            return -1

        if n == 1 or n == 2:
            return 1
        else:
            return fab(n-1) + fab(n-2)

    result = fab(35)
    if result != -1:
        print('总共有%d对小兔崽子诞生!' % result)

    注:迭代计算时间远比递归少,因为递归要循环出入栈

    024 递归:汉诺塔

    递归求解汉诺塔

     

    对于游戏的玩法,我们可以简单分解为三个步骤

    将前63个盘子从X移动到Y上。

    将最底下的第64个盘子从X移动到Z上。

    Y上的63个盘子移动到Z上。

    问题一:将X上的63个盘子借助Z移到Y上;

    问题二:将Y上的63个盘子借助X移到Z上。

     

    对于游戏的玩法,我们可以简单分解为三个步骤

    将前63个盘子从X移动到Y上。

    将最底下的第64个盘子从X移动到Z上。

    Y上的63个盘子移动到Z上。

    问题一:将X上的63个盘子借助Z移到Y上;

    问题二:将Y上的63个盘子借助X移到Z上。

    实例:

    def hanoi(n, x, y, z):
        if n == 1:
            print(x, ' --> ', z)
        else:
            hanoi(n-1, x, z, y) # 将前n-1个盘子从x移动到y上
            print(x, ' --> ', z) # 将最底下的最后一个盘子从x移动到z上
            hanoi(n-1, y, x, z) # 将y上的n-1个盘子移动到z上

    n = int(input('请输入汉诺塔的层数:'))
    hanoi(n, 'X', 'Y', 'Z')

    025 字典:当索引不好用时

    映射

    创建和访问字典

    >>> dict4 = dict(小甲鱼='让编程改变世界',李宁='一切皆有可能')
    >>> dict4
    {'小甲鱼': '让编程改变世界', '李宁': '一切皆有可能'}

    >>> dict4['爱迪生'] = '天才是99%的汗水加1%的灵感'
    >>> dict4
    {'小甲鱼': '让编程改变世界', '李宁': '一切皆有可能', '爱迪生': '天才是99%的汗水加1%的灵感'}

    026 字典:当索引不好用时2

    fromkey()方法用于创建并返回一个新的字典它有两个参数,第一个参数是字典的键;第二个参数是可选的,是传入键的值。如果不提供,默认是None

    >>> dict1 = {}
    >>> dict1.fromkeys((1,2,3))
    {1: None, 2: None, 3: None}
    >>> dict2 = {}
    >>> dict2.fromkeys((1,2,3),"Number")
    {1: 'Number', 2: 'Number', 3: 'Number'}
    >>> dict3 = {}
    >>> dict3.fromkeys((1,2,3),('one','two','three'))
    {1: ('one', 'two', 'three'), 2: ('one', 'two', 'three'), 3: ('one', 'two', 'three')}

    访问字典的方法有key()、values()和items()

    key()用于返回字典中的键,value()用于返回字典中所有的值,item()当然就是返回字典中所有的键值对(也就是项)

    >>> dict1 = dict1.fromkeys(range(5),'赞')
    >>> dict1.keys()
    dict_keys([0, 1, 2, 3, 4])
    >>> dict1.values()
    dict_values(['赞', '赞', '赞', '赞', '赞'])
    >>> dict1.items()
    dict_items([(0, '赞'), (1, '赞'), (2, '赞'), (3, '赞'), (4, '赞')])

    get()方法提供了更宽松的方式去访问字典项,当键不存在的时候,get()方法并不会报错,只是默默第返回一个None,表示啥都没找到:

    >>> dict1.get(10)
    >>> dict1.get(4)
    '赞'

    如果希望找不到数据时返回指定的值,可以在第二个参数设置对应的默认返回值:

    >>> dict1.get(32,'木有')
    '木有'

    如果不知道一个键是否在字典中,可以使用成员资格操作符(in 或 not in)来判断
    >>> 31 in dict1
    False
    >>> 4 in dict1

    clear()可清空一个字典

    >>> dict1
    {0: '赞', 1: '赞', 2: '赞', 3: '赞', 4: '赞'}
    >>> dict1.clear()
    >>> dict1
    {}

    copy()方法是复制字典(全拷贝)

    >>> a = {1:'one',2:'two',3:'three'}
    >>> b = a.copy()
    >>> id(a)
    52448840
    >>> id(b)
    52503624
    >>> a[1] = 'four'
    >>> a
    {1: 'four', 2: 'two', 3: 'three'}
    >>> b
    {1: 'one', 2: 'two', 3: 'three'}

    pop()是给定键弹出对应的值,popitem()是随机弹出一个项

    >>> a.pop(2)
    'two'
    >>> a
    {1: 'four', 3: 'three'}
    >>> a.popitem()
    (1, 'four')
    >>> a
    {3: 'three'}

    setdefault()方法与get()方法相似,但setdefault()在字典中找不到相应的键值时会自动添加

    >>> a = {1:'one',2:'two',3:'three'}
    >>> a.setdefault(2)
    'two'
    >>> a.setdefault(4)
    >>> a
    {1: 'one', 2: 'two', 3: 'three', 4: None}

    update()方法可以更新字典

    >>> a = {1:'one','小白':None}

    >>> b = {'小白':'狗'}
    >>> a.update(b)
    >>> a
    {1: 'one', '小白': '狗'}

    027 集合:在我的世界里,你就是唯一

    字典的表亲--集合(在python3中,如果用大括号括起一堆数字但没有体现映射关系,那么就会认为这堆玩意儿就是个集合)

    >>> num1 = {}
    >>> type(num1)
    <class 'dict'>
    >>> num2 = {1,3,4}
    >>> type(num2)
    <class 'set'>

    集合中的元素都是唯一的(集合会自动帮我们把重复的数据清理掉,集合是无序的,所以不能试图去索引集合中的某一个元素

    >>> num = {1,2,3,4,5,5,4,3,2,1}
    >>> num
    {1, 2, 3, 4, 5}

    如何创建一个集合有两种方法:1、直接把一堆元素用大括号括起来;2、用set()

    一种是直接把一堆元素用花括号括起来

    >>> set1 = {'小甲鱼','小鱿鱼','小甲鱼'}

    一种是使用set()工厂函数

    >>> set2 = set(['小甲鱼','小鱿鱼','小甲鱼'])
    >>> set1 == set2
    True

    课堂搞搞看

    要求:去掉列表中重复的元素

    [0, 1, 2, 3, 4, 5, 5, 3, 1]

    方法一、

    >>> list1 = [1,2,3,4,5,5,3,1,0]

    >>> temp = list1[:]
    >>> list1.clear()
    >>> list1
    []
    >>> for each in temp:
        if each not in list1:
            list1.append(each) #append()表示向列表中添加元素

    方法二、

    >>> list1 = list(set(list1))
    >>> list1
    [0, 1, 2, 3, 4, 5]

    #set(list1)先将list1列表转变为集合, list(set(list1))再讲集合转变为列表

    如何访问集合中的值

    由于集合中的元素是无序的,所以并不能像序列那样用下标来进行访问,但是可以使用迭代把集合中的数据一个个读取出来

    可以使用for把集合中的数据一个个读取出来

    >>> set1 = {1,2,3,4,5,4,3,2,1,0}
    >>> for each in set1:
        print(each,end = ' ')

        
    0 1 2 3 4 5 

    •也可以通过innot in判断一个元素是否在集合中已经存在

    >>> 0 in set1
    True
    >>> 8 in set1
    False

    使用add()方法可以为集合添加元素,使用remove()方法可以删除集合中已知的元素:

    >>> set1.add(6)
    >>> set1
    {0, 1, 2, 3, 4, 5, 6}
    >>> set1.remove(5)
    >>> set1
    {0, 1, 2, 3, 4, 6}

    不可变集合(把元素给froze冰冻起来)(像元组一样不能随意地增加或删除集合中的元素)

    028 文件:因为懂你,所以永恒

    大多数u程序都是:首先接收输入数据,然后按照要求进行处理,最后输出数据

    虽然当前数据放在内存中存取的速度要比硬盘中快,但一旦断电则会丢失,所以尽量ctrl+s保持到硬盘中


    什么是文件

    打开文件

    open(file, mode='r', buffering=-1, encoding=None,errors=None, newline=None, closefd=True, opener=None)

    open()的第一个参数是传入的文件名,第二个参数是指定文件的打开模式

    文件对象方法

    >>> f = open("D:\\python3.3.2\Hello.txt")
    >>> f
    <_io.TextIOWrapper name='D:\\python3.3.2\\Hello.txt' mode='r' encoding='cp936'>
    >>> f.read()
    "A. HISTORY OF THE SOFTWARE\n==========================\n\nPython was created in the early 1990s by Guido van Rossum at Stichting\nMathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands\nas a successor of a language called ABC.  Guido remains Python's\nprincipal author, although it includes many contributions from others.\n\nIn 1995, Guido continued his work on Python at the Corporation for\nNational Research Initiatives (CNRI, see http://www.cnri.reston.va.us)\nin Reston, Virginia where he released several versions of the\nsoftware."
    >>> f.close()
    >>> f = open("D:\\python3.3.2\Hello.txt")
    >>> f.read(5)
    'A. HI'
    >>> f.tell()   #返回当前光标所在文件的位置
    5
    >>> f.readline()
    'STORY OF THE SOFTWARE\n'
    将f放入到列表

    >>> f = open("D:\\python3.3.2\Hello.txt",'w')#w模式写入会覆盖已存在的文件(即原文件内容全部被删除),a模式则在末尾追加写入
    >>> f.write('who are you')          #返回的是写入的字符数
    11
    >>> f.close()

    029 文件:一个任务

    任务:将文件(record.txt)中的数据进行分割并按照以下规律保存起来:

    小甲鱼的对话单独保存为boy_*.txt的文件(去掉“小甲鱼:”)

    小客服的对话单独保存为girl_*.txt的文件(去掉“小客服:”)

    文件中总共有三段对话,分别保存为boy_1.txt, girl_1.txtboy_2.txt, girl_2.txt, boy_3.txt, gril_3.txt6个文件(提示:文件中不同的对话间已经使用“==========分割

    test1:

    f = open("record.txt")

    boy = []
    girl = []
    count = 1

    for each_line in f:
        if each_line[:6] != '======':#判断是否连续读到六个=
            (role,line_spoken) = each_line.split(':',1)#split以:进行字符切割,
            #将切得到的两部分内容依次存放在role与line_spoken中
            if role == '小甲鱼':
                boy.append(line_spoken)#将小甲鱼说的内容添加到列表boy中
            if role == '小客服':
                girl.append(line_spoken)#将小客服说的内容添加到列表girl中
        else:
            file_name_boy = 'boy_' + str(count) + '.txt'
            file_name_girl = 'girl_' + str(count) + '.txt'

            boy_file = open(file_name_boy,'w')#以w模式新建一个以file_name_boy命名的txt文件
            girl_file = open(file_name_girl,'w')#并贴上boy_file的标签

            boy_file.writelines(boy)#将列表boy中的内容写入到boy_file文件中
            girl_file.writelines(girl)

            boy_file.close()#关闭boy_file文件
            girl_file.close()

            boy = []#清空列表boy
            girl = []
            count += 1

    file_name_boy = 'boy_' + str(count) + '.txt'
    file_name_girl = 'girl_' + str(count) + '.txt'

    boy_file = open(file_name_boy,'w')
    girl_file = open(file_name_girl,'w')

    boy_file.writelines(boy)
    girl_file.writelines(girl)

    boy_file.close()
    girl_file.close()#记得关闭文件

    test2:

     

    def save_file(boy,girl,count):
        file_name_boy = 'boy_' + str(count) + '.txt'
        file_name_girl = 'girl_' + str(count) + '.txt'

        boy_file = open(file_name_boy,'w')
        girl_file = open(file_name_girl,'w')

        boy_file.writelines(boy)
        girl_file.writelines(girl)

        boy_file.close()
        girl_file.close()

    def split_file(file_name):
        f = open(file_name)

        boy = []
        girl = []
        count = 1

        for each_line in f:
            if each_line[:6] != '======':
                (role,line_spoken) = each_line.split(':',1)#split以:进行字符切割,
                #将切得到的两部分内容依次存放在role与line_spoken中
                if role == '小甲鱼':
                    boy.append(line_spoken)
                if role == '小客服':
                    girl.append(line_spoken)
            else:
                save_file(boy,girl,count)

                boy = []
                girl = []
                count += 1


        save_file(boy,girl,count)
        f.close()

    split_file('record.txt')

    030 文件系统:介绍一个高大上的东西

    os模块中关于文件/目录常用的函数使用方法

    >>> import os
    >>> os.getcwd()
    'D:\\python3.3.2\\小甲鱼python\\python程序\\第二十九课'

    >>> os.listdir('D:\\python3.3.2\\小甲鱼python\\python程序\\第二十九课')
    ['boy_1.txt', 'boy_2.txt', 'boy_3.txt', 'girl_1.txt', 'girl_2.txt', 'girl_3.txt', 'record.txt', 'test.py', 'test2.py']

    os.path模块中关于路径常用的函数使用方法

     >>> os.path.getsize('python.exe')  #获取文件的尺寸,返回值以字节为单位


    031 永久存储:腌制一缸美味的泡菜(pickle)

    python提供了一个标准的模块pickle可以非常容易地将列表、字典这类复杂的数据类型存储为文件。它几乎可以把所有python的对象都转化为二进制的形式存放,这个过程称为pickling,从二进制转换回对象的过程称为unpickling

    pickling过程

    >>> import pickle
    >>> my_list = [123,3,14,'小甲鱼',['another list']]

    >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十节课\my_list.pkl','wb')  #二进制写形式打开文件
    >>> pickle.dump(my_list,pickle_file)
    >>> pickle_file.close()

    unpickling过程       

    >>> import pickle
    >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十节课\my_list.pkl','rb')#以二进制读形式打开文件
    >>> my_list = pickle.load(pickle_file)
    >>> print(my_list)
    [123, 3, 14, '小甲鱼', ['another list']]

    实例:城市天气打包

    >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十一节课\city_data.pkl','wb')
    >>> pickle.dump(city,pickle_file)
    >>> pickle_file.close()

    032 异常处理:你不可能总是对的

    实例1:

    file_name = input('请输入需要打开的文件名:')
    file = open(file_name)
    print('文件的内容是:')
    for each_line in file:
        print(each_line)
    file.close()

    注:py文件与要打开的文件在同一个文件下则不需要加路径

    Python标准异常总结

    以下是 Python 内置异常类的层次结构:

            

    033 异常处理:你不可能总是对的2

    try-except语句

    try:

      检测范围

    except Exception[as reason]:

      出现异常(Exception)后的处理代码

    实例1:

    try:
        f = open('TE.txt')
        print(f.read())
        f.close()
    except OSError:
        print('文件打开过程中出错了!!!')

    实例2:

    try:
        f = open('TE.txt')
        print(f.read())
        f.close()
    except OSError as reason:
        print('文件打开出错原因是:\n' + str(reason))

    实例3:

    try:
        sum = 1 + '1'
        f = open('TE.txt')
        print(f.read())
        f.close()
    except OSError as reason:
        print('文件打开出错原因是:\n' + str(reason))
    except TypeError as reason:
        print('类型出错原因是:\n' + str(reason))

    实例4(多个异常统一处理):

    try:
        sum = 1 + '1'
        f = open('TE.txt')
        print(f.read())
        f.close()
    except(OSError, TypeError):
        print('出错了')

    注:try语句一旦检测到异常,剩下的语句将不会被执行

    try-finally语句

    try:

      检测范围

    except Exception[as reason]:

      出现异常(Exception)后的处理代码

    finally:

      无论如何都会被执行的代码

    实例5:

    try:
        f = open('test.txt')
        print(f.read())
        sum = 1 + '1'
    except (OSError,TypeError)as reason:
        print('出错了\n原因是:' + str(reason))
    finally:
        f.close()

    raise语句可以自己抛出一个异常

    034 丰富的else语句及简洁的with语句

    丰富的else语句

    要么怎样,要么不怎样

    if 条件:
        条件为真执行
    else:
        条件为假执行
          

    干完了能怎样,干不完就别想怎样

    实例1:

    def showMaxFactor(num):
        count = num // 2#//为整除,判断是素数,只需依次判断当前数num除以1到(num // 2)都不能整除即可
        while count > 1:
            if num % count == 0:#判断是否整除
                print('%d最大的约数是%d' % (num, count))
                break#跳出循环后else并不执行
            count -= 1
        else:#当while循环不成立时,或者理解为while循环完全被执行完了,没有给中途跳出(即break)
            print('%d是素数!' % num)

    num = int(input('请输入一个数:'))
    showMaxFactor(num)

    注:else与for语句搭配与while语句相同

    没有问题?那就干

    只要try语句块里没有出现任何异常,那么就会执行else语句块里的内容啦

    实例2:

    try:#尝试运行以下程序
        print(int('abc'))
    except ValueError as reason:#如果程序有异常时
        print('出错了:' + str(reason))
    else:#程序无异常时
        print('没有任何异常!')

    实例3:

    try:
        print(int('123'))
    except ValueError as reason:
        print('出错了:' + str(reason))
    else:
        print('没有任何异常!')

    简洁的with语句(with会自动帮你关闭文件)

    实例4:

    try:
        with open('test.txt','w') as f:
            for each_line in f:
                print(each_line)
    except (OSError,TypeError) as reason:
        print('出错了\n原因是:' + str(reason))

    035 图形用户界面入门:EasyGui

    图形用户界面编程,也就是平时常说的GUI(Graphical User  Interface),python有一个非常简单的GUI工具包:EasyGui

    GUI的安装

    导入方法一:

    >>> import easygui         #导入EasyGui
    >>> easygui.msgbox('嗨,亦我飞也')

    导入方法二:

    >>> from easygui import *
    >>> msgbox('嗨,亦我飞也')

    导入方法三(推荐使用):

    >>> import easygui as g
    >>> g.msgbox('嗨,亦我飞也')

    显示图片(注:图片需要为GIF格式,且存放在python.exe通目录

    >>> easygui.buttonbox(msg='你喜欢以下哪种水果',title='亦我飞也',choices=('草莓','西瓜','芒果'),image='aa.gif')

    实例1:

    import easygui as g
    import sys
     
    while 1:
        g.msgbox("嗨,欢迎进入第一个界面小游戏")
        msg = "请问你希望在鱼C工作室学习到什么知识呢"
        title="小游戏互动"
        choices=["谈恋爱","编程","OOXX","琴棋书画"]
        choice=g.choicebox(msg,title,choices)
     
        #note that we convert choice to string,in case
        #the user cancelled the choice,and we got None
        g.msgbox("你的选择是:"+str(choice),"结果")
        msg="你希望重新开始小游戏吗?"
        title=" 请选择"
        if g.ccbox(msg,title):  #show a Contiue/Cancel dialog
            pass #user chose Contonue
        else:
            sys.exit(0)  #user chose Cancel

    修改窗口大小(choicebox)

    修改文字大小(PROPORTIONAL_FONT)

    036 类和对象:给大家介绍对象

    给大家介绍对象

    把乱七八糟的数据扔进列表里,称数据层面的封装

    把常用的代码段打包成一个函数,称语句层面的封装

    把数据和代码都封装在一起,称对象层面的封装

    对象 = 属性 + 方法

    对象可以从静态(属性)动态(方法)两个特征来描述

    OO(面向对象)的特征

    继承

    class Turtle: # Python 中的类名约定以大写字母开头
        """关于类的一个简单例子"""
        # 属性
        color = 'green'
        weight = 10
        legs = 4
        shell = True
        mouth = '大嘴'

        # 方法
        def climb(self):
            print("我正在很努力的向前爬......")

        def run(self):
            print("我正在飞快的向前跑......")

        def bite(self):
            print("咬死你咬死你!!")

        def eat(self):
            print("有得吃,真满足^_^")

        def sleep(self):
            print("困了,睡了,晚安,Zzzz")

    调用类中的方法:

    >>> tt = Turtle()     #声明tt对象继承Turtle()
    >>> tt.climb()
    我正在很努力的向前爬......
    >>> tt.bite()
    咬死你咬死你!!

    定义一个带列表类MyList,将list2对象继承于它,则列表的功能继承它的对象都可以使用

    >>> class MyList(list):
        pass

    >>> list2 = MyList()

    >>> list2.append(5)
    >>> list2.append(6)

    >>> list2.append(1)
    >>> list2
    [5, 6, 1]
    >>> list2.sort()
    >>> list2
    [1, 5, 6]

    多态(下例中都调用的名字相同的方法,但实现不一样)

    >>> class A:
        def fun(self):
            print('我是小A。。。')

            
    >>> class B:
        def fun(self):
            print('我是小B。。。')

            
    >>> a = A()
    >>> b = B()
    >>> a.fun()
    我是小A。。。
    >>> b.fun()
    我是小B。。。

    037 类和对象:面向对象编程

    self是什么?

    Python的self其实就相当于C++的this指针。由同一个类可以生产无数对象,当一个对象的方法被调用的时候,对象会将自身的引用作为第一个参数传给该方法,那么python就知道需要操作哪个对象的方法了。

    >>> class Ball:
        def setName(self,name):
            self.name = name
        def kick(self):
            print('我叫%s,该死的,谁踢我。。。' % self.name)

            
    >>> a = Ball()

    >>> a.setName('球A')
    >>> b = Ball()

    >>> b.setName('球B')

    >>> a.kick()
    我叫球A,该死的,谁踢我。。。
    >>> b.kick()
    我叫球B,该死的,谁踢我。。。

    你听说过Python的魔法方法吗?

    python的这些具有魔法的方法,总是被双下划线所包围,例如__init__(),即构造方法,也称构造函数,这个方法会在对象被创建时自动调用。其实,实例化对象时是可以传入参数的,这些参数会自动传入__init__()方法中,可以通过重写这个方法来自定义对象的初始化操作

    实例:

    >>> class Ball():
        def __init__(self,name):
            self.name = name
        def kick(self):
            print('我叫%s,该死的,谁踢我。。。' % self.name)

            
    >>> b = Ball('小土豆')
    >>> b.kick()
    我叫小土豆,该死的,谁踢我。。。

    公有和私有?python内部采用了一种叫 name mangling(名字改编)的技术

    默认上对象的属性和方法都是公开的,可以直接通过点操作符(.)进行访问:

    >>> class Person:
        name = '亦我飞也'

        
    >>> p = Person()
    >>> p.name
    '亦我飞也'

    为了实现定义私有变量,只需要在变量名或函数名前加上"__"两个下划线,那么这个函数或变量就会变成私有的了:

    私有变量不可以直接由外部访问

    >>> class Person:
        __name = '亦我飞也'

        
    >>> p = Person()
    >>> p.__name
    Traceback (most recent call last):
      File "<pyshell#65>", line 1, in <module>
        p.__name
    AttributeError: 'Person' object has no attribute '__name'

    室友变量可以由内部(内部函数)进行访问

    >>> class Person:
        __name = '亦我飞也'
        def getName(self):
            return self.__name

        
    >>> p = Person()
    >>> p.__name
    Traceback (most recent call last):
      File "<pyshell#72>", line 1, in <module>
        p.__name
    AttributeError: 'Person' object has no attribute '__name'

    >>> p.getName()
    '亦我飞也'

    其实,name mangling(名字改编)技术,只是把双下划线开头的变量进行了改名而已。实际上在外部使用“_类名__变量名“即可访问双下划线开头的私有变量了

    >>> p._Person__name
    '亦我飞也'

    038 类和对象:继承

    继承

                      子类                              父类

    class DerivedClassName(BaseClassName):

    ……

    实例:一个子类可以继承它的父类的所有属性和方法

    >>> class Parent:
        def hello(self):
            print('正在调用父类的方法。。。')

            

    >>> class Child(Parent):    #子类继承父类
        pass     #直接往下执行

    >>> p = Parent()
    >>> p.hello()
    正在调用父类的方法。。。
    >>> c = Child()
    >>> c.hello()
    正在调用父类的方法。。。

    如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法和属性(即子类方法属性改变,父类是不变的)

    >>> class Child(Parent):
        def hello(self):
            print('正在调用子类的方法')

            
    >>> c = Child()
    >>> c.hello()
    正在调用子类的方法
    >>> p.hello()
    正在调用父类的方法。。。

    实例2:

    import random as r
    class Fish:
        def __init__(self):
            self.x = r.randint(0,10)
            self.y = r.randint(0,10)

        def move(self):
            self.x -= 1
            print('我的位置是:',self.x,self.y)


    class Goldfish(Fish):
        pass

    class Garp(Fish):
        pass

    class Shark(Fish):
        def __init__(self):
            self.hungry = True

        def eat(self):
            if self.hungry:
                print('吃货的梦想就是天天有的吃')
                self.hungry = False
            else:
                print('太撑了,吃不下了!')

    >>> fish = Fish()
    >>> fish.move()
    我的位置是: -1 10
    >>> fish.move()
    我的位置是: -2 10
    >>> goldfish = Goldfish()
    >>> goldfish.move()
    我的位置是: 2 3
    >>> goldfish.move()
    我的位置是: 1 3
    >>> shark = Shark()
    >>> shark.eat()
    吃货的梦想就是天天有的吃
    >>> shark.eat()
    太撑了,吃不下了!
    >>> shark.move()    #报错原因时因为子类重写构造函数,覆盖了父类D的构造函数
    Traceback (most recent call last):
      File "<pyshell#9>", line 1, in <module>
        shark.move()
      File "D:\python3.3.2\小甲鱼python\python程序\第三十八节课\fish.py", line 8, in move
        self.x -= 1
    AttributeError: 'Shark' object has no attribute 'x'

    注:继承父类属性的子类,其变量值只属于当前子类,是子类的局部变量

    报错修改部分解决方法一:调用未绑定的父类方法

    >>> shark = Shark()
    >>> shark.move()
    我的位置是: 2 1
    >>> shark.move()
    我的位置是: 1 1

    报错修改部分解决方法二:使用super函数super函数会帮我们自动找到基类的方法,而且还自动为我们传入self参数

    >>> shark = Shark()
    >>> shark.move()
    我的位置是: 1 1
    >>> shark.move()
    我的位置是: 0 1

    多重继承

    class DerivedClassName(Base1, Base2, Base3):

    ……

    实例:子类c同时继承基类Base1和基类Base2

    >>> class Base1:
        def fool1(self):
            print('我是fool1,我为Base1代言。。。')

            
    >>> class Base2:
        def fool2(self):
            print('我是fool2,我为Base2代言。。。')

            
    >>> class C(Base1,Base2):
        pass

    >>> c = C()
    >>> c.fool1()
    我是fool1,我为Base1代言。。。
    >>> c.fool2()
    我是fool2,我为Base2代言。。。

    039 类和对象:拾遗

    组合(将需要的类一起进行实例化并放入新的类中)

    实例:

    class Turtle:
        def __init__(self,x):
            self.num = x

    class Fish:
        def __init__(self,x):
            self.num = x

    class Pool:
        def __init__(self,x,y):
            self.turtle = Turtle(x)
            self.fish = Fish(y)

        def print_num(self):
            print('水池里一共有乌龟 %d 条,鱼 %d 条' % (self.turtle.num,self.fish.num))

    >>> pool = Pool(5,2)
    >>> pool.print_num()
    水池里一共有乌龟 5 条,鱼 2 条

    现在要求定义一个类,叫水池,水池里要有乌龟和鱼。

    类、类对象和实例对象

    以下例子可见,对实例对象c的count属性赋值后,就相当于覆盖了类对象C的count属性。如果没有赋值覆盖,那么引用的是类对象的count属性

    >>> a = C()
    >>> b = C()
    >>> c = C()
    >>> print(a.count,b.count,c.count)
    0 0 0
    >>> c.count += 10
    >>> print(a.count,b.count,c.count)
    0 0 10
    >>> C.count += 100
    >>> print(a.count,b.count,c.count)
    100 100 10

    另外,如果属性的名字跟方法名相同,属性会覆盖方法:

    >>> class C:
        def x(self):
            print('X-man')

            
    >>> c = C()
    >>> c.x()
    X-man
    >>> c.x = 1              #新定义对象c的一个x属性,并赋值为1
    >>> c.x
    1
    >>> c.x()     #可见,方法x()已经被属性x给覆盖了
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        c.x()
    TypeError: 'int' object is not callable

    结论:不要试图在一个类里边定义出所有能想到的特性和方法,应该利用继承和组合机制来进行扩展;用不同的词性命名,如属性名用名词、方法名用动词,并使用骆驼命名法等。

    到底什么是绑定?

    实例1:(python严格要求需要有实例才能被调用,即绑定概念)

    >>> class BB:
        def printBB():        #缺少self,导致无法绑定具体对象
            print('no zuo no die')

            
    >>> BB.printBB()
    no zuo no die
    >>> bb = BB()
    >>> bb.printBB()        #出现错误原因是由于绑定机制,自动把bb对象作为第一个参数传入
    Traceback (most recent call last):
      File "<pyshell#15>", line 1, in <module>
        bb.printBB()
    TypeError: printBB() takes 0 positional arguments but 1 was given

     

    Python严格要求方法需要有实例才能被调用,这种限制其实就是Python所谓的绑定概念。

    040 类和对象:一些相关的BIF

    一些相关的BIF

    issubclass(class, classinfo)  如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False

    >>> class A:
        pass

    >>> class B(A):
        pass

    >>> issubclass(B,A)
    True
    >>> issubclass(B,B)   #一个类被认为是其自身的子类
    True
    >>> issubclass(B,object)      # object是所有类的基类
    True
    >>> class C:
        pass

    >>> issubclass(B,C)
    False

    isinstance(object, classinfo)  如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False

    >>> issubclass(B,C)       注:第一个参数如果不是对象,则永远返回False
    False
    >>> b1 = B()
    >>> isinstance(b1,B)
    True
    >>> isinstance(b1,C)
    False
    >>> isinstance(b1,A)
    True
    >>> isinstance(b1,(A,B,C))
    True

    hasattr(object, name)  用来测试一个对象里是否有指定的属性,第一个参数(object)是对象,第二个参数(name)是属性名(属性的字符串名字)

    >>> class C:
        def __init__(self,x=0):
            self.x = x

            
    >>> c1 = C()
    >>> hasattr(c1,'x')    
    #注意,属性名要用引号括起来
    True

     

    getattr(object, name[, default])  返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数);若没有设置default参数,则抛出异常

    >>> getattr(c1,'x')
    0
    >>> getattr(c1,'y')

    Traceback (most recent call last):
      File "<pyshell#25>", line 1, in <module>
        getattr(c1,'y')
    AttributeError: 'C' object has no attribute 'y'

    setattr(object, name, value)  可以设置对象中指定属性的值,如果指定的属性不存在,则会新建属性并赋值
    >>> setattr(c1,'y','FishC')
    >>> getattr(c1,'y')
    'FishC'

    delattr(object, name)  用于删除对象中指定的属性,如果属性不存在,抛出异常。

    >>> delattr(c1,'y')
    >>> delattr(c1,'Z')

    Traceback (most recent call last):
      File "<pyshell#30>", line 1, in <module>
        delattr(c1,'Z')
    AttributeError: Z

    property(fget=None, fset=None, fdel=None, doc=None)  用来通过属性设置属性,第一个参数是获取属性的方法名,第二个参数是设置属性的方法名,第三个参数是删除属性的方法名

    >>> class C:
        def __init__(self,size =10):
            self.size = size
        def getSize(self):
            return self.size
        def setSize(self,value):
            self.size = value
        def delSize(self):
            del self.size
        x=property(getSize,setSize,delSize)

        

    >>> c = C()
    >>> c.x         #调用getSize()
    10
    >>> c.x = 12      #调用SetSize()
    >>> c.x
    12
    >>> c.size
    12
    >>> del c.x      #调用DelSize()
    >>> c.size
    Traceback (most recent call last):
      File "<pyshell#53>", line 1, in <module>
        c.size
    AttributeError: 'C' object has no attribute 'size'

    041 魔法方法:构造和析构

    __init__(self[, ...]) 方法是类在实例化成对象的时候首先会调用的一个方法

    >>> class Rectangle:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def getPeri(self):
            return (self.x + self.y) * 2
        def getArea(self):
            return self.x * self.y

    >>> rect = Rectangle(5,2)
    >>> rect.getPeri()
    14
    >>> rect.getArea()
    10

       注:__init__()方法的返回值一定是None 

    其实,__new__()才是在一个对象实例化时候所调用的第一个方法,它的第一个参数是这个类(cla),而其他的参数会直接传递给__init__()方法

    __new__(cls[, ...])

    >>> class CapStr(str):
        def __new__(cls,string):
            string = string.upper()
            return str.__new__(cls,string)

        
    >>> a = CapStr('hello world')
    >>> a
    'HELLO WORLD

    __del__(self)  当对象将要被销毁的时候,这个方法就会被调用。但要注意,并非del x就相当于调用x.__del__(),__del__()方法是当垃圾回收机制回收这个对象的时候才调用的。

    >>> class C:
        def __init__(self):
            print('我是__init__方法,我被调用了...')
        def __del__(self):
            print('我是__del__方法,我被调用l...')

            
    >>> c1 = C()     #创建对象c1
    我是__init__方法,我被调用了...
    >>> c2 = c1
    >>> c3 = c2
    >>> del c1
    >>> del c2
    >>> del c3   #删除c3时,对象c1才会彻底被删除(即没有标签指向对象c1时,其才会被回收)
    我是__del__方法,我被调用l...

    042 魔法方法:算术运算

    python2.2以后,对类和类型进行了统一,做法就是讲int()、float()、str()、list()、tuple()这些BIF转换为工厂函数(类对象):

    >>> type(len)
    <class 'builtin_function_or_method'>            #普通的BIF
    >>> type(int)
    <class 'type'>             #工厂函数(类对象),当调用它们的时候,其实就是创建了一个相应的实例对象
    >>> type(dir)
    <class 'builtin_function_or_method'>
    >>> type(list)
    <class 'type'>

    >>> a = int('123')        #创建一个相应的实例对象a
    >>> b = int('345')
    >>> a + b              #python在两个对象进行相加操作
    468

    举个例子,下面定义一个比较特立独行的类:

    >>> class New_int(int):
        def __add__(self,other):
            return int.__sub__(self,other)
        def __sub__(self,other):
            return int.__add__(self,other)

        
    >>> a = New_int(3)
    >>> b = New_int(5)
    >>> a + b    #两个对象相加,触发 __add__(self,other)方法
    -2
    >>> a - b
    8
    >>>

    实例2:

    >>> class New_int(int):
        def __add__(self,other):
            return (int(self) + int(other))       #将self与other强制转换为整型,所以不会出现两个对象相加触发__add__()方法
        def __sub__(self,other):
            return (int(self) - int(other))

        
    >>> a = New_int(3)
    >>> b = New_int(5)
    >>> a + b
    8

    043 魔法方法:算术运算2

    实例1:

    >>> class int(int):
        def __add__(self,other):
            return int.__sub__(self,other)

        
    >>> a = int(3)
    >>> b = int(2)
    >>> a + b
    1

    反运算:

    反运算与算术运算符的不同之处是,反运算多了一个'r',例如 __add__()的反运算对应为 __radd__()

    >>> a + b

    这里a是加数,b是被加数,如果a对象的__add__()方法没有实现或者不支持相应的操作,那么python就会自动调用b的__radd__()方法

    实例:

    >>> class Nint(int):
        def __radd__(self,other):
            return int.__sub__(self,other)

        
    >>> a = Nint(5)
    >>> b = Nint(3)
    >>> a + b      #由于a对象默认有__add__()方法,所以b的__radd__()没有执行
    8

    实例2:

    >>> class Nint(int):
        def __radd__(self,other):
            return int.__sub__(self,other)

        
    >>> b = Nint(5)
    >>> 3 + b         #由于3无__add__()方法,所以执行b的反运算__radd__(self,other)方法,其中self是b对象
    2

    注:在重写反运算魔法方法时,一定要注意顺序问题。

    增量赋值运算:

    比较操作符:

    其它操作符:

    044 魔法方法:简单定制

    简单定制

    基本要求:

    定制一个计时器的类

    startstop方法代表启动计时和停止计时

    假设计时器对象t1print(t1)和直接调用t1均显示结果

    当计时器未启动或已经停止计时,调用stop方法会给予温馨的提示

    两个计时器对象可以进行相加:t1 + t2

    只能使用提供的有限资源完成

    你需要这些资源

    使用time模块的localtime方法获取时间

    扩展阅读:time 模块详解(时间获取和转换)

    有关time模块的localtime方法获取时间(参考:

    https://fishc.com.cn/forum.php?mod=viewthread&tid=51326&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

    time.localtime返回struct_time的时间格式

    表现你的类:__str__ __repr__

    实例:

    import time as t   #导入时间模块,调用对象t

    class Mytimer():
        def __init__(self):
            self.unit = ['年','月','天','小时','分钟','秒']
            self.prompt = "未开始计时"
            self.lasted = []
            self.begin = 0  #属性
            self.end = 0
        def __str__(self):
            return self.prompt

        __repr__ = __str__

        def __add__(self,other):   #重写加法操作符,运行时间相加
            prompt = "总共运行了"
            result = []
            for index in range(6):
                result.append(self.lasted[index] + other.lasted[index])
                if result[index]:
                    prompt += (str(result[index]) + self.unit[index])
            return prompt
                               
        #开始计时
        def start(self):    #方法,属性名和方法名不能相同
            if not self.stop:
                self.prompt = ("提示:请先调用stop()停止计时!")
            else:
                self.begin = t.localtime()
                print('计时开始...')

        #停止计时
        def stop(self):
            if not self.begin:
                print('提示:请先调用start()进行计时!')
            else:
                self.end = t.localtime()
                self._calc()
                print('计时结束!')

        #内部方法,计算运行时间
        def _calc(self):
            self.prompt = "总共运行了"
            for index in range(6):
                self.lasted.append(self.end[index] - self.begin[index])
                if self.lasted[index]:
                    self.prompt += (str(self.lasted[index]) + self.unit[index])
            #为下一轮计时初始化变量
            self.begin = 0
            self.end = 0

    >>> t1 = Mytimer()
    >>> t1.stop()
    提示:请先调用start()进行计时!
    >>> t1.start()
    计时开始...
    >>> t1.stop()
    计时结束!
    >>> t1
    总共运行了4秒
    >>> t2 = Mytimer()
    >>> t2.start()
    计时开始...
    >>> t2.stop()
    计时结束!
    >>> t2
    总共运行了4秒
    >>> t1 + t2
    '总共运行了8秒'        

    进阶定制

    如果开始计时的时间是202222216:30:30,停止时间是202512315:30:30,那按照我们用停止时间减开始时间的计算方式就会出现负数3-11-1小时)你应该对此做一些转换

    现在的计算机速度都非常快,而我们这个程序最小的计算单位却只是秒,精度是远远不够的

    045 魔法方法:属性访问

    属性访问

    __getattr__(self, name)

    定义当用户试图获取一个不存在的属性时的行为

    __getattribute__(self, name)

    定义当该类的属性被访问时的行为

    __setattr__(self, name, value)

    定义当一个属性被设置时的行为

    __delattr__(self, name)

    定义当一个属性被删除时的行为

    实例1:

    class C:
        def __getattribute__(self, name):
            print('getattribute')
            # 使用 super() 调用 object 基类的 __getattribute__ 方法
            return super().__getattribute__(name)

        def __setattr__(self, name, value):
            print('setattr')
            super().__setattr__(name, value)

        def __delattr__(self, name):
            print('delattr')
            super().__delattr__(name)

        def __getattr__(self, name):
            print('getattr')

    >>> c = C()
    >>> c.x
    getattribute
    getattr
    >>> c.x = 1
    setattr
    >>> c.x
    getattribute
    1
    >>> del c.x
    delattr
    >>> setattr(c,'y','Yellow')
    setattr

    练习要求

    写一个矩形类,默认宽和高两个属性

    如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长。

    实例2:

    class Rectangle:
        def __init__(self, width=0, height=0):
            self.width = width
            self.height = height

        def __setattr__(self, name, value):#一发生赋值操作,则会触发__setattr__()魔法方法
            if name == 'square':#判断name属性是否为正方形
                self.width = value
                self.height = value
            else:
                self.__dict__[name] = value

        def getArea(self):
            return self.width * self.height

    >>> r1 = Rectangle(4,5)
    >>> r1.getArea()
    20
    >>> r1.square = 10
    >>> r1.getArea()
    100

    046 魔法方法:描述符(Property的原理)

    描述符

    描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

    __get__(self, instance, owner)

    用于访问属性,它返回属性的值

    __set__(self, instance, value)

    将在属性分配操作中调用,不返回任何内容

    __delete__(self, instance)

    控制删除操作,不返回任何内容

    实例:

    >>> class MyDecriptor:
        def __get__(self,instance,owner):
            print("getting...",self,instance,owner)
        def __set__(self,instance,value):
            print("setting...",self,instance,value)
        def __delete__(self,instance):
            print("deleting...",self,instance)

     

    >>> class Test:
        x = MyDecriptor()   #取Mydecriptor类的实例指派给Test类的属性x

    >>> test = Test()
    >>> test.x
    getting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98> <class '__main__.Test'>
    >>> test
    <__main__.Test object at 0x000000000335EF98>
    >>> test.x = "X-man"
    setting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98> X-man
    >>> del test.x
    deleting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98>

     

    实例2:

    >>> class MyProperty:
        def __init__(self,fget = None,fset = None,fdel = None):
            self.fget = fget
            self.fset = fset
            self.fdel = fdel
        def __get__(self,instance,owner):
            return self.fget(instance)
        def __set__(self,instance,value):
            self.fset(instance,value)
        def __delete__(self,instance):
            self.fdel(instance)

            
    >>> class C:
        def __init__(self):
            self._x = None
        def getX(self):
            return self._x
        def setX(self,value):
            self._x = value
        def delX(self):
            del self._x
        x = MyProperty(getX,setX,delX)

        
    >>> c = C()
    >>> c.x = "HELLOW"
    >>> c.x
    'HELLOW'
    >>> c._x
    'HELLOW'
    >>> del c.x
    >>> c._x
    Traceback (most recent call last):
      File "<pyshell#70>", line 1, in <module>
        c._x
    AttributeError: 'C' object has no attribute '_x'

    练习要求

    先定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性

    要求个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果。

    实例3:

    ss Celsius:  #摄氏度描述符类
        def __init__(self,value = 26.0):#self为描述符类自身(此为摄氏度描述符类)的实例(此为cel)
            self.value = float(value)
        def __get__(self,instance,owner):#instance是这个描述符的拥有者所在的类的实例(此为temp)
            return self.value
        def __set__(self,instance,value):#owner是这个描述符的拥有者所在的类本身(此为温度类)
            self.value = float(value)

    class Fahrenheit:   #华氏度描述符类
        def __get__(self,instance,owner):
            return instance.cel * 1.8 +32  #摄氏度转华氏度
        def __set__(self,instance,value):
            instance.cel = ((float)(value)- 32)/ 1.8   #华氏度转摄氏度
            
    class Temperature:   #温度类
        cel = Celsius()   #设置摄氏度属性(描述符类的实例指派给了温度类的属性)
        fah = Fahrenheit()#设置华氏度属性

    >>> temp = Temperature()
    >>> temp.cel
    26.0
    >>> temp.fah
    78.80000000000001
    >>> temp.fah = 78.8
    >>> temp.cel
    25.999999999999996

    047 魔法方法:定制序列

    协议是什么?

    协议(Protocols)与其他编程语言中的接口很相似,它规定你哪些方法必须要定义。然而,在Python中的协议就显得不那么正式。事实上,在Python中,协议更像是一种指南

    容器类型的协议

    如果说你希望定制的容器是不可变的话,你只需要定义__len__()__getitem__()方法。

    如果你希望定制的容器是可变的话,除了__len__()__getitem__()方法,你还需要定义__setitem__()__delitem__()两个方法。

    练习要求

    编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。

    class CountList:  #定义记录列表中每个元素访问次数类
        def __init__(self,*args): #参数是可变类型的
            self.values = [x for x in args]#将args的数据存入列表self.values中
            self.count = {}.fromkeys(range(len(self.values)),0)#创建字典,初试化为0

        def __len__(self):  #返回容器中元素的个数
            return len(self.values)#len方法用于返回参数的长度 
        def __getitem__(self,key):  #获取容器中指定元素的行为,key为访问对应的键
            self.count[key] += 1#每访问一次,字典键对应的键值加1
            return self.values[key]

    >>> c1 = CountList(1,3,5,7,9)
    >>> c2 = CountList(2,4,6,8,10)
    >>> c1[1]  #c1[1]第一次访问
    3
    >>> c2[2]
    6
    >>> c1[1] + c2[2] #c1[1]第二次访问
    9
    >>> c1.count
    {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
    >>> c2.count
    {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}

    048 魔法方法:迭代器

    迭代的意思类似于循环,每一次重复的过程被称为一次迭代的过程,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。提供迭代方法的容器称为迭代器(如序列(列表、元组、字符串)、字典等)。

    对一个容器对象调用iter()就得到它的迭代器,调用next()迭代器就会返回下一个值。入托迭代器没有值可以返回了,就会抛出异常。

    •iter()

    –__iter__()

    •next()

    –__next__()

    实例1:

    >>> string = "FishC"
    >>> it = iter(string)
    >>> next(it)
    'F'
    >>> next(it)
    'i'
    >>> next(it)
    's'
    >>> next(it)
    'h'
    >>> next(it)
    'C'
    >>> next(it)
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        next(it)
    StopIteration

    一个容器如果是迭代器,那就必须实现__iter__()魔法方法,这个方法实际上就是返回迭代器本身。重点要实现的是__next__()魔法方法,因为它决定了迭代的规则。

    实例2:

    >>> class Fibs:
        def __init__(self):
            self.a = 0
            self.b = 1
        def __iter__(self):
            return self
        def __next__(self):
            self.a,self.b = self.b,self.a + self.b
            return self.a

        
    >>> fibs = Fibs()
    >>> for each in fibs:
        if each < 20:
            print(each)
        else:
            break

        
    1
    1
    2
    3
    5
    8
    13

    实例3:

     

    >>> class Fibs:
        def __init__(self,n =20):
            self.a = 0
            self.b = 1
            self.n = n
        def __iter__(self):
            return self
        
        def __next__(self):
            self.a,self.b = self.b,self.a + self.b
            if self.a > self.n:
                raise StopIteration
            return self.a

        
    >>> fibs = Fibs()
    >>> for each in fibs:
        print(each)

        
    1
    1
    2
    3
    5
    8
    13

     

    >>> fibs = Fibs(10)
    >>> for each in fibs:
        print(each)

        
    1
    1
    2
    3
    5
    8

     

    049 乱入:生成器

    所谓协同程序,就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始。

    生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后在再次调用它的时候,从上次暂停的位置继续执行下去。

    一个函数中如果有yield语句,则被定义为生成器。

    实例1:

    >>> def myGen():
        print("生成器被执行了!")
        yield 1   #暂停一次,相当于return,返回1
        yield 2     #暂停一次,相当于return,返回2

        
    >>> myG = myGen()
    >>> next(myG)
    生成器被执行了!
    1
    >>> next(myG)
    2

    像前面介绍的斐波那契的例子,也可以用生成器来实现:

    >>> def fibs():
        a = 0
        b = 1
        while True:
            a,b = b,a + b
            yield a

            
    >>> for each in fibs():
        if each > 100:
            break
        print(each)

        
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89

    列表推导式表达:

    100以内,能被2整除,但不能被3整除的所有整数

    >>> a = [i for i in range(100) if not (i % 2) and (i % 3 )]
    >>> a
    [2, 4, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, 38, 40, 44, 46, 50, 52, 56, 58, 62, 64, 68, 70, 74, 76, 80, 82, 86, 88, 92, 94, 98]

    字典推导式:

    10以内是否为偶数

    >>> a = {i:i % 2 == 0 for i in range(10)}
    >>> a
    {0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}

    集合推导式:

    >>> a = {i for i in [1,2,3,3,4,5,5,5,6,7,7,8]}
    >>> a
    {1, 2, 3, 4, 5, 6, 7, 8}

    元组生成器推导式:

    >>> e = (i for i in range(5))
    >>> next(e)
    0
    >>> next(e)
    1
    >>> next(e)
    2

    050 模块:模块就是程序

    什么是模块

    容器 -> 数据封装

    函数 -> 语句封装

    -> 方法和属性的封装

    模块 -> 模块就是程序

    命名空间

    爱的宣言:世界上只有一个名字,使我这样牵肠挂肚,像有一根看不见的线,一头牢牢系在我心尖上,一头攥在你手中,这个名字就叫做鱼C工作室计算机一班的小花……

    导入模块

    第一种:import 模块名

    实例1:import导入模块

    实例2:import导入模块

    第二种:from 模块名 import 函数名(不推荐使用)

    第三种:import 模块名 as 名字(推荐使用)

    TemperatureConversion文件:

    def c2f(cal):
        return cal * 1.8 + 32
    def f2c(fah):
        return (fah - 32)/1.8

    calc文件:

    import TemperatureConversion as tc  #tc为取得新名字

    print("32摄氏度 = %.2f 华氏度\n" % tc.c2f(32))
    print("99华氏度 = %.2f 摄氏度" % tc.f2c(99))

    051 模块:__name__='__main__'、搜索路径和包

    模块!模块!

    实例1:为TemperatureConversion添加测试程序(TemperatureConversion被作为程序运行)

    def c2f(cal):
        return cal * 1.8 + 32

    def f2c(fah):
        return (fah - 32)/1.8

    def test():
        print("0摄氏度 = %.2f 华氏度\n" % c2f(0))
        print("0华氏度 = %.2f 摄氏度" % f2c(0))

    test()

    运行calc文

    当希望TemperatureConversion被调用时作为模块导入时

    def c2f(cal):
        return cal * 1.8 + 32

    def f2c(fah):
        return (fah - 32)/1.8

    def test():
        print("0摄氏度 = %.2f 华氏度" % c2f(0))
        print("0华氏度 = %.2f 摄氏度" % f2c(0))

    if __name__ == "__main__":#当此文件当做程序运行时,执行test(),否则不执行
        test()

    运行calc文件

    if __name__ == ‘__main__’

    搜索路径(系统会首先搜索的路径)

    >>> import sys
    >>> sys.path
    ['D:\\python3.3.2\\小甲鱼python\\python程序\\第五十节课\\Temperature', 'D:\\python3.3.2\\Lib\\idlelib', 'C:\\windows\\system32\\python33.zip', 'D:\\python3.3.2\\DLLs', 'D:\\python3.3.2\\lib', 'D:\\python3.3.2', 'D:\\python3.3.2\\lib\\site-packages']

    添加搜索路径:

    >>> import TemperatureConversion
    Traceback (most recent call last):
      File "<pyshell#0>", line 1, in <module>
        import TemperatureConversion
    ImportError: No module named 'TemperatureConversion'

    >>> import sys
    >>> sys.path.append("D:\\python3.3.2\WODE\Temperature")
    >>> sys.path
    ['', 'D:\\python3.3.2\\Lib\\idlelib', 'C:\\windows\\system32\\python33.zip', 'D:\\python3.3.2\\DLLs', 'D:\\python3.3.2\\lib', 'D:\\python3.3.2', 'D:\\python3.3.2\\lib\\site-packages', 'D:\\python3.3.2\\WODE\\Temperature']
    >>> import TemperatureConversion
    >>> TemperatureConversion.f2c(59)
    15.0

    package

    1.创建一个文件夹,用于存放相关的模块,文件夹的名字即包的名字;

    2.文件夹中创建一个__init__.py的模块文件,内容可以为空;

    3.相关的模块放入文件夹中

    052 模块:像个极客一样去思考

    使用print调用__doc__属性,可以带格式查看这个模块的简介

    使用dir()可以查询到该模块定义了哪些变量、函数和类

    053 论一只爬虫的自我修养

    Python如何访问互联网?

     

    URL的一般格式为(带方括号[]的为可选项)

    protocol :// hostname[:port] / path / [;parameters][?query]#fragment

    URL由三部分组成:

    第一部分是协议httphttpsftpfileed2k…

    第二部分是存放资源的服务器的域名系统或IP地址(有时候要包含端口号,各种传输协议都有默认的端口号,如http的默认端口为80)。

    第三部分是资源的具体地址,如目录文件名

    054 论一只爬虫的自我修养2:实战

    import urllib.request

    response = urllib.request.urlopen('http://placekitten.com/g/500/600')#  返回文件对象response
    cat_imag = response.read()

    with open('cat_500_600.jpg','wb') as f:
        f.write(cat_imag)

    >>> response.geturl()
    'http://placekitten.com/g/500/600'
    >>> response.info()
    <http.client.HTTPMessage object at 0x00000000034EAA20>
    >>> print(response.info())
    Date: Sat, 27 Jul 2019 02:44:18 GMT
    Content-Type: image/jpeg
    Transfer-Encoding: chunked
    Connection: close
    Set-Cookie: __cfduid=d3cd08233581619b9ef8464ae93f7d5ff1564195458; expires=Sun, 26-Jul-20 02:44:18 GMT; path=/; domain=.placekitten.com; HttpOnly
    Access-Control-Allow-Origin: *
    Cache-Control: public, max-age=86400
    Expires: Sun, 28 Jul 2019 02:44:18 GMT
    CF-Cache-Status: HIT
    Age: 66459
    Vary: Accept-Encoding
    Server: cloudflare
    CF-RAY: 4fcb454ecc35ce6b-LHR


    >>> response.getcode()
    200

    055 论一只爬虫的自我修养3:隐藏

    修改 headers

    通过Requestheaders参数修改

    通过Request.add_header()方法修改

    代理

    步骤:

    1. 参数是一个字典 {‘类型’:‘代理ip:端口号’}

    proxy_support = urllib.request.ProxyHandler({})

     

    2. 定制、创建一个 opener

    opener = urllib.request.build_opener(proxy_support)

     

    3a. 安装 opener

    urllib.request.install_opener(opener)

    3b. 调用 opener

    opener.open(url)

     

    064 GUI的终极选择:Tkinter

     

    >>> import tkinter   #Tkinter是python默认的GUI库,导入Tkinter模块
    >>> 

    实例1:

    import tkinter as tk

    root = tk.Tk()#创建一个主窗口,用于容纳整个GUI程序
    root.title("FishC Demo")#设置主窗口对象的标题栏

    #添加一个Label组件,可以显示文本、图标或者图片(此处显示文本)
    theLabel = tk.Label(root,text = "我的第二个窗口程序")
    theLabel.pack()#调用Label组件的pack方法,用于自动调节组件自身尺寸

    root.mainloop()#执行此语句后,窗口才会显示,程序进入主事件循环

    实例2:

    import tkinter as tk

    class App:#创建类App
        def __init__(self,root):#self为指向App类的指针
            #创建一个框架,然后在里面添加一个Button按钮组件,框架用来将复杂布局中按钮分组
            frame = tk.Frame(root)
            frame.pack(side = tk.RIGHT,padx = 10,pady = 10)#调节框架自身尺寸,此处设置为右对齐(右上角为原点),偏移(10,10)
            
            #创建一个按钮组件,fg(foreground),设置前景色
            #创建一个Button按钮,属性为self.hi_there,属于frame框架,按钮按下时调用self.say_hi方法
            #设置前景色为黑色,背景色为白色
            self.hi_there = tk.Button(frame,text = "打招呼",bg = "black",fg = "white",command = self.say_hi)
            self.hi_there.pack()#自动调节自身尺寸
            
            #say_hi()方法定义实现   
        def say_hi(self):
            print("互联网广大朋友们好,我是亦我飞也!")
            
            
    root = tk.Tk()#创建一个主窗口(toplever的根窗口),并把它作为参数实例化app对象,用于容纳整个GUI程序,
    app = App(root)#创建类App的一个实例对象app,传入参数为root

    app.mainloop()#执行此语句后,窗口才会显示,程序进入主事件循环
     

    065 GUI的终极选择:Tkinter2

    实例1:Label组件显示文字与gif图片

    #导入tkinter模块的所有内容
    from tkinter import *

    #创建主窗口
    root = Tk()
    #创建一个文本Label对象,文字为左对齐,离左边边框距离为10
    textLabel = Label(root,
                      text = "您下载的影片含有未成年人限制内容,\n请满18周岁后再点击观看!",
                      justify = LEFT,padx = 10)
    #Label组件为左对齐
    textLabel.pack(side = LEFT)

    #创建一个图像Label对象
    #用PhotoImage实例化一个图片对象(支持gif格式的图片)
    photo = PhotoImage(file = "18.gif")
    imgLabel = Label(root,image = photo)
    imgLabel.pack(side = RIGHT)

    mainloop()
     

    实例2:

    例2:文字显示在图片上

    #导入tkinter模块的所有内容
    from tkinter import *

    #创建主窗口
    root = Tk()

    #创建一个图像Label对象
    photo = PhotoImage(file = "bg.gif")
    #创建一个文本Label对象
    textLabel = Label(root,
                      text = "学Python\n到FishC!",
                      font = ("宋体",20),
                      fg = "white",
                      justify = LEFT,  #文字左对齐
                      image = photo,
                      compound = CENTER, #设置文本和图像的混合模式
                      )
    #文本Label对象偏移,离左窗口与上窗口都为10
    textLabel.pack(side = LEFT,padx =10,pady =10)

    mainloop()
     

    实例2:Button组件

    #导入tkinter模块的所有内容
    from tkinter import *

    def callback():
        var.set("吹吧你,我才不信呢~")

    #创建主窗口
    root = Tk()
    #设置主窗口对象的标题栏
    root.title("TK")

    frame1 = Frame(root)#框架1
    frame2 = Frame(root)#框架2

    #创建一个文本Label对象,文字为左对齐
    var = StringVar()
    var.set("您下载的影片含有未成年人限制内容,\n请满18周岁后再点击观看!")
    textLabel = Label(frame1,
                      textvariable = var, #Button显示一个StringVar的变量
                      justify = LEFT)
    #Label组件为左对齐
    textLabel.pack(side = LEFT)

    #创建一个图像Label对象
    #用PhotoImage实例化一个图片对象(支持gif格式的图片)
    photo = PhotoImage(file = "18.gif")
    imgLabel = Label(root,image = photo)
    imgLabel.pack(side = RIGHT)

    #加一个按钮
    theButton = Button(frame2,text = "已满18周岁",command = callback)
    theButton.pack()
    frame1.pack(padx = 10,pady = 10)
    frame2.pack(padx = 10,pady = 10)

    mainloop()

    066 GUI的终极选择:Tkinter3

    实例1:Checkbutton 组件

    from tkinter import *

    root = Tk()
    #需要一个Tkinter变量,用于表示该按钮是否被选中
    v = IntVar()
    c = Checkbutton(root,text="测试一下",variable = v)

    c.pack()
    #如果被选中,那么变量v被赋值为1,否则为0
    #可以用个Label标签动态地给大家展示:
    lable = Label(root,textvariable = v)
    lable.pack()

    mainloop()

    实例2:

    from tkinter import *

    root = Tk()

    GIRLS = ["貂蝉","王昭君","西施","杨玉环"]
    v = []
    for girl in GIRLS:
        v.append(girl)
        c = Checkbutton(root,text = girl,variable = v[-1])#-1表示每次取v列表中最后一个元素,即刚加入的那个元素
        c.pack(anchor = W)#W(western)向左对齐

    mainloop()

    实例3:Radiobutton 组件

    from tkinter import *

    root = Tk()

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    Radiobutton(root,text = "One",variable = v,value = 1).pack(anchor = W)
    #value表示第一个按钮被选中时,v的值赋值给variable

    Radiobutton(root,text = "Two",variable = v,value = 2).pack(anchor = W)

    Radiobutton(root,text = "Three",variable = v,value = 3).pack(anchor = W)

    Radiobutton(root,text = "Four",variable = v,value = 4).pack(anchor = W)

    mainloop()

    实例4:循环处理

    from tkinter import *

    root = Tk()

    LANGS = [
        ("Python",1),
        ("Perl",2),
        ("Ruby",3),
        ("Lua",4)]
         

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    v.set(1)#将1设置为默认值
    for lang,num in LANGS:
        b= Radiobutton(root,text = lang,variable = v,value = num)
        b.pack(anchor = W)
    #value表示第一个按钮被选中时,v的值赋值给variable

    mainloop()

    实例5:改成按钮形式

    from tkinter import *

    root = Tk()

    LANGS = [
        ("Python",1),
        ("Perl",2),
        ("Ruby",3),
        ("Lua",4)]
         

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    v.set(1)#将1设置为默认值
    for lang,num in LANGS:
        b= Radiobutton(root,text = lang,variable = v,value = num,indicatoron = False)
        b.pack(fill = X)#表示横向填充
    #value表示第一个按钮被选中时,v的值赋值给variable

    mainloop()

    实例6:LabelFrame 组件

    from tkinter import *

    root = Tk()

    group = LabelFrame(root,text = "最好的脚本语言是?",padx = 10,pady = 10)#按钮相对边框的偏移
    group.pack(padx = 10,pady = 10)#框架相对边框的偏移

    LANGS = [
        ("Python",1),
        ("Perl",2),
        ("Ruby",3),
        ("Lua",4)]
         

    v = IntVar()#如果被选中,v被赋值为1,否则为0
    v.set(1)#将1设置为默认值
    for lang,num in LANGS:
        b= Radiobutton(group,text = lang,variable = v,value = num,indicatoron = False)
        b.pack(fill = X)
    #value表示第一个按钮被选中时,v的值赋值给variable

    mainloop()

    067 GUI的终极选择:Tkinter4

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口
    e = Entry(root)#在主窗口中插入输入框
    e.pack(padx = 20,pady = 20)

    e.delete(0,END)#清空输入框
    e.insert(0,"默认文本...")#设置输入框内容

    mainloop()

    实例2:

    from tkinter import *

    def button1_show():
        print("作品:《%s》" % e1.get())#将e1.get()中得到的输入框1的内容格式化为字符串
        print("作者:%s" % e2.get())

    root = Tk()#创建主窗口

    Label(root,text = "作品:",padx = 20,pady = 10).grid(row=0,column=0)#第1行第1列,偏移是相对于当前操作组件的相邻x轴或y轴的偏移距离
    Label(root,text = "小甲鱼:").grid(row=1,column=0)#第1行第0列


    e1 = Entry(root)#在主窗口中插入输入框,文本框的内容通过e1调用
    e2 = Entry(root)#在主窗口中插入输入框
    e1.grid(row=0,column=1,padx=10)#x方向偏移是相对于"作品"的x方向偏移的;y方向偏移表示此输入框与y方向相邻物体或边框之间偏移的距离(y方向偏移)
    e2.grid(row=1,column=1,padx=10,pady=20)#x方向偏移是相对于"小甲鱼"的x方向偏移的;y方向偏移表示此输入框与y方向相邻上下物体或边框偏移的距离(y方向偏移)


    #加两个按钮
    Button1 = Button(root,text = "获取信息",command = button1_show)\
              .grid(row = 2,column = 0,sticky = W,padx = 10,pady=10)#加入反斜杠可实现分行编辑,方位设置为最西边(即靠左)
    Button2 = Button(root,text = "退出",command = root.quit).grid(row = 2,column = 1,sticky = E,padx=10)#方位设置为最东边(即靠右)

    #注:双击打开文件时退出才有效
    e1.delete(0,END)#清空输入框
    e1.insert(0,"零基础入门学习Python")#设置输入框内容

    e2.delete(1,END)#清空输入框
    e2.insert(1,"小甲鱼")#设置输入框内容

    mainloop()

    按下获取信息

    更改输入框数据,然后按下获取信息

    实例2:账号密码设置

    from tkinter import *

    def show():
        print("作品:《%s》" % e1.get())#将e1.get()中得到的输入框1的内容格式化为字符串
        print("作者:%s" % e2.get())
        e1.delete(0,END)#清空输入框1
        e2.delete(0,END)#清空输入框2

    root = Tk()#创建主窗口
    #Tkinter总共提供了三种布局组件的方法:pack()、grid()和place()
    #grid()方法允许你用表格的形式来管理组件的位置
    #row选项代表行,coulumn选项代表列
    #row = 1,column = 2表示第二行第三列(0表示第一行)

    Label(root,text = "账号:").grid(row=0)#第1行
    Label(root,text = "密码:").grid(row=1)#第2行
    v1 = StringVar()
    v2 = StringVar()

    e1 = Entry(root,textvariable = v1)#在主窗口中插入输入框,文本框的内容通过e1调用
    e2 = Entry(root,textvariable = v2,show="*")#在主窗口中插入输入框
    e1.grid(row=0,column=1,padx=10,pady=5)#x方向偏移是相对于"作品"的x方向偏移的;y方向偏移表示此输入框与y方向相邻物体或边框之间偏移的距离(y方向偏移)
    e2.grid(row=1,column=1,padx=10,pady=5)#x方向偏移是相对于"小甲鱼"的x方向偏移的;y方向偏移表示此输入框与y方向相邻上下物体或边框偏移的距离(y方向偏移)


    #可以使用sticky选项来设置组件的位置
    #使用N、E、S、W以及他们的组合NE、SE、SW、NW来表示方位

    #加两个按钮
    Button(root,text = "芝麻开门",command = show)\
              .grid(row = 2,column = 0,sticky = W,padx = 10,pady=5)#加入反斜杠可实现分行编辑,方位设置为最西边(即靠左)
    Button(root,text = "退出",command = root.quit).grid(row = 2,column = 1,sticky = E,padx=10)#方位设置为最东边(即靠右)

    mainloop()

    实例3:验证函数validatecommand

    from tkinter import *

    master = Tk()

    def test():
        if e1.get() == "小甲鱼":
            print("正确!")
            return True
        else:
            print("错误!")
            e1.delete(0, END)
            return False

    v = StringVar()

    #focusout表示Entry组件失去焦点的时候验证,调用validatecommand的test函数

    e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test)
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    mainloop()
     

    实例4:invalidcommand函数

    from tkinter import *

    master = Tk()

    def test():
        if e1.get() == "小甲鱼":
            print("正确!")
            return True
        else:
            print("错误!")
            e1.delete(0, END)
            return False

    def test2():
        print("我被调用了...")

    v = StringVar()

    #focusout表示Entry组件失去焦点的时候验证,调用validatecommand的test函数
    #invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用
    e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test,\
               invalidcommand=test2)
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    mainloop()
     

    实例5:验证函数提供一些额外的选项

    validatecommand(f,s1,s2,...)

    其中,f是验证函数名,s1,s2,s3是额外的选项,这些选项会作为参数一次传给f函数。在此之前,需要调用register()方法将验证函数包装起来。

    from tkinter import *

    master = Tk()

    v = StringVar()

    def test(content, reason, name):
        if content == "小甲鱼":
            print("正确!")
            print(content, reason, name)
            return True
        else:
            print("错误!")
            print(content, reason, name)
            return False

    testCMD = master.register(test)
    e1 = Entry(master, textvariable=v, validate="focusout", \
               validat
               ecommand=(testCMD, '%P', '%v', '%W'))
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    mainloop()
     

    实例6:设计一个 计算器

    from tkinter import *
    #计算函数
    def calc():
        result = int(v1.get())+int(v2.get())#强制转换为整型
        v3.set(result)#将result中的内容放到v3中

    #创建窗口
    root = Tk()
    #创建窗口中的一个frame框架
    frame = Frame(root)
    #设置框架位置并显示
    frame.pack(padx = 10,pady = 10)

    v1 = StringVar()
    v2 = StringVar()
    v3 = StringVar()

    #注意,这里不能使用e1.get()或者v1.get()来获取输入的内容,因为validate选项
    #指定为“key"的时候,有任何输入操作都会被拦截到这个函数中
    #也就是说先拦截,只有这个函数返回True,那么输入的内容才会到变量里去
    #所以要用%P来获取最新的输入框内容
    def test(content):
        if content.isdigit():
            return True
        else:
            return False

    #创建三个Entry组件
    testCMD = frame.register(test)
    #创建2个输入组件,输入的数据赋值给v1、v2
    e1 = Entry(frame, textvariable=v1,width=10, validate="key",\
               validatecommand=(testCMD, '%P'))
    e2 = Entry(frame, textvariable=v2,width=10, validate="key",\
               validatecommand=(testCMD, '%P'))
    #一个输出组件,设置为只读模式(readonly),v3的数据赋值给textvariable进行输出显示
    e3 = Entry(frame, textvariable=v3,width=10, validate="key",\
               validatecommand=(testCMD, '%P'),state="readonly")
    #位置设置
    e1.grid(row=0,column=0,padx=10,pady=10)
    e2.grid(row=0,column=2,padx=10)
    e3.grid(row=0,column=4,padx=10)

    #创建两个Label组件
    Label(frame,text="+").grid(row=0,column=1)
    Label(frame,text="=").grid(row=0,column=3)

    #创建一个按钮,宽度为10
    button=Button(frame,text="计算结果",width=10,command=calc)
    button.grid(row=1,column=2,pady=10)

    mainloop()

    068 GUI的终极选择:Tkinter5

    Listbox组件

    如果需要提供选项给用户选择,单选可以用Radiobutton组件,多选可以用Checkbutton,如果提供的选项非常多,可以考虑使用Listbox组件。Listbox是以列表的形式显示出来,并支持滚动条操作。

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口

    theLB = Listbox(root,setgrid = True,selectmode=EXTENDED)#创建一个空列表
    theLB.pack()

    #往列表里添加数据
    for item in ["鸡蛋","鸭蛋","鹅蛋","李狗蛋"]:
        theLB.insert(END,item)#每次在列表最后插入一个数据

    #创建一个按钮,ACTIVE表示当前选中的数据
    theButton = Button(root,text="删除",command = lambda x = theLB:x.delete(ACTIVE))
    theButton.pack()

    #theLB.delete(0,END)删除所有列表数据

    mainloop()

    注:listbox.delete(0,END)可以删除列表中所有项目

    实例2:添加height选项

    from tkinter import *

    root = Tk()#创建主窗口

    #height=11表示可以显示11个项目
    theLB = Listbox(root,setgrid = True,\
                    selectmode=BROWSE,height=11)#创建一个空列表,选择模式为单选
    theLB.pack()

    #往列表里添加数据
    for item in range(11):
        theLB.insert(END,item)#每次在列表最后插入一个数据

    #创建一个按钮,ACTIVE表示当前选中的数据
    theButton = Button(root,text="删除",command = lambda x = theLB:x.delete(ACTIVE))
    theButton.pack()

    #theLB.delete(0,END)删除所有列表数据

    mainloop()

    Scrollbar组件

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口

    sb = Scrollbar(root)
    sb.pack(side=RIGHT,fill=Y)

    lb = Listbox(root,yscrollcommand=sb.set)#创建一个空列表
    for i in range(1000):
        lb.insert(END,i)
    lb.pack(side=LEFT,fill=BOTH)

    sb.config(command = lb.yview)

    mainloop()

    事实上,这是一个互联互通的过程。当用户操作滚动条时,滚动条响应滚动并同时通过Listbox组件的yview()方法滚动列表框里的内容;同样,当列表框中可视范围发生改变的时候,Listbox组件通过调用Scrollbar组件的set()方法设置滚动条的最新位置。

    Scale组件

    Scale组件主要是通过滑块来表示某个范围内的一个数字,可以通过修改选项设置范围以及分辨率(精度)

    实例1:

    from tkinter import *

    root = Tk()#创建主窗口
    Scale(root,from_=0,to=42).pack()#创建铅锤方向滚动条
    Scale(root,from_=0,to=200,orient=HORIZONTAL).pack()#创建水平方向滚动条

    mainloop()

    实例2:打印当前位置

    from tkinter import *

    def show():
        print(s1.get(),s2.get())#使用get()方法获取当前滑块的位置

    root = Tk()#创建主窗口
    s1 = Scale(root,from_=0,to=42)#创建铅锤方向滚动条
    s1.pack()
    s2 = Scale(root,from_=0,to=200,orient=HORIZONTAL)#创建水平方向滚动条
    s2.pack()

    #创建一个按钮
    Button(root,text="获取位置",command=show).pack()

    mainloop()

    实例3:通过resolution选项控制分辨率(步长),通过tickinterval选项设置刻度

    from tkinter import *

    def show():
        print(s1.get(),s2.get())#使用get()方法获取当前滑块的位置

    root = Tk()#创建主窗口
    #tickinterval表示设置刻度,即每隔多少显示一个刻度
    #length表示滚动条的长度所占的像素数
    #resolution用来控制分辨率(步长)
    s1 = Scale(root,from_=0,to=42,tickinterval=5,length=200,\
               resolution=5,orient=VERTICAL)#创建铅锤方向滚动条
    s1.pack()
    s2 = Scale(root,from_=0,to=200,tickinterval=10,\
               length=600,orient=HORIZONTAL)#创建水平方向滚动条
    s2.pack()

    #创建一个按钮
    Button(root,text="获取位置",command=show).pack()

    mainloop()

    069 GUI的终极选择:Tkinter6

    Text组件

    Text(文本)组件用于显示和处理多种任务。虽然该组件的主要目的是显示多行文本,但它常常也被用于作为简单的文本编辑器和网页浏览器使用。

    实例1:插入内容

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=2)
    text.pack()
    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love\n")#光标当前的位置插入

    #END,对应Text组件的文本缓存区最后一个字符的下一个位置
    text.insert(END,"FishC.com!")

    mainloop()

    实例2:插入image对象windows组件

    from tkinter import *

    def show():
        print("哟,我被点了一下~")

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入

    #创建一个按钮
    b1=Button(root,text="点我点我",command=show)
    text.window_create(INSERT,window=b1)

    mainloop()
     

    实例3:单击按钮显示一张图片

    from tkinter import *

    def show():
        text.image_create(INSERT,image=photo)

    root = Tk()
    text = Text(root,width=30,height=50)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入

    photo = PhotoImage(file='fishc.gif')

    #创建一个按钮
    b1=Button(root,text="点我点我",command=show)
    text.window_create(INSERT,window=b1)

    mainloop()

    Indexer用法

    实例1:“line.column”

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    print(text.get(1.2,1.6))#获取第一行第2列到第一行第六列的数据

    mainloop()

    实例2:“line.end”

    行号加上字符串".end"格式表示为该行最后一个字符的位置

    实例:

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    print(text.get("1.2","1.end"))#获取第一行第2列到第一行第六列的数据

    mainloop()

    Mask用法

    mask(标记)通常是嵌入到Text组件文本中的不可见对象。事实上,Marks是指定字符间的位置,并跟随相应的字符一起移动。

    实例:Mark事实上就是索引,用于表示位置

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置光标位置为1.2
    text.insert("here","插")

    mainloop()

    实例2:如果Mark前面的内容发生改变,Mark的位置也会跟着移动

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前光标位置为1.2
    text.insert("here","插")#执行后当前光标位置(Mark位置)变成了1.3
    text.insert("here","入")
    #text.insert("1.3","入")

    mainloop()

    实例3:如果Mark周围的文本被删除了,Mark仍然存在

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前光标位置为1.2
    text.insert("here","插")#执行后当前光标位置变成了1.3
    text.delete("1.0",END)
    text.insert("here","入")#here表示当前Mark的位置,如果Mark左边并没有数据则会插入到最左边

    mainloop()

    例4:只有mark_unset()方法可以解除Mark的封印

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前光标位置为1.2
    text.insert("here","插")#执行后当前光标位置变成了1.3
    text.mark_unset("here")

    text.delete("1.0",END)
    text.insert("here","入")#here表示当前Mark的位置

    mainloop()

    默认插入内容是插入到Mark左侧(就是说插入一个字符后,Mark向后移动了一个字符的位置)

    实例5:插入内容到Mark的右侧

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.mark_set("here","1.2")#设置当前Mark位置为1.2
    text.mark_gravity("here",LEFT)

    text.insert("here","插")#执行后当前Mark位置变成了1.3
    text.insert("here","入")#here表示当前Mark的位置

    mainloop()

    070 GUI的终极选择:Tkinter7

    实例1:添加Tags

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式
    text.tag_config("tag1",background ="yellow",foreground="red")

    mainloop()

    实例2:Tags覆盖

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式
    text.tag_add("tag2","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式

    text.tag_config("tag1",background ="yellow",foreground="red")
    text.tag_config("tag2",background ="blue")

    mainloop()
     

    实例2:降低Tag优先级

    from tkinter import *

    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式
    text.tag_add("tag2","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,,与1.14设置Tag样式

    text.tag_config("tag1",background ="yellow",foreground="red")
    text.tag_config("tag2",background ="blue")

    text.tag_lower("tag2")#降低tag2的优先级

    mainloop()

    实例3:Tags事件绑定

    from tkinter import *
    import webbrowser#导入网页模块

    def show_hand_cursor(event):
        text.config(cursor="arrow")

    def show_arrow_cursor(event):
        text.config(cursor="xterm")

    def click(event):
        webbrowser.open("http://www.fishc.com")
        
    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("link","1.7","1.16")#1.7(第一行第八列)到1.16
    #设置蓝色前景色并底部划线
    text.tag_config("link",foreground="blue",underline=True)

    #当进入绑定文本段时,鼠标样式切换为“arrow"形态
    text.tag_bind("link","<Enter>",show_hand_cursor)
    #当离开绑定文本段时,鼠标样式切换为“xterm"形态
    text.tag_bind("link","<Leave>",show_arrow_cursor)
    #当触发鼠标“左键单击”时,使用默认浏览器打开鱼C网址
    text.tag_bind("link","<Button-1>",click)

    mainloop()

    实例4:判断内容是否发生改变

    from tkinter import *
    import hashlib

    def getSig(contents):
        m = hashlib.md5(contents.encode())
        return m.digest()

    def check():#检查
        contents = text.get(1.0,END)
        if sig!=getSig(contents):
            print("警报,内容发生变动")
        else:
            print("风平浪静")
        
    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    #获取文本内容
    contents=text.get(1.0,END)

    sig = getSig(contents)

    Button(root,text="检查",command=check).pack()

    mainloop()

    实例5:查找操作(使用search()方法可以搜索Text组件中的内容)

    from tkinter import *
    import hashlib

    #将任何格式的索引号统一为元组(行,列)的格式输出
    def getIndex(text,index):
        #split这里以"."拆分字符串,将1.3拆分为字符1和3,然后通过map将字符转换为整型
        return tuple(map(int,str.split(text.index(index),".")))
        
    root = Tk()
    text = Text(root,width=30,height=5)
    text.pack()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入

    #将任何格式的索引号统一为元组(行、列)的格式输出
    start = 1.0
    while True:
        pos = text.search("o",start,stopindex=END)#从开始到结束全文搜索
        if not pos:
            break
        print("找到了,位置是:",getIndex(text,pos))
        start = pos + "+1c"#将start指向找到的字符位置的下一个字符,以便进行下一次搜索

    mainloop()

    Text组件内部有一个栈专门用于记录内容的每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。

    实例6:撤销

    from tkinter import *

    #将任何格式的索引号统一为元组(行,列)的格式输出
    def show():
        text.edit_undo()
        
    root = Tk()
    text = Text(root,width=30,height=5,undo=True)
    text.pack()
    text.insert(INSERT,"I love FishC")

    Button(root,text="撤销",command=show).pack()

    mainloop()


    实例7:每次撤销一个字符

    from tkinter import *

    def callback(event):
        text.edit_separator()

    def show():
        text.edit_undo()#执行撤回操作
        
    root = Tk()

    #autoseparators表示一次完整的操作结束后自动插入“分隔符”,此处设置为False
    text = Text(root,width=30,height=5,autoseparators=False,undo=True,maxundo=10)
    text.pack()

    text.insert(INSERT,"I love FishC!")
    text.bind('<Key>',callback)#每次有输入就插入一个“分隔符”

    Button(root,text="撤销",command=show).pack()

    mainloop()

    071 GUI的终极选择:Tkinter8

    Canvas(画布)组件

    一个可以让你随心所欲绘制界面的组件。通常用于显示和编辑图形,可以用它来绘制直线、图形、多边形,甚至是绘制其他组件。

    实例1:

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="black")
    w.pack()

    #画一条黄色的线
    w.create_line(0,50,200,50,fill="yellow")
    #画一条红色的竖线(虚线)
    w.create_line(100,0,100,100,fill="red")
    #中间画一个蓝色的矩形
    w.create_rectangle(50,25,150,75,fill="blue")

    mainloop()

    实例2:

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="black")
    w.pack()

    #画一条黄色的线(参数为其x、y轴坐标)
    line1 = w.create_line(0,50,200,50,fill="yellow")
    #画一条红色的竖线(虚线)
    line2 = w.create_line(100,0,100,100,fill="red")
    #中间画一个蓝色的矩形
    rect1 = w.create_rectangle(50,25,150,75,fill="blue")

    w.coords(line1,0,25,200,25)#将line1移动到新的坐标
    w.itemconfig(rect1,fill="red")#重新设置矩形的填充色为红色
    w.delete(line2)#删除线2

    #创建一个按钮,按下时删除所有图形
    Button(root,text="删除全部",command=(lambda x=ALL:w.delete(x))).pack()

    mainloop()

    实例3:在Canvas上显示文本

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="black")
    w.pack()

    #画一条绿色的斜线(参数为其x、y轴坐标),宽度为三个像素点
    line1 = w.create_line(0,0,200,100,fill="green",width=3)
    #画一条绿色的斜线
    line2 = w.create_line(200,0,0,100,fill="green",width=3)
    #中间画两个矩形
    rect1 = w.create_rectangle(40,20,160,80,fill="blue")
    rect2 = w.create_rectangle(60,30,140,70,fill="yellow")
    #在矩形正中(默认)显示文本,坐标为文本正中坐标
    w.create_text(100,50,text="Hadley")

    #创建一个按钮,按下时删除所有图形
    Button(root,text="删除全部",command=(lambda x=ALL:w.delete(x))).pack()

    mainloop()

    实例4:绘制椭圆

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="white")
    w.pack()

    #绘制一个虚线的矩形
    w.create_rectangle(40,20,160,80,dash=(4,4))
    #绘制椭圆,粉色填充
    w.create_oval(40,20,160,80,fill="pink")
    #在矩形正中(默认)显示文本,坐标为文本正中坐标
    w.create_text(100,50,text="Hadley")

    mainloop()
    实例5:绘制圆形

    from tkinter import *
    root = Tk()
    #创建canvas对象框,设置其宽度、高度与背景色
    w = Canvas(root,width=200,height=100,background="white")
    w.pack()

    #绘制一个虚线的矩形
    w.create_rectangle(40,20,160,80,dash=(4,4))
    #绘制圆形,粉色填充
    #w.create_oval(40,20,160,80,fill="pink")
    w.create_oval(70,20,130,80,fill="pink")
    #在矩形正中(默认)显示文本,坐标为文本正中坐标
    w.create_text(100,50,text="Hadley")

    mainloop()

    实例6:绘制多边形

    from tkinter import *
    import math as m

    root = Tk()
    w=Canvas(root,width=200,height=150,background="red")
    w.pack()
    center_x = 100
    center_y = 80
    r = 70
    points = [
        #左上角A
        center_x - int(r*m.sin(2*m.pi/5)),
        center_y - int(r*m.cos(2*m.pi/5)),
        #右上角C
        center_x + int(r*m.sin(2*m.pi/5)),
        center_y - int(r*m.cos(2*m.pi/5)),
        #左下角E
        center_x - int(r*m.sin(m.pi/5)),
        center_y + int(r*m.cos(m.pi/5)),
        #顶点D
        center_x,
        center_y - r,
        #右下角B
        center_x + int(r*m.sin(m.pi/5)),
        center_y + int(r*m.cos(m.pi/5)),
        ]
    #创建多边形方法,会自动按ACEDBA的形式连线,如果构成闭环,则会自动填充
    w.create_polygon(points,outline="green",fill="yellow")

    w.create_text(100,80,text="Hadley")

    mainloop()

    实例7:

    from tkinter import *

    root = Tk()
    w=Canvas(root,width=400,height=200,background="white")
    w.pack()

    def paint(event):#画小圆
        x1,y1 = (event.x - 1),(event.y -1)
        x2,y2 = (event.x + 1),(event.y +1)
        w.create_oval(x1,y1,x2,y2,fill="red")

    w.bind("<B1 - Motion>",paint)#画布与鼠标进行绑定
    Label(root,text="按住鼠标左键并移动,开始绘制你的理想蓝图吧。。。").pack(side=BOTTOM)

    mainloop()

    073 GUI的终极选择:Tkinter10

    Munu组件

    Tkinter提供了一个Menu组件,用于实现顶级菜单、下拉菜单和弹出菜单。

    实例1:创建一个顶级菜单(或称窗口主菜单

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()


    menubar = Menu(root)#创建一个顶级菜单
    menubar.add_command(label="Hello",command=callback)#创建一个顶级菜单对象
    menubar.add_command(label="Quit",command=root.quit)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    实例2:创建添加到主菜单上的下拉菜单

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单
    menubar = Menu(root)

    #创建下拉菜单filemenu包含内容
    filemenu=Menu(menubar,tearoff=False)#创建一个从属于menubar的子菜单(下拉菜单)filemenu
    filemenu.add_command(label="打开",command=callback)#创建一个下拉菜单对象
    filemenu.add_command(label="保存",command=callback)
    filemenu.add_separator()#插入分隔线
    filemenu.add_command(label="退出",command=root.quit)
    #创建一个顶级菜单对象“文件”,filemenu从属于这个对象(或称将filemenu添加到顶级菜单“文件”中)
    menubar.add_cascade(label="文件",menu=filemenu)

    #创建另一个下拉菜单editmenu包含内容
    editmenu=Menu(menubar,tearoff=False)#创建一个从属于menubar的子菜单(下拉菜单)editmenu
    editmenu.add_command(label="剪切",command=callback)
    editmenu.add_command(label="拷贝",command=callback)
    editmenu.add_separator()#插入分隔线
    editmenu.add_command(label="粘贴",command=callback)
    #创建一个顶级菜单对象“编辑”,editmenu从属于这个对象(或称将editmenu添加到顶级菜单“编辑”中)
    menubar.add_cascade(label="编辑",menu=editmenu)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    实例3:创建一个弹出菜单方法

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    def popup(event):
        menu.post(event.x_root,event.y_root)#在此时鼠标位置弹出显示窗口
        
    #创建一个顶级菜单menu
    menu = Menu(root,tearoff=False)

    #创建顶级菜单menu包含内容
    menu.add_command(label="撤销",command=callback)#创建一个顶级菜单对象
    menu.add_command(label="重做",command=callback)
    #创建一个框架
    frame = Frame(root,width=100,height=100)
    frame.pack()

    #将鼠标右键与popup方法绑定
    frame.bind("<Button-3>",popup)

    #显示菜单
    #root.config(menu=menu)

    mainloop()

    实例4:菜单弹出

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    def popup(event):
        menu.post(event.x_root,event.y_root)#在此时鼠标位置弹出显示窗口
        
    #创建一个顶级菜单menu
    menu = Menu(root,tearoff=True)

    #创建顶级菜单menu包含内容
    menu.add_command(label="撤销",command=callback)#创建一个顶级菜单对象
    menu.add_command(label="重做",command=callback)
    #创建一个框架
    frame = Frame(root,width=500,height=500)
    frame.pack()

    #将鼠标右键与popup方法绑定
    frame.bind("<Button-3>",popup)

    #显示菜单
    #root.config(menu=menu)

    mainloop()

    实例5:添加单选组件radiobutton和多选按钮checkbutton

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单
    menubar = Menu(root)
    #创建checkbutton关联变量
    openVar = IntVar()
    saveVar = IntVar()
    exitVar = IntVar()
    #创建下拉菜单filemenu包含内容
    filemenu=Menu(menubar,tearoff=True)#创建一个从属于menubar的子菜单(下拉菜单)filemenu
    filemenu.add_checkbutton(label="打开",command=callback,variable=openVar)#创建一个下拉菜单对象
    filemenu.add_checkbutton(label="保存",command=callback,variable=saveVar)
    filemenu.add_separator()#插入分隔线
    filemenu.add_checkbutton(label="退出",command=root.quit,variable=exitVar)
    #创建一个顶级菜单对象“文件”,filemenu从属于这个对象(或称将filemenu添加到顶级菜单“文件”中)
    menubar.add_cascade(label="文件",menu=filemenu)

    #创建radiobutton关联变量
    editVar = IntVar()
    editVar.set(1)

    #创建另一个下拉菜单editmenu包含内容
    editmenu=Menu(menubar,tearoff=True)#创建一个从属于menubar的子菜单(下拉菜单)editmenu
    editmenu.add_radiobutton(label="剪切",command=callback,variable=editVar,value=1)
    editmenu.add_radiobutton(label="拷贝",command=callback,variable=editVar,value=2)
    editmenu.add_separator()#插入分隔线
    editmenu.add_radiobutton(label="粘贴",command=callback,variable=editVar,value=3)
    #创建一个顶级菜单对象“编辑”,editmenu从属于这个对象(或称将editmenu添加到顶级菜单“编辑”中)
    menubar.add_cascade(label="编辑",menu=editmenu)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    Menubutton组件(希望菜单按钮出现在其它位置时)

    Menubutton组件是一个与Menu组件相关联的按钮,它可以放在窗口中的任意位置,并且在被按下时弹出下拉菜单

    实例1:

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单Menubutton按钮,设置为浮起显示(RAISED)
    mb = Menubutton(root,text="点我",relief=RAISED)

    mb.pack(side=RIGHT)#设置为右中显示

    #创建下拉菜单filemenu包含内容
    filemenu = Menu(mb,tearoff=False)#创建一个从属于mb的下拉菜单filemenu
    filemenu.add_checkbutton(label="打开",command=callback,selectcolor="yellow")
    filemenu.add_command(label="保存",command=callback)#创建一个下拉菜单对象"保存“
    filemenu.add_separator()
    filemenu.add_command(label="退出",command=root.quit)
    #显示菜单
    mb.config(menu=filemenu)

    mainloop()

    OptionMenu(选项菜单)组件

    选项菜单的发明弥补了Listbox组件无法实现下拉列表框的遗憾

    实例1:

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    variable = StringVar()#创建字符串变量variable
    variable.set("one")#初始值设置为"one"
    w = OptionMenu(root,variable,"one","two","three")
    w.pack()

    mainloop()

    实例2:多个选项添加到选项菜单中

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    OPTIONS = [
        "Hadley",
        "小土豆",
        "yiwofeiye",
        "RAN"
        ]

    variable = StringVar()#创建字符串变量variable
    variable.set(OPTIONS[0])#初始值设置为"one"
    w = OptionMenu(root,variable,*OPTIONS)
    w.pack()

    def callback():
        print(variable.get())

    Button(root,text="点我",command=callback).pack()

    mainloop()
     

    074  GUI的终极选择:Tkinter11

    事件绑定

    对于每个组件来说,可以通过bind()方法将函数或方法绑定到具体的事件上。当被触发的事件满足该组件绑定的事件时,Tkinter就会带着事件描述去调用handler()方法

    实例1:捕获单击鼠标位置

    from tkinter import*

    root = Tk()

    def callback(event):
        print("点击位置:",event.x,event.y)

    frame = Frame(root,width=200,height=200)
    #Button表示鼠标点击事件
    #1代表左键 2代表中间滚轮点击 3代表右键
    frame.bind("<Button-1>",callback)#按键按下时,调用callback方法
    frame.pack()

    mainloop()

    实例2:捕获键盘事件

    #捕获单击鼠标的位置
    from tkinter import*

    root = Tk()

    def callback(event):
        print("敲击位置:",repr(event.char))#打印当前按下按键的字符
        print(event.char)

    frame = Frame(root,width=200,height=200)
    #Key为键盘事件
    frame.bind("<Key>",callback)#按键按下时,调用callback方法
    frame.focus_set()#获得焦点
    frame.pack()

    mainloop()

    实例3:捕获鼠标在组件上的运动轨迹

    #当鼠标在组件内移动的整个过程均触发该事件

    from tkinter import*

    root = Tk()

    def callback(event):
        print("当前位置:",event.x,event.y)#打印当前按下按键的字符

    frame = Frame(root,width=200,height=200)
    frame.bind("<Motion>",callback)#按键按下时,调用callback方法
    frame.pack()

    mainloop()

    事件序列

    Tkinter使用一种称为事件序列的机制来允许用户定义事件,用户需要使用bind()方法将具体的事件序列与自定义的方法绑定

    Event对象(按键名keysym和按键码keycode)

    实例1:打印当前按下按键的按键名

    from tkinter import*

    root = Tk()

    def callback(event):
        print(event.keysym)#打印当前按下按键的按键名
        print(event.char)

    frame = Frame(root,width=200,height=200)
    #Key为键盘事件
    frame.bind("<Key>",callback)#按键按下时,调用callback方法
    frame.focus_set()#获得焦点
    frame.pack()

    mainloop()

    075 GUI的终极选择:Tkinter12

    Message组件

    Message(消息)组件是Label组件的变体,用于显示多行文本信息。Message组件能够自动换行,并调整文本的尺寸使其适应给定得尺寸。

    实例1:

    from tkinter import *

    root = Tk()
    w1 = Message(root,text="这是一则消息",width=100)
    w1.pack()
    w2 = Message(root,text="这是一条骇人听闻的长消息!",width=100)
    w2.pack()

    mainloop()

    Spinbox组件

    Entry组件的变体,用于从一些固定的值中选取一个。使用Spinbox组件,可以通过返回或者元组指定允许用户输入的内容。

    实例1:

    from tkinter import *

    root = Tk()

    #w = Spinbox(root,from_=0,to=10)#指定输入值为0-10
    w = Spinbox(root,value=("Hadley","小土豆","雅馨"))#指定输入
    w.pack()

    mainloop()

    PanedWindow组件

    与Frame类似,都是为组件提供一个框架,但其还允许让用户调整应用程序的空间划分

    实例1:两窗格

    from tkinter import *

    root = Tk()

    m = PanedWindow(orient = VERTICAL)#设置为上下分布
    m.pack(fill=BOTH,expand=1)#设置为框架覆盖全局

    top = Label(m,text="top pane")#顶窗格
    m.add(top)

    bottom = Label(m,text="bottom pane")#底窗格
    m.add(bottom)

    mainloop()

    实例2:三窗格

    from tkinter import *

    root = Tk()

    m1 = PanedWindow()#默认为左右分布
    m1.pack(fill=BOTH,expand=1)
    left = Label(m1,text="left pane")#左窗格
    m1.add(left)

    m2 = PanedWindow(orient=VERTICAL)
    m1.add(m2)
    top=Label(m2,text="top pane")#顶窗格
    m2.add(top)
    bottom = Label(m2,text="bottom pane")#底窗格
    m2.add(bottom)

    mainloop()

    实例3:显示“分割线”

    from tkinter import *

    root = Tk()

    #showhandle=True表示显示“手柄”
    #sashrelief=SUNKEN表示分隔线的样式设置为向下凹
    m1 = PanedWindow(showhandle=True,sashrelief=SUNKEN)
    m1.pack(fill=BOTH,expand=1)
    left = Label(m1,text="left pane")
    m1.add(left)

    m2 = PanedWindow(orient=VERTICAL,showhandle=True,sashrelief=SUNKEN)
    m1.add(m2)
    top=Label(m2,text="top pane")
    m2.add(top)
    bottom = Label(m2,text="bottom pane")
    m2.add(bottom)

    mainloop()

    Toplevel组件

    Topleve(顶级窗口)l组件类似于Frame组件,但其是一个独立的顶级窗口,通常拥有标题栏、边框等部件。通常用在显示额外的窗口、对话框和其他弹出窗口中。

    实例1:按钮按下创建一个顶级窗口

    from tkinter import *

    def create():
        top = Toplevel()#创建一个独立的顶级窗口
        top.title("FishC Demo")
        msg = Message(top,text="I love FishC.com")
        msg.pack()
        
    root = Tk()
    Button(root,text="创建顶级窗口",command=create).pack()

    mainloop()

    实例2:Toplevel的窗口设置为50%透明

    from tkinter import *

    def create():
        top = Toplevel()
        top.title("FishC Demo")
        top.attributes("-alpha",0.5)#设置为50%透明度
        msg = Message(top,text="I love FishC.com")
        msg.pack()
        
    root = Tk()
    Button(root,text="创建顶级窗口",command=create).pack()

    mainloop()

    076 GUI的终极选择:Tkinter13

    布局管理器

    布局管理器就是管理你的那些组件如何排列的家伙。Tkinter有三个布局管理器,分别是pack、grid和place

    pack:按添加顺序排列组件

    grid:按行/列形式排列组件

    place:允许程序员指定组件的大小和位置

    pack

    实例1:生成一个Listbox组件并将它填充到root窗口

    from tkinter import *

    root = Tk()
    listbox = Listbox(root)
    #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间
    #BOTH表示同时横向和纵向扩展;X表示横向;Y表示纵向
    #expand选项是告诉窗口管理器是否将父组件的额外空间也填满(任意拉伸窗口依旧会填满)

    #默认情况下pack是将添加的组件依次纵向排列
    listbox.pack(fill=BOTH,expand=True)
    for i in range(10):
        listbox.insert(END,str(i))

    mainloop()

    实例2:纵向排列,横向填充

    from tkinter import *

    root = Tk()
    #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间
    #BOTH表示同时横向和纵向扩展;X表示横向;Y表示纵向
    #expand选项是告诉窗口管理器是否将父组件的额外空间也填满

    #默认情况下pack的side属性是将添加的组件依次纵向排列
    Label(root, text="red", bg="red", fg="white").pack(fill=X)
    Label(root, text="green", bg="green", fg="black").pack(fill=X)
    Label(root, text="blue", bg="blue", fg="white").pack(fill=X)

    mainloop()

    实例3:横向排列,纵向填充

    from tkinter import *

    root = Tk()
    #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间
    #BOTH表示同时横向和纵向扩展;X表示横向;Y表示纵向
    #expand选项是告诉窗口管理器是否将父组件的额外空间也填满

    #将pack设置为横向排列
    Label(root, text="red", bg="red", fg="white").pack(side=LEFT)
    Label(root, text="green", bg="green", fg="black").pack(side=LEFT)
    Label(root, text="blue", bg="blue", fg="white").pack(side=LEFT)

    mainloop()

    grid

    使用一个grid就可以简单地实现你用很多个框架和pack搭建起来的效果。使用grid排列组件,只需告诉它你想要将组件放置的位置(行row/列column)。

    实例1:

    from tkinter import *

    root = Tk()

    #column默认值是0
    #默认情况下组件会居中显示在对应的网格里
    #Label(root,text="用户名").grid(row=0)
    #Label(root,text="密码").grid(row=1)
    #设置sticky=W使Label左对齐
    Label(root,text="用户名").grid(row=0,sticky=W)#左对齐
    Label(root,text="密码").grid(row=1,sticky=W)

    Entry(root).grid(row=0,column=1)
    Entry(root,show="*").grid(row=1,column=1)

    mainloop()

    实例2:设置rowspan与columnspan实现跨行和跨列功能

    from tkinter import *

    root = Tk()

    #column默认值是0
    #默认情况下组件会居中显示在对应的网格里
    #Label(root,text="用户名").grid(row=0)
    #Label(root,text="密码").grid(row=1)
    #设置sticky=W使Label左对齐
    #创建Label文本
    Label(root,text="用户名").grid(row=0,sticky=W)
    Label(root,text="密码").grid(row=1,sticky=W)
    #创建输入
    Entry(root).grid(row=0,column=1)
    Entry(root,show="*").grid(row=1,column=1)
    #插入Label图像
    photo = PhotoImage(file="logo.gif")
    #rowspan=2跨两行,边距5
    Label(root,image=photo).grid(row=0,column=2,rowspan=2,padx=5,pady=5)
    #columnspan=3跨三列(默认为居中显示),边距5
    Button(text="提交",width=10).grid(row=2,columnspan=3,pady=5)

    mainloop()

    place

    通常情况下不建议使用place布局管理器

    实例1:将子组件显示在父组件的正中间

    from tkinter import *

    def callback():
        print("正中靶心")
    root = Tk()
    #relx和rely指定的是子组件相对于父组件的位置,范围是(00`1.0),0.5则表示一半,正中间
    #anchor=CENTER表示正中显示
    Button(root,text="点我",command=callback).place(relx=0.5,rely=0.5,anchor=CENTER)

    mainloop()

    实例2:Button组件覆盖Label组件

    from tkinter import *

    def callback():
        print("正中靶心")
    root = Tk()

    photo = PhotoImage(file="logo_big.gif")
    Label(root,image=photo).pack()
    #relx和rely指定的是子组件相对于父组件的位置,范围是(00`1.0),0.5则表示一半,正中间
    Button(root,text="点我",command=callback).place(relx=0.5,rely=0.5,anchor=CENTER)

    mainloop()

    实例3:

    from tkinter import *

    root = Tk()

    #relx和rely指定的是子组件相对于父组件的位置,范围是(00`1.0),0.5则表示一半,正中间
    #relwidth和relheight选项指定相对父组件的尺寸
    Label(root,bg="red").place(relx=0.5,rely=0.5,relheight=0.75,relwidth=0.75,anchor=CENTER)
    Label(root,bg="yellow").place(relx=0.5,rely=0.5,relheight=0.5,relwidth=0.5,anchor=CENTER)
    Label(root,bg="green").place(relx=0.5,rely=0.5,relheight=0.25,relwidth=0.25,anchor=CENTER)

    mainloop()

    077 GUI的终极选择:Tkinter14

    Tkinter提供了三种标准对话框模块,分别是:messagebox、filedialog、colorchooser

    messagebox(消息对话框)

    实例1:askokcancel函数

    from tkinter import *

    print(messagebox.askokcancel("FishC Demo","发射核弹?"))

    mainloop()

    实例2:askquestion函数

    实例3:asiretrycancel函数

    实例4:askyesno函数

    实例5:showerror函数

    from tkinter import *

    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    print(messagebox.showerror("FishC Demo","Error!!!"))

    mainloop()

    实例6:showinfo函数

    from tkinter import *

    #options参数可设置为default、icon与parent
    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    #print(messagebox.showerror("FishC Demo","Error!!!"))
    messagebox.showinfo("Hadley","Great!!!",icon="info")

    mainloop()

    实例7:showwarning函数

    from tkinter import *

    #options参数可设置为default、icon与parent
    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    #print(messagebox.showerror("FishC Demo","Error!!!"))
    #messagebox.showinfo("Hadley","Great!!!",icon="info")
    messagebox.showwarning("Hadley","Warning!!!",icon="warning")

    mainloop()

    filedialog(文本对话框)

    当应用程序需要使用打开文件或保存文件的功能时

    实例1:

    from tkinter import *

    root = Tk()

    def callback():
        #askopenfilename函数用来打开文件
        #asksaveasfilename函数用来保存文件
        fileName = filedialog.askopenfilename()
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    实例2:限制打开文件类型

    from tkinter import *

    root = Tk()

    def callback():
        #askopenfilename函数用来打开文件
        #asksaveasfilename函数用来保存文件
        #fileName = filedialog.askopenfilename()
        #限制打开文件类型
        fileName = filedialog.askopenfilename(filetypes=[("PNG",".png"),("GIF",".gif")])
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    colorchooser(颜色选择对话框)

    颜色对话框提供一个让用户选择颜色的界面

    实例1:

    from tkinter import *

    root = Tk()

    def callback():
        #colorchooser函数用于打开颜色选择对话框
        fileName = colorchooser.askcolor()
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    对应的RGB值及其对应的16进制值

    078 Pygame:初次见面,请大家多多关照

     

    展开全文
  • JAVA框架

    千次阅读 2019-10-15 10:42:43
    :scope(singleton)单例默认值、@scope(prototype)多实例(singleton:容器在初始化时,就会创建对象(唯一的一个),支持延迟加载@Lazy、prototype:容器在初始化时,创建对象;只是在每次使用时创建对象) ...

    Spring

    Spring是一个基于IOC和AOP的结构J2EE系统的框架,对象的生命周期由Spring来管理
    IOC(Inversion Of Control):控制反转
    DI(Dependency Inject): 依赖注入
    AOP(Aspect Oriented Program):面向切面编程(实现周边功能,比如性能统计,日志,事务管理等)
    方法
    新建对象的set和get方法
    新建bean configuration … - applicationContext.xml、并在这个xml配置文件(SpringIOC容器)中使用Bean创建对象并且给对象及其属性赋值

        <bean id="id(唯一)" class="包名.类名">
            <property name="属性名" value="属性值" />
            <property name="属性名" ref="属性映射(对应对象的id值)" />
        </bean>
    

    然后通过主函数中 ApplicationContext取出对象

     public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            类 实例名 = (类名) context.getBean("id");
    

    三种依赖注入方式:set方法注入(同上)、构造方法注入( < constructor-arg value(ref)=“value” type=“type” index=“0” name=“属性名”>< /constructor-arg>)、p命名空间注入(引入p命名空间、简单类型:p:属性名=“属性值”、引用类型(除了String外):p:属性名-ref=“引用的id”)
    IOC容器赋值:如果是简单类型(8个基本+String),value; 如果是对象类型,ref=“需要引用的id值”,因此实现了对象与对象之间的依赖关系、赋值null< property name=“name” > < null/>< /property>、赋值空值< property name=“name” > < value>< /value> < /property>、集合类型赋值proprety中使用< set> < list> < array>等,可以混着用。注意< map> 中使用< entry> < key>< value>
    value与< value>注入方式的区别:value必须使用双引号,不能使用type属性,只能采用实体引用处理特殊符号,后者可以使用<![CDATA[ ]]>标记处理特殊符号
    自动装配:bean中的autowire只适用于ref类型,autowire=“byName|byType|constructor|no”、实现全局自动装配default-autowire=“byName|by…”、注解形式的自动装配@Autowired、默认是byType、 @Qualifier(“id”)、改为byName
    使用注解
    ApplicationContext中增加扫描包

    < context:component-scan base-package="包名">
    </ context:component-scan>
    

    对应包中加入注解:@Component(“id”)细化:(dao层注解:@Repository)(service层注解:@Service)(控制器层注解:@Controller)
    使用注解实现声明式事务:实现数据库的事务
    配置:配置数据库相关、配置事务管理器txManager、增加对事务的支持;使用:方法明前增加注解@Transactional并增加其属性和方法
    AOP:前置通知、后置通知、环绕通知、异常通知
    实现方式:实现类、实现接口、注解、配置
    接口形式AOP:实现接口并重写方法(前置MethodBeforeAdvice)(后置AfterReturningAdvice)(环绕MethodInterceptor)(异常ThrowsAdvice)、 ApplicationContext中加入业务类方法的对象和通知类方法的对象、增加配置:

    < aop:config>
    		<!-- 切入点(连接线的一端:业务类的具体方法) -->
    		< aop:pointcut expression="execution(public * 业务类名.方法名(参数类型))"   id="poioncut"/>
    		<!-- (连接线的另一端:通知类) -->
            < aop:advisor advice-ref="通知类名"  pointcut-ref="poioncut" />
    </aop:config>
    

    环绕通知:可以获取目标方法的全部控制权,目标方法的一切信息可以通过invocation参数获取到,底层是通过拦截器实现的、环绕通知类实现MethodInterceptor接口和invoke方法, result = invocation.proceed() ;中result获得返回值,invocation.proceed()实现业务类方法执行,try中实现前置通知和后置通知、catch中捕获异常并实现异常通知
    注解形式AOP:@Component引入注解识别@Aspect修饰类形成通知类 @Before(pointcut=“execution(public * 业务类名.方法名(参数类型))”)修饰方法形成通知前置方法、后置@AfterReturning(returning=“returningValue”)、异常@AfterThrowing(throwing=“e”)、环绕@Around、方法中的参数可以使用JoinPoint jp、返回值参数Object returningValue、返回异常参数NullPointerException e、环绕通知中参数使用ProceedingJoinPoint jp
    配置:增加扫描包、开启AOP注解代理< aop:aspectj-autoproxy>< /aop:aspectj-autoproxy>
    配置形式AOP:写通知类logSchema及其通知方法,并将通知类和业务类加入到IOC容器中,applicationContext配置:

    <aop:config>
    		<!-- 切入点(连接线的一端:业务类的具体方法) -->
    		<aop:pointcut expression=""   id="pcShema"/>
    		<!-- 原方法(连接线的另一端:通知类)<aop:advisor advice-ref="logSchea"  pointcut-ref="pcShema" />-->
    		 <!-- schema方式 -->
    		 <aop:aspect ref="logSchema">
    		 <!-- 连接线:连接业务和通知before -->
    		 <aop:before method="before" pointcut-ref="pcShema"/>
    		 <!-- 连接线:连接业务和通知afterReturning -->
    		 <aop:after-returning method="afterReturning" returning="returnValue" pointcut-ref="pcShema"/>
    		 <!-- 连接线:连接业务和通知Exception -->
    		 <aop:after-throwing method="Exception" pointcut-ref="pcShema" throwing="e"/>
    		 <!-- 环绕通知 -->
    		 <aop:around method="around" pointcut-ref="pcShema" />
             </aop:aspect>
    </aop:config>
    

    WEB项目整合Spring
    IOC初始化:当服务启动时(tomcat),通过监听器将SpringIOC容器初始化一次(该监听器 spring-web.jar已经提供)web.xml配置、推荐方法:

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
      			classpath:applicationContext.xml,
      			<!--.默认约定的位置:WEB-INF/applicationContext.xml-->
      			classpath:applicationContext-*.xml
      	</param-value>
    </context-param>
    

    将applicationContext分层:cotroller、service、dao并分别实例化和依赖注入
    实现Web容器和IOC容器的整合(手动方式):从IOC容器中求出实例对象、一般在servlet初始化中实现

    public void init() throws ServletException  {
    		//ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext-Service");
    		ApplicationContext context=WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    		studentService=(IStudentService)context.getBean("studentService");
    		<!--类 实例名 = (类名) context.getBean("id");-->
    	}
    

    最后完善web项目

    Spring进阶

    注解形式存放bean
    (配置类)必须有@Configuration注解

    //存bean
    @bean
    //取bean
    ApplicationContext context  = new AnnotationConfigApplicationContext(配置类.class) ;
    

    三层组件:(@Controller、@Service、@Repository -> @Component)+配置文件或者注解扫描器@component-scan(只对三层组件负责)、扫描器指定规则(FilterType(ANNOTATION,ASSIGNABLE_TYPE,CUSTOM))
    非三层组件(转换器): @Bean+方法的返回值、@import(直接编写到@Import中且id值是全类名、——自定义ImportSelector接口的实现类,通过selectimports方法实现(方法的返回值就是要纳入IoC容器的Bean),并且告知程序自己编写的实现类@Import({Orange.class,MyImportSelector.class})、——编写ImportBeanDefinitionRegistrar接口的实现类,重写方法
    @Import({Orange.class,MyImportSelector.class,ImportBeanDefinitionRegistrar.class})并且告知程序自己编写的实现类)、FactoryBean(工厂Bean、准备bean实现类和重写方法,注册bean注册到@Bean中)
    bean的作用域:scope(singleton)单例默认值、@scope(prototype)多实例(singleton:容器在初始化时,就会创建对象(唯一的一个),支持延迟加载@Lazy、prototype:容器在初始化时,不创建对象;只是在每次使用时创建对象)
    条件注解:可以让某一个Bean在某些条件下加入Ioc容器,其他情况下不加入容器,通过实现condition接口
    Bean的生命周期:创建(new …)、初始化(赋初值)、 …、销毁
    @Bean+返回值方式

    @Bean(value="stu",initMethod = "myInit",destroyMethod = "myDestroy") 
    

    三层组件功能性注解方式:将响应组件加入@Component(value="")注解、 给初始化方法加@PostConstruct、给销毁方法加@PreDestroy
    三次组件接口方法:InitializingBean初始化、DisposableBean 销毁并实现其中的方法
    接口BeanPostProcessor:拦截了所有中容器的Bean,并且可以进行bean的初始化 、销毁
    BeanFactoryPostProcessor:bean被实例化之前
    BeanDefinitionRegistryPostProcessor:bean被加载之前
    具体顺序:BeanDefinitionRegistryPostProcessor(a) ->加载bean->BeanFactoryPostProcessor(b)->实例化bean->BeanPostProcessor
    自动装配
    @Autowired根据类型自动注入、结合@Qualifier("")根据名字注入、默认值@primary、@Autowired(required=false)修改未匹配为null
    三层组件:如果@Autowired在属性前标注则不调用set方式;如果标注在set前面则调用set方式;不能放在方法的参数前
    Bean+返回值:@Autowired可以在方法的参数前(也可以省略)、方法前(如果只有一个有参构造方法则@Autowired也可以省略)
    @Resource根据名字自动注入,没有名字然后根据类型
    @Inject默认根据类型匹配(引入jar包)
    底层组件:实现Aware的子接口,从而获取到开发的组件对象,对对象进行操作
    环境切换:bean前@Profile说明条件注入
    激活
    1.运行环境中设置参数-Dspring.profiles.active=@Profile环境名
    2.硬编码:IoC容器在使用时必须refresh(),需要设置保存点|配置类的编写处

    //注解方式
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext() ;
    ConfigurableEnvironment environment = (ConfigurableEnvironment)context.getEnvironment();
    environment.setActiveProfiles("@Profile环境名");
    //保存点
    context.register(配置类名.class);
    context.refresh();
    

    监听器:可以监听事件,监听的对象必须是ApplicationEvent自身或其子类/子接口
    1.监听类实现ApplicationEvent接口
    2.监听类注解形式申明是监听方法

    @Component
    public class MyListener {
    //本方法是一个监听方法
    @EventListener(classes = {ApplicationEvent.class})
    public void myListenerMethod(ApplicationEvent event){
    System.out.println("--0000000--------"+event);
        }
    }
    

    SpringMVC

    SpringMVC构建Web应用程序的全功能MVC模块
    配置与使用
    JSP发出请求
    将Servlet的访问通过web.xml配置文件拦截请求,交给SpringMVC处理、快速创建:alt+/ dispatcherServlet

    	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
    	<servlet>
    		<servlet-name>springDispatcherServlet</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<!--默认路径/WEB-INF/springDispatcherServlet-servlet.xml-->
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>classpath:springmvc.xml</param-value>
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    
    	<!-- Map all requests to the DispatcherServlet for handling -->
    	<servlet-mapping>
    		<servlet-name>springDispatcherServlet</servlet-name>
    		<url-pattern>/</url-pattern>
    	</servlet-mapping>
    

    springmvc.xml中配置扫描包和视图解析器(用于解析返回值地址并通过请求转发实现页面跳转)

    	<!--配置视图解析器(InternalResourceViewResolver)  -->
    	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="prefix" value="/views/"></property>
    		<property name="suffix" value=".jsp"></property>
    	</bean>
    	<!--view-name会被视图解析器加上前缀和后缀  -->
    

    控制器中添加注解@controller(实现扫描包扫描)@RequestMapping(映射匹配类、类之前)、@RequestMapping(value,method,params)(映射匹配方法、方法之前)(可以增加个属性和约束条件对请求参数进行约束)、@PathVariable获取动态参数
    REST风格:GET :查、POST :增、DELETE :删、PUT :改
    springmvc实现给普通浏览器增加 put|delete请求方式 :put|post请求方式的步骤:
    1.web.xml增加HiddenHttpMethodFilte过滤器

    <filter>
    			<filter-name>HiddenHttpMethodFilte</filter-name>
    			<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
    			<filter-name>HiddenHttpMethodFilte</filter-name>
    			<url-pattern>/*</url-pattern>
    </filter-mapping>
    

    2.表单提交必须是post方法且通过隐藏域的value值设置实际的请求方式DELETE|PUT (< input type=“hidden” name="_method" value=“DELETE”/>)
    3.控制器实现提交方式约束匹配、参数获取和功能执行
    处理数据模型:跳转时需要带数据和页面(ModelAndView、ModelMap、Map、Model -数据放在了request作用域)
    ModelAndView:

    public ModelAndView testModelAndView() {
    ModelAndView mv = new ModelAndView("jsp");
    mv.addObject("属性名", 属性值或对象);
    return mv;}
    

    ModelMap:

    public String testModelMap(ModelMap mm) {
    mm.put("属性名", 属性值或对象);
    return "jsp";}
    

    Map:

    public String testMap(Map<String,Object> m) {
    m.put("属性名", 属性值或对象);
    return "jsp";}
    

    Model:

    public String testMode(Model model) {
    model.addAttribute("属性名", 属性值或对象);
    return "jsp";}
    

    @SessionAttributes(value\type):实现将数据放在request中的同时也将数据放在session中
    @ModelAttribute:通过@ModelAttribute修饰的方法,会在每次请求前先执行;并且该方法的参数map.put()可以将对象放入即将查询的参数中,可以进行属性值的修改
    视图的顶级接口:Views
    视图解析器的顶级接口:ViewsResolver
    国际化:针对不同地区、不同国家进行不同的显示 (创建资源文件、配置springmvc.xml并加载资源文件、通过jstl使用国际化)

    <!--加载国际化资源文件-->
    	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    	<property name="basename" value="i18n"></property>
    	</bean>
    

    请求方法改变:return “redirect:/views/success.jsp”;
    直接跳转(不通过控制器):< mvc:view-controller path=“handle/a” view-name=“success” /> < mvc:annotation-driven></ mvc:annotation-driven>
    静态资源访问:(MVC交给Servlet处理)加入配置:

    <!--该注解会让springmvc:接收一个请求,并且该请求没有对应@requestmapping时,将该请求交给服务器默认的servlet去处理(直接访问)-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    <mvc:annotation-driven></mvc:annotation-driven>
    

    类型转换:Spring自带一些常见的类型转换器、自定义类型转换器MyConverter(编写自定义类型转换类并引入接口Converter、配置将MyConverter加入到springmvc中、测试转换器)@RequestParam(“name”)是触发转换器的桥梁
    格式化:springMVC配置数据格式化注解所依赖的bean+通过注解使用@DateTimeFormat(pattern=“yyyy-MM-dd”)@NumberFormat(parttern="###,#") 、错误消息处理(参数BindingResult result返回前面参数的错误信息,可以放在map里面传递到前端打印)、数据检验:JSR303和Hibernate Validator(jar包+配置(自动加载一个LocalValidatorFactoryBean类)+注解(给校验的对象前增加@Valid))
    JSON:@ResponseBod修饰的方法,会将该方法的返回值以一个json数组的形式返回给前台
    文件上传:配置MultipartResolver将其加入springIOC容器(可以设置属性)、处理方法中(参数@RequestParam(“file”) MultipartFile file获取前端传过来的文件数据)根据IO方法写输入输出流
    拦截器:原理和过滤器原理相同(编写拦截器实现HandlerInterceptor接口、处理请求响应和渲染过程+将自己写的拦截器配置到springmvc中(< mvc:interceptor>))
    异常处理:如果一个方法用于处理异常,并且只处理当前类中的异常:@ExceptionHandler({Exception.class});如果一个方法用于处理异常,并且处理所有类中的异常: 类前加@ControllerAdvice、 处理异常的方法前加@ExceptionHandler;自定义异常显示页面@ResponseStatus;
    通过配置实现的异常处理:SimpleMappingExceptionResolver

    <bean  class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    		<!-- 如果发生异常,异常对象会被保存在  exceptionAttribute的value值中;并且会放入request域中;异常变量的默认值是exception-->
    		<!--<property name="exceptionAttribute" value="exception"></property>-->
    			<property name="exceptionMappings">
    					<props>
    						<!-- 相当于catch(ArithmeticException exception){ 跳转:error页面 } -->
    						<prop key="java.lang.ArithmeticException">error</prop>
    						<prop key="java.lang.NullPointerException">error</prop>
    					</props>
    			</property>
    </bean>
    

    Mybatis

    Mybatis可以简化JDBC操作和实现数据的持久化
    ORM:Object Relational Mapping开发人员 像操作对象一样操作数据库表
    SqlSessionFactory -> SqlSession ->StudentMapper ->CRUD
    配置与使用
    引入mybatis-jar包和数据库jar包
    配置mybatis
    conf.xml:配置数据库信息和需要加载的映射文件(运行环境默认是development、可以通过build的第二参数指定数据库环境)

    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <!--如果使用的事务方式为jdbc,则需要手工commit提交,即session.commit()-->
    <dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
    </configuration>
    

    表 - 类
    映射文件xxMapper.xml :增删改查标签< select>

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="org.mybatis.example.BlogMapper">
    <select id="selectBlog" resultType="Blog" parameterType="int>
    <!--parameterType:输入参数的类型,resultType:返回类型、输入参数parameterType 和输出参数resultType,在形式上都只能有一个、如果输入参数:是简单类型(8个基本类型+String)是可以使用任何占位符,#{xxxx}、如果是对象类型,则必须是对象的属性#{属性名}-->
    select * from Blog where id = #{id}
    </select>
    </mapper>
    

    测试类
    session.selectOne(“需要查询的SQL的namespace.id”,“SQL的参数值”)

    public static void main(String[] args) throws IOException {
    		//加载MyBatis配置文件(为了访问数据库)
    		Reader reader = Resources.getResourceAsReader("conf.xml") ;
    		SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader) ;
    		//session - connection
    		SqlSession session = sessionFactory.openSession() ;
    		String statement = "org.lanqiao.entity.personMapper.queryPersonById" ;
    		Student person = session.selectOne( statement,1 ) ;
    		System.out.println(person);
    		session.close(); 
    

    mapper动态代理方式CRUD:省略掉statement,即根据约定直接可以定位出SQL语句
    配置环境:mybatis.jar/ojdbc.jar、conf.xml、mapper.xml
    新建类和方法实现接口必须遵循以下约定:
    1.方法名和mapper.xml文件中标签的id值相同
    2.方法的输入参数和mapper.xml文件中标签的parameterType类型一致 (如果mapper.xml的标签中没有parameterType,则说明方法没有输入参数)
    3.方法的返回值和mapper.xml文件中标签的resultType类型一致(无论查询结果是一个还是多个(student、List< Student>),在mapper.xml标签中的resultType中只写 一个(Student);如果没有resultType,则说明方法的返回值为void)
    4.namespace的值就是接口的全类名( 接口 - mapper.xml 一 一对应)
    5.测试中使用

    T t = session.getMapper(接口.class) ;
    返回类 返回值 = t.接口中的方法(参数) ;//接口中的方法->SQL语句
    StudentMapper studentMapper = session.getMapper(StudentMapper.class) ;
    studentMapper.方法();
    

    6.优化:将配置信息单独放入db.properties文件中然后再动态引入、MyBatis全局参数在conf.xml中设置、设置别名
    类型转换器(自定义):实现java类型和jdbc类型的相互转化
    1.创建转换器:实现TypeHandler接口或者继承BaseTypeHandler
    2.配置conf.xml< typeHandlers>和mapper.xml与类对应(resultMap可以实现类型转换和属性-字段的映射关系)
    输入参数:parameterType(简单类型:8个基本类型+String、.对象类型)、取值方法(#{}、${}都可以获取对象值和嵌套类型对象)(不同之处:简单类型的标识符、#{}会给String类型加上单引号、#{}可以防止sql注入)、参数类型为HashMap(用map中key的值匹配占位符#{stuAge},如果匹配成功就用map的value替换占位符)、参数为多个数据(可以使用[arg3, arg2, arg1, arg0, param3, param4, param1, param2]、在接口中通过@Param(“sNo”) 指定sql中参数的名字)
    输出参数:resultType、resultMap(实体类的属性、数据表的字段: 类型、名字不同时、除此之外:实体类属性和数据字段不匹配时可以使用resultType+HashMap)、(输出类型为简单类型、对象类型、集合类型(resultType依然写集合的元素类型)、hashmap类型(本身是一个集合,可以存放多个元素))
    输出参数为Hashmap

    //根据@MapKey("stuNo")知道 Map的key是stuNo
        @MapKey("STUNO")  //oracle的元数据(字段名、表名 )都是大写
        HashMap<Integer,Student> queryStudentsByHashMap();
    //程序根据select的返回值 知道map的value就是 Student ,
        <select id="queryStudentsByHashMap" resultType="HashMap">
             select stuNo ,stuName ,stuAge  from student
        </select>
    

    储存过程:数据库新建储存过程、Mapper中使用CALL调用存储过程(通过statementType="CALLABLE"设置SQL的执行方式是存储过程、存储过程的输入参数需要通过HashMap来指定)、使用过程(通过hashmap的put方法传入输入参数的值、通过hashmap的Get方法获取输出参数的值。)
    动态sql语句:where-if(只能处理第一个有效的and)

    <select id="queryStuByNOrAWishSQLTag" 	 parameterType="student"	resultType="student" >
    		select stuno,stuname,stuage from student
    		<where>
    			<!-- <if test="student有stuname属性 且不为null"> -->
    			<if test="stuName !=null and stuName!=''  "> 
    				and stuname = #{stuName}
    			</if>
    			<if test="stuAge !=null and stuAge!=0  "> 
    				 and stuage = #{stuAge}
    			</if>
    		</where>
    	</select>
    

    foreach:< foreach>迭代的类型:数组、对象数组、集合、属性(包括属性类)、(简单类型的数组:在mapper.xml中必须用array代替该数组、集合类型的数组:必须用list代替该集合、对象数组:必须用object代替该集合、使用时用对象.属性)

    	<select id="queryStudentsWithNosInGrade"  parameterType="grade" resultType="student">
    	  	select * from student 
    	  	<where>
    	  		 <if test="stuNos!=null and stuNos.size>0">
    	  		 	<foreach collection="stuNos" open=" and  stuno in (" close=")" 
    	  		 		item="stuNo" separator=",">   
    	  		 		#{stuNo}
    	  		 		<!--此时stuNo为对象的属性,本身是一个集合-->
    	  		 	</foreach>
    	  		 </if>
    	  	</where>
    	</select>
    

    refid:(提取并引用sql语句)
    trim:增删改中有效使用

    //添加where并处理拼接sql中开头或结尾的第一个和最后一个and
    <trim prefix="where" prefixOverrides="and" suffixOverrides="and">
    

    关联查询:一对一(业务扩展类核心:用resultType指定类的属性包含多表查询的所有字段、resultMap:通过属性成员建立起对应关系,对象成员使用 association映射、javaType指定该属性的类型(一对一:association,一对多:collection))一对多:resultMap:通过属性成员建立起对应关系,对象成员使用 collection映射、ofType指定该属性中元素的类型
    日志:通过日志信息,详细的阅读mybatis执行情况
    log4j.jar包、开启日志conf.xml、编写配置日志输出文件log4j.properties

    <settings>
    <!-- 开启日志,并指定使用的具体日志 -->
    <setting name="logImpl" value="LOG4J"/>
    </settings>
    

    延迟加载:实现部分查找和懒加载
    开启延迟加载conf.xml、配置mapper.xml(查询对象的sql是通过select属性指定的关联mapper,并且通过column指定外键名)、如果增加了mapper.xml,要修改conf.xml配置文件(将新增的mapper.xml加载进去)

    <settings>
    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 关闭立即加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    

    查询缓存
    一级缓存:MyBatis默认开启一级缓存,如果用同样SqlSession对象查询相同的数据,则只会在第一次查询时向数据库发送SQL语句,并将查询的结果 放入到SQLSESSION中(作为缓存在);后续再次查询该同样的对象时,则直接从缓存中查询该对象即可(即省略了数据库的访问)、commit语句会清理数据库缓存
    二级缓存:只要产生的Mapper对象来自于同一namespace,则这些对象共享二级缓存
    conf.xml中配置二级缓存< setting name=“cacheEnabled” value=“true”/>
    mapper.xml中声明开启 < cache/>
    Mapper的对应类、对应类的父类和级联属性都要需要实现序列化接口Serializable
    触发将对象写入二级缓存的时机为SqlSession对象的close()方法
    禁用二级缓存:select标签中 useCache=“false”
    清理二级缓存:select标签中 flushCache="true"或者 其他操作的commit(),不能用查询的commit();
    ehcache二级缓存整合:jar包、编写ehcache配置文件 Ehcache.xml、Mapper.xml中开启

    <cache  type="org.mybatis.caches.ehcache.EhcacheCache">
    <property name="maxElementsInMemory" value="2000"/>
    <property name="maxElementsOnDisk" value="3000"/>
    </cache>
    

    逆向工程:表、类、接口、mapper.xml四者密切相关,根据表生成其他三个
    jar包:mybatis-generator-core.jar、mybatis.jar、ojdbc.jar
    逆向工程的配置文件generator.xml
    执行

    public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
            //配置文件
    		File file = new File("src/generator.xml") ;
    		List<String> warnings = new ArrayList<>();
    		ConfigurationParser cp = new ConfigurationParser(warnings);
    		Configuration config = cp.parseConfiguration(file);
    		DefaultShellCallback callBack = new DefaultShellCallback(true);
    		//逆向工程的核心类
    		MyBatisGenerator generator = new MyBatisGenerator(config, callBack,warnings  );
    		generator.generate(null);
    	}
    

    增加conf.xml和db.properties文件
    使用query by CriteriaQBC

    Mybatis进阶

    数据库版本切换
    conf.xml切换environment并配置Provider别名

    <!-- 配置数据库支持类-->
    <databaseIdProvider type="DB_VENDOR">
            <property name="MySQL" value="mysql" />
            <property name="Oracle" value="oracle" />
    </databaseIdProvider>
    

    mapper.xml写不同数据SQL语句,配置databaseId=“Provider别名”

    <select id="queryStudentByNoWithONGL" parameterType="student" resultType="student" databaseId="oracle">
    

    注解方式
    将sql语句写在接口的方法上@Select("")
    将接口的全类名写入< mapper>,或者批量写入

    <mappers>
    	<!--以下可以将com.yanqun.mapper 包中的注解接口和xml全部一次性引入 -->
        <package name="com.yanqun.mapper" />
    </mappers>
    

    增删改返回值可以是void、Integer、Long、Boolean,都不需要在mapper.xml中设置返回值类型
    事务提交

    //手动提交:
      sessionFactory.openSession();
      session.commit();
    //自动提交:每个dml语句 自动提交
      sessionFactory.openSession(true);
    

    自增语句

    //mysql设置自增数据为Integer类型的null,设置返回的自增数据,实现回写
    useGeneratedKeys="true" keyProperty="number"
    //oracle通过序列模拟实现(before和after方式)
    

    鉴别器 : 对查询结果进行分支处理: 如果是a年级则真名,如果b年级显示昵称,由于字段不一致需要在resultMap中写

    <discriminator javaType="string"  column="gname">
        <case value="a" resultType="com.yanqun.entity.Student" >
             <result  column="sname" property="stuName"/>
        </case>
        <case value="b" resultType="student">
             <result  column="nickname" property="stuName"/>
        </case>
    </discriminator>
    

    架构分析
    接口层:广义接口(增删改查)
    数据处理层:参数处理,sql解析与执行,处理结果
    框架支撑层:事务管理,缓存机制,连接池管理
    引导层:XML配置,注解方式
    源码分析
    获取SqlSessionFactory对象
    (通过parseConfiguration()在configuration标签设置了properties、settings、environments等属性标签)(所有的配置信息放在Configuration对象中)(解析所有的Mapper.xml文件(分析其中的增删改查标签)成MappedStatement对象并放在Configuration中)(SqlSessionFactory对象 ->DefaultSqlSessionFactory ->Configuration ->包含了一切配置信息)
    获取SqlSession对象
    (根据不同的类型execType,产生不同的 Executorconfiguration.newExecutor(tx, execType) ->SimpleExecutor、通过装饰模式将刚才产生的executor包装成一个更加强大的executor:executor = (Executor) interceptorChain.pluginAll(executor)、返回DefaultSqlSession(configuration,executor,事务))
    (SqlSession ->openSession()->openSessionFromDataSource()->DefaultSqlSession对象->执行SQL)
    获取XxxMapper对象
    (代理接口中的方法、mapper.xml中的< select>等标签)(SqlSession(configuration,executor,事务)、代理接口的对象(MapperInterface)、methodCache(存放查询缓存,底层是CurrentHashMap))
    执行< select>等标签中定义的SQL语句
    (MyBatis底层在执行CRUD时 可能会涉及到四个处理器:处理对象ParameterHandler的处理器:StatementHandler、处理参数的处理器:ParameterHandler、类转化器处理器:TypeHandler、处理结果集的处理器:ResultSetHandler)(动态代理:MapperProxy对象、执行增删改查->MapperProxy/invoke()–>InvocationHandler :JDK动态代理接口)(实际调用增删改查的方法:mapperMethod.execute(sqlSession,args) )(处理增删改查方法的参数:method.convertArgsToSqlCommandParam(args))(boundSql :将我们写的SQL和参数值进行了拼接后的对象,即最终能被真正执行的SQL)(通过Executor执行sql语句、底层执行方法:PreparedStatement的execute())
    自定义插件
    四大核心对象:都涉及到了拦截器用于增强并且包含了该增强操作
    StatementHandler、ParameterHandler、ResultSetHandler、Executor
    步骤
    编写拦截器

    //目標方法target
            Object target = invocation.getTarget();
    //目标对象target的包装后的产物MetaObject
            MetaObject metaObject = SystemMetaObject.forObject(target);
    //metaObject.getValue获取参数
            Object value = metaObject.getValue("parameterHandler.parameterObject");
    //metaObject.setValue设置参数
            metaObject.setValue("parameterHandler.parameterObject",2);
    //放行
            Object proceed = invocation.proceed();
            return proceed;
    

    编写签名注解

    @Intercepts({
    //@Signature(type = StatementHandler.class , method ="query",args = {Statement.class, ResultHandler.class})
    @Signature(type = StatementHandler.class , method ="parameterize",args = {Statement.class})
    })
    

    conf.xml配置

    <plugins>
            <plugin interceptor="com.yanqun.my.interceptors.MyInterceptor">
                <property name="name" value="zs"/>
                <property name="age" value="23"/>
            </plugin>
    </plugins>
    

    批量操作:预编译一次sessionFactory.openSession(ExecutorType.BATCH)、不推荐拼接SQL方式
    分页插件:PageHelper
    引入jar(jsqlparser-0.9.5.jar、pagehelper-5.1.8.jar)
    配置conf.xml插件
    执行PageHelper.startPage(当前页, 每页数据)
    使用页面信息(PageInfo、Page对象)
    插件:Mybatis-Plus、通用Mapper

    Maven

    Maven是一个基于Java平台的自动化构建工具–将java、js、jsp等各个文件进行筛选、组装,变成一个可以直接发布的项目
    http://www.mvnrepository.com
    清理:删除编译的结果,为重新编译做准备
    编译:java->class
    测试:针对于项目中的关键点进行测试,亦可用项目中的测试代码去测试开发代码;
    报告:将测试的结果进行显示
    打包:将项目中包含的多个文件 压缩成一个文件, 用于安装或部署:(java项目-jar、web项目-war)
    安装:将打成的包放到本地仓库,供其他项目使用
    部署:将打成的包放到服务器上准备运行
    下载配置maven
    使用maven
    mvn compile 只编译main目录中的java文件
    mvn test 测试
    mvn package 打成jar/war
    mvn install 将开发的模块放入本地仓库供其他模块使用(放入的位置是通过gav决定)
    mvn clean 删除target目录(删除编译文件的目录)
    运行mvn命令必须在pom.xml文件所在目录
    目录结构

    -src				
    	--main		          程序功能代码
    		--java		      java代码  
    		--resources       资源代码、配置代码
    	--test			      测试代码
    		--java			
    		--resources	
    pom.xml                   项目对象模型
    

    POM(项目对象模型):Maven工程的基本工作单元XML文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖

    	<groupId>域名翻转.项目名</groupId>
    	<groupId>org.lanqiao.maven</groupId>
    
    	<artifactId>子模块名</artifactId>
    	<artifactId>HelloWorld</artifactId>
    
    	<version>版本号</version>
    	<version>0.0.1-SNAPSHOT</version>
    

    依赖
    在maven项目中如果要使用一个当时不存在的Jar或模块,则可以通过依赖实现(例如依赖测试类)

    <dependency>
    	<groupId>junit</groupId>
    	<artifactId>junit</artifactId>
    	<version>4.0</version>
    	<scope>test</scope>
    </dependency>
    

    依赖排除:

    <exclusions>
    <exclusion>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    </exclusion>
    </exclusions>
    

    依赖关联:
    A.jar->B.jar->C.jar
    要使 A.jar ->C.jar:当且仅当 B.jar依赖于C.jar的范围是compile
    依赖原则:路径最短原则(防止冲突)
    路径相同距离:同一个pom文件后覆盖前、不同pom文件前覆盖后
    统一编号和版本:

    <properties>  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <xx>版本号</xx>
    </properties>  
     //使用动态版本
     <version>${xx} </version>
    

    生命周期
    clean lifecycle :清理 pre-clean、clean、post-clearn
    default lifecycle :默认(常用)
    site lifecycle:站点 pre-site、site、post-site site-deploy
    继承
    父->子工程,可以通过父工程 统一管理依赖的版本
    父工程pom方式打包
    在父工程pom中编写依赖

    <dependencyManagement>
      	<dependencies>
      		<dependency>
    

    给当前工程继承父工程

    <parent>
    <!-- 1加入父工程坐标gav -->
    <groupId>org.lanqiao.maven</groupId>
    <artifactId>B</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!-- 2当前工程的Pom.xml到父工程的Pom.xml之间的相对路径 --> 
    <relativePath>../B/pom.xml</relativePath>
    </parent>
    

    在子类中需要声明使用哪些父类的依赖

    <dependency>
    <!-- 声明:需要使用到父类的junit(只需要ga) -->
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    </dependency>
    

    聚和
    聚合可以将拆分的多个子工程合起来共同操作(前置关联工程的install操作)
    新建总工程中配置:只能配置在打包为pom方式的maven中

    <modules>
    <!--项目的根路径  -->
    <module>../Maven1</module>
    <module>../Maven2</module>
    </modules>
    

    配置完聚合之后,操作总工程会自动操作改聚合中配置过的工程
    部署Web
    新建Maven项目并打war包
    pom中配置servlet-api

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    

    补全路径名:webapp->WEB-INF->web.xml
    实际开发中开发人员将自己的项目开发完毕后打成war包(package) 交给实施人员去部署

    SpringBoot

    springboot可以快速开发微服务模块,springboot将各个应用/三方框架设置成了一个个“场景”starthttps://spring.io可以自动生成SpringBoot的文件项目
    准备JAVA和MAVEN的环境、实现springboot开发工具、目录结构resources(static:静态资源(js css 图片 音频 视频)
    、templates:模板文件(模版引擎freemarker ,thymeleaf;默认不支持jsp)、application.properties: 配置文件对端口号等服务端信息进行配置)
    注解
    @SpringBootApplication:springboot的主配置类
    @Configuration:表示“配置类”:该类是一个配置类 、自动纳入Spring容器(@Component)
    @EnableAutoConfiguration:使spring boot可以自动配置:spring boot在启动时,就会将该包及所有的子包全部纳入spring容器,会根据META-INF/spring.factories找到相应的三方依赖,并将这些依赖引入本项目、Xxx_AutoConfiguration会实现自动装配并给予默认值
    @SpringBootConfiguration:自己代码的自动配置
    @ConditionalOnXxx:当这些条件都满足时此配置自动装配生效(手工修改改 自动装配:XxxProperties文件中的prefix.属性名=value
    配置文件
    application.properties : k=v,或行内写法(k: v,[Set/List/数组] {map,对象类型的属性},并且 []可省,{}不能省)
    application.yml:k:空格v、通过垂直对齐指定层次关系、默认可以不写引号 (双引号会将其中的转义符进行转义)
    @Component:将此类型加入Javabean
    @ConfigurationProperties(prefix=“student”):根据前缀名实现该类型自动装配,同yml文件相匹配
    @Autowired:自动装配从bean中取值
    @Value(""):实现单个值注入,可以和全局配置文件实现互补
    @Validated:开启jsr303数据校验的注解
    @Test:此类型为测试类
    @ImportResource(locations={“classpath:spring.xml”}):识别spring.xml配置文件
    @bean:通过实现注解配置将此类型加入到bean中

    @ConfigurationProperties@Value
    注值批量注入单个
    松散语法支持不支持
    SpEL不支持支持
    JSR303数据校验支持不支持
    注入复杂类型支持不支持

    多环境切换:配置环境切换和动态环境切换
    配置文件的位置:properties的优先级要大于yml
    外部文件和外部参数:Run configuration ,argumenets方式和命令控制行
    日志:spring boot默认选用slf4j和logback、日志级别、日志信息格式、日志输出路径
    Web静态资源:静态资源的存放路径通过WebMvcAutoConfiguration类-addResourceHandlers()指定:/webjars/成静态资源存放目录: {“classpath:/META-INF/resources/”,“classpath:/resources/”,“classpath:/static/”,“classpath:/public/”}以上目录存放资源文件后,访问时不需要加前缀、欢迎页(任意一个静态资源目录中的Index.html)、Logo(任意一个静态资源目录中的favicon.ico)、自定义静态资源存放路径(spring.resources.static-locations=…)
    Web动态资源:默认不支持jsp、html等,但是推荐使用模板引擎thymeleaf(网页=模板+数据)
    引入依赖

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    </dependency>
    <dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
    </dependency>
    

    使用thymeleaf只需要将文件放入目录:“classpath:/templates/”; 文件的后缀: “.html”;
    thymeleaf的th指定方式和符号取值方式(usingthymeleaf文档)

    Dubbo

    RPC(Remote Procedure Call):远程过程调用
    SOA(Service-Oriented Architecture):面向服务架构
    Dubbo中控制器和服务端分离
    步骤:提供方服务器运行提供方提供的服务、注册中心发布服务、消费方订阅服务、注册中心推送服务、消费方向提供方调用服务
    开发dubbo程序:准备环境(linux环境中的jdk及其环境变量和zookeeper)、服务方(引入依赖:pom.xml、接口及实现类@Service、补全WEB-INF/web.xml、配置spring:applicationContext.xml)、消费方(引入依赖:pom.xml改端口号、补全WEB-INF/web.xml、配置springmvc、编写控制器代码@Reference:用于访问服务方提供的服务代码)、监听器(下载incubator-dubbo-ops、打包war、在linux中的tomcat中运行)

    Quartz

    quartz:定时执行、异步任务
    任务:Job、触发器:定义时间、调度器:将任务和触发器 一一对应
    步骤
    引入jar包
    新建类制定具体任务
    新建类任务调用的实现继承Job接口并执行具体任务

    public class PlanJob implements Job {
        MeetingService meetingService = new MeetingService();
        //jobExecutionContext可以获取设置的各种参数值
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            TriggerKey triggerkey =
                    jobExecutionContext.getTrigger().getKey();
            JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
            System.out.println("----");
            System.out.println(triggerkey+"\n"+jobKey);
            JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
            List<String> infos = (List<String>)jobDataMap.get("infos");
            System.out.println(infos);
            //存放 计划执行的任务...
            meetingService.calClassMeeting();
        }
    }
    

    新建测试方法类(产生)

    public class TestQuartz {
    //XxxBuilder ->withIdentity()-->Xxx
    public static void main(String[] args) throws SchedulerException, InterruptedException, ParseException {
    //        PlanJob
            JobBuilder jobBuilder = JobBuilder.newJob(PlanJob.class);//PlanJob PlanJob PlanJob
            //产生实际使用的Job
            JobDetail jobDetail = jobBuilder.withIdentity("meeting Job", "group1").build();
    
            //向Job的execute()中传入一些参数。。。
    //        JobDatMap
            JobDataMap jobDataMap = jobDetail.getJobDataMap();
            List<String> names = Arrays.asList(new String[]{"zs","ls","ww"});
            jobDataMap.put("infos",names);
    
            // 触发器(Trigger):时间规则  ,依赖2个对象(TriggerBuilder ,Scheduel)
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            triggerBuilder = triggerBuilder.withIdentity("meeting trigger", "group1");
            triggerBuilder.startNow();//当满足条件时  立刻执行
            // 2019-03-13 09:46:30   --  // 2019-03-13 09:46:45
    
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date start = sdf.parse("2019-03-13 09:46:30");
            Date end = sdf.parse("2019-03-13 09:46:45");
    
    //        triggerBuilder.startAt(start);
    //        triggerBuilder.endAt(end);
    
            //scheduelBuilder:定执行的周期(时机)
    //        SimpleScheduleBuilder scheduelBuilder = SimpleScheduleBuilder.simpleSchedule();
    //        scheduelBuilder.withIntervalInSeconds(1) ;//每隔1秒执行一次
    //        scheduelBuilder.withRepeatCount(3) ;//重复执行3次
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("5,10,15,30,45 * * * * ? *");
            //产生触发器
            // SimpleTrigger trigger = triggerBuilder.withSchedule(ScheduleBuilder).build();
            CronTrigger trigger = triggerBuilder.withSchedule(cronScheduleBuilder).build();
    //        调度器(工厂产生调度器)
             SchedulerFactory secheduleFacotry = new StdSchedulerFactory();
            //产生调度器
            Scheduler scheduler = secheduleFacotry.getScheduler();
            //通过调度器 将 任务 和 触发器一一对应
            scheduler.scheduleJob(jobDetail,trigger) ;
            scheduler.start();
    //        scheduler.shutdown(true);
        }
    }
    

    框架整合

    Spring-MyBatis整合

    引入jar包
    配置类-表对应
    配置Spring配置文件(applicationContext.xml)

    <!-- 加载db.properties文件 -->
    <bean  id="config" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
    <property name="locations">
    <array><value>classpath:db.properties</value></array>
    </property>
    </bean>
    
    <!-- 引入service层的bean -->
    <bean id="studentService" class="org.lanqiao.service.impl.StudentServiceImpl">
    <property name="studentMapper" ref="studentMapper"></property>
    </bean>
    
    <!-- 配置配置数据库信息(替代mybatis的配置文件conf.xml) -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${username}"></property>
    <property name="password" value="${password}"></property>
    </bean>
    
    <!-- 在SpringIoc容器中 创建MyBatis的核心类 SqlSesionFactory -->
    <bean id="sqlSessionFacotry" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <!-- 加载mapper.xml路径 -->
    <property name="mapperLocations" value="org/lanqiao/mapper/*.xml"></property>
    </bean>
    

    配置mapper中的sql语句
    使用Spring-MyBatis整合产物开发程序

    1. DAO层实现类继承SqlSessionDaoSupport类
    2. 直接MyBatis提供的 Mapper实现类:org.mybatis.spring.mapper.MapperFactoryBean
    <bean id="studentMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="org.lanqiao.mapper.StudentMapper"></property>
    <property name="sqlSessionFactory" ref="sqlSessionFacotry"></property>
    </bean>
    
    1. 批量生成多个mapper
    <!--Mapper对在SpringIOC中的id默认就是首字母小写接口名,service在ref对象要首字母小写-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFacotry"></property>
    <!--指定批量产生 哪个包中的mapper对象-->
    <property name="basePackage" value="org.lanqiao.mapper"></property>
    </bean>
    

    使用:

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    context.getBean("bean") ;
    

    Spring-SpringMVC-MyBatis整合

    引入jar包
    配置类-表对应
    配置Spring配置文件(applicationContext.xml):使用Spring整合MyBatis
    编写springmvc配置文件:视图解析器、基础配置
    配置mapper中的sql语句
    配置Web配置文件

    <!-- Web项目中,引入Spring -->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 整合SPringMVC -->
    <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-controller.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    Springboot-JSP整合

    新建boot项目war包
    建立基本的web项目所需要的目录结构
    webapps/WEB-INF(需要)
    webapps/WEB-INF/web.xml (不需要)
    webapps/index.jsp
    创建tomcat实例并部署项目:如果是一个war包的spring boot项目,在启动服务器tomcat时,会自动调用ServletInitializer类中的configure方法,configure方法会调用spring boot的主配置类从而启动spring boot

    Dubbo-SSM整合

    创建父工程(引入公共依赖、实体类POJO依赖、module子模块依赖、解决Maven内置tomcat打包不引入xml问题的代码)
    创建实体类(实体类需要implements Serializable接口)
    创建公共模块实现接口共用(引入实体类POJO依赖、提供公共接口)
    创建dao层(映射文件mapper、动态代理接口mapper、spring文件application(加载db文件、配置数据库信息、创建核心类sqlsessionfactory)、将核心类交给spring处理)
    创建service层(补全WEB-INF/web.xml并拦截交给spring处理、pom依赖父工程+dao层+公共接口+tomcat代码、编写spring的application文件并顺带启动dao层spring的application、service的具体实现类)
    创建显示web层(补全WEB-INF/web.xml并拦截交给springMVC处理、pom依赖父工程+公共接口+tomcat代码、编写控制器、配置sppringMVC)
    其他
    在maven中引入一个 mvn中央仓库中不存在的Jar:将jar自己安装到本地mvn仓库:cmd命令(ojdbc7.jar为例):mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc7 -Dversion=10.2.0.5.0 -Dpackaging=jar -Dfile=f:\ojdbc7.jar
    解决Maven内置tomcat打包不引入xml问题:

    # 在父工程pom.xml的<project>元素中加入
      <build>
        	<resources> 
    	  	<resource> 
    	  	<directory>src/main/java</directory> 
    	  	<includes> 
    	  	<include>**/*.xml</include> 
    	  	</includes> 
    	  	<filtering>false</filtering>
    		</resource> 
    		<resource>
    		<directory>src/main/resources</directory>
    		</resource>
    	</resources>
     </build>
    

    Spring-Redis整合

    建立java文件并引入jar包
    指定连接 Redis 服务器的相关信息redis.properties(实现分配连接池和线程)
    在spring的applicationContext配置文件中配置连接池、连接工厂和操作模板,并把RedisUtil加入bean中
    使用工具类和测试类进行Redis的访问和检测

    Spring-Quartz整合

    引入jar包
    创建实体类封装Job信息
    创建实体类调用的具体方法
    创建测试方法初始化容器和开始执行
    创建配置文件applicationContext.xml

     <!--配置任务信息-->
        <bean id="scheduleJobEntity" class="com.yanqun.entity.ScheduleJob"  >
                <property name="jobId" value="j001"></property>
                <property name="jobName" value="任务1"></property>
                <property name="jobGroup" value="任务组1"></property>
                <property name="jobStatus" value="1"></property>
                <property name="cronExpression" value="5,10,30,50 * * * * ? *"></property>
                <property name="desc" value="描述..."></property>
        </bean>
        
    <!--配置job类信息-->
        <bean  id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" >
                <property name="jobClass" value="com.yanqun.job.PlanJob"></property>
                <property name="jobDataAsMap">
                        <map>
                            <entry key="scheduleJob">
                                <ref bean="scheduleJobEntity"></ref>
                            </entry>
                        </map>
                </property>
        </bean>
    
    <!-- cronTrigger触发器:定义时间规则
        <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    
                <property name="jobDetail" ref="jobDetail" >
                </property>
                <property name="cronExpression" value="#{scheduleJobEntity.cronExpression}">
                </property>
        </bean>-->
        
    <!--SimpleTrigger触发器-->
        <bean  id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"  >
                <property name="repeatInterval" value="2000"></property>
                <property name="repeatCount" value="10"></property>
                <property name="startDelay" value="3"></property>
                <property name="jobDetail" ref="jobDetail" ></property>
        </bean>
    <!--配置调度器-->
          <bean id="schedulerFactoryBean"  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
                <property name="triggers">
                    <list>
                            <ref bean="simpleTrigger" />
                    </list>
                </property>
          </bean>
    
    展开全文
  • mybatis

    千次阅读 2019-06-28 13:27:05
    创建实现类对象并调用实现类方法查询所有用户信息 UserDaoImpl dao = new UserDaoImpl ( factory ); List < User > users = dao . findAll (); //4. 遍历输出 for ( User user : users ) { ...

    mybatis第一天

    1.mybatis概述和环境搭建

    mybatis概述

    mybatis环境搭建

    1. 创建maven工程、添加开发依赖、创建数据库和表;

    2. 创建domain实体类和dao

    mybatis是一门java语言编写持久层框架,大大简化了jdbc操作,省去了我们注册驱动,获取连接等细节操作。

     

    org.mybatis

    mybatis

    3.4.5

     

    mysql

    mysql-connector-java

    8.0.11

     

    junit

    junit

    4.11

    test

     

    log4j

    log4j

    1.2.12

    3. resource中编写mybatis主配置文件

    4. 编写每个dao接口的映射配置文件

    /**

    * 用户的持久层接口

    */

    public interface IUserDao {

    /**

    * 查询所有操作

    * @return

    */

    List<User> findAll();

    }

    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

    "http://mybatis.org/dtd/mybatis-3-config.dtd">

     

    default="mysql">

    id="mysql">

    type="jdbc">

    type="pooled">

    name="driver" value="com.mysql.jdbc.Driver"/>

    name="url" value="jdbc:mysql://localhost:3306/mybatis"/>

    name="username" value="root"/>

    name="password" value="78910J"/>

     

    resource="com/itheima/dao/IUserDao.xml">

    5.注意事项

    6.测试类中测试方法编写

    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    namespace="com.itheima.dao.IUserDao">//namespace:接口的路径 定位类中方法的具体位置

    id="findAll" resultType="com.itheima.domain.User"> SELECT * FROM USER

    1.resource中创建IUserDao.xml时,文件的位置必须和IUserDao包结构相同,创建IUserDao.xml所在目录结

    构时要一级一级创建。

    2.映射配置文件中的mappernamespace属性值必须是IUserDao的全类名

    3.执行sql操作的配置id属性必须是执行方法的名词,例如 <select id="findAll"

    @Test

    public void testFindAll() throws IOException {

    //1.读取SqlMapConfig.xml配置文件

    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂类

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

    SqlSessionFactory factory = builder.build(is);

    //3.使用SqlSessionFactory工厂创建SqlSession核心对象

    /*SqlSessionFactoryMyBatis的关键对象,它是个单个数据库映射

    关系经过编译后的内存镜像.SqlSessionFactory对象的实例可以通

    SqlSessionFactoryBuilder对象类获得,SqlSessionFactoryBuilder

    则可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory

    的实例.每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心.

    同时SqlSessionFactory也是线程安全的,SqlSessionFactory一旦被创建,应该在

    应用执行期间都存在.在应用运行期间不要重复创建多次,建议使用单例模式.SqlSessionFactory

    是创建SqlSession的工厂.*/

    SqlSession session = factory.openSession();//openSession方法默认手动提交 填写false就是自动

    提交 当对表数据crud就需要提交事务

    //4.使用SqlSession创建接口的代理对象

    IUserDao userDao = session.getMapper(IUserDao.class);

    //5.执行查询方法

    List<User> users = userDao.findAll();

    for (User user : users) {

    System.out.println(user);

    }

    //6.释放资源

    is.close();

    session.close();

    }2.mybatis注解开发和编写dao实体类方式

    mybatis注解开发

    编写dao实现类开发

    1.在核心配置文件SqlMapConfig.xml<mappers>中添加注解映射方式:

    <mappers>

    <mapper class="com.itheima.dao.IUserMapper"/>

    mappers>

    2.定义接口和抽象方法,在抽象方法上添加对应的注解:

    public interface IUserMapper {

    /**

    * 查询所有用户信息

    */

    @Select("select * from user")

    public List<User> findList();

    }

    3.进行测试:

    @Test

    public void testFindList() throws IOException {

    //1.读取核心配置文件

    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂类

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

    SqlSessionFactory factory = builder.build(is);

    //3.创建SqlSession核心对象

    SqlSession session = factory.openSession();

    //4.获取dao代理对象

    IUserMapper mapper = session.getMapper(IUserMapper.class);

    //5.执行查询操作

    List<User> users = mapper.findList();

    for (User user : users) {

    System.out.println(user);

    }

    //6.释放资源

    is.close();

    session.close();

    }

    1.编写实现类:

    public class UserDaoImpl implements IUserDao{

    //1.声明SqlSessionFactory工厂类

    private SqlSessionFactory factory;

    //2.构造赋值

    public UserDaoImpl(SqlSessionFactory factory) {

    this.factory = factory;

    }

    @Override

    public List<User> findAll() {注意:同一个dao接口不允许配置两种映射方式,例如:下列写法是错误的

    3.自定义mybatis框架

    流程分析

    自定义mybatis流程分析.png

    alt text

    Alt text

    alt text

    新建module,根据入门案例测试方法创建缺少的接口和类

    导入资料中utils目录中的XMLConfifigBuilder.java文件,并修改错误

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用查询的相关方法,哪个namespace中的哪个id方法对应的sql

    List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");

    //3.释放资源

    session.close();

    return users;

    }

    }

    2.进行测试

    @Test

    public void testFindAllImpl() throws IOException {

    //1.读取核心配置文件

    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂类

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

    SqlSessionFactory factory = builder.build(is);

    //3.创建实现类对象并调用实现类方法查询所有用户信息

    UserDaoImpl dao=new UserDaoImpl(factory);

    List<User> users = dao.findAll();

    //4.遍历输出

    for (User user : users) {

    System.out.println(user);

    }

    //4.释放资源

    is.close();

    }

     

    resource="com/itheima/dao/IUserDao.xml"/>

    class="com.itheima.dao.IUserDao"/>

    完成SqlSessionFactoryBuilderSqlSessionFactory接口实现类、SqlSession接口实现类功能(实现类要自

    己创建)

    SqlSessionFactoryBuilder代码实现

    1.添加依赖:

    <dependency>

    <groupId>dom4jgroupId>

    <artifactId>dom4jartifactId>

    <version>1.6.1version>

    dependency>

    <dependency>

    <groupId>jaxengroupId>

    <artifactId>jaxenartifactId>

    <version>1.1.6version>

    dependency>

    2.创建ConfigurationMapper两个javabean对象

    public class Configuration {

    private String driver;

    private String url;

    private String username;

    private String password;

    private Map<String,Mapper> mappers=new HashMap<String, Mapper>();//保存多组映射信息的map

    // getter/setter方法省略

    public void setMappers(Map<String, Mapper> mappers) {

    this.mappers.putAll(mappers);

    }

    }

    public class Mapper {

    private String queryString;

    private String resultType;

    // getter/setter方法省略

    }

    /**

    * 构建者对象

    */

    public class SqlSessionFactoryBuilder {

    /**

    * 根据配置文件输入流对象创建工厂对象

    * @param configStream 配置文件输入流

    * @return

    */

    public SqlSessionFactory build(InputStream configStream) {

    //1.使用XMLConfigBuilder解析配置文件

    Configuration configuration = XMLConfigBuilder.loadConfiguration(configStream);

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

    //2.创建工厂实现类对象并返回

    return new DefaultSqlSessionFactory(configuration);DefaultSqlSessionFactory代码实现

    DefaultSqlSession代码实现

    }

    }

    /**

    * SqlSessionFactory接口的实现类

    */

    public class DefaultSqlSessionFactory implements SqlSessionFactory {

    //声明封装主配置文件对象

    private Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {

    this.configuration=configuration;

    }

    /**

    * 生产操作数据库的核心SqlSession对象

    * @return

    */

    public SqlSession openSession() {

    //创建SqlSession实现类对象并返回

    return new DefaultSqlSession(configuration);

    }

    }

    /**

    * 操作数据库核心SqlSession接口的实现类

    */

    public class DefaultSqlSession implements SqlSession {

    //声明封装主配置文件对象,因为getMapper需要连接和映射信息,所以需要这个配置对象

    private Configuration configuration;

    private Connection conn;

    public DefaultSqlSession(Configuration configuration) {

    this.configuration=configuration;

    //从连接池工具类中获取连接对象

    conn= DataSourceUtils.getConnection(configuration);

    }

    /**

    * 通过动态代理的方式创建接口的实现类对象

    * @param daoClass 接口的Class对象

    * @param 返回接口实现类对象

    * @return

    */

    public <T> T getMapper(Class<T> daoClass) {

    //创建dao代理对象并返回

    T t = (T) Proxy.newProxyInstance(daoClass.getClassLoader(),

    new Class[]{daoClass},

    new MapperProxy(configuration.getMappers(),conn));

    return t;DataSourceUtils工具类获取连接

    InvocationHandler实现类MapperProxy的代码实现

    }

    /**

    * 释放资源

    */

    public void close() {

    try {

    if (conn!=null) {

    conn.close();

    }

    } catch (SQLException e) {

    e.printStackTrace();

    }

    }

    }

    /**

    * 将来封装连接池的工具类,目前不使用连接池

    */

    public class DataSourceUtils {

    public static Connection getConnection(Configuration cfg) {

    //1.注册驱动

    try {

    Class.forName(cfg.getDriver());

    //2.获取连接

    return

    DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());

    } catch (Exception e) {

    throw new RuntimeException(e);

    }

    }

    }

    /**

    * dao代理对象的方法被调用时,该监听类的invoke方法就会执行

    */

    public class MapperProxy implements InvocationHandler {

    //map集合的key=全类名+方法名

    private Map<String, Mapper> mappers;//封装了sql语句和结果类型

    private Connection conn;//连接对象

    public MapperProxy(Map<String, Mapper> mappers,Connection conn) {

    this.mappers=mappers;

    this.conn=conn;

    }

    //dao代理对象的方法被调用时,invoke方法就会执行,参数method表示代理对象调用的方法

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    //1.根据执行的method方法从mappers集合中找到对应的mapper对象

    //1.1.获取方法名MapperProxy中需要的Executor类从资料中复制到项目的utils包下,该Executor类的selectList方法帮

    我们查询所有信息并封装到List

    注意:在没网的情况下,需要删除SqlMapConfifig和映射文件的约束信息;

    mybatis第二天

    1.mybatisCRUD操作

    1.1.映射配置文件IUserDao.xml信息

    String methodName = method.getName();

    //1.2.获取全类名

    String className = method.getDeclaringClass().getName();

    //1.3.拼接map集合的key,找到对应的mapper对象

    String key=className+"."+methodName;

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

    //1.4.找到要的Mapper对象

    Mapper mapper = mappers.get(key);

    if (mapper==null) {

    //没找到,配置的全类名有误或者方法有误

    throw new IllegalStateException(""+className+"类中没有"+methodName+"方法");

    }

    //2.调用Executor工具类的selectList方法执行查询操作

    return new Executor().selectList(mapper,conn);

    }

    }

    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    namespace="com.itheima.dao.IUserDao">

    id="findAll" resultType="com.itheima.domain.User"> SELECT * from user

    id="saveUser" parameterType="com.itheima.domain.User">

    /*添加用户信息之后查询新用户的id keyProperty对应的是userid属性*/

    keyColumn="id" keyProperty="id" resultType="int" order="AFTER">

    select last_insert_id();

    /*selectKey是后来加的,在添加用户信息时可以先忽略*/

    insert into user values (null,#{username},#{birthday},#{sex},#{address})

    id="updateUser" parameterType="com.itheima.domain.User">1.2.封装数据的User类省略

    1.3.测试类代码

    update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}

    where id=#{id}

    id="deleteUser" parameterType="Integer">

    delete from user where id=#{id}

    id="findById" resultType="com.itheima.domain.User" parameterType="int"> select * FROM user where id=#{id}

    id="findByName" parameterType="String" resultType="com.itheima.domain.User"> select * from user where username like #{username}

    id="findTotal" resultType="int"> select count(*) from user

    public class MybatisTest {

    private InputStream is;

    private SqlSession session;

    private IUserDao userDao;

    /**

    * 初始化mybatis

    */

    @Before

    public void init() throws IOException {

    //1.加载核心配置文件SqlMapConfig.xml

    is = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂对象

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

    SqlSessionFactory factory = builder.build(is);

    //3.创建核心SqlSession对象

    session = factory.openSession();

    //4.创建IUserDao对象

    userDao = session.getMapper(IUserDao.class);

    }

    /**

    * 测试查询所有用户信息的findAll()方法

    */

    @Test

    public void testFindAll(){

    List<User> users = userDao.findAll();

    //遍历打印

    for (User user : users) {System.out.println(user);

    }

    }

    /**

    * 测试保存用户信息的saveUser()方法

    */

    @Test

    public void testSaveUser(){

    //创建用户信息

    User user=new User();

    user.setUsername("老周");

    user.setBirthday(new Date());

    user.setAddress("武汉");

    user.setSex("");

    System.out.println("添加前user = " + user);

    //添加到数据

    int row = userDao.saveUser(user);

    System.out.println("影响的行数row = " + row);

    //IUserDao.xml配置了selectKey后,添加完user就会查询到新用户的id并封装到userid属性上。

    System.out.println("添加后user = " + user);

    }

    /**

    * 测试更新用户信息的updateUser()方法

    */

    @Test

    public void testUpdateUser(){

    //准备要修改用户的信息信息

    User user=new User();

    user.setId(52);

    user.setUsername("老李");

    user.setBirthday(new Date());

    user.setAddress("孝感");

    user.setSex("");

    //修改用户

    int row = userDao.updateUser(user);

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

    }

    /**

    * 测试更加用户id删除用户信息的deleteUser()方法

    */

    @Test

    public void testDeleteUser(){

    //根据id删除用户信息

    int row = userDao.deleteUser(52);

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

    }

    /**

    * 测试根据id查询用户信息的findById()方法

    */

    @Test

    public void testFindById(){

    User user = userDao.findById(50);

    System.out.println(user);2.mybatis映射配置参数和返回值类型深入学习

    2.1.parameterType传递pojo包装对象

    QueryVo实体类

    映射文件配置

    }

    /**

    * 测试根据用户名称模糊查询的findByName()方法

    */

    @Test

    public void testFindByName(){

    List<User> users = userDao.findByName("%%");

    for (User user : users) {

    System.out.println(user);

    }

    }

    /**

    * 测试查询总记录条数的findTotal()方法

    */

    @Test

    public void testFindTotal(){

    int total = userDao.findTotal();

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

    }

    /**

    * 释放资源

    */

    @After

    public void destory() throws IOException {

    //提交事务

    session.commit();

    //6.释放资源

    session.close();

    is.close();

    }

    }

    public class QueryVo {

    private User user;

    public User getUser() {

    return user;

    }

    public void setUser(User user) {

    this.user = user;

    }

    }测试类测试方法编写

    mybatisdao层开发模式

    返回值类型-javabean的属性和表字段名一致情况(了解)

    javabean实体类

    id="findByVo" parameterType="com.itheima.domain.QueryVo" resultType="com.itheima.domain.User"> /*username是user的属性,user是QueryVo的属性*/ select * from user where username like #{user.username}

    /**

    * 测试根据 QueryVo 中的条件查询用户的findByVo()方法

    */

    @Test

    public void testFindByVo(){

    //创建QueryVo对象

    QueryVo queryVo=new QueryVo();

    //创建模糊查询条件封装到user对象中

    User user=new User();

    user.setUsername("%%");

    queryVo.setUser(user);

    //执行查询

    List<User> users = userDao.findByVo(queryVo);

    //遍历打印

    for (User u : users) {

    System.out.println(u);

    }

    }

    * 基于DAO代理方式(推荐使用)

    * CRUD操作(保存操作|修改操作|删除操作|查询一个|模糊查询|单值查询|获取保存对象的id)

    * 模糊查询需要注意

    * 获取保存对象的id

    * 输入参数 parameterType属性

    * 输入参数的类型:intstringUserplain old java object、包装类型ValueObject

    * 输出参数 resultType属性、

    * 输入参数的类型:Userintstring

    * 解决实体类属性和数据库列名不对应

    * sql语句上取别名, 对应, 简单

    * 通过resultMap属性来映射java对象和数据库列名之间的关系

    * <result property="userName" column="username">result>

    property 遵循的是驼峰规则; column 单词下划线隔开解决办法1sql的结果集使用别名,别名和javabean的属性一致

    好处:查询效率高;弊端:书写麻烦,开发效率低。

    解决办法2:使用mybatis给我们提供的配置结果集映射

    好处:id="UMap"可以重复使用,开发效率高;弊端:查询效率略低。

    编写测试类测试方法

    public class U {

    private Integer userId;

    private String userName;

    private Date userBirthday;

    private String userSex;

    private String userAddress;

    //自动生成settergettertoString省略,此时属性和表的字段名不一致

    }

    id="findAllU" resultType="com.itheima.domain.U"> select id userId,username userName,birthday userBirthday,sex userSex,address userAddress from user;

    id="UMap" type="com.itheima.domain.U">

    column="id" property="userId"/>

    column="username" property="userName"/>

    column="birthday" property="userBirthday"/>

    column="sex" property="userSex"/>

    column="address" property="userAddress"/>

    id="findAllU" resultMap="UMap"> select * from user; 3.mybatis中编写dao实现类的使用(了解)

    UserDaoImpl实现类代码

    /**

    * 测试使用别名,查询所有用户信息的findAllU()方法

    */

    @Test

    public void testFindAllU(){

    List<U> us = userDao.findAllU();

    //遍历打印

    for (U u : us) {

    System.out.println(u);

    }

    }

    public class UserDaoImpl implements IUserDao {

    private SqlSessionFactory factory;

    //接收工厂对象,用于创建SqlSession对象

    public UserDaoImpl(SqlSessionFactory factory) {

    this.factory = factory;

    }

    @Override

    public List<User> findAll() {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用selectList方法查询所有用户信息

    List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");

    //3.释放资源

    session.close();

    return users;

    }

    @Override

    public int saveUser(User user) {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用insert方法添加一条用户信息

    int row = session.insert("com.itheima.dao.IUserDao.saveUser", user);

    //3.提交并释放资源

    session.commit();

    session.close();

    //返回影响的函数

    return row;

    }

    @Override

    public int updateUser(User user) {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用update方法修改一条用户信息

    int row = session.update("com.itheima.dao.IUserDao.updateUser", user);

    //3.提交并释放资源session.commit();

    session.close();

    //返回影响的函数

    return row;

    }

    @Override

    public int deleteUser(int id) {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用delete方法删除一条用户信息

    int row = session.delete("com.itheima.dao.IUserDao.deleteUser", id);

    //3.提交并释放资源

    session.commit();

    session.close();

    //返回影响的函数

    return row;

    }

    @Override

    public User findById(Integer id) {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用selectList方法查询所有用户信息

    User user = session.selectOne("com.itheima.dao.IUserDao.findById",id);

    //3.释放资源

    session.close();

    return user;

    }

    @Override

    public List<User> findByName(String username) {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用selectList方法根据username模糊查询所有用户信息

    List<User> users = session.selectList("com.itheima.dao.IUserDao.findByName",username);

    //3.释放资源

    session.close();

    return users;

    }

    @Override

    public int findTotal() {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用delete方法删除一条用户信息

    int total = session.selectOne("com.itheima.dao.IUserDao.findTotal");

    //3.释放资源

    session.close();

    //返回总记录数

    return total;

    }

    @Override

    public List<User> findByVo(QueryVo vo) {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用selectList方法根据vo.user.username模糊查询所有用户信息修改测试类init方法和destory方法即可,其他测试方法都不需要改

    4.mybatis中编写dao实现类的使用过程分析

    5.properties标签、typeAliases标签、package标签使用

    5.1.properties标签的作用

    List<User> users =

    session.selectList("com.itheima.dao.IUserDao.findByName",vo.getUser().getUsername());

    //3.释放资源

    session.close();

    return users;

    }

    @Override

    public List<U> findAllU() {

    //1.创建核心的SqlSession对象

    SqlSession session = factory.openSession();

    //2.调用selectList方法查询所有用户信息

    List<U> us = session.selectList("com.itheima.dao.IUserDao.findAllU");

    //3.释放资源

    session.close();

    return us;

    }

    }

    @Before

    public void init() throws IOException {

    //1.加载核心配置文件SqlMapConfig.xml

    is = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂对象

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();

    SqlSessionFactory factory = builder.build(is);

    /*如果是使用dao代理方式,则需要创建SqlSession对象,并获取dao的代理对象*/

    /*//3.创建核心SqlSession对象

    session = factory.openSession();

    //4.创建IUserDao对象

    userDao = session.getMapper(IUserDao.class);*/

    /*如果使用的是自定义dao实现类方法,则需要创建dao实现类对象,并传递factory工厂对象*/

    userDao=new UserDaoImpl(factory);

    }

    @After

    public void destory() throws IOException {

    //提交事务,如果是自定义dao实现类就不需要,在实现类内部提交

    //session.commit();

    //6.释放资源,如果是自定义dao实现类就不需要,在实现类内部关闭

    //session.close();

    is.close();

    }作用:将连接数据库的信息单独配置到一个properties属性文件中,配置方式如下

    外部jdbcConfifig.properties配置文件

    SqlMapConfifig.xml跟标签下使用properties标签引入jdbcConfifig.properties

    dataSource中引入外部properties的各个属性

    5.2.typeAliases标签、package标签使用作用

    SqlMapConfifig.xml跟标签下使用typeAliases标签作用 :javabean取别名(了解)

    jdbc.driver=com.mysql.jdbc.Driver

    jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai

    jdbc.username=root

    jdbc.password=78910J

     

    name="driver" value="com.mysql.jdbc.Driver"/>

    name="url" value="jdbc:mysql://localhost:3306/mybatis?

    serverTimezone=Asia/Shanghai"/>

    name="username" value="root"/>

    name="password" value="78910J"/>

    或者

    resource="jdbcConfig.properties">

    或者

    url="

    file:///D:/IdeaProjects/day02_eesy_01mybatisCRUD/src/main/resources/jdbcConfig.prop

    erties">

    type="pooled">

    name="driver" value="${jdbc.driver}"/>

    name="url" value="${jdbc.url}"/>

    name="username" value="${jdbc.username}"/>

    name="password" value="${jdbc.password}"/>

    typeAliases标签下使用package标签作用 :指定包下的所有javabean的类名就是其别名,不区分大小写

    (实用)

    Mappers标签下使用package标签作用 :不用写mapper标签,resourcesclass指定包下所有dao对应的

    映射文件都不用引入了,并且可以找到dao的接口或者dao接口对应的配置。(实用)

    mybatis第三天

    1.mybatis中连接池以及事务管理

    1.1 mybatis连接池

    mybatis中三种数据源介绍

    sqlMapConfifig.xml中配置

     

    type="com.itheima.domain.User" alias="user"/>

     

     

    name="com.itheima.dao"/>

    UNPOOLED 不使用连接池的数据源,需要连接对象就使用DriverManager创建获取一个,使用完成就直接销毁

    POOLED 使用连接池的数据源 (很常用)

    JNDI 使用 JNDI 实现的数据源(最后讲解,了解)1.2 mybatis事务管理

    mybatis中默认是开启也是,也就是增删改之后需要手动session.commit()方法手动提交事务;但是我们也可以设

    置自动提交,在增删改完成之后不需要手动调用session.commit()提交事务。只需要在session =

    factory.openSession(true);参数true表示自动提交。

    2.mybatis中动态SQL

    使用场景:如果条件存在就带条件查询,如果条件不存在就不带条件查询,适用于搜索等场景。

    2.1 if标签 - 动态添加条件

    IUserDao.xml中配置查询语句

    2.2 where 标签 - 代替where 1=1

    IUserDao.xml中修改之前配置的查询语句

    sqlMapConfig.xml中配置如下

    type="pooled">

    name="driver" value="${jdbc.driver}"/>

    name="url" value="${jdbc.url}"/>

    name="username" value="${jdbc.username}"/>

    name="password" value="${jdbc.password}"/>

    MyBatis 在初始化时,根据type 属性来创建相应类型的的数据源 DataSource,即:

    type="POOLED"MyBatis 会创建 PooledDataSource 实例

    type="UNPOOLED"MyBatis 会创建 UnpooledDataSource 实例

    type="JNDI"MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用

    id="findUserByCondition1" parameterType="user" resultType="user"> select * from user where 1=1 test="username!=null and username!=''">//连接判断条件不能为&&和|| and username = #{username} test="sex!=null"> and sex = #{sex} 2.3 foreach标签 - 遍历,适用于in的条件

    foreach元素的属性主要有 itemindexcollectionopenseparatorclose

    item: 循环体中的具体对象。支持属性的点路径访问,如 item.age,item.info.details。具体说明:在 list 和数

    组中是其中的对象,在 map 中是 value,该参数为必选。(它是每一个元素进行迭代时的别名)

    index:在 list 和数组中,index 是元素的序号;在 map 中,index 是元素的 key

    open :表示该语句以什么开始

    separator :表示在每次进行迭代之间以什么符号作为分隔符

    close :表示以什么结束

    _parameter :不只是方法传递过来的参数可以被用来判断,取值

    _parameter:代表整个参数

    单个参数:_parameter就是这个参数

    多个参数: 1.参数会被封装为一个map:_parameter就是代表这个map

    2._parameter 可以get(0)得到第一个参数。

    在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况 下,该

    属性的值是不一样的,主要有一下3种情况:

    1.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list

    2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array

    3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map

    遍历map集合

    id="findUserByCondition1" parameterType="user" resultType="user"> select * from user test="username!=null and username!=''"> and username = #{username} test="sex!=null"> and sex = #{sex} 需求:根据qvlist集合查询用户信息-

    IUserDao.xml中配置查询语句

    测试类中测试- QueryVo中添加ids属性省略了

    id="findByMap" parameterType="map" resultType="cn.itcast.mybatis.pojo.Product"> select * from user collection="_parameter" index="key" item="value" separator="and"> test="value !=null"> ${key} = #{value}

    id="findUserByCondition2" parameterType="QueryVo" resultType="user"> select * from user test="ids!=null and ids.size>0"> id="findUserByCondition2" parameterType="QueryVo" resultType="user">

    select * from user

     

    test="ids!=null and ids.size>0">

    and id in(

    collection="ids" item="uid" separator=",">

    #{uid}

    )

    @Test

    public void testFindUserByCondition2(){

    QueryVo qv=new QueryVo();

    List<Integer> ids=new ArrayList<Integer>();

    ids.add(41);2.4 补充1:抽取公共的sql语句

    IUserDao.xml中跟标签中使用sql标签定义公共sql语句

    中使用标签引入公共sql 3.mybatis多表之间关系 3.1 一对一关系 需求:一个账户只属于一个用户,查询账户信息的同时查询用户信息 实现步骤: 一对一映射 代码示例: 1.创建Account实体类以及IAccountDao接口 ids.add(45); ids.add(46); ids.add(48); qv.setIds(ids); List<User> list = userDao.findUserByCondition2(qv); //变量展示 for (User u : list) { System.out.println("u = " + u); } } id="selectTemplate"> select * from user id="findAll" resultType="com.itheima.domain.User">

    refid="selectTemplate"/>

    1.创建Account实体类以及IAccountDao接口

    2.创建IAccountDao.xml映射问题

    3.创建AccountDaoTest测试类进行测试

    public class Account implements Serializable {

    private Integer id;

    private Integer uid;

    private Double money;

    //封装对应的user对象2.创建IAccountDao.xml映射文件

    3.创建AccountTest测试类进行测试

    private User user;

    //settergettertoString方法省略

    }

    public interface IAccountDao {

    /**

    * 查询所有账号信息同时查询对应用户信息

    * @return

    */

    List<Account> findAll();

    }

    namespace="com.itheima.dao.IAccountDao">

    id="accountUserMap" type="account">

    column="aid" property="id"/>

    column="uid" property="uid"/>

    column="money" property="money"/>

    property="user" javaType="user" column="uid">

    column="id" property="id"/>

    column="username" property="username"/>

    column="birthday" property="birthday"/>

    column="sex" property="sex"/>

    column="address" property="address"/>

    id="findAll" resultMap="accountUserMap"> /*注意:当多表查询结果有相同字段名时,我们需要取别名区分*/ SELECT u.*,a.id aid,a.UID,a.MONEY FROM user u,account a where u.id=a.UID;

    @Test

    public void testFindAll(){

    //1.调用findAll方法

    List<Account> list = accountDao.findAll();

    //2.遍历打印结果

    for (Account account : list) {

    System.out.println(account);

    }

    }注意:当多表查询结果有相同字段名时,我们需要取别名区分

    3.2 一对多关系

    需求:一个用户有多个账户,查询用户信息的同时查询其所有账户信息

    实现步骤

    一对多映射

    代码示例

    IUserDao中添加新的查询方法

    IUserDao.xml中重新配置查询语句

    编写UserTest测试类进行测试

    1.user中添加List<Account> accounts属性,在IUserDao中添加新的查询方法

    2.IUserDao.xml中重新配置查询语句

    3.AccountDaoTest测试类进行测试

    /**

    * 多表查询:查询用户信息的同时查询其所有账户信息

    * @return

    */

    List<User> findAllUserAndAccount();

    id="userAccountMap" type="user">

    column="id" property="id"/>

    column="username" property="username"/>

    column="birthday" property="birthday"/>

    column="sex" property="sex"/>

    column="address" property="address"/>

    property="accounts" javaType="java.util.List" ofType="account">

    column="id" property="id"/>

    column="uid" property="uid"/>

    column="money" property="money"/>

    id="findAllUserAndAccount" resultMap="userAccountMap"> SELECT * FROM user u LEFT OUTER JOIN account a ON u.id = a.UID; 3.3 多对多关系

    3.3.1 需求1:查询角色信息以及该角色对应的所有用户信息

    实现步骤

    代码示例

    1.创建实体类Role以及IRoleDao接口

    /**

    * 多表查询:查询用户信息的同时查询其所有账户信息

    */

    @Test

    public void testFindAllUserAndAccount(){

    List<User> users = userDao.findAllUserAndAccount();

    for (User user : users) {

    System.out.println(user);

    }

    }

    1.创建实体类Role以及IRoleDao接口

    2.创建IRoleDao.xml映射文件,并配置查询信息

    3.创建RoleDaoTest测试类,并测试查询方法

    public class Role {

    private Integer id;

    private String roleName;

    private String roleDesc;

    //封装该角色对应的所有用户信息

    private List<User> users;

    //settergettertoString方法省略

    }

    public interface IRoleDao {2.创建IRoleDao.xml映射文件,并配置查询信息

    3.创建RoleDaoTest测试类,并测试查询方法

    3.3.2 需求2:查询用户信息以及该用户对应的所有角色信息

    实现步骤

    代码示例

    /**

    * 需求1:查询角色信息以及该角色对应的所有用户信息

    */

    List<Role> findAll();

    }

    namespace="com.itheima.dao.IRoleDao">

    id="roleMap" type="role">

    column="rid" property="id"/>

    column="ROLE_NAME" property="roleName"/>

    column="ROLE_DESC" property="roleDesc"/>

    property="users" javaType="java.util.List" ofType="user">

    column="id" property="id"/>

    column="username" property="username"/>

    column="birthday" property="birthday"/>

    column="sex" property="sex"/>

    column="address" property="address"/>

    id="findAll" resultMap="roleMap"> SELECT r.ID rid,r.ROLE_NAME,r.ROLE_DESC,u.* FROM role r LEFT JOIN user_role ur ON r.ID = ur.RID LEFT JOIN user u ON ur.UID = u.id

    @Test

    public void testFindAll(){

    List<Role> roles = roleDao.findAll();

    for (Role role : roles) {

    System.out.println(role);

    }

    }

    1.在实体类User中添加List<Role> roles属性,并在IUserDao中添加新的查询方法

    2.IUserDao.xml中重新配置查询语句

    3.UserDaoTest测试类进行测试1.IUserDao中添加新的查询方法

    2.IUserDao.xml中重新配置查询语句

    3.UserDaoTest测试类进行测试

    4.补充:JNDI

    mybatis第四天

    1.mybatis延迟加载

    1.1 概念

    /**

    * 需求2:查询用户信息以及该用户对应的所有角色信息

    */

    List<User> findAllUserAndRole();

    id="userRoleMap" type="user">

    column="id" property="id"/>

    column="username" property="username"/>

    column="birthday" property="birthday"/>

    column="sex" property="sex"/>

    column="address" property="address"/>

    property="roles" javaType="java.util.List" ofType="role">

    column="rid" property="id"/>

    column="ROLE_NAME" property="roleName"/>

    column="ROLE_DESC" property="roleDesc"/>

    id="findAllUserAndRole" resultMap="userRoleMap"> SELECT u.*,r.ID rid,r.ROLE_NAME,r.ROLE_DESC FROM user u LEFT JOIN user_role ur ON u.id = ur.UID LEFT JOIN role r ON ur.RID = r.ID;

    @Test

    public void testFindAllUserAndRole(){

    List<User> users = userDao.findAllUserAndRole();

    for (User user : users) {

    System.out.println(user);

    }

    }1.2 一对一实现延迟加载

    1.2.1 在主配置文件SqlMapConfifig.xml中开启全局延迟加载

    1.2.2 在映射配置文件IAccountDao.xml中配置延迟加载查询

    1.2.3 AccountDaoTest测试类中进行测试

    1.3 一对多实现延迟加载

    1.3.1 在映射配置文件IUserDao.xml中配置延迟加载查询

    1.立即加载:只要一调用方法,不管用不用的到数据都立马去关联查询

    2.延迟加载:只有再用到数据时才去关联查询,不用就不关联查询,好处:先从单表查询,需要时再从关联表去关联查

    询,大大提高数据库性能;延迟加载也叫按需加载或懒加载

     

    name="lazyLoadingEnabled" value="true"/>

    name="aggressiveLazyLoading" value="false"/>

    id="accountMap" type="account">

    column="id" property="id"/>

    column="uid" property="uid"/>

    column="money" property="money"/>

    property="user" javaType="user" column="uid"

    select="com.itheima.dao.IUserDao.findById"/>

    id="findAll" resultMap="accountMap"> select * FROM account

    /**

    * 延迟加载:查询所有账号信息同时查询对应用户信息

    */

    @Test

    public void testFindAll() {

    List<Account> accounts = accountDao.findAll();

    /*只要不调用和user相关的方法,就不会查关联表中的数据*/

    for (Account account : accounts) {

    System.out.println(account.getId()+","+account.getUid()+","+account.getMoney());

    }

    }1.3.2 IAccountDao中添加fifindAccountById方法并在IAccountDao.xml配置

    1.3.3 UserDaoTest测试类中进行测试

    2.mybatis中的一级缓存和二级缓存

    2.1 缓存的概念

    2.2 一级缓存

    概念

    id="userMap" type="user">

    column="id" property="id"/>

    column="username" property="username"/>

    column="birthday" property="birthday"/>

    column="sex" property="sex"/>

    column="address" property="address"/>

    property="accounts" ofType="account"

    select="com.itheima.dao.IAccountDao.findAccountById" column="id"/>

    id="findAll" resultMap="userMap"> SELECT * FROM USER ;

    id="findAccountById" parameterType="int" resultType="account"> SELECT * FROM account where uid=#{uid};

    @Test

    public void testFindAll() {

    List<User> list = userDao.findAll();

    for (User u : list) {

    System.out.println(u.getId()+","+u.getUsername()+","+u.getBirthday()+","+u.getSex()+","+u.getAd

    dress());

    /*只要不调用和account相关的方法,就不会查关联表中的数据*/

    /*System.out.println(u.getAccounts());*/

    }

    }

    存在内存中的临时数据就是缓存代码演示

    结果说明

    2.3 二级缓存

    概念

    代码演示

    它指的是MybatisSqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到SqlSession为我们

    提供一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有

    的话直接拿出来用。当SqlSession对象消失时(close方法被调用),mybatis的一级缓存也就消失了。

    @Test

    public void testFirstLeverCache(){

    User user1 = userDao.findById(43);

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

    //测试一下关闭session

    /*session.close();

    session=factory.openSession(true);

    userDao=session.getMapper(IUserDao.class);*/

    //也可以清除缓存

    //session.clearCache();

    //再次执行相同的查询

    User user2 = userDao.findById(43);

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

    //判断user1user2是不是同一个对象

    System.out.println("user1==user2 : " + (user1==user2));

    }

    如果是同一个session对象,那么第二次查询的user对象和第一次查询的user对象是同一个user对象,最后打印的

    结果是true,通过控制台日志可以看出只执行了一次查询;当中间关闭了session或者调用clearCache方法清除缓存之

    后,那两个user对象就不是同一个对象了,控制台查看日志也会发现执行了两次查询。

    它指的是MybatisSqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享

    其缓存。

    二级缓存的使用步骤:

    第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)

    第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

    第三步:让当前的操作支持二级缓存(在select标签中配置)

    @Test

    public void testSecondLeverCache(){

    //1.获取第一个sqlSession对象并查询user

    SqlSession session1 = factory.openSession(true);

    IUserDao userDao1 = session1.getMapper(IUserDao.class);

    User user1 = userDao1.findById(43);结果说明以及注意事项

    3.mybatis注解开发

    3.1 mybatis注解开发CRUD操作

    3.1.1 SqlMapConfifig核心配置文件环境搭建

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

    //关闭session1

    session1.close();

    //2.获取第二个sqlSession对象并查询user

    SqlSession session2 = factory.openSession(true);

    IUserDao userDao2 = session2.getMapper(IUserDao.class);

    User user2 = userDao2.findById(43);

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

    session2.close();

    //判断user1user2是不是同一个对象

    System.out.println("user1==user2 : " + (user1==user2));

    }

    两个对象虽然不是同一个对象,但是通过控制台发现只执行了一次查询

    注意:一定要关闭之前的sqlSession对象

    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

    "http://mybatis.org/dtd/mybatis-3-config.dtd">

     

    resource="jdbcConfig.properties"/>

     

    name="com.itheima.domain"/>

    default="mysql">

    id="mysql">

    type="jdbc">

    type="pooled">

    name="driver" value="${jdbc.driver}"/>

    name="url" value="${jdbc.url}"/>

    name="username" value="${jdbc.username}"/>

    name="password" value="${jdbc.password}"/>

     

    name="com.itheima.dao"/>

    3.1.2 IUserDao中给方法添加注解

    /**

    * 查询所有操作

    * @return

    */

    @Select("select * from user")

    List<User> findAll();

    /**

    * 保存用户

    * @param user

    * @return 影响数据库记录的行数

    */

    @Insert("insert into user values(null,#{username},#{birthday},#{sex},#{address})")

    //配置SelectKey将新添加用户的id封装到user

    @SelectKey(keyColumn = "id",keyProperty = "id",resultType =int.class,before = false, statement =

    "select last_insert_id()")

    int saveUser(User user);

    /**

    * 更新用户

    * @param user

    * @return 影响数据库记录的行数

    */

    @Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}

    where id=#{id}")

    int updateUser(User user);

    /**

    * 根据 id 删除用户

    * @param id

    * @return 影响数据库记录的行数

    */

    @Delete("delete from user where id=#{id}")

    int deleteUser(int id);

    /**

    * 根据 id 查询

    * @param id

    * @return

    */

    @Select("select * from user where id=#{id}")

    User findById(Integer id);

    /**

    * 根据名称模糊查询

    * @param username

    * @return

    */

    @Select("select * from user where username LIKE #{username};")

    List<User> findByName(String username);

    /**3.1.3 在测试类中测试

    * 查询总记录条数

    * @return 总记录条数

    */

    @Select("select count(*) from user;")

    int findTotal();

    /**

    * 根据 QueryVo 中的条件查询用户

    * @param vo

    * @return

    */

    @Select(" select * from user where username like #{user.username}")

    List<User> findByVo(QueryVo vo);

    private InputStream is;

    private SqlSession session;

    private IUserDao userDao;

    private SqlSessionFactory factory;

    @Before

    public void init() throws IOException {

    //1.获取主配置文件输入流

    is = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂对象

    factory = new SqlSessionFactoryBuilder().build(is);

    //3.创建核心SqlSession对象

    session = factory.openSession(true);

    //4.获取IAccountDao代理对象

    userDao = session.getMapper(IUserDao.class);

    }

    //查询所有操作

    @Test

    public void findAll(){

    List<User> users = userDao.findAll();

    for (User user : users) {

    System.out.println(user);

    }

    }

    //保存用户

    @Test

    public void saveUser(){

    //创建用户信息

    User user=new User();

    user.setUsername("老周");

    user.setBirthday(new Date());

    user.setAddress("武汉");

    user.setSex("");

    System.out.println("添加前user = " + user);

    //添加到数据

    int row = userDao.saveUser(user);

    System.out.println("影响的行数row = " + row);System.out.println("添加后user = " + user);

    }

    //更新用户

    @Test

    public void updateUser(){

    //准备要修改用户的信息信息

    User user=new User();

    user.setId(55);

    user.setUsername("老李");

    user.setBirthday(new Date());

    user.setAddress("孝感");

    user.setSex("");

    //修改用户

    int row = userDao.updateUser(user);

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

    }

    //根据 id 删除用户

    @Test

    public void deleteUser(){

    //根据id删除用户信息

    int row = userDao.deleteUser(55);

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

    }

    //根据 id 查询

    @Test

    public void findById(){

    User user = userDao.findById(50);

    System.out.println(user);

    }

    //根据名称模糊查询

    @Test

    public void findByName(){

    List<User> users = userDao.findByName("%%");

    for (User user : users) {

    System.out.println(user);

    }

    }

    //查询总记录条数

    @Test

    public void findTotal(){

    int total = userDao.findTotal();

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

    }

    //根据 QueryVo 中的条件查询用户

    @Test

    public void findByVo(){

    //创建QueryVo对象

    QueryVo queryVo=new QueryVo();

    //创建模糊查询条件封装到user对象中

    User user=new User();

    user.setUsername("%%");

    queryVo.setUser(user);

    //执行查询3.2 mybatis注解开发实例类属性与表列名不一致

    3.2.1 IUserDao中给方法添加注解

    3.2.2 在测试类中测试

    3.3 mybatis注解开发一对一/多查询

    3.3.1 IUserDao中给方法添加注解

    List<User> users = userDao.findByVo(queryVo);

    //遍历打印

    for (User u : users) {

    System.out.println(u);

    }

    }

    @After

    public void destroy() throws IOException {

    session.close();

    is.close();

    }

    /**

    * 使用别名,查询所有用户信息;

    * @return 所有用户信息

    */

    @Select("select * from user")

    //id="uMap"表示结果集映射的id,将来可以给其他@ResultMap复用

    @Results(id="uMap",value = {

    //id=true表示是主键,默认值是false

    @Result(id=true,column = "id",property = "userId"),

    @Result(id=false,column = "username",property = "userName"),

    @Result(column = "birthday",property = "userBirthday"),

    @Result(column = "sex",property = "userSex"),

    @Result(column = "address",property = "userAddress")

    }

    )

    List<U> findAllU();

    @Test

    public void findAllU(){

    List<U> us = userDao.findAllU();

    //遍历打印

    for (U u : us) {

    System.out.println(u);

    }

    }

    /**3.3.2 IAccountDao中创建fifindAccountById方法并添加注解

    3.3.3 在测试类中测试

    * 立即查询:查询账户信息的同时查询其用户信息

    * @return

    */

    @Select("select * from account")

    @Results(id="accountMap",value ={

    @Result(id = true,column = "id",property = "id"),

    @Result(column = "uid",property = "uid"),

    @Result(column = "money",property = "money"),

    @Result(column = "uid",property = "user",one = @One(

    select="com.itheima.dao.IUserDao.findById",

    fetchType = FetchType.EAGER//立即查询

    )),

    })

    List<Account> findAllAccountAndUser();

    /**

    * 延时查询:查询用户信息的同时查询其所有账户信息

    */

    @Select("select * from user")

    @Results(id="userMap",value = {

    @Result(id=true,column = "id",property = "id"),

    @Result(column = "username",property = "username"),

    @Result(column = "birthday",property = "birthday"),

    @Result(column = "sex",property = "sex"),

    @Result(column = "address",property = "address"),

    @Result(column = "id",property = "accounts",many = @Many(

    select="com.itheima.dao.IAccountDao.findAccountById",

    fetchType = FetchType.LAZY//延时查询

    )),

    })

    List<User> findAllUserAndAccount();

    public interface IAccountDao {

    /**

    * 根据uid查询对应的所有账户信息

    * @param uid

    * @return

    */

    @Select("select * from account where uid=#{uid}")

    List<Account> findAccountById(int uid);

    }

    //立即查询:查询账户信息的同时查询其用户信息

    @Test

    public void findAllAccountAndUser(){

    List<Account> accounts = userDao.findAllAccountAndUser();

    //变量打印3.4 mybatis注解开发使用二级缓存

    3.4.1 在被测试的dao类上添加注解开启二级缓存即可

    注意:SqlMapConfifig.xml中的中的

    cacheEnabled默认值就是true,可以不用配置。

    3.4.2 代码测试

    mybatis扩展内容

    for (Account account : accounts) {

    System.out.println(account.getId()+"-"+account.getUid()+"-"+account.getMoney());

    System.out.println(account.getUser());

    }

    }

    //延时查询:查询用户信息的同时查询其所有账户信息

    @Test

    public void findAllUserAndAccount(){

    List<User> users = userDao.findAllUserAndAccount();

    for (User u : users) {

    System.out.println(u.getId()+"-"+u.getUsername()+"-"+u.getBirthday()+"-"+u.getSex()+"-

    "+u.getAddress());

    // System.out.println(u.getAccounts());

    }

    }

    //该接口查询开启二级缓存

    @CacheNamespace(blocking = true)

    public interface IUserDao {}

    //测试二级缓存

    @Test

    public void testSecondLeverCache(){

    //1.获取第一个sqlSession对象并查询user

    SqlSession session1 = factory.openSession(true);

    IUserDao userDao1 = session1.getMapper(IUserDao.class);

    User user1 = userDao1.findById(43);

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

    //关闭session1

    session1.close();

    //2.获取第二个sqlSession对象并查询user

    SqlSession session2 = factory.openSession(true);

    IUserDao userDao2 = session2.getMapper(IUserDao.class);

    User user2 = userDao2.findById(43);

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

    session2.close();

    //判断user1user2是不是同一个对象

    System.out.println("user1==user2 : " + (user1==user2));

    }1.mybatis使用第三方连接池

    1.1 mybatis中连接池原理

    1.2 使用第三方连接池步骤

    1.2.1 自定义工厂类实现mybatisDataSourceFactory接口

    MyBatis 在初始化时,根据<dataSource>type 属性来创建相应类型的的数据源 DataSource,即:

    type="POOLED"MyBatis 会创建 PooledDataSource 实例

    type="UNPOOLED"MyBatis 会创建 UnpooledDataSource 实例

    type="JNDI"MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用

    当我们在<dataSource type="POOLED">配置POOLEDmybatis就会为我们创建PooledDataSource对象,这个对象是

    通过连接池工程创建出来的:

    public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

    public PooledDataSourceFactory() {

    this.dataSource = new PooledDataSource();

    }

    }

    public class UnpooledDataSourceFactory implements DataSourceFactory {

    //代码省略

    }

    public interface DataSourceFactory {

    void setProperties(Properties props);

    DataSource getDataSource();

    }

    mybatis创建PooledDataSourceFactory工厂对象的同时会创建一个PooledDataSource连接池对象,然后调用工

    厂对象的setProperties(Properties props)方法将SqlMapConfig.xml中的连接池配置信息通过props传递进来,当

    进行CRUD操作时就调用getDataSource()方法获取连接池对象。

    也就是说要想让mybatis使用第三方连接池,我们就得自己定义一个工厂类,在工厂来中创建第三方连接池对象并

    setProperties(Properties props)方法中设置连接参数,在getDataSource()方法中返回连接池对象

    package com.itheima.factory;

    import com.alibaba.druid.pool.DruidDataSource;

    import org.apache.ibatis.datasource.DataSourceFactory;

    import javax.sql.DataSource;

    import java.util.Properties;

    //DruidDataSourceFactory不是alibabaDruidDataSourceFactory

    public class DruidDataSourceFactory implements DataSourceFactory {

    //声明连接池对象

    private DruidDataSource dataSource;

    public DruidDataSourceFactory() {

    //创建连接池对象

    this.dataSource = new DruidDataSource();

    }

    @Override

    public void setProperties(Properties props) {

    //SqlMapConfig.xml中的连接池配置信息设置给druid连接池对象

    dataSource.setDriverClassName(props.getProperty("driver"));1.2.2 SqlMapConfifig.xml中使用第三方连接池

    1.2.3 在测试类中进行测试

    dataSource.setUrl(props.getProperty("url"));

    dataSource.setUsername(props.getProperty("username"));

    dataSource.setPassword(props.getProperty("password"));

    }

    @Override

    public DataSource getDataSource() {

    return dataSource;

    }

    }

    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

    "http://mybatis.org/dtd/mybatis-3-config.dtd">

     

    resource="jdbcConfig.properties"/>

     

    name="com.itheima.domain"/>

    default="mysql">

    id="mysql">

    type="jdbc">

    type="com.itheima.factory.DruidDataSourceFactory">

    name="driver" value="${jdbc.driver}"/>

    name="url" value="${jdbc.url}"/>

    name="username" value="${jdbc.username}"/>

    name="password" value="${jdbc.password}"/>

     

    name="com.itheima.dao"/>

    2.mybatis查询传递其他类型参数

    2.1 参数是Map集合

    IUserDao.xml中配置查询信息

    在测试类中测试

    //查询所有操作

    @Test

    public void findAll(){

    //获取连接池对象的名称

    DataSource dataSource = session.getConfiguration().getEnvironment().getDataSource();

    System.out.println("dataSource = " + dataSource.getClass());//dataSource = class

    com.alibaba.druid.pool.DruidDataSource

    //查询所有用户信息

    List<User> users = userDao.findAll();

    for (User user : users) {

    System.out.println(user);

    }

    }

    id="findUserByMap" resultType="user"> select * from USER test="username!=null and username!=''"> and username like #{username} test="sex!=null and username!=''"> and sex = #{sex}

    //动态sql:根据map集合中的条件,查询用户信息

    @Test

    public void testFindUserByMap(){

    //创建Map集合并封装查询参数

    Map<String,Object> map=new HashMap<String,Object>();

    map.put("username","%%");

    map.put("sex","");

    //查询符合条件的用户信息

    List<User> users = userDao.findUserByMap(map);

    for (User user : users) {

    System.out.println(user);

    }

    }结论:使用map集合或者javabean对象作为参数,其实是一样的,map集合对应的就是javabean的属性。

    2.2 参数是List集合

    IUserDao.xml中配置查询信息

    在测试类中测试

    结论:mybatis其实是将参数list集合先存到map中,keylist(map.pust("list",list集合对象)),所以

    collection="list"中的属性值必须是list,就相当于从map中取出list遍历

    2.3 参数是数组- 和参数是list几乎一样

    IUserDao.xml中配置查询信息

    id="findUserByList" resultType="user"> SELECT * FROM USER and id in( collection="list" item="id" separator=","> #{id} )

    //动态sql:查询list集合中指定iduser信息

    @Test

    public void testFindUserByList(){

    //创建list集合并存储要查的用户id

    List<Integer> ids=new ArrayList<Integer>();

    ids.add(41);

    ids.add(43);

    ids.add(48);

    ids.add(50);

    //查询符合条件的用户信息

    List<User> users = userDao.findUserByList(ids);

    for (User user : users) {

    System.out.println(user);

    }

    }在测试类中测试

    结论:查询参数是Array与查询参数是list集合的原理是一样的,都是现将对象存到map集合中,只不过参数

    Array时,存到map集合中的keyarray,所以collection="array"的值必须是array

    2.4 多个参数情况以及@Param注解

    IUserDao中定义查询方法

    IUserDao.xml中配置查询信息

    id="findUserByArray" resultType="user"> SELECT * FROM USER and id in( collection="array" item="id" separator=","> #{id} )

    //动态sql:查询数组中指定iduser信息

    @Test

    public void testFindUserByArray(){

    //创建list集合并存储要查的用户id

    Integer[] ids={41,43,48,50};

    //查询符合条件的用户信息

    List<User> users = userDao.findUserByArray(ids);

    for (User user : users) {

    System.out.println(user);

    }

    }

    /**

    * 多个参数:根据用户名和性别查询用户信息

    * @Param("username") :定义参数的别名,将来在映射文件中使用

    */

    public List<User> findUserByUsernameAndSex(@Param("username") String username,@Param("sex")

    String sex);

    }

    id="findUserByUsernameAndSex" resultType="user"> SELECT * FROM USER where username like #{username} and sex= #{sex} 在测试类中测试

    结论:最好是结果@Param注解一起使用,映射文件中#{username}使用的变量名就是 @Param("username")注解的参数值 3.mybatis注解开发-动态sql 3.1 方式1:在select注解中添加script脚本 在在IUserMapper方法上定义注解 测试类测试 /** * 多个参数:根据用户名和性别查询用户信息 */ @Test public void testFindUserByUsernameAndSex(){ List<User> users = userDao.findUserByUsernameAndSex("%王%", "女"); for (User user : users) { System.out.println(user); } } /** * 动态sql:查询list集合中指定id的user信息 */ @Select({""}) public List<User> findUserByList(List<Integer> ids); //动态sql:查询list集合中指定id的user信息 @Test public void testFindUserByList(){ //创建list集合并存储要查的用户id List<Integer> ids=new ArrayList<Integer>(); ids.add(41); ids.add(43); ids.add(48); ids.add(50);总结:其实就是将xml映射文件中的配置拿过来用了,也是支持的。适用于带in的查询 3.1 方式2:字符串拼接(了解) 定义sql提供者UserMapperProvider类 在IUserMapper方法上定义注解 测试类测试 //查询符合条件的用户信息 List<User> users = userMapper.findUserByList(ids); for (User user : users) { System.out.println(user); } } /** * 为注解开发动态sql查询提供sql */ public class UserMapperProvider { /** * 动态sql:根据map集合中的条件,查询用户信息 * @param map 查询条件 * @return 根据条件返回sql */ public String findUserByMap(Map<String,Object> map){ StringBuilder sb=new StringBuilder("select * from user where 1=1 "); if(map.get("username")!=null){ sb.append(" and username like #{username}"); } if(map.get("sex")!=null){ sb.append(" and sex = #{sex}"); } return sb.toString(); } } public interface IUserMapper { /** * 动态sql:根据map集合中的条件,查询用户信息 */ @SelectProvider(type=UserMapperProvider.class,method = "findUserByMap") public List<User> findUserByMap(Map<String,Object> map); }3.2 方式3:使用mybatis中提供的SQL对象动态构建sql语句 修改UserMapperProvider提供者中的fifindUserByMap方法 4.mybatis使用Redis做二级缓存 总结 使用xml配置完成CURD操作 环境 jar包坐标 //动态sql:根据map集合中的条件,查询用户信息 @Test public void testFindUserByMap(){ //创建Map集合并封装查询参数 Map<String,Object> map=new HashMap<String,Object>(); map.put("username","%王%"); map.put("sex","女"); //查询符合条件的用户信息 List<User> users = userMapper.findUserByMap(map); for (User user : users) { System.out.println(user); } } public String findUserByMap(Map<String,Object> map){ return new SQL(){ //静态代码块 { SELECT("*"); FROM("user"); if (map.get("username")!=null) { WHERE("username like #{username}"); } if (map.get("sex")!=null) { AND(); WHERE("sex = #{sex}"); } } }.toString(); } 配置文件 单表查询 org.mybatis mybatis 3.4.5 mysql mysql-connector-java 5.1.38 junit junit 4.10 log4j log4j 1.2.12 测试多表查询 一对多 多表查询 多对多 使用annotation完成CURD操作 环境 与xml相同 单表查询多表查询 一对多

    展开全文
  • WPF学习

    万次阅读 多人点赞 2019-03-05 22:00:17
    标记扩展也是为属性赋值,只不过依赖于其他对象属性值。尽管很方便,只有MarkupExtension类的派生类才能使用标记扩展语法。 ,Path=Value,Mode=OneWay}" Margin="5"/> 属性元素也可以做到,比较复杂...
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    C#基础教程-c#实例教程,适合初学者。...当然仅靠一章的内容就完全掌握C#语言是可能的,如需进一步学习C#语言,还需要认真阅读有关C#语言的专著。 1.1 C#语言特点 Microsoft.NET(以下简称.NET)框...
  • JavaWeb

    千次阅读 多人点赞 2017-12-26 09:09:24
    包装的对象中需要获得被包装对象的引用,缺点:如果接口的方法比较多,增强其中的某个方法,其他的功能方法需要原有调用如bufferedinputstream和inputstream 3、动态代理:被增强的对象实现接口就可以 手动的抽取...
  • 史上最全面Java面试汇总(面试题+答案)

    万次阅读 多人点赞 2018-07-06 14:09:25
    void wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。 31.sleep和wait的区别(考察的方向...
  • 我们的项目导出WebGL后运行了,因为我们使用了System.Reflection.Assembly.GetExecutingAssembly().CreateInstance 来根据类名创建对象。解决方法是创建一个工厂类,根据类名调用不同的代码new对象。而然这样很...
  • 2.7 列表对象的 remove() 方法删除首次出现的指定元素,如果列表中存在要删除的元素,则抛出异常。 2.8 假设列表对象 aList 的值为 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] ,那么切片 aList[3:7] 得到的值是 ...
  • Jquery对象常用方法

    千次阅读 2018-04-20 11:19:06
    一、JQuery对象的基本方法:(1) get(); 取得所有匹配的元素(2) get(index); 取得其中一个匹配的元素 $(this).get(0) 等同于 $(this)[0](3) Number index(jqueryObj); 搜索子对象(4) each(callback); 类似foreach,...
  • Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别! 1,Assembly.Load() 这个方法通过程序集的长名称(包括程序集名,版本信息,语言文化,公钥标记)来加载程序集的,会加载此...
  • (1)Name属性:用来获取设置窗体的名称,在应用程序中可通过Name属性来引用窗体。  (2) WindowState属性: 用来获取设置窗体的窗口状态。 取值有三种: Normal (窗体正常显示)、 Minimized (窗体以最小...
  • JavaBean对象属性copy

    千次阅读 2019-07-15 20:37:12
      在日常编码中,经常会遇到DO、DTO对象之间的转换,如果对象本身的属性比较少的时候,那么采用Hard Code工作量也不大,但如果对象属性比较多的情况下,Hard Code效率就比较低。这时候就要使用其它工具类来进行...
  • FileReader、Blob、File对象 1.1–FileReader对象 隶属于window对象,创建方式: var xxx = new FileReader(); 该对象拥有的方法 FileReader.abort() 中止读取操作。在返回时,readyState属性为DONE。 ==下列方法为...
  • WebBrowser常用属性方法介绍

    千次阅读 2016-10-25 20:54:11
    WebBrowser 常用属性方法 ■■方法 ==============================  ▲GoBack 相当于IE的“后退”按钮,使你在当前历史列表中后退一项  ▲GoForward 相当于IE的“前进”按钮,使你在当前历史列表中前进...
  • Python实现支持JSON存储和解析的对象

    千次阅读 2014-11-28 13:47:32
    本文就在前文“ Python JSON模块”的基础上,实现python支持JSON存储的对象对象能够采取JSON存储和解析是有很大意义的。例如机器学习中所有分类算法的训练过程中都存在大量的数据计算,如果每次启动分类都需要...
  • 前言 ...由于QML引擎与Qt元对象系统的紧密集成,可以从QML代码访问由QObject衍生的类适当公开的任何功能。这使得不用做太多改动,C ++数据和函数可以直接从QML访问。 任何QML代码都可以访问QObject...
  • python特殊属性方法的运用

    万次阅读 2012-05-15 13:51:45
    用__dict__把dict转换为对象属性 1 class Messenger: 2 def __init__(self, **kwargs): 3 self.__dict__ = kwargs 4 5 6 m = Messenger(info="some information", b=[’a’, ’list’]) 7 m.more = ...
  • 上一次我们通过File API 里面的 FileReader类型里的readAsText,readAsDataURL等方法来读取文件File。但是如果一个文件十分大的时候,或者只需要读取部分内容,如(文本文件),那么我们就可以通过这次介绍的slice方法对...
  • JS导出Word——修正jquery.wordexport.js不支持IE8问题

    千次阅读 热门讨论 2017-03-13 22:39:57
    由于IE8不支持Blob类型、Canvas类型,致使jquery.wordexport.js无法使用,在此记录一下该问题的解决方式 1、canvas 1)不支持问题 使用excanvas.js可以解决游览器不支持canvas问题,下载地址为:...
  • 3.pymysql .connect属性方法(tcy)

    千次阅读 2019-01-27 09:40:43
     connect() 方法生成一个 connect 对象, 我们通过这个对象来访问数据库  2.connect对象方法: 2 begin() 作用:开启一个事务,类似于BEGIN TRANSACTION 3 commit()将任何挂起的事务提交到数据库 作用:提交...
  • 今天结合自己以前搜集的一些资料简单总结下Winform窗体常用的控件使用及其属性方法、事件等…… 一、按钮(Button)控件  几乎存在于所有Windows对话框中,是Windows应用程序中最常用的控件之一。按钮控件允许...
  • 服务路径 (注意:1.0不支持自定义路径,总是使用接口名,如果有1.0调2.0,配置服务路径可能兼容) 1.0.12以上版本 delay delay int 可选 0 性能调优 延迟注册服务时间(毫秒) ,设为-1时,表示延迟到Spring容器初始...
  • StackMap属性解析

    千次阅读 2019-07-01 17:59:12
    由于我们会在后续的文章介绍class验证环节,其中在校验方法时需要使用到StackMap....为了支持新算法,Class文件从版本50开始添加了一个新的属性表,叫做StackMapTable,里面记录的是一个方法中操作数栈与局部变...
  • 标准安装的Python中用列表(list)保存一组值,可以用来当作数组使用,不过由于列表的元素可以是任何对象,因此列表中所保存的是对象的指针。这样为了保存一个简单的[1,2,3],需要有3个指针和三个整数对象。对于数值...
  • spring中解析元素最重要的一个对象应该就属于 BeanDefinition了;这个Spring容器中最基本的内部数据结构;它让xml文件中所有的配置有了一个归属的地方;在xml中存在的配置都能在BeanDefinition找到对应的属性;我们...
  • 什么问题 WEB开发过程中,很多时候我们都是写一些简单的Demo,并...双击HTML文件,直接在浏览器中运行演示如果此时Demo中有AJAX操作,浏览器就会报一个错:XMLHttpRequest cannot load file:///Users/iceStone/Documen
  • 认识JVM--第二篇-java对象内存模型

    千次阅读 2011-07-03 23:57:14
    前一段写了一篇《认识JVM》,不过在一些方面可以继续阐述的,在这里继续探讨一下,本文重点在于在heap区域内部对象之间的组织关系,以及各种粒度之间的关系,以及JVM常见优化方法,文章目录如下所示: 1、回顾--...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 79,803
精华内容 31,921
关键字:

对象不支持loadfile属性或方法