1 package cn.temptation; 2 3 public class Sample01 { 4 public static void main(String[] args) { 5 // 继承关系的子类可以重写父类给它的成员方法 6 // 有时,父类不希望它的成员方法被子类重写(覆盖),对于这种要求,如何处理? 7 // 答:首先会想到把public 改为 private,但是这样会导致外部无法调用该方法。所以,Java提供了 final 关键字 8 9 // final 使用格式: 10 // 修饰符 final 成员方法(...) { ... } 11 12 } 13 } 14 15 //class Father { 16 //// public void show() { 17 //// System.out.println("这是父类的show方法"); 18 //// } 19 // 20 // public final void show() { 21 // System.out.println("这是父类的show方法"); 22 // } 23 //} 24 // 25 //class Baby extends Father { 26 // // 父类的show方法使用final修饰后,子类中就无法再重写show方法了 27 // // 语法错误:Cannot override the final method from Father 28 //// public void show() { 29 //// System.out.println("这是子类的show方法"); 30 //// } 31 //}
1 package cn.temptation; 2 3 public class Sample02 { 4 public static void main(String[] args) { 5 // final关键字可以用来修饰 成员变量、成员方法 和 类 6 7 // final修饰类,该类不能被继承 8 9 // final修饰类的理解:断子绝孙 10 } 11 } 12 13 final class Test { 14 15 } 16 17 // Test类使用final修饰后,不能被继承 18 // 语法错误:The type TestEx cannot subclass the final class Test 19 //class TestEx extends Test { 20 // 21 //}
1 package cn.temptation; 2 3 public class Sample03 { 4 public static void main(String[] args) { 5 // final修饰成员变量:赋值一次后不能再进行赋值 6 // Child child = new Child(); 7 // child.show(); 8 9 // 常量: 10 // 1、字面量常量:true、false、123、'a'、"China" 11 // 2、自定义常量:使用final修饰符修饰的成员变量 final int i = 2; 12 13 // 所以,自定义常量一般也使用常量的编码规范:即全部字母均大写,如果是多个单词组成,使用下划线_连接 14 } 15 } 16 17 //class Parent { 18 // public int i = 2; 19 // public final int j = 4; 20 // 21 // // 对于非final修饰的成员变量,不进行初始化赋值,是可以使用其默认值 22 // public int x; 23 // // 对于final修饰的成员变量,必须进行初始化赋值 24 // // 语法错误:The blank final field y may not have been initialized 25 //// public final int y; 26 // // 先定义成员变量y,接着进行赋值也是有语法错误 27 //// y = 123; 28 // 29 // // 自定义常量一般也使用常量的编码规范:即全部字母均大写,如果是多个单词组成,使用下划线_连接 30 // public final int K = 100; 31 // public final boolean CHECK_FLAG = true; 32 //} 33 // 34 //class Child extends Parent { 35 // public void show() { 36 // // 下面两组代码效果相同 37 //// this.i = 3; 38 //// System.out.println(this.i); 39 // 40 // super.i = 3; 41 // System.out.println(super.i); 42 // 43 // // final修饰的成员变量不能被改变 44 // // 语法错误:The final field Parent.j cannot be assigned 45 //// this.j = 5; 46 //// System.out.println(this.j); 47 // // 语法错误:The final field Parent.j cannot be assigned 48 //// super.j = 5; 49 //// System.out.println(super.j); 50 // } 51 //}
1 package cn.temptation; 2 3 public class Sample04 { 4 public static void main(String[] args) { 5 // final修饰局部变量 6 // 1、final修饰值类型的局部变量,赋值一次后不能再赋值 7 // 2、final修饰引用数据类型的局部变量,new过一次后不能再new了,创建过一次引用数据类型对象后就不能再创建了 8 // 也就是说该局部变量在堆中有一个内存空间后就不能再创建新的内存空间了 9 10 int i = 2; 11 System.out.println(i); 12 i = 3; 13 System.out.println(i); 14 15 final int j = 4; 16 System.out.println(j); 17 // The final local variable j cannot be assigned. It must be blank and not using a compound assignment 18 // j = 5; 19 // System.out.println(j); 20 21 System.out.println("--------------------------------------"); 22 23 // Demo demo = new Demo(); 24 // System.out.println(demo); 25 // System.out.println(demo.k); 26 // demo = new Demo(); 27 // demo.k = 88; 28 // System.out.println(demo); 29 // System.out.println(demo.k); 30 31 final Demo demoEx = new Demo(); 32 System.out.println(demoEx); 33 System.out.println(demoEx.k); 34 // The final local variable demoEx cannot be assigned. It must be blank and not using a compound assignment 35 // demoEx = new Demo(); 36 demoEx.k = 77; 37 System.out.println(demoEx); 38 System.out.println(demoEx.k); 39 40 // 注意: 41 // 使用final修饰的引用数据类型的局部变量虽然不能更改其在堆内存中创建的空间地址,但是其在堆内存中的成员变量的值还是可以修改的 42 } 43 } 44 45 class Demo { 46 int k = 99; 47 }
1 package cn.temptation; 2 3 public class Sample05 { 4 public static void main(String[] args) { 5 // 使用final、final static修饰的成员变量 与 构造代码块内外 赋值的问题 6 7 TestEx testEx = new TestEx(); 8 } 9 } 10 11 class TestEx { 12 // 成员变量 13 int i = 2; 14 15 // 语法错误:The blank final field j may not have been initialized 16 final int j; 17 18 final int k = 3; 19 20 final static int m = 4; 21 22 // 语法错误:The blank final field n may not have been initialized 23 // final static int n; 24 25 final int x = 123; 26 27 final static int y = 987; 28 29 // 构造代码块 30 { 31 System.out.println(i); 32 i = 3; 33 System.out.println(i); 34 35 System.out.println(k); 36 37 // 语法错误:The blank final field j may not have been initialized 38 // System.out.println(j); 39 // 在构造代码块之前使用final修饰成员变量并不赋值,在构造代码块中进行赋值,语法OK 40 j = 5; 41 System.out.println(j); 42 43 // 在构造代码块之前使用final static修饰成员变量并不赋值,在构造代码块中进行赋值,语法也出错 44 // 语法错误:The final field TestEx.n cannot be assigned 45 // n = 6; 46 // System.out.println(n); 47 48 System.out.println(x); 49 // 对于构造代码块之前已经赋值的final修饰的成员变量,在构造代码块中进行赋值,语法也出错 50 // 语法出错:The final field TestEx.x cannot be assigned 51 // x = 456; 52 // System.out.println(x); 53 54 System.out.println(y); 55 // 对于构造代码块之前已经赋值的final static修饰的成员变量,在构造代码块中进行赋值,语法也出错 56 // 语法出错:The final field TestEx.y cannot be assigned 57 // y = 654; 58 // System.out.println(y); 59 } 60 }
1 package cn.temptation; 2 3 public class Sample06 { 4 public static void main(String[] args) { 5 // 多态的引入: 6 // 变形金刚:变化为不同的形态:一会儿是机器人,一会儿变成汽车 7 // 水:三种不同的形态:气态、液态、固态 8 9 // 多态:同一个对象,在不同的时间、场合表现出不同的形态或状态 10 11 // 比如:狗这个动物就是可爱啊! 12 // 这句话其实有几层意思: 13 // 1、狗是一种动物,也就是说狗从动物继承而来的,dog is an animal 14 // 2、描述的重点在动物上,这个动物在我们描述的时候以狗这种形态出现的 15 // 类似:猫这个动物就是要人哄啊! 16 17 // 多态的前提: 18 // 1、继承是多态的基础,必须有继承的关系,才可以探讨多态 19 // 2、要有override重写 20 // 3、要有父类的引用(声明)指向子类的对象:父类 对象名 = new 子类(); 父类在前,子类在后 21 // 对比:之前写法 子类 对象名 = new 子类(); 22 23 // 理解: 24 // 狗 对象名 = new 狗(); 狗 是 狗 √ 25 // 动物 对象名 = new 狗(); 狗 是 动物 √ 26 // 狗 对象名 = new 动物(); 动物 是 狗 × 27 28 // Son son = new Son(); 29 // System.out.println(son); 30 31 // Father obj = new Son(); 32 // System.out.println(obj); 33 34 // 语法错误:Type mismatch: cannot convert from Father to Son 35 // Son obj = new Father(); 36 // System.out.println(obj); 37 38 // 多态中成员的关系: 39 // 1、成员变量 40 // 编译时去赋值号左侧的类型里找,执行时去赋值号左侧的类型里找 41 // 2、构造函数 42 // 是否是多态的写法不影响,都是通过继承关系先找到父类的构造函数,执行后再走入子类的构造函数 43 // 3、成员方法 44 // 编译时去赋值号左侧的类型里找,执行时去赋值号右侧的类型里找 45 // 4、静态成员变量 46 // 编译时去赋值号左侧的类型里找,执行时去赋值号左侧的类型里找 47 // 5、静态成员方法 48 // 编译时去赋值号左侧的类型里找,执行时去赋值号左侧的类型里找 49 50 // 非多态写法 51 // Son son = new Son(); 52 // System.out.println(son); // cn.temptation.Son@15db9742 53 // System.out.println(son.i); // 3 54 // son.show(); // 子类的show方法 55 // System.out.println(son.m); // 5 56 // System.out.println(Son.m); // 5 57 // son.use(); // 子类的静态use方法 58 // Son.use(); // 子类的静态use方法 59 60 // 多态写法 61 Father obj = new Son(); 62 System.out.println(obj); // cn.temptation.Son@15db9742 63 System.out.println(obj.i); // 2 64 obj.show(); // 子类的show方法 65 System.out.println(obj.m); // 4 66 System.out.println(Father.m); // 4 67 System.out.println(Son.m); // 5 68 obj.use(); // 父类的静态use方法 69 Father.use(); // 父类的静态use方法 70 Son.use(); // 子类的静态use方法 71 72 // 注意: 73 // 多态时,需要从 编译时 和 执行时 两个方面去观察 74 // 多态中,非静态的成员方法在执行时去赋值号右侧的类型中找相应的方法进行调用,这种调用也称为 后期调用 75 } 76 } 77 78 // 父类 79 class Father { 80 // 成员变量 81 public int i = 2; 82 public static int m = 4; 83 84 // 构造函数 85 public Father() { 86 System.out.println("父类的构造函数"); 87 } 88 89 // 成员方法 90 public void show() { 91 System.out.println("父类的show方法"); 92 } 93 94 public static void use() { 95 System.out.println("父类的静态use方法"); 96 } 97 } 98 99 // 子类 100 class Son extends Father { 101 // 成员变量 102 public int i = 3; 103 public static int m = 5; 104 105 // 构造函数 106 public Son() { 107 System.out.println("子类的构造函数"); 108 } 109 110 // 成员方法 111 public void show() { 112 System.out.println("子类的show方法"); 113 } 114 115 public static void use() { 116 System.out.println("子类的静态use方法"); 117 } 118 }
1 package cn.temptation; 2 3 public class Sample07 { 4 public static void main(String[] args) { 5 // 不用装刘备的时候,可以做自己,就可以用自己特有的play方法 6 // LiuShan personEx = new LiuShan(); 7 // personEx.work(); 8 // personEx.play(); 9 10 // 让刘禅接替刘备的位置(多态的使用) 11 LiuBei person = new LiuShan(); 12 System.out.println(person); // cn.temptation.LiuShan@15db9742 13 person.work(); 14 // 让刘禅装刘备时,不能做自己,不能使用自己特有的play方法 15 // 语法错误:The method play() is undefined for the type LiuBei 16 // person.play(); 17 // 问:非要使用子类中特有的成员方法,怎么办? 答:因为我们知道其对象其实是子类类型的对象,所以可以考虑使用一下强制类型装换 18 ((LiuShan)person).play(); 19 } 20 } 21 22 // 父类:刘备 23 class LiuBei { 24 public void work() { 25 System.out.println("千辛万苦打江山!"); 26 } 27 } 28 29 // 子类:刘禅 30 class LiuShan extends LiuBei { 31 // 成员方法 32 // 重写父类的成员方法work 33 public void work() { 34 System.out.println("被逼着做主公,也不知道有多难受!"); 35 } 36 37 // 子类特有的成员方法play 38 public void play() { 39 System.out.println("玩的都乐不思蜀了!"); 40 } 41 }
1 package cn.temptation; 2 3 public class Sample08 { 4 public static void main(String[] args) { 5 // 多态的优点: 6 // 1、使用多态,让程序有较好的扩展性 7 // 2、使用多态,让程序由较好的健壮性(没有继承关系的两个类不能玩多态) 8 9 // 不使用多态的写法 10 // BasketBall basketBall = new BasketBall(); 11 // basketBall.play(); 12 // 13 // FootBall footBall = new FootBall(); 14 // footBall.play(); 15 // 16 // VolleyBall volleyBall = new VolleyBall(); 17 // volleyBall.play(); 18 19 // 使用多态的写法 20 // 写法1 21 // Sport basketBall = new BasketBall(); 22 // basketBall.play(); 23 // 24 // Sport footBall = new FootBall(); 25 // footBall.play(); 26 // 27 // Sport volleyBall = new VolleyBall(); 28 // volleyBall.play(); 29 30 // 写法2 31 // Sport sport1 = new BasketBall(); 32 // sport1.play(); 33 // 34 // Sport sport2 = new FootBall(); 35 // sport2.play(); 36 // 37 // Sport sport3 = new VolleyBall(); 38 // sport3.play(); 39 40 // 写法3(数组的动态初始化) 41 // 因为上面三个变量的类型都是一致的,所以考虑使用数组来存放一下,声明一个Sport类型的数组 42 // Sport[] sports = new Sport[3]; 43 // sports[0] = new BasketBall(); 44 // sports[1] = new FootBall(); 45 // sports[2] = new VolleyBall(); 46 47 // 写法4(数组的静态初始化) 48 Sport[] sports = { 49 new BasketBall(), // 匿名对象 50 new FootBall(), 51 new VolleyBall(), 52 new Swim() 53 }; 54 55 for (Sport item : sports) { 56 item.play(); 57 } 58 } 59 } 60 61 // 父类:运动类 62 class Sport { 63 public void play() { 64 System.out.println("做运动"); 65 } 66 } 67 68 // 子类:篮球类 69 class BasketBall extends Sport { 70 @Override 71 public void play() { 72 System.out.println("篮球的玩法"); 73 } 74 } 75 76 class FootBall extends Sport { 77 @Override 78 public void play() { 79 System.out.println("足球的玩法"); 80 } 81 } 82 83 class VolleyBall extends Sport { 84 public void play() { 85 System.out.println("排球的玩法"); 86 } 87 } 88 89 class Swim extends Sport { 90 @Override 91 public void play() { 92 System.out.println("游泳的玩法"); 93 } 94 }
1 package cn.temptation; 2 3 public class Sample09 { 4 public static void main(String[] args) { 5 // 多态的缺点: 6 // 使用多态,对于子类有但是父类没有的成员方法(子类特有的成员方法),无法使用 7 // Parent obj = new Child(); 8 // obj.show(); 9 10 // 语法错误:The method use() is undefined for the type Parent 11 // obj.use(); 12 13 // 要想使用子类特有的成员方法,只有在明确其实该对象是子类类型的对象时,进行强制类型转换 14 // ((Child)obj).use(); 15 } 16 } 17 18 //class Parent { 19 // public void show() { 20 // System.out.println("父类的成员方法"); 21 // } 22 //} 23 // 24 //class Child extends Parent { 25 // @Override 26 // public void show() { 27 // System.out.println("子类的成员方法"); 28 // } 29 // 30 // public void use() { 31 // System.out.println("子类特有的成员方法"); 32 // } 33 //}
1 package cn.temptation; 2 3 public class Sample10 { 4 public static void main(String[] args) { 5 // 对于多态的缺点:使用多态,对于子类有但是父类没有的成员方法(子类特有的成员方法),无法使用,非要使用,怎么办? 6 // 方法1、在父类中加上子类特有的成员方法,语法OK,但是不能这样写,因为这样就违背了父类设计的初衷 7 // 方法2、因为子类实际在替代父类的事情,所以在编译阶段只能用父类中的成员方法, 8 // 如果明确实际使用的是子类对象,使用时让其改为子类的类型,就可以使用其自己的成员方法了 9 // 把父类类型的变量名(引用)强制类型转换为子类类型的引用,称为多态的向下转型 10 11 // Parent obj = new Child(); 12 // obj.show(); 13 // 语法错误:The method use() is undefined for the type Parent 14 // obj.use(); 15 16 // Parent obj = new Child(); 17 // // 写法1 18 //// ((Child)obj).show(); 19 //// ((Child)obj).use(); 20 // // 写法2 21 // Child child = (Child) obj; 22 // child.show(); 23 // child.use(); 24 // 25 // // 不转型 26 // Child childEx = new Child(); 27 28 // 所谓的转型:就是拿着赋值号右边的类型 和 赋值号左边的类型进行比较 29 // 右边的类型是左边类型的下级(子),向上转型 30 // 右边的类型是左边类型的上级(父),向下转型 31 Parent obj = new Child(); // 向上转型 32 Child child = (Child)obj; // 向下转型 33 34 // 没有关系的两个类,是无法进行强制类型转换的 35 // 语法错误:Cannot cast from Parent to Other 36 // Other other = (Other)obj; 37 } 38 } 39 40 class Parent { 41 public void show() { 42 System.out.println("父类的成员方法"); 43 } 44 } 45 46 class Child extends Parent { 47 @Override 48 public void show() { 49 System.out.println("子类的成员方法"); 50 } 51 52 public void use() { 53 System.out.println("子类特有的成员方法"); 54 } 55 } 56 57 class Other { 58 public void show() { 59 System.out.println("其他类的成员方法"); 60 } 61 }
1 package cn.temptation; 2 3 public class Sample11 { 4 public static void main(String[] args) { 5 // 多态的一些问题: 6 // 1、父类、子类都有的同名同参数列表成员方法,编译时找父类的成员方法,执行时找子类的成员方法 7 // 2、父类有、子类没有的成员方法,编译时找父类的成员方法,执行时找父类的成员方法(因为子类中没有重写该成员方法,所以使用父类中的成员方法) 8 ParentTest obj = new ChildTest(); 9 obj.show(); 10 } 11 } 12 13 class ParentTest { 14 public void show() { 15 System.out.println("父类有、子类没写的成员方法"); 16 } 17 } 18 19 class ChildTest extends ParentTest { 20 21 }
1 package cn.temptation; 2 3 public class Sample12 { 4 public static void main(String[] args) { 5 Animal animal = new Dog(); 6 animal.eat(); 7 // animal.guard(); // 语法错误 8 9 Dog dog = (Dog) animal; 10 dog.guard(); 11 12 // 执行出错,产生异常:java.lang.ClassCastException: cn.temptation.Dog cannot be cast to cn.temptation.Cat 13 // 原因: 14 // 第5行的animal变量是一个引用,animal变量的类型是Animal类类型,它指向堆内存中创建出的Dog类型的空间 15 // 下句的强制类型转换,是要把animal这个引用转换为Cat类型,如果正常的使用,那么这个cat变量的引用,应该指向堆内存中创建的Cat类型的空间 16 Cat cat = (Cat) animal; // 这句语法上没有错误,编译可以通过,是因为有继承关系的子类和父类可以进行向上转型 和 向下转型 17 cat.eat(); 18 cat.bask(); 19 // 本质上这样的写法就是"指鹿为马" 20 21 // 平行的两个子类,是无法进行强制类型转换的 22 Dog dogEx = new Dog(); 23 Cat catEx = new Cat(); 24 // 语法错误:Cannot cast from Dog to Cat 25 // catEx = (Cat)dogEx; 26 } 27 } 28 29 class Animal { 30 public void eat() { 31 System.out.println("动物都会吃"); 32 } 33 } 34 35 class Dog extends Animal { 36 @Override 37 public void eat() { 38 System.out.println("狗吃肉"); 39 } 40 41 public void guard() { 42 System.out.println("看门"); 43 } 44 } 45 46 class Cat extends Animal { 47 @Override 48 public void eat() { 49 System.out.println("猫吃鱼"); 50 } 51 52 public void bask() { 53 System.out.println("晒太阳"); 54 } 55 }