继承补充:
方法重写的注意事项:
1、父类中私有的方法不能被重写
2、子类重写父类的方法时候,访问权限不能更低
要么子类重写的方法访问权限比父类的访问权限要高或者一样
建议:以后子类重写父类的方法的时候,权限修饰符写一样就不会发生这样的问题。
3、父类中静态的方法不能被重写,也就是说不能被Override修饰,因为静态的是属于类本身的东西。
class OldPhone { private String name; public void call(String name) { System.out.println("打电话给" + name); } public static void play() {//当这个方法是静态时后面子方法便无法重写 System.out.println("玩俄罗斯方块"); } } class NewPhone extends OldPhone { @Override public void call(String name) { System.out.println("看抖音"); } public static void play(){ System.out.println("打王者"); } } public class ExtendsDemo1 { public static void main(String[] args) { NewPhone newPhone=new NewPhone(); newPhone.call("小王"); newPhone.play(); } } 第二个
class A { static int a = 10; //将父类中的静态成员看作一个全局共享的,被所有的子类共享 public static void fun() { System.out.println("hello"); } } class B extends A { public void fun2() { a = 200; System.out.println(a); } public static void fun() { System.out.println("world"); } } public class ExtendsDemo2 { public static void main(String[] args) { B b=new B(); System.out.println(b.a); b.fun(); b.fun2();//当调用完这个后改变了a的值为200导致后面的调用a变成了200 System.out.println(b.a); System.out.println(A.a); } }
final的特点:
final:最终的的意思。它可以修饰类,成员变量,成员方法
特点:
1、修饰类,类不能被继承
2、修饰成员变量,变量变常量,并且只能赋值一次,在构造方法完毕之前赋值即可。
常量:
字面值常量
自定义常量:被final修饰变量变成自定义常量
3、final修饰成员方法:方法不能被重写
class Fu2 { int num = 10; final int num2 = 20; } class Zi2 extends Fu2 { public void show() { num = 100; System.out.println(num); // num2=300;//报错无法为num2赋值 System.out.println(num2); } } public class FinalDemo { public static void main(String[] args) { Zi2 zi2 = new Zi2(); zi2.show(); } }
final修饰局部变量
1、在方法内部,修饰基本数据类型的变量,变量值只能赋值一次,不能发生改变
2、final修饰引用数据类型的变量呢?
引用的地址值不可以发生改变,但是该对象的堆内存中的值是可以发生改变的。
class Student { int age = 10; } public class FinalDemo2 { public static void main(String[] args) { int x = 100; x = 300; System.out.println(x); final int y = 200; // y = 400; //无法为最终变量y分配值 System.out.println(y); Student student=new Student(); System.out.println(student.age); final Student s2=new Student(); System.out.println(s2.age); s2.age=202; System.out.println(s2.age); System.out.println("==========="); // s2 = new Student(); //无法为最终变量s2分配值 } }
有些时候,我们不想让子类覆盖重写父类中的方法或者功能,只能让子类去使用。
那怎么办呢?
针对这个情况,java提供了一个关键字:final
final:最终的意思。可以修饰类,成员变量,成员方法。
class Fu{ public final void show(){ System.out.println("这是父类中的show方法"); } } class Zi extends Fu{ // @Override // public void show(){ // System.out.println("这是子类中的show方法"); // } } public class ZiDemo { public static void main(String[] args) { Zi zi = new Zi(); zi.show(); } }
多态:
多态:某一事物,在不同时刻表现出来的不同状态
举例:
水:
固态、液态、气态
固态的水是水、液态的水也是水、气态的水也是水
水果:
波罗蜜、香蕉、榴莲
波罗蜜是水果、香蕉是水果、榴莲是水果
水果是波罗蜜。// 反过来说是有问题的
动物:
狗、虎、猫、大象
狗是动物,这么说是没问题的
动物是狗,这么说是不可以。
通过观察例子发现,要想有多态,就必须要继承,继承是多态的前提。
多态的前提:
要有继承关系
要有方法的重写
其实没有重写也是可以的,但是不重写就没有意义
动物都有吃这个方法,但是每个具体的动物吃的实现不一样,变现出不同动物的特有属性
要有父类的引用指向子类的对象
父类名 f = new 子类名(...);
多态访问成员的特点:
1、成员变量
编译看左,运行看左
2、构造方法
创建子类对象的时候,先访问父类中的构造方法,对父类的数据先进行初始化
3、成员方法
编译看左,运行看右。
因为成员方法存在重写,所以访问看右边
4、静态成员方法
编译看左,运行也看左。
由于被static修饰的成员都是与类相关的,这里不是重写,所以运行的时候,访问的还是左边的。
class Fu3{ int num=100; public void show(){ System.out.println("这是父类中的show()f方法"); } public static void fun(){ System.out.println("这是父类中的静态fun()方法"); } } class Zi3 extends Fu3{ int num=1000; @Override public void show() { System.out.println("这是子类中的show()方法"); } public void show2() { System.out.println("这是子类中的特有方法"); } public static void fun() { System.out.println("这是子类中的静态fun()方法"); } } public class PolymorphicDemo { public static void main(String[] args) { Fu3 fu3=new Zi3(); System.out.println(fu3.num); fu3.show();// 这是子类中的show()方法 fu3.fun();//这是父类里的静态方法 } }
class Father{ public void show(){ System.out.println("这是父类的show方法"); } } class Son extends Father{ @Override public void show() { System.out.println("这是子类里的show方法"); } public void show2() { System.out.println("这是子类里特有的show方法"); } } public class PolymorphicDemo2 { public static void main(String[] args) { Father father=new Son(); father.show(); //需求:我现在就想调用父类中的show方法,怎么办? } }
多态的弊端:
多态无法访问父类中的方法名一样的方法
1、我就想使用父类中的方法,能不能用,能。不使用多态
2、如果我想使用子类中的特有方法,还必须使用多态,咋办?
1)就不使用多态,创建子类对象然后调用方法
但是在我上一个例子上,再次创建对象,还会在堆内存中开辟空间,很有可能会造成资源浪费
2、将子类看成一个小的类型,将父类看成一个大的类型,现在想要用小的类型中的方法
我们应该要将大的类型转成小的类型。在我们之前的做法是用强制类型转换。
如果在继承关系,也有类似的这样的做法就好了。
java替我们考虑到这样的问题,提供了一个技术给我们使用:向下转型
将父类的引用强制转换成子类的引用
子类类名 变量名 = (子类类名)父类的引用;
对象之间转型的问题:
1、向上转型:
Fu f = new Son();
2、向下转型
Son s = (Son)f;
int a = 10;
byte b = (byte)a;
向下转型需要注意的一个问题:
要求转型的类与父类引用存在继承关系,并且一开始创建多态的时候,使用的是该类。
关键字abstract,它可以修饰类,方法
1、抽象类中可以没有抽象方法,但是有抽象方法的类一定是抽象类
2、抽象类不能被实例化
3、要想实例化抽象类,必须用一个具体的子类继承它
注意:具体的子类继承抽象类,必须重写该抽象类中的所有的抽象方法
4、最终以抽象多态的形式进行实例化。
class Father2{ public void show(){ System.out.println("这是父类中的show方法"); } } class Son2 extends Father2{ @Override public void show() { System.out.println("这是子类中的show方法"); } public void show2() { System.out.println("这是子类中的特殊show方法"); } } public class PolymorphicDemo3 { public static void main(String[] args) { Father2 father=new Son2(); //father.show2();//这里不能直接调用show2()因为父类里没有show2()无法编译 Son2 s=(Son2) father; s.show2(); s.show(); } }
实例:
动物之猫和狗
class Animal2{ public void eat(){ System.out.println("吃"); } } class Dog extends Animal2{ @Override public void eat() { System.out.println("狗吃肉"); } public void lookDoor(){ System.out.println("看门"); } } class Cat extends Animal2{ @Override public void eat() { System.out.println("猫吃鱼"); } public void catchMouse(){ System.out.println("猫捉老鼠"); } } public class PolymorphicDemo4 { public static void main(String[] args) { Animal2 animal2=new Dog(); animal2.eat(); Dog d=(Dog) animal2; d.eat(); d.lookDoor(); Animal2 animal21=new Cat(); animal21.eat(); Cat c=(Cat) animal21; animal21.eat(); ((Cat) animal21).catchMouse(); } }
不同地方饮食文化不同的案例
Person
eat()
SouthPerson
eat()
NorthPerson
eat()
class Person { public void eat() { System.out.println("吃"); } } class SouthPerson extends Person { @Override public void eat() { System.out.println("南方人吃米饭"); } public void playMaJiang() { System.out.println("南方人打麻将"); } } class NorthPerson extends Person { @Override public void eat() { System.out.println("北方人吃面食"); } public void bath() { System.out.println("北方人搓澡"); } } public class PolymorphicDemo5 { public static void main(String[] args) { Person person = new SouthPerson(); person.eat(); SouthPerson s = (SouthPerson) person; s.eat(); s.playMaJiang(); Person person1 = new NorthPerson(); person1.eat(); NorthPerson n = (NorthPerson) person1; n.bath(); n.eat(); } }
多态的好处有哪些?
1、多态可以使代码的扩展性很好(这是由继承所保证的)
2、多态可以使代码的维护性很好(这是由多态保证的)
class Animal { String name; int age; Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public void sleep() { System.out.println("睡觉"); } public void eat() { System.out.println("吃"); } } class Dog1 extends Animal { public Dog1() { } public Dog1(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 Cat1 extends Animal { public Cat1() { } public Cat1(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 AnimalTool {//工具类 private AnimalTool() { } public static void useDog(Dog1 dog1) { dog1.sleep(); dog1.eat(); } public static void useCat(Cat1 cat1) { cat1.sleep(); cat1.eat(); } public static void useAnimal(Animal animal) { animal.eat(); animal.sleep(); } } public class CatDogDemo { public static void main(String[] args) { System.out.println("=====第二种通过下面创建方法调用=====");//第一种方法调用子类 Dog1 dog1 = new Dog1("小白哦", 2); dog1.eat(); dog1.sleep(); Cat1 cat1 = new Cat1("小号", 3); cat1.eat(); cat1.sleep(); System.out.println("=====第二种通过下面创建方法调用=====");//第二种通过下面创建方法调用 Dog1 d1 = new Dog1("小白", 2); useDog(d1); Cat1 c1 = new Cat1("小红", 2); useCat(c1); System.out.println("====第三种通过工具类调用=====");//第三种通过工具类调用 Dog1 d2 = new Dog1("小白", 2); AnimalTool.useDog(d2); Cat1 c2 = new Cat1("小红", 2); AnimalTool.useCat(c2);//这里注意工具类是私有的不可直接调用use方法 //工具类里的内容是私有的不好改动 System.out.println("=====用多态改进====="); Animal a1 = new Dog1("小白",2); AnimalTool.useAnimal(a1); Animal a2 = new Cat1("小红",2); AnimalTool.useAnimal(a2); } public static void useDog(Dog1 dog1) {//创建方法调用 dog1.sleep(); dog1.eat(); } public static void useCat(Cat1 cat1) { cat1.sleep(); cat1.eat(); } }
抽象的表述:
之前所说的猫,狗,猪,熊猫,老虎等等都是动物具体的例子,而动物本身是一个抽象的概念 但是回想一下我们之前都是将动物写成了一个具体的,而类又可以创建对象,但是实际上抽象的东西本身应该不能被实例化.
并且动物中吃的方法应该也不是一个具体的实现,以及所有动物共同拥有的方法在动物中应该都是抽象的表现
今天之后,把一个不具体的功能,叫做抽象方法,而如果一个类中有抽象方法,我们就把这个类称之为抽象类。
抽象类的特点:
1、抽象类和抽象方法都要用一个关键字修饰:abstract
修饰一个类 放在class的前面
举例: abstract class Animal3{}
修饰一个方法 一般是放在权限修饰符后面
定义一个抽象的show方法
举例:public abstract void show();
2、有抽象方法的类一定要是抽象类,抽象类不一定要有抽象方法,具体的类中不能有抽象方法,抽象类中既可以存在抽象方法
也可以存在有方法体的方法。
3、抽象类不能被实例化
既然不能被实例化,那写在抽象类中的方法如何被调用呢?
抽象类如何创建呢?
通过多态的形式,使用具体的子类去实例化调用方法,专业术语称之为:抽象多态
4、如果继承抽象类的是一个具体的子类,需要重写该抽象类中所有的抽象方法
如果继承抽象的也是一个抽象类,可以不去重写父类中的抽象方法,也可以选择性的去重写。
abstract class Animal3 { //注意: //抽象方法没有方法体{},连大括号都没有,直接以分号结尾 //java: 抽象方法不能有主体 public abstract void eat(); public abstract void drink(); } class Dog3 extends Animal3 { @Override public void eat() { System.out.println("狗吃肉"); } @Override public void drink() { System.out.println("狗喝水"); } } abstract class Demo2 { public abstract void fun(); public abstract void fun2(); } abstract class Demo3 extends Demo2 { @Override public void fun() { System.out.println("抽象类Demo3中重写了fun方法"); } @Override public void fun2() { System.out.println("抽象类Demo3中重写了fun2方法"); } } public class AbstractDemo1 { public static void main(String[] args) { // Demo2 demo2=new Demo2(); //java: com.shujia.wyh.day11.Demo2是抽象的; 无法实例化 //利用具体子类多态形式创建对象 //抽象多态的形式 Animal3 animal3 = new Dog3(); animal3.drink(); animal3.eat(); //Demo2 demo2= new Demo2() //都是抽象无法调用 } }
抽象类的成员的特点:
成员变量:
既可以是变量,也可以是常量
构造方法:
可以存在构造方法,上一个程序中总结出抽象类不能被实例化,这里构造方法意义是什么?
之前在继承中要想初始化子类,必须先初始化父类,所以这里构造方法是提供初始化父类的作用
成员方法:
可以是抽象方法,但是具体的子类必须要重写该方法
也可以不是抽象方法,提高代码的复用性。
abstract class Animal4 { int a = 100; final int b = 20; Animal4() { System.out.println("这是Animal4中无参构造方法");//即使不去定义也会存在 } public abstract void eat(); public void show() { System.out.println("父类中不是抽象方法的show"); } } class Cat4 extends Animal4{ @Override public void eat() { System.out.println("猫吃鱼"); } } public class AbstractDemo2 { public static void main(String[] args) { Animal4 a = new Cat4(); System.out.println(a.a); System.out.println(a.b); a.eat(); a.show(); } }
不写抽象方法,也是可以的。
1、抽象类中可以没有抽象方法
2、抽象类不能被实例
抽象类中可以存在哪些关键字?
abstract关键字不能和哪些关键字共存?
private
static
final
abstract class YeTi{ // YeTi(){ // super(); // }//不会报错因为所有的类都有一个共同的父类:object //abstract可以和public关键字共存 public abstract void show(); //private和abstract关键字冲突 //private abstract void show2(); java: 非法的修饰符组合: abstract和private //static和abstract关键字冲突 // static abstract void show3(); // java: 非法的修饰符组合: abstract和static //final和abstract关键字冲突 // final abstract void show4(); // java: 非法的修饰符组合: abstract和final } public class AbstractDemo3 { public static void main(String[] args) { } }
抽象类改进猫狗案例
abstract class Animal5{ public abstract void eat(); public abstract void sleep(); } class Dog5 extends Animal5 { @Override public void eat() { System.out.println("狗吃肉"); } @Override public void sleep() { System.out.println("狗圈着睡"); } public void play(){ System.out.println("狗和狗玩"); } } class Cat5 extends Animal5{ @Override public void eat() { System.out.println("猫吃鱼"); } @Override public void sleep() { System.out.println("猫趴着睡"); } public void play(){ System.out.println("猫和猫玩"); } } public class AbstractTest1 { public static void main(String[] args) { Animal5 animal5=new Dog5(); animal5.eat(); animal5.sleep(); Dog5 dog5=(Dog5) animal5; dog5.play(); Animal5 animal51=new Cat5(); animal51.sleep(); animal51.eat(); Cat5 cat5=(Cat5) animal51; cat5.play(); } }
假如我们在开发一个系统时需要对员工类进行设计,员工包含3个属性:姓名、工号以及工资。
经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。
请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
分析:
普通员工:
成员变量:姓名,工号,工资
成员方法:工作(敲代码)
经理
成员变量:姓名,工号,工资,奖金
成员方法:工作(做PPT)
abstract class Stuff{ private String name; private String id; private int salary; public Stuff(){ } public Stuff(String name,String id,int salary){ this.id=id; this.name=name; this.salary=salary; } public void setName(String name) { this.name = name; } public void setId(String id) { this.id = id; } public void setSalary(int salary) { this.salary = salary; } public String getName() { return name; } public String getId() { return id; } public int getSalary() { return salary; } public abstract void work(); } class CommonStaff extends Stuff{ public CommonStaff(){ } public CommonStaff(String name, String id, int salary) { super(name, id, salary); } @Override public void work() { System.out.println("敲代码"); } } class Manager extends Stuff{ //特有的属性 奖金 private int bonus; public Manager(int bonus) { this.bonus = bonus; } public Manager(String name, String id, int salary, int bonus) { super(name, id, salary); this.bonus = bonus; } @Override public void work() { System.out.println("做PPT"); } } public class AbstractTest4 { public static void main(String[] args) { //创建第一个对象 Stuff s1 = new CommonStaff("小虎","KZ0080",100000); s1.work(); //创建第二个对象 Stuff s2 = new Manager("小明","SJ00030",200000,1000000); s2.work(); } }