从0开始学Java(006)_类与对象

本文深入讲解面向对象编程的核心概念,包括封装、继承和多态,以及类与对象的基本定义和使用。通过具体示例,详细介绍了类的定义、属性封装、构造方法、匿名对象等关键知识点。

面向对象简介

  • 面向过程:指的是针对某一个问题单独提出解决方案和代码开发。
  • 面向对象:以模块化的形式进行代码设计,优点是代码可重用。特征:
  • 封装性:内部操作对外部不可见,保证内部结构安全性。
  • 继承性:在已有的程序结构上扩充新的功能。
  • 多态性:相同的操作、函数或过程可作用于多种类型的对象上并获得不同的结果

面向对象开发步骤:OOA(面向对象分析),OOD(面向对象设计),OOP(面向对象编程)。

类与对象

  • 类与对象是面向对象中最基础的组成单元。类是某一类事务共性的集合,对象是某一个性的产物。
  • 类用于描述对象的结构。例如每个人的名字、年龄等一系列特征。类除了包含特征(属性)外,还包括许多行为(方法)。根据这个类生成的对象都具有相同的行为。
  • 对象所拥有的行为由类决定,无法执行超出类定义范畴的操作。

综上所述,类是对象的模版,但类无法直接使用,必须通过实例对象来使用。对象是由类产生的。

类与对象的定义及使用

类的定义

语法: class class_name {}

类的组成
  • field(属性,成员,变量):一堆变量的集合;
  • method(方法,行为):由对象调用。
    范例:定义类
package com.java.entity;
public class Book { // 类名首字母大写
   // 定义属性
   public String title;
   public double price;
   // 定义方法
   public void getInfo(){
       System.out.println("书名:" + title + ",价格:" + price);
   }
}
类的使用

要使用类,必须先生成对象,对象定义的语法有如下两种:

  • 声明并实例化对象:class_name object_name = new class_name()
  • 分步完成:

第一步-声明对象:class_name object_name = null;

第二步-实例化对象:object_name =new class_name();

引用数据类型与基本数据类型最大区别是引用数据类型需要开辟和使用内存。关键字new的主要功能就是开辟内存空间。

当对象实例化后,可利用如下方式操作类:

  • object_name.field:操作类中的属性;
  • object_name.method():调用类中的方法。

范例:使用类

public class Demo {
    public static void main(String[] args) {
        Book book = new Book(); // 声明并实例化对象
        book.title = "Java开发"; // 定义属性
        book.price = 66.6;
        book.getInfo(); // 调用方法
    }
}

对象内存分析

内存类型说明
堆内存保存对象的属性内容,通过关键字new开辟
栈内存保存堆内存的地址,通过地址找到堆内存的内容

在这里插入图片描述
范例:分步使用实例化对象

public class Demo {
    public static void main(String[] args) {
        Book book = null; // 声明对象
        book = new Book(); // 实例化对象
        book.title = "Java开发";
        book.price = 66.6;
        book.getInfo();
    }
}

内存分析:使用关键字new开辟了新的堆内存,堆内存中保存类定义的属性,此时所有的属性值都为默认值
在这里插入图片描述
使用未实例化的对象,程序运行时会出现NullPointerException(空指向异常)

对象引用分析

  • 程序中每次使用new关键字便会开辟一个新的堆内存。

假设声明了两个对象,且用new分别进行了实例化,那么两个对象占据的是不同的堆内存,因此不会互相影响。

范例:声明两个对象

public class Demo {
   public static void main(String[] args) {
       Book bkA = new Book();
       Book bkB = new Book();
       bkA.title = "Java开发";
       bkA.price = 66.6;
       bkA.getInfo();
       bkB.title = "C++开发";
       bkB.price = 22.6;
       bkB.getInfo();
   }
}

在这里插入图片描述
范例:对象引用传递

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book();
        Book bkB = null;
        bkA.title = "Java开发";
        bkA.price = 66.6;
        bkA.getInfo(); // 66.6
        bkB = bkA; // 引用传递
        bkB.price = 90.5;
        bkA.getInfo(); // 90.5
        bkB.getInfo(); // 90.5
    }
}

在这里插入图片描述
由于两个对象指向同一块堆内存,所以任意一个对象修改了堆内存的属性值后,都会影响到其他对象的值。一块堆内存可以被多个栈内存指向,但一个栈内存只能保存一个堆内存的地址。

范例:引用传递

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book();
        Book bkB = new Book();
        bkA.title = "Java开发";
        bkA.price = 66.6;
        bkB.title = "C++开发";
        bkB.price = 90.5;
        bkB = bkA; // 引用传递
        bkB.price = 100;
        bkA.getInfo(); // 100
        bkB.getInfo(); // 100
    }
}

在这里插入图片描述
通过内存分析可知,一块没有栈内存指向的堆内存将成为垃圾,最后被垃圾收集器(GC)回收,回收后,释放其所占空间。

类属性封装

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book();
        bkA.title = "Java开发";
        bkA.price = -66.6;
        bkA.getInfo();
    }
}

上述代码不存在语法错误,但存在业务逻辑错误(书的价格不能为负数)。造成该问题是因为**对象可以在类的外部直接访问属性。**因此需要将Book类的属性设为对外不可见(只允许本类访问),可以使用private关键字定义属性。

public class Book {
    private String title;
    private double price;
    public void getInfo(){
        System.out.println("书名:" + title + ",价格:" + price);
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book();
        bkA.title = "Java开发"; // 报错,无法访问私有属性
    }
}

此时外部对象无法直接调用类中的属性,但程序要正常执行,必须让对象可以操作类中的属性。因此在开发中,对于属性有该要求:类中的属性要使用private声明,如果属性需被外部使用,按照要求定义对应的setter()/getter().

  • 以Book类中的title属性为例,定义setter()/getter():
setter()getter()
作用设置属性值取得属性值
语法public void setTitle(String t)public void getTitle()
是否含参有参无参
package com.java.entity;
public class Book {
    private String title;
    private double price;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public void getInfo(){
        System.out.println("书名:" + title + ",价格:" + price);
    }
}

如果要有价格不能为负数的功能,可以在setter()中添加:

public void setPrice(double price) {
    if (price > 0.0){
        this.price = price; 
    }
}

实际开发中,对于数据的验证,应由其他辅助代码完成,setter()只是简单地设置数据,getter()只用于返回数据。

构造方法与匿名对象

构造方法

  • 定义对象的语法:类名称 对象名称 = new 类名称();
  • 类名称:定义对象的类型;
  • 对象名称:标识符,要使用对象,需要有一个对象名;
  • new:用于开辟堆内存空间,实例化对象;
  • 类名称():一个方法名和类名称一样的方法,这就是构造方法。

上述说明中,存在构造方法,但程序中我们并未定义构造方法。之所以能使用构造方法,是因为Java语言在程序没有自定义构造方法时,程序编译会自动在类中添加一个无参无返回值的构造方法。

  • 构造方法的定义要求:方法名称与类名称相同;构造方法没有返回值;构造方法也不能使用void关键字

程序编译和程序解释都是按照代码的结构进行的,如果构造方法使用了void关键字,那么程序无法区分其与普通方法的区别。

class Book {
    public Book() { // 系统自动生成的构造方法
    }
}
  • 构造方法在对象使用new实例化时调用。
    范例:证明构造方法被调用
public class Book {
   public Book() {
       System.out.println("构造方法被调用");
   }
}

public class Demo {
   public static void main(String[] args) {
       Book book = null ; // 声明对象
       book = new Book(); // 实例化对象时调用构造方法
       //结果:构造方法被调用
   }
}
  • 构造方法与普通方法的最大区别:构造方法只在实例化对象时调用一次。普通方法在对象实例化后可以多次调用。

范例:自定义构造方法

class Book {
    private String title;
    private double price;
    // 自定义构造方法后,系统不再自动生成无参无返回值的构造方法
    public Book(String t, double p) {
        title = t;
        price = p;
    }

    public void getInfo() {
        System.out.println("书名:" + title + ",价格:" + price);
    }
}

public class Demo {
    public static void main(String[] args) {
        Book book = new Book("Java开发", 66.6);
        book.getInfo();
    }
}

由上述代码可知构造方法的作用:在类对象实例化时设置属性的初始值,即构造方法用于属性初始化

  • 构造方法也属于方法,因此可以进行重载。
    范例:构造方法重载
class Book {
    public Book() {
        System.out.println("系统自动生成的构造方法");
    }
    // 进行方法重载的构造方法
    public Book(String t, double p) {
        System.out.println("方法重载后的构造方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bookA = new Book(); // 系统自动生成的构造方法
        Book bookB = new Book("Java开发", 66.6); // 方法重载后的构造方法
    }
}

重载方法时建议:按照参数个数,对方法进行升序或者降序排列。

  • 在定义类时可为属性设置默认值,但该默认值只有在对象实例化后才进行设置。而对象实例化是对象构造的最后一步。对象实例化过程的步骤:类的加载,内存的分配,默认值的设置,构造方法
class Book {
    private String title = "Java开发"; // 设置默认值
    private double price;

    public Book() {
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        if (price > 0.0){
            this.price = price;
        }
    }

    public void getInfo() {
        System.out.println("书名:" + title + ",价格:" + price);
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book();
        bkA.getInfo(); // 书名:Java开发,价格:0.0
    }
}

本程序中,只有在对象实例化后,才会将Java开发设置给title属性。在没有实例化前,title的值仍为其数据类型的默认值,即null真实的对象信息都保存在堆内存中。

匿名对象

匿名对象:没有栈内存指向的对象,即没有标识符的对象

public class Demo {
    public static void main(String[] args) {
        new Book("Java开发",6.6).getInfo();
    }
}

由于匿名对象没有标识符,也没有其他对象对其引用,因此只能使用一次,之后该对象变为垃圾,等待回收。

  • 何时使用匿名对象:当一个对象仅需要被使用一次时就使用匿名对象。当一个对象要被反复调用时,就使用非匿名对象。

简单Java类实践

题目要求:定义一个雇员类,类中有雇员编号、姓名、职位、基本工资、佣金等属性。
提示:这种类被称为简单java类,因为这种类不包含过于复杂的程序逻辑。

对于简单Java类而言,它的要求如下:

  • 类名必须有意义;
  • 类中所有属性必须private封装,封装后的属性必须提供setter和getter方法;
  • 类中可以有多个构造方法,但必须保留无参构造方法;
  • 类中不允许出现输出语句,信息输出必须交给调用处。
  • 类中需要提供有一个取得对象完整信息的方法,暂定为getInfo(),返回值类型为String型。
  • 第一步:定义类
public class Emp {
    private int eId; // 编号
    private String eName; // 姓名
    private String job; // 职位
    private double sal; // 工资
    private double comm; // 佣金
    // 定义构造方法
    public Emp() {
    }
    
    public Emp(int eId, String eName, String job, double sal, double comm) {
        this.eId = eId;
        this.eName = eName;
        this.job = job;
        this.sal = sal;
        this.comm = comm;
    }
    // 定义setter和getter方法
    public int geteId() {
        return eId;
    }

    public void seteId(int eId) {
        this.eId = eId;
    }

    public String geteName() {
        return eName;
    }

    public void seteName(String eName) {
        this.eName = eName;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    public double getComm() {
        return comm;
    }

    public void setComm(double comm) {
        this.comm = comm;
    }
    // 定义普通方法
    public String getInfo() {
        return "编  号" + this.eId + "\n" +
                "姓 名" + this.eName + "\n" +
                "职 位" + this.job + "\n" +
                "工 资" + this.sal + "\n" +
                "佣 金" + this.comm;
    }
}
  • 第二步:测试
public class TEmp {
    public static void main(String[] args) {
        Emp e = new Emp(1, "Tom", "保安", 800.0, 10.0);
        System.out.println(e.getInfo()); // 获取全部信息
        System.out.println(e.geteId()); // 通过getter()获取单一信息
    }
}

类中的setter()/getter()必须写。setter()除了有设置属性内容功能外,还有修改属性值的功能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会画画的画师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值