什么是面向对象?
面向对象是相对于面向过程而言的。
无论过程还是对象都是生活中的一种思考方式。
面向过程:注重的是执行的过程,强调的是执行的动作。
C语言就是面向过程的语言,实现这个需求,其实就是定义三个功能(函数);
面向对象:注重的是具体的事物,将具体的功能定义到该事物当中。
以后不用再定义具体功能,只要找到该事物,就已经具备了
这些功能。这个事物称为对象。
总结面向对象:
面向对象是一种符合现在人们思考习惯的一种的思想。
面向对象出现让角色做着转变,从执行到了指挥者。
面向对象将复杂的事情简单化。
不强调过程,而是强调对象,找到对象,其实就是找到了这些功能。
面向对象其实就是基于面向过程而来。
完成需求时:
先去找具有所需的功能的对象来用。
如果该对象不存在,那么创建一个具有所需功能的对象。
这样简化开发并提高复用。
面向对象的开发:不断地创建对象,使用对象,并维护对象和对象之间的关系。
———————————————————————————————————————
面向对象的三个特征:
封装,继承,多态
———————————————————————————————————————
类与对象的关系?
类就是对现实事物的描述,就是一组对象的
共性属性和行为的抽象。
而对象就是该类事物具体实实在在的个体。
如何创建对象?
类名 对象名 = new 类名();
Car car = new Car();
这里的Car是类名,而car是类类型变量。
类类型变量指定指向对象。
———————————————————————————————————————
什么是成员变量,什么是局部变量?
定义在类中的变量就是成员变量,定义在函数的变量就是局部变量。
成员变量与局部变量的区别?
作用范围:
成员变量:(实例变量)定义在类中,作用于整个类。
局部变量:只能在它所属的局部大括号中有效
存储情况:
成员变量:存储在堆内存中,随着对象的存在而存在。
当对象变成垃圾被回收时,该对象中的成员变量
也就随之消失
局部变量:存储在栈内存中,当方法被调用,或者语句被执
行的时候,才存在,当方法执行完毕,或者语句
运行完毕时,局部变量会被自动释放。
初始化情况:
成员变量:在内存中有默认初始化值。
局部变量:必须手动初始化。
class Demo{
int num;
public void show(){
int x = 4;
}
}
num就是成员变量,x就是局部变量。
当在一个方法中,局部变量与成员变量重名,会先使用局部变量。
class Demo{
int num = 100;
public void show(){
int num = 4;
System.out.println(num);
}
}
运行结果是4;
———————————————————————————————————————
什么是匿名对象?
没有名字的对象,是定义对象的一种简化形式。
当对象对方法进行一次调用的时候,可以使用匿名对象对代码进行简化
Car c = new Car();
c.run();只是非匿名对象
new Car().run();匿名对象
什么是封装(Encapsulation)?
是指隐藏对象的属性和实现细节,仅仅对外提供公共访问方式。
封装的好处:
将变化隔离
便于使用
提高重用性
提高安全性
封装的原则:
将不需要对外提供的内容都隐藏起来
把属性都隐藏,提供公共方法对其访问
private 是权限修饰符,只能修饰成员(成员变量和成员函数)
私有的成员只在本类中有效。
当成员私有后,提高了安全性。但访问权限降低了。
可以通过对外提供公有方法的形式对其进行访问。
私有就是封装的一种体现而已。
构造函数的定义:
class Person{
Person(){
System.out.println(“person run”);
}
}
———————————————————————————————————————
构造函数与普通函数的区别:
构造函数在对象建立时就被直接调用,用于给对象进行初始化。
一般函数用于定义对象应该具备的功能,而构造函数是在创建对象时,应具备的初始化的内容
构造函数是在对象建立时被调用。给对象初始化。
一般函数是在对象建立后,当对象调用该功能时才会执行。
当类中没有定义构造函数时,系统会指定给该类加上一个空参数的构造函数。这个
是类中默认的构造函数,当类中如果自定义了构造函数,这是默认构造函数就没有了。
在一个类中可以有多个构造函数,进行不同的初始化。
构造函数的特点:
函数名与类名相同
不用定义返回值类型
没有具体的返回值
构造函数的作用?
给对象进行初始化
**默认构造函数的特点。
多个构造函数以重载的形式存在。
———————————————————————————————————————
关键字this:
当局部变量和成员变量重名时,为了以示区分。
可以用一个关键字this来完成。
this:关键字代表是对象的引用。也就是this在指向一个对象。
this:就代表所在函数所属对象的引用。哪个对象调用this所在的函数,
this就代表哪个对象。
类中的非静态方法都需要被对象所调用,
所以每一个方法都有自己所属的对象,都用this表示。
对象的建立以及初始化过程:
Person p = new Person();
将person.class文件加载进内存。
如果p定义在函数中,那么在栈内存中开辟一个变量空间p
在堆内存中给对象分配空间
给对象中的属性进行默认初始化
给对象中的属性进行显示初始化
调用构造代码块对对象进行初始化。(执行类中的构造代码块)
调用对应的构造函数进行对象初始化。(对象初始化完毕)
将对象内存地址赋值给p变量。让p变量指向对象
关键字static:
static是一个修饰符,用于修饰成员(成员变量和成员函数)
1.静态成员随着类的加载而加载,不是随着对象而产生。
2.静态成员优先于对象存在。
3.静态成员被所有对象共享
4.静态成员多了一个调用方式,类名点的方式调用。
静态使用注意事项:
1.静态方法只能访问静态成员。
2.非静态方法可以访问静态数据。
3.静态方法中不可以定义this,super关键字(因为this代表是对象,而静态存在时,有可能没有对象,this没有任何对象代表)
4.主函数是静态的。
静态好处与弊端?
好处:静态成员多了一种调用方式,可以直接被类名调用,也可以被对象调用
弊端:静态方法只能访问静态成员,出现了访问局限性
静态修饰的数据是对象共享的数据,非静态数据是每一个对象特有的数据。
当成员变量被静态修饰后,和非静态成员变量的区别?
1.静态变量也称为类变量,也就是直接可以被类名调用的,这个变量是属于类的。
非静态变量称为成员变量,或者实例变量,是被对象调用的。
2.静态变量随着类的加载而加载,也以为这随着类的消失而消失,生命周期最长。
实例变量,随着对象的创建而加载,随着对象的消失而消失
3.静态变量存储在方法区的静态区中。实例变量存在于对象所属的堆内存中。
4.静态变量数据,被所有对象所共享。实例变量是对象中的特有数据
什么时候使用static?
成员变量:
什么时候被静态修饰呢?
当该成员变量所存储的数据,每一个对象都一样时,这时没有必要把该数据存储到每一个对象中,只要让所有对象共享该数据即可
当数据需要被对象共享时,就用static修饰。
成员函数:
当成员函数内并没有访问对象中的特有数据时,就将该方法定义成静态的。
该函数如果访问了成员变量,该函数就是非静态的。
该函数没有访问过成员变量,或者访问过静态的成员变量,为了程序的严谨性,将该方法定义成静态的,因为该方法不需要对象也可以被调用。
———————————————————————————————————————
继承(extends):子类拥有父类的属性和方法。
继承的出现,让类与类之间产生了关系。而这个关系出现,就导致了面向对象的第三个特征,继承就是多态的前提。
Java中只支持单继承,不支持多继承(java将多继承进行改良,提高安全性);
Java中可以存在多层(多重)继承。
class A{}
class B extends a{}
class c extends b{}
这是一个继承体系。
A类中定义是这个体系中的最共性的功能。
要使用这个体系的功能,建议建立最子类的对象来完成调用。
父类的由来都是子类不断向上抽取而来的。就代表着,A中定义的功能是该体系中最共性的功能。所以要了解这个体系的功能,只要参考A类中的功能即可。所以建议建立c对象,因为c中可以使用a类中的功能,也可以使用c类中的特有功能。
什么时候使用继承?
继承是用于程序设计的。只要一个事物是另一个事物的一种,就可以用继承体现。
怎么判断是另一个事物中的一种?
那么可以先视为可以继承,用更简单的方式判断,一个类如果继承了另一个类,那么
这个类是否应该具备另一个类中的所有成员,如果可以,继承有效,如果不可以,那么
无法继承。
继承的好处:
可以提高代码的复用性。有利于程序的扩展,不需要改动原来的代码的情况下,就可以实现程序的功能扩展。
**不要为了获取另一个类中的部分功能而继承。这样是为了提高复用性而继承,并没有判断
事物之间的关系,这样不可取。
继承出现后,在子父类中成员的特点。
当子父类中出现同名的成员变量时,这时为了区分两个变量,在子类用this调用子类变量,用super调用的是父类的变量。
当子父类中出现一模一样的函数时,子类对象在调用该函数时,运行的是子类中的函数。
父类中的函数好像被覆盖一样。
这就是函数的另一个特性:覆盖(复写,重写)override 函数的另一个特性:重载overload
覆盖:在子父类中出现一模一样的函数,称为覆盖。
子父类中构造函数的特点:
class Fu{
Fu(){
System.out.println(“fu”);
}
} class Zi extends Fu(){
Zi(){
System.out.println(“zi”);
}
}
创建子类之后,运行结果是fu zi。
这是因为在子类的构造函数的第一行,其实就是一条默认的隐式语句 super();
super():和this():用法是一样的,this():调用了本类中的构造函数。
super():调用了父类中的构造函数。
子类实例化的时候,因为子类继承父类的成员数据,
**所以必须要先看父类是如何对自己的成员初始化的。
子类的所有构造函数默认都会访问父类中空参数的构造函数。
当父类中没有定义空参数的构造时,子类必须通过super语句明确指定要访问的父类中的构造函数。(子类的构造函数无论如何都必须访问父类中的构造函数,要明确父类的初始化过程)
Super语句用于访问父类的初始化,而初始化动作要先完成,所以super语句必须定义在构造函数的第一行。
那么就和曾经的this语句冲突了,因为this语句也要定义在构造函数的第一行。
所以一个构造函数中,只能有一个要么this语句,要么super语句。
而且不冲突,因为子类中至少会有一个构造函数会去访问父类中的构造函数,一样可以完成父类初始化。
这个就是子类的实例化过程。
class Fu{
Fu(int x){
System.out.println(“fu .. ”+x);
}
}
class Zi extends Fu{
Zi(){
this(20);
System.out.println(“zi ... ”+x);
}
Zi(int x){
super(90);
System.out.println(“zi ... ”+x);
}
}
在创建子类的时候,如果调用无参的构造方法,会先访问子类中有参的构造方法,然后去访问父类的构造方法。
什么时候使用覆盖?
当子类的一些方法不同其他完全相同时,我们可以把与父类不同的方法覆盖掉。
super和this的用法很相似。但是
this代表是本类对象的引用,可以区分局部变量和成员变量重名。
super代表的是父类的存储空间,可以区分子父类变量重名。
子类为什么可以访问父类中的成员?
因为子类中有持有一个父类空间的标示super。在创建子类的时候,会先有父类的属性和方法。
子类不可以直接访问父类中的私有成员,可以通过间接的形式访问。
子父类中覆盖的一些细节:
1.子类覆盖父类必须要保证权限大于等于父类
2.静态覆盖静态(静态不能覆盖非静态,非静态也不能覆盖静态)
继承的弊端:
打破的封装性。
如果恶意继承进行不正确的覆盖,会导致原功能的错误。打破了封装性。
————————————————————————————————
关键字final:
1.它可以修饰类,可以修饰变量
2.final修饰的是一个最终类,不可以被继承。
3.final修饰的方法不可以被覆盖。
4.final修饰的变量是一个常量,只能被赋值一次。这个赋指的是显示初始化。
什么时候将变量修饰成final?
通常在程序中会使用一些不会变化的数据。比如3.14
哪儿这个数据直接使用是可以的,但是不易阅读,所以可以起一个容易阅读的名称。
final double PI = 3.14;
Final修饰的常量定义一般都有规范书写,被final修饰的常量名称,所有字母都大写
如果有多个单词,单词之间用下划线(_)连接,MY_NUMBER;
————————————————————————————————
抽象:
抽象就是不具体的。
抽象类:
两个类中有共性的功能,声明相同,但是具体内容不同,这时,我们只对相同的功能声明进行抽取,而不抽取功能的内容。
**当类中出现了抽象的方法后,该类也必须变成抽象类。
抽象类的特点:
1.抽象类和方法必须用abstract关键字修饰。
2.抽象方法一定要存放在抽象类中。
3.抽象类不可以被实例化。也就是不可以通过该类建立对象。(因为抽象类建立对象后,调用抽象方法没意义)
4.只有抽象类的子类将抽象类中的抽象方法全部覆盖,该子类就可以建立对象了
如果只有部分覆盖,那么子类还是一个抽象类。
抽象类什么时候定义:
当对象具备的相同的功能,但是具体的实现细节不相同,这时就可以抽象一个类。
**抽象类可以定义事物的共性内容,并且强迫子类去实现,否则子类还是抽象类。
抽象类中的一些细节:
1.抽象类中是否有构造函数
有(只要是class定义的类,里面就有构造函数)
抽象类中的构造函数用于给子类提供实例化
2.抽象类中是否可以不定义抽象方法
可以(没有抽象方法的抽象类存在的意义仅仅是不让该类创建对象,因为创建没有意义,在java awt中有体现)
3.抽象关键字和哪些关键字不可以共存
final :如果方法被抽象了,就需要被覆盖,而final是不可以被覆盖的。
private :如果函数被私有了,子类无法直接访问,无法覆盖
static:不需要对象,类名既可以调用方法,而调用抽象方法没有意义。
——————————————————————————————————————
接口:
当抽象类中的方法都是抽象的时候,这时候可以把该类定义成接口的形式。
接口是特殊的抽象类。
(java中的interface,java中的接口的体现。
广义理解:只要对外提供规则都属于接口范畴)
1.接口就是对外暴露的规则
2.接口是功能的扩展
3.接口的出现降低了耦合性(紧密联系程度);
接口的特点:
1.接口的方法都是抽象的。
2.接口不可以被实例化。
3.只有子类实现了接口中的所有抽象方法后,该子类才可以实例化,否则子类还是一个抽象类
格式特点:
当定义接口时,接口中的常见的成员有两种;
1.全局常量。
2.抽象方法。
而且接口中的成员都有固定的修饰符:
全局常量:public static final
成员方法:public abstract
这些修饰符不写,系统也会自动加上。
接口中的成员都是public权限的。
因为接口成员都是abstract,所以子类想要实例化,必须把全部方法都实现。
如果B是一个接口,A去实现B用到关键字implements,而且子类的方法必须是public
(因为接口中是方法都public)
class A implements B{
}
继承和实现的区别?
1.类与类之间称为继承:因为该类无论是抽象的还是非抽象的,它内部都可以定义非抽象方法,这个方法可以直接被子类使用,所以子类继承就可以使用了。
类与接口是实现关系:因为接口中的方法都是抽象的,必须由子类实现才能实例化
所以就用了更确切的关键字来表示:implements
2.类只能单继承,接口可以多实现
接口的好处:
一个类可以实现多个接口。
一个类在继承另一个类的同时可以实现多个接口。
接口和抽象类的区别:
1.抽象类只能被单继承,接口可以多实现
2.抽象类中可以定义非抽象方法,直接被子类使用。
接口中只有抽象方法,必须被子类实现后才可以被使用。
3.抽象类中定义体系中的基本共性功能。
接口中通常定义体系中的扩展功能。
4.接口的出现避免了单继承的局限性。
5.抽象类被继承,是从属(is a)关系。
接口可以被实现,是类似(like a)关系
接口与接口之间也可以有继承关系,接口可以多继承。类不可以。
——————————————————————————————————————
多态:
某一类事物的多种存在形态。
1. 在程序中的体现:(多态的实现机制)
父类引用或者接口引用指向了自己的子类对象。
2. 好处:
提高了代码的扩展性。
3. 前提:
1.类与类之间必须有继承关系或者实现关系。
2.通常都需要进行覆盖。
4. 弊端:
进行多态应用时,前期并不知道后期会有多少个子类出现,但是可以使用父类的引用来调用父类的方法。而后期哪个子类对象出现就执行哪个子类的具体方法实现。
这就是程序的扩展性。
但是,前期调用的时候,只能调用父类中的方法,而不能调用具体子类的方法,因为前期还不知道具体的子类是谁。
多态的应用思想:
以前,定义对象,并指挥对象做事情(调用对象方法)
当对象多了以后,指挥动作就会变得很麻烦。
这时重新思考问题,找到这些对象的共性类型。
直接指挥这个共性类型做事情即可,这样凡是属于这个类型的个体都会执行。
Animal a = new Cat();
这个叫父类应用指向子类对象,子类对象提升为父类型,向上转型,自动类型提升
想要使用cat的特有方法,只能进行强制转换
Cat c = (Cat)a;
c.eat();
c.catchMouse();
在转型过程中,其实都是子类对象在做着变化。
当你想使用子类的特有功能时,必需将父类型转成子类型才可以使用。
注意:必需保证,这个父类型指向的是该子类型的对象。
绝对不要将父类对象转成子类类型。
可以对传入的对象进行类型的判断。通过instanceof关键字完成。
对象instanceof类型用于判断该对象是否所属于该类型。
多用于接口类型的引用指向其子类对象。
多态中的细节:
成员变量:
编译时期:看的引用型变量所属的类中是否有所调用的变量。
运行时期:也是看引用型变量所属的类是否有调用的变量。
简单的说:成员变量,编译和运行都看等号左边。
成员函数:
编译时期:看的引用型变量所属的类中是否有调用的方法。
运行时期:看的对象所属的类中是否有调用方法。
简单说:对于成员函数,编译看左,运行看右。
静态函数:
编译时期:看的是引用型变量所属的类中是否有调用的方法。
运行时期:也是看的是引用型变量所属的类中是否有调用的方法。
简单说:对于静态函数,编译看左边,运行也看左边。
成员变量和静态成员:编译和运行都看左边。
只有非静态的成员函数,编译看左边,运行看右边。
面向对象是相对于面向过程而言的。
无论过程还是对象都是生活中的一种思考方式。
面向过程:注重的是执行的过程,强调的是执行的动作。
C语言就是面向过程的语言,实现这个需求,其实就是定义三个功能(函数);
面向对象:注重的是具体的事物,将具体的功能定义到该事物当中。
以后不用再定义具体功能,只要找到该事物,就已经具备了
这些功能。这个事物称为对象。
总结面向对象:
面向对象是一种符合现在人们思考习惯的一种的思想。
面向对象出现让角色做着转变,从执行到了指挥者。
面向对象将复杂的事情简单化。
不强调过程,而是强调对象,找到对象,其实就是找到了这些功能。
面向对象其实就是基于面向过程而来。
完成需求时:
先去找具有所需的功能的对象来用。
如果该对象不存在,那么创建一个具有所需功能的对象。
这样简化开发并提高复用。
面向对象的开发:不断地创建对象,使用对象,并维护对象和对象之间的关系。
———————————————————————————————————————
面向对象的三个特征:
封装,继承,多态
———————————————————————————————————————
类与对象的关系?
类就是对现实事物的描述,就是一组对象的
共性属性和行为的抽象。
而对象就是该类事物具体实实在在的个体。
如何创建对象?
类名 对象名 = new 类名();
Car car = new Car();
这里的Car是类名,而car是类类型变量。
类类型变量指定指向对象。
———————————————————————————————————————
什么是成员变量,什么是局部变量?
定义在类中的变量就是成员变量,定义在函数的变量就是局部变量。
成员变量与局部变量的区别?
作用范围:
成员变量:(实例变量)定义在类中,作用于整个类。
局部变量:只能在它所属的局部大括号中有效
存储情况:
成员变量:存储在堆内存中,随着对象的存在而存在。
当对象变成垃圾被回收时,该对象中的成员变量
也就随之消失
局部变量:存储在栈内存中,当方法被调用,或者语句被执
行的时候,才存在,当方法执行完毕,或者语句
运行完毕时,局部变量会被自动释放。
初始化情况:
成员变量:在内存中有默认初始化值。
局部变量:必须手动初始化。
class Demo{
int num;
public void show(){
int x = 4;
}
}
num就是成员变量,x就是局部变量。
当在一个方法中,局部变量与成员变量重名,会先使用局部变量。
class Demo{
int num = 100;
public void show(){
int num = 4;
System.out.println(num);
}
}
运行结果是4;
———————————————————————————————————————
什么是匿名对象?
没有名字的对象,是定义对象的一种简化形式。
当对象对方法进行一次调用的时候,可以使用匿名对象对代码进行简化
Car c = new Car();
c.run();只是非匿名对象
new Car().run();匿名对象
什么是封装(Encapsulation)?
是指隐藏对象的属性和实现细节,仅仅对外提供公共访问方式。
封装的好处:
将变化隔离
便于使用
提高重用性
提高安全性
封装的原则:
将不需要对外提供的内容都隐藏起来
把属性都隐藏,提供公共方法对其访问
private 是权限修饰符,只能修饰成员(成员变量和成员函数)
私有的成员只在本类中有效。
当成员私有后,提高了安全性。但访问权限降低了。
可以通过对外提供公有方法的形式对其进行访问。
私有就是封装的一种体现而已。
构造函数的定义:
class Person{
Person(){
System.out.println(“person run”);
}
}
———————————————————————————————————————
构造函数与普通函数的区别:
构造函数在对象建立时就被直接调用,用于给对象进行初始化。
一般函数用于定义对象应该具备的功能,而构造函数是在创建对象时,应具备的初始化的内容
构造函数是在对象建立时被调用。给对象初始化。
一般函数是在对象建立后,当对象调用该功能时才会执行。
当类中没有定义构造函数时,系统会指定给该类加上一个空参数的构造函数。这个
是类中默认的构造函数,当类中如果自定义了构造函数,这是默认构造函数就没有了。
在一个类中可以有多个构造函数,进行不同的初始化。
构造函数的特点:
函数名与类名相同
不用定义返回值类型
没有具体的返回值
构造函数的作用?
给对象进行初始化
**默认构造函数的特点。
多个构造函数以重载的形式存在。
———————————————————————————————————————
关键字this:
当局部变量和成员变量重名时,为了以示区分。
可以用一个关键字this来完成。
this:关键字代表是对象的引用。也就是this在指向一个对象。
this:就代表所在函数所属对象的引用。哪个对象调用this所在的函数,
this就代表哪个对象。
类中的非静态方法都需要被对象所调用,
所以每一个方法都有自己所属的对象,都用this表示。
对象的建立以及初始化过程:
Person p = new Person();
将person.class文件加载进内存。
如果p定义在函数中,那么在栈内存中开辟一个变量空间p
在堆内存中给对象分配空间
给对象中的属性进行默认初始化
给对象中的属性进行显示初始化
调用构造代码块对对象进行初始化。(执行类中的构造代码块)
调用对应的构造函数进行对象初始化。(对象初始化完毕)
将对象内存地址赋值给p变量。让p变量指向对象
关键字static:
static是一个修饰符,用于修饰成员(成员变量和成员函数)
1.静态成员随着类的加载而加载,不是随着对象而产生。
2.静态成员优先于对象存在。
3.静态成员被所有对象共享
4.静态成员多了一个调用方式,类名点的方式调用。
静态使用注意事项:
1.静态方法只能访问静态成员。
2.非静态方法可以访问静态数据。
3.静态方法中不可以定义this,super关键字(因为this代表是对象,而静态存在时,有可能没有对象,this没有任何对象代表)
4.主函数是静态的。
静态好处与弊端?
好处:静态成员多了一种调用方式,可以直接被类名调用,也可以被对象调用
弊端:静态方法只能访问静态成员,出现了访问局限性
静态修饰的数据是对象共享的数据,非静态数据是每一个对象特有的数据。
当成员变量被静态修饰后,和非静态成员变量的区别?
1.静态变量也称为类变量,也就是直接可以被类名调用的,这个变量是属于类的。
非静态变量称为成员变量,或者实例变量,是被对象调用的。
2.静态变量随着类的加载而加载,也以为这随着类的消失而消失,生命周期最长。
实例变量,随着对象的创建而加载,随着对象的消失而消失
3.静态变量存储在方法区的静态区中。实例变量存在于对象所属的堆内存中。
4.静态变量数据,被所有对象所共享。实例变量是对象中的特有数据
什么时候使用static?
成员变量:
什么时候被静态修饰呢?
当该成员变量所存储的数据,每一个对象都一样时,这时没有必要把该数据存储到每一个对象中,只要让所有对象共享该数据即可
当数据需要被对象共享时,就用static修饰。
成员函数:
当成员函数内并没有访问对象中的特有数据时,就将该方法定义成静态的。
该函数如果访问了成员变量,该函数就是非静态的。
该函数没有访问过成员变量,或者访问过静态的成员变量,为了程序的严谨性,将该方法定义成静态的,因为该方法不需要对象也可以被调用。
———————————————————————————————————————
继承(extends):子类拥有父类的属性和方法。
继承的出现,让类与类之间产生了关系。而这个关系出现,就导致了面向对象的第三个特征,继承就是多态的前提。
Java中只支持单继承,不支持多继承(java将多继承进行改良,提高安全性);
Java中可以存在多层(多重)继承。
class A{}
class B extends a{}
class c extends b{}
这是一个继承体系。
A类中定义是这个体系中的最共性的功能。
要使用这个体系的功能,建议建立最子类的对象来完成调用。
父类的由来都是子类不断向上抽取而来的。就代表着,A中定义的功能是该体系中最共性的功能。所以要了解这个体系的功能,只要参考A类中的功能即可。所以建议建立c对象,因为c中可以使用a类中的功能,也可以使用c类中的特有功能。
什么时候使用继承?
继承是用于程序设计的。只要一个事物是另一个事物的一种,就可以用继承体现。
怎么判断是另一个事物中的一种?
那么可以先视为可以继承,用更简单的方式判断,一个类如果继承了另一个类,那么
这个类是否应该具备另一个类中的所有成员,如果可以,继承有效,如果不可以,那么
无法继承。
继承的好处:
可以提高代码的复用性。有利于程序的扩展,不需要改动原来的代码的情况下,就可以实现程序的功能扩展。
**不要为了获取另一个类中的部分功能而继承。这样是为了提高复用性而继承,并没有判断
事物之间的关系,这样不可取。
继承出现后,在子父类中成员的特点。
当子父类中出现同名的成员变量时,这时为了区分两个变量,在子类用this调用子类变量,用super调用的是父类的变量。
当子父类中出现一模一样的函数时,子类对象在调用该函数时,运行的是子类中的函数。
父类中的函数好像被覆盖一样。
这就是函数的另一个特性:覆盖(复写,重写)override 函数的另一个特性:重载overload
覆盖:在子父类中出现一模一样的函数,称为覆盖。
子父类中构造函数的特点:
class Fu{
Fu(){
System.out.println(“fu”);
}
} class Zi extends Fu(){
Zi(){
System.out.println(“zi”);
}
}
创建子类之后,运行结果是fu zi。
这是因为在子类的构造函数的第一行,其实就是一条默认的隐式语句 super();
super():和this():用法是一样的,this():调用了本类中的构造函数。
super():调用了父类中的构造函数。
子类实例化的时候,因为子类继承父类的成员数据,
**所以必须要先看父类是如何对自己的成员初始化的。
子类的所有构造函数默认都会访问父类中空参数的构造函数。
当父类中没有定义空参数的构造时,子类必须通过super语句明确指定要访问的父类中的构造函数。(子类的构造函数无论如何都必须访问父类中的构造函数,要明确父类的初始化过程)
Super语句用于访问父类的初始化,而初始化动作要先完成,所以super语句必须定义在构造函数的第一行。
那么就和曾经的this语句冲突了,因为this语句也要定义在构造函数的第一行。
所以一个构造函数中,只能有一个要么this语句,要么super语句。
而且不冲突,因为子类中至少会有一个构造函数会去访问父类中的构造函数,一样可以完成父类初始化。
这个就是子类的实例化过程。
class Fu{
Fu(int x){
System.out.println(“fu .. ”+x);
}
}
class Zi extends Fu{
Zi(){
this(20);
System.out.println(“zi ... ”+x);
}
Zi(int x){
super(90);
System.out.println(“zi ... ”+x);
}
}
在创建子类的时候,如果调用无参的构造方法,会先访问子类中有参的构造方法,然后去访问父类的构造方法。
什么时候使用覆盖?
当子类的一些方法不同其他完全相同时,我们可以把与父类不同的方法覆盖掉。
super和this的用法很相似。但是
this代表是本类对象的引用,可以区分局部变量和成员变量重名。
super代表的是父类的存储空间,可以区分子父类变量重名。
子类为什么可以访问父类中的成员?
因为子类中有持有一个父类空间的标示super。在创建子类的时候,会先有父类的属性和方法。
子类不可以直接访问父类中的私有成员,可以通过间接的形式访问。
子父类中覆盖的一些细节:
1.子类覆盖父类必须要保证权限大于等于父类
2.静态覆盖静态(静态不能覆盖非静态,非静态也不能覆盖静态)
继承的弊端:
打破的封装性。
如果恶意继承进行不正确的覆盖,会导致原功能的错误。打破了封装性。
————————————————————————————————
关键字final:
1.它可以修饰类,可以修饰变量
2.final修饰的是一个最终类,不可以被继承。
3.final修饰的方法不可以被覆盖。
4.final修饰的变量是一个常量,只能被赋值一次。这个赋指的是显示初始化。
什么时候将变量修饰成final?
通常在程序中会使用一些不会变化的数据。比如3.14
哪儿这个数据直接使用是可以的,但是不易阅读,所以可以起一个容易阅读的名称。
final double PI = 3.14;
Final修饰的常量定义一般都有规范书写,被final修饰的常量名称,所有字母都大写
如果有多个单词,单词之间用下划线(_)连接,MY_NUMBER;
————————————————————————————————
抽象:
抽象就是不具体的。
抽象类:
两个类中有共性的功能,声明相同,但是具体内容不同,这时,我们只对相同的功能声明进行抽取,而不抽取功能的内容。
**当类中出现了抽象的方法后,该类也必须变成抽象类。
抽象类的特点:
1.抽象类和方法必须用abstract关键字修饰。
2.抽象方法一定要存放在抽象类中。
3.抽象类不可以被实例化。也就是不可以通过该类建立对象。(因为抽象类建立对象后,调用抽象方法没意义)
4.只有抽象类的子类将抽象类中的抽象方法全部覆盖,该子类就可以建立对象了
如果只有部分覆盖,那么子类还是一个抽象类。
抽象类什么时候定义:
当对象具备的相同的功能,但是具体的实现细节不相同,这时就可以抽象一个类。
**抽象类可以定义事物的共性内容,并且强迫子类去实现,否则子类还是抽象类。
抽象类中的一些细节:
1.抽象类中是否有构造函数
有(只要是class定义的类,里面就有构造函数)
抽象类中的构造函数用于给子类提供实例化
2.抽象类中是否可以不定义抽象方法
可以(没有抽象方法的抽象类存在的意义仅仅是不让该类创建对象,因为创建没有意义,在java awt中有体现)
3.抽象关键字和哪些关键字不可以共存
final :如果方法被抽象了,就需要被覆盖,而final是不可以被覆盖的。
private :如果函数被私有了,子类无法直接访问,无法覆盖
static:不需要对象,类名既可以调用方法,而调用抽象方法没有意义。
——————————————————————————————————————
接口:
当抽象类中的方法都是抽象的时候,这时候可以把该类定义成接口的形式。
接口是特殊的抽象类。
(java中的interface,java中的接口的体现。
广义理解:只要对外提供规则都属于接口范畴)
1.接口就是对外暴露的规则
2.接口是功能的扩展
3.接口的出现降低了耦合性(紧密联系程度);
接口的特点:
1.接口的方法都是抽象的。
2.接口不可以被实例化。
3.只有子类实现了接口中的所有抽象方法后,该子类才可以实例化,否则子类还是一个抽象类
格式特点:
当定义接口时,接口中的常见的成员有两种;
1.全局常量。
2.抽象方法。
而且接口中的成员都有固定的修饰符:
全局常量:public static final
成员方法:public abstract
这些修饰符不写,系统也会自动加上。
接口中的成员都是public权限的。
因为接口成员都是abstract,所以子类想要实例化,必须把全部方法都实现。
如果B是一个接口,A去实现B用到关键字implements,而且子类的方法必须是public
(因为接口中是方法都public)
class A implements B{
}
继承和实现的区别?
1.类与类之间称为继承:因为该类无论是抽象的还是非抽象的,它内部都可以定义非抽象方法,这个方法可以直接被子类使用,所以子类继承就可以使用了。
类与接口是实现关系:因为接口中的方法都是抽象的,必须由子类实现才能实例化
所以就用了更确切的关键字来表示:implements
2.类只能单继承,接口可以多实现
接口的好处:
一个类可以实现多个接口。
一个类在继承另一个类的同时可以实现多个接口。
接口和抽象类的区别:
1.抽象类只能被单继承,接口可以多实现
2.抽象类中可以定义非抽象方法,直接被子类使用。
接口中只有抽象方法,必须被子类实现后才可以被使用。
3.抽象类中定义体系中的基本共性功能。
接口中通常定义体系中的扩展功能。
4.接口的出现避免了单继承的局限性。
5.抽象类被继承,是从属(is a)关系。
接口可以被实现,是类似(like a)关系
接口与接口之间也可以有继承关系,接口可以多继承。类不可以。
——————————————————————————————————————
多态:
某一类事物的多种存在形态。
1. 在程序中的体现:(多态的实现机制)
父类引用或者接口引用指向了自己的子类对象。
2. 好处:
提高了代码的扩展性。
3. 前提:
1.类与类之间必须有继承关系或者实现关系。
2.通常都需要进行覆盖。
4. 弊端:
进行多态应用时,前期并不知道后期会有多少个子类出现,但是可以使用父类的引用来调用父类的方法。而后期哪个子类对象出现就执行哪个子类的具体方法实现。
这就是程序的扩展性。
但是,前期调用的时候,只能调用父类中的方法,而不能调用具体子类的方法,因为前期还不知道具体的子类是谁。
多态的应用思想:
以前,定义对象,并指挥对象做事情(调用对象方法)
当对象多了以后,指挥动作就会变得很麻烦。
这时重新思考问题,找到这些对象的共性类型。
直接指挥这个共性类型做事情即可,这样凡是属于这个类型的个体都会执行。
Animal a = new Cat();
这个叫父类应用指向子类对象,子类对象提升为父类型,向上转型,自动类型提升
想要使用cat的特有方法,只能进行强制转换
Cat c = (Cat)a;
c.eat();
c.catchMouse();
在转型过程中,其实都是子类对象在做着变化。
当你想使用子类的特有功能时,必需将父类型转成子类型才可以使用。
注意:必需保证,这个父类型指向的是该子类型的对象。
绝对不要将父类对象转成子类类型。
可以对传入的对象进行类型的判断。通过instanceof关键字完成。
对象instanceof类型用于判断该对象是否所属于该类型。
多用于接口类型的引用指向其子类对象。
多态中的细节:
成员变量:
编译时期:看的引用型变量所属的类中是否有所调用的变量。
运行时期:也是看引用型变量所属的类是否有调用的变量。
简单的说:成员变量,编译和运行都看等号左边。
成员函数:
编译时期:看的引用型变量所属的类中是否有调用的方法。
运行时期:看的对象所属的类中是否有调用方法。
简单说:对于成员函数,编译看左,运行看右。
静态函数:
编译时期:看的是引用型变量所属的类中是否有调用的方法。
运行时期:也是看的是引用型变量所属的类中是否有调用的方法。
简单说:对于静态函数,编译看左边,运行也看左边。
成员变量和静态成员:编译和运行都看左边。
只有非静态的成员函数,编译看左边,运行看右边。