目录
第八课-----继承
一、继承的含义
继承描述的是事物之间的所属关系,这种关系是:`is-a` 的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
继承:就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
继承的好处
- 提高代码的复用性(减少代码冗余,相同代码重复利用)。
- 使类与类之间产生了关系。
二、继承的格式
通过 `extends` 关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
需要注意:
- Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。
- 继承实际上是子类相同的属性和行为可以定义在父类中,子类特有的属性和行为由自己定义,这样就实现了相同属性和行为的重复利用,从而提高了代码复用。
- 子类继承父类,就可以直接得到父类的成员变量和方法。
- 子类不能继承父类的构造方法。
- 子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
- 子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。如果此时想访问父类成员变量我们可以使用super关键字。
- super代表的是父类对象的引用,this代表的是当前对象的引用。
- 如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
三、方法重写
概念:子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
使用场景与案例
发生在子父类之间的关系。
子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方 法。
@Override:注解,重写注解校验!
这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
注意事项
- 1. 方法重写是发生在子父类之间的关系。
- 2. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 3. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
第九课-----多态
一、多态的形式
多态是继封装、继承之后,面向对象的第三大特性。
多态是出现在继承或者实现关系中的。
多态体现的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
多态的前提:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
多态的使用场景:
- 当一个方法的形参是一个类,我们可以传递这个类所有的子类对象。
- 当一个方法的形参是一个接口,我们可以传递这个接口所有的实现类对象。
- 而且多态还可以根据传递的不同对象来调用不同类中的方法。
二、多态的定义和前提:
多态: 是指同一行为,具有多个不同表现形式。
例如,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。
前提:
- 1. 有继承或者实现关系
- 2. 方法的重写【意义体现:不重写,无意义】
- 3. 父类引用指向子类对象【格式体现】
多态的运行特点:
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
代码示例:
Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();
多态的弊端:
我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了。
多态的写法就无法访问子类独有功能了。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
回顾基本数据类型转换:
- 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
- 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
原因:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。所以子类范围小可以直接自动转型给父类类型的变量。
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
三、 instanceof关键字
为了避免ClassCastException的发生,Java提供了 `instanceof` 关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
所以,转换前,我们最好先做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
第十课-----抽象类与接口
一、抽象类概述
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话说,父类可能知道子类应该有哪个功能,但是功能具体怎么实现父类是不清楚的(由子类自己决定),父类只需要提供一个没有方法体的定义即可,具体实现交给子类自己去实现。我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
抽象方法: 没有方法体的方法。
抽象类:包含抽象方法的类。
二、抽象类
如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
定义格式:
abstract class 类名字 {
}
//代码举例:
public abstract class Animal {
public abstract void run();
}
抽象类的使用:
要求:继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。
抽象类的特征:抽象类的特征总结起来可以说是有得有失
有得:抽象类得到了拥有抽象方法的能力。
有失:抽象类失去了创建对象的能力。
其他成员(构造方法,实例方法,静态方法等)抽象类都是具备的。
抽象类的细节:
- 1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
- 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
- 2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
- 3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
- 4. 抽象类的子类,必须重写抽象父类中**所有的**抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
- 理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
- 5. 抽象类存在的意义是为了被子类继承。
- 理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。
抽象类存在的意义:
抽象类存在的意义是为了被子类继承,否则抽象类将毫无意义。抽象类可以强制让子类,一定要按照规定的格式进行重写。
三、接口
概述
我们已经学完了抽象类,抽象类中可以用抽象方法,也可以有普通方法,构造方法,成员变量等。那么什么是接口呢?接口是更加彻底的抽象,JDK7之前,包括JDK7,接口中全部是抽象方法。接口同样是不能创建对象的。
定义格式
//接口的定义格式:
interface 接口名称{
// 抽象方法
}
// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”
接口成分的特点:在JDK7,包括JDK7之前,接口中的只有包含:抽象方法和常量
抽象方法:
使用`abstract` 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
//修饰符 abstract 返回值类型 方法名 (参数列表);
//代码举例:
public abstract void run();
注意:接口中的抽象方法默认会自动加上public abstract修饰程序员无需自己手写!!
按照规范:以后接口中的抽象方法建议不要写上public abstract。因为没有必要啊,默认会加上。
常量:
在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。
实现接口的概述:
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 ` implements`关键字。
实现接口的格式:
/**接口的实现:
在Java中接口是被实现的,实现接口的类称为实现类。
实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{
}
从上面格式可以看出,接口是可以被多实现的。
类实现接口的要求和意义:
- 1. 必须重写实现的全部接口中所有抽象方法。
- 2. 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
- 3. 意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。
接口与接口的多继承:
Java中,接口与接口之间是可以多继承的:也就是一个接口可以同时继承多个接口。大家一定要注意:
类与接口是实现关系
接口与接口是继承关系
接口继承接口就是把其他接口的抽象方法与本接口进行了合并。
案例演示:
public interface Abc {
void go();
void test();
}
/** 法律规范:接口*/
public interface Law {
void rule();
void test();
}
*
* 总结:
* 接口与类之间是多实现的。
* 接口与接口之间是多继承的。
* */
public interface SportMan extends Law , Abc {
void run();
}
第十一课-----内部类与代码块
一、什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
什么时候使用内部类:
一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用。
1. 人里面有一颗心脏。
2. 汽车内部有一个发动机。
3. 为了实现更好的封装性。
二、内部类的分类
按定义的位置来分:
- 1. 成员内部内,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
- 2. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
- 3. 局部内部类,类定义在方法内
- 4. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。
成员内部类特点:
无static修饰的内部类,属于外部类对象的。
宿主:外部类对象。
内部类的使用格式:
外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
获取成员内部类对象的两种方式:
方式一:外部直接创建成员内部类的对象
外部类.内部类 变量 = new 外部类().new 内部类();
方式二:在外部类中定义一个方法提供内部类的对象
编写成员内部类的注意点:
- 1. 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
- 2. 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
- 3. 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。
注意:
- 内部类被private修饰,外界无法直接获取内部类的对象,只能通过3.3节中的方式二获取内部类的对象。
- 被其他权限修饰符修饰的内部类一般用3.3节中的方式一直接获取内部类的对象。
- 内部类被static修饰是成员内部类中的特殊情况,叫做静态内部类下面单独学习。
- 内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上。
静态内部类特点:
静态内部类是一种特殊的成员内部类。
有static修饰,属于外部类本身的。
总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。
拓展1:静态内部类可以直接访问外部类的静态成员。
拓展2:静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。
拓展3:静态内部类中没有银行的Outer.this。
静态内部类对象的创建格式:
外部类.内部类 变量 = new 外部类.内部类构造器;
调用方法的格式:
- 调用非静态方法的格式:先创建对象,用对象调用
- 调用静态方法的格式:外部类名.内部类名.方法名();
局部内部类 :定义在方法中的类。
定义格式:
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
class 内部类 {
// 成员变量
// 成员方法
}
}
}
匿名内部类 :是内部类的简化写法。他是一个隐含了名字的内部类。开发中,最常用到的内部类就是匿名内部类了。
格式:
new 类名或者接口名() {
重写方法;
};
包含了:
- 继承或者实现关系
- 方法重写
- 创建对象
所以从语法上来讲,这个整体其实是匿名内部类对象。
如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用是为了简化代码。
匿名内部类前提和格式:
匿名内部类必须继承一个父类或者实现一个父接口。
匿名内部类格式:
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
三、代码块
在Java中,使用{}括起来的代码被称为代码块(Code block),根据其位置和声明的不同,可以分为:
- 局部代码块。
- 构造代码块。
- 同步代码块。
- 静态代码块。
局部代码块:
在方法中出现,可以限定变量生命周期,及早释放,提高内存利用率。
构造代码块:
在类中方法外出现,每次调用构造方法都会执行,并且在构造方法前执行。
Tip:
构造代码块与构造函数的区别:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。
也就是说,构造代码块中定义的是不同对象共性的初始化内容。
静态代码块:
在类中方法外出现,并加上static修饰,常用于给类进行初始化,在加载的时候就执行,并且静态代码块执行一次。