基本介绍
继承是 Java 中一般到特殊的关系,是一种子类到父类的关系
- 被继承的类称为:父类/超类。
- 继承父类的类称为:子类。
继承的作用:
- 提高代码的复用,相同代码可以定义在父类中
- 子类继承父类,可以直接使用父类这些代码(相同代码重复利用)
- 子类得到父类的属性(成员变量)和行为(方法),还可以定义自己的功能,子类更强大
继承的特点:
- 子类的全部构造器默认先访问父类的无参数构造器,再执行自己的构造器
- 单继承:一个类只能继承一个直接父类
- 多层继承:一个类可以间接继承多个父类(家谱)
- 一个类可以有多个子类
- 一个类要么默认继承了 Object 类,要么间接继承了 Object 类,Object 类是 Java 中的祖宗类
继承的格式:
子类 extends 父类{
}
子类不能继承父类的东西:
- 子类不能继承父类的构造器,子类有自己的构造器
- 子类是不能可以继承父类的私有成员的,可以反射暴力去访问继承自父类的私有成员
- 子类是不能继承父类的静态成员的,子类只是可以访问父类的静态成员,父类静态成员只有一份可以被子类共享访问,共享并非继承
public class ExtendsDemo {
public static void main(String[] args) {
Cat c = new Cat();
// c.run();
Cat.test();
System.out.println(Cat.schoolName);
}
}
class Cat extends Animal{
}
class Animal{
public static String schoolName ="seazean";
public static void test(){}
private void run(){}
}
变量访问
继承后成员变量的访问特点:就近原则,子类有找子类,子类没有找父类,父类没有就报错
如果要申明访问父类的成员变量可以使用:super.父类成员变量,super指父类引用
public class ExtendsDemo {
public static void wmain(String[] args) {
Wolf w = new Wolf();w
w.showName();
}
}
class Wolf extends Animal{
private String name = "子类狼";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部name
System.out.println(this.name); // 子类对象的name
System.out.println(super.name); // 父类的
System.out.println(name1); // 父类的
//System.out.println(name2); // 报错。子类父类都没有
}
}
class Animal{
public String name = "父类动物名称";
public String name1 = "父类";
}
方法访问
子类继承了父类就得到了父类的方法,可以直接调用,受权限修饰符的限制,也可以重写方法
方法重写:子类重写一个与父类申明一样的方法来覆盖父类的该方法
方法重写的校验注解:@Override
- 方法加了这个注解,那就必须是成功重写父类的方法,否则报错
- @Override 优势:可读性好,安全,优雅
子类可以扩展父类的功能,但不能改变父类原有的功能,重写有以下三个限制:
- 子类方法的访问权限必须大于等于父类方法
- 子类方法的返回类型必须是父类方法返回类型或为其子类型
- 子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型
继承中的隐藏问题:
- 子类和父类方法都是静态的,那么子类中的方法会隐藏父类中的方法
- 在子类中可以定义和父类成员变量同名的成员变量,此时子类的成员变量隐藏了父类的成员变量,在创建对象为对象分配内存的过程中,隐藏变量依然会被分配内存
public class ExtendsDemo {
public static void main(String[] args) {
Wolf w = new Wolf();
w.run();
}
}
class Wolf extends Animal{
@Override
public void run(){}//
}
class Animal{
public void run(){}
}
面试问题
-
为什么子类构造器会先调用父类构造器?
- 子类的构造器的第一行默认 super() 调用父类的无参数构造器,写不写都存在
- 子类继承父类,子类就得到了父类的属性和行为。调用子类构造器初始化子类对象数据时,必须先调用父类构造器初始化继承自父类的属性和行为
- 参考 JVM → 类加载 → 对象创建
class Animal{ public Animal(){ System.out.println("==父类Animal的无参数构造器=="); } } class Tiger extends Animal{ public Tiger(){ super(); // 默认存在的,根据参数去匹配调用父类的构造器。 System.out.println("==子类Tiger的无参数构造器=="); } public Tiger(String name){ //super(); 默认存在的,根据参数去匹配调用父类的构造器。 System.out.println("==子类Tiger的有参数构造器=="); } } -
为什么 Java 是单继承的?
答:反证法,假如 Java 可以多继承,请看如下代码:
class A{ public void test(){ System.out.println("A"); } } class B{ public void test(){ System.out.println("B"); } } class C extends A , B { public static void main(String[] args){ C c = new C(); c.test(); // 出现了类的二义性!所以Java不能多继承!! } }
继承后 super 调用父类构造器,父类构造器初始化继承自父类的数据。
总结与拓展:
- this 代表了当前对象的引用(继承中指代子类对象):this.子类成员变量、this.子类成员方法、**this(...)**可以根据参数匹配访问本类其他构造器。
- super 代表了父类对象的引用(继承中指代了父类对象空间):super.父类成员变量、super.父类的成员方法、super(...)可以根据参数匹配访问父类的构造器
注意:
- this(...) 借用本类其他构造器,super(...) 调用父类的构造器。
- this(...) 或 super(...) 必须放在构造器的第一行,否则报错!
- this(...) 和 super(...) 不能同时出现在构造器中,因为构造函数必须出现在第一行上,只能选择一个。
public class ThisDemo {
public static void main(String[] args) {
// 需求:希望如果不写学校默认就是”张三“!
Student s1 = new Student("天蓬元帅", 1000 );
Student s2 = new Student("齐天大圣", 2000, "清华大学" );
}
}
class Study extends Student {
public Study(String name, int age, String schoolName) {
super(name , age , schoolName) ;
// 根据参数匹配调用父类构造器
}
}
class Student{
private String name ;
private int age ;
private String schoolName ;
public Student() {
}
public Student(String name , int age){
// 借用兄弟构造器的功能!
this(name , age , "张三");
}
public Student(String name, int age, String schoolName) {
this.name = name;
this.age = age;
this.schoolName = schoolName;
}
// .......get + set
}
final
基本介绍
final 用于修饰:类,方法,变量
- final 修饰类,类不能被继承了,类中的方法和变量可以使用
- final 可以修饰方法,方法就不能被重写
- final 修饰变量总规则:变量有且仅能被赋值一次
面试题:final 和 abstract 的关系是互斥关系,不能同时修饰类或者同时修饰方法!
修饰变量
静态变量
final 修饰静态成员变量,变量变成了常量
常量:有 public static final 修饰,名称字母全部大写,多个单词用下划线连接。
final 修饰静态成员变量可以在哪些地方赋值:
-
定义的时候赋值一次
-
可以在静态代码块中赋值一次
public class FinalDemo {
//常量:public static final修饰,名称字母全部大写,下划线连接。
public static final String SCHOOL_NAME = "张三" ;
public static final String SCHOOL_NAME1;
static{
//SCHOOL_NAME = "java";//报错
SCHOOL_NAME1 = "张三1";
//SCHOOL_NAME1 = "张三2"; // 报错,第二次赋值!
}
}
实例变量
final 修饰变量的总规则:有且仅能被赋值一次
final 修饰实例成员变量可以在哪些地方赋值 1 次:
- 定义的时候赋值一次
- 可以在实例代码块中赋值一次
- 可以在每个构造器中赋值一次
public class FinalDemo {
private final String name = "张三" ;
private final String name1;
private final String name2;
{
// 可以在实例代码块中赋值一次。
name1 = "张三1";
}
//构造器赋值一次
public FinalDemo(){
name2 = "张三2";
}
public FinalDemo(String a){
name2 = "张三2";
}
public static void main(String[] args) {
FinalDemo f1 = new FinalDemo();
//f1.name = "张三1"; // 第二次赋值 报错!
}
}



