多态概述
某一个事物,在不同时刻表现出来的不同状态。
举例:
猫可以是猫的类型。猫 m = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。
动物 d = new 猫();
在举一个例子:水在不同时刻的状态
多态前提和体现
有继承关系
有方法重写
有父类引用指向子类对象
成员访问特点
成员变量
编译看左边,运行看左边
成员方法
编译看左边,运行看右边
静态方法
编译看左边,运行看左边
所以前面我说静态方法不能算方法的重写
多态的好处和弊端
多态的好处
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
多态的弊端
不能访问子类特有功能
那么我们如何才能访问子类的特有功能呢?
多态中的转型问题
向上转型:
Animal an = new Cat();
父类引用 指向 子类对象
左边 (父类型) = 右边 (子类型)
向下转型:
Cat cat = (Cat)an;
父类引用转为子类对象,需要通过强制转换来完成
左边(子类类型) = 右边(父类类型)
---------------------------------------
方法:
子类没有重写父类方法:
编译看左边的父类方法
运行是父类方法(方法从父类继承过来的)
子类重写了父类方法
编译看左边的父类方法
运行看右边的子类方法
*/
class Animal {
public void eat(){
System.out.println("小动物吃饭");
}
}
class Cat extends Animal {
/*
从父类中继承过来的
public void eat(){
System.out.println("小动物吃饭");
}
*/
//特有方法
public void playGame(){
System.out.println("英雄联盟");
}
}
class DuoTaiDemo3 {
public static void main(String[] args) {
//向上转型
Animal an = new Cat();
//an.playGame();
an.eat();
//向下转型
Cat cat = (Cat)an;
cat.playGame();
cat.eat();
}
}
抽象类概述
回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。
为什么呢?因为,我说动物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。 所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。在Java中,一个没有方法体的方法应该定义为抽象方法
抽象类特点
抽象类和抽象方法必须用abstract关键字修饰
格式
abstract class 类名 {}
public abstract void eat();
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
那么,抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
抽象类的子类要么是抽象类,要么重写抽象类中的所有抽象方法
抽象类的成员特点
成员变量
可以是变量
也可以是常量
构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?
用于子类访问父类数据的初始化
成员方法
可以有抽象方法 限定子类必须完成某些动作
也可以有非抽象方法 提高代码服用性
一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
可以,可以防止其他人 创建本类对象
abstract不能和哪些关键字共存
private 冲突
final 冲突
static 无意义
abstract class Animal {
//public abstract void method();
//private abstract void method(); //错误: 非法的修饰符组合: abstract和private
//final abstract void method();//错误: 非法的修饰符组合: abstract和final
//static abstract void method();//错误: 非法的修饰符组合: abstract和static
public static void show(){
System.out.println("show");
}
}
class AbstractDemo3 {
public static void main(String[] args) {
//Animal an = new Animal();
Animal.show();
//Animal.method();
}
}
抽象的举例:
abstract class Teacher {
private String name;
private int age;
//构造方法
public Teacher(){}
public Teacher(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
//讲课(抽象方法)
public abstract void teach();
}
//基础班老师
class BaseTeacher extends Teacher {
//构造方法
public BaseTeacher(){}
public BaseTeacher(String name, int age){
super(name, age);
}
//重写抽象类中的抽象方法
public void teach(){
System.out.println("带着大家学好Java,准备飞");
}
}
//就业班老师
class JobTeacher extends Teacher {
//构造方法
public JobTeacher(){}
public JobTeacher(String name, int age){
super(name, age);
}
//重写抽象类中的抽象方法
public void teach(){
System.out.println("带着大家一起开心的飞");
}
}
class AbstractTest2 {
public static void main(String[] args) {
BaseTeacher bt = new BaseTeacher("张三", 18);
bt.teach();
Teacher t = new JobTeacher("李四", 18);
t.teach();
}
}
接口概述
继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了,对不。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的,对不。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被培训,只需要这部分猫狗把这些额外功能实现即可。
接口特点
接口用关键字interface表示
格式:interface 接口名 {}
类实现接口用implements表示
格式:class 类名 implements 接口名 {}
接口不能实例化
那么,接口如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。
接口的子类
要么是抽象类
要么重写接口中的所有抽象方法
接口成员特点
成员变量
只能是常量
默认修饰符 public static final
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法
默认修饰符 public abstract
类与类,类与接口以及接口与接口的关系
类与类
继承关系,只能单继承,但是可以多层继承
类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
接口与接口
继承关系,可以单继承,也可以多继承
成员区别
抽象类 变量,常量;有抽象方法;抽象方法,非抽象方法
接口 常量;抽象方法
关系区别
类与类 继承,单继承
类与接口 实现,单实现,多实现
接口与接口 继承,单继承,多继承
设计理念区别
抽象类 被继承体现的是:”is a”的关系。共性功能
接口 被实现体现的是:”like a”的关系。扩展功能
接口举例:
乒乓球运动员和足球运动员。
乒乓球教练和足球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识:
分析,这个案例中有哪些抽象类,哪些接口,哪些具体类
接口: 英语接口
学英语
人:抽象类
属性 name
方法 eat(抽象方法)
(抽象类)运动员 extends 人:
方法:study(抽象方法)
(具体类)乒乓球运动员 extends 运动员 implements 英语接口
(具体类)足球运动员 extends 运动员
(抽象类)教练 extends 人:
方法:teach(抽象方法)
(具体类)乒乓球教练 extends 教练 implements 英语接口
(具体类)足球教练 extends 教练
*/
//英语接口
interface English {
public abstract void studyEnglish();//学英语
}
//人:抽象类
abstract class Person {
//成员变量
private String name;
//构造方法
public Person(){}
public Person(String name) {
this.name = name;
}
//成员方法 setXxx getXxx
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
//抽象方法
//方法 eat(抽象方法)
public abstract void eat();
}
//(抽象类)运动员 extends 人:
abstract class Player extends Person {
//构造方法
public Player(){}
public Player(String name){
super(name);
}
//方法:study(抽象方法)
public abstract void study();
}
//(抽象类)教练 extends 人:
abstract class Coacher extends Person {
//构造方法
public Coacher(){}
public Coacher(String name){
super(name);
}
//方法:teach(抽象方法)
public abstract void teach();
}
//(具体类)乒乓球运动员 extends 运动员 implements 英语接口
class PingPongPlayer extends Player implements English {
//构造方法
public PingPongPlayer(){}
public PingPongPlayer(String name){
super(name);
}
//重写所有的抽象方法
public void study(){
System.out.println("学习扣球");
}
public void eat(){
System.out.println("吃鱼");
}
public void studyEnglish(){
System.out.println("学英语");
}
}
//(具体类)足球运动员 extends 运动员
class FootballPlayer extends Player {
//构造方法
public FootballPlayer(){}
public FootballPlayer(String name){
super(name);
}
//重写所有的抽象方法
public void study(){
System.out.println("学习踢假球");
}
public void eat(){
System.out.println("吃馒头");
}
}
//(具体类)乒乓球教练 extends 教练 implements 英语接口
class PingPongCoacher extends Coacher implements English {
//构造方法
public PingPongCoacher(){}
public PingPongCoacher(String name){
super(name);
}
//重写所有的抽象方法
public void teach(){
System.out.println("教如何扣球");
}
public void eat(){
System.out.println("吃鲍鱼");
}
public void studyEnglish(){
System.out.println("学英语");
}
}
//(具体类)足球教练 extends 教练
class FootballCoacher extends Coacher {
//构造方法
public FootballCoacher(){}
public FootballCoacher(String name){
super(name);
}
//重写所有的抽象方法
public void teach(){
System.out.println("教如何踢假球");
}
public void eat(){
System.out.println("吃鸡骨头");
}
}
class InterfaceTest3 {
public static void main(String[] args) {
//测试
//乒乓球运动员
PingPongPlayer ppp = new PingPongPlayer("张三");
System.out.println(ppp.getName() );
ppp.study();
ppp.eat();
ppp.studyEnglish();
}
}
类及其组成可以用的修饰符
类:
默认,public,final,abstract
我们自己定义:public居多
成员变量:
四种权限修饰符均可,final,static
我们自己定义:private居多
构造方法:
四种权限修饰符均可,其他不可
我们自己定义:public 居多
成员方法:
四种权限修饰符均可,fianl,static,abstract
我们自己定义:public居多
内部类概述
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象。
内部类位置
按照内部类在类中定义的位置不同,可以分为如下两种格式:
成员位置(成员内部类)
局部位置(局部内部类)
如:
///外部类
class Outer {
//成员区域
//成员内部类
//class Inner {
//}
public void method(){
//局部区域
//局部内部类
class Inner {
}
}
}
class InnerClassDemo2 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
///外部类
class Outer {
//成员变量
private int num = 10;
//成员内部类
//成员 类类型成员
class Inner {
private int num2 = 20;
public void show(){
System.out.println(num);
System.out.println(num2);
}
}
}
成员内部类
外界如何创建对象
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
class InnerClassDemo3 {
public static void main(String[] args) {
//创建成员内部类对象
//Inner in = new Inner();
//格式:
// 外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
成员内部的常见修饰符
private 为了保证数据的安全性
static 为了让数据访问更方便
被静态修饰的成员内部类只能访问外部类的静态成员
内部类被静态修饰后的方法
静态方法
非静态方法
成员内部类的修饰符
private: 提高安全性
static : 方便内部类的访问
class Body{//身体
private class Heart {//心脏
public void operator(){//操作
System.out.println("心脏搭桥");
}
}
public void method(){
if (你是外科医生吗?) {
Heart h = new Heart();
h.operator();
}
}
}
Body.Heart bh = new Body().new Heart();
bh.operator();
在静态修饰的成员内部类中,访问的成员只能是静态修饰的
class Outer {
private int num = 10;
private static int num2 = 20;
//成员内部类
public static class Inner {
public void method(){
//System.out.println(num);
System.out.println(num2);
}
public static void method2(){
//System.out.println(num);
System.out.println(num2);
}
}
}
class InnerClassDemo4 {
public static void main(String[] args) {
//创建内部类对象
//错误: 限定的新静态类
//Outer.Inner oi = new Outer().new Inner();
//内部类被static修饰后,格式为:
// 外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
oi.method();
oi.method2();
Outer.Inner.method2();
}
}
成员内部类面试题
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(?);//num;
System.out.println(??);//this.num;
System.out.println(???);//Outer.this.num;
}
}
}
在控制分别输出:30,20,10
局部内部类
可以直接访问外部类的成员
可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
局部内部类可以直接访问外部类的成员
局部内部类访问局部变量的注意事项:
必须被final修饰?为什么呢?
因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下
*/
//外部类
class Outer {
//成员变量
private int num = 10;
public void method(){
//局部变量
//int num2 = 20;
final int num2 = 20;
//局部内部类
class Inner {
//成员变量
private int num3 = 30;
public void show(){
System.out.println(num);//10
//错误: 从内部类中访问本地变量num2; 需要被声明为最终类型 final
System.out.println(num2);//20
System.out.println(num3);//30
}
}
//创建一个内部类对象,调用show()方法
new Inner().show();
}
}
class InnerClassDemo6 {
public static void main(String[] args) {
//创建外部类对象,调用method()方法
Outer ou = new Outer();
ou.method();
}
}
匿名内部类
就是内部类的简化写法。
前提:存在一个类或者接口
这里的类可以是具体类也可以是抽象类。
格式:
new 类名或者接口名() {重写方法;}
本质:
是一个继承了类或者实现了接口的子类匿名对象
案例:
interface Inter {
public abstract void show();
public abstract void show2();
}
//外部类
class Outer {
public void method(){
匿名内部类:
Inter in = new Inter() {
//重写抽象方法
public void show(){
System.out.println("haha");
}
public void show2(){
System.out.println("xixi");
}
};
in.show();
in.show();
in.show2();
in.show();
}
}
案列二:
abstract class Person {
public abstract void eat();
}
class PersonDemo {
public void method (Person p){
p.eat();
}
}
class NiMingInnerClassDemo2 {
public static void main(String[] args) {
//程序员的方式
PersonDemo pd = new PersonDemo();
pd.method( new Person(){
//重写抽象方法
public void eat(){
System.out.println("吃饭");
}
});
}
}
匿名内部类面试题
按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
如下:
interface Inter {
void show();
}
class Outer {
//补齐代码
public static Inter method(){
//返回 实现了该接口的类的实例对象 (Inter对象)
return new Inter(){
//重写抽象方法
public void show(){
System.out.println("HelloWorld");
}
};
}
}
class NiMingInnerClassTest {
public static void main(String[] args) {
Outer.method().show();
/*
Outer.method(),静态方法,在Outer类中
Outer.method().show()
method()执行完后返回一个包含show()方法的对象, 对象的数据类型为 Inter类型
*/
}
}