Java语言是一种面向对象的编程语言,号称一切皆对象。面向对象语言就会存在三个特性,封装,继承和多态。
封装
封装将代码的实现细节封装在类中,对外只暴露公共的接口。Java中的封装是通过private等权限修饰符来实现的。
标准JavaBean
public class Student {
private int age;
private String name;
public Student(){}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
标准的JavaBean应该拥有三个特性
- 必须是一个公开的类,public类型的
- 必须拥有无参构造函数
- 属性用private修饰,通过getter和setter方法暴露给外界
继承
好处和弊端
- 好处
1、提高了代码的复用性
2、提高了代码的维护性
这皆是因为子类可以使用父类的属性和方法,可以减少重复代码,并且在维护的时候只需要修改一个地方就可以了。 - 弊端
1、继承是通过连接类与类,类的耦合性增强了,容易造成高耦合,在更改代码的时候,很难解耦合。
变量的访问特点
子类中变量的访问采用的是就近原则
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 没有就报错
this&super
this指代本类对象的引用,super代表父类存储空间的标识。
继承中子类可以通过super调用父类的属性和方法。
构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造函数。
因为子类可能会使用父类的属性和方法,所以在子类初始化之前,必须先初始化父类。所以子类构造方法的第一条语句默认都是super(),如果没有,则会由编译器自动添加。
如果父类没有无参构造函数,则在继承的时候必须通过super显示的调用父类的有参构造函数,或者父类提供无参构造函数。否则无法初始化父类,导致报错。
方法访问的特点
- 子类成员范围找
- 父类成员范围找
- 否则报错
内存图
对象在堆内存中创建,子类创建的对象会单独存在一块super的区域,用来存放父类的数据。
方法重写
Java中有方法重写和方法重载
- 方法重载发生在同一个类中,说的是在同一个类中,可以定义多个同名的方法,方法名相同,参数列表不同,这样的方法属于方法重载。
- 方法重写发生在父类和子类中。当父类的方法不满足需要,可以在子类中定义同名、同参数列表的方法,子类使用该方法时,会覆盖父类的方法。
方法重写的注意事项
- 私有方法不能够被重写,因为父类私有成员是子类不能继承的(两同)
- 子类的访问权限不能降低(两小)
- 子类抛出的异常不能更高
- 子类的返回值类型小于或等于父类(一大)
阻断继承
使用final类和final方法可以阻断继承。
使用final修饰的类,成为最终类,不能继承。定义为final的类,其中的所有方法都自动成为了final类型的。
被final修饰的类和方法主要原因就是:确保它们不会在子类中改变语义。
如果方法很简短,被频繁调用而且确实没有被覆盖,那么即时编译器就会将这个方法进行内联处理,如果方法被覆盖,则会取消对方法的内联。
多态
多态可以分为编译时多态和运行时多态,编译时多态指的就是方法重载。运行时多态是这里讲的多态,指的是在不同时刻表现出来的不同形态。
多态的前提
- 要有继承或实现关系
- 要有方法重写
- 父类引用指向子类对象
多态中的成员访问特点
- 成员变量
编译看父类,运行看父类 - 成员方法
编译看父类,运行看子类
public class test {
public static void main(String[] args) {
Animal animal = new Cat();
System.out.println(animal.age);
System.out.println(animal.getAge());
}
}
class Animal {
int age = 20;
public int getAge(){
return this.age;
}
}
class Cat extends Animal{
int age = 10;
public int getAge(){
return this.age;
}
}
输出
20
10
多态的好处和弊端
- 好处
提高程序的扩展性。定义方法的时候,使用父类作为参数,在使用的时候,使用具体的子类性参与操作,在方法参数中比较常见。 - 弊端
不能使用子类的特有成员
多态中的转型
- 向上转型
父类引用指向子类对象就是向上转型 - 向下转型
当需要使用子类特有成员时,需要将对象转成特有的子类类型。子类型 对象名 = (子类型)父类引用;
在Java中,子类引用的数组可以转换成超类引用的数组,而不需要使用强制类型转换。
Child child = new Child();
Parent parent = child;
但这种使用会破坏语义,因为可以父类数组存储子类的元素,这样会产生矛盾的语义。
方法调用
1、编译器查看对象的声明类型和方法名。编译器此步知道了所有可能被调用的方法。
2、编译器确定方法调用中提供的参数类型。如果存在重载方法,则会找到一个参数匹配的方法,这个过程称为重载解析。如果没有匹配的,则会报错。
3、对于private、static和final方法,编译器可以在编译时期确定,这用称为静态绑定。
而像多态中的方法调用,调用的方法依赖于隐式参数的实际类型,必须在运行时动态绑定。
4、对于动态绑定的方法,虚拟机会调用对象所引用的实际类型对象的方法。
但是每次调用方法都需要完成整个搜索,时间开销特别大,所以虚拟机预先为每个类计算了一个方法表,其中列举了所有方法的签名和要调用的实际方法。在真正调用的时候,就直接在方法表中查找就行了。
那么实际的调用方法过程为:
1、虚拟机获取对象的实际类型的方法表
2、虚拟机查找定义了相应方法签名的类
3、虚拟机调用对应方法