面向对象基础

158 阅读15分钟

Day7-第5章 面向对象基础上(续)

5.6 面向对象

5.6.1 面向对象概述

1、C语言和Java语句

C语言是一种面向过程的程序设计语言,因为C语言是在面向过程思想的指引下去设计、开发计算机程序的。

Java语言是一种面向对象的程序设计语言,因为Java语言是在面向对象思想的指引下去设计、开发计算机程序的。

其中面向对象和面向过程都是一种编程思想,基于不同的思想会产生不同的程序设计方法。

  1. 面向过程的程序设计思想(Process-Oriented Programming),简称POP

    • 关注的焦点是过程:过程就是操作数据的步骤。面向过程是分析出解决问题所需要的步骤,不同的步骤可以抽象为一个一个的函数。
    • 代码结构:以函数为组织单位。独立于函数之外的数据称为全局数据,在函数内部的称为局部数据。
  2. 面向对象的程序设计思想( Object Oriented Programming),简称OOP

    • 关注的焦点是类和对象:面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,用类来表示。某个事物的一个具体个体称为实例或对象。面向对象是把构成问题事务分解成各个对象,关注的是解决问题需要哪些对象。
    • 代码结构:以类为组织单位。每种事物都具备自己的属性(即表示和存储数据,在类中用成员变量表示)和行为/功能(即操作数据,在类中用成员方法表示)。

2、面向过程与面向对象的区别

面向过程:

  • 优点是性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源。而系统软件(例如各种操作系统)等一般采用面向过程开发,性能是最重要的因素。
  • 缺点是没有面向对象易维护,易复用,易扩展。可维护性差,不易修改。

面向对象:

  • 优点是易维护,易复用,易扩展。由于面向对象由封装,继承,多态性的特性,可以设计出耦合度低的系统,使系统更加灵活,更加易于维护。
  • 缺点是性能比面向过程低。

举例说明:

3 故事:非活字印刷与活字印刷

....
​
如果是有了活字印刷,则只需更改几个字就可,其余工作都未白做。
一、要改,只需更改要改之字,此为可维护;
二、这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;
三、此诗若要加字,只需另刻字加入即可,这是可扩展;
四、字的排列其实可能是竖排可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”
​
在活字印刷术出现之前,上面的四种特性都无法满足,要修改,必须重刻,要加字,必须重刻,要重新排列,必须重刻,印完这本书后,此版已无任何可再利用价值。

5.6.2 类和对象

1、什么是类

是一类具有相同特性的事物的抽象描述,是一组相关属性行为的集合。

  • 属性:就是该事物的状态信息。
  • 行为:就是在你这个程序中,该状态信息要做什么操作,或者基于事物的状态能做什么。

2、什么是对象

对象是一类事物的一个具体个体(对象并不是找个女朋友)。即对象是类的一个实例,必然具备该类事物的属性和行为。

3、类与对象的关系

  • 类是对一类事物的描述,是抽象的
  • 对象是一类事物的实例,是具体的
  • 类是对象的模板,对象是类的实体

5.6.3 如何定义类

1、类的定义格式

关键字:class(小写)

【修饰符】 class 类名{
​
}

类的定义格式举例:

public class Student{
    
}

2、对象的创建

关键字:new

new 类名()//也称为匿名对象//给创建的对象命名
//或者说,把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
类名 对象名 = new 类名();

那么,对象名中存储的是什么呢?答:对象地址

public class TestStudent{
    public static void main(String[] args){
        System.out.println(new Student());//Student@7852e922
​
        Student stu = new Student();
        System.out.println(stu);//Student@4e25154f
        
        int[] arr = new int[5];
        System.out.println(arr);//[I@70dea4e
    }
}

发现学生对象和数组对象类似,直接打印对象名和数组名都是显示“类型@对象的hashCode值",所以说类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。

那么像“Student@4e25154f”是对象的地址吗?不是,因为Java是对程序员隐藏内存地址的,不暴露内存地址信息,所以打印对象时不直接显示内存地址,而是JVM帮你调用了对象的toString方法,将对象的基本信息转换为字符串并返回,默认toString方法返回的是“对象的运行时类型@对象的hashCode值的十六进制值”,程序员可以自己改写toString方法的代码(后面会讲如何改写)。

1561597909862.png

5.7 包(Package)

5.7.1 包的作用

(1)可以避免类重名:有了包之后,类的全名称就变为:包.类名

(2)可以控制某些类型或成员的可见范围

如果某个类型或者成员的权限修饰缺省的话,那么就仅限于本包使用。

(3)分类组织管理众多的类

例如:

  • java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread等,提供常用功能
  • java.net----包含执行与网络相关的操作的类和接口。
  • java.io ----包含能提供多种输入/输出功能的类。
  • java.util----包含一些实用工具类,如集合框架类、日期时间、数组工具类Arrays,文本扫描仪Scanner,随机值产生工具Random。
  • java.text----包含了一些java格式化相关的类
  • java.sql和javax.sql----包含了java进行JDBC数据库编程的相关类/接口
  • java.awt和java.swing----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。

5.7.2 如何声明包

关键字:package

package 包名;

注意:

(1)必须在源文件的代码首行

(2)一个源文件只能有一个声明包的package语句

包的命名规范和习惯: (1)所有单词都小写,每一个单词之间使用.分割 (2)习惯用公司的域名倒置开头

例如:com.atguigu.xxx;

建议大家取包名时不要使用“java.xx"包

5.7.3 如何跨包使用类

==注意:==只有public的类才能被跨包使用

(1)使用类型的全名称

例如:java.util.Scanner input = new java.util.Scanner(System.in);

(2)使用import 语句之后,代码中使用简名称

import语句告诉编译器到哪里去寻找类。

import语句的语法格式:

import 包.类名;
import 包.*;

注意:

使用java.lang包下的类,不需要import语句,就直接可以使用简名称

import语句必须在package下面,class的上面

当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。一个使用全名称,一个使用简名称

示例代码:



import java.util.Date;
import java.util.Scanner;
​
public class TestPackage {
    public static void main(String[] args) {
/*        java.util.Scanner input = new java.util.Scanner(System.in);
        com.atguigu.test01.oop.Student stu = new com.atguigu.test01.oop.Student();*/
​
        Scanner input = new Scanner(System.in);
        Student student = new Student();
​
        Date d1 = new Date();
        java.sql.Date d2 = new java.sql.Date(0);
    }
}

5.8 成员变量之实例变量

5.8.1 如何声明实例变量

【修饰符】 class 类名{
    【修饰符】 数据类型  成员变量名; 
}

Java类的成员变量分为两大类,静态变量(加staitc修饰)和非静态变量(不加static修饰)。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。==接下来先学习实例变量。==

示例:

public class Person{
    String name;
    char gender;
    int age;
}

位置要求:必须在类中,方法外

类型要求:可以是Java的任意类型,包括基本数据类型、引用数据类型(类、接口、数组等)

修饰符:例如:public、protected、private、volatile、transient、final等,后面会一一学习。暂时都写public。

==注意:==如果实例变量的权限修饰符public没写的话,也仅限于本包使用,其他包的类是无法访问的。

5.8.2 对象的实例变量

1、实例变量的特点

(1)实例变量的值是属于某个对象的

  • 必须通过对象才能访问实例变量
  • 每个对象的实例变量的值是独立的

(2)实例变量有默认值

分类数据类型默认值
基本类型整数(byte,short,int,long)0
浮点数(float,double)0.0
字符(char)'\u0000'
布尔(boolean)false
数据类型默认值
引用类型数组,类,接口null

2、实例变量的访问

对象.实例变量

例如:

public class TestPerson {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";
        p1.age = 23;
        p1.gender = '男';
​
        Person p2 = new Person();
        /*
        (1)实例变量的值是属于某个对象的
        - 必须通过对象才能访问实例变量
        - 每个对象的实例变量的值是独立的
        (2)实例变量有默认值
         */
        System.out.println("p1对象的实例变量:");
        System.out.println("p1.name = " + p1.name);
        System.out.println("p1.age = " + p1.age);
        System.out.println("p1.gender = " + p1.gender);
​
        System.out.println("p2对象的实例变量:");
        System.out.println("p2.name = " + p2.name);
        System.out.println("p2.age = " + p2.age);
        System.out.println("p2.gender = " + p2.gender);
    }
}

3、实例变量的内存分析

内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。Java虚拟机要运行程序,必须要对内存进行空间的分配和管理,每一片区域都有特定的处理数据方式和内存管理方式。

JVM的运行时内存区域分为:方法区、堆、虚拟机栈、本地方法栈、程序计数器几大块。

1561465258546.png

区域名称作用
程序计数器程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址
本地方法栈当程序中调用了native的本地方法时,本地方法执行期间的内存区域
方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
堆内存存储对象(包括数组对象),new来创建的,都存储在堆内存。
虚拟机栈用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。

Java对象保存在内存中时,由以下三部分组成:

  • 对象头

    • Mark Word:记录了和当前对象有关的GC、锁等信息。(Java高级再详细讲)
    • 指向类的指针:每一个对象需要记录它是由哪个类创建出来的,而Java对象的类数据保存在方法区,指向类的指针就是记录创建该对象的类数据在方法区的首地址。该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
    • 数组长度(只有数组对象才有)
  • 实例数据

    • 即实例变量的值
  • 对齐填充

    • 因为JVM要求Java对象占的内存大小应该是8bit的倍数,如果不满足该大小,则需要补齐至8bit的倍数,没有特别的功能。

image-20211226153433712.png

5.8.3 实例变量是引用数据类型(警惕空指针异常)

实例变量也是变量,数据类型可以是8种基本数据类型,也可以是引用数据类型(数组、类等)。

注意:引用数据类型的实例变量,如果没有赋值给他一个对象,那么通过它再访问成员或元素,将会发生空指针异常。

class Husband{
    String name;
    Wife wife;
}
class Wife{
    String name;
    Husband husband;
}
class TestMarry{
    public static void main(String[] args){
        Husband h = new Husband();
        h.name = "张三";
​
        Wife w = new Wife();
        w.name = "翠花";
​
        System.out.println("丈夫的姓名:" + h.name + ",妻子:" + h.wife);
         h.wife = w;
        System.out.println("丈夫的姓名:" + h.name + ",妻子:" + h.wife.name);//警惕空指针异常
​
        System.out.println("妻子的姓名:" + w.name + ",丈夫:" + w.husband);
        w.husband = h;
        System.out.println("妻子的姓名:" + w.name + ",丈夫:" + w.husband.name);//警惕空指针异常
        System.out.println("---------------------------------------");
​
        //离婚
        h.wife = null;
        w.husband = null;
​
        h.wife = new Wife();
        h.wife.name = "小何";
        h.wife.husband = h;
        System.out.println("丈夫的姓名:" + h.name + ",妻子:" + h.wife.name);
        System.out.println("妻子的姓名:" + h.wife.name + ",丈夫:" + h.wife.husband.name);
    }
}

总结:

(1)一个引用数据类型的变量,可以.出什么,和这个变量的类型有关,这个类型中有什么成员,就可以.出什么成员。

(2).操作是不是会发生空指针异常,要看.前面的变量有没有“引用”一个对象

5.8.4 对象的实例方法

1、在实例方法中直接使用当前对象的实例变量

实例方法依赖于对象,因为实例方法必须由对象来调用,那么调用当前方法的对象称为当前对象,在实例方法中使用this表示。

如果当前实例方法中没有局部变量与实例变量重名,也可以省略this.,如果有局部变量与实例变量重名,就必须加this.。

//(1)声明一个MyDate类型,有属性:年,月,日
public class MyDate {
    public int year;
    public int month;
    public int day;
​
    public void setValue(int year, int month ,int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }
​
    public String getInfo(){
        return year+"年" + month+"月" + day+"日";
    }
}

2、调用格式(其他类中)

当出现某个类的多个对象都要进行相同操作时,这些操作的重复性代码,就可以封装为实例方法。

在其他类中调用实例方法:

对象名.实例方法(【实参列表】)
//1、创建Scanner的对象
Scanner input = new Scanner(System.in);//System.in默认代表键盘输入//2、提示输入xx
System.out.print("请输入一个整数:"); //对象.非静态方法(实参列表)//3、接收输入内容
int num = input.nextInt();  //对象.非静态方法()

案例演示:

public class MyDate {
    public int year;
    public int month;
    public int day;
​
    public void setValue(int year, int month ,int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }
​
    public String getInfo(){
        return year+"年" + month+"月" + day+"日";
    }
}
​
public class Employee {
    public String name;
    public MyDate birthday;
​
    public void setBirthday(int year, int month, int day){
        birthday = new MyDate();
//        birthday.year = year;
//        birthday.month = month;
//        birthday.day = day;
        birthday.setValue(year, month, day);
    }
​
    public String getInfo(){
//        return "姓名:" + name +",生日:" + birthday.year+"年" + birthday.month+"月" + birthday.day+"日";
        return "姓名:" + name +",生日:" + birthday.getInfo();
    }
}
public class EmployeeTest {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        e1.name = "张三";
//        e1.birthday = new MyDate();
//        e1.birthday.year = 1990;
//        e1.birthday.month = 5;
//        e1.birthday.day = 1;
        e1.setBirthday(1990,5,1);
​
//        System.out.println("e1的姓名:" + e1.name +",生日:" + e1.birthday.year+"年" + e1.birthday.month+"月" + e1.birthday.day+"日");
        System.out.println("e1的" + e1.getInfo());
​
​
        Employee e2 = new Employee();
        e2.name = "李四";
//        e2.birthday = new MyDate();
//        e2.birthday.year = 1995;
//        e2.birthday.month = 6;
//        e2.birthday.day = 1;
        e2.setBirthday(1996,6,1);
​
//        System.out.println("e2的姓名:" + e2.name +",生日:" + e2.birthday.year+"年" + e2.birthday.month+"月" + e2.birthday.day+"日");
        System.out.println("e2的" + e2.getInfo());
    }
}

3、调用格式(本类中)

在本类中:直接使用,但是仅限于在本类中的另一个实例方法中直接使用,不能在本类中的另一个静态方法中直接使用。(关于静态方法与实例方法的区别请看下一章)

实例方法(【实参列表】)

案例演示:

public class Circle {
    double radius;
​
    double area(){
        return Math.PI * radius * radius;
    }
​
    double perimeter(){
        return 2 * Math.PI * radius;
    }
​
    String getInfo(){
        return "半径:" + radius +",周长:" + perimeter() + ",面积:" + area();
    }
}
public class TestCircle{
    public static void main(String[] args) {
        //创建两个Circle圆对象
        Circle c1 = new Circle();
        Circle c2 = new Circle();
​
        //为两个圆对象的半径属性赋值
        c1.radius = 1.2;
        c2.radius = 2.5;
​
        //显示
        //System.out.println("c1的半径:" + c1.radius + ",周长:" + 2 * Math.PI * c1.radius + ",面积:" + Math.PI * c1.radius * c1.radius);
        //System.out.println("c2的半径:" + c2.radius + ",周长:" + 2 * Math.PI * c2.radius  + ",面积:" + Math.PI * c2.radius * c2.radius);
        System.out.println("c1" + c1.getInfo()) ;
        System.out.println("c2" + c2.getInfo()) ;
    }
}

4、实例方法的内存分析

5.9 对象数组

数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。

即数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型是,我们称为对象数组。

注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。

5.9.1 对象数组的声明和使用

案例:

(1)定义矩形类,包含长、宽属性,area()求面积方法,perimeter()求周长方法,String getInfo()返回圆对象的详细信息的方法

(2)在测试类中创建长度为5的Rectangle[]数组,用来装3个矩形对象,并给3个矩形对象的长分别赋值为10,20,30,宽分别赋值为5,15,25,遍历输出

public class Rectangle {
    double length;
    double width;
​
    double area(){//面积
        return length * width;
    }
​
    double perimeter(){//周长
        return 2 * (length + width);
    }
​
    String getInfo(){
        return "长:" + length +
                ",宽:" + width +
                ",面积:" + area() + //直接调用本类的另一个实例方法
                ",周长:" + perimeter();
    }
}
​
public class ObjectArrayTest {
    public static void main(String[] args) {
        //声明并创建一个长度为3的矩形对象数组
        Rectangle[] array = new Rectangle[3];
​
        //创建3个矩形对象,并为对象的实例变量赋值,
        //3个矩形对象的长分别是10,20,30
        //3个矩形对象的宽分别是5,15,25
        //调用矩形对象的getInfo()返回对象信息后输出
        for (int i = 0; i < array.length; i++) {
            //创建矩形对象
            array[i] = new Rectangle();
​
            //为矩形对象的成员变量赋值
            array[i].length = (i+1) * 10;
            array[i].width = (2*i+1) * 5;
​
            //获取并输出对象对象的信息
            System.out.println(array[i].getInfo());
        }
    }
}

5.9.2 对象数组的内存图分析

对象数组中数组元素存储的是元素对象的首地址。

image-20211228153827819.png