什么是面向对象?
面向对象是一种基于面向过程的编程思想,是向现实世界模型的自然延伸,这是一种“万物皆对象”
的编程思想。由执行者变为指挥者,在现实⽣生活中的任何物体都可以归一类事物,而一个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动。
什么是对象?
我个人的理解是一个实例的所有属性的综合
,比如说一个学生,当创建一个学生的对象,那就需要学生的就读学校,就读班级,定位学生的学号,学生的学习成绩,学生的授课老师,这些综合起来就可以描述一个学生的群体,反正总的一句话,万事万物皆对象
。
面向对象和面向过程的区别
编程思路不同:面向过程以实现功能的函数开发为主,而面向对象要首先抽象出类、属性及其⽅方法,然后通过实例化类、执行方法来完成功能。
封装性:都具有封装性,但是面向过程是封装的是功能,而面向对象封装的是数据和功能。
面向对象具有继承性和多态性,而面向过程没有继承性和多态性,所以面向对象优势很明显。
java语言的三大特性
封装
把对象的基本属性和基于属性的操作封装起来,使其构造出一个不可分割的独立整体,数据被保护在对象的内部,尽可能地隐藏内部的细节,使用者不需要知道里面的细节,这个时候对数据的访问或赋值等操作就只能通过已经定义的接口进行访问。
优点
减少耦合:可以独立开发,测试,优化,修改等 。
减轻维护的负担:更容易被理解,而且可以独立调试不影响其他的模块,提高了软件的可用性
降低了构建大型系统的风险: 即使整个系统不可用,但是这些独立的模块却有可能是可用的
实例代码:
封装的Teacher类中包含有name和subject属性,外界只能通过已经封装好的get和set方法访问Teacher类中的属性。
public class Teacher {
private String name;
private String subject;
public Teacher() {
}
public Teacher(String name, String subject) {
this.name = name;
this.subject = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public void teach(){
System.out.println(name + "老师教导"+ subject);
}
private String teachNo(){
return "没有老师教学。";
}
public static void main(String[] args) {
// 创建对象
Teacher teacher = new Teacher("小红","数学");
// 通过已经定义的接口进行修改属性值
teacher.setName("小明");
// 打印验证
teacher.teach();
}
}
结果:
继承
继承指的是从已有的类中得到继承信息并创建新类的过程,提供继承信息的类被称作父类(超类/基类),得到继承信息的被称为子类(派生类)。继承实现了IS-A关系,继承应当遵循里氏替换原则,子类对象必须能够替换掉父类的所有对象。
实例代码:
创建父类Person对象
public class Person {
private int age;
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// public int getAge() {
// return age;
// }
private int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void speak(){
System.out.println("我的名字是" + name + "我今年" + age + "岁了");
}
}
创建子类Student对象
public class Student extends Person{
private String id;
private String subject;
public Student() {
}
public Student(String id, String subject) {
this.id = id;
this.subject = subject;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public static void main(String[] args) {
Student student = new Student();
// 子类可以通过封装的方法访问到父类和之类的所有get和set方法。
student.setId("123456789");
student.setName("小红");
student.setAge(18);
student.setSubject("语文");
System.out.println(student.id);
System.out.println(student.name);
System.out.println(student.getAge());
System.out.println(student.subject);
// 报错:子类无法获取到父类的私有属性和私有方法
// System.out.println(student.age);
// System.out.println(student.getAge());
}
}
结果:
代码分析:
Student类继承Person类,Person是超类(基类),而Student类是子类(派生类),Student类继承自Person类,因此Student类对象 可以访问到超类的所有非private属性或者方法。
动态绑定和静态绑定
绑定:把一个方法与其所在类/对象关联起来叫做绑定,绑定分为前期绑定(静态绑定)和后期绑定(动态绑定)。
静态绑定
静态绑定(前期绑定)指的是程序在运行前就知道这个方法是属于哪个类,在编译时就可以直接连接到这个类中,直接定位到这个方法里面。在java中,比如说final,private,static等修饰的方法和构造函数都是静态绑定的,不需要程序运行,也可以不需要具体的实例对象就可以知道这个方法的具体内容。
实例代码:
public class Car {
// 静态绑定
private int price;
// 静态绑定
public int getPrice() {
return price;
}
// 静态绑定
public void setPrice(int price) {
this.price = price;
}
// 静态绑定
static {
System.out.println("我是静态代码块!");
}
public void move(){
System.out.println("Moving");
}
public static void main(String[] args) {
Car car = new Car();
car.setPrice(300);
System.out.println("这辆车的价格是:" + car.getPrice());
}
}
结果:
动态绑定
动态绑定指的是程序在运行过程中,需要具体的实例对象才能确定具体是哪一个方法,简单来说在编译时期不知道这个方法是属于哪个对象的,需要等对象创建出来,通过对象实例才能实现方法调用。
动态绑定的过程:
虚拟机提取对象的实际类型的方法表。
虚拟机搜索方法签名 。
调用方法
实例代码:
public class Head {
public void initialization(){
System.out.println("脑袋初始化完毕!已苏醒!");
}
}
public class Mouth extends Head{
public void initialization(){
System.out.println("嘴巴初始化完毕!");
}
public void eat(){
initialization();
System.out.println("嘴巴正在吃东西");
}
public static void main(String[] args) {
Mouth mouth = new Mouth();
mouth.eat();
}
}
结果:
向上转型
以父类对象当作引用对象同时创建子类对象,简单来说就是左边的引用参数是父类,右边创建对象的参数是子类,这就叫向上转型,同时因为引用对象是父类的对象,所以只能调用父类的方法,如果子类重写了父类的方法,那么程序在执行的过程中会优先寻找子类已经重写的方法,如果找不到,那就向上转型寻找父类的方法。同时父类可以是类或者是接口(接口只能定义常量和方法,不可以定义变量)
实例代码:
public class Person {
int age;
String name = " 父类属性 ";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
System.out.println("我是父类方法,修改age值:" + age);
this.age = age;
}
public void speak(){
System.out.println("我的名字是" + name + "我今年" + age + "岁了");
}
}
public class Student extends Person{
String name = "子类属性";
public String getName() {
System.out.println("我是子类方法");
return name;
}
public void setName(String name) {
System.out.println("我是子类方法,修改name值:" + name);
this.name = this.name + name;
System.out.println(this.name);
}
public static void main(String[] args) {
Person person = new Student();
person.setAge(32);
person.setName("小红");
person.speak();
}
}
结果:
代码分析:
代码中修改了父类的age和name属性值,子类重写了name属性的get和set方法,因此调用的是子类重写的方法,而修改age值调用的是父类的方法。另外属性值的使用,子类中也包含有name属性,但是在修改使用name属性的过程中,程序优先使用父类的属性。
向下转型
第一种情况: 父类的引用对象指向的是子类创建的对象,那么在向下转型的过程中是安全的,在编译和运行的过程中都不会发生异常。
实例代码:
父类为Animal类
public class Animal {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
子类为Cat类
public class Cat extends Animal{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "我是一只名字叫" + name + "的猫" + "我可以在" + getType() + "运动";
}
}
测试调用类为Dynamic类
public class DynamicTest {
public static void main(String[] args) {
// 向上转型,创建的实例对象为子类的对象
Animal animal = new Cat();
// 向下转型,不会出现任何问题
Cat cat = (Cat) animal;
cat.setName("汤姆");
cat.setType("地上");
System.out.println( cat.toString());;
}
}
结果:
第二种情况: 父类的引用对象是父类本身,那么在向下转型的过程中是不安全的,程序在编译的时候不会出错,但是在运行的时候会发生java.lang.ClassCastException
异常,这个时候就需要instanceof关键字
解决异常。instanceof关键字
通过返回一个布尔值来比较出这个对象是否是这个特定类或者是它的子类的一个实例。
实例代码:
public class Bird extends Animal{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "我是一只名字叫" + name + "的鸟" + "我可以在 " + getType() + "运动";
}
}
public class DynamicTest {
public static void main(String[] args) {
Animal animal = new Animal();
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.setName("汤姆");
cat.setType("地上");
System.out.println(cat.toString());
}else if(animal instanceof Bird){
Bird bird = (Bird) animal;
bird.setName("咕咕");
bird.setType("天上");
System.out.println(bird.toString());
}
}
}
代码分析:
上面的代码不会有执行结果的,因为创建的对象实例是父类Animal类,它经过if else 语句的时候直接跳过运行,全部为false。如果说将第一行代码改为Animal animal = new Bird();
,结果为:
总结
父类的引用可以指向子类,但是子类的引用不可以指向父类,一旦这样操作,由于对象的继承关系,程序在编译的时候不会发生异常,但是在运行的过程中会
java.lang.ClassCastException
异常。
在向上转型的过程中,对象引用不可以调用子类的新生的方法,只能调用父类的实现方法,但是子类中如果重写有父类的方法,对象引用还是可以调用重写后的方法的。
在向下转型的过程中,如果父类引用指向的是子类的对象创建,那么程序在编译和运行阶段都可以正常运行,如果父类引用指向的是父类的对象创建,将对象引用指向子类,编译的时候不会会发生异常,但是在运行阶段会发生
java.lang.ClassCastException
异常。
程序可以先向上转型,然后再向下转型,比如上文中向下转型的第一种情况。但是不可以一开始就向下转型。
多态
多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性
和运行时的多态性
。
运行时多态
方法重写( override )实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要三个条件∶
1.方法重写
:子类继承父类并重写父类中已有的或抽象的方法;
2.对象造型
:用父类型引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为。也就是向上转型。
3继承
:继承就是重已有的类中得到信息并创建新类的过程
编译时多态
方法重载( overload )实现的是编译时的多态性(也称为前绑定),在java程序中,使用final,static,private修饰的方法,属性,类(static可以修饰内部类)在编译的时候就确定了是哪个对象里面的内容。
实例代码:
public class University {
private String address;
private String name;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "大学的地址是" + address + ", 名字叫作:" + name;
}
}
public class Teacher extends University{
private String name;
private String subject;
public Teacher() {
}
public Teacher(String name, String subject) {
this.name = name;
this.subject = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public void teach(){
System.out.println(name + "老师教导"+ subject);
}
public static void main(String[] args) {
// 创建对象
Teacher teacher = new Teacher("小红","数学");
teacher.setAddress("中国湖南");
teacher.setName("超级大学");
System.out.println(teacher.toString());
teacher.teach();
}
}
结果: