继承 extends
面向对象编程的三个非常重要的特征:继承、封装和多态。
继承: 类之间存在父子关系,子类会继承父类的成员属性和成员方法。
类的继承只能是单继承,子类只能有一个父类(没有母类或其他父类)。
但是,一个父类可以有多个子类。
子类继承父类,实际上是把父类的成员属性和成员方法,拷贝了一份存储在子类中。
如果子类重写了父类的成员属性或成员方法,在子类内部访问时,可通过关键字 super 和 this 来进行区分。
- super 代表父类对象;
- this 代表当前类的对象。访问当前类的属性或方法时,this 可以省略。
每次创建子类对象时,必须优先调用(JVM 隐式调用或手动显式调用)父类的构造方法。(只是调用父类的构造方法,并未创建父类对象)
创建子类对象时,子类默认会调用父类的构造方法,完成父类属性的初始化操作。前提是父类的构造方法没有显式声明,或者声明了无参的构造方法。
如果父类显式声明了包含参数的构造方法,那么 JVM 就无法隐式调用父类的构造方法。此时,就需要在子类中,显式调用父类的构造方法。
new 关键字,每次只会创建一个对象。
创建子类对象时,会开辟一个内存空间存储子类对象,然后执行子类的构造方法,此时,会在子类对象的内存空间开辟一小块区域,优先执行父类的构造方法,
并将父类的基本信息存储在这块小区域中,然后继续执行子类的构造方法。
多态
多态:同一个对象在不同场景下表现出的不同状态或形态。
多态约束:
多态同时也会约束对象的使用场景(功能),一个对象可以使用的功能取决于它的类型。
对于子类对象,它不仅具有父类的功能,还具有自身的功能。当然,前提是子类对象的类型是子类类型,如果是父类类型的子类对象,它就无法使用子类自身的功能。
方法的重载
方法的重载:同一个类中的多个方法,方法名相同,但参数列表(个数、顺序、类型)不同。
构造方法也支持重载。
方法的重写
方法的重写:子类对父类的同名方法进行重写,子类的方法(方法名、参数列表、返回值)和父类一致,只是内部实现的功能不一样。
因为父类的方法对于多个子类来说,具有通用性。如果子类想实现个性化的功能,就可以对父类的方法进行重写。
重写,并不是覆盖掉父类的方法,只是父类的方法在当前场合不太适用。在子类中,仍然可以通过 super 关键字访问被重写的父类方法。
子类无法重写父类的构造方法,因为方法名不一样。
- 子类必须重写抽象类中的抽象方法。
- 实现类必须重写接口中的抽象方法。
- 抽象类和接口中允许存在默认就实现了的默认方法,关键字是 default。
访问权限
在 java 中,访问权限分四种:private、default、protected、public
在 java 的源文件中,只能声明一个 public 的类,且类名要和文件名保持一致。
main 方法:必须是 public,由 JVM 调用,且是 static 静态的。
所谓的访问权限控制,最关键的是弄清楚两点:
- 谁在访问?调用的代码在哪,就是谁在访问。
- 访问谁的?要访问的类、属性或方法,是谁的。
private 私有的
私有的方法或属性,只在当前类的内部可以访问。
default 默认的
默认权限,就是没有显式指定权限。JVM 会默认提供一个权限,这个权限就是包权限(路径权限)。
只在当前包内可访问(子包无法访问)。
protected 受保护的
受保护的方法或属性,在包内、子类可以访问(就算子类在子包中也可访问)。
public 公共的
公共的方法或属性,在任何地方都可访问。
内部类
内部类:在一个类的内部声明的另一个类。内部类可当作外部类的属性使用。
外部类:源码文件中的不是内部类的类,都是外部类。
java 中不允许外部类使用 private 和 protected 修饰符。
final 关键字
final 可用于修饰变量、属性、方法、类。
- 修饰变量、属性时,表示变量、属性只能初始化一次,之后不允许修改。
- 修复方法时,表示该方法不能被子类重写。
- 修饰类时,表示该类没有子类,即不能被继承。
- 修饰方法的参数时,表示方法的参数值无法修改,不能改变形参的值。
- final 不能修饰构造方法。
示例
class A {
String name = "a";
// 构造方法
A(String name) {
System.out.println("Parent..." + name);
}
void test2() {
System.out.println("父类的方法test2...");
}
}
// 子类 B 继承了父类 A
class B extends A {
String name = "b";
// 构造方法
B(String name) {
super(name); // 父类的构造方法,必须在第一行优先执行。
System.out.println("构造方法重载" + name);
}
// 子类特有的方法
void test() {
System.out.println(super.name); // 如果子类中重写了 name,默认访问的是父类的 name,否则访问的是继承来的 name。
System.out.println(this.name); // 如果子类中重写了 name,默认访问的是子类自身的 name,否则访问的是继承来的 name。
System.out.println(name); // 如果子类中重写了 name,默认访问的是子类自身的 name,否则访问的是继承来的 name。
}
// 方法的重载
void test(String s) {
System.out.println("方法的重载1 " + s);
}
// 方法的重载
void test(int s) {
System.out.println("方法的重载2 " + s);
}
// 方法的重写
void test2() {
super.test2(); // 调用父类的同名方法
System.out.println("子类的方法test2...");
}
// 内部类 innerC
protected class InnerC {
void innerM() {
System.out.println("这是内部类的方法");
}
}
}
B b = new B("abc");
b.test();
b.test("reload");
b.test2();
// 内部类的使用
B.InnerC c = b.new InnerC();
c.innerM();