1. 关键字:this
1.1 this是什么?
-
在Java中,this关键字不算难理解,它的作用和其词义很接近。
-
它在方法(准确的说是实例方法或非static的方法)内部使用,表示调用该方法的对象
-
它在构造器内部使用,表示该构造器正在初始化的对象。
-
-
this可以调用的结构:成员变量、方法和构造器
1.2 什么时候使用this
1.2.1 实例方法或构造器中使用当前对象的成员
在实例方法或构造器中,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的可读性。不过,通常我们都习惯省略this。
但是,当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加this来表明该变量是类的成员变量。即:我们可以用this来区分成员变量
和局部变量
。比如:
1.2.2 同一个类中构造器互相调用
this可以作为一个类中构造器相互调用的特殊格式。
-
this():调用本类的无参构造器
-
this(实参列表):调用本类的有参构造器
public class Student {
private String name;
private int age;
// 无参构造
public Student() {
// this("",18);//调用本类有参构造器
}
// 有参构造
public Student(String name) {
this();//调用本类无参构造器
this.name = name;
}
// 有参构造
public Student(String name,int age){
this(name);//调用本类中有一个String参数的构造器
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getInfo(){
return "姓名:" + name +",年龄:" + age;
}
}
注意:
-
不能出现递归调用。比如,调用自身构造器。
-
推论:如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了"this(形参列表)"
-
-
this()和this(实参列表)只能声明在构造器首行。
-
推论:在类的一个构造器中,最多只能声明一个"this(参数列表)"
-
2. 面向对象特征二:继承(Inheritance)
2.1 继承的概述
2.1.1 生活中的继承
-
财产继承:
2.1.2 Java中的继承
角度一:从上而下
说明:Student类继承了父类Person的所有属性和方法,并增加了一个属性school。Person中的属性和方法,Student都可以使用。
角度二:从下而上
2.1.3 继承的好处
-
继承的出现减少了代码冗余,提高了代码的复用性。
-
继承的出现,更有利于功能的扩展。
-
继承的出现让类与类之间产生了
is-a
的关系,为多态的使用提供了前提。-
继承描述事物之间的所属关系,这种关系是:
is-a
的关系。可见,父类更通用、更一般,子类更具体。
-
注意:不要仅为了获取其他类中某个功能而去继承!
2.2 继承的语法
2.2.1 继承中的语法格式
通过 extends
关键字,可以声明一个类B继承另外一个类A,定义格式如下:
[修饰符] class 类A {
...
}[修饰符] class 类B extends 类A {
...
}
2.2.2 继承中的基本概念
类B,称为子类、派生类(derived class)、SubClass
类A,称为父类、超类、基类(base class)、SuperClass
2.3 代码举例
1、父类
/*
* 定义动物类Animal,做为父类
*/
public class Animal {
// 定义name属性
String name;
// 定义age属性
int age;
// 定义动物的吃东西方法
public void eat() {
System.out.println(age + "岁的"
+ name + "在吃东西");
}
}
2、子类
/*
* 定义猫类Cat 继承 动物类Animal
*/
public class Cat extends Animal {
int count;//记录每只猫抓的老鼠数量
// 定义一个猫抓老鼠的方法catchMouse
public void catchMouse() {
count++;
System.out.println("抓老鼠,已经抓了"
+ count + "只老鼠");
}
}
3、测试类
public class TestCat {
public static void main(String[] args) {
// 创建一个猫类对象
Cat cat = new Cat();
// 为该猫类对象的name属性进行赋值
cat.name = "Tom";
// 为该猫类对象的age属性进行赋值
cat.age = 2;
// 调用该猫继承来的eat()方法
cat.eat();
// 调用该猫的catchMouse()方法
cat.catchMouse();
cat.catchMouse();
cat.catchMouse();
}
}
2.4 继承性的细节说明
1、子类会继承父类所有的实例变量和实例方法
从类的定义来看,类是一类具有相同特性的事物的抽象描述。父类是所有子类共同特征的抽象描述。而实例变量和实例方法就是事物的特征,那么父类中声明的实例变量和实例方法代表子类事物也有这个特征。
-
当子类对象被创建时,在堆中给对象申请内存时,就要看子类和父类都声明了什么实例变量,这些实例变量都要分配内存。
-
当子类对象调用方法时,编译器会先在子类模板中看该类是否有这个方法,如果没找到,会看它的父类甚至父类的父类是否声明了这个方法,遵循
从下往上
找的顺序,找到了就停止,一直到根父类都没有找到,就会报编译错误。
所以继承意味着子类的对象除了看子类的类模板还要看父类的类模板。
2、子类不能直接访问父类中私有的(private)的成员变量和方法
子类虽会继承父类私有(private)的成员变量,但子类不能对继承的私有成员变量直接进行访问,可通过继承的get/set方法进行访问。如图所示:
3、在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”
子类在继承父类以后,还可以定义自己特有的方法,这就可以看做是对父类功能上的扩展。
4、Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
说明:
子类和父类是一种相对的概念
顶层父类是Object类。所有的类默认继承Object,作为父类。
5、一个父类可以同时拥有多个子类
class A{}
class B extends A{}
class D extends A{}
class E extends A{}
6、Java只支持单继承,不支持多重继承
public class A{}
class B extends A{}
//一个类只能有一个父类,不可以有多个直接父类。
class C extends B{} //ok
class C extends A,B... //error
3. 方法的重写(override/overwrite)
父类的所有方法子类都会继承,但是当某个方法被继承到子类之后,子类觉得父类原来的实现不适合于自己当前的类,该怎么办呢?子类可以对从父类中继承来的方法进行改造,我们称为方法的重写 (override、overwrite)
。也称为方法的重置
、覆盖
。
在程序执行时,子类的方法将覆盖父类的方法。
3.1 方法重写举例
比如新的手机增加来电显示头像的功能,代码如下:
public class Phone {
public void sendMessage(){
System.out.println("发短信");
}
public void call(){
System.out.println("打电话");
}
public void showNum(){
System.out.println("来电显示号码");
}
}
//SmartPhone:智能手机
public class SmartPhone extends Phone{
//重写父类的来电显示功能的方法
@Override
public void showNum(){
//来电显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
//重写父类的通话功能的方法
@Override
public void call() {
System.out.println("语音通话 或 视频通话");
}
}
public class TestOverride {
public static void main(String[] args) {
// 创建子类对象
SmartPhone sp = new SmartPhone();
// 调用父类继承而来的方法
sp.call();
// 调用子类重写的方法
sp.showNum();
}
}
@Override使用说明:
写在方法上面,用来检测是不是满足重写方法的要求。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。建议保留,这样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这是一个重写的方法。
3.2 方法重写的要求
-
子类重写的方法
必须
和父类被重写的方法具有相同的方法名称
、参数列表
。 -
子类重写的方法的返回值类型
不能大于
父类被重写的方法的返回值类型。(例如:Student < Person)。
注意:如果返回值类型是基本数据类型和void,那么必须是相同 -
子类重写的方法使用的访问权限
不能小于
父类被重写的方法的访问权限。(public > protected > 缺省 > private)
注意:① 父类私有方法不能重写 ② 跨包的父类缺省的方法也不能重写