Java类与对象:定义、创建、内存、方法、参数、封装、构造与关键字

一、类的定义

在计算机编程中,类(Class)是一种抽象数据类型(ADT),它是面向对象编程(OOP)的基本概念之一。类是对现实世界中对象的抽象,它定义了对象的属性(成员变量)和行为(成员方法)。


类的定义通常包括以下几个要素:

  1. 类名(Class Name):类的名称用于标识该类,在代码中可以通过类名来引用该类。类名通常使用大驼峰命名法(Pascal Case)。

  2. 成员变量(Member Variables):也称为属性或字段(Fields),用于描述类的状态或特征。成员变量可以是各种数据类型(如整数、浮点数、字符串等),它们代表了对象的各种属性。在类的定义中,成员变量通常以变量名和数据类型的形式列出。

  3. 成员方法(Member Methods):也称为函数或操作(Methods),用于描述类的行为或功能。成员方法定义了对象可以执行的操作,它们可以操作对象的状态,并且可以被外部代码调用以执行特定的任务。在类的定义中,成员方法通常以方法名、参数列表和返回类型的形式列出。

  4. 构造方法(Constructor):是一种特殊类型的成员方法,用于在创建对象时初始化对象的状态。构造方法的名称与类名相同,并且通常没有返回类型。在Java等编程语言中,通过调用构造方法可以创建类的实例。

  5. 访问修饰符(Access Modifiers):用于控制类的成员对外部代码的可见性和访问权限。常见的访问修饰符包括public、protected、private等。

一个简单的类定义示例(使用Java语言)如下所示:

public class MyClass {
    // 成员变量
    private int myNumber;
    private String myString;
    
    // 构造方法
    public MyClass(int number, String str) {
        this.myNumber = number;
        this.myString = str;
    }
    
    // 成员方法
    public void printDetails() {
        System.out.println("Number: " + myNumber);
        System.out.println("String: " + myString);
    }
}
/*
 在这个示例中,类名为MyClass,包含了两个成员变量(myNumber和myString)、一个构造方法(MyClass)和一个成员方法(printDetails)。
 这个类定义了一个简单的数据结构,表示了一个具有整数和字符串属性的对象,并且提供了一个方法用于打印对象的属性。       
 */

二、类对象的创建和使用

创建和使用类对象是面向对象编程中的基本操作,它们使我们能够使用类定义的属性和方法来操作对象。


以下是创建和使用类对象的一般步骤:

  1. 类定义:首先,我们需要定义一个类,其中包括类的属性(成员变量)和方法(成员方法)。
  2. 对象实例化:在程序中,通过使用类的构造方法来创建类的实例(对象)。构造方法会初始化对象的状态,并返回一个指向该对象的引用。
  3. 访问成员变量:一旦对象被创建,我们可以使用点操作符(.)来访问对象的成员变量,并为其赋值或获取值。
  4. 调用成员方法:同样,我们也可以使用点操作符来调用对象的成员方法,并向方法传递参数(如果需要)。

以下是一个简单的示例,演示了如何创建类对象并使用它:

public class MyClass {
    // 成员变量
    private int myNumber;
    private String myString;
    
    // 构造方法
    public MyClass(int number, String str) {
        this.myNumber = number;
        this.myString = str;
    }
    
    // 成员方法
    public void printDetails() {
        System.out.println("Number: " + myNumber);
        System.out.println("String: " + myString);
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建类对象
        MyClass obj = new MyClass(10, "Hello");
        
        // 访问成员变量
        int number = obj.myNumber;
        String str = obj.myString;
        System.out.println("Number: " + number);
        System.out.println("String: " + str);
        
        // 调用成员方法
        obj.printDetails();
    }
}
/*
 在这个示例中,我们首先定义了一个名为MyClass的类,其中包含了一个构造方法(用于初始化对象的状态)和一个成员方法(用于打印对象的属性)。
 然后,在Main类中,我们通过调用MyClass的构造方法创建了一个名为obj的对象。
 接着,我们通过点操作符访问对象的成员变量,并调用对象的成员方法来操作对象。 
 */

三、JVM内存分析

package com.camellia.oop01;
/*实例变量属于成员变量,成员变量如果没有手动赋值,系统会赋默认值
    数据类型        默认值
    ----------------------
    byte            0
    short           0
    int             0
    long            0L
    float           0.0F
    double          0.0
    boolean         false
    char            \u0000
    引用数据类型      null
 */
public class Student {

    // 属性:姓名,年龄,性别,他们都是实例变量

    // 姓名
    String name;

    // 年龄
    int age;

    // 性别
    boolean gender;
}
package com.camellia.oop01;

public class StudentTest01 {
    public static void main(String[] args) {
        // 局部变量
        int i = 10;

        // 通过学生类Student实例化学生对象(通过类创造对象)
        // Student s1; 是什么?s1是变量名。Student是一种数据类型名。属于引用数据类型。
        // s1也是局部变量。和i一样。
        // s1变量中保存的是:堆内存中Student对象的内存地址。
        // s1有一个特殊的称呼:引用
        // 什么是引用?引用的本质上是一个变量,这个变量中保存了java对象的内存地址。
        // 引用和对象要区分开。对象在JVM堆当中。引用是保存对象地址的变量。
        Student s1 = new Student();

        // 访问对象的属性(读变量的值)
        // 访问实例变量的语法:引用.变量名
        // 两种访问方式:第一种读取,第二种修改。
        // 读取:引用.变量名 s1.name; s1.age; s1.gender;
        // 修改:引用.变量名 = 值; s1.name = "jack"; s1.age = 20; s1.gender = true;
        System.out.println("姓名:" + s1.name); // null
        System.out.println("年龄:" + s1.age); // 0
        System.out.println("性别:" + (s1.gender ? "男" : "女"));

        // 修改对象的属性(修改变量的值,给变量重新赋值)
        s1.name = "张三";
        s1.age = 20;
        s1.gender = true;

        System.out.println("姓名:" + s1.name); // 张三
        System.out.println("年龄:" + s1.age); // 20
        System.out.println("性别:" + (s1.gender ? "男" : "女")); // 男

        // 再创建一个新对象
        Student s2 = new Student();

        // 访问对象的属性
        System.out.println("姓名=" + s2.name); // null
        System.out.println("年龄=" + s2.age); // 0
        System.out.println("性别=" + (s2.gender ? "男" : "女"));

        // 修改对象的属性
        s2.name = "李四";
        s2.age = 20;
        s2.gender = false;

        System.out.println("姓名=" + s2.name); // 李四
        System.out.println("年龄=" + s2.age); // 20
        System.out.println("性别=" + (s2.gender ? "男" : "女")); // 女


    }
}

在这里插入图片描述

从此图可以看出,开始将所有类的字节码存储到元空间当中,当对象被创建时就在堆内存中开辟一个空间,用于存储对象和实例变量等。然后通过引用实现对对象的一系列操作。
其中对象属性等的改变都发生在堆内存中,引用只不过保存了它的地址(这和C++中的指针很像)。

四、实例变量和实例方法的访问

  1. 实例变量要想访问,必须先new对象。通过引用来访问实例变量。
  2. 实例变量是不能通过类名直接访问的。
  3. 我们通常描述一个对象的行为动作时,不加static。 没有添加static的方法,被叫做:实例方法。(对象方法)
  4. 空指针异常:一个空引用访问实例相关的,都会出现空指针异常。
public class Pet {
    
    String name; // 实例变量
    // 出生日期
    String birth;
    // 性别
    char sex;

    // 方法:行为动作
    // 吃
    public void eat(){ // 实例方法
        System.out.println("宠物在吃东西");
    }
    // 跑
    public void run(){
        System.out.println("宠物在跑步");
    }
}
public class PetTest02 {
    public static void main(String[] args) {
        // 创建宠物对象
        Pet dog = new Pet();

        // 给属性赋值
        dog.name = "小黑";
        dog.birth = "2012-10-11";
        dog.sex = '雄';

        // 读取属性的值
        System.out.println("狗狗的名字:" + dog.name);
        System.out.println("狗狗的生日:" + dog.birth);
        System.out.println("狗狗的性别:" + dog.sex);

        dog = null;

        // 注意:引用一旦为null,表示引用不再指向对象了。但是通过引用访问name属性,编译可以通过。
        // 运行时会出现异常:空指针异常。NullPointerException。这是一个非常著名的异常。
        // 为什么会出现空指针异常?因为运行的时候会找真正的对象,如果对象不存在了,就会出现这个异常。
        //System.out.println("狗狗的名字:" + dog.name);

        // 会出现空指针异常。
        dog.eat();

        // 会出现空指针异常。
        //dog.run();
    }
}
//java.lang.NullPointerException

如果没有任何引用指向对象,该对象最终会被当做垃圾被GC回收。

五、方法调用时传递参数

5.1、方法调用时传递基本数据类型

package com.camellia.oop04;

/**
 * 面试题:判断该程序的输出结果
 */
public class ArgsTest01 {
    public static void main(String[] args) {
        int i = 10;
        // 调用add方法的时候,将i传进去,实际上是怎么传的?将i变量中保存值10复制了一份,传给了add方法。
        add(i);
        System.out.println("main--->" + i); // 10
    }
    public static void add(int i){ // 方法的形参是局部变量。
        i++;
        System.out.println("add--->" + i); // 11
    }
}

在这里插入图片描述

5.2、方法调用时传递引用数据类型

package com.camellia.oop04;
public class User {
    int age;
}
package com.camellia.oop04;

/**
 * 面试题:分析以下程序输出结果
 */
public class ArgsTest02 {
    public static void main(String[] args) {
        User u = new User();
        u.age = 10;
        // u是怎么传递过去的。实际上和i原理相同:都是将变量中保存的值传递过去。
        // 只不过这里的u变量中保存的值比较特殊,是一个对象的内存地址。
        add(u);
        System.out.println("main-->" + u.age); // 11
    }
    public static void add(User u) { // u是一个引用。
        u.age++;
        System.out.println("add-->" + u.age); // 11
    }
}

这个和基本数据类型原理相同。首先明确引用数据类型中应用存储的是User的地址,所以add(User u);实质上是将main中的引用u里存储的值(就是new User();的地址)复制一份给add方法。

* 六、封装

概念: 封装是面向对象编程中的一个重要概念,它指的是将数据和操作数据的方法捆绑在一起,并限制对数据的访问。
封装的目的是隐藏对象的内部细节,只向外界暴露必要的接口,以防止外部代码直接访问对象的内部状态,从而提高代码的安全性和可维护性。
如何实现封装:
数据隐藏(Data Hiding): 封装通过将对象的数据隐藏起来即属性私有化,只允许通过对象的方法来访问和修改数据,从而防止外部直接访问对象的内部状态。
访问控制(Access Control): 通常,封装会将对象的属性设置为私有(private),只允许通过公共(public)方法来访问和修改这些属性。

package com.camellia.oop07;
/**
 * 为了保证User类型对象的age属性的安全,我们需要使用封装机制。实现封装的步骤是什么?
 *      第一步:属性私有化。(什么是私有化?使用 private 进行修饰。)
 *      属性私有化的作用是:禁止外部程序对该属性进行随意的访问。
 *      所有被private修饰的,都是私有的,私有的只能在本类中访问。
 *
 *      第二步:对外提供setter和getter方法。
 *      为了保证外部的程序仍然可以访问age属性,因此要对外提供公开的访问入口。
 *      访问一般包括两种:
 *          读:读取属性的值
 *          改:修改属性的值
 *      那么应该对外提供两个方法,一个负责读,一个负责修改。
 *      读方法的格式:getter
 *          public int getAge(){}
 *      改方法的格式:setter
 *          public void setAge(int age){}
 */
public class User {
    private int age;

    // 读取age属性的值
    // getter方法是绝对安全的。因为这个方法是读取属性的值,不会涉及修改操作。
    public int getAge(){
        //return this.age;
        return age;
    }

    // 修改age属性的值
    // setter方法当中就需要编写拦截过滤代码,来保证属性的安全。
    // java有就近原则,若不加this关键字都默认是形参age。
    public void setAge(int age){
        if(age < 0 || age > 100) {
            System.out.println("对不起,您的年龄值不合法!");
            return;
        }
        // this. 大部分情况下可以省略。
        // this. 什么时候不能省略?用来区分局部变量和实例变量的时候。
        this.age = age;
    }
}
package com.camellia.oop07;

public class UserTest {
    public static void main(String[] args) {
        User u = new User();
        // 读
        System.out.println("年龄:" + u.getAge());

        // 改
        u.setAge(-100);
        
        // 读
        System.out.println("年龄:" + u.getAge());
        
        //改
        u.setAge(50);
        
        //读
        System.out.println("年龄:" + u.getAge()); // 50
    }
}

* 七、构造方法

7.1、构造方法的基本知识点

1、构造方法的作用

对象的创建:
构造方法通过调用完成对象的创建。当使用 new 关键字实例化一个对象时,构造方法被调用,对象在内存中被创建并分配空间。
对象的初始化:
构造方法用于给对象的所有属性赋值,即对象的初始化。它确保对象在创建后处于一个合适的状态,属性被赋予初始值,以便对象可以正常运行。

2、定义构造方法的方式
[修饰符列表] 构造方法名(形参列表) {
构造方法体;
}

注意事项:

  • 构造方法名必须和类名一致,以便编译器能够识别并与类关联。
  • 构造方法不需要提供返回值类型,因为它的主要目的是创建对象,而不是返回值。
  • 如果提供了返回值类型,则该方法不再是构造方法,而是普通方法,不能用于对象的创建。
3、构造方法怎么调用呢?
  • 使用new运算符来调用。
  • 语法:new 构造方法名(实参);
  • 注意:构造方法最终执行结束之后,会自动将创建的对象的内存地址返回。但构造方法体中不需要提供“return 值;”这样的语句。
4、构造方法相关注意事项
  • 在Java语言中,如果一个类没有显式定义构造方法,系统会默认提供一个无参数的构造方法。这个构造方法通常称为缺省构造器。
  • 如果一个类显式定义了构造方法,系统则不再提供缺省构造器。因此,为了对象创建更加方便,建议手动编写一个无参数的构造方法。
  • 在Java中,一个类可以定义多个构造方法,并且这些构造方法自动构成了方法的重载。这意味着可以根据不同的参数列表调用不同的构造方法来创建对象。
  • 构造方法中给属性赋值是对象第一次创建时属性的初始值。然而,单独定义set方法给属性赋值的好处在于后期可以灵活地修改属性的值。这种方式允许在对象创建后,根据需要修改对象的属性,从而增加了对象的灵活性和可维护性。
5、构造方法的执行原理
  • 构造方法的执行包括两个重要的阶段:

    • 第一阶段:对象的创建
    • 第二阶段:对象的初始化
  • 对象在什么时候创建的?

    • 当使用new关键字实例化一个对象时,在堆内存中直接开辟空间。这个过程中,会给对象的所有属性赋默认值,完成对象的创建。这一过程发生在构造方法体执行之前。
  • 对象初始化在什么时候完成的?

    • 构造方法体开始执行时,标志着对象的初始化过程开始。在构造方法体中,可以对对象的属性进行赋值等初始化操作。构造方法体执行完毕,表示对象初始化完毕。此时,对象处于可用状态,可以被程序进一步操作和调用。
6、构造代码块
  • 语法格式:

    • 构造代码块的语法格式为一对大括号{},没有参数列表。
  • 执行时机及次数:

    • 每次在使用new关键字创建对象时,构造代码块都会被执行。
    • 构造代码块是在构造方法执行之前执行的,因此在对象的初始化过程中,构造代码块是首先被执行的。
public class Example {
    private int x;
    private int y;

    // 构造代码块
    {
        System.out.println("构造代码块被执行");
        x = 5;
        y = 10;
    }

    // 构造方法
    public Example() {
        System.out.println("构造方法被调用");
    }

    // 获取x的值
    public int getX() {
        return x;
    }

    // 获取y的值
    public int getY() {
        return y;
    }

    public static void main(String[] args) {
        // 创建对象
        Example obj = new Example();
        // 输出属性值
        System.out.println("x 的值为:" + obj.getX());
        System.out.println("y 的值为:" + obj.getY());
    }
}

7、构造代码块的作用

构造代码块可以用于将对象初始化时共享的代码抽取出来,实现代码的复用。具体而言:

  • 如果所有的构造方法在最开始的时候有相同的一部分代码,可以将这部分代码放入构造代码块中。
  • 构造代码块会在每次对象创建时都执行,确保共享的代码被执行,并且避免了代码重复。
    这样,通过构造代码块,可以提高代码的可维护性和可读性,减少代码冗余,提高代码复用性。

*八、this关键字

this 本质上是一个引用。this 中保存的是当前对象的内存地址。
在Java中,this 是一个关键字,用于引用当前对象的实例。它通常用于区分实例变量和方法参数之间的命名冲突,或者在一个类的方法内部调用同一个类的另一个方法。下面详细解释 this 的几个常见用途:

  1. 区分实例变量和方法参数:当方法参数的名称与实例变量的名称相同时,使用 this 来引用当前对象的实例变量。这样可以明确指示要访问的是实例变量而不是方法参数。

    public class MyClass {
        private int value;
    
        public void setValue(int value) {   //这个形参与属性value相同,若不加this则根据Java中的就近原则,方法中的value都是形参value。
            this.value = value; // 使用 this 引用实例变量
        }
    }
    

    这里 this.value 指的是当前对象的 value 实例变量,而 value 是方法的参数。

  2. 在构造器中调用另一个构造器:可以使用 this 调用同一个类的另一个构造器,且只能出现在第一行。这种方法通常被称为构造器重载

    public class MyClass {
        private int value;
        private String name;
    
        public MyClass() {
            //new MyClass(10,"giaogiao");  这么会创建一个新对象
            this(10,"giaogiao");// 调用另一个构造器
    
            /*这个的目的是什么?
              当要求类在初始化时,给它赋指定的默认值。
              EG:this.value=10;
                  this.name="giaogiao";
              这段代码其实是重复的。而通过this(10,"giaogiao");获取当前对象,调用MyClass(int value,String name)构造器以简化开发。
            */
             
        }
    
        public MyClass(int value,String name) {
            this.value = value;
            this.name=name;
        }
    }
    

    在这个例子中,无参构造器调用了带参构造器,以避免重复代码。

  3. 传递当前对象的引用:可以将当前对象的引用传递给其他方法,这在某些情况下很有用。

在这里插入图片描述

package com.camellia.oop6;

// 学生类
public class Student {
  // 私有成员变量 name
  private String name;

  // 带参构造方法,用于初始化 name
  public Student(String name) {
    this.name = name;
  }

  // 无参构造方法
  public Student() {
  }

  // 获取学生姓名的方法
  public String getName() {
    return name;
  }

  // 设置学生姓名的方法
  public void setName(String name) {
    this.name = name;
  }

  // 学习方法,打印当前对象的引用地址
  public void study(){
    System.out.println("study---->" + this);  // 验证this是当前对象的引用。
  }
}

package com.camellia.oop6;

// 测试类
public class StudentTest {
  public static void main(String[] args) {
    // 创建学生对象s1,初始化姓名为"小吴"
    Student s1 = new Student("小吴");
    // 打印s1对象的引用地址
    System.out.println("main---->" + s1);
    // 调用s1对象的study()方法
    s1.study();

    // 创建学生对象s2,初始化姓名为"小花"
    Student s2 = new Student("小花");
    // 打印s2对象的引用地址
    System.out.println("main---->" + s2);
    // 调用s2对象的study()方法
    s2.study();
  }
}

在Java中,当对象调用自己的方法时,方法体内的 this 关键字会引用该对象的实例。
在方法被调用时,Java虚拟机会隐式地将当前对象的引用传递给方法,以便方法能够访问对象的成员变量和方法。
因此,在普通方法中通过 this 关键字引用的就是调用该方法的当前对象。

九、static关键字

在Java中,使用static关键字声明的成员(变量、方法、代码块)是类级别的,而不是与类的每个实例相关联的。
因此,它们可以通过类名直接访问,而无需创建类的实例。

9.1、静态变量存储图

1、没使用静态变量是的存储图

package com.camellia.oop7;

public class User {
    //用户id
    private String id;
    //用户国籍
    private String country="China";

    public User() {
    }

    public User(String id) {
        this.id = id;
    }
    public void PrintInfo(){
        System.out.println("ID: " + id+"\tCountry: " + country);
    }
}
package com.camellia.oop7;

public class UserTest {
    public static void main(String[] args) {
        User user1 = new User("1001");
        user1.PrintInfo();
        User user2 = new User("1002");
        user2.PrintInfo();
        User user3 = new User("1003");
        user3.PrintInfo();
    }
}

在这里插入图片描述

2、使用静态变量时的存储图

package com.camellia.oop8;

public class User {
    //用户id
    private String id;
    //用户国籍
    //静态变量什么时候开劈空间(初始化)、存储在哪里?
    //类加载时初始化
    //JDK8之后:静态变量存储在堆内存之中

    private static String country="China";

    public User() {
    }

    public User(String id) {
        this.id = id;
    }
    public void PrintInfo(){
        System.out.println("ID: " + id+"\tCountry: " + country);
    }
}
package com.camellia.oop8;

import com.camellia.oop7.User;

public class UserTest {
    public static void main(String[] args) {
        com.camellia.oop7.User user1 = new com.camellia.oop7.User("1001");
        user1.PrintInfo();
        com.camellia.oop7.User user2 = new com.camellia.oop7.User("1002");
        user2.PrintInfo();
        com.camellia.oop7.User user3 = new User("1003");
        user3.PrintInfo();
    }
}

在这里插入图片描述

9.2、Java中静态变量和方法的访问,以及静态变量不能使用this关键字

  1. 静态变量和方法建议使用类名.调用。虽然用引用.也可以,但是实质还是通过类来调用,而且这样容易和实例变量和方法的访问相混淆。
  2. 静态方法不能使用 this 关键字是因为 this 关键字代表当前对象的实例,而静态方法是与类相关联的,不依赖于任何特定的实例。所以无法直接访问实例变量和方法。

9.3、静态代码块

静态代码块是使用 static 关键字声明的代码块,它在类被加载时执行,并且只执行一次。
静态代码块通常用于在类加载时进行初始化操作,例如初始化静态变量或执行静态方法。它们的执行顺序是在类加载时按照代码顺序执行。

静态代码块的特点包括:

  1. 使用 static 关键字声明:静态代码块使用 static 关键字进行声明,以标识它们是与类相关联的,而不是与类的实例相关联的。

  2. 在类加载时执行:静态代码块在类被加载时执行,并且只执行一次。类的加载是指当 JVM 第一次加载类时发生的操作,通常在首次创建类的实例之前。

  3. 仅执行一次:静态代码块只会在类加载时执行一次,即使没有创建类的实例也会执行。

示例:

public class StaticTest01 { 

    // 实例方法
    public void doSome(){ 
        System.out.println(name);
    }

    // 实例变量
    String name = "zhangsan"; 

    // 静态变量
    static int i = 100;

    // 静态代码块
    static {
        // 报错原因:在静态上下文中无法直接访问实例相关的数据。
        //System.out.println(name);
        // 这个i可以访问,是因为i变量是静态变量,正好也是在类加载时初始化。
        System.out.println(i);
        System.out.println("静态代码块1执行了");
        // j无法访问的原因是:程序执行到这里的时候,j变量不存在。
        //System.out.println(j);

        System.out.println("xxxx-xx-xx xx:xx:xx 000 -> StaticTest01.class完成了类加载!");
    }

    // 静态变量
    static int j = 10000;

    // 静态代码块
    static {
        System.out.println("静态代码块2执行了");
    }

    public static void main(String[] args) {
        System.out.println("main execute!");
    }

    // 静态代码块
    static {
        System.out.println("静态代码块3执行了");
    }
}
//静态代码的执行顺序只能靠编写顺序的来确定,编写时在前的也就先执行。
  • 50
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值