目录
封装
继承
父类成员的访问
如何在子类中访问父类同名的成员
子类构造方法
代码块在继承上的执行顺序
多态
多态的实现条件
重写
多态的优缺点
面向对象程序的三大特征:封装继承和多态。
封装
· 封装:将数据和操作数据的方法进行有效的结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
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;//访问子类自己的c
System.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);
}
@Override
public void eat() {
System.out.println(name + "吃鱼");
}
}
class Dog extends Animal2 {
public Dog(String name, int age) {
super(name, age);
}
@Override
public 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、多态的缺点是运行效率较低