目录
子类:又叫派生类
父类:又叫基类/超类
继承:就是对共性的抽取,从而达到对代码的复用【重复使用】
在Java中,只能继承一个类,不能多继承
当子类继承父类之后,就会把父类中的成员变量和成员方法全部继承到子类中
私有的成员也可以被继承,但是不能被访问。要想被访问,父类的成员中要提供公开的方法。
子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
先去子类中找,找到则访问,找不到再去父类中找,找到则访问,否则编译报错。
若子类和父类都有此成员,就近原则,优先访问子类。
子类继承父类中的所有成员变量和成员方法,父类中的puiblic修饰的成员子类都能直接访问
当父类和子类中拥有同名的成员变量时,遵循就近原则,会优先访问子类自己的
非要访问父类的,前面要加个【super点号】
和访问父类中的成员变量一样
当父类和子类中拥有同名的成员方法时,遵循就近原则,会优先访问子类自己的
非要访问父类的,前面要加个【super点号】
重载:可以发生在一个类里的多个方法之间,也可以发生在继承当中
重写:在子类中把父类本身有的方法重新写一遍
访问权限由大到小:【public > protected > default > private 】
具体见多态。
由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果想要在子类方法中访问父类同名成员,需要用到super关键字。
1. super.data;【访问父类的普通(非静态)成员变量】
2. super.func();【调用父类的普通(非静态)成员方法】
3. super();【调用父类的构造方法】
不用super去调用静态的成员变量或方法,静态的是直接通过类名去调用的。
super只能在非静态方法中使用,静态方法中不能有super,不能有this,静态方法中只能是静态的。
这样就会有一个面试问题:super和this的区别是什么?
同:
异:
先给从父类中继承的成员变量赋值,再给子类自己新增的成员变量赋值
实例化子类对象时,JVM会将对象中的成员初始化成初始值,接着会调用子类构造方法。super();的作用是:调用父类的构造方法。所以,会先给从父类中继承的成员变量赋值,再给子类自己新增的成员变量赋值。
注意:
1. 虽然执行了父类的构造方法,但是并没有生成父类的对象,这里只是帮助子类给从父类继承过来的属性赋值。
2. 在子类构造方法中,super(...)调用父类构造时,必须是子类构造方法中第一条语句。
3. super(...)不能和this同时出现在一个构造方法中,因为它们都要放在构造方法的第一条语句,会形成冲突,不知道执行哪条语句。
那么在子类构造方法第一行默认会有隐含的super()调用【调用父类构造方法】
只要是无参构造方法,无论子类和父类有没有提供,或是一方提供一方没提供,子类构造方法第一行有没有super();
只要没有,编译器都会帮我们生成一份默认的无参构造方法,没有的编译器都会帮我们补全
最终会补全成下面这样:【子类父类都有无参构造方法,子类的无参构造方法第一行有super();】
子类中提供有参构造方法,父类中是显式定义无参或者默认的无参构造方法,
那么编译器将不会帮我们在子类中生成无参构造方法,若父类中不是显式定义无参,编译器会帮我们在父类中生成一份默认的无参构造方法,且在子类有参构造方法第一行默认有隐含的super();
最终会补全成下面这样:【子类有参父类无参,子类的有参构造方法第一行有super();】
那么在子类构造方法第一行不会有隐含的super()调用【调用父类构造方法】需要自己写
无论子类中的构造方法是有参还是无参,也都得自己写,都得是显性的。
成员变量包括:从父类继承的和子类自己新增的
1. 定义时就直接赋值
2. 在main函数中通过【引用.成员变量】进行赋值
3. 在子类中使用成员方法
4. 在子类中使用构造方法
(有参数的构造方法、构造方法中调用本类当中的其他构造方法)
【使用有参数的构造方法进行赋值最好用】
- class Animals{
- public String name;
- public int age;
- public String sex = "男";// 1. 定义时就直接赋值
-
- }
- public class Dog extends Animals {
- public boolean silly;
-
- /**
- * 3. 使用成员方法
- */
-
- public void setDog(int age,boolean silly){
- this.age = age;
- this.silly = silly;
- }
-
- @Override
- public String toString() {
- return name+" "+age+" "+sex+" "+silly;
- }
-
- public static void main(String[] args) {
- Dog dog = new Dog();
- /**
- * 2. 在main函数中通过【引用.成员变量】进行赋值
- */
- dog.name = "大黄";
- dog.setDog(10,true);
- System.out.println(dog);
- }
- }
4.1 使用有参数的构造方法
4.2 使用无参数的构造方法,并调用本类当中的其他构造方法
4.3 使用有参数的构造方法,并调用本类当中的其他构造方法
相同成员变量中,如何分别给继承的父类成员变量和子类自己的成员变量赋值?
使用构造方法进行赋值时,父类是有参构造方法且参数是同名的成员变量时,是给继承的父类成员变量赋值;父类是默认的无参构造方法时,说明根本没有访问到父类的成员变量,所以是给子类自己的成员变量赋值。
(!!!)就近原则,this在父类的构造方法中,就是给父类的成员变量赋值;this在子类的构造方法中,就是给子类的成员变量赋值。
使用构造方法给继承的父类成员变量和子类自己的成员变量赋值对比:
(1)有参数的构造方法:
(2) 构造方法中调用本类当中的其他构造方法:
给子类自己的成员变量赋值:
1. 定义时就直接赋值
2. 在子类中使用成员方法(因为就近原则,方法中的成员变量都是子类自己的)
3. 使用构造方法
(有参数的构造方法、构造方法中调用本类当中的其他构造方法)
给继承的父类成员变量赋值:
1. 定义时就直接赋值
2. 在子类中使用成员方法【成员变量前要加super点号】
3. 在父类中使用成员方法(因为就近原则,方法中的成员变量都是父类自己的)
4. 使用构造方法
(有参数的构造方法、构造方法中调用本类当中的其他构造方法)
- class Animals{
- public String name = "小黑";// 1. 定义时就直接赋值
- public int age;
- public String sex;
-
- }
- public class Dog extends Animals {
- public String name;
- public int age;
-
- /**
- * 2. 在子类中使用成员方法【成员变量前面要加super点号】
- */
- public void setDog(String name,int age,String sex) {
- super.name = name;
- super.age = age;
- super.sex = sex;
- }
-
- @Override
- public String toString() {
- return sex+" "+name+" "+age+" "+" "+super.name+" "+super.age;
- }
-
- public static void main(String[] args) {
- Dog dog = new Dog();
- dog.setDog("hello",18,"男");
- System.out.println(dog);
-
- }
- }
- class Animals{
- public String name;
- public int age;
- public String sex;
-
- /**
- * 3. 在父类中使用成员方法
- */
- public void setDog(String name,int age){
- this.name = name;
- this.age = age;
- }
- }
- public class Dog extends Animals {
- public String name;
- public int age;
-
- @Override
- public String toString() {
- return sex+" 子类中的:"+name+" "+age+" "+"父类中的:"+super.name+" "+super.age;
- }
-
- public static void main(String[] args) {
- Dog dog = new Dog();
- dog.setDog("小灰",2);
- System.out.println(dog);
-
- }
- }
静态代码块在类加载的时候就被执行了,它不依赖于对象,且只会执行一次
实例代码块和无参数的构造方法只有在创建对象时才会执行,且创建几个对象执行几次
1. 父类静态代码块优先于子类静态代码块执行,且是最早执行
2. 父类实例代码块和父类构造方法紧接着执行
3. 子类的实例代码块和子类构造方法紧接着再执行
4. 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
顺序:父类中的静态代码块,子类中的静态代码块,父类中的实例代码块,父类中的构造代码块,子类中的实例代码块,子类中的构造代码块
- class Animals{
- public String name;
- public int age;
- public String sex;
- static{
- System.out.println("Animals::static{}");
- }
-
- {
- System.out.println("Animals::{}");
- }
- public Animals(){
- System.out.println("Animals()");
- }
-
- }
- class Cat extends Animals{
- static{
- System.out.println("Cat::static{}");
- }
- public Cat(){
- System.out.println("Cat()");
- }
-
- {
- System.out.println("Cat::{}");
- }
-
- }
- public class Dog extends Animals {
- public String name;
- public int age;
-
- static{
- System.out.println("Dog::static{}");
- }
- public Dog(){
- System.out.println("Dog()");
- }
-
- {
- System.out.println("Dog::{}");
- }
-
-
- public static void main(String[] args) {
-
- Dog dog = new Dog();
- Cat cat = new Cat();
-
- }
- }
可访问范围:同一个包中,不同包中的它的子类
final关键可以用来修饰变量、成员方法以及类
1. 修饰变量或字段,表示常量(即不能修改)【public final int a = 10;】
2. 修饰方法:就变成了密封方法,表示该方法不能被重写
3. 修饰类:就变成了密封类,表示此类不能被继承【public final class A{}】
组合也能达到代码复用的效果。组合并没有涉及到特殊的语法, 仅仅是将一个类的实例作为另外一个类的字段。
继承表示对象之间是is-a的关系:
class Dog extends Animal -》 Dog is a Animal
组合表示对象之间是a part of/has-a的关系:
学生和老师是学校的一部分
学校有学生和老师
- class Teacher{
-
- }
- class Student{
-
- }
- class School{
- //一个学校有很多老师和很多学生组成
- public Teacher[] teacher;
- public Student[] student;
- }