黑马程序员——Java面向对象(二)之封装、继承、多态、接口等

时间:2023-02-14 08:16:44

-----------android培训java培训、java学习型技术博客、期待与您交流!------------

五、面向对象的特征

 面向对象主要有三大特征:

 1.特征一 —— 封装

 1)定义:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

 2)好处:

  a)将变化隔离。

  b)便于使用。

  c)提高重用性。

  d)提高安全性。

 3)封装原则:

  a)将不需要对外提供的内容都隐藏起来。

  b)把属性都隐藏,提供公共方法对其访问。

 4)封装表现形式(private):

  什么是private?

  private(私有)是一种权限修饰符,用于修饰类中的成员(成员变量,成员函数、构造函数)。

 注:私有只在本类中有效,仅仅只是封装的一种表现形式。

  封装的作用:

  将成员变量私有化,对外提供对应的set,get方法对其进行访问,提高对数据访问的安全性。如果对构造函数进行私有,则不能对该类建立对象。

 5)代码实例:

//建立一个Person类,用来描述人的事物
class Person
{
//将age私有化
private int age;
//将name私有化
private String name;

//设置年龄
public void setAge(int age)
{
if(age>0&&age<120)
{
this.age=age;
}
else
System.out.println("非法年龄");
}
//设置姓名
public void setName(String name)
{
this.name=name;
}

//获取年龄
public int getAge()
{
return age;
}
//获取姓名
public String getName()
{
return name;
}

}
class PersonDemo
{
public static void main(String[] args)
{
//建立person对象
Person p=new Person();

p.setAge(40);//设置年龄
p.setName("张三");//设置姓名

System.out.println("age="+p.getAge());//获取年龄并打印
System.out.println("name="+p.getName());//获取姓名并打印

}
}
 在该实例中,将Person类中age和name通过私有进行隐藏,而提供setAge()、setName()和getAge()、getName()的公共访问方式分别进行设置和获取,因此很好体现了封装的特性。运行结果如下图:

黑马程序员——Java面向对象(二)之封装、继承、多态、接口等

 2.特征二 —— 继承

 1)定义:当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只需通过一种方式去获取这些共性内容即可,而这种方式就称为继承。

 2)作用:

  a)提高了代码的复用性。

  b)让类与类之间产生了关系,有了这个关系,才有了多态。

 注:千万不要为了获取其他类中的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。

 3)特点:

  a)java中类与类之间只支持单继承,不支持多继承,即只能继承一个类,但有一种情况例外,就是接口可以实现多继承。

  b)当子类和父类存在一模一样的函数(包括返回值类型)时,子类的函数会覆盖父类的相同函数。

  c)子类访问本类中的引用,用this,子类访问父类中的引用,用super。如果存在多级继承,那么super会先访问直接父类,没有再找上一级,即逐级访问。

  d)子类要成功覆盖父类,那么子类的权限必须大于或等于父类的权限,静态只能覆盖静态。

  e)子类继承父类,只延袭父类非私有内容。

 4)子父类构造函数:

  a)子类构造函数默认第一行会存在super()语句,用来访问父类构造函数,如果父类的构造函数为自定义构造函数,且非默认形式,则子类需指定访问父类的构造函数。

  b)父类构造函数优先子类构造函数初始化。

  c)子类所有的构造函数,默认都会访问父类中空参数的构造函数,因为每一个构造函数内的第一行都有一句隐式super(),子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,但子类至少有一个构造函数会访问父类的构造函数。

 5)问题思考:

  a)为什么类与类之间只支持单继承?

  因为如果一个类可以同时继承多个类,多个类中可能出现相同函数,但功能不同,那么在子类中就可能存在多个相同函数,但一个类中是不允许存在多个相同函数,只能通过覆盖,但java无法确定多个父类中的同名函数的覆盖顺序,所以是不允许多继承。

  b)为什么this和super不能同时在构造函数当中?

  因为this和super语句必须位于构造函数的第一行,所以不可能同时存在构造函数当中。

  c)为什么this和super语句必须位于第一行?

  因为this和super语句是用来进行初始化,初始化动作要先执行。

 6)代码实例:

//定义一个Person类
class Person
{
//定义人的属性,姓名和年龄
String name;
int age;

//初始化对象的属性
Person(String name,int age)
{
this.name=name;
this.age=age;
System.out.println(name+"----"+age);
}

//人都具有吃饭的功能
public void eat()
{
System.out.println("吃饭");
}

}

//定义一个Student类,并继承Person类
class Student extends Person
{
Student(String name,int age)
{
super(name,age);//由于父类已经有name和age的属性,因此只需调用父类构造函数进行初始化即可
}

//学生具有特有的听课功能
public void listen()
{
System.out.println("听课");
}

}

class ExtendsDemo
{
public static void main(String[] args)
{
//建立一个学生对象,并初始化
Student stu= new Student("lisi",20);

//调用继承父类的吃饭功能
stu.eat();

//调用学生的特有的听课功能
stu.listen();
}
}
  运行结果如下图:

黑马程序员——Java面向对象(二)之封装、继承、多态、接口等

 在该实例中,由于学生属于人中的一员,因此可通过继承人来获取共有的方法和属性,提高了代码的复用性,但学生自身也有其特有的功能,因此将这些特有的功能定义在学生类中。

 3.抽象类

 1)什么是抽象类?

  抽象类是指不能够直接建立实例对象的类,用关键字abstract进行修饰。通常抽象类都会存在无法定义功能主体的方法。

 2)特点:

  a)抽象方法指只能定义在抽象类中,只有功能定义,没有功能主体(即没有大括号及内容),同一个类中抽象方法可以被非抽象方法调用。

  b)抽象只能修饰方法和类,且抽象方法和抽象类都必须被abstract关键字修饰。

  c)抽象类不可以用new建立对象。

  d)抽象类中的方法要被使用,必须由子类复写其所有抽象方法后,才能建立子类对象调用,如只覆盖了部分方法,那么子类还是一个抽象类。

 3)问题思考

  抽象类与一般类有什么区别?

  抽象类既可以定义抽象方法,也可以定义非抽象方法,而一般类只能定义非抽象方法,但抽象类不能被实例化,而一般类可以被实例化。

 4)代码示例:

//定义一个抽象类Animal
abstract class Animal
{
//定义抽象方法eat()
public abstract void eat();
}

//定义一个Cat类,并继承Animal
class Cat extends Animal
{
//复写父类的eat()方法
public void eat()
{
System.out.println("吃鱼");
}
}

//定义一个Dog类,并继承Animal
class Dog extends Animal
{
//复写父类的eat()方法
public void eat()
{
System.out.println("吃骨头");

}
}
class AbstractDemo
{
public static void main(String[] args)
{
//建立一个Cat对象,并调用对象的eat()方法
Cat c=new Cat();
c.eat();

//建立一个Dog对象,并调用对象的eat()方法
Dog d=new Dog();
d.eat();
}
}
 运行结果如下图:

黑马程序员——Java面向对象(二)之封装、继承、多态、接口等
 在上面的实例中可以看到,Animal就是一个抽象类,因为Cat类和Dog类都有各自的吃的方法,因此对吃的方法进行向上抽取,就得到了一个吃的抽象方法,而吃的抽象方法就必须存在抽象类中,子类要建立实例对象就必须复写Animal中的eat()方法,因此抽象类还具有强制继承的子类去做一些事情的功能。

 4.接口

 1)概念:当抽象类中的方法都是抽象的,那么把这样的类的形式就描述为接口。接口的定义用关键字interface,子类实现父类用关键字implements。

 2)格式:

     interface 接口名

    {

      定义常量:public static final 返回值类型 常量名称

      定义方法:public abstract 返回值类型 方法名称(形参1,形参2,....) { }

    }

 注:上面常量和方法的格式也可部分或全部省略,java会默认自动补上,但不可省略返回值类型和名称。

 3)特点:

  a)接口不可以创建对象,如果被子类实现,需要覆盖接口的全部抽象方法后,子类才可以实例化,否则子类是一个抽象类。

  b)接口中只能存在成员常量和抽象的成员方法,两者可以同时存在或只存在一个,或都不存在。

  c)类对接口可以多实现,接口对接口可以多继承。

  d)接口是对外暴露的规则。

  e)接口是程序的功能扩展。

 4)代码示例:

//定义一个接口Animal
interface Animal
{
//定义抽象方法eat()
public abstract void eat();
}

//定义一个Cat类,并实现Animal
class Cat implements Animal
{
//复写父类的eat()方法
public void eat()
{
System.out.println("吃鱼");
}
}


class InterfaceDemo
{
public static void main(String[] args)
{
//建立一个Cat对象,并调用对象的eat()方法
Cat c=new Cat();
c.eat();

}
}
 程序运行的结果如下图:

黑马程序员——Java面向对象(二)之封装、继承、多态、接口等

 5.特征三 —— 多态

 1)定义:某类事物具有的多种表现形态。

 2)前提:

  a)必须是类与类之间必须有关系,要么继承,要么实现。

  b)通常情况还应存在覆盖。

 3)体现:

  a)父类的引用指向子类对象;

  b)父类的引用也可以接受自己的子类对象。

 4)利弊:

  好处:多态的出现大大提高了程序的扩展性,降低了程序的耦合性。

  弊端:虽然提高了程序的扩展性,但是只能使用父类的引用访问父类中的成员。

 5)特点:

  a)非静态成员函数:

  编译时期:如果父类的引用指向子类对象,那么该引用调用的方法必须父类要有,否则编译不通过。

  运行时期:父类的引用先查找子类的方法,如没有则执行父类的方法,其实运行的都是子类对象的方法。

  b)成员变量:无论编译还是运行,都看左边,即引用所属的类如果所属父类就调用父类的成员变量,即使子类有覆盖也一样。

  c)静态成员方法:无论编译还是运行,都参考左边。也就是说引用所属的类为父类就调用父类的方法,否则调用子类的方法。

 6)代码实例:

//定义一个抽象类Animal
abstract class Animal
{
//动物都具有吃的功能
public abstract void eat();
}

//定义一个类Cat,并继承Animal
class Cat extends Animal
{
//复写父类的eat()方法
public void eat()
{
System.out.println("吃鱼");
}
//定义特有的方法
public void catchMouse()
{
System.out.println("抓老鼠");
}

}

//定义一个类Dog,并继承Animal
class Dog extends Animal
{
//复写父类的eat()方法
public void eat()
{
System.out.println("吃骨头");
}
//定义特有的方法
public void kanJia()
{
System.out.println("看家");
}

}
class DuoTaiDemo
{
public static void main(String[] args)
{
Animal a= new Dog();//类型提升,多态的体现
function(a);

Cat c=new Cat();
function(c);
}

//父类引用只能访问父类的方法,如果访问的方法子类已经覆盖或实现,则执行子类的方法,
//如果要访问子类的特有方法,那么需要把父类引用强转成子类。该方法体现了多态的性质。
public static void function(Animal a)
{
a.eat();
//判断动物a是否为cat
if(a instanceof Cat)
{

/*强制将父类的引用,转成子类类型,向下转型.
强制转换的是父类的引用而不是父类的对象,只有指向子类的
父类的引用才可以进行强制转换,多态自始至终都是子类对象
在做着变化。
*/
Cat c= (Cat)a;
c.catchMouse();
}
}


}