day12复习
Java-day13学习笔记
一、继承
1.1 继承的注意事项
1. 子类可以继承父类的所有成员(私有的成员不能被访问)
2. 父类的构造方法可以被继承吗?不可以
因为子类的内容肯定比父类的内容多,所以不能继承
因为父类的构造方法名和子类的构造方法名不一致,所以不能被继承
3. 不要为了部分功能去继承
1.2 继承的特点
1. Java可以单继承
class Father {}
class Son extends Father {}
2. Java不可以多继承
class Father {}
class Mother {}
class Son extends Father, Mother {} //错误
class Son extends Father extends Mother {} //错误
3. Java可以多层继承(继承体系)
class GrandFather {}
class Father extends GrandFather {}
class Son extends Father {}
1.3 继承中变量之间的关系
1. 如果子类和父类中变量名不同的话,直接根据不同的名字访问即可
2. 如果子类和父类中变量名相同的话,那么有如下访问规则:
先在子类的局部范围内去找,如果找到就使用
如果找不到就在子类的成员范围内去找,如果找到就使用
如果找不到就在父类的成员范围内去找,如果找到就使用
如果找不到就报错(在不考虑父类也有父类的情况),父类的局部范围不考虑(因为根本访问不到)
1.4 super关键字
super可以类比this来学习
this:哪个对象调用方法this就代表哪个对象
super:代表了指向父类的地址(可以认为是父类的对象)
class Fu {
//父类的成员位置
String name = "曹操";
public void test() {
//父类的局部位置
String name = "曹冲";
}
}
class Zi extends Fu {
//子类的成员位置
String name = "曹丕";
public void show() {
//子类的局部位置
String name = "曹植";
System.out.println(name);
System.out.println(this.name); //如果子类有name就访问子类的,如果子类没有就访问父类的name,如果都没有就报错
System.out.println(super.name); //访问父类的name,如果父类没有就报错
}
}
public class Demo {
public static void main(String[] args) {
//创建子类对象
Zi z = new Zi();
z.show();
}
}
1.5 super和this的区别
this:代表了子类的对象
super:代表了指向父类的地址(可以认为是父类的对象)
访问变量:
this:可以访问子类的成员变量也可以访问父类的成员变量
super:只能访问父类的成员变量
格式:
this.变量名;
super.变量名;
访问方法:
this:可以访问子类的成员方法也可以访问父类的成员方法
super:只能访问父类的成员方法
格式:
this.方法名;
super.方法名;
访问构造方法:
格式:
this()访问子类的无参构造方法,this(...)访问子类的带参构造方法
super()访问父类的无参构造方法,super(...)访问父类的带参构造方法
注意:
1. 子类的构造方法在执行之前会先执行其父类的构造方法,是因为要先将父类的内容通过构造方法初始化之后,子类才可以使用,所以子类会晚于父类进行初始化
2. 子类的构造方法第一行默认带有super(),用来访问父类的无参构造方法
3. this()和super()只能放在构造方法的第一行
4. this()和super()不能同时存在
5. this()和super()一旦显示声明了,那么会将默认的super()给覆盖掉
class Father {
public Father() {
System.out.println("Father的无参构造方法");
}
public Father(String s) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
public Son() {
System.out.println("Son的无参构造方法");
}
public Son(String s) {
System.out.println("Son的带参构造方法");
}
}
public class Demo3 {
public static void main(String[] args) {
Son s = new Son();
}
}
1.6 继承中构造方法之间的关系【了解】
如果父类没有无参构造方法怎么办?
1. 子类的所有构造方法第一行都通过super(...)访问父类的带参构造方法
2. 子类的其中一个构造方法通过super(...)访问父类的带参构造方法,而子类的其它构造方法只需要访问已经访问过父类的构造方法即可(也就是间接的完成了父类的访问)。
1.7 继承中成员方法之间的关系
如果子类和父类的方法名不一样,那么直接按照不同的方法名访问即可
如果子类和父类的方法名一样,那么有如下规则:
1. 如果只是方法名一样,但是参数列表不一样,那么还是属于不同的方法,直接访问不同的方法即可
2. 如果方法名一样,参数列表也一样,与返回值类型有关,那么就会执行子类的内容。以上现象被称为方法的重写。
方法重写:在继承关系中,方法名相同,参数列表也相同,与返回值类型有关。注解@Override
注意:方法一旦被重写,那么就会访问重写后的方法。
练习:
写一个父类(手机类)功能(方法use)有发短信,一个子类(智能手机类)功能(方法use),不仅可以发短信,也可以玩游戏,请用继承完成如上代码。
class Phone {
public void use(){
System.out.println("发短信");
}
}
class SmartPhone extends Phone {
public void use(){
System.out.println("发短信");
super.use();
System.out.println("玩游戏");
}
}
public class Demo2 {
public static void main(String[] args) {
SmartPhone sp = new SmartPhone();
sp.use();
}
}
二、多态
一个事物的多种形态。如:水有三种状态
Java中的多态:对同一件事物的同一个行为(方法)做出不同的反应(方法的内容)。
2.1 前提
1. 得有继承关系
2. 方法的重写
3. 父类引用指向子类对象
class Fu {
public void eat(){
System.out.println("吃饭");
}
}
//1. 继承关系
class Zi extends Fu {
//2. 有方法的重写
public void eat(){
System.out.println("吃面包");
}
}
public class Demo {
public static void main(String[] args) {
Fu f = new Zi();
f.eat();
}
}
2.2 成员的访问特点
Fu f = new Zi(); 等号的左边是父类,等号的右边是子类
成员变量:
编译看左边的父类有没有,如果有就编译通过,如果没有直接报错
运行看左边的父类的内容
总结:成员变量是编译看左边运行也看左边。
成员方法:
编译看左边的父类有没有,如果有就编译通过,如果没有直接报错
运行看右边的子类的内容
总结:成员方法是编译看左边运行看右边。
发现了一个问题:在多态的环境下,子类中的特有功能不能被使用,那么处理才能使用呢?
1. 创建子类对象
2. 向下转型
2.3 向上转型和向下转型
向上转型:从小的类型转为大的类型 Fu f = new Zi();
向下转型:从大的类型转为小的类型 Zi z = (Zi)f;
class Fu {
int i = 10;
int j = 30;
public void a(){
System.out.println("Fu...a");
}
public void b(){
System.out.println("Fu...b");
}
}
class Zi extends Fu {
int i = 20;
int k = 50;
public void a(){
System.out.println("Zi...a");
}
public void c(){
System.out.println("Zi...c");
}
}
class D extends Fu {}
public class Demo {
public static void main(String[] args) {
Fu f = new Zi();
// System.out.println(f.i); //10
// System.out.println(f.j); //30
// System.out.println(f.k); //因为父类中没有此变量,所以编译报错
f.a();
f.b();
// f.c(); //编译不通过
Zi z = new Zi();
z.c();
//较小的数据类型 变量名 = (较小的数据类型)较大的数据类型
Zi zz = (Zi)f;
// D d = (D)f; //运行报错,类型转换异常
zz.c();
}
}
2.4 好处
1. 提高了代码的可维护性 (通过继承保证)
2. 提高了代码的可扩展性 (通过多态保证)
/*
动物类
*/
class Animal {
String name;
int age;
public void eat(){
}
public void sleep(){
}
}
/*
猫类
*/
class Cat extends Animal {
public Cat(String name, int age){
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
System.out.println("猫趴着睡觉");
}
}
/*
狗类
*/
class Dog extends Animal {
public Dog(String name, int age){
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("狗吃肉");
}
@Override
public void sleep() {
System.out.println("狗站着睡觉");
}
}
/*
老虎
*/
class Tiger extends Animal {
public Tiger(String name, int age){
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("吃武松,但被武松反杀");
}
@Override
public void sleep() {
System.out.println("老虎躺着睡觉");
}
}
/*
测试类
*/
public class Demo2 {
public static void main(String[] args) {
Cat c = new Cat("小黑", 2);
//初级版本:一个个调用
// c.eat();
// c.sleep();
feed(c);
Cat c2 = new Cat("小黑黑", 3);
// c2.eat();
// c2.sleep();
feed(c2);
Cat c3 = new Cat("小黑黑黑", 1);
// c3.eat();
// c3.sleep();
feed(c3);
System.out.println("-----------");
Dog d = new Dog("小黄", 1);
feed(d);
Dog d2 = new Dog("小黄黄", 2);
feed(d2);
Dog d3 = new Dog("小黄黄黄", 3);
feed(d3);
System.out.println("-----------");
Tiger t = new Tiger("小虎", 1);
feed(t);
}
//终极版本
public static void feed(Animal a){ //Animal a = new Cat("小黑", 2); //Animal a = new Tiger("小虎", 1);
a.eat();
a.sleep();
}
/*
//升级版本:通过方法统一调用
public static void feed(Cat c){ //传入的类型是类类型,实际上传递的是该类的对象
c.eat();
c.sleep();
}
public static void feed(Dog d) {
d.eat();
d.sleep();
}
public static void feed(Tiger t){
t.eat();
t.sleep();
}
*/
}