Java——面向对象——基础

概述

对象,就是指客观存在的事物,万物皆对象。

  • 客观存在的任何一种事物,都可以看作为程序中的对象
  • 使用面向对象思想可以将复杂的问题简单化
  • 将我们从执行者的位置,变成了指挥者

面向对象和面向过程的思想对比

面向过程编程

是一种以过程为中心的编程思想,实现功能的每一步,都是自己实现的。

面向对象编程

是一种以对象为中心的编程思想,通过指挥对象实现具体的功能。

什么是类

类是对现实生活中一类具有共同属性和行为的事物的抽象。简单的理解的就是类是对事物,也就是对象的一种描述,可以将类理解为一张设计图,可以根据设计图,创造出具体存在的事物。

可以根据类创造对象。

类的组成

  • 属性:该事物的各种特征,例如学生事物的属性,姓名、年龄毕业院校...
  • 行为:该事物存在的功能,就是能够做的事情,例如学生的行为,学习、Java开发编程。

类和对象的关系

  • 类:是对现实生活中一类具有共同属性和行为的事物的抽象,类就是对象的描述
  • 对象:是能够看得到摸得着的真实存在的实体,对象就是类的实体

类的定义

说明

类的组成是属性和行为,属性在代码中可以通过成员变量(类中方法外的变量)来体现。行为在代码中可以通过成员方法来体现,和普通方法类似,去掉关键字static即可。

步骤

定义类 ——》 定义类的成员变量 ——》编写类的成员方法

代码实现如下

public class 类名 {
    // 成员变量
    变量1的数据类型 变量1;
    变量2的数据类型 变量2;
    ...
    // 成员方法
    方法1;
    方法2;
    ...
}

比如创建一个Student学生类,学生类的成员变量有姓名(name)、年龄(age),成员方法有学习(study),如下

// 属性 姓名、年龄
// 成员变量 跟定义普通对象的格式一样,只不过位置发生了改变,类中方法外
String name;
int age;

// 行为 学习
// 成员方法 跟普通定义方法的格式一样,去掉了static关键字
public void study(){
    System.out.println("学习");
}

这样,一个基本的类就定义好了。

对象的创建

参照已经定义好的类,我们可以在测试类中来创建对象,格式如下

类名 对象名 = new 类名();

例子

Student stu = new Student();

对象的使用

在测试类中使用对象的格式如下

// 使用成员变量
对象名.对象名

// 使用成员方法
对象名.方法名()

例子

System.out.println(stu.age);   // 0
stu.study();   // 学习

这里需要注意的是,即使我们没有给对象的属性赋值,也还是可以拿到具体的值,这里叫做默认初始化值,不会编译出错,比如String 为 null, int 为 0。

也可以对属性进行赋值操作,如下

stu.name = "张三";
stu.age = 27;
System.out.println(stu.name);   // 张三
System.out.println(stu.age);   // 27

对象的内存存储

有如下俩个类,一个Student类,一个TestStudent测试类

Student

public class Student {
    String name;
    int age;
    
    public void study(){
        System.out.println("学习");
    }
}

TestStudent

public class TestStudent {
    public static void main(String[] args) {
        Student stu = new Student();
        System.out.println(stu.age);   // 0
        stu.name = "张三";
        stu.age = 27;
        System.out.println(stu.name);   // 张三
        System.out.println(stu.age);   // 27
        stu.study();   // 学习
    }
}    

首先拥有主方法的测试类的字节码文件(TestStudent.class)加载进方法区,目前这个字节码文件中只有一个主方法main,主方法被虚拟机自动调用执行,进入到栈内存,第一句代码Student = stu...就是声明了一个对象类型的变量,由于内存中没有Student这个类,所以系统这一步也会把Student这个类的字节码文件(Student.class)加载进方法区,加载进来之后,成员变量和成员方法在方法区都是存在的,加载进来之后,继续 new Student(),有new就会进堆,自动在堆内存中开辟空间,产生地址,由于对象是根据类来创建的,所以类中存在的成员变量在堆内存当中也同时存在着这么一份,堆内存中的数据都有自己的默认初始化值,所以name,age都有了自己的默认初始化值,null和0,此时,成员方法是不会进入堆内存的,但是,堆内存中是会有那么一块地址是用于存在方法的引用地址的,将来可以通过这个地址找到方法区中的成员方法,此时就可以把堆内存中的地址赋值给stu了,所以如果此时打印stu,打印的就是地址值,接下来打印成员变量,就可以通过地址值找到堆内存中的这块内存,也可以根据name和age继续找到成员变量的值,null和0,并且改变它们。调用study方法是通过stu找到堆内存中的这块地址,然后再通过保存过的这个地址,找到方法区中的字节码文件的study方法,找到之后,把study方法加载进栈内存当中运行,执行完study里面的功能之后,就会从栈内存中弹栈消失。由于study()是主方法中的最后一句代码,study调用完毕之后,主方法也会从栈内存当中弹栈消失。至此,程序执行完毕。

注意

如果再次创建Student对象,是不会再次Student这个类的字节码文件(Student.class)加载进方法区的。

 俩个引用指向同一个对象

情况如下,有俩个类

Student

public class Student {
    String name;
    int age;

    public void study(){
        System.out.println("学习");
    }
}

TestStudent

public class TestStudent {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.name = "张三";
        Student stu2 = stu1;
        stu2.name = "李四";
        System.out.println(stu1.name + "------" + stu2.name);   // 李四------李四
    }
}

由于俩个引用指向了同一个内存空间,所以只要一个改变了对象,俩个再次取值看到的就是改动之后的结果了。

这里我可以把对象变量赋值为null来断开连接,如下:

stu1 = null;
System.out.println(stu1.name);   // NullPointerException(空指针异常)

如果设为null之后,再次通过stu1取属性值,就会提示空指针异常。此时不会影响另一个引用(stu2),我们还是可以通过stu2找到堆内存中的这块地址,如下

stu1 = null;
// System.out.println(stu1.name);   // NullPointerException(空指针异常)
System.out.println(stu2.name);   // 李四

如果之后又将stu2赋值为null,此时就没有引用能够找到堆内存中的这块地址内容了,这块内容就成了内存中的垃圾对象,垃圾对象会被Java的垃圾回收器在空闲的时候,自动进行清理。下面有描述。

垃圾回收

当堆内存中,对象或数组产生的地址,通过任何方式都不能被找到后,就会被判定为内存中的"垃圾","垃圾"会被Java垃圾回收器,空闲的时候,自动清理。有部分语言,"垃圾"是需要程序员手动清理的。

成员变量和局部变量

  • 成员变量——就是类中方法外的变量,判断一个变量是不是成员变量,就看它是不是类中方法外的变量,就就可以了
  • 局部变量——就是方法当中的变量
public class Student {
    String name;
    int age;

    public void study(){
        int i = 0;
        System.out.println("学习");
    }
}

上面代码,name 和 age 就是成员变量,study方法中的 i 就是局部变量。

成员变量和局部变量有如下区别

区别 成员变量 局部变量
类中位置不同 类中方法外 方法内或者方法声明上(形参)
内存中位置不同 堆内存 栈内存
生命周期不同 随着对象的存在而存在,随着对象的消失而消失 随着方法的调用而存在,随着方法的调用完毕消失
初始化值不同 有默认的初始值 么有默认的初始化值,必须先定义,赋值,才能使用

封装

private

private是一个访问修饰符,这个单词的意思是私有的,它的作用可以用来修饰成员(变量和方法)。被它修饰过的成员,只能在本类中进行访问。如下

public class Student {
    private String name;
    int age;

    public void show(){
        System.out.println(name + "-----" + age);
    }
}

上面Student类中的name成员变量就被private修饰符修饰过了。这样的话,其他的类就无法访问它了。

public class TestStudent {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.name = "张三";   // name在com.baidu.Student中是private访问控制
    }
}

使用访问修饰符修饰成员就是可以提高了数据的安全性,如果使用private,还需要提供如下的操作

  • 提供"get变量名()"方法,用于获取成员变量的值,方法用public修饰
  • 提供"set变量名(参数)"方法,用于设置成员变量的值,方法用public的值
public class Student {
    private String name;
    private int age;
    public String getName(){
        return name;
    }
    public void setName(String n){
        name = n;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int a){
        age = a;
    }
    public void show(){
        System.out.println(name + "-----" + age);
    }
}

如此使用的直观影响就是在其他类中是无法通过"对象.变量名"的方式获取和修改成员变量了,但是也提高了安全性,比如用户在设置年龄的时候,我们在设置年龄的方法中(setAge),加以判断,必须在某个范围内,才能设置成功。如下

// Student.java
public void setAge(int a){
    if(a >= 0 && a <= 120){
        age = a;
    }else{
        System.out.println("您设置的年龄有误");
    }

}

// TestStudent.java
Student stu = new Student();
stu.setAge(130);   // 您设置的年龄有误

总结:今天在定义成员变量时,都需要采用private修饰。再提供get、set方法,从而提高代码的安全性。

this

在Java中,当成员变量和局部变量重名时,Java会采用就近原则,如下

// Student.java
public class Student {
    private int age = 10;

    public void show() {
        int age = 20;
        System.out.println(age);
    }
}

// ThisTest01.java
public class ThisTest01 {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.show();   // 20
    }
}

这时,如果需要访问打印成员变量age,而不是局部变量age,就可以加上this关键字,如下

// Student .java
public class Student {
    private int age = 10;

    public void show() {
        int age = 20;
        System.out.println(this.age);
    }
}

// ThisTest01.java
public class ThisTest01 {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.show();   // 10
    }
}

所以,this关键字的作用就是可以调用本类的成员(变量、方法),解决成员变量和局部变量的重名问题。

this代表所在类的引用,方法被哪个对象调用,this就代表哪个对象。

 构造方法

构造方法就是构建、创造对象的时候,所调用的方法。

它的作用就是用于给对象的数据(属性)进行初始化。

构造方法的书写格式有如下特点

  • 方法名与类名相同,大小写也要一致
  • 没有返回值类型,连void都没有
  • 没有具体的返回值,不能由return带回结果数据
  • 创建对象的时候调用,每创建一次对象,不能手动调用

以下就是一个最简单的构造方法

// Student.java
public class Student {
    public Student(){
        System.out.println("我是Student类的构造方法");
    }
}

// TestStudent.java
public class TestStudent {
    public static void main(String[] args) {
        Student stu = new Student();   // 我是Student类的构造方法
    }
}

根据Student类创建出来对象的时候,就会执行Student类的构造方法。

因为构造方法在创建对象的时候会自动调用一次,所以,我们可以让它额外帮我们初始化一下成员变量,如下就是构造方法的标准使用

// Student.java
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

// TestStudent.java
public class TestStudent {
    public static void main(String[] args) {
        Student stu = new Student("张三",27);
        System.out.println(stu.getName() + "——" + stu.getAge());   // 张三——27
    }
}

上面代码提供了一个空参构造方法和一个有参构造方法,这样我们我们创建类的时候就可以看情况初始化对象的属性。

构造方法也有一些注意事项,如下

  • 如果没有定义构造方法,系统将会给出一个默认的无参构造方法
  • 如果定义了构造方法,系统将不再提供默认的构造方法
  • 如果自定义了带参构造方法,还要使用无参构造方法,就必须再写一个无参数 构造方法
  • 还是建议今后无论是否使用,都手动书写无参数构造方法和带参数构造方法

JavaBean类

上面用于封装数据的Student类就称为JavaBean类。

原文地址:https://www.cnblogs.com/xulinjun/p/14707869.html