黑马程序员——java基础---面向对象
一、概述
什么是面向对象?
答:面向对象和面向过程一样,是一种处理事物的思维模式。
面向对象和面向过程有什么关系?
答:面向对象是基于面向过程的,面向过程注重的是处理事物时的行为,而面相对像则侧重于具备具备处理能力的对象。
面向对象有什么特点?
答:可以让复杂的问题变简单;可以从执行者的角色转换到调度者;对象只需对外提供特有的方法供调度者使用。
类与对象的关系?
答:类是对某种事物的特征或功能的描述。对象是具备某种特征或功能的具体实体。
例如:对学生类的描述可以有姓名、学号、年龄、班级等。而如果我们具体到对象那就是张三、1012789、16、高二(1)班。
面向对象的三大特性:封装、继承、多态。
二、封装
概念:隐藏了对象的属性和实现方式,仅对外提供公共的访问方式。
好处:将变化隔离;方便使用;提高了重用性;提高了安全性;
原则:将属性都隐藏,提供公共访问方法即可。将其它不需要对外提供的内容也都隐藏。
——>private关键字
private:权限修饰符,成员修饰符,用于修饰类中的成员变量及函数,被私有化的成员只在该类中有效。
作用:将成员变量私有化以后,类以外即使建立了对象也不能直接访问。可以对外提供对应的set ,get方法对其进行访问。在方法中加入逻辑判断等语句,对访问的数据进行操作,提高代码健壮性和对数据访问的安全性。
——>构造函数
概念:函数名与类名相同,不用定义返回值类型,不可以写return语句。调用者通过new字段来使用构造函初始化对象,它不能像普通函数那样被对象调用。当一个类中没有定义构造函数时,那么系统会默认给该类加入一个无参的构造函数。当在类中自定义了构造函数后,默认的构造函数就没有了。默认构造函数的权限和所属类一致。默认构造函数的权限是随着的类的变化而变化的。
构造函数与一般函数的区别:
1、写法不同
构造函数的函数名与类名相同;不用定义返回值类型;不可以写return语句。
一般函数需要定义返回值类型,只有当返回值类型是void并且return语句在函数最后一行时才可以省略不写。
2、运行不同
构造函数是在对象一建立就运行,给对象初始化。一个对象建立,构造函数只运行一次。
而一般方法是对象调用才执行,给是对象添加对象具备的功能。可以被该对象调用多次。
3、调用不同
构造函数可以调用一般函数。
一般函数不可以调用构造函数(因为构造函数中有this语句和super语句,而this语句和super语句只能存在于构造函数的第一行)。
——> 构造代码块
对象一建立就运行,而且优先于构造函数执行。
作用:给所有本类对象进行初始化。
构造代码块和构造函数的区别:构造代码块是给本类中所有对象进行统一初始化,而构造函数是给本类中对应的对象初始化。构造代码块中定义的是不同对象共性的初始化内容。
——>this 关键字和 super 关键字
this关键字: this代本类对象的引用。即其所在函数所属对象的引用。哪个对象在调用this所在的函数,this就代表哪个对象。在同一类中,this可以省略,但当变量出现同名情况时,一定要加this。本类中非静态前省略了this,静态前省略了类名。this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。但凡本类功能内部使用了了本类对象,都用this表示。
this 语句: 用于构造函数之间进行互相调用。this语句只能定义在构造函数的第一行。因为初始化要先执行。
super关键字:和this的用法几乎一致。this代表本类对象的引用;而super代表父类对象的引用,即父类的内存空间的标识。当子父类出现同名成员时,可以用super进行区分,子类要调用父类指定构造函数时,可以使用super。
super语句:子类中所有的构造函数默认第一行都有一条隐式的语句 super();它会访问父类中空参数的构造函数。子类构造函数也可以通过this语句访问本类中的构造函数。但是子类中肯定至少有一个构造函数会访问父类。super语句一定定义在子类构造函数的第一行。this语句也是只能放在第一行,所以在同一个构造函数内只能存在一个this或一个super语句,二者不能共存。
——>static关键字
概念:成员修饰符,静态。用于修饰成员(成员变量和成员函数)。
特点:
1、随着类的加载而加载。也就是说:静态会随着类的消失而消失。说明它的生命周期最长。
2、优先于对象存在。静态是先存在的。对象是后存在的。
3、被所有对象所共享。
4、可以直接被类名所调用。格式:类名.静态成员。
实例变量和类变量的区别:
成员变量又叫实例变量,被static修饰的成员变量又叫类变量。
1、存放位置。
类变量随着类的加载而存在于方法区中。
实例变量随着对象的建立而存在于堆内存中。
2、生命周期:
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失。
成员变量和局部变量的区别:
1、作用范围
成员变量定义在类中,作用于整个类中。
局部变定义在局部范围内,只作用于局部范围(如函数内或者语句中)。
2、在内存中的位置:
成员变量随着对象的建立而建立在对象所在的堆内存中,随着对象的消失而消失。
局部变量存在于栈内存中。作用的范围结束,变量空间会自动释放。
3、默认初始化值
成员变量有默认初始化值。
局部变量没有默认初始化值。
什么时候使用静态?
静态的优缺点:
优点:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份;可以直接被类名调用。
缺点:生命周期过长;访问出现局限性。(静态虽好,只能访问静态)
所以使用静态要从两方面入手,静态修饰的内容有成员变量和成员函数。
1、什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态存在于堆内存中。
2、什么时候定义静态函数呢?
当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。
注意:
1、静态方法只能访问静态成员。非静态方法既可以访问静态成员也可以访问非静态成员。
2、静态方法中不可以定义this,super关键字。因为静态优先于对象存在。所以静态方法中不可以出现this。
——>静态代码块
代码格式:
1 static 2 { 3 //执行语句。 4 }
特点:随着类的加载而执行,只执行一次,并优先于主函数(如果本类中有主函数)。
二、继承
概念:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过 extends 关键字让类与类之间产生继承关系。
特点:继承的出现提高了代码的复用性。让类与类之间产生了关系,提供了多态的前提。
1、Java只支持单继承,不支持多继承。一个类只能有一个父类,不可以有多个父类。因为多继承容易带来安全隐患:当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。所以java不支持多继承,但将这种机制换成另一个种安全的方式来体现,多实现。
2、Java支持多层继承,形成一个继承体系.想要使用体系功能:查阅父类功能,建立子类对象调用功能。查阅父类功能是因为父类中定义的是该体系中共性功能。通过了解共性功能,就可以知道该体系的基本功能。那么这个体系已经可以基本使用了。创建最子类的对象调用功能是因为有可能父类不能创建对象,而且创建子类对象可以使用更多的功能,包括基本的也包括特有的。
继承中成员的特点:
子父类中的变量:如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this;子类要访问父类中的同名变量,用super。一般不会出现这种情况,因为父类中有了,子类不需要定义。而且父类定义时,一般变量都私有化。
子父类中的函数:子类可以直接访问父类中非私有的成员函数。特殊情况:当子类中定义了与父类一模一样的方法时,会发生覆盖操作。这时当子类对象调用该函数,会运行子类函数的内容。覆盖只是一种形象的说法,其实父类的方法还在内存当中,只是没有被执行而已。
——>函数覆盖
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写。
1、什么时候使用覆盖?
当父类的功能要被修改时,不建议修改源码。因为是灾难。只要通过一个类继承原有类,定义一个新的升级后的功能即可。但是功能是相同的,只是实现方法改变。这时子类可以沿袭父类中的功能定义,并重写功能内容。
2、重载与重写的区别?
重载:存在于同一类中函数与函数之间,只看同名函数的参数列表。
重写:存在于父类与子类的函数之间,子父类方法要一模一样(权限修饰符子类要大于父类)。
注意:
1、子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2、父类中的私有方法不会被覆盖,子类不可以具备父类中私有的内容。
3、在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
子父类中的构造函数:构造函数可以给本类进行对象初始化,也可以给子类对象进行初始化。在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类中所有的构造函数默认第一行有一条隐式的语句 super(); super语句会访问父类中空参数的构造函数。
为什么子类一定要访问父类中的构造函数?
答:因为子类可以直接获取父类中的数据,所以必须要先明确父类对数据的初始化过程。子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数。当父类中没有空参数构造函数时,子类构造函数必须通过手动定义super语句来明确要访问的父类中指定的构造函数。
子类的实例化过程:子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。子类的所有的构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一条默认的语句super();当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。子类中至少有一个构造函数会访问父类中的构造函数。这是一定的,否则编译失败。
——>final关键字
概念: 最终。修饰符,可以修饰类,函数,变量。
特点:
(1)被final修饰的类不可以被继承。
(2)被final修饰的方法不可以被复写。
(3)被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,也可以修饰局部变量。
(4)内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。
注意:
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final修饰。作为常量。
——>抽象类
定义:Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该没有功能主体的方法称为抽象方法,包含抽象方法的类就是抽象类。抽象是从多个事物中将共性的,本质的内容抽取出来。
格式:修饰符 abstract 返回值类型 函数名(参数列表);
特点:
1、抽象方法只有方法声明,没有方法体。抽象方法一定在抽象类中,因为抽象函数所在类,也必须被抽象标识。
2、抽象方法和抽象类都必须被abstract关键字修饰。
3、抽象类不可以实例化,也就是不可以用new创建对象。
4、抽象类中的抽象方法要被使用,必须通过其子类实例化,让子类复写所有的抽象方法后才可以建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
抽象类与一般类的区别:抽象类也有构造函数,因为抽象类是一个父类,要给子类提供实例的初始化。在抽象类中可以定义抽象方法(也可以不定义抽象方法,这样做仅仅是不让该类建立对象)。抽象类不可以实例化,也就是说不能建立对象。
三、多态
概念:某一类事物的多种体现形态。
多态的体现:父类的引用指向了自己的子类对象或者父类的引用接收了自己的子类对象。
多态的两个前提:
1、必须是类与类之间有继承或者实现关系。
2、必须存在覆盖操作。
好处:多态的出现大大的提高了程序的扩展性和后期可维护性。
坏处:虽然提高了扩展性,但是只能使用父类的引用访问父类中的成员。
多态中成员的特点:
1、在多态中,非静态成员函数的特点:在编译时期:参阅引用型变量所属的类中是否有调用的函数。如果有,编译通过,如果没有编译失败。在运行时期:参阅对象所属的类中是否有调用的函数。简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
2、在多态中,静态成员函数的特点:无论编译和运行,都参考做左边(引用型变量所属的类)。
3、在多态中,成员变量的特点:无论编译和运行,都参考左边。
多态demo代码:
1 public class DuoTaiTest { 2 public static void main(String[] args) 3 { 4 FuLei f = new ZiLei(); 5 System.out.println(f.num); //111 6 ZiLei z = new ZiLei(); 7 System.out.println(z.num); //222 8 f.method1(); //打印结果ZiLei_method_1 9 f.method2(); //打印结果FuLei_method_2 10 //f.method3(); //编译不通过,找不到method3 11 f.method4(); //打印结果FuLei_method_4 12 } 13 } 14 class FuLei 15 { 16 static int num = 111; 17 void method1() 18 { 19 System.out.println("FuLei_method_1"); 20 } 21 void method2() 22 { 23 System.out.println("FuLei_method_2"); 24 } 25 static void method4() 26 { 27 System.out.println("FuLei_method_4"); 28 } 29 } 30 class ZiLei extends FuLei 31 { 32 static int num = 222; 33 void method1() 34 { 35 System.out.println("ZiLei_method_1"); 36 } 37 void method3() 38 { 39 System.out.println("ZiLei_method_3"); 40 } 41 42 static void method4() 43 { 44 System.out.println("ZiLei_method_4"); 45 } 46 }
——>转型
向上转型:父类引用指向子类对象
Animal a=new Cat();//向上转型,类型提升
向下转型:强制将父类的引用转成子类类型。
Animal a=new Cat();
Cat c = (Cat)a;//向下转型,强制将父类引用转成子类类型
注意:千万不要将父类对象转成子类类型。我们能转换的是父类的引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。多态自始至终都是子类对象在做着变化。
——>instanceof关键字
作用:用于判断对象的类型。判断某一类型引用指向的对象,到底符合什么类型(类型为类类型或接口类型)的时候,用这个关键字。
格式:对象 intanceof 类型。
应用:
1、子类型有限。
2、当传的类型需要进行其它操作(如比较)时,必须要确定它到底是哪种子类型,才能调用它的特有方法来比较。
——>内部类
概念:当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物在使用外部事物的内容。将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。内部类全名: 外部类.内部类。
特点:
1、内部类可以直接访问外部类中的成员,包括私有。因为内部类中持有了一个外部类的引用,格式 外部类名.this.
2、外部类要访问内部类,必须建立内部类对象。
内部类的访问格式(按内部类位置分):
内部类定义在成员位置上:
1、当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中直接建立内部类对象。
外部类名.内部类名 变量名 = 外部类对象.内部类对象; Outer.Inner in = new Outer().new Inner();
2、当内部类在成员位置上,就可以被成员修饰符所修饰。
private :将内部类在外部类中进行封装。
static :当内部类被static修饰后,只能直接访问外部类中的static成员(静态里不能有Outer.this.)。出现了访问局限。
在外部其他类中,直接访问static内部类的非静态成员: new Outer.Inner().function();
在外部其他类中,直接访问static内部类的静态成员 : Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法需要访问内部类时,内部类也必须是static的。内部类定义在局部位置上:
1、不可以被成员修饰符修饰,因为public/static等成员修饰符只能修饰成员,不能修饰局部。
2、可以直接访问外部类中的成员,因为还持有外部类中的引用,但是只能所在局部访问被final修饰的变量。
3、内部类内部不能定义静态成员,因为当内部类中定义了静态成员,该内部类必须是static的。
——>匿名内部类
1、匿名内部类其实就是内部类的简写格式。
2、定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
3、匿名内部类的格式:new父类或者接口(){覆盖父类或接口中的方法,也可以定义子类的内容}
4、匿名内部类就是建立一个带内容的外部类或者接口的子类匿名对象,这个对象有点胖。
5、匿名内部类中定义的方法最好不要超过2个。
注意:当没有父类时,可以使用object类作为父类使用。