继承
-
继承概述
继承的格式
- 格式:pulic class 子类名 extends 父类名()
- 范例:public class Zi extends Fu()
- Fu: 是父类,也被称为基类、超类
- Zi:是子类,也被称为派生类
继承中子类的特点:
- 子类可以有父类的内容
- 子类还可以有自己特有的内容
-
继承的好处和弊端
- 继承的好处
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
- 继承的弊端
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了独立性
- 什么时候使用继承?
- 继承体现的关系: is a
- 假设法:我有两个类A和B,如果他们满足A时B的一种,就说明他们存在继承关系,这个时候就可以考虑使用继承来体现,否则就不能滥用继承
- 举例:苹果和水果,猫和动物,猫和狗
- 继承的好处
-
继承中变量的访问特点
在子类方法中访问一个变量
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲)
-
super
super关键字的用法和this关键字的用法相似
-
this:代表本类对象的引用(this关键字指向调用该方法的对象,一般我们是在当前类中使用this关键字,所以我们常说this代表本类对象的引用)
-
super:代表父类存储空间的标识(可以理解为父类对象引用)
关键字 访问成员变量 访问构造方法 访问成员方法 this this.成员变量
访问本类成员变量this(…)
访问本类构造方法this.成员方法(…)
访问本类成员方法super super.成员变量
访问父类成员变量super(…)
访问父类构造方法super.成员方法(…)
访问父类成员方法
-
继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法
- 因为子类会继承父类中的数据,可能还会使用父类的数据。所有,子类初始化之前,一定要先完成父类数据初始化
- 每一个子类构造方法的第一条语句默认都是super()
如果父类中没有无参构造方法,只有带参构造方法,该怎么办?
-
通过使用super关键字去显示的调用父类的带参构造方法
-
在父类中自己提供一个无参构造方法
推荐:自己给出无参构造方法
-
继承中成员方法的访问特点
通过子类对象访问一个方法
- 子列成员范围内找
- 父类成员范围内找
- 如果都没有就报错(不考虑父亲的父亲)
-
super内存图
main方法加载到栈内存,接着指向main方法中的语句Zi Z 加载到main方法中,new Zi() 堆内存出现由new申请的地址,并进行子类数据的初始化,调用Zi的构造方法加载到栈内存,在Zi的构造方法中第一行访问默认的无参构造方法,其中第一行为super(),访问Fu的内容,Fu的内容进行初始化在堆内存中由super空间存储Fu类初始化的数据,再访问Fu类构造方法,Fu类构造方法加载到栈内存,在控制台输出Fu类无参构造方法被调用,Fu类无参都在方法指向完毕从栈内存消失,访问Fu类无参构造方法结束后回到栈内存访问zi类无参构造方法,结束后Zi类无参构造方法从栈内存消失,再调用Zi.show()方法,show()方法被加载到栈内存并访问执行,执行完毕后在栈内存中消失。再往下看method()方法,zi类中并无method()方法但Fu类中有,故调用Fu类method()方法。最后main方法执行完毕,从栈内存中消失。
-
方法重写
方法重写概述
- 子类中出现了和父类中一模一样的方法声明
方法重写的应用
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,既沿袭了父类的功能,又定义了子类特有的内容。
@override
- 是一个注解
- 可以帮助我们检查重写方法的方法声明的正确性
-
方法重写的注意事项
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能更低(public>默认>私有)
-
Java中继承的注意事项
- Java中类只支持单继承,不支持多继承
- Java中类支持多层继承
-
修饰符
-
包
-
包的概述和使用
其实就是文件夹
作用:对类进行分类管理
包的定义格式:
- 格式:package 包名;(多级包用.分开)
- 范例:package com.itheima;
带包的Java类编译和执行
-
手动创建包:
新建文件夹com,在其中再建一个包heima,把编译生成(javac)的文件放到其中,完成。
-
自动创建包:
javac -d . HelloWorld.java
运行时带包运行 java com.itheima.HelloWorld
-
-
导包
-
导包的概述和使用
使用不同包下的类时,使用的时候要写全类的全路径,写起来太麻烦了
为了简化带包的操作,Java就提供了导包的功能
导包的格式
- 格式:import包名;
- 范例:import cn.itcast.Teacher
-
-
修饰符
-
修饰符的分类
-
权限修饰符
修饰符 同一个类中 同一个包中子类无关类 不同包的子类 不同包的无关类 private √ 默认 √ √ protected √ √ √ public √ √ √ √ -
状态修饰符
-
final
final关键字时最终的意思,可以修饰成员方法、成员变量、类
final修饰的特点
- 修饰方法:表明该方法是最终方法,不能被重写
- 修饰变量:表明该变量是常量,不能被再次赋值
- 修饰类:表明该类是最终类,不能被继承
final修饰局部变量
- 变量是基本类型:final修饰指的是基本类型的数据值不能发生改变
- 变量是引用类型:final修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的
-
static
static关键字是静态的意思,可以修饰成员方法、成员变量
static修饰的特点
-
被类的所有对象共享
这也是我们判断使用使用静态关键字的条件
-
可以通过类名调用
当然也可以通过对象名调用
private String name; private int age; public static String university;
Students.university="PKU"; Students s1=new Students(); s1.name="ling"; s1.age=20; s1.show(); Students s2=new Students(); s2.name="feng"; s2.age=20; s2.show();
static访问特点
- 静态成员方法只能访问静态成员
-
-
-
-
多态
-
多态概述
同一个对象,在不同时刻表现出来不同形态
举例:猫
我们可以说猫是猫:猫 cat =new 猫();
也可以说猫是动物:动物 animal=new 猫();
这里猫在不同的时刻表现出来了不同的形态,这就是多态
多态的前提和体现
- 有继承/实现关系
- 有方法重写
- 有父类引用指向子类对象
-
多态中成员访问特点
- 成员变量:编译看左边,执行看左边
- 成员方法:编译看左边,执行看右边
为什么成员变量和成员方法的访问不一样呢?
- 因为成员方法有重写,而成员变量没有
-
多态的好处和弊端
-
多态的好处:提高了程序的扩展性
具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型与操作
-
多态的弊端:不能使用子类特有的功能
package heima06; public class Animal { public void eat() { System.out.println("动物吃东西"); } }
package heima06; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
package heima06; public class Pig extends Animal { @Override public void eat() { System.out.println("猪吃白菜"); } }
package heima06; public class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); } public void lookdoor() { System.out.println("狗看门"); } }
package heima06; public class AnimalOperator { /* public void useAnimal(Cat c){ c.eat(); } public void useAnimal(Dog d){ d.eat(); }*/ public void useAnimal(Animal a) { a.eat(); // a.lookdoor();//报错 } }
package heima06; public class AnimalDemo { public static void main(String[] args) { AnimalOperator ao = new AnimalOperator(); Cat c = new Cat(); ao.useAnimal(c); Dog d = new Dog(); ao.useAnimal(d); Pig p = new Pig(); ao.useAnimal(p); } }
-
-
多态中的转型
-
向上转型
从子到父
父类引用指向子类对象
-
向下转型
从父到子
父类引用转为子类对象
package heima07; public class Animal { public void eat() { System.out.println("动物吃东西"); } }
package heima07; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
package heima07; public class AnimalDemo { public static void main(String[] args) { Animal a = new Cat(); //向上转型 a.eat(); // a.playGame();//报错 Animal中没有playGame方法 /*//创建Cat类对象 Cat c =new Cat(); c.eat(); c.playGame();*/ //向下转型 Cat c = (Cat) a; c.eat(); c.playGame(); //解决了多态不能使用子类特有功能的弊端 } }
-
-
多态转型内存图解
抽象类
-
抽象类概述
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
-
抽象类的特点
-
抽象类和抽象方法必须使用abstract关键字修饰
- public abstract class 类名 {}
- public abstract void eat();
-
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
抽象类不能实例化
抽象类如何实例化?参照多态的方式,通过子类对象实例化,这叫抽象类多态
-
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
package heima08; /* 抽象类 */ public abstract class Animal { /*public void eat(){ System.out.println("吃东西"); }*/ //抽象方法 public abstract void eat(); public void sleep() { System.out.println("睡觉"); } }
package heima08; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
package heima08; public abstract class Dog extends Animal { }
package heima08; public class AnimalDemo { public static void main(String[] args) { /*Animal a=new Animal(); a.eat();*/ Animal a = new Cat(); a.eat(); a.sleep(); } }
-
-
抽象类的成员特点
-
成员变量
可以是变量
也可以是常量
-
构造方法
有构造方法,但是不能实例化
用于子类访问父类数据的初始化
-
成员方法
可以有抽象方法:限定子类必须完成某些动作
也可以有非抽象方法:提高代码复用性
案例:猫和狗
package heima09; public 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(); }
package heima09; public class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } }
package heima09; //测试类 public class AnimalDemo { public static void main(String[] args) { //创建对象,按照多态的方式 Animal a = new Cat(); a.setName("coffe"); a.setAge(4); System.out.println(a.getName() + "," + a.getAge()); a.eat(); System.out.println("--------------"); a = new Cat("coffe", 5); System.out.println(a.getName() + "," + a.getAge()); a.eat(); } }
-
接口
-
接口概述
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用
Java的接口更多的体现在对行为的抽象
-
接口的特点
-
接口用关键字interface修饰
public interface 接口名{}
-
类实现接口用implements表示
public class 类名 implements 接口名{}
-
接口不能实例化
接口参照多态的方式,通过实现类对象实例化,这叫接口多态
多态的形式:具体类多态、抽象类多态、接口多态
多态的前提:有继承或者实现关系;有方法重写;有父(类/接口)引用指向(子/实现)类对象
-
接口的实现类
要么重写接口中的所有抽象方法
要么是抽象类
package heima10; public class Cat implements Jumpping { @Override public void jump() { System.out.println("猫可以跳高了"); } }
package heima10; public abstract class Dog implements Jumpping { }
package heima10; public interface Jumpping { public abstract void jump(); }
package heima10; public class JumppingDemo { public static void main(String[] args) { Jumpping j = new Cat(); j.jump(); } }
-
-
接口的成员特点
-
成员变量
只能是常量
默认修饰符:public static final
-
构造方法
接口没有构造方法,因为接口主要是对行为进行抽象的,是没有具体存在
一个类如果没有父类,默认继承Object类
-
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
案例:猫和狗
对猫和狗进行训练,他们就可以跳高了,这里加入了跳高功能。请采用抽象类和接口来实现猫狗案 例。
package heima12; public interface Jumpping { public abstract void jump(); }
package heima12; public 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(); }
package heima12; public class Cat extends Animal implements Jumpping { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } @Override public void jump() { System.out.println("猫可以跳高了"); } }
package heima12; public class AnimalDemo { public static void main(String[] args) { Jumpping j = new Cat(); j.jump(); System.out.println("-----------------------"); Animal a = new Cat(); a.setName("coffe"); a.setAge(4); System.out.println(a.getName() + "," + a.getAge()); a.eat(); // a.jump(); a = new Cat("coffe", 5); System.out.println(a.getName() + "," + a.getAge()); a.eat(); System.out.println("-----------------------"); Cat c = new Cat(); c.setName("coffe"); c.setAge(3); System.out.println(c.getName() + "," + c.getAge()); c.eat(); c.jump(); } }
-
-
子类和接口的关系
-
类和类的关系
继承关系,只能单继承,但是可以多层继承
-
类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口和接口的关系
继承关系,可以单继承,也可以多继承
-
-
抽象类和接口的区别
-
成员区别
抽象类 变量,常量:有构造方法;有抽象方法,也有非抽象方法
接口 常量;抽象方法
-
关系区别
类与类 继承,单继承
类与接口 实现,可以单实现,也可以多实现
接口与接口 继承,单继承,多继承
-
设计理念区别
抽象类 对类抽象,包括属性、行为
接口 对行为抽象,主要是行为
抽象类是对事物的抽象,而接口是对行为的抽象
-
-
案例:运动员和教练
需求:我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了出国交流,跟乒乓球相关的人员都需要学习英语。请用所学只是分析,这个案例中有哪些具体类,哪些抽象类,哪些接口,并用代码实现。
分析:从具体到抽象
乒乓球运动员
乒乓球教练
篮球教练
实现:从抽象到具体
使用:使用的是具体的类的对象
思路:
- 定义说英语接口 成员方法:说英语();
- 定义抽象人类 成员变量:姓名,年龄;构造方法:无参,带参;成员方法:get/set方法,吃饭();
- 定义抽象教练类,继承人类 构造方法:无参,带参;成员方法:教();
- 定义抽象运动员类,继承人类 构造方法:无参,带参;成员方法:学();
- 定义具体篮球教练类,继承教练类 构造方法:无参,带参;成员方法:重写吃饭(){…},重写教(){…}
- 定义具体乒乓球教练,继承教练类,实现说英语接口 构造方法:无参,带参;成员方法:重写吃饭(){…},重写教(){…},重写说英语(){…}
- 定义具体篮球运动员类,继承运动员类 构造方法:无参,带参;成员方法:重写吃饭(){…},重写学习(){…}
- 定义具体乒乓球运动员类,继承运动员类,实现说英语接口 构造方法:无参,带参;成员方法:重写吃饭(){…},重写学习(){…},重写说英语(){…}
- 定义测试类,写代码测试
package heima13;
public interface SpeakEnglish {
public abstract void speak();
}
package heima13;
public abstract class Person {
private String name;
private int age;
public Person(){
}
public Person(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();
}
package heima13;
public abstract class Player extends Person {
public Player() {
}
public Player(String name, int age) {
super(name, age);
}
public abstract void studey();
}
package heima13;
public abstract class Coach extends Person {
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
package heima13;
public class BasketballCoach extends Coach {
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练教如何运球和投篮");
}
@Override
public void eat() {
System.out.println("篮球教练吃羊肉,喝羊奶");
}
}
package heima13;
public class PingPangCoach extends Coach implements SpeakEnglish {
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练教如何发球喝接球");
}
@Override
public void eat() {
System.out.println("乒乓球教练吃小白菜,喝大米粥");
}
@Override
public void speak() {
System.out.println("乒乓球教练说英语");
}
}
package heima13;
public class BasketballPlayer extends Player {
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age) {
super(name, age);
}
@Override
public void studey() {
System.out.println("篮球运动员学习如何运球喝投篮");
}
@Override
public void eat() {
System.out.println("篮球运动员吃牛肉,喝牛奶");
}
}
package heima13;
public class PingPangPlayer extends Player implements SpeakEnglish {
public PingPangPlayer() {
}
public PingPangPlayer(String name, int age) {
super(name, age);
}
@Override
public void studey() {
System.out.println("乒乓球运动员学习如何发球和接球");
}
@Override
public void eat() {
System.out.println("乒乓球运动员吃大白菜,和小米粥");
}
@Override
public void speak() {
System.out.println("乒乓球运动员说英语");
}
}
package heima13;
public class PersonDemo {
public static void main(String[] args) {
PingPangPlayer ppp = new PingPangPlayer();
ppp.setName("wh");
ppp.setAge(30);
System.out.println(ppp.getName() + "," + ppp.getAge());
ppp.eat();
ppp.studey();
ppp.speak();
System.out.println("--------------------------");
BasketballPlayer bp = new BasketballPlayer();
bp.setName("ym");
bp.setAge(30);
System.out.println(bp.getName() + "," + bp.getAge());
bp.studey();
bp.eat();
}
}
行参和返回值
-
形参和返回值
-
类名作为形参和返回值
package heima14; public class Cat { public void eat() { System.out.println("猫吃鱼"); } }
package heima14; public class CatOperator { public void useCat(Cat c) { c.eat(); } }
package heima14; public class CatDemo { public static void main(String[] args) { //创建操作类对象,并调用方法 CatOperator co = new CatOperator(); Cat c = new Cat(); co.useCat(c); } }
- 方法的形参是类名,其实需要的是该类的对象
- 方法的返回值是类名,其实返回的是该类的对象
-
抽象类名作为形参和返回值
package heima15; public abstract class Animal { public abstract void eat(); }
package heima15; public class AnimalOperator { public void useAnimal(Animal a) {//Animal a=new Cat(); a.eat(); } }
package heima15; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
package heima15; public class AnimalDemo { public static void main(String[] args) { AnimalOperator ao = new AnimalOperator(); Animal a = new Cat(); ao.useAnimal(a); } }
- 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
- 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
-
接口名作为形参和返回
package heima16; public interface Jumpping { void jump(); }
package heima16; public class JumppingOperator { public void useJumpping(Jumpping j) {//Jumpping j=new Cat(); j.jump(); } public Jumpping getJumpping() { Jumpping j = new Cat(); return j; } }
package heima16; public class Cat implements Jumpping { @Override public void jump() { System.out.println("猫可以跳高"); } }
package heima16; public class JumppingDemo { public static void main(String[] args) { JumppingOperator jo = new JumppingOperator(); Jumpping j = new Cat(); jo.useJumpping(j); Jumpping j2 = jo.getJumpping();//new Cat() j2.jump(); } }
- 方法的形参是接口名,其实需要的是该接口的实现类对象
- 方法的返回值是接口名,其实返回的是该接口的实现类对象
-
内部类
-
内部类概述
内部类:就是在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
内部类的定义格式
-
格式
public class 类名{
修饰符class 类名{
}
}
-
范例:
public class Outer{
public class Inner{
}
}
内部类的访问特点
-
内部类可以直接访问外部类的成员,包括私有
-
外部类要访问内部类的成员,必须创建对象
package heima17; public class Outer { private int num = 10; public class Inner { public void show() { System.out.println(num);//内不类可以直接访问外部类对象 } } public void method() { // show();//外部类不可以直接访问内部类方法 Inner i = new Inner(); i.show();//需要创建内部类对象调用他们的方法 } }
-
-
-
成员内部类
按照内部类在类中定义的位置不同,可以分为如下两种形式
-
在类的成员位置:成员内部类
-
在类的局部位置:局部内部类
成员内部类,外界如何创建对象使用?
-
格式:外部类名.内部类名 对象名 =外部类对象.内部类对象;
-
范例:Outer.Inner oi=new Outer().new Inner();
package heima17; public class InnerDemo { public static void main(String[] args) { //创建内部类对象,并调用方法 // Inner i =new Inner();//不能直接创建内部类 Outer.Inner oi = new Outer().new Inner(); oi.show(); } }
但一般这样用:
package heima17; import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; public class Outer { private int num = 10; /* public class Inner { public void show() { System.out.println(num);//内不类可以直接访问外部类对象 } }*/ private class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } }
package heima17; public class InnerDemo { public static void main(String[] args) { //创建内部类对象,并调用方法 // Inner i =new Inner();//不能直接创建内部类 Outer o = new Outer(); o.method(); } }
-
-
-
局部内部类
局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
package heima17; import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; public class Outer { private int num = 10; public void method() { int num2=20; class Inner { //局部内部类外部是无法直接访问的,也要通过外部类调用method方法间接访问 public void show() { System.out.println(num); System.out.println(num2); } } Inner i = new Inner(); i.show(); } }
package heima17; public class InnerDemo { public static void main(String[] args) { Outer o = new Outer(); o.method(); } }
-
匿名内部类
前提:存在一个类或接口,这里的类可以是具体类也可以是抽象类
-
格式
new 类名或接口名(){
重写方法;
};
-
范例:
new Inter(){
public void show(){
}}
本质:是一个继承了该类或者实现了该接口的子类匿名对象
package heima17; public interface Inter { void show(); }
package heima17; import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; public class Outer { public void method() { /*new Inter(){ @Override public void show() { System.out.println("匿名内部类"); } };*/ //这样它只是一个方法,并没有被调用使用 new Inter() { @Override public void show() { System.out.println("匿名内部类"); } }.show();//这样才调用了内部的show方法 //如果是多次调用 Inter i = new Inter() { @Override public void show() { System.out.println("匿名内部类"); } }; i.show(); } }
package heima17; public class InnerDemo { public static void main(String[] args) { Outer o = new Outer(); o.method(); } }
-
-
匿名内部类在开发中的使用
package heima18; public class JumppingDemo { public static void main(String[] args) { //需求:创建接口操作的对象,调用method方法 JumppingOperator jo = new JumppingOperator(); Jumpping j = new Cat(); jo.method(j); Jumpping j2 = new Dog(); jo.method(j2); System.out.println("-----------------------"); //用匿名内部类实现 jo.method(new Jumpping() { @Override public void jump() { System.out.println("猫可以跳高了"); } }); jo.method(new Jumpping() { @Override public void jump() { System.out.println("狗可以跳高了"); } }); //采用后面的匿名内部类的形式就不需要另外创建两个类 } }