面向对象(三)
访问修饰符
在Java中访问修饰符有四种,用于对类、成员变量,成员方法的访问范围进行控制。
- public,公开的,对外公开。
- protected,受保护的,同包和子类都可以访问
- 默认,同一个包可以访问
- private,私有的,同一个类可以访问,对外不公开
访问修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
默认 | 可以 | 可以 | 不可以 | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
类只能用public、默认修饰
面向对象的三大特征
封装、继承、多态
构造函数
- 子类必须调用父类构造器,完成父类的初始化
- 不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造函数,如果父类没有提供默认的无参构造函数,则必须在子类中使用super确定调用哪个父类的构造函数,否则就会报错,编译无法通过。如果想要显示调用某个父类的构造器使用super,super语句只能是第一条语句,this()和super()不能同时存在子类的构造函数中,只能写一个,都要在第一行。
- 所有对象都是Object类的子类,对父类构造函数的调用不限于直接父类,将一直向上追溯至Object类
- Java中只能单继承,每一个类只有一个直接父类
- 不能滥用继承关系
封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
继承本质
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
创建子类对象的时候,在内存里面发生了什么?建立查找关系,通过一个实例分析:
public class GrandPa {
String name = "大头爷爷";
int age = 100;
String hobby = "旅游";
}
public class Father extends GrandPa{
String name = "大头爸爸";
int age = 30;
}
public class Son extends Father {
String name = "大头儿子";
}
Java中的继承只能单继承,但是可以通过内部类继承其他类来实现多继承
继承的优点:
子类拥有父类的所有属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用;
继承初始化顺序
1、初始化父类再初始化子类
2、先执行初始化对象中属性,再执行构造方法中的初始化。
基于上面两点,我们就知道实例化一个子类,java程序的执行顺序是:
父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化--->子类对象构造方法
super和this
super在对象的内部使用,可以代表父类对象。
- 访问父类的成员
- 用来区分子类和父类的成员
- 调用父类的构造函数,只能在子类的构造函数中调用,且只能是第一句,不能何this同时使用。
this在对象内部使用,可以代表当前对象。
1.访问当前对象的成员
2.用来区分形参和成员属性
3.调用对象的其它构造函数,只能在构造函数中使用,且只能是第一句,不能何super同时使用
方法覆盖
子类方法名和参数要跟父类一样,子类方法的返回值要么跟父类一样要么是父类的子类型;子类方法不能缩小父类方法的访问权限。
方法重写和重载的相同点和区别:
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
方法的重写 (Overriding) 和重载 (Overloading) 是 java 多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
- (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载 (Overloading)。
- (2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写 (Overriding)。
- (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
多态
多态分为:方法的多态和对象的多态
方法的多态:方法的重载和重写
对象的多态(重点)
几个重要的规则:
- 一个对象的编译类型和运行时类型可以不一致
- 编译类型在定义对象的时候就确定了,不能改变
- 运行时类型可以变化
- 编译时类型看定义时“=” 左边的,运行时类型看“="右边的
多态的细节
多态的发生,一定是两个类之间是存在继承关系
向上转型:父类引用指向子类对象,此时可以调用父类的所有成员,遵循访问修饰符,不能调用子类特有的成员。最终运行效果看子类具体的实现
向下转型:子类类型 引用名 = (子类类型)父类引用。只能强转父类的引用,不能强转父类的对象;要求父类的引用必须指向当前目标类型的对象;向下转型后,可以调用子类类型的所有成员;
属性没有重写之说,属性是没有多态,属性看的是编译类型
instanceof比较操作符,用于判断对象是否是某类型或者某类型的子类型,看的是运行类型
class AA{}
class BB extends AA{}
//编译类型是AA,运行时类型是BB
AA aa = new BB();
System.out.println(aa instanceof AA);//true 看的是运行时类型
System.out.println(aa instanceof BB);//true 看的是运行时类型
Java动态绑定机制(重点)
public class AA {
public int i = 10;
public int getI(){
return i;
}
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
}
public class BB extends AA {
public int i = 20;
@Override
public int getI(){
return i;
}
@Override
public int sum() {
return i + 20;
}
@Override
public int sum1() {
return i + 10;
}
}
AA aa = new BB();
System.out.println(aa.sum());//40
System.out.println(aa.sum1());//30
//当子类没有sum方法时候,怎么调用,使用方法的调用机制,一直向上找,在父类中发现有方法,就调用父类的方法,此时,发现父类方法中
//有有一个语句:getI() + 10,getI()在子类和父类中都存在,此时就会触发Java的动态绑定机制,来决定调用哪个方法
Java的动态绑定机制:
1.当调用对象方法的时候,该方法会和该对象的运行时类型绑定;
2.调用属性的时候,没有动态绑定机制,哪里声明,哪里调用;
public class AA {
public int i = 10;
public int getI(){
return i;
}
public int sum(){
return getI() + 10;
}
public int sum1(){
return i + 10;
}
}
public class BB extends AA {
public int i = 20;
@Override
public int getI(){
return i;
}
}
//aa的编译类型是AA,运行时类型是BB
AA aa = new BB();
System.out.println(aa.sum());//30
System.out.println(aa.sum1());//20
当调用aa.sum方法的时候,先看方法的运行时类型为BB,先在类BB中查看,是否有sum方法,没有就会触发继承机制,在父类中查找,是否有sum方法,父类中存在,return getI() + 10;
,这时会出现一个问题,getI在父类和子类中都存在,那么如何调用,这时就会触发动态绑定机制,方法会和运行时对象绑定,触发该方法的aa的运行时类型是BB,所以调用的是BB中的getI,所以输出的是20+10,输出30;,当调用sum1方法的时候,会使用同样的机制找到父类的方法sum1,此时语句为return i + 10;
,属性没有动态绑定机制,哪里声明,调用哪里,先看AA中是否有i,发现有就返回10+10,输出20.
多态的应用
多态数组,定义父类对象数组,实际存放的是子类对象的实例
多态参数,形参是父类类型,实际传入的参数是子类类型
Object类
-
equals方法
经典面试题:== 与 equals的区别?
== 是一个比较运算符,既可以判断基本类型也可以判断引用类型,判断基本类型判断的是值是否相等,判断引用类型,判断的是地址是否相同,即判断是不是同一个对象。equals是Object类的方法,只能判断引用类型,默认判断的是地址值是否相同,子类往往重写该方法,用于判断内容是否相同。
-
hashCode
1.提高具有哈希结构的容器的效率
2.两个引用如果指向同一个对象,则哈希值一定一样
3.两个引用如果指向不同的对象,则哈希值不一样
4.哈希值主要根据地址来的,但不能完全将哈希值与地址等同