1.继承
⑴为什么需要继承:专门为了用来进行共性抽取,实现代码复用
⑵继承的概念:继承机制 是面向对象程序设计代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到负复杂的认知过程。主要解决的问题是:共性的抽取,实现代码复用
⑶继承的语法:在java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下
class Animal{
String name;
int age;
public void eat() {
System.out.println(name+"正在吃饭");
}
public void sleep() {
System.out.println(name+"正在睡觉");
}
}
class Dogg extends Animal {
void bark() {
System.out.println(name+"汪汪汪 ~~~");
}
}
class Catt extends Animal {
}
public class HolidayDemo {
public static void main(String[] args) {
Dogg dog = new Dogg();
//dog类中没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
//dog访问的eat()和sleep也是从Animal中继承下来的
dog.eat();
dog.sleep();
dog.bark();
}
}
注意:
☯子类会将父类中的成员变量或者成员方法继承到子类中了
☯子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没必要继承了
⑷子类访问父类的成员方法
总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错
2.super关键字:该关键字的主要作用是在子类方法中访问父类的成员
注意看代码注释就简单啦~
class Base{
int a;
int b;
public void methodA() {
System.out.println("Base中的method()");
}
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()方法");
}
public void methodB() {
System.out.println("Derived中的methodB ()方法");
}
public void methtodC() {
//对于同名的成员变量直访问时,访问的都是子类的
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()
}
}
public class HolidayDemo24 {
public static void main(String[] args) {
Base base = new Base();
base.methodA();
base.methodB();
System.out.println("==============================");
Derived de = new Derived();
de.methodA();
de.methodB();
System.out.println("====================");
de.methtodC();
}
}
注意:
☯super只能在静态方法中使用
☯在子类方法中,访问父类的成员变量和方法
3.子类构造方法:即 子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法
class Basee{
//构造方法
public Basee(){
System.out.println("Base()");
}
}
class Derivedd extends Basee {
public Derivedd() {
//super();//注意子类构造方法中默认会调用基类的无参构造方法:super()
//用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句
//并且只能出现一次
System.out.println("Derivedd()");
}
}
public class HolidayDemo25 {
public static void main(String[] args) {
Derivedd d = new Derivedd();
}
}
总结:在子类构造方法中没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分,父子父子肯定是先有父再有子,所以在构造子类对象时,先调用基类的构造方法,将从基类继承下来的成员构造完整,然后再用子类自己的构造方法,将子类自己新加的成员初始化完整
注意:
☯若父类显示定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
☯如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显示定义构造方法,并在子类构造方法中选择适合的父类构造方法调用,否则编译失败
☯在子类构造方法中,super(...)调用父类构造时,必须是字了构造函数中第一条语句
☯super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
4.super和this
super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,让我们来看看他们有什么区别呢?
【相同点】
♨都是java中的关键字
♨只能在类的非静态方法中使用,用来访问非静态成员方法和字段
♨在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
【不同点】
♨this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
♨在非静态成员方法中,this用来访问本类的方法和属性,super 用来访问父类继承下来的方法和属性
♨this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
♨成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this阿里访问的;在子类中如果super访问父类成员,编译之后在字节码层面super实际是不存在的(通过字节码文件可以验证)
♨在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
♨构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有
5.protected关键字
No | 范围 | private | default | public | ♨ |
1 | 同一包中的同一类 | √ | √ | √ | ♨ |
2 | 同一包中的不同类 | √ | √ | ♨ | |
3 | 不同包中的子类 | √ | √ | ♨ | |
4 | 不同包中的非子类 | √ | ♨ |
基类:B在demo1包中
/**
* @author HP
*/
// 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了
// demo1包中
public class B {
private int a;
protected int b;
public int c;
int d;
}
⑴同一包中的子类
/**
* @author HP
*/ //demo1包中的
//同一包中的子类
class D extends B{
public void method(){
// super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
super.b = 20; // 父类中protected成员在相同包子类中可以===直接访问==
super.c = 30; // 父类中public成员在相同包子类中可以===直接访问==
super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以===直接访问==
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
d.method();
}
}
⑵不同包中的子类
//demo2包中
//不同包中的子类
class C extends B {
public void method(){
// super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
//super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
}
}
public class TestDemo {
}
⑶不同包中的类
// extend02包中
// 不同包中的类
public class TestC {
public static void main(String[] args) {
C c = new C();
c.method();
// System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
// System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
// System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
}
}
6.继承方式
7.final关键字:final关键字可以用来修饰变量,成员方法以及类
⑴修饰变量或字段,表示常量(即不能修改)
⑵修饰类:表示此类不能被继承
⑶修饰方法:表示该方法不能被重写
8.继承与组合:继承表示对象之间是is-a的关系;;组合表示对象之间是has-a的关系
/**
* @author HP
*/
// 轮胎类
class Tire{
// ...
}
// 发动机类
class Engine{
// ...
}
// 车载系统类
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
private VehicleSystem vs; // 可以复用车载系统中的属性和方法
// ...
}
// 奔驰是汽车
class Benz extends Car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}
public class HolidayDemo27 {
}
♨组合和继承都可以实现代码复用,应该使用继承还是组合,需根据应用场景来选择,一般建议:能用组合尽量用组合
9.多态
⑴多态的概念:通俗来说就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态
⑵多态实现条件
☀必须在继承体系下
☀子类必须要对父类中方法进行重写
☀通过父类的引用调用重写的方法
♦多态实现:在代码运行时,当传递不同类对象时,会调用对应类中的方法
/**
* @author HP
*/
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);
}
@Override
public void eat(){
System.out.println(name+"吃鱼~~~");
}
}
class Dog extends Animal {
public Dog(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃骨头~~~");
}
}
public class TestAnimal {
// 编译器在编译代码时,并不知道要调用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);
}
}
10.重写:返回值和形参都不能改变。即外壳不变,核心重写!
【方法重写的规则】
☀子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表) 要完全一致
☀JDK7以后,被重写的方法返回值类型可以不同,但是必须是具有父子关系的
☀访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为 protected
☀父类被static、private修饰的方法、构造方法都不能被重写。
☀子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
☀子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
☀重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.
【重写和重载的区别】
区别点 | 重载 | 重写 |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
访问限定符 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
异常 | 可以修改 | 可以减少删除,一定不能抛新的或者更广的异常 |
所以:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代 表函数重载
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体 调用那个类的方法。
11.向上转型和向下转型
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
public class TestAnimall {
// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
public static void eatFood(Animal a){
a.eat();
}
// 3. 作返回值:返回任意子类对象
public static Animal buyAnimal(String var){
if("狗" == var){
return new Dog("狗狗",1);
}else if("猫" == 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();
}
}
☀向上转型的优点:让代码实现更简单灵活。
☀向上转型的缺陷:不能调用到子类特有的方法
向下转型:
笔记: