1.继承:
1.1继承的概念
- 概念
一个类继承另一个类,则称继承的类为子类,被继承的类为父类。 - 主要解决的问题
共性的抽取,实现代码复用
1.共性:都具备的特性。例如猫,狗都属于动物类
2.java中不支持多层继承 建议不超过3层继承 而且只能继承一个类
3.父类=基类 子类=派生类
1.2继承的语法
java中表示类之间继承关系,用extend关键字来实现,具体如下;
修饰符 class 子类 extends 父类{ //代码的实现 }
例:
//父类->动物类class Animal{ String name; int age; public void eat(){ System.out.println(name + "正在吃饭"); } public void sleep(){ System.out.println(name + "正在睡觉"); }}//子类->猫类 继承 动物类class Cat extends Animal{ //mew是Cat类独有的方法 void mew(){ System.out.println(name + "喵喵喵~~~"); }}//子类->狗类 继承 动物类class Dog extends Animal{ //bark是Dog类独有的方法 void bark(){ System.out.println(name + "汪汪汪~~~"); }}public class Test { public static void main(String[] args) { Dog dog = new Dog(); // Dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的 System.out.println(dog.name); System.out.println(dog.age); //Dog中没有eat()和sleep()方法,因为继承父类,所以可以调用父类的eat()和sleep()方法 dog.eat(); dog.sleep(); //bark是Dog类独有的方法 dog.bark(); }}
注:
1.子类继承父类,会把父类的成员变量和方法继承到子类。2.子类要有独有的方法,否则继承无意义。
1.3父类成员的调用
1.3.1子类调用父类的成员变量
1.子类与父类的成员变量不同名
public class Base { int a; int b;}public class Derived extends Base{ int c; public void method(){ a = 10; // 访问从父类中继承下来的a 并修改 b = 20; // 访问从父类中继承下来的b 并修改 c = 30; // 访问子类自己的c 并修改 }}
2.子类与父类的成员变量同名
class Base { int a; int b; int c;} class Derived extends Base{ int a; // 与父类中成员a同名,且类型相同 char b; // 与父类中成员b同名,但类型不同 public void method(){ a = 100; // 访问父类继承的a,还是子类自己新增的a? b = 101; // 访问父类继承的b,还是子类自己新增的b? c = 102; // 子类没有c,访问的肯定是从父类继承下来的c // d = 103; // 编译失败,因为父类和子类都没有定义成员变量d }}
在子类方法中 或者 通过子类对象访问成员时:
- 如果访问的成员变量子类中有,优先访问自己的成员变量。
- 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
- 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
- 成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
1.3.2子类中访问父类的成员方法
1.成员方法名字不同
class Base { public void methodA(){ System.out.println("Base中的methodA()"); }}class Derived extends Base{ public void methodB(){ System.out.println("Derived中的methodB()方法");}public void methodC(){ methodB(); // 访问子类自己的methodB() methodA(); // 访问父类继承的methodA() // methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()}
总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的, 自己没有时再到父类中找,如果父类中也没有则报错。
1.成员方法名字相同
class Base { public void methodA(){ System.out.println("Base中的methodA()"); } public void methodB(){ System.out.println("Base中的methodB()"); }}class Derived extends Base{ public void methodA(int a) { System.out.println("Derived中的method(int)方法"); } public void methodB(){ System.out.println("Derived中的methodB()方法"); } public void methodC(){ methodA(); // 没有传参,访问父类中的methodA() methodA(20); // 传递int参数,访问子类中的methodA(int) methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到 }}
【说明】
派生类(子类):在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错
1.4supe和this关键字
- super作用
在子类方法中访问父类的成员变量和方法
super.变量名:访问父类的普通成员变量
super.方法名();调用父类的的普通成员方法
super():调用父类的构造方法 - this作用
this是当前对象的引用,当前对象即调用实例方法的对象
注:不能利用super调用父类的静态方法
this 会优先范围跟子类自己的,如果子类没有,访问的是父类的
class Base { int a; int b; public void methodA(){ System.out.println("Base中的methodA()"); } public void methodB(){ System.out.println("Base中的methodB()"); }}class Derived extends Base{ int a; // 与父类中成员变量同名且类型相同 char b; // 与父类中成员变量同名但类型不同 // 与父类中methodA()构成重载 public void methodA(int a) { System.out.println("Derived中的method()方法"); } // 与父类中methodB()构成重写(即原型一致) public void methodB(){ System.out.println("Derived中的methodB()方法"); } public void methodC(){ // 对于同名的成员变量,直接访问时,访问的都是子类的->就近原则 a = 100; // 等价于: this.a = 100; b = 101; // 等价于: this.b = 101; // 注意:this是当前对象的引用 // 访问父类的成员变量时,需要借助super关键字 // super是获取到子类对象中从父类继承下来的部分 super.a = 200;//修改的父类成员变量数据 super.b = 201;//修改的父类成员变量数据 // 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法 methodA(); // 没有传参,访问父类中的methodA() methodA(20); // 传递int参数,访问子类中的methodA(int) // 如果在子类中要访问重写的基类方法,则需要借助super关键字 methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),父类类的无法访问到 super.methodB(); // 访问父类的methodB() }}
注:
- 只能在非静态方法中使用
- 在子类方法中,访问父类的成员变量和方法。
1.5super和this的区别
- this是当前对象的引用,当前对象即调用实例方法的对象
- super相当于是子类对象中从父类继承下来部分成员的引用
- 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
- 在构造方法中:this()用来访问本类构造方法,super()用于调用父类构造方法,两种调用不能同时在构造方法中出现
- 构造方法中一定会存在super()的调用,用户没有写编译器也会增加,但事实this()用户不写则没有
- super this不能同时出现,只能出现一次(在一个方法中) super this 必须写在方法的第一行
- 当子类构造完成之前,先帮助父类完成初始化
1.5.1代码块的执行顺序
静态代码块>实例代码块>构造代码块
1:父类静态代码块优先于子类代码块执行,且最早执行
2:父类实例代码块和父类构造方法紧接着再执行
3:子类的实例代码块和子类的构造方法紧接着再执行
4:第二次实例化子类对象时,父类和子类的静态代码块都不会再执行
1.6子类构造方法
public class Base { public Base(){ System.out.println("Base()"); }}public class Derived extends Base{ public Derived(){ // super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(), // 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句, // 并且只能出现一次 System.out.println("Derived()"); }}public class Test { public static void main(String[] args) { Derived d = new Derived(); }}结果打印:Base()Derived()
释:
在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子肯定是先有父再有子,所以在构造子类对象时,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
注:
- 在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法。
- 在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。
- super(…)只能在子类构造方法中出现一次,并且不能和this同时出现
1.7访问修饰符
2.多态
2.1多态的概念
- 概念
- 多态的概念:指多种形态,就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
例如:狗和猫都去吃饭,狗吃狗粮,猫吃猫粮。
2.2多态的实现条件
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
例:
class Animal { String name; int age; public Animal(String name, int age){ this.name = name; this.age = age; } public void eat(){ System.out.println(name + "吃饭"); } } class Cat extends Animal{ public Cat(String name, int age){ super(name, age); } public void eat(){ System.out.println(name+"吃小鱼干"); } } public class Dog extends Animal { public Dog(String name, int age){ super(name, age); } public void eat(){ System.out.println(name+"吃狼骨头"); }} public class Test {// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法// 注意:此处的形参类型必须时父类类型才可以 public static void eat(Animal a){ a.eat(); } public static void main(String[] args) { Cat cat = new Cat("元宝",2);//传入参数 Dog dog = new Dog("小七", 1);//传入参数 eat(cat); eat(dog); }} 运行结果:元宝吃小鱼干元宝正在睡觉小七吃狼骨头小七正在睡觉
2.3重写(发生再继承)
2.3.1重写的概念
- 概念
重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写, 返回值和形参都不能改变。即**外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
2.3.2重写的规则
- 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
- 被重写的方法返回值类型可以不同,但是必须是具有父子关系的。即继承关系
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
- 父类被static、private修饰的方法、构造方法都不能被重写。
-重写的方法,可以使用@Override 注解来显式指定,有了这个注解能帮我们进行一些合法性校验。 例如不小心将方法名字拼写错了 (比如写成 aet),那么此时编译器就会发现父类中没有 aet方法,就会编译报错,提示无法构成重写。
2.3.3重写和重载的区别
重写:
1:方法名称相同
2:返回值相同//具有父子关系的可以不相同
3:参数列表相同(数据类型、个数、顺序)
- 子类的访问权限要>=父类的权限
- private修饰的方法、final修饰的方法、static修饰的方法 不可以重写
- 当在父类中调用子类重写方法的时候 此时会调用子类重写的方法
重载:
1:方法名称相同
2:参数列表不同数据类型、个数、顺序)
3:返回值没有要求
重写和重载的区别
区别点 | 重写 | 重载 |
---|---|---|
参数列表 | 不能修改 | 必须修改 |
返回类型 | 不能修改(除非父子关系) | 可以修改 |
访问修饰符 | 可以降低限制 | 可以修改 |
2.4向上、向下转型
2.4.1向上转型
- 概念
创建一个子类对象,将其当成父类对象来使用。 - 语法格式
父类类型 对象名= new 子类类型();
例:Animal animal = new Cat();
class Animal { String name; int age; public void eat(){ System.out.println(name + "吃饭"); }}class Cat extends Animal{ public void eat(){ System.out.println(name + "吃小鱼干"); }}class Dog extends Animal{ public void eat(){ System.out.println(name + "吃狗粮"); }}public class Test { // 2. 方法传参:形参为父类型引用,可以接收任意子类的对象 public static void eatFood(Animal a){ a.eat(); } // 3. 作返回值:返回任意子类对象 public static Animal buyAnimal(String var){ if("狗".equals(var) ){ return new Dog("狗狗",1); }else if("猫" .equals(var)){ return new Cat("猫猫", 1); }else{ return null; } } public static void main(String[] args) { Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象 Dog dog = new Dog("小七", 1); eatFood(cat); eatFood(dog); Animal animal = buyAnimal("狗"); animal.eat(); animal = buyAnimal("猫"); animal.eat(); }}
优缺点:
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
2.4.2向下转型
- 概念
为了重新获得因为向上转型而丢失的子类独有的特性 - 格式
父类类型 父类对象 = 子类实例;
子类类型 子类对象 = (子类)父类对象;
例 Animal a = new Dog();
Dog b = (Dog) a;
释:
向下转型不安全,用的比较少,一般情况用instanceof关键字来提高向下转型的安全
2.4.3 instanceof
- 功能
判断一个对象是否是某个类(或接口)的实例
例 对象 instanceof 类(接口)
//如果成立返回 true 否则 flaseif(对象 instanceof 类(接口)){ //运行这里的代码}