本文章收录于

JavaSE系列教程【JavaSE零基础系列教程目录】

第四篇04【面向对象、封装、static】


一、面向对象

1.1 面向对象概述

1.1.1 什么是对象

在Java中,对象(Object)是指一个具体事物的实例,任何事物都可以使用对象(类)来描述,如猫、狗、计算机、杯子、云、水、空气、叶子、灰尘等看得见的、看不见的、宏观的、微观的、具体的、抽象的都是对象,总之"万物皆对象";

1.1.2 面向对象程序设计

我们前面就提到过,Java是一门面向对象的编程语言,面向对象是一种程序设计思想,与之对应的还有面向过程程序设计;面向对象是把一个对象的特征(属性)和行为单独封装到对象源代码中;这些属性和行为都被集中到一个地方,这样比把方法或者过程与数据分散开来更为方便和安全,含义更加明确;

1.1.3 面向对象和面向过程

举例:开车去上班

  • 面向过程:去车库提车、拿钥匙、打火、踩离合、挂挡、踩油门、刹车、加油、到公司、找车库停车
  • 面向对象:找个司机(对象)、到公司

可以看得出来,面向过程关注的是步骤,将所有步骤连在一起"我就能开车到公司上班",面向对象则关注的是"开车去上班"这个事物整体,完成这件事的步骤全部封装起来,交给指定的对象去做(司机);


面向过程适合简单、不需要协作的事务,重点关注如何执行;面向过程来处理事物时,我们首先思考的是"如何按步骤来实现?"一步一步,最终完成,适合简单任务,不需要过多协作的情况;

但是当我们思考较为复杂的设计任务时,比如将"开车"改为了"造车",就会发现我们列出步骤来一步步执行是不现实的,一个汽车厂不可能将造汽车的全部过程都执行一遍,从一个个螺丝的加工生产到发动机的生产、座椅的生产、汽车玻璃的提炼/生产、甚至车载芯片的研发/生产、发动机的研发、电器设备的研发等,相信国内汽车厂没有一家是这样造汽车的,因为自己要做的事物太多了。

适合小白看的Java面向对象,超详细_类和对象

这个时候,面向对象更加符合我们的思维方式。我们首先思考的是"车由什么组成?"开始思考问题,为了协作,我们找到轮胎厂采购轮胎(交给轮胎厂对象来生产轮胎),找到玻璃厂采购玻璃(玻璃厂对象来生产玻璃),找到发动机厂采购发动机等,最终进行产品的组装。而不是按步骤一个个造出来。这就是面向对象思维方式

但是需要注意的是,具体到某个轮胎厂、玻璃厂的一个个流水线操作仍然是有步骤的,仍然离不开面向过程思维;面向对象可以帮助我们从整体上分析整个系统,但是具体到某一个操作上,任然需要面向过程的思路去处理;面向对象和面向过程是相辅相成的,面向对象离不开面向过程;

面向对象与面向过程都是解决问题的一种思维方式,也都是代码的组织方式:

  • 面向过程是一种"执行者思维",主要用于解决一些简单问题的场景
  • 面向对象是一种"设计者思维",主要解决复杂、需要协作的问题场景

1.1.4 面向对象的特点

面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态。

Tps:关于面向对象和面向过程大家不必太过纠结,我们现在是初学者,理解表面意思即可,随着后面的深入学习,我们会对面向对象有着更深刻的理解;

1.2 类和对象

1.2.1 类和对象的区分

在现实世界中,属于同一类的对象很多,类是抽象的,不是具体的,我们人习惯以对象的方式认识现实世界;

例如我说一辆汽车,那你脑海中立马就能够呈现出一辆汽车的模样吧,不管他是什么品牌、什么颜色、什么价格、什么参数等总而言之,都是一辆汽车,都是属于"汽车类"产品;

适合小白看的Java面向对象,超详细_面向过程_02

tips:类是抽象的,对象是具体的,对象是类的实例化;

类是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征行为特征来描述该类事物。

  • 属性:该事物的状态信息;
  • 行为:该事物的功能信息;

1.2.2 类和对象的举例

  • 类举例:

举例:猫类

属性:名字、体重、年龄、颜色; 行为:走、跑、叫;

  • 实例化对象:

猫对象:

属性:旺财、8kg、6岁、棕色;行为:悄悄的走、飞快的跑、喵喵叫;

1.3 Java类的定义

1.3.1 类的定义格式

public class 类名 {
	//成员变量
    //成员方法
}
  • 定义类:就是定义类的成员,包括成员变量和成员方法。
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
  • 成员方法:和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面课程中再详细讲解。

类的定义格式举例:

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Student {
    // 成员变量(属性)
    String name;        //姓名
    int age;            //年龄
    // 成员方法(行为) 
    public void study(){
        System.out.println("学习");
    }
    public void eat(){
        System.out.println("吃饭");
    }
}

1.3.2 类的实例化

前面我们说道过,类是抽象的,不是具体的,类只是负责把事物描述起来,提供模板;对象是类的实例化,是具体的;我们定义好一个Java类后需要通过对象将类进行实例化;

  • 创建对象:
类名 对象名 = new 类名();
  • 使用对象访问类中的成员:
对象名.成员变量;
对象名.成员方法();
  • 练习:
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01 {
    public static void main(String[] args) {
        //创建对象格式: 类名 对象名 = new 类名();
        Student s = new Student();
        System.out.println("s: " + s);            //s: com.dfbz.demo.Student@1540e19d
        //直接输出成员变量的值
        System.out.println("姓名: " + s.name);      //null
        System.out.println("年龄" + s.age);         //0
        System.out.println("--------");
        //给成员变量赋值
        s.name = "刘德华";
        s.age = 38;
        //再次输出成员变量的值
        System.out.println("姓名: " + s.name);      //刘德华
        System.out.println("年龄: " + s.age);       //18
        System.out.println("---------");
        //调用成员方法
        s.study();          //学习
        s.eat();            //吃饭
    }
}

1.3.3 成员变量的默认值

数据类型

默认值

基本类型

整数(byte,short,int,long)

0

浮点数(float,double)

0.0

字符(char)

'\u0000'

布尔(boolean)

false

引用数据类型

数组,对象,String

null

  • 定义手机类并使用:
  • 属性:品牌,价格,颜色;
  • 行为:打电话,发短信

1)定义Phone类:

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Phone {
    //成员变量
    String brand;
    int price;
    String color;
    //成员方法
    //打电话
    public void call(String name){
        System.out.println("给"+name+"打电话");
    }
    //发短信
    public void sendMessage(){
        System.out.println("大家新年好!");
    }
}

2)定义测试类:

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02 {
    public static void main(String[] args) {
        //创建对象
        Phone p=new Phone();
        System.out.println("品牌: "+p.brand);     // null
        System.out.println("价格: "+p.price);     // 0
        System.out.println("颜色: "+p.color);     // null
        System.out.println("-------");
        p.brand="华为";
        p.price=2999;
        p.color="银白色";
        System.out.println("品牌: "+p.brand);     //华为
        System.out.println("价格: "+p.price);     //2999
        System.out.println("颜色: "+p.color);     //银白色
        System.out.println("------------");
        p.call("刘德华");
        p.sendMessage();
    }
}

1.4 对象内存图

回顾JVM内存,JVM总共分为5大内存区域,寄存器、本地方法栈、方法区、栈内存(虚拟机栈)、堆内存;和我们程序员有关的为方法区、栈内存、堆内存;

  • 方法区:存储类的信息(有多少变量、方法、是什么修饰符修饰的等)、常量信息、静态变量等信息
  • 栈内存(VM栈):方法调用时进栈内存执行,也就是方法运行时消耗的内存;
  • 堆内存:存储类的实例信息,只要是new出来的信息都存在堆内存

new这个类的时候,Jvm去方法区找有没有这个class,没有就加载到方法区,属性方法这些都是在方法区class中的;Jvm加载完后,就根据这个模板在堆中创建对象给属性赋默认值,然后再执行赋值语句给对象赋值;

一个对象内存图:

适合小白看的Java面向对象,超详细_面向对象_03

两个对象内存图:

适合小白看的Java面向对象,超详细_面向对象_04

对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息只保存一份,节约内存空间。

1.5 局部变量和成员变量

变量根据定义位置的不同,我们给变量起了不同的名字,看下列测试类:

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Person {
    String name;            // 成员变量
    int age;                // 成员变量
    /**
     * 跑步方法
     */
    public void running() {
        int stretch = 10;           // 局部变量
        System.out.println("跑了" + stretch + "公里");
    }
    /**
     * 自我介绍方法
     * @param name: 姓名(局部变量)
     * @param age: 年龄(局部变量)
     */
    public void intro(String name, int age) {
        System.out.println("大家好,我叫" + name + ",今年" + age + "岁");
    }
    /**
     * 计算方法(列出0~num之间的偶数)
     * @param num: 局部变量
     */
    public void calculate(int num) {
        for (int i = 0; i < num; i++) {
            if (num % 2 == 0) {
                System.out.println(num);
            }
        }
    }
}
  • 局部变量与成员变量:

适合小白看的Java面向对象,超详细_JavaSE_05

关于static关键字我们后面再解释,暂时把不用把实例变量与类变量区分开;

  • 成员变量和局部变量的区别:
  • 成员变量:
  • 1)成员变量定义在类中,在整个类中都可以被访问。
  • 2)成员变量分为类成员变量实例成员变量(对象成员变量),实例变量存在于对象所在的堆内存中。
  • 3)成员变量有默认初始化值。
  • 4)成员变量的权限修饰符可以根据需要,选择任意一个
  • 局部变量:
  • 1)局部变量只定义在局部范围内,如:方法内,代码块内等。
  • 2)局部变量存在于栈内存中,当方法弹栈(执行完毕)后,局部变量销毁;
  • 3)局部变量没有默认初始化值,使用前必须手动赋值;
  • 4)局部变量声明时不指定权限修饰符;

1.6 值传递和引用传递

1.6.1 思考

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo04 {
    public static void main(String[] args) {
        Phone p1=new Phone();
        p1.price=2899;
        test(p1);
        System.out.println(p1.price);           // 2899 or 3899?
        int i=20;
        test2(i);
        System.out.println(i);                  // 20 or 50?
    }
    public static void test(Phone p){
        p.price=3899;
    }
    public static void test2(int i){
        i=50;
    }
}

1.6.2 形参和实参

形参也叫形式参数,是一个方法的参数列表中的参数;实参也叫实际参数,是调用者在调用方法时实际传递的参数;

适合小白看的Java面向对象,超详细_Java基础_06

1.6.3 值传递和引用传递概念

  • 值传递:(参数类型是基本数据类型):方法调用时,实参把它的值传递给对应的形参,形参只是用实参的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形参值的改变不影响实参的值。
  • 引用传递:(参数类型是引用数据类型参数):也称为传地址。方法调用时,实参是对象(或数组),这时实参与形参指向同一个地址,在方法执行中,对形参的操作实际上就是对实参的操作,这个结果在方法结束后被保留了下来,所以方法执行中形参的改变将会影响实参。

在Java中,除了基本数据类型之外的数据类型都是引用数据类型,都是通过new在堆内存开辟空间;

  • 引用传递对象内存图:

适合小白看的Java面向对象,超详细_类和对象_07

1.6.4 小练习

  • 示例代码:
public class Demo {
    public static void main(String[] args) {
        String[] arr1 = {"1", "2", "3"};
        String[] arr2 = {"100", "200", "300"};
        test(arr1, arr2);
        System.out.println("-------arr1-------");
        for (int i = 0; i < arr1.length; i++) {
            System.out.print(arr1[i] + ',');
        }
        
        System.out.println();
        System.out.println("-------arr2-------");
        for (int i = 0; i < arr2.length; i++) {
            System.out.print(arr2[i] + ',');
        }
    }
    public static void test(String[] arr1, String[] arr2) {
        String[] temp;
        temp = arr1;
        arr1 = arr2;
        arr2 = temp;
    }
}

1.7 匿名对象

顾名思义,匿名就是没有名字的对象,在创建对象时,只通过new的动作在堆内存开辟空间,却没有把堆内存空间的地址值赋值给栈内存的某个变量用以存储;

使用场景:

  • 1)如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
  • 2)我们经常将匿名对象作为实参传递给一个方法调用。
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo05 {
    public static void main(String[] args) {
        Student student=new Student();
        intro(student);
        student.study();
        // 使用匿名对象传递
        intro(new Student());
        // 使用匿名对象调用方法
        new Student().study();
    }
    public static void intro(Student student) {
        System.out.println("姓名: "+student.name+",年龄: "+student.age);
    }
}