面向对象(下)
八、继承
8.1、继承的概述
当多个类具有相同的属性和行为时,可以将这些相同的属性和行为抽取到一个独立的类中;多个类就不需要再单独定义这些内容,只要继承独立的类即可,简化了代码书写。
抽取而成的独立类称为父类或者超类,被抽取的多个类称为子类;父类就是子类共性的抽取,子类可以访问父类的成员,private修饰的除外。
在java中,体现类之间的继承关系用关键字:extends,格式为:class Son extends Father{}。
8.2、继承的特点
在java中,只支持单继承,也支持多层继承,但不支持多继承!
多继承的弊端:子类同时继承多个父类时,称之为多继承。因为当两个父类具有相同的方法,子类要执行哪个就不确定了;为了提高代码的安全性,所以java不支持多继承,而是用多实现的机制代替了多继承。
8.3、super关键字
super关键字:代表父类的内存空间的标识;代表父类对象的引用。
用法:(1)、当子父类同名的成员时,可以用super进行区分;
(2)、子类要调用父类的构造函数时,可以使用super语句。其实子类构造数都隐藏了super()语句,默认调用父类的无参构造函数。注意:super语句必须写在第一行。
super和this的异同点:在构造函数时,super和this语句都必须放在第一行,所以super和this不能同时出现在同一个构造函数中;super代表的是父类的引用,this代表的是子类中的引用。
8.4、函数覆盖(Override)
函数覆盖(Override):当子父类中出现一模一样的方法时,子类的方法会覆盖了父类的方法,这种现象就称之为覆盖,也就是重写或者复写。
覆盖的应用:当子类需要用到父类中的功能,而该功能又需要实现新的内容时,可以用覆盖;覆盖既沿袭了父类的功能,也定义了子类的特有功能。
注意:(1)、父类中的私有方法不可覆盖,因为父类的私有方法子类根本不可见;
(2)、子类的方法权限一定要大于等于父类的方法权限;
(3)、静态只能覆盖静态。
覆盖与重载的区别:子父类中的方法一模一样时,才能构成覆盖;在一个类中,方法名相同,参数列表不同就可以构成重载,与返回值无关。
8.5、子类的实例化过程
子类的实例化过程:当要创建一个子类的对象时,系统会先创建父类中的数据,然后才创建子类中的数据。即:父类对象-à子类对象。子类中所有的构造函数默认都会访问父类中的无参构造函数,因为子类中的每一个构造函数第一行都默认隐藏了super();语句。当要调用父类中的有参构造函数时,必须在子类构造函数的第一行写上super(参数列表);语句。
8.6、final关键字
final是java中的一个关键字,意为:终极,最后的。
final关键字可以修饰类、方法、变量。final修饰的类称为终极类,不可以被继承;final修饰的方法称为终极方法,不可以被覆盖;final修饰的变量称为常量,值不可被修改,只能赋值一次。
下面用代码演示继承关系
package itheima.day07;
//演示继承关系
public class ExtendsDemo1 {
public static void main(String[] args) {
// fu() run...1 --> zi() run...1
Zi z1 = new Zi();
// fun(int) run...15 --> zi(int) run...15
Zi z2 = new Zi(15);
}
}
//父类
class Fu{
protected int num =1;
// 父类的无参构造函数
Fu(){
System.out.println("fu() run..."+num);
}
// 父类的有参构造函数
Fu(int num){
this.num = num;
System.out.println("fun(int) run..."+num);
}
}
//子类
class Zi extends Fu{
// 子类的无参构造函数
Zi(){
// super();第一行默认隐藏了super();语句,用于初始化时调用父类的无参构造函数
System.out.println("zi() run..."+num);
}
// 调用父类的有参构造函数
Zi(int num){
super(num);
System.out.println("zi(int) run..."+num);
}
}
九、抽象类
9.1、抽象类与抽象方法的概述
抽象类:声明了方法,但没有具体的方法实现(方法体),而是将方法的实现交给子类去实现,这样的类就叫做抽象类,抽象类用abstract关键字修饰,格式为:abstract class AbsClass{}。
注意:抽象类也可以没有抽象方法,在AWT中的适配器就是一个例子,适配器继承于监听器,但监听器中的方法都是抽象方法,适配器实现了监听器中的方法,但创建对象毫无意义,所以声明为抽象类。
抽象方法:定义了方法名称,但并没有具体的实现,这样的方法就叫做抽象方法,抽象方法有关键字abstract修饰,格式为:修饰符 abstract 返回值类型 函数名(参数列表);。
抽象类的由来:从多个事物中将共性的、本质的内容抽取出来。例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。
抽象方法的由来:多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。
9.2、抽象类特点
特点:抽象类不可以用new创建对象,子类必须实现出抽象类中的抽象方法,否则该子类也是抽象类,子类实现了抽象类中的全部抽象方法后才可以new创建对象。
下面代码演示抽象类:
package itheima.day07;
/*
需求:公司中普通员工有姓名,工号,薪水,工作内容。
经理除了有姓名,工号,薪水,还有奖金,工作内容。
对给出需求进行数据建模。
*/
/*
思路:首先把普通员工和经理的共性内容抽取出来,形成一个抽象类
然后在普通员工类和经理类中中分别继承并实现抽象类中的共性内容
经理类中加入自己特有的内容
*/
public class AstractClassTest {
public static void main(String[] args) {
// 普通员工
Professional pro = new Professional("zhangsan","1010",5000);
// 普通员工工作
pro.work();
// 经理
Manager man =new Manager("lisi","1000",10000,100);
// 经理工作
man.work();
}
}
//职工抽象类
//普通员工与经理抽取而成的抽象类
abstract class Emploee{
private String name;
private String id;
private double pay;
Emploee(String name,String id,double pay){
this.name = name;
this.id = id;
this.pay = pay;
}
public abstract void work();
}
//普通员工类
class Professional extends Emploee{
Professional(String name,String id,double pay){
super(name,id,pay);
}
// 实现抽象方法,普通员工工作
public void work(){
System.out.println("professional work...");
}
}
//经理类
class Manager extends Emploee{
private int hours;
Manager(String name,String id,double pay,int hours){
super(name,id,pay);
this.hours = hours;
}
// 实现抽象方法,经理工作
public void work(){
System.out.println("manager work...");
}
}
十、接口
10.1、接口的概述
接口:因为多继承带来的不安全性,所以java不支持多继承;接口的出现就是为了代替C++中的多继承。在java中,一个类可以实现多个接口,称为多实现。接口的格式为:interface{}。
接口的成员修饰符固定,成员变量:public static final;成员函数:public abstract。
10.2、抽象类与接口的共性与区别:
共性:都是不断抽取出来的抽象概念,不可以用new创建对象。
区别:(1)、抽象类体现继承关系,一个类只能单继承;接口体现实现关系,一个类可
以多实现。
(2)、抽象类是继承,是“is a”关系;接口是现实,是“like a”关系.
(3)、抽象类可以定义非抽象方法,供子类直接使用;接口的方法都是抽象的,
接口中的成员都有固定修饰符。
下面代码演示接口:
package itheima.day07;
//演示接口的功能
public class InterfaceDemo {
public static void main(String[] args) {
SmokerStudent zhangsan = new SmokerStudent();//zhangsan是个抽烟的学生
zhangsan.smoke();//zhangsan抽烟
zhangsan.study();//zhangsan学习
zhangsan.sleep();//zhangsan睡觉
Non_SmokeStudent lisi = new Non_SmokeStudent();//lisi是个不抽烟的学生
lisi.study();//lisi学习
lisi.sleep();//lisi睡觉
}
}
//抽烟的接口
interface Smoking{
public void smoke();
}
//学生的抽象类
abstract class Student{
abstract void study();
void sleep(){
System.out.println("student sleep...");
}
}
//抽烟的学生,继承学生类,实现抽烟的接口
class SmokerStudent extends Student implements Smoking{
// 覆盖学习的方法
void study(){
System.out.println("study...");
}
// 实现抽烟的方法
public void smoke(){
System.out.println("这是一群为国家财政收入做出伟大贡献的哥们...");
}
}
//不抽烟的学生,只继承学生类
class Non_SmokeStudent extends Student{
// 覆盖学习方法
void study(){
System.out.println("study...");
}
}
十一、多态
11.1、多态的概述
多态:一种事物的多种形态,叫做多态。例如:动物中有狗,猫等;对于狗来说,既可以称为狗,也可以称为动物,这就是多态的体现。
代码体现:因为狗也是动物,所以在程序中可以:动物 d = new狗();简单说:父类或者接口的引用指向自己的子类对象!
11.2多态的特点
多态的特点:多态的发生必须存在具有继承关系的子父类中或者有实现关系的接口子类中;成员函数在编译时期取决于引用变量所属的类中是否有所调用的成员,运行时期取决于对象所属的类中是否有所调用的成员;成员变量只取决于变量所属的类。
好处:多态可以提高程序的扩展性和后期的维护性。
下面代码演示多态:
package itheima.day08;
//演示多态
public class DuotaiDemo {
public static void main(String[] args) {
// 父类的引用指向子类的对象,多态的体现
Animal cat = new Cat();
cat.eat();
// cat.catchMouse();//编译通不过
function(new Dog());//Animal a = new Dog();多态!
}
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();
}
}
}
//动物类,抽象
abstract class Animal{
public abstract void eat();
}
//狗
class Dog extends Animal{
public void eat(){
System.out.println("坑骨头");
}
public void kanjia(){
System.out.println("看家");
}
}
// 猫
class Cat extends Animal{
public void eat(){
System.out.println("吃老鼠");
}
public void CatchMouse(){
System.out.println("抓老鼠");
}
}
11.3、多态扩展--Object类中的多态
在java中,Object类是所有类直接或者间接的父类,类似于上帝。根据共性抽取原则,可以把所有对象都应该具有的功能都定义在Object类中,但父类并不知道子类的所需,所有我们有时候必须覆盖Object类提供的方法,例如:equal()方法,toString()方法,为了更好的理解,下面代码演示:
package itheima.day08;
public class ObjectDemo {
/*
Object是所有对象的直接或者间接父类,上帝。
该类中定义了所有对象都具备的功能
boolean equals(Object obj)
指示其他某个对象是否与此对象“相等”。
String toString()
返回该对象的字符串表示
*/
public static void main(String[] args) {
Demo d1 = new Demo(1);
Demo d2 = new Demo(2);
System.out.println(d1.equals(d2));//false
System.out.println(d1.toString());//Demo:1
System.out.println(d2.toString());//Demo:2
}
}
//extends Object ,Demo类其实继承了Object类
class Demo{
private int num;
Demo(int num){
this.num = num;
}
// 根据自己需要,复写了Object类中的equals()方法
public boolean equals(Object obj){
if(!(obj instanceof Demo))
return false;
Demo d = (Demo)obj;
return this.num ==d.num;
}
// 根据自己需要,复写了Object类中的toString()方法
public String toString(){
return "Demo:"+num;
}
}
十二、内部类与匿名内部类
12.1、内部类
在生活中,有些事物内部还有事物,比如:人体中有心脏;这种情况在java中被描述为内部类
内部类:将一个类定义在另外一个类中,里面的那个类就称为内部类。
特点:内部类可以直接访问外部类中的成员,包括私有成员;外部类要访问内部类中的成员必须要建立内部类的对象。
注意:内部类可以定义在成员位置上,这时可以被private、static成员修饰符修饰,被static修饰的内部类只能访问外部类中的静态成员;内部类还可以定义在局部位置上,这时要访问局部中的局部变量,但必须要被final修饰。
下面代码演示内部类:
package itheima.day09;
//演示内部类
public class InnerClassDemo {
public static void main(String[] args) {
Outer out = new Outer();
out.method();
// 创建内部类的方式
Outer.Inner in = new Outer().new Inner();
in.function();
}
}
//外部类
class Outer{
// 外部类成员变量
private int num = 10;
// 内部类
class Inner{
// 内部类成员变量
private int num = 1;
void function(){
// 引用的是内部类的成员变量
System.out.println("Inner:"+num);
// 当内部类的成员变量与外部类的成员变量重名时,用Outer.this.区分
System.out.println("Inner:"+Outer.this.num);
}
}
void method(){
// 外部类使用内部类方法时,必须先创建内部类对象
Inner in = new Inner();
in.function();
}
}
12.2、匿名内部类
匿名内部类:内部类的简化形式。
前提:匿名内部类必须继承或实现一个外部类或接口。
格式:new外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容。)}
使用方法:通常在使用方法是接口类型参数,并该接口中的方法不超过三个时,可以将匿名内部类作为参数传递。
下面代码演示匿名内部类:
package itheima.day09;
//演示匿名内部类
interface Inter{//匿名内部类的前提
void method();
}
class Test{
static Inter function(){
// 匿名内部类
return new Inter(){
public void method(){
System.out.println("metod run...");
}
};
}
}
public class InnerClassTest {
public static void main(String[] args) {
Test.function().method();
}
}