目录
1、继承
1.1 继承概念
面向对象的第二大特征:继承。
继承的主要作用 在对已有基础上进行复用以及进行功能的扩充。
人 { 吃、喝、睡 }
工人 {吃、喝、睡、 搬砖}
1.2 继承语法规则
class 子类 extend 父类 {}
C++ 中 称之为 基类 和 派生类
(1) Java中 一个子类只能继承一个父类
(而C++/Python等语言支持多继承,java是单继承的), 常见 C++菱形继承
当然为了解决单继承的问题,我们引入了接口.
(2)子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用.
(3)子类对象在进行实例化前一定会首先实例化父类对象。
默认调用父类的构造方法后再调用子类构造方法进行子类对象初始化。
注意:实际上在子类的构造方法之中,相当于隐含了一个语句 super();
(4)如果父类里没有提供无参构造,那么这个时候就必须使用super()明确指明你要调用
的父类构造方法。不管如何操作,一定要先实例化父类对象。
(5) Java不允许多重继承,但是允许多层继承,并且子类会继承父类的所有结构。
(包含私有属性、构造方法、普通方法)
需要注意的是,子类从父类中继承的包括:
所有的非私有操作属于显示继承(可以直接调用),
所有的私有操作属于隐式继承,除非对外提供方法(例如setter或getter) ,否则子类无法进行访问
1.3 super 关键字
父类也被称为超类(Super Class)
1.3.1 super 理解
(1)super 关键字代表父类对象的引用 , this 代表当前对象的引用
当子类继承父类的时候,首先会帮助父类进行构造
(2) 在子类的构造方法内部,调用父类的构造方法
类似于super()代表显示调用父类的构造方法,
所以构造方法不是被继承的,而是在子类构造方法中被显示调用. (子类构造,隐含super() )
(3) 需要在子类内部调用父类方法时,super 表示获取到父类对象的引用,可以使用super 关键字.
1.3.2 super的常见用法
用法一
使用 super关键字来调用父类的构造器方法
如果子类的构造方法中没有显示的调用父类构造方法,则系统默认调用父类无参数的构造方法,
此时需要注意:
如果子类的构造方法中既没有显示的调用父类构造方法,
父类中又没有无参的构造方法,则此时编译出错,
所以如果我们在父类中没有定义无参的构造函数,而定义了有参的构造函数时,
则子类的构造函数(不管多少个)中都必须显示调用父类的有参构造函数,
且这个显式调用必须放在子类的构造函数的第一行,且只能调用一次。
class Person {
public String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void printHello(){
System.out.println("I am you fathor");
}
}
class Student extends Person{
public String name;
private int age;
public Student(String name, int age) {
//此句必须放在第一行,且此句只能放在构造方法中,且每个子类构造只能调用一次,
super(name,age); // 父类无默认构造,需显示调用父类构造
this.name = name;
this.age = age;
super.printHello();
}
@Override
public String toString(){
return "Student{" +
"name:'" + name + '\'' +
", age:" +age +
'}';
}
}
用法二
使用 super 来调用父类的 非静态 以及 非私有 的成员方法
和访问 非静态 以及 非私有的成员变量
(1)父类中没有无参构造函数,不管定义几个构造函数,都必须显示调用父类中的有参构造函数,
(2)super不能访问父类中私有的实例成员变量和调用私有的实例成员方法
同时也不能访问父类中静态的成员变量和调用静态的成员方法
class Person {
public String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void printHello(){
System.out.println("I am you fathor");
}
}
class Student extends Person{
public String name;
private int age;
public Student(String name, int age) {
//所以父类中的构造方法并不是被继承的,而是在子类的中被显示调用的
super(name,age);
//super访问父类的实例成员变量
super.name = "我是你老豆";
this.name = name;
this.age = age;
//super访问父类的实例成员方法
super.printHello();
}
@Override
public String toString(){
return "Student{" +
"name:'" + name + '\'' +
", age:" +age +
'}';
}
}
super关键字的总结
super不能访问父类中私有的实例成员变量和调用私有的实例成员方法
同时也不能访问父类中静态的成员变量和调用静态的成员方法
super关键字不能用在静态方法中,与this相似,
super关键字代表父类对象的引用,而静态是不依赖对象的
super调用父类构造方法只能用在构造方法中,不能在成员方法中调用父类构造方法,
且只能放在第一行,并且只能调用一次
之前我们还学过this关键字,那么在此我们来做下对比
1.3.3 比较this 和 super
this | super |
---|---|
当前对象的引用 | 父类对象的引用 |
访问当前对象 属性和方法 | 子类访问父类属性和方法 |
先访问当前对象,若无,访问父类 属性和方法 | 子类直接访问父类属性和方法 |
先访问当前对象任意权限,不可访问 静态成员 | 子类直接访问父类属性和方法,不能直接访问私有成员以及静态成员 |
2、覆写(override)
子类定义了与父类相同的方法或属性的时候,
这样的操作就称为覆写(override)
2.1 方法的覆写
子类定义了与父类方法名称、参数类型及个数完全相同的方法。但是被覆写不能够拥有比父类更为严格的访问控制权限
class Person{
public void print(){
System.out.println("Person");
}
}
class Student extends Person{
public void print(){
System.out.println("Student");
}
}
public class Test{
public static void main(String[] args) {
new Student().print(); // new Student
}
}
进行覆写操作的时候注意关注以下两点:
- 当前使用的对象是通过哪个类new的。
- 当调用某个方法,如果该方法已经被子类所覆写了,
那么调用的一定是被覆写过的方法。
被覆写不能够拥有比父类更为严格的访问控制权限
如果现在父类方法使用了private定义,
那么就表示该方法只能被父类使用,子类无法使用。
换而言之,子类根本就不知道类有这样的方法。
这个时候该方法只是子类定义的新方法而已,和父类的方法没有任何关系
class Person{
public void fun(){
this.print();
}
//如果现在⽗类⽅方法使用了private定义,只能被父类使用,子类无法使用
private void print(){
System.out.println("Person");
}
}
class Student extends Person{
//这个时候该方法只是子类定义的新方法而已,和父类的方法没有任何关系
public void print(){
System.out.println("Student");
}
}
public class Test{
public static void main(String[] args) {
new Student().fun();
}
}
2.2 属性的复写
当子类定义了和父类属性名称完全相同的属性的时候,就成为属性的覆盖。
范例例:属性覆盖
class Person{
public String info = "Person";
}
class Student extends Person{
// 按照就近取⽤用原则,肯定找被覆盖的属性。
public String info = "Student";
}
public class Test{
public static void main(String[] args) {
System.out.println(new Student().info);
}
}
这种操作本身没有任何意义,其核心的原因在于:
类中的属性都要求使用private封装,一旦封装了,
子类不知道父类具有什么属性,那么也就不存在属性覆盖的问题了。
3、final 关键字
面向对象,被修饰的无非主要为三种,属性,方法、类
3.1 final 修饰属性
final成员变量表示常量,只能被赋值一次,赋值后值不再修改。
基本数据类型 --- 不可改值
引用数据类型 --- 不可改引用 (地址)
final修饰一个成员属性,必须要显示初始化。
(1)在变量声明的时候初始化;
(2)在声明变量的时候不赋初值,在构造函数中进行初始化;
面试题:请解释重载(overload)和覆写(override)的区别?
3.2 final 修饰方法
父类的final方法不能被子类所覆盖的,
即子类是不能存在和父类相同方法。
类的private方法会隐式地被为final修饰
注意
父类的private方法,将会导致子类中不能直接继承到此方法,
因而可以存在与父类相同方法
3.3 final 修饰类
final修饰一个类时,表明这个类不能被继承。
如果一个类你永远不会让他被继承,就可以用final进行修饰。
在使用final修饰类的时候,要注意谨慎选择,
除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
final 关键字的功能是 限制 类被继承
常见String 类