第九章 面向对象三大特征:封装
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); // 1111111
System.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 getter
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;
}
}
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();
}
// 无参数构造方法
@HotSpotIntrinsicCandidate
public Object() {}
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
@HotSpotIntrinsicCandidate
public native int hashCode();
// public: 公开的
// boolean: 方法的返回值类型
// equals(相等):方法名
// (Object obj) 形参
public boolean equals(Object obj) {
//方法体
return (this == obj);
}
// 已有对象a,想创建一个和a一模一样的对象,可以调用这个克隆方法。
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
// toString()方法执行结束之后返回一个字符串。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
@HotSpotIntrinsicCandidate
public final native void notify();
@HotSpotIntrinsicCandidate
public 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@5305068a
System.out.println(100);
System.out.println(true);
// Product@5305068a
System.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); //false
if(a6 instanceof Cat){ // 如果a6是一只Cat
Cat 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;
}
*/
}