Chapter. 5 继承

继承作为面向对象的三大特征之一,它是多态的前提。它主要解决的问题是共性抽取

Java中的继承,是单继承、多级继承的。

已存在的类,被称为超类、基类、父类(parent class);新类,被称为子类(subclass)、派生类。

每一个子类的直接父亲是唯一的,但一个父亲可拥有多个子类。子类比父类拥有的功能更加丰富。

5.1 继承的格式

定义父类的格式(普通类的定义)

public class 父类名称{
//...
}

定义子类的格式

public class 子类名称 extends 父类名称{
//...
}

5.2 继承中成员的访问特点

优先使用本类的成员变量与成员方法,若没有,则往上寻找。

当局部变量名、本类与父类的成员变量名均相同时,可以用以下语法进行区分使用。

  • 局部变量:直接写该变量名
  • 本类的成员变量:this.成员变量名
  • 父类的成员变量:super.成员变量名

5.2.1 覆写(Override)

由于,父类中的有些方法对于子类并不一定适用。因此需要一个新的方法来覆盖父类中的这个方法。

覆写(或称覆盖、重写),发生在继承关系当中,方法的名称一样,参数列表也一样

区分:重载(Overload),方法的名称一样,参数列表不一样

特点:覆写创建的是子类对象,则优先使用子类方法。

@Override:注解(annotation),写在方法前面,用于检测是否为有效的正确覆写。(属于安全检测手段)

注意事项:

  1. 子类方法的返回值必须小于等于父类方法的返回值范围。

    java.long.Object类是所有类的公共最高父类(祖宗类)

  2. 子类方法的权限必须大于等于父类方法的权限修饰符。

    public > protected > (default) > private

    (default)代表什么都不写,直接留空。

  3. 若父类抛出多个异常,子类覆写父类方法时,应该抛出 与父类相同的异常 或者 父类异常的子类 或者 不抛出异常;若父类没有抛出异常,子类覆写时不能抛出异常,此时子类产生该异常,只能捕获处理而不能声明抛出。

/* Phone.java文件中 */
public class Phone {
public void show(){
System.out.println("显示号码");
}
} /* NewPhone.java文件中 */
public class NewPhone extends Phone {
@Override
public void show(){
super.show();
System.out.println("显示头像");
System.out.println("显示号码归属地");
}
}

5.3 继承中构造方法的访问特点

  1. 子类构造方法当中有一个默认隐含的super()调用,所以一定是先调用父类无参构造方法,后执行子类的构造方法。

  2. 子类构造中可通过super关键字,来调用父类重载的构造方法。

    /* fa.java文件中 */
    public class fa{
    public fa(){
    System.out.println("父类无参构造!");
    }
    public fa(int num){
    System.out.println("父类构造方法!");
    }
    }
    /* son.java文件中 */
    public class son{
    public son(){
    super(20); //调用父类重载的构造方法
    System.out.println("子类构造方法!");
    }
    }
  3. super的父类构造调用,必须为子类构造方法中的第一个只能为第一条语句。因此,super()this()这两种构造调用,不能同时使用。

5.4 抽象类

如果父类当中的方法不确定如何进行{}方法体实现,那么这应该是一个抽象方法,相应地,这个父类也必然是抽象类。人们只将它作为派生其他类的基类,而不作为想使用的特定的实例类。

  • 抽象方法:在方法返回值之前加上abstract关键字,然后去掉大括号,直接分号结束。

  • 抽象类:抽象方法所在的类,必须是抽象类。需在class之前利用abstract关键字去声明。

public abstract class Animal{ //抽象类
public abstract void eat(); //抽象方法
public void normalMethod(){ //普通的成员方法
//...
}
}

注意事项:

  1. 不能创建抽象类对象,必须用一子类去继承抽象父类。

  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

  3. 抽象类中,不一定包含抽象方法,但是具有抽象方法的类必定是抽象类。

  4. 子类必须覆写抽象父类当中所有的抽象方法(除非该子类也是抽象类),即子类中,去掉抽象方法的abstract关键字,然后补上方法体大括号。

    public class Cat extends Animal{
    @Override
    public void eat(){
    System.out.println("The cat eats fish");
    }
    }

Chapter 6. 接口

接口interface)技术,主要用来描述类具有什么功能,而并不给出每个功能的具体实现。一个类可以实现implement)一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。

6.1 接口概述

在Java中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。重要的是,接口不能提供哪些功能,绝不能含有实例域,也不能在接口中实现方法、静态代码块、构造方法。提供实例域和方法实现的任务,应该由实现接口的那个类(可以看做为接口的子类)来完成,实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

举个例子,Arrays类中的sort方法承诺可以对对象数组进行排序,但要求满足前提——对象所属的类必须实现了Comparable接口。

接口是一种引用数据类型,它的内部主要封装了方法,包含抽象方法(JDK7以前),默认方法和静态方法(JDK8),私有方法(JDK9)。

6.2 定义格式与基本实现

接口的定义格式:(使用interface关键字)

public interface 接口名称{
//抽象方法
//默认方法
//静态方法
//私有方法
}

在使用IDEA创建interface时,应该新建interface而不是class

但是,换成关键字interface之后,.java文件编译生成的字节码文件仍然为.class

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可称为接口的子类。实现使用implements关键字(注意s

class 类名 implements 接口名{
//重写接口中抽象方法(必须!)
//重写接口中默认方法(可选)
}

6.2.1 含有抽象方法

抽象方法: 使用 abstract 关键字修饰,没有方法体。该方法供子类实现使用

定义接口:

public interface LiveAble{
public abstract void eat();//定义抽象方法
public abstract void sleep();
}

定义实现类:

public class Animal implements LiveAble{
@Override
public void eat(){
System.out.println("获取食物");
}
@Override
public void sleep(){
System.out.println("地上睡觉");
}
}

TIPS:对于IDEA而言,鼠标选中整个接口名称,按alt + Enter键就能方便覆写:

定义测试类:

public class InterfaceDemo{
public static void main(String[] args){
Animal a = new Animal(); //创建子类对象
a.eat(); a.sleep();
}
}

6.2.2 含有默认方法

默认方法:使用default修饰,不可省略,供子类调用或者子类覆写

定义接口:

public interface LiveAble{
public default void fly(){
System.out.println("天上飞");
}
}

实现类可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。

  1. 继承默认方法来定义实现类:

    public class Animal implements LiveAble{
    //继承,什么都不用写,直接调用
    }
  2. 重写默认方法来定义实现类:

    public class Animal implements LiveAble{
    @Override
    public void fly(){
    System.out.println("自由自在地飞");
    }
    }

定义测试类:

public class InterfaceDemo{
public static void main(String[] args){
Animal a = new Animal(); //创建子类对象
a.fly(); //1 的输出结果为:天上飞;2 的输出结果为:自由自在地飞。
}
}

6.2.3 含有静态方法

静态方法:使用static修饰,供接口直接调用。

定义接口:

public interface LiveAble{
public static void run(){
System.out.println("跑起来!");
}
}

静态与.class文件相关,只能使用接口名调用,不可通过实现类的类名或者实现类的对象调用。

定义实现类:

public class Animal implements LiveAble{
//无法重写静态方法!
}

定义测试类:

public class InterfaceDemo{
public static void main(String[] args){
//Animal.run(); //错误写法,无法继承方法也不能调用。
LiveAble.run();
}
}

6.2.4 含有私有方法

若一个接口中有多个默认方法,且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。从设计的角度来讲,私有的方法是对默认方法和静态方法的辅助。

  • 普通私有方法:只有默认方法可以调用。

    private 返回值类型 方法名称(参数列表){
    方法体
    }
  • 静态私有方法:默认方法和静态方法可以调用。

    private static 返回值类型 方法名称(参数列表){
    方法体
    }

举例定义接口:

public interface LiveAble{
public default void eat(){
System.out.println("Eat something...");
methodCommon();
}
public default void sleep(){
System.out.println("Go to sleep...");
methodCommon();
}
private void methodCommon(){ //该方法是专门为了上面两个方法所抽取出来的,不应被实现类访问。
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}

6.2.5 含有常量

接口当中其实也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰,从效果上看就是接口的“常量”。

定义接口:

public interface LiveAble{
public static final int num = 233;
//public 说明接口内外部都可使用;static 使得num与对象无关;final 使得num不可变
}

注意,接口当中的常量必须进行赋值!

6.3 实现多个接口

6.3.1 接口与抽象类的比较

使用抽象类表示通用属性会存在问题——每个类只能拓展于一个类。不同于C++的多继承特性,为了避免将语言变得非常复杂或者效率降低,Java语言利用接口机制,来实现多继承的大部分功能,由此每个类可以同时实现多个接口

6.3.2 定义格式

public class 实例类名 implements 接口A, 接口B{
//覆盖所有抽象方法
}

注意事项:

  1. 若实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
  2. 若实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆写。(也就说,不能直接继承这些默认方法)
  3. 一个类中若直接父类当中的方法,与接口当中的默认方法产生了冲突,优先使用父类当中的方法。

6.4 接口之间的多继承

类与类之间是单继承的,而接口与接口之间是多继承的。

接口的继承也是使用extends关键字。

定义父接口:

public interface A(){
public default void method(){
System.out.println("AAA");
}
}
public interface B(){
public default void method(){
System.out.println("BBB");
}
}

定义子接口:

public interface C extends A, B{
@Override
public default void method(){
System.out.println("23333");
}
}

注意事项:

  1. 如果父接口中的默认方法有重名的,那么子接口需要重写一次。
  2. 子接口重写默认方法时,default关键字可以保留。但是,子类重写默认方法时,default关键字不可以保留。

Chapter 7. 多态

多态:指同一行为,具有多个不同表现形式。

"is-a"规则的另一种表述法是置换法则,它表明程序中出现超类对象的任何地方都可以用子类对象置换。在Java中,对象变量是多态的,一个Employee父类变量既可引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象。

多态的好处在于,使得程序编写得更简单,并具有良好的拓展。

7.1 多态的体现

定义格式为:

父类名称 变量名 = new 子类对象; // Parent a = new Son();
变量名.方法名(); //a.method();

父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

7.2 多态中访问成员变量的两种方式

  1. 通过对象名称直接访问成员变量,看new等号的左边是谁,优先用谁,没有则向上找。

    如果子类定义个age成员变量,但父类没有定义,则编译器无法找到age变量

  2. 通过成员方法间接访问成员变量,看该方法属于谁,优先用谁,没有则向上找。

    子类没有覆盖重写,则向父类找;子类如果覆盖重写,则向子类找。

7.3 多态中访问成员方法的规则

new的是谁,就优先使用谁,若没有该方法则向上找。

Parent obj = new Son();
obj.method(); //父子都有该方法,优先使用子类
obj.methodOfParent(); //子类没有,但父类有,则向上找到父类。

7.4 向上转型与向下转型

  • 对象的向上转型(较安全)

    父亲引用指向子类对象,然而缺点是,对象一旦向上转型为父类,则无法调用子类原本特有的内容。

    格式为:父类名称 对象名 = new 子类名称;

  • 对象的向下转型

    实际上是一个还原动作,将父类对象还原为原来的子类对象。如果对象创建的时候不是某个类,但此时若要向下转型成该类,则会报错。

    格式为:子类名称 对象名 = (子类名称) 父类对象;

    Animal animal = new Cat(); //创建一个子类Cat对象,然后将它作为父类使用
    Cat cat = (Cat) animal; //本来是Cat,已经被当做Animal,现在还原回来成为本来的Cat

案例实现:笔记本具备使用USB设备的功能,且都预留可插入USB设备的USB接口。定义USB接口,具备基本的开启与关闭功能。鼠标和键盘若要在电脑上正常使用,则必须遵守USB规范,实现USB接口,否则无法使用。

定义USB接口:

public interface USB {
void open(); //打开某个设备
void close(); //关闭某个
}

定义鼠标类:(实现接口)

public class Mouse implements USB{
public void open(){
System.out.println("鼠标连接成功!红灯闪烁!");
}
public void close(){
System.out.println("鼠标连接断开!红灯熄灭!");
}
public void click(){
System.out.println("鼠标单击!");
}
}

定义键盘类:(实现接口)

public class KeyBoard implements USB{
public void open(){
System.out.println("键盘连接成功!绿灯常亮!");
}
public void close(){
System.out.println("键盘连接断开!绿灯熄灭!");
}
public void type(){
System.out.println("键盘打字!");
}
}

定义笔记本类:(使用接口)

public class Laptop {
public void run(){
System.out.println("笔记本运行中....");
}
public void useUSB(USB usb){ //usb已经发生向上转型了
//当笔记本对象调用该功能时,必须给其传递一个符合USB规则的USB设备
if( usb != null){
usb.open();
if(usb instanceof Mouse){
Mouse m = (Mouse) usb; //将usb向下转型(还原)为鼠标类
m.click(); //调用特定方法
}
else if(usb instanceof KeyBoard){
KeyBoard kb = (KeyBoard) usb;//将usb向下转型(还原)为键盘类
kb.type(); //调用特定方法
}
usb.close();
}
}
public void shutDown(){
System.out.println("笔记本关闭....");
}
}

Chapter 8. 内部类

如果一个事物内部包含另一个事物,那么这就是一个类内部包含另一个类。如类A定义在另一个类B里面,那么内部的类A称为内部类inner class),类B称为外部类。

而内部类有两种:成员内部类 与 局部内部类(包含匿名内部类)。

8.1 成员内部类的定义与使用

一、定义

成员内部类:定义在类中方法外的类。

定义格式为

修饰符 class 外部类名称{
修饰符 class 内部类名称{
//...
}
}

访问特点:

  • 内部类可以直接访问外部类的数据域(包括私有成员
  • 外部类要访问内部类的成员,必须要建立内部类的对象。

编译成文件时,外部类为XXX.class,其内部类为XXX$YYY.class

二、使用

  1. 间接方式:在外部类的方法中,使用内部类;然后main只是调用外部类的方法;

  2. 直接方式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称;

    Body.Heart myheart = new Body().new Heart();
    myheart.beat();

如果外部类成员变量、内部类成员变量、内部类成员方法中局部变量发生了重名现象,要访问外部类的成员变量需用:外部类名称.this.外部类成员变量名。举例使用如下:

public class Outer{
int num = 666; public class Inner{
int num = 233; public void methodInner(){
int num = 30; //内部类方法的局部变量
System.out.println(num); //局部变量,就近原则
System.out.println(this.num); //内部类成员变量
System.out.println(Outer.this.num); //外部类成员变量
}
}
}

8.2 局部内部类的定义与使用

一、定义

局部内部类:一个类定义在一个方法(包括main)内部

只有当前所属的方法才能够使用它,超出该方法则不能再使用。

普通局部内部类的定义格式:

修饰符 class 外部类名称{

    修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
//...
}
}
}

二、使用

public class Outer{

    public void methodOuter(){
class Inner{ //局部内部类,注意没有任何权限修饰符修饰!
int num = 233;
public void methodInner(){
System.out.println(num);
}
}
Inner inner = new Inner();
inner.methodInner();
}
}

注意,局部内部类倘若希望访问其所在方法的局部变量,那么该局部变量必须是“有效final”(要么被final修饰,要么只被赋一次值)

局部变量是跟随方法走的,在栈内存当中,故方法运行结束之后立刻出栈,局部变量就立刻消失。

new出来的对象在堆内存当中且持续存在,直到垃圾回收消失。

8.3 匿名内部类的定义与使用

一、定义

匿名内部类anonymous inner class)(开发中,最常用的内部类):局部内部类的简化写法,它本质为一个带具体实现的、父类或者父接口的、匿名的 子类对象

如果接口的实现类(或父类的子类)只需使用一次,那么可以省略掉该实现类(或子类)的定义,无需再创建个.java文件,而直接改为匿名内部类。

定义格式:(前提是该匿名内部类必须继承一个父类或者实现一个父接口)

接口名称 对象名 = new 接口名称(){
//方法覆写
@Override
public void 成员方法名称{
//...
}
}; //别忘了分号

二、使用(以接口为例)

public class DemonMain{
public static void main(String[] args){
/* 普通写法:
MyInterface obj = new MyInterfaceImpl();
obj.method();
*/
//使用匿名内部类,但不属于匿名对象。
//等号左边:多态赋值,接口类型引用该子类对象
MyInterface obj = new MyInterface(){ //等号右边:定义并创建该接口的子类对象
@Override
public void method(){
System.out.println("匿名内部类实现了方法!");
}
}; //大括号内是匿名内部类的内容。
obj.method(); //调用覆写的方法 //使用匿名内部类,而且省略了对象名称,故也属于匿名对象
new MyInterface(){
@Override
public void method2(){
System.out.println("匿名内部类实现了方法!");
}
}.method2(); //注意此处调用了方法。
}
}

匿名内部类在创建对象的时候,只使用唯一一次(若希望多次创建对象,且类的内容一样的话,那么就必须使用单独定义的实现类)。

注意区分,匿名对象,在调用方法时,只能调用唯一一次(若希望对同一对象调用多次方法,那么就必须为该对象起名字)。

hero.setSkill(new Skill(){
@Override
public void use(){
System.out.println("匿名内部类与匿名对象的同时使用!")
}
});

JavaSE 学习笔记03丨继承、接口、多态、内部类的更多相关文章

  1. JavaSE 学习笔记02丨对象与类

    Chapter 4. 对象与类 4.1 面向对象程序设计概述 面向对象程序设计(简称OOP),是当今主流程序设计范型.面向对象的程序是由对象组成的,每个对象(来自于标准库或自定义的)包含对用户公开的特 ...

  2. (C/C++学习笔记) 十八. 继承和多态

    十八. 继承和多态 ● 继承的概念 继承(inheritance): 以旧类为基础创建新类, 新类包含了旧类的数据成员和成员函数(除了构造函数和析构函数), 并且可以派生类中定义新成员. 形式: cl ...

  3. [core java学习笔记][第六章接口与内部类]

    接口域内部类 接口 描述类具有什么功能,不给出具体实现. 内部类 用于设计协作关系的类集合 代理 实现任意接口的对象. 6.1 接口 接口声明 public interface Comparable ...

  4. JavaSE 学习笔记01丨开发前言与环境搭建、基础语法

    本蒟蒻学习过C/C++的语法,故在学习Java的过程中,会关注于C++与Java的区别.开发前言部分,看了苏星河教程中的操作步骤.而后,主要阅读了<Java核心技术 卷1 基础知识>(第8 ...

  5. 《java JDK7 学习笔记》之继承与多态

    1.面向对象中,子类继承父类,避免重复的行为定义,不过并非为了避免重复定义行为就使用继承.应该正确判断使用继承的时机及继承之后灵活的运用多态,才是学习继承时的重点. 2.程序代码重复在程序设计上,就是 ...

  6. Java学习笔记——封装、继承和多态

    先说说封装: 用new 一条狗来举个例子: public class Dog { //私有化字段 private String name; private int age; //无参构造 Dog(){ ...

  7. JavaSE 学习笔记07丨IO流

    Chapter 13. IO流 13.1 File类 java.io.File类是文件(file)和目录(文件夹)(directory)路径名(path)的抽象表示,主要用于文件和目录的创建.查找和删 ...

  8. JavaSE 学习笔记06丨并发

    Chapter 12. 并发 12.1 并发与并行 并发:指两个或多个事件在同一个时间段内发生. 并行:指两个或多个事件在同一时刻发生(同时发生). 在操作系统中,并发指的是在一段时间内宏观上有多个程 ...

  9. JavaSE 学习笔记05丨泛型、集合

    Chapter. 10 泛型 10.1 泛型程序设计 泛型,指可以在类或方法中预支地使用未知的类型.泛型程序设计(Generic programming),意味着编写的代码可被很多不同类型的对象所重用 ...

随机推荐

  1. 使用 k8s 搭建 confluence 6.10.x 版本

    将公司中已有的 confluence 服务迁移到 k8s 集群中,需要保留当前已有的数据.整体需要分为如下几个步骤: 备份 mysql 数据 备份 confluence 安装目录 备份 conflue ...

  2. 【总结】git

    一.基本介绍 1.历史 git版本控制工具,Linus开发linux时最开始用的是bitkeeper,后来该公司取消了linux社区的免费使用权力,linus用一周的时间开发出第一版git 2.git ...

  3. SQL Server 列存储索引 第四篇:实时运营数据分析

    实时运营数据分析(real-time operational analytics )是指同时在同一张数据表上执行分析处理和业务处理.分析查询主要是对海量数据执行聚合查询,而事务主要是指对数据表进行少量 ...

  4. Django项目之打分系统

    打分系统 关注公众号"轻松学编程"了解更多. 项目GitHub地址:https://github.com/liangdongchang/VoteSys.git 1.开发需求 a. ...

  5. c100k

    sysctl -w fs.file-max=10485760 #系统允许的文件描述符数量10msysctl -w net.ipv4.tcp_rmem=1024 #每个tcp连接的读取缓冲区1k,一个连 ...

  6. Prometheus安装配置

    一.什么是Prometheus? Prometheus受启发于Google的Brogmon监控系统(相似的Kubernetes是从Google的Brog系统演变而来),从2012年开始由前Google ...

  7. 从比心APP源码的成功,分析陪玩系统源码应该如何开发

    提起游戏陪玩系统,相信大家都不陌生.作为一名骨灰级的手游玩家,小编对于陪玩系统源码也有些了解.在互联网络发展愈发迅速的今天,游戏产业在一中领域中脱颖而出,据统计,手机游戏用户已经达到5.29亿,较20 ...

  8. leetcode133:3sum-closest

    题目描述 给出含有n个整数的数组s,找出s中和加起来的和最接近给定的目标值的三个整数.返回这三个整数的和.你可以假设每个输入都只有唯一解. 例如,给定的整数 S = {-1 2 1 -4}, 目标值 ...

  9. Spring源码解析之BeanFactoryPostProcessor(一)

    BeanFactoryPostProcessor 在前面几个章节,笔者有介绍过BeanFactoryPostProcessor,在spring在解析BeanDefinition之后,正式初始化bean ...

  10. W3C中不同标准的含义

    学习CSS/HTML的过程中,当出现释义冲突时,W3C(万维网联盟)官网所陈列的技术标准是最核心的判断参考.但是新手在查阅W3C标准索引页面时,会发现同一个属性或者模型会出现多个不同的阶段规范展示结果 ...