第九章 面向对象三大特征:封装
1. 封装基本概念
1.1 封装的作用
第一个作用:保证内部结构的安全。
第二个作用:屏蔽复杂,暴露简单。
在代码级别上,封装有什么用? 一个类体当中的数据,假设封装之后,对于代码的调用人员来说, 不需要关心代码的复杂实现,只需要通过一个简单的入口就可以访问了; 另外,类体中安全级别较高的数据封装起来,外部人员不能随意访问, 来保证数据的安全性。
1.2 怎么进行封装
第一步:属性私有化(使用private关键字进行修饰。)
第二步:对外提供简单的操作入口。
注意:
set和get方法都是实例方法,不能带static。
不带static的方法称为实例方法,实例方法的调用必须先new对象。
set和get方法写的时候有严格的规范要求:(大家要按照规矩来)
set方法长这个样子:
public void set+属性名首字母大写(1个参数){
xxx = 1个参数;
}
get方法长这个样子:
public 返回值类型 get+属性名首字母大写(无参){
return xxx;
}
第十章 static、this关键字
1. static关键字
1.1 static关键字概述
- static修饰的统一都是静态的,都是类相关的,不需要new对象。直接采用“类名.”访问。(需注意的是**静态的使用“引用.”也行**,为了更好区分建议静态采用类名访问)- 当一个属性是类级别的属性,所有对象的这个属性的值是一样的,建议定义为静态变量。- 静态方法有一个优点,是不需要new对象,直接采用类名调用,极其方便(在同一个类中静态修饰的可以省略"类名.")。工具类就是为了方便,所以工具类中的方法一般都是static的。
class VarTest{// 以下实例的,都是对象相关的,访问时采用“引用.”的方式访问。需要先new对象。// 实例相关的,必须先有对象,才能访问,可能会出现空指针异常。// 成员变量中的实例变量int i;// 实例方法public void m2(){// 局部变量int x = 200;}// 以下静态的,都是类相关的,访问时采用“类名.”的方式访问。不需要new对象。// 不需要对象的参与即可访问。没有空指针异常的发生。// 成员变量中的静态变量static int k;// 静态方法public static void m1(){// 局部变量int m = 100;}}
1.2 空指针异常进一步分析
public class StaticTest03{public static void main(String[] args){// 通过"类名."的方式访问静态变量System.out.println(Chinese.country);// 创建对象Chinese c1 = new Chinese("1111111", "张三");System.out.println(c1.idCard); // 1111111System.out.println(c1.name); // 张三System.out.println(c1.country); // 中国// c1是空引用c1 = null;// 分析这里会不会出现空指针异常?// 不会出现空指针异常。// 因为静态变量不需要对象的存在。// 实际上以下的代码在运行的时候,还是:System.out.println(Chinese.country);System.out.println(c1.country);// 这个会出现空指针异常,因为name是实例变量。//System.out.println(c1.name);}}class Chinese{// 实例变量String idCard;String name;// 静态变量static String country = "中国";//构造方法public Chinese(String x, String y){idCard = x;name = y;}}
1.3 静态代码块
- static静态代码块在什么时候执行
类加载时执行。并且只执行一次;在main方法执行之前执行
- 是一个特殊的时机,类加载时机
1.4 实例语句块(代码块)
- 代码块在什么时候执行
在类加载是并没有执行,只要是构造方法执行,必然在构造方法执行之前,自动执行“实例语句块”中的代码。
- 是一个特殊的时机,对象构建时机
1.5 代码执行顺序
public class CodeOrder{// 静态代码块static{System.out.println("A");}// 入口// A X Y C B Z(最终执行顺序)public static void main(String[] args){System.out.println("Y");new CodeOrder();System.out.println("Z");}// 构造方法public CodeOrder(){System.out.println("B");}// 实例语句块{System.out.println("C");}// 静态代码块static {System.out.println("X");}}
2. this关键字
2.1 this在内存中的表示

this只能使用在实例方法中。谁调用这个实例方法,this就是谁;所以this代表的是:当前对象。
this不能使用在静态方法:this代表当前对象,静态方法中不存在当前对象。
2.2 this啥时候不能省略
/*1、this可以使用在实例方法中,不能使用在静态方法中。2、this关键字大部分情况下可以省略,什么时候不能省略呢?在实例方法中,或者构造方法中,为了区分局部变量和实例变量,这种情况下:this. 是不能省略的。*/public class ThisTest03{public static void main(String[] args){Student s = new Student();s.setNo(111);s.setName("张三");System.out.println("学号:" + s.getNo());System.out.println("姓名:" + s.getName());Student s2 = new Student(2222, "李四");System.out.println("学号:" + s2.getNo());System.out.println("姓名:" + s2.getName());}}// 分析一下:以下代码哪里写的不好。// 学生类class Student{//学号private int no;//姓名private String name;//构造方法无参public Student(){}//构造方法有参/*public Student(int i, String s){no = i;name = s;}*/// 上面的构造方法也增强以下可读性public Student(int no, String name){this.no = no;this.name = name;}// setter and getter方法/*public void setNo(int i){no = i;}*//*public void setNo(int no){ // 就近原则。no = no; //这两个no都是局部变量no,和实例变量no没关系。}*/public void setNo(int no){//no是局部变量//this.no 是指的实例变量。this.no = no; // this. 的作用是:区分局部变量和实例变量。}public int getNo(){return no;//return this.no;}/*public void setName(String s){name = s;}*//*public void setName(String name){ // 就近原则name = name; //这两个name都是局部变量name,和实例变量name没关系。}*/public void setName(String name){this.name = name;}/*public String getName(){return name;}*/public String getName(){ // getName实际上获取的是“当前对象”的名字。//return this.name; // 严格来说,这里是有一个 this. 的。只不过这个 this. 是可以省略的。return name;}}
2.3 this(实际参数列表)用法
/*1、this除了可以使用在实例方法中,还可以用在构造方法中。2、新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:this(实际参数列表);通过一个构造方法1去调用构造方法2,可以做到代码复用。但需要注意的是:“构造方法1”和“构造方法2” 都是在同一个类当中。3、this() 这个语法作用是什么?代码复用。4、死记硬背:对于this()的调用只能出现在构造方法的第一行。*/public class ThisTest04{public static void main(String[] args){// 调用无参数构造方法Date d1 = new Date();d1.detail();// 调用有参数构造方法Date d2 = new Date(2008, 8, 8);d2.detail();}}/*需求:1、定义一个日期类,可以表示年月日信息。2、需求中要求:如果调用无参数构造方法,默认创建的日期为:1970年1月1日。当然,除了调用无参数构造方法之外,也可以调用有参数的构造方法来创建日期对象。*/class Date{ // 以后写代码都要封装,属性私有化,对外提供setter and getter//年private int year;//月private int month;//日private int day;// 构造方法无参// 调用无参数构造方法,初始化的日期是固定值。public Date(){//错误: 对this的调用必须是构造器中的第一个语句//System.out.println(11);/*this.year = 1970;this.month = 1;this.day = 1;*/this(1970, 1, 1);}// 构造方法有参数public Date(int year, int month, int day){this.year = year;this.month = month;this.day = day;}// 提供一个可以打印日期的方法public void detail(){//System.out.println(year + "年" + month + "月" + day + "日");System.out.println(this.year + "年" + this.month + "月" + this.day + "日");}//setter and getterpublic void setYear(int year){// 设立关卡(有时间可以设立关卡)this.year = year;}public int getYear(){return year;}public void setMonth(int month){// 设立关卡(有时间可以设立关卡)this.month = month;}public int getMonth(){return month;}public void setDay(int day){// 设立关卡(有时间可以设立关卡)this.day = day;}public int getDay(){return day;}}
2.4 小结:
只要负责调用的方法a和被调用的方法b在同一个类当中:<br /> this. 可以省略<br /> 类名. 可以省略
第十一章 面向对象三大特征:继承
1. 继承的作用、相关特性
1.1 继承的作用
基本作用:子类继承父类,代码可以得到复用。(这个不是重要的作用,是基本作用。)
主要(重要)作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。
1.2 继承的相关特性
- B类继承A类,则称A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、派生类、扩展类。- java 中的继承只支持**单继承**,不支持多继承,C++中支持多继承,这也是 java 体现简单性的一点,换句话说,java 中不允许这样写代码:class B extends A,C{ } 这是错误的。- 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,例如:class C extends B,class B extends A,也就是说,C 直接继承 B,其实 C 还间接继承 A。- java 中规定,子类继承父类,除构造方法不能继承之外,剩下都可以继承。但是私有的属性无法在子类中直接访问。(父类中private修饰的不能在子类中直接访问。可以通过间接的手段来访问;即通过getter、setter方法来间接访问)- java 中的类没有显式的继承任何类,则默认继承 Object类,Object类是java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有Object类型中所有的特征。- 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它们之间的耦合度非常高Account 类发生改变之后会马上影响到 CreditAccount 类。
本质上,子类继承父类之后,是将父类继承过来的方法归为自己所有。 实际上调用的也不是父类的方法,是他子类自己的方法(因为已经继承过来了 就属于自己的)。
1.3 print进一步分析
public class Test{// 静态变量static Student stu = new Student();// 入口public static void main(String[] args){//拆分为两行Student s = Test.stu;s.exam();//合并代码Test.stu.exam();//和上面的代码进行对比,可以知道out对象是System类的静态变量,//print()是out的实例方法System.out.println("Hello World!");}}class Student{// 实例方法public void exam(){System.out.println("考试。。。。。");}}
1.4 object类的一些方法
/*public class Object {// 注意:当源码当中一个方法以“;”结尾,并且修饰符列表中有“native”关键字// 表示底层调用C++写的dll程序(dll动态链接库文件)private static native void registerNatives();// 静态代码块static {// 调用registerNatives()方法。registerNatives();}// 无参数构造方法@HotSpotIntrinsicCandidatepublic Object() {}@HotSpotIntrinsicCandidatepublic final native Class<?> getClass();@HotSpotIntrinsicCandidatepublic native int hashCode();// public: 公开的// boolean: 方法的返回值类型// equals(相等):方法名// (Object obj) 形参public boolean equals(Object obj) {//方法体return (this == obj);}// 已有对象a,想创建一个和a一模一样的对象,可以调用这个克隆方法。@HotSpotIntrinsicCandidateprotected native Object clone() throws CloneNotSupportedException;// toString()方法执行结束之后返回一个字符串。public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}@HotSpotIntrinsicCandidatepublic final native void notify();@HotSpotIntrinsicCandidatepublic final native void notifyAll();public final void wait() throws InterruptedException {wait(0L);}public final native void wait(long timeoutMillis) throws InterruptedException;public final void wait(long timeoutMillis, int nanos) throws InterruptedException {if (timeoutMillis < 0) {throw new IllegalArgumentException("timeoutMillis value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {timeoutMillis++;}wait(timeoutMillis);}@Deprecated(since="9")protected void finalize() throws Throwable { }}*/public class ExtendsTest05 {// ExtendsTest05默认继承Object// ExtendsTest05类当中是有toString()方法// 不过toString()方法是一个实例方法,需要创建对象才能调用。/*public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}*/public static void main(String[] args){// 分析这个代码可以执行吗?//ExtendsTest05.toString();// 先new对象ExtendsTest05 et = new ExtendsTest05();String retValue = et.toString();// 2f92e0f4 可以“等同”看做对象在堆内存当中的内存地址。// 实际上是内存地址经过“哈希算法”得出的十六进制结果。System.out.println(retValue); // ExtendsTest05@2f92e0f4// 创建对象Product pro = new Product();String retValue2 = pro.toString();System.out.println(retValue2); // Product@5305068a// 以上两行代码能否合并为一行!!!可以System.out.println(pro.toString()); //Product@5305068a// 如果直接输出“引用”呢???????System.out.println(pro); //Product@5305068aSystem.out.println(100);System.out.println(true);// Product@5305068aSystem.out.println(pro); // println方法会自动调用pro的toString()方法。}}class Product{/*public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}*/}
2. 方法覆盖
2.1 什么时候使用方法覆盖
父类中的方法无法满足子类的业务需求,子类有必要对继承过来的方法进行覆盖。
2.2 方法覆盖的构成条件
第一:有继承关系的两个类
第二:具有相同方法名、返回值类型、形式参数列表(返回值类型可以不同)
// 父类class MyClass1{public Animal getAnimal(){return null;}}// 子类class MyClass2 extends MyClass1{// 重写父类的方法/*public Animal getAnimal(){return null;}*/// 重写的时候返回值类型由Animal变成了Cat,变小了。(可以,java中允许)/*public Cat getAnimal(){return null;}*/// 重写的时候返回值类型由Animal变成了Object。变大了。(不行,java中不允许)/*public Object getAnimal(){return null;}*/}
第三:访问权限不能更低。<br /> 第四:抛出异常不能更多。
什么条件满足之后能够构成方法重载overload? 条件一:在同一个类当中 条件二:方法名相同 条件三:参数列表不同(个数、顺序、类型)
注意事项:
注意1:方法覆盖只是针对于方法,和属性无关。
注意2:私有方法无法覆盖。
注意3:构造方法不能被继承,所以构造方法也不能被覆盖。
注意4:方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。
2.3 object类的toString方法覆盖
/*关于Object类中的toString()方法1、toString()方法的作用是什么?作用:将“java对象”转换成“字符串的形式”。2、Object类中toString()方法的默认实现是什么?public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}toString: 方法名的意思是转换成String含义:调用一个java对象的toString()方法就可以将该java对象转换成字符串的表示形式。3、那么toString()方法给的默认实现够用吗?*/public class OverrideTest04{public static void main(String[] args){// 创建一个日期对象MyDate t1 = new MyDate();// 调用toString()方法(将对象转换成字符串形式。)// 问:你对这个输出结果满意吗?不满意,希望输出:xxxx年xx月xx日// 重写MyDate的toString()方法之前的结果//System.out.println(t1.toString()); //MyDate@28a418fc// 重写MyDate的toString()方法之后的结果System.out.println(t1.toString());// 大家是否还记得:当输出一个引用的时候,println方法会自动调用引用的toString方法。System.out.println(t1);MyDate t2 = new MyDate(2008, 8, 8);System.out.println(t2); //2008年8月8日//创建学生对象Student s = new Student(1111, "zhangsan");// 重写toString()方法之前//System.out.println(s); //Student@87aac27// 重写toString()方法之后// 输出一个学生对象的时候,可能更愿意看到学生的信息,不愿意看到对象的内存地址。System.out.println(s.toString());System.out.println(s);}}// 日期类class MyDate {private int year;private int month;private int day;public MyDate(){this(1970,1,1);}public MyDate(int year,int month,int day){this.year = year;this.month = month;this.day = day;}public void setYear(int year){this.year = year;}public int getYear(){return year;}public void setMonth(int month){this.month = month;}public int getMonth(){return month;}public void setDay(int day){this.day = day;}public int getDay(){return day;}// 从Object类中继承过来的那个toString()方法已经无法满足我业务需求了。// 我在子类MyDate中有必要对父类的toString()方法进行覆盖/重写。// 我的业务要求是:调用toString()方法进行字符串转换的时候,// 希望转换的结果是:xxxx年xx月xx日,这种格式。public String toString() {return year + "年" + month + "月" + day + "日";}}class Student{int no;String name;public Student(int no, String name){this.no = no;this.name = name;}// 重写 方法覆盖public String toString() {return "学号:" + no + ",姓名:" + name;}}
第十二章 面向对象三大特征:多态
1. 向上转型和向下转型的概念
1.1 向上转型
子—->父 (upcasting) :又被称为自动类型转换:Animal a = new Cat();
1.2 向下转型
父—->子 (downcasting) : 又被称为强制类型转换:Cat c = (Cat)a; 需要添加强制类型转换符。
什么时候需要向下转型?
需要调用或者执行子类对象中特有的方法,必须进行向下转型,才可以调用。
向下转型有风险吗?
容易出现ClassCastException(类型转换异常)
怎么避免这个风险?
instanceof运算符,可以在程序运行阶段动态的判断某个引用指向的对象
是否为某一种类型。
养成好习惯,向下转型之前一定要使用instanceof运算符进行判断。
不管是向上转型还是向下转型,首先他们之间必须有继承关系,这样编译器就不会报错。
题外话: 与别人聊天的时候,要专业一些,说向上转型和向下转型,不要说自动类型转换,也不要说强制类型转换,因为自动类型转换和强制类型转换是使用在基本数据类型方面的,在引用类型转换这里只有向上和向下转型。
2. 多态
2.1 多态的基础语法
public class Test01{public static void main(String[] args){Animal a1 = new Animal();a1.move(); //动物在移动!!!Cat c1 = new Cat();c1.move(); //猫走猫步!Bird b1 = new Bird();b1.move(); //鸟儿在飞翔!!!// 代码可以这样写吗?/*1、Animal和Cat之间有继承关系吗?有的。2、Animal是父类,Cat是子类。3、Cat is a Animal,这句话能不能说通?能。4、经过测试得知java中支持这样的一个语法:父类型的引用允许指向子类型的对象。Animal a2 = new Cat();a2就是父类型的引用。new Cat()是一个子类型的对象。允许a2这个父类型引用指向子类型的对象。*/Animal a2 = new Cat();Animal a3 = new Bird();// 没有继承关系的两个类型之间存在转型吗?// 错误: 不兼容的类型: Dog无法转换为Animal// Animal a4 = new Dog();// 调用a2的move()方法/*什么是多态?多种形态,多种状态。分析:a2.move();java程序分为编译阶段和运行阶段。先来分析编译阶段:对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检查语法的时候,会去Animal.class字节码文件中找move()方法,找到了,绑定上move()方法,编译通过,静态绑定成功。(编译阶段属于静态绑定。)再来分析运行阶段:运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行Cat对象的move()方法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定。)多态表示多种形态:编译的时候一种形态。运行的时候另一种形态。*/a2.move(); //cat走猫步!// 调用a3的move()方法a3.move(); //鸟儿在飞翔!!!// ======================================================================Animal a5 = new Cat(); // 底层对象是一只猫。// 分析这个程序能否编译和运行呢?// 分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定。// 只有编译通过的代码才能运行。没有编译,根本轮不到运行。// 错误: 找不到符号// why??? 因为编译器只知道a5的类型是Animal,去Animal.class文件中找catchMouse()方法// 结果没有找到,所以静态绑定失败,编译报错。无法运行。(语法不合法。)//a5.catchMouse();// 假设代码写到了这里,我非要调用catchMouse()方法怎么办?// 这个时候就必须使用“向下转型”了。(强制类型转换)// 以下这行代码为啥没报错????// 因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系。所以没报错。Cat x = (Cat)a5;x.catchMouse(); //猫正在抓老鼠!!!!// 向下转型有风险吗?Animal a6 = new Bird(); //表面上a6是一个Animal,运行的时候实际上是一只鸟儿。/*分析以下程序,编译报错还是运行报错???编译器检测到a6这个引用是Animal类型,而Animal和Cat之间存在继承关系,所以可以向下转型。编译没毛病。运行阶段,堆内存实际创建的对象是:Bird对象。在实际运行过程中,拿着Bird对象转换成Cat对象就不行了。因为Bird和Cat之间没有继承关系。运行时出现异常,这个异常和空指针异常一样非常重要,也非常经典:java.lang.ClassCastException:类型转换异常。java.lang.NullPointerException:空指针异常。这个也非常重要。*///Cat y = (Cat)a6;//y.catchMouse();// 怎么避免ClassCastException异常的发生???/*新的内容,运算符:instanceof (运行阶段动态判断)第一:instanceof可以在运行阶段动态判断引用指向的对象的类型。第二:instanceof的语法:(引用 instanceof 类型)第三:instanceof运算符的运算结果只能是:true/false第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。假设(c instanceof Cat)为true表示:c引用指向的堆内存中的java对象是一个Cat。假设(c instanceof Cat)为false表示:c引用指向的堆内存中的java对象不是一个Cat。程序员要养成一个好习惯:任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof 运算符进行判断。(java规范中要求的;因为开发过程中代码可能是别人写的,某一方法的参数列表可能是不同的对象)这样可以很好的避免:ClassCastException*/System.out.println(a6 instanceof Cat); //falseif(a6 instanceof Cat){ // 如果a6是一只CatCat y = (Cat)a6; // 再进行强制类型转换y.catchMouse();}}}
2.2 多态在开发中的作用
降低程序的耦合度,提高程序的扩展力。
public class Master{
public void feed(Dog d){}
public void feed(Cat c){}
}
以上的代码中表示:Master和Dog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。
public class Master{
public void feed(Pet pet){
pet.eat();
}
}
以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类。
这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。
面向对象的三大特征:封装、继承、多态 一环扣一环,有了封装,有了这种整体的概念之后,对象和对象之间产生了继承,有了继承之后,才有了方法的覆盖和多态。
软件开发原则: 七大原则最基本的原则:OCP(对扩展开放,对修改关闭) 目的是:降低程序耦合度,提高程序扩展力。面向抽象编程,不建议面向具体编程。
2.3 方法覆盖与多态
1、方法覆盖需要和多态机制联合起来使用才有意义。
Animal a = new Cat();
a.move();
要的是什么效果?
编译的时候move()方法是Animal的。
运行的时候自动调用到子类重写move()方法上。
假设没有多态机制,只有方法覆盖机制,你觉得有意义吗?
没有多态机制的话,方法覆盖可有可无。
没有多态机制,方法覆盖也可以没有,如果父类的方法无法满足
子类业务需求的时候,子类完全可以定义一个全新的方法。
方法覆盖和多态不能分开。
2、静态方法存在方法覆盖吗?<br /> 多态自然就和对象有关系了。<br /> 而静态方法的执行不需要对象。<br /> 所以,一般情况下,我们会说静态方法“不存在”方法覆盖。<br /> 不探讨静态方法的覆盖。
public class OverrideTest05{public static void main(String[] args){// 静态方法可以使用“引用.”来调用吗?可以// 虽然使用“引用.”来调用,但是和对象无关。Animal a = new Cat(); //多态// 静态方法和对象无关。// 虽然使用“引用.”来调用。但是实际运行的时候还是:Animal.doSome()//可以这样理解:引用只能调用父类的,想调用子类的必须用类名来调a.doSome();Animal.doSome();Cat.doSome();}}class Animal{// 父类的静态方法public static void doSome(){System.out.println("Animal的doSome方法执行!");}}class Cat extends Animal{// 尝试在子类当中对父类的静态方法进行重写public static void doSome(){System.out.println("Cat的doSome方法执行!");}}
3、方法覆盖“返回值类型”
对于返回值类型是基本数据类型来说,必须一致。
对于返回值类型是引用数据类型来说,重写之后返回值类型可以变的更小(但意义不大,实际应用 中没人这样写)
public class OverrideTest07{public static void main(String[] args){// 一般重写的时候都是复制粘贴;不动、不改}}class Animal{/*public double sum(int a, int b){return a + b;}*//*public long sum(int a, int b){return a + b;}*//*public int sum(int a, int b){return a + b;}*/}class Cat extends Animal{// 重写// 错误: Cat中的sum(int,int)无法覆盖Animal中的sum(int,int)/*public int sum(int a, int b){return a + b;}*//*public double sum(int a, int b){return a + b;}*///错误: Cat中的sum(int,int)无法覆盖Animal中的sum(int,int)/*public long sum(int a, int b){return a + b;}*/}// 父类class MyClass1{public Animal getAnimal(){return null;}}// 子类class MyClass2 extends MyClass1{// 重写父类的方法/*public Animal getAnimal(){return null;}*/// 重写的时候返回值类型由Animal变成了Cat,变小了。(可以,java中允许)/*public Cat getAnimal(){return null;}*/// 重写的时候返回值类型由Animal变成了Object。变大了。(不行,java中不允许)/*public Object getAnimal(){return null;}*/}
