精华内容
下载资源
问答
  • 2021-01-14 21:39:13

    python是面向对象的,第一个公开发行版发行于1991年,Python语法简洁而清晰,具有丰富和强大的类库。

    Python是一种面向对象、解释型计算机程序设计语言,由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年,Python 源代码同样遵循 GPL(GNU General Public License)协议。Python语法简洁而清晰,具有丰富和强大的类库。

    面向对象和面向过程的区别

    一、面向过程

    1、面向过程:核心是过程二字,过程指的是解决问题的步骤,好比如设计一条流水线,是一种机械式的思维方式。

    就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。

    2、优缺点:

    优点:将复杂的问题流程化,进而简单化。

    缺点:扩展性差

    3、实例:面向过程式的登录注册程序

    View Code

    注意:一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便。

    二、面向对象

    1、面向对象:核心是对象二字,特征和技能的结合体。

    2、优缺点:

    优点:可扩展性高

    缺点:编程复杂度高

    3、应用场景:用户需求经常变化,互联网应用,游戏,企业内应用等。

    4、关于面向对象的几个名词解释

    类:一个类即是对一系列对象相似的特征和技能的结合体,如同一个模板。在类中定义了这些对象的都具备的属性,共同的方法。

    属性:人类包含很多特征,把这些特征用程序来描述的话,叫做属性,比如年龄、身高、性别、姓名等都叫做属性,一个类中,可以有多个属性。

    方法:人类不止有身高、年龄、性别这些属性,还能做好多事情,比如说话、走路、吃饭等,相比较于属性是名词,说话、走路是动词,这些动词用程序来描述就叫做方法。

    实例(对象):一个对象即是一个类的实例化后的实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同。

    实例化:把一个类转变为一个对象的过程就叫实例化。

    5、简单实例# 现实世界中的对象:

    '''

    对象1

    特征

    职业:学生

    姓名:王二

    性别:男

    年龄:22

    技能

    学习

    打游戏

    对象2

    特征

    职业:学生

    姓名:张三

    性别:男

    年龄:20

    技能

    学习

    打游戏

    看书

    对象3

    特征

    职业:学生

    姓名:婉婉

    性别:女

    年龄:18

    技能

    学习

    打游戏

    看书

    现实中的类:

    特征:

    职业:学生

    技能:

    学习

    打游戏

    看书

    在程序中:

    '''

    # 先有类

    class School_learn:

    job = 'student'

    def study(self):

    print('study hard!')

    def play_game(self):

    print('play games as little as you can')

    def read(self):

    print('read books more and more')

    # 再调用类产生对象

    student1 = School_learn()

    student2 = School_learn()

    student3 = School_learn()

    print(student1) # <__main__.school_learn object at> 对象

    print(student2)

    print(student3)

    三、面向对象三大特性

    <1>封装(Encapsulation):在类中对数据的赋值、内部调用对外部用户来说是透明不可见的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法。

    <2>继承(Inheritance):一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。

    <3>多态(Polymorphism):多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。

    注意:这里只是简单说明下几大特性,后续我们会深入探讨的的。更多相关免费学习推荐:python视频教程

    更多相关内容
  • java面向对象

    万次阅读 多人点赞 2018-08-21 16:51:59
    包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。 1、面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是...

    本文内容是根据毕晓东老师的视频教程总结而得。包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。

    1、面向对象

    • 面向对象是相对面向过程而言
    • 面向对象和面向过程都是一种思想
    • 面向过程强调的是功能、行为
    • 面向对象:将功能封装进对象,强调具备了功能的对象
    • 面向对象是基于面向过程的

    面向过程例子:

    把大象放进冰箱里分为以下步骤:把冰箱门打开;把大象放进去;关上冰箱门(强调过程和过程中所涉及的行为(强调行为、动作、过程))。

    用面向对象思想考虑:无论是打开冰箱,放进大象,关闭冰箱,所有操作都是操作冰箱这个对象,所以只需要将所有功能都定义在冰箱这个对象上,冰箱上就有打开、存储、关闭得所有功能 。      

    由上可知,面向对象是一种思想,能让复杂问题简单化,程序员不需要了解具体的实现过程,只需要指挥对象去实现功能。例,面试官面试面试者就是面向对象的体现,面试官需要找具有编程功能的对象(面试者),而我就是一个具有编程功能的对象,面试完后,让面试者去编程,面试者就去实现编程功能。

    面向过程和面向对象图示:

    2、类与对象的关系

    面向对象三大基本特征:封装、继承、多态。而面向对象的过程就是找对象、建立对象、使用对象、维护对象的关系的过程。

    2.1类和对象的关系:

    类:是对现实生活中事物的描述。

    对象:就是这类事物,实实在在存在的个体。

    如现实生活中的对象:张三、李四。想要描述对象张三和李四,就需要提取对象中的共性内容。即对具体对象的共性的抽取。在描述时,这些对象的共性有:姓名、性别、年龄、学习java功能。而每个学员又有自己独有的姓名、性别、年龄、学习方式。

    在java中,描述是用类的方式实现,而类是通过new操作符所产生的实体来实现,而这个实体在堆内存中再映射到java中去。简单的说,描述就是class定义的类,具体对象就是对应java在堆内存中用new建立的实体

    描述事物其实就是在描述事物的属性和行为(方法),属性对应的是类中的变量,行为对应的是类中的函数(方法)。其实定义类,就是在描述事物,就是在定义属性和行为,属性和行为共同成为类中的成员(成员变量和成员方法)。

    示例:描述汽车。

    package com.vnb.javabase;
    public class Car {
       //描述颜色
       String color="red";
       //描述轮胎数
       int num = 4;
       //运行行为
       void run(){
          System.out.println("color:"+color+";  轮胎数:"+num);
       }
    }
    
    class CarDemo{
       public static void main(String[] args) {
          //生产汽车。在java中通过new操作符来完成,其实就是在堆内存中产生一个实体
          //car引用型变量(句柄),car就是一个类类型变量。类类型变量指向对象(该类产生的实体)
          //堆内存中有默认初始化值(color=null);而"red"称为显示初始化值;
          Car car = new Car();
    
          //需求:将已有车的颜色改为蓝色,指挥该对象做使用。在java中指挥方式是,对象.对象成员
          car.color="blue";
    
          //让车行驶起来
          car.run();
       }
    }

    生产汽车类在内存中的图示及解析:

    如上图,首先在栈内存中有一个Car c;然后在堆内存中new Car(),默认初始化color为null,num为0;再将color设置显示初始化值为”red”,将num设置显示初始化值为4;将new Car()产生的地址值0x0099赋给栈内存,再由栈指向堆中的0x0099的地址。

    生产两辆车(建立多个对象)在内存中的图示及解析:

    如上图所示,建立多个对象的过程:首先在栈中初始化c,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color赋值为”red”,将num赋值为4。将new Car()产生的地址值0x0099赋给栈内存,再由栈指向堆中的0x0099的地址。

    在栈中初始化C1,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color赋值为”red”,将num赋值为4。将new Car()产生的地址值0x0045赋给栈内存,再由栈指向堆中的0x0045的地址。

    此处产生的是两个对象对应的两个地址值。

    多个引用指向同一个对象的图示及解析:

    如上图所示,首先在栈中初始化c,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color设置显示初始化值为”red”,将num设置显示初始化值为4。将new Car()产生的地址值0x0078赋给c的栈内存,再由栈指向堆中的0x0078的地址。再将堆中num的值改为5(因为成员变量在堆内存中);

    建立第二个引用时,首先在栈中初始化c1,然后将c的地址值0x0078赋给c1,并且c1指向堆中的0x0078所引用的对象,再将堆中的对象的color改为green。由此,无论在c还是c1引用上修改值后,指向的都是堆内存中的对象地址值0x0078。所以打印出的值为:5,green。

    2.2成员变量和局部变量的区别:

    作用范围:成员变量作用于整个类中。局部变量作用于方法中或者语句中。

    在内存中的位置:成员变量在堆内存中,因为对象的存在,才在内存中存在;局部变量存在栈内存中。

    2.3匿名对象:

    匿名对象是对象的简化形式。有两种使用情况:当对对象方法仅进行一次调用时;匿名对象可以作为实际参数进行传递

    例如:

    Car c = new Car();

    c.num = 5;

    可简化为:new Car().num = 5;

                      new Car().color = “red”;

                      new Car().run();

    匿名对象和非匿名对象在内存中存储方式区别:

    如上图,匿名对象和非匿名对象区别(注:匿名对象在栈中无数据)。使用匿名对象时,当new Car().num = 5;执行完后创建的对象就成为垃圾;new Car().color = “red”执行完后,此对象也会成为垃圾,因此匿名对象调用属性是没有意义的。但是,new Car().run()有意义。

    综上所述,匿名对象调用属性无意义,调用方法有意义

    匿名对象的两种使用方式:

    • 匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样比较简化;但是,如果对一个对象进行多个成员调用,必须给这个对象起名字。例如:

    Car c =  new Car();

    c.run();

    c.num=4;

    new Car().run();

    • 匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。例如:

    需求:汽车修配厂,对汽车进行改装,将来的车都改成黑色且只有三个轮胎。

    main(){

     Car c = new Car();

     show(c);

    }

    public static void show(Car c){

    c.num = 3;

    c.color = “black”;

    c.run();

    }

    上例的内存图示及解析:

    执行main方法后,首先会再栈内存中有一个Car c的引用,然后会在堆内存中new一个Car对象,生成对应的地址值0x0034;当执行show()方法时,会在show方法中再建立一个引用c(注意连个引用不同),再将main方法中c的地址值通过匿名类参数形式传递给show()方法,因此两个引用指向的是同一个地址0x0034。匿名对象传入show()方法时,当show方法执行完后,栈中show()指向的内存空间将会成为垃圾,其对应指向的对象地址和对象也成为垃圾。

    所以就有了使用java程序写缓存机制:用java定义空间存储缓存型的数据,需要的时候就用,不需要的时候就不用。此时需保证其的生命周期,不指定则虚拟机会直接回收,而虚拟机回收垃圾是不定时的,且有些对象不会回收。所以写缓存的时候,在释放对象空间时,必须考虑对象的强引用、弱引用、软引用、虚引用。

    3、封装

    封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。(在使用对象时,没有必要知道对象内容是如何完成对应功能的,我们只需要指挥对象去执行即可)。

    封装的好处:将变化隔离(内部功能细节变化不影响使用);便于使用(不用了解内部的具体实现);提高重用性;提高安全性(只对外暴露一些简单的内容供使用)。

    封装的原则:将不需要对外提供的内容都隐藏起来;把属性都隐藏起来,只提供公共方法对其访问。

    函数本身就是java代码中的最简单的封装体。也是封装体,类中方法不一定全部暴露出去,可通过权限修饰符进行隐藏。也是封装体,如框架,拿来即用。

    函数封装体:

    封装具体示例:

    如上图,当p.age=-20时,就会出现安全隐患,人的年龄不可能为负数,所以可以使用修饰符进行控制权限。使用private int age;私有化后,类以外即使建立了对象也不能再直接访问该属性。但是属性或方法一旦私有了,就需要对外提供访问方式(即提供getter和setter方法等操作)。因此需要给这个人的年龄提供对外访问方式。

    之所以对外提供访问方式,是因为可以在这种访问方式中加入逻辑判断等语句,对访问的数据进行操作。提高代码的健壮性。如下例所示:

     在设置人的年龄时,set方法中需要满足条件,才可继续执行,否则提示非法。

    该示例在内存中表示,如下图:

    执行main()方法后,在栈内存中产生了一个p的引用,然后在堆内存中建立Person对象,并分配内存空间0x0012,并将这个地址值赋给p引用,然后由p再指向Person对象中的这个地址0x0012。new Person对象,执行到private int age时,在堆内存(成员变量在堆内存中分配空间)中产生了一个age,并有了默认初始化值0。当p引用设置年龄为40时(p.setAge(40)),会通过setAge()方法中的this.age = a(this即代表p引用)将堆中的age值改为40。当执行p.speak()时,拿到成员变量age的值40并输出(成员变量age由局部(setAge())接收进来并改变其值后,其他方法(speak())也可以用(拿到的还是setAge()执行后的40))。

    私有仅仅是封装的一种表现形式,只要权限在别人访问不到的范围都是封装。

     

    4、构造函数

    构造函数特点:

    • 函数名与类名相同
    • 不用定义返回值类型
    • 不可以写return语句

    作用:给对象进行初始化。

    构造函数示例:

    构造函数对象一建立就会调用与之对应的构造函数,可用于给对象进行初始化。当一个类中没有定义构造函数时,系统会默认给该类加入一个空参数的构造函数,当自己定义了构造函数后,默认的空构造函数就不存在了。

    注意:默认构造函数的特点;多个构造函数是以重载的形式存在的

    多个构造函数以重载形式存在的示例:

    构造代码块:

    在构造代码块中,对象一建立就立即运行,而且优先于构造函数执行。构造代码块中定义的是不同对象具有共性的初始化内容

    构造代码块和构造函数的区别:构造代码块是给所有对象进行统一初始化;而构造函数是给对应的对象初始化。

    5、this关键字

    5.1this关键字的基本阐述

    如上图,发现Person(String a)构造函数中,a变量是无意义的,不能见名知意。因此将a改成name表示姓名,即局部变量和成员变量名称相同。但如下图,发现执行结果name并没有赋值成功:

     

    因此如何区分局部和成员变量?即使用this关键字。

    如上图,this关键字看上去,是用于区分局部变量和成员变量同名情况。其实,this代表本类的对象。

    那么this到底代表哪一个对象?如下图:

    this代表它所在函数所属对象的引用。简单的说,哪个对象在调用this所在的函数,this就代表哪个对象。例如,当执行到Person p = new Person("lisi");时,建立Person对象,执行到Person对象的构造函数Person(String name)后,此时的this代表的是p引用。而当代码执行到Person p1 = new Person("zhangsan");时,执行到Person对象的构造函数Person(String name)后,this代表的是p1引用。

    5.2this关键字的应用:

    需求:给人定义一个判断是否是同龄人的功能。

    如上图,this和p1的地址值是指向同一个对象。当执行到boolean b = p1.compare(p2);时,会调用到compare(Person p)方法,此时,p2会以参数形式传给compare()方法,而由于是由p1调用的compare()方法,所以p1引用即当前对象this引用。即his和p1的地址值是指向同一个对象。

    this的应用:当定义类中方法时,该方法内部要用到调用该方法的对象时,这时用this表示这个对象。但凡本类功能内部使用到了本类对象,都用this表示。

    5.3this关键字在构造函数中的应用:构造函数间调用,只能使用this进行互相调用,this函数不能用在一般函数间

    this(name);对Person对象进行姓名初始化,this代表本对象。this.name = name是将值传递过去。

    this语句(不是this关键字)只能定义在构造函数的第一行。如下图:

    初始化动作要先执行,如先执行this(name)必须在this.name=name前才行。

    6、static关键字

    6.1概述

    static关键字:用于修饰成员(成员变量和成员函数)

    被修饰后的成员具备以下特点:

    • 随着类的加载而加载(类一加载到内存中时,静态static就已经加载到内存空间(方法区)中,反之随着类的消失而消失,说明它的生命周期最长)
    • 优先于对象存在(静态是先存在的,对象是后存在的)
    • 被所有对象所共享
    • 可以直接被类名调用

    使用注意:

    • 静态方法只能访问静态成员
    • 静态方法中不可以写this,super关键字
    • 主函数是静态的

    6.2为什么要使用静态static关键字?

    如以上两图所示,创建了两个对象引用P和P1,如果只在中国范围内,而不使用静态关键字static,则需要在堆内存中开辟两个country=”cn”空间(多个空间存在共同数据),那么对象创建得越多占用的内存就越多,如下图,将country=”cn”定义成静态的,则只需要在方法区中定义一次即可(static String country = “CN”),如下图:

    方法区(共享区、数据区):存放共享数据,方法及方法体等内容。

    当成员(包括成员变量和方法)被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。格式:类名.成员。

    静态static优先于对象的存在,即静态会随着类的加载而加载,消失而消失(说明生命周期最长)。静态的成员变量也叫类变量,非静态的成员变量也叫实例变量。

    6.3为什么不把所有的成员都定义为静态static?

    要区分什么数据是对象特有的,什么数据是多个对象共享的,这样才符合生活中的描述;对象在被用完后会被回收,但是静态static的数据生命周期特别长,用完后还会一直存在,因此会存在垃圾

    6.4实例变量和类变量的区别

    • 存放位置:类变量(静态成员变量)随着类的加载而存在于方法区中;实例变量随着对象的建立而存在于堆内存中
    • 生命周期:类变量生命周期最长,随着类的消失而消失;实例变量生命周期随着对象的消失而消失。

    6.5静态的使用注意事项

    • 静态方法只能访问静态成员(成员变量和方法),非静态方法既可以访问静态成员也可以访问非静态成员

    • 静态方法中不可以定义this,super关键字。因为静态优先于对象存在

    如下图,this.name不可用,this代表对象,而静态方法优先于对象创建,在执行静态方法show()时,对象Person还未创建,所以此时this 还未初始化过,所以不可用。

    6.6静态有利有弊

    利:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中存储一份;可以直接被类名调用。

    弊:生命周期过长(可能会有垃圾不能被回收);访问出现局限性(静态虽好,但只能访问静态)。

    6.7主函数中的静态

    主函数是静态的:主函数是一个特殊的函数,作为程序的入口,可以直接被JVM调用。

    主函数的定义:

    • public:代表着该函数访问权限是最大的
    • static:代表主函数随着类的加载就已经存在了
    • void:代表主函数没有具体的返回值返给虚拟机JVM
    • main:不是关键字,但是是一个特殊的单词,可以被JVM识别,且是主函数特有的。
    • String[] args: 函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组。

    主函数是固定格式的:JVM识别

    public static void main(int x){}也可以写(主函数被重载),但是JVM会优先从public static void main(String[] args){}开始执行。JVM在调用主函数时,传入的是 new String[0],作用:可以往参数里传入数据,在主函数里就可以拿到参数。

    传递参数给主函数并由主函数获取:

    • 使用命令传: java 类名 参数

    • 通过另一个类的主函数传值,然后由测试类主函数进行获取:

    6.8什么时候使用静态?

    要从两个方面下手:因为静态修饰的内容包括成员变量和成员函数。

    什么时候定义静态变量(类变量)?

    当对象中出现共享数据时,该数据需要被静态所修饰;而对象中的特有数据要定义成非静态存在堆内存中。

    6.9什么时候定义静态函数?

    当功能内部没有访问到非静态数据(对象的特有数据(非静态成员变量))时,该功能可以定义成静态的。

    例:对象是为了封装数据的,但是下例中对象里的特有数据name并没有使用到,所以show()方法可以使用静态。

    class Person{
    String name;
    public static void show(){
      System.out.println(“haha”);
    
    }
    
    public static void main(String[] args){
        Person.show();
       }
    }

    但是下例中有使用到对象中的非静态成员name,所以show()方法绝对不能使用静态:

    class Person{
    String name;
    public static void show(){
      System.out.println(name+“haha”);//因为静态先于对象存在,在创建静态方法show时,对象Person还不存在,所以name也不存在
    }
    
    public static void main(String[] args){
     Person p = new Person();
     p.show();
    }
    }

    6.10静态的应用—工具类

    如上图,一个获取数组中最大值的类。可以发现,如果有很多类,都需要使用到获取数组最大值这个方法时,每个类都需要写一遍这些代码,因此可以将获取数组最大值的代码统一封装到一个静态工具类中,需要使用时调用即可。如下图所示:

    因此,静态工具类即将每一个应用程序中都有共性的功能,进行抽取,独立封装,以便复用。

    原始的ArrayTool.java类:

    package com.vnb.javabase;
    /**
     * 
     * Description:数组工具类
     * @author li.mf
     * @date 2018年8月15日
     *
     */
    public class ArrayTool {
    
    	/**
    	 * 获取数组最大值
    	 * @param arr
    	 * @return
    	 */
    	public int getMax(int[] arr){
    	
    		int max = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[max]<arr[i]){
    				max = i;
    			}
    		}
    		return arr[max];
    	}
    	
    	/**
    	 * 获取数组最小值
    	 * @param arr
    	 * @return
    	 */
    	public int getMin(int[] arr){
    		
    		int min = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[min]>arr[i]){
    				min = i;
    			}
    		}
    		return arr[min];
    	}
    	/**
    	 * 选择排序
    	 * @param arr
    	 */
    	public void selectSort(int[] arr){
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					swap(arr,i,j);
    				}
    			}
    		}
    		
    	}
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public void bubbleSort(int[] arr){
    		for (int i = 0; i < arr.length; i++) {
    			for (int j = 0; j < arr.length-i-1; j++) {
    				if(arr[j]>arr[j+1]){
    					swap(arr,j,j+1);
    				}
    			}
    		}
    		
    	}
    	
    	public void swap(int[] arr,int a,int b){
    		int temp = arr[a];
    		arr[a] = arr[b];
    		arr[b] = temp;
    	}
    	
    	public void printArray(int[] arr){
    		System.out.print("{");
    		for (int i = 0; i < arr.length; i++) {
    			if(i!=arr.length-1){
    				System.out.print(arr[i]+",");
    			}else{
    				System.out.print(arr[i]+"}");
    			}
    		}
    	}
    }
    

    数组工具类的测试类ArrayToolTest.java:

    package com.vnb.javabase;
    
    public class ArrayToolTest {
    
    	public static void main(String[] args) {
    
    		int[] arr = {1,2,6,8,3};
    		
    		ArrayTool arrayTool = new ArrayTool();
    		int max = arrayTool.getMax(arr);
    		System.out.println("最大值为:"+max);
    		
    		int min = arrayTool.getMin(arr);
    		System.out.println("最小值为:"+min);
    		
    		arrayTool.printArray(arr);
    		arrayTool.selectSort(arr);
    		arrayTool.printArray(arr);
    	}
    }
    

    由例可见,ArrayTool类都没有用到ArrayTool中特有的数据(非静态的成员变量),都是由用户传参的arr进去,所以可以写成static。虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。但是,对象是用于封装数据的,而ArrayTool对象并未封装特有数据;操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。所以,这时就可以考虑,让程序更严谨而不需要对象。可以将ArrayTool中的方法都定义成static的,直接通过类名调用即可。

    ArrayTool.java中所有方法写成静态,以便调用:

    package com.vnb.javabase;
    /**
     * 
     * Description:数组工具类
     * @author li.mf
     * @date 2018年8月15日
     *
     */
    public class ArrayTool {
    
    	/**
    	 * 获取数组最大值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMax(int[] arr){
    	
    		int max = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[max]<arr[i]){
    				max = i;
    			}
    		}
    		return arr[max];
    	}
    	
    	/**
    	 * 获取数组最小值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMin(int[] arr){
    		
    		int min = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[min]>arr[i]){
    				min = i;
    			}
    		}
    		return arr[min];
    	}
    	/**
    	 * 选择排序
    	 * @param arr
    	 */
    	public static void selectSort(int[] arr){
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					swap(arr,i,j);
    				}
    			}
    		}
    		
    	}
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public static void bubbleSort(int[] arr){
    		for (int i = 0; i < arr.length; i++) {
    			for (int j = 0; j < arr.length-i-1; j++) {
    				if(arr[j]>arr[j+1]){
    					swap(arr,j,j+1);
    				}
    			}
    		}
    		
    	}
    	
    	public static void swap(int[] arr,int a,int b){
    		int temp = arr[a];
    		arr[a] = arr[b];
    		arr[b] = temp;
    	}
    	
    	public static void printArray(int[] arr){
    		System.out.print("{");
    		for (int i = 0; i < arr.length; i++) {
    			if(i!=arr.length-1){
    				System.out.print(arr[i]+",");
    			}else{
    				System.out.print(arr[i]+"}");
    			}
    		}
    	}
    }
    

    ArrayToolTest.java

    package com.vnb.javabase;
    
    public class ArrayToolTest {
    
    	public static void main(String[] args) {
    
    		int[] arr = {1,2,6,8,3};
    		
    		int max = ArrayTool.getMax(arr);
    		System.out.println("最大值为:"+max);
    		
    		int min = ArrayTool.getMin(arr);
    		System.out.println("最小值为:"+min);
    		
    		ArrayTool.printArray(arr);
    		ArrayTool.selectSort(arr);
    		ArrayTool.printArray(arr);
    	}
    }
    

    将方法都写成静态后,可以方便与使用,但是仍然发现问题,即该类还是可以被其他程序建立对象的。所以为了更为严谨,强制该类不可以实例化以建立对象,所以可以考虑通过将构造函数私有化完成(私有构造)。

    package com.vnb.javabase;
    /**
     * 
     * Description:数组工具类
     * @author li.mf
     * @date 2018年8月15日
     *
     */
    public class ArrayTool {
    
    	private ArrayTool(){
    		
    	}
    	/**
    	 * 获取数组最大值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMax(int[] arr){
    	
    		int max = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[max]<arr[i]){
    				max = i;
    			}
    		}
    		return arr[max];
    	}
    	
    	/**
    	 * 获取数组最小值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMin(int[] arr){
    		
    		int min = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[min]>arr[i]){
    				min = i;
    			}
    		}
    		return arr[min];
    	}
    	/**
    	 * 选择排序
    	 * @param arr
    	 */
    	public static void selectSort(int[] arr){
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					swap(arr,i,j);
    				}
    			}
    		}
    		
    	}
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public static void bubbleSort(int[] arr){
    		for (int i = 0; i < arr.length; i++) {
    			for (int j = 0; j < arr.length-i-1; j++) {
    				if(arr[j]>arr[j+1]){
    					swap(arr,j,j+1);
    				}
    			}
    		}
    		
    	}
    	
    	private static void swap(int[] arr,int a,int b){
    		int temp = arr[a];
    		arr[a] = arr[b];
    		arr[b] = temp;
    	}
    	
    	public static void printArray(int[] arr){
    		System.out.print("{");
    		for (int i = 0; i < arr.length; i++) {
    			if(i!=arr.length-1){
    				System.out.print(arr[i]+",");
    			}else{
    				System.out.print(arr[i]+"}");
    			}
    		}
    	}
    }
    

    ArrayToolTest.java

    package com.vnb.javabase;
    
    public class ArrayToolTest {
    
    	public static void main(String[] args) {
    
    		int[] arr = {1,2,6,8,3};
    		
    		int max = ArrayTool.getMax(arr);
    		System.out.println("最大值为:"+max);
    		
    		int min = ArrayTool.getMin(arr);
    		System.out.println("最小值为:"+min);
    		
    		ArrayTool.printArray(arr);
    		ArrayTool.selectSort(arr);
    		ArrayTool.printArray(arr);
    	}
    }
    

     

    6.11帮助文档的制作(javadoc)

    如果从别的地方拿来的java文件且不在同一目录下,则执行时会报错:找不到文件。如下图,

    所以可以在dos命令行中设置classpath:set classpath-.i;c:\myclass 。-.i;先在当前目录下找,再C盘指定目录myclass下去找。

    /** */  里面写类的描述信息、开发人、开发时间、版本号等。java通过javadoc.exe程序来进行帮助文档的制作。通过命令可以将帮助文档存放到指定位置:javadoc –d myhelp –author –version ArrayTool.java。(如果无此文件夹,会自动创建)。

    要将一个文件写出帮助文档,需要将类修饰符写为public或者protected 。以供外部读取。

    ArrayTool.java帮助文档的查看:

    从索引页面开始看即可(index.html)。

    帮助文档示例:

    一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,如果类被public修饰,那么默认的构造函数也带public修饰,如果没有,默认的空构造也没有。注意:默认的构造函数不是自己写的空构造函数。

    6.12静态代码块

    格式:

    static{
         静态代码块的执行语句
    }

    示例:

    static{
         System.out.println(“asdf”);
    }

    静态代码块特点:静态代码块随着类的加载而执行,且执行一次(类加载完后,执行完了,就已经在内存中了),用于给类进行初始化,且优先于主函数执行。

    静态代码块和主函数执行,示例:

    打印顺序:b c a。

    如上图,想让show方法执行,必须先加载StaticCodeDemo类。

    如上图,该类类变量没有任何实体指向,所以StaticCode类不会进行加载,所以没有打印结果。

    如上图,执行顺序:a c d ; 注:如果想要执行特定的构造函数需要在调用的时候进行指定,如上例new StaticCode(4);指定调用的是StaticCode(int x){}构造方法,而不是空参数的构造方法。

    • a表示静态代码块给初始化;
    • b不会执行,因为还未创建过与之对应的对象(已经指定调用其他的构造方法);
    • c表示构造代码块给对象初始化
    • d表示构造函数给对应对象初始化

    {}表示构造代码块给对象初始化的,所以可以用this

    6.13对象初始化的过程

    对象建立过程:

    当类的主函数入口main()方法执行到Person p = new Person(“zhangsan”,20);时,会有一个p引用加载到栈中:

    1)new Person()时会将Person.class文件从硬盘中通过java的虚拟机JVM加载进内存;

    2)执行静态代码块

    3)并开辟了堆内存空间。(非静态成员变量,如属性:name、age)

    4)初始化动作,构造代码块

    执行结果null,0。

    所以会先有默认初始化null和0,再有显示初始化(成员属性的初始化),如下图:

    再进行构造代码块初始化(此代码中无),再构造函数初始化(赋值zhangsan,20)。

    总结:Person p = new Person(“zhangsan”,20);的执行过程?

    1. 因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中
    2. 执行该类中的static代码块,如果有的话,给Person.class类进行初始化
    3. 在堆内存中开辟空间,分配内存地址
    4. 在堆内存中建立对象的特有属性,并进行默认初始化(对象为null,整数为0)
    5. 对属性进行显示初始化(类中声明成员变量初始化值)
    6. 对对象进行构造代码块初始化
    7. 对对象进行对应的构造函数初始化
    8. 将内存地址值赋给栈内存中的p引用
    9. p引用再指向堆内存中的地址值

    6.14对象调用成员过程

    Person p = new Person(“zhangsan”,20);

    p.setName(“lisi”);

    1)在栈中有main主函数的p引用生成

    2)然后在方法区中初始化Person类的方法(showCountry()、setName()、speak())、方法体、静态变量

    3)在堆中开辟空间

    4)在默认初始化(对象为null,整数为0)

    5)再到堆中new Person(“Zhangsan”,20)

    6)再将地址值0x0023赋给栈,再从栈中指向堆中的地址

    7)setName()在栈中开辟空间,非静态的只要一调用this就会有值(指向),this的引用用于给对象赋值(哪个对象调用就代表哪个对象,这里this代表p),即this为0x0023,即this指向堆中的0x0023,因此 setName中this.name = name一执行时,局部变量name的lisi赋值给了堆内存中的this.name。

    调用过程的内存图示:

    当再创建一个对象p1时,在内存中的调用过程,如下:

    当再创建一个对象p1后,会重新在栈内存中初始化p1引用,然后在堆内存中重新new 一个Person对象,设置默认初始化值,然后设置显示初始化值name为ahah,age为90,并开辟空间设置地址值,并将地址值赋给p1,当setName(“qq”)时,会在栈中开辟一块空间,通过this.name = name将堆内存中的name值设置为qq。

    如上图,静态方法showCountry()方法一被调用,在开辟完空间后,没有找到this关键字,所以showCountry()方法就会所属于Person类,不会再进堆内存。调用showCountry()方法时也会用“类名.showCountry()”执行里面的静态方法。

    成员只有被调用才能用,静态的本类省略了类名.method。非静态对象省略this.方法; 静态省略类名.静态方法。

    7、单例设计模式

    7.1概念

    设计模式: 解决某一类问题最行之有效的方法。java中共有23种设计模式。

    单例模式:解决一个类在内存中只存在一个对象(不能new多个实例)

    想要保证对象唯一:

    1. 为了避免其他程序过多建立该类对象,先要控制禁止其他程序建立该类对象
    2. 为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象
    3. 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式

    代码实现:

    1. 将构造函数私有化(以控制其他程序建立该类对象)
    2. 在类中创建一个本类对象
    3. 提供一个方法可以获取到该对象(方便其他程序对自定义对象的访问)

    7.2饿汉式单例模式

    package com.vnb.javabase;
    /**
     * 
     * Description:单例模式
     * @author li.mf
     * @date 2018年8月16日
     *
     */
    public class Singleton {
    	
    	private static Singleton instance = new Singleton();
    	
    	private Singleton(){
    		
    	}
    	
    	public static Singleton getInstance(){
    		
    		return instance;
    	}
    }
    
    class SingleDemo{
    	
    	public static void main(String[] args) {
    		Singleton ss = Singleton.getInstance();
    	}
    }
    

    内存中实现过程:

    再获取一次单例,发现获取的实例时一致的。如下图:

    测试是否获取的是同一个实例:

    class SingleDemo{
       public static void main(String[] args) {
          Singleton s1 = Singleton.getInstance();
          Singleton s2 = Singleton.getInstance();
          s1.setNum(20);
          System.out.println("num值为"+s2.getNum());
       }
    }

    结果发现,s2获取的值是s1 set进的值。

    单例模式,对于事物该怎么描述,还怎么描述,当需要将该事物的对象保证在内存中唯一时,就将以上者三步加上即可。

    7.3懒汉式单例模式

    package com.vnb.javabase;
    public class Single {
    	private static Single instance = null;
    	private Single(){	
    	}
    	public static Single getInstance(){
    		if(instance == null){
    			instance = new Single();
    		}
    		return instance;
    	}
    }
    

    对象在被调用时,才进行初始化,就叫做对象的延时加载,也称为懒汉式。

    如下图,Single类进内存时,对象还没有存在,而是只有调用了getInstance方法时,才建立对象。

    懒汉式单例模式在内存中表示:

    开发一般使用饿汉式,因为其简单且安全,且懒汉式当有多个人来调用这个方法时会出现问题。当A执行到if(s==null) 后挂掉了,突然去执行其他程序了,此时其他程序B来判断后new了一个对象,之后A又继续执行了,但是A并不知道B已经创建了对象,所以B又new了一个对象,这样就new了多个对象不唯一了。

    为解决上述问题:可以在方法上加同步锁synchronized

    package com.vnb.javabase;
    public class Single {
    	private static Single instance = null;
    	private Single(){	
    	}
    	public static synchronized Single getInstance(){
    		if(instance == null){
    			instance = new Single();
    		}
    		return instance;
    	}
    }
    

    但是加上同步锁后,每次进getInstance()方法后都要执行同步锁,程序的效率会变低。

    为解决低效率问题:可以使用双重判断形式(多线程时进行讲解),使用同步代码块。如下图。但是此方法代码会较多。所以定义单例时建立使用饿汉式。

    package com.vnb.javabase;
    public class Single {
    	private static Single instance = null;
    	private Single(){	
    	}
    	public static Single getInstance(){
    		if(instance == null){
    			synchronized (Single.class) {
    				if(instance == null){
    					instance = new Single();
    				}
    			}
    		}
    		return instance;
    	}
    }
    

    8、继承

    本节主要内容:继承的概述;继承的特点;super关键字;函数覆盖;子类的实例化过程;final关键字。

    8.1继承的概述

    有以下两个类:

    将学生和工人两个类的共性描述提取出来,单独进行描述,只要让学生和工人与单独描述的这个类有关系,就可以了。

     

    继承的好处:提高了代码的复用性;继承让类与类之间产生了关系,有了这个关系,才有了多态的特性。

    注意:千万不能为了获取其他类的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。所属关系即 is a。java语言中,只支持单继承,不支持多继承,因为多继承容易带来安全隐患(接口与接口之间可以多继承(因为接口之间没有方法体,方法之间不会冲突))。

    如上图,继承多个类,当多个父类中定义了相同功能,当功能内容不同时,子类对象不知道运行哪个功能,就会出现问题。但是java保留另一种体现形式来完成这种表示,叫接口多实现。

    java支持多层继承。也就是一个继承体系。如下图所示:

    C类也可以使用到A类中的功能。

    如何使用一个继承体系中的功能呢?

    想要使用体系,先查阅体系中父类的描述,因为父类中定义的是该体系中的共性功能,通过了解共性功能,就可以知道该体系的基本功能。也可以基本使用了。在具体调用时,为什么要创建最子类的对象?一是因为有可能父类不能创建对象;二是创建子类对象可以使用更多的功能,包括父类的也包括特有的。简单的一句话:查阅父类功能,创建子类对象使用功能。

    8.2类与类之间关系—聚集关系(组合关系)

    事物之间不一定是继承关系,也有聚集关系 has a,简单的说就是谁里面有谁。

    聚合:球员和球队(球队中有球员)

    组合:事物之间的联系比聚合关系更高,比如,心脏和人;手和人,心脏和手必不可少。

    8.3子父类中变量的特点

    以上当父类子类成员变量相同时,打印的是4,因为子类中的num 即为this.num。要打印父类的变量,使用super.num即可,打印子类的变量直接使用num即可。

    new 子类class文件时,会先加载父类的class文件

    变量:如果子类中出现非私有的同名变量时,子类要访问本类中的变量,用this;子类要访问父类中的同名变量,用super。super的使用和this的使用几乎一致,this代表本类对象的引用;super代表父类对象的引用。

    如上图,num前省略super和this打印结果都是4,原因:子类继承父类,子类也能拿到父类的num 4(相当于子类里有自己的num),所以用子类自己的this能拿到num=4;super是指父类引用,this是指本类引用,由于现在没有父类对象只有子类对象(Zi z = new Zi()),所以现在super和this引用指向的是同一个对象(Zi子类对象)即new Zi(),所以只有一个num=4,即同一个子类对象的num(这就是面向对象的多态即父类引用指向子类对象List list = new ArrayList())。

    8.4子父类中函数的特点—覆盖(重写)

    如上图,若子父类中的方法一致,子类运行就会覆盖了父类中的方法。子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,如同父类的函数被覆盖一样,这种情况是函数的另一个特性,重写(覆盖)。

    当子类继承了父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但是功能的内容却和父类却不一致,这时,就没有必要定义新功能,而是使用覆盖特性,保留父类功能定义,并重新父类内容。这就叫重写,也叫做覆盖。如下图:

    用重写特性提高程序扩展性:

    new的时候只要new NewTel即可,而不需要修改Tel类的任何内容,但是重写时会有代码重复以实现父类原有的功能,所以只要使用super调用原有内容即可。如下图:

    覆盖:子类覆盖父类,必须保证子类权限要大于或等于父类权限,才可以覆盖,且父类不能为private修饰(如果是private修饰,子类都不知道父类有此方法存在),否则编译失败;静态只能覆盖静态(虽然此做法无意义);

    重载和重写的区别:

    • 重载只看同名函数的参数列表;
    • 重写是子父类方法要一模一样包括返回值类型。

    如上图,是不允许存在的。因此重写要求子父类中的方法名和返回值类型必须相同。

    8.5子父类中构造函数的特点—子类实例化过程

    子父类构造函数不能覆盖,因为覆盖必须函数名和返回值等都一模一样,但是构造函数的名字必须和类名一致;子类的构造函数有一句隐示的语句super();如下图:

    在对子类对象进行初始化时,父类构造函数也会运行,是因为子类的构造函数默认第一行有一条隐式的语句super(),super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。如下图所示:

    如下图,当父类中没有空参数的构造时,系统会报错,所以必须手动指定子类引用父类的哪个非空的构造函数。

    为什么子类一定要访问父类中的构造函数?见下图:

    总结:(如上图)因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在对象初始化时,要先访问一下父类中的构造函数,如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式进行指定。如上图,在建立子类对象后后去查看父类有没有改变num的值,这里父类已经将num值设置为60,所以子类的num也对应改了,最后打印的z.num即父类中设置的60。

    应用:

    父类已经给name赋过值了,子类继承父类后,直接调用(super(name))父类的方法即可获取到父类中设置的值。

    总结:父类已经定义过的方法属性,子类想要使用,直接通过super即可拿到

    如下图,super()语句必须写在子类构造函数的第一行,因为需要先把父类的数据初始化完成后,再初始化自己的。

    当子类构造函数中写了this()方法后,就不能再用super()了,this和super不能同时存在。此时zi(int x)中的this()会访问本类中的zi()空构造,而zi()构造函数中默认有super()就能调用到父类中的属性和方法。

    结论(子类的实例化过程):子类中所有的构造函数,默认都会访问父类中的空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式的super(),当父类中没有空参数的构造函数时,子类必须手动通过super语句或者this语句形式来指定要访问的父类中的构造函数。子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少要有一个构造函数会访问父类中的构造函数(没写也会有默认的)。

    this和super为什么不能同时存在?因为this和super都必须存在第一行。

    为什么this和super都必须存在第一行?因为初始化动作必须先做。

    8.6 final关键字

    final关键字:

    • 作为一个修饰符,可以修饰类、函数、变量。
    • 被final修饰的类不可以被继承(继承的弊端,打破了封装性,可以复写掉父类的东西,很多基类就会很不安全,可能会被修改),为了避免被继承,被子类复写功能,在类上写final即可(叫做最终类,最终类是不可以被继承的)。
    • 被final修饰的方法,不可以被复写。

    1. 被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量,也可以修饰局部变量。如下图:

    只要在内存中,x和y的值都是固定的,不可以更改。

    当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便阅读,而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有的字母都大写,如果有多个单词组成,单词间通过下划线连接。

    2.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。

    8.7抽象类

    如上图,发现两个方法都一致,所以需要抽取相同功能。但发现虽然两者都在学习,但是学习内容却不一样。解决:可以进行向上抽取,只抽取功能定义,不抽取功能主体(即抽象类)。对于这种方法,必须使用abstract进行修饰。抽象方法必须定义在抽象类中。如下图:

    抽象:即看不懂的事物。

    抽象类的特点:

    1. 抽象方法一定在抽象类中
    2. 抽象方法和抽象类都必须被abstract关键字修饰
    3. 抽象类不可以用new创建对象,因为调用抽象方法没意义(里面的抽象方法连方法体都没有)
    4. 抽象类中的抽象方法要想被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用,如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

    抽象类和一般类没有太大的不同。只是要注意该如何描述事物就如何描述事物,只不过该事物中出现了一些看不懂的东西。这些不确定的部分也是该事物的功能,需要明确出来,但是无法定义主体。而通过抽象方法来表示。

    • 抽象类比一般类多了抽象方法。就是在类中可以定义抽象方法,也可以不定义抽象方法。
    • 抽象类不可以实例化。
    • 抽象类也可以不定义抽象方法,这样只是为了不让该类创建对象。

    抽象类练习:

    假如我们在开发一个系统时需要对员工进行建模,员工包含3个属性:姓名、工号、工资。经理也是员工,除了含有员工的属性外,另外还有一个奖金属性,请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。

    分析:

    员工类:name、id、pay

    经理类:继承了员工并有自己特有的奖金属性bonus

    会有一个抽象员工类,员工属于这一类,经理也属于这一类

    8.8模板方法设计模式:

    需求:获取一段程序运行的时间

    原理:获取程序开始和结束的时间并相减即可。获取本机系统时间:System.currentTimeMillis()获取当前时间的毫秒值,返回当前时间减去1970年1月1日0点0分0秒之间的时间差。

    问题:想获取另一段代码的运行时间。解决:直接子类复写掉getTime方法

    但是发现代码重复性很高。

    解决:可以将需要运行代码提取出来形成另一个方法,子类复写时,只需要复写提取出来的代码即可。如下图:

    但是却发现提取出来的需要运行的代码是不确定的,解决:只要将方法写成抽象方法并由子类继承后进行具体实现即可,且getTime方法不可复写所以使用final进行修饰。如下图:

    当代码完成优化后,就可以解决这类问题,这种方式叫做模板方法设计模式。

    什么是模板方法?

    在定义功能时,功能的一部分是确定的,但是一部分是不确定的,而确定的部分在使用不确定的部分时就将不确定的部分暴露出去,由该类的子类去完成。这样可以提高扩展性和复用性。注意:暴露出去的方法不一定抽象,有时会有默认的实现方式。

    8.9接口

    接口:初期理解,可以认为是一个特殊的抽象类,当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表示。

    接口定义时,格式特点:

    1. 接口中常见定义:常量,抽象方法
    2. 接口中的成员都有固定修饰符(常量:public static final;方法:public abstract)

    总结:接口中的成员(包括成员变量和方法)都是public的

    如下,即使少写了成员上的,interface也会进行默认的补全。

    但是,一般建议写全:

    接口也是不可能创建对象的,因为接口中的方法都是抽象的,而抽象方法需要被子类实现。即子类对接口中的抽象方法全都覆盖后,子类才可以实例化,若子类只是实现了父类,却没有覆盖父类的方法,那么子类仍是一个抽象类。如下图:

    如上图中,实现类Test实现了Inter接口后,覆盖了父类接口中的show()方法。因此Test类才可以被实例化。

    接口可以被类多实现,也是对多继承不支持的转换形式,java不支持多继承,但是支持多实现。如下图:

    java不支持多继承是因为父类当中的方法有重复,多继承会导致子类调用时出现冲突,为什么接口不会出现这种问题?

    因为多实现接口中,接口没有方法主体,实现类在实现时,可以重写方法体,即使出现接口中出现多个相同方法,实现类中一个方法就可以全部重写多个接口中的那些相同方法。如下图所示:

    注意:多个接口的相同方法的返回值必须一致。

    类和接口相互之间的关系:

    • 类与类之间:继承
    • 类与接口之间:实现
    • 接口与接口之间关系:继承关系

    接口与接口之间可以多继承(因为接口之间没有方法体,方法之间不会冲突)。所以java中也存在多继承,只是多继承只存在在接口与接口之间,而不存在与类与类或者类与接口之间。

    接口的特点:

    1. 接口是对外暴露的规则
    2. 接口是程序的功能扩展
    3. 接口可以用来多实现(降低了耦合性)
    4. 类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
    5. 接口与接口之间可以有继承关系

    9、多态

    9.1概念

    多态:可以理解为事物存在的多种体现形态

    人:男人、女人

    动物:猫、狗

    猫 x = new 猫();

    动物 x = new 猫();

    函数也具有多态性:重载和覆盖

    本节主要内容:多态的体现;多态的前提;多态的好处;多态的应用等。

    9.2多态—扩展性

    需求:动物(猫、狗)

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		Cat c = new Cat();
    		function(c);
    		function(new Dog());
    	}
    	
    	public static void function(Cat c){
    		c.eat();
    	}
    	public static void function(Dog d){
    		d.eat();
    	}
    }
    

    由例可发现,例中只有继承,如果再要加其他动物,还得写其他动物的类并继承动物类,且还要重写动物的吃方法。

    分析发现,所有动物都具有吃的行为,因为他们都是动物。如下图:

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		/*Cat c = new Cat();
    		function(c);
    		function(new Dog());*/
    		Animal a = new Cat();
    		a.eat();
    	}
    	
    	public static void function(Cat c){
    		c.eat();
    	}
    	public static void function(Dog d){
    		d.eat();
    	}
    }
    

    总结:多态的体现:父类的引用指向了自己的子类对象(Animal a = new Cat())。

    父类的引用也可以接收自己的子类对象(提高代码的扩展性:以后也其他的猪、老虎类,只要有这个类继承动物类即可)。所以可以通过动物类,去new其子类(即多态),传入什么动物,便由其自己去调用自己的吃方法。如下图:

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		function(new Cat());
    		function(new Dog());
    	}
    	public static void function(Animal a){
    		a.eat();
    	}
    }
    

    多态的好处:大大提高了程序的扩展性

    多态的前提:必须是类与类之间有关系(继承或者实现);通常还有一个前提,存在覆盖。

    多态的弊端:提高了扩展性,但是只能使用父类的引用访问父类的成员(如Animal的eat()方法)。

    9.3多态—转型

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		
    		Animal a = new Cat();//引用数据类型的类型提升,也称为向上转型
    	}
    	public static void function(Animal a){
    		a.eat();
    	}
    }
    

    多态:Animal a = new Cat();存在引用数据类型的类型提升,即向上转型。

    如果想要调用猫的特有方法时,要如何操作?

    解决:可以向上转型,也可以向下转型,强制将父类的引用,转成子类类型。如下例:

    Animal a = new Cat();

    a.eat();

    Cat c = (Cat)a;

    a.catchMouse();//调用子类特有方法

    注意:Animal a = new Animal();

    Cat c = (Cat)a;//这里是不能强转的,这里的动物类是不明确到底是哪个动物类的

    千万不要出现以下操作:即将父类对象转成子类类型。能转换的是父类引用指向了自己的子类对象时,该引用可以被提升(运行结果是子类结果),也可以被强转转换(向下转型)

    规律:多态自始至终都是子类对象在变化。

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		
    		Animal a = new Cat();//引用数据类型的类型提升,也称为向上转型
    	}
    	public static void function(Animal a){
    		a.eat();
    		if(a instanceof Cat){
    			Cat c = (Cat)a;
    			c.catchMouse();
    		}else if(a instanceof Dog){
    			Dog d = (Dog)a;
    			d.kanJia();
    		}
    	}
    }
    

    a instanceof Cat 判断a是否属于Cat这个引用类型。

    一般只用于传的类型是有限的;或传的类型的该类的所属类型,并对此类型有所使用。

    9.4 多态—示例(应用)

    需求:基础班的学生:学习、睡觉等

    高级班的学生:学习、睡觉等

    可以将这两类事物进行抽取。

    9.5多态中成员的特点

    运行结果:

    在多态中(父类有指向子类对象时)成员函数的特点:

    • 在编译时期,参阅引用型变量(Fu类)所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败;
    • 在运行时期,参阅对象(Zi类)所属的类中是否有调用的方法(静态方法除外)。

    对于Fu f = new Zi();后调用子父类中的方法说明:子父类中都有此方法,使用子类中的方法(子类覆盖了父类方法);子类中没有此方法,调用父类的此方法(子继承父类);Fu类中没有此方法,编译失败。如下图(method3会编译失败):

     多态中,成员变量的特点,如下图:

    输出结果:

    在多态中,成员变量的特点:无论编译还是运行,都参考左边(引用型变量所属的类)(此情况一般只会面试出现)。因为有父类是,会先加载父类去拿num。

    打印结果:(开发一般不会出现这种情况)

    所以静态时不会出现多态中成员函数的特点。

    总结:在多态中,静态成员函数的特点:无论是编译时期还是运行时期,都是参考左边。

    因为静态方法存在方法区中,不需要对象,只需要Fu.method4()即可,看的是引用型对象加载的内容(Fu),而不是对象(Zi),只要父类型引用还在,用的还是父类引用,而不是对象。如下图:

    当调用f.method1()时,f确实执行的是对象,method1()被运行的时候是被对象运行,对象在调用非静态方法时,访问的是对象中的数据;但是静态方法method4()本身不访问对象特有的数据,用类名调用的话,它只看所属引用型(Fu)变量中的数据类型,即静态区中的方法,它不参考右边非静态区的方法。如下图:

    动态静态绑定:

    当method4()方法一进内存,因为是静态方法,就已经被绑定在方法所属的类的引用上(Fu),即method4属于Fu。而静态区上,this是动态的,this指向哪个对象就代表哪个对象。而现在写的是Fu f = new Zi(),f.method1(), new的还是Zi,所以运行的还是子类的方法(动态绑定);当Fu f = new Fu()此时就找的是Fu类上的方法。

    多态总结:

    9.6多态的电脑的主板示例

    需求:电脑运行示例,电脑运行是基于主板

    上图中,主板就可以跑起来了

    但是发现问题:运行一段时间后,想上网,想听音乐,主板没有此功能,因此就需要给主板提供扩展性的功能(原来功能毫无扩展性)。如下图:

    该类在内存中表示:

    为降低网卡声卡等和主板的耦合性,会在主板上留一个槽,并约定这些槽的规则,此后,只要符合这个规则,就可以在主板上运行。而这些插槽即接口的编写。如上图的PCI即设定的某些规则。代码如下图所示:

    PCI p = new NetCard();//接口型引用指向自己的子类对象。

    如上图,网卡声卡等都需要符合规则才能用,所以都需要实现PCI接口。

    9.7多态的扩展示例

    需求:对数据库的操作

    数据是:用户信息

    1. 连接数据库 JDBC Hibernate
    2. 操作数据库 CRUD
    3. 关闭数据库连接

    问题:某天发现JDBC太麻烦,需要使用某种框架进行连接数据库,按照以上方法,就需要修改所有方法。如下图:

    内存中:

    但是发现,主程序也需要改,这样代码耦合性太高,所以需要重新设计代码。

    分析:无论如何连接数据库,最后要的只是CRUD,无论采用哪种方式连接,内部的实现方式仍然相同,所以可以专门定义出数据库连接的规则。如下图,interface接口,事先定义好CRUD的接口。之后再需要使用其它数据库连接方式时,也只需要实现该接口即可。

    由此发现,连接数据库的程序和主程序不再有强的耦合关系。

    9.8 Object类 —equals()

    Object:是所有对象的直接或者间接父类,该类中定义的肯定是所有对象都具备的功能。

    java指定所有对象都具备比较性,都能比较两个对象是否相同。此方法定义在Object中,即equals()方法。

    例如:

    equals()方法比较的是对象的地址值。equals()里的参数不确定,所以使用Object,即可多态实现比较。

    自定义比较方法:

    使用“==”对象不同,数据相同即为真,如下图:

    问题:Demo中已经有了比较的方法equals()(超类Object),就不需要重新比较了(功能一致,可以沿袭父类,写自己特有的功能(复写(覆盖)equals()))。如下图:

    Object中没有定义过num,如果不向下转型,是不能进行编译的。

    问题,当传入的对象不同时,运行时出现不可转换错误。解析:一般是自己的对象和自己比较,不是则直接返回false。如下图:

    输出false。

    9.9Object类—toString()

    java 任意对象都可以通过toString()方法返回字符串,所以类.toString()就可以拿到对象的hash值。而hashCode方法也可以返回对象的hash值。

    图中Demo如何得到?

    一个对象的建立要根据类文件进行建立。一个类文件中包含名称、很多构造函数、一般方法等很多东西,如何获取到某个构造函数,只有这个对象最明确,所以可以通过Class来描述这些class文件,通过  类.getClass() 进行获得。

    类中有Class.getClass()方法,可以通过类.getMethods()获取到类文件中所有的方法。

    10、内部类

    10.1内部类概念

    将一个类定义在另一个类的里面,而里面那个类就称为内部类(内置类、嵌套类)。

    内部类访问特点:内部类可以直接访问外部类中的成员,包括私有成员;而外部类要访问内部类中的成员必须要建立内部类的对象。

    好处:可以直接访问外部类中的成员(包括私有成员),而不用创建对象。

    外部能不能直接访问内部类中的方法(function)?

    分析:inner不是独立存在,而是在Outer中,所以使用inner所在类.inner即可(很少用,因为内部类很可能会被private修饰(当内部类是外部类的成员时))。如下图:

    为什么内部类能直接访问外部类中的内容?

    输出结果为6。

    当内部外部存在变量相同时,优先获取内部变量;

    想拿到4(内部类的变量),使用this.x;想要拿到外部类的x=3,可用Outer.this.x。如下图:

    之所以可以直接访问外部类中的成员是因为内部类中持有了一个外部类的引用,该引用写法是”外部类名.this”。

    10.2静态内部类

    访问格式:

    • 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部对象。格式:外部类名.内部类名  变量名 = 外部类对象.内部类对象(Outer.Inner in = new Outer().new Inner())
    • 当内部类在成员位置上,就可以被成员修饰符所修饰。比如,private:将内部类在外部类中进行封装,Static:内部类就具备了静态的特性。当内部类被静态修饰后,只能直接访问外部类中的静态成员了(外部成员也要用static修饰才行)。修饰出现限制了。如下图:

    修改后:在外部其他类中,如何直接访问static内部类的非静态呢?new Outer.Inner().function();如下图:

    在外部其他类中,如何直接访问static内部类的静态呢?

    new Outer.Inner.function();但是这种方式使用得很少。

    注意当内部类中定义了静态成员,该内部类必须是静态的(static)。如下例:

    即当function()方法被static修饰时,Inner也必须是静态修饰

    当外部类中的静态方法访问内部类时,静态类也必须是静态的。如下图:

    改为以下即可:

    10.3内部类定义原则

    类是用来描述现实中事物的,当描述事物时,事物内部还有事物,该事物就用内部类来描述,因为内部事物在使用外部事物中的内容(成员和函数等)。

    示例:描述人体,在描述心脏时,认为心脏也是一个功能,但是心脏描述比较复杂,有跳动、血液等等众多属性,对于这些属性就得用类进行描述,因为,心脏可以访问人体中其他部分,所以,将心脏定义成内部类最合适(内部类:类中包含另一个类),而且心脏不能直接被外部访问,但是提供外部访问的功能。

    10.4匿名内部类

    只有定义在成员位置上,才能被私有或静态所修饰。内部类可以写在类的任意位置上(成员、局部成员(变量或方法)等)

    如下例,访问规则没变:

    上例已经不能被静态私有修饰,Inner在局部方法里,在这里也不能使用static。局部内部类不能用static修饰,非静态没对象不会运行(必须有调用的地方new Inner().function(),如下)。

    方法中的局部变量,内部类在局部,那么内部类就能访问局部变量?

    修改后:

    有上例可知,

    1. 内部类定义在局部时,不能被成员修饰符修饰;
    2. 可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部变量,只能访问被final修饰的局部变量

    如上图,方法中的参数也必须是final。

    如何只有一个对象实现多次调用method(),如下图:

    调用method()时a=7进栈,执行完后,会释放掉,再调用method()a =8,所以不会冲突。

    匿名内部类:

    1. 匿名内部类其实就是内部类的简写格式
    2. 定义匿名内部类的前提:内部类必须继承一个类,或者实现接口

    将下列例子简化成匿名内部类:

    简化后:匿名了,没有Inner类了,怎么new对象?如下:

    可以使用 new AbsDemo(){

                      void show();

                    }

    AbsDemo本身是抽象类,不能new,但是可以像下面这样写,在复写掉里面的方法。红框的内容是一个对象,是AbsDemo的子类对象(只有子类才可以复写父类的抽象方法)。

    匿名内部类调用本匿名内部类中的方法:

    1. 总结:匿名内部类的格式,new 父类或者接口(构造函数,可以往其传参){定义子类的内容}
    2. 其实匿名内部类就是一个匿名子类对象,可以理解为带内容的对象

    匿名内部类中也可以有子类特有的方法,不一定是覆盖父类的方法,但是,不可以同时调用show和abc方法(匿名内部类只能调用一次)。如下图:

    但是可以,建立多次匿名对象:

    简化,可以将多个匿名内部类取名(利用多态),如下图:

    d.show()可以是父类的方法,d.abc()不能调用,多态中的父类没有abc()方法。

    注:匿名内部类中定义的方法最好不要超过3个。可读性差。

    匿名内部类练习:

    如上图,Test.function:Test类中有一个静态方法function,.method():function这个方法运算后的结果是一个对象。而且是一个Inter类型的对象。因为是Inter类型的对象,才可以调用method()方法。

    Test.function().method();即等同于:

    Inter in = Test.function();

    in.method();

    什么时候用匿名内部类?

    当使用的方法参数类型是接口时,可以再调用方法时传一个匿名内部类(如AWT匿名监听器等)。如下图:

    若没有父类也没有抽象类或接口,想写个匿名内部类,怎么写?

    使用所有类的父类(Object对象),如下图:

    11、异常

    11.1异常概述

    异常:程序运行时出现不正常情况。

    异常由来:问题也是现实生活中的一个具体事物,也可以通过java的类的形式来进行描述,并封装成对象。异常其实就是java对不正常情况进行描述后的对象体现。

    对于问题的划分:一种是严重的问题;一种是非严重的问题。

    对于严重的,java通过Error类进行描述。对于Error类问题一般不编写针对性的代码进行处理;对于非严重的问题,java通过Exception类进行描述,对于Exception类的问题可以使用针对性的处理方式进行处理。

    无论Error或者是Exception都具有一些共性内容:不正常情况的信息、引发原因等,会向上抽取,抽取出来的即为:

    Throwable

         |--Error

         |--Exception

    API解释:

     

    Error异常:

    Exception异常:

    11.2异常try catch

    java 有内置的异常处理方式,只要有某些异常就会进行处理。java提供了特有的语句处理方式,即

    try{

      需要检测的代码

    }catch(异常类 变量){

      处理异常的代码(处理方式)

    } finally{

      一定会执行的语句

    }

    例如:

    在方法div()中,捕获到异常后封装成new AritchmeticException()后,会抛给main中的有try进行异常捕获newAritchmeticException()异常,捕获到后不会继续往下执行,而是有catch到(Exception e = new ArithmeticException();),再执行catch中的语句。

    对捕获到的异常对象进行常见方法操作:

    • String getMessage()  打印异常信息
    • String toString()  打印异常名及信息
    • void printStackTrace();  异常信息、类名及异常出现位置

    其实JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。

    11.3异常声明throws

    编写功能时,不清楚是否调用者会传入正确的参数,所以,可以在功能上通过throws的关键字声明该功能有可能会出现问题。例:

    编译时会出现以下错误:

    但是如果不抛出,编译时不会出现问题,而运行会报错:

    如果一直抛出,直至抛给JVM,则会使用JVM默认的异常处理机制:

    抛异常和捕捉异常是有使用场景的。

    11.4多异常处理

    对多异常的处理:

    • 声明异常时,建议声明更为具体的异常,这样处理得更具体

    • 原则:对方声明几个异常就有几个catch块,不要定义多余的catch块,如果多个catch块中的异常出现继承关系,则父类异常catch块放在最下面。建议在进行catch处理时,catch一定要定义具体的处理方式,不要简单定义依据e.printStackTrace(),也不要简单的书写一条输出语句。做法:用硬盘文件记录异常日志文件。

    也可以抛出父类异常Exception:

    若出现了,意料之外的异常,应该是停止执行,让我们发现问题,进行针对性处理,而不是通过Exception进行。如下图:

    11.5自定义异常

    因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象,所以对这些特有的问题,可以按照java的对问题封装的思想,对本项目中的特有问题进行自定义异常封装。

    需求:在本程序中,对于除数是-1,也视为是错误的无法进行运算。那么就需要对这个问题进行自定义的描述。

    当函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作,要么在内部try catch;要么在函数上声明让调用者处理。一般情况在函数内出现异常,函数上需要声明。

    打印结果:

    发现打印结果中只有异常名称却没有异常信息,因为自定义异常并未定义所属信息,那么如何定义异常信息?

    打印结果:

    问题:发现父类构造函数中有个带message的方法

    父类中已经定义了构造函数接收异常信息,所以就会有变量接收这个信息,子类在继承这个类时,只要new Exception()就能获取到这些信息。类似下例:

    所以,异常类如下:

    总结:因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时将异常信息通过super语句传递给父类,就可以直接通过getMessage()获取异常信息。

    自定义类,根据特有数据定义异常,如异常类名、异常的值等,如下图:

    输出结果:

    自定义异常:必须是自定义类继承Exception类。

    为什么继承Exception:

    异常体系有一个特点,因为异常类和异常对象都需要被抛出,他们都具备可抛性,这个可抛性是Throwable这个体系中的独有特点,只有这个体系中的类和对象才可以被throws和throw操作。只有继承Throwable、Exception、Error,只有在这个体系中才能使用throws和throw进行抛出异常。

    11.5异常—throws和throw的区别

    throws和throw的区别:

    1. throws使用在函数上,throw使用在函数内;
    2. throws后面跟的异常类,可以跟多个,用逗号隔开。throw后面跟的是异常对象(throw new 异常类)

    11.6RuntimeException

    ArithmeticException类构造函数有定义message的方法,可以直接使用这个构造方法打印错误信息:

    打印结果:发现函数内抛了,但是函数上并没有声明过,但是编译通过了。

    但是,当抛Exception时发现有安全隐患,编译时直接报错。

    原因:

    由API可发现ArithmeticException异常类的父类是RuntimeException异常,即运行时异常。该异常很特殊。RuntimeException或者RuntimeException的子类如果在函数内抛出了该异常,函数上可以不用声明异常,编译一样通过。如果在函数上声明了该异常,调用者可以不用进行处理(catch或throws),编译一样通过(如下例)。

    原因:之所以不用再函数上声明是因为不需要让调用者处理,当该异常发生,希望程序直接停止。因为在运行时出现了无法继续运算的情况,希望停止程序后由程序员对代码进行修正。如int x = 3/0,一旦3/0被允许执行了,那么x将无法得到正确的值,因此必须对3/0进行正确处理才行

    示例:

    如上例,发现当name传null时,很容易出现空指针异常,我们再出现name传null时,必须要程序员进行修改才行。因此要让其抛异常才行。而且不能在方法上使用throws,让其抛出去。如下图:

    总结:自定义异常时,如果该异常的发生,无法继续进行运算,就让自定义异常继承RuntimeExcetion。

    所以自定义异常示例可改为:

    输出结果:

    对于异常分两种:

    1. 编译时被检测的异常(javac编译时,发现方法中抛出了非RuntimeException及其子类,而方法上没有标识throws,就会认为有安全隐患。此异常时可处理的,要标识出去,让调用者进行对应的处理;如果函数上标识了throws,函数的调用者也必须进行处理,try或抛);
    2. 编译时不被检测的异常(运行时异常:RuntimeException及其子类)(方法内抛throw,但是方法上不标识throws,它会拿着异常对象去判断是否是运行时异常(e instanceof RuntimeException),如果是,无论标识与否都不管)

    11.7 异常练习(应用)

    需求:毕老师用电脑上课

     

    异常:电脑宕机、电脑蓝屏

    要对问题进行描述,封装成对象

    根据电脑操作状态的不同,导致不同问题的产生。

     

    电脑冒烟了,不能抛出去,因为抛出去了其他老师也处理不了,但是当冒烟出现问题后,讲课进度无法继续,出现了讲课进度无法完成的问题,所以虽然捕捉到的是MaoYanExcetion,但是要把它封装成我的问题再跑出去(讲课无法完成问题)再抛出去

    注意:throw后面不会继续执行,throw是函数结束的标志。

    11.7异常—finally

    finally块的代码无论如何都会执行,通常用于释放资源

    示例:数据库连接(数据库连接有限,一旦占用不释放,其他人就会一直连不上)

    连接数据库;数据库操作(throw new SQLException());关闭数据库(无论操作是否成功一定要关闭资源)

    数据没有存储成功也是异常,SQLException我们处理不了,但是数据没有存储成功是可以处理的(分层思想,模块式开发):

    11.8异常—处理语句其他格式

    以下,因为throw new Exception()语句,已经被catch捕捉处理掉,所以语句正确

    catch是处理异常,如果没有catch就代表异常没有被处理,如果该异常时检测时异常,那么必须声明。

    11.9异常—覆盖时异常的特点

    异常在子父类覆盖中的体现:

    • 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。

    父类已经有问题了,子类要继承父类的话不能比分类还有问题,只能跑父类或者父类的子类的异常

    有一个子类覆盖父类,继承CException,且Test中传入子类对象:

    这样程序会挂,编译就是失败。

    • 如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集
    • 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常;如果子类发生了异常,就必须要进行try处理,绝对不能抛

     

    10.10异常—练习

    有一个圆形和长方形,都可以获取面积,对于面积如果出现非法的数值,视为获取面积出现问题,问题通过异常来表示。

    基本设计:

     

    当传入负数时,面积为负数,无意义

    之前处理方式:

    会发现正常流程代码和问题处理代码结合比较紧密,阅读性差。异常的产生可以让流程处理代码和问题处理代码分离。

     

    输出:

    如果真的出现问题,一旦为负数,就没意义,不会继续执行,所以直接使用RuntimeException即可,也不用进行标识。主函数中也就不需要catch了

    求圆的面积:写RuntimeException也可以,但是,此名称跟程序意义无关,不能见名知意

    所以还是写自定义异常比较直观:

    11.11异常—总结

    异常:是对问题的描述,将问题进行对象的封装。

    异常体系:

    Throwable

    |--Error

    |--Exception

       |--RuntimeException

    异常体系的特点:异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作。只有异常体系具备这个特点。

    throw和throws的用法:

    throw定义在函数内,用于抛出异常对象,throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开。

    当函数内容有throw抛出异常对象,并未进行try处理。必须在函数上声明,否则编译失败。注意RuntimeException除外。也就是说,如果函数内抛出的是RuntimeException异常,函数上可以不用声明。

    如果函数声明了异常,调用者需要进行处理,处理方式可throws可以try。

    异常有两种:一种叫做编译时被检测异常(该异常在编译时,如果没有处理(没有抛也没有try),那么编译失败,该异常被标识,代表着可以被处理);一种叫做运行时异常(编译时不检测)(在编译时,不需要处理,编译器不检查,该异常的发生,建议不处理,让程序停止,需要对代码进行修正)

    异常处理语句:

    try{

      需要被检测的代码

    }catch(){

      处理异常的代码

    }finally{

      一定会处理的代码

    }

    有三种结合格式:

    try{

    }catch(){

    }

    try{

    }finally{

    }

    try{

    }catch(){

    }finally{

    }

    注意:

    1. finally中定义的通常是关闭资源代码,因为资源必须要释放。
    2. finally有一种情况读不到:即系统退出(System.exit(0)虚拟机结束时,finally不会再执行)

    自定义异常:

    定义类继承Exception或者RuntimeException

    1. 为了让该自定义类具备可抛性
    2. 让该类具备操作异常的共性方法

    当要定义自定义异常的信息时,可以使用父类已经定义好的功能。异常信息传递给父类的构造函数

    class MyException extends Exception{

    MyException(String message){

      super(message);

    }

    }

    自定义异常:

    按照java的面向对象思想,将程序中出现的特有问题进行封装

    自定义异常好处:

    1. 将问题进行封装
    2. 将正常流程代码和问题处理代码相分离,方便阅读

    异常的处理原则:

    1. 处理方式有两种:try 或者throws
    2. 调用到抛出异常的功能时,抛出几个,就处理几个(不多抛不多处理)。一个try对应多个catch的情况。
    3. 多个catch,父类的catch放到最下面
    4. catch内需要定义针对性的处理方式,不要简单的定义printStackTrace输出语句,也不要不写。当捕获到的异常,本功能处理不了时,可以继续在catch中抛出即:

    try{

      throw new AException();

    }catch(AException e){

      throw e;

    }

    如果该异常处理不了,但并不属于该功能出现的异常,可以将异常妆花后,再抛出和该功能相关的异常。

    或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道,并处理。也可以将捕获异常处理后,转换为新的异常

    try{

      throw new AException();

    }catch(AException e){

      //捕获到AException但是AException处理不了,可以转换为新的异常处理类

     throw new BException();

    }

    比如,汇款的例子

    异常的注意事项:

    在子父类覆盖时:

    1. 子类抛出的异常必须是父类的异常的子类或者子集;
    2. 如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛

    参阅:ExceptionTest.java 老师用电脑上课

          ExceptionTest1.java图形的面积

    11.12异常—练习四

    如上图,首先执行main中的func()方法,调用到func(),执行try抛异常,执行finally中B,再由main()中捕捉到异常执行main中catch得到C,A在func()有异常后不再执行,再执行D,所以,输出为B  C  D。

    如上图,main方法中首先执行new Demo后,执行到默认的super()方法(默认有,没有写而已),从而执行到其父类Test类,输出Test,然后输出Demo,在执行main中new Test(),输出Test。

    在多态中(父类有指向子类对象时)成员函数的特点:

    在编译时期,参阅引用型变量(Fu类)所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败;//此处A接口中没有定义func()方法,所以编译失败

    在运行时期,参阅对象(Zi类)所属的类中是否有调用的方法。

    如上图,首次执行到for循环时,f.show(‘A’)执行Demo类中Demo()方法,打印A输出false,而f.show(‘B’)也执行Demo类中的Demo()方法打印B输出false满足条件,(i<2)不再执行,因此循环直接结束。(在运行时期,参阅对象(Zi类)所属的类中是否有调用的方法。)。

    如上图,A接口中没有定义Test方法,编译失败。

    如上图,执行main中new Demo(“A”)后,执行到Demo类中构造方法,而其中会有个默认的super()方法,从而执行到父类Super中的狗仔函数Super(),从而打印B,执行i+=2,然后继续执行Demo类中Demo()构造函数中代码,打印C,将i赋值为5,然后继续执行mian(),从而打印d.i  为5

    如上图,定义匿名内部类的前提:内部类必须继承一个类,或者实现接口。

    interface Inter{

    void show(int a,int b);

    void func();

    }

    class Demo {

    public static void main(String[] args){

      Inter in = new Inter(){

        public void show(int a,int b){}

        public void func(){}

    }

    in.show();

    in.func();

    }

    }

    匿名内部类一定要在方法上加上public标识共有;

    要调用匿名内部类的多个方法,使用Inter in = new Inter(){}格式后,用实例名in调用即可

    如上图,编译失败,非静态内部类,不可以定义静态成员;

    内部类中如果定义了静态成员,该内部类必须被静态修饰

    如上图,读清楚题目,是存在于Demo的子类中,不是再同一个函数的重载

    A 可以,覆盖

    B 不可以 权限不够

    C 可以  和父类不是一个函数。没有覆盖相当于重载

    D 不可以,因为该函数不可以和给定函数出现在同一类中,或者子父类中

    E 不可以,静态只能覆盖静态

    this:代表本类对象,哪个对象调用this所在函数,this就代表哪个对象

    final:

    1. 修饰类,变量(成员变量、静态变量、局部变量),函数
    2. 修饰的类不可以被继承
    3. 修饰的方法不可以被覆盖
    4. 修饰的变量是一个常量,只能赋值一次
    5. 内部类只能访问局部的final形式变量

    11.

    如上图,输出结果:4  5  showZi  showZi

    成员变量看左边,变量不存在覆盖;

    方法看右边

     

    如上图,定义成员变量,然后计算和

    局部接收进来的,值改变后,其他方法也可以用

    如上图,输出BCD

    如上图,编译失败,应该父类中缺少空参数的构造函数;或者子类应该通过super语句指定要调用的父类中的构造函数

    如上图,编译失败:因为子父类中的get方法没有覆盖,子类调用的时候不能明确返回值是什么类型,所以这样的函数不能存在在子父类中。

    如上图,编译失败:因为打印字符串“A”的输出语句执行不到。

    如上图,16题和13题区:把抛出异常封装在方法中,代表有可能有问题,有可能没有问题,所以13题中的A是有可能执行到的,但是16题中A绝对不可能执行到。

    如上图,A ok 

    B 不可以,因为主函数是静态的,如果要访问inner需要被static修饰

    C 错误,格式错误

    D 错误 因为inner不是静态的

    编译失败,多个catch时,父类的catch要放在最下面。

     

     

    如上图,方法时静态的,所以调用多次后,前面一次方法不会销毁,且output也是共享的不会销毁,foo(0)执行后,在执行foo(1)时继续累加

    return语句后,finally中的语句继续执行,但是finally外的不会继续执行

    所以结果是 13423         

     

    已经做过

     

     

    输出4

     

    已经做过

    如上图,数组查找,数组可能无序,所以不要折半

     

    如上图,用到本类对象this

    前面的cir用this表示,cir2传入

    12包

    12.1包package

    1. 对类文件进行分类管理
    2. 给类提供多层命名空间
    3. 写在程序文件的第一行
    4. 类名的全称是 包名.类名
    5. 包也是一种封装形式

    命令中加参数用于创建包的目录

    javac –d . PackageDemo.java

    -d 指定包所存放的位置

    . 表示当前目录

    PackageDemo.java 类名

    执行时:

    java pack.PackageDemo

    不存在当前目录(而存放到指定目录):

    javac –d c:\ PackageDemo.java

    set classpath–c:\  指向包(pack)的父目录即可

    java pack.PackageDemo

    包的出现可以让java的类文件和class文件相分离,当别人要执行时,可以只给class运行文件,而不用给源文件

    12.2包与包之间的访问

     

    先编译DemoA,再编译PackageDemo

    编译时将编译文件存在存一个路径

    发现报错。

    错误原因:类名写错

    因为类名的全名是:包名.类名

    改正后:

    发现又报错:

    错误原因:软件包不存在

    packa包不再当前目录下,需要设置classpath,告诉JVM去哪里找指定的packa包

    包找到了,但是又发现其他错误

    有了包,范围变大,一个保重的类要被访问,必须要有足够大的权限,所以要被访问的类需要被public修饰

    因为DemoA类的修饰符没有权限

    类的修饰符有两个:protected、public

    改完后发现其他错误:

    错误原因:类共有后,被访问的成员也要共有才可以被访问

    改正后,运行正确

    总结:

    • 包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰
    • 不同包中的子类还可以直接访问父类中被protected权限修饰的成员(java给包与包中的方法提供权限protected权限(默认),使其不继承也可以拿到某类中的方法)

    包与包之间使用的权限只有两种:public和protected(只能给子类用)

     

    public

    protected

    default

    private

    同一类中

    Ok

    Ok

    Ok

    Ok

    同一包中

    Ok

    Ok

    Ok

     

    子类

    Ok

    Ok

     

     

    不同保重

    Ok

     

     

     

    一个java文件里不能出现两个公有类或接口:(以下代码错误)

    可以将两个文件放在同一个包下

    12.3导入import

    为了简化类名的书写,使用一个关键字:import

    同一目录下有多个类时,使用*,导入到文件

    import导入的是包中的类,如果有包是不能导入的

    建议不要写通配符*,需要用到包中的那个类就导入哪个类,否则占用内存太多

    导入不同包的同名的包时,必须写清楚类的全名(包名+类名)

    如上例,packa和backb包中存在同名的DemoC类时,使用时,必须写DemoCracy的全名packa.DemoC c = new packa.DemoC();

    建议定义包名时不要重复,可以使用URL来完成定义,URL是唯一的

    12.4jar包

    java的压缩包

    1. 方便项目的携带
    2. 方便与使用,只要在classpath设置jar路径即可
    3. 数据库驱动,SSH框架等都是以jar包体现的

    java打jar包需要借助java JDK工具java.exe

    jar命令的用法:

    如:

    -c 创建新的归档文件

    -f 创建的归档文件名

    haha.jar jar包名字

    packa pack 需要打成包的文件夹(注:命令必须在当前文件夹下执行,如例两个文件夹在myclass文件下)

    jar包和打成其他包的区别:

    jar –tf haha.jar  查看归档目录

    存放了java特有的配置文件,且

    目录下存放的文件都删除了,双击jar包时仍然可以执行的。如下,

    使用jar –cvf a.jar packa pack  可以显示打印时的详细信息

    jar –tvf a.jar packa pack 可以显示时间等详细信息

    数据重定向

    将打包时的数据定向到1.txt文件夹内:

    将jar包中所有的类重定向放到rt.txt文件中

    展开全文
  • 深入理解Python面向对象

    万次阅读 多人点赞 2017-06-12 06:42:41
    面向过程与面向对象的对比 id、type和value的讲解 类和对象的概念 初始化构造函数__init__的作用 self关键字的使用 继承的概念 组合的概念 接口的概念 抽象类的概念 1、面向过程与面向对象的对比面向过程的程序设计...

    博客核心内容:


    1. 面向过程与面向对象的对比
    2. id、type和value的讲解
    3. 类和对象的概念
    4. 初始化构造函数__init__的作用
    5. self关键字的使用
    6. 继承的概念
    7. 组合的概念
    8. 接口的概念
    9. 抽象类的概念
    10. 属性与方法遍历顺序的问题(MRO列表)
    11. super关键字的使用
    12. 多态的概念
    13. 封装的概念
    14. @property的用法
    15. 绑定方法与非绑定方法
    16. staticmethod与classmethod的区别
    17. 综合应用的一个小例子

    1、面向过程与面向对象的对比

    面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
    优点是:极大的降低了程序的复杂度
    缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
    应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
    面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。对象是特征和技能的结合,其中特征和技能分别对应对象的数据属性和方法属性。
    优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
    缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
    应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

    2、id、type和value的概念

    在python当中一切皆对象,每产生一个对象会对应三个属性:id、类型type和数值
    id可以理解为在内存当中的位置(其实不是,id实际指向的是对象的地址)
    is是身份运算符,其中id对应的就是身份。
    id相同,数值肯定相同;id不相同,数值一定不相同吗?不是。
    代码示例:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    x = 10
    print(id(x))
    print(type(x))
    print(x)
    
    y = 10
    print(id(y))
    print(type(y))
    print(y)
    
    #判断x和y的内存地址是否相同
    print(x is y)
    #判断x和y的数值是否相同
    print(x == 7)
    

    运行结果:

    1547159920
    <class 'int'>
    10
    1547159920
    <class 'int'>
    10
    True
    False
    
    Process finished with exit code 0

    代码示例:(不是在PyCharm当中操作的)

    >>> x = 300
    >>> y = 300(
    >>> id(x)
    6368784
    >>> id(y)
    7343856
    >>> x == y
    3、类和对象的概念

    1>把一类事物的静态属性和动态可以执行的操作组合在一起所得到的这个概念就是类
    2>类的一个个体就是对象,对象是具体的,实实在在的事物
    3>对象是特征与技能的结合体,其中特征和技能分别对应对象的数据属性和方法属性
    4>对象(实例)本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样
    在类内部定义的属性属于类本身的,由操作系统只分配一块内存空间,大家公用这一块内存空间
    5>创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性:而类中有两种属性:数据属性和函数属性,其中类的数据属性是共享给所有对象的,而类的函数属性是绑定到所有对象的。
    6>创建一个对象(实例)就会创建一个对象(实例)的名称空间,存放对象(实例)的名字,称为对象(实例)的属性
    7>在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常。
    8>类的相关方法:

    类的相关方法(定义一个类,也会产生自己的名称空间)
    类名.__name__   # 类的名字(字符串)
    类名.__doc__    # 类的文档字符串
    类名.__base__   # 类的第一个父类(在讲继承时会讲)
    类名.__bases__  # 类所有父类构成的元组(在讲继承时会讲)
    类名.__dict__   # 类的字典属性、名称空间
    类名.__module__ # 类定义所在的模块
    类名.__class__  # 实例对应的类(仅新式类中)

    9>其余概念:

    1.创建出类会产生名称空间,实例化对象也会产生名称空间。
    
    2.用户自己定义的一个类,实际上就是定义了一个类型,类型与类是统一的。
    
    3.用户先是从自己的命名空间找,如果找不大,在从类的命名空间找。
        student1.langage = "1111"
        print(student1.__dict__)  ===>先是从自己的命名空间找
        print(Student.__dict__)   ===>然后在从类的命名空间找
    
    4.通过类来访问,访问的是函数,通过对象来访问,访问的是方法,在类内部定义的方式实际上是绑定到对象的身上来用的。
    <function Student.fun at 0x000000000267DAE8>
    <bound method Student.fun of <__main__.Student object at 0x0000000002684128>>
    
    <function Student.fun at 0x00000000025CDAE8>
    <bound method Student.fun of <__main__.Student object at 0x00000000025D4160>>
    <bound method Student.fun of <__main__.Student object at 0x00000000025D4198>>
    
    
    5.总结:类的数据属性是大家共有的,而且大家的内部地址是一样的,用的就是一个
           类的函数属性是绑定到大家身上的,内部地址不一样,绑定方法指的是绑定到对象身上。
           绑定方法:绑定到谁的身上,就是给谁用的,谁来调用就会自动把自己当做第一个参数传入。
           **定义在类内部的变量,是所有对象共有的,id全一样,
           **定义在类内部的函数,是绑定到所有对象的,是给对象来用的,obj.fun()会把obj本身当做
           一个参数来传递。
    
    6.在类内部定义的函数虽然可以由类来调用,但是并不是为了给类用的,在类内部定义的函数的目的就是为了绑定到对象身上的。
    
    
    7.在类的内部来说,__init__是类的函数属性,但是对于对象来说,就是绑定方法。
    
    
    8.命名空间的问题:先从对象的命名空间找,随后在从类的命名空间找,随后在从父类的命名
    空间找。
    print(student1.x)
    
    9.在定义类的时候,可以想什么先写什么。

    示例程序1:编写一个学生类,产生一堆学生对象,要求有一个计数器(属性),统计总共实例了多少个对象。

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    class Student():
        #在类内部定义的属性属于类本身的,由操作系统只分配一块内存空间,大家公用这一块内存空间。
        count = 0
        def __init__(self,name,age):
            self.name = name
            self.age = age
            Student.count +=1
    
    
    if __name__ == '__main__':
        student1 = Student("lidong",25)
        print(student1.__dict__)
        student2 = Student("wangwu",28)
        print(student2.__dict__)
        print(Student.count)
    

    运行结果:

    {'age': 25, 'name': 'lidong'}
    {'age': 28, 'name': 'wangwu'}
    2
    
    Process finished with exit code 0

    示例程序2:(对象之间的交互,重点)

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    """
    1、什么叫做抽象方法?含有@abc.abstractmethod标识符的就是?
    2、原样抄下来也是重写?
    """
    
    #定义盖伦类和瑞文类,并进行互相残血
    #对象之间的交互问题(面向对象之间互相交互)
    class Garen:
        camp = "Demacia"
        #定义一个对象的时候,指定了这个对象的生命值和杀伤力
        def __init__(self,nickname,life_value=200,aggre_value=100):
            self.nickname = nickname
            self.life_value = life_value
            self.aggre_value = aggre_value
        def attack(self,enemy):
            enemy.life_value = enemy.life_value - self.aggre_value
    
    class Riven:
        camp = "Demacia"
        # 定义一个对象的时候,指定了这个对象的生命值和杀伤力
        def __init__(self, nickname, life_value=100, aggre_value=200):
            self.nickname = nickname
            self.life_value = life_value
            self.aggre_value = aggre_value
        def attack(self, enemy):
            #python为弱类型语言
            enemy.life_value = enemy.life_value - self.aggre_value
    
    g = Garen("盖伦")
    r = Riven("瑞文")
    print("盖伦的生命值是%s"%g.life_value)
    print("瑞文的生命值是%s"%r.life_value)
    
    g.attack(r)
    print("瑞文的生命值是%s"%r.life_value)
    

    运行结果:

    盖伦的生命值是200
    瑞文的生命值是100
    瑞文的生命值是0
    
    Process finished with exit code 0
    4、初始化构造函数\__init_的作用

    所谓初始化构造函数就是在构造对象的同时被对象自动调用,完成对事物的初始化,一个类只要生成一个类对象,它一定会调用初始化构造函数.
    特点:
    1>一个类中只能有一个初始化构造函数
    2>不能有返回值
    3>可以用它来为每个实例定制自己的特征
    示例程序:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    class Student():
        def __init__(self,name,age):
            self.name = name
            self.age = age
            print(self.name,self.age)
    
    
    if __name__ == '__main__':
        #在构造对象的时候会自动调用初始化构造函数
        student = Student("Alex",100)

    运行结果:

    Alex 100
    
    Process finished with exit code 0

    5、self关键字的用法

    为了辨别此时此刻正在处理哪个对象,self指针变量指向当前时刻正在处理的对象,即构造出来的对象
    在构造方法中self代表的是:self指针变量指向当前时刻正在创建的对象
    构造函数中self.name = name 的含义:将局部变量name的数值发送给当前时刻正在创建的对象中的name成员
    示例程序:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    class Student():
        def __init__(self):
            print("当前对象的地址是:%s"%self)
    
    if __name__ == '__main__':
        student1 = Student()
        student2 = Student()

    运行结果:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    class Student():
        def __init__(self):
            print("当前对象的地址是:%s"%self)
    
    if __name__ == '__main__':
        student1 = Student()
        print(student1)
        student2 = Student()
        print(student2)

    运行结果:

    当前对象的地址是:<__main__.Student object at 0x00000000025ACF28>
    <__main__.Student object at 0x00000000025ACF28>
    当前对象的地址是:<__main__.Student object at 0x00000000025D4048>
    <__main__.Student object at 0x00000000025D4048>
    
    Process finished with exit code 0

    在上面的程序中,student1、student2、self实际上都是指针变量,存放的是地址,指定当前时刻正在调用的那个对象。

    6、继承的概念

    1、一个类从已有的类那里获得其已有的属性与方法,这种现象叫做类的继承
    2、方法重写指在子类中重新定义父类中已有的方法,这中现象叫做方法的重写
    3、 若A类继承了B类,则aa对象既是A,又是B,继承反映的是一种谁是谁的关系,只有在谁是谁的情况下,才能用继承解决代码冗余的问题。
    4、寻找属性和方法的顺序问题:先从对象自己的命名空间中找,然后在自己的类中,最后在从父类当中去找
    5、在python3当中,所有的类都是新式,所有的类都直接或者间接的继承了Object
    6、在python中,新建的类可以继承一个或多个父类

    示例代码1:继承和方法重写

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    class People:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
        def tell(self):
            print("%s-%s-%s"%(self.name,self.age,self.sex))
    
    class Student(People):
        def __init__(self,name,age,sex,salary):
            # self.name = name
            # self.age = age
            # self.sex = sex
            People.__init__(self,name,age,sex)
            self.salary = salary
    
        def tell(self):
            print("%s是最棒的!"%self.name)
    
    if __name__ == '__main__':
        student = Student("alex",20,"man",2000)
        student.tell()

    运行结果:

    alex是最棒的!
    
    Process finished with exit code 0

    代码示例2:属性的搜索顺序问题

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    class People:
        def __init__(self,name,age):
            self.name = name
            self.age = age
            self.tell()
    
        def tell(self):
            print("%s---%s"%(self.name,self.age))
    
    class Student(People):
        def tell(self):
            print("呵呵!")
    
    if __name__ == '__main__':
        student = Student("alex",20)

    运行结果:

    呵呵

    示例代码3:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    class People:
        def __init__(self,name,age):
            self.name = name
            self.age = age
            self.tell()
    
        def tell(self):
            print("%s---%s"%(self.name,self.age))
    
    class Student(People):
        def tell(self):
            print("呵呵!")
    
    if __name__ == '__main__':
        student = Student("alex",20)
        #查看Student所有的父类
        print(Student.__bases__)
        #查看最近的父类
        print(Student.__base__)
        #student既是Student类,又是People类
        print(isinstance(student,Student))
        print(isinstance(student,People))

    运行结果:

    呵呵!
    (<class '__main__.People'>,)
    <class '__main__.People'>
    True
    True
    
    Process finished with exit code 0
    7、组合的概念

    1、一个类的属性可以是一个类对象,通常情况下在一个类里面很少定义一个对象就是它本身,实际意义很少
    2、将另外一个对象作为自己的属性成员(自己的一个属性来自于另外一个对象),这就是组合
    3、组合也可以解决代码冗余的问题,但是组合反应的是一种什么是什么的关系。
    示例代码1:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    class Date:
        def __init__(self,year,month,day):
            self.year = year
            self.month = month
            self.day = day
        def tell(self):
            print("%s--%s--%s"%(self.year,self.month,self.day))
    
    class People:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    class Student(People):
        def __init__(self,name,age,sex,year,month,day):
            People.__init__(self,name,age)
            self.sex = sex
            #下面这一步骤就是组合
            self.birth = Date(year,month,day)
    
    if __name__ == '__main__':
        student = Student("alex",25,"man",2015,12,31)
        print("student的birth成员指向了一个Date对象!")
        print("%s"%student.birth)
        student.birth.tell()

    运行结果:

    student的birth成员指向了一个Date对象!
    <__main__.Date object at 0x0000000002604358>
    2015--12--31
    
    Process finished with exit code 0
    8、接口的概念

    1、通过接口可以实现不相关类的相同行为,可以起到一个标志的作用.
    2、接口提供了不同的类进行相互协作的平台
    3、在python中根本就没有一个叫做interface的关键字,如果非要去模仿接口的概念,可以借助第三方模块
    4、raise:主动抛出异常,本来没有错,主动抛出错。
    raise TypeError(“类型错误”)
    示例程序:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    #模拟Java中接口的概念
    class S1:
        def read(self):
            raise TypeError("类型错误")
        def write(self):
            raise TypeError("类型错误")
    
    class S2(S1):
        def read(self):
            print("from S2")
        def write(self):
            print("from S2")
    
    class S3(S1):
        def read(self):
            print("from S3")
        def read(self):
            print("from S3")
    
    if __name__ == '__main__':
        s2 = S2()
        s2.read()
        s3 = S3()
        s3.read()

    运行结果:

    from S2
    from S3
    
    Process finished with exit code 0

    在上面的程序中存在着一个问题,在S2和S3中如果不实现read和write方法,仍然可以实例化,如何解决,看抽象类的概念。

    9、抽象类的概念

    抽象类是为了更好的对类加以分类,抽象类通常情况下是作为一个类族的最顶层的父类,如植物,并用最底层的类来描述现实世界中的具体的事物.
    这里写图片描述
    1>Python中抽象方法定义的方式:利用abc模块实现抽象类,在Java当中如果一个方法没有执行体就叫做抽象方法,而在Python中不是以执行体的有无作为标准,而是以一个方法是否有@abc.abstractmethod装饰器作为标准,有则是抽象方法
    2>抽象方法通过子类的实现可以变成普通的方法
    3>抽象方法不存在所谓重写的问题,却存在着实现的问题
    4>含有抽象方法的类一定是抽象类,但是抽象类不一定含有抽象方法,此时也就没有什么意义了
    5>抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
    示例程序:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    """
    1、什么叫做抽象方法?含有@abc.abstractmethod标识符的就是?
    2、原样抄下来也是重写?
    """
    import abc
    class File(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def read(self):
            pass
        #抽象类中可以有普通方法
        def write(self):
            print("11111")
    
    class B(File):
        #如果写pass,也是可以的,此时子类将会覆盖掉父类
        def read(self):
            pass
    
    if __name__ == '__main__':
        bb = B()
        bb.read()
        bb.write()

    运行结果:

    11111
    
    Process finished with exit code 0
    
    10、属性与方法的遍历问题(MRO列表)

    python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如:

    >>> F.mro() #等同于F.__mro__
    [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

    为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止
    而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类
    示例程序:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    """
    1、什么叫做抽象方法?含有@abc.abstractmethod标识符的就是?
    2、原样抄下来也是重写?
    """
    
    class  A:
        def fun(self):
            print("aaaa")
    
    class B(A):
        def fun(self):
            print("bbbb")
    
    class C:
        def fun(self):
            print("cccc")
    
    class D(C):
        def fun(self):
            print("dddd")
    
    class F(B,D):
        def fun(self):
            print("ffff")
    
    if __name__ == '__main__':
        print(F.mro())
        #F==>B==>A==>D==>C===>Object
        ff = F()
        ff.fun()

    运行结果:

    [<class '__main__.F'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.C'>, <class 'object'>]
    ffff
    
    Process finished with exit code 0
    
    11、super关键字的使用

    1、super关键字产生的原因:在子类当中可以通过使用super关键字来调用父类的中相应的方法,简化代码。
    2、使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表。
    示例代码:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    class Foo:
        def test(self):
            print("from foo")
    
    class Bar(Foo):
        def test(self):
            #Foo.test(self)
            super().test()
            print("bar")
    
    if __name__ == '__main__':
        bb = Bar()
        bb.test()
    

    运行结果:

    from foo
    bar
    
    Process finished with exit code 0
    12、多态的概念

    1、所谓多态指的是一个父类的引用既可以指向父类的对象,也可以指向子类的对象,它可以根据当前时刻指向的不同,自动调用不同对象的方法,这就是多态的概念。(当然,Python中的多态没必要理解的这么复杂,因为Python自带多态的性能)
    2、多态性依赖于同一种事物的不同种形态
    3、Python是一门弱类型的语言,所谓弱类型语言指的是对参数没有类型限制,而这是我们可以随意传入对象的根本原因
    示例程序:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    #模拟Java中接口的概念
    class People:
        def fun(self):
            print("1111")
    
    class Student(People):
        def fun(self):
            print("2222")
    
    class Teacher(People):
        def fun(self):
            print("3333")
    
    def g(aa):
        aa.fun()
    
    if __name__ == '__main__':
        print("只要传入的对象是people的子类即可")
        g(People())
        g(Student())
        g(Teacher())
    

    运行结果:

    只要传入的对象是people的子类即可
    1111
    2222
    3333
    
    Process finished with exit code 0
    13、封装的概念

    1、在面向对象中,所有的类通常情况下很少让外部类直接访问类内部的属性和方法,而是向外部类提供一些按钮,对其内部的成员进行访问,以保证程序的安全性,这就是封装
    2、在python中用双下划线的方式实现隐藏属性,即实现封装
    3、访问控制符的用法___包括两种:在类的内部与在类的外部
    1>在一个类的内部,所有的成员之间彼此之间都可以进行相互访问,访问控制符__是透明的,失效的
    2>在一个类的外部,通过_类名_对象的方式才可以访问到对象中的_成员
    综上:内部之间可以直接访问,在类的外部必须换一种语法方式进行访问
    4、在python当中如何实现一个隐藏的效果呢?答案:在Python里面没有真正意义上的隐藏,只能
    从语法级别去实现这件事。
    5、在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:子类名__x,而父类中变形成了:父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的
    示例程序1:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    class Student:
        def __init__(self,name,age):
            self.__name = name
            self.__age = age
    
        def setter(self,name,age):
            if not isinstance(name,str):
                raise TypeError("名字必须是字符串类型")
            if not isinstance(age,int):
                raise TypeError("年龄必须是整数类型")
    
            self.__name = name
            self.__age = age
    
        def tell(self):
            print("学生的信息是:%s\t%s"%(self.__name,self.__age))
    
    if __name__ == '__main__':
        student = Student("Alex",25)
        student.tell()
        student.setter("Alex_sb",40)
        student.tell()

    运行结果:

    学生的信息是:Alex 25
    学生的信息是:Alex_sb  40
    
    Process finished with exit code 0
    14、@property的用法

    1、property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
    2、将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则(使用的原因)
    3、一旦给函数加上一个装饰器@property,调用函数的时候不用加括号就可以直接调用函数了
    代码示例:

    class Student:
        @property
        def fun(self):
            print("1111111")
    
    if __name__ == '__main__':
        student = Student()
        student.fun 

    示例程序2:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    """
    1、什么叫做抽象方法?含有@abc.abstractmethod标识符的就是?
    2、原样抄下来也是重写?
    """
    
    class Student:
        def __init__(self,name):
            self.__name = name
    
        @property
        def name(self):
            return self.__name
    
    if __name__ == '__main__':
        student = Student("alex")
        student._Student__name = "sb_alex"
        print(student.name)
    
    ###本节需要补
    15、绑定方法与非绑定方法

    绑定方法的使用:
    1、在类内部定义的方法,在没有被任何装饰器修饰的情况下,就是为了绑定到对象给对象
    用的,self关键字含有自动传值的过程,不管写不写self关键子。
    2、默认情况下,在类内部定义的方法都是绑定到对象的方法。
    3、绑定方法绑定到谁的身上,谁就作为第一个参数进行传入。
    4、绑定到类的方法给对象使用是没有任何意义的。

    非绑定方法:
    statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
    不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
    只有绑定方法才存在自动传值的说法。

    总结:
    绑定到对象的方法,调用的时候会将对象参数自动传入, ===>方法上面什么也不加
    绑定到类的方法,调用的时候会将类作为参数自动传入, ===>方法上面加classmethod
    非绑定方法不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。===>static

    使用场景:
    看使用什么调用?类对象or类or什么参数都不需要.

    模拟数据库登陆的场景。
    (只有类才有实例化的说法)

    创建数据库的时候加上一个id属性,指定是哪一个链接。
    每次数据库实例话的时候都要赋值一个id。
    示例程序1:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    
    
    #造成id的方式,用hash算法
    import time
    import hashlib
    
    def create_id():
        m = hashlib.md5(str(time.clock()).encode("utf-8"))
        return m.hexdigest()
    
    
    #time.clock()计算的是cpu真实的时间
    print(create_id())
    print(create_id())
    print(create_id())
    print(create_id())

    运行结果:

    3a92235c44873cbcf618a132a2781157
    aa0968a1f467ba19f1595c70efb2c3ee
    abc4e2f842e0283b2186d459c069d301
    28e76bcb1724833a847851e8f2286e65
    
    Process finished with exit code 0

    示例程序2:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    import settings
    import time
    import hashlib
    
    class MySQL:
        def __init__(self,host, port):
            self.id = self.create_id()
            self.host = host
            self.port = port
            print("connecting.....")
    
        def select(self):  # 绑定到对象的方法
            print(self)
            print("select function")
    
        # 绑定到类的方法,从配置文件中获取主机名和端口号,用户默认链接数据库的一种方式
        @classmethod
        def from_conf(cls):
            # 实例话的结果得到了一个类对象
            # 通过绑定对象的方法间接的去创建了一个类对象
            return cls(settings.HOST, settings.PORT)  # 相当于MySQL("127.1.1.1",3306)
    
        #工具包既不依赖于类,也不依赖于对象
        #非绑定方法就是类中普通的工具包,不依赖于self和cls的参数
        @staticmethod
        def create_id():
            m = hashlib.md5(str(time.clock()).encode("utf-8"))
            return m.hexdigest()
    
    
    if __name__ == '__main__':
        conn = MySQL("192.168.80.100", 3306)
        conn.select()
        conn2 = MySQL.from_conf()
        conn2.select()
    
        print(conn.id)
        print(conn2.id)

    运行结果:

    connecting.....
    <__main__.MySQL object at 0x0000000002584438>
    select function
    connecting.....
    <__main__.MySQL object at 0x0000000002584EB8>
    select function
    3a92235c44873cbcf618a132a2781157
    294e6de59d0e8d4a75717d5bb20f92e0
    
    Process finished with exit code 0
    16、staticmethod与classmethod的区别

    staticmethod与classmethod的区别:前者是非绑定方法,后者是绑定到类的方法
    示例程序1:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    import settings
    import time
    import hashlib
    
    class MySQL:
        def __init__(self,host, port):
            self.host = host
            self.port = port
            print("connecting.....")
    
        @staticmethod
        def from_conf():
            return  MySQL(settings.HOST,settings.PORT) #相当于MySQL("127.1.1.1",3306)
    
        def __str__(self):
            return "父类"
    
    class Mariab(MySQL):
        def __str__(self):
            return "子类"
    
    
    if __name__ == '__main__':
        conn = MySQL.from_conf()
        print(conn.host)
        conn1 = Mariab.from_conf()
        print(conn1.host)
        #本来想获取Mariab的一个对象,但是现在获取的是MySQL的一个对象,这是子类继承的一个问题
        print(conn1)

    运行结果:

    connecting.....
    127.1.1.1
    connecting.....
    127.1.1.1
    父类
    
    Process finished with exit code 0

    示例程序2:基于1的改进

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    import settings
    import time
    import hashlib
    
    class MySQL:
        def __init__(self,host, port):
            self.host = host
            self.port = port
            print("connecting.....")
    
        @classmethod
        def from_conf(cls):
            return  cls(settings.HOST,settings.PORT) #相当于MySQL("127.1.1.1",3306)
    
        def __str__(self):
            return "父类"
    
    class Mariab(MySQL):
        def __str__(self):
            return "子类"
    
    
    if __name__ == '__main__':
        conn = MySQL.from_conf()
        print(conn.host)
        conn1 = Mariab.from_conf()
        print(conn1.host)
        #本来想获取Mariab的一个对象,但是现在获取的是MySQL的一个对象,这是子类继承的一个问题
        print(conn1)

    运行结果:

    connecting.....
    127.1.1.1
    connecting.....
    127.1.1.1
    子类
    
    Process finished with exit code 0
    17、总和应用的一个小例子

    要求:
    定义MySQL类
    1.对象有id、host、port三个属性
    2.定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一
    3.提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化
    4.为对象定制方法,save和get,save能自动将对象序列化到文件中,文件名为id号,文件路径为配置文件中DB_PATH;get方法用来从文件中反序列化出对象。
    代码示例:

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    
    import time
    import hashlib
    import settings
    import random
    import pickle
    import os
    
    """
    HOST = "127.1.1.1"
    PORT = 3306
    DB_PATH = r"D:\Python Work Location\Python 0507\day07\db"
    """
    
    class MySQL:
        @staticmethod
        def create_id():
            m = hashlib.md5(str(time.clock()).encode("utf-8"))
            return m.hexdigest()
    
        def __init__(self,host,port):
            #为每一个对象创建了一个ID
            self.id = self.create_id()
            self.host = host
            self.port = port
    
        #从配置文件中读取在这里用到了classmethod
        @classmethod
        def from_conf(cls):
            return cls(settings.HOST,settings.PORT)
    
        def save(self):
            file_path = r"%s%s%s"%(settings.DB_PATH,os.sep,self.id)
            #将这个对象以二进制的形式写到硬盘当中
            pickle.dump(self,open(file_path,"wb"))
    
        def get(self):
            #在这里面通过id的方式保证了文件的名字是唯一的
            file_path = r"%s%s%s" % (settings.DB_PATH, os.sep, self.id)
            return pickle.load(open(file_path,"rb"))
    
        #对于Python中的对象json无法进行序列化
    
    if __name__ == '__main__':
        conn1 = MySQL("172.1.2.1","3306")
        print(conn1.id)
        conn1.save()
        result = conn1.get()
        print(result.id)
        #通过os.listdir命令可以浏览某一个目录下面有哪些文件,然后循环的反序列化
        print(os.listdir(r"D:\Python Work Location\Python 0507\day07\db"))
    

    运行结果:

    85b8a467159e14ec4b2d16bff39ed199
    85b8a467159e14ec4b2d16bff39ed199
    ['85b8a467159e14ec4b2d16bff39ed199', 'd0cff574feed705df3655a209c79c7ef']
    
    Process finished with exit code 0
    
    展开全文
  • python 面向对象全面详解

    万次阅读 多人点赞 2019-01-12 14:25:23
    一、对面向对象的理解 1、面向对象的编程---object oriented programming 2、python面向对象的重要术语: 3、函数和面向对象编程的区别 二、封装、继承、多态 1、封装(Encapsulation) 2、继承(Inheritance...

    目录

     

    一、对面向对象的理解

    1、面向对象的编程---object oriented programming

    2、python面向对象的重要术语:

    3、函数和面向对象编程的区别

    二、封装、继承、多态

    1、封装(Encapsulation)

    2、继承(Inheritance)

    3、多态(Polymorphism)

    三、面向对象的各种方法

    1、静态方法  (用这个装饰器来表示  @staticmethod  )

    2、类方法  (用这个装饰器来表示 @classmethod)

    3、属性方法 (用这个装饰器表示 @property)

    四、高级面向对象

    1、成员修饰符

    2、特殊成员

    3、类与对象

    4、异常处理

    5、反射/自省

    6、单例模式


    一、对面向对象的理解

    1、面向对象的编程---object oriented programming

    简称:OOP,是一种编程的思想。OOP把对象当成一个程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象的出现极大的提高了编程的效率,使其编程的重用性增高。

    2、python面向对象的重要术语:

      1、多态(polymorphism):一个函数有多种表现形式,调用一个方法有多种形式,但是表现出的方法是不一样的。

      2、继承(inheritance)子项继承父项的某些功能,在程序中表现某种联系

      3、封装(encapsulation)把需要重用的函数或者功能封装,方便其他程序直接调用

      4、类:对具有相同数据或者方法的一组对象的集合

      5、对象:对象是一个类的具体事例

      6、实例化:是一个对象事例话的实现

      7、标识:每个对象的事例都需要一个可以唯一标识这个事例的标记

      8、实例属性:一个对象就是一组属性的集合

      9、事例方法:所有存取或者更新对象某个实例一条或者多条属性函数的集合。

      10、类属性:属于一个类中所有对象的属性,

      11、类方法:那些无须特定的对性实例就能够工作的从属于类的函数。

    3、函数和面向对象编程的区别

      相同点:都是把程序进行封装、方便重复利用,提高效率。

      不同点:函数重点是用于整体调用,一般用于一段不可更改的程序。仅仅是解决代码重用性的问题。

      而面向对象除了代码重用性。还包括继承、多态等。使用上更加灵活。

    二、封装、继承、多态

    1、封装(Encapsulation)

    封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。 
    对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。

    class Foo:
    
        def __init__(self, name, age ,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
        def eat(self):
            print "%s,%s岁,%s,吃奶" %(self.name, self.age, self.gender)
    
        def he(self):
            print "%s,%s岁,%s,喝水" %(self.name, self.age, self.gender)
    
        def shui(self):
            print "%s,%s岁,%s,睡觉" %(self.name, self.age, self.gender)
    
    a = Foo('jack', 10, '男')
    a.eat()
    a.he()
    a.shui()
    
    b = Foo('rose', 11, '女')
    b.eat()
    b.he()
    b.shui()

    2、继承(Inheritance)

    继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。

    例如:

    猫可以:喵喵叫、吃、喝、拉、撒

    狗可以:汪汪叫、吃、喝、拉、撒

    公共的部分就是 吃、喝、拉、撒

    如下实现:

    class Animal:
    
        def eat(self):
            print "%s 吃 " %self.name
    
        def drink(self):
            print "%s 喝 " %self.name
    
        def shit(self):
            print "%s 拉 " %self.name
    
        def pee(self):
            print "%s 撒 " %self.name
    
    class Cat(Animal):
    
        def __init__(self, name):
            self.name = name
            self.breed = '猫'
    
        def cry(self):
            print '喵喵叫'
    
    class Dog(Animal):
    
        def __init__(self, name):
            self.name = name
            self.breed = '狗'
    
        def cry(self):
            print '汪汪叫'
    
    # ######### 执行 #########
    
    c1 = Cat('猫one')
    c1.eat()
    
    c2 = Cat('猫two')
    c2.drink()
    
    d1 = Dog('狗one')
    d1.eat()

    注意: 关于多继承 
    - 在Python中,如果父类和子类都重新定义了构造方法init( ),在进行子类实例化的时候,子类的构造方法不会自动调用父类的构造方法,必须在子类中显示调用。 
    - Python的类可以继承多个类,Java和C#中则只能继承一个类 
    - Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先 
    - 当类是经典类时,多继承情况下,会按照深度优先方式查找,当类是新式类时,多继承情况下,会按照广度优先方式查找

    经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

    3、多态(Polymorphism)

    首先Python不支持多态,也不用支持多态,python是一种多态语言,崇尚鸭子类型。

    在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述: 
    “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

    在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为鸭的对象,并调用它的走和叫方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的走和叫方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的走和叫方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

    鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。

    例子:

    class A:    
        def prt(self):    
            print "A"    
    
    class B(A):    
        def prt(self):    
            print "B"    
    
    class C(A):    
        def prt(self):    
            print "C"    
    
    class D(A):    
        pass    
    
    class E:    
        def prt(self):    
            print "E"    
    
    class F:    
        pass    
    
    def test(arg):    
        arg.prt()    
    
    a = A()    
    b = B()    
    c = C()    
    d = D()    
    e = E()    
    f = F()    
    
    test(a)    
    test(b)    
    test(c)    
    test(d)    
    test(e)    
    test(f) 
    
    # 结果
    A  
    B  
    C  
    A  
    E  
    Traceback (most recent call last):  
      File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 33, in <module>  
        test(a),test(b),test(c),test(d),test(e),test(f)  
      File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 24, in test  
        arg.prt()  
    AttributeError: F instance has no attribute 'prt'  

    没有谁规定test方法是接收的参数是什么类型的。test方法只规定,接收一个参数,调用这个参数的prt方法。在运行的时候如果这个参数有prt方法,python就执行,如果没有,python就报错,因为abcde都有prt方法,而f没有,所以得到了上边得结果,这就是python的运行方式。

    三、面向对象的各种方法

    1、静态方法  (用这个装饰器来表示  @staticmethod  )

    意思是把 @staticmethod 下面的函数和所属的类截断了,这个函数就不属于这个类了,没有类的属性了,只不是还是要通过类名的方式调用  

    看个小例子:

    错误示例:
     
    class Person(object):
        def __init__(self, name):
            self.name = name
     
        @staticmethod  # 把eat方法变为静态方法
        def eat(self):
            print("%s is eating" % self.name)
     
     
    d = Person("xiaoming")
    d.eat()  
     
    ############## 
     
    结果:
    TypeError: eat() missing 1 required positional argument: 'self'

    因为用静态方法把eat这个方法与Person这个类截断了,eat方法就没有了类的属性了,所以获取不到self.name这个变量。

    正确示例: 
    
     
    class Person(object):
        def __init__(self, name):
            self.name = name
    
        @staticmethod  # 把eat方法变为静态方法
        def eat(x):
            print("%s is eating" % x)
    
    
    d = Person("xiaoming")
    d.eat("jack")   
    
    #就把eat方法当作一个独立的函数给他传参就行了

    2、类方法  (用这个装饰器来表示 @classmethod)

    类方法只能访问类变量,不能访问实例变量

    看个例子:

    错误示例: 
    
    class Person(object):
        def __init__(self, name):
            self.name = name
    
        @classmethod  # 把eat方法变为类方法
        def eat(self):
            print("%s is eating" % self.name)
    
    
    d = Person("xiaoming")
    d.eat()  
    
    ###########   
    
    结果: 
    AttributeError: type object 'Person' has no attribute 'name'

    因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量   

    class Person(object):
        name="杰克"
        def __init__(self, name):
            self.name = name
    
        @classmethod  # 把eat方法变为类方法
        def eat(self):
            print("%s is eating" % self.name)
    
    
    d = Person("xiaoming")
    d.eat()

    3、属性方法 (用这个装饰器表示 @property)

    把一个方法变成一个静态属性,属性就不用加小括号那样的去调用了

    看个小例子:

    错误示例: 
    
    class Person(object):
    
        def __init__(self, name):
            self.name = name
    
        @property  # 把eat方法变为属性方法
        def eat(self):
            print("%s is eating" % self.name)
    
    
    d = Person("xiaoming")
    d.eat()  
    
    
    ########## 
    结果: 
    TypeError: 'NoneType' object is not callable

    因为eat此时已经变成一个属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了 

    class Person(object):
    
        def __init__(self, name):
            self.name = name
    
        @property  # 把eat方法变为属性方法
        def eat(self):
            print("%s is eating" % self.name)
    
    
    d = Person("xiaoming")
    d.eat

    四、高级面向对象

    1、成员修饰符

    python的类中只有私有成员和公有成员两种,不像c++中的类有公有成员(public),私有成员(private)和保护成员(protected).并且python中没有关键字去修饰成员,默认python中所有的成员都是公有成员,但是私有成员是以两个下划线开头的名字标示私有成员,私有成员不允许直接访问,只能通过内部方法去访问,私有成员也不允许被继承。

    class a:  # 说明父类的私有成员无法在子类中继承
        def __init__(self):
            self.ge=123
            self.__gene=456
    
    class b(a):
        def __init__(self,name):
            self.name=name
            self.__age=18
            super(b,self).__init__()  # 这一行会报错
        def show(self):
            print(self.name)
            print(self.__age)
            print(self.ge)
            print(self.__gene)  # 这一行也会报错
    obj=b("xiaoming")
    print(obj.name)
    print(obj.ge)
    # print(obj.__gene)  # 这个也会报错
    obj.show()

    上面就是类里面的私有成员了。

    2、特殊成员

    1.__init__

    __init__方法可以简单的理解为类的构造方法(实际并不是构造方法,只是在类生成对象之后就会被执行),之前已经在上一篇博客中说明过了。

    2.__del__

    __del__方法是类中的析构方法,当对象消亡的时候(被解释器的垃圾回收的时候会执行这个方法)这个方法默认是不需要写的,不写的时候,默认是不做任何操作的。因为你不知道对象是在什么时候被垃圾回收掉,所以,除非你确实要在这里面做某些操作,不然不要自定义这个方法。

    3.__call__

    __call__方法在类的对象被执行的时候(obj()或者 类()())会执行。

    4.__int__

    __int__方法,在对象被int()包裹的时候会被执行,例如int(obj)如果obj对象没有、__int__方法,那么就会报错。在这个方法中返回的值被传递到int类型中进行转换。

    5.__str__

    __str__方法和int方法一样,当对象被str(obj)包裹的时候,如果对象中没有这个方法将会报错,如果有这个方法,str()将接收这个方法返回的值在转换成字符串。

    6.__add__

    __add__方法在两个对象相加的时候,调用第一个对象的__add__方法,将第二个对象传递进来,至于怎么处理以及返回值,那是程序员自定义的,就如下面的例子:

    class abc:
        def __init__(self,age):
            self.age=age
        def __add__(self,obj):
            return self.age+obj.age
    a1=abc(18)
    a2=abc(20)
    print(a1+a2)
    #执行结果:38

    7.__dict__

    __dict__方法在类里面有,在对象里面也有,这个方法是以字典的形式列出类或对象中的所有成员。就像下面的例子:

    class abc:
        def __init__(self,age):
            self.age=age
        def __add__(self,obj):
            return self.age+obj.age
    a1=abc(18)
    print(abc.__dict__)
    print(a1.__dict__)
    #执行结果:
    {'__add__': <function abc.__add__ at 0x0000020666C9E2F0>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'abc' objects>, '__init__': <function abc.__init__ at 0x0000020666C9E268>, '__doc__': None, '__dict__': <attribute '__dict__' of 'abc' objects>}
    {'age': 18}

    8.__getitem__ __setitem__ __delitem__

    __getitem__方法匹配 对象[索引] 这种方式,__setitem__匹配 对象[索引]=value 这种方式,__delitem__匹配 del 对象[索引] 这种方式,例子如下:

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def __getitem__(self, item):  # 匹配:对象[item]这种形式
            return item+10
        def __setitem__(self, key, value):  # 匹配:对象[key]=value这种形式
            print(key,value)
        def __delitem__(self, key):  # 匹配:del 对象[key]这种形式
            print(key)
    
    li=Foo("alex",18)
    print(li[10])
    li[10]=100
    del li[10]
    执行结果:
    20
    10 100
    10

    9.__getslice__ __setslice__ __delslice__

    这三种方式在python2.7中还存在,用来对对象进行切片的,但是在python3之后,将这些特殊方法给去掉了,统一使用上面的方式对对象进行切片,因此在使用__getitem__ __setitem__ 这两个方法之前要先判断传递进参数的类型是不是slice对象。例子如下:

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
            self.li=[1,2,3,4,5,6,7]
        def __getitem__(self, item):  # 匹配:对象[item]这种形式
            if isinstance(item,slice):  # 如果是slice对象,返回切片后的结果
                return self.li[item]  # 返回切片结果
            elif isinstance(item,int):  # 如果是整形,说明是索引
                return item+10
        def __setitem__(self, key, value):  # 匹配:对象[key]=value这种形式
            print(key,value)
        def __delitem__(self, key):  # 匹配:del 对象[key]这种形式
            print(key)
        def __getslice__(self,index1,index2):
            print(index1,index2)
    
    li=Foo("alex",18)
    print(li[3:5])
    #执行结果:
    [4, 5]

    10.__iter__

    类的对象如果想要变成一个可迭代对象,那么对象中必须要有__iter__方法,并且这个方法返回的是一个迭代器。

    for 循环的对象如果是一个可迭代的对象,那么会先执行对象中的__iter__方法,获取到迭代器,然后再执行迭代器中的__next__方法获取数据。如果for循环的是一个迭代器,那么直接执行迭代器中的__next__方法。

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def __iter__(self):
            return iter([1,2,3,4,5])  # 返回的是一个迭代器
    li=Foo("alex",18)
    
    # 1.如果类中有__iter__方法,他的对象就是可迭代对象
    # 2.对象.__iter()的返回值是一个迭代器
    # 3.for循环的如果是迭代器,直接执行.next方法
    # 4.for循环的如果是可迭代对象,先执行对象.__iter(),获取迭代器再执行next
    
    for i in li:
        print(i)
    #执行结果:
    1
    2
    3
    4
    5

    11.isinstance和issubclass

    之前讲过isinstance可以判断一个变量是否是某一种数据类型,其实,isinstance不只可以判断数据类型,也可以判断对象是否是这个类的对象或者是这个类的子类的对象,代码如下:

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    class Son(Foo):
        pass
    obj=Son("xiaoming",18)
    print(isinstance(obj,Foo))
    执行结果:True

    issubclass用来判断一个类是否是某个类的子类,返回的是一个bool类型数据,代码如下:

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    class Son(Foo):
        pass
    obj=Son("xiaoming",18)
    print(issubclass(Son,Foo))
    执行结果:True

    3、类与对象

    __new__和__metaclass__

    在python中,一切皆对象,我们定义的类其实。。。也是一个对象,那么,类本身是谁的对象呢?在python2.2之前(或者叫经典类中),所有的类,都是class的对象,但是在新式类中,为了将类型(int,str,float等)和类统一,所以,所有的类都是type类型的对象。当然,这个规则可以被修改,在类中有一个属性 __metaclass__ 可以指定当前类该由哪个类进行实例化。而创建对象过程中,其实构造器不是__init__方法,而是__new__方法,这个方法会返回一个对象,这才是对象的构造器。下面是一个解释类实例化对象内部实现过程的代码段:

    class Mytype(type):
        def __init__(self, what, bases=None, dict=None):
            super(Mytype,self).__init__(what, bases, dict)
        def __call__(self, *args, **kwargs):
            obj=self.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
    class Foo:
        __metaclass__=Mytype
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    obj=Foo("xiaoming",18)
    print(obj.name,obj.age)
    执行结果:xiaoming 18

    4、异常处理

    python中使用try except finally组合来实现异常扑捉,不像java中是使用try catch finally......其中,except中的Exception是所有异常的父类,下面是一个异常处理的示例:

    try:
        int("aaa")  #可能出现异常的代码
    except IndexError as e:  # 捕捉索引异常的子异常,注意,这里的as e在老版本的py中可以写成,e但是新版本中用as e,",e"未来可能会淘汰
        print("IndexError:",e)
    except ValueError as e:  # 捕捉value错误的子异常
        print("ValueError:",e)
    except Exception as e:  # 如果上面两个异常没有捕获到,那么使用Exception捕获,Exception能够捕获所有的异常
        print("Exception:",e)
    else:  # 如果没有异常发生,执行else中的代码块
        print("true")
    finally:  # 不管是否发生异常,在最后都会执行finally中的代码,假如try里面的代码正常执行,先执行else中的代码,再执行finally中的代码
        print("finally")
    执行结果:
    ValueError: invalid literal for int() with base 10: 'aaa'
    finally

    那么既然Exception是所有异常的父类,我们可以自已定义Exception的子类,实现自定义异常处理,下面就是实现例子:

    class OldBoyError(Exception):  # 自定义错误类型
        def __init__(self,message):
            self.message=message
        def __str__(self):  # 打印异常的时候会调用对象里面的__str__方法返回一个字符串
            return self.message
    try:
        raise OldBoyError("我错了...")  # raise是主动抛出异常,可以调用自定义的异常抛出异常
    except OldBoyError as e:
        print(e)
    执行结果:我错了...

    异常处理里面还有一个断言,一般用在判断执行环境上面,只要断言后面的条件不满足,那么就抛出异常,并且后面的代码不执行了。

    print(123)
    assert 1==2  # 断言,故意抛出异常,做环境监测用,环境监测不通过,报错并结束程序
    print("456")
    执行结果:
        assert 1==2  # 断言,故意抛出异常,做环境监测用,环境监测不通过,报错并结束程序
    123
    AssertionError

    5、反射/自省

    python中的反射/自省的实现,是通过hasattr、getattr、setattr、delattr四个内置函数实现的,其实这四个内置函数不只可以用在类和对象中,也可以用在模块等其他地方,只是在类和对象中用的很多,所以单独提出来进行解释。

    1. hasattr(key)返回的是一个bool值,判断某个成员或者属性在不在类或者对象中
    2. getattr(key,default=xxx)获取类或者对象的成员或属性,如果不存在,则会抛出AttributeError异常,如果定义了default那么当没有属性的时候会返回默认值。
    3. setattr(key,value)假如有这个属性,那么更新这个属性,如果没有就添加这个属性并赋值value
    4. delattr(key)删除某个属性

    注意,上面的key都是字符串,而不是变量,也就是说可以通过字符串处理类中的成员或者对象中的属性。下面是一个例子代码:

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def show(self):
            return self.name,self.age
    obj=Foo("xiaoming",18)
    print(getattr(obj,"name"))
    setattr(obj,"k1","v1")
    print(obj.k1)
    print(hasattr(obj,"k1"))
    delattr(obj,"k1")
    show_fun=getattr(obj,"show")
    print(show_fun())
    执行结果:
    xiaoming
    v1
    True
    ('xiaoming', 18)

    反射/自省能够直接访问以及修改运行中的类和对象的成员和属性,这是一个很强大的功能,并且并不像java中效率很低,所以用的很多。

    下面是一个反射/自省用在模块级别的例子:

    import s2
    operation=input("请输入URL:")
    if operation in s2.__dict__:
        getattr(s2,operation)()
    else:
        print("404")
    
    #模块s2中的代码:
    def f1():
        print("首页")
    def f2():
        print("新闻")
    def f3():
        print("精选")
    执行结果:
    请输入URL:f1
    首页

    6、单例模式

    这里介绍一个设计模式,设计模式在程序员写了两三年代码的时候,到一定境界了,才会考虑到设计模式对于程序带来的好处,从而使用各种设计模式,这里只是简单的介绍一个简单的设计模式:单例模式。在面向对象中的单例模式就是一个类只有一个对象,所有的操作都通过这个对象来完成,这就是面向对象中的单例模式,下面是实现代码:

    class Foo:  # 单例模式
        __v=None
        @classmethod
        def ge_instance(cls):
            if cls.__v:
                return cls.__v
            else:
                cls.__v=Foo()
                return cls.__v
    obj1=Foo.ge_instance()
    print(obj1)
    obj2=Foo.ge_instance()
    print(obj2)
    obj3=Foo.ge_instance()
    print(obj3)
    执行结果:
    <__main__.Foo object at 0x000001D2ABA01860>
    <__main__.Foo object at 0x000001D2ABA01860>
    <__main__.Foo object at 0x000001D2ABA01860>

    可以看到,三个对象的内存地址都是一样的,其实,这三个变量中存储的都是同一个对象的内存地址,这样有什么好处呢?能够节省资源,就比如在数据库连接池的时候就可以使用单例模式,只创建一个类的对象供其他程序调用,还有在web服务中接收请求也可以使用单例模式来实现,节省资源。

     

    勇气通往天堂,怯懦通往地狱。

    展开全文
  • 【C/C++面试必备】面向对象与面向过程的区别

    千次阅读 多人点赞 2021-07-14 07:29:01
    这篇文章来说下面向对象编程! 那什么是面向对象编程呢? 是这样? 还是这样? 当然,都不是! 介绍面向对象之前,那必须先说一下面向过程。 什么是面向过程呢? 面向过程(Procedure-Oriented ...
  • C++面向对象程序设计 面向对象编程

    万次阅读 多人点赞 2018-09-12 22:39:50
    1.1 面向过程的编程风格与面向对象的编程风格 C语言是面向过程语言,也称为命令型语言,面向过程通常采用自顶向下设计,问题复杂时不断运用自顶向下设计(即函数分解法)直到容易处理。自顶向下设计优点是直观有...
  • 面向对象在生活世界

    千次阅读 2016-12-15 17:13:26
    面向对象是怎么来的,是由面向过程演变而来,所以我们要对面向过程有一个简单的了解,最常见的面向过程理解就是想用有什么就创建一个什么来用,没有太多的全面思考,而面向对象的出现就是为了修复面向过程的种种不足...
  • 如何理解Java面向对象

    万次阅读 多人点赞 2018-08-01 14:28:30
    &nbsp;&nbsp;...我的理解是:面向对象是向现实世界模型的自然延伸,这是一种”万物皆对象”的编程思想。现实生活的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。 &
  • js面向对象理解

    万次阅读 多人点赞 2019-05-13 21:25:29
    面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,ECMAScript 没有类的概念,因此它的对象也与基于类的语言的对象有所不同。 js(如果没有作特殊说明,本文...
  • python是面向对象的语言吗

    千次阅读 2021-01-13 19:56:59
    Python从设计之初就已经是一门面向对象的语言,正因为如此,Python中创建一个...类变量:类变量整个实例化的对象中是公用的。类变量定义类中且函数体之外。类变量通常作为实例变量使用。数据成员:类变量...
  • C++面试题-面向对象-面向对象概念

    千次阅读 2019-01-11 00:16:27
    C++面试题-面向对象-面向对象概念 问:说说C++和C的主要区别? 答: C语言属于面向过程语言,通过函数来实现程序功能。而C++是面向对象语言,主要通过类的形式来实现程序功能。 使用C++编写的面向对象应用程序比...
  • java 面向对象的特征(详解):

    千次阅读 2019-09-21 15:27:18
    Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下, 使用Java语言去设计、开发计算机程序。 这里的对象泛指现实一切事物,每种事物都具备自己的属性和行为。...
  • 浅谈面向对象方法学

    千次阅读 热门讨论 2020-05-15 18:00:32
    一、面向对象的基本概念 对象、类、实例、消息、方法 二、面向对象的基本特征 封装、继承、多态 三、面向对象的建模 对象模型、动态模型、功能模型 四、面向对象设计 五、面向对象的实现 设计风格 测试策略...
  • 面向对象程序设计

    千次阅读 2021-06-04 13:22:52
    面向对象是相对于面向过程的,比如你要充话费,你会想,可以下个支付宝,然后绑定银行卡,然后淘宝上买卡,自己冲,这种种过程。但是对于你女朋友就一样了,她是面向“对象”的,她会想,谁会充话费呢?当然是你...
  • Java程序设计(面向对象)- 基本概念

    万次阅读 多人点赞 2019-05-25 20:10:06
    Java 是面向对象的编程语言,对象就是面向对象程序设计的核心。所谓对象就是真实世界的实体,对象与实体是一一对应的,也就是说现实世界每一个实体都是一个对象,它是一种具体的概念。 定义:以基于对象的思维...
  • 面向对象测试

    千次阅读 2020-04-01 17:03:52
    一、面向对象影响测试  传统的测试软件是从“小型测试”开始,逐步过渡到“大型测试”,即从单元测试开始,逐步进入集成测试,最后进行确认测试和系统测试。对于传统的软件系统来说,单元测试集中测试最小的可编译...
  • 软考(软件设计师)考点总结 -- 面向对象设计基础

    千次阅读 多人点赞 2021-05-21 17:31:21
    软考软件设计师考点总结 -- 面向对象基础:面向对象基本概念、面向对象的特性、分析、设计原则、设计模式:创建型模式、结构型模式、行为型模式、UML基本概念、UML模型。
  • Kotlin面向对象(一)

    千次阅读 2017-05-24 16:56:36
    面向对象的含义大家应该并陌生,通过将事物抽象成对象,大大简化了程序的开发难度。我们常用的Java、Python、C++都属于面向对象的编程语言。Kotlin和java很相似,也是一种面向对象的语言。作为Kotlin最重要的一...
  • 面向对象的五大基本原则

    万次阅读 2019-01-06 20:18:24
    面向对象的三大特性:封装、继承、多态 面向对象的七(或五大原则,前五项)大基本原则:单一职责原则(SRP) 、开放封闭原则(OCP) 、里氏替换原则(LSP)、 依赖倒置原则(DIP)、 接口隔离原则(ISP)、迪米特...
  • Java面向对象的理解

    千次阅读 多人点赞 2019-03-17 00:03:55
    1. 面向对象 Java 是面向对象的编程语言,对象就是面向对象程序设计的核心。其基本思想是使用对象、类、继承、封装、多态等基本概念来进行程序设计。从现实世界客观存在的事物(即对象)出发来构造软件系统,并且...
  • Kotlin面向对象

    千次阅读 2021-12-14 16:09:00
    我们常用的编程语言:Java、C++、Python都属于面向对象编程。Kotlin与java类似,也是一种面向对象编程语言。本文从面向对象三个基本特征:封装、继承、多态,来阐述一下Kotlin面向对象编程。
  • java面向对象简介

    千次阅读 多人点赞 2021-11-06 13:59:14
    因此,尽管人们发现C++太适用,但设计Java的时候还是尽可能地接近C++,以便系统更易于理解,Java剔除了C++许多很少使用、难以理解、易混淆的特性。目前看来,这些特性带来的麻烦远远多于其带来的好处。 的确...
  • 面向对象

    千次阅读 2017-07-26 16:10:29
    软件生命周期和设计原则 什么是软件生命周期?...可拓展性:当软件需要升级增加新的功能,能够现有的系统架构上方便的创建新的模块,而不是需要改变软件现有的结构,也不会影响已经存在的模块。 可维
  • 面向对象编程基本概念

    千次阅读 2018-04-18 15:41:53
    面向对象编程(OOP)为软件社区引入了一场革命,以帮助解决这些问题。 OOP专注于模块化,改变容忍度,代码重用,易于理解和分布式开发。今天大多数项目都使用面向对象的概念。由于C ++引入了OOP,因此用户体验已经...
  • C++面向对象基础

    万次阅读 多人点赞 2018-05-20 12:40:59
    面向对象基础面向对象三大特性封装性:数据和代码捆绑一起,避免外界干扰和确定性访问。封装可以使得代码模块化。优点:确保用户代码不会无意间破坏封装对象的状态被封装的类的具体实现细节可以随时改变,而无须...
  • 面向对象设计的七大设计原则详解

    万次阅读 多人点赞 2018-10-03 12:32:21
    面向对象的七大设计原则 文章目录面向对象的七大设计原则简述七大原则之间的关系一、开闭原则(The Open-Closed Principle ,OCP)概念理解系统设计需要遵循开闭原则的原因开闭原则的实现方法一个符合开闭原则的...
  • java面向对象三大特性

    千次阅读 2021-03-01 19:31:10
    程序使用对象来映射现实的事物使用对象的关系来描述事物之间的联系,这种思想就是面向对象。 提到面向对象,自然会想到面向过程,面向过程就是分析解决问题所需要的步骤,然后用函数把这些步骤一一实现,使用...
  • 深入理解Java面向对象

    万次阅读 多人点赞 2016-08-03 09:13:10
    深入理解Java面向对象
  • 面向对象基本概念

    万次阅读 多人点赞 2019-02-06 21:56:15
    面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象...
  • 什么是面向对象?(通俗易懂)

    千次阅读 2019-05-27 11:59:00
    大佬:我们正式进入学习这部分前,了解一下面向过程和面向对象这两个概念,对于我们 接下来的学习有很大的好处。别急,下面我就来和你说一说。 面向过程——步骤化 面向过程就是分析出实现需求所需要的步骤,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 279,630
精华内容 111,852
关键字:

在面向对象中不属于对象