1.继承的含义:
把多个类中相同的成员给提取出来定义到一个独立的类中。然后让这多个类和该独立的类产生一个关系, 这多个类就具备了这些内容。这个关系叫继承。
2.Java中表示继承的格式:
1)用关键字extends表示2)格式: class 子类名 extends 父类名 {}
3.继承的好处:
1)提高了代码的复用性2)提高了代码的维护性3)让类与类产生了一个关系,是多态的前提
4.继承的弊端:
1)让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。
原则:低耦合,高内聚。耦合:类与类的关系内聚:自己完成某件事情的能力
2)打破了封装性
5.Java中继承的特点
1)Java中类只支持单继承2)Java中可以多层(重)继承(继承体系)
6.继承的注意事项:
A:子类不能继承父类的私有成员B:子类不能继承父类的构造方法,但是可以通过super去访问C:不要为了部分功能而去继承
7.什么时候使用继承呢?
1)继承体现的是:is a的关系。2)采用假设法
8.Java继承中的成员关系
1)成员变量
A:子类的成员变量名称和父类中的成员变量名称不一样B:子类的成员变量名称和父类中的成员变量名称一样,访问方式:
子类的方法访问变量的查找顺序:在子类方法的局部范围找,有就使用。在子类的成员范围找,有就使用。在父类的成员范围找,有就使用。找不到,就报错。
2)构造方法
A:子类的构造方法默认会去访问父类的无参构造方法, 为了子类访问父类数据的初始化B:父类中如果没有无参构造方法解决办法:
子类通过super去明确调用带参构造子类通过this调用本身的其他构造,但是一定会有一个去访问了父类的构造让父类提供无参构造
3)成员方法
A:子类的成员方法和父类中的成员方法名称不一样B:子类的成员方法和父类中的成员方法名称一样, 通过子类对象访问一个方法的查找顺序:
在子类中找,有就使用在父类中找,有就使用找不到,就报错
9.猫狗案例的分析和实现
package com.itheima; /* 猫狗案例讲解 先找到具体的事物,然后发现具体的事物有共性,才提取出一个父类。 猫: 成员变量:姓名,年龄,颜色 构造方法:无参,带参 成员方法: getXxx()/setXxx() eat() palyGame() 狗: 成员变量:姓名,年龄,颜色 构造方法:无参,带参 成员方法: getXxx()/setXxx() eat() lookDoor() 共性: 成员变量:姓名,年龄,颜色 构造方法:无参,带参 成员方法: getXxx()/setXxx() eat() 把共性定义到一个类中,这个类的名字叫:动物。 动物类: 成员变量:姓名,年龄,颜色 构造方法:无参,带参 成员方法: getXxx()/setXxx() eat() 猫: 构造方法:无参,带参 成员方法:palyGame() 狗: 构造方法:无参,带参 成员方法:lookDoor() */ //定义动物类 class Animal { // 姓名 private String name; // 年龄 private int age; // 颜色 private String color; public Animal() { } public Animal(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void eat() { System.out.println("不要睡了,该吃饭了"); } } // 定义猫类 class Cat extends Animal { public Cat() { } public Cat(String name, int age, String color) { super(name, age, color); } public void playGame() { System.out.println("猫玩英雄联盟"); } } // 定义狗类 class Dog extends Animal { public Dog() { } public Dog(String name, int age, String color) { super(name, age, color); } public void lookDoor() { System.out.println("狗看家"); } } // 测试类 class ExtendsTest5 { public static void main(String[] args) { // 测试猫 // 方式1 Cat c1 = new Cat(); c1.setName("Tom"); c1.setAge(3); c1.setColor("白色"); System.out.println("猫的名字是:" + c1.getName() + ";年龄是:" + c1.getAge() + ";颜色是:" + c1.getColor()); c1.eat(); c1.playGame(); System.out.println("---------------"); // 方式2 Cat c2 = new Cat("杰瑞", 5, "土豪金"); System.out.println("猫的名字是:" + c2.getName() + ";年龄是:" + c2.getAge() + ";颜色是:" + c2.getColor()); c2.eat(); c2.playGame(); // 测试狗 // 方式1 Dog d1 = new Dog(); d1.setName("John"); d1.setAge(5); d1.setColor("黑色"); System.out.println("狗的名字是:" + d1.getName() + ";年龄是:" + d1.getAge() + ";颜色是:" + d1.getColor()); d1.eat(); d1.lookDoor(); System.out.println("---------------"); // 方式2 Dog d2 = new Dog("Smith", 7, "黄"); System.out.println("狗的名字是:" + d2.getName() + ";年龄是:" + d2.getAge() + ";颜色是:" + d2.getColor()); d2.eat(); d2.lookDoor(); } }
二、多态
1.多态的描述:
2.多态的前提:同一个对象在不同时刻体现出来的不同状态。
1)有继承或者实现关系。
2)有方法重写。
3)有父类或者父接口引用指向子类对象。
3.多态的分类:
1)具体类多态
class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
2)抽象类多态
abstract class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
3)接口多态
interface Fu {}
class Zi implements Fu {}
Fu f = new Zi();
4.多态中的成员访问特点
1)成员变量:编译看左边,运行看左边
2)构造方法:子类的构造都会默认访问父类构造
3)成员方法:编译看左边,运行看右边
4)静态方法:编译看左边,运行看左边
5.多态的好处与弊端:
1)好处:
A:提高代码的维护性(继承体现)
2)弊端:B:提高代码的扩展性(多态体现)
父不能使用子的特有功能。现象:子可以当作父使用,父不能当作子使用。
6.多态中的转型
1)向上转型:从子到父
7.猫狗案例2)向下转型:从父到子
/* 猫狗案例 */ class Animal { public void eat() { System.out.println("吃饭"); } } class Dog extends Animal { public void eat() { System.out.println("狗吃肉"); } public void lookDoor() { System.out.println("狗看门"); } } class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } } class DuoTaiTest { public static void main(String[] args) { // 定义为狗 Animal a = new Dog(); a.eat(); System.out.println("--------------"); // 还原成狗 Dog d = (Dog) a; d.eat(); d.lookDoor(); System.out.println("--------------"); // 变成猫 a = new Cat(); a.eat(); System.out.println("--------------"); // 还原成猫 Cat c = (Cat) a; c.eat(); c.playGame(); System.out.println("--------------"); // 演示错误的内容 // Dog dd = new Animal(); // Dog ddd = new Cat(); // ClassCastException // Dog dd = (Dog)a; } }
三、抽象类
1.为什么用抽象类:
在定义方法声明一样,但是每个具体的对象在具体实现的时候内容不一样的共性方法的时,则不能给出具体的方法体。
而一个没有具体的方法体的方法是抽象的方法。在一个类中如果有抽象方法,该类必须定义为抽象类。
2.抽象类的特点:
1)抽象类和抽象方法必须用关键字abstract修饰
2)抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
3)抽象类不能实例化
4)抽象类的子类
A:是一个抽象类。
B:是一个具体类。这个类必须重写抽象类中的所有抽象方法。
3.抽象类的成员特点:
1)成员变量:有变量,有常量
2)构造方法:有构造方法
3)成员方法:有抽象,有非抽象
4.抽象类的几个小问题
1)抽象类有构造方法,不能实例化,那么构造方法有什么用?
用于子类访问父类数据的初始化
2)一个类如果没有抽象方法,却定义为了抽象类,有什么用?
为了不让创建对象
3)abstract不能和哪些关键字共存
a:final 冲突
b:private 冲突
c:static 无意义
5.猫狗案例
/* 猫狗案例 具体事物:猫,狗 共性:姓名,年龄,吃饭 分析:从具体到抽象 猫: 成员变量:姓名,年龄 构造方法:无参,带参 成员方法:吃饭(猫吃鱼) 狗: 成员变量:姓名,年龄 构造方法:无参,带参 成员方法:吃饭(狗吃肉) 因为有共性的内容,所以就提取了一个父类。动物。 但是又由于吃饭的内容不一样,所以吃饭的方法是抽象的, 而方法是抽象的类,类就必须定义为抽象类。 抽象动物类: 成员变量:姓名,年龄 构造方法:无参,带参 成员方法:吃饭(); 实现:从抽象到具体 动物类: 成员变量:姓名,年龄 构造方法:无参,带参 成员方法:吃饭(); 狗类: 继承自动物类 重写吃饭(); 猫类: 继承自动物类 重写吃饭(); */ //定义抽象的动物类 abstract class Animal { // 姓名 private String name; // 年龄 private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 定义一个抽象方法 public abstract void eat(); } // 定义具体的狗类 class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } public void eat() { System.out.println("狗吃肉"); } } // 定义具体的猫类 class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } public void eat() { System.out.println("猫吃鱼"); } } // 测试类 class AbstractTest { public static void main(String[] args) { // 测试狗类 // 具体类用法 // 方式1: Dog d = new Dog(); d.setName("旺财"); d.setAge(3); System.out.println(d.getName() + "---" + d.getAge()); d.eat(); // 方式2: Dog d2 = new Dog("旺财", 3); System.out.println(d2.getName() + "---" + d2.getAge()); d2.eat(); System.out.println("---------------------------"); Animal a = new Dog(); a.setName("旺财"); a.setAge(3); System.out.println(a.getName() + "---" + a.getAge()); a.eat(); Animal a2 = new Dog("旺财", 3); System.out.println(a2.getName() + "---" + a2.getAge()); a2.eat(); // 测试猫类 // 具体类用法 // 方式1: Cat c = new Cat(); c.setName("喵喵"); c.setAge(4); System.out.println(c.getName() + "---" + c.getAge()); c.eat(); // 方式2: Cat c2 = new Cat("喵喵", 4); System.out.println(c2.getName() + "---" + c2.getAge()); c2.eat(); System.out.println("---------------------------"); Animal b = new Cat(); b.setName("喵喵"); b.setAge(4); System.out.println(b.getName() + "---" + b.getAge()); b.eat(); Animal b2 = new Cat("喵喵", 4); System.out.println(b2.getName() + "---" + b2.getAge()); b2.eat(); } }
四、接口
1.接口存在的意义:
像猫钻火圈,狗跳高等功能,不是动物本身就具备的,是在后面的培养中训练出来的,这种额外的功能,java提供了接口表示。
2.接口的特点:
1)接口用关键字interface修饰:interface 接口名 {}
2)类实现接口用implements修饰:class 类名 implements 接口名 {}
3)接口不能实例化
4)接口的实现类
A:是一个抽象类。
B:是一个具体类,这个类必须重写接口中的所有抽象方法。
3.接口的成员特点:
1)成员变量,只能是常量,默认修饰符:public static final
2)没有构造方法
3)成员方法,只能是抽象的,默认修饰符:public abstract
4.类与接口之间的关系:
1)类与类:继承关系,只能单继承,可以多层继承
2)类与接口:实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时,实现多个接口
3)接口与接口:继承关系,可以单继承,也可以多继承
5.抽象类和接口的区别:
1)成员区别
抽象类:可以是常量也可以是变量
接口:只能是常量
2)关系区别:
类与类:继承
类与接口:实现关系,可以单实现,也可以多实现。
接口与接口:继承关系,可以单继承,也可以多继承
3)设计理念不同
抽象类:is a,抽象类中定义的是共性功能。
接口:like a,接口中定义的是扩展功能。
6.猫狗案例
/* 猫狗案例,加入跳高的额外功能 分析:从具体到抽象 猫: 姓名,年龄 吃饭,睡觉 狗: 姓名,年龄 吃饭,睡觉 由于有共性功能,所以,我们抽取出一个父类: 动物: 姓名,年龄 吃饭(); 睡觉(){} 猫:继承自动物 狗:继承自动物 跳高的额外功能是一个新的扩展功能,所以我们要定义一个接口 接口: 跳高 部分猫:实现跳高 部分狗:实现跳高 实现; 从抽象到具体 使用: 使用具体类 */ //定义跳高接口 interface Jumpping { // 跳高功能 public abstract void jump(); } // 定义抽象类 abstract class Animal { // 姓名 private String name; // 年龄 private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 吃饭(); public abstract void eat(); // 睡觉(){} public void sleep() { System.out.println("睡觉觉了"); } } // 具体猫类 class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } public void eat() { System.out.println("猫吃鱼"); } } // 具体狗类 class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } public void eat() { System.out.println("狗吃肉"); } } // 有跳高功能的猫 class JumpCat extends Cat implements Jumpping { public JumpCat() { } public JumpCat(String name, int age) { super(name, age); } public void jump() { System.out.println("跳高猫"); } } // 有跳高功能的狗 class JumpDog extends Dog implements Jumpping { public JumpDog() { } public JumpDog(String name, int age) { super(name, age); } public void jump() { System.out.println("跳高狗"); } } class InterfaceTest { public static void main(String[] args) { // 定义跳高猫并测试 JumpCat jc = new JumpCat(); jc.setName("哆啦A梦"); jc.setAge(3); System.out.println(jc.getName() + "---" + jc.getAge()); jc.eat(); jc.sleep(); jc.jump(); System.out.println("-----------------"); JumpCat jc2 = new JumpCat("加菲猫", 2); System.out.println(jc2.getName() + "---" + jc2.getAge()); jc2.eat(); jc2.sleep(); jc2.jump(); } }
五、内部类
1.内部类的定义:
把类定义在另一个类的内部,该类就被称为内部类。例如:把类B定义在类A中,类B就被称为内部类。
2.内部类的访问规则
1)可以直接访问外部类的成员,包括私有
2)外部类要想访问内部类成员,必须创建对象
3.内部类的分类
1)成员内部类
A:private 为了数据的安全性
B:static 为了访问的方便性
成员内部类不是静态的:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
成员内部类是静态的:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
2)局部内部类
局部内部类访问局部变量必须加final修饰。因为局部变量使用完毕就消失,而堆内存的数据并不会立即消失。所以,堆内存还是用该变量,而改变量已经没有了。
为了让该值还存在,就加final修饰。通过反编译工具我们看到了,加入final后,堆内存直接存储的是值,而不是变量名。
4.匿名内部类
1)是局部内部类的简化形式
2)前提存在一个类或者接口
3)格式:
new 类名或者接口名() { 重写方法; }
4)本质:其实是继承该类或者实现接口的子类匿名对象
5.匿名内部类在开发中的使用
在开发的时候,会看到抽象类,或者接口作为参数。而这个时候,我们知道实际需要的是一个子类对象。
如果该方法仅仅调用一次,我们就可以使用匿名内部类的格式简化。
interface Person { public abstract void study(); } class PersonDemo { public void method(Person p) { p.study(); } } class PersonTest { public static void main(String[] args) { PersonDemo pd = new PersonDemo(); pd.method(new Person() { public void study() { System.out.println("好好学习,天天向上"); } }); } }