目录
一、继承
1.继承的概念
用来进行共性抽取,实现代码的复用。
继承机制:在保持原有类特性的基础上进行扩展,增加新功能,产生新的类,称为派生类。
父类/基类/超类
子类/派生类
2.继承的语法
修饰符 class 子类 extends 父类{
//~~~~
}
class Animal {
//父类
public String name;
public int age;
public void eat() {
System.out.println(this.name + "正在吃饭");
}
public void sleep() {
System.out.println(this.name + "正在睡觉");
}
}
class Dog extends Animal {
//子类
public void bark() {
System.out.println(this.name + "汪汪汪");
}
}
子类会将父类中的成员变量或者成员方法继承到子类中
子类继承父类之后,必须新添加自己特有的成员变量,体现出与基类的不同,否则就没有必要继承
3.父类成员访问
3.1子类中访问父类的成员变量
如果访问的成员变量子类中有,优先访问自己的成员变量
如果访问的成员变量子类中无,则访问父类继承下来的,如果父类中也无定义,则编译报错
如果访问的成员变量与父类中成员变量同名,则优先访问自己的(成员变量访问遵循就近原则)
父类中的private修饰的成员变量在子类中不能直接访问,但也是继承到子类中了
3.2子类中访问父类的成员方法
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错
通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错
//父类
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "正在吃饭");
}
public void sleep() {
System.out.println(this.name + "正在睡觉");
}
}
//子类
class Dog extends Animal{
public void eat() {
System.out.println(this.name + "正在吃饭");
}
public void sleep(int num) {
System.out.println(this.name + "正在睡觉");
}
public void bark() {
System.out.println(this.name + "汪汪汪");
}
}
public class Text {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); //访问子类自己的eat()方法
dog.sleep();//访问父类的sleep()方法
dog.sleep(2);//访问子类的sleep()方法
dog.bark();//访问子类自己的bark()方法
}
}
4.super关键字
在子类中访问父类的成员
注意事项:
只能在非静态方法中使用
在子类方法中,访问父类的成员方法或变量
super只能指代当前的父类,不能指代父类的父类,甚至更远
4.1子类的构造方法
子类对象构造时,需要先调用父类构造方法,然后再执行子类的构造方法
若父类显示定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用父类的构造方法
如果父类构造方法是带有 参数的,此时需要用户为子类显示定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败
在子类构造方法中,super(~)调用父类构造方法时,必须是子类构造函数中的第一条语句
super(~)只能在子类构造方法中出现一次,并且不能和this()同时出现
class Base {
public Base() {
System.out.println("Base()");
}
}
class Derived extends Base {
public Derived() {
//super();
//用户没有写时,编译器·会自动添加,而且super()必须是子类构造方法中的第一条语句
//比且只能出现一次
System.out.println("Derived()");
}
}
public class Text {
public static void main(String[] args) {
Derived d = new Derived();
}
}
//Base()
//Derived()
//Base()比Derived()先执行
class Animal {
public String name;
public int age;
public Animal(String name,int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(this.name + "正在吃饭");
}
public void sleep() {
System.out.println(this.name + "正在睡觉");
}
}
class Dog extends Animal{
public Dog() {
//调用父类的构造方法
super("小黑",5);
}
public Dog(String name,int age) {
//调用父类的构造方法
super(name,age);
}
public void eat() {
System.out.println(this.name + "正在吃饭");
}
public void bark() {
System.out.println(this.name + "汪汪汪");
}
}
public class Text {
public static void main(String[] args) {
Dog dog = new Dog("小黑",5);
System.out.println(dog.name);
System.out.println(dog.age);
dog.eat(); //访问子类自己的eat()方法
dog.sleep();//访问父类的sleep()方法
dog.bark();//访问子类自己的bark()方法
}
}
//小黑
//5
//小黑正在吃饭
//小黑正在睡觉
//小黑汪汪汪
4.2super和this
相同点:
都是Java中的关键字
只能在类的非静态方法中使用,用来访问非静态成员和字段
在构造方法调用时,必须是构造方法的第一句,并不能同时存在
不同点:
this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类中从父类其中继承下来部分成员的引用
在非静态成员方法中,this用来访问本类方法和属性,super用类访问子类继承父类下来的方法和属性
在构造方法中:this(~)用于调用本类构造方法,super(~)用于调用父类构造方法,两者调用不能同时出现在构造方法中
不带参数的构造方法中一定会存在super(~)的调用,用户没写编译器也会增加,但是this()用户不写则没有
5.继承关系
6.再谈初始化
父类静态代码块优先于子类静态代码块执行,且是最早执行的
父类实例代码块和父类构造方法紧接着执行
子类的实例代码块和构造方法紧接着再执行
第二次实例化子类对象时,父类和子类的静态代码块都将不会再次执行
class Base {
static {
System.out.println("Base()::静态代码块执行");
}
{
System.out.println("Base()::实例代码块执行");
}
public Base() {
System.out.println("Base()::构造方法执行");
}
}
class Derived extends Base {
static {
System.out.println("Derived()::静态代码块执行");
}
{
System.out.println("Derived()::实例代码块执行");
}
public Derived() {
//super();
//用户没有写时,编译器·会自动添加,而且super()必须是子类构造方法中的第一条语句
//比且只能出现一次
System.out.println("Derived()::构造方法执行");
}
}
public class Text {
public static void main(String[] args) {
Derived d = new Derived();
}
}
//Base()::静态代码块执行
//Derived()::静态代码块执行
//Base()::实例代码块执行
//Base()::构造方法执行
//Derived()::实例代码块执行
//Derived()::构造方法执行
7.final关键字
7.1final修饰变量或字段(常量)
public class Text {
public static void main(String[] args) {
final int a = 10; // fianl修饰的变量或者字符是常量
a = 20; // 报错,a是常量,不能被修改
}
}
7.2final修饰类
表示此类不能被继承
平时用的String字符串类,就使用final修饰的,不能被继承
7.3fianl修饰方法
表示方法不能被重写
8.继承和组合
//轮胎类
class Tire {
//~~
}
//发动机类
class Engine {
//~~
}
//车载系统类
class VehicleSystem {
//~~
}
//组合
class Car {
private Tire tire;//可以复用轮胎中的属性和方法
private Engine engine;//可以复用发动机中的属性和方法
private VehicleSystem vs;//可以复用车载系统中的属性和方法
//~~
}
class Benz extends Car {
//其汽车中包含的:轮胎、发动机、车载系统全部继承下来
}
组合和继承都可以实现代码的复用,一般建议:能组合尽量用组合
二、多态
1.概念
就是完成某个行为,当不同的对象去完成时会产生不同的状态
2.多态实现条件
必须在继承体系下
子类必须要对父类中的方法进行重写
通过父类的引用调用重写的方法
3.向上转型
3.1概念
实际就是创建一个子类对象,将其当成父类对象来使用
3.2语法格式
父类类型 对象名 = new 子类类型();
Animal cat = new Cat();
//cat是Animal的子类
Animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换
3.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 Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
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 + "吃鱼~");
}
}
public class Text {
public static void main(String[] args) {
Animal dog = new Dog("小黑",2);//向上转型
Animal cat = new Cat("小花",1);//向上转型
dog.eat();
cat.eat();
}
}
//小黑吃骨头~
//小花吃鱼~
方法传参:形参为父类引用,可以接收任意子类的对象
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 Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
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 + "吃鱼~");
}
}
public class Text {
//向上转型
public static void eat(Animal a) {
a.eat();
}
public static void main(String[] args) {
Dog dog = new Dog("小黑",2);
Cat cat = new Cat("小花",1);
eat(dog);
eat(cat);
}
}
//小黑吃骨头~
//小花吃鱼~
方法返回:返回任意子类对象
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 Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
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 + "吃鱼~");
}
}
public class Text {
//向上转型
public static Animal buyAnimal(String var) {
if("狗".equals(var)) {
return new Dog("狗狗",2);
}else if("猫".equals(var)) {
return new Cat("猫猫",1);
}else {
return null;
}
}
public static void main(String[] args) {
Animal animal1 = buyAnimal("狗");
Animal animal2 = buyAnimal("猫");
animal1.eat();
animal2.eat();
}
}
向上转型的优点:让代码直线更简单灵活
向上转型的缺点:不能调用子类特有的方法
4.向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类特有的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型
4.1instanceof
保留关键字,二元运算符,左边对象,右边是类,返回类型为boolean,当对象是右边类或子类所创建对象时,返回true;否则,返回false。
4.2向下转型
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 Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name + "吃骨头~");
}
public void bark() {
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 + "吃鱼~");
}
public void acting() {
System.out.println(name + "抓老鼠~");
}
}
public class Text {
public static void main(String[] args) {
Animal animal1 = new Dog("小黑",2);//向上转型
Animal animal2 = new Cat("小花",1);//向上转型
animal1.eat();
animal2.eat();
//向下转型
if(animal1 instanceof Dog) {//判断animal1是否为Dog的实例对象
Dog dog =(Dog)animal1;//向下转型
dog.bark();
}
if(animal2 instanceof Cat) {//判断animal2是否为Cat的实例对象
Cat cat =(Cat)animal2;//向下转型
cat.acting();
}
}
}
//小黑吃骨头~
//小花吃鱼~
//小黑汪汪汪~
//小花抓老鼠~
5.重写
5.1概念
也称为覆盖。重写是子类对父类非静态、非private、非final、非构造方法等的实现过程进行重新编写,返回值和形参都不能改变。
5.2重写的规则
子类在重写父类的方法时,一般必须与父类方法的原型一致 :返回值类型、方法名、参数列表,要完全一致
被重写的方法返回值类型可以不同,但是必须是具有父子关系的
访问权限不能比父类重写的方法的访问权限更低。例如:如果父类方法被public修饰,那么子类重写该方法就不能声明为proteccted
父类被static、private、final修饰的方法、构造方法都不能被重写
5.3重写与重载的区别
区别点 | 重写(override) | 重载 |
参数列表 | 一定不能修改 | 必须修改 |
返回值类型 | 一定不能修改【除非可以构成父子类关系】 | 可以修改 |
访问限定符 | 一定不能做更严格的限制 | 可以修改 |
即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现
6.动态绑定 (运行时绑定)
在编译时,不能确定方法的行为,需要等到程序运行时,才能确定具体调用哪个类的方法
当父类引用子类对象,子类重写父类的方法时,通过父类的引用调用父类和子类的重写方法,最终显示是调用了子类的方法
7.静态绑定
在编译时,根据用户所传递实参类型就确定了具体调用了哪个方法,如:方法的重载
8.Object类
所有类的父类,就算没有显示继承关系,也是Object的子类
9.多态的优缺点
优点:
9.1降低圈复杂度
若要打印圆、花、花、圆。
class Shape {
public void draw() {
System.out.println("画图形~");
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("⚪");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("❀");
}
}
public class Text {
//用多态
public static void main(String[] args) {
Shape[] shapes = {new Cycle(),new Flower(),new Flower(),new Cycle()};
for(Shape shape : shapes) {
shape.draw();
}
}
//不用多态
public static void main1(String[] args) {
Cycle cycle = new Cycle();
Flower flower = new Flower();
String[] shapes = {"cycle","flower","flower","cycle"};
for(String shape : shapes) {
if(shape.equals("cycle")) {
cycle.draw();
}else if(shape.equals("flower")) {
flower.draw();
}
}
}
}
//⚪
//❀
//❀
//⚪
9.2可扩展能力更强
上述代码可一另加别的图形
public class Triangle extends Shape {
@Override
public void draw() {
System.out.println("🔺");
}
}
9.3缺点
代码的运行效率低
属性没有多态性,当父类子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
通过父类引用不能调用子类特有的属性和方法(向上转型)
构造方法没有多态性
10.避免在构造方法中调用重写方法
class B {
public B() {
func();
}
public void func() {
System.out.println("B.func()~");
}
}
class D extends B {
private int num1 = 1;
final int num2 = 2;
@Override
public void func() {
System.out.println("D.func()~" + num1 + " " num2);
}
}
public class Text {
public static void main(String[] args) {
new D();
}
}
//D.func()~0 2
构造D对象的同时,会调用B的构造方法,B的构造方法中调用了func()方法,此时会触发动态绑定,会调用到D中的func()
此时D对象自身还没有构造,num1处于未初始化的状态,值为0。如果具有多态性,num1的值应为1
所以在构造函数内,尽量避免使用实例方法,除了final和静态方法