目录
封装
继承
父类成员的访问
如何在子类中访问父类同名的成员
子类构造方法
代码块在继承上的执行顺序
多态
多态的实现条件
重写
多态的优缺点
面向对象程序的三大特征:封装继承和多态。
封装
· 封装:将数据和操作数据的方法进行有效的结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
Java主要通过类和访问权限来实现封装,类可以将数据以及封装数据的方法结合在一起,而访问权限用来控制方法或者字段是否能在类外使用。
访问权限限定符有四个:public、protected、private、default(默认权限)
继承
面向对象思想中提出了继承的概念,专门用来提取代码的共用性,实现代码复用。继承允许程序员在保持原有类特性的基础上进行扩展,添加新的功能,这样产生新的类称为派生类。继承主要解决的问题就是共性的抽取,实现代码的复用。
继承的语法
修饰符 class 子类 extends 父类 {}
代码示例
父类Animal.java
public class Animal {public String name;public int age;public void eat() {System.out.println(this.name + "吃饭");}public void sleep() {System.out.println(this.name + "睡觉");}
}
子类Cat.java
public class Cat extends Animal {public void mew() {System.out.println(this.name + "~喵");}
}
子类会将父类的成员方法和变量继承到子类中,子类继承父类后需要添加自己特有的成员变量和方法。
父类成员的访问
· 当父类和子类不存在同名变量时,子类可以任意访问父类的成员变量。
public class Base {int a;int b;
}
class Derived extends Base {int c;public void method() {a = 10;//访问从父类继承下来的b = 20;//访问从父类继承下来的c = 30;//访问子类自己的cSystem.out.println("a = " + a + ", b = " + b + ", c = " + c);}
}
class Test {public static void main(String[] args) {Derived derived = new Derived();derived.method();}
}
· 子类和父类存在同名变量
public class Base {int a;int b;int c;
}
class Derived extends Base {int a;//与父类的成员变量名相同,且类型相同char b;//与父类中成员变量名相同,但类型不同public void method() {a = 10;//优先访问自己的b = 101;//优先访问自己的c = 30;//子类中未定义c,访问从父类继承下来的System.out.println("a = " + a + ", b = " + b + ", c = " + c);}
}
class Test {public static void main(String[] args) {Derived derived = new Derived();derived.method();}
}
在子类方法中或者通过子类对象访问成员时
· 如果访问的成员变量子类中有,优先访问子类的成员变量
· 如果访问的成员变量子类中没有,访问从父类继承的,如果父类也未定义,则编译报错
· 如果访问的成员变量与父类的成员变量名称相同,优先访问子类的,即子类将父类的同名变量隐藏了
· 在子类中访问父类的成员方法时
· 当成员方法不同名时,优先访问自己的,如果子类中没有,就去父类中找,如果父类中也没有就报错
· 当成员方法同名时:
通过子类对象访问父类和子类同名的方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法时传递的参数选择合适的方法访问,如果没有报错;如果父类和子类同名方法原型一致(重写),则只能访问到子类的,父类的无法通过子类对象直接访问到
如何在子类中访问父类同名的成员
使用super关键字,super关键字的作用就是在子类中访问父类中的与子类同名的成员
例如:
public class Base1 {int a = 10;public void method() {System.out.println("父类同名方法");}
}
class Derived1 extends Base1 {int a = 20;public void method() {System.out.println("子类同名方法");}public void superMethod() {System.out.println(super.a);super.method();}
}
class Test1 {public static void main(String[] args) {Derived1 d = new Derived1();d.superMethod();}
}
注意事项:super只能在非静态方法中使用;在子类方法中,访问父类的成员变量
子类构造方法
在构造子类对象时,先执行父类的构造方法,然后再执行子类的构造方法。因为子类对象是一个父类对象,在构造子类对象时,先要将父类继承下来的成员初始化完整,然后再初始化子类自己新添加的成员
注意事项
1、如果父类定义了无参或者默认的构造方法,在子类构造方法第一行会默认有隐含的super()调用,用来调用父类的构造方法
2、如果父类的构造方法带有参数,此时编译器不会再给子类生成默认的构造方法,就需要coder为子类显示的定义构造方法,并在子类构造方法中选择合适的父类构造方法调用
3、在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数的第一条语句
4、super(...)只能在子类构造方法中出现一次,且不能和this同时出现
public class Base2 {String name;int age;public Base2() {System.out.println("Base2()");}
}
class Derived2 extends Base2 {public Derived2() {System.out.println("Derived2()");}
}
class Test2 {public static void main(String[] args) {Derived2 derived2 = new Derived2();}
}
先执行父类的构造方法,再执行子类的
如果基类的构造方法带有参数,那么在子类中需要使用super(...)调用父类的构造方法
在子类使用了super后,可以额外定义自己的构造方法
代码块在继承上的执行顺序
通过下述代码来观察代码块在继承上的执行顺序
public class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;System.out.println("Person: 构造方法执行");}{System.out.println("Person: 普通代码块执行");}static {System.out.println("Person: 静态代码块执行");}
}
class Student extends Person {public Student(String name, int age) {super(name, age);System.out.println("Student: 构造方法执行");}{System.out.println("Student: 普通代码块执行");}static {System.out.println("Student: 静态代码块执行");}
}
class Test3 {public static void main(String[] args) {Student student = new Student("小明", 10);System.out.println("============================");Student student1 = new Student("小光", 15);}
}
通过观察,可以总结出
1、静态代码块无论子类还是父类都是优先初始化的,且父类先初始化再初始化子类,且只初始化一次
2、 普通代码块优先于构造方法执行,且父类的先执行,执行完后再执行子类的
多态
通俗的说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时产生不同的状态
多态的实现条件
在Java中要实现多态,必须要满足以下几个条件:
1、必须在继承的体系下
2、子类必须要对父类中的方法进行重写
3、通过父类的引用调用重写的方法
多态的体现,在代码运行时,当传递不同的对象时,会调用对应类中的方法
public class Animal2 {String name;int age;public Animal2(String name, int age) {this.name = name;this.age = age;}public void eat() {System.out.println(name + "吃饭");}
}
class Cat2 extends Animal2 {public Cat2(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name + "吃鱼");}
}
class Dog extends Animal2 {public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name + "吃骨头");}
}
class TestAnimal {public static void eat(Animal2 a) {a.eat();}public static void main(String[] args) {Cat2 cat = new Cat2("喵", 10);Dog dog = new Dog("茶杯犬", 5);eat(cat);eat(dog);}
}
在编写eat方法时,参数类型为Animal(父类),此时该方法内部并不知道也不关注当前的a指向的是哪个子类的实例,此时a这个引用调用eat方法可能会有多种不同形式的表现(和a引用的实例相关),这种行为就称为多态。
重写
也称为覆盖,重写是子类对父类非静态、非private、非final修饰、非构造方法等的实现过程进行重新编写,返回值和形参都不能变,也就是外壳不变,重写核心,重写的好处在于子类可以根据需要,定义特定于自己的行为。
方法重写的规则
1、子类在重写父类方法时,一般必须与父类方法原型一致:修饰符、返回值类型、方法名、参数列表要完全一致
2、JDK1.7以后,被重写的方法返回值类型可以不同,但是必须具有父子关系
3、访问权限不能比父类方法的访问权限低
4、父类中被static、private修饰的方法、构造方法都不能重写
5、子类和父类在同一个包中,则子类可以重写父类的所有方法,除了声明为private和final的方法
6、子类和父类不在同一个包中,那么子类只能够重写父类声明为public和protected的非final方法
多态的优缺点
1、能够降低代码的“圈复杂度”,避免使用大量的if-else语句
2、可扩展能力强,如果要新加一种状态,使用多态的方式代码改动成本较低
3、多态的缺点是运行效率较低