面向对象的三大特性
## 三大特性
封装
一句话:属性私有化,get/set
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
优点
- 提高程序的安全性,保护数据。
- 隐藏信息,实现细节。
- 统一接口
- 减少耦合。
实现封装
- 修改属性的可见性来限制对属性的访问(一般限制为private), 只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
- 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问
public class Student {
private int age;// 年龄
private String name;// 名字
// 提供一些public 的get、set方法。
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 100 || age < 0) {
this.age = 3;
} else {
this.age = age;
}
}
}
采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
public class Application {
public static void main(String[] args) {
Student student = new Student();
//student.name; // private私有化之后,外部类无法访问该属性。
student.setName("小明");
student.setAge(18); // 年龄在内部处理逻辑
// student.setAge(999); //处理不合理的年龄
String name = student.getName();
int age = student.getAge();
System.out.println(name + age +"岁啦");
}
}
以上实例中public方法是外部类访问该类成员变量的入口。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。
继承
一句话:子类继承父类的特征和行为。
extends 是"扩展"的意思,子类是父类的扩展。
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。(重写)
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
关键字
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,
当一个类没有继承的两个关键字,则默认继承object祖先类(这个类在 java.lang 包中,所以不需要 import)。
extends:在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
implements:可以同时继承多个接口(接口跟接口之间采用逗号分隔),使java具有多继承的特性。
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
super:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this:指向自己的引用。
final :声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:
区别 | 前提 | 构造方法 | |
---|---|---|---|
this | 表示本身调用者这个对象 | 没有继承也可以使用 | this() |
super | 代表父类对象的引用 | 只能在继承条件才可以使用 | super() |
注意点
- super()调用父类的构造方法,必须在构造方法的第一个
- this()调用自己的构造方法,必须在构造方法的第一个,所以this() super() 不能同时出现
- super()只能出现在子类的方法或构造方法中!
继承格式
class 父类 {
}
class 子类 extends 父类 {
}
父类Person
/**
* 隐式继承Object类
*/
public class Person {
private String name;
public void say() {
System.out.println("说话说话");
}
}
子类Student继承Person
public class Student extends Person {
}
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
}
}
子类继承了父类的公共方法和属性
继承类型
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
IDEA快捷键 ctrl + H 查看继承关系
方法重写
重写都是方法的重写,与属性无关
重写:需要有继承关系,子类重写父类的非静态方法!
- 方法名必须相同
- 参数列表必须相同
- 修饰符可以扩大但不能缩小 public >protected>default>private
- 抛出的异常范围:可以被缩小但不能扩大;
为什么需要重写?父类的功能,子类不一定需要,或者不一定满足。
多态
一句话:同一个方法可以根据发送对象的不同而采取多种不同的行为方式。
使用手机扫描二维码支付时,二维码并不知道客户是通过何种方式进行支付,只有通过二维码后才能判断是走哪种支付方式执行对应流程。
多态可以实现动态编译:类型在执行过程中才可以确定:增强可扩展性~
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
前提条件
- 继承
- 重写
- 父类引用指向子类
特征
- 对象能执行的方法和左边的类型有关,和右边的关系不大
- 子类能调用的方法都是自己的或者继承父类的。
- 父类型可以指向子类,但是不能调用子类独有的方法。
- 父类想要执行子类的方法需要向下转型,高转低是强制转换!
优点
- 减耦合
- 增强可以替换性
- 可扩展性
- 灵活性等
关键词instanceof
instanceof:测试它左边的对象是否是它右边的类的实例
判断一个对象是什么类型,instanceof可以判断两个类之间是否存在父子关系
public class Application {
public static void main(String[] args) {
// instanceof
// 1.先判断左侧的引用类型与右边类是否有关系
// 2.再判断左侧的实际类型与右边类是否有关系
// 继承关系
// Object
// / \
// String Person
// / \
// Student Teacher
// 公式 ClassA obj = new ClassB();
// obj instanceof 任意类
// 能够通过编译 取决于ClassA 与 任意类 有没有关系
// true/false 取决于ClassB 与 任意类 有没有关系
// 对象的引用类型是Student类
Student obj1 = new Student();
System.out.println("=======Student->Student==========");
System.out.println(obj1 instanceof Student);
System.out.println(obj1 instanceof Person);
System.out.println(obj1 instanceof Object);
// System.out.println(obj1 instanceof Teacher); // Student 和 Teacher 是兄弟关系,报错~
// System.out.println(obj1 instanceof String); // Student 与 String无直接联系,报错!
Person obj2 = new Student();
System.out.println("=======Person->Student==========");
System.out.println(obj2 instanceof Student);
System.out.println(obj2 instanceof Person);
System.out.println(obj2 instanceof Object);
System.out.println(obj2 instanceof Teacher);
// obj2 指向的是Student类,与Teacher无直接联系
//System.out.println(obj2 instanceof String); // Person 与 String无直接联系,报错!
Object obj3 = new Student();
System.out.println("=======Object->Student==========");
System.out.println(obj3 instanceof Student);
System.out.println(obj3 instanceof Person);
System.out.println(obj3 instanceof Object);
System.out.println(obj3 instanceof Teacher);
System.out.println(obj3 instanceof String);
Person obj4 = new Person();
System.out.println("=======Person->Person==========");
//
System.out.println(obj4 instanceof Student);
System.out.println(obj4 instanceof Person);
System.out.println(obj4 instanceof Object);
System.out.println(obj4 instanceof Teacher);
// System.out.println(obj4 instanceof String);
// Person 与 String无直接联系,报错!
}
}
类型转换
基础类型转换
- 低 ------>高 隐式转换
- 高------->低 强制转换
引用类型转换
-
低(子类)----->高(父类)
-
低转高:Person obj1 = new Student(); 即向上转换,不需要强制转换
-
高转低 Student obj2 = (Student)obj1; 即向下转换,需要强制转换
注意:之前学习继承的时候说,子类是父类的扩展,所以子类转换成父类时会丢失一些本身的方法或属性。
父类型可以指向子类,但是不能调用子类独有的方法。因为子类独有的方法丢失了,无法调用。
多态小结
-
多态是方法的多态,属性没有多态
-
父类和子类
- 有联系。instanceof
- 类型转换异常!ClassCastException!
- 子类转换成父类,向上转型,丢失方法
- 父类转换成子类,向下转型 (强制转换)
-
存在条件
- 继承关系
- 方法需要重写
- 父类引用指向子类对象!
-
不能重写的方法
- static 静态方法属于类,不属于实例
- final常量修饰不能继承
- private 方法私有
OOP面向对象的编程思想就是抽象
!
[代码区](# 多态代码)
多态代码
第一种情况 对象的实际类型确定,指向的引用类型不确定(可以用父类引用类型指向子类对象)
如下有Person类
Person.java
public class Person {
public void say() {
System.out.println("father---say");
}
}
Student.java
// 继承 Person 类
public class Student extends Person {
}
有继承关系才可以指向
Application.java
/**
* 测试多态的表现
*/
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student(); // 创建出一个学生
// new Person(); // 创建出一个人
// 可以指向的引用类型就不确定了
Student s1 = new Student(); // new 一个学生 名字叫 学生
Person s2 = new Student();// new 一个学生 名字叫 人(学生也是人,他继承了人)
Object s3 = new Student();
s2.say();
}
}
运行结果
father---say
子类能调用的方法都是自己的或者继承父类的。如果子类中没有say方法,则调用继承于父类的say方法。
如果在子类重写了父类的say方法,则调用本身的say方法。如下代码重写了父类的say方法:
Student.java
public class Student extends Person {
@Override
public void say() { // 重写父类say方法
System.out.println("son ---- say");
}
}
Application.java
/**
* 测试多态的表现
*/
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student(); // 创建出一个学生
// new Person(); // 创建出一个人
// 可以指向的引用类型就不确定了
Student s1 = new Student(); // new 一个学生 由 学生
Person s2 = new Student();// new 一个学生 由人接收(学生也是人,他继承了人)
Object s3 = new Student();
s1.say();// 学生 说
s2.say();// 学生 说
}
}
运行结果
son ---- say
son ---- say
因为实例是确定的,s1,s2都是new 出来的Student()实例对象,方法的功能与实例的实现有关。
如果在Student类中添加一个eat()方法
public class Student extends Person {
@Override
public void say() {
System.out.println("son ---- say");
}
public void eat() {
System.out.println("son ---- eat");
}
}
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
Person s2 = new Student();
//对象能执行的方法和左边的类型有关,和右边的关系不大
s1.say();// 学生 说
s2.say();// 学生 说
s1.eat(); // s1 左边是学生类,学生类里面有say方法。成功调用
//s2.eat(); // s2 左边是人类,人类中没有eat方法,又不能继承,所以调用失败
}
}
对象能执行的方法和左边的类型有关,涉及到类型转换,高-->低 会丢失精度。比如
public class Test {
public static void main(String[] args) {
float a = 1.5f;
long b = 99999999999999L;
System.out.println((int)a); // 结果打印1 丢失了小数部分
System.out.println((int)b); // 结果打印276447231 丢失数据溢出部分
}
}
但在学习继承的时候知道,子类继承父类获取父类的非私有属性和方法,子类是父类的扩展。
这里引用类型和基本类型的转换是存在区别的,基本类型高精度转型为低精度会丢失数据。而引用类型子类转型为父类会丢失方法或属性。