------- android培训、java培训、期待与您交流! ----------
一、继承(面向对象的特点之二)
1、概述:
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需在定义这些属性和行为,只要继承那个类即可。
多个类可以称为子类,单独这个类称为父类或者超类。
子类可以直接访问父类中的非私有的属性和行为。
2、怎样使用继承:
通过extends关键字让类与类之间产生继承关系
示例:
class 类A extend 类B{}
3、继承的好处:
A:提高了代码的复用性
B:继承的出现让类与类之间产生了关系,提供了多态的前提
4、继承的特点:
A:java只支持单继承,不支持多继承
例如:
class A
{
void show()
{
System.out.println("a");
}
}
class B
{
void show()
{
System.out.println("b");
}
}
class C extends A,B
{
C c = new C();
c.show();
}
这样,结果不确定。因为多继承容易带来安全隐患:当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。
B:java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。
通过了解共性功能,就可以知道该体系的基本功能。
那么这个体系已经可以基本使用了。
那么在具体调用时,要创建最子类的对象,为什么呢?
一是因为有可能父类不能创建对象,
二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
定义继承时需要注意的细节:
a:不要仅为了获取其他类中的某个功能而去继承
b:类与类之间要有所属(” is a ”)关系。xx1是xx2的一种
5、继承的成员关系:
A:成员变量
在子类中使用一个变量的时候:
首先在局部范围内找,如果有就使用。
否则,在子类中找,如果有就使用。
否则,在父类中找,如果有就使用。
否则,就报错。
this和super的区别:
this代表本类对象的引用
super代表父类对象的引用
this():表示调用本类的构造方法
super():表示调用父类的构造方法
this.成员变量:表示调用本类的成员变量
super.成员变量:表示调用父类的成员变量
this.成员方法:表示调用本类的成员方法
super.成员方法:表示调用父类的成员方法
最常见的用法:
this用于区分局部变量和成员变量
super用于区分子类和父类相同名称的变量
B:成员方法
在子类中使用一个方法的时候:
首先,在子类中找,如果有就使用。
否则,在父类中找,如果有就使用。
否则,就报错。
子父类同名的方法,在java中被称为方法重写。
要求:方法声明相同。
子类的修饰符权限要大于等于父类的权限。
不能重写父类的私有方法。
重写和重载的区别?
答:重写override表示子类中的方法可以与父类中的某个方法完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中定义的方法,这相当于把父类中定义的那个完全相同的方法覆盖了。
重载overload表示同一个类中可以有多个名称相同的方法,但是参数列表各不相同。
C:构造方法
在创建子类对象的时候,会去创建父类的对象。
因为在子类的构造方法中,默认第一条语句有一个super()。
假如父类没有无参构造,那么怎么解决呢?
a:通过super调用父类的其他构造
b:通过this调用本类的其他构造。
一般建议把无参构造方法自己给出。
代码示例:
class Fu//定义父类
{
int num = 10;//父类中变量
public Fu()//父类无参构造方法
{
System.out.println("Fu num:"+num);
}
public void method()//父类中方法
{
System.out.println("Fu method");
}
public void function()//父类中function方法
{
System.out.println("Fu function");
}
}
class Zi extends Fu//定义子类继承父类
{
int num = 20;//定义子类变量
public Zi()//子类构造方法
{
System.out.println("Zi num:"+num);
}
public void show()//子类show方法
{
int num = 30;
System.out.println("num:"+num);
System.out.println("num:"+this.num);
System.out.println("num:"+super.num);
}
public void method()//子类method方法
{
System.out.println("Zi method");
}
}
class ExtendsTest//测试类
{
public static void main(String[] args)
{
//请按照顺序写出下列程序的运行结果。
Zi zi = new Zi();//创建子类对象
/*结果:
Fu num:10
Zi num:20
*/
zi.show();//调用子类show方法
/*结果:
num:30
num:20
num:10
*/
zi.method();//调用method方法
/*结果
Zi method
*/
zi.function();//调用function方法
}
}
6、函数的覆盖(override)
定义:
当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。这种情况是函数的另一个特性:重写(覆盖)
当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。
注意事项:
A:子类覆盖父类必须保证子类访问权限大于等于父类访问权限
B:静态只能覆盖静态
C:父类中的私有方法不可以被覆盖
D:在子类覆盖的方法中,继续使用被覆盖的方法可以通过super.函数名获取
代码示例:
class Fu
{
private void study()
{
System.out.println("Fu - study");
}
public void show()
{
System.out.println("Fu - show");
}
}
class Zi extends Fu
{
/*
public void study()
{
System.out.println("Zi - study");
}
*/
public void show()//覆盖父类中的show方法
{
super.show();
System.out.println("Zi - show");
}
public void method()
{
System.out.println("Zi - method");
}
}
class ExtendsDemo3
{
public static void main(String[] args)
{
Zi zi = new Zi();
zi.show();
//zi.method();
//zi.study();
}
}
覆盖的应用:
当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,有定义了子类特有的内容。
7、子类的实例化过程
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
这就是子类的实例化过程。
结论:
子类的所有的构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。子类中至少会有一个构造函数会访问父类中的构造函数。
class Fu //extends Object
{
int num ;
Fu()
{
//super();
num= 60;
System.out.println("fu run");
}
Fu(int x)
{
System.out.println("fu ...."+x);
}
}
class Zi extends Fu
{
Zi()
{
super(); //子类的所有的构造函数,默认都会访问父类中空参数的构造函数。
//因为子类每一个构造函数内的第一行都有一句隐式super();
//super(4);
System.out.println("zi run");
}
Zi(int x)
{
this();//子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
//子类中至少会有一个构造函数会访问父类中的构造函数。
//super();
//super(3);
System.out.println("zi..."+x);
}
}
class ExtendsDemo4
{
public static void main(String[] args)
{
Zi z = new Zi(0);
System.out.println(z.num);
}
}
8、final关键字
final是“最终”的意思,作为一个修饰符,可以修饰类、变量、函数。被final修饰的类不可以被继承,为了避免被继承,被子类复写功能;被final修饰的方法不可以被复写;被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量,也可以修饰局部变量。
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。而这个值不需要改变,所以加上final修饰。作为常量,所有字母都大写,如果有多个单词组成,单词间通过_(下划线)连接。
内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。
因为当函数不被调用的时候,变量也会随之消失,为了能让内部类使用该变量,该变量就要被final修饰,成为常量,延长了变量的生命周期。
class Demo
{
final int x = 3;
public static final double PI = 3.14;
final void show1()
{}
void show2()
{
final int y = 4;
System.out.println(3.14);
}
}
class SubDemo extends Demo
{
//void show1(){}
}
class FinalDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
二、多态(面向对象的特点之三)
1、定义:
就是对象在不同时刻代表的不同状态
2、多态的前提:
A:类与类之间有继承关系或者实现关系
B:有方法的重写
C:有父类引用指向子类对象
父类的引用也可以接收自己的子类对象
举例;
class Fu{ public void show(){sop("Fu")} }
class Zi extends Fu{ public void show(){sop("zi")} }
多态:
Fu f = new Zi();
3、多态的好处:
A:提高了代码的后期的维护性
B:提高了代码的后期的扩展性
4、多态的弊端:
只能使用父类中定义过的方法,子类特有方法不能使用
abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void kanJia()
{
System.out.println("看家");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("饲料");
}
public void gongDi()
{
System.out.println("拱地");
}
}
//-----------------------------------------
class DuoTaiDemo2
{
public static void main(String[] args)
{
//Animal a = new Cat();//类型提升。 向上转型。
//a.eat();
//如果想要调用猫的特有方法时,如何操作?
//强制将父类的引用。转成子类类型。向下转型。
///Cat c = (Cat)a;
//c.catchMouse();
//千万不要出现这样的操作,就是将父类对象转成子类类型。
//我们能转换的是父类应用指向了自己的子类对象时,该应用可以被提升,也可以被强制转换。
//多态自始至终都是子类对象在做着变化。
// Animal a = new Animal();
// Cat c = (Cat)a;
/*
毕姥爷 x = new 毕老师();
x.讲课();
毕老师 y = (毕老师)x;
y.看电影();
*/
function(new Dog());
function(new Cat());
}
public static void function(Animal a)//Animal a = new Cat();
{
a.eat();
/*
if(a instanceof Animal)
{
System.out.println("haha");
}
else
*/
if(a instanceof Cat)
{
Cat c = (Cat)a;
c.catchMouse();
}
else if(a instanceof Dog)
{
Dog c = (Dog)a;
c.kanJia();
}
/*
instanceof : 用于判断对象的类型。 对象 intanceof 类型(类类型 接口类型)
*/
}
}
多态的测试:
向上转型:是为了多个对象使用同一个引用调用。
向下转型:是为了对象使用自己的特殊方法。
5、在多态中非静态成员函数的特点:
在编译时期:看引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有,编译失败;在运行时期:看对象所属的类中是否有调用的方法。
简单总结:成员函数在多态调用时,边一看左边,运行看右边。
在多态中,成员变量的特点:无论编译和运行,都参考左边(引用型变量所属的类)
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边
class Fu
{
static int num = 5;
void method1()
{
System.out.println("fu method_1");
}
void method2()
{
System.out.println("fu method_2");
}
static void method4()
{
System.out.println("fu method_4");
}
}
class Zi extends Fu
{
static int num = 8;
void method1()
{
System.out.println("zi method_1");
}
void method3()
{
System.out.println("zi method_3");
}
static void method4()
{
System.out.println("zi method_4");
}
}
class DuoTaiDemo4
{
public static void main(String[] args)
{
// Fu f = new Zi();
//
// System.out.println(f.num);
//
// Zi z = new Zi();
// System.out.println(z.num);
//f.method1();
//f.method2();
//f.method3();//编译失败,因为父类中没有方法method3
Fu f = new Zi();
System.out.println(f.num);
f.method4();
Zi z = new Zi();
z.method4();
// Zi z = new Zi();
// z.method1();
// z.method2();
// z.method3();
}
}