以下内容若有误,欢迎私信我或在下方留言,谢谢^_−
面向对象
1.面向对象概述
面向对象是相对于面向过程而言的,所以,理解面向对象,先要理解面向过程是什么。
面向过程(Procedure Oriented)关注的是我该怎么做,是通过分析要解决的问题,并拆分成若干个步骤,然后按照这些步骤依次进行。
比如说我现在想在家里吃面,那就需要依次进行买面、煮面、吃面、洗碗这些步骤,这就是面向过程的思想。
面向对象(Object Oriented,OO)关注的是我该让谁做,是把构成问题的事物按照一定规则划分为多个独立的对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
比如同样是上面的例子,我现在不是在家里了,而是去面馆,面不再需要自己煮,所以买面由面馆的进货员负责,煮面由面馆的厨师负责,洗碗则由面馆的后勤负责,而我只需要负责吃面就可以了,这就是面向对象的思想。
面向对象编程(Procedure Oriented Programming,OOP)的本质:以类的方式组织代码,以对象的组织封装数据。
2.面向对象特性
2.1 封装
(1)封装是面向对象的核心思想,将对象的特性和行为封装起来,不需要让外界知道具体实现细节。比如玩手机,我们并不需要知道手机内部到底是怎么运作的,就可以使用手机。
(2)程序的设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。
(3)封装的优点:
- 良好的封装可以降低耦合度。
- 保证数据的安全性,不让外界随意更改内部信息。
- 隐藏代码,实现细节。
- 增强系统的可维护性。
public class Student {
// 使用private对变量进行修饰,达到封装的目的
// 成员变量私有
private String name; // 姓名
private int age; // 年龄
private char sex; // 性别
// 使用public对方法进行修饰,以便外部能够通过使用get、set方法对成员变量进行操作
// 方法公有
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
// 对setAge()方法添加判断条件,外部看不到
public void setAge(int age) {
if (age < 120 && age > 0) {
this.age = age;
}else {
age = 0;
}
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
}
2.2 继承
(1)继承主要描述的是类与类、接口与接口之间的关系,继承可以在无需重新编写原有类的情况下,对原有类的功能进行扩展,同时也解决了类中存在共同代码的问题。
(2)继承的关键字是extends。Java类只有单继承,但可以多重继承,即一个子类可以有一个父类,该父类可以有自己的父类。
举个栗子:你只有一个亲生爸爸,不能有两个亲生爸爸,但你的爸爸可以有自己的爸爸(也就是你爷爷),而你不能既是你爸爸的儿子又是你爷爷的儿子,也不能是其他男性的亲生儿子。
小提一下:父类也称为超类、基类,子类也称为派生类、拓展类。
/*动物类(父类)*/
public class Animal {
private String name;
private double weight;
public void eat() {
System.out.println("吃饭ing");
}
}
/*狗类(子类)*/
public class Dog extends Animal {
public void run() {
System.out.println("跑步ing");
}
}
/*测试类*/
public class Test {
public static void main(String[] args) {
// 创建一个对象
Dog dog = new Dog();
// 调用动物类的eat()方法
dog.eat();
// 调用狗类的run()方法
dog.run();
}
}
/*
输出结果:
吃饭ing
跑步ing
*/
2.3 多态
(1)概念
多态指的是在一个类中定义的字段和方法被其他类继承后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同行为特性。
(2)多态的三个前提
- 存在继承或实现关系
- 子类重写父类方法
- 父类引用指向子类对象(如Father father = new Son();)
(3)好处和弊端
- 好处:提高程序的扩展性。定义方法时,使用父类型作为参数,以后使用的时候,就可以使用不同具体的子类型参与操作。
- 弊端:不能使用子类特有方法。
/*动物类*/
public class Animal {
public int age = 2;
public void eat() {
System.out.println("吃东西");
}
}
// 子类(狗类)继承父类(动物类)
public class Dog extends Animal {
public int age = 3;
public int weight = 20;
// 重写父类(动物类)方法
@Override
public void eat() {
System.out.println("狗吃狗粮");
}
// 子类特有方法
public void lookHouse() {
System.out.println("狗狗看家");
}
}
/*测试类*/
public class Test {
public static void main(String[] args) {
/*
向上转型:
从子类到父类,父类引用指向子类对象
*/
Animal dog = new Dog();
// 多态是方法的多态,属性没有多态性,因此下面输出为:2
System.out.println(dog.age);
// 子类重写父类方法,执行子类方法,因此下面输出为:狗吃狗粮
dog.eat();
// 不能调用子类特有的方法
dog.lookHouse(); // 报错:Cannot resolve method 'lookHouse' in 'Animal'
/*
向下转型:
从父类到子类,父类引用转为子类对象
*/
((Dog)dog).lookHouse();
}
/*
运行结果:
2
狗吃狗粮
狗狗看家
*/
编译看左边,执行看右边:
父类变量引用指向子类对象时,该变量的成员变量和静态方法和父类保持一致,而对于非静态方法,编译时与父类一致,运行时则与子类一致。
3.类与对象的关系
类是对某一类事物的抽象描述,包括字段(成员变量)和行为(成员方法)。
比如人,有身高、体重等属性,也有吃、喝、玩等行为,将这些共同的属性和行为提取出来就可以形成一个类,所以一个类可以简单理解为就是一个模板。
对象用于表示现实中该类事物的个体,是类的实例化、具体化,并且一个类可以对应多个对象。
比如猫猫、狗狗、鸡鸡、鸭鸭等就是动物类的对象。当然,猫又分为黑猫、白猫、大猫、小猫,所以,猫也可以单独作为一个类。
类是抽象的,对象是具体的
public class Demo {
public static void main(String[] args) {
// 通过new创建一个对象
Animal animal = new Animal();
// 给该对象赋值
animal.name = "小一";
animal.weight = 20.20;
// 调用该对象的方法
animal.eat(); // 小一正在吃饭!现在是20.2kg。
}
}
// 定义一个动物类
class Animal {
// 定义两个成员变量
String name;
double weight;
// 定义一个行为(方法)
public void eat() {
System.out.println(name + "正在吃饭!现在是" + weight + "kg。");
}
}
4.构造方法
构造方法,也称为构造器,是一种专门用来创建对象的方法,其功能主要是完成对象的初始化,与其他方法一样也可以重载。
public class Person {
String name;
int age;
// 无参构造方法
public Person() {
}
// 有参构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
注意:
- 构造方法必须和所在类的名字一模一样。
- 构造方法不需要写返回类型,也不能写void和return语句。
- 如果类中没有编写任何构造方法,编译器会自动创建一个无参构造方法(可以通过IDEA反编译查看.class文件)。
- 如果类中编写了至少一个构造方法,那编译器就不会自动创建无参构造方法,若需要使用无参构造方法,则需要手动添加(强烈建议)。
拓展1:值传递和引用传递
值传递:指在调用函数时将实际参数复制一份传递给函数,这样在函数中对参数进行修改,将不会影响到实际参数。
/*值传递*/
public class Demo {
public static void main(String[] args) {
int num = 2;
System.out.println(num); // 输出结果:2
change(num); // 并没有改变实际参数
System.out.println(num); // 输出结果:2
}
// 无返回值
private static void change(int a) {
a = 20;
System.out.println(a); // 输出结果:20
}
}
引用传递:指在调用函数时将实际参数的地址直接传递到函数中,这样在函数中对参数所进行的修改,将影响到实际参数。
Java中只有值传递!以下举例是错误的,因为调用change方法(函数)时,是将实参所引用的地址通过值传递给了形参,修改的其实是引用地址所指向的堆内存中的数据,并不是修改地址。
public class Demo {
public static void main(String[] args) {
Student student = new Student();
// 输出结果:null今年0岁!
System.out.println(student.name + "今年" + student.age + "岁了!");
change(student);
// 输出结果:小一今年18岁!
System.out.println(student.name + "今年" + student.age + "岁了!");
}
// 无返回值
private static void change(Student student) {
student.name = "小一";
student.age = 18;
}
}
// 定义Student类
class Student {
String name;
int age;
}
引用传递举例(C++):
#include <iostream>
using namespace std;
void change(int& num)
{
num = 10;
}
int main()
{
int num = 5;
cout << num << endl; // 输出结果:5
change(num);
cout << num << endl; // 输出结果:10
return 0;
}
拓展2:方法重写
方法重写用于继承关系中,即子类重写父类的方法(名称相同,方法体不同)。当子类需要父类的功能,而子类又有自己的特有内容时,就可以通过方法重写实现,这样,子类即继承了父类的功能,又有自己的特有内容。
/*人类*/
public class Person {
// 父类方法
public void sleep (String name) {
System.out.println(name + "在睡觉!");
}
}
/*学生类*/
public class Student extends Person{
// 重写父类方法
public void sleep(String name) {
System.out.println(name + "在玩手机");
// 继承父类方法
super.sleep(name);
}
}
/*测试类*/
public class Test {
public static void main(String[] args) {
// 创建对象,调用方法
Person person = new Person();
person.sleep("小一");
System.out.println("-------------");
Student student = new Student();
student.sleep("小二");
}
/*
运行结果:
小一在睡觉!
-------------
小二在玩手机
小二在睡觉!
*/
注意事项:
- 方法名必须相同
- 参数列表必须相同
- 修饰符的权限范围只增不减(public>protected>default>private)
- 被static、final、private修饰的方法不能被重写
注解:@Override
作用:帮助检查重写方法的方法声明的正确性。
【程序员养成之路】Java基础篇 2-初学Java必知的基础语法
【程序员养成之路】Java基础篇 3-反手就能写个冒泡排序的数组
【程序员养成之路】Java基础篇 5-从异常机制认识常见bug
【程序员养成之路】Java基础篇 7-流进流出的IO流(一)
【程序员养成之路】Java基础篇 8-流进流出的IO流(二)
【程序员养成之路】Java基础篇 9-认识一下类加载器与反射