一、final
1.继承的代码实现:
由于继承中方法有一个现象:方法重写。
所以,父类的功能,就会被子类给覆盖掉
有些时候,我们不想让子类去覆盖掉父类的功能,只能让他使用。
这个时候,针对这种情况,java就提供了一个关键字:final
final:"最终"的意思,常见的是它可以修饰类,方法,变量。
class Fu{
// Zi中的show()无法覆盖Fu中的show()
public final void show(){
System.out.println("绝密文件,任何人都不能修改");
}
}
class Zi extends Fu{
public void show(){
System.out.println("这是一堆垃圾");
}
}
class FinalDemo{
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
2.final修饰类,方法,变量
final可以修饰类,方法,变量
特点:
final可以修饰类:该类不能被继承。
final可以修饰方法:该方法不能被重写(覆盖,复写)
final可以修饰变量:该变量不能被重新赋值,因为这个变量其实就是常量。
常量:
A、字面值常量
"hello"、10、true
B、自定义常量
final int x = 10
class Fu{
public final int num= 100;
}
class Zi extends Fu{
public void show(){
//无法为最终变量num分配值
num = 10;
System.out.println(num);
}
}
class FinalDemo{
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
3.final修饰局部变量的问题
基本类型:基本类型的值不能发生改变
引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
比如:教室的地址不能变,但是教室里面的学生可以改变。
class Student{
int age = 10;
}
class FinalDemo{
public static void main(String[] args){
//局部变量是基本数据类型
int x = 10;
x = 100;
System.out.println(x);
final int y = 10;
//错误: 无法为最终变量y分配值
//y = 10;
System.out.println(y);
System.out.println("------------");
//局部变量是引用数据类型
Student s = new Student();
System.out.println(s.age);
s.age = 100;
System.out.println(s.age);
System.out.println("-------------");
final Student ss = new Student();
System.out.println(ss.age);
ss.age = 100;
System.out.println(ss.age);
System.out.println("-----------");
//不能这样写,因为引用变量传递是地址值,
//我们这样做,是在重新开辟一个空间
//而final修饰的引用变量是无法重新开辟空间的。
//重新分配内存空间
//错误: 无法为最终变量ss分配值
ss = new Student();
}
}
4.final修饰变量的初始化时机
A:被final修饰的变量只能赋值一次
B:在构造方法完毕前(非静态的常量)
class Demo{
//int num = 10;
//final int num2 = 20;
int num;
final int num2;
public Demo(){
num = 100;
num2 = 200;
}
}
class FinalDemo{
public static void main(String[] args){
Demo d = new Demo();
System.out.println(d.num);
}
}
二、多态
1.概述和前提条件
多态:同一个对象(事物),在不同时刻体现出来的不同状态。
举例:
猫是猫,猫是动物。
水(液态,固态,气态)。
多态的前提:
A:要有继承关系
B:要有方法重写。
其实没有也是可以的,但是如果没有这个就没有意义。
C:要有父类引用指向子类对象。
父 f = new 子();
用代码体现一下
多态中的成员访问特点:
A:成员变量:编译看左边,运行看左边。
B:构造方法
创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
C:成员方法
编译看左边,运行看右边。(因为方法存在方法覆盖)
**如果子类中存在方法重写,那么
父类 f = new 子类
中f调用的就是子类中的方法,如果调用的方法子类中没有,那么就调用父类中的方法
D:静态方法
编译看左边,运行看左边
(静态和类相关,算不上重写,所以,访问还是左边的)
由于成员方法存在方法重写,所以它运行看右边。
class Fu{
public int num = 100;
public void show(){
System.out.println("showFu");
}
}
class Zi extends Fu{
public int num = 1000;
public int num2 = 200;
public void show(){
System.out.println("showZi");
}
public void method(){
System.out.println("method");
}
}
class DuoTaiDemo{
public static void main(String[] args){
//要有父类引用指向子类对象。
//父 f = new 子();
Fu f = new Zi();
System.out.println(f.num);
//结果是100
// 错误: 找不到符号
//System.out.println(f.num2);
f.show();
// 错误: 找不到符号
//f.method();
}
}
2.多态的好处
A:提高了代码的维护性(继承保证)
B:提高了代码的维护性
猫狗案例代码
class Animal{
public void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃肉");
}
public void sleep(){
System.out.println("狗站着睡觉");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void sleep(){
System.out.println("猫趴着睡觉");
}
}
class Pig extends Animal{
public void eat(){
System.out.println("猪吃白菜");
}
public void sleep(){
System.out.println("猪侧着睡觉");
}
}
//针对动物操作的工具类
class AnimalTool{
//私有化工具类构造方法,别人无法创建对象
private AnimalTool(){}
/*
//调用猫的功能
public static void useCat(Cat c){
c.eat();
c.sleep();
}
//调用狗的功能
public static void useDog(Dog d){
d.eat();
d.sleep();
}
//调用猪的功能
public static void usePig(Pig p){
p.eat();
p.sleep();
}
*/
public static void useAnimal(Animal a){
a.eat();
a.sleep();
}
}
class DuoTaiDemo{
public static void main(String[] args){
//我喜欢猫,就养了一只
Cat c = new Cat();
c.eat();
c.sleep();
//我很喜欢猫,所以,又养了一只
Cat c2 = new Cat();
c2.eat();
c2.sleep();
//我特别喜欢猫,又养了一只
Cat c3 = new Cat();
c3.eat();
c3.sleep();
System.out.println("---------------");
//问题来了,我养了很多只猫,每次创建对象是可以接受的
//但是呢?调用方法,你不觉得很相似吗?仅仅是对象名不一样。
//我们准备用方法改进
//调用方式
/*
useCat(c1);
useCat(c2);
useCat(c3);
*/
//AnimalTool.useCat(c);
//AnimalTool.useCat(c2);
//AnimalTool.useCat(c3);
AnimalTool.useAnimal(c);
AnimalTool.useAnimal(c2);
AnimalTool.useAnimal(c3);
System.out.println("---------------");
//我喜欢狗
Dog d = new Dog();
Dog d2 = new Dog();
Dog d3 = new Dog();
//AnimalTool.useDog(d);
//AnimalTool.useDog(d2);
//AnimalTool.useDog(d3);
AnimalTool.useAnimal(d);
AnimalTool.useAnimal(d2);
AnimalTool.useAnimal(d3);
System.out.println("---------------");
//我喜欢宠物猪
//定义一个猪类,她要继承自动物,提供两个方法,并且还要在工具类中添加该类的调用
Pig p = new Pig();
Pig p2 = new Pig();
Pig p3 = new Pig();
//AnimalTool.usePig(p);
//AnimalTool.usePig(p2);
//AnimalTool.usePig(p3);
AnimalTool.useAnimal(p);
AnimalTool.useAnimal(p2);
AnimalTool.useAnimal(p3);
System.out.println("---------------");
//我喜欢宠物狼,老虎,豹子...
//定义对应的类,继承自动物,提供对应的方法重写,并在工具类添加方法调用
//前面几个必须写,但是,工具类每次都改,我就想,能不能不改了?
//所以改用另一种解决方案。
//
}
/*
//调用猫的功能
public static void useCat(Cat c){
c.eat();
c.sleep();
}
//调用狗的功能
public static void useDog(Dog d){
d.eat();
d.sleep();
}
*/
}
3.多态的弊端
不能使用子类的特有功能
class Fu{
public void show(){
System.out.println("show Fu");
}
}
class Zi extends Fu{
public void show(){
System.out.println("show Zi");
}
public void method(){
System.out.println("method");
}
}
class DuoTaiDemo{
public static void main(String[] args){
Fu fu = new Zi();
fu.show();
//错误: 找不到符号
fu.method();
}
}
4.多态中向上转型和向下转型
多态的弊端:
不能使用子类的特有功能
我就想使用子类的特有功能?行不行?
行。
怎么用呢?
A:创建子类对象调用方法即可。(可以,但是很多时候不合理,而且,太占内存了)
B:把父类的引用强制转换为子类的引用。(向下转型)
对象间的转换问题:
向上转型:
Fu f = new zi();
向下转型:
Zi z = (Zi)f;//要求这里的f必须是能够转换为子的。
class Fu{
public void show(){
System.out.println("show Fu");
}
}
class Zi extends Fu{
public void show(){
System.out.println("show Zi");
}
public void method(){
System.out.println("method");
}
}
class DuoTaiDemo{
public static void main(String[] args){
Fu fu = new Zi();
fu.show();
//错误: 找不到符号
//fu.method();
//创建子类对象
//方式1:使用这种方法,内存是两个对象
//Zi z = new Zi();
//z.show();
//z.method();
//方式2:使用这种方法,内存中是一个对象。
//你能够把子的对象赋值给父亲,那么我能不能
//把父的引用赋值给子的引用呢?
Zi z = (Zi)fu;
z.show();
z.method();
}
}
4.多态问题理解——孔子装爹案例
class 孔子爹 {
public int age = 40;
public void teach(){
System.out.println("讲解javase");
}
}
class 孔子 extends 孔子爹 {
public int age = 20;
public void teach(){
System.out.println("讲解javase");
}
public void playGame(){
System.out.println("英雄联盟");
}
}
//java培训特别火,很多人来请孔子爹去讲课,这一天孔子爹被请走了,
//但是还有人来请,就剩孔子在家,价格还挺高,孔子一想,我是不是可以考虑去呢?
//然互就穿上爹的衣服,带上爹的眼镜,粘上爹的胡子。就开始装爹。
//向上转型
孔子爹 k爹 = new 孔子();
//到人家那里去了
System.out.println(k爹.age);
k爹.teach();
k爹.playGame();//这是儿子才能做的
//讲完了,下班回家了
//脱下爹的装备,换上自己的装备
孔子 K = (孔子)k爹;
System.out.println(k.age);//20
k.teach();//讲解论语
k.playGame();//英雄联盟
/*
ClassCastException:类型转换异常
一般在多态的向下转型中容易出现
*/
class Animal{
public void eat(){}
}
class Dog extends Animal{
public void eat(){}
public void lookDoor(){}
}
class Cat extends Animal{
public void eat(){}
public void playGame(){}
}
class DuoTaiDemo{
public static void main(String[] args){
//内存中的是狗
Animal a = new Dog();
Dog d = (Dog)a;
//内存中的是猫
a = new Cat();
Cat c = (Cat)a;
//内存中的是猫
Dog dd = (Dog)a;//.ClassCastException
//java中类型转换不匹配的错误
}
}
7.面试题
/*
看程序写结果:先判断有没有问题,如果没有,写出结果
多态的成员访问特点:
方法:编译看左边,运行看 右边
继承的时候:
子类中有和父类中一样的方法,叫重写。
子类中没有和父亲中出现过的方法,方法就被继承
*/
class A{
public void show(){
show2();
}
public void show2(){
System.out.println("我");
}
}
class B extends A{
/*
public void show(){
show2();
}
*/
public void show2(){
System.out.println("爱");
}
}
class C extends B{
public void show(){
super.show();
}
public void show2(){
System.out.println("你");
}
}
public class DuoTaiTest2{
public static void main(String[] args){
A a = new B();
a.show();
B b = new C();
b.show();
}
}
/*
结果:爱你
*/