JavaSE 学习笔记03丨继承、接口、多态、内部类
Chapter. 5 继承
继承作为面向对象的三大特征之一,它是多态的前提。它主要解决的问题是共性抽取。
Java中的继承,是单继承、多级继承的。
已存在的类,被称为超类、基类、父类(parent class);新类,被称为子类(subclass)、派生类。
每一个子类的直接父亲是唯一的,但一个父亲可拥有多个子类。子类比父类拥有的功能更加丰富。
5.1 继承的格式
定义父类的格式(普通类的定义)
public class 父类名称{
//...
}
定义子类的格式
public class 子类名称 extends 父类名称{
//...
}
5.2 继承中成员的访问特点
优先使用本类的成员变量与成员方法,若没有,则往上寻找。
当局部变量名、本类与父类的成员变量名均相同时,可以用以下语法进行区分使用。
- 局部变量:直接写该变量名
- 本类的成员变量:
this.成员变量名
- 父类的成员变量:
super.成员变量名
5.2.1 覆写(Override)
由于,父类中的有些方法对于子类并不一定适用。因此需要一个新的方法来覆盖父类中的这个方法。
覆写(或称覆盖、重写),发生在继承关系当中,方法的名称一样,参数列表也一样。
区分:重载(Overload),方法的名称一样,参数列表不一样。
特点:覆写创建的是子类对象,则优先使用子类方法。
@Override
:注解(annotation),写在方法前面,用于检测是否为有效的正确覆写。(属于安全检测手段)
注意事项:
子类方法的返回值必须小于等于父类方法的返回值范围。
java.long.Object
类是所有类的公共最高父类(祖宗类)子类方法的权限必须大于等于父类方法的权限修饰符。
public
>protected
>(default)
>private
(default)
代表什么都不写,直接留空。若父类抛出多个异常,子类覆写父类方法时,应该抛出 与父类相同的异常 或者 父类异常的子类 或者 不抛出异常;若父类没有抛出异常,子类覆写时不能抛出异常,此时子类产生该异常,只能捕获处理而不能声明抛出。
/* 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 继承中构造方法的访问特点
子类构造方法当中有一个默认隐含的
super()
调用,所以一定是先调用父类无参构造方法,后执行子类的构造方法。子类构造中可通过
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("子类构造方法!");
}
}
super
的父类构造调用,必须为子类构造方法中的第一个且只能为第一条语句。因此,super()
和this()
这两种构造调用,不能同时使用。
5.4 抽象类
如果父类当中的方法不确定如何进行{}
方法体实现,那么这应该是一个抽象方法,相应地,这个父类也必然是抽象类。人们只将它作为派生其他类的基类,而不作为想使用的特定的实例类。
抽象方法:在方法返回值之前加上
abstract
关键字,然后去掉大括号,直接分号结束。抽象类:抽象方法所在的类,必须是抽象类。需在
class
之前利用abstract
关键字去声明。
public abstract class Animal{ //抽象类
public abstract void eat(); //抽象方法
public void normalMethod(){ //普通的成员方法
//...
}
}
注意事项:
不能创建抽象类对象,必须用一子类去继承抽象父类。
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
抽象类中,不一定包含抽象方法,但是具有抽象方法的类必定是抽象类。
子类必须覆写抽象父类当中所有的抽象方法(除非该子类也是抽象类),即子类中,去掉抽象方法的
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("天上飞");
}
}
实现类可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。
继承默认方法来定义实现类:
public class Animal implements LiveAble{
//继承,什么都不用写,直接调用
}
重写默认方法来定义实现类:
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{
//覆盖所有抽象方法
}
注意事项:
- 若实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
- 若实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆写。(也就说,不能直接继承这些默认方法)
- 一个类中若直接父类当中的方法,与接口当中的默认方法产生了冲突,优先使用父类当中的方法。
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");
}
}
注意事项:
- 如果父接口中的默认方法有重名的,那么子接口需要重写一次。
- 子接口重写默认方法时,
default
关键字可以保留。但是,子类重写默认方法时,default
关键字不可以保留。
Chapter 7. 多态
多态:指同一行为,具有多个不同表现形式。
"is-a"规则的另一种表述法是置换法则,它表明程序中出现超类对象的任何地方都可以用子类对象置换。在Java中,对象变量是多态的,一个Employee
父类变量既可引用一个Employee
类对象,也可以引用一个Employee
类的任何一个子类的对象。
多态的好处在于,使得程序编写得更简单,并具有良好的拓展。
7.1 多态的体现
定义格式为:
父类名称 变量名 = new 子类对象; // Parent a = new Son();
变量名.方法名(); //a.method();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
7.2 多态中访问成员变量的两种方式
通过对象名称直接访问成员变量,看
new
等号的左边是谁,优先用谁,没有则向上找。如果子类定义个
age
成员变量,但父类没有定义,则编译器无法找到age
变量通过成员方法间接访问成员变量,看该方法属于谁,优先用谁,没有则向上找。
子类没有覆盖重写,则向父类找;子类如果覆盖重写,则向子类找。
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
二、使用
间接方式:在外部类的方法中,使用内部类;然后
main
只是调用外部类的方法;直接方式:
外部类名称.内部类名称 对象名 = 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丨继承、接口、多态、内部类的更多相关文章
- JavaSE 学习笔记02丨对象与类
Chapter 4. 对象与类 4.1 面向对象程序设计概述 面向对象程序设计(简称OOP),是当今主流程序设计范型.面向对象的程序是由对象组成的,每个对象(来自于标准库或自定义的)包含对用户公开的特 ...
- (C/C++学习笔记) 十八. 继承和多态
十八. 继承和多态 ● 继承的概念 继承(inheritance): 以旧类为基础创建新类, 新类包含了旧类的数据成员和成员函数(除了构造函数和析构函数), 并且可以派生类中定义新成员. 形式: cl ...
- [core java学习笔记][第六章接口与内部类]
接口域内部类 接口 描述类具有什么功能,不给出具体实现. 内部类 用于设计协作关系的类集合 代理 实现任意接口的对象. 6.1 接口 接口声明 public interface Comparable ...
- JavaSE 学习笔记01丨开发前言与环境搭建、基础语法
本蒟蒻学习过C/C++的语法,故在学习Java的过程中,会关注于C++与Java的区别.开发前言部分,看了苏星河教程中的操作步骤.而后,主要阅读了<Java核心技术 卷1 基础知识>(第8 ...
- 《java JDK7 学习笔记》之继承与多态
1.面向对象中,子类继承父类,避免重复的行为定义,不过并非为了避免重复定义行为就使用继承.应该正确判断使用继承的时机及继承之后灵活的运用多态,才是学习继承时的重点. 2.程序代码重复在程序设计上,就是 ...
- Java学习笔记——封装、继承和多态
先说说封装: 用new 一条狗来举个例子: public class Dog { //私有化字段 private String name; private int age; //无参构造 Dog(){ ...
- JavaSE 学习笔记07丨IO流
Chapter 13. IO流 13.1 File类 java.io.File类是文件(file)和目录(文件夹)(directory)路径名(path)的抽象表示,主要用于文件和目录的创建.查找和删 ...
- JavaSE 学习笔记06丨并发
Chapter 12. 并发 12.1 并发与并行 并发:指两个或多个事件在同一个时间段内发生. 并行:指两个或多个事件在同一时刻发生(同时发生). 在操作系统中,并发指的是在一段时间内宏观上有多个程 ...
- JavaSE 学习笔记05丨泛型、集合
Chapter. 10 泛型 10.1 泛型程序设计 泛型,指可以在类或方法中预支地使用未知的类型.泛型程序设计(Generic programming),意味着编写的代码可被很多不同类型的对象所重用 ...
随机推荐
- 使用 k8s 搭建 confluence 6.10.x 版本
将公司中已有的 confluence 服务迁移到 k8s 集群中,需要保留当前已有的数据.整体需要分为如下几个步骤: 备份 mysql 数据 备份 confluence 安装目录 备份 conflue ...
- 【总结】git
一.基本介绍 1.历史 git版本控制工具,Linus开发linux时最开始用的是bitkeeper,后来该公司取消了linux社区的免费使用权力,linus用一周的时间开发出第一版git 2.git ...
- SQL Server 列存储索引 第四篇:实时运营数据分析
实时运营数据分析(real-time operational analytics )是指同时在同一张数据表上执行分析处理和业务处理.分析查询主要是对海量数据执行聚合查询,而事务主要是指对数据表进行少量 ...
- Django项目之打分系统
打分系统 关注公众号"轻松学编程"了解更多. 项目GitHub地址:https://github.com/liangdongchang/VoteSys.git 1.开发需求 a. ...
- c100k
sysctl -w fs.file-max=10485760 #系统允许的文件描述符数量10msysctl -w net.ipv4.tcp_rmem=1024 #每个tcp连接的读取缓冲区1k,一个连 ...
- Prometheus安装配置
一.什么是Prometheus? Prometheus受启发于Google的Brogmon监控系统(相似的Kubernetes是从Google的Brog系统演变而来),从2012年开始由前Google ...
- 从比心APP源码的成功,分析陪玩系统源码应该如何开发
提起游戏陪玩系统,相信大家都不陌生.作为一名骨灰级的手游玩家,小编对于陪玩系统源码也有些了解.在互联网络发展愈发迅速的今天,游戏产业在一中领域中脱颖而出,据统计,手机游戏用户已经达到5.29亿,较20 ...
- leetcode133:3sum-closest
题目描述 给出含有n个整数的数组s,找出s中和加起来的和最接近给定的目标值的三个整数.返回这三个整数的和.你可以假设每个输入都只有唯一解. 例如,给定的整数 S = {-1 2 1 -4}, 目标值 ...
- Spring源码解析之BeanFactoryPostProcessor(一)
BeanFactoryPostProcessor 在前面几个章节,笔者有介绍过BeanFactoryPostProcessor,在spring在解析BeanDefinition之后,正式初始化bean ...
- W3C中不同标准的含义
学习CSS/HTML的过程中,当出现释义冲突时,W3C(万维网联盟)官网所陈列的技术标准是最核心的判断参考.但是新手在查阅W3C标准索引页面时,会发现同一个属性或者模型会出现多个不同的阶段规范展示结果 ...