Day09笔记
课程内容
1、继承中成员方法的关系和方法的重写
2、final
3、多态
4、抽象类
5、接口
继承剩余内容
继承中成员方法的关系
1、在子父类中,有不同名称的成员方法
在子类中,可以直接访问父类中定义的成员方法,也可以访问子类中定义的成员方法
2、在子父类中,有相同声明的方法(方法的返回值类型、方法名称、参数列表)
称为:方法重写(Override)
在子父类中,出现了一模一样的方法声明,但是方法的实现却各不相同
作用:在子类中,【如果】不想改变父类中定义过的方法声明,但是还想改变这个方法的实现内容。
3、方法重载和方法重写的比较:
重载:在同一个类中,方法名相同,参数列表不同,与返回值类型无关
重写:在子父类中,方法名相同,参数列表也相同,与返回值类型有关(相同)
4、方法重写的说明:
1、别称:覆写、覆盖、override
2、注解:@Override
作用:检查当前的方法是否在重写父类中的某个方法
3、在子类中重写父类的方法,只是在改变父类方法的内容,使用的还是父类中的方法定义:子类中被重写的这个方法,所属关系,属于父类的,只是子类重写了内容而已。
4、如果在子类中,还想访问父类的方法,使用super.方法名称();
代码示例
class Demo01_方法的重写 {
public static void main(String[] args) {
DayOne d = new DayOne();
d.singRedSong();
d.paoNiu();
System.out.println("--------");
d.test();
}
}
class DoubleRiver {
public void singRedSong() {
System.out.println("小小竹筏江中游");
}
public void paoNiu() {
System.out.println("通过唱歌搞定林夕合鸟女士");
}
}
class DayOne extends DoubleRiver {
@Override
public void paoNiu() {
System.out.println("霸王硬上弓");
}
public void test() {
this.paoNiu();
super.paoNiu();
}
}
重写的注意事项
1、私有的方法不能被重写
父类中的私有方法,在子类中根本就看不到,子类也继承不了父类中的私有成员,也就没有办法重写
在子类中,仍然可以定义一个和父类私有方法同名的方法,但是这不是对父类方法的重写,而是在子类中心定义了一个方法
2、方法在重写的时候,权限不能越来越小
工程上的原因:将来面向父类、面向接口编程,有这些方法的,代码运行的时候是运行子类的方法,方法没有了
记忆:子类重写父类,必须功能越来越强,权限不能越来越小
代码示例
class Demo02_重写的注意事项 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
class Fu {
public void test1() {
System.out.println("Fu111");
}
private void test2() {
System.out.println("Fu222");
}
public void test3() {
System.out.println("Fu333");
}
}
class Zi extends Fu {
@Override
public void test1() {
System.out.println("Zi111");
}
/*@Override//私有方法不能重写
public void test2() {
System.out.println("Zi222");
}
@Override//权限不能越写越小
private void test3() {
System.out.println("Zi333");
}*/
}
final关键字
概述
1、final:最后的、最终的、不能改变的
2、Java中,可以修饰类、方法、变量(局部变量和成员变量)
3、final修饰类:
该类不能被继承,类中的任何内容都无法改变,类中的成员方法,都不能被重写了
4、final修饰方法:
该方法不能被重写
5、final修饰变量:
变量就变成了常量,只能赋值一次
代码示例1
class Demo03_final修饰类 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
final class DoubleRiver {
public void singRedSong() {
System.out.println("小小竹筏江中游");
}
public void paoNiu() {
System.out.println("通过唱歌搞定林夕合鸟女士");
}
}
//编译报错,final修饰之后,不能被继承
class DayOne extends DoubleRiver {
}
代码示例2
class Demo04_final修饰方法 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
class DoubleRiver {
public void singRedSong() {
System.out.println("小小竹筏江中游");
}
public final void paoNiu() {
System.out.println("通过唱歌搞定");
}
}
class DayOne extends DoubleRiver {
//编译报错,因为父类中的paoNiu是final的,不能被重写
public void paoNiu() {
}
public void singRedSong() {
}
}
代码示例3
class Demo05_final修饰变量 {
public static void main(String[] args) {
final int a = 10;
System.out.println(a);
a = 20;//编译报错,因为a是最终变量,只能赋值一次
System.out.println(a);
}
}
final修饰局部变量和成员变量的注意事项
1、final修饰局部变量的注意事项:
final修饰哪个变量,哪个变量不能改变
final修饰基本数据类型,变量中的值不能改变
final修饰引用数据类型,变量中的地址值不能改变
2、final修饰成员变量的注意事项:
final修饰成员变量,需要注意初始化时机,原因:
成员变量有很多的初始化步骤,所以有可能在我们没有手动给变量赋值之前,变量就已经赋值很多次了
时机:
在构造方法结束之前,必须给final修饰的成员变量赋值
final修饰的成员变量,没有默认初始化
结论:final修饰的成员变量,只能有显式初始化、构造方法初始化
代码示例1
class Demo06_final修饰局部变量 {
public static void main(String[] args) {
Person p = new Person();
p.name = "zhangsan";
p.name = "lisi";
final Person p1 = new Person();
p1.name = "zhangsan";
System.out.println(p1.name);
p1.name = "lisi";
System.out.println(p1.name);
//p1 = new Person();//编译报错,因为p1是最终变量,不能修改内容
}
}
class Person {
String name;
}
代码示例2
class Demo07_final修饰成员变量 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
class Person {
//final String name;//编译报错,没有在有限的两次机会中初始化该变量
final int age = 6;
final String sex;
public Person() {
sex = "m";
}
/*public Person(int age) {
}*///编译报错,如果调用该构造方法,没有初始化sex属性
/*public void setSex(String sex) {
this.sex = sex;
}*///编译报错,因为已经确保最终变量在创建对象时,已经分配值
}
多态
多态的概述
1、多态:事物的多种状态,polymorphic
对象的多态性:同一个对象,可以有不同的名称,有不同的类型的引用指向它
本质:同一个对象有不同的名称和描述
类型的多态性:同一个类型的引用,将来可以指向不同的子类对象
本质:同一个名称可以描述多种具体的事物
2、多态的前提:
1、要有子父类(接口和实现类)的继承关系(实现关系)
2、要有方法的重写
3、父类的引用指向子类的对象
代码示例
class Demo08_多态概述 {
public static void main(String[] args) {
Man m = new Man();
m.speak();
Person p = new Man();
p.speak();
}
}
class Person {
public void speak() {
System.out.println("我是人");
}
}
class Man extends Person {
@Override
public void speak() {
System.out.println("我是大男人");
}
}
在多态中成员变量的访问特点
1、编译看左边,运行看左边
2、在编译阶段,使用父类的引用访问某个成员变量,检查在引用所属的类型中(赋值符号左边的类型)是否有该变量的定义,如果有,编译成功,如果没有就编译失败。
3、在运行阶段,使用父类的引用访问某个成员变量,访问的还是引用所属的类中(赋值符号左边的类型)对该变量的赋值
代码示例
class Demo09_多态中成员变量的访问特点 {
public static void main(String[] args) {
Person p = new Man();
System.out.println(p.name);
}
}
class Person {
//String name = "张三";
}
class Man extends Person {
String name = "张三丰";
}
在多态中成员方法的访问特点
1、编译看左边,运行看右边
2、使用父类的引用访问了某个成员方法,在编译阶段,检查引用所属的类型中(赋值符号左边的类型),是否有该方法的定义,如果有,编译成功,如果没有,就编译失败
3、使用父类的引用访问某个成员方法,在运行阶段,运行的内容是对象所属的类型(赋值符号右边的类型)重写过的内容。
代码示例
class Demo10_多态中成员方法的访问特点 {
public static void main(String[] args) {
Person p = new Man();
p.speak();
Person p1 = new Woman();
p1.speak();
}
}
class Person {
public void speak() {
System.out.println("我叫张三");
}
}
class Man extends Person {
public void speak() {
System.out.println("我叫张三丰");
}
}
class Woman extends Person {
public void speak() {
System.out.println("我是张小凤");
}
}
多态中静态方法的访问特点
1、编译看左边,运行看左边
2、编译时,要看【=】左边的引用所属的类型(父类)是否有该方法的定义,如果有就编译成功,如果没有,就编译失败
3、运行时,要看【=】右边的引用所属的类型(父类)究竟是如何实现该方法的,运行的是父类中方法的实现
4、静态理解总结:
静态变量:存储在类的字节码中,被所有该类对象共享,这个变量的值不会随着对象的不同,而又不同的值,都是相同的值,称为“静态”
静态方法:只会引用所属的父类,决定运行内容,不会随着对象的不同而运行不同的方法实现,称为“静态方法”
代码示例
class Demo11_多态中静态方法的访问特点 {
public static void main(String[] args) {
Person p = new Man();
//父类中定义了speak这个方法,如果是子类重写了这个方法,那么在父类调用这个方法的时候,就应该走子类的实现
//而运行结果是父类的内容,因此说明子类没有重写这个方法,而是新定义了一个方法
p.speak();
Person p1 = new Woman();
p1.speak();
}
}
class Person {
public static void speak() {
System.out.println("我是张三");
}
}
class Man extends Person {
//@Override //静态方法没有重写的概念
public static void speak() {
System.out.println("我是张三丰");
}
}
class Woman extends Person {
public static void speak() {
System.out.println("我是张小凤");
}
}
超人案例
代码示例
class Demo12_超人案例 {
public static void main(String[] args) {
Man m = new SuperMan();
System.out.println(m.name);
m.谈生意();
//向下转型:恢复原本的访问范围
SuperMan sm = (SuperMan)m;
sm.fly();
}
}
class Man {
String name = "Mike";
public void 谈生意() {
System.out.println("谈一谈不同的小生意");
}
}
class SuperMan extends Man {
String name = "Spider";
@Override
public void 谈生意() {
System.out.println("谈的是几个亿的大单子");
}
public void fly() {
System.out.println("到处飞着救人");
}
}
多态的内存理解
引用类型的向上向下转型
1、向上转型:多态的体现
父类的引用指向了子类的对象
以前:子类的引用 = 子类的对象
现在:父类的引用 = 子类的对象
本质:从概念上说,把概念扩大了,但是从功能和数据说,把访问范围缩小了
2、向下转型:
本质:把曾经扩大的概念进行恢复;把曾经缩小的访问范围恢复
前提:曾经向上转型过
格式:
子类类型 子类引用名称 = (子类类型)父类引用名称;
多态的好处
1、提高了代码的可扩展性
同一个类型的引用,可以有不同的子类对象作为实现
2、在方法的形式参数中,使用父类类型的引用,将来在调用方法的时候,传入的实际参数可以是这个父类的所有子类的对象。
3、不在方法的形式参数中,使用父类类型的引用,指向的对象,来源非常广泛的(不仅仅是new出来的),可以是通过反射的方式获取的,可以是通过文件读取获取的,可以是网络中传过来的,数据库中读取的。以上方式,都是程序员在写代码的时候,不知道对象具体的子类类型,仍然可以使用父类的引用指向他们,将来根据具体子类的不同,仍然可以运行不同的代码。
代码示例
import java.io.BufferedReader;
import java.io.FileReader;
class Demo13_榨汁机案例 {
public static void main(String[] args) throws Exception {
JuiceMachine jm = new JuiceMachine();
/*Apple a = new Apple();
jm.makeJuice(a);
Orange o = new Orange();
jm.makeJuice(o);*/
BufferedReader br = new BufferedReader(new FileReader("config.txt"));
String className = br.readLine();
Class c = Class.forName(className);
//Object也是最顶层的父类,指向了我们不知道的具体类型的子类对象
Object obj = c.newInstance();
//父类的Fruit类型的对象,指向了我们不知道具体类型的子类对象
Fruit f = (Fruit)obj;
jm.makeJuice(f);
}
}
class JuiceMachine {
public void makeJuice(Fruit f) {//Fruit f = new Orange();
f.flow();
}
}
class Fruit {
public void flow() {
}
}
class Apple extends Fruit {
public void flow() {
System.out.println("流出苹果汁");
}
}
class Orange extends Fruit {
public void flow() {
System.out.println("流出橘子汁");
}
}
class WaterMelon extends Fruit {
public void flow() {
System.out.println("流出西瓜汁");
}
}
抽象类
抽象方法
1、抽象:抽取像的,相同的相似的内容出来
2、抽象方法:
在子类中,对同一个方法,不同的子类有不同的实现,不同子类中的这些方法,就只有方法声明是相同的,所以把方法名称抽取到父类中,就是抽象方法。
抽象方法:就是只有方法声明,没有方法实现的方法
3、定义格式:
1、没有方法体,只有方法实现,连方法体的大括号都没有,通过分号结束方法
2、在方法声明上,需要加一个abstract关键字来说明这个方法是抽象方法
代码示例
class Demo14_抽象方法 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
class Employee {
public abstract void work();
}
class Coder extends Employee {
public void work() {
System.out.println("敲代码");
}
}
class Tester extends Employee {
public void work() {
System.out.println("软件测试");
}
}
抽象类
1、可以定义抽象方法的类,就是抽象类
2、定义格式:
abstract class 类名 {
抽象方法;
}
代码示例
class Demo15_抽象类 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
abstract class Employee {
public abstract void work();
}
class Coder extends Employee {
public void work() {
System.out.println("敲代码");
}
}
class Tester extends Employee {
public void work() {
System.out.println("软件测试");
}
}
抽象类的特点
1、抽象类和抽象方法都需要使用abstract关键字声明
abstract class 类名 {}
public abstract 返回值类型 方法名称() {}
2、抽象类和抽象方法的关系:
1、抽象类中,未必有抽象方法
2、抽象方法所在的类,一定是抽象类
3、抽象类不能实例化(创建对象)
抽象类中有抽象方法,如果能创建对象,就会调用没有意义的方法
只能定义子类,重写(实现)抽象方法之后,使用子类来创建对象
4、抽象类的子类:
1、如果子类没有把父类中的所有抽奖方法都重写,那么这个子类就还是一个抽象类
2、如果子类重写了父类中所有的抽象方法,那么子类就变成了一个具体类。
代码示例
class Demo16_抽象类的特点 {
public static void main(String[] args) {
Coder c = new Coder();
}
}
abstract class Employee {
public void test() {}
public abstract void test2();
}
class Coder extends Employee {
}
抽象类中成员的特点
1、成员变量的特点:
既可以是变量、也可以是常量
但是不能被抽象
2、构造方法的特点:
抽象类中,有构造方法
用于子类创建对象的时候,要访问父类的构造方法
一个类中是否需要定义构造方法,不是取决于这个类是否可以创建对象,而是取决于该类是否可以定义成员变量
3、成员方法的特点:
可以是抽象方法:强制让子类重写这个抽象方法
也可以是非抽象方法:用于给子类继承
代码示例
class Demo17_抽象类中成员的特点 {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
abstract class Employee {
String name;
final int age = 65;
}
员工类案例完善
代码示例
class Demo18_员工类案例 {
/*
定义程序员类和项目经理类
程序员类:属性(姓名、工号、工资)、方法(工作:敲代码)
项目经理类:属性(姓名、工号、工资、奖金)、方法(工作:项目进度控制
*/
public static void main(String[] args) {
//Coder c = new Coder("李白", "lb666", 20000.0);
//抽象父类的引用,指向具体子类的对象:抽象类的多态
Employee e1 = new Coder("李白", "lb666", 20000.0);
System.out.println(e1.getName() + "..." + e1.getId() + "..." + e1.getSalary());
e1.work();
//Manager m = new Manager("秦始皇", "qsh888", 0, 999999999);
Employee e2 = new Manager("秦始皇", "qsh888", 0, 999999999);
//编译报错,因为在父类Employee中没有getBonus这个方法
//System.out.println(e2.getName() + "..." + e2.getId() + "..." + e2.getSalary() + "..." + e2.getBonus());
System.out.println(e2.getName() + "..." + e2.getId() + "..." + e2.getSalary());
e2.work();
}
}
abstract class Employee {
private String name;
private String id;
private double salary;
public Employee() {}
public Employee(String name, String id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getSalary() {
return salary;
}
public abstract void work();
}
class Coder extends Employee {
public Coder() {}
public Coder(String name, String id, double salary) {
super(name, id, salary);
}
public void work() {
System.out.println("敲代码");
}
}
class Manager extends Employee {
private int bonus;
public Manager() {}
public Manager(String name, String id, double salary, int bonus) {
super(name, id, salary);
this.bonus = bonus;
}
public void setBonus(int bonus) {
this.bonus = bonus;
}
public int getBonus() {
return bonus;
}
public void work() {
System.out.println("项目进度控制");
}
}