继承关系当中的特点:
1、子类可以拥有父类的内容
2、子类还可以拥有自己专有的内容
定义父类的格式:(一个普通的类定义)
public class 父类名称{
//...
}
定义子类的格式:
public class 子类名称 extends 父类名称{
//...
}
示例
//定义一个父类:员工
public class Employee {
public void method(){
System.out.println("方法执行了");
}
}
//定义一个员工的子类:老师
public class Teacher extends Employee {
}
//定义了员工的另一个子类:助教
public class Assistant extends Employee {
}
public class Demo01Extends {
public static void main(String[] args) {
//创建了一个子类对象
Teacher teacher = new Teacher();
//Teacher类当中虽然什么都没写,但会继承来自父类的method方法。
teacher.method();
//创建另一个子类助教的对象
Assistant assistant = new Assistant();
//达到了代码复用的作用
assistant.method();
}
}
继承关系中,访问同名变量、方法优先级问题
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
1、直接通过子类对象访问成员变量:
等号左边 是谁,就优先用谁,没有则向上找
2、间接通过成员方法访问成员变量:
该方法属于谁,就优先用谁,没有则向上找。
创建一个父类
public class Fu {
int numFu = 20;
int num = 100;
public void fuMethod(){
System.out.println(num);
}
}
创建一个子类
局部变量: 直接写成员变量
本类的成员变量: this.成员变量名
父类的成员变量: super.成员变量名
public class Zi extends Fu{
int numZi = 40;
int num = 200;
public void ziMethod(){
int num =300;
System.out.println(num);//300 局部变量
System.out.println(this.num);//200 本类中的成员变量
System.out.println(super.num);//100 父类的成员变量
}
}
public class Demo02Extends {
public static void main(String[] args) {
Fu fu = new Fu();//创建父类对象
System.out.println(fu.numFu);//只能使用父类的东西,没有任何子类内容
Zi zi = new Zi();//创建子类对象
System.out.println(zi.numZi);//可以有子类自己的东西
System.out.println(zi.numFu);//也可以有父类的东西
System.out.println(zi.num);//优先子类,200
//该方法属于子类,就优先用子类的,没有则向上找。
zi.ziMethod();//200
//该方法属于父类,就优先用父类的
fu.fuMethod();//100
}
}
方法覆盖重写的注意事项:
1、必须保证父子类之间的方法的名称相同,参数列表也相同
为了确保是覆盖重写:
可选)@Override:写在方法前面,用来检测是不是有效的正确覆盖重写
2、子类方法的返回值必须【小于等于】父类方法的返回值范围
小扩展:Object类是所有类的(公共最高父类),String就是Object类的子类。
3、子类方法的权限必须【大于等于】父类方法的权限修饰符。
小扩展:public>protected>(default)>private
注意:(default)不是关键字default,而是什么都不写,留空
public class Fu {
public void method(){
}
}
public class Zi extends Fu {
@Override
public void method(){
}
}
//老款手机类(图纸)
public class Phone {
public void call(){
System.out.println("打电话");
}
public void sent(){
System.out.println("发短信");
}
public void show(){
System.out.println("来电显示");
}
}
//定义一个新手机,使用老手机作为父类
public class NewPhone extends Phone {
@Override
public void show() {
super.show();//把父类的show方法拿来重复利用
//子类自己再添加更多内容
System.out.println("显示姓名");
System.out.println("显示头像");
}
}
//子类必须调用父类的构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个还必须是第一个
public class DemoPhone {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.sent();
phone.show();
System.out.println("=================");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.sent();
newPhone.show();
}
}
// 打电话
// 发短信
// 来电显示
// =================
// 打电话
// 发短信
// 来电显示
// 显示姓名
// 显示头像
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束
抽象类:抽象方法所在的类,必须是抽象类才行,在class之前写上abstract即可。
如何使用抽象类和抽象方法:
1、不能直接创建new抽象类对象
2、必须用一个子类来继承抽象父类
3、子类必须覆盖重写抽象父类当中的抽象方法
覆盖重写(实现): 子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
4、 创建子类对象进行使用
public abstract class Animal {
//这是一个抽象方法,代表吃东西,但具体吃什么(大括号的内容)不确定
public abstract void eat();
//这是普通的成员方法
public void normalMethod(){
}
}
public class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
public class DemoMain {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
}
}
接口就是多个类的公共规范。
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
如何定义一个接口的格式:
public interface 接口名称{
//接口内容
}
接口使用步骤:
1、接口不能直接使用,必须有一个”实现类“来实现该接口。
格式:
public class 实现类名称 implements 接口名称{
//...
}
2、接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
去掉abstract关键字,加上方法体大括号。
3、创建实现类的对象,进行使用。
注意事项:
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类
在任何版本的Java中,接口都能定义抽象方法:
public abstract 返回值类型 方法名称(参数列表);
注意事项:
1、接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2、这两个关键字修饰符,可以选择性的省略。
定义一个接口
public interface MyInterfaceAbstract {
public abstract void methodAbs();
}
接口的实现
public class MyInterfaceImpl implements MyInterfaceAbstract {
@Override
public void methodAbs() {
System.out.println("这是方法!");
}
}
创建对象
public class Demo01Interface {
public static void main(String[] args) {
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.methodAbs();//这是方法!
}
}
接口中的“成员变量”
接口当中也可以定义"成员变量",但是必须使用public static final三个关键字进行修饰
从效果上看,这其实就是接口的【常量】。
格式:
public static final 数据类型 常量名称 = 数据值;
备注:
一旦使用final关键字及逆行修饰,说明不可改变。
注意事项:
1、接口当中的常量,可以省略public static final,注意:不写这照样是这样
2、接口当中的常量,必须进行赋值,不能不赋值。
public interface MyInterfaceConst {
//这其实就是一个常量,一旦赋值,不可修改
public static final int NUM_OF_MY_CLASS = 12;
//用的时候
//MyInterfaceConst.NUM_OF_MY_CLASS
}
接口的默认方法
从Java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
备注:接口当中的默认方法,可以解决接口升级的问题。
1、接口默认方法,可以通过接口实现类对象,直接调用
2、接口默认方法,也可以被接口实现类进行覆盖重写
public interface MyInterfaceDefault {
//抽象方法
public abstract void methodAbs();
//新添加了一个抽象方法,但是接口已投入使用,为了不影响使用
//public abstract void methodAbs2();
//新添加的方法,改成默认的方法
//对象调用默认方法,如果实现类中没有,会想上找接口
public default void methodDefault(){
System.out.println("这是添加的默认方法");
}
}
public class MyInterfaceDefaultA implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("实现了抽象方法,AAA");
}
}
public class MyInterfaceDefaultB implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("实现了抽象方法,BBB");
}
}
public class DemoInterface {
public static void main(String[] args) {
//创建一个实现类对象
MyInterfaceDefaultA a = new MyInterfaceDefaultA();
a.methodAbs();//调用抽象方法,实际运行的是右侧实现类的重写方法
//调用默认方法,如果实现类当中没有,会向上找接口
a.methodDefault();
}
}
接口的私有方法
问题描述:
我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。
但是这个共有方法不应该让实现类使用,应该是私有化的。
解决方案:
从Java 9开始,接口当中允许定义私有方法。
1、普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表){
方法体
}
2、静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表){
方法体
}
public interface MyInterfacePrivateA {
public default void methodDefault1(){
System.out.println("默认方法1");
// System.out.println("AAA");
// System.out.println("BBB");
// System.out.println("CCC");
methodCommon();
}
public default void methodDefault2(){
System.out.println("默认方法2");
// System.out.println("AAA");
// System.out.println("BBB");
// System.out.println("CCC");
methodCommon();
}
// public default void methodCommon(){
// System.out.println("AAA");
// System.out.println("BBB");
// System.out.println("CCC");
// }
private void methodCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
接口的静态方法
从Java 8开始,接口当中允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
提示:就是将abstract或者default换成static,带上方法体
public interface MyInterfaceStatic {
public static void methodStatic(){
System.out.println("这是接口的静态方法");
}
}
注意:不能通过接口实现类的对象来调用接口当中的静态方法。
正确用法:通过接口名称,直接调用其中的静态方法
public class MyInterfaceStaticImpl implements MyInterfaceStatic{
}
public class Demo03Interface {
public static void main(String[] args) {
MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();
//错误写法
// impl.methodStatic();
//直接通过接口名称调用静态方法
MyInterfaceStatic.methodStatic();
}
}
JDK8中,Oracle引入默认方法和静态方法,用来减少抽象类和接口的差异,可以
在接口中提供默认的实现方法并实现该接口的类不用强制去实现这个方法。JDK8中
接口的静态方法只能通过接口名直接去调用,接口中的默认方法因为不是abstract
的,所以可重写,也可以不重写。
一些区别
1.抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。
2.抽象类使用的是is-a关系。接口使用的has-a关系。
什么时候用抽象类?什么时候用接口?
1.如果抽象出来的一组行为,和子类具有从属关系(is-a),那么就使用抽象类。
如果没有从属关系,只是希望具备某个能力,那么使用接口。
2.如果需求变更会导致父类的功能扩展,那么使用抽象类修改的代码会少些,
而使用接口所有子类都需要调整
使用接口的时候,需要注意:
1、接口是没有静态代码块、构造方法的
2、一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
//覆盖重写所有抽象方法
}
3、如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次
4、如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
5、一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先父类
public interface MyInterfaceA {
//错误写法!接口不能有静态代码块
//static{
//
// }
//错误写法!接口不能有构造方法
//public MyInterfaceA(){
//
// }
public abstract void methodA();
//A、B接口重复的抽象方法,只需在实现类重写一次即可
public abstract void methodAbs();
}
public interface MyInterfaceB {
//错误写法!接口不能有静态代码块
//static{
//
// }
//错误写法!接口不能有构造方法
//public MyInterfaceA(){
//
// }
public abstract void methodB();
//A、B接口重复的抽象方法,只需在实现类重写一次即可
public abstract void methodAbs();
}
public class MtInterfaceImpl/*extends Object*/ implements MyInterfaceA,MyInterfaceB {
//重写快捷键Alt+Ent
@Override
public void methodA() {
System.out.println("覆盖重写了A方法");
}
//A、B接口重复的抽象方法,只需在实现类重写一次即可
@Override
public void methodAbs() {
}
@Override
public void methodB() {
System.out.println("覆盖重写了B方法");
}
}
public interface Interface1 {
public abstract void method1();
public abstract void methodCon();
public default void methodDefault(){
System.out.println("111");
}
}
public interface Interface2 {
public abstract void method1();
public abstract void methodCon();
public default void methodDefault(){
System.out.println("222");
}
}
public interface Interface extends Interface1,Interface2 {
public abstract void method();
@Override
default void methodDefault() {
}
}
/*
这个子接口当中有几个方法?
4个
method1:来源于接口1
method2:来源于接口2
methodCon:同时来源于接口1、2
method:来源于自己
*/
注意:
多个父接口中出现重复的抽象方法,没关系(无方法体)
多个父接口中出现重复的默认方法,就冲突了,那么字接口需要Override【default关键字不能省】