Java第七课
一、继承 extends
- 子类继承父类:在每一个子类对象中都有独有的父类对象
- 继承是一种高级封装
- 父类哪些东西不能继承
- 私有的 private
- 构造方法
- 非同包父类受保护类型 protected
- 通过访问修饰符和包进行访问权限控制
好处
提高代码的复用性。
类与类之间产生了关系,是多态的前提。
class 父类 { ...
}
class 子类 extends 父类 { ...
}
/*
* 定义员工类Employee,做为父类
* */
class Employee {
String name; // 定义name属性
// 定义员工的工作方法
public void work() {
System.out.println("尽心尽力地工作");
}
}
/* *
* 定义讲师类Teacher 继承 员工类Employee
* */
class Teacher extends Employee{
// 定义一个打印name的方法
public void printName() {
System.out.println("name=" + name);
}
}
/* *
定义测试类
*/
public class ExtendDemo01 {
public static void main(String[] args) {
// 创建一个讲师类对象
Teacher t = new Teacher();
// 为该员工类的name属性进行赋值
t.name = "小明";
// 调用该员工的printName()方法
t.printName();// name = 小明
// 调用Teacher类继承来的work()方法
t.work(); // 尽心尽力地工作
}
}
二、权限修饰符
项目 | 类内 | 同包 | 不同包子类 | 其他 |
---|---|---|---|---|
private | √ | |||
(default) | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
三、static
- 构造方法<普通代码块<静态代码块
- 被static修饰的属性和方法称之为类属性和类方法、无需对象,通过类名就可以调用,所以static违背了面向对象的原则,还会占用大量内存。
- 原因:因为JVM在启动时static就会创建,所以不用创建对象,他的存活周期和JVM一样长
- 当一个属性或方法使用频次高且当前工程中许多类需要时要用static,否则少用。
- 静态方法只能访问静态属性和静态方法
类方法的执行流程
public class Father {
public int a = 1;
public static int b = 2;
static {
System.out.println("静态代码块");
}
public Father() {
System.out.println("构造代码块");
}
{
System.out.println("普通代码块");
}
}
public class Test {
public static void main(String[] args) {
Father father;
System.out.println(Father.b);
}
}
结果
静态代码块
2
public class Test {
public static void main(String[] args) {
Father father=new Father();
System.out.println(father.a);
System.out.println(Father.b);
}
}
结果
静态代码块
普通代码块
构造代码块
1
2
子类与父类的方法执行流程
public class Father {
static {
System.out.println("父类静态代码块");
}
public Father() {
System.out.println("父类构造代码块");
}
{
System.out.println("父类普通代码块");
}
}
public class Son extends Father {
static {
System.out.println("子类静态代码块");
}
public Son() {
System.out.println("子类构造代码块");
}
{
System.out.println("子类普通代码块");
}
}
public class Test {
public static void main(String[] args) {
new Son();
}
}
结果
父类静态代码块
子类静态代码块
父类普通代码块
父类构造代码块
子类普通代码块
子类构造代码块
四、this和super
- super :代表父类的存储空间标识(可以理解为父亲的引用)。
- this :代表当前对象的引用(谁调用就代表谁)。
this.成员变量 ‐‐ 本类的
super.成员变量 ‐‐ 父类的
this.成员方法名() ‐‐ 本类的
super.成员方法名() ‐‐ 父类的
五、重载(overload)和重写(override)
- 方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返 回值类型无关。 参数列表:个数不同,数据类型不同,顺序不同。 重载方法调用:JVM通过方法的参数列表,调用不同的方法。
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
- 如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效 果,也称为重写或者复写。声明不变,重新实现。
public class Person {
public void show(){
System.out.println("Person的show方法");
}
}
public class Teacher extends Person {
@Override
public void show(){
System.out.println("重写Person父类的show方法");
}
}
测试类
public class Test7 {
public static void main(String[] args) {
Teacher teacher=new Teacher();
teacher.show();
}
}
结果
重写Person父类的show方法
六、抽象类 abstract
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。 - 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。 - 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。 - 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
public abstract class Person {
public abstract void show();
}
子类重写父类的抽象方法,除非子类也是抽象方法。
public class Teacher extends Person {
@Override
public void show(){
System.out.println("重写Person父类的show方法");
}
}
七、接口 interface
- 方法默认为公共的抽象方法(public abstract)
- 属性默认为公共的静态的常量(public static
- 子类必须实现父接口中所有的抽象方法,除非其子类或接口也是抽象类
接口和接口之间的继承用extends - 接口不能直接实化
作用:
* 同步开发
* 隐藏实现
父接口
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("睡觉");
}
}
测试类
public class InterfaceDemo {
public static void main(String[] args) {
//创建子类对象
Animal animal=new Animal();
//调用实现后的方法
animal.eat();
animal.sleep();
}
}
结果
吃东西
睡觉
八、多态
- 自动类型转换
- 父类引用指向子类对象
多态体现的格式:
- 强制类型转换
- 子类类型 变量名 = (子类类型) 父类变量名
- . 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】 一个父类有多个子类,在构建方法时以父类的引用作为参数,在调用方法时,传入不同子类对象实现
定义父类:
public abstract class Animal {
public abstract void eat();
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Animal a1 = new Cat();
// 调用的是 Cat 的 eat
a1.eat();
// 多态形式,创建对象
Animal a2 = new Dog();
// 调用的是 Dog 的 eat
a2.eat();
}
}
多态的好处
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。代码如下:
定义父类
public abstract class Animal {
public abstract void eat();
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
在这里插入代码片
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当 然可以把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致, 所以showAnimalEat完全可以替代以上两方法。 不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用 showAnimalEat都可以完成。
所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。
转型的异常
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse();// 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了 Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。 为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:
//如果变量属于该数据类型,返回true。 如果变量不属于该数据类型,返回false
变量名 instanceof 数据类型
所以,转换前,我们好先做一个判断,代码如下:
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
}
}